///|
pub(all) struct VariantSpec {
name : String
overrides : Array[(String, BtValue)]
expected : BtStatus
max_ticks : Int
} derive(Debug, Eq)
///|
pub fn variant(
name : String,
overrides : Array[(String, BtValue)],
expected? : BtStatus,
max_ticks? : Int,
) -> VariantSpec {
{
name,
overrides,
expected: expected.unwrap_or(Success),
max_ticks: max_ticks.unwrap_or(16),
}
}
///|
pub(all) struct VariantResult {
name : String
status : BtStatus
expected : BtStatus
ticks : Int
digest : String
ok : Bool
detail : String
} derive(Debug, Eq)
///|
pub fn VariantResult::to_line(self : VariantResult) -> String {
self.name +
": status=" +
self.status.to_text() +
", expected=" +
self.expected.to_text() +
", ticks=" +
self.ticks.to_string() +
", ok=" +
(if self.ok { "true" } else { "false" }) +
", digest=" +
self.digest +
", detail=" +
self.detail
}
///|
pub(all) struct MatrixReport {
name : String
results : Array[VariantResult]
} derive(Debug, Eq)
///|
pub fn MatrixReport::passed(self : MatrixReport) -> Int {
let mut count = 0
let mut i = 0
while i < self.results.length() {
if self.results[i].ok {
count = count + 1
}
i = i + 1
}
count
}
///|
pub fn MatrixReport::failed(self : MatrixReport) -> Int {
self.results.length() - self.passed()
}
///|
pub fn MatrixReport::ok(self : MatrixReport) -> Bool {
self.failed() == 0
}
///|
pub fn MatrixReport::summary(self : MatrixReport) -> String {
self.name +
": variants=" +
self.results.length().to_string() +
", passed=" +
self.passed().to_string() +
", failed=" +
self.failed().to_string()
}
///|
pub fn MatrixReport::lines(self : MatrixReport) -> Array[String] {
let out = Array::new()
out.push(self.summary())
let mut i = 0
while i < self.results.length() {
out.push(self.results[i].to_line())
i = i + 1
}
out
}
///|
pub fn run_variant_dsl(base_dsl : String, spec : VariantSpec) -> VariantResult {
let source = apply_overrides(base_dsl, spec.overrides)
match parse_dsl(source) {
Ok(doc) => {
let engine = new_engine(doc.tree, blackboard=doc.blackboard)
match engine.run_until_done(max_ticks=spec.max_ticks) {
Ok(result) => {
let ok = result.status == spec.expected
{
name: spec.name,
status: result.status,
expected: spec.expected,
ticks: result.tick,
digest: engine.trace_digest(),
ok,
detail: if ok { "matched expected status" } else { "unexpected status" },
}
}
Err(err) =>
{
name: spec.name,
status: Failure,
expected: spec.expected,
ticks: engine.tick_count,
digest: engine.trace_digest(),
ok: false,
detail: err.message(),
}
}
}
Err(err) =>
{
name: spec.name,
status: Failure,
expected: spec.expected,
ticks: 0,
digest: "parse-error",
ok: false,
detail: err.message(),
}
}
}
///|
pub fn run_matrix(
name : String,
base_dsl : String,
variants : Array[VariantSpec],
) -> MatrixReport {
let results = Array::new()
let mut i = 0
while i < variants.length() {
results.push(run_variant_dsl(base_dsl, variants[i]))
i = i + 1
}
{ name, results }
}
///|
pub fn boolean_matrix(
name : String,
source : String,
key : String,
true_expected : BtStatus,
false_expected : BtStatus,
max_ticks? : Int,
) -> MatrixReport {
run_matrix(
name,
source,
[
variant("true", [(key, BoolValue(true))], expected=true_expected, max_ticks=max_ticks.unwrap_or(16)),
variant("false", [(key, BoolValue(false))], expected=false_expected, max_ticks=max_ticks.unwrap_or(16)),
],
)
}
///|
pub fn int_sweep_matrix(
name : String,
source : String,
key : String,
values : Array[Int],
expected : BtStatus,
max_ticks? : Int,
) -> MatrixReport {
let variants = Array::new()
let mut i = 0
while i < values.length() {
variants.push(
variant(
key + "_" + values[i].to_string(),
[(key, IntValue(values[i]))],
expected~,
max_ticks=max_ticks.unwrap_or(16),
),
)
i = i + 1
}
run_matrix(name, source, variants)
}
///|
pub fn fixture_matrix_report() -> MatrixReport {
run_matrix(
"built_in_branch_matrix",
guarded_attack_dsl(),
[
variant("enemy_visible", [("enemy_visible", BoolValue(true)), ("ammo", IntValue(3))], expected=Success, max_ticks=8),
variant("enemy_missing", [("enemy_visible", BoolValue(false)), ("ammo", IntValue(3))], expected=Success, max_ticks=8),
variant("empty_ammo", [("enemy_visible", BoolValue(true)), ("ammo", IntValue(0))], expected=Success, max_ticks=8),
],
)
}
///|
pub fn apply_overrides(source : String, overrides : Array[(String, BtValue)]) -> String {
let mut out = source
let mut i = 0
while i < overrides.length() {
out = replace_blackboard_line(out, overrides[i].0, overrides[i].1)
i = i + 1
}
out
}
///|
pub fn matrix_markdown(report : MatrixReport) -> String {
let lines = Array::new()
lines.push("## Matrix: " + report.name)
lines.push("")
lines.push("- " + report.summary())
let mut i = 0
while i < report.results.length() {
lines.push("- " + report.results[i].to_line())
i = i + 1
}
join_strings(lines, "\n")
}