Support Bazel's test-sharding protocol. Bazel can shard tests, but runner runs a lot of tests inside a single “test”, as Go sees it. In order to shard within runner we implement support for Bazel's environment variables[1] directly. This does mean that the handful of other tests in runner are run in every shard. [1] https://docs.bazel.build/versions/1.1.0/test-encyclopedia.html#initial-conditions Change-Id: Idaa5c6ae5225cd86951cd40f47b5f86f31664e04 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/51245 Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 370fa94..4c1c955 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go
@@ -19309,8 +19309,22 @@ noneOfPattern = strings.Split(*skipTest, ";") } + shardIndex, shardTotal, err := getSharding() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + if shardTotal > 0 { + fmt.Printf("This is shard %d of 0..%d (inclusive)\n", shardIndex, shardTotal-1) + } + var foundTest bool for i := range testCases { + if shardTotal > 0 && i%shardTotal != shardIndex { + continue + } + matched, err := match(oneOfPatternIfAny, noneOfPattern, testCases[i].name) if err != nil { fmt.Fprintf(os.Stderr, "Error matching pattern: %s\n", err) @@ -19348,7 +19362,7 @@ } } - if !foundTest { + if !foundTest && shardTotal == 0 { fmt.Fprintf(os.Stderr, "No tests run\n") os.Exit(1) }
diff --git a/ssl/test/runner/sharding.go b/ssl/test/runner/sharding.go new file mode 100644 index 0000000..5061a6f --- /dev/null +++ b/ssl/test/runner/sharding.go
@@ -0,0 +1,77 @@ +// Copyright (c) 2022, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package runner + +import ( + "fmt" + "io/ioutil" + "os" + "strconv" +) + +const ( + shardStatusFileEnv = "TEST_SHARD_STATUS_FILE" + shardTotalEnv = "TEST_TOTAL_SHARDS" + shardIndexEnv = "TEST_SHARD_INDEX" + shardPrefix = "RUNNER_" +) + +func init() { + // When run under `go test`, init() functions may be run twice if the + // test binary ends up forking and execing itself. Therefore we move + // the environment variables to names that don't interfere with Go's + // own support for sharding. If we recorded and erased them, then they + // wouldn't exist the second time the binary runs. + for _, key := range []string{shardStatusFileEnv, shardTotalEnv, shardIndexEnv} { + value := os.Getenv(key) + if len(value) > 0 { + os.Setenv(shardPrefix+key, value) + os.Setenv(key, "") + } + } +} + +// getSharding returns the shard index and count, or zeros if sharding is not +// enabled. +func getSharding() (index, total int, err error) { + statusFile := os.Getenv(shardPrefix + shardStatusFileEnv) + totalNumStr := os.Getenv(shardPrefix + shardTotalEnv) + indexStr := os.Getenv(shardPrefix + shardIndexEnv) + if len(totalNumStr) == 0 || len(indexStr) == 0 { + return 0, 0, nil + } + + totalNum, err := strconv.Atoi(totalNumStr) + if err != nil { + return 0, 0, fmt.Errorf("$%s is %q, but expected a number\n", shardTotalEnv, totalNumStr) + } + + index, err = strconv.Atoi(indexStr) + if err != nil { + return 0, 0, fmt.Errorf("$%s is %q, but expected a number\n", shardIndexEnv, indexStr) + } + + if index < 0 || index >= totalNum { + return 0, 0, fmt.Errorf("shard index/total of %d/%d is invalid\n", index, totalNum) + } + + if len(statusFile) > 0 { + if err := ioutil.WriteFile(statusFile, nil, 0664); err != nil { + return 0, 0, err + } + } + + return index, totalNum, nil +}