-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Writing "Prevent hidden dependencies" section, update tutorial README.
- Loading branch information
Showing
7 changed files
with
661 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
|
||
|
||
// Hidden dependency tests | ||
|
||
#[should_panic(expected = "Hidden dependency")] | ||
#[test] | ||
fn test_require_hidden_dependency_panics() { | ||
fn run() -> Result<(), io::Error> { | ||
let mut pie = test_pie(); | ||
let temp_dir = create_temp_dir()?; | ||
|
||
let file = temp_dir.path().join("in_out.txt"); | ||
write(&file, "Hello, World!")?; | ||
|
||
let read = ReadFile(file.clone(), FileStamper::Modified); | ||
let write = WriteFile(Box::new(Return("Hi there")), file.clone(), FileStamper::Modified); | ||
|
||
pie.require_then_assert_one_execute(&write)?; | ||
pie.require_then_assert_one_execute(&read)?; | ||
|
||
Ok(()) | ||
} | ||
run().unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
|
||
#[should_panic(expected = "Hidden dependency")] | ||
#[test] | ||
fn test_provide_hidden_dependency_panics() { | ||
fn run() -> Result<(), io::Error> { | ||
let mut pie = test_pie(); | ||
let temp_dir = create_temp_dir()?; | ||
|
||
let file = temp_dir.path().join("in_out.txt"); | ||
write(&file, "Hello, World!")?; | ||
|
||
let read = ReadFile(file.clone(), FileStamper::Modified); | ||
let write = WriteFile(Box::new(Return("Hi there")), file.clone(), FileStamper::Modified); | ||
|
||
pie.require_then_assert_one_execute(&read)?; | ||
pie.require_then_assert_one_execute(&write)?; | ||
|
||
Ok(()) | ||
} | ||
run().unwrap(); | ||
} |
136 changes: 136 additions & 0 deletions
136
tutorial/src/3_min_sound/6_hidden_dep/e_1_read_origin.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
use std::io::{BufWriter, ErrorKind, Read, Stdout}; | ||
use std::path::PathBuf; | ||
|
||
use dev_shared::write_until_modified; | ||
use pie::{Context, Pie, Task}; | ||
use pie::stamp::FileStamper; | ||
use pie::tracker::CompositeTracker; | ||
use pie::tracker::event::EventTracker; | ||
use pie::tracker::writing::WritingTracker; | ||
|
||
/// Testing tracker composed of an [`EventTracker`] for testing and stdout [`WritingTracker`] for debugging. | ||
pub type TestTracker<T> = CompositeTracker<EventTracker<T, <T as Task>::Output>, WritingTracker<BufWriter<Stdout>>>; | ||
pub fn test_tracker<T: Task>() -> TestTracker<T> { | ||
CompositeTracker(EventTracker::default(), WritingTracker::with_stdout()) | ||
} | ||
|
||
/// Testing [`Pie`] using [`TestTracker`]. | ||
pub type TestPie<T> = Pie<T, <T as Task>::Output, TestTracker<T>>; | ||
pub fn test_pie<T: Task>() -> TestPie<T> { | ||
TestPie::with_tracker(test_tracker()) | ||
} | ||
|
||
/// Testing extensions for [`TestPie`]. | ||
pub trait TestPieExt<T: Task> { | ||
/// Require `task` in a new session, assert that there are no dependency check errors, then runs `test_assert_func` | ||
/// on the event tracker for test assertion purposes. | ||
fn require_then_assert( | ||
&mut self, | ||
task: &T, | ||
test_assert_func: impl FnOnce(&EventTracker<T, T::Output>), | ||
) -> T::Output; | ||
|
||
/// Require `task` in a new session, asserts that there are no dependency check errors. | ||
fn require(&mut self, task: &T) -> T::Output { | ||
self.require_then_assert(task, |_| {}) | ||
} | ||
|
||
/// Require `task` in a new session, then assert that it is not executed. | ||
fn require_then_assert_no_execute(&mut self, task: &T) -> T::Output { | ||
self.require_then_assert(task, |t| | ||
assert!(!t.any_execute_of(task), "expected no execution of task {:?}, but it was executed", task), | ||
) | ||
} | ||
/// Require `task` in a new session, then assert that it is executed exactly once. | ||
fn require_then_assert_one_execute(&mut self, task: &T) -> T::Output { | ||
self.require_then_assert(task, |t| | ||
assert!(t.one_execute_of(task), "expected one execution of task {:?}, but it was not executed, or was executed more than once", task), | ||
) | ||
} | ||
} | ||
impl<T: Task> TestPieExt<T> for TestPie<T> { | ||
fn require_then_assert(&mut self, task: &T, test_assert_func: impl FnOnce(&EventTracker<T, T::Output>)) -> T::Output { | ||
let mut session = self.new_session(); | ||
let output = session.require(task); | ||
assert!(session.dependency_check_errors().is_empty(), "expected no dependency checking errors, but there are \ | ||
dependency checking errors: {:?}", session.dependency_check_errors()); | ||
test_assert_func(&self.tracker().0); | ||
output | ||
} | ||
} | ||
|
||
/// Testing tasks enumeration. | ||
#[derive(Clone, Eq, PartialEq, Hash, Debug)] | ||
pub enum TestTask { | ||
Return(&'static str), | ||
ReadFile(PathBuf, FileStamper, Option<Box<TestTask>>), | ||
WriteFile(Box<TestTask>, PathBuf, FileStamper), | ||
ToLower(Box<TestTask>), | ||
ToUpper(Box<TestTask>), | ||
Sequence(Vec<TestTask>), | ||
} | ||
impl Task for TestTask { | ||
type Output = Result<TestOutput, ErrorKind>; | ||
fn execute<C: Context<Self>>(&self, context: &mut C) -> Self::Output { | ||
match self { | ||
TestTask::Return(string) => Ok(string.to_string().into()), | ||
TestTask::ReadFile(path, stamper, origin) => { | ||
if let Some(origin) = origin { | ||
context.require_task(origin)?; | ||
} | ||
let mut string = String::new(); | ||
if let Some(mut file) = context.require_file_with_stamper(path, *stamper).map_err(|e| e.kind())? { | ||
file.read_to_string(&mut string).map_err(|e| e.kind())?; | ||
} | ||
Ok(string.into()) | ||
} | ||
TestTask::WriteFile(string_provider_task, path, stamper) => { | ||
let string = context.require_task(string_provider_task.as_ref())?.into_string(); | ||
write_until_modified(path, string.as_bytes()).map_err(|e| e.kind())?; | ||
context.provide_file_with_stamper(path, *stamper).map_err(|e| e.kind())?; | ||
Ok(TestOutput::Unit) | ||
} | ||
TestTask::ToLower(string_provider_task) => { | ||
let string = context.require_task(string_provider_task)?.into_string(); | ||
Ok(string.to_lowercase().into()) | ||
} | ||
TestTask::ToUpper(string_provider_task) => { | ||
let string = context.require_task(string_provider_task)?.into_string(); | ||
Ok(string.to_uppercase().into()) | ||
} | ||
TestTask::Sequence(tasks) => { | ||
for task in tasks { | ||
context.require_task(task)?; | ||
} | ||
Ok(TestOutput::Unit) | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// [`TestTask`] output enumeration. | ||
#[derive(Clone, Eq, PartialEq, Hash, Debug)] | ||
pub enum TestOutput { | ||
String(String), | ||
Unit, | ||
} | ||
impl From<String> for TestOutput { | ||
fn from(value: String) -> Self { Self::String(value) } | ||
} | ||
impl From<()> for TestOutput { | ||
fn from(_: ()) -> Self { Self::Unit } | ||
} | ||
impl TestOutput { | ||
pub fn as_str(&self) -> &str { | ||
match self { | ||
Self::String(s) => &s, | ||
_ => panic!("{:?} does not contain a string", self), | ||
} | ||
} | ||
pub fn into_string(self) -> String { | ||
match self { | ||
Self::String(s) => s, | ||
_ => panic!("{:?} does not contain a string", self), | ||
} | ||
} | ||
} |
Oops, something went wrong.