mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 12:19:12 +00:00
feat(cli): add --coverage
flag to deno run
command (#29329)
closes #16440 This PR adds `--coverage` flag to `deno run` command. When the flag is specified, it generates the coverage profile in the directory specified (default is `coverage`). The coverage directory can also be specified from the env var `DENO_COVERAGE_DIR`. --------- Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
parent
bff09506bd
commit
56d7660ee4
9 changed files with 142 additions and 0 deletions
|
@ -336,6 +336,7 @@ pub struct RunFlags {
|
||||||
pub script: String,
|
pub script: String,
|
||||||
pub watch: Option<WatchFlagsWithPaths>,
|
pub watch: Option<WatchFlagsWithPaths>,
|
||||||
pub bare: bool,
|
pub bare: bool,
|
||||||
|
pub coverage_dir: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunFlags {
|
impl RunFlags {
|
||||||
|
@ -345,6 +346,7 @@ impl RunFlags {
|
||||||
script,
|
script,
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: false,
|
bare: false,
|
||||||
|
coverage_dir: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3306,6 +3308,7 @@ fn run_args(command: Command, top_level: bool) -> Command {
|
||||||
})
|
})
|
||||||
.arg(env_file_arg())
|
.arg(env_file_arg())
|
||||||
.arg(no_code_cache_arg())
|
.arg(no_code_cache_arg())
|
||||||
|
.arg(coverage_arg())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_subcommand() -> Command {
|
fn run_subcommand() -> Command {
|
||||||
|
@ -4475,6 +4478,21 @@ fn no_code_cache_arg() -> Arg {
|
||||||
.action(ArgAction::SetTrue)
|
.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/'.
|
||||||
|
<p(245)>This option can also be set via the DENO_COVERAGE_DIR environment variable."))
|
||||||
|
.value_hint(ValueHint::AnyPath)
|
||||||
|
}
|
||||||
|
|
||||||
fn permit_no_files_arg() -> Arg {
|
fn permit_no_files_arg() -> Arg {
|
||||||
Arg::new("permit-no-files")
|
Arg::new("permit-no-files")
|
||||||
.long("permit-no-files")
|
.long("permit-no-files")
|
||||||
|
@ -5600,6 +5618,7 @@ fn run_parse(
|
||||||
ext_arg_parse(flags, matches);
|
ext_arg_parse(flags, matches);
|
||||||
|
|
||||||
flags.code_cache_enabled = !matches.get_flag("no-code-cache");
|
flags.code_cache_enabled = !matches.get_flag("no-code-cache");
|
||||||
|
let coverage_dir = matches.remove_one::<String>("coverage");
|
||||||
|
|
||||||
if let Some(mut script_arg) = matches.remove_many::<String>("script_arg") {
|
if let Some(mut script_arg) = matches.remove_many::<String>("script_arg") {
|
||||||
let script = script_arg.next().unwrap();
|
let script = script_arg.next().unwrap();
|
||||||
|
@ -5608,6 +5627,7 @@ fn run_parse(
|
||||||
script,
|
script,
|
||||||
watch: watch_arg_parse_with_paths(matches)?,
|
watch: watch_arg_parse_with_paths(matches)?,
|
||||||
bare,
|
bare,
|
||||||
|
coverage_dir,
|
||||||
});
|
});
|
||||||
} else if bare {
|
} else if bare {
|
||||||
return Err(app.override_usage("deno [OPTIONS] [COMMAND] [SCRIPT_ARG]...").error(
|
return Err(app.override_usage("deno [OPTIONS] [COMMAND] [SCRIPT_ARG]...").error(
|
||||||
|
@ -6566,6 +6586,7 @@ mod tests {
|
||||||
exclude: vec![],
|
exclude: vec![],
|
||||||
}),
|
}),
|
||||||
bare: false,
|
bare: false,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -6591,6 +6612,7 @@ mod tests {
|
||||||
exclude: vec![],
|
exclude: vec![],
|
||||||
}),
|
}),
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -6617,6 +6639,7 @@ mod tests {
|
||||||
exclude: vec![],
|
exclude: vec![],
|
||||||
}),
|
}),
|
||||||
bare: false,
|
bare: false,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -6643,6 +6666,7 @@ mod tests {
|
||||||
exclude: vec![],
|
exclude: vec![],
|
||||||
}),
|
}),
|
||||||
bare: false,
|
bare: false,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -6669,6 +6693,7 @@ mod tests {
|
||||||
exclude: vec![],
|
exclude: vec![],
|
||||||
}),
|
}),
|
||||||
bare: false,
|
bare: false,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -6696,6 +6721,7 @@ mod tests {
|
||||||
exclude: vec![],
|
exclude: vec![],
|
||||||
}),
|
}),
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -6726,6 +6752,7 @@ mod tests {
|
||||||
exclude: vec![],
|
exclude: vec![],
|
||||||
}),
|
}),
|
||||||
bare: false,
|
bare: false,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -6755,6 +6782,7 @@ mod tests {
|
||||||
exclude: vec![String::from("foo")],
|
exclude: vec![String::from("foo")],
|
||||||
}),
|
}),
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -6781,6 +6809,7 @@ mod tests {
|
||||||
exclude: vec![String::from("bar")],
|
exclude: vec![String::from("bar")],
|
||||||
}),
|
}),
|
||||||
bare: false,
|
bare: false,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -6808,6 +6837,7 @@ mod tests {
|
||||||
exclude: vec![String::from("foo"), String::from("bar")],
|
exclude: vec![String::from("foo"), String::from("bar")],
|
||||||
}),
|
}),
|
||||||
bare: false,
|
bare: false,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -6834,6 +6864,7 @@ mod tests {
|
||||||
exclude: vec![String::from("baz"), String::from("qux"),],
|
exclude: vec![String::from("baz"), String::from("qux"),],
|
||||||
}),
|
}),
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
..Flags::default()
|
..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]
|
#[test]
|
||||||
fn run_v8_flags() {
|
fn run_v8_flags() {
|
||||||
let r = flags_from_vec(svec!["deno", "run", "--v8-flags=--help"]);
|
let r = flags_from_vec(svec!["deno", "run", "--v8-flags=--help"]);
|
||||||
|
@ -7170,6 +7219,7 @@ mod tests {
|
||||||
script: "gist.ts".to_string(),
|
script: "gist.ts".to_string(),
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
permissions: PermissionFlags {
|
permissions: PermissionFlags {
|
||||||
deny_read: Some(vec![]),
|
deny_read: Some(vec![]),
|
||||||
|
@ -8456,6 +8506,7 @@ mod tests {
|
||||||
script: "script.ts".to_string(),
|
script: "script.ts".to_string(),
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
permissions: PermissionFlags {
|
permissions: PermissionFlags {
|
||||||
deny_net: Some(svec!["127.0.0.1"]),
|
deny_net: Some(svec!["127.0.0.1"]),
|
||||||
|
@ -8643,6 +8694,7 @@ mod tests {
|
||||||
script: "script.ts".to_string(),
|
script: "script.ts".to_string(),
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
permissions: PermissionFlags {
|
permissions: PermissionFlags {
|
||||||
deny_sys: Some(svec!["hostname"]),
|
deny_sys: Some(svec!["hostname"]),
|
||||||
|
@ -8942,6 +8994,7 @@ mod tests {
|
||||||
script: "script.ts".to_string(),
|
script: "script.ts".to_string(),
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
}
|
}
|
||||||
|
@ -9252,6 +9305,7 @@ mod tests {
|
||||||
script: "script.ts".to_string(),
|
script: "script.ts".to_string(),
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
log_level: Some(Level::Error),
|
log_level: Some(Level::Error),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
|
@ -9372,6 +9426,7 @@ mod tests {
|
||||||
script: "script.ts".to_string(),
|
script: "script.ts".to_string(),
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
type_check_mode: TypeCheckMode::None,
|
type_check_mode: TypeCheckMode::None,
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
|
@ -9540,6 +9595,7 @@ mod tests {
|
||||||
script: "script.ts".to_string(),
|
script: "script.ts".to_string(),
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
node_modules_dir: Some(NodeModulesDirMode::Auto),
|
node_modules_dir: Some(NodeModulesDirMode::Auto),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
|
@ -10763,6 +10819,7 @@ mod tests {
|
||||||
script: "foo.js".to_string(),
|
script: "foo.js".to_string(),
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
inspect_wait: Some("127.0.0.1:9229".parse().unwrap()),
|
inspect_wait: Some("127.0.0.1:9229".parse().unwrap()),
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
|
@ -11453,6 +11510,7 @@ mod tests {
|
||||||
script: "script.ts".to_string(),
|
script: "script.ts".to_string(),
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
type_check_mode: TypeCheckMode::None,
|
type_check_mode: TypeCheckMode::None,
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
|
@ -12006,6 +12064,7 @@ mod tests {
|
||||||
script: "script.ts".to_string(),
|
script: "script.ts".to_string(),
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: true,
|
bare: true,
|
||||||
|
coverage_dir: None,
|
||||||
}),
|
}),
|
||||||
config_flag: ConfigFlag::Disabled,
|
config_flag: ConfigFlag::Disabled,
|
||||||
code_cache_enabled: true,
|
code_cache_enabled: true,
|
||||||
|
|
|
@ -762,6 +762,11 @@ impl CliOptions {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(ToOwned::to_owned)
|
.map(ToOwned::to_owned)
|
||||||
.or_else(|| env::var("DENO_COVERAGE_DIR").ok()),
|
.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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -579,6 +579,7 @@ fn filter_coverages(
|
||||||
|| e.url.starts_with("data:")
|
|| e.url.starts_with("data:")
|
||||||
|| e.url.ends_with("__anonymous__")
|
|| e.url.ends_with("__anonymous__")
|
||||||
|| e.url.ends_with("$deno$test.mjs")
|
|| e.url.ends_with("$deno$test.mjs")
|
||||||
|
|| e.url.ends_with("$deno$stdin.mts")
|
||||||
|| e.url.ends_with(".snap")
|
|| e.url.ends_with(".snap")
|
||||||
|| is_supported_test_path(Path::new(e.url.as_str()))
|
|| is_supported_test_path(Path::new(e.url.as_str()))
|
||||||
|| doc_test_re.is_match(e.url.as_str())
|
|| doc_test_re.is_match(e.url.as_str())
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub async fn deploy(
|
||||||
),
|
),
|
||||||
watch: None,
|
watch: None,
|
||||||
bare: false,
|
bare: false,
|
||||||
|
coverage_dir: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
tools::run::run_script(
|
tools::run::run_script(
|
||||||
|
|
36
tests/specs/run/run_coverage/__test__.jsonc
Normal file
36
tests/specs/run/run_coverage/__test__.jsonc
Normal file
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
tests/specs/run/run_coverage/child_process_coverage_test.out
Normal file
13
tests/specs/run/run_coverage/child_process_coverage_test.out
Normal file
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
4
tests/specs/run/run_coverage/coverage_summary.out
Normal file
4
tests/specs/run/run_coverage/coverage_summary.out
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
| File | Branch % | Line % |
|
||||||
|
| --------- | -------- | ------ |
|
||||||
|
| foo.ts | 0.0 | 57.1 |
|
||||||
|
| All files | 0.0 | 57.1 |
|
17
tests/specs/run/run_coverage/foo.ts
Normal file
17
tests/specs/run/run_coverage/foo.ts
Normal file
|
@ -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);
|
Loading…
Add table
Add a link
Reference in a new issue