| // 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) |
| } |
| } |
| } |