Merge api/trait-consistency (#76)
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct ConvergenceOptions {
|
pub struct ConvergenceOptions {
|
||||||
pub max_iter: usize,
|
pub max_iter: usize,
|
||||||
pub epsilon: f64,
|
pub epsilon: f64,
|
||||||
|
|||||||
+1
-1
@@ -43,7 +43,7 @@ pub trait Drift<T: Time>: Copy + Debug + Send + Sync {
|
|||||||
/// A non-finite gamma is caught a second time regardless:
|
/// A non-finite gamma is caught a second time regardless:
|
||||||
/// `History::converge` validates the drift variance each competitor actually
|
/// `History::converge` validates the drift variance each competitor actually
|
||||||
/// accumulates, which also covers a custom [`Drift`] implementation.
|
/// accumulates, which also covers a custom [`Drift`] implementation.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct ConstantDrift(f64);
|
pub struct ConstantDrift(f64);
|
||||||
|
|
||||||
impl ConstantDrift {
|
impl ConstantDrift {
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ impl DiffFactor {
|
|||||||
/// `p_draw` and `convergence` apply to ranked outcomes (`Game::ranked`).
|
/// `p_draw` and `convergence` apply to ranked outcomes (`Game::ranked`).
|
||||||
/// `score_sigma` applies only to scored outcomes (`Game::scored`); it controls
|
/// `score_sigma` applies only to scored outcomes (`Game::scored`); it controls
|
||||||
/// how much the engine trusts the observed score margin (smaller σ = more trust).
|
/// how much the engine trusts the observed score margin (smaller σ = more trust).
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct GameOptions {
|
pub struct GameOptions {
|
||||||
pub p_draw: f64,
|
pub p_draw: f64,
|
||||||
pub score_sigma: f64,
|
pub score_sigma: f64,
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
//! The traits a consumer needs on the public types, pinned so they cannot be
|
||||||
|
//! removed by accident.
|
||||||
|
//!
|
||||||
|
//! This is written from a consumer's position — deriving `Debug` on a struct
|
||||||
|
//! that *holds* a `History` — because that is the thing that failed. Asserting
|
||||||
|
//! `History: Debug` in isolation would not have caught the generic-bound half:
|
||||||
|
//! `Rating` derives `PartialEq`, but that is only usable if `D: PartialEq`, and
|
||||||
|
//! the crate's own only `Drift` impl did not satisfy it.
|
||||||
|
|
||||||
|
use trueskill_tt::{
|
||||||
|
ConstantDrift, ConvergenceOptions, ConvergenceReport, Event, GameOptions, Gaussian, History,
|
||||||
|
HistoryBuilder, InferenceError, Member, Outcome, Rating, Team,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The reported failure, verbatim: a consumer holding a history in app state.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(
|
||||||
|
dead_code,
|
||||||
|
reason = "held only so `derive(Debug)` has something to render"
|
||||||
|
)]
|
||||||
|
struct App {
|
||||||
|
history: History,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_struct_holding_a_history_can_derive_debug() {
|
||||||
|
let app = App {
|
||||||
|
history: History::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let rendered = format!("{app:?}");
|
||||||
|
|
||||||
|
// Summarising, not a dump of every skill store — the same choice `Joint`'s
|
||||||
|
// manual `Debug` makes about its n² factorisation.
|
||||||
|
assert!(rendered.contains("competitors"), "{rendered}");
|
||||||
|
assert!(rendered.contains("time_slices"), "{rendered}");
|
||||||
|
assert!(
|
||||||
|
!rendered.contains("SkillStore"),
|
||||||
|
"History's Debug should summarise, not dump: {rendered}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn history_builder_is_debug_and_clone() {
|
||||||
|
let b: HistoryBuilder<i64, ConstantDrift, _, &'static str> = History::builder();
|
||||||
|
let cloned = b.clone();
|
||||||
|
assert!(!format!("{cloned:?}").is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_and_input_value_types_are_comparable() {
|
||||||
|
assert_eq!(ConstantDrift::new(0.1), ConstantDrift::new(0.1));
|
||||||
|
assert_ne!(ConstantDrift::new(0.1), ConstantDrift::new(0.2));
|
||||||
|
|
||||||
|
assert_eq!(ConvergenceOptions::default(), ConvergenceOptions::default());
|
||||||
|
assert_eq!(GameOptions::default(), GameOptions::default());
|
||||||
|
|
||||||
|
// `Rating: PartialEq` is only reachable through `D: PartialEq`.
|
||||||
|
assert_eq!(Rating::<i64, ConstantDrift>::default(), Rating::default());
|
||||||
|
assert_ne!(
|
||||||
|
Rating::default(),
|
||||||
|
Rating::<i64, ConstantDrift>::default().with_drift_scale(2.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(Member::new("a"), Member::new("a"));
|
||||||
|
assert_ne!(Member::new("a"), Member::new("b"));
|
||||||
|
assert_eq!(
|
||||||
|
Team::with_members([Member::new("a")]),
|
||||||
|
Team::with_members([Member::new("a")])
|
||||||
|
);
|
||||||
|
|
||||||
|
let event = || Event {
|
||||||
|
time: 1,
|
||||||
|
teams: [
|
||||||
|
Team::with_members([Member::new("a")]),
|
||||||
|
Team::with_members([Member::new("b")]),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
outcome: Outcome::winner(0, 2),
|
||||||
|
};
|
||||||
|
assert_eq!(event(), event());
|
||||||
|
|
||||||
|
assert_eq!(Gaussian::default(), Gaussian::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_history_is_send_and_sync_and_default() {
|
||||||
|
fn assert_send_sync<X: Send + Sync>() {}
|
||||||
|
assert_send_sync::<History>();
|
||||||
|
assert_send_sync::<InferenceError>();
|
||||||
|
|
||||||
|
let mut h = History::default();
|
||||||
|
let report: ConvergenceReport = h.converge().expect("an empty history converges");
|
||||||
|
assert_eq!(report, report.clone());
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user