Source file src/encoding/json/internal/jsonwire/decode_test.go

     1  // Copyright 2023 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  //go:build goexperiment.jsonv2
     6  
     7  package jsonwire
     8  
     9  import (
    10  	"errors"
    11  	"io"
    12  	"math"
    13  	"reflect"
    14  	"strings"
    15  	"testing"
    16  )
    17  
    18  func TestConsumeWhitespace(t *testing.T) {
    19  	tests := []struct {
    20  		in   string
    21  		want int
    22  	}{
    23  		{"", 0},
    24  		{"a", 0},
    25  		{" a", 1},
    26  		{" a ", 1},
    27  		{" \n\r\ta", 4},
    28  		{" \n\r\t \n\r\t \n\r\t \n\r\t", 16},
    29  		{"\u00a0", 0}, // non-breaking space is not JSON whitespace
    30  	}
    31  
    32  	for _, tt := range tests {
    33  		t.Run("", func(t *testing.T) {
    34  			if got := ConsumeWhitespace([]byte(tt.in)); got != tt.want {
    35  				t.Errorf("ConsumeWhitespace(%q) = %v, want %v", tt.in, got, tt.want)
    36  			}
    37  		})
    38  	}
    39  }
    40  
    41  func TestConsumeLiteral(t *testing.T) {
    42  	tests := []struct {
    43  		literal string
    44  		in      string
    45  		want    int
    46  		wantErr error
    47  	}{
    48  		{"null", "", 0, io.ErrUnexpectedEOF},
    49  		{"null", "n", 1, io.ErrUnexpectedEOF},
    50  		{"null", "nu", 2, io.ErrUnexpectedEOF},
    51  		{"null", "nul", 3, io.ErrUnexpectedEOF},
    52  		{"null", "null", 4, nil},
    53  		{"null", "nullx", 4, nil},
    54  		{"null", "x", 0, NewInvalidCharacterError("x", "in literal null (expecting 'n')")},
    55  		{"null", "nuxx", 2, NewInvalidCharacterError("x", "in literal null (expecting 'l')")},
    56  
    57  		{"false", "", 0, io.ErrUnexpectedEOF},
    58  		{"false", "f", 1, io.ErrUnexpectedEOF},
    59  		{"false", "fa", 2, io.ErrUnexpectedEOF},
    60  		{"false", "fal", 3, io.ErrUnexpectedEOF},
    61  		{"false", "fals", 4, io.ErrUnexpectedEOF},
    62  		{"false", "false", 5, nil},
    63  		{"false", "falsex", 5, nil},
    64  		{"false", "x", 0, NewInvalidCharacterError("x", "in literal false (expecting 'f')")},
    65  		{"false", "falsx", 4, NewInvalidCharacterError("x", "in literal false (expecting 'e')")},
    66  
    67  		{"true", "", 0, io.ErrUnexpectedEOF},
    68  		{"true", "t", 1, io.ErrUnexpectedEOF},
    69  		{"true", "tr", 2, io.ErrUnexpectedEOF},
    70  		{"true", "tru", 3, io.ErrUnexpectedEOF},
    71  		{"true", "true", 4, nil},
    72  		{"true", "truex", 4, nil},
    73  		{"true", "x", 0, NewInvalidCharacterError("x", "in literal true (expecting 't')")},
    74  		{"true", "trux", 3, NewInvalidCharacterError("x", "in literal true (expecting 'e')")},
    75  	}
    76  
    77  	for _, tt := range tests {
    78  		t.Run("", func(t *testing.T) {
    79  			var got int
    80  			switch tt.literal {
    81  			case "null":
    82  				got = ConsumeNull([]byte(tt.in))
    83  			case "false":
    84  				got = ConsumeFalse([]byte(tt.in))
    85  			case "true":
    86  				got = ConsumeTrue([]byte(tt.in))
    87  			default:
    88  				t.Errorf("invalid literal: %v", tt.literal)
    89  			}
    90  			switch {
    91  			case tt.wantErr == nil && got != tt.want:
    92  				t.Errorf("Consume%v(%q) = %v, want %v", strings.Title(tt.literal), tt.in, got, tt.want)
    93  			case tt.wantErr != nil && got != 0:
    94  				t.Errorf("Consume%v(%q) = %v, want %v", strings.Title(tt.literal), tt.in, got, 0)
    95  			}
    96  
    97  			got, gotErr := ConsumeLiteral([]byte(tt.in), tt.literal)
    98  			if got != tt.want || !reflect.DeepEqual(gotErr, tt.wantErr) {
    99  				t.Errorf("ConsumeLiteral(%q, %q) = (%v, %v), want (%v, %v)", tt.in, tt.literal, got, gotErr, tt.want, tt.wantErr)
   100  			}
   101  		})
   102  	}
   103  }
   104  
   105  func TestConsumeString(t *testing.T) {
   106  	var errPrev = errors.New("same as previous error")
   107  	tests := []struct {
   108  		in             string
   109  		simple         bool
   110  		want           int
   111  		wantUTF8       int // consumed bytes if validateUTF8 is specified
   112  		wantFlags      ValueFlags
   113  		wantUnquote    string
   114  		wantErr        error
   115  		wantErrUTF8    error // error if validateUTF8 is specified
   116  		wantErrUnquote error
   117  	}{
   118  		{``, false, 0, 0, 0, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   119  		{`"`, false, 1, 1, 0, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   120  		{`""`, true, 2, 2, 0, "", nil, nil, nil},
   121  		{`""x`, true, 2, 2, 0, "", nil, nil, NewInvalidCharacterError("x", "after string value")},
   122  		{` ""x`, false, 0, 0, 0, "", NewInvalidCharacterError(" ", "at start of string (expecting '\"')"), errPrev, errPrev},
   123  		{`"hello`, false, 6, 6, 0, "hello", io.ErrUnexpectedEOF, errPrev, errPrev},
   124  		{`"hello"`, true, 7, 7, 0, "hello", nil, nil, nil},
   125  		{"\"\x00\"", false, 1, 1, stringNonVerbatim | stringNonCanonical, "", NewInvalidCharacterError("\x00", "in string (expecting non-control character)"), errPrev, errPrev},
   126  		{`"\u0000"`, false, 8, 8, stringNonVerbatim, "\x00", nil, nil, nil},
   127  		{"\"\x1f\"", false, 1, 1, stringNonVerbatim | stringNonCanonical, "", NewInvalidCharacterError("\x1f", "in string (expecting non-control character)"), errPrev, errPrev},
   128  		{`"\u001f"`, false, 8, 8, stringNonVerbatim, "\x1f", nil, nil, nil},
   129  		{`"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"`, true, 54, 54, 0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", nil, nil, nil},
   130  		{"\" !#$%'()*+,-./0123456789:;=?@[]^_`{|}~\x7f\"", true, 41, 41, 0, " !#$%'()*+,-./0123456789:;=?@[]^_`{|}~\x7f", nil, nil, nil},
   131  		{`"&"`, false, 3, 3, 0, "&", nil, nil, nil},
   132  		{`"<"`, false, 3, 3, 0, "<", nil, nil, nil},
   133  		{`">"`, false, 3, 3, 0, ">", nil, nil, nil},
   134  		{"\"x\x80\"", false, 4, 2, stringNonVerbatim | stringNonCanonical, "x\ufffd", nil, ErrInvalidUTF8, errPrev},
   135  		{"\"x\xff\"", false, 4, 2, stringNonVerbatim | stringNonCanonical, "x\ufffd", nil, ErrInvalidUTF8, errPrev},
   136  		{"\"x\xc0", false, 3, 2, stringNonVerbatim | stringNonCanonical, "x\ufffd", io.ErrUnexpectedEOF, ErrInvalidUTF8, io.ErrUnexpectedEOF},
   137  		{"\"x\xc0\x80\"", false, 5, 2, stringNonVerbatim | stringNonCanonical, "x\ufffd\ufffd", nil, ErrInvalidUTF8, errPrev},
   138  		{"\"x\xe0", false, 2, 2, 0, "x", io.ErrUnexpectedEOF, errPrev, errPrev},
   139  		{"\"x\xe0\x80", false, 4, 2, stringNonVerbatim | stringNonCanonical, "x\ufffd\ufffd", io.ErrUnexpectedEOF, ErrInvalidUTF8, io.ErrUnexpectedEOF},
   140  		{"\"x\xe0\x80\x80\"", false, 6, 2, stringNonVerbatim | stringNonCanonical, "x\ufffd\ufffd\ufffd", nil, ErrInvalidUTF8, errPrev},
   141  		{"\"x\xf0", false, 2, 2, 0, "x", io.ErrUnexpectedEOF, errPrev, errPrev},
   142  		{"\"x\xf0\x80", false, 4, 2, stringNonVerbatim | stringNonCanonical, "x\ufffd\ufffd", io.ErrUnexpectedEOF, ErrInvalidUTF8, io.ErrUnexpectedEOF},
   143  		{"\"x\xf0\x80\x80", false, 5, 2, stringNonVerbatim | stringNonCanonical, "x\ufffd\ufffd\ufffd", io.ErrUnexpectedEOF, ErrInvalidUTF8, io.ErrUnexpectedEOF},
   144  		{"\"x\xf0\x80\x80\x80\"", false, 7, 2, stringNonVerbatim | stringNonCanonical, "x\ufffd\ufffd\ufffd\ufffd", nil, ErrInvalidUTF8, errPrev},
   145  		{"\"x\xed\xba\xad\"", false, 6, 2, stringNonVerbatim | stringNonCanonical, "x\ufffd\ufffd\ufffd", nil, ErrInvalidUTF8, errPrev},
   146  		{"\"\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\U0001f602\"", false, 25, 25, 0, "\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\U0001f602", nil, nil, nil},
   147  		{`"¢"`[:2], false, 1, 1, 0, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   148  		{`"¢"`[:3], false, 3, 3, 0, "¢", io.ErrUnexpectedEOF, errPrev, errPrev}, // missing terminating quote
   149  		{`"¢"`[:4], false, 4, 4, 0, "¢", nil, nil, nil},
   150  		{`"€"`[:2], false, 1, 1, 0, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   151  		{`"€"`[:3], false, 1, 1, 0, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   152  		{`"€"`[:4], false, 4, 4, 0, "€", io.ErrUnexpectedEOF, errPrev, errPrev}, // missing terminating quote
   153  		{`"€"`[:5], false, 5, 5, 0, "€", nil, nil, nil},
   154  		{`"𐍈"`[:2], false, 1, 1, 0, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   155  		{`"𐍈"`[:3], false, 1, 1, 0, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   156  		{`"𐍈"`[:4], false, 1, 1, 0, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   157  		{`"𐍈"`[:5], false, 5, 5, 0, "𐍈", io.ErrUnexpectedEOF, errPrev, errPrev}, // missing terminating quote
   158  		{`"𐍈"`[:6], false, 6, 6, 0, "𐍈", nil, nil, nil},
   159  		{`"x\`, false, 2, 2, stringNonVerbatim, "x", io.ErrUnexpectedEOF, errPrev, errPrev},
   160  		{`"x\"`, false, 4, 4, stringNonVerbatim, "x\"", io.ErrUnexpectedEOF, errPrev, errPrev},
   161  		{`"x\x"`, false, 2, 2, stringNonVerbatim | stringNonCanonical, "x", NewInvalidEscapeSequenceError(`\x`), errPrev, errPrev},
   162  		{`"\"\\\b\f\n\r\t"`, false, 16, 16, stringNonVerbatim, "\"\\\b\f\n\r\t", nil, nil, nil},
   163  		{`"/"`, true, 3, 3, 0, "/", nil, nil, nil},
   164  		{`"\/"`, false, 4, 4, stringNonVerbatim | stringNonCanonical, "/", nil, nil, nil},
   165  		{`"\u002f"`, false, 8, 8, stringNonVerbatim | stringNonCanonical, "/", nil, nil, nil},
   166  		{`"\u`, false, 1, 1, stringNonVerbatim, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   167  		{`"\uf`, false, 1, 1, stringNonVerbatim, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   168  		{`"\uff`, false, 1, 1, stringNonVerbatim, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   169  		{`"\ufff`, false, 1, 1, stringNonVerbatim, "", io.ErrUnexpectedEOF, errPrev, errPrev},
   170  		{`"\ufffd`, false, 7, 7, stringNonVerbatim | stringNonCanonical, "\ufffd", io.ErrUnexpectedEOF, errPrev, errPrev},
   171  		{`"\ufffd"`, false, 8, 8, stringNonVerbatim | stringNonCanonical, "\ufffd", nil, nil, nil},
   172  		{`"\uABCD"`, false, 8, 8, stringNonVerbatim | stringNonCanonical, "\uabcd", nil, nil, nil},
   173  		{`"\uefX0"`, false, 1, 1, stringNonVerbatim | stringNonCanonical, "", NewInvalidEscapeSequenceError(`\uefX0`), errPrev, errPrev},
   174  		{`"\uDEAD`, false, 7, 1, stringNonVerbatim | stringNonCanonical, "\ufffd", io.ErrUnexpectedEOF, errPrev, errPrev},
   175  		{`"\uDEAD"`, false, 8, 1, stringNonVerbatim | stringNonCanonical, "\ufffd", nil, NewInvalidEscapeSequenceError(`\uDEAD"`), errPrev},
   176  		{`"\uDEAD______"`, false, 14, 1, stringNonVerbatim | stringNonCanonical, "\ufffd______", nil, NewInvalidEscapeSequenceError(`\uDEAD______`), errPrev},
   177  		{`"\uDEAD\uXXXX"`, false, 7, 1, stringNonVerbatim | stringNonCanonical, "\ufffd", NewInvalidEscapeSequenceError(`\uXXXX`), NewInvalidEscapeSequenceError(`\uDEAD\uXXXX`), NewInvalidEscapeSequenceError(`\uXXXX`)},
   178  		{`"\uDEAD\uBEEF"`, false, 14, 1, stringNonVerbatim | stringNonCanonical, "\ufffd\ubeef", nil, NewInvalidEscapeSequenceError(`\uDEAD\uBEEF`), errPrev},
   179  		{`"\uD800\udea`, false, 7, 1, stringNonVerbatim | stringNonCanonical, "\ufffd", io.ErrUnexpectedEOF, errPrev, errPrev},
   180  		{`"\uD800\udb`, false, 7, 1, stringNonVerbatim | stringNonCanonical, "\ufffd", io.ErrUnexpectedEOF, NewInvalidEscapeSequenceError(`\uD800\udb`), io.ErrUnexpectedEOF},
   181  		{`"\uD800\udead"`, false, 14, 14, stringNonVerbatim | stringNonCanonical, "\U000102ad", nil, nil, nil},
   182  		{`"\u0022\u005c\u002f\u0008\u000c\u000a\u000d\u0009"`, false, 50, 50, stringNonVerbatim | stringNonCanonical, "\"\\/\b\f\n\r\t", nil, nil, nil},
   183  		{`"\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\ud83d\ude02"`, false, 56, 56, stringNonVerbatim | stringNonCanonical, "\u0080\u00f6\u20ac\ud799\ue000\ufb33\ufffd\U0001f602", nil, nil, nil},
   184  	}
   185  
   186  	for _, tt := range tests {
   187  		t.Run("", func(t *testing.T) {
   188  			if tt.wantErrUTF8 == errPrev {
   189  				tt.wantErrUTF8 = tt.wantErr
   190  			}
   191  			if tt.wantErrUnquote == errPrev {
   192  				tt.wantErrUnquote = tt.wantErrUTF8
   193  			}
   194  
   195  			switch got := ConsumeSimpleString([]byte(tt.in)); {
   196  			case tt.simple && got != tt.want:
   197  				t.Errorf("consumeSimpleString(%q) = %v, want %v", tt.in, got, tt.want)
   198  			case !tt.simple && got != 0:
   199  				t.Errorf("consumeSimpleString(%q) = %v, want %v", tt.in, got, 0)
   200  			}
   201  
   202  			var gotFlags ValueFlags
   203  			got, gotErr := ConsumeString(&gotFlags, []byte(tt.in), false)
   204  			if gotFlags != tt.wantFlags {
   205  				t.Errorf("consumeString(%q, false) flags = %v, want %v", tt.in, gotFlags, tt.wantFlags)
   206  			}
   207  			if got != tt.want || !reflect.DeepEqual(gotErr, tt.wantErr) {
   208  				t.Errorf("consumeString(%q, false) = (%v, %v), want (%v, %v)", tt.in, got, gotErr, tt.want, tt.wantErr)
   209  			}
   210  
   211  			got, gotErr = ConsumeString(&gotFlags, []byte(tt.in), true)
   212  			if got != tt.wantUTF8 || !reflect.DeepEqual(gotErr, tt.wantErrUTF8) {
   213  				t.Errorf("consumeString(%q, false) = (%v, %v), want (%v, %v)", tt.in, got, gotErr, tt.wantUTF8, tt.wantErrUTF8)
   214  			}
   215  
   216  			gotUnquote, gotErr := AppendUnquote(nil, tt.in)
   217  			if string(gotUnquote) != tt.wantUnquote || !reflect.DeepEqual(gotErr, tt.wantErrUnquote) {
   218  				t.Errorf("AppendUnquote(nil, %q) = (%q, %v), want (%q, %v)", tt.in[:got], gotUnquote, gotErr, tt.wantUnquote, tt.wantErrUnquote)
   219  			}
   220  		})
   221  	}
   222  }
   223  
   224  func TestConsumeNumber(t *testing.T) {
   225  	tests := []struct {
   226  		in      string
   227  		simple  bool
   228  		want    int
   229  		wantErr error
   230  	}{
   231  		{"", false, 0, io.ErrUnexpectedEOF},
   232  		{`"NaN"`, false, 0, NewInvalidCharacterError("\"", "in number (expecting digit)")},
   233  		{`"Infinity"`, false, 0, NewInvalidCharacterError("\"", "in number (expecting digit)")},
   234  		{`"-Infinity"`, false, 0, NewInvalidCharacterError("\"", "in number (expecting digit)")},
   235  		{".0", false, 0, NewInvalidCharacterError(".", "in number (expecting digit)")},
   236  		{"0", true, 1, nil},
   237  		{"-0", false, 2, nil},
   238  		{"+0", false, 0, NewInvalidCharacterError("+", "in number (expecting digit)")},
   239  		{"1", true, 1, nil},
   240  		{"-1", false, 2, nil},
   241  		{"00", true, 1, nil},
   242  		{"-00", false, 2, nil},
   243  		{"01", true, 1, nil},
   244  		{"-01", false, 2, nil},
   245  		{"0i", true, 1, nil},
   246  		{"-0i", false, 2, nil},
   247  		{"0f", true, 1, nil},
   248  		{"-0f", false, 2, nil},
   249  		{"9876543210", true, 10, nil},
   250  		{"-9876543210", false, 11, nil},
   251  		{"9876543210x", true, 10, nil},
   252  		{"-9876543210x", false, 11, nil},
   253  		{" 9876543210", true, 0, NewInvalidCharacterError(" ", "in number (expecting digit)")},
   254  		{"- 9876543210", false, 1, NewInvalidCharacterError(" ", "in number (expecting digit)")},
   255  		{strings.Repeat("9876543210", 1000), true, 10000, nil},
   256  		{"-" + strings.Repeat("9876543210", 1000), false, 1 + 10000, nil},
   257  		{"0.", false, 1, io.ErrUnexpectedEOF},
   258  		{"-0.", false, 2, io.ErrUnexpectedEOF},
   259  		{"0e", false, 1, io.ErrUnexpectedEOF},
   260  		{"-0e", false, 2, io.ErrUnexpectedEOF},
   261  		{"0E", false, 1, io.ErrUnexpectedEOF},
   262  		{"-0E", false, 2, io.ErrUnexpectedEOF},
   263  		{"0.0", false, 3, nil},
   264  		{"-0.0", false, 4, nil},
   265  		{"0e0", false, 3, nil},
   266  		{"-0e0", false, 4, nil},
   267  		{"0E0", false, 3, nil},
   268  		{"-0E0", false, 4, nil},
   269  		{"0.0123456789", false, 12, nil},
   270  		{"-0.0123456789", false, 13, nil},
   271  		{"1.f", false, 2, NewInvalidCharacterError("f", "in number (expecting digit)")},
   272  		{"-1.f", false, 3, NewInvalidCharacterError("f", "in number (expecting digit)")},
   273  		{"1.e", false, 2, NewInvalidCharacterError("e", "in number (expecting digit)")},
   274  		{"-1.e", false, 3, NewInvalidCharacterError("e", "in number (expecting digit)")},
   275  		{"1e0", false, 3, nil},
   276  		{"-1e0", false, 4, nil},
   277  		{"1E0", false, 3, nil},
   278  		{"-1E0", false, 4, nil},
   279  		{"1Ex", false, 2, NewInvalidCharacterError("x", "in number (expecting digit)")},
   280  		{"-1Ex", false, 3, NewInvalidCharacterError("x", "in number (expecting digit)")},
   281  		{"1e-0", false, 4, nil},
   282  		{"-1e-0", false, 5, nil},
   283  		{"1e+0", false, 4, nil},
   284  		{"-1e+0", false, 5, nil},
   285  		{"1E-0", false, 4, nil},
   286  		{"-1E-0", false, 5, nil},
   287  		{"1E+0", false, 4, nil},
   288  		{"-1E+0", false, 5, nil},
   289  		{"1E+00500", false, 8, nil},
   290  		{"-1E+00500", false, 9, nil},
   291  		{"1E+00500x", false, 8, nil},
   292  		{"-1E+00500x", false, 9, nil},
   293  		{"9876543210.0123456789e+01234589x", false, 31, nil},
   294  		{"-9876543210.0123456789e+01234589x", false, 32, nil},
   295  		{"1_000_000", true, 1, nil},
   296  		{"0x12ef", true, 1, nil},
   297  		{"0x1p-2", true, 1, nil},
   298  	}
   299  
   300  	for _, tt := range tests {
   301  		t.Run("", func(t *testing.T) {
   302  			switch got := ConsumeSimpleNumber([]byte(tt.in)); {
   303  			case tt.simple && got != tt.want:
   304  				t.Errorf("ConsumeSimpleNumber(%q) = %v, want %v", tt.in, got, tt.want)
   305  			case !tt.simple && got != 0:
   306  				t.Errorf("ConsumeSimpleNumber(%q) = %v, want %v", tt.in, got, 0)
   307  			}
   308  
   309  			got, gotErr := ConsumeNumber([]byte(tt.in))
   310  			if got != tt.want || !reflect.DeepEqual(gotErr, tt.wantErr) {
   311  				t.Errorf("ConsumeNumber(%q) = (%v, %v), want (%v, %v)", tt.in, got, gotErr, tt.want, tt.wantErr)
   312  			}
   313  		})
   314  	}
   315  }
   316  
   317  func TestParseHexUint16(t *testing.T) {
   318  	tests := []struct {
   319  		in     string
   320  		want   uint16
   321  		wantOk bool
   322  	}{
   323  		{"", 0, false},
   324  		{"a", 0, false},
   325  		{"ab", 0, false},
   326  		{"abc", 0, false},
   327  		{"abcd", 0xabcd, true},
   328  		{"abcde", 0, false},
   329  		{"9eA1", 0x9ea1, true},
   330  		{"gggg", 0, false},
   331  		{"0000", 0x0000, true},
   332  		{"1234", 0x1234, true},
   333  	}
   334  
   335  	for _, tt := range tests {
   336  		t.Run("", func(t *testing.T) {
   337  			got, gotOk := parseHexUint16([]byte(tt.in))
   338  			if got != tt.want || gotOk != tt.wantOk {
   339  				t.Errorf("parseHexUint16(%q) = (0x%04x, %v), want (0x%04x, %v)", tt.in, got, gotOk, tt.want, tt.wantOk)
   340  			}
   341  		})
   342  	}
   343  }
   344  
   345  func TestParseUint(t *testing.T) {
   346  	tests := []struct {
   347  		in     string
   348  		want   uint64
   349  		wantOk bool
   350  	}{
   351  		{"", 0, false},
   352  		{"0", 0, true},
   353  		{"1", 1, true},
   354  		{"-1", 0, false},
   355  		{"1f", 0, false},
   356  		{"00", 0, false},
   357  		{"01", 0, false},
   358  		{"10", 10, true},
   359  		{"10.9", 0, false},
   360  		{" 10", 0, false},
   361  		{"10 ", 0, false},
   362  		{"123456789", 123456789, true},
   363  		{"123456789d", 0, false},
   364  		{"18446744073709551614", math.MaxUint64 - 1, true},
   365  		{"18446744073709551615", math.MaxUint64, true},
   366  		{"18446744073709551616", math.MaxUint64, false},
   367  		{"18446744073709551620", math.MaxUint64, false},
   368  		{"18446744073709551700", math.MaxUint64, false},
   369  		{"18446744073709552000", math.MaxUint64, false},
   370  		{"18446744073709560000", math.MaxUint64, false},
   371  		{"18446744073709600000", math.MaxUint64, false},
   372  		{"18446744073710000000", math.MaxUint64, false},
   373  		{"18446744073800000000", math.MaxUint64, false},
   374  		{"18446744074000000000", math.MaxUint64, false},
   375  		{"18446744080000000000", math.MaxUint64, false},
   376  		{"18446744100000000000", math.MaxUint64, false},
   377  		{"18446745000000000000", math.MaxUint64, false},
   378  		{"18446750000000000000", math.MaxUint64, false},
   379  		{"18446800000000000000", math.MaxUint64, false},
   380  		{"18447000000000000000", math.MaxUint64, false},
   381  		{"18450000000000000000", math.MaxUint64, false},
   382  		{"18500000000000000000", math.MaxUint64, false},
   383  		{"19000000000000000000", math.MaxUint64, false},
   384  		{"19999999999999999999", math.MaxUint64, false},
   385  		{"20000000000000000000", math.MaxUint64, false},
   386  		{"100000000000000000000", math.MaxUint64, false},
   387  		{"99999999999999999999999999999999", math.MaxUint64, false},
   388  		{"99999999999999999999999999999999f", 0, false},
   389  	}
   390  
   391  	for _, tt := range tests {
   392  		t.Run("", func(t *testing.T) {
   393  			got, gotOk := ParseUint([]byte(tt.in))
   394  			if got != tt.want || gotOk != tt.wantOk {
   395  				t.Errorf("ParseUint(%q) = (%v, %v), want (%v, %v)", tt.in, got, gotOk, tt.want, tt.wantOk)
   396  			}
   397  		})
   398  	}
   399  }
   400  
   401  func TestParseFloat(t *testing.T) {
   402  	tests := []struct {
   403  		in     string
   404  		want32 float64
   405  		want64 float64
   406  		wantOk bool
   407  	}{
   408  		{"0", 0, 0, true},
   409  		{"-1", -1, -1, true},
   410  		{"1", 1, 1, true},
   411  
   412  		{"-16777215", -16777215, -16777215, true}, // -(1<<24 - 1)
   413  		{"16777215", 16777215, 16777215, true},    // +(1<<24 - 1)
   414  		{"-16777216", -16777216, -16777216, true}, // -(1<<24)
   415  		{"16777216", 16777216, 16777216, true},    // +(1<<24)
   416  		{"-16777217", -16777216, -16777217, true}, // -(1<<24 + 1)
   417  		{"16777217", 16777216, 16777217, true},    // +(1<<24 + 1)
   418  
   419  		{"-9007199254740991", -9007199254740992, -9007199254740991, true}, // -(1<<53 - 1)
   420  		{"9007199254740991", 9007199254740992, 9007199254740991, true},    // +(1<<53 - 1)
   421  		{"-9007199254740992", -9007199254740992, -9007199254740992, true}, // -(1<<53)
   422  		{"9007199254740992", 9007199254740992, 9007199254740992, true},    // +(1<<53)
   423  		{"-9007199254740993", -9007199254740992, -9007199254740992, true}, // -(1<<53 + 1)
   424  		{"9007199254740993", 9007199254740992, 9007199254740992, true},    // +(1<<53 + 1)
   425  
   426  		{"-1e1000", -math.MaxFloat32, -math.MaxFloat64, false},
   427  		{"1e1000", +math.MaxFloat32, +math.MaxFloat64, false},
   428  	}
   429  
   430  	for _, tt := range tests {
   431  		t.Run("", func(t *testing.T) {
   432  			got32, gotOk32 := ParseFloat([]byte(tt.in), 32)
   433  			if got32 != tt.want32 || gotOk32 != tt.wantOk {
   434  				t.Errorf("ParseFloat(%q, 32) = (%v, %v), want (%v, %v)", tt.in, got32, gotOk32, tt.want32, tt.wantOk)
   435  			}
   436  
   437  			got64, gotOk64 := ParseFloat([]byte(tt.in), 64)
   438  			if got64 != tt.want64 || gotOk64 != tt.wantOk {
   439  				t.Errorf("ParseFloat(%q, 64) = (%v, %v), want (%v, %v)", tt.in, got64, gotOk64, tt.want64, tt.wantOk)
   440  			}
   441  		})
   442  	}
   443  }
   444  

View as plain text