174 lines
6.1 KiB
Rust
174 lines
6.1 KiB
Rust
//! `flowrsc run`: a document run with no editor, at T0 and from its exported program, the two
|
|
//! side by side. The T1 runs build real exports with cargo (`--locked`), each program alone, so
|
|
//! they run on the build host with the rest of the suite.
|
|
|
|
use flowrs_cli::{Cli, Command, EXIT_INVALID, EXIT_OK, EXIT_USAGE, run};
|
|
use std::path::{Path, PathBuf};
|
|
use tempfile::TempDir;
|
|
|
|
fn root() -> PathBuf {
|
|
Path::new(env!("CARGO_MANIFEST_DIR"))
|
|
.parent()
|
|
.and_then(|p| p.parent())
|
|
.unwrap()
|
|
.to_path_buf()
|
|
}
|
|
|
|
struct Ran {
|
|
code: i32,
|
|
out: String,
|
|
err: String,
|
|
}
|
|
|
|
impl Ran {
|
|
fn json(&self) -> serde_json::Value {
|
|
serde_json::from_str(&self.out)
|
|
.unwrap_or_else(|e| panic!("{e}: {}\n{}", self.out, self.err))
|
|
}
|
|
}
|
|
|
|
fn flowrsc_run(graph: &Path, tier: &str, target: Option<&Path>, files: &[String]) -> Ran {
|
|
let cli = Cli {
|
|
command: Command::Run {
|
|
graph: graph.to_path_buf(),
|
|
tier: tier.into(),
|
|
json: true,
|
|
steps: None,
|
|
files: files.to_vec(),
|
|
project: None,
|
|
target_dir: target.map(Path::to_path_buf),
|
|
offline: false,
|
|
tolerance: 0.0,
|
|
timeout: 120,
|
|
},
|
|
quiet: false,
|
|
};
|
|
let (mut out, mut err) = (Vec::new(), Vec::new());
|
|
let code = run(&cli, &mut out, &mut err);
|
|
Ran {
|
|
code,
|
|
out: String::from_utf8(out).unwrap(),
|
|
err: String::from_utf8(err).unwrap(),
|
|
}
|
|
}
|
|
|
|
/// The reading that carries `port`, at `tier`.
|
|
fn reading(j: &serde_json::Value, port: &str, tier: &str) -> serde_json::Value {
|
|
j["readings"]
|
|
.as_array()
|
|
.unwrap()
|
|
.iter()
|
|
.find(|r| r["ports"].as_array().unwrap().iter().any(|p| p == port))
|
|
.unwrap_or_else(|| panic!("no reading carries {port}: {j}"))[tier]
|
|
.clone()
|
|
}
|
|
|
|
/// The specification's example runs with no compiler: two and three make five, at T0.
|
|
#[test]
|
|
fn a_document_runs_at_t0_with_no_compiler() {
|
|
let doc = root().join("spec/conformance/flowrs/add-two-numbers.flowrs");
|
|
let r = flowrsc_run(&doc, "t0", None, &[]);
|
|
assert_eq!(r.code, EXIT_OK, "{}", r.err);
|
|
let j = r.json();
|
|
assert_eq!(j["tiers"], serde_json::json!(["t0"]));
|
|
assert_eq!(reading(&j, "n2.sum", "t0").as_f64(), Some(5.0));
|
|
assert!(j["agree"].is_null(), "one tier agrees with nothing");
|
|
// The graph text of the same graph reads the same.
|
|
let text = flowrsc_run(
|
|
&root().join("spec/conformance/fdsl/add.fdsl"),
|
|
"t0",
|
|
None,
|
|
&[],
|
|
);
|
|
assert_eq!(reading(&text.json(), "n2.sum", "t0").as_f64(), Some(5.0));
|
|
}
|
|
|
|
/// The exported program computes what the interpreter does, reading for reading, for documents
|
|
/// across the corpus: arithmetic, comparisons, a clamp, a rail check, lists, text, a sine, a
|
|
/// filtered spectrum (flowRS's DSP crates carried in vendor/) and a held loop over ten passes.
|
|
#[test]
|
|
fn t0_and_the_exported_program_agree_on_the_corpus_documents() {
|
|
let target = TempDir::new().unwrap();
|
|
let mut compared = 0;
|
|
let mut read = 0;
|
|
for rel in [
|
|
"e2e/fixtures/apps/float_sum.flowrs",
|
|
"e2e/fixtures/apps/scale_and_offset.flowrs",
|
|
"e2e/fixtures/apps/compare_threshold.flowrs",
|
|
"e2e/fixtures/apps/clamp_reading.flowrs",
|
|
"e2e/fixtures/apps/greater_than.flowrs",
|
|
"e2e/fixtures/apps/sum_to_5.flowrs",
|
|
"e2e/fixtures/apps/rail_check.flowrs",
|
|
"e2e/fixtures/apps/vec_push_len.flowrs",
|
|
"e2e/fixtures/apps/logic_and_strings.flowrs",
|
|
"e2e/fixtures/apps/sine_plot.flowrs",
|
|
"e2e/fixtures/apps/dsp_filter_spectrum.flowrs",
|
|
"e2e/fixtures/appexport/counter.fdsl",
|
|
] {
|
|
let r = flowrsc_run(&root().join(rel), "both", Some(target.path()), &[]);
|
|
assert_eq!(r.code, EXIT_OK, "{rel}: {}\n{}", r.out, r.err);
|
|
let j = r.json();
|
|
assert_eq!(j["agree"], true, "{rel}: {j}");
|
|
assert_eq!(j["tiers"], serde_json::json!(["t0", "t1"]));
|
|
for rd in j["readings"].as_array().unwrap() {
|
|
compared += 1;
|
|
if !rd["t1"].is_null() {
|
|
read += 1;
|
|
}
|
|
}
|
|
}
|
|
assert!(compared >= 12, "{compared} readings compared");
|
|
assert!(
|
|
read * 10 >= compared * 9,
|
|
"{read} of {compared} readings were printed"
|
|
);
|
|
}
|
|
|
|
/// A held loop runs the passes its export runs: the counter has counted to the export's steps.
|
|
#[test]
|
|
fn a_held_loop_runs_the_exports_passes() {
|
|
let doc = root().join("e2e/fixtures/appexport/counter.fdsl");
|
|
let r = flowrsc_run(&doc, "t0", None, &[]);
|
|
assert_eq!(r.code, EXIT_OK, "{}", r.err);
|
|
let j = r.json();
|
|
let steps = j["steps"].as_f64().unwrap();
|
|
assert!(steps > 1.0, "a step loop runs more than one pass");
|
|
assert_eq!(reading(&j, "n1.sum", "t0").as_f64(), Some(steps));
|
|
}
|
|
|
|
/// A document whose app reads nothing has no readings, and says so rather than looking empty.
|
|
#[test]
|
|
fn a_document_whose_app_reads_nothing_says_so() {
|
|
let r = flowrsc_run(
|
|
&root().join("e2e/fixtures/apps/held_counter.flowrs"),
|
|
"t0",
|
|
None,
|
|
&[],
|
|
);
|
|
assert_eq!(r.code, EXIT_OK, "{}", r.err);
|
|
assert_eq!(r.json()["readings"], serde_json::json!([]));
|
|
assert!(r.err.contains("reads nothing"), "{}", r.err);
|
|
}
|
|
|
|
#[test]
|
|
fn what_cannot_run_is_refused_with_why() {
|
|
let dir = TempDir::new().unwrap();
|
|
let missing = dir.path().join("nope.flowrs");
|
|
assert_eq!(flowrsc_run(&missing, "t0", None, &[]).code, EXIT_USAGE);
|
|
let doc = root().join("spec/conformance/flowrs/add-two-numbers.flowrs");
|
|
let r = flowrsc_run(&doc, "t2", None, &[]);
|
|
assert_eq!(r.code, EXIT_USAGE);
|
|
assert!(r.err.contains("t0, t1 or both"), "{}", r.err);
|
|
let r = flowrsc_run(&doc, "t0", None, &["no-equals-sign".into()]);
|
|
assert_eq!(r.code, EXIT_USAGE);
|
|
assert!(r.err.contains("NAME=PATH"), "{}", r.err);
|
|
// A graph whose wire comes from nowhere is invalid: judged, not run.
|
|
let r = flowrsc_run(
|
|
&root().join("spec/conformance/flowrs/dangling-wire.flowrs"),
|
|
"t0",
|
|
None,
|
|
&[],
|
|
);
|
|
assert_eq!(r.code, EXIT_INVALID, "{}", r.err);
|
|
assert!(r.out.is_empty());
|
|
}
|