Skip to content

Commit

Permalink
Auto merge of #14272 - epage:test-docs, r=weihanglo
Browse files Browse the repository at this point in the history
docs(test): Expand documentation of cargo-test-support

### What does this PR try to resolve?

In wanting to document #14039, I felt it would be good to put that documentation in `cargo-test-support`.  To do so, I wanted a baseline of existing documentation for it to build on top of.

I was tempted to move more of "Writing tests" contrib documentation here, as its more about using `cargo-test-support` but I decided to hold off for now as most of that was long-form documentation and this is mostly focused on reference documentation.

### How should we test and review this PR?

### Additional information
  • Loading branch information
bors committed Jul 22, 2024
2 parents 693d49c + 83cbca0 commit ea14e86
Show file tree
Hide file tree
Showing 10 changed files with 523 additions and 228 deletions.
43 changes: 43 additions & 0 deletions crates/cargo-test-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,49 @@ use std::path::Path;
use std::process::Command;
use std::sync::Once;

/// Replacement for `#[test]`
///
/// The `#[cargo_test]` attribute extends `#[test]` with some setup before starting the test.
/// It will create a filesystem "sandbox" under the "cargo integration test" directory for each test, such as `/path/to/cargo/target/tmp/cit/t123/`.
/// The sandbox will contain a `home` directory that will be used instead of your normal home directory.
///
/// The `#[cargo_test]` attribute takes several options that will affect how the test is generated.
/// They are listed in parentheses separated with commas, such as:
///
/// ```rust,ignore
/// #[cargo_test(nightly, reason = "-Zfoo is unstable")]
/// ```
///
/// The options it supports are:
///
/// * `>=1.64` --- This indicates that the test will only run with the given version of `rustc` or newer.
/// This can be used when a new `rustc` feature has been stabilized that the test depends on.
/// If this is specified, a `reason` is required to explain why it is being checked.
/// * `nightly` --- This will cause the test to be ignored if not running on the nightly toolchain.
/// This is useful for tests that use unstable options in `rustc` or `rustdoc`.
/// These tests are run in Cargo's CI, but are disabled in rust-lang/rust's CI due to the difficulty of updating both repos simultaneously.
/// A `reason` field is required to explain why it is nightly-only.
/// * `requires_<cmd>` --- This indicates a command that is required to be installed to be run.
/// For example, `requires_rustfmt` means the test will only run if the executable `rustfmt` is installed.
/// These tests are *always* run on CI.
/// This is mainly used to avoid requiring contributors from having every dependency installed.
/// * `build_std_real` --- This is a "real" `-Zbuild-std` test (in the `build_std` integration test).
/// This only runs on nightly, and only if the environment variable `CARGO_RUN_BUILD_STD_TESTS` is set (these tests on run on Linux).
/// * `build_std_mock` --- This is a "mock" `-Zbuild-std` test (which uses a mock standard library).
/// This only runs on nightly, and is disabled for windows-gnu.
/// * `public_network_test` --- This tests contacts the public internet.
/// These tests are disabled unless the `CARGO_PUBLIC_NETWORK_TESTS` environment variable is set.
/// Use of this should be *extremely rare*, please avoid using it if possible.
/// The hosts it contacts should have a relatively high confidence that they are reliable and stable (such as github.com), especially in CI.
/// The tests should be carefully considered for developer security and privacy as well.
/// * `container_test` --- This indicates that it is a test that uses Docker.
/// These tests are disabled unless the `CARGO_CONTAINER_TESTS` environment variable is set.
/// This requires that you have Docker installed.
/// The SSH tests also assume that you have OpenSSH installed.
/// These should work on Linux, macOS, and Windows where possible.
/// Unfortunately these tests are not run in CI for macOS or Windows (no Docker on macOS, and Windows does not support Linux images).
/// See [`cargo-test-support::containers`](https://doc.rust-lang.org/nightly/nightly-rustc/cargo_test_support/containers) for more on writing these tests.
/// * `ignore_windows="reason"` --- Indicates that the test should be ignored on windows for the given reason.
#[proc_macro_attribute]
pub fn cargo_test(attr: TokenStream, item: TokenStream) -> TokenStream {
// Ideally these options would be embedded in the test itself. However, I
Expand Down
3 changes: 0 additions & 3 deletions crates/cargo-test-support/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ homepage.workspace = true
repository.workspace = true
description = "Testing framework for Cargo's testsuite."

[lib]
doctest = false

[dependencies]
anstream.workspace = true
anstyle.workspace = true
Expand Down
34 changes: 32 additions & 2 deletions crates/cargo-test-support/src/compare.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
//! Routines for comparing and diffing output.
//!
//! # Patterns
//! # Deprecated comparisons
//!
//! Cargo's tests are in transition from internal-only pattern and normalization routines used in
//! asserts like [`crate::Execs::with_stdout`] to [`assert_e2e`] and [`assert_ui`].
//!
//! ## Patterns
//!
//! Many of these functions support special markup to assist with comparing
//! text that may vary or is otherwise uninteresting for the test at hand. The
Expand All @@ -22,7 +27,7 @@
//! can use this to avoid duplicating the `with_stderr` call like:
//! `if cfg!(target_env = "msvc") {e.with_stderr("...[DIRTY]...");} else {e.with_stderr("...");}`.
//!
//! # Normalization
//! ## Normalization
//!
//! In addition to the patterns described above, the strings are normalized
//! in such a way to avoid unwanted differences. The normalizations are:
Expand Down Expand Up @@ -86,6 +91,19 @@ macro_rules! regex {
/// Other heuristics are applied to try to ensure Windows-style paths aren't
/// a problem.
/// - Carriage returns are removed, which can help when running on Windows.
///
/// # Example
///
/// ```no_run
/// # use cargo_test_support::compare::assert_e2e;
/// # use cargo_test_support::file;
/// # let p = cargo_test_support::project().build();
/// # let stdout = "";
/// assert_e2e().eq(stdout, file!["stderr.term.svg"]);
/// ```
/// ```console
/// $ SNAPSHOTS=overwrite cargo test
/// ```
pub fn assert_ui() -> snapbox::Assert {
let mut subs = snapbox::Redactions::new();
subs.extend(MIN_LITERAL_REDACTIONS.into_iter().cloned())
Expand Down Expand Up @@ -129,6 +147,18 @@ pub fn assert_ui() -> snapbox::Assert {
/// Other heuristics are applied to try to ensure Windows-style paths aren't
/// a problem.
/// - Carriage returns are removed, which can help when running on Windows.
///
/// # Example
///
/// ```no_run
/// # use cargo_test_support::compare::assert_e2e;
/// # use cargo_test_support::str;
/// # let p = cargo_test_support::project().build();
/// assert_e2e().eq(p.read_lockfile(), str![]);
/// ```
/// ```console
/// $ SNAPSHOTS=overwrite cargo test
/// ```
pub fn assert_e2e() -> snapbox::Assert {
let mut subs = snapbox::Redactions::new();
subs.extend(MIN_LITERAL_REDACTIONS.into_iter().cloned())
Expand Down
103 changes: 54 additions & 49 deletions crates/cargo-test-support/src/git.rs
Original file line number Diff line number Diff line change
@@ -1,60 +1,66 @@
/*
# Git Testing Support
## Creating a git dependency
`git::new()` is an easy way to create a new git repository containing a
project that you can then use as a dependency. It will automatically add all
the files you specify in the project and commit them to the repository.
Example:
```
let git_project = git::new("dep1", |project| {
project
.file("Cargo.toml", &basic_manifest("dep1", "1.0.0"))
.file("src/lib.rs", r#"pub fn f() { println!("hi!"); } "#)
});
// Use the `url()` method to get the file url to the new repository.
let p = project()
.file("Cargo.toml", &format!(r#"
[package]
name = "a"
version = "1.0.0"
[dependencies]
dep1 = {{ git = '{}' }}
"#, git_project.url()))
.file("src/lib.rs", "extern crate dep1;")
.build();
```
## Manually creating repositories
`git::repo()` can be used to create a `RepoBuilder` which provides a way of
adding files to a blank repository and committing them.
If you want to then manipulate the repository (such as adding new files or
tags), you can use `git2::Repository::open()` to open the repository and then
use some of the helper functions in this file to interact with the repository.
*/
//! # Git Testing Support
//!
//! ## Creating a git dependency
//! [`new()`] is an easy way to create a new git repository containing a
//! project that you can then use as a dependency. It will automatically add all
//! the files you specify in the project and commit them to the repository.
//!
//! ### Example:
//!
//! ```no_run
//! # use cargo_test_support::project;
//! # use cargo_test_support::basic_manifest;
//! # use cargo_test_support::git;
//! let git_project = git::new("dep1", |project| {
//! project
//! .file("Cargo.toml", &basic_manifest("dep1", "1.0.0"))
//! .file("src/lib.rs", r#"pub fn f() { println!("hi!"); } "#)
//! });
//!
//! // Use the `url()` method to get the file url to the new repository.
//! let p = project()
//! .file("Cargo.toml", &format!(r#"
//! [package]
//! name = "a"
//! version = "1.0.0"
//!
//! [dependencies]
//! dep1 = {{ git = '{}' }}
//! "#, git_project.url()))
//! .file("src/lib.rs", "extern crate dep1;")
//! .build();
//! ```
//!
//! ## Manually creating repositories
//!
//! [`repo()`] can be used to create a [`RepoBuilder`] which provides a way of
//! adding files to a blank repository and committing them.
//!
//! If you want to then manipulate the repository (such as adding new files or
//! tags), you can use `git2::Repository::open()` to open the repository and then
//! use some of the helper functions in this file to interact with the repository.

use crate::{paths::CargoPathExt, project, Project, ProjectBuilder, SymlinkBuilder};
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Once;
use url::Url;

/// Manually construct a [`Repository`]
///
/// See also [`new`], [`repo`]
#[must_use]
pub struct RepoBuilder {
repo: git2::Repository,
files: Vec<PathBuf>,
}

/// See [`new`]
pub struct Repository(git2::Repository);

/// Create a `RepoBuilder` to build a new git repository.
/// Create a [`RepoBuilder`] to build a new git repository.
///
/// Call `build()` to finalize and create the repository.
/// Call [`RepoBuilder::build()`] to finalize and create the repository.
pub fn repo(p: &Path) -> RepoBuilder {
RepoBuilder::init(p)
}
Expand Down Expand Up @@ -130,7 +136,7 @@ impl Repository {
}
}

/// Initialize a new repository at the given path.
/// *(`git2`)* Initialize a new repository at the given path.
pub fn init(path: &Path) -> git2::Repository {
default_search_path();
let repo = t!(git2::Repository::init(path));
Expand Down Expand Up @@ -158,16 +164,15 @@ fn default_repo_cfg(repo: &git2::Repository) {
t!(cfg.set_str("user.name", "Foo Bar"));
}

/// Create a new git repository with a project.
/// Create a new [`Project`] in a git [`Repository`]
pub fn new<F>(name: &str, callback: F) -> Project
where
F: FnOnce(ProjectBuilder) -> ProjectBuilder,
{
new_repo(name, callback).0
}

/// Create a new git repository with a project.
/// Returns both the Project and the git Repository.
/// Create a new [`Project`] with access to the [`Repository`]
pub fn new_repo<F>(name: &str, callback: F) -> (Project, git2::Repository)
where
F: FnOnce(ProjectBuilder) -> ProjectBuilder,
Expand All @@ -182,14 +187,14 @@ where
(git_project, repo)
}

/// Add all files in the working directory to the git index.
/// *(`git2`)* Add all files in the working directory to the git index
pub fn add(repo: &git2::Repository) {
let mut index = t!(repo.index());
t!(index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None));
t!(index.write());
}

/// Add a git submodule to the repository.
/// *(`git2`)* Add a git submodule to the repository
pub fn add_submodule<'a>(
repo: &'a git2::Repository,
url: &str,
Expand All @@ -207,7 +212,7 @@ pub fn add_submodule<'a>(
s
}

/// Commit changes to the git repository.
/// *(`git2`)* Commit changes to the git repository
pub fn commit(repo: &git2::Repository) -> git2::Oid {
let tree_id = t!(t!(repo.index()).write_tree());
let sig = t!(repo.signature());
Expand All @@ -226,7 +231,7 @@ pub fn commit(repo: &git2::Repository) -> git2::Oid {
))
}

/// Create a new tag in the git repository.
/// *(`git2`)* Create a new tag in the git repository
pub fn tag(repo: &git2::Repository, name: &str) {
let head = repo.head().unwrap().target().unwrap();
t!(repo.tag(
Expand Down
3 changes: 3 additions & 0 deletions crates/cargo-test-support/src/install.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Helpers for testing `cargo install`

use std::env::consts::EXE_SUFFIX;
use std::path::Path;

Expand All @@ -23,6 +25,7 @@ fn check_has_installed_exe<P: AsRef<Path>>(path: P, name: &'static str) -> bool
path.as_ref().join("bin").join(exe(name)).is_file()
}

/// `$name$EXE`
pub fn exe(name: &str) -> String {
format!("{}{}", name, EXE_SUFFIX)
}
Loading

0 comments on commit ea14e86

Please sign in to comment.