1package jlexer 2 3import ( 4 "bytes" 5 "encoding/json" 6 "reflect" 7 "testing" 8) 9 10func TestString(t *testing.T) { 11 for i, test := range []struct { 12 toParse string 13 want string 14 wantError bool 15 }{ 16 {toParse: `"simple string"`, want: "simple string"}, 17 {toParse: " \r\r\n\t " + `"test"`, want: "test"}, 18 {toParse: `"\n\t\"\/\\\f\r"`, want: "\n\t\"/\\\f\r"}, 19 {toParse: `"\u0020"`, want: " "}, 20 {toParse: `"\u0020-\t"`, want: " -\t"}, 21 {toParse: `"\ufffd\uFFFD"`, want: "\ufffd\ufffd"}, 22 {toParse: `"\ud83d\ude00"`, want: ""}, 23 {toParse: `"\ud83d\ude08"`, want: ""}, 24 {toParse: `"\ud8"`, wantError: true}, 25 26 {toParse: `"test"junk`, want: "test"}, 27 28 {toParse: `5`, wantError: true}, // not a string 29 {toParse: `"\x"`, wantError: true}, // invalid escape 30 {toParse: `"\ud800"`, want: "�"}, // invalid utf-8 char; return replacement char 31 } { 32 { 33 l := Lexer{Data: []byte(test.toParse)} 34 35 got := l.String() 36 if got != test.want { 37 t.Errorf("[%d, %q] String() = %v; want %v", i, test.toParse, got, test.want) 38 } 39 err := l.Error() 40 if err != nil && !test.wantError { 41 t.Errorf("[%d, %q] String() error: %v", i, test.toParse, err) 42 } else if err == nil && test.wantError { 43 t.Errorf("[%d, %q] String() ok; want error", i, test.toParse) 44 } 45 } 46 { 47 l := Lexer{Data: []byte(test.toParse)} 48 49 got := l.StringIntern() 50 if got != test.want { 51 t.Errorf("[%d, %q] String() = %v; want %v", i, test.toParse, got, test.want) 52 } 53 err := l.Error() 54 if err != nil && !test.wantError { 55 t.Errorf("[%d, %q] String() error: %v", i, test.toParse, err) 56 } else if err == nil && test.wantError { 57 t.Errorf("[%d, %q] String() ok; want error", i, test.toParse) 58 } 59 } 60 } 61} 62 63func TestStringIntern(t *testing.T) { 64 data := []byte(`"string interning test"`) 65 var l Lexer 66 67 allocsPerRun := testing.AllocsPerRun(1000, func() { 68 l = Lexer{Data: data} 69 _ = l.StringIntern() 70 }) 71 if allocsPerRun != 0 { 72 t.Fatalf("expected 0 allocs, got %f", allocsPerRun) 73 } 74 75 allocsPerRun = testing.AllocsPerRun(1000, func() { 76 l = Lexer{Data: data} 77 _ = l.String() 78 }) 79 if allocsPerRun != 1 { 80 t.Fatalf("expected 1 allocs, got %f", allocsPerRun) 81 } 82} 83 84func TestBytes(t *testing.T) { 85 for i, test := range []struct { 86 toParse string 87 want string 88 wantError bool 89 }{ 90 {toParse: `"c2ltcGxlIHN0cmluZw=="`, want: "simple string"}, 91 {toParse: " \r\r\n\t " + `"dGVzdA=="`, want: "test"}, 92 93 {toParse: `5`, wantError: true}, // not a JSON string 94 {toParse: `"foobar"`, wantError: true}, // not base64 encoded 95 {toParse: `"c2ltcGxlIHN0cmluZw="`, wantError: true}, // invalid base64 padding 96 } { 97 l := Lexer{Data: []byte(test.toParse)} 98 99 got := l.Bytes() 100 if bytes.Compare(got, []byte(test.want)) != 0 { 101 t.Errorf("[%d, %q] Bytes() = %v; want: %v", i, test.toParse, got, []byte(test.want)) 102 } 103 err := l.Error() 104 if err != nil && !test.wantError { 105 t.Errorf("[%d, %q] Bytes() error: %v", i, test.toParse, err) 106 } else if err == nil && test.wantError { 107 t.Errorf("[%d, %q] Bytes() ok; want error", i, test.toParse) 108 } 109 } 110} 111 112func TestNumber(t *testing.T) { 113 for i, test := range []struct { 114 toParse string 115 want string 116 wantError bool 117 }{ 118 {toParse: "123", want: "123"}, 119 {toParse: "-123", want: "-123"}, 120 {toParse: "\r\n12.35", want: "12.35"}, 121 {toParse: "12.35e+1", want: "12.35e+1"}, 122 {toParse: "12.35e-15", want: "12.35e-15"}, 123 {toParse: "12.35E-15", want: "12.35E-15"}, 124 {toParse: "12.35E15", want: "12.35E15"}, 125 126 {toParse: `"a"`, wantError: true}, 127 {toParse: "123junk", wantError: true}, 128 {toParse: "1.2.3", wantError: true}, 129 {toParse: "1e2e3", wantError: true}, 130 {toParse: "1e2.3", wantError: true}, 131 } { 132 l := Lexer{Data: []byte(test.toParse)} 133 134 got := l.number() 135 if got != test.want { 136 t.Errorf("[%d, %q] number() = %v; want %v", i, test.toParse, got, test.want) 137 } 138 err := l.Error() 139 if err != nil && !test.wantError { 140 t.Errorf("[%d, %q] number() error: %v", i, test.toParse, err) 141 } else if err == nil && test.wantError { 142 t.Errorf("[%d, %q] number() ok; want error", i, test.toParse) 143 } 144 } 145} 146 147func TestBool(t *testing.T) { 148 for i, test := range []struct { 149 toParse string 150 want bool 151 wantError bool 152 }{ 153 {toParse: "true", want: true}, 154 {toParse: "false", want: false}, 155 156 {toParse: "1", wantError: true}, 157 {toParse: "truejunk", wantError: true}, 158 {toParse: `false"junk"`, wantError: true}, 159 {toParse: "True", wantError: true}, 160 {toParse: "False", wantError: true}, 161 } { 162 l := Lexer{Data: []byte(test.toParse)} 163 164 got := l.Bool() 165 if got != test.want { 166 t.Errorf("[%d, %q] Bool() = %v; want %v", i, test.toParse, got, test.want) 167 } 168 err := l.Error() 169 if err != nil && !test.wantError { 170 t.Errorf("[%d, %q] Bool() error: %v", i, test.toParse, err) 171 } else if err == nil && test.wantError { 172 t.Errorf("[%d, %q] Bool() ok; want error", i, test.toParse) 173 } 174 } 175} 176 177func TestSkipRecursive(t *testing.T) { 178 for i, test := range []struct { 179 toParse string 180 left string 181 wantError bool 182 }{ 183 {toParse: "5, 4", left: ", 4"}, 184 {toParse: "[5, 6], 4", left: ", 4"}, 185 {toParse: "[5, [7,8]]: 4", left: ": 4"}, 186 187 {toParse: `{"a":1}, 4`, left: ", 4"}, 188 {toParse: `{"a":1, "b":{"c": 5}, "e":[12,15]}, 4`, left: ", 4"}, 189 190 // array start/end chars in a string 191 {toParse: `[5, "]"], 4`, left: ", 4"}, 192 {toParse: `[5, "\"]"], 4`, left: ", 4"}, 193 {toParse: `[5, "["], 4`, left: ", 4"}, 194 {toParse: `[5, "\"["], 4`, left: ", 4"}, 195 196 // object start/end chars in a string 197 {toParse: `{"a}":1}, 4`, left: ", 4"}, 198 {toParse: `{"a\"}":1}, 4`, left: ", 4"}, 199 {toParse: `{"a{":1}, 4`, left: ", 4"}, 200 {toParse: `{"a\"{":1}, 4`, left: ", 4"}, 201 202 // object with double slashes at the end of string 203 {toParse: `{"a":"hey\\"}, 4`, left: ", 4"}, 204 } { 205 l := Lexer{Data: []byte(test.toParse)} 206 207 l.SkipRecursive() 208 209 got := string(l.Data[l.pos:]) 210 if got != test.left { 211 t.Errorf("[%d, %q] SkipRecursive() left = %v; want %v", i, test.toParse, got, test.left) 212 } 213 err := l.Error() 214 if err != nil && !test.wantError { 215 t.Errorf("[%d, %q] SkipRecursive() error: %v", i, test.toParse, err) 216 } else if err == nil && test.wantError { 217 t.Errorf("[%d, %q] SkipRecursive() ok; want error", i, test.toParse) 218 } 219 } 220} 221 222func TestInterface(t *testing.T) { 223 for i, test := range []struct { 224 toParse string 225 want interface{} 226 wantError bool 227 }{ 228 {toParse: "null", want: nil}, 229 {toParse: "true", want: true}, 230 {toParse: `"a"`, want: "a"}, 231 {toParse: "5", want: float64(5)}, 232 233 {toParse: `{}`, want: map[string]interface{}{}}, 234 {toParse: `[]`, want: []interface{}{}}, 235 236 {toParse: `{"a": "b"}`, want: map[string]interface{}{"a": "b"}}, 237 {toParse: `[5]`, want: []interface{}{float64(5)}}, 238 239 {toParse: `{"a":5 , "b" : "string"}`, want: map[string]interface{}{"a": float64(5), "b": "string"}}, 240 {toParse: `["a", 5 , null, true]`, want: []interface{}{"a", float64(5), nil, true}}, 241 242 {toParse: `{"a" "b"}`, wantError: true}, 243 {toParse: `{"a": "b",}`, wantError: true}, 244 {toParse: `{"a":"b","c" "b"}`, wantError: true}, 245 {toParse: `{"a": "b","c":"d",}`, wantError: true}, 246 {toParse: `{,}`, wantError: true}, 247 248 {toParse: `[1, 2,]`, wantError: true}, 249 {toParse: `[1 2]`, wantError: true}, 250 {toParse: `[,]`, wantError: true}, 251 } { 252 l := Lexer{Data: []byte(test.toParse)} 253 254 got := l.Interface() 255 if !reflect.DeepEqual(got, test.want) { 256 t.Errorf("[%d, %q] Interface() = %v; want %v", i, test.toParse, got, test.want) 257 } 258 err := l.Error() 259 if err != nil && !test.wantError { 260 t.Errorf("[%d, %q] Interface() error: %v", i, test.toParse, err) 261 } else if err == nil && test.wantError { 262 t.Errorf("[%d, %q] Interface() ok; want error", i, test.toParse) 263 } 264 } 265} 266 267func TestConsumed(t *testing.T) { 268 for i, test := range []struct { 269 toParse string 270 wantError bool 271 }{ 272 {toParse: "", wantError: false}, 273 {toParse: " ", wantError: false}, 274 {toParse: "\r\n", wantError: false}, 275 {toParse: "\t\t", wantError: false}, 276 277 {toParse: "{", wantError: true}, 278 } { 279 l := Lexer{Data: []byte(test.toParse)} 280 l.Consumed() 281 282 err := l.Error() 283 if err != nil && !test.wantError { 284 t.Errorf("[%d, %q] Consumed() error: %v", i, test.toParse, err) 285 } else if err == nil && test.wantError { 286 t.Errorf("[%d, %q] Consumed() ok; want error", i, test.toParse) 287 } 288 } 289} 290 291func TestJsonNumber(t *testing.T) { 292 for i, test := range []struct { 293 toParse string 294 want json.Number 295 wantLexerError bool 296 wantValue interface{} 297 wantValueError bool 298 }{ 299 {toParse: `10`, want: json.Number("10"), wantValue: int64(10)}, 300 {toParse: `0`, want: json.Number("0"), wantValue: int64(0)}, 301 {toParse: `0.12`, want: json.Number("0.12"), wantValue: 0.12}, 302 {toParse: `25E-4`, want: json.Number("25E-4"), wantValue: 25e-4}, 303 304 {toParse: `"10"`, want: json.Number("10"), wantValue: int64(10)}, 305 {toParse: `"0"`, want: json.Number("0"), wantValue: int64(0)}, 306 {toParse: `"0.12"`, want: json.Number("0.12"), wantValue: 0.12}, 307 {toParse: `"25E-4"`, want: json.Number("25E-4"), wantValue: 25e-4}, 308 309 {toParse: `"foo"`, want: json.Number("foo"), wantValueError: true}, 310 {toParse: `null`, want: json.Number(""), wantValueError: true}, 311 312 {toParse: `"a""`, want: json.Number("a"), wantValueError: true}, 313 314 {toParse: `[1]`, want: json.Number(""), wantLexerError: true, wantValueError: true}, 315 {toParse: `{}`, want: json.Number(""), wantLexerError: true, wantValueError: true}, 316 {toParse: `a`, want: json.Number(""), wantLexerError: true, wantValueError: true}, 317 } { 318 l := Lexer{Data: []byte(test.toParse)} 319 320 got := l.JsonNumber() 321 if got != test.want { 322 t.Errorf("[%d, %q] JsonNumber() = %v; want %v", i, test.toParse, got, test.want) 323 } 324 325 err := l.Error() 326 if err != nil && !test.wantLexerError { 327 t.Errorf("[%d, %q] JsonNumber() lexer error: %v", i, test.toParse, err) 328 } else if err == nil && test.wantLexerError { 329 t.Errorf("[%d, %q] JsonNumber() ok; want lexer error", i, test.toParse) 330 } 331 332 var valueErr error 333 var gotValue interface{} 334 switch test.wantValue.(type) { 335 case float64: 336 gotValue, valueErr = got.Float64() 337 default: 338 gotValue, valueErr = got.Int64() 339 } 340 341 if !reflect.DeepEqual(gotValue, test.wantValue) && !test.wantLexerError && !test.wantValueError { 342 t.Errorf("[%d, %q] JsonNumber() = %v; want %v", i, test.toParse, gotValue, test.wantValue) 343 } 344 345 if valueErr != nil && !test.wantValueError { 346 t.Errorf("[%d, %q] JsonNumber() value error: %v", i, test.toParse, valueErr) 347 } else if valueErr == nil && test.wantValueError { 348 t.Errorf("[%d, %q] JsonNumber() ok; want value error", i, test.toParse) 349 } 350 } 351} 352 353func TestFetchStringUnterminatedString(t *testing.T) { 354 for _, test := range []struct { 355 data []byte 356 }{ 357 {data: []byte(`"sting without trailing quote`)}, 358 {data: []byte(`"\"`)}, 359 {data: []byte{'"'}}, 360 } { 361 l := Lexer{Data: test.data} 362 l.fetchString() 363 if l.pos > len(l.Data) { 364 t.Errorf("fetchString(%s): pos=%v should not be greater than length of Data = %v", test.data, l.pos, len(l.Data)) 365 } 366 if l.Error() == nil { 367 t.Errorf("fetchString(%s): should add parsing error", test.data) 368 } 369 } 370} 371