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 only4
// 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 student8
// scores, classifies each score pass/fail against a threshold and into a letter9
// 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 is11
// asserted byte-for-byte.12
//13
// Modules exercised together:14
// - io : print, str, format15
// - statistics : mean, median, stdev16
// - string : center, ljust, rjust17
// - builtins : len, float (to_float)18
// - math : round, min, max19
//20
// Note: purrc treats a newline as a statement terminator, so every call stays21
// on a single source line; wide rows are assembled via `+` concatenation.22
#include "e2e_harness.hpp"24
TEST(SystemApps, GradeReport) {25
e2e::expect_e2e("app_grades", R"PURR(import io26
import string27
import math28
import statistics29
import builtins31
# --- classify a numeric score into a letter grade ---32
fn 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"38
}40
# --- fixed, deterministic dataset ---41
let names = ["Ada", "Bjarne", "Cy", "Dennis", "Edsger"]42
let scores = [91.5, 67.0, 100.0, 82.25, 58.5]43
let pass_mark = 70.045
# ---------------- header ----------------46
io.print(string.center(" GRADE REPORT ", 40, "="))47
let head = string.ljust("NAME", 10) + " " + string.rjust("SCORE", 7) + " " + string.rjust("GRADE", 6) + " STATUS"48
io.print(head)49
io.print(string.ljust("", 40, "-"))51
# ---------------- per-student table rows ----------------52
let passed = 053
let hi = scores[0]54
let lo = scores[0]55
let i = 056
while 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 + 165
}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 + 171
}73
# ---------------- summary ----------------74
io.print(string.ljust("", 40, "-"))75
let n = len(scores)76
let rate = math.round(100.0 * float(passed) / float(n))77
let stdev2 = math.round(statistics.stdev(scores) * 100.0) / 100.078
io.print(io.format("students : {}", n))79
io.print(io.format("passed : {}/{} ({} pct)", passed, n, rate))80
io.print(io.format("mean : {}", statistics.mean(scores)))81
io.print(io.format("median : {}", statistics.median(scores)))82
io.print(io.format("stdev : {}", stdev2))83
io.print(io.format("range : {} .. {}", math.round(lo), math.round(hi)))84
io.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");102
}