use std::cmp::Ordering;
use argonauts_core::events::task::{TaskKind, TaskSpec};
use argonauts_core::limits::TrackRecord;
use argonauts_core::names::ModelRef;
use argonauts_core::settings::fallback::UnknownTier;
use crate::engine::host::Host;
pub const MIN_RECORD: u32 = 5;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Strength {
pub tier: Option<u8>,
pub record: Option<TrackRecord>,
}
pub fn compare(fallback: Strength, original: Strength, unknown: UnknownTier) -> Ordering {
if let (Some(fallback), Some(original)) = (fallback.tier, original.tier) {
return fallback.cmp(&original);
}
if let (Some(fallback), Some(original)) = (fallback.record, original.record)
&& fallback.of >= MIN_RECORD
&& original.of >= MIN_RECORD
{
let left = u64::from(fallback.passed) * u64::from(original.of);
let right = u64::from(original.passed) * u64::from(fallback.of);
return left.cmp(&right);
}
match unknown {
UnknownTier::Weaker => Ordering::Less,
UnknownTier::Equal => Ordering::Equal,
}
}
pub fn task_type(kind: &TaskKind) -> &'static str {
match kind {
TaskKind::Captain => "captain",
TaskKind::Interface => "interface",
TaskKind::AcceptanceTests { .. } => "acceptance_tests",
TaskKind::Build => "build",
TaskKind::Chore => "chore",
TaskKind::Docs => "docs",
TaskKind::Experiment { .. } => "experiment",
}
}
pub fn strength(host: &Host, worker: &ModelRef, task: &TaskSpec) -> Strength {
let services = host.services();
Strength {
tier: services.registry.get(worker).and_then(|entry| entry.tier),
record: services.track_records.get(worker, task_type(&task.kind)),
}
}
pub fn weaker(
host: &Host,
task: &TaskSpec,
worker: &ModelRef,
than: &ModelRef,
unknown: UnknownTier,
) -> bool {
worker != than
&& compare(
strength(host, worker, task),
strength(host, than, task),
unknown,
) == Ordering::Less
}
pub fn strongest<'a>(
host: &Host,
task: &TaskSpec,
workers: impl IntoIterator<Item = &'a ModelRef>,
unknown: UnknownTier,
) -> Option<ModelRef> {
let mut best: Option<&ModelRef> = None;
for worker in workers {
best = match best {
Some(current) if !weaker(host, task, current, worker, unknown) => Some(current),
_ => Some(worker),
};
}
best.cloned()
}
#[cfg(test)]
mod tests {
use super::*;
fn tier(tier: u8) -> Strength {
Strength {
tier: Some(tier),
record: None,
}
}
fn record(passed: u32, of: u32) -> Strength {
Strength {
tier: None,
record: Some(TrackRecord { passed, of }),
}
}
const UNKNOWN: Strength = Strength {
tier: None,
record: None,
};
#[test]
fn tiers_you_set_decide_first() {
let weaker = UnknownTier::Weaker;
assert_eq!(compare(tier(3), tier(5), weaker), Ordering::Less);
assert_eq!(compare(tier(5), tier(5), weaker), Ordering::Equal);
assert_eq!(compare(tier(5), tier(4), weaker), Ordering::Greater);
let both = |t, passed, of| Strength {
tier: Some(t),
record: Some(TrackRecord { passed, of }),
};
assert_eq!(
compare(both(5, 1, 10), both(4, 10, 10), weaker),
Ordering::Greater
);
}
#[test]
fn track_records_need_five_finished_tasks_each() {
let weaker = UnknownTier::Weaker;
assert_eq!(
compare(record(6, 10), record(9, 10), weaker),
Ordering::Less
);
assert_eq!(
compare(record(4, 5), record(8, 10), weaker),
Ordering::Equal
);
assert_eq!(
compare(record(9, 10), record(3, 5), weaker),
Ordering::Greater
);
assert_eq!(compare(record(4, 4), record(1, 10), weaker), Ordering::Less);
assert_eq!(
compare(record(4, 4), record(1, 10), UnknownTier::Equal),
Ordering::Equal
);
}
#[test]
fn unknown_tiers_follow_the_setting() {
assert_eq!(
compare(UNKNOWN, UNKNOWN, UnknownTier::Weaker),
Ordering::Less
);
assert_eq!(
compare(UNKNOWN, UNKNOWN, UnknownTier::Equal),
Ordering::Equal
);
assert_eq!(
compare(tier(5), UNKNOWN, UnknownTier::Weaker),
Ordering::Less
);
assert_eq!(
compare(UNKNOWN, tier(1), UnknownTier::Equal),
Ordering::Equal
);
}
#[test]
fn task_types_name_the_task_kind() {
assert_eq!(task_type(&TaskKind::Build), "build");
assert_eq!(
task_type(&TaskKind::AcceptanceTests {
for_task: argonauts_core::TaskId::numbered(1)
}),
"acceptance_tests"
);
}
}