Source file src/encoding/json/v2/arshal_time_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 json
     8  
     9  import (
    10  	"fmt"
    11  	"math"
    12  	"testing"
    13  	"time"
    14  
    15  	"encoding/json/internal/jsonwire"
    16  )
    17  
    18  func baseLabel(base uint64) string {
    19  	if log10 := math.Log10(float64(base)); log10 == float64(int64(log10)) {
    20  		return fmt.Sprintf("1e%d", int(log10))
    21  	}
    22  	return fmt.Sprint(base)
    23  }
    24  
    25  var formatDurationTestdata = []struct {
    26  	td          time.Duration
    27  	base10Sec   string
    28  	base10Milli string
    29  	base10Micro string
    30  	base10Nano  string
    31  }{
    32  	{math.MaxInt64, "9223372036.854775807", "9223372036854.775807", "9223372036854775.807", "9223372036854775807"},
    33  	{1e12 + 1e12, "2000", "2000000", "2000000000", "2000000000000"},
    34  	{1e12 + 1e11, "1100", "1100000", "1100000000", "1100000000000"},
    35  	{1e12 + 1e10, "1010", "1010000", "1010000000", "1010000000000"},
    36  	{1e12 + 1e9, "1001", "1001000", "1001000000", "1001000000000"},
    37  	{1e12 + 1e8, "1000.1", "1000100", "1000100000", "1000100000000"},
    38  	{1e12 + 1e7, "1000.01", "1000010", "1000010000", "1000010000000"},
    39  	{1e12 + 1e6, "1000.001", "1000001", "1000001000", "1000001000000"},
    40  	{1e12 + 1e5, "1000.0001", "1000000.1", "1000000100", "1000000100000"},
    41  	{1e12 + 1e4, "1000.00001", "1000000.01", "1000000010", "1000000010000"},
    42  	{1e12 + 1e3, "1000.000001", "1000000.001", "1000000001", "1000000001000"},
    43  	{1e12 + 1e2, "1000.0000001", "1000000.0001", "1000000000.1", "1000000000100"},
    44  	{1e12 + 1e1, "1000.00000001", "1000000.00001", "1000000000.01", "1000000000010"},
    45  	{1e12 + 1e0, "1000.000000001", "1000000.000001", "1000000000.001", "1000000000001"},
    46  	{+(1e9 + 1), "1.000000001", "1000.000001", "1000000.001", "1000000001"},
    47  	{+(1e9), "1", "1000", "1000000", "1000000000"},
    48  	{+(1e9 - 1), "0.999999999", "999.999999", "999999.999", "999999999"},
    49  	{+100000000, "0.1", "100", "100000", "100000000"},
    50  	{+120000000, "0.12", "120", "120000", "120000000"},
    51  	{+123000000, "0.123", "123", "123000", "123000000"},
    52  	{+123400000, "0.1234", "123.4", "123400", "123400000"},
    53  	{+123450000, "0.12345", "123.45", "123450", "123450000"},
    54  	{+123456000, "0.123456", "123.456", "123456", "123456000"},
    55  	{+123456700, "0.1234567", "123.4567", "123456.7", "123456700"},
    56  	{+123456780, "0.12345678", "123.45678", "123456.78", "123456780"},
    57  	{+123456789, "0.123456789", "123.456789", "123456.789", "123456789"},
    58  	{+12345678, "0.012345678", "12.345678", "12345.678", "12345678"},
    59  	{+1234567, "0.001234567", "1.234567", "1234.567", "1234567"},
    60  	{+123456, "0.000123456", "0.123456", "123.456", "123456"},
    61  	{+12345, "0.000012345", "0.012345", "12.345", "12345"},
    62  	{+1234, "0.000001234", "0.001234", "1.234", "1234"},
    63  	{+123, "0.000000123", "0.000123", "0.123", "123"},
    64  	{+12, "0.000000012", "0.000012", "0.012", "12"},
    65  	{+1, "0.000000001", "0.000001", "0.001", "1"},
    66  	{0, "0", "0", "0", "0"},
    67  	{-1, "-0.000000001", "-0.000001", "-0.001", "-1"},
    68  	{-12, "-0.000000012", "-0.000012", "-0.012", "-12"},
    69  	{-123, "-0.000000123", "-0.000123", "-0.123", "-123"},
    70  	{-1234, "-0.000001234", "-0.001234", "-1.234", "-1234"},
    71  	{-12345, "-0.000012345", "-0.012345", "-12.345", "-12345"},
    72  	{-123456, "-0.000123456", "-0.123456", "-123.456", "-123456"},
    73  	{-1234567, "-0.001234567", "-1.234567", "-1234.567", "-1234567"},
    74  	{-12345678, "-0.012345678", "-12.345678", "-12345.678", "-12345678"},
    75  	{-123456789, "-0.123456789", "-123.456789", "-123456.789", "-123456789"},
    76  	{-123456780, "-0.12345678", "-123.45678", "-123456.78", "-123456780"},
    77  	{-123456700, "-0.1234567", "-123.4567", "-123456.7", "-123456700"},
    78  	{-123456000, "-0.123456", "-123.456", "-123456", "-123456000"},
    79  	{-123450000, "-0.12345", "-123.45", "-123450", "-123450000"},
    80  	{-123400000, "-0.1234", "-123.4", "-123400", "-123400000"},
    81  	{-123000000, "-0.123", "-123", "-123000", "-123000000"},
    82  	{-120000000, "-0.12", "-120", "-120000", "-120000000"},
    83  	{-100000000, "-0.1", "-100", "-100000", "-100000000"},
    84  	{-(1e9 - 1), "-0.999999999", "-999.999999", "-999999.999", "-999999999"},
    85  	{-(1e9), "-1", "-1000", "-1000000", "-1000000000"},
    86  	{-(1e9 + 1), "-1.000000001", "-1000.000001", "-1000000.001", "-1000000001"},
    87  	{math.MinInt64, "-9223372036.854775808", "-9223372036854.775808", "-9223372036854775.808", "-9223372036854775808"},
    88  }
    89  
    90  func TestFormatDuration(t *testing.T) {
    91  	var gotBuf []byte
    92  	check := func(td time.Duration, s string, base uint64) {
    93  		a := durationArshaler{td, base}
    94  		gotBuf, _ = a.appendMarshal(gotBuf[:0])
    95  		if string(gotBuf) != s {
    96  			t.Errorf("formatDuration(%d, %s) = %q, want %q", td, baseLabel(base), string(gotBuf), s)
    97  		}
    98  		if err := a.unmarshal(gotBuf); err != nil {
    99  			t.Errorf("parseDuration(%q, %s) error: %v", gotBuf, baseLabel(base), err)
   100  		}
   101  		if a.td != td {
   102  			t.Errorf("parseDuration(%q, %s) = %d, want %d", gotBuf, baseLabel(base), a.td, td)
   103  		}
   104  	}
   105  	for _, tt := range formatDurationTestdata {
   106  		check(tt.td, tt.base10Sec, 1e9)
   107  		check(tt.td, tt.base10Milli, 1e6)
   108  		check(tt.td, tt.base10Micro, 1e3)
   109  		check(tt.td, tt.base10Nano, 1e0)
   110  	}
   111  }
   112  
   113  var parseDurationTestdata = []struct {
   114  	in      string
   115  	base    uint64
   116  	want    time.Duration
   117  	wantErr bool
   118  }{
   119  	{"0", 1e0, 0, false},
   120  	{"0.", 1e0, 0, true},
   121  	{"0.0", 1e0, 0, false},
   122  	{"0.00", 1e0, 0, false},
   123  	{"00.0", 1e0, 0, true},
   124  	{"+0", 1e0, 0, true},
   125  	{"1e0", 1e0, 0, true},
   126  	{"1.000000000x", 1e9, 0, true},
   127  	{"1.000000x", 1e6, 0, true},
   128  	{"1.000x", 1e3, 0, true},
   129  	{"1.x", 1e0, 0, true},
   130  	{"1.0000000009", 1e9, +time.Second, false},
   131  	{"1.0000009", 1e6, +time.Millisecond, false},
   132  	{"1.0009", 1e3, +time.Microsecond, false},
   133  	{"1.9", 1e0, +time.Nanosecond, false},
   134  	{"-9223372036854775809", 1e0, 0, true},
   135  	{"9223372036854775.808", 1e3, 0, true},
   136  	{"-9223372036854.775809", 1e6, 0, true},
   137  	{"9223372036.854775808", 1e9, 0, true},
   138  	{"-1.9", 1e0, -time.Nanosecond, false},
   139  	{"-1.0009", 1e3, -time.Microsecond, false},
   140  	{"-1.0000009", 1e6, -time.Millisecond, false},
   141  	{"-1.0000000009", 1e9, -time.Second, false},
   142  }
   143  
   144  func TestParseDuration(t *testing.T) {
   145  	for _, tt := range parseDurationTestdata {
   146  		a := durationArshaler{base: tt.base}
   147  		switch err := a.unmarshal([]byte(tt.in)); {
   148  		case a.td != tt.want:
   149  			t.Errorf("parseDuration(%q, %s) = %v, want %v", tt.in, baseLabel(tt.base), a.td, tt.want)
   150  		case (err == nil) && tt.wantErr:
   151  			t.Errorf("parseDuration(%q, %s) error is nil, want non-nil", tt.in, baseLabel(tt.base))
   152  		case (err != nil) && !tt.wantErr:
   153  			t.Errorf("parseDuration(%q, %s) error is non-nil, want nil", tt.in, baseLabel(tt.base))
   154  		}
   155  	}
   156  }
   157  
   158  func FuzzFormatDuration(f *testing.F) {
   159  	for _, tt := range formatDurationTestdata {
   160  		f.Add(int64(tt.td))
   161  	}
   162  	f.Fuzz(func(t *testing.T, want int64) {
   163  		var buf []byte
   164  		for _, base := range [...]uint64{1e0, 1e3, 1e6, 1e9} {
   165  			a := durationArshaler{td: time.Duration(want), base: base}
   166  			buf, _ = a.appendMarshal(buf[:0])
   167  			switch err := a.unmarshal(buf); {
   168  			case err != nil:
   169  				t.Fatalf("parseDuration(%q, %s) error: %v", buf, baseLabel(base), err)
   170  			case a.td != time.Duration(want):
   171  				t.Fatalf("parseDuration(%q, %s) = %v, want %v", buf, baseLabel(base), a.td, time.Duration(want))
   172  			}
   173  		}
   174  	})
   175  }
   176  
   177  func FuzzParseDuration(f *testing.F) {
   178  	for _, tt := range parseDurationTestdata {
   179  		f.Add([]byte(tt.in))
   180  	}
   181  	f.Fuzz(func(t *testing.T, in []byte) {
   182  		for _, base := range [...]uint64{1e0, 1e3, 1e6, 1e9, 60} {
   183  			a := durationArshaler{base: base}
   184  			if err := a.unmarshal(in); err == nil && base != 60 {
   185  				if n, err := jsonwire.ConsumeNumber(in); err != nil || n != len(in) {
   186  					t.Fatalf("parseDuration(%q) error is nil for invalid JSON number", in)
   187  				}
   188  			}
   189  		}
   190  	})
   191  }
   192  
   193  type formatTimeTestdataEntry struct {
   194  	ts        time.Time
   195  	unixSec   string
   196  	unixMilli string
   197  	unixMicro string
   198  	unixNano  string
   199  }
   200  
   201  var formatTimeTestdata = func() []formatTimeTestdataEntry {
   202  	out := []formatTimeTestdataEntry{
   203  		{time.Unix(math.MaxInt64/int64(1e0), 1e9-1).UTC(), "9223372036854775807.999999999", "9223372036854775807999.999999", "9223372036854775807999999.999", "9223372036854775807999999999"},
   204  		{time.Unix(math.MaxInt64/int64(1e1), 1e9-1).UTC(), "922337203685477580.999999999", "922337203685477580999.999999", "922337203685477580999999.999", "922337203685477580999999999"},
   205  		{time.Unix(math.MaxInt64/int64(1e2), 1e9-1).UTC(), "92233720368547758.999999999", "92233720368547758999.999999", "92233720368547758999999.999", "92233720368547758999999999"},
   206  		{time.Unix(math.MinInt64, 1).UTC(), "-9223372036854775807.999999999", "-9223372036854775807999.999999", "-9223372036854775807999999.999", "-9223372036854775807999999999"},
   207  		{time.Unix(math.MinInt64, 0).UTC(), "-9223372036854775808", "-9223372036854775808000", "-9223372036854775808000000", "-9223372036854775808000000000"},
   208  	}
   209  	for _, tt := range formatDurationTestdata {
   210  		out = append(out, formatTimeTestdataEntry{time.Unix(0, int64(tt.td)).UTC(), tt.base10Sec, tt.base10Milli, tt.base10Micro, tt.base10Nano})
   211  	}
   212  	return out
   213  }()
   214  
   215  func TestFormatTime(t *testing.T) {
   216  	var gotBuf []byte
   217  	check := func(ts time.Time, s string, pow10 uint64) {
   218  		gotBuf = appendTimeUnix(gotBuf[:0], ts, pow10)
   219  		if string(gotBuf) != s {
   220  			t.Errorf("formatTime(time.Unix(%d, %d), %s) = %q, want %q", ts.Unix(), ts.Nanosecond(), baseLabel(pow10), string(gotBuf), s)
   221  		}
   222  		gotTS, err := parseTimeUnix(gotBuf, pow10)
   223  		if err != nil {
   224  			t.Errorf("parseTime(%q, %s) error: %v", gotBuf, baseLabel(pow10), err)
   225  		}
   226  		if !gotTS.Equal(ts) {
   227  			t.Errorf("parseTime(%q, %s) = time.Unix(%d, %d), want time.Unix(%d, %d)", gotBuf, baseLabel(pow10), gotTS.Unix(), gotTS.Nanosecond(), ts.Unix(), ts.Nanosecond())
   228  		}
   229  	}
   230  	for _, tt := range formatTimeTestdata {
   231  		check(tt.ts, tt.unixSec, 1e0)
   232  		check(tt.ts, tt.unixMilli, 1e3)
   233  		check(tt.ts, tt.unixMicro, 1e6)
   234  		check(tt.ts, tt.unixNano, 1e9)
   235  	}
   236  }
   237  
   238  var parseTimeTestdata = []struct {
   239  	in      string
   240  	base    uint64
   241  	want    time.Time
   242  	wantErr bool
   243  }{
   244  	{"0", 1e0, time.Unix(0, 0).UTC(), false},
   245  	{"0.", 1e0, time.Time{}, true},
   246  	{"0.0", 1e0, time.Unix(0, 0).UTC(), false},
   247  	{"0.00", 1e0, time.Unix(0, 0).UTC(), false},
   248  	{"00.0", 1e0, time.Time{}, true},
   249  	{"+0", 1e0, time.Time{}, true},
   250  	{"1e0", 1e0, time.Time{}, true},
   251  	{"1234567890123456789012345678901234567890", 1e0, time.Time{}, true},
   252  	{"9223372036854775808000.000000", 1e3, time.Time{}, true},
   253  	{"9223372036854775807999999.9999", 1e6, time.Unix(math.MaxInt64, 1e9-1).UTC(), false},
   254  	{"9223372036854775807999999999.9", 1e9, time.Unix(math.MaxInt64, 1e9-1).UTC(), false},
   255  	{"9223372036854775807.999999999x", 1e0, time.Time{}, true},
   256  	{"9223372036854775807000000000", 1e9, time.Unix(math.MaxInt64, 0).UTC(), false},
   257  	{"-9223372036854775808", 1e0, time.Unix(math.MinInt64, 0).UTC(), false},
   258  	{"-9223372036854775808000.000001", 1e3, time.Time{}, true},
   259  	{"-9223372036854775808000000.0001", 1e6, time.Unix(math.MinInt64, 0).UTC(), false},
   260  	{"-9223372036854775808000000000.x", 1e9, time.Time{}, true},
   261  	{"-1234567890123456789012345678901234567890", 1e9, time.Time{}, true},
   262  }
   263  
   264  func TestParseTime(t *testing.T) {
   265  	for _, tt := range parseTimeTestdata {
   266  		a := timeArshaler{base: tt.base}
   267  		switch err := a.unmarshal([]byte(tt.in)); {
   268  		case a.tt != tt.want:
   269  			t.Errorf("parseTime(%q, %s) = time.Unix(%d, %d), want time.Unix(%d, %d)", tt.in, baseLabel(tt.base), a.tt.Unix(), a.tt.Nanosecond(), tt.want.Unix(), tt.want.Nanosecond())
   270  		case (err == nil) && tt.wantErr:
   271  			t.Errorf("parseTime(%q, %s) = (time.Unix(%d, %d), nil), want non-nil error", tt.in, baseLabel(tt.base), a.tt.Unix(), a.tt.Nanosecond())
   272  		case (err != nil) && !tt.wantErr:
   273  			t.Errorf("parseTime(%q, %s) error is non-nil, want nil", tt.in, baseLabel(tt.base))
   274  		}
   275  	}
   276  }
   277  
   278  func FuzzFormatTime(f *testing.F) {
   279  	for _, tt := range formatTimeTestdata {
   280  		f.Add(tt.ts.Unix(), int64(tt.ts.Nanosecond()))
   281  	}
   282  	f.Fuzz(func(t *testing.T, wantSec, wantNano int64) {
   283  		want := time.Unix(wantSec, int64(uint64(wantNano)%1e9)).UTC()
   284  		var buf []byte
   285  		for _, base := range [...]uint64{1e0, 1e3, 1e6, 1e9} {
   286  			a := timeArshaler{tt: want, base: base}
   287  			buf, _ = a.appendMarshal(buf[:0])
   288  			switch err := a.unmarshal(buf); {
   289  			case err != nil:
   290  				t.Fatalf("parseTime(%q, %s) error: %v", buf, baseLabel(base), err)
   291  			case a.tt != want:
   292  				t.Fatalf("parseTime(%q, %s) = time.Unix(%d, %d), want time.Unix(%d, %d)", buf, baseLabel(base), a.tt.Unix(), a.tt.Nanosecond(), want.Unix(), want.Nanosecond())
   293  			}
   294  		}
   295  	})
   296  }
   297  
   298  func FuzzParseTime(f *testing.F) {
   299  	for _, tt := range parseTimeTestdata {
   300  		f.Add([]byte(tt.in))
   301  	}
   302  	f.Fuzz(func(t *testing.T, in []byte) {
   303  		for _, base := range [...]uint64{1e0, 1e3, 1e6, 1e9} {
   304  			a := timeArshaler{base: base}
   305  			if err := a.unmarshal(in); err == nil {
   306  				if n, err := jsonwire.ConsumeNumber(in); err != nil || n != len(in) {
   307  					t.Fatalf("parseTime(%q) error is nil for invalid JSON number", in)
   308  				}
   309  			}
   310  		}
   311  	})
   312  }
   313  

View as plain text