// Copyright 2020 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 proftest import ( "regexp" "testing" "time" ) func TestParseBenchmarkNumber(t *testing.T) { benchNumRE, err := regexp.Compile("benchmark (\\d+):") if err != nil { t.Fatalf("failed to get benchmark regexp: %v", err) } numBenchmarks := 150 for _, tc := range []struct { desc string line string wantBenchNum int wantErr bool }{ { desc: "zero parsed successfully as benchmark number", line: "benchmark 0: 2020/05/15 23:37:56 start uploading profile", wantBenchNum: 0, }, { desc: "benchmark number for empty line can be parsed", line: "benchmark 5:", wantBenchNum: 5, }, { desc: "multi-digit benchmark number can be parsed", line: "benchmark 149: line", wantBenchNum: 149, }, { desc: "benchmark number can be parsed when it is not at the start of the line", line: "Mon May 18 00:00:00 UTC 2020: benchmark 5: line", wantBenchNum: 5, }, { desc: "an error is returned when benchmark number is outside the expected range of benchmark numbers", line: "benchmark 150: line", wantErr: true, }, { desc: "an error is returned when benchmark number is not a number", line: "benchmark abc: 2020/05/15 23:40:46 creating a new profile via profiler service", wantErr: true, }, } { t.Run(tc.desc, func(t *testing.T) { benchNum, err := parseBenchmarkNumber(tc.line, numBenchmarks, benchNumRE) if (err != nil) != tc.wantErr { t.Errorf("got err = %v; want (err != nil) = %v", err, tc.wantErr) } if tc.wantErr { return } if benchNum != tc.wantBenchNum { t.Errorf("got benchmark number = %v, want %v", benchNum, tc.wantBenchNum) } }) } } func TestParseLogTime(t *testing.T) { for _, tc := range []struct { desc string line string wantTime time.Time wantErr bool }{ { desc: "a valid timestamp is parsed correctly", line: "Fri May 15 23:39:53 UTC 2020: benchmark 31: creating a new profile via profiler service", wantTime: time.Date(2020, 5, 15, 23, 39, 53, 0, time.UTC), }, { desc: "an error is returned when the timestamp is invalid", line: "Fri May 15 26:39:53 UTC 2020: benchmark 31: creating a new profile via profiler service", wantErr: true, }, } { t.Run(tc.desc, func(t *testing.T) { logTime, err := parseLogTime(tc.line) if (err != nil) != tc.wantErr { t.Errorf("got err = %v; want (err != nil) = %v", err, tc.wantErr) } if tc.wantErr { return } if !logTime.Equal(tc.wantTime) { t.Errorf("got log time = %v, want %v", logTime, tc.wantTime) } }) } } func TestParseBackoffDuration(t *testing.T) { for _, tc := range []struct { desc string line string wantBackoffDur time.Duration wantErr bool }{ { desc: "a valid backoff duration is parsed correctly", line: "Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 32m0s", wantBackoffDur: 32 * time.Minute, }, { desc: "a floating-point backoff duration is parsed correctly", line: "Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 2000.000s", wantBackoffDur: 2000 * time.Second, }, { desc: "an error is returned when the backoff duration is invalid", line: "Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 32..0.s", wantErr: true, }, { desc: "a backoff duration specifying hours, minutes, seconds, milliseconds and microseconds is parsed correctly.", line: "Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 1h1m1s1ms1us", wantBackoffDur: time.Hour + time.Minute + time.Second + time.Millisecond + time.Microsecond, }, } { t.Run(tc.desc, func(t *testing.T) { backoffDur, err := parseBackoffDuration(tc.line) if (err != nil) != tc.wantErr { t.Errorf("got err = %v; want (err != nil) = %v", err, tc.wantErr) } if tc.wantErr { return } if backoffDur != tc.wantBackoffDur { t.Errorf("backoff duration: got %v, want %v", backoffDur, tc.wantBackoffDur) } }) } } func TestCheckSerialOutputForBackoffs(t *testing.T) { for _, tc := range []struct { desc string logs string numBenchmarks int serverBackoffSubstring string createProfileSubstring string benchmarkNumPrefix string wantErr bool }{ { desc: "no error when one benchmark running and the next profile is created after backoff interval", logs: ` Fri May 15 22:05:00 UTC 2020: benchmark 0: creating a new profile via profiler service Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 32m0s Fri May 15 22:37:01 UTC 2020: benchmark 0: creating a new profile via profiler service `, numBenchmarks: 1, serverBackoffSubstring: "action throttled, backoff for", createProfileSubstring: "creating a new profile via profiler service", benchmarkNumPrefix: "benchmark", }, { desc: "no error when one benchmark running and the next profile is created 1 minute after backoff interval has elapsed", logs: ` Fri May 15 22:05:00 UTC 2020: benchmark 0: creating a new profile via profiler service Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 50m0s Fri May 15 22:56:01 UTC 2020: benchmark 0: creating a new profile via profiler service `, numBenchmarks: 1, serverBackoffSubstring: "action throttled, backoff for", createProfileSubstring: "creating a new profile via profiler service", benchmarkNumPrefix: "benchmark", }, { desc: "no error when one benchmark running and the next profile is created 1 minute before backoff interval has elapsed", logs: ` Fri May 15 22:05:00 UTC 2020: benchmark 0: creating a new profile via profiler service Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 45m0s Fri May 15 22:49:01 UTC 2020: benchmark 0: creating a new profile via profiler service `, numBenchmarks: 1, serverBackoffSubstring: "action throttled, backoff for", createProfileSubstring: "creating a new profile via profiler service", benchmarkNumPrefix: "benchmark", }, { desc: "error when one benchmark running and the next profile is created more than 1 minute before backoff interval has elapsed", logs: ` Fri May 15 22:05:00 UTC 2020: benchmark 0: creating a new profile via profiler service Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 32m0s Fri May 15 22:36:00 UTC 2020: benchmark 0: creating a new profile via profiler service `, numBenchmarks: 1, serverBackoffSubstring: "action throttled, backoff for", createProfileSubstring: "creating a new profile via profiler service", benchmarkNumPrefix: "benchmark", wantErr: true, }, { desc: "error when one benchmark running and the next profile is created more than 1 minute after backoff interval has elapsed", logs: ` Fri May 15 22:05:00 UTC 2020: benchmark 0: creating a new profile via profiler service Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 32m0s Fri May 15 22:38:02 UTC 2020: benchmark 0: creating a new profile via profiler service `, numBenchmarks: 1, serverBackoffSubstring: "action throttled, backoff for", createProfileSubstring: "creating a new profile via profiler service", benchmarkNumPrefix: "benchmark", wantErr: true, }, { desc: "error when there are no log entries indicating server-specifed backoff", logs: ` Fri May 15 22:05:00 UTC 2020: benchmark 0: creating a new profile via profiler service Fri May 15 22:37:01 UTC 2020: benchmark 0: creating a new profile via profiler service `, numBenchmarks: 1, serverBackoffSubstring: "action throttled, backoff for", createProfileSubstring: "creating a new profile via profiler service", benchmarkNumPrefix: "benchmark", wantErr: true, }, { desc: "error when missing CreateProfile requests after server specified backoff", logs: ` Fri May 15 22:05:00 UTC 2020: benchmark 0: creating a new profile via profiler service Fri May 15 22:05:00 UTC 2020: benchmark 1: creating a new profile via profiler service Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 15m0s Fri May 15 22:05:01 UTC 2020: benchmark 1: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 32m0s Fri May 15 22:37:01 UTC 2020: benchmark 1: creating a new profile via profiler service `, numBenchmarks: 2, serverBackoffSubstring: "action throttled, backoff for", createProfileSubstring: "creating a new profile via profiler service", benchmarkNumPrefix: "benchmark", wantErr: true, }, { desc: "no error when no missing CreateProfile requests after server specified backoff because benchmarks finished before next request could happen.", logs: ` Fri May 15 22:05:00 UTC 2020: benchmark 0: creating a new profile via profiler service Fri May 15 22:05:00 UTC 2020: benchmark 1: creating a new profile via profiler service Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 32m0s Fri May 15 22:05:01 UTC 2020: benchmark 1: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 32m0s Fri May 15 22:37:01 UTC 2020: benchmark 0: creating a new profile via profiler service `, numBenchmarks: 2, serverBackoffSubstring: "action throttled, backoff for", createProfileSubstring: "creating a new profile via profiler service", benchmarkNumPrefix: "benchmark", }, { desc: "no error when there are non-benchmark logs", logs: ` Fri May 15 22:00:00 UTC 2020: prologue Fri May 15 22:05:00 UTC 2020: benchmark 0: creating a new profile via profiler service Fri May 15 22:05:01 UTC 2020: benchmark 0: failed to create profile, will retry: rpc error: code = Aborted desc = generic::aborted: action throttled, backoff for 32m0s Fri May 15 22:37:01 UTC 2020: benchmark 0: creating a new profile via profiler service Fri May 15 22:40:00 UTC 2020: epilogue `, numBenchmarks: 1, serverBackoffSubstring: "action throttled, backoff for", createProfileSubstring: "creating a new profile via profiler service", benchmarkNumPrefix: "benchmark", }, } { t.Run(tc.desc, func(t *testing.T) { if err := CheckSerialOutputForBackoffs(tc.logs, tc.numBenchmarks, tc.serverBackoffSubstring, tc.createProfileSubstring, tc.benchmarkNumPrefix); (err != nil) != tc.wantErr { t.Errorf("got err = %v; want (err != nil) = %v", err, tc.wantErr) } }) } }