Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for cargo-nextest #4

Closed
flub opened this issue Oct 4, 2023 · 5 comments · Fixed by #7
Closed

Support for cargo-nextest #4

flub opened this issue Oct 4, 2023 · 5 comments · Fixed by #7

Comments

@flub
Copy link
Owner

flub commented Oct 4, 2023

When using cargo-nextest the detection for being in the same testrun is probably broken.

@hellertime
Copy link

Just leaving a 👍 for this. Really enjoying using testdir, but nexttest definitely causes some issues with paths disappearing unexpectedly!

@hellertime
Copy link

I ended up copying a bunch of the logic out of testdir into my work, to fast track a nextest compatible version of testdir.

This is mainly to serve as an example, and would require a different approach for integrating the changes into testdir proper.

At its core, there is a second reusefn needed to support nextest, and a test against the NEXTEST env var to decide when to use it:

pub(crate) mod nextestdir {
    use std::path::Path;

    const NEXTEST_RUN_ID_FILE_NAME: &str = "nextest-run-id";

    pub(crate) fn reuse_nextest(dir: &Path) -> bool {
        let file_name = dir.join(NEXTEST_RUN_ID_FILE_NAME);
        if let Ok(content) = std::fs::read_to_string(file_name) {
            if let Ok(read_nextest_run_id) = content.parse::<String>() {
                if let Ok(nextest_run_id) = std::env::var("NEXTEST_RUN_ID") {
                    return read_nextest_run_id == nextest_run_id;
                }
            }
        }
        false
    }

    pub(crate) fn create_nextest_run_id_file(dir: &Path) {
        if let Ok(nextest_run_id) = std::env::var("NEXTEST_RUN_ID") {
            let file_name = dir.join(NEXTEST_RUN_ID_FILE_NAME);
            if !file_name.exists() {
                std::fs::write(&file_name, nextest_run_id.to_string())
                    .expect("Failed to write nextest run ID");
            }
        }
    }

    pub(crate) fn with_nextesdir<F, R>(func: F) -> R
    where
        F: FnOnce(&testdir::NumberedDir) -> R,
    {
        let test_dir = testdir::TESTDIR.get_or_init(|| {
            let mut builder =
                testdir::NumberedDirBuilder::new(String::from("init_testdir-not-called"));
            builder.reusefn(reuse_nextest);
            let testdir = builder.create().expect("Failed to create testdir");
            create_nextest_run_id_file(testdir.path());
            testdir
        });
        func(test_dir)
    }
}

/// Specialized version of testdir::init_testdir!() that will work with nextest
macro_rules! init_nextestdir {
    () => {{
        testdir::TESTDIR.get_or_init(move || {
            let parent = match testdir::private::cargo_metadata::MetadataCommand::new().exec() {
                Ok(metadata) => metadata.target_directory.into(),
                Err(_) => {
                    // In some environments cargo-metadata is not available,
                    // e.g. cargo-dinghy.  Use the directory of test executable.
                    let current_exe = ::std::env::current_exe().expect("no current exe");
                    current_exe
                        .parent()
                        .expect("no parent dir for current exe")
                        .into()
                }
            };
            let pkg_name = "testdir";
            let mut builder = testdir::NumberedDirBuilder::new(pkg_name.to_string());
            builder.set_parent(parent);
            builder.reusefn(crate::nextestdir::reuse_nextest);
            let testdir = builder.create().expect("Failed to create testdir");
            crate::nextestdir::create_nextest_run_id_file(testdir.path());
            testdir
        })
    }};
}

/// Specialized version of testdir::testdir!() that will work with nextest
macro_rules! nextestdir {
    () => {{
        init_nextestdir!();
        let module_path = ::std::module_path!();
        let test_name = testdir::private::extract_test_name(&module_path);
        let subdir_path = ::std::path::Path::new(&module_path.replace("::", "/")).join(&test_name);
        crate::nextestdir::with_nextesdir(move |tdir| {
            tdir.create_subdir(subdir_path)
                .expect("Failed to create test-scope sub-directory")
        })
    }};
}

/// Compatible layer to give us either testdir!() or nextestdir!()
macro_rules! testdir_compat {
  () => {{
      let is_nextest = if let Ok(nextest) = std::env::var("NEXTEST") {
          if let Ok(read_nextest) = nextest.parse::<i32>() {
              read_nextest == 1
           } else {
               false
           }
       } else {
            false
       };
       if is_nextest {
            nextestdir!()
        } else {
            testdir::testdir!()
        }
    }};
}

@flub
Copy link
Owner Author

flub commented Nov 13, 2023

awesome! thanks for these pointers, I still haven't managed to find time to work on this but this certainly will speed things up!

(if you feel like making a PR out of this that'd also be cool ;) but no worries if not)

@hellertime
Copy link

I had originally planned implement this in a fork of testdir, before I realized everything I needed was public and could be re-implemented.

If you'd be OK with the approach that tries to detect if its running under nextest and falls back to the default test runner approach if not, I'd be OK to work this into a PR.

@flub
Copy link
Owner Author

flub commented Nov 25, 2023

@hellertime

Apologies for taking so long to come back to this. I've found some time to wrap my head around testdir again and look at this issue.

To my surprise I ended up making just a tiny change: #7

I haven't tested it that widely yet, so maybe I missed something. I would be grateful if you could check out that approach and see if it works for your situation.

@flub flub closed this as completed in #7 Nov 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants