Skip to content

Commit

Permalink
Merge pull request #75 from mobusoperandi/subsequent-repl-test
Browse files Browse the repository at this point in the history
subsequent repl test
  • Loading branch information
mightyiam authored Feb 20, 2024
2 parents 0c39f6c + 221f195 commit 4ae18b7
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 34 deletions.
24 changes: 21 additions & 3 deletions src/app/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,13 @@ impl State {
if !acc.ends_with('\n') {
vec![]
} else if Self::sanitize(acc)? == expected.as_str() {
session_live.expecting = ReplSessionExpecting::Result {
acc: String::new(),
expected_result: expected_result.clone(),
session_live.expecting = if let Some(expected_result) = expected_result {
ReplSessionExpecting::Result {
acc: String::new(),
expected_result: expected_result.clone(),
}
} else {
ReplSessionExpecting::BlankLine { saw_cr: false }
};
vec![]
} else {
Expand Down Expand Up @@ -185,6 +189,20 @@ impl State {
session_live.expecting = ReplSessionExpecting::Prompt(String::new());
vec![]
}
ReplSessionExpecting::BlankLine { saw_cr: false } => {
anyhow::ensure!(
ch == b'\r',
"expecting carriage return, got {:?}",
ch as char,
);
session_live.expecting = ReplSessionExpecting::BlankLine { saw_cr: true };
vec![]
}
ReplSessionExpecting::BlankLine { saw_cr: true } => {
anyhow::ensure!(ch == b'\n', "expecting line feed, got {:?}", ch as char,);
session_live.expecting = ReplSessionExpecting::Prompt(String::new());
vec![]
}
};

Ok(output)
Expand Down
7 changes: 5 additions & 2 deletions src/app/state/repl_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,15 @@ pub(crate) enum ReplSessionExpecting {
Echo {
acc: String,
last_query: ReplQuery,
expected_result: ExpectedResult,
expected_result: Option<ExpectedResult>,
},
Result {
acc: String,
expected_result: ExpectedResult,
},
BlankLine {
saw_cr: bool,
},
}

impl ReplSessionLive {
Expand All @@ -75,7 +78,7 @@ impl Iterator for ReplSessionLive {
}
}

#[derive(Debug, Clone, derive_more::Deref, derive_more::Display)]
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Deref, derive_more::Display)]
pub(crate) struct ExpectedResult(pub(crate) String);

impl From<LFLine> for ExpectedResult {
Expand Down
15 changes: 5 additions & 10 deletions src/repl/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};

use crate::example_id::ExampleId;

#[derive(Debug, Clone, derive_more::Deref)]
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Deref, derive_more::Display)]
pub(crate) struct LFLine(String);

impl std::str::FromStr for LFLine {
Expand All @@ -27,17 +27,12 @@ impl std::str::FromStr for LFLine {
}
}

#[derive(Debug, Clone, derive_more::Deref)]
#[derive(Debug, Clone, derive_more::Deref, PartialEq, Eq)]
pub(crate) struct ReplQuery(LFLine);

impl TryFrom<LFLine> for ReplQuery {
type Error = anyhow::Error;

fn try_from(line: LFLine) -> Result<Self, Self::Error> {
let Option::Some(("", query)) = line.split_once("nix-repl> ") else {
return Err(anyhow::anyhow!("missing prompt {line:?}"));
};
Ok(Self(query.parse().unwrap()))
impl ReplQuery {
pub fn new(query: LFLine) -> Self {
Self(query)
}
}

Expand Down
71 changes: 55 additions & 16 deletions src/repl/example.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use itertools::Itertools;

use crate::{app::state::repl_state::ExpectedResult, example_id::ExampleId};

use super::driver::{LFLine, ReplQuery};
Expand Down Expand Up @@ -28,34 +26,75 @@ impl ReplExample {
#[derive(Debug, Clone, derive_more::IntoIterator)]
pub(crate) struct ReplExampleEntries(Vec<ReplEntry>);

#[derive(Debug, Default)]
struct ParseState {
entries: Vec<ReplEntry>,
expecting: Expecting,
}

#[derive(Debug, Default, PartialEq, Eq)]
enum Expecting {
#[default]
PromptAndQuery,
ResultOrBlankLine(ReplQuery),
BlankLine(ReplQuery, Option<ExpectedResult>),
}

impl std::str::FromStr for ReplExampleEntries {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let entries = s
.split_inclusive('\n')
.map(|line| LFLine::from_str(line).unwrap())
.filter(|line| **line != "\n")
.tuples::<(_, _)>()
.map(|(query, expected_result)| {
let query = ReplQuery::try_from(query)?;
let expected_result = ExpectedResult::from(expected_result);
Ok(ReplEntry::new(query, expected_result))
})
.collect::<anyhow::Result<Vec<_>>>()?;
let final_state =
s.split_inclusive('\n')
.try_fold(ParseState::default(), |mut state, line| {
let line = LFLine::from_str(line)?;
match state.expecting {
Expecting::PromptAndQuery => {
let Some(line) = line.strip_prefix("nix-repl> ") else {
anyhow::bail!("expected prompt, found {line:?}");
};
let query = LFLine::from_str(line).unwrap();
let query = ReplQuery::new(query);
state.expecting = Expecting::ResultOrBlankLine(query);
}
Expecting::ResultOrBlankLine(query) => {
if line.as_str() == "\n" {
state.entries.push(ReplEntry::new(query, None));
state.expecting = Expecting::PromptAndQuery;
} else {
let expected = Some(ExpectedResult::from(line));
state.expecting = Expecting::BlankLine(query, expected);
}
}
Expecting::BlankLine(query, expected) => {
anyhow::ensure!(
line.as_str() == "\n",
"expected blank line, found {line:?}"
);
state.entries.push(ReplEntry::new(query, expected));
state.expecting = Expecting::PromptAndQuery;
}
}
Ok(state)
})?;

anyhow::ensure!(
final_state.expecting == Expecting::PromptAndQuery,
"failed to parse"
);

Ok(Self(entries))
Ok(Self(final_state.entries))
}
}

#[derive(Debug, Clone)]
pub(crate) struct ReplEntry {
pub(crate) query: ReplQuery,
pub(crate) expected_result: ExpectedResult,
pub(crate) expected_result: Option<ExpectedResult>,
}

impl ReplEntry {
pub(crate) fn new(query: ReplQuery, expected_result: ExpectedResult) -> Self {
pub(crate) fn new(query: ReplQuery, expected_result: Option<ExpectedResult>) -> Self {
Self {
query,
expected_result,
Expand Down
6 changes: 4 additions & 2 deletions tests/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ fn all_examples_tested() {
```nix-repl
nix-repl> 1 + 1
2
```
```nix
Expand All @@ -31,6 +32,7 @@ fn all_examples_tested() {
```nix-repl
nix-repl> 1 + 2
3
```
"})
.unwrap();
Expand All @@ -39,8 +41,8 @@ fn all_examples_tested() {

eelco.assert().success().stderr(
predicates::str::contains(format!("PASS: {file_path}:1"))
.and(predicates::str::contains(format!("PASS: {file_path}:6")))
.and(predicates::str::contains(format!("PASS: {file_path}:10"))),
.and(predicates::str::contains(format!("PASS: {file_path}:7")))
.and(predicates::str::contains(format!("PASS: {file_path}:11"))),
);
});
}
28 changes: 27 additions & 1 deletion tests/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn fails_to_parse() {
eelco
.assert()
.failure()
.stderr("Error: missing prompt LFLine(\"nix-shnepl> nope\\n\")\n");
.stderr("Error: expected prompt, found LFLine(\"nix-shnepl> nope\\n\")\n");
});
}

Expand All @@ -29,6 +29,7 @@ fn result_mismatch() {
```nix-repl
nix-repl> 1 + 1
3
```
"})
.unwrap();
Expand All @@ -50,6 +51,7 @@ fn pass() {
```nix-repl
nix-repl> 1 + 1
2
```
"})
.unwrap();
Expand All @@ -69,6 +71,30 @@ fn pass_assignment() {
file.write_str(indoc! {"
```nix-repl
nix-repl> a = 1
```
"})
.unwrap();

let file_path = file.path().to_str().unwrap();

eelco
.assert()
.success()
.stderr(format!("PASS: {file_path}:1\n"));
});
}

#[test]
fn pass_subsequent_query() {
with_eelco(|file, eelco| {
file.write_str(indoc! {"
```nix-repl
nix-repl> a = 1
nix-repl> a
1
```
"})
.unwrap();
Expand Down

0 comments on commit 4ae18b7

Please sign in to comment.