@@ -7,67 +7,89 @@ package atomic_test
77import (
88 "encoding/json"
99 "strconv"
10- stdatomic "sync/atomic"
1110 "testing"
11+ "time"
1212
1313 "github.com/NVIDIA/aistore/cmn/atomic"
14+ "github.com/NVIDIA/aistore/cmn/cos"
15+
16+ jsoniter "github.com/json-iterator/go"
1417)
1518
19+ // Each atomic type with a custom MarshalJSON/UnmarshalJSON must round-trip
20+ // through cos.MustMarshal (cos.JSON, frozen) into any of the three decoders:
21+ // - default jsoniter: jsoniter.ConfigDefault
22+ // - project frozen config cos.JSON
23+ // - stdlib encoding/json.
24+ var unmarshalers = []struct {
25+ name string
26+ fn func ([]byte , any ) error
27+ }{
28+ {"jsoniter" , jsoniter .Unmarshal },
29+ {"cos.JSON" , cos .JSON .Unmarshal },
30+ {"stdlib" , json .Unmarshal },
31+ }
32+
1633func TestInt64JSONRoundTrip (t * testing.T ) {
1734 for _ , want := range []int64 {0 , 1 , - 1 , 1 << 30 , - 1 << 30 , 1700000000000000000 } {
18- in := atomic .NewInt64 (want )
19- data , err := json .Marshal (in )
20- if err != nil {
21- t .Fatalf ("marshal %d: %v" , want , err )
22- }
23- if string (data ) != `{"v":` + strconv .FormatInt (want , 10 )+ `}` {
24- t .Fatalf ("unexpected wire format for %d: %s" , want , data )
25- }
26- var got atomic.Int64
27- if err := json .Unmarshal (data , & got ); err != nil {
28- t .Fatalf ("unmarshal %s: %v" , data , err )
35+ data := cos .MustMarshal (atomic .NewInt64 (want ))
36+ if s , exp := string (data ), `{"v":` + strconv .FormatInt (want , 10 )+ `}` ; s != exp {
37+ t .Fatalf ("wire format for %d: got %s, want %s" , want , s , exp )
2938 }
30- if got .Load () != want {
31- t .Fatalf ("round-trip mismatch: want %d, got %d (json=%s)" , want , got .Load (), data )
39+ for _ , u := range unmarshalers {
40+ t .Run (strconv .FormatInt (want , 10 )+ "/" + u .name , func (t * testing.T ) {
41+ var got atomic.Int64
42+ if err := u .fn (data , & got ); err != nil {
43+ t .Fatalf ("unmarshal %s: %v" , data , err )
44+ }
45+ if got .Load () != want {
46+ t .Fatalf ("round-trip mismatch: want %d, got %d" , want , got .Load ())
47+ }
48+ })
3249 }
3350 }
3451}
3552
36- // Cross-version IC sync compat: new code must accept legacy `{}` (from a
37- // pre-MarshalJSON peer), and an older default-struct decoder must accept the
38- // new `{"v":N}` without error - both yielding zero, matching current behavior.
39- func TestInt64JSONMixedVersionCompat (t * testing.T ) {
40- type newShape struct {
41- EndTimeX atomic.Int64 `json:"EndTimeX"`
42- }
43- type oldShape struct {
44- EndTimeX stdatomic.Int64 `json:"EndTimeX"`
45- }
46-
47- // old -> new: legacy `{}` decodes to zero
48- var n newShape
49- if err := json .Unmarshal ([]byte (`{"EndTimeX":{}}` ), & n ); err != nil {
50- t .Fatalf ("new failed to decode legacy {}: %v" , err )
51- }
52- if n .EndTimeX .Load () != 0 {
53- t .Fatalf ("expected 0 from legacy {}, got %d" , n .EndTimeX .Load ())
54- }
55-
56- // backward-compat: `{"v":N}` parsed to 0 by default struct decoder (unknown key ignored)
57- var o oldShape
58- if err := json .Unmarshal ([]byte (`{"EndTimeX":{"v":1700000000000000000}}` ), & o ); err != nil {
59- t .Fatalf ("old failed to decode new format: %v" , err )
60- }
61- if o .EndTimeX .Load () != 0 {
62- t .Fatalf ("old decoder should yield zero from unknown nested object, got %d" , o .EndTimeX .Load ())
53+ func TestBoolJSONRoundTrip (t * testing.T ) {
54+ for _ , want := range []bool {true , false } {
55+ data := cos .MustMarshal (atomic .NewBool (want ))
56+ if s , exp := string (data ), strconv .FormatBool (want ); s != exp {
57+ t .Fatalf ("wire format for %v: got %s, want %s" , want , s , exp )
58+ }
59+ for _ , u := range unmarshalers {
60+ t .Run (strconv .FormatBool (want )+ "/" + u .name , func (t * testing.T ) {
61+ var got atomic.Bool
62+ if err := u .fn (data , & got ); err != nil {
63+ t .Fatalf ("unmarshal %s: %v" , data , err )
64+ }
65+ if got .Load () != want {
66+ t .Fatalf ("round-trip mismatch: want %v, got %v" , want , got .Load ())
67+ }
68+ })
69+ }
6370 }
71+ }
6472
65- // forward-compat: new also accepts a bare number
66- var n2 newShape
67- if err := json .Unmarshal ([]byte (`{"EndTimeX":12345}` ), & n2 ); err != nil {
68- t .Fatalf ("new failed to decode bare number: %v" , err )
69- }
70- if n2 .EndTimeX .Load () != 12345 {
71- t .Fatalf ("expected 12345, got %d" , n2 .EndTimeX .Load ())
73+ func TestTimeJSONRoundTrip (t * testing.T ) {
74+ for _ , want := range []time.Time {
75+ time .Unix (0 , 0 ),
76+ time .Unix (0 , 1 ),
77+ time .Unix (0 , 1700000000000000000 ),
78+ } {
79+ data := cos .MustMarshal (atomic .NewTime (want ))
80+ if s , exp := string (data ), strconv .FormatInt (want .UnixNano (), 10 ); s != exp {
81+ t .Fatalf ("wire format for %v: got %s, want %s" , want , s , exp )
82+ }
83+ for _ , u := range unmarshalers {
84+ t .Run (strconv .FormatInt (want .UnixNano (), 10 )+ "/" + u .name , func (t * testing.T ) {
85+ var got atomic.Time
86+ if err := u .fn (data , & got ); err != nil {
87+ t .Fatalf ("unmarshal %s: %v" , data , err )
88+ }
89+ if got .Load ().UnixNano () != want .UnixNano () {
90+ t .Fatalf ("round-trip mismatch: want %v, got %v" , want .UnixNano (), got .Load ().UnixNano ())
91+ }
92+ })
93+ }
7294 }
7395}
0 commit comments