Skip to content

Add documentation for creating test dependencies. #5939

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/doc/src/reference/source-replacement.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ source, but available on the local filesystem (aka vendoring). Local registries
are downloaded ahead of time, typically sync'd with a `Cargo.lock`, and are
made up of a set of `*.crate` files and an index like the normal registry is.

The primary way to manage and crate local registry sources is through the
The primary way to manage and create local registry sources is through the
[`cargo-local-registry`][cargo-local-registry] subcommand, available on
crates.io and can be installed with `cargo install cargo-local-registry`.

Expand Down
52 changes: 52 additions & 0 deletions tests/testsuite/support/git.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,43 @@
/*
# 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"))
.file("src/lib.rs", r#"pub fn f() { println!("hi!"); } "#)
}).unwrap();

// 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.

*/

use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
Expand All @@ -16,6 +56,9 @@ pub struct RepoBuilder {

pub struct Repository(git2::Repository);

/// Create a `RepoBuilder` to build a new git repository.
///
/// Call `build()` to finalize and create the repository.
pub fn repo(p: &Path) -> RepoBuilder {
RepoBuilder::init(p)
}
Expand All @@ -35,19 +78,23 @@ impl RepoBuilder {
}
}

/// Add a file to the repository.
pub fn file(self, path: &str, contents: &str) -> RepoBuilder {
let mut me = self.nocommit_file(path, contents);
me.files.push(PathBuf::from(path));
me
}

/// Add a file that will be left in the working directory, but not added
/// to the repository.
pub fn nocommit_file(self, path: &str, contents: &str) -> RepoBuilder {
let dst = self.repo.workdir().unwrap().join(path);
t!(fs::create_dir_all(dst.parent().unwrap()));
t!(t!(File::create(&dst)).write_all(contents.as_bytes()));
self
}

/// Create the repository and commit the new files.
pub fn build(self) -> Repository {
{
let mut index = t!(self.repo.index());
Expand Down Expand Up @@ -80,6 +127,7 @@ impl Repository {
}
}

/// Create a new git repository with a project.
pub fn new<F>(name: &str, callback: F) -> Result<Project, ProcessError>
where
F: FnOnce(ProjectBuilder) -> ProjectBuilder,
Expand All @@ -98,6 +146,7 @@ where
Ok(git_project)
}

/// Add all files in the working directory to the git index.
pub fn add(repo: &git2::Repository) {
// FIXME(libgit2/libgit2#2514): apparently add_all will add all submodules
// as well, and then fail b/c they're a directory. As a stopgap, we just
Expand All @@ -121,6 +170,7 @@ pub fn add(repo: &git2::Repository) {
t!(index.write());
}

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

/// 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 @@ -155,6 +206,7 @@ pub fn commit(repo: &git2::Repository) -> git2::Oid {
))
}

/// 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
33 changes: 33 additions & 0 deletions tests/testsuite/support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ let p = project()
.build();
```

If you do not specify a `Cargo.toml` manifest using `file()`, one is
automatically created with a project name of `foo` using `basic_manifest()`.

To run cargo, call the `cargo` method and use the `hamcrest` matchers to check
the output.

Expand Down Expand Up @@ -78,6 +81,34 @@ or overwrite a binary immediately after running it. Under some conditions
Windows will fail with errors like "directory not empty" or "failed to remove"
or "access is denied".

## Specifying Dependencies

You should not write any tests that use the network such as contacting
crates.io. Typically, simple path dependencies are the easiest way to add a
dependency. Example:

```
let p = project()
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "1.0.0"

[dependencies]
bar = {path = "bar"}
"#)
.file("src/lib.rs", "extern crate bar;")
.file("bar/Cargo.toml", &basic_manifest("bar", "1.0.0"))
.file("bar/src/lib.rs", "")
.build();
```

If you need to test with registry dependencies, see
`support::registry::Package` for creating packages you can depend on.

If you need to test git dependencies, see `support::git` to create a git
dependency.

*/

use std::env;
Expand Down Expand Up @@ -530,6 +561,8 @@ impl Execs {
}

/// Verify the exit code from the process.
///
/// This is not necessary if the expected exit code is `0`.
pub fn with_status(mut self, expected: i32) -> Execs {
self.expect_exit_code = Some(expected);
self
Expand Down
119 changes: 119 additions & 0 deletions tests/testsuite/support/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,70 @@ pub fn alt_api_url() -> Url {
Url::from_file_path(&*alt_api_path()).ok().unwrap()
}

/// A builder for creating a new package in a registry.
///
/// This uses "source replacement" using an automatically generated
/// `.cargo/config` file to ensure that dependencies will use these packages
/// instead of contacting crates.io. See `source-replacement.md` for more
/// details on how source replacement works.
///
/// Call `publish` to finalize and create the package.
///
/// If no files are specified, an empty `lib.rs` file is automatically created.
///
/// The `Cargo.toml` file is automatically generated based on the methods
/// called on `Package` (for example, calling `dep()` will add to the
/// `[dependencies]` automatically). You may also specify a `Cargo.toml` file
/// to override the generated one.
///
/// This supports different registry types:
/// - Regular source replacement that replaces `crates.io` (the default).
/// - A "local registry" which is a subset for vendoring (see
/// `Package::local`).
/// - An "alternative registry" which requires specifying the registry name
/// (see `Package::alternative`).
///
/// This does not support "directory sources". See `directory.rs` for
/// `VendorPackage` which implements directory sources.
///
/// # Example
/// ```
/// // Publish package "a" depending on "b".
/// Package::new("a", "1.0.0")
/// .dep("b", "1.0.0")
/// .file("src/lib.rs", r#"
/// extern crate b;
/// pub fn f() -> i32 { b::f() * 2 }
/// "#)
/// .publish();
///
/// // Publish package "b".
/// Package::new("b", "1.0.0")
/// .file("src/lib.rs", r#"
/// pub fn f() -> i32 { 12 }
/// "#)
/// .publish();
///
/// // Create a project that uses package "a".
/// let p = project()
/// .file("Cargo.toml", r#"
/// [package]
/// name = "foo"
/// version = "0.0.1"
///
/// [dependencies]
/// a = "1.0"
/// "#)
/// .file("src/main.rs", r#"
/// extern crate a;
/// fn main() { println!("{}", a::f()); }
/// "#)
/// .build();
///
/// assert_that(
/// p.cargo("run"),
/// execs().with_stdout("24"));
/// ```
pub struct Package {
name: String,
vers: String,
Expand Down Expand Up @@ -128,6 +192,8 @@ pub fn init() {
}

impl Package {
/// Create a new package builder.
/// Call `publish()` to finalize and build the package.
pub fn new(name: &str, vers: &str) -> Package {
init();
Package {
Expand All @@ -143,47 +209,92 @@ impl Package {
}
}

/// Call with `true` to publish in a "local registry".
///
/// See `source-replacement.html#local-registry-sources` for more details
/// on local registries. See `local_registry.rs` for the tests that use
/// this.
pub fn local(&mut self, local: bool) -> &mut Package {
self.local = local;
self
}

/// Call with `true` to publish in an "alternative registry".
///
/// The name of the alternative registry is called "alternative".
///
/// See `unstable.html#alternate-registries` for more details on
/// alternative registries. See `alt_registry.rs` for the tests that use
/// this.
pub fn alternative(&mut self, alternative: bool) -> &mut Package {
self.alternative = alternative;
self
}

/// Add a file to the package.
pub fn file(&mut self, name: &str, contents: &str) -> &mut Package {
self.files.push((name.to_string(), contents.to_string()));
self
}

/// Add an "extra" file that is not rooted within the package.
///
/// Normal files are automatically placed within a directory named
/// `$PACKAGE-$VERSION`. This allows you to override that behavior,
/// typically for testing invalid behavior.
pub fn extra_file(&mut self, name: &str, contents: &str) -> &mut Package {
self.extra_files
.push((name.to_string(), contents.to_string()));
self
}

/// Add a normal dependency. Example:
/// ```
/// [dependencies]
/// foo = {version = "1.0"}
/// ```
pub fn dep(&mut self, name: &str, vers: &str) -> &mut Package {
self.full_dep(name, vers, None, "normal", &[], None)
}

/// Add a dependency with the given feature. Example:
/// ```
/// [dependencies]
/// foo = {version = "1.0", "features": ["feat1", "feat2"]}
/// ```
pub fn feature_dep(&mut self, name: &str, vers: &str, features: &[&str]) -> &mut Package {
self.full_dep(name, vers, None, "normal", features, None)
}

/// Add a platform-specific dependency. Example:
/// ```
/// [target.'cfg(windows)'.dependencies]
/// foo = {version = "1.0"}
/// ```
pub fn target_dep(&mut self, name: &str, vers: &str, target: &str) -> &mut Package {
self.full_dep(name, vers, Some(target), "normal", &[], None)
}

/// Add a dependency to an alternative registry.
/// The given registry should be a URI to the alternative registry.
pub fn registry_dep(&mut self, name: &str, vers: &str, registry: &str) -> &mut Package {
self.full_dep(name, vers, None, "normal", &[], Some(registry))
}

/// Add a dev-dependency. Example:
/// ```
/// [dev-dependencies]
/// foo = {version = "1.0"}
/// ```
pub fn dev_dep(&mut self, name: &str, vers: &str) -> &mut Package {
self.full_dep(name, vers, None, "dev", &[], None)
}

/// Add a build-dependency. Example:
/// ```
/// [build-dependencies]
/// foo = {version = "1.0"}
/// ```
pub fn build_dep(&mut self, name: &str, vers: &str) -> &mut Package {
self.full_dep(name, vers, None, "build", &[], None)
}
Expand All @@ -208,11 +319,18 @@ impl Package {
self
}

/// Specify whether or not the package is "yanked".
pub fn yanked(&mut self, yanked: bool) -> &mut Package {
self.yanked = yanked;
self
}

/// Create the package and place it in the registry.
///
/// This does not actually use Cargo's publishing system, but instead
/// manually creates the entry in the registry on the filesystem.
///
/// Returns the checksum for the package.
pub fn publish(&self) -> String {
self.make_archive();

Expand Down Expand Up @@ -358,6 +476,7 @@ impl Package {
t!(ar.append(&header, contents.as_bytes()));
}

/// Returns the path to the compressed package file.
pub fn archive_dst(&self) -> PathBuf {
if self.local {
registry_path().join(format!("{}-{}.crate", self.name, self.vers))
Expand Down