Common function that is used both in `tests` and `src` not found - rust

I have a number of tests defined inside some of my modules with:
#[cfg(test)]
mod tests {
...
and a number of tests found inside my /tests directory. All of these happen to need a common function called get_local() that I have put inside a utils module
+src
|-lib.rs
|-utils.rs
|-...
+tests
|_common.rs
Now since this function is only used in case I run tests I thought I should add a #[cfg(test)] above it.
#[cfg(test)]
pub fn get_local() -> SomeResult { ... }
and then I make sure it is inside lib.rs:
pub mod utils;
When I try using the function in tests/common.rs though:
use my_crate::utils::get_local;
and run the tests with the test command I get the error: no get_local in utils. This error only goes away if I remove #[cfg(test)] from the declaration of get_local. Why does this happen?

Why does this happen?
#[cfg(test)] is only active when the crate is being compiled into a test binary (instead of a library or normal binary) — the same condition under which #[test] functions are compiled to be run rather than ignored. But for a tests/ test, the crate being compiled in test mode is not your library — it's a separate binary crate for the test, which Cargo sets up to depend on your library compiled normally.
Thus, tests/ tests may only test the "public API" of your library — they see it no differently than a dependent crate outside your library.
As a workaround, you can use #[doc(hidden)] instead of #cfg(test)] so that the function is always available but undocumented, or you can put it in a module which is compiled into the test as well as the main library (but it's tricky to write code that works in both cases).

As for the "why does this happen?", Kevin Reid already answered: code in tests/ are "Integration Tests" and, therefore, uses the regular version of your lib (not the test-compiled one). See Integration Testing.
You may use the approach bellow to have a common test module for a single crate. Note that, for Integration Tests, each file inside tests/ is compiled as an individual crate. You may still create modules there.
With that said, my approach to having common test code is to put them inside a "test_commons" module and use:
lib.rs:
#[cfg(test)]
mod test_commons;
The contents of test_commons.rs has nothing special -- functions are defined without any #[cfg(test)] annotation.
Then, to use it:
#[cfg(test)]
mod tests {
use super::*;
use crate::test_commons::{self,ContainerKind,Blocking};
...
For Integration Tests inside tests/ to share that same code, simply use:
#[path = "../../src/test_commons.rs"] mod test_commons;

Related

How do I keep a mod private and use it in another module within the same project in Rust?

So I want to have some modules defined in lib.rs but not make them public but only available for use within the project.
in my lib.rs, if I have the definition as this:
pub mod args;
in my main.rs, I can use the args modules this way:
use my_lib::args::Cli;
where my_lib is defined in Cargo.tml as
[lib]
name = "my_lib"
path = "src/lib.rs"
but I don't want pub mod args;. I tried changing to pub(crate) mod args; but this leads to compilation error that the args module cannot be found.
How do I make a module like args defined in lib.rs available without have to give it the most permissive visibility?
Since Rust separates the library lib.rs and binary main.rs into separate crates there is no simple way to include things that are in lib.rs and not pub from main.rs.
I suggest you follow the way of big crates (serde comes to mind) and add a pub mod __private; which conveys the meaning. You can additionaly annotate it with #[doc(hidden)] to hide it from the documentation making it even more obvious.

Why duplicate function names in mod.rs as another .rs file?

For example, have module within /xyz/ sub-directory. Inside the directory are two files, mod.rs and network.rs say.
Why do mod.rs and network.rs have the same function names, but different code within the functions? Is there any reason for this? I thought mod.rs was just basically a defintions file to declare a module, and specify which other .rs files within the sub-directory should be treated as their own creates / modules.
Any help?
It sounds like you are referring to a design decision made by a specific crate. You are correct in assuming there is no special consideration given by the compiler to function/type/ident names in separate files/modules.
That being said it seems likely that what you are referring to might be using conditional compilation. Conditional compilation lets the compiler decide if a given piece of code is compiled or not. You will usually see this used to handle which implementation of a function is used when compiling code on different operating systems since it is often it too inefficient or simply impossible to check at runtime. Some library authors might also decide to add an implementation that it can fallback to instead of throwing a hard error.
Here is a quick example of why xyz might want to have 3 different implementations of foobar.
// xyz/mod.rs
mod windows;
mod unix;
// If this crate is compiled on windows re-export the contents of windows.rs
#[cfg(windows)]
pub use windows::*;
// If this crate is compiled on unix/linux re-export the contents of unix.rs
#[cfg(unix)]
pub use unix::*;
// If not on either windows or unix provide a default implementation to use instead
#[cfg(not(any(windows, unix)))]
pub fn foobar() -> i32 {
panic!("This function is unsupported on the current os")
}

Where can I find rust test binary for my project?

I'm having problem on a specific function of a test of a specific file:
mod test {
//...
#[test]
#[cfg(feature = "proto-igmp")]
fn test_handle_igmp() {
I've found here https://github.com/rust-lang/cargo/issues/1407 that I can test specific tests by passing its name as an argument to the test binary. But where's such binary? And can I make println work inside tests?
I want to run test_handle_igmp to print some things and see why the error is happening.
You can find it in target/$MODE/$NAME-$hash, e.g. target/debug/example-3beac917983bc7e3.exe. Note that there might be multiple ones, some for doc tests, some for #[test] functions.
That being said if you just want to run test_handle_igmp, you can just use
cargo test test_handle_igmp -- --nocapture
to run your test and see the println outputs. See cargo test --help or the online documentation.

What is a Rust systest?

I sometimes see that a Rust crate has a folder called systest. I guess that the name stands for "system test", but I can't find any documentation of this.
My questions are:
What is the purpose of a systest? Just testing that a crate compiles fine, or also testing that some code in another crate runs fine?
What are the rules to follow when writing a systest? Is it just a crate in a folder called systest?
Why does the lib.rs in systest/src seem to always include a file all.rs generated from build.rs?
systest is not a standard name used in Rust or Cargo. However, it is the name suggested by the documentation for ctest, which performs automated testing for FFI bindings. build.rs uses ctest to generate the all.rs file which contains the tests, and this is included from the main file.
According to the documentation, the tests generate include ensuring that all function signatures, constant values, struct layout/alignment, type size/alignment, etc., all match their C equivalent.

"cargo check" gives dead code warning for function used only by test [duplicate]

I'm writing a program in Rust and I have some tests for it. I wrote a helper function for these tests, but whenever I build using cargo build it warns me that the function is never used:
warning: function is never used: ... #[warn(dead_code)] on by default
How I can mark this function as used so as not to get the warnings?
Specific question
How I can mark this function as used so as not to get the warnings?
The Rust compiler runs many lints to warn you about possible issues in your code and the dead_code lint is one of them. It can be very useful in pointing out mistakes when code is complete, but may also be a nuisance at earlier stages. Often, this can be solved by either deleting unused code, or by marking a public method. However, all lints can be turned off by allowing them, and your error message (#[warn(dead_code)] on by default) contains the name of the lint you could disable.
#[allow(dead_code)]
fn my_unused_function() {}
Alternative for testing
I wrote a helper function for these tests, but whenever I build using cargo build it warns me that the function is never used.
This happens to be a special case, which is that code that is only used for testing isn't needed in the real executable and should probably not be included.
In order to optionally disable compilation of test code, you can mark it accordingly using the cfg attribute with the test profile.
#[cfg(test)]
fn my_test_specific_function() {}
When marked in this way, the compiler knows to ignore the method during compilation. This is similar to commonly used ifdef usage in other languages like C or C++, where you are telling a preprocessor to ignore the enclosed code unless TESTING is defined.
#ifdef TESTING
...
#endif
For people getting this warning while making a rust library, you may get this if you don't have your modules set to pub in your lib.rs.
pub mod foo;
If something is only used in tests, it should be omitted altogether. This can be done with the #[cfg(test)] attribute.
dead_code is a lint, which means you can allow it on the thing that's causing it to trigger.
#[allow(dead_code)]
fn dummy() {}
fn main() {}
There is another situation where this can occur. If you have several helper functions in a module, e.g. in tests/utils/mod.rs and then several integration tests (tests/a.rs, tests/b.rs) each of which does
mod utils;
use utils::...;
then you will get dead code warnings if you do not use all of the code from all of the tests. For example if test a.rs only uses utils::foo and b.rs only uses utils::bar then you will get dead code warnings for both.
That is because each test is compiled as an independent crate. Here is the bug report for it. It looks difficult to solve so I wouldn't hold my breath.
You can disable specific lints for a whole project in Rust by going into your main.rs file and adding the following at the very top of the file:
#![allow(
dead_code,
unused_imports
)]
You max prefix the unused function name with an underscore:
fn _dummy() {}
fn main() {}
See: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#dead-code
For some reason I found that setting the main function to public:
pub fn main()
and then copying my main.rs file to lib.rs
cp src/main.rs src/lib.rs
then recompiling fixed this.
No need to use a macro for it, although the macro should work for non main functions.

Resources