@@ -6,6 +6,8 @@ package pipeline
66
77import (
88 "encoding/json"
9+ "fmt"
10+ "strings"
911 "testing"
1012
1113 "github.com/google/go-cmp/cmp"
@@ -318,3 +320,108 @@ func TestDiffUlite(t *testing.T) {
318320 })
319321 }
320322}
323+
324+ var jsonUnmarshalUsingNumberTests = []struct {
325+ name string
326+ msg string
327+ }{
328+ {
329+ name : "empty" ,
330+ msg : "" , // Will error "unexpected end of JSON input".
331+ },
332+ {
333+ name : "string" ,
334+ msg : `"message"` ,
335+ },
336+ {
337+ name : "array" ,
338+ msg : "[1,2,3,4,5]" ,
339+ },
340+ {
341+ name : "object" ,
342+ msg : `{"key":42}` ,
343+ },
344+ {
345+ name : "object" ,
346+ msg : `{"key":42}answer` , // Will error "invalid character 'a' after top-level value".
347+ },
348+ // Test extra data whitespace parity with json.Unmarshal for error parity.
349+ {
350+ name : "object" ,
351+ msg : `{"key":42} ` ,
352+ },
353+ {
354+ name : "object" ,
355+ msg : `{"key":42}` + "\t " ,
356+ },
357+ {
358+ name : "object" ,
359+ msg : `{"key":42}` + "\r " ,
360+ },
361+ {
362+ name : "object" ,
363+ msg : `{"key":42}` + "\n " ,
364+ },
365+ {
366+ name : "0x1p52+1" ,
367+ msg : fmt .Sprint (uint64 (0x1p52 ) + 1 ),
368+ },
369+ {
370+ name : "0x1p53-1" ,
371+ msg : fmt .Sprint (uint64 (0x1p53 ) - 1 ),
372+ },
373+ // The following three cases will fail if json.Unmarshal is used in place
374+ // of jsonUnmarshalUsingNumber, as they are past the cutover.
375+ {
376+ name : "0x1p53+1" ,
377+ msg : fmt .Sprint (uint64 (0x1p53 ) + 1 ),
378+ },
379+ {
380+ name : "0x1p54+1" ,
381+ msg : fmt .Sprint (uint64 (0x1p54 ) + 1 ),
382+ },
383+ {
384+ name : "long" ,
385+ msg : "9223372036854773807" ,
386+ },
387+ }
388+
389+ func TestJsonUnmarshalUsingNumberRoundTrip (t * testing.T ) {
390+ // This tests that jsonUnmarshalUsingNumber behaves the same
391+ // way as json.Unmarshal with the exception that numbers are
392+ // not unmarshaled through float64. This is important to avoid
393+ // low-bit truncation of long numeric values that are greater
394+ // than or equal to 0x1p53, the limit of bijective equivalence
395+ // with 64 bit-integers.
396+
397+ for _ , test := range jsonUnmarshalUsingNumberTests {
398+ t .Run (test .name , func (t * testing.T ) {
399+ var val interface {}
400+ err := jsonUnmarshalUsingNumber ([]byte (test .msg ), & val )
401+
402+ // Confirm that we get the same errors with jsonUnmarshalUsingNumber
403+ // as are returned by json.Unmarshal.
404+ jerr := json .Unmarshal ([]byte (test .msg ), new (interface {}))
405+ if (err == nil ) != (jerr == nil ) {
406+ t .Errorf ("unexpected error: got:%#v want:%#v" , err , jerr )
407+ }
408+ if err != nil {
409+ return
410+ }
411+
412+ // Confirm that we round-trip the message correctly without
413+ // alteration beyond trailing whitespace.
414+ got , err := json .Marshal (val )
415+ if err != nil {
416+ t .Errorf ("unexpected error: got:%#v want:%#v" , err , jerr )
417+ }
418+ // Truncate trailing whitespace from the input since it won't
419+ // be rendered in the output. This set of space characters is
420+ // defined in encoding/json/scanner.go as func isSpace.
421+ want := strings .TrimRight (test .msg , " \t \r \n " )
422+ if string (got ) != want {
423+ t .Errorf ("unexpected result: got:%v want:%v" , val , want )
424+ }
425+ })
426+ }
427+ }
0 commit comments