mirror of
https://github.com/rust-lang/rustlings.git
synced 2026-08-14 14:26:56 +00:00
run exercises with CWD set to their directory
This commit is contained in:
parent
0f0d3b78ca
commit
343e21b3bd
@ -6,18 +6,14 @@
|
|||||||
// Let's simulate this using asynchronous programming. Each person is
|
// Let's simulate this using asynchronous programming. Each person is
|
||||||
// represented as an asynchronous task, which can be executed concurrently.
|
// represented as an asynchronous task, which can be executed concurrently.
|
||||||
|
|
||||||
const SCORES_CLASS_A: &str = "exercises/24_async/scores_class_a.txt";
|
|
||||||
const SCORES_CLASS_B: &str = "exercises/24_async/scores_class_b.txt";
|
|
||||||
const SCORES_CLASS_C: &str = "exercises/24_async/scores_class_c.txt";
|
|
||||||
|
|
||||||
// Async tasks need to be executed by a "runtime", which is not provided by
|
// Async tasks need to be executed by a "runtime", which is not provided by
|
||||||
// Rust's standard library. Here, we use the mainstream runtime `tokio`.
|
// Rust's standard library. Here, we use the mainstream runtime `tokio`.
|
||||||
// The macro `tokio::main` wraps the entire main function in a runtime.
|
// The macro `tokio::main` wraps the entire main function in a runtime.
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let mean_score_a = tokio::spawn(calculate_mean_score(SCORES_CLASS_A));
|
let mean_score_a = tokio::spawn(calculate_mean_score("scores_class_a.txt"));
|
||||||
let mean_score_b = tokio::spawn(calculate_mean_score(SCORES_CLASS_B));
|
let mean_score_b = tokio::spawn(calculate_mean_score("scores_class_b.txt"));
|
||||||
let mean_score_c = tokio::spawn(calculate_mean_score(SCORES_CLASS_C));
|
let mean_score_c = tokio::spawn(calculate_mean_score("scores_class_c.txt"));
|
||||||
|
|
||||||
// TODO: Await the spawned tasks to check their results.
|
// TODO: Await the spawned tasks to check their results.
|
||||||
assert_eq!(mean_score_a, 84); // alice
|
assert_eq!(mean_score_a, 84); // alice
|
||||||
|
|||||||
@ -6,18 +6,14 @@
|
|||||||
// Let's simulate this using asynchronous programming. Each person is
|
// Let's simulate this using asynchronous programming. Each person is
|
||||||
// represented as an asynchronous task, which can be executed concurrently.
|
// represented as an asynchronous task, which can be executed concurrently.
|
||||||
|
|
||||||
const SCORES_CLASS_A: &str = "exercises/24_async/scores_class_a.txt";
|
|
||||||
const SCORES_CLASS_B: &str = "exercises/24_async/scores_class_b.txt";
|
|
||||||
const SCORES_CLASS_C: &str = "exercises/24_async/scores_class_c.txt";
|
|
||||||
|
|
||||||
// Async tasks need to be executed by a "runtime", which is not provided by
|
// Async tasks need to be executed by a "runtime", which is not provided by
|
||||||
// Rust's standard library. Here, we use the mainstream runtime `tokio`.
|
// Rust's standard library. Here, we use the mainstream runtime `tokio`.
|
||||||
// The macro `tokio::main` wraps the entire main function in a runtime.
|
// The macro `tokio::main` wraps the entire main function in a runtime.
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let mean_score_a = tokio::spawn(calculate_mean_score(SCORES_CLASS_A));
|
let mean_score_a = tokio::spawn(calculate_mean_score("scores_class_a.txt"));
|
||||||
let mean_score_b = tokio::spawn(calculate_mean_score(SCORES_CLASS_B));
|
let mean_score_b = tokio::spawn(calculate_mean_score("scores_class_b.txt"));
|
||||||
let mean_score_c = tokio::spawn(calculate_mean_score(SCORES_CLASS_C));
|
let mean_score_c = tokio::spawn(calculate_mean_score("scores_class_c.txt"));
|
||||||
|
|
||||||
assert_eq!(mean_score_a.await.unwrap(), 84); // alice
|
assert_eq!(mean_score_a.await.unwrap(), 84); // alice
|
||||||
assert_eq!(mean_score_b.await.unwrap(), 89); // bob
|
assert_eq!(mean_score_b.await.unwrap(), 89); // bob
|
||||||
|
|||||||
23
src/cmd.rs
23
src/cmd.rs
@ -13,7 +13,12 @@ const TIMEOUT_SECS: u64 = 30;
|
|||||||
|
|
||||||
/// Run a command with a description for a possible error and append the merged stdout and stderr.
|
/// Run a command with a description for a possible error and append the merged stdout and stderr.
|
||||||
/// The boolean in the returned `Result` is true if the command's exit status is success.
|
/// The boolean in the returned `Result` is true if the command's exit status is success.
|
||||||
fn run_cmd(mut cmd: Command, description: &str, output: Option<&mut Vec<u8>>) -> Result<bool> {
|
fn run_cmd(
|
||||||
|
mut cmd: Command,
|
||||||
|
description: &str,
|
||||||
|
cwd: Option<&str>,
|
||||||
|
output: Option<&mut Vec<u8>>,
|
||||||
|
) -> Result<bool> {
|
||||||
let spawn = |mut cmd: Command| {
|
let spawn = |mut cmd: Command| {
|
||||||
// The closure drops `cmd` which prevents a pipe deadlock.
|
// The closure drops `cmd` which prevents a pipe deadlock.
|
||||||
cmd.stdin(Stdio::null())
|
cmd.stdin(Stdio::null())
|
||||||
@ -25,6 +30,9 @@ fn run_cmd(mut cmd: Command, description: &str, output: Option<&mut Vec<u8>>) ->
|
|||||||
.wait_timeout(Duration::from_secs(TIMEOUT_SECS))
|
.wait_timeout(Duration::from_secs(TIMEOUT_SECS))
|
||||||
.with_context(|| format!("Failed to wait on `{description}` to exit"))
|
.with_context(|| format!("Failed to wait on `{description}` to exit"))
|
||||||
};
|
};
|
||||||
|
if let Some(cwd) = cwd {
|
||||||
|
cmd.current_dir(cwd);
|
||||||
|
}
|
||||||
|
|
||||||
let mut handle = if let Some(output) = output {
|
let mut handle = if let Some(output) = output {
|
||||||
let (mut reader, writer) =
|
let (mut reader, writer) =
|
||||||
@ -133,7 +141,12 @@ impl CmdRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The boolean in the returned `Result` is true if the command's exit status is success.
|
/// The boolean in the returned `Result` is true if the command's exit status is success.
|
||||||
pub fn run_debug_bin(&self, bin_name: &str, output: Option<&mut Vec<u8>>) -> Result<bool> {
|
pub fn run_debug_bin(
|
||||||
|
&self,
|
||||||
|
bin_name: &str,
|
||||||
|
cwd: &str,
|
||||||
|
output: Option<&mut Vec<u8>>,
|
||||||
|
) -> Result<bool> {
|
||||||
// 7 = "/debug/".len()
|
// 7 = "/debug/".len()
|
||||||
let mut bin_path =
|
let mut bin_path =
|
||||||
PathBuf::with_capacity(self.target_dir.as_os_str().len() + 7 + bin_name.len());
|
PathBuf::with_capacity(self.target_dir.as_os_str().len() + 7 + bin_name.len());
|
||||||
@ -141,7 +154,7 @@ impl CmdRunner {
|
|||||||
bin_path.push("debug");
|
bin_path.push("debug");
|
||||||
bin_path.push(bin_name);
|
bin_path.push(bin_name);
|
||||||
|
|
||||||
run_cmd(Command::new(&bin_path), bin_name, output)
|
run_cmd(Command::new(&bin_path), bin_name, Some(cwd), output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +174,7 @@ impl CargoSubcommand<'_> {
|
|||||||
|
|
||||||
/// The boolean in the returned `Result` is true if the command's exit status is success.
|
/// The boolean in the returned `Result` is true if the command's exit status is success.
|
||||||
pub fn run(self, description: &str) -> Result<bool> {
|
pub fn run(self, description: &str) -> Result<bool> {
|
||||||
run_cmd(self.cmd, description, self.output)
|
run_cmd(self.cmd, description, None, self.output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +192,7 @@ mod tests {
|
|||||||
cmd.arg("Hello");
|
cmd.arg("Hello");
|
||||||
|
|
||||||
let mut output = Vec::with_capacity(8);
|
let mut output = Vec::with_capacity(8);
|
||||||
run_cmd(cmd, "echo …", Some(&mut output)).unwrap();
|
run_cmd(cmd, "echo …", None, Some(&mut output)).unwrap();
|
||||||
|
|
||||||
assert_eq!(output, b"Hello\n\n");
|
assert_eq!(output, b"Hello\n\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,7 @@ pub fn solution_link_line(
|
|||||||
// Compilation must be done before calling this method.
|
// Compilation must be done before calling this method.
|
||||||
fn run_bin(
|
fn run_bin(
|
||||||
bin_name: &str,
|
bin_name: &str,
|
||||||
|
cwd: &str,
|
||||||
mut output: Option<&mut Vec<u8>>,
|
mut output: Option<&mut Vec<u8>>,
|
||||||
cmd_runner: &CmdRunner,
|
cmd_runner: &CmdRunner,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
@ -46,7 +47,7 @@ fn run_bin(
|
|||||||
output.push(b'\n');
|
output.push(b'\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
let success = cmd_runner.run_debug_bin(bin_name, output.as_deref_mut())?;
|
let success = cmd_runner.run_debug_bin(bin_name, cwd, output.as_deref_mut())?;
|
||||||
|
|
||||||
if let Some(output) = output
|
if let Some(output) = output
|
||||||
&& !success
|
&& !success
|
||||||
@ -123,6 +124,14 @@ pub trait RunnableExercise {
|
|||||||
output.clear();
|
output.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cwd_buf;
|
||||||
|
let cwd = if let Some(dir) = self.dir() {
|
||||||
|
cwd_buf = format!("exercises/{dir}");
|
||||||
|
cwd_buf.as_str()
|
||||||
|
} else {
|
||||||
|
"exercises"
|
||||||
|
};
|
||||||
|
|
||||||
if self.test() {
|
if self.test() {
|
||||||
let output_is_some = output.is_some();
|
let output_is_some = output.is_some();
|
||||||
let mut test_cmd = cmd_runner.cargo("test", bin_name, output.as_deref_mut());
|
let mut test_cmd = cmd_runner.cargo("test", bin_name, output.as_deref_mut());
|
||||||
@ -131,7 +140,7 @@ pub trait RunnableExercise {
|
|||||||
}
|
}
|
||||||
let test_success = test_cmd.run("cargo test …")?;
|
let test_success = test_cmd.run("cargo test …")?;
|
||||||
if !test_success {
|
if !test_success {
|
||||||
run_bin(bin_name, output, cmd_runner)?;
|
run_bin(bin_name, cwd, output, cmd_runner)?;
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +160,7 @@ pub trait RunnableExercise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let clippy_success = clippy_cmd.run("cargo clippy …")?;
|
let clippy_success = clippy_cmd.run("cargo clippy …")?;
|
||||||
let run_success = run_bin(bin_name, output, cmd_runner)?;
|
let run_success = run_bin(bin_name, cwd, output, cmd_runner)?;
|
||||||
|
|
||||||
Ok(clippy_success && run_success)
|
Ok(clippy_success && run_success)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user