@@ -2,31 +2,37 @@ package fastjson
2
2
3
3
import (
4
4
"fmt"
5
+ "math/rand"
5
6
"testing"
6
7
"time"
7
8
)
8
9
9
- func TestArena (t * testing.T ) {
10
+ // Generic test driver that tests both serial and concurrent executions of the test with a given arena
11
+ func testArenaDriver (t * testing.T , iterations int , doTest func (a * Arena ) error ) {
12
+ t .Helper ()
10
13
t .Run ("serial" , func (t * testing.T ) {
11
14
var a Arena
12
- for i := 0 ; i < 10 ; i ++ {
13
- if err := testArena (& a ); err != nil {
15
+ for i := 0 ; i < iterations ; i ++ {
16
+ if err := doTest (& a ); err != nil {
14
17
t .Fatal (err )
15
18
}
16
19
a .Reset ()
17
20
}
18
21
})
19
22
t .Run ("concurrent" , func (t * testing.T ) {
20
23
var ap ArenaPool
21
- workers := 4
24
+ workers := 128
22
25
ch := make (chan error , workers )
23
26
for i := 0 ; i < workers ; i ++ {
24
27
go func () {
25
28
a := ap .Get ()
26
- defer ap .Put (a )
29
+ defer func () {
30
+ a .Reset ()
31
+ ap .Put (a )
32
+ }()
27
33
var err error
28
- for i := 0 ; i < 10 ; i ++ {
29
- if err = testArena (a ); err != nil {
34
+ for i := 0 ; i < iterations ; i ++ {
35
+ if err = doTest (a ); err != nil {
30
36
break
31
37
}
32
38
}
@@ -39,13 +45,17 @@ func TestArena(t *testing.T) {
39
45
if err != nil {
40
46
t .Fatal (err )
41
47
}
42
- case <- time .After (time .Second ):
48
+ case <- time .After (time .Second * 3 ):
43
49
t .Fatalf ("timeout" )
44
50
}
45
51
}
46
52
})
47
53
}
48
54
55
+ func TestArena (t * testing.T ) {
56
+ testArenaDriver (t , 1000 , func (a * Arena ) error { return testArena (a ) })
57
+ }
58
+
49
59
func testArena (a * Arena ) error {
50
60
o := a .NewObject ()
51
61
o .Set ("nil1" , a .NewNull ())
@@ -56,6 +66,7 @@ func testArena(a *Arena) error {
56
66
o .Set ("ni" , ni )
57
67
o .Set ("nf" , a .NewNumberFloat64 (1.23 ))
58
68
o .Set ("ns" , a .NewNumberString ("34.43" ))
69
+ o .Set ("nbs" , a .NewNumberStringBytes (s2b ("-98.765" )))
59
70
s := a .NewString ("foo" )
60
71
o .Set ("str1" , s )
61
72
o .Set ("str2" , a .NewStringBytes ([]byte ("xx" )))
@@ -69,9 +80,60 @@ func testArena(a *Arena) error {
69
80
o .Set ("obj" , obj )
70
81
71
82
str := o .String ()
72
- strExpected := `{"nil1":null,"nil2":null,"false":false,"true":true,"ni":123,"nf":1.23,"ns":34.43,"str1":"foo","str2":"xx","a":["foo",123],"obj":{"s":"foo"}}`
83
+ strExpected := `{"nil1":null,"nil2":null,"false":false,"true":true,"ni":123,"nf":1.23,"ns":34.43,"nbs":-98.765," str1":"foo","str2":"xx","a":["foo",123],"obj":{"s":"foo"}}`
73
84
if str != strExpected {
74
85
return fmt .Errorf ("unexpected json\n got\n %s\n want\n %s" , str , strExpected )
75
86
}
76
87
return nil
77
88
}
89
+
90
+ func TestArenaDeepCopyValue (t * testing.T ) {
91
+ testArenaDriver (t , 100 , func (a * Arena ) error { return testArenaDeepCopyValue (a ) })
92
+ }
93
+
94
+ func randValidChar () rune {
95
+ for {
96
+ c := rune ('0' + rand .Intn (78 ))
97
+ if c != '\\' {
98
+ return c
99
+ }
100
+ }
101
+ }
102
+
103
+ func testArenaDeepCopyValue (a * Arena ) error {
104
+ const jsonTest = `{"nil":null,"false":false,"true":true,"ni":123,"nf":1.23,"ns":34.43,"nbs":-98.765,"str1":"foo","str2":"xx","a":["foo",123,{"s":"x","n":-1.0,"o":{},"nil":null}],"obj":{"s":"foo","a":[123,"s",{"f":{"f2":"v","f3":{"a":98}}}]}}`
105
+ // Use a locally controlled parser that we'll reset and reuse to ensure the deep copy truly copied all the values
106
+ var p Parser
107
+ tempValue , err := p .Parse (jsonTest )
108
+ if err != nil {
109
+ return fmt .Errorf ("failed to parse test json: %w" , err )
110
+ }
111
+ // Validate that the serialized value matches the original test
112
+ tempSerialized := b2s (tempValue .MarshalTo (nil ))
113
+ if tempSerialized != jsonTest {
114
+ return fmt .Errorf ("initial parsed test JSON does not match\n got\n %s\n want\n %s" , tempSerialized , jsonTest )
115
+ }
116
+ // Do a deep copy to preserve the values after the parser is reused
117
+ shallowCopy := tempValue
118
+ deepCopy := a .DeepCopyValue (tempValue )
119
+ // Now reuse the parser enough times so it should trash a shallow copy
120
+ for i := 0 ; i < 100 ; i ++ {
121
+ rn := rand .Int63n (2 ^ 40 )
122
+ rs := fmt .Sprintf ("%c%d%d%c" , randValidChar (), rand .Int63 (), rand .Int63 (), randValidChar ())
123
+ mixerJSON := fmt .Sprintf (`{"n1":%d,"s1":"%s","o1":{"a1":[%d,true,%d,false,"%s",{"f1":%d,"f2":[%d,%d,[%d,"%s"]]}]}}` , rn , rs , rn , rn , rs , rn , rn , rn , rn , rs )
124
+ _ , err = p .Parse (mixerJSON )
125
+ if err != nil {
126
+ return fmt .Errorf ("failed reusing parser to parser random JSON: %w\n JSON\n %s" , err , mixerJSON )
127
+ }
128
+ }
129
+ // Now check that the deep copy is good and the shallow copy is bad
130
+ deepCopyJSON := b2s (deepCopy .MarshalTo (nil ))
131
+ if deepCopyJSON != jsonTest {
132
+ return fmt .Errorf ("deep copy JSON does not match\n got\n %s\n want\n %s" , deepCopyJSON , jsonTest )
133
+ }
134
+ shallowCopyJSON := b2s (shallowCopy .MarshalTo (nil ))
135
+ if shallowCopyJSON == jsonTest {
136
+ return fmt .Errorf ("shallow copy JSON matches when it should not match!\n shallow_copy\n %s" , shallowCopyJSON )
137
+ }
138
+ return nil
139
+ }
0 commit comments