///|
/// Ordered results for all requested features.
pub(all) struct ResultCollection {
feature_ids : Array[String]
results : Array[FeatureResult]
pass_count : Int
fail_count : Int
unmeasured_count : Int
}
///|
fn first_observation(
feature_id : String,
observations : Array[Observation],
) -> Observation? {
let mut result = None
for observation in observations {
if observation.feature_id == feature_id && result is None {
result = Some(observation)
}
}
result
}
///|
/// Collect one feature result per input feature in source order.
pub fn collect_feature_results(
features : Array[Feature],
observations : Array[Observation],
) -> ResultCollection {
let feature_ids = []
let results = []
let mut pass_count = 0
let mut fail_count = 0
let mut unmeasured_count = 0
for feature in features {
let result = feature_result(
feature~,
observation=first_observation(feature.id, observations),
)
feature_ids.push(feature.id)
results.push(result)
match result.verdict {
Pass => pass_count += 1
Fail(_) => fail_count += 1
Unmeasured => unmeasured_count += 1
}
}
{ feature_ids, results, pass_count, fail_count, unmeasured_count }
}
///|
/// Find the first result for a feature identifier.
pub fn result_for_feature(
collection : ResultCollection,
feature_id : String,
) -> FeatureResult? {
let mut result = None
for item in collection.results {
if item.feature_id == feature_id && result is None {
result = Some(item)
}
}
result
}
///|
/// Return result coverage as a fraction of requested features.
pub fn result_coverage(collection : ResultCollection) -> Double {
let total = collection.results.length()
if total == 0 {
0.0
} else {
(collection.pass_count + collection.fail_count).to_double() /
total.to_double()
}
}
///|
/// Return whether every feature has a classified result.
pub fn result_is_complete(collection : ResultCollection) -> Bool {
collection.results.length() > 0 && collection.unmeasured_count == 0
}
///|
/// Return whether every classified feature passed.
pub fn result_is_clean(collection : ResultCollection) -> Bool {
result_is_complete(collection) && collection.fail_count == 0
}
///|
/// Return the worst result by fail/unmeasured/pass priority.
pub fn worst_feature_result(collection : ResultCollection) -> FeatureResult? {
let mut result = None
for item in collection.results {
match result {
None => result = Some(item)
Some(current) => {
let worse = worse_verdict(current.verdict, item.verdict)
if verdict_code(worse) == verdict_code(item.verdict) {
result = Some(item)
}
}
}
}
result
}
///|
/// Return the number of high-risk feature results.
pub fn result_high_risk_count(collection : ResultCollection) -> Int {
let mut result = 0
for item in collection.results {
if item.risk == RiskLevel::High {
result += 1
}
}
result
}
///|
/// Render stable collection counts.
pub fn render_result_collection(collection : ResultCollection) -> String {
"features=" +
collection.results.length().to_string() +
" pass=" +
collection.pass_count.to_string() +
" fail=" +
collection.fail_count.to_string() +
" unmeasured=" +
collection.unmeasured_count.to_string() +
" coverage=" +
result_coverage(collection).to_string()
}
///|
/// Return whether a collection has a hard failure.
pub fn result_has_failure(collection : ResultCollection) -> Bool {
collection.fail_count > 0
}
///|
/// Return the number of result records.
pub fn result_count(collection : ResultCollection) -> Int {
collection.results.length()
}