blob: b8de308ccfb75ece62be5316e26b07f5621451a3 [file] [log] [blame]
// Copyright 2020 The LUCI Authors.
//
// 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 purger
import (
"context"
"fmt"
"strconv"
"testing"
"time"
"cloud.google.com/go/spanner"
durpb "google.golang.org/protobuf/types/known/durationpb"
"go.chromium.org/luci/common/clock"
"go.chromium.org/luci/server/span"
"go.chromium.org/luci/resultdb/internal/artifacts"
"go.chromium.org/luci/resultdb/internal/invocations"
"go.chromium.org/luci/resultdb/internal/spanutil"
"go.chromium.org/luci/resultdb/internal/testutil"
"go.chromium.org/luci/resultdb/internal/testutil/insert"
"go.chromium.org/luci/resultdb/pbutil"
pb "go.chromium.org/luci/resultdb/proto/v1"
. "github.com/smartystreets/goconvey/convey"
)
// makeTestResultsWithVariants creates test results with a number of passing/failing variants.
//
// There'll be two rows for each variant. If the variant is a passing variant, both results
// will have a passing status, otherwise the first will be passing, and the second faled.
func makeTestResultsWithVariants(invID, testID string, nPassingVariants, nFailedVariants int) []*pb.TestResult {
nVariants := nPassingVariants + nFailedVariants
// For every variant we'll generate two rows.
statuses := []pb.TestStatus{pb.TestStatus_PASS, pb.TestStatus_PASS}
trs := make([]*pb.TestResult, nVariants*len(statuses))
for v := 0; v < nVariants; v++ {
// We'll generate all passing variants first, so we only have to change the
// list of statuses once.
if v == nPassingVariants {
statuses[len(statuses)-1] = pb.TestStatus_FAIL
}
for s, status := range statuses {
rIndex := v*len(statuses) + s
resultID := fmt.Sprintf("%d", rIndex)
trs[rIndex] = &pb.TestResult{
Name: pbutil.TestResultName(invID, testID, resultID),
TestId: testID,
ResultId: resultID,
Variant: pbutil.Variant("k1", "v1", "k2", fmt.Sprintf("v%d", v)),
Expected: status == pb.TestStatus_PASS,
Status: status,
Duration: &durpb.Duration{Seconds: int64(rIndex), Nanos: 234567000},
}
}
}
return trs
}
func insertInvocation(ctx context.Context, invID invocations.ID, nTests, nPassingVariants, nFailedVariants, nArtifactsPerResult int) invocations.ID {
now := clock.Now(ctx).UTC()
// Insert an invocation,
testutil.MustApply(ctx, insert.Invocation(invID, pb.Invocation_FINALIZED, map[string]any{
"ExpectedTestResultsExpirationTime": now.Add(-time.Minute),
"CreateTime": now.Add(-time.Hour),
"FinalizeTime": now.Add(-time.Hour),
}))
// Insert test results and artifacts.
inserts := []*spanner.Mutation{}
for i := 0; i < nTests; i++ {
results := makeTestResultsWithVariants(string(invID), fmt.Sprintf("Test%d", i), nPassingVariants, nFailedVariants)
inserts = append(inserts, insert.TestResultMessages(results)...)
for _, res := range results {
for j := 0; j < nArtifactsPerResult; j++ {
inserts = append(inserts, spanutil.InsertMap("Artifacts", map[string]any{
"InvocationId": invID,
"ParentId": artifacts.ParentID(res.TestId, res.ResultId),
"ArtifactId": strconv.Itoa(j),
}))
}
}
}
testutil.MustApply(ctx, testutil.CombineMutations(inserts)...)
return invID
}
func countRows(ctx context.Context, invID invocations.ID) (testResults, artifacts int64) {
st := spanner.NewStatement(`
SELECT
(SELECT COUNT(*) FROM TestResults WHERE InvocationId = @invocationId),
(SELECT COUNT(*) FROM Artifacts WHERE InvocationId = @invocationId),
`)
st.Params["invocationId"] = spanutil.ToSpanner(invID)
So(spanutil.QueryFirstRow(span.Single(ctx), st, &testResults, &artifacts), ShouldBeNil)
return
}
func TestPurgeExpiredResults(t *testing.T) {
Convey(`TestPurgeExpiredResults`, t, func() {
ctx := testutil.SpannerTestContext(t)
Convey(`Some results are unexpected`, func() {
inv := insertInvocation(ctx, "inv-some-unexpected", 10, 9, 1, 0)
err := purgeOneShard(ctx, 0)
So(err, ShouldBeNil)
// 10 tests * 1 variant with unexpected results * 2 results per test variant.
testResults, _ := countRows(ctx, inv)
So(testResults, ShouldEqual, 20)
})
Convey(`No unexpected results`, func() {
inv := insertInvocation(ctx, "inv-no-unexpected", 10, 10, 0, 0)
err := purgeOneShard(ctx, 0)
So(err, ShouldBeNil)
// 10 tests * 0 variants with unexpected results * 2 results per test variant.
testResults, _ := countRows(ctx, inv)
So(testResults, ShouldEqual, 0)
})
Convey(`Purge artifacts`, func() {
inv := insertInvocation(ctx, "inv", 10, 9, 1, 10)
err := purgeOneShard(ctx, 0)
So(err, ShouldBeNil)
// 10 tests * 1 variant with unexpected results * 2 results per test variant * 10 artifacts..
_, artifacts := countRows(ctx, inv)
So(artifacts, ShouldEqual, 200)
})
})
}