google.golang.org/grpc@v1.62.1/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 "strings" 25 "testing" 26 "time" 27 28 "google.golang.org/grpc/internal/grpcrand" 29 ) 30 31 // Tests both marshalling and unmarshalling of Durations. 32 func TestDuration_MarshalUnmarshal(t *testing.T) { 33 testCases := []struct { 34 json string 35 td time.Duration 36 unmarshalErr error 37 noMarshal bool 38 }{ 39 // Basic values. 40 {json: `"1s"`, td: time.Second}, 41 {json: `"-100.700s"`, td: -100*time.Second - 700*time.Millisecond}, 42 {json: `".050s"`, td: 50 * time.Millisecond, noMarshal: true}, 43 {json: `"-.001s"`, td: -1 * time.Millisecond, noMarshal: true}, 44 {json: `"-0.200s"`, td: -200 * time.Millisecond}, 45 // Positive near / out of bounds. 46 {json: `"9223372036s"`, td: 9223372036 * time.Second}, 47 {json: `"9223372037s"`, td: math.MaxInt64, noMarshal: true}, 48 {json: `"9223372036.854775807s"`, td: math.MaxInt64}, 49 {json: `"9223372036.854775808s"`, td: math.MaxInt64, noMarshal: true}, 50 {json: `"315576000000s"`, td: math.MaxInt64, noMarshal: true}, 51 {json: `"315576000001s"`, unmarshalErr: fmt.Errorf("out of range")}, 52 // Negative near / out of bounds. 53 {json: `"-9223372036s"`, td: -9223372036 * time.Second}, 54 {json: `"-9223372037s"`, td: math.MinInt64, noMarshal: true}, 55 {json: `"-9223372036.854775808s"`, td: math.MinInt64}, 56 {json: `"-9223372036.854775809s"`, td: math.MinInt64, noMarshal: true}, 57 {json: `"-315576000000s"`, td: math.MinInt64, noMarshal: true}, 58 {json: `"-315576000001s"`, unmarshalErr: fmt.Errorf("out of range")}, 59 // Parse errors. 60 {json: `123s`, unmarshalErr: fmt.Errorf("invalid character")}, 61 {json: `"5m"`, unmarshalErr: fmt.Errorf("malformed duration")}, 62 {json: `"5.3.2s"`, unmarshalErr: fmt.Errorf("malformed duration")}, 63 {json: `"x.3s"`, unmarshalErr: fmt.Errorf("malformed duration")}, 64 {json: `"3.xs"`, unmarshalErr: fmt.Errorf("malformed duration")}, 65 {json: `"3.1234567890s"`, unmarshalErr: fmt.Errorf("malformed duration")}, 66 {json: `".s"`, unmarshalErr: fmt.Errorf("malformed duration")}, 67 {json: `"s"`, unmarshalErr: fmt.Errorf("malformed duration")}, 68 } 69 for _, tc := range testCases { 70 // Seed `got` with a random value to ensure we properly reset it in all 71 // non-error cases. 72 got := Duration(grpcrand.Uint64()) 73 err := got.UnmarshalJSON([]byte(tc.json)) 74 if (err == nil && time.Duration(got) != tc.td) || 75 (err != nil) != (tc.unmarshalErr != nil) || !strings.Contains(fmt.Sprint(err), fmt.Sprint(tc.unmarshalErr)) { 76 t.Errorf("UnmarshalJSON of %v = %v, %v; want %v, %v", tc.json, time.Duration(got), err, tc.td, tc.unmarshalErr) 77 } 78 79 if tc.unmarshalErr == nil && !tc.noMarshal { 80 d := Duration(tc.td) 81 got, err := d.MarshalJSON() 82 if string(got) != tc.json || err != nil { 83 t.Errorf("MarshalJSON of %v = %v, %v; want %v, nil", d, string(got), err, tc.json) 84 } 85 } 86 } 87 }