| // Copyright 2022 Google LLC | |
| // | |
| // 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 bigquery | |
| import ( | |
| "testing" | |
| "time" | |
| "cloud.google.com/go/internal/testutil" | |
| ) | |
| func TestParseInterval(t *testing.T) { | |
| testcases := []struct { | |
| inputStr string | |
| wantInterval *IntervalValue | |
| wantErr bool | |
| }{ | |
| { | |
| inputStr: "", | |
| wantErr: true, | |
| }, | |
| { | |
| inputStr: "1-2 3", | |
| wantErr: true, | |
| }, | |
| { | |
| inputStr: "1-2 3 4:5:6", | |
| wantInterval: &IntervalValue{Years: 1, Months: 2, Days: 3, Hours: 4, Minutes: 5, Seconds: 6, SubSecondNanos: 0}, | |
| }, | |
| { | |
| inputStr: "1-2 3 4:5:6.5", | |
| wantInterval: &IntervalValue{Years: 1, Months: 2, Days: 3, Hours: 4, Minutes: 5, Seconds: 6, SubSecondNanos: 500000000}, | |
| }, | |
| { | |
| inputStr: "-1-2 3 -4:5:6.123", | |
| wantInterval: &IntervalValue{Years: -1, Months: -2, Days: 3, Hours: -4, Minutes: -5, Seconds: -6, SubSecondNanos: -123000000}, | |
| }, | |
| { | |
| inputStr: "0-0 0 1:1:1.000000001", | |
| wantInterval: &IntervalValue{Hours: 1, Minutes: 1, Seconds: 1, SubSecondNanos: 1}, | |
| }, | |
| } | |
| for _, tc := range testcases { | |
| gotInterval, err := ParseInterval(tc.inputStr) | |
| if tc.wantErr { | |
| if err != nil { | |
| continue | |
| } | |
| t.Errorf("input %s: wanted err, got success", tc.inputStr) | |
| } | |
| if err != nil { | |
| t.Errorf("input %s got err: %v", tc.inputStr, err) | |
| } | |
| if diff := testutil.Diff(gotInterval, tc.wantInterval); diff != "" { | |
| t.Errorf("input %s: got=-, want=+:\n%s", tc.inputStr, diff) | |
| } | |
| } | |
| } | |
| func TestCanonicalInterval(t *testing.T) { | |
| testcases := []struct { | |
| description string | |
| input *IntervalValue | |
| wantCanonical *IntervalValue | |
| wantString string | |
| }{ | |
| { | |
| description: "already canonical", | |
| input: &IntervalValue{Years: 1, Months: 2, Days: 3, Hours: 4, Minutes: 5, Seconds: 6, SubSecondNanos: 0}, | |
| wantCanonical: &IntervalValue{Years: 1, Months: 2, Days: 3, Hours: 4, Minutes: 5, Seconds: 6, SubSecondNanos: 0}, | |
| wantString: "1-2 3 4:5:6", | |
| }, | |
| { | |
| description: "mixed Y-M", | |
| input: &IntervalValue{Years: -1, Months: 28}, | |
| wantCanonical: &IntervalValue{Years: 1, Months: 4, Days: 0, Hours: 0, Minutes: 0, Seconds: 0, SubSecondNanos: 0}, | |
| wantString: "1-4 0 0:0:0", | |
| }, | |
| { | |
| description: "mixed Y-M", | |
| input: &IntervalValue{Years: -1, Months: 28}, | |
| wantCanonical: &IntervalValue{Years: 1, Months: 4, Days: 0, Hours: 0, Minutes: 0, Seconds: 0, SubSecondNanos: 0}, | |
| wantString: "1-4 0 0:0:0", | |
| }, | |
| { | |
| description: "big month Y-M", | |
| input: &IntervalValue{Years: 0, Months: -13}, | |
| wantCanonical: &IntervalValue{Years: -1, Months: -1, Days: 0, Hours: 0, Minutes: 0, Seconds: 0, SubSecondNanos: 0}, | |
| wantString: "-1-1 0 0:0:0", | |
| }, | |
| { | |
| description: "big days not normalized", | |
| input: &IntervalValue{Days: 1000}, | |
| wantCanonical: &IntervalValue{Years: 0, Months: 0, Days: 1000, Hours: 0, Minutes: 0, Seconds: 0, SubSecondNanos: 0}, | |
| wantString: "0-0 1000 0:0:0", | |
| }, | |
| { | |
| description: "time reduced", | |
| input: &IntervalValue{Minutes: 181, Seconds: 61, SubSecondNanos: 5}, | |
| wantCanonical: &IntervalValue{Hours: 3, Minutes: 2, Seconds: 1, SubSecondNanos: 5}, | |
| wantString: "0-0 0 3:2:1.000000005", | |
| }, | |
| { | |
| description: "subseconds oversized", | |
| input: &IntervalValue{SubSecondNanos: 1900000000}, | |
| wantCanonical: &IntervalValue{Years: 0, Months: 0, Days: 0, Hours: 0, Minutes: 0, Seconds: 1, SubSecondNanos: 900000000}, | |
| wantString: "0-0 0 0:0:1.9", | |
| }, | |
| } | |
| for _, tc := range testcases { | |
| gotCanonical := tc.input.Canonicalize() | |
| if diff := testutil.Diff(gotCanonical, tc.wantCanonical); diff != "" { | |
| t.Errorf("%s: got=-, want=+:\n%s", tc.description, diff) | |
| } | |
| gotStr := tc.input.String() | |
| if gotStr != tc.wantString { | |
| t.Errorf("%s mismatched strings. got %s want %s", tc.description, gotStr, tc.wantString) | |
| } | |
| } | |
| } | |
| func TestIntervalDuration(t *testing.T) { | |
| testcases := []struct { | |
| description string | |
| inputInterval *IntervalValue | |
| wantDuration time.Duration | |
| wantInterval *IntervalValue | |
| }{ | |
| { | |
| description: "hour", | |
| inputInterval: &IntervalValue{Hours: 1}, | |
| wantDuration: time.Duration(time.Hour), | |
| wantInterval: &IntervalValue{Hours: 1}, | |
| }, | |
| { | |
| description: "minute oversized", | |
| inputInterval: &IntervalValue{Minutes: 62}, | |
| wantDuration: time.Duration(62 * time.Minute), | |
| wantInterval: &IntervalValue{Hours: 1, Minutes: 2}, | |
| }, | |
| { | |
| description: "other parts", | |
| inputInterval: &IntervalValue{Months: 1, Days: 2}, | |
| wantDuration: time.Duration(32 * 24 * time.Hour), | |
| wantInterval: &IntervalValue{Hours: 32 * 24}, | |
| }, | |
| } | |
| for _, tc := range testcases { | |
| gotDuration := tc.inputInterval.ToDuration() | |
| // interval -> duration | |
| if gotDuration != tc.wantDuration { | |
| t.Errorf("%s: mismatched duration, got %v want %v", tc.description, gotDuration, tc.wantDuration) | |
| } | |
| // duration -> interval (canonical) | |
| gotInterval := IntervalValueFromDuration(gotDuration) | |
| if diff := testutil.Diff(gotInterval, tc.wantInterval); diff != "" { | |
| t.Errorf("%s: got=-, want=+:\n%s", tc.description, diff) | |
| } | |
| } | |
| } |