Merge branch 'unittests-in-ci' of github.com:tertsdiepraam/coreutils into unittests-in-ci

This commit is contained in:
Terts Diepraam 2022-03-19 10:58:59 +01:00
commit 76edba0e1e
12 changed files with 227 additions and 89 deletions

View file

@ -20,6 +20,13 @@ env:
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
cargo-deny:
name: Style/cargo-deny
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: EmbarkStudios/cargo-deny-action@v1
style_deps: style_deps:
## ToDO: [2021-11-10; rivy] 'Style/deps' needs more informative output and better integration of results into the GHA dashboard ## ToDO: [2021-11-10; rivy] 'Style/deps' needs more informative output and better integration of results into the GHA dashboard
name: Style/deps name: Style/deps

View file

@ -137,6 +137,8 @@ jobs:
echo "::error ::Failed to find summary of test results (missing '${SUITE_LOG_FILE}'); failing early" echo "::error ::Failed to find summary of test results (missing '${SUITE_LOG_FILE}'); failing early"
exit 1 exit 1
fi fi
# Compress logs before upload (fails otherwise)
gzip ${{ steps.vars.outputs.TEST_LOGS_GLOB }}
- name: Reserve SHA1/ID of 'test-summary' - name: Reserve SHA1/ID of 'test-summary'
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:

View file

@ -94,6 +94,16 @@ uutils: add new utility
gitignore: add temporary files gitignore: add temporary files
``` ```
## cargo-deny
This project uses [cargo-deny](https://github.com/EmbarkStudios/cargo-deny/) to
detect duplicate dependencies, checks licenses, etc. To run it locally, first
install it and then run with:
```
cargo deny --all-features check all
```
## Licensing ## Licensing
uutils is distributed under the terms of the MIT License; see the `LICENSE` file uutils is distributed under the terms of the MIT License; see the `LICENSE` file

29
Cargo.lock generated
View file

@ -877,7 +877,7 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f37978dab2ca789938a83b2f8bc1ef32db6633af9051a6cd409eff72cbaaa79a" checksum = "f37978dab2ca789938a83b2f8bc1ef32db6633af9051a6cd409eff72cbaaa79a"
dependencies = [ dependencies = [
"paste 1.0.6", "paste",
] ]
[[package]] [[package]]
@ -1431,31 +1431,12 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "paste"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880"
dependencies = [
"paste-impl",
"proc-macro-hack",
]
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.6" version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
[[package]]
name = "paste-impl"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6"
dependencies = [
"proc-macro-hack",
]
[[package]] [[package]]
name = "peeking_take_while" name = "peeking_take_while"
version = "0.1.2" version = "0.1.2"
@ -1574,12 +1555,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.36" version = "1.0.36"
@ -2559,7 +2534,7 @@ dependencies = [
"clap 3.0.10", "clap 3.0.10",
"coz", "coz",
"num-traits", "num-traits",
"paste 0.1.18", "paste",
"quickcheck", "quickcheck",
"rand", "rand",
"smallvec", "smallvec",

95
deny.toml Normal file
View file

@ -0,0 +1,95 @@
# spell-checker:ignore SSLeay RUSTSEC
# This section is considered when running `cargo deny check advisories`
# More documentation for the advisories section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
[advisories]
db-path = "~/.cargo/advisory-db"
db-urls = ["https://github.com/rustsec/advisory-db"]
vulnerability = "warn"
unmaintained = "warn"
yanked = "warn"
notice = "warn"
ignore = [
#"RUSTSEC-0000-0000",
]
# This section is considered when running `cargo deny check licenses`
# More documentation for the licenses section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
[licenses]
unlicensed = "deny"
allow = [
"MIT",
"Apache-2.0",
"ISC",
"BSD-2-Clause",
"BSD-2-Clause-FreeBSD",
"BSD-3-Clause",
"CC0-1.0",
"MPL-2.0", # XXX considered copyleft?
]
copyleft = "deny"
allow-osi-fsf-free = "neither"
default = "deny"
confidence-threshold = 0.8
exceptions = [
{ allow = ["OpenSSL"], name = "ring" },
]
[[licenses.clarify]]
name = "ring"
# SPDX considers OpenSSL to encompass both the OpenSSL and SSLeay licenses
# https://spdx.org/licenses/OpenSSL.html
# ISC - Both BoringSSL and ring use this for their new files
# MIT - "Files in third_party/ have their own licenses, as described therein. The MIT
# license, for third_party/fiat, which, unlike other third_party directories, is
# compiled into non-test libraries, is included below."
# OpenSSL - Obviously
expression = "ISC AND MIT AND OpenSSL"
license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }]
# This section is considered when running `cargo deny check bans`.
# More documentation about the 'bans' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
[bans]
multiple-versions = "deny"
wildcards = "allow"
highlight = "all"
# For each duplicate dependency, indicate the name of the dependency which
# introduces it.
# spell-checker: disable
skip = [
# blake2d_simd
{ name = "arrayvec", version = "=0.7.2" },
# flimit/unix_socket
{ name = "cfg-if", version = "=0.1.10" },
# ordered-multimap
{ name = "hashbrown", version = "=0.9.1" },
# kernel32-sys
{ name = "winapi", version = "=0.2.8" },
# bindgen 0.59.2
{ name = "clap", version = "=2.34.0" },
{ name = "strsim", version = "=0.8.0" },
{ name = "textwrap", version = "=0.11.0" },
{ name = "cpp_common", version = "=0.4.0" },
# quickcheck
{ name = "env_logger", version = "=0.8.4" },
# cpp_*
{ name = "memchr", version = "=1.0.2" },
{ name = "quote", version = "=0.3.15" },
{ name = "unicode-xid", version = "=0.0.4" },
# exacl
{ name = "nix", version = "=0.21.0" },
]
# spell-checker: enable
# This section is considered when running `cargo deny check sources`.
# More documentation about the 'sources' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
[sources]
unknown-registry = "warn"
unknown-git = "warn"
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
allow-git = []

View file

@ -23,7 +23,7 @@ smallvec = "1.7" # TODO(nicoo): Use `union` feature, requires Rust 1.49 or late
uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" } uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" }
[dev-dependencies] [dev-dependencies]
paste = "0.1.18" paste = "1.0.6"
quickcheck = "1.0.3" quickcheck = "1.0.3"
[[bin]] [[bin]]

View file

@ -63,7 +63,7 @@ fn sleep(args: &[&str]) -> UResult<()> {
args.iter().try_fold( args.iter().try_fold(
Duration::new(0, 0), Duration::new(0, 0),
|result, arg| match uucore::parse_time::from_str(&arg[..]) { |result, arg| match uucore::parse_time::from_str(&arg[..]) {
Ok(m) => Ok(m + result), Ok(m) => Ok(m.saturating_add(result)),
Err(f) => Err(USimpleError::new(1, f)), Err(f) => Err(USimpleError::new(1, f)),
}, },
)?; )?;

View file

@ -1162,46 +1162,6 @@ where
Ok(()) Ok(())
} }
fn split_into_n_chunks_by_line_round_robin<R>(
settings: &Settings,
reader: &mut R,
num_chunks: u64,
) -> UResult<()>
where
R: BufRead,
{
// This object is responsible for creating the filename for each chunk.
let mut filename_iterator = FilenameIterator::new(
&settings.prefix,
&settings.additional_suffix,
settings.suffix_length,
settings.suffix_type,
);
// Create one writer for each chunk. This will create each
// of the underlying files (if not in `--filter` mode).
let mut writers = vec![];
for _ in 0..num_chunks {
let filename = filename_iterator
.next()
.ok_or_else(|| USimpleError::new(1, "output file suffixes exhausted"))?;
let writer = platform::instantiate_current_writer(&settings.filter, filename.as_str());
writers.push(writer);
}
let num_chunks: usize = num_chunks.try_into().unwrap();
for (i, line_result) in reader.lines().enumerate() {
let line = line_result.unwrap();
let maybe_writer = writers.get_mut(i % num_chunks);
let writer = maybe_writer.unwrap();
let bytes = line.as_bytes();
writer.write_all(bytes)?;
writer.write_all(b"\n")?;
}
Ok(())
}
fn split(settings: &Settings) -> UResult<()> { fn split(settings: &Settings) -> UResult<()> {
let mut reader = BufReader::new(if settings.input == "-" { let mut reader = BufReader::new(if settings.input == "-" {
Box::new(stdin()) as Box<dyn Read> Box::new(stdin()) as Box<dyn Read>
@ -1228,9 +1188,6 @@ fn split(settings: &Settings) -> UResult<()> {
let chunk_number = chunk_number - 1; let chunk_number = chunk_number - 1;
kth_chunk_by_line(settings, &mut reader, chunk_number, num_chunks) kth_chunk_by_line(settings, &mut reader, chunk_number, num_chunks)
} }
Strategy::Number(NumberType::RoundRobin(num_chunks)) => {
split_into_n_chunks_by_line_round_robin(settings, &mut reader, num_chunks)
}
Strategy::Number(_) => Err(USimpleError::new(1, "-n mode not yet fully implemented")), Strategy::Number(_) => Err(USimpleError::new(1, "-n mode not yet fully implemented")),
Strategy::Lines(chunk_size) => { Strategy::Lines(chunk_size) => {
let mut writer = LineChunkWriter::new(chunk_size, settings) let mut writer = LineChunkWriter::new(chunk_size, settings)

View file

@ -6,11 +6,39 @@
// file that was distributed with this source code. // file that was distributed with this source code.
// spell-checker:ignore (vars) NANOS numstr // spell-checker:ignore (vars) NANOS numstr
//! Parsing a duration from a string.
//!
//! Use the [`from_str`] function to parse a [`Duration`] from a string.
use std::time::Duration; use std::time::Duration;
use crate::display::Quotable; use crate::display::Quotable;
/// Parse a duration from a string.
///
/// The string may contain only a number, like "123" or "4.5", or it
/// may contain a number with a unit specifier, like "123s" meaning
/// one hundred twenty three seconds or "4.5d" meaning four and a half
/// days. If no unit is specified, the unit is assumed to be seconds.
///
/// This function uses [`Duration::saturating_mul`] to compute the
/// number of seconds, so it does not overflow. If overflow would have
/// occurred, [`Duration::MAX`] is returned instead.
///
/// # Errors
///
/// This function returns an error if the input string is empty, the
/// input is not a valid number, or the unit specifier is invalid or
/// unknown.
///
/// # Examples
///
/// ```rust
/// use std::time::Duration;
/// use uucore::parse_time::from_str;
/// assert_eq!(from_str("123"), Ok(Duration::from_secs(123)));
/// assert_eq!(from_str("2d"), Ok(Duration::from_secs(60 * 60 * 24 * 2)));
/// ```
pub fn from_str(string: &str) -> Result<Duration, String> { pub fn from_str(string: &str) -> Result<Duration, String> {
let len = string.len(); let len = string.len();
if len == 0 { if len == 0 {
@ -39,5 +67,42 @@ pub fn from_str(string: &str) -> Result<Duration, String> {
let whole_secs = num.trunc(); let whole_secs = num.trunc();
let nanos = (num.fract() * (NANOS_PER_SEC as f64)).trunc(); let nanos = (num.fract() * (NANOS_PER_SEC as f64)).trunc();
let duration = Duration::new(whole_secs as u64, nanos as u32); let duration = Duration::new(whole_secs as u64, nanos as u32);
Ok(duration * times) Ok(duration.saturating_mul(times))
}
#[cfg(test)]
mod tests {
use crate::parse_time::from_str;
use std::time::Duration;
#[test]
fn test_no_units() {
assert_eq!(from_str("123"), Ok(Duration::from_secs(123)));
}
#[test]
fn test_units() {
assert_eq!(from_str("2d"), Ok(Duration::from_secs(60 * 60 * 24 * 2)));
}
#[test]
fn test_saturating_mul() {
assert_eq!(from_str("9223372036854775808d"), Ok(Duration::MAX));
}
#[test]
fn test_error_empty() {
assert!(from_str("").is_err());
}
#[test]
fn test_error_invalid_unit() {
assert!(from_str("123X").is_err());
}
#[test]
fn test_error_invalid_magnitude() {
assert!(from_str("12abc3s").is_err());
}
} }

View file

@ -1,3 +1,4 @@
// spell-checker:ignore dont
use crate::common::util::*; use crate::common::util::*;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -115,3 +116,28 @@ fn test_sleep_sum_duration_many() {
fn test_sleep_wrong_time() { fn test_sleep_wrong_time() {
new_ucmd!().args(&["0.1s", "abc"]).fails(); new_ucmd!().args(&["0.1s", "abc"]).fails();
} }
// TODO These tests would obviously block for a very long time. We
// only want to verify that there is no error here, so we could just
// figure out a way to terminate the child process after a short
// period of time.
// #[test]
#[allow(dead_code)]
fn test_dont_overflow() {
new_ucmd!()
.arg("9223372036854775808d")
.succeeds()
.no_stderr()
.no_stdout();
}
// #[test]
#[allow(dead_code)]
fn test_sum_overflow() {
new_ucmd!()
.args(&["100000000000000d", "100000000000000d", "100000000000000d"])
.succeeds()
.no_stderr()
.no_stdout();
}

View file

@ -605,19 +605,3 @@ fn test_line_bytes() {
assert_eq!(at.read("xac"), "cccc\ndd\n"); assert_eq!(at.read("xac"), "cccc\ndd\n");
assert_eq!(at.read("xad"), "ee\n"); assert_eq!(at.read("xad"), "ee\n");
} }
#[test]
fn test_round_robin() {
let (at, mut ucmd) = at_and_ucmd!();
let file_read = |f| {
let mut s = String::new();
at.open(f).read_to_string(&mut s).unwrap();
s
};
ucmd.args(&["-n", "r/2", "fivelines.txt"]).succeeds();
assert_eq!(file_read("xaa"), "1\n3\n5\n");
assert_eq!(file_read("xab"), "2\n4\n");
}

View file

@ -1,3 +1,4 @@
// spell-checker:ignore dont
use crate::common::util::*; use crate::common::util::*;
// FIXME: this depends on the system having true and false in PATH // FIXME: this depends on the system having true and false in PATH
@ -64,3 +65,19 @@ fn test_preserve_status() {
.no_stderr() .no_stderr()
.no_stdout(); .no_stdout();
} }
#[test]
fn test_dont_overflow() {
new_ucmd!()
.args(&["9223372036854775808d", "sleep", "0"])
.succeeds()
.code_is(0)
.no_stderr()
.no_stdout();
new_ucmd!()
.args(&["-k", "9223372036854775808d", "10", "sleep", "0"])
.succeeds()
.code_is(0)
.no_stderr()
.no_stdout();
}