add tests templates
This commit is contained in:
		
							
								
								
									
										129
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										129
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -183,6 +183,23 @@ dependencies = [ | ||||
|  "windows-sys 0.52.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "fastrand" | ||||
| version = "2.0.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" | ||||
|  | ||||
| [[package]] | ||||
| name = "getrandom" | ||||
| version = "0.2.12" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "libc", | ||||
|  "wasi", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "glob" | ||||
| version = "0.3.1" | ||||
| @@ -195,6 +212,12 @@ version = "0.3.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" | ||||
|  | ||||
| [[package]] | ||||
| name = "hex" | ||||
| version = "0.4.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" | ||||
|  | ||||
| [[package]] | ||||
| name = "io-lifetimes" | ||||
| version = "1.0.11" | ||||
| @@ -206,6 +229,12 @@ dependencies = [ | ||||
|  "windows-sys 0.48.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "lazy_static" | ||||
| version = "1.4.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | ||||
|  | ||||
| [[package]] | ||||
| name = "libc" | ||||
| version = "0.2.152" | ||||
| @@ -303,6 +332,12 @@ dependencies = [ | ||||
|  "siphasher", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "ppv-lite86" | ||||
| version = "0.2.17" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" | ||||
|  | ||||
| [[package]] | ||||
| name = "pretty_assertions" | ||||
| version = "1.4.0" | ||||
| @@ -322,6 +357,29 @@ dependencies = [ | ||||
|  "unicode-ident", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "procfs" | ||||
| version = "0.16.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" | ||||
| dependencies = [ | ||||
|  "bitflags 2.4.1", | ||||
|  "hex", | ||||
|  "lazy_static", | ||||
|  "procfs-core", | ||||
|  "rustix 0.38.30", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "procfs-core" | ||||
| version = "0.16.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" | ||||
| dependencies = [ | ||||
|  "bitflags 2.4.1", | ||||
|  "hex", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "quote" | ||||
| version = "1.0.35" | ||||
| @@ -337,6 +395,18 @@ version = "0.8.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
|  "rand_chacha", | ||||
|  "rand_core", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rand_chacha" | ||||
| version = "0.3.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" | ||||
| dependencies = [ | ||||
|  "ppv-lite86", | ||||
|  "rand_core", | ||||
| ] | ||||
|  | ||||
| @@ -345,6 +415,9 @@ name = "rand_core" | ||||
| version = "0.6.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" | ||||
| dependencies = [ | ||||
|  "getrandom", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "rayon" | ||||
| @@ -366,6 +439,15 @@ dependencies = [ | ||||
|  "crossbeam-utils", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "redox_syscall" | ||||
| version = "0.4.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" | ||||
| dependencies = [ | ||||
|  "bitflags 1.3.2", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "regex" | ||||
| version = "1.10.2" | ||||
| @@ -395,6 +477,15 @@ version = "0.8.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" | ||||
|  | ||||
| [[package]] | ||||
| name = "rlimit" | ||||
| version = "0.10.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3560f70f30a0f16d11d01ed078a07740fe6b489667abc7c7b029155d9f21c3d8" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "roff" | ||||
| version = "0.2.1" | ||||
| @@ -461,6 +552,19 @@ dependencies = [ | ||||
|  "windows", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "tempfile" | ||||
| version = "3.9.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "fastrand", | ||||
|  "redox_syscall", | ||||
|  "rustix 0.38.30", | ||||
|  "windows-sys 0.52.0", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "terminal_size" | ||||
| version = "0.2.6" | ||||
| @@ -524,15 +628,22 @@ dependencies = [ | ||||
|  "clap", | ||||
|  "clap_complete", | ||||
|  "clap_mangen", | ||||
|  "libc", | ||||
|  "phf", | ||||
|  "phf_codegen", | ||||
|  "pretty_assertions", | ||||
|  "procfs", | ||||
|  "rand", | ||||
|  "regex", | ||||
|  "rlimit", | ||||
|  "tempfile", | ||||
|  "textwrap", | ||||
|  "uu_lscpu", | ||||
|  "uu_mountpoint", | ||||
|  "uu_pwdx", | ||||
|  "uu_renice", | ||||
|  "uucore", | ||||
|  "xattr", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -578,6 +689,7 @@ checksum = "d5de2eba1364f6274f35f121eb8671b98ac5fa8fe1271694721e17034e85e8bc" | ||||
| dependencies = [ | ||||
|  "clap", | ||||
|  "glob", | ||||
|  "libc", | ||||
|  "nix", | ||||
|  "once_cell", | ||||
|  "os_display", | ||||
| @@ -602,6 +714,12 @@ version = "0.0.24" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d841f8408028085ca65896cdd60b9925d4e407cb69989a64889f2bebbb51147b" | ||||
|  | ||||
| [[package]] | ||||
| name = "wasi" | ||||
| version = "0.11.0+wasi-snapshot-preview1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" | ||||
|  | ||||
| [[package]] | ||||
| name = "wild" | ||||
| version = "2.2.0" | ||||
| @@ -784,6 +902,17 @@ version = "0.52.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" | ||||
|  | ||||
| [[package]] | ||||
| name = "xattr" | ||||
| version = "1.3.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" | ||||
| dependencies = [ | ||||
|  "libc", | ||||
|  "linux-raw-sys 0.4.12", | ||||
|  "rustix 0.38.30", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "yansi" | ||||
| version = "0.5.1" | ||||
|   | ||||
							
								
								
									
										16
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								Cargo.toml
									
									
									
									
									
								
							| @@ -42,6 +42,9 @@ libc = "0.2.152" | ||||
| phf = "0.11.2" | ||||
| phf_codegen = "0.11.2" | ||||
| textwrap = { version = "0.16.0", features = ["terminal_size"] } | ||||
| xattr = "1.3.1" | ||||
| tempfile = "3.9.0" | ||||
| rand = { version = "0.8", features = ["small_rng"] } | ||||
|  | ||||
| [dependencies] | ||||
| clap = { workspace = true } | ||||
| @@ -60,10 +63,23 @@ mountpoint = { optional = true, version = "0.0.1", package = "uu_mountpoint", pa | ||||
|  | ||||
| [dev-dependencies] | ||||
| pretty_assertions = "1" | ||||
| regex = { workspace = true } | ||||
| tempfile = { workspace = true } | ||||
| libc = { workspace = true } | ||||
| rand = { workspace = true } | ||||
| uucore = { workspace = true, features = ["entries", "process", "signals"] } | ||||
|  | ||||
| [target.'cfg(unix)'.dev-dependencies] | ||||
| xattr = { workspace = true } | ||||
|  | ||||
| [target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies] | ||||
| procfs = { version = "0.16", default-features = false } | ||||
| rlimit = "0.10.1" | ||||
|  | ||||
| [build-dependencies] | ||||
| phf_codegen = { workspace = true } | ||||
|  | ||||
|  | ||||
| [[bin]] | ||||
| name = "util-linux" | ||||
| path = "src/bin/util-linux.rs" | ||||
|   | ||||
							
								
								
									
										15
									
								
								tests/by-util/test_lscpu.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tests/by-util/test_lscpu.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| // This file is part of the uutils util-linux package. | ||||
| // | ||||
| // For the full copyright and license information, please view the LICENSE | ||||
| // file that was distributed with this source code. | ||||
| // spell-checker:ignore (words) symdir somefakedir | ||||
|  | ||||
| use std::path::PathBuf; | ||||
|  | ||||
| use crate::common::util::{TestScenario, UCommand}; | ||||
|  | ||||
| #[test] | ||||
| #[ignore] | ||||
| fn test_invalid_arg() { | ||||
|     new_ucmd!().arg("--definitely-invalid").fails().code_is(1); | ||||
| } | ||||
							
								
								
									
										14
									
								
								tests/by-util/test_mountpoint.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/by-util/test_mountpoint.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| // This file is part of the uutils util-linux package. | ||||
| // | ||||
| // For the full copyright and license information, please view the LICENSE | ||||
| // file that was distributed with this source code. | ||||
| // spell-checker:ignore (words) symdir somefakedir | ||||
|  | ||||
| use std::path::PathBuf; | ||||
|  | ||||
| use crate::common::util::{TestScenario, UCommand}; | ||||
|  | ||||
| #[test] | ||||
| fn test_invalid_arg() { | ||||
|     new_ucmd!().arg("--definitely-invalid").fails().code_is(1); | ||||
| } | ||||
							
								
								
									
										14
									
								
								tests/by-util/test_pwdx.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/by-util/test_pwdx.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| // This file is part of the uutils util-linux package. | ||||
| // | ||||
| // For the full copyright and license information, please view the LICENSE | ||||
| // file that was distributed with this source code. | ||||
| // spell-checker:ignore (words) symdir somefakedir | ||||
|  | ||||
| use std::path::PathBuf; | ||||
|  | ||||
| use crate::common::util::{TestScenario, UCommand}; | ||||
|  | ||||
| #[test] | ||||
| fn test_invalid_arg() { | ||||
|     new_ucmd!().arg("--definitely-invalid").fails().code_is(1); | ||||
| } | ||||
							
								
								
									
										13
									
								
								tests/by-util/test_renice.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tests/by-util/test_renice.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| // This file is part of the uutils util-linux package. | ||||
| // | ||||
| // For the full copyright and license information, please view the LICENSE | ||||
| // file that was distributed with this source code. | ||||
| // spell-checker:ignore (words) symdir somefakedir | ||||
|  | ||||
|  | ||||
| use crate::common::util::{TestScenario, UCommand}; | ||||
|  | ||||
| #[test] | ||||
| fn test_invalid_arg() { | ||||
|     new_ucmd!().arg("--definitely-invalid").fails().code_is(1); | ||||
| } | ||||
							
								
								
									
										93
									
								
								tests/common/macros.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								tests/common/macros.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| // This file is part of the uutils coreutils package. | ||||
| // | ||||
| // For the full copyright and license information, please view the LICENSE | ||||
| // file that was distributed with this source code. | ||||
|  | ||||
| /// Platform-independent helper for constructing a `PathBuf` from individual elements | ||||
| #[macro_export] | ||||
| macro_rules! path_concat { | ||||
|     ($e:expr, ..$n:expr) => {{ | ||||
|         use std::path::PathBuf; | ||||
|         let n = $n; | ||||
|         let mut pb = PathBuf::new(); | ||||
|         for _ in 0..n { | ||||
|             pb.push($e); | ||||
|         } | ||||
|         pb.to_str().unwrap().to_owned() | ||||
|     }}; | ||||
|     ($($e:expr),*) => {{ | ||||
|         use std::path::PathBuf; | ||||
|         let mut pb = PathBuf::new(); | ||||
|         $( | ||||
|             pb.push($e); | ||||
|         )* | ||||
|         pb.to_str().unwrap().to_owned() | ||||
|     }}; | ||||
| } | ||||
|  | ||||
| /// Deduce the name of the test binary from the test filename. | ||||
| /// | ||||
| /// e.g.: `tests/by-util/test_cat.rs` -> `cat` | ||||
| #[macro_export] | ||||
| macro_rules! util_name { | ||||
|     () => { | ||||
|         module_path!() | ||||
|             .split("_") | ||||
|             .nth(1) | ||||
|             .and_then(|s| s.split("::").next()) | ||||
|             .expect("no test name") | ||||
|     }; | ||||
| } | ||||
|  | ||||
| /// Convenience macro for acquiring a [`UCommand`] builder. | ||||
| /// | ||||
| /// Returns the following: | ||||
| /// - a [`UCommand`] builder for invoking the binary to be tested | ||||
| /// | ||||
| /// This macro is intended for quick, single-call tests. For more complex tests | ||||
| /// that require multiple invocations of the tested binary, see [`TestScenario`] | ||||
| /// | ||||
| /// [`UCommand`]: crate::tests::common::util::UCommand | ||||
| /// [`TestScenario]: crate::tests::common::util::TestScenario | ||||
| #[macro_export] | ||||
| macro_rules! new_ucmd { | ||||
|     () => { | ||||
|         TestScenario::new(util_name!()).ucmd() | ||||
|     }; | ||||
| } | ||||
|  | ||||
| /// Convenience macro for acquiring a [`UCommand`] builder and a test path. | ||||
| /// | ||||
| /// Returns a tuple containing the following: | ||||
| /// - an [`AtPath`] that points to a unique temporary test directory | ||||
| /// - a [`UCommand`] builder for invoking the binary to be tested | ||||
| /// | ||||
| /// This macro is intended for quick, single-call tests. For more complex tests | ||||
| /// that require multiple invocations of the tested binary, see [`TestScenario`] | ||||
| /// | ||||
| /// [`UCommand`]: crate::tests::common::util::UCommand | ||||
| /// [`AtPath`]: crate::tests::common::util::AtPath | ||||
| /// [`TestScenario]: crate::tests::common::util::TestScenario | ||||
| #[macro_export] | ||||
| macro_rules! at_and_ucmd { | ||||
|     () => {{ | ||||
|         let ts = TestScenario::new(util_name!()); | ||||
|         (ts.fixtures.clone(), ts.ucmd()) | ||||
|     }}; | ||||
| } | ||||
|  | ||||
| /// If `common::util::expected_result` returns an error, i.e. the `util` in `$PATH` doesn't | ||||
| /// include a coreutils version string or the version is too low, | ||||
| /// this macro can be used to automatically skip the test and print the reason. | ||||
| #[macro_export] | ||||
| macro_rules! unwrap_or_return { | ||||
|     ( $e:expr ) => { | ||||
|         match $e { | ||||
|             Ok(x) => x, | ||||
|             Err(e) => { | ||||
|                 println!("test skipped: {}", e); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										8
									
								
								tests/common/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tests/common/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| // This file is part of the uutils coreutils package. | ||||
| // | ||||
| // For the full copyright and license information, please view the LICENSE | ||||
| // file that was distributed with this source code. | ||||
| #[macro_use] | ||||
| pub mod macros; | ||||
| pub mod random; | ||||
| pub mod util; | ||||
							
								
								
									
										339
									
								
								tests/common/random.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										339
									
								
								tests/common/random.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,339 @@ | ||||
| // This file is part of the uutils coreutils package. | ||||
| // | ||||
| // For the full copyright and license information, please view the LICENSE | ||||
| // file that was distributed with this source code. | ||||
|  | ||||
| use rand::distributions::{Distribution, Uniform}; | ||||
| use rand::{thread_rng, Rng}; | ||||
|  | ||||
| /// Samples alphanumeric characters `[A-Za-z0-9]` including newline `\n` | ||||
| /// | ||||
| /// # Examples | ||||
| /// | ||||
| /// ```rust,ignore | ||||
| /// use rand::{Rng, thread_rng}; | ||||
| /// | ||||
| /// let vec = thread_rng() | ||||
| ///     .sample_iter(AlphanumericNewline) | ||||
| ///     .take(10) | ||||
| ///     .collect::<Vec<u8>>(); | ||||
| /// println!("Random chars: {}", String::from_utf8(vec).unwrap()); | ||||
| /// ``` | ||||
| #[derive(Clone, Copy, Debug)] | ||||
| pub struct AlphanumericNewline; | ||||
|  | ||||
| impl AlphanumericNewline { | ||||
|     /// The charset to act upon | ||||
|     const CHARSET: &'static [u8] = | ||||
|         b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\n"; | ||||
|  | ||||
|     /// Generate a random byte from [`Self::CHARSET`] and return it as `u8`. | ||||
|     /// | ||||
|     /// # Arguments | ||||
|     /// | ||||
|     /// * `rng`: A [`rand::Rng`] | ||||
|     /// | ||||
|     /// returns: u8 | ||||
|     fn random<R>(rng: &mut R) -> u8 | ||||
|     where | ||||
|         R: Rng + ?Sized, | ||||
|     { | ||||
|         let idx = rng.gen_range(0..Self::CHARSET.len()); | ||||
|         Self::CHARSET[idx] | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Distribution<u8> for AlphanumericNewline { | ||||
|     fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u8 { | ||||
|         Self::random(rng) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Generate a random string from a [`Distribution`] | ||||
| /// | ||||
| /// # Examples | ||||
| /// | ||||
| /// ```rust,ignore | ||||
| /// use crate::common::random::{AlphanumericNewline, RandomString}; | ||||
| /// use rand::distributions::Alphanumeric; | ||||
| /// | ||||
| /// // generates a 100 byte string with characters from AlphanumericNewline | ||||
| /// let random_string = RandomString::generate(AlphanumericNewline, 100); | ||||
| /// assert_eq!(100, random_string.len()); | ||||
| /// | ||||
| /// // generates a 100 byte string with 10 newline characters not ending with a newline | ||||
| /// let string = RandomString::generate_with_delimiter(Alphanumeric, b'\n', 10, false, 100); | ||||
| /// assert_eq!(100, random_string.len()); | ||||
| /// ``` | ||||
| pub struct RandomString; | ||||
|  | ||||
| impl RandomString { | ||||
|     /// Generate a random string from the given [`Distribution`] with the given `length` in bytes. | ||||
|     /// | ||||
|     /// # Arguments | ||||
|     /// | ||||
|     /// * `dist`: A u8 [`Distribution`] | ||||
|     /// * `length`: the length of the resulting string in bytes | ||||
|     /// | ||||
|     /// returns: String | ||||
|     pub fn generate<D>(dist: D, length: usize) -> String | ||||
|     where | ||||
|         D: Distribution<u8>, | ||||
|     { | ||||
|         thread_rng() | ||||
|             .sample_iter(dist) | ||||
|             .take(length) | ||||
|             .map(|b| b as char) | ||||
|             .collect() | ||||
|     } | ||||
|  | ||||
|     /// Generate a random string from the [`Distribution`] with the given `length` in bytes. The | ||||
|     /// function takes a `delimiter`, which is randomly distributed in the string, such that exactly | ||||
|     /// `num_delimiter` amount of `delimiter`s occur. If `end_with_delimiter` is set, then the | ||||
|     /// string ends with the delimiter, else the string does not end with the delimiter. | ||||
|     /// | ||||
|     /// # Arguments | ||||
|     /// | ||||
|     /// * `dist`: A `u8` [`Distribution`] | ||||
|     /// * `delimiter`: A `u8` delimiter, which does not need to be included in the `Distribution` | ||||
|     /// * `num_delimiter`: The number of `delimiter`s contained in the resulting string | ||||
|     /// * `end_with_delimiter`: If the string shall end with the given delimiter | ||||
|     /// * `length`: the length of the resulting string in bytes | ||||
|     /// | ||||
|     /// returns: String | ||||
|     /// | ||||
|     /// # Examples | ||||
|     /// | ||||
|     /// ```rust,ignore | ||||
|     /// use crate::common::random::{AlphanumericNewline, RandomString}; | ||||
|     /// | ||||
|     /// // generates a 100 byte string with 10 '\0' byte characters not ending with a '\0' byte | ||||
|     /// let string = RandomString::generate_with_delimiter(AlphanumericNewline, 0, 10, false, 100); | ||||
|     /// assert_eq!(100, random_string.len()); | ||||
|     /// assert_eq!( | ||||
|     ///     10, | ||||
|     ///     random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|     /// ); | ||||
|     /// assert!(!random_string.as_bytes().ends_with(&[0])); | ||||
|     /// ``` | ||||
|     pub fn generate_with_delimiter<D>( | ||||
|         dist: D, | ||||
|         delimiter: u8, | ||||
|         num_delimiter: usize, | ||||
|         end_with_delimiter: bool, | ||||
|         length: usize, | ||||
|     ) -> String | ||||
|     where | ||||
|         D: Distribution<u8>, | ||||
|     { | ||||
|         if length == 0 { | ||||
|             return String::new(); | ||||
|         } else if length == 1 { | ||||
|             return if num_delimiter > 0 { | ||||
|                 String::from(delimiter as char) | ||||
|             } else { | ||||
|                 String::from(thread_rng().sample(&dist) as char) | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         let samples = length - 1; | ||||
|         let mut result: Vec<u8> = thread_rng().sample_iter(&dist).take(samples).collect(); | ||||
|  | ||||
|         if num_delimiter == 0 { | ||||
|             result.push(thread_rng().sample(&dist)); | ||||
|             return String::from_utf8(result).unwrap(); | ||||
|         } | ||||
|  | ||||
|         let num_delimiter = if end_with_delimiter { | ||||
|             num_delimiter - 1 | ||||
|         } else { | ||||
|             num_delimiter | ||||
|         }; | ||||
|  | ||||
|         let between = Uniform::new(0, samples); | ||||
|         for _ in 0..num_delimiter { | ||||
|             let mut pos = between.sample(&mut thread_rng()); | ||||
|             let turn = pos; | ||||
|             while result[pos] == delimiter { | ||||
|                 pos += 1; | ||||
|                 if pos >= samples { | ||||
|                     pos = 0; | ||||
|                 } | ||||
|                 if pos == turn { | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             result[pos] = delimiter; | ||||
|         } | ||||
|  | ||||
|         if end_with_delimiter { | ||||
|             result.push(delimiter); | ||||
|         } else { | ||||
|             result.push(thread_rng().sample(&dist)); | ||||
|         } | ||||
|  | ||||
|         String::from_utf8(result).unwrap() | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use rand::distributions::Alphanumeric; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_random_string_generate() { | ||||
|         let random_string = RandomString::generate(AlphanumericNewline, 0); | ||||
|         assert_eq!(0, random_string.len()); | ||||
|  | ||||
|         let random_string = RandomString::generate(AlphanumericNewline, 1); | ||||
|         assert_eq!(1, random_string.len()); | ||||
|  | ||||
|         let random_string = RandomString::generate(AlphanumericNewline, 100); | ||||
|         assert_eq!(100, random_string.len()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_random_string_generate_with_delimiter_when_length_is_zero() { | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 0, false, 0); | ||||
|         assert_eq!(0, random_string.len()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_random_string_generate_with_delimiter_when_num_delimiter_is_greater_than_length() { | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 2, false, 1); | ||||
|         assert_eq!(1, random_string.len()); | ||||
|         assert!(random_string.as_bytes().contains(&0)); | ||||
|         assert!(random_string.as_bytes().ends_with(&[0])); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_random_string_generate_with_delimiter_should_end_with_delimiter() { | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, true, 1); | ||||
|         assert_eq!(1, random_string.len()); | ||||
|         assert_eq!( | ||||
|             1, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|         assert!(random_string.as_bytes().ends_with(&[0])); | ||||
|  | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, false, 1); | ||||
|         assert_eq!(1, random_string.len()); | ||||
|         assert_eq!( | ||||
|             1, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|         assert!(random_string.as_bytes().ends_with(&[0])); | ||||
|  | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, true, 2); | ||||
|         assert_eq!(2, random_string.len()); | ||||
|         assert_eq!( | ||||
|             1, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|         assert!(random_string.as_bytes().ends_with(&[0])); | ||||
|  | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 2, true, 2); | ||||
|         assert_eq!(2, random_string.len()); | ||||
|         assert_eq!( | ||||
|             2, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|         assert!(random_string.as_bytes().ends_with(&[0])); | ||||
|  | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, true, 3); | ||||
|         assert_eq!(3, random_string.len()); | ||||
|         assert_eq!( | ||||
|             1, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|         assert!(random_string.as_bytes().ends_with(&[0])); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_random_string_generate_with_delimiter_should_not_end_with_delimiter() { | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 0, false, 1); | ||||
|         assert_eq!(1, random_string.len()); | ||||
|         assert_eq!( | ||||
|             0, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|  | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 0, true, 1); | ||||
|         assert_eq!(1, random_string.len()); | ||||
|         assert_eq!( | ||||
|             0, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|  | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, false, 2); | ||||
|         assert_eq!(2, random_string.len()); | ||||
|         assert_eq!( | ||||
|             1, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|         assert!(!random_string.as_bytes().ends_with(&[0])); | ||||
|  | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 1, false, 3); | ||||
|         assert_eq!(3, random_string.len()); | ||||
|         assert_eq!( | ||||
|             1, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|         assert!(!random_string.as_bytes().ends_with(&[0])); | ||||
|  | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 2, false, 3); | ||||
|         assert_eq!(3, random_string.len()); | ||||
|         assert_eq!( | ||||
|             2, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|         assert!(!random_string.as_bytes().ends_with(&[0])); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_generate_with_delimiter_with_greater_length() { | ||||
|         let random_string = | ||||
|             RandomString::generate_with_delimiter(Alphanumeric, 0, 100, false, 1000); | ||||
|         assert_eq!(1000, random_string.len()); | ||||
|         assert_eq!( | ||||
|             100, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|         assert!(!random_string.as_bytes().ends_with(&[0])); | ||||
|  | ||||
|         let random_string = RandomString::generate_with_delimiter(Alphanumeric, 0, 100, true, 1000); | ||||
|         assert_eq!(1000, random_string.len()); | ||||
|         assert_eq!( | ||||
|             100, | ||||
|             random_string.as_bytes().iter().filter(|p| **p == 0).count() | ||||
|         ); | ||||
|         assert!(random_string.as_bytes().ends_with(&[0])); | ||||
|     } | ||||
|  | ||||
|     /// Originally used to exclude an error within the `random` module. The two | ||||
|     /// affected tests timed out on windows, but only in the ci. These tests are | ||||
|     /// also the source for the concrete numbers. The timed out tests are | ||||
|     /// `test_tail.rs::test_pipe_when_lines_option_given_input_size_has_multiple_size_of_buffer_size` | ||||
|     /// `test_tail.rs::test_pipe_when_bytes_option_given_input_size_has_multiple_size_of_buffer_size`. | ||||
|     #[test] | ||||
|     fn test_generate_random_strings_when_length_is_around_critical_buffer_sizes() { | ||||
|         let length = 8192 * 3; | ||||
|         let random_string = RandomString::generate(AlphanumericNewline, length); | ||||
|         assert_eq!(length, random_string.len()); | ||||
|  | ||||
|         let length = 8192 * 3 + 1; | ||||
|         let random_string = | ||||
|             RandomString::generate_with_delimiter(Alphanumeric, b'\n', 100, true, length); | ||||
|         assert_eq!(length, random_string.len()); | ||||
|         assert_eq!( | ||||
|             100, | ||||
|             random_string | ||||
|                 .as_bytes() | ||||
|                 .iter() | ||||
|                 .filter(|p| **p == b'\n') | ||||
|                 .count() | ||||
|         ); | ||||
|         assert!(!random_string.as_bytes().ends_with(&[0])); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										3418
									
								
								tests/common/util.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3418
									
								
								tests/common/util.rs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										22
									
								
								tests/tests.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tests/tests.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| // This file is part of the uutils coreutils package. | ||||
| // | ||||
| // For the full copyright and license information, please view the LICENSE | ||||
| // file that was distributed with this source code. | ||||
| #[macro_use] | ||||
| mod common; | ||||
|  | ||||
| #[cfg(feature = "lscpu")] | ||||
| #[path = "by-util/test_lscpu.rs"] | ||||
| mod test_lscpu; | ||||
|  | ||||
| #[cfg(feature = "pwdx")] | ||||
| #[path = "by-util/test_pwdx.rs"] | ||||
| mod test_pwdx; | ||||
|  | ||||
| #[cfg(feature = "mountpoint")] | ||||
| #[path = "by-util/test_mountpoint.rs"] | ||||
| mod test_mountpoint; | ||||
|  | ||||
| #[cfg(feature = "renice")] | ||||
| #[path = "by-util/test_renice.rs"] | ||||
| mod test_renice; | ||||
		Reference in New Issue
	
	Block a user
	 Sylvestre Ledru
					Sylvestre Ledru