How to opt out of running a doc test? - rust

I'm writing a Rust library and I want to provide examples in my documentation that
compile as part of running cargo test
do not run.
Is this possible?
I'm writing a database client library, and the examples make use of a hypothetical, non-existing database server. As such, the examples always fail when run, but it's important that the examples be valid syntactically. Hence my requirements above.
If there's no way to do what I want, then how does one opt out of having cargo test run a specific doc test? I.e., have cargo run compile-and-run some doc tests but completely ignore some others?

This is documented in The rustdoc book, specifically the chapter about attributes.
Your opening codeblock delimiter should look like:
/// ```no_run
From the book:
/// ```no_run
/// loop {
/// println!("Hello, world");
/// }
/// ```
The no_run attribute will compile your code, but not run it. This is
important for examples such as "Here's how to retrieve a web page,"
which you would want to ensure compiles, but might be run in a test
environment that has no network access.
To omit build completely use ignore instead of no_run.

Put this in Cargo.toml:
[lib]
doctest = false
Found it here: https://doc.rust-lang.org/cargo/commands/cargo-test.html

Related

How to turn off cargo doc test and compile for a specific module in Rust

I have some generated .rs code (from grpc proto files) and they are checked in with my normal Rust code under src but in some sub modules. The issue is that when doing cargo test the doc test will run and some of the generated .rs have comments with indentations (code blocks) and cargo doc test will try to compile them and fail.
For example cargo test will try to compile (and perhaps run these lines) show here.
Is there a way to exclude or ignore those generated .rs for doc test (without manually changing them)?
My solution
I've resolved this by injecting code that separates the offending comment and the structure underneath:
fn main() {
tonic_build::configure()
.type_attribute(
".google.api.HttpRule",
"#[cfg(not(doctest))]\n\
#[allow(dead_code)]\n\
pub struct HttpRuleComment{}\n\
/// HACK: see docs in [`HttpRuleComment`] ignored in doctest pass",
)
.compile(
&["proto/api/google/api/http.proto"],
&["proto/"],
)
.unwrap();
}
Explanation:
tonic_build allows adding attributes to specific protobuf paths, but it does not discern between attributes or any other strings.
#[cfg(not(doctest))] excludes the comment's offending code from doc tests, because it excludes the structure it's added to, this trick is taken from discussion on Rust user forum.
#[allow(dead_code)] silences warnings about injected structure not being used (as it shouldn't be used).
pub struct HttpRuleComment{} creates a public structure that can be referenced in the docstring for the original struct.
/// ...[HttpRuleComment]... is a docstring referencing injected struct, so that the original documentation can still be accessed.
Notes:
I'm not sure if your case is the same, but I found myself on this SO page a lot while searching for an answer and decided to share the results.
In my case, the only offending comment was in HttpRule, you may need to write a macro or do lots of copy-paste if you happen to have multiple offending comments.

Conditional compilation for Rust build.rs script?

The Rust language supports conditional compilation using attributes like #[cfg(test)].
Rust also supports build scripts using a build.rs file to run code as part of the build process to prepare for compilation.
I would like to use conditional compilation in Rust code to conditionally compile depending on whether we're compiling for a build script, similar to how that is possible for test builds.
Imagine the following:
#[cfg(build)]
fn main() {
// Part of build script
}
#[cfg(not(build))]
fn main() {
// Not part of build script, probably regular build
}
This does not work, because build is not a valid identifier here.
Is it possible to do this using a different attribute, or could some other trick be used to achieve something similar?
For some context on this issue:
My goal is to generate shell completion scripts through clap at compile time. I've quite a comprehensive App definition across multiple files in the application. I'd like to use this in build.rs by including these parts using the include!(...) macro (as suggested by clap), so I don't have to define App a second time. This pulls some dependencies with it, which I'd like to exclude when used by the build.rs file as they aren't needed in that case. This is what I'm trying to make available in my build.rs script.
You can just put the build code in build.rs (or presumably have build.rs declare a mod xyz to pull in another file).
I wonder if the question you are trying to ask is whether you can reference the same code from build.rs and main.rs, and if so can that code tell if it's being called by one or the other. It seems you could switch on an environment variable set when using build.rs (using something like option_env, but possibly a nicer way might be to enable a feature in the main code from within build.rs.
(Have a read of the documentation for build scripts if you haven't already.)
This is what works for me:
fn main() {
if std::env::var("PROFILE").unwrap() == "debug" {
// Here I do something that is needed for tests
// only, not for 'release'
}
}

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.

Is there a way to enable a Cargo feature only when rustdoc verifies examples?

I have the following piece in the crate documentation:
//! # Examples
//! ```rust,no_run
//! extern crate stm32f103xx;
//! // ...
//! ```
The problem is that the dependency on stm32f103xx crate is optional. Everything works fine if I enable feature stm32f103xx by default, but I don't want to make it default. Is there any way to enable that feature only when rustdoc verifies examples?
No. Features are chosen by the end user of the crate, and you aren't the only person who chooses to run the tests. If you could do what you are asking, you'd actually force anyone who wanted to run the tests to download and compile the "optional" dependency, making it not very optional.
What you can do instead is only include that piece of the documentation when the feature is enabled. It's not obvious, but documentation comments are transformed into the attribute syntax (#[doc = "..."]). Combined with cfg_attr, you can conditionally include documentation, thus conditionally compile and run an example:
#![cfg_attr(feature = "alpha", doc = "
# Examples
```rust
fn alpha() {}
```
")]
Likewise, you can have the opposite case for a bit of documentation that says "check out this awesome feature!".
See also:
doc attribute documentation
How would one achieve conditional compilation with Rust projects that have doctests?
Generating documentation in macros
In order to always have a dependency when compiling any parts of the project (including tests such as that one), Development Dependencies are a good fit.
[dev-dependencies]
stm32f103xx = "0.7.5"
As you mention that the crate is also optional as a main dependency, you can keep it as so in your manifest.
[dependencies]
stm32f103xx = { version = "0.7.5", optional = true }

How do I generate cargo docs for all platforms?

The cargo command line tool allows me to generate documentation for my crate, but I have structures like:
#[cfg(target-platform("windows")]
mod winstuff {
/// Explanation of Windows-specific tasks
}
#[cfg(target-platfrom("linux")]
mod linstuff {
/// Explanation of Linux-specific tasks
}
When I run cargo docs --no-deps, only the platform-specific module I'm generating documentation for gets generated. How can I generate documentation for all platforms?
This is a known bug, there is no current fix.

Resources