17
.pre-commit-config.yaml
Normal file
17
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: rust-linting
|
||||
name: Rust linting
|
||||
description: Run cargo fmt on files included in the commit.
|
||||
entry: cargo +stable fmt --
|
||||
pass_filenames: true
|
||||
types: [file, rust]
|
||||
language: system
|
||||
- id: rust-clippy
|
||||
name: Rust clippy
|
||||
description: Run cargo clippy on files included in the commit.
|
||||
entry: cargo +stable clippy --workspace --all-targets --all-features --
|
||||
pass_filenames: false
|
||||
types: [file, rust]
|
||||
language: system
|
||||
53
Cargo.lock
generated
53
Cargo.lock
generated
@@ -262,6 +262,22 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e9666f4a9a948d4f1dff0c08a4512b0f7c86414b23960104c243c10d79f4c3"
|
||||
dependencies = [
|
||||
"ctor-proc-macro",
|
||||
"dtor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor-proc-macro"
|
||||
version = "0.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f211af61d8efdd104f96e57adf5e426ba1bc3ed7a4ead616e15e5881fd79c4d"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.4.0"
|
||||
@@ -310,6 +326,21 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtor"
|
||||
version = "0.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "222ef136a1c687d4aa0395c175f2c4586e379924c352fd02f7870cf7de783c23"
|
||||
dependencies = [
|
||||
"dtor-proc-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtor-proc-macro"
|
||||
version = "0.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055"
|
||||
|
||||
[[package]]
|
||||
name = "dunce"
|
||||
version = "1.0.5"
|
||||
@@ -1198,6 +1229,7 @@ dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"clap_mangen",
|
||||
"ctor",
|
||||
"dns-lookup",
|
||||
"libc",
|
||||
"nix",
|
||||
@@ -1231,6 +1263,7 @@ dependencies = [
|
||||
"uu_uuidgen",
|
||||
"uucore",
|
||||
"uuid",
|
||||
"uutests",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
@@ -1472,6 +1505,26 @@ dependencies = [
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uutests"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12453ee52c9cffa6bf2c74f9f35ed0c824b846cb0b4bdee829d8150332e7204c"
|
||||
dependencies = [
|
||||
"ctor",
|
||||
"glob",
|
||||
"libc",
|
||||
"nix",
|
||||
"pretty_assertions",
|
||||
"rand",
|
||||
"regex",
|
||||
"rlimit",
|
||||
"tempfile",
|
||||
"time",
|
||||
"uucore",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
|
||||
@@ -56,6 +56,7 @@ libmount-sys = "0.1.1"
|
||||
linux-raw-sys = { version = "0.10.0", features = ["ioctl"] }
|
||||
md-5 = "0.10.6"
|
||||
nix = { version = "0.30", default-features = false }
|
||||
parse_datetime = "0.11.0"
|
||||
phf = "0.12.0"
|
||||
phf_codegen = "0.12.1"
|
||||
rand = { version = "0.9.0", features = ["small_rng"] }
|
||||
@@ -71,21 +72,21 @@ textwrap = { version = "0.16.0", features = ["terminal_size"] }
|
||||
thiserror = "2.0"
|
||||
uucore = "0.1.0"
|
||||
uuid = { version = "1.16.0", features = ["rng-rand"] }
|
||||
uutests = "0.1.0"
|
||||
windows = { version = "0.61.1" }
|
||||
xattr = "1.3.1"
|
||||
parse_datetime = "0.11.0"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
clap_complete = { workspace = true }
|
||||
clap_mangen = { workspace = true }
|
||||
dns-lookup = { workspace = true }
|
||||
parse_datetime = {workspace = true}
|
||||
phf = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
textwrap = { workspace = true }
|
||||
uucore = { workspace = true }
|
||||
parse_datetime = {workspace = true}
|
||||
|
||||
#
|
||||
blockdev = { optional = true, version = "0.0.1", package = "uu_blockdev", path = "src/uu/blockdev" }
|
||||
@@ -107,6 +108,7 @@ setsid = { optional = true, version = "0.0.1", package = "uu_setsid", path ="src
|
||||
uuidgen = { optional = true, version = "0.0.1", package = "uu_uuidgen", path ="src/uu/uuidgen" }
|
||||
|
||||
[dev-dependencies]
|
||||
ctor = "0.4.1"
|
||||
# dmesg test require fixed-boot-time feature turned on.
|
||||
dmesg = { version = "0.0.1", package = "uu_dmesg", path = "src/uu/dmesg", features = ["fixed-boot-time"] }
|
||||
libc = { workspace = true }
|
||||
@@ -114,6 +116,7 @@ pretty_assertions = "1"
|
||||
rand = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
uutests = { workspace = true }
|
||||
uucore = { workspace = true, features = ["entries", "process", "signals"] }
|
||||
uuid = { workspace = true }
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
@@ -23,9 +25,12 @@ fn test_report_mutually_exclusive_with_others() {
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux {
|
||||
use crate::common::util::TestScenario;
|
||||
use regex::Regex;
|
||||
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_fails_on_first_error() {
|
||||
new_ucmd!()
|
||||
@@ -56,7 +61,9 @@ mod linux {
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
mod non_linux {
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_fails_on_unsupported_platforms() {
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
// file that was distributed with this source code.
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
#[cfg(target_os = "linux")]
|
||||
use uutests::util::TestScenario;
|
||||
#[cfg(target_os = "linux")]
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
use crate::common::util::TestScenario;
|
||||
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
@@ -23,7 +25,10 @@ fn test_operations_mutually_exclusive() {
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux {
|
||||
use crate::common::util::TestScenario;
|
||||
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_fails_on_non_existing_path() {
|
||||
@@ -48,7 +53,9 @@ mod linux {
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
mod non_linux {
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_fails_on_unsupported_platforms() {
|
||||
|
||||
@@ -4,8 +4,13 @@
|
||||
// file that was distributed with this source code.
|
||||
// spell-checker:ignore (words) symdir somefakedir
|
||||
|
||||
use uutests::at_and_ucmd;
|
||||
#[cfg(unix)]
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
#[cfg(unix)]
|
||||
use uutests::util::TestScenario;
|
||||
#[cfg(unix)]
|
||||
use uutests::util_name;
|
||||
|
||||
#[cfg(unix)]
|
||||
use regex::Regex;
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
// file that was distributed with this source code.
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
#[cfg(target_os = "linux")]
|
||||
use uutests::util::TestScenario;
|
||||
#[cfg(target_os = "linux")]
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
use std::path::Path;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
fn write_file_content(dir: &Path, name: &str, content: &str) {
|
||||
std::fs::create_dir_all(dir).unwrap();
|
||||
|
||||
@@ -7,7 +7,9 @@ use std::io::Write;
|
||||
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_verb() {
|
||||
@@ -24,7 +26,9 @@ fn test_no_terminal() {
|
||||
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
mod non_unix {
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_fails_on_unsupported_platforms() {
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
// file that was distributed with this source code.
|
||||
// spell-checker:ignore (words) symdir somefakedir
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::at_and_ucmd;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
mod unix {
|
||||
use crate::common::util::{TestScenario, UCommand, TESTS_BINARY};
|
||||
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::get_tests_binary;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util::UCommand;
|
||||
use uutests::util_name;
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
||||
@@ -77,8 +80,11 @@ mod unix {
|
||||
|
||||
#[test]
|
||||
fn unprivileged_user_cannot_steal_controlling_tty() {
|
||||
let shell_cmd =
|
||||
format!("{TESTS_BINARY} setsid -w -c {TESTS_BINARY} setsid -w -c /b/usrin/true");
|
||||
let shell_cmd = format!(
|
||||
"{} setsid -w -c {} setsid -w -c /b/usrin/true",
|
||||
get_tests_binary(),
|
||||
get_tests_binary()
|
||||
);
|
||||
UCommand::new()
|
||||
.terminal_simulation(true)
|
||||
.arg(&shell_cmd)
|
||||
@@ -92,7 +98,8 @@ mod unix {
|
||||
#[test]
|
||||
fn unprivileged_user_can_take_new_controlling_tty() {
|
||||
let shell_cmd = format!(
|
||||
"/usr/bin/cat /proc/self/stat; {TESTS_BINARY} setsid -w -c /usr/bin/cat /proc/self/stat"
|
||||
"/usr/bin/cat /proc/self/stat; {} setsid -w -c /usr/bin/cat /proc/self/stat",
|
||||
get_tests_binary()
|
||||
);
|
||||
|
||||
let cmd_result = UCommand::new()
|
||||
@@ -124,7 +131,8 @@ mod unix {
|
||||
#[test]
|
||||
fn setsid_takes_session_leadership() {
|
||||
let shell_cmd = format!(
|
||||
"/usr/bin/cat /proc/self/stat; {TESTS_BINARY} setsid /usr/bin/cat /proc/self/stat"
|
||||
"/usr/bin/cat /proc/self/stat; {} setsid /usr/bin/cat /proc/self/stat",
|
||||
get_tests_binary()
|
||||
);
|
||||
|
||||
let cmd_result = UCommand::new()
|
||||
@@ -161,7 +169,9 @@ mod unix {
|
||||
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
mod non_unix {
|
||||
use crate::common::util::TestScenario;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
use uutests::util_name;
|
||||
|
||||
#[test]
|
||||
fn unsupported_platforms() {
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::common::util::{TestScenario, UCommand};
|
||||
use uutests::{new_ucmd, util::TestScenario, util::UCommand, util_name};
|
||||
|
||||
fn assert_ver_eq(cmd: &mut UCommand, ver: uuid::Version) {
|
||||
let uuid = Uuid::parse_str(
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// 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;
|
||||
@@ -1,340 +0,0 @@
|
||||
// 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.
|
||||
|
||||
use rand::distr::{Distribution, Uniform};
|
||||
use rand::{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.random_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>,
|
||||
{
|
||||
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(rng().sample(&dist) as char)
|
||||
};
|
||||
}
|
||||
|
||||
let samples = length - 1;
|
||||
let mut result: Vec<u8> = rng().sample_iter(&dist).take(samples).collect();
|
||||
|
||||
if num_delimiter == 0 {
|
||||
result.push(rng().sample(&dist));
|
||||
return String::from_utf8(result).unwrap();
|
||||
}
|
||||
|
||||
let num_delimiter = if end_with_delimiter {
|
||||
num_delimiter - 1
|
||||
} else {
|
||||
num_delimiter
|
||||
};
|
||||
|
||||
// safe to unwrap because samples is at least 1, thus high > low
|
||||
let between = Uniform::new(0, samples).unwrap();
|
||||
for _ in 0..num_delimiter {
|
||||
let mut pos = between.sample(&mut 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(rng().sample(&dist));
|
||||
}
|
||||
|
||||
String::from_utf8(result).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rand::distr::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]));
|
||||
}
|
||||
}
|
||||
3701
tests/common/util.rs
3701
tests/common/util.rs
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,20 @@
|
||||
// This file is part of the uutils coreutils package.
|
||||
// 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.
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
use std::env;
|
||||
|
||||
pub const TESTS_BINARY: &str = env!("CARGO_BIN_EXE_util-linux");
|
||||
|
||||
// Use the ctor attribute to run this function before any tests
|
||||
#[ctor::ctor]
|
||||
fn init() {
|
||||
unsafe {
|
||||
// Necessary for uutests to be able to find the binary
|
||||
std::env::set_var("UUTESTS_BINARY_PATH", TESTS_BINARY);
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "lscpu")]
|
||||
#[path = "by-util/test_lscpu.rs"]
|
||||
mod test_lscpu;
|
||||
|
||||
Reference in New Issue
Block a user