go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/changepoints/inputbuffer/input_buffer_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 inputbuffer 16 17 import ( 18 "testing" 19 "time" 20 21 . "github.com/smartystreets/goconvey/convey" 22 ) 23 24 func TestEncodeAndDecode(t *testing.T) { 25 Convey(`Encode and decode should return the same result`, t, func() { 26 history := History{ 27 Verdicts: []PositionVerdict{ 28 { 29 CommitPosition: 1345, 30 IsSimpleExpectedPass: true, 31 Hour: time.Unix(1000*3600, 0), 32 }, 33 { 34 CommitPosition: 1355, 35 IsSimpleExpectedPass: false, 36 Hour: time.Unix(1005*3600, 0), 37 Details: VerdictDetails{ 38 IsExonerated: false, 39 Runs: []Run{ 40 { 41 Expected: ResultCounts{ 42 PassCount: 1, 43 FailCount: 2, 44 CrashCount: 3, 45 AbortCount: 4, 46 }, 47 Unexpected: ResultCounts{ 48 PassCount: 5, 49 FailCount: 6, 50 CrashCount: 7, 51 AbortCount: 8, 52 }, 53 IsDuplicate: false, 54 }, 55 { 56 Expected: ResultCounts{ 57 PassCount: 1, 58 }, 59 Unexpected: ResultCounts{ 60 FailCount: 2, 61 }, 62 IsDuplicate: true, 63 }, 64 }, 65 }, 66 }, 67 { 68 CommitPosition: 1357, 69 IsSimpleExpectedPass: true, 70 Hour: time.Unix(1003*3600, 0), 71 }, 72 { 73 CommitPosition: 1357, 74 IsSimpleExpectedPass: false, 75 Hour: time.Unix(1005*3600, 0), 76 Details: VerdictDetails{ 77 IsExonerated: true, 78 Runs: []Run{ 79 { 80 Expected: ResultCounts{ 81 PassCount: 1, 82 }, 83 Unexpected: ResultCounts{ 84 FailCount: 2, 85 }, 86 IsDuplicate: true, 87 }, 88 { 89 Expected: ResultCounts{ 90 PassCount: 9, 91 FailCount: 10, 92 CrashCount: 11, 93 AbortCount: 12, 94 }, 95 Unexpected: ResultCounts{ 96 PassCount: 13, 97 FailCount: 14, 98 CrashCount: 15, 99 AbortCount: 16, 100 }, 101 IsDuplicate: false, 102 }, 103 }, 104 }, 105 }, 106 }, 107 } 108 109 hs := &HistorySerializer{} 110 encoded := hs.Encode(history) 111 decodedHistory := History{ 112 Verdicts: make([]PositionVerdict, 0, 100), 113 } 114 err := hs.DecodeInto(&decodedHistory, encoded) 115 So(err, ShouldBeNil) 116 So(len(decodedHistory.Verdicts), ShouldEqual, 4) 117 So(decodedHistory, ShouldResemble, history) 118 }) 119 120 Convey(`Encode and decode long history should not have error`, t, func() { 121 history := History{} 122 history.Verdicts = make([]PositionVerdict, 2000) 123 for i := 0; i < 2000; i++ { 124 history.Verdicts[i] = PositionVerdict{ 125 CommitPosition: i, 126 IsSimpleExpectedPass: false, 127 Hour: time.Unix(int64(i*3600), 0), 128 Details: VerdictDetails{ 129 IsExonerated: false, 130 Runs: []Run{ 131 { 132 Expected: ResultCounts{ 133 PassCount: 1, 134 }, 135 Unexpected: ResultCounts{ 136 FailCount: 2, 137 }, 138 IsDuplicate: false, 139 }, 140 { 141 Expected: ResultCounts{ 142 PassCount: 1, 143 }, 144 Unexpected: ResultCounts{ 145 FailCount: 2, 146 }, 147 IsDuplicate: false, 148 }, 149 { 150 Expected: ResultCounts{ 151 PassCount: 1, 152 }, 153 Unexpected: ResultCounts{ 154 FailCount: 2, 155 }, 156 IsDuplicate: false, 157 }, 158 }, 159 }, 160 } 161 } 162 hs := &HistorySerializer{} 163 encoded := hs.Encode(history) 164 decodedHistory := History{ 165 Verdicts: make([]PositionVerdict, 0, 2000), 166 } 167 err := hs.DecodeInto(&decodedHistory, encoded) 168 So(err, ShouldBeNil) 169 So(len(decodedHistory.Verdicts), ShouldEqual, 2000) 170 So(decodedHistory, ShouldResemble, history) 171 }) 172 } 173 174 func TestInputBuffer(t *testing.T) { 175 Convey(`Add item to input buffer`, t, func() { 176 ib := NewWithCapacity(10, 100) 177 originalHotBuffer := ib.HotBuffer.Verdicts 178 originalColdBuffer := ib.ColdBuffer.Verdicts 179 180 // Insert 9 verdicts into hot buffer. 181 ib.InsertVerdict(createTestVerdict(1, 4)) 182 So(ib.IsColdBufferDirty, ShouldBeFalse) 183 ib.InsertVerdict(createTestVerdict(2, 2)) 184 So(ib.IsColdBufferDirty, ShouldBeFalse) 185 ib.InsertVerdict(createTestVerdict(3, 3)) 186 So(ib.IsColdBufferDirty, ShouldBeFalse) 187 ib.InsertVerdict(createTestVerdict(2, 3)) 188 So(ib.IsColdBufferDirty, ShouldBeFalse) 189 ib.InsertVerdict(createTestVerdict(4, 5)) 190 So(ib.IsColdBufferDirty, ShouldBeFalse) 191 ib.InsertVerdict(createTestVerdict(1, 1)) 192 So(ib.IsColdBufferDirty, ShouldBeFalse) 193 ib.InsertVerdict(createTestVerdict(2, 3)) 194 So(ib.IsColdBufferDirty, ShouldBeFalse) 195 ib.InsertVerdict(createTestVerdict(7, 8)) 196 So(ib.IsColdBufferDirty, ShouldBeFalse) 197 ib.InsertVerdict(createTestVerdict(7, 7)) 198 So(ib.IsColdBufferDirty, ShouldBeFalse) 199 So(len(ib.HotBuffer.Verdicts), ShouldEqual, 9) 200 So(ib.HotBuffer.Verdicts, ShouldResemble, []PositionVerdict{ 201 createTestVerdict(1, 1), 202 createTestVerdict(1, 4), 203 createTestVerdict(2, 2), 204 createTestVerdict(2, 3), 205 createTestVerdict(2, 3), 206 createTestVerdict(3, 3), 207 createTestVerdict(4, 5), 208 createTestVerdict(7, 7), 209 createTestVerdict(7, 8), 210 }) 211 // Insert the last verdict, expecting a compaction. 212 ib.InsertVerdict(createTestVerdict(6, 2)) 213 So(ib.IsColdBufferDirty, ShouldBeTrue) 214 So(len(ib.HotBuffer.Verdicts), ShouldEqual, 0) 215 So(len(ib.ColdBuffer.Verdicts), ShouldEqual, 10) 216 So(ib.ColdBuffer.Verdicts, ShouldResemble, []PositionVerdict{ 217 createTestVerdict(1, 1), 218 createTestVerdict(1, 4), 219 createTestVerdict(2, 2), 220 createTestVerdict(2, 3), 221 createTestVerdict(2, 3), 222 createTestVerdict(3, 3), 223 createTestVerdict(4, 5), 224 createTestVerdict(6, 2), 225 createTestVerdict(7, 7), 226 createTestVerdict(7, 8), 227 }) 228 229 // The pre-allocated buffer should be retained, at the same capacity. 230 So(&ib.HotBuffer.Verdicts[0:1][0], ShouldEqual, &originalHotBuffer[0:1][0]) 231 So(&ib.ColdBuffer.Verdicts[0], ShouldEqual, &originalColdBuffer[0:1][0]) 232 }) 233 234 Convey(`Compaction should maintain order`, t, func() { 235 ib := Buffer{ 236 HotBufferCapacity: 5, 237 HotBuffer: History{ 238 Verdicts: []PositionVerdict{ 239 createTestVerdict(1, 1), 240 createTestVerdict(3, 1), 241 createTestVerdict(5, 1), 242 createTestVerdict(7, 1), 243 createTestVerdict(9, 1), 244 }, 245 }, 246 ColdBufferCapacity: 10, 247 ColdBuffer: History{ 248 // Allocate with capacity 10 so there is enough 249 // space to do an in-place compaction. 250 Verdicts: append(make([]PositionVerdict, 0, 10), []PositionVerdict{ 251 createTestVerdict(2, 1), 252 createTestVerdict(4, 1), 253 createTestVerdict(6, 1), 254 createTestVerdict(8, 1), 255 createTestVerdict(10, 1), 256 }...), 257 }, 258 } 259 originalHotBuffer := ib.HotBuffer.Verdicts 260 originalColdBuffer := ib.ColdBuffer.Verdicts 261 So(cap(originalColdBuffer), ShouldEqual, 10) 262 263 ib.Compact() 264 So(len(ib.HotBuffer.Verdicts), ShouldEqual, 0) 265 So(len(ib.ColdBuffer.Verdicts), ShouldEqual, 10) 266 So(ib.ColdBuffer.Verdicts, ShouldResemble, []PositionVerdict{ 267 createTestVerdict(1, 1), 268 createTestVerdict(2, 1), 269 createTestVerdict(3, 1), 270 createTestVerdict(4, 1), 271 createTestVerdict(5, 1), 272 createTestVerdict(6, 1), 273 createTestVerdict(7, 1), 274 createTestVerdict(8, 1), 275 createTestVerdict(9, 1), 276 createTestVerdict(10, 1), 277 }) 278 279 // The pre-allocated buffer should be retained, at the same capacity. 280 So(&ib.HotBuffer.Verdicts[0:1][0], ShouldEqual, &originalHotBuffer[0:1][0]) 281 So(&ib.ColdBuffer.Verdicts[0], ShouldEqual, &originalColdBuffer[0:1][0]) 282 }) 283 284 Convey(`Cold buffer should keep old verdicts after compaction`, t, func() { 285 ib := Buffer{ 286 HotBufferCapacity: 2, 287 HotBuffer: History{ 288 Verdicts: []PositionVerdict{ 289 createTestVerdict(7, 1), 290 createTestVerdict(9, 1), 291 }, 292 }, 293 ColdBufferCapacity: 5, 294 ColdBuffer: History{ 295 Verdicts: []PositionVerdict{ 296 createTestVerdict(2, 1), 297 createTestVerdict(4, 1), 298 createTestVerdict(6, 1), 299 createTestVerdict(8, 1), 300 createTestVerdict(10, 1), 301 }, 302 }, 303 } 304 305 ib.Compact() 306 So(len(ib.HotBuffer.Verdicts), ShouldEqual, 0) 307 So(len(ib.ColdBuffer.Verdicts), ShouldEqual, 7) 308 So(ib.ColdBuffer.Verdicts, ShouldResemble, []PositionVerdict{ 309 createTestVerdict(2, 1), 310 createTestVerdict(4, 1), 311 createTestVerdict(6, 1), 312 createTestVerdict(7, 1), 313 createTestVerdict(8, 1), 314 createTestVerdict(9, 1), 315 createTestVerdict(10, 1), 316 }) 317 }) 318 319 Convey(`EvictBefore`, t, func() { 320 buffer := History{ 321 Verdicts: []PositionVerdict{ 322 createTestVerdict(2, 1), 323 createTestVerdict(4, 1), 324 createTestVerdict(6, 1), 325 createTestVerdict(8, 1), 326 createTestVerdict(10, 1), 327 }, 328 } 329 originalVerdictsBuffer := buffer.Verdicts 330 So(cap(buffer.Verdicts), ShouldEqual, 5) 331 332 Convey(`Start of slice`, func() { 333 buffer.EvictBefore(0) 334 So(buffer, ShouldResemble, History{ 335 Verdicts: []PositionVerdict{ 336 createTestVerdict(2, 1), 337 createTestVerdict(4, 1), 338 createTestVerdict(6, 1), 339 createTestVerdict(8, 1), 340 createTestVerdict(10, 1), 341 }, 342 }) 343 344 So(&buffer.Verdicts[0:1][0], ShouldEqual, &originalVerdictsBuffer[0]) 345 So(cap(buffer.Verdicts), ShouldEqual, 5) 346 }) 347 Convey(`Middle of slice`, func() { 348 buffer.EvictBefore(2) 349 So(buffer, ShouldResemble, History{ 350 Verdicts: []PositionVerdict{ 351 createTestVerdict(6, 1), 352 createTestVerdict(8, 1), 353 createTestVerdict(10, 1), 354 }, 355 }) 356 357 // The pre-allocated buffer should be retained, at the same capacity. 358 So(&buffer.Verdicts[0:1][0], ShouldEqual, &originalVerdictsBuffer[0]) 359 So(cap(buffer.Verdicts), ShouldEqual, 5) 360 }) 361 Convey(`End of slice`, func() { 362 buffer.EvictBefore(5) 363 So(buffer, ShouldResemble, History{ 364 Verdicts: []PositionVerdict{}, 365 }) 366 367 // The pre-allocated buffer should be retained, at the same capacity. 368 So(&buffer.Verdicts[0:1][0], ShouldEqual, &originalVerdictsBuffer[0]) 369 So(cap(buffer.Verdicts), ShouldEqual, 5) 370 }) 371 Convey(`Empty slice`, func() { 372 buffer := History{ 373 Verdicts: []PositionVerdict{}, 374 } 375 buffer.EvictBefore(0) 376 So(buffer, ShouldResemble, History{ 377 Verdicts: []PositionVerdict{}, 378 }) 379 }) 380 }) 381 382 Convey(`Clear`, t, func() { 383 ib := NewWithCapacity(5, 10) 384 ib.HotBuffer.Verdicts = append(ib.HotBuffer.Verdicts, []PositionVerdict{ 385 createTestVerdict(1, 1), 386 createTestVerdict(3, 1), 387 createTestVerdict(5, 1), 388 createTestVerdict(7, 1), 389 createTestVerdict(9, 1), 390 }...) 391 ib.ColdBuffer.Verdicts = append(ib.ColdBuffer.Verdicts, []PositionVerdict{ 392 createTestVerdict(2, 1), 393 createTestVerdict(4, 1), 394 createTestVerdict(6, 1), 395 createTestVerdict(8, 1), 396 createTestVerdict(10, 1), 397 }...) 398 originalHotBuffer := ib.HotBuffer.Verdicts 399 originalColdBuffer := ib.ColdBuffer.Verdicts 400 401 ib.Clear() 402 403 So(ib, ShouldResemble, &Buffer{ 404 HotBufferCapacity: 5, 405 HotBuffer: History{ 406 Verdicts: []PositionVerdict{}, 407 }, 408 ColdBufferCapacity: 10, 409 ColdBuffer: History{ 410 Verdicts: []PositionVerdict{}, 411 }, 412 }) 413 // The pre-allocated buffer should be retained, at the same capacity. 414 So(&ib.HotBuffer.Verdicts[0:1][0], ShouldEqual, &originalHotBuffer[0]) 415 So(&ib.ColdBuffer.Verdicts[0:1][0], ShouldEqual, &originalColdBuffer[0]) 416 }) 417 } 418 419 func createTestVerdict(pos int, hour int) PositionVerdict { 420 return PositionVerdict{ 421 CommitPosition: pos, 422 IsSimpleExpectedPass: true, 423 Hour: time.Unix(int64(3600*hour), 0), 424 } 425 }