diff --git a/Cargo.lock b/Cargo.lock index 819fc9b584..b4f4d9ceef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,14 +88,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cargo" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytesize 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crates-io 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crates-io 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "crypto-hash 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "curl 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -106,8 +107,8 @@ dependencies = [ "flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "fwdansi 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", - "git2-curl 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "git2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "git2-curl 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "home 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -127,7 +128,6 @@ dependencies = [ "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", "serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "shell-escape 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -137,6 +137,7 @@ dependencies = [ "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -144,7 +145,7 @@ dependencies = [ name = "cargo-tarpaulin" version = "0.7.0" dependencies = [ - "cargo 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cargo 0.34.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "coveralls-api 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -257,7 +258,7 @@ dependencies = [ [[package]] name = "crates-io" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "curl 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -486,13 +487,27 @@ dependencies = [ "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "git2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "libgit2-sys 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.41 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "git2-curl" -version = "0.8.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "curl 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", - "git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", + "git2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1342,6 +1357,15 @@ dependencies = [ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "url_serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "utf8-ranges" version = "1.0.2" @@ -1436,7 +1460,7 @@ dependencies = [ "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum bytesize 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010" -"checksum cargo 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deeeb1b78be36648a57aa0a6f2139f09623429ca6f11d3cbcf69bb9e1c4fabb" +"checksum cargo 0.34.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f7e90b5f23ae79af3ec0e4dc670349167fd47d6c1134f139cf0627817a4792bf" "checksum cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "4390a3b5f4f6bce9c1d0c00128379df433e53777fdd30e92f16a529332baec4e" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" @@ -1447,7 +1471,7 @@ dependencies = [ "checksum core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4e2640d6d0bf22e82bed1b73c6aef8d5dd31e5abe6666c57e6d45e2649f4f887" "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" "checksum coveralls-api 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08077f00788d0871245bec89418b0da009de85a0d5b06c04739bab0533af5b24" -"checksum crates-io 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "af82085c3ecd9dcc570ce7a6890c321000dae5b66ab9f4ff6a617e1f26b8664c" +"checksum crates-io 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "091018c3f5e8109d82d94b648555f0d4a308d15626da2fb22c76f32117e24569" "checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" "checksum crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e91d5240c6975ef33aeb5f148f35275c25eda8e8a5f95abe421978b05b8bf192" "checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" @@ -1471,7 +1495,8 @@ dependencies = [ "checksum fwdansi 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34dd4c507af68d37ffef962063dfa1944ce0dd4d5b82043dbab1dabe088610c3" "checksum gimli 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7f6ee5390883802431e4abe323390f52f10ff16e8f8d2d6ce598251f900ede" "checksum git2 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "591f8be1674b421644b6c030969520bc3fa12114d2eb467471982ed3e9584e71" -"checksum git2-curl 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0173e317f8ba21f3fff0f71549fead5e42e67961dbd402bf69f42775f3cc78b4" +"checksum git2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7339329bfa14a00223244311560d11f8f489b453fb90092af97f267a6090ab0" +"checksum git2-curl 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d58551e903ed7e2d6fe3a2f3c7efa3a784ec29b19d0fbb035aaf0497c183fbdd" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865" "checksum goblin 0.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "c65cd533b33e3d04c6e393225fa8919ddfcf5862ca8919c7f9a167c312ef41c2" @@ -1574,6 +1599,7 @@ dependencies = [ "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +"checksum url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74e7d099f1ee52f823d4bdd60c93c3602043c728f5db3b97bdb548467f7bddea" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" "checksum uuid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0238db0c5b605dd1cf51de0f21766f97fba2645897024461d6a00c036819a768" "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" diff --git a/Cargo.toml b/Cargo.toml index 7ff0af0498..da068b9962 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ edition = "2018" name = "cargo-tarpaulin" [dependencies] -cargo = "0.33" +cargo = "0.34" chrono = "0.4" clap = "2.31.2" coveralls-api = "0.4.0" @@ -31,7 +31,7 @@ log = "0.4.6" memmap = "0.7.0" nix = "0.13.0" object = "0.11" -proc-macro2 = "0.4.24" +proc-macro2 = "0.4.27" quick-xml = "0.13.0" regex = "1.1" rustc-demangle = "0.1.11" diff --git a/src/config/mod.rs b/src/config/mod.rs index a033870848..f4d53cff3d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -13,10 +13,12 @@ mod parse; mod types; /// Specifies the current configuration tarpaulin is using. -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Config { /// Path to the projects cargo manifest pub manifest: PathBuf, + /// Types of tests for tarpaulin to collect coverage on + pub run_types: Vec, /// Flag to also run tests with the ignored attribute pub run_ignored: bool, /// Flag to ignore test functions in coverage statistics @@ -70,10 +72,45 @@ pub struct Config { pub release: bool, } +impl Default for Config { + + fn default() -> Config { + Config { + run_types: vec![RunType::Tests], + manifest: Default::default(), + run_ignored: false, + ignore_tests: false, + ignore_panics: false, + force_clean: false, + verbose: false, + debug: false, + count: false, + line_coverage: true, + branch_coverage: false, + generate: vec![], + coveralls: None, + ci_tool: None, + report_uri: None, + forward_signals: false, + no_default_features: false, + features: vec![], + all: false, + packages: vec![], + exclude: vec![], + excluded_files: vec![], + varargs: vec![], + test_timeout: Duration::from_secs(60), + release: false, + all_features: false, + } + } +} + impl<'a> From<&'a ArgMatches<'a>> for Config { fn from(args: &'a ArgMatches<'a>) -> Self { Config { manifest: get_manifest(args), + run_types: get_run_types(args), run_ignored: args.is_present("ignored"), ignore_tests: args.is_present("ignore-tests"), ignore_panics: args.is_present("ignore-panics"), diff --git a/src/config/parse.rs b/src/config/parse.rs index a2ec762056..4a0a790395 100644 --- a/src/config/parse.rs +++ b/src/config/parse.rs @@ -52,6 +52,10 @@ pub(super) fn get_outputs(args: &ArgMatches) -> Vec { values_t!(args.values_of("out"), OutputFile).unwrap_or(vec![]) } +pub(super) fn get_run_types(args: &ArgMatches) -> Vec { + values_t!(args.values_of("run-types"), RunType).unwrap_or(vec![RunType::Tests]) +} + pub(super) fn get_excluded(args: &ArgMatches) -> Vec { let mut files = vec![]; diff --git a/src/config/types.rs b/src/config/types.rs index aec8f8b559..c829ddb625 100644 --- a/src/config/types.rs +++ b/src/config/types.rs @@ -1,8 +1,17 @@ use clap::{_clap_count_exprs, arg_enum}; +use cargo::core::compiler::CompileMode; use coveralls_api::CiService; use std::str::FromStr; use void::Void; +arg_enum! { + #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)] + pub enum RunType { + Tests, + Doctests, + } +} + arg_enum! { #[derive(Debug)] pub enum OutputFile { @@ -23,6 +32,15 @@ impl Default for OutputFile { pub struct Ci(pub CiService); +impl From for CompileMode { + fn from(run: RunType) -> Self { + match run { + RunType::Tests => CompileMode::Test, + RunType::Doctests => CompileMode::Doctest + } + } +} + impl FromStr for Ci { /// This can never fail, so the error type is uninhabited. type Err = Void; diff --git a/src/errors/mod.rs b/src/errors/mod.rs index 9c663b91e0..49ab417783 100644 --- a/src/errors/mod.rs +++ b/src/errors/mod.rs @@ -43,6 +43,8 @@ pub enum RunError { Html(String), #[fail(display = "Failed to generate XML report! Error: {}", _0)] XML(cobertura::Error), + #[fail(display = "Tarpaulin experienced an internal error")] + Internal, } impl From for RunError { diff --git a/src/lib.rs b/src/lib.rs index 7a2ffa7b61..a16b78d869 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,12 +6,14 @@ use crate::test_loader::*; use crate::traces::*; use cargo::core::{compiler::CompileMode, Package, Shell, Workspace}; use cargo::ops; +use cargo::ops::{CompileOptions, CompileFilter, Packages, CleanOptions, clean, compile, TestOptions}; use cargo::util::{homedir, Config as CargoConfig}; use log::{debug, info, trace, warn}; use nix::unistd::*; use std::env; use std::ffi::CString; use std::path::{Path, PathBuf}; +use walkdir::WalkDir; pub mod breakpoint; pub mod config; @@ -26,6 +28,8 @@ pub mod traces; mod personality; mod ptrace_control; +static DOCTEST_FOLDER: &str = "target/doctests"; + pub fn run(config: &Config) -> Result<(), RunError> { let (tracemap, ret) = launch_tarpaulin(config)?; report_coverage(config, &tracemap)?; @@ -39,6 +43,8 @@ pub fn run(config: &Config) -> Result<(), RunError> { /// Launches tarpaulin with the given configuration. pub fn launch_tarpaulin(config: &Config) -> Result<(TraceMap, i32), RunError> { + setup_environment(&config); + cargo::core::enable_nightly_features(); let cwd = match config.manifest.parent() { Some(p) => p.to_path_buf(), None => PathBuf::new(), @@ -52,69 +58,65 @@ pub fn launch_tarpaulin(config: &Config) -> Result<(TraceMap, i32), RunError> { }; let mut cargo_config = CargoConfig::new(Shell::new(), cwd, home); let flag_quiet = if config.verbose { None } else { Some(true) }; - cargo::core::enable_nightly_features(); + // This shouldn't fail so no checking the error. let _ = cargo_config.configure(0u32, flag_quiet, &None, false, false, &None, &[]); let workspace = Workspace::new(config.manifest.as_path(), &cargo_config) .map_err(|e| RunError::Manifest(e.to_string()))?; - setup_environment(&config); - - let mut copt = ops::CompileOptions::new(&cargo_config, CompileMode::Test) - .map_err(|e| RunError::Cargo(e.to_string()))?; - if let ops::CompileFilter::Default { - ref mut required_features_filterable, - } = copt.filter - { - *required_features_filterable = true; - } - copt.features = config.features.clone(); - copt.all_features = config.all_features; - copt.no_default_features = config.no_default_features; - copt.build_config.release = config.release; - copt.spec = match ops::Packages::from_flags( - config.all, - config.exclude.clone(), - config.packages.clone(), - ) { - Ok(spec) => spec, - Err(e) => { - return Err(RunError::Packages(e.to_string())); - } - }; + let mut compile_options = get_compile_options(&config, &cargo_config)?; info!("Running Tarpaulin"); if config.force_clean { debug!("Cleaning project"); // Clean isn't expected to fail and if it does it likely won't have an effect - let clean_opt = ops::CleanOptions { + let clean_opt = CleanOptions { config: &cargo_config, spec: vec![], target: None, release: false, doc: false, }; - let _ = ops::clean(&workspace, &clean_opt); + let _ = clean(&workspace, &clean_opt); } let mut result = TraceMap::new(); + let mut return_code = 0i32; info!("Building project"); - let compilation = ops::compile(&workspace, &copt); + for copt in compile_options.drain(..) { + let run_result = match copt.build_config.mode { + CompileMode::Test => run_tests(&workspace, copt, config), + CompileMode::Doctest => run_doctests(&workspace, copt, config), + e => { + debug!("Internal tarpaulin error. Unsupported compile mode {:?}", e); + Err(RunError::Internal) + }, + }?; + result.merge(&run_result.0); + return_code |= run_result.1; + } + result.dedup(); + Ok((result, return_code)) +} + +fn run_tests(workspace: &Workspace, compile_options: CompileOptions, config: &Config) -> Result<(TraceMap, i32), RunError> { + let mut result = TraceMap::new(); let mut return_code = 0i32; + let compilation = compile(&workspace, &compile_options); match compilation { Ok(comp) => { - for &(ref package, ref _target_kind, ref name, ref path) in &comp.tests { + for &(ref package, _, ref name, ref path) in &comp.tests { debug!("Processing {}", name); if let Some(res) = - get_test_coverage(&workspace, package, path.as_path(), config, false)? + get_test_coverage(&workspace, Some(package), path.as_path(), config, false)? { result.merge(&res.0); return_code |= res.1; } if config.run_ignored { if let Some(res) = - get_test_coverage(&workspace, package, path.as_path(), config, true)? + get_test_coverage(&workspace, Some(package), path.as_path(), config, true)? { result.merge(&res.0); return_code |= res.1; @@ -124,15 +126,81 @@ pub fn launch_tarpaulin(config: &Config) -> Result<(TraceMap, i32), RunError> { result.dedup(); Ok((result, return_code)) } - Err(e) => Err(RunError::TestCompile(e.to_string())), + Err(e) => return Err(RunError::TestCompile(e.to_string())), + } +} + +fn run_doctests(workspace: &Workspace, + compile_options: CompileOptions, + config: &Config) -> Result<(TraceMap, i32), RunError> { + info!("Running doctests"); + let mut result = TraceMap::new(); + let mut return_code = 0i32; + + let opts = TestOptions { + no_run: false, + no_fail_fast: false, + compile_opts: compile_options, + }; + let _ = ops::run_tests(workspace, &opts, &[]); + + // go over all the doc tests and run them. + let doctest_dir = match config.manifest.parent() { + Some(p) => p.join(DOCTEST_FOLDER), + None => PathBuf::from(DOCTEST_FOLDER) + }; + let walker = WalkDir::new(&doctest_dir).into_iter(); + for dt in walker.filter_map(|e| e.ok()).filter(|e| e.file_type().is_file()) { + if let Some(res) = get_test_coverage(&workspace, None, dt.path(), config, false)? + { + result.merge(&res.0); + return_code |= res.1; + } + + } + result.dedup(); + Ok((result, return_code)) +} + + +fn get_compile_options<'a>(config: &Config, + cargo_config: &'a CargoConfig) -> Result>, RunError> { + let mut result = Vec::new(); + for run_type in &config.run_types { + let mut copt = CompileOptions::new(cargo_config, (*run_type).into()) + .map_err(|e| RunError::Cargo(e.to_string()))?; + if run_type == &RunType::Tests { + if let CompileFilter::Default { ref mut required_features_filterable } = copt.filter + { + *required_features_filterable = true; + } + } else if run_type == &RunType::Doctests { + copt.filter = CompileFilter::new(true, vec![], false, vec![], false, vec![], false, vec![], false, false); + } + + copt.features = config.features.clone(); + copt.all_features = config.all_features; + copt.no_default_features = config.no_default_features; + copt.build_config.release = config.release; + copt.spec = match Packages::from_flags( + config.all, + config.exclude.clone(), + config.packages.clone(), + ) { + Ok(spec) => spec, + Err(e) => { + return Err(RunError::Packages(e.to_string())); + } + }; + result.push(copt); } + Ok(result) } fn setup_environment(config: &Config) { + let common_opts = " -C relocation-model=dynamic-no-pic -C link-dead-code -C opt-level=0 -C debuginfo=2 "; let rustflags = "RUSTFLAGS"; - let mut value = - " -C relocation-model=dynamic-no-pic -C link-dead-code -C opt-level=0 -C debuginfo=2 " - .to_string(); + let mut value = common_opts.to_string(); if config.release { value = format!("{}-C debug-assertions=off ", value); } @@ -140,6 +208,13 @@ fn setup_environment(config: &Config) { value.push_str(vtemp.as_ref()); } env::set_var(rustflags, value); + // doesn't matter if we don't use it + let rustdoc = "RUSTDOCFLAGS"; + let mut value = format!("{} --persist-doctests {} -Z unstable-options ", common_opts, DOCTEST_FOLDER); + if let Ok(vtemp) = env::var(rustdoc) { + value.push_str(vtemp.as_ref()); + } + env::set_var(rustdoc, value); } fn accumulate_lines( @@ -246,7 +321,7 @@ pub fn report_coverage(config: &Config, result: &TraceMap) -> Result<(), RunErro /// Returns the coverage statistics for a test executable in the given workspace pub fn get_test_coverage( project: &Workspace, - package: &Package, + package: Option<&Package>, test: &Path, config: &Config, ignored: bool, @@ -300,7 +375,7 @@ fn collect_coverage( /// Launches the test executable fn execute_test( test: &Path, - package: &Package, + package: Option<&Package>, ignored: bool, config: &Config, ) -> Result<(), RunError> { @@ -314,8 +389,10 @@ fn execute_test( Err(e) => return Err(RunError::Trace(e.to_string())), } info!("running {}", test.display()); - if let Some(parent) = package.manifest_path().parent() { - let _ = env::set_current_dir(parent); + if let Some(pack) = package { + if let Some(parent) = pack.manifest_path().parent() { + let _ = env::set_current_dir(parent); + } } let mut envars: Vec = vec![CString::new("RUST_TEST_THREADS=1").unwrap()]; diff --git a/src/main.rs b/src/main.rs index b70e935459..84ae9589d1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -81,6 +81,9 @@ fn main() { Arg::from_usage("--out -o [FMT] 'Output format of coverage report'") .possible_values(&OutputFile::variants()) .multiple(true), + Arg::from_usage("--run-types [TYPE] 'Type of the coverage run'") + .possible_values(&RunType::variants()) + .multiple(true), Arg::from_usage("--root -r [DIR] 'Root directory containing Cargo.toml to use'") .validator(is_dir), Arg::from_usage("--ciserver [SERVICE] 'CI server being used, if unspecified tarpaulin may automatically infer for coveralls uploads'") diff --git a/tests/data/doc_coverage/Cargo.lock b/tests/data/doc_coverage/Cargo.lock new file mode 100644 index 0000000000..32b776a98f --- /dev/null +++ b/tests/data/doc_coverage/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "doc_coverage" +version = "0.1.0" + diff --git a/tests/data/doc_coverage/Cargo.toml b/tests/data/doc_coverage/Cargo.toml new file mode 100644 index 0000000000..a186496671 --- /dev/null +++ b/tests/data/doc_coverage/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "doc_coverage" +version = "0.1.0" +authors = ["xd009642 "] +edition = "2018" + +[dependencies] diff --git a/tests/data/doc_coverage/src/lib.rs b/tests/data/doc_coverage/src/lib.rs new file mode 100644 index 0000000000..f71e24ebea --- /dev/null +++ b/tests/data/doc_coverage/src/lib.rs @@ -0,0 +1,12 @@ + + + +///This is a doc comment +/// ``` +/// use doc_coverage::uncovered_by_tests; +/// assert_eq!(4, uncovered_by_tests(4)); +/// ``` +pub fn uncovered_by_tests(x: i32) -> i32 { + let y = x.pow(2); + y / x +} diff --git a/tests/doc_coverage.rs b/tests/doc_coverage.rs new file mode 100644 index 0000000000..3a75a9c6cb --- /dev/null +++ b/tests/doc_coverage.rs @@ -0,0 +1,34 @@ +use cargo_tarpaulin::config::{Config, RunType}; +use cargo_tarpaulin::launch_tarpaulin; +use std::env; +use std::time::Duration; + +#[test] +fn doc_test_coverage() { + let mut config = Config::default(); + config.verbose = true; + config.test_timeout = Duration::from_secs(60); + let mut test_dir = env::current_dir().unwrap(); + test_dir.push("tests"); + test_dir.push("data"); + test_dir.push("doc_coverage"); + env::set_current_dir(test_dir.clone()).unwrap(); + config.manifest = test_dir.clone(); + config.manifest.push("Cargo.toml"); + + config.run_types = vec![RunType::Doctests]; + + let (res, ret) = launch_tarpaulin(&config).unwrap(); + + assert_eq!(ret, 0); + assert!(res.total_covered() > 0); + assert_eq!(res.total_covered(), res.total_coverable()); + + config.run_types = vec![RunType::Tests]; + + let (res, ret) = launch_tarpaulin(&config).unwrap(); + + assert_eq!(ret, 0); + assert_eq!(res.total_covered(), 0); + +}