blob: bed182428bb72899c0c9764a5cf2c582c7a9602e [file] [log] [blame]
// Copyright 2022 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 datastoreutil
import (
"context"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
"go.chromium.org/luci/bisection/model"
"go.chromium.org/luci/common/clock"
"go.chromium.org/luci/common/clock/testclock"
"go.chromium.org/luci/gae/impl/memory"
"go.chromium.org/luci/gae/service/datastore"
)
func TestGetBuild(t *testing.T) {
t.Parallel()
c := memory.Use(context.Background())
Convey("No build found", t, func() {
buildModel, err := GetBuild(c, 100)
So(err, ShouldBeNil)
So(buildModel, ShouldBeNil)
})
Convey("Build found", t, func() {
// Prepare datastore
failed_build := &model.LuciFailedBuild{
Id: 101,
}
So(datastore.Put(c, failed_build), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
buildModel, err := GetBuild(c, 101)
So(err, ShouldBeNil)
So(buildModel, ShouldNotBeNil)
So(buildModel.Id, ShouldEqual, 101)
})
}
func TestGetAnalysisForBuild(t *testing.T) {
t.Parallel()
c := memory.Use(context.Background())
Convey("No build found", t, func() {
analysis, err := GetAnalysisForBuild(c, 100)
So(err, ShouldBeNil)
So(analysis, ShouldBeNil)
})
Convey("No analysis found", t, func() {
// Prepare datastore
failedBuild := &model.LuciFailedBuild{
Id: 101,
}
So(datastore.Put(c, failedBuild), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
analysis, err := GetAnalysisForBuild(c, 101)
So(err, ShouldBeNil)
So(analysis, ShouldBeNil)
})
Convey("Analysis found", t, func() {
// Prepare datastore
failedBuild := &model.LuciFailedBuild{
Id: 101,
}
So(datastore.Put(c, failedBuild), ShouldBeNil)
compileFailure := &model.CompileFailure{
Id: 101,
Build: datastore.KeyForObj(c, failedBuild),
}
So(datastore.Put(c, compileFailure), ShouldBeNil)
compileFailureAnalysis := &model.CompileFailureAnalysis{
Id: 1230001,
CompileFailure: datastore.KeyForObj(c, compileFailure),
FirstFailedBuildId: 101,
}
So(datastore.Put(c, compileFailureAnalysis), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
analysis, err := GetAnalysisForBuild(c, 101)
So(err, ShouldBeNil)
So(analysis, ShouldNotBeNil)
So(analysis.Id, ShouldEqual, 1230001)
So(analysis.FirstFailedBuildId, ShouldEqual, 101)
})
Convey("Related analysis found", t, func() {
// Prepare datastore
firstFailedBuild := &model.LuciFailedBuild{
Id: 200,
}
So(datastore.Put(c, firstFailedBuild), ShouldBeNil)
firstCompileFailure := &model.CompileFailure{
Id: 200,
Build: datastore.KeyForObj(c, firstFailedBuild),
}
So(datastore.Put(c, firstCompileFailure), ShouldBeNil)
failedBuild := &model.LuciFailedBuild{
Id: 201,
}
So(datastore.Put(c, failedBuild), ShouldBeNil)
compileFailure := &model.CompileFailure{
Id: 201,
Build: datastore.KeyForObj(c, failedBuild),
MergedFailureKey: datastore.KeyForObj(c, firstCompileFailure),
}
So(datastore.Put(c, compileFailure), ShouldBeNil)
compileFailureAnalysis := &model.CompileFailureAnalysis{
Id: 1230002,
CompileFailure: datastore.KeyForObj(c, firstCompileFailure),
}
So(datastore.Put(c, compileFailureAnalysis), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
analysis, err := GetAnalysisForBuild(c, 201)
So(err, ShouldBeNil)
So(analysis, ShouldNotBeNil)
So(analysis.Id, ShouldEqual, 1230002)
})
}
func TestGetHeuristicAnalysis(t *testing.T) {
t.Parallel()
c := memory.Use(context.Background())
Convey("No heuristic analysis found", t, func() {
compileFailureAnalysis := &model.CompileFailureAnalysis{
Id: 1230003,
}
heuristicAnalysis, err := GetHeuristicAnalysis(c, compileFailureAnalysis)
So(err, ShouldBeNil)
So(heuristicAnalysis, ShouldBeNil)
})
Convey("Heuristic analysis found", t, func() {
// Prepare datastore
compileFailureAnalysis := &model.CompileFailureAnalysis{
Id: 1230003,
}
So(datastore.Put(c, compileFailureAnalysis), ShouldBeNil)
compileHeuristicAnalysis := &model.CompileHeuristicAnalysis{
Id: 4560001,
ParentAnalysis: datastore.KeyForObj(c, compileFailureAnalysis),
}
So(datastore.Put(c, compileHeuristicAnalysis), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
heuristicAnalysis, err := GetHeuristicAnalysis(c, compileFailureAnalysis)
So(err, ShouldBeNil)
So(heuristicAnalysis, ShouldNotBeNil)
So(heuristicAnalysis.Id, ShouldEqual, 4560001)
})
}
func TestGetSuspects(t *testing.T) {
t.Parallel()
c := memory.Use(context.Background())
datastore.GetTestable(c).AutoIndex(true)
Convey("No suspects found", t, func() {
// Prepare datastore
heuristicAnalysis := &model.CompileHeuristicAnalysis{
Id: 700,
}
So(datastore.Put(c, heuristicAnalysis), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
suspects, err := GetSuspects(c, heuristicAnalysis)
So(err, ShouldBeNil)
So(len(suspects), ShouldEqual, 0)
})
Convey("All suspects found", t, func() {
// Prepare datastore
heuristicAnalysis := &model.CompileHeuristicAnalysis{
Id: 701,
}
So(datastore.Put(c, heuristicAnalysis), ShouldBeNil)
suspect1 := &model.Suspect{
ParentAnalysis: datastore.KeyForObj(c, heuristicAnalysis),
Score: 1,
}
suspect2 := &model.Suspect{
ParentAnalysis: datastore.KeyForObj(c, heuristicAnalysis),
Score: 3,
}
suspect3 := &model.Suspect{
ParentAnalysis: datastore.KeyForObj(c, heuristicAnalysis),
Score: 4,
}
suspect4 := &model.Suspect{
ParentAnalysis: datastore.KeyForObj(c, heuristicAnalysis),
Score: 2,
}
So(datastore.Put(c, suspect1), ShouldBeNil)
So(datastore.Put(c, suspect2), ShouldBeNil)
So(datastore.Put(c, suspect3), ShouldBeNil)
So(datastore.Put(c, suspect4), ShouldBeNil)
// Add a different heuristic analysis with its own suspect
otherHeuristicAnalysis := &model.CompileHeuristicAnalysis{
Id: 702,
}
So(datastore.Put(c, heuristicAnalysis), ShouldBeNil)
otherSuspect := &model.Suspect{
ParentAnalysis: datastore.KeyForObj(c, otherHeuristicAnalysis),
Score: 5,
}
So(datastore.Put(c, otherSuspect), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
suspects, err := GetSuspects(c, heuristicAnalysis)
So(err, ShouldBeNil)
So(len(suspects), ShouldEqual, 4)
So(suspects[0].Score, ShouldEqual, 4)
So(suspects[1].Score, ShouldEqual, 3)
So(suspects[2].Score, ShouldEqual, 2)
So(suspects[3].Score, ShouldEqual, 1)
})
}
func TestGetCompileFailureForAnalysisID(t *testing.T) {
t.Parallel()
c := memory.Use(context.Background())
datastore.GetTestable(c).AutoIndex(true)
Convey("No analysis found", t, func() {
_, err := GetCompileFailureForAnalysisID(c, 100)
So(err, ShouldNotBeNil)
})
Convey("Have analysis for compile failure", t, func() {
build := &model.LuciFailedBuild{
Id: 111,
}
So(datastore.Put(c, build), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
compileFailure := &model.CompileFailure{
Id: 123,
Build: datastore.KeyForObj(c, build),
}
So(datastore.Put(c, compileFailure), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
analysis := &model.CompileFailureAnalysis{
Id: 456,
CompileFailure: datastore.KeyForObj(c, compileFailure),
}
So(datastore.Put(c, analysis), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
cf, err := GetCompileFailureForAnalysisID(c, 456)
So(err, ShouldBeNil)
So(cf.Id, ShouldEqual, 123)
})
}
func TestGetRerunsForRerunBuild(t *testing.T) {
t.Parallel()
c := memory.Use(context.Background())
datastore.GetTestable(c).AddIndexes(&datastore.IndexDefinition{
Kind: "SingleRerun",
SortBy: []datastore.IndexColumn{
{
Property: "rerun_build",
},
{
Property: "start_time",
},
},
})
cl := testclock.New(testclock.TestTimeUTC)
c = clock.Set(c, cl)
Convey("GetRerunsForRerunBuild", t, func() {
// Set up reruns
rerunBuildModel := &model.CompileRerunBuild{
Id: 8800,
}
So(datastore.Put(c, rerunBuildModel), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
reruns, err := GetRerunsForRerunBuild(c, rerunBuildModel)
So(err, ShouldBeNil)
So(len(reruns), ShouldEqual, 0)
_, err = GetLastRerunForRerunBuild(c, rerunBuildModel)
So(err, ShouldNotBeNil)
singleRerun1 := &model.SingleRerun{
Id: 1,
RerunBuild: datastore.KeyForObj(c, rerunBuildModel),
StartTime: clock.Now(c),
}
singleRerun2 := &model.SingleRerun{
Id: 2,
RerunBuild: datastore.KeyForObj(c, rerunBuildModel),
StartTime: clock.Now(c).Add(time.Hour),
}
singleRerun3 := &model.SingleRerun{
Id: 3,
RerunBuild: datastore.KeyForObj(c, rerunBuildModel),
StartTime: clock.Now(c).Add(time.Minute),
}
So(datastore.Put(c, singleRerun1), ShouldBeNil)
So(datastore.Put(c, singleRerun2), ShouldBeNil)
So(datastore.Put(c, singleRerun3), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
reruns, err = GetRerunsForRerunBuild(c, rerunBuildModel)
So(err, ShouldBeNil)
So(len(reruns), ShouldEqual, 3)
r, err := GetLastRerunForRerunBuild(c, rerunBuildModel)
So(err, ShouldBeNil)
So(r.Id, ShouldEqual, 2)
})
}
func TestGetNthSectionAnalysis(t *testing.T) {
t.Parallel()
c := memory.Use(context.Background())
Convey("No nthsection analysis found", t, func() {
cfa := &model.CompileFailureAnalysis{
Id: 123,
}
nsa, err := GetNthSectionAnalysis(c, cfa)
So(err, ShouldBeNil)
So(nsa, ShouldBeNil)
})
Convey("More than one nthsection analysis found", t, func() {
cfa := &model.CompileFailureAnalysis{
Id: 456,
}
So(datastore.Put(c, cfa), ShouldBeNil)
nsa1 := &model.CompileNthSectionAnalysis{
ParentAnalysis: datastore.KeyForObj(c, cfa),
}
nsa2 := &model.CompileNthSectionAnalysis{
ParentAnalysis: datastore.KeyForObj(c, cfa),
}
So(datastore.Put(c, nsa1), ShouldBeNil)
So(datastore.Put(c, nsa2), ShouldBeNil)
_, err := GetNthSectionAnalysis(c, cfa)
So(err, ShouldNotBeNil)
})
Convey("Nthsection analysis found", t, func() {
cfa := &model.CompileFailureAnalysis{
Id: 789,
}
So(datastore.Put(c, cfa), ShouldBeNil)
nsa1 := &model.CompileNthSectionAnalysis{
ParentAnalysis: datastore.KeyForObj(c, cfa),
Id: 345,
}
So(datastore.Put(c, nsa1), ShouldBeNil)
nsa, err := GetNthSectionAnalysis(c, cfa)
So(err, ShouldBeNil)
So(nsa.Id, ShouldEqual, 345)
})
}
func TestGetCompileFailureAnalysis(t *testing.T) {
t.Parallel()
c := memory.Use(context.Background())
Convey("GetCompileFailureAnalysis", t, func() {
_, err := GetCompileFailureAnalysis(c, 123)
So(err, ShouldNotBeNil)
cfa := &model.CompileFailureAnalysis{
Id: 123,
}
So(datastore.Put(c, cfa), ShouldBeNil)
analysis, err := GetCompileFailureAnalysis(c, 123)
So(err, ShouldBeNil)
So(analysis.Id, ShouldEqual, 123)
})
}
func TestGetOtherSuspectsWithSameCL(t *testing.T) {
t.Parallel()
c := memory.Use(context.Background())
Convey("GetOtherSuspectsWithSameCL", t, func() {
suspect := &model.Suspect{
ReviewUrl: "https://this/is/review/url",
Id: 123,
}
So(datastore.Put(c, suspect), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
suspects, err := GetOtherSuspectsWithSameCL(c, suspect)
So(err, ShouldBeNil)
So(len(suspects), ShouldEqual, 0)
suspect1 := &model.Suspect{
ReviewUrl: "https://this/is/review/url",
Id: 124,
}
So(datastore.Put(c, suspect1), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
suspects, err = GetOtherSuspectsWithSameCL(c, suspect)
So(err, ShouldBeNil)
So(len(suspects), ShouldEqual, 1)
So(suspects[0].Id, ShouldEqual, 124)
})
}
func TestGetLatestBuildFailureAndAnalysis(t *testing.T) {
t.Parallel()
c := memory.Use(context.Background())
datastore.GetTestable(c).AddIndexes(&datastore.IndexDefinition{
Kind: "LuciFailedBuild",
SortBy: []datastore.IndexColumn{
{
Property: "project",
},
{
Property: "bucket",
},
{
Property: "builder",
},
{
Property: "end_time",
Descending: true,
},
},
})
datastore.GetTestable(c).CatchupIndexes()
cl := testclock.New(testclock.TestTimeUTC)
c = clock.Set(c, cl)
Convey("GetLatestBuildFailureAndAnalysis", t, func() {
build, err := GetLatestBuildFailureForBuilder(c, "project", "bucket", "builder")
So(err, ShouldBeNil)
So(build, ShouldBeNil)
analysis, err := GetLatestAnalysisForBuilder(c, "project", "bucket", "builder")
So(err, ShouldBeNil)
So(analysis, ShouldBeNil)
bf1 := &model.LuciFailedBuild{
Id: 123,
LuciBuild: model.LuciBuild{
Project: "project",
Bucket: "bucket",
Builder: "builder",
EndTime: clock.Now(c),
},
}
bf2 := &model.LuciFailedBuild{
Id: 456,
LuciBuild: model.LuciBuild{
Project: "project",
Bucket: "bucket",
Builder: "builder",
EndTime: clock.Now(c).Add(time.Hour),
},
}
So(datastore.Put(c, bf1), ShouldBeNil)
So(datastore.Put(c, bf2), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
build, err = GetLatestBuildFailureForBuilder(c, "project", "bucket", "builder")
So(err, ShouldBeNil)
So(build.Id, ShouldEqual, 456)
cf1 := &model.CompileFailure{
Id: 123,
Build: datastore.KeyForObj(c, bf1),
}
So(datastore.Put(c, cf1), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
cf2 := &model.CompileFailure{
Id: 456,
Build: datastore.KeyForObj(c, bf2),
MergedFailureKey: datastore.KeyForObj(c, cf1),
}
So(datastore.Put(c, cf2), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
cfa := &model.CompileFailureAnalysis{
Id: 789,
CompileFailure: datastore.KeyForObj(c, cf1),
}
So(datastore.Put(c, cfa), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
analysis, err = GetLatestAnalysisForBuilder(c, "project", "bucket", "builder")
So(err, ShouldBeNil)
So(analysis.Id, ShouldEqual, 789)
})
}
func TestGetRerunsForAnalysis(t *testing.T) {
t.Parallel()
c := memory.Use(context.Background())
cl := testclock.New(testclock.TestTimeUTC)
c = clock.Set(c, cl)
Convey("GetRerunsForAnalysis", t, func() {
cfa := &model.CompileFailureAnalysis{
Id: 123,
}
So(datastore.Put(c, cfa), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
nsa := &model.CompileNthSectionAnalysis{
ParentAnalysis: datastore.KeyForObj(c, cfa),
}
So(datastore.Put(c, nsa), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
rr1 := &model.SingleRerun{
Type: model.RerunBuildType_CulpritVerification,
Analysis: datastore.KeyForObj(c, cfa),
}
rr2 := &model.SingleRerun{
Id: 333,
Type: model.RerunBuildType_NthSection,
Analysis: datastore.KeyForObj(c, cfa),
}
So(datastore.Put(c, rr1), ShouldBeNil)
So(datastore.Put(c, rr2), ShouldBeNil)
datastore.GetTestable(c).CatchupIndexes()
reruns, err := GetRerunsForAnalysis(c, cfa)
So(err, ShouldBeNil)
So(len(reruns), ShouldEqual, 2)
nsectionReruns, err := GetRerunsForNthSectionAnalysis(c, nsa)
So(err, ShouldBeNil)
So(len(nsectionReruns), ShouldEqual, 1)
So(nsectionReruns[0].Id, ShouldEqual, 333)
})
}