blob: 3311a706294c2a891d5505782e77df76735b0efb [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package run
import (
// runTestsFunc is a function to run local/remote tests matched with patterns.
// When it returns prematurely before running all requested tests, it may return
// a list of unstarted test names. If the function cannot compute the list,
// it should return nil as unstarted. Note that nil slice and non-nil empty
// slice are distinguished in this case; non-nil empty slice is considered that
// there is no remaining test.
type runTestsFunc func(ctx context.Context, patterns []string) (results []*EntityResult, unstarted []string, err error)
// beforeRetryFunc is a function to recover from premature runner exits.
// If it is okay to proceed to retry, return true. Otherwise no further retry
// is attempted. If the function encounters any error, it should write it to
// logs.
type beforeRetryFunc func(ctx context.Context) bool
// runTestsWithRetry runs local/remote tests in a loop. If cfg.continueAfterFailure
// is true and runTests returns non-empty unstarted test names, it calls recover
// followed by runTests again to restart testing.
func runTestsWithRetry(ctx context.Context, cfg *Config, patterns []string, runTests runTestsFunc, beforeRetry beforeRetryFunc) ([]*EntityResult, error) {
var allResults []*EntityResult
for {
results, unstarted, rerr := runTests(ctx, patterns)
allResults = append(allResults, results...)
if rerr == nil {
cfg.Logger.Logf("Test runner failed: %v", rerr)
// If runTests didn't provide a list of remaining tests, give up.
if unstarted == nil {
return allResults, rerr
// If we know that there are no more tests left to execute, report the overall run as having succeeded.
// The test that was in progress when the run failed will be reported as having failed.
if len(unstarted) == 0 {
// If we don't want to try again, or we'd just be doing the same thing that we did last time, give up.
if !cfg.continueAfterFailure || reflect.DeepEqual(patterns, unstarted) {
return allResults, rerr
cfg.Logger.Logf("Trying to run %v remaining test(s)", len(unstarted))
if !beforeRetry(ctx) {
return allResults, rerr
patterns = unstarted
return allResults, nil