Skip to content

Commit

Permalink
Consolidate most examples data into a YAML file (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
samestep authored Apr 4, 2021
1 parent d85fc00 commit c6235de
Show file tree
Hide file tree
Showing 20 changed files with 213 additions and 76 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ jobs:
run: cargo install --locked --path .
# partially covered by tests/cli.rs, but this checks the shebang
- name: Check output of hello example
run: examples/hello.qn | git diff --no-index examples/run/out/hello.txt -
run: diff <(examples/hello.qn) <(echo 'Hello, world!')
- name: Upload quench for next job
uses: actions/upload-artifact@v2
with:
Expand Down
21 changes: 20 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,6 @@ goldenfile = "1"
itertools = "0.10"
pretty_assertions = "0.7"
regex = "1"
serde = "1"
serde_yaml = "0.8"
toml = "0.5"
yaml-rust = "0.4"
Empty file.
Empty file removed examples/compile/err/hello.txt
Empty file.
3 changes: 0 additions & 3 deletions examples/compile/err/hello_syntax_errors.txt

This file was deleted.

Empty file.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Empty file removed examples/run/err/complicated.txt
Empty file.
Empty file removed examples/run/err/hello.txt
Empty file.
3 changes: 0 additions & 3 deletions examples/run/err/hello_syntax_errors.txt

This file was deleted.

5 changes: 0 additions & 5 deletions examples/run/out/complicated.txt

This file was deleted.

1 change: 0 additions & 1 deletion examples/run/out/hello.txt

This file was deleted.

Empty file.
3 changes: 1 addition & 2 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -563,8 +563,7 @@ mod tests {

#[test]
fn test_diagnostics_hello_world() {
let (db, uri) =
foo_db(slurp::read_all_to_string("examples/hello_syntax_errors.qn").unwrap());
let (db, uri) = foo_db(slurp::read_all_to_string("examples/errors.qn").unwrap());
let diagnostics = db.diagnostics(uri);
assert_eq!(
diagnostics,
Expand Down
180 changes: 152 additions & 28 deletions tests/cli.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use assert_cmd::{assert::OutputAssertExt, prelude::CommandCargoExt};
use goldenfile::Mint;
use pretty_assertions::assert_eq;
use std::{
collections::BTreeMap,
ffi::OsStr,
path::Path,
fs::File,
io,
path::{Path, PathBuf},
process::{Command, Output},
str,
};
use std::{fs, io::Write};

Expand All @@ -17,41 +22,160 @@ fn test_help() {
file.write_all(&assert.get_output().stdout).unwrap();
}

fn assert_expected(subcmd: &str, src: &Path) {
let mut cmd = Command::cargo_bin("quench").unwrap();
let assert = cmd.env("NO_COLOR", "1").arg(subcmd).arg(&src).assert();
let Output {
status,
stdout,
stderr,
} = assert.get_output();
fn subcmd<I, S>(name: &str, args: I) -> Output
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
Command::cargo_bin("quench")
.unwrap()
.env("NO_COLOR", "1")
.arg(name)
.args(args)
.output()
.unwrap()
}

if stderr.is_empty() {
assert!(status.success());
fn to_nonempty_string(bytes: Vec<u8>) -> Option<String> {
if bytes.is_empty() {
None
} else {
assert!(!status.success());
Some(str::from_utf8(&bytes).unwrap().to_string())
}
}

#[derive(Debug, serde::Deserialize, PartialEq)]
struct Example {
compile: Option<String>,
status: Option<i32>,
out: Option<String>,
err: Option<String>,
}

let dir = src.parent().unwrap().join(subcmd);
let txt_path = src.with_extension("txt");
let txt_name = Path::new(txt_path.file_name().unwrap());
fn try_example(path: PathBuf) -> (String, Example) {
let stem = path.file_stem().unwrap().to_str().unwrap().to_string();
let example = {
let Output {
status,
stdout,
stderr,
} = subcmd("compile", &[&path]);
if status.success() {
assert!(stderr.is_empty());
{
let mut mint = Mint::new("examples");
let mut file = mint
.new_goldenfile(Path::new(&stem).with_extension("js"))
.unwrap();
file.write_all(&stdout).unwrap();
}

let mut out_mint = Mint::new(dir.join("out"));
let mut out = out_mint.new_goldenfile(txt_name).unwrap();
out.write_all(stdout).unwrap();
let Output {
status,
stdout,
stderr,
} = subcmd("run", &[path]);
Example {
compile: None,
// we don't just use status.code() here, because we assume there was an exit code
status: {
let code = status.code().unwrap();
if code == 0 {
None
} else {
Some(code)
}
},
out: to_nonempty_string(stdout),
err: to_nonempty_string(stderr),
}
} else {
assert!(stdout.is_empty());
Example {
compile: to_nonempty_string(stderr),
status: None,
out: None,
err: None,
}
}
};
(stem, example)
}

let mut err_mint = Mint::new(dir.join("err"));
let mut err = err_mint.new_goldenfile(txt_name).unwrap();
err.write_all(stderr).unwrap();
fn write_literal(writer: &mut impl Write, key: &str, value: &Option<String>) -> io::Result<()> {
if let Some(string) = value {
write!(writer, " {}: |\n", key)?;
for line in string.lines() {
write!(writer, " {}\n", line)?;
}
}
Ok(())
}

#[test]
fn test_examples() {
for entry in fs::read_dir("examples").unwrap() {
let path = entry.unwrap().path();
if path.extension() == Some(OsStr::new("qn")) {
assert_expected("compile", &path);
assert_expected("run", &path);
fn write_example(writer: &mut impl Write, name: &str, example: &Example) -> io::Result<()> {
write!(writer, "{}:\n", name)?;
let Example {
compile,
status,
out,
err,
} = example;
write_literal(writer, "compile", compile)?;
if let Some(code) = status {
write!(writer, " status: {}\n", code)?;
}
write_literal(writer, "out", out)?;
write_literal(writer, "err", err)?;
Ok(())
}

type Examples = BTreeMap<String, Example>;

fn write_examples(writer: &mut impl Write, examples: &Examples) -> io::Result<()> {
let mut it = examples.iter();
if let Some((name, example)) = it.next() {
write_example(writer, name, example)?;
for (name, example) in it {
write!(writer, "\n")?;
write_example(writer, name, example)?;
}
}
Ok(())
}

#[test]
fn test_examples() {
let examples: Examples = fs::read_dir("examples")
.unwrap()
.filter_map(|entry| {
let path = entry.unwrap().path();
if path.extension() == Some(OsStr::new("qn")) {
Some(try_example(path))
} else {
None
}
})
.collect();

let goldenfiles = "tests/goldenfiles";
let filename = "examples.yml";

// assert that the actual YAML file matches what we generate from running the examples; the
// goldenfile paradigm provides a nice testing UX via its REGENERATE_GOLDENFILES env var, but we
// need to use a custom write_examples function because yaml-rust doesn't emit literal scalars
// https://github.com/chyh1990/yaml-rust/pull/132 https://github.com/chyh1990/yaml-rust/pull/137
write_examples(
&mut Mint::new(goldenfiles).new_goldenfile(filename).unwrap(),
&examples,
)
.unwrap();

// redundant check, to ensure that our write_examples function works correctly
assert_eq!(
examples,
serde_yaml::from_reader::<File, Examples>(
File::open(Path::new(goldenfiles).join(filename)).unwrap()
)
.unwrap(),
);
}
17 changes: 17 additions & 0 deletions tests/goldenfiles/examples.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
complicated:
out: |
foo
bar
undefined
👻 ba # not a comment
z 🙃
errors:
compile: |
0:6 to 0:14 Error: syntax (ERROR (string))
0:24 to 0:24 Error: syntax (MISSING ";")
Error: Failed to parse.
hello:
out: |
Hello, world!
51 changes: 20 additions & 31 deletions tests/version.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use comrak::ComrakOptions;
use regex::Regex;
use std::{collections::HashSet, path::PathBuf, str};
use yaml_rust::{Yaml, YamlLoader};

#[test]
fn test_version() {
Expand Down Expand Up @@ -76,36 +75,26 @@ fn test_minimum_rustc() {

let ci_versions: HashSet<String> = {
let re = Regex::new(r"^(\d+\.\d+)\.\d+$").unwrap();
YamlLoader::load_from_str(&slurp::read_all_to_string(".github/workflows/ci.yml").unwrap())
.unwrap()
.get(0)
.unwrap()
.as_hash()
.unwrap()
.get(&Yaml::String(String::from("jobs")))
.unwrap()
.as_hash()
.unwrap()
.get(&Yaml::String(String::from("rust")))
.unwrap()
.as_hash()
.unwrap()
.get(&Yaml::String(String::from("strategy")))
.unwrap()
.as_hash()
.unwrap()
.get(&Yaml::String(String::from("matrix")))
.unwrap()
.as_hash()
.unwrap()
.get(&Yaml::String(String::from("rust")))
.unwrap()
.as_vec()
.unwrap()
.iter()
.filter_map(|version| re.captures(version.as_str().unwrap()))
.map(|m| String::from(&m[1]))
.collect()
serde_yaml::from_str::<serde_yaml::Value>(
&slurp::read_all_to_string(".github/workflows/ci.yml").unwrap(),
)
.unwrap()
.get("jobs")
.unwrap()
.get("rust")
.unwrap()
.get("strategy")
.unwrap()
.get("matrix")
.unwrap()
.get("rust")
.unwrap()
.as_sequence()
.unwrap()
.iter()
.filter_map(|version| re.captures(version.as_str().unwrap()))
.map(|m| String::from(&m[1]))
.collect()
};
assert!(
readme_versions.is_subset(&ci_versions),
Expand Down

0 comments on commit c6235de

Please sign in to comment.