diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 6f6096ae46..2d8e9a407e 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -336,6 +336,7 @@ pub struct RunFlags { pub script: String, pub watch: Option, pub bare: bool, + pub coverage_dir: Option, } impl RunFlags { @@ -345,6 +346,7 @@ impl RunFlags { script, watch: None, bare: false, + coverage_dir: None, } } @@ -3306,6 +3308,7 @@ fn run_args(command: Command, top_level: bool) -> Command { }) .arg(env_file_arg()) .arg(no_code_cache_arg()) + .arg(coverage_arg()) } fn run_subcommand() -> Command { @@ -4475,6 +4478,21 @@ fn no_code_cache_arg() -> Arg { .action(ArgAction::SetTrue) } +fn coverage_arg() -> Arg { + Arg::new("coverage") + .long("coverage") + .value_name("DIR") + .num_args(0..=1) + .require_equals(true) + .default_missing_value("coverage") + .conflicts_with("inspect") + .conflicts_with("inspect-wait") + .conflicts_with("inspect-brk") + .help(cstr!("Collect coverage profile data into DIR. If DIR is not specified, it uses 'coverage/'. + This option can also be set via the DENO_COVERAGE_DIR environment variable.")) + .value_hint(ValueHint::AnyPath) +} + fn permit_no_files_arg() -> Arg { Arg::new("permit-no-files") .long("permit-no-files") @@ -5600,6 +5618,7 @@ fn run_parse( ext_arg_parse(flags, matches); flags.code_cache_enabled = !matches.get_flag("no-code-cache"); + let coverage_dir = matches.remove_one::("coverage"); if let Some(mut script_arg) = matches.remove_many::("script_arg") { let script = script_arg.next().unwrap(); @@ -5608,6 +5627,7 @@ fn run_parse( script, watch: watch_arg_parse_with_paths(matches)?, bare, + coverage_dir, }); } else if bare { return Err(app.override_usage("deno [OPTIONS] [COMMAND] [SCRIPT_ARG]...").error( @@ -6566,6 +6586,7 @@ mod tests { exclude: vec![], }), bare: false, + coverage_dir: None, }), code_cache_enabled: true, ..Flags::default() @@ -6591,6 +6612,7 @@ mod tests { exclude: vec![], }), bare: true, + coverage_dir: None, }), code_cache_enabled: true, ..Flags::default() @@ -6617,6 +6639,7 @@ mod tests { exclude: vec![], }), bare: false, + coverage_dir: None, }), code_cache_enabled: true, ..Flags::default() @@ -6643,6 +6666,7 @@ mod tests { exclude: vec![], }), bare: false, + coverage_dir: None, }), code_cache_enabled: true, ..Flags::default() @@ -6669,6 +6693,7 @@ mod tests { exclude: vec![], }), bare: false, + coverage_dir: None, }), code_cache_enabled: true, ..Flags::default() @@ -6696,6 +6721,7 @@ mod tests { exclude: vec![], }), bare: true, + coverage_dir: None, }), code_cache_enabled: true, ..Flags::default() @@ -6726,6 +6752,7 @@ mod tests { exclude: vec![], }), bare: false, + coverage_dir: None, }), code_cache_enabled: true, ..Flags::default() @@ -6755,6 +6782,7 @@ mod tests { exclude: vec![String::from("foo")], }), bare: true, + coverage_dir: None, }), code_cache_enabled: true, ..Flags::default() @@ -6781,6 +6809,7 @@ mod tests { exclude: vec![String::from("bar")], }), bare: false, + coverage_dir: None, }), code_cache_enabled: true, ..Flags::default() @@ -6808,6 +6837,7 @@ mod tests { exclude: vec![String::from("foo"), String::from("bar")], }), bare: false, + coverage_dir: None, }), code_cache_enabled: true, ..Flags::default() @@ -6834,6 +6864,7 @@ mod tests { exclude: vec![String::from("baz"), String::from("qux"),], }), bare: true, + coverage_dir: None, }), code_cache_enabled: true, ..Flags::default() @@ -6862,6 +6893,24 @@ mod tests { ); } + #[test] + fn run_coverage() { + let r = flags_from_vec(svec!["deno", "run", "--coverage=foo", "script.ts"]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Run(RunFlags { + script: "script.ts".to_string(), + watch: None, + bare: false, + coverage_dir: Some("foo".to_string()), + }), + code_cache_enabled: true, + ..Flags::default() + } + ); + } + #[test] fn run_v8_flags() { let r = flags_from_vec(svec!["deno", "run", "--v8-flags=--help"]); @@ -7170,6 +7219,7 @@ mod tests { script: "gist.ts".to_string(), watch: None, bare: true, + coverage_dir: None, }), permissions: PermissionFlags { deny_read: Some(vec![]), @@ -8456,6 +8506,7 @@ mod tests { script: "script.ts".to_string(), watch: None, bare: true, + coverage_dir: None, }), permissions: PermissionFlags { deny_net: Some(svec!["127.0.0.1"]), @@ -8643,6 +8694,7 @@ mod tests { script: "script.ts".to_string(), watch: None, bare: true, + coverage_dir: None, }), permissions: PermissionFlags { deny_sys: Some(svec!["hostname"]), @@ -8942,6 +8994,7 @@ mod tests { script: "script.ts".to_string(), watch: None, bare: true, + coverage_dir: None, }), ..Flags::default() } @@ -9252,6 +9305,7 @@ mod tests { script: "script.ts".to_string(), watch: None, bare: true, + coverage_dir: None, }), log_level: Some(Level::Error), code_cache_enabled: true, @@ -9372,6 +9426,7 @@ mod tests { script: "script.ts".to_string(), watch: None, bare: true, + coverage_dir: None, }), type_check_mode: TypeCheckMode::None, code_cache_enabled: true, @@ -9540,6 +9595,7 @@ mod tests { script: "script.ts".to_string(), watch: None, bare: true, + coverage_dir: None, }), node_modules_dir: Some(NodeModulesDirMode::Auto), code_cache_enabled: true, @@ -10763,6 +10819,7 @@ mod tests { script: "foo.js".to_string(), watch: None, bare: true, + coverage_dir: None, }), inspect_wait: Some("127.0.0.1:9229".parse().unwrap()), code_cache_enabled: true, @@ -11453,6 +11510,7 @@ mod tests { script: "script.ts".to_string(), watch: None, bare: true, + coverage_dir: None, }), type_check_mode: TypeCheckMode::None, code_cache_enabled: true, @@ -12006,6 +12064,7 @@ mod tests { script: "script.ts".to_string(), watch: None, bare: true, + coverage_dir: None, }), config_flag: ConfigFlag::Disabled, code_cache_enabled: true, diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 08746d294e..d05fe9a6eb 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -762,6 +762,11 @@ impl CliOptions { .as_ref() .map(ToOwned::to_owned) .or_else(|| env::var("DENO_COVERAGE_DIR").ok()), + DenoSubcommand::Run(flags) => flags + .coverage_dir + .as_ref() + .map(ToOwned::to_owned) + .or_else(|| env::var("DENO_COVERAGE_DIR").ok()), _ => None, } } diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 097a388cac..4654f4193e 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -579,6 +579,7 @@ fn filter_coverages( || e.url.starts_with("data:") || e.url.ends_with("__anonymous__") || e.url.ends_with("$deno$test.mjs") + || e.url.ends_with("$deno$stdin.mts") || e.url.ends_with(".snap") || is_supported_test_path(Path::new(e.url.as_str())) || doc_test_re.is_match(e.url.as_str()) diff --git a/cli/tools/deploy.rs b/cli/tools/deploy.rs index 97886c4a11..dd4e8f948e 100644 --- a/cli/tools/deploy.rs +++ b/cli/tools/deploy.rs @@ -37,6 +37,7 @@ pub async fn deploy( ), watch: None, bare: false, + coverage_dir: None, }); tools::run::run_script( diff --git a/tests/specs/run/run_coverage/__test__.jsonc b/tests/specs/run/run_coverage/__test__.jsonc new file mode 100644 index 0000000000..50006c3532 --- /dev/null +++ b/tests/specs/run/run_coverage/__test__.jsonc @@ -0,0 +1,36 @@ +{ + "tempDir": true, + "tests": { + "run_coverage": { + "steps": [ + { + "args": "run --coverage foo.ts", + "output": "0\n0\n" + }, + { + "args": "coverage", + "output": "coverage_summary.out" + } + ] + }, + "run_coverage_env": { + "steps": [ + { + "args": "run foo.ts", + "output": "0\n0\n", + "envs": { + "DENO_COVERAGE_DIR": "my_coverage_dir" + } + }, + { + "args": "coverage my_coverage_dir", + "output": "coverage_summary.out" + } + ] + }, + "test_child_process_coverage": { + "args": "test -A --coverage child_process_coverage_test.ts", + "output": "child_process_coverage_test.out" + } + } +} diff --git a/tests/specs/run/run_coverage/child_process_coverage_test.out b/tests/specs/run/run_coverage/child_process_coverage_test.out new file mode 100644 index 0000000000..aa9300399d --- /dev/null +++ b/tests/specs/run/run_coverage/child_process_coverage_test.out @@ -0,0 +1,13 @@ +Check file:///[WILDLINE]child_process_coverage_test.ts +running 2 tests from ./child_process_coverage_test.ts +foo 1 0 ... ok ([WILDLINE]s) +foo 0 1 ... ok ([WILDLINE]s) + +ok | 2 passed | 0 failed ([WILDLINE]s) + +| File | Branch % | Line % | +| --------- | -------- | ------ | +| foo.ts | 100.0 | 100.0 | +| All files | 100.0 | 100.0 | +Lcov coverage report has been generated at file:///[WILDLINE]/coverage/lcov.info +HTML coverage report has been generated at file:///[WILDLINE]/coverage/html/index.html diff --git a/tests/specs/run/run_coverage/child_process_coverage_test.ts b/tests/specs/run/run_coverage/child_process_coverage_test.ts new file mode 100644 index 0000000000..a04b34b925 --- /dev/null +++ b/tests/specs/run/run_coverage/child_process_coverage_test.ts @@ -0,0 +1,6 @@ +Deno.test("foo 1 0", () => runDenoCommand("run foo.ts 1 0")); +Deno.test("foo 0 1", () => runDenoCommand("run foo.ts 0 1")); + +async function runDenoCommand(args: string) { + await new Deno.Command(Deno.execPath(), { args: args.split(" ") }).output(); +} diff --git a/tests/specs/run/run_coverage/coverage_summary.out b/tests/specs/run/run_coverage/coverage_summary.out new file mode 100644 index 0000000000..a4cdc320c0 --- /dev/null +++ b/tests/specs/run/run_coverage/coverage_summary.out @@ -0,0 +1,4 @@ +| File | Branch % | Line % | +| --------- | -------- | ------ | +| foo.ts | 0.0 | 57.1 | +| All files | 0.0 | 57.1 | diff --git a/tests/specs/run/run_coverage/foo.ts b/tests/specs/run/run_coverage/foo.ts new file mode 100644 index 0000000000..ca59a76bef --- /dev/null +++ b/tests/specs/run/run_coverage/foo.ts @@ -0,0 +1,17 @@ +function foo(a: number, b: number) { + if (a > 0) { + console.log(a); + } else { + console.log(a); + } + + if (b > 0) { + console.log(b); + } else { + console.log(b); + } +} + +const [a = 0, b = 0] = Deno.args; + +foo(+a, +b);