go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/rerun/rerun_test.go (about)

     1  // Copyright 2022 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package rerun
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  	"time"
    21  
    22  	"go.chromium.org/luci/bisection/internal/buildbucket"
    23  	"go.chromium.org/luci/bisection/model"
    24  	pb "go.chromium.org/luci/bisection/proto/v1"
    25  	"go.chromium.org/luci/bisection/util"
    26  	"go.chromium.org/luci/bisection/util/testutil"
    27  
    28  	"github.com/golang/mock/gomock"
    29  	. "github.com/smartystreets/goconvey/convey"
    30  	bbpb "go.chromium.org/luci/buildbucket/proto"
    31  	. "go.chromium.org/luci/common/testing/assertions"
    32  	"google.golang.org/protobuf/types/known/structpb"
    33  	"google.golang.org/protobuf/types/known/timestamppb"
    34  
    35  	"go.chromium.org/luci/common/clock"
    36  	"go.chromium.org/luci/common/clock/testclock"
    37  	"go.chromium.org/luci/gae/impl/memory"
    38  	"go.chromium.org/luci/gae/service/datastore"
    39  )
    40  
    41  func TestRerun(t *testing.T) {
    42  	t.Parallel()
    43  
    44  	Convey("getRerunPropertiesAndDimensions", t, func() {
    45  		c := memory.Use(context.Background())
    46  		cl := testclock.New(testclock.TestTimeUTC)
    47  		c = clock.Set(c, cl)
    48  
    49  		// Setup mock for buildbucket
    50  		ctl := gomock.NewController(t)
    51  		defer ctl.Finish()
    52  		mc := buildbucket.NewMockedClient(c, ctl)
    53  		c = mc.Ctx
    54  		bootstrapProperties := &structpb.Struct{
    55  			Fields: map[string]*structpb.Value{
    56  				"bs_key_1": structpb.NewStringValue("bs_val_1"),
    57  			},
    58  		}
    59  
    60  		targetBuilder := &structpb.Struct{
    61  			Fields: map[string]*structpb.Value{
    62  				"builder": structpb.NewStringValue("linux-test"),
    63  				"group":   structpb.NewStringValue("buildergroup1"),
    64  			},
    65  		}
    66  
    67  		res := &bbpb.Build{
    68  			Builder: &bbpb.BuilderID{
    69  				Project: "chromium",
    70  				Bucket:  "ci",
    71  				Builder: "linux-test",
    72  			},
    73  			Input: &bbpb.Build_Input{
    74  				Properties: &structpb.Struct{
    75  					Fields: map[string]*structpb.Value{
    76  						"builder_group":         structpb.NewStringValue("buildergroup1"),
    77  						"$bootstrap/properties": structpb.NewStructValue(bootstrapProperties),
    78  						"another_prop":          structpb.NewStringValue("another_val"),
    79  					},
    80  				},
    81  			},
    82  			Infra: &bbpb.BuildInfra{
    83  				Swarming: &bbpb.BuildInfra_Swarming{
    84  					TaskDimensions: []*bbpb.RequestedDimension{
    85  						{
    86  							Key:   "dimen_key_1",
    87  							Value: "dimen_val_1",
    88  						},
    89  						{
    90  							Key:   "os",
    91  							Value: "ubuntu",
    92  						},
    93  						{
    94  							Key:   "gpu",
    95  							Value: "Intel",
    96  						},
    97  					},
    98  				},
    99  			},
   100  		}
   101  		Convey("has extra prop and dim", func() {
   102  			mc.Client.EXPECT().GetBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(res, nil).AnyTimes()
   103  			extraProps := map[string]any{
   104  				"analysis_id":     4646418413256704,
   105  				"compile_targets": []string{"target"},
   106  				"bisection_host":  "luci-bisection.appspot.com",
   107  			}
   108  			extraDimens := map[string]string{
   109  				"id": "bot-12345",
   110  			}
   111  
   112  			props, dimens, err := getRerunPropertiesAndDimensions(c, 1234, extraProps, extraDimens)
   113  			So(err, ShouldBeNil)
   114  			So(props, ShouldResemble, &structpb.Struct{
   115  				Fields: map[string]*structpb.Value{
   116  					"builder_group":  structpb.NewStringValue("buildergroup1"),
   117  					"target_builder": structpb.NewStructValue(targetBuilder),
   118  					"$bootstrap/properties": structpb.NewStructValue(&structpb.Struct{
   119  						Fields: map[string]*structpb.Value{
   120  							"bs_key_1": structpb.NewStringValue("bs_val_1"),
   121  						},
   122  					}),
   123  					"analysis_id":     structpb.NewNumberValue(4646418413256704),
   124  					"compile_targets": structpb.NewListValue(&structpb.ListValue{Values: []*structpb.Value{structpb.NewStringValue("target")}}),
   125  					"bisection_host":  structpb.NewStringValue("luci-bisection.appspot.com"),
   126  				},
   127  			})
   128  			So(dimens, ShouldResemble, []*bbpb.RequestedDimension{
   129  				{
   130  					Key:   "os",
   131  					Value: "ubuntu",
   132  				},
   133  				{
   134  					Key:   "gpu",
   135  					Value: "Intel",
   136  				},
   137  				{
   138  					Key:   "id",
   139  					Value: "bot-12345",
   140  				},
   141  			})
   142  		})
   143  
   144  		Convey("no extra dim", func() {
   145  			mc.Client.EXPECT().GetBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(res, nil).AnyTimes()
   146  
   147  			_, dimens, err := getRerunPropertiesAndDimensions(c, 1234, nil, nil)
   148  			So(err, ShouldBeNil)
   149  			So(dimens, ShouldResemble, []*bbpb.RequestedDimension{
   150  				{
   151  					Key:   "os",
   152  					Value: "ubuntu",
   153  				},
   154  				{
   155  					Key:   "gpu",
   156  					Value: "Intel",
   157  				},
   158  			})
   159  		})
   160  
   161  		Convey("builder is a tester", func() {
   162  			res.Input.Properties.Fields["parent_build_id"] = structpb.NewStringValue("123")
   163  			parentBuild := &bbpb.Build{
   164  				Infra: &bbpb.BuildInfra{Swarming: &bbpb.BuildInfra_Swarming{
   165  					TaskDimensions: []*bbpb.RequestedDimension{{Key: "os", Value: "parent os"}}},
   166  				}}
   167  			gomock.InOrder(
   168  				mc.Client.EXPECT().GetBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(res, nil),
   169  				mc.Client.EXPECT().GetBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(parentBuild, nil),
   170  			)
   171  
   172  			_, dimens, err := getRerunPropertiesAndDimensions(c, 1234, nil, nil)
   173  			So(err, ShouldBeNil)
   174  			So(dimens, ShouldResemble, []*bbpb.RequestedDimension{
   175  				{
   176  					Key:   "os",
   177  					Value: "parent os",
   178  				},
   179  			})
   180  		})
   181  	})
   182  
   183  }
   184  
   185  func TestCreateRerunBuildModel(t *testing.T) {
   186  	t.Parallel()
   187  	c := memory.Use(context.Background())
   188  	cl := testclock.New(testclock.TestTimeUTC)
   189  	c = clock.Set(c, cl)
   190  
   191  	// Setup mock for buildbucket
   192  	ctl := gomock.NewController(t)
   193  	defer ctl.Finish()
   194  	mc := buildbucket.NewMockedClient(c, ctl)
   195  	c = mc.Ctx
   196  	res := &bbpb.Build{
   197  		Infra: &bbpb.BuildInfra{
   198  			Swarming: &bbpb.BuildInfra_Swarming{
   199  				TaskDimensions: []*bbpb.RequestedDimension{
   200  					{
   201  						Key:   "dimen_key_1",
   202  						Value: "dimen_val_1",
   203  					},
   204  					{
   205  						Key:   "os",
   206  						Value: "ubuntu",
   207  					},
   208  					{
   209  						Key:   "gpu",
   210  						Value: "Intel",
   211  					},
   212  				},
   213  			},
   214  		},
   215  	}
   216  	mc.Client.EXPECT().GetBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(res, nil).AnyTimes()
   217  
   218  	build := &bbpb.Build{
   219  		Builder: &bbpb.BuilderID{
   220  			Project: "chromium",
   221  			Bucket:  "findit",
   222  			Builder: "luci-bisection-single-revision",
   223  		},
   224  		Input: &bbpb.Build_Input{
   225  			GitilesCommit: &bbpb.GitilesCommit{
   226  				Host:    "chromium.googlesource.com",
   227  				Project: "chromium/src",
   228  				Ref:     "ref",
   229  				Id:      "3425",
   230  			},
   231  		},
   232  		Id:         123,
   233  		Status:     bbpb.Status_STARTED,
   234  		CreateTime: &timestamppb.Timestamp{Seconds: 100},
   235  		StartTime:  &timestamppb.Timestamp{Seconds: 101},
   236  	}
   237  
   238  	Convey("Create rerun build", t, func() {
   239  		compileFailure := &model.CompileFailure{
   240  			Id:            111,
   241  			OutputTargets: []string{"target1"},
   242  		}
   243  		So(datastore.Put(c, compileFailure), ShouldBeNil)
   244  		datastore.GetTestable(c).CatchupIndexes()
   245  
   246  		analysis := &model.CompileFailureAnalysis{
   247  			Id:             444,
   248  			CompileFailure: datastore.KeyForObj(c, compileFailure),
   249  		}
   250  		So(datastore.Put(c, analysis), ShouldBeNil)
   251  		datastore.GetTestable(c).CatchupIndexes()
   252  
   253  		nsa := &model.CompileNthSectionAnalysis{
   254  			ParentAnalysis: datastore.KeyForObj(c, analysis),
   255  		}
   256  		So(datastore.Put(c, nsa), ShouldBeNil)
   257  		datastore.GetTestable(c).CatchupIndexes()
   258  
   259  		heuristicAnalysis := &model.CompileHeuristicAnalysis{
   260  			ParentAnalysis: datastore.KeyForObj(c, analysis),
   261  		}
   262  		So(datastore.Put(c, heuristicAnalysis), ShouldBeNil)
   263  		datastore.GetTestable(c).CatchupIndexes()
   264  
   265  		suspect := &model.Suspect{
   266  			Score:          10,
   267  			ParentAnalysis: datastore.KeyForObj(c, heuristicAnalysis),
   268  			GitilesCommit: bbpb.GitilesCommit{
   269  				Host:    "chromium.googlesource.com",
   270  				Project: "chromium/src",
   271  				Ref:     "ref",
   272  				Id:      "3425",
   273  			},
   274  		}
   275  		So(datastore.Put(c, suspect), ShouldBeNil)
   276  		datastore.GetTestable(c).CatchupIndexes()
   277  
   278  		Convey("Invalid data", func() {
   279  			_, err := CreateRerunBuildModel(c, build, model.RerunBuildType_CulpritVerification, nil, nsa, 0)
   280  			So(err, ShouldNotBeNil)
   281  			_, err = CreateRerunBuildModel(c, build, model.RerunBuildType_NthSection, suspect, nil, 0)
   282  			So(err, ShouldNotBeNil)
   283  		})
   284  
   285  		Convey("Culprit verification", func() {
   286  			rerunBuildModel, err := CreateRerunBuildModel(c, build, model.RerunBuildType_CulpritVerification, suspect, nil, 100)
   287  			datastore.GetTestable(c).CatchupIndexes()
   288  			So(err, ShouldBeNil)
   289  			So(rerunBuildModel, ShouldResemble, &model.CompileRerunBuild{
   290  				Id: 123,
   291  				LuciBuild: model.LuciBuild{
   292  					BuildId: 123,
   293  					Project: "chromium",
   294  					Bucket:  "findit",
   295  					Builder: "luci-bisection-single-revision",
   296  					Status:  bbpb.Status_STARTED,
   297  					GitilesCommit: bbpb.GitilesCommit{
   298  						Host:    "chromium.googlesource.com",
   299  						Project: "chromium/src",
   300  						Ref:     "ref",
   301  						Id:      "3425",
   302  					},
   303  					CreateTime: build.CreateTime.AsTime(),
   304  					StartTime:  build.StartTime.AsTime(),
   305  				},
   306  			})
   307  
   308  			// Check SingleRerun
   309  			q := datastore.NewQuery("SingleRerun").Eq("rerun_build", datastore.KeyForObj(c, rerunBuildModel))
   310  			singleReruns := []*model.SingleRerun{}
   311  			err = datastore.GetAll(c, q, &singleReruns)
   312  			So(err, ShouldBeNil)
   313  			So(len(singleReruns), ShouldEqual, 1)
   314  			So(singleReruns[0].Suspect, ShouldResemble, datastore.KeyForObj(c, suspect))
   315  			So(singleReruns[0].Analysis, ShouldResemble, datastore.KeyForObj(c, analysis))
   316  			So(singleReruns[0].Type, ShouldEqual, model.RerunBuildType_CulpritVerification)
   317  			So(singleReruns[0].Dimensions, ShouldResembleProto, util.ToDimensionsPB(res.Infra.Swarming.TaskDimensions))
   318  		})
   319  
   320  		Convey("Nth Section", func() {
   321  			build.Id = 124
   322  			rerunBuildModel1, err := CreateRerunBuildModel(c, build, model.RerunBuildType_NthSection, nil, nsa, 100)
   323  			datastore.GetTestable(c).CatchupIndexes()
   324  			So(err, ShouldBeNil)
   325  			So(rerunBuildModel1, ShouldResemble, &model.CompileRerunBuild{
   326  				Id: 124,
   327  				LuciBuild: model.LuciBuild{
   328  					BuildId: 124,
   329  					Project: "chromium",
   330  					Bucket:  "findit",
   331  					Builder: "luci-bisection-single-revision",
   332  					Status:  bbpb.Status_STARTED,
   333  					GitilesCommit: bbpb.GitilesCommit{
   334  						Host:    "chromium.googlesource.com",
   335  						Project: "chromium/src",
   336  						Ref:     "ref",
   337  						Id:      "3425",
   338  					},
   339  					CreateTime: build.CreateTime.AsTime(),
   340  					StartTime:  build.StartTime.AsTime(),
   341  				},
   342  			})
   343  
   344  			// Check SingleRerun
   345  			q := datastore.NewQuery("SingleRerun").Eq("rerun_build", datastore.KeyForObj(c, rerunBuildModel1))
   346  			singleReruns := []*model.SingleRerun{}
   347  			err = datastore.GetAll(c, q, &singleReruns)
   348  			So(err, ShouldBeNil)
   349  			So(len(singleReruns), ShouldEqual, 1)
   350  			So(singleReruns[0].NthSectionAnalysis, ShouldResemble, datastore.KeyForObj(c, nsa))
   351  			So(singleReruns[0].Analysis, ShouldResemble, datastore.KeyForObj(c, analysis))
   352  			So(singleReruns[0].Type, ShouldEqual, model.RerunBuildType_NthSection)
   353  			So(singleReruns[0].Dimensions, ShouldResembleProto, util.ToDimensionsPB(res.Infra.Swarming.TaskDimensions))
   354  		})
   355  	})
   356  }
   357  
   358  func TestUpdateRerunStatus(t *testing.T) {
   359  	t.Parallel()
   360  
   361  	Convey("TestUpdateRerunStatus", t, func() {
   362  		c := memory.Use(context.Background())
   363  		testutil.UpdateIndices(c)
   364  
   365  		// Setup mock for buildbucket
   366  		ctl := gomock.NewController(t)
   367  		defer ctl.Finish()
   368  		mc := buildbucket.NewMockedClient(c, ctl)
   369  		c = mc.Ctx
   370  		res := &bbpb.Build{
   371  			Id: 1234,
   372  			Builder: &bbpb.BuilderID{
   373  				Project: "chromium",
   374  				Bucket:  "findit",
   375  				Builder: "luci-bisection-single-revision",
   376  			},
   377  			Status:    bbpb.Status_STARTED,
   378  			StartTime: &timestamppb.Timestamp{Seconds: 100},
   379  			EndTime:   &timestamppb.Timestamp{Seconds: 200},
   380  		}
   381  
   382  		Convey("build starts", func() {
   383  			mc.Client.EXPECT().GetBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(res, nil).AnyTimes()
   384  			rerunBuild := &model.CompileRerunBuild{
   385  				Id: 1234,
   386  			}
   387  			So(datastore.Put(c, rerunBuild), ShouldBeNil)
   388  			datastore.GetTestable(c).CatchupIndexes()
   389  			singleRerun := &model.SingleRerun{
   390  				RerunBuild: datastore.KeyForObj(c, rerunBuild),
   391  				Status:     pb.RerunStatus_RERUN_STATUS_IN_PROGRESS,
   392  			}
   393  			So(datastore.Put(c, singleRerun), ShouldBeNil)
   394  			datastore.GetTestable(c).CatchupIndexes()
   395  			So(UpdateCompileRerunStatus(c, 1234), ShouldBeNil)
   396  			datastore.GetTestable(c).CatchupIndexes()
   397  
   398  			// Checking the start time
   399  			So(datastore.Get(c, rerunBuild), ShouldBeNil)
   400  			So(rerunBuild.StartTime.Unix(), ShouldEqual, 100)
   401  			So(rerunBuild.Status, ShouldEqual, bbpb.Status_STARTED)
   402  			So(datastore.Get(c, singleRerun), ShouldBeNil)
   403  			So(singleRerun.StartTime.Unix(), ShouldEqual, 100)
   404  			So(singleRerun.Status, ShouldEqual, pb.RerunStatus_RERUN_STATUS_IN_PROGRESS)
   405  		})
   406  
   407  		Convey("build ends", func() {
   408  			res.Status = bbpb.Status_SUCCESS
   409  			mc.Client.EXPECT().GetBuild(gomock.Any(), gomock.Any(), gomock.Any()).Return(res, nil).AnyTimes()
   410  			Convey("rerun didn't end", func() {
   411  				rerunBuild := &model.CompileRerunBuild{
   412  					Id: 1234,
   413  				}
   414  				So(datastore.Put(c, rerunBuild), ShouldBeNil)
   415  				singleRerun := &model.SingleRerun{
   416  					RerunBuild: datastore.KeyForObj(c, rerunBuild),
   417  					Status:     pb.RerunStatus_RERUN_STATUS_IN_PROGRESS,
   418  				}
   419  				So(datastore.Put(c, singleRerun), ShouldBeNil)
   420  				datastore.GetTestable(c).CatchupIndexes()
   421  
   422  				So(UpdateCompileRerunStatus(c, 1234), ShouldBeNil)
   423  				datastore.GetTestable(c).CatchupIndexes()
   424  				// Checking the end time and status.
   425  				So(datastore.Get(c, rerunBuild), ShouldBeNil)
   426  				So(rerunBuild.EndTime.Unix(), ShouldEqual, 200)
   427  				So(rerunBuild.Status, ShouldEqual, bbpb.Status_SUCCESS)
   428  				So(datastore.Get(c, singleRerun), ShouldBeNil)
   429  				So(singleRerun.EndTime.Unix(), ShouldEqual, 200)
   430  				So(singleRerun.Status, ShouldEqual, pb.RerunStatus_RERUN_STATUS_INFRA_FAILED)
   431  			})
   432  			Convey("rerun ends", func() {
   433  				rerunBuild := &model.CompileRerunBuild{
   434  					Id: 1234,
   435  				}
   436  				So(datastore.Put(c, rerunBuild), ShouldBeNil)
   437  				singleRerun := &model.SingleRerun{
   438  					RerunBuild: datastore.KeyForObj(c, rerunBuild),
   439  					Status:     pb.RerunStatus_RERUN_STATUS_PASSED,
   440  					EndTime:    time.Unix(101, 0).UTC(),
   441  				}
   442  				So(datastore.Put(c, singleRerun), ShouldBeNil)
   443  				datastore.GetTestable(c).CatchupIndexes()
   444  
   445  				So(UpdateCompileRerunStatus(c, 1234), ShouldBeNil)
   446  				datastore.GetTestable(c).CatchupIndexes()
   447  				// Checking the end time and status.
   448  				So(datastore.Get(c, rerunBuild), ShouldBeNil)
   449  				So(rerunBuild.EndTime.Unix(), ShouldEqual, 200)
   450  				So(rerunBuild.Status, ShouldEqual, bbpb.Status_SUCCESS)
   451  				So(datastore.Get(c, singleRerun), ShouldBeNil)
   452  				So(singleRerun.EndTime.Unix(), ShouldEqual, 101)
   453  				So(singleRerun.Status, ShouldEqual, pb.RerunStatus_RERUN_STATUS_PASSED)
   454  			})
   455  		})
   456  	})
   457  }
   458  
   459  func TestUpdateTestRerunStatus(t *testing.T) {
   460  	t.Parallel()
   461  
   462  	Convey("TestUpdateRerunStatus", t, func() {
   463  		c := memory.Use(context.Background())
   464  		testutil.UpdateIndices(c)
   465  		cl := testclock.New(testclock.TestTimeUTC)
   466  		cl.Set(time.Unix(10000, 0).UTC())
   467  		c = clock.Set(c, cl)
   468  
   469  		build := &bbpb.Build{
   470  			Id: 1234,
   471  			Builder: &bbpb.BuilderID{
   472  				Project: "chromium",
   473  				Bucket:  "findit",
   474  				Builder: "luci-bisection-single-revision",
   475  			},
   476  			Status:    bbpb.Status_STARTED,
   477  			StartTime: &timestamppb.Timestamp{Seconds: 100},
   478  			EndTime:   &timestamppb.Timestamp{Seconds: 200},
   479  		}
   480  
   481  		Convey("build starts", func() {
   482  			singleRerun := &model.TestSingleRerun{
   483  				ID:     1234,
   484  				Status: pb.RerunStatus_RERUN_STATUS_IN_PROGRESS,
   485  			}
   486  			So(datastore.Put(c, singleRerun), ShouldBeNil)
   487  			datastore.GetTestable(c).CatchupIndexes()
   488  			So(UpdateTestRerunStatus(c, build), ShouldBeNil)
   489  			datastore.GetTestable(c).CatchupIndexes()
   490  
   491  			// Checking the start time
   492  			So(datastore.Get(c, singleRerun), ShouldBeNil)
   493  			So(singleRerun.LUCIBuild.StartTime.Unix(), ShouldEqual, 100)
   494  			So(singleRerun.LUCIBuild.Status, ShouldEqual, bbpb.Status_STARTED)
   495  		})
   496  
   497  		Convey("build ends", func() {
   498  			build.Status = bbpb.Status_SUCCESS
   499  			Convey("rerun didn't end", func() {
   500  				tfa := testutil.CreateTestFailureAnalysis(c, &testutil.TestFailureAnalysisCreationOption{
   501  					ID: 100,
   502  				})
   503  				nsa := testutil.CreateTestNthSectionAnalysis(c, &testutil.TestNthSectionAnalysisCreationOption{
   504  					ID:                1000,
   505  					ParentAnalysisKey: datastore.KeyForObj(c, tfa),
   506  				})
   507  				singleRerun := testutil.CreateTestSingleRerun(c, &testutil.TestSingleRerunCreationOption{
   508  					AnalysisKey: datastore.KeyForObj(c, tfa),
   509  					ID:          1234,
   510  				})
   511  				So(datastore.Put(c, singleRerun), ShouldBeNil)
   512  				datastore.GetTestable(c).CatchupIndexes()
   513  
   514  				So(UpdateTestRerunStatus(c, build), ShouldBeNil)
   515  				datastore.GetTestable(c).CatchupIndexes()
   516  
   517  				// Checking the end time and status.
   518  				So(datastore.Get(c, singleRerun), ShouldBeNil)
   519  				So(singleRerun.LUCIBuild.EndTime.Unix(), ShouldEqual, 200)
   520  				So(singleRerun.LUCIBuild.Status, ShouldEqual, bbpb.Status_SUCCESS)
   521  				So(singleRerun.Status, ShouldEqual, pb.RerunStatus_RERUN_STATUS_INFRA_FAILED)
   522  
   523  				So(datastore.Get(c, nsa), ShouldBeNil)
   524  				So(nsa.Status, ShouldEqual, pb.AnalysisStatus_ERROR)
   525  				So(nsa.RunStatus, ShouldEqual, pb.AnalysisRunStatus_ENDED)
   526  				So(nsa.EndTime.Unix(), ShouldEqual, 10000)
   527  
   528  				So(datastore.Get(c, tfa), ShouldBeNil)
   529  				So(tfa.Status, ShouldEqual, pb.AnalysisStatus_ERROR)
   530  				So(tfa.RunStatus, ShouldEqual, pb.AnalysisRunStatus_ENDED)
   531  				So(tfa.EndTime.Unix(), ShouldEqual, 10000)
   532  			})
   533  
   534  			Convey("rerun ends", func() {
   535  				singleRerun := &model.TestSingleRerun{
   536  					ID:     1234,
   537  					Status: pb.RerunStatus_RERUN_STATUS_PASSED,
   538  				}
   539  				So(datastore.Put(c, singleRerun), ShouldBeNil)
   540  				datastore.GetTestable(c).CatchupIndexes()
   541  
   542  				So(UpdateTestRerunStatus(c, build), ShouldBeNil)
   543  				datastore.GetTestable(c).CatchupIndexes()
   544  				// Checking the end time and status.
   545  				So(datastore.Get(c, singleRerun), ShouldBeNil)
   546  				So(singleRerun.LUCIBuild.EndTime.Unix(), ShouldEqual, 200)
   547  				So(singleRerun.LUCIBuild.Status, ShouldEqual, bbpb.Status_SUCCESS)
   548  				So(singleRerun.Status, ShouldEqual, pb.RerunStatus_RERUN_STATUS_PASSED)
   549  			})
   550  		})
   551  	})
   552  }