Add documentation for creating test dependencies.

Using `Package` has always been a little unclear to me. I think I understand
it now. Maybe this will help.
This commit is contained in:
Eric Huss 2018-08-24 15:09:38 -07:00
parent 676d866fee
commit d970d05f06
4 changed files with 205 additions and 1 deletions

View file

@ -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`.

View file

@ -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};
@ -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)
}
@ -35,12 +78,15 @@ 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()));
@ -48,6 +94,7 @@ impl RepoBuilder {
self
}
/// Create the repository and commit the new files.
pub fn build(self) -> Repository {
{
let mut index = t!(self.repo.index());
@ -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,
@ -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
@ -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,
@ -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());
@ -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(

View file

@ -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.
@ -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;
@ -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

View file

@ -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,
@ -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 {
@ -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)
}
@ -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();
@ -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))