/* * * Copyright 2023 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package serviceconfig import ( "fmt" "math" "strings" "testing" "time" "google.golang.org/grpc/internal/grpcrand" ) // Tests both marshalling and unmarshalling of Durations. func TestDuration_MarshalUnmarshal(t *testing.T) { testCases := []struct { json string td time.Duration unmarshalErr error noMarshal bool }{ // Basic values. {json: `"1s"`, td: time.Second}, {json: `"-100.700s"`, td: -100*time.Second - 700*time.Millisecond}, {json: `".050s"`, td: 50 * time.Millisecond, noMarshal: true}, {json: `"-.001s"`, td: -1 * time.Millisecond, noMarshal: true}, {json: `"-0.200s"`, td: -200 * time.Millisecond}, // Positive near / out of bounds. {json: `"9223372036s"`, td: 9223372036 * time.Second}, {json: `"9223372037s"`, td: math.MaxInt64, noMarshal: true}, {json: `"9223372036.854775807s"`, td: math.MaxInt64}, {json: `"9223372036.854775808s"`, td: math.MaxInt64, noMarshal: true}, {json: `"315576000000s"`, td: math.MaxInt64, noMarshal: true}, {json: `"315576000001s"`, unmarshalErr: fmt.Errorf("out of range")}, // Negative near / out of bounds. {json: `"-9223372036s"`, td: -9223372036 * time.Second}, {json: `"-9223372037s"`, td: math.MinInt64, noMarshal: true}, {json: `"-9223372036.854775808s"`, td: math.MinInt64}, {json: `"-9223372036.854775809s"`, td: math.MinInt64, noMarshal: true}, {json: `"-315576000000s"`, td: math.MinInt64, noMarshal: true}, {json: `"-315576000001s"`, unmarshalErr: fmt.Errorf("out of range")}, // Parse errors. {json: `123s`, unmarshalErr: fmt.Errorf("invalid character")}, {json: `"5m"`, unmarshalErr: fmt.Errorf("malformed duration")}, {json: `"5.3.2s"`, unmarshalErr: fmt.Errorf("malformed duration")}, {json: `"x.3s"`, unmarshalErr: fmt.Errorf("malformed duration")}, {json: `"3.xs"`, unmarshalErr: fmt.Errorf("malformed duration")}, {json: `"3.1234567890s"`, unmarshalErr: fmt.Errorf("malformed duration")}, {json: `".s"`, unmarshalErr: fmt.Errorf("malformed duration")}, {json: `"s"`, unmarshalErr: fmt.Errorf("malformed duration")}, } for _, tc := range testCases { // Seed `got` with a random value to ensure we properly reset it in all // non-error cases. got := Duration(grpcrand.Uint64()) err := got.UnmarshalJSON([]byte(tc.json)) if (err == nil && time.Duration(got) != tc.td) || (err != nil) != (tc.unmarshalErr != nil) || !strings.Contains(fmt.Sprint(err), fmt.Sprint(tc.unmarshalErr)) { t.Errorf("UnmarshalJSON of %v = %v, %v; want %v, %v", tc.json, time.Duration(got), err, tc.td, tc.unmarshalErr) } if tc.unmarshalErr == nil && !tc.noMarshal { d := Duration(tc.td) got, err := d.MarshalJSON() if string(got) != tc.json || err != nil { t.Errorf("MarshalJSON of %v = %v, %v; want %v, nil", d, string(got), err, tc.json) } } } }