Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(96)

Side by Side Diff: build_scheduler/go/db/testutil.go

Issue 2296763008: [task scheduler] Move files from build_scheduler/ to task_scheduler/ (Closed) Base URL: https://skia.googlesource.com/buildbot@master
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « build_scheduler/go/db/task_test.go ('k') | build_scheduler/go/parse_task_cfg/main.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 package db
2
3 import (
4 "fmt"
5 "net/url"
6 "testing"
7 "time"
8
9 assert "github.com/stretchr/testify/require"
10
11 "go.skia.org/infra/go/testutils"
12 "go.skia.org/infra/go/util"
13 )
14
15 const DEFAULT_TEST_REPO = "go-on-now.git"
16
17 func makeTask(ts time.Time, commits []string) *Task {
18 return &Task{
19 Created: ts,
20 Repo: DEFAULT_TEST_REPO,
21 Commits: commits,
22 Name: "Test-Task",
23 }
24 }
25
26 func TestDB(t *testing.T, db DB) {
27 defer testutils.AssertCloses(t, db)
28
29 _, err := db.GetModifiedTasks("dummy-id")
30 assert.True(t, IsUnknownId(err))
31
32 id, err := db.StartTrackingModifiedTasks()
33 assert.NoError(t, err)
34
35 tasks, err := db.GetModifiedTasks(id)
36 assert.NoError(t, err)
37 assert.Equal(t, 0, len(tasks))
38
39 t1 := makeTask(time.Time{}, []string{"a", "b", "c", "d"})
40
41 // AssignId should fill in t1.Id.
42 assert.Equal(t, "", t1.Id)
43 assert.NoError(t, db.AssignId(t1))
44 assert.NotEqual(t, "", t1.Id)
45 // Ids must be URL-safe.
46 assert.Equal(t, url.QueryEscape(t1.Id), t1.Id)
47
48 // Task doesn't exist in DB yet.
49 noTask, err := db.GetTaskById(t1.Id)
50 assert.NoError(t, err)
51 assert.Nil(t, noTask)
52
53 // Set Creation time. Ensure Created is not the time of AssignId to test the
54 // sequence (1) AssignId, (2) initialize task, (3) PutTask.
55 now := time.Now().Add(time.Nanosecond)
56 t1.Created = now
57
58 // Insert the task.
59 assert.NoError(t, db.PutTask(t1))
60
61 // Check that DbModified was set.
62 assert.False(t, util.TimeIsZero(t1.DbModified))
63 t1LastModified := t1.DbModified
64
65 // Task can now be retrieved by Id.
66 t1Again, err := db.GetTaskById(t1.Id)
67 assert.NoError(t, err)
68 testutils.AssertDeepEqual(t, t1, t1Again)
69
70 // Ensure that the task shows up in the modified list.
71 tasks, err = db.GetModifiedTasks(id)
72 assert.NoError(t, err)
73 testutils.AssertDeepEqual(t, []*Task{t1}, tasks)
74
75 // Ensure that the task shows up in the correct date ranges.
76 timeStart := time.Time{}
77 t1Before := t1.Created
78 t1After := t1Before.Add(1 * time.Nanosecond)
79 timeEnd := now.Add(2 * time.Nanosecond)
80 tasks, err = db.GetTasksFromDateRange(timeStart, t1Before)
81 assert.NoError(t, err)
82 assert.Equal(t, 0, len(tasks))
83 tasks, err = db.GetTasksFromDateRange(t1Before, t1After)
84 assert.NoError(t, err)
85 testutils.AssertDeepEqual(t, []*Task{t1}, tasks)
86 tasks, err = db.GetTasksFromDateRange(t1After, timeEnd)
87 assert.NoError(t, err)
88 assert.Equal(t, 0, len(tasks))
89
90 // Insert two more tasks. Ensure at least 1 nanosecond between task Crea ted
91 // times so that t1After != t2Before and t2After != t3Before.
92 t2 := makeTask(now.Add(time.Nanosecond), []string{"e", "f"})
93 t3 := makeTask(now.Add(2*time.Nanosecond), []string{"g", "h"})
94 assert.NoError(t, db.PutTasks([]*Task{t2, t3}))
95
96 // Check that PutTasks assigned Ids.
97 assert.NotEqual(t, "", t2.Id)
98 assert.NotEqual(t, "", t3.Id)
99 // Ids must be URL-safe.
100 assert.Equal(t, url.QueryEscape(t2.Id), t2.Id)
101 assert.Equal(t, url.QueryEscape(t3.Id), t3.Id)
102
103 // Ensure that both tasks show up in the modified list.
104 tasks, err = db.GetModifiedTasks(id)
105 assert.NoError(t, err)
106 testutils.AssertDeepEqual(t, []*Task{t2, t3}, tasks)
107
108 // Make an update to t1 and t2. Ensure modified times change.
109 t2LastModified := t2.DbModified
110 t1.Status = TASK_STATUS_RUNNING
111 t2.Status = TASK_STATUS_SUCCESS
112 assert.NoError(t, db.PutTasks([]*Task{t1, t2}))
113 assert.False(t, t1.DbModified.Equal(t1LastModified))
114 assert.False(t, t2.DbModified.Equal(t2LastModified))
115
116 // Ensure that both tasks show up in the modified list.
117 tasks, err = db.GetModifiedTasks(id)
118 assert.NoError(t, err)
119 testutils.AssertDeepEqual(t, []*Task{t1, t2}, tasks)
120
121 // Ensure that all tasks show up in the correct time ranges, in sorted o rder.
122 t2Before := t2.Created
123 t2After := t2Before.Add(1 * time.Nanosecond)
124
125 t3Before := t3.Created
126 t3After := t3Before.Add(1 * time.Nanosecond)
127
128 timeEnd = now.Add(3 * time.Nanosecond)
129
130 tasks, err = db.GetTasksFromDateRange(timeStart, t1Before)
131 assert.NoError(t, err)
132 assert.Equal(t, 0, len(tasks))
133
134 tasks, err = db.GetTasksFromDateRange(timeStart, t1After)
135 assert.NoError(t, err)
136 testutils.AssertDeepEqual(t, []*Task{t1}, tasks)
137
138 tasks, err = db.GetTasksFromDateRange(timeStart, t2Before)
139 assert.NoError(t, err)
140 testutils.AssertDeepEqual(t, []*Task{t1}, tasks)
141
142 tasks, err = db.GetTasksFromDateRange(timeStart, t2After)
143 assert.NoError(t, err)
144 testutils.AssertDeepEqual(t, []*Task{t1, t2}, tasks)
145
146 tasks, err = db.GetTasksFromDateRange(timeStart, t3Before)
147 assert.NoError(t, err)
148 testutils.AssertDeepEqual(t, []*Task{t1, t2}, tasks)
149
150 tasks, err = db.GetTasksFromDateRange(timeStart, t3After)
151 assert.NoError(t, err)
152 testutils.AssertDeepEqual(t, []*Task{t1, t2, t3}, tasks)
153
154 tasks, err = db.GetTasksFromDateRange(timeStart, timeEnd)
155 assert.NoError(t, err)
156 testutils.AssertDeepEqual(t, []*Task{t1, t2, t3}, tasks)
157
158 tasks, err = db.GetTasksFromDateRange(t1Before, timeEnd)
159 assert.NoError(t, err)
160 testutils.AssertDeepEqual(t, []*Task{t1, t2, t3}, tasks)
161
162 tasks, err = db.GetTasksFromDateRange(t1After, timeEnd)
163 assert.NoError(t, err)
164 testutils.AssertDeepEqual(t, []*Task{t2, t3}, tasks)
165
166 tasks, err = db.GetTasksFromDateRange(t2Before, timeEnd)
167 assert.NoError(t, err)
168 testutils.AssertDeepEqual(t, []*Task{t2, t3}, tasks)
169
170 tasks, err = db.GetTasksFromDateRange(t2After, timeEnd)
171 assert.NoError(t, err)
172 testutils.AssertDeepEqual(t, []*Task{t3}, tasks)
173
174 tasks, err = db.GetTasksFromDateRange(t3Before, timeEnd)
175 assert.NoError(t, err)
176 testutils.AssertDeepEqual(t, []*Task{t3}, tasks)
177
178 tasks, err = db.GetTasksFromDateRange(t3After, timeEnd)
179 assert.NoError(t, err)
180 testutils.AssertDeepEqual(t, []*Task{}, tasks)
181 }
182
183 func TestTooManyUsers(t *testing.T, db DB) {
184 defer testutils.AssertCloses(t, db)
185
186 // Max out the number of modified-tasks users; ensure that we error out.
187 for i := 0; i < MAX_MODIFIED_TASKS_USERS; i++ {
188 _, err := db.StartTrackingModifiedTasks()
189 assert.NoError(t, err)
190 }
191 _, err := db.StartTrackingModifiedTasks()
192 assert.True(t, IsTooManyUsers(err))
193 }
194
195 // Test that PutTask and PutTasks return ErrConcurrentUpdate when a cached Task
196 // has been updated in the DB.
197 func TestConcurrentUpdate(t *testing.T, db DB) {
198 defer testutils.AssertCloses(t, db)
199
200 // Insert a task.
201 t1 := makeTask(time.Now(), []string{"a", "b", "c", "d"})
202 assert.NoError(t, db.PutTask(t1))
203
204 // Retrieve a copy of the task.
205 t1Cached, err := db.GetTaskById(t1.Id)
206 assert.NoError(t, err)
207 testutils.AssertDeepEqual(t, t1, t1Cached)
208
209 // Update the original task.
210 t1.Commits = []string{"a", "b"}
211 assert.NoError(t, db.PutTask(t1))
212
213 // Update the cached copy; should get concurrent update error.
214 t1Cached.Status = TASK_STATUS_RUNNING
215 err = db.PutTask(t1Cached)
216 assert.True(t, IsConcurrentUpdate(err))
217
218 {
219 // DB should still have the old value of t1.
220 t1Again, err := db.GetTaskById(t1.Id)
221 assert.NoError(t, err)
222 testutils.AssertDeepEqual(t, t1, t1Again)
223 }
224
225 // Insert a second task.
226 t2 := makeTask(time.Now(), []string{"e", "f"})
227 assert.NoError(t, db.PutTask(t2))
228
229 // Update t2 at the same time as t1Cached; should still get an error.
230 t2.Status = TASK_STATUS_MISHAP
231 err = db.PutTasks([]*Task{t2, t1Cached})
232 assert.True(t, IsConcurrentUpdate(err))
233
234 {
235 // DB should still have the old value of t1.
236 t1Again, err := db.GetTaskById(t1.Id)
237 assert.NoError(t, err)
238 testutils.AssertDeepEqual(t, t1, t1Again)
239
240 // DB should also still have the old value of t2, but to keep In MemoryDB
241 // simple, we don't check that here.
242 }
243 }
244
245 // Test UpdateWithRetries when no errors or retries.
246 func testUpdateWithRetriesSimple(t *testing.T, db DB) {
247 begin := time.Now()
248
249 // Test no-op.
250 tasks, err := UpdateWithRetries(db, func() ([]*Task, error) {
251 return nil, nil
252 })
253 assert.NoError(t, err)
254 assert.Equal(t, 0, len(tasks))
255
256 // Create new task t1. (UpdateWithRetries isn't actually useful in this case.)
257 tasks, err = UpdateWithRetries(db, func() ([]*Task, error) {
258 t1 := makeTask(time.Time{}, []string{"a", "b", "c", "d"})
259 assert.NoError(t, db.AssignId(t1))
260 t1.Created = time.Now().Add(time.Nanosecond)
261 return []*Task{t1}, nil
262 })
263 assert.NoError(t, err)
264 assert.Equal(t, 1, len(tasks))
265 t1 := tasks[0]
266
267 // Update t1 and create t2.
268 tasks, err = UpdateWithRetries(db, func() ([]*Task, error) {
269 t1, err := db.GetTaskById(t1.Id)
270 assert.NoError(t, err)
271 t1.Status = TASK_STATUS_RUNNING
272 t2 := makeTask(t1.Created.Add(time.Nanosecond), []string{"e", "f "})
273 return []*Task{t1, t2}, nil
274 })
275 assert.NoError(t, err)
276 assert.Equal(t, 2, len(tasks))
277 assert.Equal(t, t1.Id, tasks[0].Id)
278 assert.Equal(t, TASK_STATUS_RUNNING, tasks[0].Status)
279 assert.Equal(t, []string{"e", "f"}, tasks[1].Commits)
280
281 // Check that return value matches what's in the DB.
282 t1, err = db.GetTaskById(t1.Id)
283 assert.NoError(t, err)
284 t2, err := db.GetTaskById(tasks[1].Id)
285 assert.NoError(t, err)
286 testutils.AssertDeepEqual(t, tasks[0], t1)
287 testutils.AssertDeepEqual(t, tasks[1], t2)
288
289 // Check no extra tasks in the DB.
290 tasks, err = db.GetTasksFromDateRange(begin, time.Now().Add(3*time.Nanos econd))
291 assert.NoError(t, err)
292 assert.Equal(t, 2, len(tasks))
293 assert.Equal(t, t1.Id, tasks[0].Id)
294 assert.Equal(t, t2.Id, tasks[1].Id)
295 }
296
297 // Test UpdateWithRetries when there are some retries, but eventual success.
298 func testUpdateWithRetriesSuccess(t *testing.T, db DB) {
299 begin := time.Now()
300
301 // Create and cache.
302 t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"})
303 assert.NoError(t, db.PutTask(t1))
304 t1Cached := t1.Copy()
305
306 // Update original.
307 t1.Status = TASK_STATUS_RUNNING
308 assert.NoError(t, db.PutTask(t1))
309
310 // Attempt update.
311 callCount := 0
312 tasks, err := UpdateWithRetries(db, func() ([]*Task, error) {
313 callCount++
314 if callCount >= 3 {
315 if task, err := db.GetTaskById(t1.Id); err != nil {
316 return nil, err
317 } else {
318 t1Cached = task
319 }
320 }
321 t1Cached.Status = TASK_STATUS_SUCCESS
322 t2 := makeTask(begin.Add(2*time.Nanosecond), []string{"e", "f"})
323 return []*Task{t1Cached, t2}, nil
324 })
325 assert.NoError(t, err)
326 assert.Equal(t, 3, callCount)
327 assert.Equal(t, 2, len(tasks))
328 assert.Equal(t, t1.Id, tasks[0].Id)
329 assert.Equal(t, TASK_STATUS_SUCCESS, tasks[0].Status)
330 assert.Equal(t, []string{"e", "f"}, tasks[1].Commits)
331
332 // Check that return value matches what's in the DB.
333 t1, err = db.GetTaskById(t1.Id)
334 assert.NoError(t, err)
335 t2, err := db.GetTaskById(tasks[1].Id)
336 assert.NoError(t, err)
337 testutils.AssertDeepEqual(t, tasks[0], t1)
338 testutils.AssertDeepEqual(t, tasks[1], t2)
339
340 // Check no extra tasks in the DB.
341 tasks, err = db.GetTasksFromDateRange(begin, time.Now().Add(3*time.Nanos econd))
342 assert.NoError(t, err)
343 assert.Equal(t, 2, len(tasks))
344 assert.Equal(t, t1.Id, tasks[0].Id)
345 assert.Equal(t, t2.Id, tasks[1].Id)
346 }
347
348 // Test UpdateWithRetries when f returns an error.
349 func testUpdateWithRetriesErrorInFunc(t *testing.T, db DB) {
350 begin := time.Now()
351
352 myErr := fmt.Errorf("NO! Bad dog!")
353 callCount := 0
354 tasks, err := UpdateWithRetries(db, func() ([]*Task, error) {
355 callCount++
356 // Return a task just for fun.
357 return []*Task{
358 makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"}),
359 }, myErr
360 })
361 assert.Error(t, err)
362 assert.Equal(t, myErr, err)
363 assert.Equal(t, 0, len(tasks))
364 assert.Equal(t, 1, callCount)
365
366 // Check no tasks in the DB.
367 tasks, err = db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nanos econd))
368 assert.NoError(t, err)
369 assert.Equal(t, 0, len(tasks))
370 }
371
372 // Test UpdateWithRetries when PutTasks returns an error.
373 func testUpdateWithRetriesErrorInPutTasks(t *testing.T, db DB) {
374 begin := time.Now()
375
376 callCount := 0
377 tasks, err := UpdateWithRetries(db, func() ([]*Task, error) {
378 callCount++
379 // Task has zero Created time.
380 return []*Task{
381 makeTask(time.Time{}, []string{"a", "b", "c", "d"}),
382 }, nil
383 })
384 assert.Error(t, err)
385 assert.Contains(t, err.Error(), "Created not set.")
386 assert.Equal(t, 0, len(tasks))
387 assert.Equal(t, 1, callCount)
388
389 // Check no tasks in the DB.
390 tasks, err = db.GetTasksFromDateRange(begin, time.Now().Add(time.Nanosec ond))
391 assert.NoError(t, err)
392 assert.Equal(t, 0, len(tasks))
393 }
394
395 // Test UpdateWithRetries when retries are exhausted.
396 func testUpdateWithRetriesExhausted(t *testing.T, db DB) {
397 begin := time.Now()
398
399 // Create and cache.
400 t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"})
401 assert.NoError(t, db.PutTask(t1))
402 t1Cached := t1.Copy()
403
404 // Update original.
405 t1.Status = TASK_STATUS_RUNNING
406 assert.NoError(t, db.PutTask(t1))
407
408 // Attempt update.
409 callCount := 0
410 tasks, err := UpdateWithRetries(db, func() ([]*Task, error) {
411 callCount++
412 t1Cached.Status = TASK_STATUS_SUCCESS
413 t2 := makeTask(begin.Add(2*time.Nanosecond), []string{"e", "f"})
414 return []*Task{t1Cached, t2}, nil
415 })
416 assert.True(t, IsConcurrentUpdate(err))
417 assert.Equal(t, NUM_RETRIES, callCount)
418 assert.Equal(t, 0, len(tasks))
419
420 // Check no extra tasks in the DB.
421 tasks, err = db.GetTasksFromDateRange(begin, time.Now().Add(3*time.Nanos econd))
422 assert.NoError(t, err)
423 assert.Equal(t, 1, len(tasks))
424 assert.Equal(t, t1.Id, tasks[0].Id)
425 assert.Equal(t, TASK_STATUS_RUNNING, tasks[0].Status)
426 }
427
428 // Test UpdateTaskWithRetries when no errors or retries.
429 func testUpdateTaskWithRetriesSimple(t *testing.T, db DB) {
430 begin := time.Now()
431
432 // Create new task t1.
433 t1 := makeTask(time.Time{}, []string{"a", "b", "c", "d"})
434 assert.NoError(t, db.AssignId(t1))
435 t1.Created = time.Now().Add(time.Nanosecond)
436 assert.NoError(t, db.PutTask(t1))
437
438 // Update t1.
439 t1Updated, err := UpdateTaskWithRetries(db, t1.Id, func(task *Task) erro r {
440 task.Status = TASK_STATUS_RUNNING
441 return nil
442 })
443 assert.NoError(t, err)
444 assert.Equal(t, t1.Id, t1Updated.Id)
445 assert.Equal(t, TASK_STATUS_RUNNING, t1Updated.Status)
446 assert.NotEqual(t, t1.DbModified, t1Updated.DbModified)
447
448 // Check that return value matches what's in the DB.
449 t1Again, err := db.GetTaskById(t1.Id)
450 assert.NoError(t, err)
451 testutils.AssertDeepEqual(t, t1Again, t1Updated)
452
453 // Check no extra tasks in the DB.
454 tasks, err := db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nano second))
455 assert.NoError(t, err)
456 assert.Equal(t, 1, len(tasks))
457 assert.Equal(t, t1.Id, tasks[0].Id)
458 }
459
460 // Test UpdateTaskWithRetries when there are some retries, but eventual success.
461 func testUpdateTaskWithRetriesSuccess(t *testing.T, db DB) {
462 begin := time.Now()
463
464 // Create new task t1.
465 t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"})
466 assert.NoError(t, db.PutTask(t1))
467
468 // Attempt update.
469 callCount := 0
470 t1Updated, err := UpdateTaskWithRetries(db, t1.Id, func(task *Task) erro r {
471 callCount++
472 if callCount < 3 {
473 // Sneakily make an update in the background.
474 t1.Commits = append(t1.Commits, fmt.Sprintf("z%d", callC ount))
475 assert.NoError(t, db.PutTask(t1))
476 }
477 task.Status = TASK_STATUS_SUCCESS
478 return nil
479 })
480 assert.NoError(t, err)
481 assert.Equal(t, 3, callCount)
482 assert.Equal(t, t1.Id, t1Updated.Id)
483 assert.Equal(t, TASK_STATUS_SUCCESS, t1Updated.Status)
484
485 // Check that return value matches what's in the DB.
486 t1Again, err := db.GetTaskById(t1.Id)
487 assert.NoError(t, err)
488 testutils.AssertDeepEqual(t, t1Again, t1Updated)
489
490 // Check no extra tasks in the DB.
491 tasks, err := db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nano second))
492 assert.NoError(t, err)
493 assert.Equal(t, 1, len(tasks))
494 assert.Equal(t, t1.Id, tasks[0].Id)
495 }
496
497 // Test UpdateTaskWithRetries when f returns an error.
498 func testUpdateTaskWithRetriesErrorInFunc(t *testing.T, db DB) {
499 begin := time.Now()
500
501 // Create new task t1.
502 t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"})
503 assert.NoError(t, db.PutTask(t1))
504
505 // Update and return an error.
506 myErr := fmt.Errorf("Um, actually, I didn't want to update that task.")
507 callCount := 0
508 noTask, err := UpdateTaskWithRetries(db, t1.Id, func(task *Task) error {
509 callCount++
510 // Update task to test nothing changes in DB.
511 task.Status = TASK_STATUS_RUNNING
512 return myErr
513 })
514 assert.Error(t, err)
515 assert.Equal(t, myErr, err)
516 assert.Nil(t, noTask)
517 assert.Equal(t, 1, callCount)
518
519 // Check task did not change in the DB.
520 t1Again, err := db.GetTaskById(t1.Id)
521 assert.NoError(t, err)
522 testutils.AssertDeepEqual(t, t1, t1Again)
523
524 // Check no extra tasks in the DB.
525 tasks, err := db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nano second))
526 assert.NoError(t, err)
527 assert.Equal(t, 1, len(tasks))
528 assert.Equal(t, t1.Id, tasks[0].Id)
529 }
530
531 // Test UpdateTaskWithRetries when retries are exhausted.
532 func testUpdateTaskWithRetriesExhausted(t *testing.T, db DB) {
533 begin := time.Now()
534
535 // Create new task t1.
536 t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"})
537 assert.NoError(t, db.PutTask(t1))
538
539 // Update original.
540 t1.Status = TASK_STATUS_RUNNING
541 assert.NoError(t, db.PutTask(t1))
542
543 // Attempt update.
544 callCount := 0
545 noTask, err := UpdateTaskWithRetries(db, t1.Id, func(task *Task) error {
546 callCount++
547 // Sneakily make an update in the background.
548 t1.Commits = append(t1.Commits, fmt.Sprintf("z%d", callCount))
549 assert.NoError(t, db.PutTask(t1))
550
551 task.Status = TASK_STATUS_SUCCESS
552 return nil
553 })
554 assert.True(t, IsConcurrentUpdate(err))
555 assert.Equal(t, NUM_RETRIES, callCount)
556 assert.Nil(t, noTask)
557
558 // Check task did not change in the DB.
559 t1Again, err := db.GetTaskById(t1.Id)
560 assert.NoError(t, err)
561 testutils.AssertDeepEqual(t, t1, t1Again)
562
563 // Check no extra tasks in the DB.
564 tasks, err := db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nano second))
565 assert.NoError(t, err)
566 assert.Equal(t, 1, len(tasks))
567 assert.Equal(t, t1.Id, tasks[0].Id)
568 }
569
570 // Test UpdateTaskWithRetries when the given ID is not found in the DB.
571 func testUpdateTaskWithRetriesTaskNotFound(t *testing.T, db DB) {
572 begin := time.Now()
573
574 // Assign ID for a task, but don't put it in the DB.
575 t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"})
576 assert.NoError(t, db.AssignId(t1))
577
578 // Attempt to update non-existent task. Function shouldn't be called.
579 callCount := 0
580 noTask, err := UpdateTaskWithRetries(db, t1.Id, func(task *Task) error {
581 callCount++
582 task.Status = TASK_STATUS_RUNNING
583 return nil
584 })
585 assert.True(t, IsNotFound(err))
586 assert.Nil(t, noTask)
587 assert.Equal(t, 0, callCount)
588
589 // Check no tasks in the DB.
590 tasks, err := db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nano second))
591 assert.NoError(t, err)
592 assert.Equal(t, 0, len(tasks))
593 }
594
595 // Test UpdateWithRetries and UpdateTaskWithRetries.
596 func TestUpdateWithRetries(t *testing.T, db DB) {
597 testUpdateWithRetriesSimple(t, db)
598 testUpdateWithRetriesSuccess(t, db)
599 testUpdateWithRetriesErrorInFunc(t, db)
600 testUpdateWithRetriesErrorInPutTasks(t, db)
601 testUpdateWithRetriesExhausted(t, db)
602 testUpdateTaskWithRetriesSimple(t, db)
603 testUpdateTaskWithRetriesSuccess(t, db)
604 testUpdateTaskWithRetriesErrorInFunc(t, db)
605 testUpdateTaskWithRetriesExhausted(t, db)
606 testUpdateTaskWithRetriesTaskNotFound(t, db)
607 }
608
609 // makeTaskComment creates a comment with its ID fields based on the given repo,
610 // name, commit, and ts, and other fields based on n.
611 func makeTaskComment(n int, repo int, name int, commit int, ts time.Time) *TaskC omment {
612 return &TaskComment{
613 Repo: fmt.Sprintf("r%d", repo),
614 Name: fmt.Sprintf("n%d", name),
615 Commit: fmt.Sprintf("c%d", commit),
616 Timestamp: ts,
617 TaskId: fmt.Sprintf("id%d", n),
618 User: fmt.Sprintf("u%d", n),
619 Message: fmt.Sprintf("m%d", n),
620 }
621 }
622
623 // makeTaskSpecComment creates a comment with its ID fields based on the given
624 // repo, name, and ts, and other fields based on n.
625 func makeTaskSpecComment(n int, repo int, name int, ts time.Time) *TaskSpecComme nt {
626 return &TaskSpecComment{
627 Repo: fmt.Sprintf("r%d", repo),
628 Name: fmt.Sprintf("n%d", name),
629 Timestamp: ts,
630 User: fmt.Sprintf("u%d", n),
631 Flaky: n%2 == 0,
632 IgnoreFailure: n>>1%2 == 0,
633 Message: fmt.Sprintf("m%d", n),
634 }
635 }
636
637 // makeCommitComment creates a comment with its ID fields based on the given
638 // repo, commit, and ts, and other fields based on n.
639 func makeCommitComment(n int, repo int, commit int, ts time.Time) *CommitComment {
640 return &CommitComment{
641 Repo: fmt.Sprintf("r%d", repo),
642 Commit: fmt.Sprintf("c%d", commit),
643 Timestamp: ts,
644 User: fmt.Sprintf("u%d", n),
645 Message: fmt.Sprintf("m%d", n),
646 }
647 }
648
649 // TestCommentDB validates that db correctly implements the CommentDB interface.
650 func TestCommentDB(t *testing.T, db CommentDB) {
651 now := time.Now()
652
653 // Empty db.
654 {
655 actual, err := db.GetCommentsForRepos([]string{"r0", "r1", "r2"} , now.Add(-10000*time.Hour))
656 assert.NoError(t, err)
657 assert.Equal(t, 3, len(actual))
658 assert.Equal(t, "r0", actual[0].Repo)
659 assert.Equal(t, "r1", actual[1].Repo)
660 assert.Equal(t, "r2", actual[2].Repo)
661 for _, rc := range actual {
662 assert.Equal(t, 0, len(rc.TaskComments))
663 assert.Equal(t, 0, len(rc.TaskSpecComments))
664 assert.Equal(t, 0, len(rc.CommitComments))
665 }
666 }
667
668 // Add some comments.
669 tc1 := makeTaskComment(1, 1, 1, 1, now)
670 tc2 := makeTaskComment(2, 1, 1, 1, now.Add(2*time.Second))
671 tc3 := makeTaskComment(3, 1, 1, 1, now.Add(time.Second))
672 tc4 := makeTaskComment(4, 1, 1, 2, now)
673 tc5 := makeTaskComment(5, 1, 2, 2, now)
674 tc6 := makeTaskComment(6, 2, 3, 3, now)
675 tc6copy := tc6.Copy() // Adding identical comment should be ignored.
676 for _, c := range []*TaskComment{tc1, tc2, tc3, tc4, tc5, tc6, tc6copy} {
677 assert.NoError(t, db.PutTaskComment(c))
678 }
679 tc6.Message = "modifying after Put shouldn't affect stored comment"
680
681 sc1 := makeTaskSpecComment(1, 1, 1, now)
682 sc2 := makeTaskSpecComment(2, 1, 1, now.Add(2*time.Second))
683 sc3 := makeTaskSpecComment(3, 1, 1, now.Add(time.Second))
684 sc4 := makeTaskSpecComment(4, 1, 2, now)
685 sc5 := makeTaskSpecComment(5, 2, 3, now)
686 sc5copy := sc5.Copy() // Adding identical comment should be ignored.
687 for _, c := range []*TaskSpecComment{sc1, sc2, sc3, sc4, sc5, sc5copy} {
688 assert.NoError(t, db.PutTaskSpecComment(c))
689 }
690 sc5.Message = "modifying after Put shouldn't affect stored comment"
691
692 cc1 := makeCommitComment(1, 1, 1, now)
693 cc2 := makeCommitComment(2, 1, 1, now.Add(2*time.Second))
694 cc3 := makeCommitComment(3, 1, 1, now.Add(time.Second))
695 cc4 := makeCommitComment(4, 1, 2, now)
696 cc5 := makeCommitComment(5, 2, 3, now)
697 cc5copy := cc5.Copy() // Adding identical comment should be ignored.
698 for _, c := range []*CommitComment{cc1, cc2, cc3, cc4, cc5, cc5copy} {
699 assert.NoError(t, db.PutCommitComment(c))
700 }
701 cc5.Message = "modifying after Put shouldn't affect stored comment"
702
703 // Check that adding duplicate non-identical comment gives an error.
704 tc1different := tc1.Copy()
705 tc1different.Message = "not the same"
706 assert.True(t, IsAlreadyExists(db.PutTaskComment(tc1different)))
707 sc1different := sc1.Copy()
708 sc1different.Message = "not the same"
709 assert.True(t, IsAlreadyExists(db.PutTaskSpecComment(sc1different)))
710 cc1different := cc1.Copy()
711 cc1different.Message = "not the same"
712 assert.True(t, IsAlreadyExists(db.PutCommitComment(cc1different)))
713
714 expected := []*RepoComments{
715 &RepoComments{Repo: "r0"},
716 &RepoComments{
717 Repo: "r1",
718 TaskComments: map[string]map[string][]*TaskComment{
719 "n1": {
720 "c1": {tc1, tc3, tc2},
721 "c2": {tc4},
722 },
723 "n2": {
724 "c2": {tc5},
725 },
726 },
727 TaskSpecComments: map[string][]*TaskSpecComment{
728 "n1": {sc1, sc3, sc2},
729 "n2": {sc4},
730 },
731 CommitComments: map[string][]*CommitComment{
732 "c1": {cc1, cc3, cc2},
733 "c2": {cc4},
734 },
735 },
736 &RepoComments{
737 Repo: "r2",
738 TaskComments: map[string]map[string][]*TaskComment{
739 "n3": {
740 "c3": {tc6copy},
741 },
742 },
743 TaskSpecComments: map[string][]*TaskSpecComment{
744 "n3": {sc5copy},
745 },
746 CommitComments: map[string][]*CommitComment{
747 "c3": {cc5copy},
748 },
749 },
750 }
751 {
752 actual, err := db.GetCommentsForRepos([]string{"r0", "r1", "r2"} , now.Add(-10000*time.Hour))
753 assert.NoError(t, err)
754 testutils.AssertDeepEqual(t, expected, actual)
755 }
756
757 // Specifying a cutoff time shouldn't drop required comments.
758 {
759 actual, err := db.GetCommentsForRepos([]string{"r1"}, now.Add(ti me.Second))
760 assert.NoError(t, err)
761 assert.Equal(t, 1, len(actual))
762 {
763 tcs := actual[0].TaskComments["n1"]["c1"]
764 assert.True(t, len(tcs) >= 2)
765 offset := 0
766 if !tcs[0].Timestamp.Equal(tc3.Timestamp) {
767 offset = 1
768 }
769 testutils.AssertDeepEqual(t, tc3, tcs[offset])
770 testutils.AssertDeepEqual(t, tc2, tcs[offset+1])
771 }
772 {
773 scs := actual[0].TaskSpecComments["n1"]
774 assert.True(t, len(scs) >= 2)
775 offset := 0
776 if !scs[0].Timestamp.Equal(sc3.Timestamp) {
777 offset = 1
778 }
779 testutils.AssertDeepEqual(t, sc3, scs[offset])
780 testutils.AssertDeepEqual(t, sc2, scs[offset+1])
781 }
782 {
783 ccs := actual[0].CommitComments["c1"]
784 assert.True(t, len(ccs) >= 2)
785 offset := 0
786 if !ccs[0].Timestamp.Equal(cc3.Timestamp) {
787 offset = 1
788 }
789 testutils.AssertDeepEqual(t, cc3, ccs[offset])
790 testutils.AssertDeepEqual(t, cc2, ccs[offset+1])
791 }
792 }
793
794 // Delete some comments.
795 assert.NoError(t, db.DeleteTaskComment(tc3))
796 assert.NoError(t, db.DeleteTaskSpecComment(sc3))
797 assert.NoError(t, db.DeleteCommitComment(cc3))
798 // Delete should only look at the ID fields.
799 assert.NoError(t, db.DeleteTaskComment(tc1different))
800 assert.NoError(t, db.DeleteTaskSpecComment(sc1different))
801 assert.NoError(t, db.DeleteCommitComment(cc1different))
802 // Delete of nonexistent task should succeed.
803 assert.NoError(t, db.DeleteTaskComment(makeTaskComment(99, 1, 1, 1, now. Add(99*time.Second))))
804 assert.NoError(t, db.DeleteTaskComment(makeTaskComment(99, 1, 1, 99, now )))
805 assert.NoError(t, db.DeleteTaskComment(makeTaskComment(99, 1, 99, 1, now )))
806 assert.NoError(t, db.DeleteTaskComment(makeTaskComment(99, 99, 1, 1, now )))
807 assert.NoError(t, db.DeleteTaskSpecComment(makeTaskSpecComment(99, 1, 1, now.Add(99*time.Second))))
808 assert.NoError(t, db.DeleteTaskSpecComment(makeTaskSpecComment(99, 1, 99 , now)))
809 assert.NoError(t, db.DeleteTaskSpecComment(makeTaskSpecComment(99, 99, 1 , now)))
810 assert.NoError(t, db.DeleteCommitComment(makeCommitComment(99, 1, 1, now .Add(99*time.Second))))
811 assert.NoError(t, db.DeleteCommitComment(makeCommitComment(99, 1, 99, no w)))
812 assert.NoError(t, db.DeleteCommitComment(makeCommitComment(99, 99, 1, no w)))
813
814 expected[1].TaskComments["n1"]["c1"] = []*TaskComment{tc2}
815 expected[1].TaskSpecComments["n1"] = []*TaskSpecComment{sc2}
816 expected[1].CommitComments["c1"] = []*CommitComment{cc2}
817 {
818 actual, err := db.GetCommentsForRepos([]string{"r0", "r1", "r2"} , now.Add(-10000*time.Hour))
819 assert.NoError(t, err)
820 testutils.AssertDeepEqual(t, expected, actual)
821 }
822
823 // Delete all the comments.
824 for _, c := range []*TaskComment{tc2, tc4, tc5, tc6} {
825 assert.NoError(t, db.DeleteTaskComment(c))
826 }
827 for _, c := range []*TaskSpecComment{sc2, sc4, sc5} {
828 assert.NoError(t, db.DeleteTaskSpecComment(c))
829 }
830 for _, c := range []*CommitComment{cc2, cc4, cc5} {
831 assert.NoError(t, db.DeleteCommitComment(c))
832 }
833 {
834 actual, err := db.GetCommentsForRepos([]string{"r0", "r1", "r2"} , now.Add(-10000*time.Hour))
835 assert.NoError(t, err)
836 assert.Equal(t, 3, len(actual))
837 assert.Equal(t, "r0", actual[0].Repo)
838 assert.Equal(t, "r1", actual[1].Repo)
839 assert.Equal(t, "r2", actual[2].Repo)
840 for _, rc := range actual {
841 assert.Equal(t, 0, len(rc.TaskComments))
842 assert.Equal(t, 0, len(rc.TaskSpecComments))
843 assert.Equal(t, 0, len(rc.CommitComments))
844 }
845 }
846 }
OLDNEW
« no previous file with comments | « build_scheduler/go/db/task_test.go ('k') | build_scheduler/go/parse_task_cfg/main.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698