mirror of
https://github.com/rust-lang/rustlings.git
synced 2024-12-26 00:00:03 +03:00
Compare commits
9 commits
3c5bbfca6c
...
daa5492f35
Author | SHA1 | Date | |
---|---|---|---|
daa5492f35 | |||
8d0aa11a35 | |||
e2674498c6 | |||
3200581d4d | |||
6afc4840b4 | |||
93aef73eb5 | |||
7ceeb81e61 | |||
84b1cf8849 | |||
3f23d26600 |
|
@ -2550,6 +2550,15 @@
|
|||
"contributions": [
|
||||
"content"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "gerases",
|
||||
"name": "gerases",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/8953623?v=4",
|
||||
"profile": "https://github.com/gerases",
|
||||
"contributions": [
|
||||
"content"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 8,
|
||||
|
|
|
@ -360,6 +360,9 @@ authors.
|
|||
<td align="center" valign="top" width="12.5%"><a href="https://github.com/neuschaefer"><img src="https://avatars.githubusercontent.com/u/1021512?v=4?s=100" width="100px;" alt="J. Neuschäfer"/><br /><sub><b>J. Neuschäfer</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=neuschaefer" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="12.5%"><a href="https://scooterhacking.org"><img src="https://avatars.githubusercontent.com/u/58905488?v=4?s=100" width="100px;" alt="Bastian Pedersen"/><br /><sub><b>Bastian Pedersen</b></sub></a><br /><a href="#content-bastianpedersen" title="Content">🖋</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="12.5%"><a href="https://github.com/gerases"><img src="https://avatars.githubusercontent.com/u/8953623?v=4?s=100" width="100px;" alt="gerases"/><br /><sub><b>gerases</b></sub></a><br /><a href="#content-gerases" title="Content">🖋</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ fn build_scores_table(results: String) -> HashMap<String, Team> {
|
|||
let team_2_score: u8 = v[3].parse().unwrap();
|
||||
// TODO: Populate the scores table with details extracted from the
|
||||
// current line. Keep in mind that goals scored by team_1
|
||||
// will be the number of goals conceded from team_2, and similarly
|
||||
// will be the number of goals conceded by team_2, and similarly
|
||||
// goals scored by team_2 will be the number of goals conceded by
|
||||
// team_1.
|
||||
}
|
||||
|
|
11
exercises/crates/mockall/mocks1/Cargo.toml
Normal file
11
exercises/crates/mockall/mocks1/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "mocks1"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
mockall = "0.11.4"
|
||||
|
||||
[[bin]]
|
||||
name = "mocks1"
|
||||
path = "mocks1.rs"
|
45
exercises/crates/mockall/mocks1/mocks1.rs
Normal file
45
exercises/crates/mockall/mocks1/mocks1.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
// mocks1.rs
|
||||
//
|
||||
// Mockall is a powerful mock object library for Rust. It provides tools to create
|
||||
// mock versions of almost any trait or struct. They can be used in unit tests as
|
||||
// a stand-in for the real object.
|
||||
//
|
||||
// These tests each contain an expectation that defines some behaviour we expect on
|
||||
// calls to the function "foo". Add the "foo" function call and get the tests to pass
|
||||
//
|
||||
// I AM NOT DONE
|
||||
|
||||
use mockall::*;
|
||||
use mockall::predicate::*;
|
||||
|
||||
#[automock]
|
||||
trait MyTrait {
|
||||
fn foo(&self) -> bool;
|
||||
}
|
||||
|
||||
fn follow_path_from_trait(x: &dyn MyTrait) -> String {
|
||||
if ??? {
|
||||
String::from("Followed path A")
|
||||
}
|
||||
else {
|
||||
String::from("Followed path B")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_follow_path_a() {
|
||||
let mut mock = MockMyTrait::new();
|
||||
mock.expect_foo()
|
||||
.times(1)
|
||||
.returning(||true);
|
||||
assert_eq!(follow_path_from_trait(&mock), "Followed path A");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_follow_path_b() {
|
||||
let mut mock = MockMyTrait::new();
|
||||
mock.expect_foo()
|
||||
.times(1)
|
||||
.returning(||false);
|
||||
assert_eq!(follow_path_from_trait(&mock), "Followed path B");
|
||||
}
|
|
@ -1319,3 +1319,10 @@ path = "exercises/23_conversions/as_ref_mut.rs"
|
|||
mode = "test"
|
||||
hint = """
|
||||
Add `AsRef<str>` or `AsMut<u32>` as a trait bound to the functions."""
|
||||
|
||||
[[exercises]]
|
||||
name = "mocks1"
|
||||
path = "exercises/crates/mockall/mocks1/Cargo.toml"
|
||||
mode = "cratetest"
|
||||
hint = """
|
||||
x.foo() needs to be called in the if conditional to get the tests to pass."""
|
102
src/exercise.rs
102
src/exercise.rs
|
@ -1,5 +1,6 @@
|
|||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use std::env;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::fs::{self, remove_file, File};
|
||||
|
@ -35,6 +36,10 @@ pub enum Mode {
|
|||
Test,
|
||||
// Indicates that the exercise should be linted with clippy
|
||||
Clippy,
|
||||
// Indicates that the exercise should be compiled as a binary and requires a crate
|
||||
CrateCompile,
|
||||
// Indicates that the exercise should be compiled as a test harness and requires a crate
|
||||
CrateTest,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -50,7 +55,7 @@ pub struct Exercise {
|
|||
pub name: String,
|
||||
// The path to the file containing the exercise's source code
|
||||
pub path: PathBuf,
|
||||
// The mode of the exercise (Test, Compile, or Clippy)
|
||||
// The mode of the exercise (Test, Compile, Clippy, CrateCompile or CrateTest)
|
||||
pub mode: Mode,
|
||||
// The hint text associated with the exercise
|
||||
pub hint: String,
|
||||
|
@ -81,12 +86,13 @@ pub struct ContextLine {
|
|||
pub struct CompiledExercise<'a> {
|
||||
exercise: &'a Exercise,
|
||||
_handle: FileHandle,
|
||||
pub stdout: String,
|
||||
}
|
||||
|
||||
impl<'a> CompiledExercise<'a> {
|
||||
// Run the compiled exercise
|
||||
pub fn run(&self) -> Result<ExerciseOutput, ExerciseOutput> {
|
||||
self.exercise.run()
|
||||
self.exercise.run(&self.stdout)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,6 +171,21 @@ path = "{}.rs""#,
|
|||
.args(["--", "-D", "warnings", "-D", "clippy::float_cmp"])
|
||||
.output()
|
||||
}
|
||||
Mode::CrateCompile => Command::new("cargo")
|
||||
.args([
|
||||
"build",
|
||||
"--manifest-path",
|
||||
self.path.to_str().unwrap(),
|
||||
"--target-dir",
|
||||
&temp_file(),
|
||||
])
|
||||
.output(),
|
||||
Mode::CrateTest => Command::new("cargo")
|
||||
.args(["test", "--no-run"])
|
||||
.args(["--manifest-path", self.path.to_str().unwrap()])
|
||||
.args(["--target-dir", &temp_file()])
|
||||
.args(["--message-format", "json-render-diagnostics"])
|
||||
.output(),
|
||||
}
|
||||
.expect("Failed to run 'compile' command.");
|
||||
|
||||
|
@ -172,8 +193,10 @@ path = "{}.rs""#,
|
|||
Ok(CompiledExercise {
|
||||
exercise: self,
|
||||
_handle: FileHandle,
|
||||
stdout: String::from_utf8_lossy(&cmd.stdout).to_string(),
|
||||
})
|
||||
} else {
|
||||
self.cleanup_temporary_dirs_by_mode();
|
||||
clean();
|
||||
Err(ExerciseOutput {
|
||||
stdout: String::from_utf8_lossy(&cmd.stdout).to_string(),
|
||||
|
@ -182,26 +205,71 @@ path = "{}.rs""#,
|
|||
}
|
||||
}
|
||||
|
||||
fn run(&self) -> Result<ExerciseOutput, ExerciseOutput> {
|
||||
fn get_crate_test_filename(&self, stdout: &str) -> Result<String, ()> {
|
||||
let json_objects = stdout.split("\n");
|
||||
for json_object in json_objects {
|
||||
let parsed_json: Value = serde_json::from_str(json_object).unwrap();
|
||||
if parsed_json["target"]["kind"][0] == "bin" {
|
||||
return Ok(String::from(parsed_json["filenames"][0].as_str().unwrap()));
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
|
||||
fn get_compiled_filename_by_mode(&self, compilation_stdout: &str) -> String {
|
||||
match self.mode {
|
||||
Mode::CrateCompile => temp_file() + "/debug/" + &self.name,
|
||||
Mode::CrateTest => {
|
||||
let get_filename_result = self.get_crate_test_filename(&compilation_stdout);
|
||||
match get_filename_result {
|
||||
Ok(filename) => filename,
|
||||
Err(()) => panic!("Failed to get crate test filename"),
|
||||
}
|
||||
}
|
||||
_ => temp_file(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cleanup_temporary_dirs_by_mode(&self) {
|
||||
match self.mode {
|
||||
Mode::CrateCompile | Mode::CrateTest => {
|
||||
fs::remove_dir_all(temp_file()).expect("Failed to cleanup temp build dir")
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&self, compilation_stdout: &str) -> Result<ExerciseOutput, ExerciseOutput> {
|
||||
let arg = match self.mode {
|
||||
Mode::Test => "--show-output",
|
||||
Mode::Test | Mode::CrateTest => "--show-output",
|
||||
_ => "",
|
||||
};
|
||||
let cmd = Command::new(temp_file())
|
||||
.arg(arg)
|
||||
.output()
|
||||
.expect("Failed to run 'run' command");
|
||||
|
||||
let output = ExerciseOutput {
|
||||
stdout: String::from_utf8_lossy(&cmd.stdout).to_string(),
|
||||
stderr: String::from_utf8_lossy(&cmd.stderr).to_string(),
|
||||
let filename = self.get_compiled_filename_by_mode(compilation_stdout);
|
||||
|
||||
let command_output = Command::new(filename).arg(arg).output();
|
||||
let result = match command_output {
|
||||
Ok(cmd) => {
|
||||
let output = ExerciseOutput {
|
||||
stdout: String::from_utf8_lossy(&cmd.stdout).to_string(),
|
||||
stderr: String::from_utf8_lossy(&cmd.stderr).to_string(),
|
||||
};
|
||||
|
||||
self.cleanup_temporary_dirs_by_mode();
|
||||
|
||||
if cmd.status.success() {
|
||||
Ok(output)
|
||||
} else {
|
||||
Err(output)
|
||||
}
|
||||
}
|
||||
Err(msg) => {
|
||||
self.cleanup_temporary_dirs_by_mode();
|
||||
println!("Error: {}", msg);
|
||||
panic!("Failed to run 'run' command");
|
||||
}
|
||||
};
|
||||
|
||||
if cmd.status.success() {
|
||||
Ok(output)
|
||||
} else {
|
||||
Err(output)
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn state(&self) -> State {
|
||||
|
|
|
@ -11,8 +11,8 @@ use indicatif::ProgressBar;
|
|||
// the output from the test harnesses (if the mode of the exercise is test)
|
||||
pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
|
||||
match exercise.mode {
|
||||
Mode::Test => test(exercise, verbose)?,
|
||||
Mode::Compile => compile_and_run(exercise)?,
|
||||
Mode::Test | Mode::CrateTest => test(exercise, verbose)?,
|
||||
Mode::Compile | Mode::CrateCompile => compile_and_run(exercise)?,
|
||||
Mode::Clippy => compile_and_run(exercise)?,
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -28,8 +28,12 @@ pub fn verify<'a>(
|
|||
|
||||
for exercise in exercises {
|
||||
let compile_result = match exercise.mode {
|
||||
Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose, success_hints),
|
||||
Mode::Compile => compile_and_run_interactively(exercise, success_hints),
|
||||
Mode::Test | Mode::CrateTest => {
|
||||
compile_and_test(exercise, RunMode::Interactive, verbose, success_hints)
|
||||
}
|
||||
Mode::Compile | Mode::CrateCompile => {
|
||||
compile_and_run_interactively(exercise, success_hints)
|
||||
}
|
||||
Mode::Clippy => compile_only(exercise, success_hints),
|
||||
};
|
||||
if !compile_result.unwrap_or(false) {
|
||||
|
@ -164,8 +168,8 @@ fn prompt_for_completion(
|
|||
State::Pending(context) => context,
|
||||
};
|
||||
match exercise.mode {
|
||||
Mode::Compile => success!("Successfully ran {}!", exercise),
|
||||
Mode::Test => success!("Successfully tested {}!", exercise),
|
||||
Mode::Compile | Mode::CrateCompile => success!("Successfully ran {}!", exercise),
|
||||
Mode::Test | Mode::CrateTest => success!("Successfully tested {}!", exercise),
|
||||
Mode::Clippy => success!("Successfully compiled {}!", exercise),
|
||||
}
|
||||
|
||||
|
@ -178,8 +182,8 @@ fn prompt_for_completion(
|
|||
};
|
||||
|
||||
let success_msg = match exercise.mode {
|
||||
Mode::Compile => "The code is compiling!",
|
||||
Mode::Test => "The code is compiling, and the tests pass!",
|
||||
Mode::Compile | Mode::CrateCompile => "The code is compiling!",
|
||||
Mode::Test | Mode::CrateTest => "The code is compiling, and the tests pass!",
|
||||
Mode::Clippy => clippy_success_msg,
|
||||
};
|
||||
println!();
|
||||
|
|
Loading…
Reference in a new issue