//! Headline RED-pin for issue #61 ("[series] Step 1 — primitive //! Series over {Int, Float}"): the simple-moving-average (SMA) worked //! example from `design/models/0007-kernel-extensions.md` §"Worked //! example — `Series` for streaming analytics" must `ail build //! --alloc=rc` to a native binary, run, and print the correct moving //! averages. //! //! Property protected: the kernel-tier `Series` library extension //! works end-to-end as an ordinary AILang consumer sees it — the bare //! kernel-tier type `(con Series (con Float))`, construction via //! `(new Series (con Float) 3)`, type-scoped ops `Series.push`, //! `Series.at`, `Series.total_count`, the bounded ring-buffer //! semantics (window size 3), financial-style indexing (index 0 = //! newest), and count/total bookkeeping. This single fixture //! transitively forces the whole Series surface to be correct: a //! wrong ring-buffer, a wrong index orientation, or wrong total_count //! bookkeeping would print different averages. //! //! The SMA fixture (`examples/series_sma.ail`) is the worked example //! from model 0007 with two VERIFIED corrections applied: //! - `>=` (line 77 of 0007) replaced with the polymorphic Ord //! prelude helper `ge` — `>=` does not exist as a symbol; both //! operands here are `Int`. //! - a newline separator (`(do io/print_str "\n")`) appended to each //! emitted average — `print` emits no trailing newline, so without //! it the four averages concatenate into an unreadable, unassertable //! blob. //! Everything else in the worked example is valid against today's tool. //! //! Why this is RED right now: the `series` kernel module does not exist //! yet. `ail check examples/series_sma.ail` rejects with //! `[bare-cross-module-type-ref]` — bare type `Series` not in scope, //! candidate list empty — i.e. the feature is ABSENT, not a parse / //! operator / scaffolding error. The GREEN side (authoring the kernel //! `series` module + its 5 ops) is a later `implement` mini-mode //! dispatch; this pin authors only the SMA *consumer*. //! //! This is a CLI-boundary E2E test because the acceptance spans the //! check -> mono -> codegen -> link -> run pipeline and the symptom is //! the printed moving averages of the produced binary. //! //! Expected stdout was derived by probing the live `%g`-style float //! formatter INDEPENDENTLY of the (nonexistent) Series, so the //! assertion encodes the intended averages formatted exactly as the //! tool will format them once Series exists. Push stream //! `1.0, 5.0, 3.0, 8.0, 6.0, 2.0`, window size 3, financial index //! (0 = newest), `emit_if_full` emits once `total_count >= n`: //! - after push 3.0: (3+5+1)/3 = 9/3 -> "3.0" //! - after push 8.0: (8+3+5)/3 = 16/3 -> "5.33333" //! - after push 6.0: (6+8+3)/3 = 17/3 -> "5.66667" //! - after push 2.0: (2+6+8)/3 = 16/3 -> "5.33333" //! Probe command (run this session): //! ./target/release/ail run /tmp/sma_probe.ail --alloc=rc //! where /tmp/sma_probe.ail printed each of 9.0/3, 16.0/3, 17.0/3, //! 16.0/3 via `(app print (app / X (app int_to_float 3)))` separated //! by `(do io/print_str "\n")`. Captured bytes (od -c): //! `3.0\n5.33333\n5.66667\n5.33333\n`. After `.trim()`: //! `3.0\n5.33333\n5.66667\n5.33333`. use std::path::{Path, PathBuf}; use std::process::Command; fn repo_root() -> PathBuf { let manifest_dir = env!("CARGO_MANIFEST_DIR"); Path::new(manifest_dir) .parent() .unwrap() .parent() .unwrap() .to_path_buf() } /// The SMA worked example from model 0007 (a pure consumer of the /// kernel-tier `Series` type) must build under `--alloc=rc`, run, and /// print the four correct moving averages, one per line. Green ⇒ the /// `Series` ring buffer, financial indexing, and count bookkeeping all /// work end-to-end. #[test] fn series_sma_worked_example_builds_runs_correct_output() { let fixture = repo_root().join("examples/series_sma.ail"); let tmp = std::env::temp_dir().join(format!( "ailang_series_sma_{}", std::process::id() )); std::fs::create_dir_all(&tmp).unwrap(); let out = tmp.join("bin"); let build = Command::new(env!("CARGO_BIN_EXE_ail")) .args(["build", fixture.to_str().unwrap(), "--alloc=rc", "-o"]) .arg(&out) .output() .expect("ail build failed to spawn"); assert!( build.status.success(), "ail build --alloc=rc failed for the SMA worked example (the \ kernel-tier `Series` library extension): stderr={}", String::from_utf8_lossy(&build.stderr) ); let run = Command::new(&out).output().expect("execute binary"); assert!( run.status.success(), "binary exited non-zero: {:?}", run.status ); let stdout = String::from_utf8_lossy(&run.stdout); assert_eq!( stdout.trim(), "3.0\n5.33333\n5.66667\n5.33333", "expected the four simple-moving-averages of the stream \ 1.0,5.0,3.0,8.0,6.0,2.0 (window 3, financial index, emit once \ total_count>=3), formatted by the live float formatter; got: {stdout:?}" ); }