cheatah
Source

tests/purrc/app_grades_test.cpp

1// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).
2// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.
3// System-level "real app" test: a multi-module grade-report program that only
4// passes if io + statistics + string + builtins + math all cooperate end to end.
5//
6// Unlike the per-module compile-run suites (which exercise one function each),
7// this is a small but genuine mini-app: it owns a fixed dataset of student
8// scores, classifies each score pass/fail against a threshold and into a letter
9// grade, builds an aligned table, and prints a summary computed from the data.
10// The program is fully DETERMINISTIC (no clocks/PIDs/RNG), so its stdout is
11// asserted byte-for-byte.
12//
13// Modules exercised together:
14// - io : print, str, format
15// - statistics : mean, median, stdev
16// - string : center, ljust, rjust
17// - builtins : len, float (to_float)
18// - math : round, min, max
19//
20// Note: purrc treats a newline as a statement terminator, so every call stays
21// on a single source line; wide rows are assembled via `+` concatenation.
22#include "e2e_harness.hpp"
24TEST(SystemApps, GradeReport) {
25 e2e::expect_e2e("app_grades", R"PURR(import io
26import string
27import math
28import statistics
29import builtins
31# --- classify a numeric score into a letter grade ---
32fn letter(score) {
33 if score >= 90.0 { return "A" }
34 if score >= 80.0 { return "B" }
35 if score >= 70.0 { return "C" }
36 if score >= 60.0 { return "D" }
37 return "F"
40# --- fixed, deterministic dataset ---
41let names = ["Ada", "Bjarne", "Cy", "Dennis", "Edsger"]
42let scores = [91.5, 67.0, 100.0, 82.25, 58.5]
43let pass_mark = 70.0
45# ---------------- header ----------------
46io.print(string.center(" GRADE REPORT ", 40, "="))
47let head = string.ljust("NAME", 10) + " " + string.rjust("SCORE", 7) + " " + string.rjust("GRADE", 6) + " STATUS"
48io.print(head)
49io.print(string.ljust("", 40, "-"))
51# ---------------- per-student table rows ----------------
52let passed = 0
53let hi = scores[0]
54let lo = scores[0]
55let i = 0
56while i < len(names) {
57 let nm = names[i]
58 let sc = scores[i]
59 hi = math.max(hi, sc)
60 lo = math.min(lo, sc)
61 let status = "FAIL"
62 if sc >= pass_mark {
63 status = "PASS"
64 passed = passed + 1
65 }
66 let col_name = string.ljust(nm, 10)
67 let col_score = string.rjust(io.str(math.round(sc)), 7)
68 let col_grade = string.rjust(letter(sc), 6)
69 io.print(col_name + " " + col_score + " " + col_grade + " " + status)
70 i = i + 1
73# ---------------- summary ----------------
74io.print(string.ljust("", 40, "-"))
75let n = len(scores)
76let rate = math.round(100.0 * float(passed) / float(n))
77let stdev2 = math.round(statistics.stdev(scores) * 100.0) / 100.0
78io.print(io.format("students : {}", n))
79io.print(io.format("passed : {}/{} ({} pct)", passed, n, rate))
80io.print(io.format("mean : {}", statistics.mean(scores)))
81io.print(io.format("median : {}", statistics.median(scores)))
82io.print(io.format("stdev : {}", stdev2))
83io.print(io.format("range : {} .. {}", math.round(lo), math.round(hi)))
84io.print(string.center(" END ", 40, "="))
85)PURR",
86 "============= GRADE REPORT =============\n"
87 "NAME SCORE GRADE STATUS\n"
88 "----------------------------------------\n"
89 "Ada 92 A PASS\n"
90 "Bjarne 67 D FAIL\n"
91 "Cy 100 A PASS\n"
92 "Dennis 82 B PASS\n"
93 "Edsger 59 F FAIL\n"
94 "----------------------------------------\n"
95 "students : 5\n"
96 "passed : 3/5 (60 pct)\n"
97 "mean : 79.85\n"
98 "median : 82.25\n"
99 "stdev : 17.09\n"
100 "range : 59 .. 100\n"
101 "================= END ==================\n");