How can I get Rust code coverage to ignore unreachable lines? - rust

Using the following as an example:
fn get_num(input: i32) -> i32 {
input*2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_it() {
for i in 1i32..72 {
match get_num(i) {
x if x%2 == 0 => { println!("even"); }
_ => {println!("odd");}
}
}
}
}
and implementing the code coverage like so:
RUSTFLAGS="-C instrument-coverage" cargo test;
llvm-profdata merge -sparse default_*.profraw -o default.profdata;
executable=$(RUSTFLAGS="-C instrument-coverage" cargo test --no-run --message-format=json | \
jq -r "select(.profile.test == true) | .filenames[]" | \
\grep -v dSY);
llvm-cov show --instr-profile=default.profdata --object "$executable";
I would like the println!("odd"); not to be marked as "not covered".
In javascript, I would use istanbul-ignore-next to ignore a line (mark them as "exercised"). In C#, one can use ExcludeFromCodeCoverage. Using, lcov (for C or C++), you can use LCOV_EXCL_LINE. In python, use # pragma: no cover. In ruby, use # :nocov:. Is there a way to do this in Rust?

Don't write unreachable code. If your test panics, it failed. If it doesn't panic, it succeeded. You don't need to examine output from the test to see if it succeeded or not.
As such, don't use match; use assert_eq!.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_it() {
for i in 1i32..72 {
assert_eq!(get_num(i) % 2, 0)
}
}
}
If the assertion fails, you'll get information in the test results about why it failed.

#[no_coverage] can be applied to functions, but is not yet stable: https://github.com/rust-lang/rust/issues/84605. From that same ticket:
Ignoring a statement or block is not currently supported.

That's not generally possible because it would require for the compiler to do some super smart reasoning. In your particular case it's "obvious" that the match arm for odd numbers can't ever be reached, but what you're asking of the compiler (and the coverage tool) is to be a general theorem prover.
Basically the tool would need to come with some logic that can prove statements about the code. This is actually an unsolvable problem (undecidable, to be precise). You can make tools that can make pretty good attempts (the aforementioned theorem provers) but the complexity of such a tool would far eclipse what you'd want in a simple code coverage tool.

Related

How to dynamically signal to Rust compiler that a given variable is non-zero?

I'd like to try to eliminate bounds checking on code generated by Rust. I have variables that are rarely zero and my code paths ensure they do not run into trouble. But because they can be, I cannot use NonZeroU64. When I am sure they are non-zero, how can I signal this to the compiler?
For example, if I have the following function, I know it will be non-zero. Can I tell the compiler this or do I have to have the unnecessary check?
pub fn f(n:u64) -> u32 {
n.trailing_zeros()
}
I can wrap the number in NonZeroU64 when I am sure, but then I've already incurred the check, which defeats the purpose ...
Redundant checks within a single function body can usually be optimized out. So you just need convert the number to NonZeroU64 before calling trailing_zeros(), and rely on the compiler to optimize the bound checks.
use std::num::NonZeroU64;
pub fn g(n: NonZeroU64) -> u32 {
n.trailing_zeros()
}
pub fn other_fun(n: u64) -> u32 {
if n != 0 {
println!("Do something with non-zero!");
let n = NonZeroU64::new(n).unwrap();
g(n)
} else {
42
}
}
In the above code, the if n != 0 makes sure n cannot be zero within the block, and compiler is smart enough to remove the unwrap call, making NonZeroU64::new(n).unwrap() an zero-cost operation. You can check the asm to verify that.
core::intrinsics::assume
Informs the optimizer that a condition is always true. If the
condition is false, the behavior is undefined.
No code is generated for this intrinsic, but the optimizer will try to
preserve it (and its condition) between passes, which may interfere
with optimization of surrounding code and reduce performance. It
should not be used if the invariant can be discovered by the optimizer
on its own, or if it does not enable any significant optimizations.
This intrinsic does not have a stable counterpart.

Is it ok to use asserts in production code (not in test suite) to ensure expected behaviour

I notice when reading the docs that they often use assert when explaining expected behaviour of simple code blocks.
In production level code, would it be considered an anti-pattern to do the same? While reading rust by example I only saw assert's being used in tests, but in the instances where you do expect vars or values to be a specific thing, is assert the correct approach?
The example I came across in my own code is a scenario similar to the following...
fn foo(values: Vec<String>, my_num: usize) {
assert_eq!(values.len(), my_num);
// run this code after
}
I expect the vector passed to have a length equal to another value in the function, and the code wouldn't work if that wasn't the case. Would asserting these two values as being equal be the correct practice?
What are some other best practices or ways of handling other error behaviour?
Assertions are OK in unsafe code. For example, if you need to ensure that a pointer is non-null. Otherwise, it’s better to use Results, Options and usual conditions.
struct FooError;
fn foo(values: Vec<String>, my_num: usize) -> Result<(), FooError> {
if values.len() != my_num {
return Err(FooError);
}
…
Ok(())
}

Is there any difference between these two ways to get lowercase `Vec<char>`

Is there any difference between these two ways to get lowercase Vec. This version iterates over chars, converts them and collects the results:
fn lower(s: &str) -> Vec<char> {
s.chars().flat_map(|c| c.to_lowercase()).collect()
}
and this version first converts to a String and then collects the chars of that:
fn lower_via_string(s: &str) -> Vec<char> {
s.to_lowercase().chars().collect()
}
A short look at the code for str::to_lowercase immediately revealed a counterexample: It appears that Σ at the end of words receives special treatment from str::to_lowercase, which chars()-then-char::to_lowercase can't give, so the results differ on "xΣ ".
Playground
Before looking at the code of std::to_lowercase, I thought: Well, it should be really easy to find a counterexample with a fuzzer. I messed up the setup at first and it didn't find anything, but now was able to get it right, so I'll add it for completeness:
cargo new theyrenotequal
cd theyrenotequal
cargo fuzz init
cat >fuzz/fuzz_targets/fuzz_target_1.rs
#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &str| {
if data.to_lowercase().chars().collect::<Vec<_>>()
!= data
.chars()
.flat_map(|c| c.to_lowercase())
.collect::<Vec<_>>()
{
panic!("Fuxxed: {}", data)
}
});
cargo fuzz run fuzz_target_1 -Zbuild-std
That spat out "AΣ#ӮѮ" after 8 million iterations.
Completing the answer of #Caesar, in case the behavioral difference doesn't matter, there is still a performance difference.
String::to_lowercase() allcates a new String and fills it with the characters. char::to_lowercase() only does that on-the-fly. So the former is expected to be much slower. I don't think there can't be a version of String::to_lowercase() that returns an iterator and avoids the penalty of the allocation, just that it hasn't done yet.

Why doesn't println! work in Rust unit tests?

I've implemented the following method and unit test:
use std::fs::File;
use std::path::Path;
use std::io::prelude::*;
fn read_file(path: &Path) {
let mut file = File::open(path).unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
println!("{}", contents);
}
#[test]
fn test_read_file() {
let path = &Path::new("/etc/hosts");
println!("{:?}", path);
read_file(path);
}
I run the unit test this way:
rustc --test app.rs; ./app
I could also run this with
cargo test
I get a message back saying the test passed but the println! is never displayed on screen. Why not?
This happens because Rust test programs hide the stdout of successful tests in order for the test output to be tidy. You can disable this behavior by passing the --nocapture option to the test binary or to cargo test (but, in this case after -- – see below):
#[test]
fn test() {
println!("Hidden output")
}
Invoking tests:
% rustc --test main.rs; ./main
running 1 test
test test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
% ./main --nocapture
running 1 test
Hidden output
test test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
% cargo test -- --nocapture
running 1 test
Hidden output
test test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
If tests fail, however, their stdout will be printed regardless if this option is present or not.
TL;DR
$ cargo test -- --nocapture
With the following code:
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PieceShape {
King, Queen, Rook, Bishop, Knight, Pawn
}
fn main() {
println!("Hello, world!");
}
#[test]
fn demo_debug_format() {
let q = PieceShape::Queen;
let p = PieceShape::Pawn;
let k = PieceShape::King;
println!("q={:?} p={:?} k={:?}", q, p, k);
}
Then run the following:
$ cargo test -- --nocapture
And you should see
Running target/debug/chess-5d475d8baa0176e4
running 1 test
q=Queen p=Pawn k=King
test demo_debug_format ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
As mentioned by L. F., --show-output is the way to go.
$ cargo test -- --show-output
Other display flags are mentioned in the documentation of cargo test in display-options.
To include print outs with println!() and keep colors for the test results, use the color and nocapture flags in cargo test.
$ cargo test -- --color always --nocapture
(cargo version: 0.13.0 nightly)
While testing, standard output is not displayed. Don't use text messages for testing but assert!, assert_eq!, and fail! instead. Rust's unit test system can understand these but not text messages.
The test you have written will pass even if something goes wrong. Let's see why:
read_to_end's signature is
fn read_to_end(&mut self) -> IoResult<Vec<u8>>
It returns an IoResult to indicate success or error. This is just a type def for a Result whose error value is an IoError. It's up to you to decide how an error should be handled. In this case, we want the task to fail, which is done by calling unwrap on the Result.
This will work:
let contents = File::open(&Path::new("message.txt"))
.read_to_end()
.unwrap();
unwrap should not be overused though.
Note that the modern solution (cargo test -- --show-output) doesn't work in doctests defined in a Markdown code-fence in the docstring of your functions. Only println! (etc.) statements done in a concrete #[test] block will be respected.
It's likely that the test output is being captured by the testing framework and not being printed to the standard output. When running tests with cargo test, the output of each test is captured and displayed only if the test fails. If you want to see the output of a test, you can use the --nocapture flag when running the test with cargo test. Like so:
cargo test -- --nocapture
Or you can use the println! macro inside a test function to print output to the standard output. Like so:
#[test]
fn test_read_file() {
let path = &Path::new("/etc/hosts");
println!("{:?}", path);
read_file(path);
println!("The test passed!");
}
Why? I don't know, but there is a small hack eprintln!("will print in {}", "tests")
In case you want to run the test displaying the printed output everytime the file changes:
sudo cargo watch -x "test -- --nocapture"
sudo might be optional depending on your set-up.

Why does rust parser need the fn keyword?

I've been reading the blogs about rust and this closure for example made me wonder:
fn each<E>(t: &Tree<E>, f: &fn(&E) -> bool) {
if !f(&t.elem) {
return;
}
for t.children.each |child| { each(child, f); }
}
why couldn't it be:
each<E>(t: &Tree<E>, f: &(&E) -> bool) {
if !f(&t.elem) {
return;
}
for t.children.each |child| { each(child, f); }
}
Maybe i'm missing something on the class system that would prevent this.
It makes parsing more complicated for compilers, syntax-highlighters, shell scripts and humans (i.e. everyone).
For example, with fn, foo takes a function that has two int arguments and returns nothing, and bar takes a pointer to a tuple of 2 ints
fn foo(f: &fn(int, int)) {}
fn bar(t: &(int, int)) {}
Without fn, the arguments for both become &(int, int) and the compiler couldn't distinguish them. Sure one could come up with other rules so they are written differently, but these almost certainly have no advantages over just using fn.
Some of the fn's may seem extraneous, but this has a side benefit that rust code is extremely easy to navigate with 'grep'. To find the definition of function 'foo', you simply grep "fn\sfoo". To see the major definitions in a source file, just grep for "(fn|struct|trait|impl|enum|type)".
This is extremely helpful at this early stage when rust lacks IDE's, and probably does simplify the grammar in other ways.
Making the grammar less ambiguous than C++ is a major goal , it makes generic programming easier (you dont have to bring in so much definition into a compile unit, in a specific order, to correctly parse it), and should make future tooling easier. A feature to auto-insert 'fn's would be pretty straightforward compared to many of the issues current C++ IDE's have to deal with.

Resources