google.golang.org/grpc@v1.72.2/internal/serviceconfig/duration_test.go (about) 1 /* 2 * 3 * Copyright 2023 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package serviceconfig 20 21 import ( 22 "fmt" 23 "math" 24 rand "math/rand/v2" 25 "strings" 26 "testing" 27 "time" 28 ) 29 30 // Tests both marshalling and unmarshalling of Durations. 31 func TestDuration_MarshalUnmarshal(t *testing.T) { 32 testCases := []struct { 33 json string 34 td time.Duration 35 unmarshalErr error 36 noMarshal bool 37 }{ 38 // Basic values. 39 {json: `"1s"`, td: time.Second}, 40 {json: `"-100.700s"`, td: -100*time.Second - 700*time.Millisecond}, 41 {json: `".050s"`, td: 50 * time.Millisecond, noMarshal: true}, 42 {json: `"-.001s"`, td: -1 * time.Millisecond, noMarshal: true}, 43 {json: `"-0.200s"`, td: -200 * time.Millisecond}, 44 // Positive near / out of bounds. 45 {json: `"9223372036s"`, td: 9223372036 * time.Second}, 46 {json: `"9223372037s"`, td: math.MaxInt64, noMarshal: true}, 47 {json: `"9223372036.854775807s"`, td: math.MaxInt64}, 48 {json: `"9223372036.854775808s"`, td: math.MaxInt64, noMarshal: true}, 49 {json: `"315576000000s"`, td: math.MaxInt64, noMarshal: true}, 50 {json: `"315576000001s"`, unmarshalErr: fmt.Errorf("out of range")}, 51 // Negative near / out of bounds. 52 {json: `"-9223372036s"`, td: -9223372036 * time.Second}, 53 {json: `"-9223372037s"`, td: math.MinInt64, noMarshal: true}, 54 {json: `"-9223372036.854775808s"`, td: math.MinInt64}, 55 {json: `"-9223372036.854775809s"`, td: math.MinInt64, noMarshal: true}, 56 {json: `"-315576000000s"`, td: math.MinInt64, noMarshal: true}, 57 {json: `"-315576000001s"`, unmarshalErr: fmt.Errorf("out of range")}, 58 // Parse errors. 59 {json: `123s`, unmarshalErr: fmt.Errorf("invalid character")}, 60 {json: `"5m"`, unmarshalErr: fmt.Errorf("malformed duration")}, 61 {json: `"5.3.2s"`, unmarshalErr: fmt.Errorf("malformed duration")}, 62 {json: `"x.3s"`, unmarshalErr: fmt.Errorf("malformed duration")}, 63 {json: `"3.xs"`, unmarshalErr: fmt.Errorf("malformed duration")}, 64 {json: `"3.1234567890s"`, unmarshalErr: fmt.Errorf("malformed duration")}, 65 {json: `".s"`, unmarshalErr: fmt.Errorf("malformed duration")}, 66 {json: `"s"`, unmarshalErr: fmt.Errorf("malformed duration")}, 67 } 68 for _, tc := range testCases { 69 // Seed `got` with a random value to ensure we properly reset it in all 70 // non-error cases. 71 got := Duration(rand.Uint64()) 72 err := got.UnmarshalJSON([]byte(tc.json)) 73 if (err == nil && time.Duration(got) != tc.td) || 74 (err != nil) != (tc.unmarshalErr != nil) || !strings.Contains(fmt.Sprint(err), fmt.Sprint(tc.unmarshalErr)) { 75 t.Errorf("UnmarshalJSON of %v = %v, %v; want %v, %v", tc.json, time.Duration(got), err, tc.td, tc.unmarshalErr) 76 } 77 78 if tc.unmarshalErr == nil && !tc.noMarshal { 79 d := Duration(tc.td) 80 got, err := d.MarshalJSON() 81 if string(got) != tc.json || err != nil { 82 t.Errorf("MarshalJSON of %v = %v, %v; want %v, nil", d, string(got), err, tc.json) 83 } 84 } 85 } 86 }