@@ -3,10 +3,7 @@ use std::path::Path;
33use anyhow:: { anyhow, bail, ensure} ;
44use tracing:: info;
55
6- use crate :: types:: {
7- TestFixture ,
8- slot_clock:: { CurrentTimeInput , FromSlotInput , FromUnixTimeInput , SlotClockTest } ,
9- } ;
6+ use crate :: types:: { TestFixture , slot_clock:: SlotClockTest } ;
107
118/// Load a slot_clock test fixture from a JSON file
129pub fn load_slot_clock_test ( path : impl AsRef < Path > ) -> anyhow:: Result < TestFixture < SlotClockTest > > {
@@ -17,14 +14,33 @@ pub fn load_slot_clock_test(path: impl AsRef<Path>) -> anyhow::Result<TestFixtur
1714 . map_err ( |err| anyhow ! ( "Failed to parse test file {}: {err}" , path. display( ) ) )
1815}
1916
17+ /// Parse a JSON value as u64, accepting both integer and float representations.
18+ fn json_u64 ( v : & serde_json:: Value , field : & str ) -> anyhow:: Result < u64 > {
19+ v. as_u64 ( )
20+ . or_else ( || v. as_f64 ( ) . map ( |f| f as u64 ) )
21+ . ok_or_else ( || anyhow ! ( "Missing or non-numeric field: {field}" ) )
22+ }
23+
2024/// Run a single slot_clock test case
2125pub fn run_slot_clock_test ( test_name : & str , test : & SlotClockTest ) -> anyhow:: Result < ( ) > {
22- info ! (
23- "Running slot_clock test: {test_name} (operation={})" ,
24- test. operation
25- ) ;
26+ // Config lives at top-level in devnet5, inside output in devnet4.
27+ let cfg = test
28+ . config
29+ . as_ref ( )
30+ . or ( test. output . config . as_ref ( ) )
31+ . ok_or_else ( || anyhow ! ( "Missing config" ) ) ?;
32+
33+ // Operation kind: plain string in devnet4, object with `kind` field in devnet5.
34+ let kind: & str = if let Some ( s) = test. operation . as_str ( ) {
35+ s
36+ } else {
37+ test. operation [ "kind" ]
38+ . as_str ( )
39+ . ok_or_else ( || anyhow ! ( "Missing operation.kind" ) ) ?
40+ } ;
41+
42+ info ! ( "Running slot_clock test: {test_name} (operation={kind})" ) ;
2643
27- let cfg = & test. output . config ;
2844 ensure ! (
2945 cfg. seconds_per_slot * 1000 == cfg. intervals_per_slot * cfg. milliseconds_per_interval,
3046 "Inconsistent config: secondsPerSlot * 1000 != intervalsPerSlot * msPerInterval" ,
@@ -33,35 +49,46 @@ pub fn run_slot_clock_test(test_name: &str, test: &SlotClockTest) -> anyhow::Res
3349 let ms_per_slot = cfg. seconds_per_slot * 1000 ;
3450 let ms_per_interval = cfg. milliseconds_per_interval ;
3551
36- match test. operation . as_str ( ) {
52+ // In devnet4 params are in `input`; in devnet5 they are in the `operation` object.
53+ let params: & serde_json:: Value = if let Some ( inp) = test. input . as_ref ( ) {
54+ inp
55+ } else {
56+ & test. operation
57+ } ;
58+
59+ // Look up a u64 param, falling back to an alternate key name if the first is missing.
60+ let param_u64 = |key : & str , alt : Option < & str > | -> anyhow:: Result < u64 > {
61+ let v = params
62+ . get ( key)
63+ . or_else ( || alt. and_then ( |k| params. get ( k) ) )
64+ . ok_or_else ( || anyhow ! ( "Missing param: {key}" ) ) ?;
65+ json_u64 ( v, key)
66+ } ;
67+
68+ match kind {
3769 "current_slot" => {
38- let input: CurrentTimeInput = serde_json:: from_value ( test. input . clone ( ) ) ?;
39- let expected = test
40- . output
41- . slot
42- . ok_or_else ( || anyhow ! ( "Missing output.slot" ) ) ?;
43- let genesis_ms = input. genesis_time * 1000 ;
44- let actual = if input. current_time_ms <= genesis_ms {
70+ let genesis_time = param_u64 ( "genesisTime" , None ) ?;
71+ // devnet4: currentTimeMs (integer), devnet5: currentTimeMilliseconds (float)
72+ let current_time_ms = param_u64 ( "currentTimeMs" , Some ( "currentTimeMilliseconds" ) ) ?;
73+ let expected = test. output . slot . ok_or_else ( || anyhow ! ( "Missing output.slot" ) ) ?;
74+ let genesis_ms = genesis_time * 1000 ;
75+ let actual = if current_time_ms <= genesis_ms {
4576 0
4677 } else {
47- ( input . current_time_ms - genesis_ms) / ms_per_slot
78+ ( current_time_ms - genesis_ms) / ms_per_slot
4879 } ;
49- ensure ! (
50- actual == expected,
51- "current_slot mismatch: expected {expected}, got {actual}"
52- ) ;
80+ ensure ! ( actual == expected, "current_slot mismatch: expected {expected}, got {actual}" ) ;
5381 }
5482 "current_interval" => {
55- let input: CurrentTimeInput = serde_json:: from_value ( test. input . clone ( ) ) ?;
56- let expected = test
57- . output
58- . interval
59- . ok_or_else ( || anyhow ! ( "Missing output.interval" ) ) ?;
60- let genesis_ms = input. genesis_time * 1000 ;
61- let actual = if input. current_time_ms <= genesis_ms {
83+ let genesis_time = param_u64 ( "genesisTime" , None ) ?;
84+ let current_time_ms = param_u64 ( "currentTimeMs" , Some ( "currentTimeMilliseconds" ) ) ?;
85+ let expected =
86+ test. output . interval . ok_or_else ( || anyhow ! ( "Missing output.interval" ) ) ?;
87+ let genesis_ms = genesis_time * 1000 ;
88+ let actual = if current_time_ms <= genesis_ms {
6289 0
6390 } else {
64- let elapsed = input . current_time_ms - genesis_ms;
91+ let elapsed = current_time_ms - genesis_ms;
6592 ( elapsed % ms_per_slot) / ms_per_interval
6693 } ;
6794 ensure ! (
@@ -70,44 +97,39 @@ pub fn run_slot_clock_test(test_name: &str, test: &SlotClockTest) -> anyhow::Res
7097 ) ;
7198 }
7299 "total_intervals" => {
73- let input: CurrentTimeInput = serde_json:: from_value ( test. input . clone ( ) ) ?;
100+ let genesis_time = param_u64 ( "genesisTime" , None ) ?;
101+ let current_time_ms = param_u64 ( "currentTimeMs" , Some ( "currentTimeMilliseconds" ) ) ?;
74102 let expected = test
75103 . output
76104 . total_intervals
77105 . ok_or_else ( || anyhow ! ( "Missing output.totalIntervals" ) ) ?;
78- let genesis_ms = input . genesis_time * 1000 ;
79- let actual = if input . current_time_ms <= genesis_ms {
106+ let genesis_ms = genesis_time * 1000 ;
107+ let actual = if current_time_ms <= genesis_ms {
80108 0
81109 } else {
82- ( input . current_time_ms - genesis_ms) / ms_per_interval
110+ ( current_time_ms - genesis_ms) / ms_per_interval
83111 } ;
84112 ensure ! (
85113 actual == expected,
86114 "total_intervals mismatch: expected {expected}, got {actual}"
87115 ) ;
88116 }
89117 "from_slot" => {
90- let input: FromSlotInput = serde_json:: from_value ( test. input . clone ( ) ) ?;
91- let expected = test
92- . output
93- . interval
94- . ok_or_else ( || anyhow ! ( "Missing output.interval" ) ) ?;
95- let actual = input. slot * cfg. intervals_per_slot ;
96- ensure ! (
97- actual == expected,
98- "from_slot mismatch: expected {expected}, got {actual}"
99- ) ;
118+ let slot = param_u64 ( "slot" , None ) ?;
119+ let expected =
120+ test. output . interval . ok_or_else ( || anyhow ! ( "Missing output.interval" ) ) ?;
121+ let actual = slot * cfg. intervals_per_slot ;
122+ ensure ! ( actual == expected, "from_slot mismatch: expected {expected}, got {actual}" ) ;
100123 }
101124 "from_unix_time" => {
102- let input: FromUnixTimeInput = serde_json:: from_value ( test. input . clone ( ) ) ?;
103- let expected = test
104- . output
105- . interval
106- . ok_or_else ( || anyhow ! ( "Missing output.interval" ) ) ?;
107- let actual = if input. unix_seconds <= input. genesis_time {
125+ let genesis_time = param_u64 ( "genesisTime" , None ) ?;
126+ let unix_seconds = param_u64 ( "unixSeconds" , None ) ?;
127+ let expected =
128+ test. output . interval . ok_or_else ( || anyhow ! ( "Missing output.interval" ) ) ?;
129+ let actual = if unix_seconds <= genesis_time {
108130 0
109131 } else {
110- ( ( input . unix_seconds - input . genesis_time ) * 1000 ) / ms_per_interval
132+ ( ( unix_seconds - genesis_time) * 1000 ) / ms_per_interval
111133 } ;
112134 ensure ! (
113135 actual == expected,
0 commit comments