1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

refactor: clean up watcher tests (#12200)

This commit is contained in:
Ryan Dahl 2021-09-23 15:12:22 -04:00 committed by GitHub
parent c5442abc23
commit da25bbff88
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,10 +1,20 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use flaky_test::flaky_test; use flaky_test::flaky_test;
use std::fs::write;
use std::io::BufRead; use std::io::BufRead;
use tempfile::TempDir; use tempfile::TempDir;
use test_util as util; use test_util as util;
macro_rules! assert_contains {
($string:expr, $($test:expr),+) => {
let string = $string; // This might be a function call or something
if !($(string.contains($test))||+) {
panic!("{:?} does not contain any of {:?}", string, [$($test),+]);
}
}
}
// Helper function to skip watcher output that contains "Restarting" // Helper function to skip watcher output that contains "Restarting"
// phrase. // phrase.
fn skip_restarting_line( fn skip_restarting_line(
@ -18,45 +28,40 @@ fn skip_restarting_line(
} }
} }
/// Helper function to skip watcher output that doesn't contain fn wait_for(s: &str, lines: &mut impl Iterator<Item = String>) {
/// "{job_name} finished" phrase.
fn wait_for_process_finished(
job_name: &str,
stderr_lines: &mut impl Iterator<Item = String>,
) {
let phrase = format!("{} finished", job_name);
loop { loop {
let msg = stderr_lines.next().unwrap(); let msg = lines.next().unwrap();
if msg.contains(&phrase) { if msg.contains(s) {
break; break;
} }
} }
} }
/// Helper function to skip watcher output that doesn't contain fn check_alive_then_kill(mut child: std::process::Child) {
/// "{job_name} failed" phrase. assert!(child.try_wait().unwrap().is_none());
fn wait_for_process_failed( child.kill().unwrap();
job_name: &str, }
stderr_lines: &mut impl Iterator<Item = String>,
) { fn child_lines(
let phrase = format!("{} failed", job_name); child: &mut std::process::Child,
loop { ) -> (impl Iterator<Item = String>, impl Iterator<Item = String>) {
let msg = stderr_lines.next().unwrap(); let stdout_lines = std::io::BufReader::new(child.stdout.take().unwrap())
if msg.contains(&phrase) { .lines()
break; .map(|r| r.unwrap());
} let stderr_lines = std::io::BufReader::new(child.stderr.take().unwrap())
} .lines()
.map(|r| r.unwrap());
(stdout_lines, stderr_lines)
} }
#[test] #[test]
fn fmt_watch_test() { fn fmt_watch_test() {
let t = TempDir::new().expect("tempdir fail"); let t = TempDir::new().unwrap();
let fixed = util::testdata_path().join("badly_formatted_fixed.js"); let fixed = util::testdata_path().join("badly_formatted_fixed.js");
let badly_formatted_original = let badly_formatted_original =
util::testdata_path().join("badly_formatted.mjs"); util::testdata_path().join("badly_formatted.mjs");
let badly_formatted = t.path().join("badly_formatted.js"); let badly_formatted = t.path().join("badly_formatted.js");
std::fs::copy(&badly_formatted_original, &badly_formatted) std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap();
.expect("Failed to copy file");
let mut child = util::deno_cmd() let mut child = util::deno_cmd()
.current_dir(util::testdata_path()) .current_dir(util::testdata_path())
@ -67,10 +72,8 @@ fn fmt_watch_test() {
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())
.spawn() .spawn()
.expect("Failed to spawn script"); .unwrap();
let stderr = child.stderr.as_mut().unwrap(); let (_stdout_lines, stderr_lines) = child_lines(&mut child);
let stderr_lines =
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
// TODO(lucacasonato): remove this timeout. It seems to be needed on Linux. // TODO(lucacasonato): remove this timeout. It seems to be needed on Linux.
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
@ -82,32 +85,25 @@ fn fmt_watch_test() {
assert_eq!(expected, actual); assert_eq!(expected, actual);
// Change content of the file again to be badly formatted // Change content of the file again to be badly formatted
std::fs::copy(&badly_formatted_original, &badly_formatted) std::fs::copy(&badly_formatted_original, &badly_formatted).unwrap();
.expect("Failed to copy file");
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
// Check if file has been automatically formatted by watcher // Check if file has been automatically formatted by watcher
let expected = std::fs::read_to_string(fixed).unwrap(); let expected = std::fs::read_to_string(fixed).unwrap();
let actual = std::fs::read_to_string(badly_formatted).unwrap(); let actual = std::fs::read_to_string(badly_formatted).unwrap();
assert_eq!(expected, actual); assert_eq!(expected, actual);
check_alive_then_kill(child);
// the watcher process is still alive
assert!(child.try_wait().unwrap().is_none());
child.kill().unwrap();
drop(t);
} }
#[test] #[test]
fn bundle_js_watch() { fn bundle_js_watch() {
use std::path::PathBuf; use std::path::PathBuf;
// Test strategy extends this of test bundle_js by adding watcher // Test strategy extends this of test bundle_js by adding watcher
let t = TempDir::new().expect("tempdir fail"); let t = TempDir::new().unwrap();
let file_to_watch = t.path().join("file_to_watch.js"); let file_to_watch = t.path().join("file_to_watch.js");
std::fs::write(&file_to_watch, "console.log('Hello world');") write(&file_to_watch, "console.log('Hello world');").unwrap();
.expect("error writing file");
assert!(file_to_watch.is_file()); assert!(file_to_watch.is_file());
let t = TempDir::new().expect("tempdir fail"); let t = TempDir::new().unwrap();
let bundle = t.path().join("mod6.bundle.js"); let bundle = t.path().join("mod6.bundle.js");
let mut deno = util::deno_cmd() let mut deno = util::deno_cmd()
.current_dir(util::testdata_path()) .current_dir(util::testdata_path())
@ -120,57 +116,41 @@ fn bundle_js_watch() {
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())
.spawn() .spawn()
.expect("failed to spawn script"); .unwrap();
let stderr = deno.stderr.as_mut().unwrap(); let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno);
let mut stderr_lines =
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines.next().unwrap().contains("file_to_watch.js")); assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.js");
assert!(stderr_lines.next().unwrap().contains("mod6.bundle.js")); assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
let file = PathBuf::from(&bundle); let file = PathBuf::from(&bundle);
assert!(file.is_file()); assert!(file.is_file());
wait_for_process_finished("Bundle", &mut stderr_lines); wait_for("Bundle finished", &mut stderr_lines);
std::fs::write(&file_to_watch, "console.log('Hello world2');") write(&file_to_watch, "console.log('Hello world2');").unwrap();
.expect("error writing file");
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines assert_contains!(stderr_lines.next().unwrap(), "File change detected!");
.next() assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.js");
.unwrap() assert_contains!(stderr_lines.next().unwrap(), "mod6.bundle.js");
.contains("File change detected!"));
assert!(stderr_lines.next().unwrap().contains("file_to_watch.js"));
assert!(stderr_lines.next().unwrap().contains("mod6.bundle.js"));
let file = PathBuf::from(&bundle); let file = PathBuf::from(&bundle);
assert!(file.is_file()); assert!(file.is_file());
wait_for_process_finished("Bundle", &mut stderr_lines); wait_for("Bundle finished", &mut stderr_lines);
// Confirm that the watcher keeps on working even if the file is updated and has invalid syntax // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
std::fs::write(&file_to_watch, "syntax error ^^") write(&file_to_watch, "syntax error ^^").unwrap();
.expect("error writing file");
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines assert_contains!(stderr_lines.next().unwrap(), "File change detected!");
.next() assert_contains!(stderr_lines.next().unwrap(), "error: ");
.unwrap() wait_for("Bundle failed", &mut stderr_lines);
.contains("File change detected!")); check_alive_then_kill(deno);
assert!(stderr_lines.next().unwrap().contains("error: "));
wait_for_process_failed("Bundle", &mut stderr_lines);
// the watcher process is still alive
assert!(deno.try_wait().unwrap().is_none());
deno.kill().unwrap();
drop(t);
} }
/// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt /// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt
#[test] #[test]
fn bundle_watch_not_exit() { fn bundle_watch_not_exit() {
let t = TempDir::new().expect("tempdir fail"); let t = TempDir::new().unwrap();
let file_to_watch = t.path().join("file_to_watch.js"); let file_to_watch = t.path().join("file_to_watch.js");
std::fs::write(&file_to_watch, "syntax error ^^") write(&file_to_watch, "syntax error ^^").unwrap();
.expect("error writing file");
let target_file = t.path().join("target.js"); let target_file = t.path().join("target.js");
let mut deno = util::deno_cmd() let mut deno = util::deno_cmd()
@ -184,45 +164,32 @@ fn bundle_watch_not_exit() {
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())
.spawn() .spawn()
.expect("failed to spawn script"); .unwrap();
let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno);
let stderr = deno.stderr.as_mut().unwrap();
let mut stderr_lines =
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines.next().unwrap().contains("error:")); assert_contains!(stderr_lines.next().unwrap(), "error:");
assert!(stderr_lines.next().unwrap().contains("Bundle failed")); assert_contains!(stderr_lines.next().unwrap(), "Bundle failed");
// the target file hasn't been created yet // the target file hasn't been created yet
assert!(!target_file.is_file()); assert!(!target_file.is_file());
// Make sure the watcher actually restarts and works fine with the proper syntax // Make sure the watcher actually restarts and works fine with the proper syntax
std::fs::write(&file_to_watch, "console.log(42);") write(&file_to_watch, "console.log(42);").unwrap();
.expect("error writing file");
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines assert_contains!(stderr_lines.next().unwrap(), "File change detected!");
.next() assert_contains!(stderr_lines.next().unwrap(), "file_to_watch.js");
.unwrap() assert_contains!(stderr_lines.next().unwrap(), "target.js");
.contains("File change detected!")); wait_for("Bundle finished", &mut stderr_lines);
assert!(stderr_lines.next().unwrap().contains("file_to_watch.js"));
assert!(stderr_lines.next().unwrap().contains("target.js"));
wait_for_process_finished("Bundle", &mut stderr_lines);
// bundled file is created // bundled file is created
assert!(target_file.is_file()); assert!(target_file.is_file());
check_alive_then_kill(deno);
// the watcher process is still alive
assert!(deno.try_wait().unwrap().is_none());
deno.kill().unwrap();
drop(t);
} }
#[flaky_test::flaky_test] #[flaky_test::flaky_test]
fn run_watch() { fn run_watch() {
let t = TempDir::new().expect("tempdir fail"); let t = TempDir::new().unwrap();
let file_to_watch = t.path().join("file_to_watch.js"); let file_to_watch = t.path().join("file_to_watch.js");
std::fs::write(&file_to_watch, "console.log('Hello world');") write(&file_to_watch, "console.log('Hello world');").unwrap();
.expect("error writing file");
let mut child = util::deno_cmd() let mut child = util::deno_cmd()
.current_dir(util::testdata_path()) .current_dir(util::testdata_path())
@ -234,99 +201,83 @@ fn run_watch() {
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())
.spawn() .spawn()
.expect("failed to spawn script"); .unwrap();
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
let stdout = child.stdout.as_mut().unwrap(); assert_contains!(stdout_lines.next().unwrap(), "Hello world");
let mut stdout_lines = wait_for("Process finished", &mut stderr_lines);
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
let stderr = child.stderr.as_mut().unwrap();
let mut stderr_lines =
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
assert!(stdout_lines.next().unwrap().contains("Hello world"));
wait_for_process_finished("Process", &mut stderr_lines);
// TODO(lucacasonato): remove this timeout. It seems to be needed on Linux. // TODO(lucacasonato): remove this timeout. It seems to be needed on Linux.
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
// Change content of the file // Change content of the file
std::fs::write(&file_to_watch, "console.log('Hello world2');") write(&file_to_watch, "console.log('Hello world2');").unwrap();
.expect("error writing file");
// Events from the file watcher is "debounced", so we need to wait for the next execution to start // Events from the file watcher is "debounced", so we need to wait for the next execution to start
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines.next().unwrap().contains("Restarting")); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert!(stdout_lines.next().unwrap().contains("Hello world2")); assert_contains!(stdout_lines.next().unwrap(), "Hello world2");
wait_for_process_finished("Process", &mut stderr_lines); wait_for("Process finished", &mut stderr_lines);
// Add dependency // Add dependency
let another_file = t.path().join("another_file.js"); let another_file = t.path().join("another_file.js");
std::fs::write(&another_file, "export const foo = 0;") write(&another_file, "export const foo = 0;").unwrap();
.expect("error writing file"); write(
std::fs::write(
&file_to_watch, &file_to_watch,
"import { foo } from './another_file.js'; console.log(foo);", "import { foo } from './another_file.js'; console.log(foo);",
) )
.expect("error writing file"); .unwrap();
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines.next().unwrap().contains("Restarting")); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert!(stdout_lines.next().unwrap().contains('0')); assert_contains!(stdout_lines.next().unwrap(), '0');
wait_for_process_finished("Process", &mut stderr_lines); wait_for("Process finished", &mut stderr_lines);
// Confirm that restarting occurs when a new file is updated // Confirm that restarting occurs when a new file is updated
std::fs::write(&another_file, "export const foo = 42;") write(&another_file, "export const foo = 42;").unwrap();
.expect("error writing file");
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines.next().unwrap().contains("Restarting")); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert!(stdout_lines.next().unwrap().contains("42")); assert_contains!(stdout_lines.next().unwrap(), "42");
wait_for_process_finished("Process", &mut stderr_lines); wait_for("Process finished", &mut stderr_lines);
// Confirm that the watcher keeps on working even if the file is updated and has invalid syntax // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
std::fs::write(&file_to_watch, "syntax error ^^") write(&file_to_watch, "syntax error ^^").unwrap();
.expect("error writing file");
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines.next().unwrap().contains("Restarting")); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert!(stderr_lines.next().unwrap().contains("error:")); assert_contains!(stderr_lines.next().unwrap(), "error:");
wait_for_process_failed("Process", &mut stderr_lines); wait_for("Process failed", &mut stderr_lines);
// Then restore the file // Then restore the file
std::fs::write( write(
&file_to_watch, &file_to_watch,
"import { foo } from './another_file.js'; console.log(foo);", "import { foo } from './another_file.js'; console.log(foo);",
) )
.expect("error writing file"); .unwrap();
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines.next().unwrap().contains("Restarting")); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert!(stdout_lines.next().unwrap().contains("42")); assert_contains!(stdout_lines.next().unwrap(), "42");
wait_for_process_finished("Process", &mut stderr_lines); wait_for("Process finished", &mut stderr_lines);
// Update the content of the imported file with invalid syntax // Update the content of the imported file with invalid syntax
std::fs::write(&another_file, "syntax error ^^").expect("error writing file"); write(&another_file, "syntax error ^^").unwrap();
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines.next().unwrap().contains("Restarting")); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert!(stderr_lines.next().unwrap().contains("error:")); assert_contains!(stderr_lines.next().unwrap(), "error:");
wait_for_process_failed("Process", &mut stderr_lines); wait_for("Process failed", &mut stderr_lines);
// Modify the imported file and make sure that restarting occurs // Modify the imported file and make sure that restarting occurs
std::fs::write(&another_file, "export const foo = 'modified!';") write(&another_file, "export const foo = 'modified!';").unwrap();
.expect("error writing file");
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines.next().unwrap().contains("Restarting")); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert!(stdout_lines.next().unwrap().contains("modified!")); assert_contains!(stdout_lines.next().unwrap(), "modified!");
wait_for_process_finished("Process", &mut stderr_lines); wait_for("Process finished", &mut stderr_lines);
check_alive_then_kill(child);
// the watcher process is still alive
assert!(child.try_wait().unwrap().is_none());
child.kill().unwrap();
drop(t);
} }
#[test] #[test]
fn run_watch_load_unload_events() { fn run_watch_load_unload_events() {
let t = TempDir::new().expect("tempdir fail"); let t = TempDir::new().unwrap();
let file_to_watch = t.path().join("file_to_watch.js"); let file_to_watch = t.path().join("file_to_watch.js");
std::fs::write( write(
&file_to_watch, &file_to_watch,
r#" r#"
setInterval(() => {}, 0); setInterval(() => {}, 0);
@ -339,7 +290,7 @@ fn run_watch_load_unload_events() {
}); });
"#, "#,
) )
.expect("error writing file"); .unwrap();
let mut child = util::deno_cmd() let mut child = util::deno_cmd()
.current_dir(util::testdata_path()) .current_dir(util::testdata_path())
@ -351,20 +302,14 @@ fn run_watch_load_unload_events() {
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())
.spawn() .spawn()
.expect("failed to spawn script"); .unwrap();
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
let stdout = child.stdout.as_mut().unwrap();
let mut stdout_lines =
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
let stderr = child.stderr.as_mut().unwrap();
let mut stderr_lines =
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
// Wait for the first load event to fire // Wait for the first load event to fire
assert!(stdout_lines.next().unwrap().contains("load")); assert_contains!(stdout_lines.next().unwrap(), "load");
// Change content of the file, this time without an interval to keep it alive. // Change content of the file, this time without an interval to keep it alive.
std::fs::write( write(
&file_to_watch, &file_to_watch,
r#" r#"
window.addEventListener("load", () => { window.addEventListener("load", () => {
@ -376,34 +321,31 @@ fn run_watch_load_unload_events() {
}); });
"#, "#,
) )
.expect("error writing file"); .unwrap();
// Events from the file watcher is "debounced", so we need to wait for the next execution to start // Events from the file watcher is "debounced", so we need to wait for the next execution to start
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
// Wait for the restart // Wait for the restart
assert!(stderr_lines.next().unwrap().contains("Restarting")); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
// Confirm that the unload event was dispatched from the first run // Confirm that the unload event was dispatched from the first run
assert!(stdout_lines.next().unwrap().contains("unload")); assert_contains!(stdout_lines.next().unwrap(), "unload");
// Followed by the load event of the second run // Followed by the load event of the second run
assert!(stdout_lines.next().unwrap().contains("load")); assert_contains!(stdout_lines.next().unwrap(), "load");
// Which is then unloaded as there is nothing keeping it alive. // Which is then unloaded as there is nothing keeping it alive.
assert!(stdout_lines.next().unwrap().contains("unload")); assert_contains!(stdout_lines.next().unwrap(), "unload");
check_alive_then_kill(child);
child.kill().unwrap();
drop(t);
} }
/// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt /// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt
#[test] #[test]
fn run_watch_not_exit() { fn run_watch_not_exit() {
let t = TempDir::new().expect("tempdir fail"); let t = TempDir::new().unwrap();
let file_to_watch = t.path().join("file_to_watch.js"); let file_to_watch = t.path().join("file_to_watch.js");
std::fs::write(&file_to_watch, "syntax error ^^") write(&file_to_watch, "syntax error ^^").unwrap();
.expect("error writing file");
let mut child = util::deno_cmd() let mut child = util::deno_cmd()
.current_dir(util::testdata_path()) .current_dir(util::testdata_path())
@ -415,32 +357,20 @@ fn run_watch_not_exit() {
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())
.spawn() .spawn()
.expect("failed to spawn script"); .unwrap();
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
let stdout = child.stdout.as_mut().unwrap();
let mut stdout_lines =
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
let stderr = child.stderr.as_mut().unwrap();
let mut stderr_lines =
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines.next().unwrap().contains("error:")); assert_contains!(stderr_lines.next().unwrap(), "error:");
assert!(stderr_lines.next().unwrap().contains("Process failed")); assert_contains!(stderr_lines.next().unwrap(), "Process failed");
// Make sure the watcher actually restarts and works fine with the proper syntax // Make sure the watcher actually restarts and works fine with the proper syntax
std::fs::write(&file_to_watch, "console.log(42);") write(&file_to_watch, "console.log(42);").unwrap();
.expect("error writing file");
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(std::time::Duration::from_secs(1));
assert!(stderr_lines.next().unwrap().contains("Restarting")); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert!(stdout_lines.next().unwrap().contains("42")); assert_contains!(stdout_lines.next().unwrap(), "42");
wait_for_process_finished("Process", &mut stderr_lines); wait_for("Process finished", &mut stderr_lines);
check_alive_then_kill(child);
// the watcher process is still alive
assert!(child.try_wait().unwrap().is_none());
child.kill().unwrap();
drop(t);
} }
#[test] #[test]
@ -451,16 +381,15 @@ fn run_watch_with_import_map_and_relative_paths() {
filecontent: &'static str, filecontent: &'static str,
) -> std::path::PathBuf { ) -> std::path::PathBuf {
let absolute_path = directory.path().join(filename); let absolute_path = directory.path().join(filename);
std::fs::write(&absolute_path, filecontent).expect("error writing file"); write(&absolute_path, filecontent).unwrap();
let relative_path = absolute_path let relative_path = absolute_path
.strip_prefix(util::testdata_path()) .strip_prefix(util::testdata_path())
.expect("unable to create relative temporary file") .unwrap()
.to_owned(); .to_owned();
assert!(relative_path.is_relative()); assert!(relative_path.is_relative());
relative_path relative_path
} }
let temp_directory = let temp_directory = TempDir::new_in(util::testdata_path()).unwrap();
TempDir::new_in(util::testdata_path()).expect("tempdir fail");
let file_to_watch = create_relative_tmp_file( let file_to_watch = create_relative_tmp_file(
&temp_directory, &temp_directory,
"file_to_watch.js", "file_to_watch.js",
@ -484,37 +413,18 @@ fn run_watch_with_import_map_and_relative_paths() {
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())
.spawn() .spawn()
.expect("failed to spawn script"); .unwrap();
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
let stdout = child.stdout.as_mut().unwrap(); assert_contains!(stderr_lines.next().unwrap(), "Process finished");
let mut stdout_lines = assert_contains!(stdout_lines.next().unwrap(), "Hello world");
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
let stderr = child.stderr.as_mut().unwrap();
let mut stderr_lines =
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
assert!(stderr_lines.next().unwrap().contains("Process finished")); check_alive_then_kill(child);
assert!(stdout_lines.next().unwrap().contains("Hello world"));
child.kill().unwrap();
drop(file_to_watch);
drop(import_map_path);
temp_directory.close().unwrap();
} }
#[flaky_test] #[flaky_test]
fn test_watch() { fn test_watch() {
macro_rules! assert_contains { let t = TempDir::new().unwrap();
($string:expr, $($test:expr),+) => {
let string = $string; // This might be a function call or something
if !($(string.contains($test))||+) {
panic!("{:?} does not contain any of {:?}", string, [$($test),+]);
}
}
}
let t = TempDir::new().expect("tempdir fail");
let mut child = util::deno_cmd() let mut child = util::deno_cmd()
.current_dir(util::testdata_path()) .current_dir(util::testdata_path())
@ -527,40 +437,32 @@ fn test_watch() {
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())
.spawn() .spawn()
.expect("failed to spawn script"); .unwrap();
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
let stdout = child.stdout.as_mut().unwrap();
let mut stdout_lines =
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
let stderr = child.stderr.as_mut().unwrap();
let mut stderr_lines =
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
assert_eq!(stdout_lines.next().unwrap(), ""); assert_eq!(stdout_lines.next().unwrap(), "");
assert_contains!( assert_contains!(
stdout_lines.next().unwrap(), stdout_lines.next().unwrap(),
"0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out" "0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out"
); );
wait_for_process_finished("Test", &mut stderr_lines); wait_for("Test finished", &mut stderr_lines);
let foo_file = t.path().join("foo.js"); let foo_file = t.path().join("foo.js");
let bar_file = t.path().join("bar.js"); let bar_file = t.path().join("bar.js");
let foo_test = t.path().join("foo_test.js"); let foo_test = t.path().join("foo_test.js");
let bar_test = t.path().join("bar_test.js"); let bar_test = t.path().join("bar_test.js");
std::fs::write(&foo_file, "export default function foo() { 1 + 1 }") write(&foo_file, "export default function foo() { 1 + 1 }").unwrap();
.expect("error writing file"); write(&bar_file, "export default function bar() { 2 + 2 }").unwrap();
std::fs::write(&bar_file, "export default function bar() { 2 + 2 }") write(
.expect("error writing file");
std::fs::write(
&foo_test, &foo_test,
"import foo from './foo.js'; Deno.test('foo', foo);", "import foo from './foo.js'; Deno.test('foo', foo);",
) )
.expect("error writing file"); .unwrap();
std::fs::write( write(
&bar_test, &bar_test,
"import bar from './bar.js'; Deno.test('bar', bar);", "import bar from './bar.js'; Deno.test('bar', bar);",
) )
.expect("error writing file"); .unwrap();
assert_eq!(stdout_lines.next().unwrap(), ""); assert_eq!(stdout_lines.next().unwrap(), "");
assert_contains!(stdout_lines.next().unwrap(), "running 1 test"); assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
@ -570,14 +472,14 @@ fn test_watch() {
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
wait_for_process_finished("Test", &mut stderr_lines); wait_for("Test finished", &mut stderr_lines);
// Change content of the file // Change content of the file
std::fs::write( write(
&foo_test, &foo_test,
"import foo from './foo.js'; Deno.test('foobar', foo);", "import foo from './foo.js'; Deno.test('foobar', foo);",
) )
.expect("error writing file"); .unwrap();
assert_contains!(stderr_lines.next().unwrap(), "Restarting"); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert_contains!(stdout_lines.next().unwrap(), "running 1 test"); assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
@ -585,23 +487,22 @@ fn test_watch() {
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
wait_for_process_finished("Test", &mut stderr_lines); wait_for("Test finished", &mut stderr_lines);
// Add test // Add test
let another_test = t.path().join("new_test.js"); let another_test = t.path().join("new_test.js");
std::fs::write(&another_test, "Deno.test('another one', () => 3 + 3)") write(&another_test, "Deno.test('another one', () => 3 + 3)").unwrap();
.expect("error writing file");
assert_contains!(stderr_lines.next().unwrap(), "Restarting"); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert_contains!(stdout_lines.next().unwrap(), "running 1 test"); assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
assert_contains!(stdout_lines.next().unwrap(), "another one"); assert_contains!(stdout_lines.next().unwrap(), "another one");
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
wait_for_process_finished("Test", &mut stderr_lines); wait_for("Test finished", &mut stderr_lines);
// Confirm that restarting occurs when a new file is updated // Confirm that restarting occurs when a new file is updated
std::fs::write(&another_test, "Deno.test('another one', () => 3 + 3); Deno.test('another another one', () => 4 + 4)") write(&another_test, "Deno.test('another one', () => 3 + 3); Deno.test('another another one', () => 4 + 4)")
.expect("error writing file"); .unwrap();
assert_contains!(stderr_lines.next().unwrap(), "Restarting"); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert_contains!(stdout_lines.next().unwrap(), "running 2 tests"); assert_contains!(stdout_lines.next().unwrap(), "running 2 tests");
assert_contains!(stdout_lines.next().unwrap(), "another one"); assert_contains!(stdout_lines.next().unwrap(), "another one");
@ -609,81 +510,65 @@ fn test_watch() {
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
wait_for_process_finished("Test", &mut stderr_lines); wait_for("Test finished", &mut stderr_lines);
// Confirm that the watcher keeps on working even if the file is updated and has invalid syntax // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax
std::fs::write(&another_test, "syntax error ^^").expect("error writing file"); write(&another_test, "syntax error ^^").unwrap();
assert_contains!(stderr_lines.next().unwrap(), "Restarting"); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert_contains!(stderr_lines.next().unwrap(), "error:"); assert_contains!(stderr_lines.next().unwrap(), "error:");
assert_contains!(stderr_lines.next().unwrap(), "Test failed"); assert_contains!(stderr_lines.next().unwrap(), "Test failed");
// Then restore the file // Then restore the file
std::fs::write(&another_test, "Deno.test('another one', () => 3 + 3)") write(&another_test, "Deno.test('another one', () => 3 + 3)").unwrap();
.expect("error writing file");
assert_contains!(stderr_lines.next().unwrap(), "Restarting"); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert_contains!(stdout_lines.next().unwrap(), "running 1 test"); assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
assert_contains!(stdout_lines.next().unwrap(), "another one"); assert_contains!(stdout_lines.next().unwrap(), "another one");
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
wait_for_process_finished("Test", &mut stderr_lines); wait_for("Test finished", &mut stderr_lines);
// Confirm that the watcher keeps on working even if the file is updated and the test fails // Confirm that the watcher keeps on working even if the file is updated and the test fails
// This also confirms that it restarts when dependencies change // This also confirms that it restarts when dependencies change
std::fs::write( write(
&foo_file, &foo_file,
"export default function foo() { throw new Error('Whoops!'); }", "export default function foo() { throw new Error('Whoops!'); }",
) )
.expect("error writing file"); .unwrap();
assert_contains!(stderr_lines.next().unwrap(), "Restarting"); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert_contains!(stdout_lines.next().unwrap(), "running 1 test"); assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
assert_contains!(stdout_lines.next().unwrap(), "FAILED"); assert_contains!(stdout_lines.next().unwrap(), "FAILED");
while !stdout_lines.next().unwrap().contains("test result") {} wait_for("test result", &mut stdout_lines);
stdout_lines.next(); stdout_lines.next();
wait_for_process_finished("Test", &mut stderr_lines); wait_for("Test finished", &mut stderr_lines);
// Then restore the file // Then restore the file
std::fs::write(&foo_file, "export default function foo() { 1 + 1 }") write(&foo_file, "export default function foo() { 1 + 1 }").unwrap();
.expect("error writing file");
assert_contains!(stderr_lines.next().unwrap(), "Restarting"); assert_contains!(stderr_lines.next().unwrap(), "Restarting");
assert_contains!(stdout_lines.next().unwrap(), "running 1 test"); assert_contains!(stdout_lines.next().unwrap(), "running 1 test");
assert_contains!(stdout_lines.next().unwrap(), "foo"); assert_contains!(stdout_lines.next().unwrap(), "foo");
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
stdout_lines.next(); stdout_lines.next();
wait_for_process_finished("Test", &mut stderr_lines); wait_for("Test finished", &mut stderr_lines);
// Test that circular dependencies work fine // Test that circular dependencies work fine
std::fs::write( write(
&foo_file, &foo_file,
"import './bar.js'; export default function foo() { 1 + 1 }", "import './bar.js'; export default function foo() { 1 + 1 }",
) )
.expect("error writing file"); .unwrap();
std::fs::write( write(
&bar_file, &bar_file,
"import './foo.js'; export default function bar() { 2 + 2 }", "import './foo.js'; export default function bar() { 2 + 2 }",
) )
.expect("error writing file"); .unwrap();
check_alive_then_kill(child);
// the watcher process is still alive
assert!(child.try_wait().unwrap().is_none());
child.kill().unwrap();
drop(t);
} }
#[flaky_test] #[flaky_test]
fn test_watch_doc() { fn test_watch_doc() {
macro_rules! assert_contains { let t = TempDir::new().unwrap();
($string:expr, $($test:expr),+) => {
let string = $string; // This might be a function call or something
if !($(string.contains($test))||+) {
panic!("{:?} does not contain any of {:?}", string, [$($test),+]);
}
}
}
let t = TempDir::new().expect("tempdir fail");
let mut child = util::deno_cmd() let mut child = util::deno_cmd()
.current_dir(util::testdata_path()) .current_dir(util::testdata_path())
@ -696,32 +581,26 @@ fn test_watch_doc() {
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())
.spawn() .spawn()
.expect("failed to spawn script"); .unwrap();
let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child);
let stdout = child.stdout.as_mut().unwrap();
let mut stdout_lines =
std::io::BufReader::new(stdout).lines().map(|r| r.unwrap());
let stderr = child.stderr.as_mut().unwrap();
let mut stderr_lines =
std::io::BufReader::new(stderr).lines().map(|r| r.unwrap());
assert_eq!(stdout_lines.next().unwrap(), ""); assert_eq!(stdout_lines.next().unwrap(), "");
assert_contains!( assert_contains!(
stdout_lines.next().unwrap(), stdout_lines.next().unwrap(),
"0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out" "0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out"
); );
wait_for_process_finished("Test", &mut stderr_lines); wait_for("Test finished", &mut stderr_lines);
let foo_file = t.path().join("foo.ts"); let foo_file = t.path().join("foo.ts");
std::fs::write( write(
&foo_file, &foo_file,
r#" r#"
export default function foo() {} export default function foo() {}
"#, "#,
) )
.expect("error writing file"); .unwrap();
std::fs::write( write(
&foo_file, &foo_file,
r#" r#"
/** /**
@ -732,14 +611,10 @@ fn test_watch_doc() {
export default function foo() {} export default function foo() {}
"#, "#,
) )
.expect("error writing file"); .unwrap();
// We only need to scan for a Check file://.../foo.ts$3-6 line that // We only need to scan for a Check file://.../foo.ts$3-6 line that
// corresponds to the documentation block being type-checked. // corresponds to the documentation block being type-checked.
assert_contains!(skip_restarting_line(stderr_lines), "foo.ts$3-6"); assert_contains!(skip_restarting_line(stderr_lines), "foo.ts$3-6");
check_alive_then_kill(child);
assert!(child.try_wait().unwrap().is_none());
child.kill().unwrap();
drop(t);
} }