Skip to content

Commit 8495ab0

Browse files
committed
assertions WRT allocation and panics
Signed-off-by: Pierre Fenoll <pierrefenoll@gmail.com>
1 parent be3ac06 commit 8495ab0

File tree

5 files changed

+210
-95
lines changed

5 files changed

+210
-95
lines changed

Cargo.lock

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,22 @@ keywords = ["xargs", "buildkit", "docker", "visualization"]
88
license = "MIT"
99
repository = "https://github.com/fenollp/buildxargs"
1010

11-
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12-
1311
[dependencies]
12+
assert_no_alloc = "1"
1413
clap = { version = "4", features = ["derive", "color"] }
14+
no-panic = "0.1"
1515
shlex = "1"
1616
tempfile = "3"
1717

1818
[dev-dependencies]
1919
assert_cmd = "2"
2020
assert_fs = "1"
2121
predicates = "3"
22+
23+
[profile.dev]
24+
lto = "thin" # no_panic
25+
opt-level = 1 # no_panic
26+
27+
[profile.test]
28+
lto = "thin" # no_panic
29+
opt-level = 1 # no_panic

src/contracts.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use assert_no_alloc::assert_no_alloc;
2+
use no_panic::no_panic;
3+
4+
// no_panic
5+
6+
// Our business logic says there's no way this panics,
7+
// so let's ensure/enforce/document it!
8+
9+
// cf https://github.com/dtolnay/no-panic/issues/8 for inlining
10+
// Also, push for proc-macro on expressions!
11+
12+
#[inline(never)]
13+
#[no_panic]
14+
fn f(x: u32) -> Option<u32> {
15+
Some(x)
16+
}
17+
18+
#[inline(never)]
19+
#[no_panic]
20+
#[test]
21+
fn do_not_panic_with_unwrap() {
22+
assert_eq!(20, f(20).unwrap());
23+
}
24+
25+
#[inline(never)]
26+
#[no_panic]
27+
#[test]
28+
fn do_not_panic_with_expect() {
29+
assert_eq!(21, f(21).expect("unreachable"));
30+
}
31+
32+
#[inline(never)]
33+
#[no_panic]
34+
#[test]
35+
fn do_not_panic_with_unwrap_unchecked() {
36+
assert_eq!(1, unsafe { f(1).unwrap_unchecked() });
37+
}
38+
39+
// no_alloc
40+
41+
//https://github.com/Windfisch/rust-assert-no-alloc/issues/11
42+
43+
#[test]
44+
#[should_panic]
45+
fn does_actually_alloc() {
46+
assert_no_alloc(|| {
47+
let xs: Vec<i64> = Vec::with_capacity(42);
48+
assert_eq!(42, xs.capacity());
49+
});
50+
}
51+
52+
#[test]
53+
fn does_not_alloc() {
54+
assert_no_alloc(|| {
55+
let xs: Vec<i64> = Vec::with_capacity(0);
56+
assert_eq!(0, xs.capacity());
57+
});
58+
}

src/lib.rs

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
use std::collections::{hash_map::Entry::Vacant, HashMap};
1+
use std::collections::{hash_map::Entry, HashMap};
22

3+
use assert_no_alloc::assert_no_alloc;
4+
5+
// TODO: #[no_panic]
36
// try_quick executes f on values slice and subslices when an error is returned
47
// until maxdepth amounts of slicing happened.
58
// It returns the indices of values that kept returning errors.
@@ -21,38 +24,50 @@ where
2124
let mut ixs: Vec<usize> = (0..values.len()).collect();
2225
let mut passed: Vec<usize> = Vec::with_capacity(values.len());
2326
let mut failed: HashMap<usize, String> = HashMap::with_capacity(values.len());
24-
for _ in 1..=maxdepth {
25-
let ixsn = ixs.len();
26-
if ixsn == 0 {
27-
return Ok(HashMap::new());
28-
}
29-
if ixsn / 2 == 0 {
30-
break;
31-
}
32-
for indices in ixs.chunks(ixsn / 2) {
33-
xs.clear();
34-
for i in indices {
35-
xs.push(values[*i].clone());
27+
assert_no_alloc(|| {
28+
for _ in 1..=maxdepth {
29+
let ixsn = ixs.len();
30+
if ixsn == 0 {
31+
return Ok(HashMap::new());
3632
}
33+
if ixsn / 2 == 0 {
34+
break;
35+
}
36+
for indices in ixs.chunks(ixsn / 2) {
37+
xs.clear();
38+
for i in indices {
39+
xs.push(values[*i].clone());
40+
}
3741

38-
match f(&xs) {
39-
Ok(()) => passed.extend(indices.iter()),
40-
Err(e) => {
41-
for &ix in indices {
42-
if let Vacant(entry) = failed.entry(ix) {
43-
let _ = entry.insert(format!("{e}")); // TODO: e.clone()
42+
match f(&xs) {
43+
Ok(()) => passed.extend(indices.iter()),
44+
Err(e) => {
45+
for &ix in indices {
46+
if let Entry::Vacant(entry) = failed.entry(ix) {
47+
let _ = entry.insert(format!("{e}")); // TODO: e.clone()
48+
}
4449
}
4550
}
4651
}
4752
}
48-
}
49-
for &index in &passed {
50-
let _ = failed.remove(&index);
51-
if let Some(ix) = ixs.iter().position(|&ix| ix == index) {
52-
let _ = ixs.swap_remove(ix);
53+
for &index in &passed {
54+
let _ = failed.remove(&index);
55+
if let Some(ix) = ixs.iter().position(|&ix| ix == index) {
56+
let _ = ixs.swap_remove(ix);
57+
}
5358
}
5459
}
55-
}
5660

57-
Ok(failed)
61+
Ok(failed)
62+
})
63+
}
64+
65+
#[test]
66+
fn single_bad_job() {
67+
let xs = vec![0];
68+
let err = format!("{}", "NaN".parse::<u32>().unwrap_err());
69+
for d in 0..=9 {
70+
let ixs_failed = try_quick(&xs, d, |_subxs| Err(err.clone()));
71+
assert_eq!(ixs_failed, Err(err.clone()));
72+
}
5873
}

0 commit comments

Comments
 (0)