1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
//! The `testdir!()` macro and friends. //! //! Tests for this are almost exclusively in the integration tests at `tests/macro.rs`. /// Creates a test directory at the requested scope. /// /// This macro creates a new or re-uses an existing [`NumberedDir`] for the current user. /// It than creates the requested sub-directory within this [`NumberedDir`]. The path for /// this directory is returned as a [`PathBuf`]. /// /// For the typical `testdir!()` invocation in a test function this would result in /// `/tmp/testdir-of-$USER/$CARGO_PKG_NAME-$N/$CARGO_CRATE_NAME/module/path/to/test_function_name`. /// A symbolic link to the most recent [`NumberedDir`] is also created as /// `/tmp/testdir-of-$USER/$CARGO_PKG_NAME-current -> $CARGO_PKG_NAME-$N`. /// /// **Reuse** of the [`NumberedDir`] is triggered when this process is being run as a /// subprocess of Cargo, as is typical when running `cargo test`. In this case the same /// [`NumberedDir`] is re-used between all Cargo sub-processes, which means it is shared /// between unittests, integration tests and doctests of the same test run. /// /// The path within the numbered directory is created based on the context and how it is /// invoked. There are several ways to specify this: /// /// * Directly provide the path using an expression, e.g. `testdir!("sub/dir"). This /// expression will be passed to [`NumberedDir::create_subdir`] and thus must evaluate to /// something which implements `AsRef<Path>`, e.g. a simple `"sub/dir"` can be used or /// something more advanced evaluating to a path, usually [`Path`] or [`PathBuf`]. /// /// * Use the scope of the current test function to create a unique and predictable /// directory: `testdir!(TestScope)`. This is the default when invoked as without any /// arguments as well: `testdir!()`. In this case the directory path will follow the crate /// name and module path, ending with the test function name. This also works in /// integration and doctests. /// /// * Use the scope of the current module: `testdir!(ModuleScope)`. In this case the crate /// name and module path is used, but with an additional final `mod` component. /// /// # Examples /// /// Inside a test function you can use the shorthand: /// ``` /// use std::path::PathBuf; /// use testdir::testdir; /// /// let path0: PathBuf = testdir!(); /// ``` /// /// This is the same as invoking: /// ``` /// # use testdir::testdir; /// let path1 = testdir!(TestScope); /// ``` /// These constructs can also be used in a doctest. /// /// The module path is valid in any scope, so can be used together with [once_cell] (or /// [lazy_static]) to share a common directory between different tests. /// ```ignore /// use std::path::PathBuf; /// use once_cell::sync::Lazy; /// use testdir::testdir; /// /// static TDIR: Lazy<PathBuf> = Lazy::new(|| testdir!(ModuleScope)); /// /// #[test] /// fn test_module_scope() { /// assert TDIR.ends_with("mod"); /// } /// ``` /// /// [lazy_static]: https://docs.rs/lazy_static /// [`NumberedDir`]: crate::NumberedDir /// [`PathBuf`]: std::path::PathBuf #[macro_export] macro_rules! testdir { () => { testdir!(TestScope) }; ( TestScope ) => {{ $crate::init_testdir!(); let module_path = ::std::module_path!(); let test_name = $crate::private::extract_test_name(&module_path); let subdir_path = ::std::path::Path::new(&module_path.replace("::", "/")).join(&test_name); $crate::with_testdir(move |tdir| tdir.create_subdir(subdir_path)) }}; ( ModuleScope ) => {{ $crate::init_testdir!(); let module_path = ::std::module_path!(); let subdir_path = ::std::path::Path::new(&module_path.replace("::", "/")).join("mod"); $crate::with_testdir(move |tdir| tdir.create_subdir(subdir_path)) }}; ( $e:expr ) => {{ $crate::init_testdir!(); $crate::with_testdir(move |tdir| tdir.create_subdir($e)) }}; } /// Initialises the global [`NumberedDir`] used by the [`testdir`] macro. /// /// This macro is implicitly called by the [`testdir`] macro to initialise the global /// [`NumberedDir`] instance with a **base** of the Cargo package name calling the macro. /// It must be called before any call to [`with_testdir`](crate::with_testdir) to ensure /// this is initialised. /// /// # Examples /// /// ``` /// use testdir::{init_testdir, with_testdir}; /// /// init_testdir!(); /// let path = with_testdir(|dir| dir.create_subdir("some/subdir")); /// assert!(path.is_dir()); /// assert!(path.ends_with("some/subdir")); /// ``` /// /// [`NumberedDir`]: crate::NumberedDir #[macro_export] macro_rules! init_testdir { () => {{ $crate::TESTDIR.get_or_init(move || { let pkg_name = String::from(::std::env!("CARGO_PKG_NAME")); let mut builder = $crate::NumberedDirBuilder::new(pkg_name); builder.reusefn($crate::private::reuse_cargo); let testdir = builder.create(); $crate::private::create_cargo_pid_file(testdir.path()); testdir }) }}; }