blob: 1e90842930830f15013478dee32741af5b03263c [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"testing"
buildbucketpb "go.chromium.org/luci/buildbucket/proto"
"go.chromium.org/luci/common/proto/git"
"go.fuchsia.dev/infra/cmd/autocorrelator/findings"
"github.com/google/go-cmp/cmp"
)
func TestCheckCI(t *testing.T) {
t.Parallel()
var tests = []struct {
log []*git.Commit
builds []*buildbucketpb.Build
status buildbucketpb.Status
summaryMarkdown string
expected *findings.SummarySimilarity
}{
{
log: []*git.Commit{
{
Id: "D",
},
{
Id: "C",
},
{
Id: "B",
},
{
Id: "A",
},
},
// SUCCESS > FAILURE case, where we are looking for FAILURE.
builds: []*buildbucketpb.Build{
// This build's status is SUCCESS. We should return a finding
// as soon as we see this, indicating that CI does not appear
// broken.
{
Id: int64(999999),
Input: &buildbucketpb.Build_Input{
GitilesCommit: &buildbucketpb.GitilesCommit{
Id: "C",
},
},
Status: buildbucketpb.Status_SUCCESS,
SummaryMarkdown: "",
},
// Even though this build's status is FAILURE, it should be
// ignored, since the above build should have been caught first.
{
Id: int64(999998),
Input: &buildbucketpb.Build_Input{
GitilesCommit: &buildbucketpb.GitilesCommit{
Id: "A",
},
},
Status: buildbucketpb.Status_FAILURE,
SummaryMarkdown: "ERROR",
},
},
status: buildbucketpb.Status_FAILURE,
summaryMarkdown: "ERROR",
expected: &findings.SummarySimilarity{
Score: 0.0,
BuildId: "999999",
// The caught build has commit ID C which is one commit away
// from the front of the log.
CommitDist: 1,
IsGreen: true,
},
},
{
log: []*git.Commit{
{
Id: "D",
},
{
Id: "C",
},
{
Id: "B",
},
{
Id: "A",
},
},
// SUCCESS > FAILURE case, where we are looking for FAILURE.
builds: []*buildbucketpb.Build{
// This build's commit ID is not in the log, and should be
// ignored, even though the build's status matches the input
// status.
{
Id: int64(999999),
Input: &buildbucketpb.Build_Input{
GitilesCommit: &buildbucketpb.GitilesCommit{
Id: "E",
},
},
Status: buildbucketpb.Status_FAILURE,
SummaryMarkdown: "ERROR",
},
// This build's status is FAILURE, which matches the input
// status. We should catch this build.
{
Id: int64(999998),
Input: &buildbucketpb.Build_Input{
GitilesCommit: &buildbucketpb.GitilesCommit{
Id: "C",
},
},
Status: buildbucketpb.Status_FAILURE,
SummaryMarkdown: "ERROR",
},
// Even though this build's status is FAILURE, it should be
// ignored, since the above build should have been caught first.
{
Id: int64(999997),
Input: &buildbucketpb.Build_Input{
GitilesCommit: &buildbucketpb.GitilesCommit{
Id: "A",
},
},
Status: buildbucketpb.Status_FAILURE,
SummaryMarkdown: "ERROR",
},
},
status: buildbucketpb.Status_FAILURE,
summaryMarkdown: "ERROR",
expected: &findings.SummarySimilarity{
Score: 1.0,
BuildId: "999998",
// The caught build has commit ID C which is one commit away
// from the front of the log.
CommitDist: 1,
IsGreen: false,
},
},
{
log: []*git.Commit{
{
Id: "D",
},
{
Id: "C",
},
{
Id: "B",
},
{
Id: "A",
},
},
// FAILURE > INFRA_FAILURE case, where we are looking for
// INFRA_FAILURE.
builds: []*buildbucketpb.Build{
// This build's status is FAILURE, which does not match the
// input status, and should be ignored.
{
Id: int64(999999),
Input: &buildbucketpb.Build_Input{
GitilesCommit: &buildbucketpb.GitilesCommit{
Id: "C",
},
},
Status: buildbucketpb.Status_FAILURE,
SummaryMarkdown: "ERROR",
},
// This build's status is INFRA_FAILURE, which does match the
// input status, and should be caught.
{
Id: int64(999998),
Input: &buildbucketpb.Build_Input{
GitilesCommit: &buildbucketpb.GitilesCommit{
Id: "B",
},
},
Status: buildbucketpb.Status_INFRA_FAILURE,
SummaryMarkdown: "ERROR: checkout failed",
},
},
status: buildbucketpb.Status_INFRA_FAILURE,
summaryMarkdown: "ERROR: checkout failed",
expected: &findings.SummarySimilarity{
Score: 1.0,
BuildId: "999998",
// The caught build has commit ID B which is three commits away
// from the front of the log.
CommitDist: 2,
IsGreen: false,
},
},
{
log: []*git.Commit{
{
Id: "D",
},
{
Id: "C",
},
{
Id: "B",
},
{
Id: "A",
},
},
// INFRA_FAILURE > FAILURE case, where we are looking for FAILURE.
builds: []*buildbucketpb.Build{
// This build's status is INFRA_FAILURE, which does not match
// the input status, and should be ignored.
{
Id: int64(999999),
Input: &buildbucketpb.Build_Input{
GitilesCommit: &buildbucketpb.GitilesCommit{
Id: "C",
},
},
Status: buildbucketpb.Status_INFRA_FAILURE,
SummaryMarkdown: "ERROR: checkout failed",
},
// This build's status is FAILURE, which does match the input
// status, and should be caught.
{
Id: int64(999998),
Input: &buildbucketpb.Build_Input{
GitilesCommit: &buildbucketpb.GitilesCommit{
Id: "A",
},
},
Status: buildbucketpb.Status_FAILURE,
SummaryMarkdown: "ERROR",
},
},
status: buildbucketpb.Status_FAILURE,
summaryMarkdown: "ERROR",
expected: &findings.SummarySimilarity{
Score: 1.0,
BuildId: "999998",
// The caught build has commit ID A which is three commits away
// from the front of the log.
CommitDist: 3,
IsGreen: false,
},
},
// Build exhaustion, where we are looking for FAILURE.
{
log: []*git.Commit{
{
Id: "D",
},
{
Id: "C",
},
{
Id: "B",
},
{
Id: "A",
},
},
builds: []*buildbucketpb.Build{
// This build's status is INFRA_FAILURE, which does not match
// the input status, and should be ignored.
{
Id: int64(999999),
Input: &buildbucketpb.Build_Input{
GitilesCommit: &buildbucketpb.GitilesCommit{
Id: "C",
},
},
Status: buildbucketpb.Status_INFRA_FAILURE,
SummaryMarkdown: "ERROR: checkout failed",
},
},
status: buildbucketpb.Status_FAILURE,
summaryMarkdown: "ERROR",
// We should have exhausted the list of builds without having found
// anything.
expected: nil,
},
}
for _, test := range tests {
comparator := mockComparator{}
ss := checkCI(test.log, test.builds, test.status, test.summaryMarkdown, comparator)
if diff := cmp.Diff(test.expected, ss); diff != "" {
t.Fatalf("different (-want +got):\n%s", diff)
}
}
}