Skip to content
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

feat(cli): run test modules in parallel #9815

Merged
merged 100 commits into from
Apr 28, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
0d8d955
Run test modules in seperate isolates
caspervonb Mar 16, 2021
09cb0a1
Add test messages and dispatching
caspervonb Mar 17, 2021
ec78030
Run test modules in parallel
caspervonb Mar 17, 2021
57cfdc1
Pretty print and handle exits
caspervonb Mar 17, 2021
499d08a
Fix test case
caspervonb Mar 17, 2021
9126b3e
Add back yanked comment
caspervonb Mar 18, 2021
9149b6b
Move to avoid holding on to sender outside of test runners
caspervonb Mar 19, 2021
313c472
Make most tests pass
caspervonb Mar 19, 2021
53db453
Implement only filter
caspervonb Mar 19, 2021
1d919fa
Fix fail fast panic
caspervonb Mar 19, 2021
14a9709
Disable unresolved promise test case
caspervonb Mar 19, 2021
3e5294e
Merge branch 'main' into feat-run-test-modules-in-parallel
caspervonb Mar 19, 2021
c14a1c0
Convert unit tests to plain deno tests and make them pass
caspervonb Mar 19, 2021
84f2e76
Lint
caspervonb Mar 19, 2021
e6ed8c3
Fixup perm test
caspervonb Mar 20, 2021
4bbeb70
Bump CI
caspervonb Mar 20, 2021
bcf1f22
Bump CI
caspervonb Mar 20, 2021
5f746f7
Bump CI
caspervonb Mar 20, 2021
c83adee
Try canonical paths
caspervonb Mar 20, 2021
0d0f3a2
Temporarily disable chdir tests
caspervonb Mar 20, 2021
a8e92c7
Debug failing case on windows
caspervonb Mar 20, 2021
1949369
Revert canonicalization of current_dir
caspervonb Mar 20, 2021
24dd8a9
Revert "Debug failing case on windows"
caspervonb Mar 20, 2021
8396be6
Add --concurrent for some quick benchmarks (replace with --jobs)
caspervonb Mar 20, 2021
864b4bf
Revert "Add --concurrent for some quick benchmarks (replace with --jo…
caspervonb Mar 20, 2021
5a57d4e
Add make style --jobs option
caspervonb Mar 21, 2021
8d28c07
Expose registered tests in internals
caspervonb Mar 21, 2021
19962c5
Add back support for suppressing console with --quiet
caspervonb Mar 21, 2021
1bc59ef
Track total elapsed time
caspervonb Mar 21, 2021
4e0254c
Remove unused variable
caspervonb Mar 21, 2021
48b6f8c
Merge branch 'main' into feat-run-test-modules-in-parallel
caspervonb Mar 21, 2021
ee1f7d2
Format
caspervonb Mar 21, 2021
50ea306
Allow too_many_arguments
caspervonb Mar 21, 2021
93300b1
Re-enable chdir tests
caspervonb Mar 21, 2021
c4e836c
Add unhandled rejection test case
caspervonb Mar 22, 2021
69a3197
Handle join errors and add unhandled rejection test
caspervonb Mar 25, 2021
b3dc290
Merge branch 'main' into feat-run-test-modules-in-parallel
caspervonb Apr 5, 2021
7508a07
Merge branch 'main' into feat-run-test-modules-in-parallel
caspervonb Apr 5, 2021
2cc1a26
Merge branch 'main' into feat-run-test-modules-in-parallel
bartlomieju Apr 7, 2021
407cba3
Add validator for jobs
caspervonb Apr 8, 2021
e6f0505
Bump CI
caspervonb Apr 8, 2021
cc9a051
Merge branch 'main' into feat-run-test-modules-in-parallel
caspervonb Apr 8, 2021
464c8c4
Invalidate cache
caspervonb Apr 8, 2021
0b427d9
Merge branch 'main' into feat-run-test-modules-in-parallel
caspervonb Apr 9, 2021
1e75812
Merge branch 'main' into feat-run-test-modules-in-parallel
caspervonb Apr 9, 2021
39c698f
Cache bust, again
caspervonb Apr 9, 2021
f6fb631
Fixup bad merge
caspervonb Apr 9, 2021
e536f6a
Revert "Cache bust, again"
caspervonb Apr 9, 2021
4817db8
Merge branch 'main' into feat-run-test-modules-in-parallel
caspervonb Apr 9, 2021
f6f064e
Merge branch 'main' into feat-run-test-modules-in-parallel
bartlomieju Apr 9, 2021
337a156
Merge branch 'main' into feat-run-test-modules-in-parallel
caspervonb Apr 11, 2021
9f96ab7
Fixup bad merge
caspervonb Apr 11, 2021
7fbc519
feat(cli): add test permissions to Deno.TestDefinition
caspervonb Apr 14, 2021
83a4469
Prefer const
caspervonb Apr 14, 2021
edf2c1a
Tag comment
caspervonb Apr 14, 2021
39caacd
checkout main -- cli/tests/unit
caspervonb Apr 14, 2021
3f81fa4
Merge branch 'feat-add-test-permissions' into feat-run-test-modules-i…
caspervonb Apr 14, 2021
71cee3b
Better permission denied tests
caspervonb Apr 14, 2021
c6a4906
Fixup hrtime argument
caspervonb Apr 14, 2021
c3115f8
Fixup test
caspervonb Apr 14, 2021
917f5cb
Better test coverage
caspervonb Apr 14, 2021
e26d2b4
Merge branch 'feat-add-test-permissions' into feat-run-test-modules-i…
caspervonb Apr 14, 2021
6c8baac
Default to unit test permissions being false
caspervonb Apr 14, 2021
3fc989a
git rm cli/tests/unit/filter_function_test.ts
caspervonb Apr 14, 2021
5e873e1
Require explicit initialization of test ops
caspervonb Apr 14, 2021
587eb56
s/request/pledge/
caspervonb Apr 14, 2021
f9191aa
s/restore_test_permissions/permissions/
caspervonb Apr 14, 2021
65e2549
Format
caspervonb Apr 15, 2021
8fc8eed
s/requestTestPermissions/pledgeTestPermissions
caspervonb Apr 15, 2021
641bacf
Merge branch 'feat-add-test-permissions' into feat-run-test-modules-i…
caspervonb Apr 15, 2021
2a469aa
Lint
caspervonb Apr 15, 2021
5c9ccfe
Merge branch 'feat-add-test-permissions' into feat-run-test-modules-i…
caspervonb Apr 15, 2021
60fe552
Always pledge permissions
caspervonb Apr 15, 2021
de1005e
Move the meat into cli/tools/test_runner.rs
caspervonb Apr 15, 2021
8da2a3b
git checkout main -- cli/dts/lib.deno.ns.d.ts
caspervonb Apr 27, 2021
88d1ac6
Merge branch 'main' into feat-run-test-modules-in-parallel
caspervonb Apr 27, 2021
c6445ee
Don't leak internal test filter
caspervonb Apr 27, 2021
24cb0a8
s/quiet/disableLog
caspervonb Apr 27, 2021
af1e9a4
Move test messages into test_runner.rs
caspervonb Apr 27, 2021
a3e0548
Re-enable failing test
caspervonb Apr 27, 2021
66c4cf7
Run tests in a module to catch unresolved promises
caspervonb Apr 27, 2021
60573ea
Remove redundant clone
caspervonb Apr 27, 2021
8896e2c
Update expectation
caspervonb Apr 27, 2021
429341f
s/run_test/run_test_file/g
caspervonb Apr 27, 2021
536c909
Report incomplete runs as FAILED
caspervonb Apr 27, 2021
0c2e8c3
Merge op modules into ops::testing
caspervonb Apr 27, 2021
e313104
Format
caspervonb Apr 27, 2021
3bba657
Remove run_event_loop call
caspervonb Apr 27, 2021
a2bea9c
Bump CI
caspervonb Apr 27, 2021
a8ca46e
Bump CI
caspervonb Apr 27, 2021
d26c0c5
Avoid stdout races
caspervonb Apr 27, 2021
a48a2fd
Don't expose tests to Deno.internal (for now at-least)
caspervonb Apr 27, 2021
6427a06
s/sendTestMessage/postTestMessage
caspervonb Apr 27, 2021
c3a342a
Stream pending tests when concurrency == 1
caspervonb Apr 27, 2021
5b3ebe0
Add tested modules as comments to invalidate
caspervonb Apr 27, 2021
fe47c1f
Format
caspervonb Apr 27, 2021
39df69b
Fix newline in test expectation
caspervonb Apr 27, 2021
a7fa141
Rename .ts to .js to avoid accidental cache overlap
caspervonb Apr 27, 2021
49ac699
Revert "Add tested modules as comments to invalidate"
caspervonb Apr 27, 2021
7a6b8e2
Merge branch 'main' into feat-run-test-modules-in-parallel
bartlomieju Apr 28, 2021
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
208 changes: 154 additions & 54 deletions cli/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.

#![deny(warnings)]
// #![deny(warnings)]
caspervonb marked this conversation as resolved.
Show resolved Hide resolved

#[macro_use]
extern crate lazy_static;
Expand Down Expand Up @@ -36,6 +36,7 @@ mod program_state;
mod source_maps;
mod specifier_handler;
mod standalone;
mod test_dispatcher;
mod text_encoding;
mod tokio_util;
mod tools;
Expand All @@ -54,9 +55,12 @@ use crate::program_state::exit_unstable;
use crate::program_state::ProgramState;
use crate::source_maps::apply_source_map;
use crate::specifier_handler::FetchHandler;
use crate::test_dispatcher::TestMessage;
use crate::test_dispatcher::TestResult;
use crate::tools::installer::infer_name_from_url;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::futures::future;
use deno_core::futures::future::FutureExt;
use deno_core::futures::Future;
use deno_core::resolve_url_or_path;
Expand All @@ -79,6 +83,8 @@ use std::iter::once;
use std::path::PathBuf;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::mpsc::channel;
use std::sync::mpsc::Sender;
use std::sync::Arc;
use std::sync::Mutex;

Expand Down Expand Up @@ -218,6 +224,7 @@ pub fn create_main_worker(
// above
ops::errors::init(js_runtime);
ops::runtime_compiler::init(js_runtime);
ops::test_dispatcher::init(js_runtime);
}
worker.bootstrap(&options);

Expand Down Expand Up @@ -896,6 +903,57 @@ async fn coverage_command(
.await
}

async fn run_test(
program_state: Arc<ProgramState>,
main_module: ModuleSpecifier,
permissions: Permissions,
channel: Sender<TestMessage>,
filter: Option<String>,
) -> Result<(), AnyError> {
let mut worker =
create_main_worker(&program_state, main_module.clone(), permissions);

{
let js_runtime = &mut worker.js_runtime;
js_runtime
.op_state()
.borrow_mut()
.put::<Sender<TestMessage>>(channel.clone());
}

let mut maybe_coverage_collector =
if let Some(ref coverage_dir) = program_state.coverage_dir {
let session = worker.create_inspector_session();
let coverage_dir = PathBuf::from(coverage_dir);
let mut coverage_collector =
tools::coverage::CoverageCollector::new(coverage_dir, session);
coverage_collector.start_collecting().await?;

Some(coverage_collector)
} else {
None
};

let options = json!({
"filter": filter,
});

let execute_result = worker.execute_module(&main_module).await;
execute_result?;
worker.execute("window.dispatchEvent(new Event('load'))")?;
worker.execute(&format!("Deno[Deno.internal].runTests({})", options))?;

worker.run_event_loop().await?;
worker.execute("window.dispatchEvent(new Event('unload'))")?;
worker.run_event_loop().await?;

if let Some(coverage_collector) = maybe_coverage_collector.as_mut() {
coverage_collector.stop_collecting().await?;
}

Ok(())
}

async fn test_command(
caspervonb marked this conversation as resolved.
Show resolved Hide resolved
flags: Flags,
include: Option<Vec<String>>,
Expand All @@ -910,7 +968,7 @@ async fn test_command(
let cwd = std::env::current_dir().expect("No current directory");
let include = include.unwrap_or_else(|| vec![".".to_string()]);
let test_modules =
tools::test_runner::prepare_test_modules_urls(include, &cwd)?;
tools::test_runner::collect_test_module_specifiers(include, &cwd)?;

if test_modules.is_empty() {
println!("No matching test modules found");
Expand All @@ -919,71 +977,113 @@ async fn test_command(
}
return Ok(());
}
let main_module = deno_core::resolve_path("$deno$test.ts")?;
// Create a dummy source file.
let source_file = File {
local: main_module.to_file_path().unwrap(),
maybe_types: None,
media_type: MediaType::TypeScript,
source: tools::test_runner::render_test_file(
test_modules.clone(),
fail_fast,
quiet,
filter,
),
specifier: main_module.clone(),

let lib = if flags.unstable {
module_graph::TypeLib::UnstableDenoWindow
} else {
module_graph::TypeLib::DenoWindow
};
// Save our fake file into file fetcher cache
// to allow module access by TS compiler
program_state.file_fetcher.insert_cached(source_file);

program_state
.prepare_module_graph(
test_modules.clone(),
lib.clone(),
permissions.clone(),
program_state.maybe_import_map.clone(),
)
.await?;

if no_run {
let lib = if flags.unstable {
module_graph::TypeLib::UnstableDenoWindow
} else {
module_graph::TypeLib::DenoWindow
};
program_state
.prepare_module_load(
main_module.clone(),
lib,
Permissions::allow_all(),
false,
program_state.maybe_import_map.clone(),
)
.await?;
return Ok(());
}

let mut worker =
create_main_worker(&program_state, main_module.clone(), permissions);

if let Some(ref coverage_dir) = flags.coverage_dir {
env::set_var("DENO_UNSTABLE_COVERAGE_DIR", coverage_dir);
}

let mut maybe_coverage_collector =
if let Some(ref coverage_dir) = program_state.coverage_dir {
let session = worker.create_inspector_session();
let coverage_dir = PathBuf::from(coverage_dir);
let mut coverage_collector =
tools::coverage::CoverageCollector::new(coverage_dir, session);
coverage_collector.start_collecting().await?;
let (sender, receiver) = channel::<TestMessage>();

let join_handles = test_modules.iter().map(|module_specifier| {
let program_state = program_state.clone();
let module_specifier = module_specifier.clone();
let permissions = permissions.clone();
let sender = sender.clone();
let filter = filter.clone();

tokio::task::spawn_blocking(move || {
let join_handle = std::thread::spawn(move || {
let future = run_test(
program_state.clone(),
module_specifier.clone(),
permissions.clone(),
sender.clone(),
filter.clone(),
);

Some(coverage_collector)
} else {
None
};
tokio_util::run_basic(future)
});

let execute_result = worker.execute_module(&main_module).await;
execute_result?;
worker.execute("window.dispatchEvent(new Event('load'))")?;
worker.run_event_loop().await?;
worker.execute("window.dispatchEvent(new Event('unload'))")?;
worker.run_event_loop().await?;
join_handle.join()
})
});

if let Some(coverage_collector) = maybe_coverage_collector.as_mut() {
coverage_collector.stop_collecting().await?;
let join_results = future::join_all(join_handles);

let handler = {
let test_modules = test_modules.clone();

tokio::task::spawn_blocking(move || {
let mut has_error = false;

for message in receiver.iter() {
match message {
TestMessage::Result {
name,
duration,
result,
} => match result {
TestResult::Ok => {
println!(
"test {} ... {} {}",
name,
colors::green("ok"),
colors::gray(format!("({}ms)", duration))
);
}
TestResult::Ignored => {
println!(
"test {} ... {} {}",
name,
colors::yellow("ignored"),
colors::gray(format!("({}ms)", duration))
);
}
TestResult::Failed(_) => {
println!(
"test {} ... {} {}",
name,
colors::red("FAILED"),
colors::gray(format!("({}ms)", duration))
);

has_error = true;
}
},
_ => {}
}

if has_error && fail_fast {
break;
}
}

has_error
})
};

let has_error = handler.await?;
if has_error {
std::process::exit(1);
}

Ok(())
Expand Down
1 change: 1 addition & 0 deletions cli/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

pub mod errors;
pub mod runtime_compiler;
pub mod test_dispatcher;

use deno_core::error::AnyError;
use deno_core::json_op_async;
Expand Down
40 changes: 40 additions & 0 deletions cli/ops/test_dispatcher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::test_dispatcher::TestMessage;
caspervonb marked this conversation as resolved.
Show resolved Hide resolved
use deno_core::error::AnyError;
use deno_core::json_op_async;
use deno_core::json_op_sync;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::BufVec;
use deno_core::JsRuntime;
use deno_core::OpState;
use deno_core::ZeroCopyBuf;
use serde::Deserialize;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::mpsc::Sender;
use std::sync::Arc;
use std::sync::Mutex;

pub fn init(rt: &mut deno_core::JsRuntime) {
super::reg_json_sync(rt, "op_send_test_message", op_send_test_message);
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct SendTestMessageArgs {
message: TestMessage,
}

fn op_send_test_message(
state: &mut OpState,
args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
let args: SendTestMessageArgs = serde_json::from_value(args)?;
let sender = state.borrow::<Sender<TestMessage>>().clone();

sender.send(args.message);

Ok(json!({}))
caspervonb marked this conversation as resolved.
Show resolved Hide resolved
}
Loading