blob: 5696541aa870b8a19afa19e22413ad40f8578d05 [file] [log] [blame]
/*
*
* 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)
}
}
}
}