go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/testresults/test_data.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 testresults
    16  
    17  import (
    18  	"context"
    19  	"time"
    20  
    21  	"google.golang.org/protobuf/types/known/timestamppb"
    22  
    23  	"go.chromium.org/luci/server/span"
    24  
    25  	"go.chromium.org/luci/analysis/pbutil"
    26  	pb "go.chromium.org/luci/analysis/proto/v1"
    27  )
    28  
    29  var testVariant1 = pbutil.Variant("key1", "val1", "key2", "val1")
    30  var testVariant2 = pbutil.Variant("key1", "val2", "key2", "val1")
    31  var testVariant3 = pbutil.Variant("key1", "val2", "key2", "val2")
    32  var testVariant4 = pbutil.Variant("key1", "val1", "key2", "val2")
    33  
    34  func createTestHistoryTestData(ctx context.Context, referenceTime time.Time) error {
    35  	_, err := span.ReadWriteTransaction(ctx, func(ctx context.Context) error {
    36  		insertTVR := func(subRealm string, variant *pb.Variant) {
    37  			span.BufferWrite(ctx, (&TestVariantRealm{
    38  				Project:     "project",
    39  				TestID:      "test_id",
    40  				SubRealm:    subRealm,
    41  				Variant:     variant,
    42  				VariantHash: pbutil.VariantHash(variant),
    43  			}).SaveUnverified())
    44  		}
    45  
    46  		insertTVR("realm", testVariant1)
    47  		insertTVR("realm", testVariant2)
    48  		insertTVR("realm", testVariant3)
    49  		insertTVR("realm2", testVariant4)
    50  
    51  		insertTV := func(partitionTime time.Time, variant *pb.Variant, invId string, status pb.TestVerdictStatus, hasUnsubmittedChanges bool, isFromBisection bool, avgDuration *time.Duration) {
    52  			baseTestResult := NewTestResult().
    53  				WithProject("project").
    54  				WithTestID("test_id").
    55  				WithVariantHash(pbutil.VariantHash(variant)).
    56  				WithPartitionTime(partitionTime).
    57  				WithIngestedInvocationID(invId).
    58  				WithSubRealm("realm").
    59  				WithStatus(pb.TestResultStatus_PASS).
    60  				WithIsFromBisection(isFromBisection)
    61  			if hasUnsubmittedChanges {
    62  				sources := Sources{
    63  					Changelists: []Changelist{
    64  						{
    65  							Host:      "anothergerrit.gerrit.instance",
    66  							Change:    5471,
    67  							Patchset:  6,
    68  							OwnerKind: pb.ChangelistOwnerKind_HUMAN,
    69  						},
    70  						{
    71  							Host:      "mygerrit-review.googlesource.com",
    72  							Change:    4321,
    73  							Patchset:  5,
    74  							OwnerKind: pb.ChangelistOwnerKind_AUTOMATION,
    75  						},
    76  					},
    77  				}
    78  				baseTestResult = baseTestResult.WithSources(sources)
    79  			} else {
    80  				baseTestResult = baseTestResult.WithSources(Sources{})
    81  			}
    82  
    83  			trs := NewTestVerdict().
    84  				WithBaseTestResult(baseTestResult.Build()).
    85  				WithStatus(status).
    86  				WithPassedAvgDuration(avgDuration).
    87  				Build()
    88  			for _, tr := range trs {
    89  				span.BufferWrite(ctx, tr.SaveUnverified())
    90  			}
    91  		}
    92  
    93  		day := 24 * time.Hour
    94  		insertTV(referenceTime.Add(-1*time.Hour), testVariant1, "inv1", pb.TestVerdictStatus_EXPECTED, false, false, newDuration(22222*time.Microsecond))
    95  		insertTV(referenceTime.Add(-12*time.Hour), testVariant1, "inv2", pb.TestVerdictStatus_EXONERATED, false, false, newDuration(1234567890123456*time.Microsecond))
    96  		insertTV(referenceTime.Add(-24*time.Hour), testVariant2, "inv1", pb.TestVerdictStatus_FLAKY, false, false, nil)
    97  
    98  		insertTV(referenceTime.Add(-day-1*time.Hour), testVariant1, "inv1", pb.TestVerdictStatus_UNEXPECTED, false, false, newDuration(33333*time.Microsecond))
    99  		insertTV(referenceTime.Add(-day-12*time.Hour), testVariant1, "inv2", pb.TestVerdictStatus_UNEXPECTEDLY_SKIPPED, true, false, nil)
   100  		insertTV(referenceTime.Add(-day-24*time.Hour), testVariant2, "inv1", pb.TestVerdictStatus_EXPECTED, true, false, nil)
   101  
   102  		insertTV(referenceTime.Add(-2*day-3*time.Hour), testVariant3, "inv1", pb.TestVerdictStatus_EXONERATED, true, false, newDuration(88888*time.Microsecond))
   103  		insertTV(referenceTime.Add(-1*time.Hour), testVariant4, "inv3", pb.TestVerdictStatus_EXPECTED, true, true, newDuration(22222*time.Microsecond))
   104  
   105  		return nil
   106  	})
   107  	return err
   108  }
   109  
   110  func newDuration(value time.Duration) *time.Duration {
   111  	d := new(time.Duration)
   112  	*d = value
   113  	return d
   114  }
   115  
   116  // June 17th, 2022 is a Friday. The preceding 5 * 24 weekday hour
   117  // are as follows:
   118  //
   119  //	Inclusive              - Exclusive
   120  //
   121  // Interval 0: (-1 day) Thursday 8am  - (now)    Friday 8am
   122  // Interval 1: (-2 day) Wednesday 8am - (-1 day) Thursday 8am
   123  // Interval 2: (-3 day) Tuesday 8am   - (-2 day) Wednesday 8am
   124  // Interval 3: (-4 day) Monday 8am    - (-3 day) Tuesday 8am
   125  // Interval 4: (-7 day) Friday 8am    - (-4 day) Monday 8am
   126  var referenceTime = time.Date(2022, time.June, 17, 8, 0, 0, 0, time.UTC)
   127  
   128  // CreateQueryFailureRateTestData creates test data in Spanner for testing
   129  // QueryFailureRate.
   130  func CreateQueryFailureRateTestData(ctx context.Context) error {
   131  	var1 := pbutil.Variant("key1", "val1", "key2", "val1")
   132  	var2 := pbutil.Variant("key1", "val2", "key2", "val1")
   133  	var3 := pbutil.Variant("key1", "val2", "key2", "val2")
   134  
   135  	_, err := span.ReadWriteTransaction(ctx, func(ctx context.Context) error {
   136  		insertTV := func(partitionTime time.Time, variant *pb.Variant, invId string, runStatuses []RunStatus, clOwnerKind pb.ChangelistOwnerKind, changeListNumber ...int64) {
   137  			baseTestResult := NewTestResult().
   138  				WithProject("project").
   139  				WithTestID("test_id").
   140  				WithVariantHash(pbutil.VariantHash(variant)).
   141  				WithPartitionTime(partitionTime).
   142  				WithIngestedInvocationID(invId).
   143  				WithSubRealm("realm").
   144  				WithStatus(pb.TestResultStatus_PASS)
   145  
   146  			var changelists []Changelist
   147  			for _, clNum := range changeListNumber {
   148  				changelists = append(changelists, Changelist{
   149  					Host:      "mygerrit-review.googlesource.com",
   150  					Change:    clNum,
   151  					Patchset:  5,
   152  					OwnerKind: clOwnerKind,
   153  				})
   154  			}
   155  			sources := Sources{
   156  				Changelists: changelists,
   157  			}
   158  			baseTestResult = baseTestResult.WithSources(sources)
   159  
   160  			trs := NewTestVerdict().
   161  				WithBaseTestResult(baseTestResult.Build()).
   162  				WithRunStatus(runStatuses...).
   163  				Build()
   164  			for _, tr := range trs {
   165  				span.BufferWrite(ctx, tr.SaveUnverified())
   166  			}
   167  		}
   168  
   169  		// pass, fail is shorthand here for expected and unexpected run,
   170  		// where for the purposes of this RPC, a flaky run counts as
   171  		// "expected" (as it has at least one expected result).
   172  		passFail := []RunStatus{Flaky, Unexpected}
   173  		failPass := []RunStatus{Unexpected, Flaky}
   174  		pass := []RunStatus{Flaky}
   175  		fail := []RunStatus{Unexpected}
   176  		failFail := []RunStatus{Unexpected, Unexpected}
   177  
   178  		day := 24 * time.Hour
   179  
   180  		automationOwner := pb.ChangelistOwnerKind_AUTOMATION
   181  		humanOwner := pb.ChangelistOwnerKind_HUMAN
   182  		unspecifiedOwner := pb.ChangelistOwnerKind_CHANGELIST_OWNER_UNSPECIFIED
   183  
   184  		insertTV(referenceTime.Add(-6*day), var1, "inv1", failPass, unspecifiedOwner)
   185  		// duplicate-cl result should not be used, inv3 result should be
   186  		// used instead (as only one verdict per changelist is used, and
   187  		// inv3 is more recent).
   188  		insertTV(referenceTime.Add(-4*day), var1, "duplicate-cl", failPass, humanOwner, 1)
   189  		// duplicate-cl2 result should not be used, inv3 result should be used instead
   190  		// (as only one verdict per changelist is used, and inv3 is flaky
   191  		// and this is not).
   192  		insertTV(referenceTime.Add(-1*time.Hour), var1, "duplicate-cl2", pass, humanOwner, 1)
   193  
   194  		insertTV(referenceTime.Add(-4*day), var1, "inv2", pass, unspecifiedOwner, 2)
   195  		insertTV(referenceTime.Add(-2*time.Hour), var1, "inv3", failPass, humanOwner, 1)
   196  
   197  		// inv4 should not be used as the CL tested is authored by automation.
   198  		insertTV(referenceTime.Add(-3*day), var1, "inv4", failPass, automationOwner, 11)
   199  		insertTV(referenceTime.Add(-3*day), var1, "inv5", passFail, unspecifiedOwner, 3)
   200  		insertTV(referenceTime.Add(-2*day), var1, "inv6", fail, humanOwner, 4)
   201  		insertTV(referenceTime.Add(-3*day), var1, "inv7", failFail, unspecifiedOwner)
   202  		// should not be used, as tests multiple CLs, and too hard
   203  		// to deduplicate the verdicts.
   204  		insertTV(referenceTime.Add(-2*day), var1, "many-cl", failPass, humanOwner, 1, 3)
   205  
   206  		// should not be used, as times  fall outside the queried intervals.
   207  		insertTV(referenceTime.Add(-7*day-time.Microsecond), var1, "too-early", failPass, humanOwner, 5)
   208  		insertTV(referenceTime, var1, "too-late", failPass, humanOwner, 6)
   209  
   210  		insertTV(referenceTime.Add(-4*day), var2, "inv1", failPass, humanOwner, 1)
   211  		insertTV(referenceTime.Add(-3*day), var2, "inv2", failPass, humanOwner, 2)
   212  
   213  		insertTV(referenceTime.Add(-5*day), var3, "duplicate-cl1", passFail, humanOwner, 1)
   214  		insertTV(referenceTime.Add(-3*day), var3, "duplicate-cl2", failPass, humanOwner, 1)
   215  		insertTV(referenceTime.Add(-1*day), var3, "inv8", failPass, humanOwner, 1)
   216  
   217  		return nil
   218  	})
   219  	return err
   220  }
   221  
   222  func QueryFailureRateSampleRequest() (project string, asAtTime time.Time, testVariants []*pb.TestVariantIdentifier) {
   223  	var1 := pbutil.Variant("key1", "val1", "key2", "val1")
   224  	var3 := pbutil.Variant("key1", "val2", "key2", "val2")
   225  	testVariants = []*pb.TestVariantIdentifier{
   226  		{
   227  			TestId:  "test_id",
   228  			Variant: var1,
   229  		},
   230  		{
   231  			TestId:  "test_id",
   232  			Variant: var3,
   233  		},
   234  	}
   235  	asAtTime = time.Date(2022, time.June, 17, 8, 0, 0, 0, time.UTC)
   236  	return "project", asAtTime, testVariants
   237  }
   238  
   239  // QueryFailureRateSampleResponse returns expected response data from QueryFailureRate
   240  // after being invoked with QueryFailureRateSampleRequest.
   241  // It is assumed test data was setup with CreateQueryFailureRateTestData.
   242  func QueryFailureRateSampleResponse() *pb.QueryTestVariantFailureRateResponse {
   243  	var1 := pbutil.Variant("key1", "val1", "key2", "val1")
   244  	var3 := pbutil.Variant("key1", "val2", "key2", "val2")
   245  
   246  	day := 24 * time.Hour
   247  
   248  	intervals := []*pb.QueryTestVariantFailureRateResponse_Interval{
   249  		{
   250  			IntervalAge: 1,
   251  			StartTime:   timestamppb.New(referenceTime.Add(-1 * day)),
   252  			EndTime:     timestamppb.New(referenceTime),
   253  		},
   254  		{
   255  			IntervalAge: 2,
   256  			StartTime:   timestamppb.New(referenceTime.Add(-2 * day)),
   257  			EndTime:     timestamppb.New(referenceTime.Add(-1 * day)),
   258  		},
   259  		{
   260  			IntervalAge: 3,
   261  			StartTime:   timestamppb.New(referenceTime.Add(-3 * day)),
   262  			EndTime:     timestamppb.New(referenceTime.Add(-2 * day)),
   263  		},
   264  		{
   265  			IntervalAge: 4,
   266  			StartTime:   timestamppb.New(referenceTime.Add(-4 * day)),
   267  			EndTime:     timestamppb.New(referenceTime.Add(-3 * day)),
   268  		},
   269  		{
   270  			IntervalAge: 5,
   271  			StartTime:   timestamppb.New(referenceTime.Add(-7 * day)),
   272  			EndTime:     timestamppb.New(referenceTime.Add(-4 * day)),
   273  		},
   274  	}
   275  
   276  	analysis := []*pb.TestVariantFailureRateAnalysis{
   277  		{
   278  			TestId:  "test_id",
   279  			Variant: var1,
   280  			IntervalStats: []*pb.TestVariantFailureRateAnalysis_IntervalStats{
   281  				{
   282  					IntervalAge:           1,
   283  					TotalRunFlakyVerdicts: 1, // inv3.
   284  				},
   285  				{
   286  					IntervalAge:                2,
   287  					TotalRunUnexpectedVerdicts: 1, // inv6.
   288  				},
   289  				{
   290  					IntervalAge:                3,
   291  					TotalRunFlakyVerdicts:      1, // inv5.
   292  					TotalRunUnexpectedVerdicts: 1, // inv7.
   293  				},
   294  				{
   295  					IntervalAge:              4,
   296  					TotalRunExpectedVerdicts: 1, // inv2.
   297  				},
   298  				{
   299  					IntervalAge:           5,
   300  					TotalRunFlakyVerdicts: 1, //inv1.
   301  
   302  				},
   303  			},
   304  			RunFlakyVerdictExamples: []*pb.TestVariantFailureRateAnalysis_VerdictExample{
   305  				{
   306  					PartitionTime:        timestamppb.New(referenceTime.Add(-2 * time.Hour)),
   307  					IngestedInvocationId: "inv3",
   308  					Changelists:          expectedPBChangelist(1, pb.ChangelistOwnerKind_HUMAN),
   309  				},
   310  				{
   311  					PartitionTime:        timestamppb.New(referenceTime.Add(-3 * day)),
   312  					IngestedInvocationId: "inv5",
   313  					Changelists:          expectedPBChangelist(3, pb.ChangelistOwnerKind_CHANGELIST_OWNER_UNSPECIFIED),
   314  				},
   315  				{
   316  					PartitionTime:        timestamppb.New(referenceTime.Add(-6 * day)),
   317  					IngestedInvocationId: "inv1",
   318  				},
   319  			},
   320  			// inv4 should not be included as it is a CL authored by automation.
   321  			RecentVerdicts: []*pb.TestVariantFailureRateAnalysis_RecentVerdict{
   322  				{
   323  					PartitionTime:        timestamppb.New(referenceTime.Add(-2 * time.Hour)),
   324  					IngestedInvocationId: "inv3",
   325  					Changelists:          expectedPBChangelist(1, pb.ChangelistOwnerKind_HUMAN),
   326  					HasUnexpectedRuns:    true,
   327  				},
   328  				{
   329  					PartitionTime:        timestamppb.New(referenceTime.Add(-2 * day)),
   330  					IngestedInvocationId: "inv6",
   331  					Changelists:          expectedPBChangelist(4, pb.ChangelistOwnerKind_HUMAN),
   332  					HasUnexpectedRuns:    true,
   333  				},
   334  				{
   335  					PartitionTime:        timestamppb.New(referenceTime.Add(-3 * day)),
   336  					IngestedInvocationId: "inv5",
   337  					Changelists:          expectedPBChangelist(3, pb.ChangelistOwnerKind_CHANGELIST_OWNER_UNSPECIFIED),
   338  					HasUnexpectedRuns:    true,
   339  				},
   340  				{
   341  					PartitionTime:        timestamppb.New(referenceTime.Add(-3 * day)),
   342  					IngestedInvocationId: "inv7",
   343  					HasUnexpectedRuns:    true,
   344  				},
   345  				{
   346  					PartitionTime:        timestamppb.New(referenceTime.Add(-4 * day)),
   347  					IngestedInvocationId: "inv2",
   348  					Changelists:          expectedPBChangelist(2, pb.ChangelistOwnerKind_CHANGELIST_OWNER_UNSPECIFIED),
   349  					HasUnexpectedRuns:    false,
   350  				},
   351  				{
   352  					PartitionTime:        timestamppb.New(referenceTime.Add(-6 * day)),
   353  					IngestedInvocationId: "inv1",
   354  					HasUnexpectedRuns:    true,
   355  				},
   356  			},
   357  		},
   358  		{
   359  			TestId:  "test_id",
   360  			Variant: var3,
   361  			IntervalStats: []*pb.TestVariantFailureRateAnalysis_IntervalStats{
   362  				{
   363  					IntervalAge:           1,
   364  					TotalRunFlakyVerdicts: 1, // inv8.
   365  				},
   366  				{
   367  					IntervalAge: 2,
   368  				},
   369  				{
   370  					IntervalAge: 3,
   371  				},
   372  				{
   373  					IntervalAge: 4,
   374  				},
   375  				{
   376  					IntervalAge: 5,
   377  				},
   378  			},
   379  			RunFlakyVerdictExamples: []*pb.TestVariantFailureRateAnalysis_VerdictExample{
   380  				{
   381  					PartitionTime:        timestamppb.New(referenceTime.Add(-1 * day)),
   382  					IngestedInvocationId: "inv8",
   383  					Changelists:          expectedPBChangelist(1, pb.ChangelistOwnerKind_HUMAN),
   384  				},
   385  			},
   386  			RecentVerdicts: []*pb.TestVariantFailureRateAnalysis_RecentVerdict{
   387  				{
   388  					PartitionTime:        timestamppb.New(referenceTime.Add(-1 * day)),
   389  					IngestedInvocationId: "inv8",
   390  					Changelists:          expectedPBChangelist(1, pb.ChangelistOwnerKind_HUMAN),
   391  					HasUnexpectedRuns:    true,
   392  				},
   393  			},
   394  		},
   395  	}
   396  
   397  	return &pb.QueryTestVariantFailureRateResponse{
   398  		Intervals:    intervals,
   399  		TestVariants: analysis,
   400  	}
   401  }
   402  
   403  func expectedPBChangelist(change int64, ownerKind pb.ChangelistOwnerKind) []*pb.Changelist {
   404  	return []*pb.Changelist{
   405  		{
   406  			Host:      "mygerrit-review.googlesource.com",
   407  			Change:    change,
   408  			Patchset:  5,
   409  			OwnerKind: ownerKind,
   410  		},
   411  	}
   412  }