How to expand macros in `tests` folder - rust

How to use cargo expand to expand macros on test files under the tests folder in a crate?
crate
- src
- lib.rs
- tests
- my_test.rs
Cargo.toml
cargo expand --lib --tests do not recognize them(?). --bin doesn't recognize them neither.
In my_test.rs:
#[test]
fn test_it() {
assert!(true);
}
cargo expand --test test_it replies with error: no test target named test_it
Do I have to add something to Cargo.toml so they are included?

I was able to expand the macros in my tests using:
cargo expand --test tests, perhaps try cargo expand --test my_test
The project is similar to yours:
crate
- src
- lib.rs
- tests
- tests.rs
Cargo.toml
Cargo version: 1.69.0-nightly

Related

How do I refer to code in main.rs from tests? [duplicate]

When creating a project with a test like so:
cargo init --bin projectname
mkdir projectname/tests
echo "extern crate projectname;" > projectname/tests/test.rs
cd projectname/
cargo build
I get this error when testing:
cargo test
Compiling projectname v0.1.0 (file:///home/username/Lab/projectname)
error[E0463]: can't find crate for `projectname`
--> tests/test.rs:1:1
|
1 | extern crate projectname;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate
How can I access the functions in ´projectname/src/main.rs´ from projectname/tests/test.rs?
How can I access functions in ´projectname/src/main.rs´ from projectname/tests/test.rs?
You cannot.
A binary cannot be used a an external crate (the same way as you can't use a ELF binary as a shared object/library)
You just have to change your initialisation to
cargo init --lib projectname
or rename your main.rs to lib.rs
If you really want to stick with a main, you may look at Rust package with both a library and a binary?.

Ignore a test based on environment variable

There are a few tests that's running locally but not on Github workflows. I've spent quite some time but not able to debug and fix them. For now, I want to ignore them on ci/cd but run them locally. Since, Github provides a handy environment variable CI, which always remains true, can I use that to ignore test?
I don't want to wrap the whole function code under if(env::var("CI").is_ok()). Is there any better way?
One way you could go about doing this is by adding a feature to your Cargo.toml file, like ci or something similar. And then have your GitHub action compile with that feature enabled, and have a conditional compilation attribute on the tests in question.
To do this, you would first add a new feature to your Cargo.toml file:
[features]
ci = []
Then in your Rust code on a test you could write something like this:
#[test]
#[cfg_attr(feature = "ci", ignore)]
fn test_some_code() {
println!("This is a test");
}
Now locally if you run cargo test you will see the test run, but if you run cargo test --features ci you will see the test shows ignored, like below:
Now you just have to change your GitHub action to compile with this feature. If your action looks something like this:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
Then add --features ci to the end of both the cargo build and cargo test commands.
For more information on conditional compilation, this is in the Rust book: https://doc.rust-lang.org/reference/conditional-compilation.html
For more information on features in Cargo, this is in the Cargo book: https://doc.rust-lang.org/cargo/reference/features.html
To add to pokeyOne's answer, you can include a build script that checks for the CI environment variable and enables a ci feature when it's present. To do this, you would add this build.rs to your project root:
fn main() {
if std::env::var("CI").is_ok() {
println!("cargo:rustc-cfg=feature=\"ci\"");
}
}
Note that build scripts are only run on clean builds, so to test this locally you'll need to run cargo clean between runs (this won't be an issue on CI since you're doing a clean build there).
$ cargo test # run all tests
$ cargo clean # make sure build.rs is re-run...
$ CI=1 cargo test # skip CI tests
Features are a special type of --cfg flag, used for conditional compilation. Instead of using a feature, you could instead use a single identifier --cfg flag, like so:
// build.rs
fn main() {
if std::env::var("CI").is_ok() {
println!("cargo:rustc-cfg=ci");
}
}
// src/main.rs
#[cfg(test)]
mod tests {
// ..(snip)..
#[test]
#[cfg(not(ci))]
fn breaks_on_ci() { ... }
}
This build.rs is equivalent to running
$ RUSTFLAGS="--cfg ci" cargo test
You can read more about build scripts, --cfg flags, and conditional compilation in the Rust book here.

Run doc test in examples/ folder with cargo

I would like to execute Rust's documentation tests written in the examples/ folder.
// examples/example.rs
//! ```rust
//! panic!();
//! ```
fn main() {}
I would expect cargo test --doc to panic, but the above documentation test is never run. When I define the same documentation test in src/lib.rs cargo test --doc panics as expected.
How do I run a documentation test which is defined in the examples/ folder?
Environment:
$ cargo --version
cargo 1.51.0 (43b129a20 2021-03-16)
$ rustc --version
rustc 1.51.0 (2fd73fabe 2021-03-23)
Documentation tests in Rust are only run for modules that are used in your library when built for cfg(doc). Also doctest = true in Cargo.toml is currently (Rust 1.51) unsupported for example sections.
One reason for this is that documentation tests are run as if they were executables that link to your library. For documentation tests on examples it is not clear how they should run. An executable linking to an example executable doesn't make much sense.
But there is a hacky workaround:
You can use the attribute path="…" to include the examples as modules in your library (e.g. in src/lib.rs) and combine this with the conditional compilation attribute cfg and doc(hidden) to only do this when generating documentation while not producing output for these modules in the result:
#[cfg(doc)]
#[doc(hidden)]
#[path = "../examples"]
mod examples {
mod example;
}
This causes documentation tests in examples/example.rs to be included when running cargo test --doc. If you want to import something from example.rs in your doctest you need to access the namespace using your_library::examples::example::… and add pub to the module declarations.
$ cargo test --doc
…
test src/../examples/example.rs - examples::example (line 2) ... FAILED
failures:
---- src/../examples/example.rs - examples::example (line 2) stdout ----
Test executable failed (exit code 101).
stderr:
thread 'main' panicked at 'explicit panic', src/../examples/example.rs:3:1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Is there a simpler way to run clippy on my build script?

In a Cargo project, I can easily run clippy on my src code using this command:
rustup run nightly cargo clippy
However, if I'm using a build script, I'd like to run clippy on that as well. For instance, if my build.rs file looks like this:
fn main() {
let foo = "Hello, world!";
println!("{}", foo);
}
I'd like to see this when I run clippy:
warning: use of a blacklisted/placeholder name `foo`, #[warn(blacklisted_name)] on by default
--> build.rs:2:9
|
2 | let foo = "Hello, world!";
| ^^^
|
= help: for further information visit https://github.com/Manishearth/rust-clippy/wiki#blacklisted_name
The only way I can think of to run clippy on my build script is to copy it into a cargo new temporary project, run clippy, make my changes there, and copy back, but this is horribly inconvenient and quickly becomes infeasible when build dependencies and the like are added to the mix.
Is there a simpler way to analyze my build script with clippy?
Note: This solution no longer works. The clippy plugin feature has been removed (source).
There are two ways to use Clippy: the cargo clippy command and the clippy compiler plugin. cargo clippy detects the build script as a dependency of the main project, so it doesn't load the compiler plugin.
Therefore, the other option is to use the compiler plugin directly. The instructions for doing this are in clippy's README. We need to make a few adaptations for using it on the build script, though.
First, we need to add clippy as a build dependency:
[build-dependencies]
clippy = { version = "*", optional = true }
[features]
default = []
Adding it to [dependencies] instead will not work (the result is error[E0463]: can't find crate for `clippy`), as Cargo will not pass the path to dependencies to the compiler when building the build script.
Then, we need to add this at the top of build.rs:
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
Finally, we need to build with the clippy feature enabled:
$ cargo build --features clippy
If you want to run clippy on both the build script and on the main project when you use the command above, add the same clippy dependency to [dependencies], then add the cfg_attr attributes to the crate root(s) (lib.rs, main.rs, etc.).

How do I use conditional compilation with `cfg` and Cargo?

I want to conditionally compile my source code using cfg with Cargo,
after Googling for a while,
it seems that the solution is to use cargo --features.
http://doc.crates.io/manifest.html
I tried adding a few
#[cfg(feature = "foo")]
in the source code and
cargo build --features foo
, but it says
Package `xxx v0.0.1 (file:///C:/yyy/xxx)` does not have these features: `foo`
How can I let cargo identify the features? Do I have to add something in Cargo.toml?
Here's the version of rustc and cargo I am using:
C:\>rustc --version
rustc 0.13.0-nightly (42deaa5e4 2014-12-16 17:51:23 +0000)
C:\>cargo --version
cargo 0.0.1-pre-nightly (5af754d 2014-12-18 01:50:48 +0000)
You have to introduce the existing features in your Cargo.toml.
I was able to conditionally compile by doing the following:
In Cargo.toml, create a features section and introduce a certain feature name:
[features]
customfeature = [] # feature has no explicit dependencies
If you want your feature to have specific dependencies check the examples in the documentation.
In your code, use #[cfg(feature="customfeature")]
Run cargo build --features customfeature
Since your steps 2 & 3 seem to be fine, there must probably be a problem with your Cargo.toml.
As stated in other answers, you can use features for this. I would like to add that features do not only allow you to conditionally compile parts of your code but also to conditionally include dependencies that may be part of that code. Consider the following snippets:
You can activate the conditional code using a feature flag as already described in other anwsers:
cargo build --features customfeature
You need to mark your conditional code to exist only when your customfeature is enabled:
#[cfg(feature = "customfeature")]
fn my_func() {
my_optional_dependency::do_something();
}
// This includes dependencies only when customfeature is enabled
#[cfg(feature = "customfeature")]
extern crate my_optional_dependency;
....
#[cfg(feature = "customfeature")]
use my_optional_dependency::*;
....
Your Cargo.toml needs to have the following sections:
[dependencies.my_optional_dependency]
version = "1.2.3"
optional = true
[features]
customfeature = ["my_optional_dependency"]
This allows you to activate certain parts of your code along with their dependencies only if a feature is enabled.
Alternatively, you could create a cargo configuration file in your project, by creating a .cargo subdir in your project main folder, adding in it a config.toml file,
then inserting this section in .cargo/config.toml:
[build]
rustflags = "--cfg my_cfg_flag"
This will make cargo call rustc with flags --cfg my_cfg_flag
See here for details:
https://doc.rust-lang.org/cargo/reference/config.html

Resources