github.com/neilotoole/jsoncolor@v0.7.2-0.20231115150201-1637fae69be1/golang_scanner_test.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package jsoncolor 6 7 import ( 8 "bytes" 9 "math" 10 "math/rand" 11 "testing" 12 ) 13 14 var validTests = []struct { 15 data string 16 ok bool 17 }{ 18 {`foo`, false}, 19 {`}{`, false}, 20 {`{]`, false}, 21 {`{}`, true}, 22 {`{"foo":"bar"}`, true}, 23 {`{"foo":"bar","bar":{"baz":["qux"]}}`, true}, 24 } 25 26 func TestValid(t *testing.T) { 27 for _, tt := range validTests { 28 if ok := Valid([]byte(tt.data)); ok != tt.ok { 29 t.Errorf("Valid(%#q) = %v, want %v", tt.data, ok, tt.ok) 30 } 31 } 32 } 33 34 // Tests of simple examples. 35 36 type example struct { 37 compact string 38 indent string 39 } 40 41 var examples = []example{ 42 {`1`, `1`}, 43 {`{}`, `{}`}, 44 {`[]`, `[]`}, 45 {`{"":2}`, "{\n\t\"\": 2\n}"}, 46 {`[3]`, "[\n\t3\n]"}, 47 {`[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"}, 48 {`{"x":1}`, "{\n\t\"x\": 1\n}"}, 49 {ex1, ex1i}, 50 {"{\"\":\"<>&\u2028\u2029\"}", "{\n\t\"\": \"<>&\u2028\u2029\"\n}"}, // See golang.org/issue/34070 51 } 52 53 var ex1 = `[true,false,null,"x",1,1.5,0,-5e+2]` 54 55 var ex1i = `[ 56 true, 57 false, 58 null, 59 "x", 60 1, 61 1.5, 62 0, 63 -5e+2 64 ]` 65 66 func TestCompact(t *testing.T) { 67 var buf bytes.Buffer 68 for _, tt := range examples { 69 buf.Reset() 70 if err := Compact(&buf, []byte(tt.compact)); err != nil { 71 t.Errorf("Compact(%#q): %v", tt.compact, err) 72 } else if s := buf.String(); s != tt.compact { 73 t.Errorf("Compact(%#q) = %#q, want original", tt.compact, s) 74 } 75 76 buf.Reset() 77 if err := Compact(&buf, []byte(tt.indent)); err != nil { 78 t.Errorf("Compact(%#q): %v", tt.indent, err) 79 continue 80 } else if s := buf.String(); s != tt.compact { 81 t.Errorf("Compact(%#q) = %#q, want %#q", tt.indent, s, tt.compact) 82 } 83 } 84 } 85 86 func TestCompactSeparators(t *testing.T) { 87 // U+2028 and U+2029 should be escaped inside strings. 88 // They should not appear outside strings. 89 tests := []struct { 90 in, compact string 91 }{ 92 {"{\"\u2028\": 1}", "{\"\u2028\":1}"}, 93 {"{\"\u2029\" :2}", "{\"\u2029\":2}"}, 94 } 95 for _, tt := range tests { 96 var buf bytes.Buffer 97 if err := Compact(&buf, []byte(tt.in)); err != nil { 98 t.Errorf("Compact(%q): %v", tt.in, err) 99 } else if s := buf.String(); s != tt.compact { 100 t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact) 101 } 102 } 103 } 104 105 func TestIndent(t *testing.T) { 106 var buf bytes.Buffer 107 for _, tt := range examples { 108 buf.Reset() 109 if err := Indent(&buf, []byte(tt.indent), "", "\t"); err != nil { 110 t.Errorf("Indent(%#q): %v", tt.indent, err) 111 } else if s := buf.String(); s != tt.indent { 112 t.Errorf("Indent(%#q) = %#q, want original", tt.indent, s) 113 } 114 115 buf.Reset() 116 if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil { 117 t.Errorf("Indent(%#q): %v", tt.compact, err) 118 continue 119 } else if s := buf.String(); s != tt.indent { 120 t.Errorf("Indent(%#q) = %#q, want %#q", tt.compact, s, tt.indent) 121 } 122 } 123 } 124 125 // Tests of a large random structure. 126 127 func TestCompactBig(t *testing.T) { 128 initBig() 129 var buf bytes.Buffer 130 if err := Compact(&buf, jsonBig); err != nil { 131 t.Fatalf("Compact: %v", err) 132 } 133 b := buf.Bytes() 134 if !bytes.Equal(b, jsonBig) { 135 t.Error("Compact(jsonBig) != jsonBig") 136 diff(t, b, jsonBig) 137 return 138 } 139 } 140 141 func TestIndentBig(t *testing.T) { 142 t.Parallel() 143 initBig() 144 var buf bytes.Buffer 145 if err := Indent(&buf, jsonBig, "", "\t"); err != nil { 146 t.Fatalf("Indent1: %v", err) 147 } 148 b := buf.Bytes() 149 if len(b) == len(jsonBig) { 150 // jsonBig is compact (no unnecessary spaces); 151 // indenting should make it bigger 152 t.Fatalf("Indent(jsonBig) did not get bigger") 153 } 154 155 // should be idempotent 156 var buf1 bytes.Buffer 157 if err := Indent(&buf1, b, "", "\t"); err != nil { 158 t.Fatalf("Indent2: %v", err) 159 } 160 b1 := buf1.Bytes() 161 if !bytes.Equal(b1, b) { 162 t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)") 163 diff(t, b1, b) 164 return 165 } 166 167 // should get back to original 168 buf1.Reset() 169 if err := Compact(&buf1, b); err != nil { 170 t.Fatalf("Compact: %v", err) 171 } 172 b1 = buf1.Bytes() 173 if !bytes.Equal(b1, jsonBig) { 174 t.Error("Compact(Indent(jsonBig)) != jsonBig") 175 diff(t, b1, jsonBig) 176 return 177 } 178 } 179 180 type indentErrorTest struct { 181 in string 182 err error 183 } 184 185 var indentErrorTests = []indentErrorTest{ 186 {`{"X": "foo", "Y"}`, &testSyntaxError{"invalid character '}' after object key", 17}}, 187 {`{"X": "foo" "Y": "bar"}`, &testSyntaxError{"invalid character '\"' after object key:value pair", 13}}, 188 } 189 190 func TestIndentErrors(t *testing.T) { 191 for i, tt := range indentErrorTests { 192 slice := make([]uint8, 0) 193 buf := bytes.NewBuffer(slice) 194 err := Indent(buf, []uint8(tt.in), "", "") 195 assertErrorPresence(t, tt.err, err, i) 196 } 197 } 198 199 func diff(t *testing.T, a, b []byte) { 200 for i := 0; ; i++ { 201 if i >= len(a) || i >= len(b) || a[i] != b[i] { 202 j := i - 10 203 if j < 0 { 204 j = 0 205 } 206 t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:])) 207 return 208 } 209 } 210 } 211 212 func trim(b []byte) []byte { 213 if len(b) > 20 { 214 return b[0:20] 215 } 216 return b 217 } 218 219 // Generate a random JSON object. 220 221 var jsonBig []byte 222 223 func initBig() { 224 n := 10000 225 if testing.Short() { 226 n = 100 227 } 228 b, err := Marshal(genValue(n)) 229 if err != nil { 230 panic(err) 231 } 232 jsonBig = b 233 } 234 235 func genValue(n int) interface{} { 236 if n > 1 { 237 switch rand.Intn(2) { 238 case 0: 239 return genArray(n) 240 case 1: 241 return genMap(n) 242 } 243 } 244 switch rand.Intn(3) { 245 case 0: 246 return rand.Intn(2) == 0 247 case 1: 248 return rand.NormFloat64() 249 case 2: 250 return genString(30) 251 } 252 panic("unreachable") 253 } 254 255 func genString(stddev float64) string { 256 n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2)) 257 c := make([]rune, n) 258 for i := range c { 259 f := math.Abs(rand.NormFloat64()*64 + 32) 260 if f > 0x10ffff { 261 f = 0x10ffff 262 } 263 c[i] = rune(f) 264 } 265 return string(c) 266 } 267 268 func genArray(n int) []interface{} { 269 f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2))) 270 if f > n { 271 f = n 272 } 273 if f < 1 { 274 f = 1 275 } 276 x := make([]interface{}, f) 277 for i := range x { 278 x[i] = genValue(((i+1)*n)/f - (i*n)/f) 279 } 280 return x 281 } 282 283 func genMap(n int) map[string]interface{} { 284 f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2))) 285 if f > n { 286 f = n 287 } 288 if n > 0 && f == 0 { 289 f = 1 290 } 291 x := make(map[string]interface{}) 292 for i := 0; i < f; i++ { 293 x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f) 294 } 295 return x 296 }