///|
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")
}