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 }