go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/util/protoutil/proto_util_test.go (about)

     1  // Copyright 2023 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 protoutil
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  	"time"
    21  
    22  	. "github.com/smartystreets/goconvey/convey"
    23  	"google.golang.org/protobuf/types/known/fieldmaskpb"
    24  	"google.golang.org/protobuf/types/known/timestamppb"
    25  
    26  	"go.chromium.org/luci/bisection/model"
    27  	pb "go.chromium.org/luci/bisection/proto/v1"
    28  	"go.chromium.org/luci/bisection/util/testutil"
    29  	buildbucketpb "go.chromium.org/luci/buildbucket/proto"
    30  	"go.chromium.org/luci/common/proto/mask"
    31  	. "go.chromium.org/luci/common/testing/assertions"
    32  	"go.chromium.org/luci/gae/impl/memory"
    33  	"go.chromium.org/luci/gae/service/datastore"
    34  )
    35  
    36  func TestConvertTestFailureAnalysisToPb(t *testing.T) {
    37  	t.Parallel()
    38  	Convey("TestConvertTestFailureAnalysisToPb", t, func() {
    39  		ctx := memory.Use(context.Background())
    40  		testutil.UpdateIndices(ctx)
    41  		tfa := testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{
    42  			ID:              int64(100),
    43  			Project:         "chromium",
    44  			Bucket:          "ci",
    45  			Builder:         "linux-rel",
    46  			StartCommitHash: "start_commit_hash",
    47  			EndCommitHash:   "end_commit_hash",
    48  			FailedBuildID:   8000,
    49  			CreateTime:      time.Unix(int64(100), 0).UTC(),
    50  			StartTime:       time.Unix(int64(110), 0).UTC(),
    51  			EndTime:         time.Unix(int64(120), 0).UTC(),
    52  			TestFailureKey:  datastore.MakeKey(ctx, "TestFailure", 100),
    53  			Status:          pb.AnalysisStatus_FOUND,
    54  			RunStatus:       pb.AnalysisRunStatus_ENDED,
    55  		})
    56  		// non-primary test failure.
    57  		testutil.CreateTestFailure(ctx, &testutil.TestFailureCreationOption{
    58  			ID:        int64(99),
    59  			Analysis:  tfa,
    60  			TestID:    "testID2",
    61  			StartHour: time.Unix(int64(100), 0).UTC(),
    62  			Ref: &pb.SourceRef{
    63  				System: &pb.SourceRef_Gitiles{
    64  					Gitiles: &pb.GitilesRef{
    65  						Host:    "chromium.googlesource.com",
    66  						Project: "chromium/src",
    67  						Ref:     "ref",
    68  					},
    69  				},
    70  			},
    71  			Project: "chromium",
    72  			Variant: map[string]string{
    73  				"key2": "val2",
    74  			},
    75  			VariantHash:      "vhash2",
    76  			RefHash:          "refhash",
    77  			StartPosition:    100,
    78  			EndPosition:      199,
    79  			StartFailureRate: 0,
    80  			EndFailureRate:   1.0,
    81  			IsDiverged:       true,
    82  		})
    83  		primaryTf := testutil.CreateTestFailure(ctx, &testutil.TestFailureCreationOption{
    84  			ID:        int64(100),
    85  			Analysis:  tfa,
    86  			IsPrimary: true,
    87  			TestID:    "testID1",
    88  			StartHour: time.Unix(int64(100), 0).UTC(),
    89  			Ref: &pb.SourceRef{
    90  				System: &pb.SourceRef_Gitiles{
    91  					Gitiles: &pb.GitilesRef{
    92  						Host:    "chromium.googlesource.com",
    93  						Project: "chromium/src",
    94  						Ref:     "ref",
    95  					},
    96  				},
    97  			},
    98  			Project: "chromium",
    99  			Variant: map[string]string{
   100  				"key1": "val1",
   101  			},
   102  			VariantHash:      "vhash1",
   103  			RefHash:          "refhash",
   104  			StartPosition:    100,
   105  			EndPosition:      199,
   106  			StartFailureRate: 0,
   107  			EndFailureRate:   1.0,
   108  		})
   109  		nsa := testutil.CreateTestNthSectionAnalysis(ctx, &testutil.TestNthSectionAnalysisCreationOption{
   110  			ID:                200,
   111  			ParentAnalysisKey: datastore.KeyForObj(ctx, tfa),
   112  			BlameList:         testutil.CreateBlamelist(4),
   113  			Status:            pb.AnalysisStatus_SUSPECTFOUND,
   114  			RunStatus:         pb.AnalysisRunStatus_ENDED,
   115  			StartTime:         time.Unix(int64(100), 0).UTC(),
   116  			EndTime:           time.Unix(int64(109), 0).UTC(),
   117  		})
   118  		culprit := testutil.CreateSuspect(ctx, &testutil.SuspectCreationOption{
   119  			ID:                 500,
   120  			ParentKey:          datastore.KeyForObj(ctx, nsa),
   121  			CommitID:           "culprit_commit_id",
   122  			ReviewURL:          "review_url",
   123  			ReviewTitle:        "review_title",
   124  			SuspectRerunKey:    datastore.MakeKey(ctx, "Suspect", 3000),
   125  			ParentRerunKey:     datastore.MakeKey(ctx, "Suspect", 3001),
   126  			VerificationStatus: model.SuspectVerificationStatus_ConfirmedCulprit,
   127  			ActionDetails: model.ActionDetails{
   128  				IsRevertCreated:  true,
   129  				RevertURL:        "http://revert",
   130  				RevertCreateTime: time.Unix(120, 0).UTC(),
   131  			},
   132  		})
   133  
   134  		// Suspect verification rerun.
   135  		testutil.CreateTestSingleRerun(ctx, &testutil.TestSingleRerunCreationOption{
   136  			ID:          3000,
   137  			AnalysisKey: datastore.KeyForObj(ctx, tfa),
   138  			CulpritKey:  datastore.KeyForObj(ctx, culprit),
   139  			Type:        model.RerunBuildType_CulpritVerification,
   140  			CreateTime:  time.Unix(103, 0).UTC(),
   141  			StartTime:   time.Unix(104, 0).UTC(),
   142  			ReportTime:  time.Unix(105, 0).UTC(),
   143  			EndTime:     time.Unix(106, 0).UTC(),
   144  			Status:      pb.RerunStatus_RERUN_STATUS_FAILED,
   145  			BuildStatus: buildbucketpb.Status_SUCCESS,
   146  			TestResult: model.RerunTestResults{
   147  				IsFinalized: true,
   148  				Results: []model.RerunSingleTestResult{
   149  					{
   150  						TestFailureKey:  datastore.KeyForObj(ctx, primaryTf),
   151  						UnexpectedCount: 1,
   152  					},
   153  				},
   154  			},
   155  			GitilesCommit: &buildbucketpb.GitilesCommit{
   156  				Host:    "chromium.googlesource.com",
   157  				Project: "chromium/src",
   158  				Ref:     "ref",
   159  				Id:      "commit2",
   160  			},
   161  		})
   162  
   163  		// Parent verification rerun.
   164  		testutil.CreateTestSingleRerun(ctx, &testutil.TestSingleRerunCreationOption{
   165  			ID:          3001,
   166  			AnalysisKey: datastore.KeyForObj(ctx, tfa),
   167  			CulpritKey:  datastore.KeyForObj(ctx, culprit),
   168  			Type:        model.RerunBuildType_CulpritVerification,
   169  			CreateTime:  time.Unix(103, 0).UTC(),
   170  			StartTime:   time.Unix(104, 0).UTC(),
   171  			ReportTime:  time.Unix(105, 0).UTC(),
   172  			EndTime:     time.Unix(106, 0).UTC(),
   173  			Status:      pb.RerunStatus_RERUN_STATUS_PASSED,
   174  			BuildStatus: buildbucketpb.Status_SUCCESS,
   175  			TestResult: model.RerunTestResults{
   176  				IsFinalized: true,
   177  				Results: []model.RerunSingleTestResult{
   178  					{
   179  						TestFailureKey: datastore.KeyForObj(ctx, primaryTf),
   180  						ExpectedCount:  1,
   181  					},
   182  				},
   183  			},
   184  			GitilesCommit: &buildbucketpb.GitilesCommit{
   185  				Host:    "chromium.googlesource.com",
   186  				Project: "chromium/src",
   187  				Ref:     "ref",
   188  				Id:      "commit3",
   189  			},
   190  		})
   191  
   192  		// Nthsection rerun1.
   193  		testutil.CreateTestSingleRerun(ctx, &testutil.TestSingleRerunCreationOption{
   194  			ID:                    2998,
   195  			AnalysisKey:           datastore.KeyForObj(ctx, tfa),
   196  			NthSectionAnalysisKey: datastore.KeyForObj(ctx, nsa),
   197  			Type:                  model.RerunBuildType_NthSection,
   198  			CreateTime:            time.Unix(103, 0).UTC(),
   199  			StartTime:             time.Unix(104, 0).UTC(),
   200  			ReportTime:            time.Unix(105, 0).UTC(),
   201  			EndTime:               time.Unix(106, 0).UTC(),
   202  			Status:                pb.RerunStatus_RERUN_STATUS_FAILED,
   203  			BuildStatus:           buildbucketpb.Status_SUCCESS,
   204  			TestResult: model.RerunTestResults{
   205  				IsFinalized: true,
   206  				Results: []model.RerunSingleTestResult{
   207  					{
   208  						TestFailureKey:  datastore.KeyForObj(ctx, primaryTf),
   209  						UnexpectedCount: 1,
   210  					},
   211  				},
   212  			},
   213  			GitilesCommit: &buildbucketpb.GitilesCommit{
   214  				Host:    "chromium.googlesource.com",
   215  				Project: "chromium/src",
   216  				Ref:     "ref",
   217  				Id:      "commit2",
   218  			},
   219  		})
   220  
   221  		// Nthsection rerun2.
   222  		testutil.CreateTestSingleRerun(ctx, &testutil.TestSingleRerunCreationOption{
   223  			ID:                    2999,
   224  			AnalysisKey:           datastore.KeyForObj(ctx, tfa),
   225  			NthSectionAnalysisKey: datastore.KeyForObj(ctx, nsa),
   226  			Type:                  model.RerunBuildType_NthSection,
   227  			CreateTime:            time.Unix(104, 0).UTC(),
   228  			StartTime:             time.Unix(105, 0).UTC(),
   229  			ReportTime:            time.Unix(106, 0).UTC(),
   230  			EndTime:               time.Unix(107, 0).UTC(),
   231  			Status:                pb.RerunStatus_RERUN_STATUS_PASSED,
   232  			BuildStatus:           buildbucketpb.Status_SUCCESS,
   233  			TestResult: model.RerunTestResults{
   234  				IsFinalized: true,
   235  				Results: []model.RerunSingleTestResult{
   236  					{
   237  						TestFailureKey: datastore.KeyForObj(ctx, primaryTf),
   238  						ExpectedCount:  1,
   239  					},
   240  				},
   241  			},
   242  			GitilesCommit: &buildbucketpb.GitilesCommit{
   243  				Host:    "chromium.googlesource.com",
   244  				Project: "chromium/src",
   245  				Ref:     "ref",
   246  				Id:      "commit3",
   247  			},
   248  		})
   249  
   250  		tfa.VerifiedCulpritKey = datastore.KeyForObj(ctx, culprit)
   251  		So(datastore.Put(ctx, tfa), ShouldBeNil)
   252  		nsa.CulpritKey = datastore.KeyForObj(ctx, culprit)
   253  		So(datastore.Put(ctx, nsa), ShouldBeNil)
   254  		datastore.GetTestable(ctx).CatchupIndexes()
   255  
   256  		Convey("TestFailureAnalysisToPb", func() {
   257  			fieldMask := &fieldmaskpb.FieldMask{
   258  				Paths: []string{"*"},
   259  			}
   260  			mask, err := mask.FromFieldMask(fieldMask, &pb.TestAnalysis{}, false, false)
   261  			So(err, ShouldBeNil)
   262  
   263  			tfaProto, err := TestFailureAnalysisToPb(ctx, tfa, mask)
   264  			So(err, ShouldBeNil)
   265  
   266  			pbSuspectRerun := &pb.TestSingleRerun{
   267  				Bbid:       3000,
   268  				CreateTime: timestamppb.New(time.Unix(103, 0).UTC()),
   269  				StartTime:  timestamppb.New(time.Unix(104, 0).UTC()),
   270  				ReportTime: timestamppb.New(time.Unix(105, 0).UTC()),
   271  				EndTime:    timestamppb.New(time.Unix(106, 0).UTC()),
   272  				Index:      "2",
   273  				Commit: &buildbucketpb.GitilesCommit{
   274  					Host:    "chromium.googlesource.com",
   275  					Project: "chromium/src",
   276  					Ref:     "ref",
   277  					Id:      "commit2",
   278  				},
   279  				RerunResult: &pb.RerunTestResults{
   280  					RerunStatus: pb.RerunStatus_RERUN_STATUS_FAILED,
   281  					Results: []*pb.RerunTestSingleResult{
   282  						{
   283  							TestId:          "testID1",
   284  							VariantHash:     "vhash1",
   285  							UnexpectedCount: 1,
   286  						},
   287  					},
   288  				},
   289  			}
   290  
   291  			pbParentRerun := &pb.TestSingleRerun{
   292  				Bbid:       3001,
   293  				CreateTime: timestamppb.New(time.Unix(103, 0).UTC()),
   294  				StartTime:  timestamppb.New(time.Unix(104, 0).UTC()),
   295  				ReportTime: timestamppb.New(time.Unix(105, 0).UTC()),
   296  				EndTime:    timestamppb.New(time.Unix(106, 0).UTC()),
   297  				Index:      "3",
   298  				Commit: &buildbucketpb.GitilesCommit{
   299  					Host:    "chromium.googlesource.com",
   300  					Project: "chromium/src",
   301  					Ref:     "ref",
   302  					Id:      "commit3",
   303  				},
   304  				RerunResult: &pb.RerunTestResults{
   305  					RerunStatus: pb.RerunStatus_RERUN_STATUS_PASSED,
   306  					Results: []*pb.RerunTestSingleResult{
   307  						{
   308  							TestId:        "testID1",
   309  							VariantHash:   "vhash1",
   310  							ExpectedCount: 1,
   311  						},
   312  					},
   313  				},
   314  			}
   315  
   316  			pbNthsectionRerun1 := &pb.TestSingleRerun{
   317  				Bbid:       2998,
   318  				CreateTime: timestamppb.New(time.Unix(103, 0).UTC()),
   319  				StartTime:  timestamppb.New(time.Unix(104, 0).UTC()),
   320  				ReportTime: timestamppb.New(time.Unix(105, 0).UTC()),
   321  				EndTime:    timestamppb.New(time.Unix(106, 0).UTC()),
   322  				Index:      "2",
   323  				Commit: &buildbucketpb.GitilesCommit{
   324  					Host:    "chromium.googlesource.com",
   325  					Project: "chromium/src",
   326  					Ref:     "ref",
   327  					Id:      "commit2",
   328  				},
   329  				RerunResult: &pb.RerunTestResults{
   330  					RerunStatus: pb.RerunStatus_RERUN_STATUS_FAILED,
   331  					Results: []*pb.RerunTestSingleResult{
   332  						{
   333  							TestId:          "testID1",
   334  							VariantHash:     "vhash1",
   335  							UnexpectedCount: 1,
   336  						},
   337  					},
   338  				},
   339  			}
   340  
   341  			pbNthsectionRerun2 := &pb.TestSingleRerun{
   342  				Bbid:       2999,
   343  				CreateTime: timestamppb.New(time.Unix(104, 0).UTC()),
   344  				StartTime:  timestamppb.New(time.Unix(105, 0).UTC()),
   345  				ReportTime: timestamppb.New(time.Unix(106, 0).UTC()),
   346  				EndTime:    timestamppb.New(time.Unix(107, 0).UTC()),
   347  				Index:      "3",
   348  				Commit: &buildbucketpb.GitilesCommit{
   349  					Host:    "chromium.googlesource.com",
   350  					Project: "chromium/src",
   351  					Ref:     "ref",
   352  					Id:      "commit3",
   353  				},
   354  				RerunResult: &pb.RerunTestResults{
   355  					RerunStatus: pb.RerunStatus_RERUN_STATUS_PASSED,
   356  					Results: []*pb.RerunTestSingleResult{
   357  						{
   358  							TestId:        "testID1",
   359  							VariantHash:   "vhash1",
   360  							ExpectedCount: 1,
   361  						},
   362  					},
   363  				},
   364  			}
   365  
   366  			culpritPb := &pb.TestCulprit{
   367  				ReviewUrl:   "review_url",
   368  				ReviewTitle: "review_title",
   369  				Commit: &buildbucketpb.GitilesCommit{
   370  					Host:    "chromium.googlesource.com",
   371  					Project: "chromium/src",
   372  					Ref:     "ref",
   373  					Id:      "culprit_commit_id",
   374  				},
   375  				CulpritAction: []*pb.CulpritAction{
   376  					{
   377  						ActionType:  pb.CulpritActionType_REVERT_CL_CREATED,
   378  						RevertClUrl: "http://revert",
   379  						ActionTime:  timestamppb.New(time.Unix(120, 0)),
   380  					},
   381  				},
   382  				VerificationDetails: &pb.TestSuspectVerificationDetails{
   383  					Status:       pb.SuspectVerificationStatus_CONFIRMED_CULPRIT,
   384  					SuspectRerun: pbSuspectRerun,
   385  					ParentRerun:  pbParentRerun,
   386  				},
   387  			}
   388  
   389  			So(tfaProto, ShouldResembleProto, &pb.TestAnalysis{
   390  				AnalysisId: 100,
   391  				Builder: &buildbucketpb.BuilderID{
   392  					Project: "chromium",
   393  					Bucket:  "ci",
   394  					Builder: "linux-rel",
   395  				},
   396  				CreatedTime: timestamppb.New(time.Unix(int64(100), 0).UTC()),
   397  				StartTime:   timestamppb.New(time.Unix(int64(110), 0).UTC()),
   398  				EndTime:     timestamppb.New(time.Unix(int64(120), 0).UTC()),
   399  				Status:      pb.AnalysisStatus_FOUND,
   400  				RunStatus:   pb.AnalysisRunStatus_ENDED,
   401  				StartCommit: &buildbucketpb.GitilesCommit{
   402  					Host:     "chromium.googlesource.com",
   403  					Project:  "chromium/src",
   404  					Ref:      "ref",
   405  					Id:       "start_commit_hash",
   406  					Position: 100,
   407  				},
   408  				EndCommit: &buildbucketpb.GitilesCommit{
   409  					Host:     "chromium.googlesource.com",
   410  					Project:  "chromium/src",
   411  					Ref:      "ref",
   412  					Id:       "end_commit_hash",
   413  					Position: 199,
   414  				},
   415  				SampleBbid:       8000,
   416  				StartFailureRate: 0,
   417  				EndFailureRate:   1.0,
   418  				TestFailures: []*pb.TestFailure{
   419  					{
   420  						TestId:      "testID1",
   421  						VariantHash: "vhash1",
   422  						RefHash:     "refhash",
   423  						Variant: &pb.Variant{
   424  							Def: map[string]string{
   425  								"key1": "val1",
   426  							},
   427  						},
   428  						IsPrimary: true,
   429  						StartHour: timestamppb.New(time.Unix(int64(100), 0).UTC()),
   430  					},
   431  					{
   432  						TestId:      "testID2",
   433  						VariantHash: "vhash2",
   434  						RefHash:     "refhash",
   435  						Variant: &pb.Variant{
   436  							Def: map[string]string{
   437  								"key2": "val2",
   438  							},
   439  						},
   440  						IsDiverged: true,
   441  						StartHour:  timestamppb.New(time.Unix(int64(100), 0).UTC()),
   442  					},
   443  				},
   444  				NthSectionResult: &pb.TestNthSectionAnalysisResult{
   445  					Status:    pb.AnalysisStatus_SUSPECTFOUND,
   446  					RunStatus: pb.AnalysisRunStatus_ENDED,
   447  					StartTime: timestamppb.New(time.Unix(int64(100), 0).UTC()),
   448  					EndTime:   timestamppb.New(time.Unix(int64(109), 0).UTC()),
   449  					BlameList: testutil.CreateBlamelist(4),
   450  					Suspect:   culpritPb,
   451  					Reruns: []*pb.TestSingleRerun{
   452  						pbNthsectionRerun1, pbNthsectionRerun2,
   453  					},
   454  				},
   455  				Culprit: culpritPb,
   456  			})
   457  		})
   458  
   459  		Convey("Bisection overview field mask", func() {
   460  			fieldMask := &fieldmaskpb.FieldMask{
   461  				Paths: []string{
   462  					"analysis_id",
   463  					"created_time",
   464  					"start_time",
   465  					"end_time",
   466  					"status",
   467  					"run_status",
   468  					"builder",
   469  					"culprit.commit",
   470  					"culprit.review_url",
   471  					"culprit.review_title",
   472  					"culprit.culprit_action",
   473  					"test_failures.*.is_primary",
   474  					"test_failures.*.start_hour",
   475  				},
   476  			}
   477  			mask, err := mask.FromFieldMask(fieldMask, &pb.TestAnalysis{}, false, false)
   478  			So(err, ShouldBeNil)
   479  
   480  			tfaProto, err := TestFailureAnalysisToPb(ctx, tfa, mask)
   481  			So(err, ShouldBeNil)
   482  
   483  			culpritPb := &pb.TestCulprit{
   484  				ReviewUrl:   "review_url",
   485  				ReviewTitle: "review_title",
   486  				Commit: &buildbucketpb.GitilesCommit{
   487  					Host:    "chromium.googlesource.com",
   488  					Project: "chromium/src",
   489  					Ref:     "ref",
   490  					Id:      "culprit_commit_id",
   491  				},
   492  				CulpritAction: []*pb.CulpritAction{
   493  					{
   494  						ActionType:  pb.CulpritActionType_REVERT_CL_CREATED,
   495  						RevertClUrl: "http://revert",
   496  						ActionTime:  timestamppb.New(time.Unix(120, 0)),
   497  					},
   498  				},
   499  			}
   500  
   501  			So(tfaProto, ShouldResembleProto, &pb.TestAnalysis{
   502  				AnalysisId: 100,
   503  				Builder: &buildbucketpb.BuilderID{
   504  					Project: "chromium",
   505  					Bucket:  "ci",
   506  					Builder: "linux-rel",
   507  				},
   508  				CreatedTime: timestamppb.New(time.Unix(int64(100), 0).UTC()),
   509  				StartTime:   timestamppb.New(time.Unix(int64(110), 0).UTC()),
   510  				EndTime:     timestamppb.New(time.Unix(int64(120), 0).UTC()),
   511  				Status:      pb.AnalysisStatus_FOUND,
   512  				RunStatus:   pb.AnalysisRunStatus_ENDED,
   513  				TestFailures: []*pb.TestFailure{
   514  					{
   515  						IsPrimary: true,
   516  						StartHour: timestamppb.New(time.Unix(int64(100), 0).UTC()),
   517  					},
   518  					{
   519  						StartHour: timestamppb.New(time.Unix(int64(100), 0).UTC()),
   520  					},
   521  				},
   522  				Culprit: culpritPb,
   523  			})
   524  		})
   525  	})
   526  }
   527  
   528  func TestRegressionRange(t *testing.T) {
   529  	t.Parallel()
   530  	ctx := memory.Use(context.Background())
   531  	testutil.UpdateIndices(ctx)
   532  
   533  	Convey("TestRegressionRange", t, func() {
   534  		tfa := testutil.CreateTestFailureAnalysis(ctx, &testutil.TestFailureAnalysisCreationOption{})
   535  		nsa := testutil.CreateTestNthSectionAnalysis(ctx, &testutil.TestNthSectionAnalysisCreationOption{
   536  			ID:                200,
   537  			ParentAnalysisKey: datastore.KeyForObj(ctx, tfa),
   538  			BlameList:         testutil.CreateBlamelist(4),
   539  			Status:            pb.AnalysisStatus_RUNNING,
   540  			RunStatus:         pb.AnalysisRunStatus_STARTED,
   541  		})
   542  		sourceRef := &pb.SourceRef{
   543  			System: &pb.SourceRef_Gitiles{
   544  				Gitiles: &pb.GitilesRef{
   545  					Host:    "chromium.googlesource.com",
   546  					Project: "chromium/src",
   547  					Ref:     "ref",
   548  				},
   549  			},
   550  		}
   551  		nsaFieldMask := fieldmaskpb.FieldMask{
   552  			Paths: []string{"*"},
   553  		}
   554  		nsaMask, err := mask.FromFieldMask(&nsaFieldMask, &pb.TestNthSectionAnalysisResult{}, false, false)
   555  		So(err, ShouldBeNil)
   556  		pbNsa, err := NthSectionAnalysisToPb(ctx, tfa, nsa, sourceRef, nsaMask)
   557  		So(err, ShouldBeNil)
   558  		So(pbNsa.RemainingNthSectionRange, ShouldResembleProto, &pb.RegressionRange{
   559  			LastPassed: &buildbucketpb.GitilesCommit{
   560  				Host:    "chromium.googlesource.com",
   561  				Project: "chromium/src",
   562  				Ref:     "ref",
   563  				Id:      "commit4",
   564  			},
   565  			FirstFailed: &buildbucketpb.GitilesCommit{
   566  				Host:    "chromium.googlesource.com",
   567  				Project: "chromium/src",
   568  				Ref:     "ref",
   569  				Id:      "commit0",
   570  			},
   571  		})
   572  
   573  		// Add a rerun, regression should update.
   574  		testutil.CreateTestSingleRerun(ctx, &testutil.TestSingleRerunCreationOption{
   575  			AnalysisKey:           datastore.KeyForObj(ctx, tfa),
   576  			NthSectionAnalysisKey: datastore.KeyForObj(ctx, nsa),
   577  			Type:                  model.RerunBuildType_NthSection,
   578  			Status:                pb.RerunStatus_RERUN_STATUS_PASSED,
   579  			BuildStatus:           buildbucketpb.Status_SUCCESS,
   580  			GitilesCommit: &buildbucketpb.GitilesCommit{
   581  				Host:    "chromium.googlesource.com",
   582  				Project: "chromium/src",
   583  				Ref:     "ref",
   584  				Id:      "commit2",
   585  			},
   586  		})
   587  
   588  		pbNsa, err = NthSectionAnalysisToPb(ctx, tfa, nsa, sourceRef, nsaMask)
   589  		So(err, ShouldBeNil)
   590  		So(pbNsa.RemainingNthSectionRange, ShouldResembleProto, &pb.RegressionRange{
   591  			LastPassed: &buildbucketpb.GitilesCommit{
   592  				Host:    "chromium.googlesource.com",
   593  				Project: "chromium/src",
   594  				Ref:     "ref",
   595  				Id:      "commit2",
   596  			},
   597  			FirstFailed: &buildbucketpb.GitilesCommit{
   598  				Host:    "chromium.googlesource.com",
   599  				Project: "chromium/src",
   600  				Ref:     "ref",
   601  				Id:      "commit0",
   602  			},
   603  		})
   604  	})
   605  }