Index: build_scheduler/go/db/testutil.go |
diff --git a/build_scheduler/go/db/testutil.go b/build_scheduler/go/db/testutil.go |
deleted file mode 100644 |
index bf3a83a60e5cf499c3ea6a49b06f994cadb70b5a..0000000000000000000000000000000000000000 |
--- a/build_scheduler/go/db/testutil.go |
+++ /dev/null |
@@ -1,846 +0,0 @@ |
-package db |
- |
-import ( |
- "fmt" |
- "net/url" |
- "testing" |
- "time" |
- |
- assert "github.com/stretchr/testify/require" |
- |
- "go.skia.org/infra/go/testutils" |
- "go.skia.org/infra/go/util" |
-) |
- |
-const DEFAULT_TEST_REPO = "go-on-now.git" |
- |
-func makeTask(ts time.Time, commits []string) *Task { |
- return &Task{ |
- Created: ts, |
- Repo: DEFAULT_TEST_REPO, |
- Commits: commits, |
- Name: "Test-Task", |
- } |
-} |
- |
-func TestDB(t *testing.T, db DB) { |
- defer testutils.AssertCloses(t, db) |
- |
- _, err := db.GetModifiedTasks("dummy-id") |
- assert.True(t, IsUnknownId(err)) |
- |
- id, err := db.StartTrackingModifiedTasks() |
- assert.NoError(t, err) |
- |
- tasks, err := db.GetModifiedTasks(id) |
- assert.NoError(t, err) |
- assert.Equal(t, 0, len(tasks)) |
- |
- t1 := makeTask(time.Time{}, []string{"a", "b", "c", "d"}) |
- |
- // AssignId should fill in t1.Id. |
- assert.Equal(t, "", t1.Id) |
- assert.NoError(t, db.AssignId(t1)) |
- assert.NotEqual(t, "", t1.Id) |
- // Ids must be URL-safe. |
- assert.Equal(t, url.QueryEscape(t1.Id), t1.Id) |
- |
- // Task doesn't exist in DB yet. |
- noTask, err := db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- assert.Nil(t, noTask) |
- |
- // Set Creation time. Ensure Created is not the time of AssignId to test the |
- // sequence (1) AssignId, (2) initialize task, (3) PutTask. |
- now := time.Now().Add(time.Nanosecond) |
- t1.Created = now |
- |
- // Insert the task. |
- assert.NoError(t, db.PutTask(t1)) |
- |
- // Check that DbModified was set. |
- assert.False(t, util.TimeIsZero(t1.DbModified)) |
- t1LastModified := t1.DbModified |
- |
- // Task can now be retrieved by Id. |
- t1Again, err := db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, t1, t1Again) |
- |
- // Ensure that the task shows up in the modified list. |
- tasks, err = db.GetModifiedTasks(id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t1}, tasks) |
- |
- // Ensure that the task shows up in the correct date ranges. |
- timeStart := time.Time{} |
- t1Before := t1.Created |
- t1After := t1Before.Add(1 * time.Nanosecond) |
- timeEnd := now.Add(2 * time.Nanosecond) |
- tasks, err = db.GetTasksFromDateRange(timeStart, t1Before) |
- assert.NoError(t, err) |
- assert.Equal(t, 0, len(tasks)) |
- tasks, err = db.GetTasksFromDateRange(t1Before, t1After) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t1}, tasks) |
- tasks, err = db.GetTasksFromDateRange(t1After, timeEnd) |
- assert.NoError(t, err) |
- assert.Equal(t, 0, len(tasks)) |
- |
- // Insert two more tasks. Ensure at least 1 nanosecond between task Created |
- // times so that t1After != t2Before and t2After != t3Before. |
- t2 := makeTask(now.Add(time.Nanosecond), []string{"e", "f"}) |
- t3 := makeTask(now.Add(2*time.Nanosecond), []string{"g", "h"}) |
- assert.NoError(t, db.PutTasks([]*Task{t2, t3})) |
- |
- // Check that PutTasks assigned Ids. |
- assert.NotEqual(t, "", t2.Id) |
- assert.NotEqual(t, "", t3.Id) |
- // Ids must be URL-safe. |
- assert.Equal(t, url.QueryEscape(t2.Id), t2.Id) |
- assert.Equal(t, url.QueryEscape(t3.Id), t3.Id) |
- |
- // Ensure that both tasks show up in the modified list. |
- tasks, err = db.GetModifiedTasks(id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t2, t3}, tasks) |
- |
- // Make an update to t1 and t2. Ensure modified times change. |
- t2LastModified := t2.DbModified |
- t1.Status = TASK_STATUS_RUNNING |
- t2.Status = TASK_STATUS_SUCCESS |
- assert.NoError(t, db.PutTasks([]*Task{t1, t2})) |
- assert.False(t, t1.DbModified.Equal(t1LastModified)) |
- assert.False(t, t2.DbModified.Equal(t2LastModified)) |
- |
- // Ensure that both tasks show up in the modified list. |
- tasks, err = db.GetModifiedTasks(id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t1, t2}, tasks) |
- |
- // Ensure that all tasks show up in the correct time ranges, in sorted order. |
- t2Before := t2.Created |
- t2After := t2Before.Add(1 * time.Nanosecond) |
- |
- t3Before := t3.Created |
- t3After := t3Before.Add(1 * time.Nanosecond) |
- |
- timeEnd = now.Add(3 * time.Nanosecond) |
- |
- tasks, err = db.GetTasksFromDateRange(timeStart, t1Before) |
- assert.NoError(t, err) |
- assert.Equal(t, 0, len(tasks)) |
- |
- tasks, err = db.GetTasksFromDateRange(timeStart, t1After) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t1}, tasks) |
- |
- tasks, err = db.GetTasksFromDateRange(timeStart, t2Before) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t1}, tasks) |
- |
- tasks, err = db.GetTasksFromDateRange(timeStart, t2After) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t1, t2}, tasks) |
- |
- tasks, err = db.GetTasksFromDateRange(timeStart, t3Before) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t1, t2}, tasks) |
- |
- tasks, err = db.GetTasksFromDateRange(timeStart, t3After) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t1, t2, t3}, tasks) |
- |
- tasks, err = db.GetTasksFromDateRange(timeStart, timeEnd) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t1, t2, t3}, tasks) |
- |
- tasks, err = db.GetTasksFromDateRange(t1Before, timeEnd) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t1, t2, t3}, tasks) |
- |
- tasks, err = db.GetTasksFromDateRange(t1After, timeEnd) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t2, t3}, tasks) |
- |
- tasks, err = db.GetTasksFromDateRange(t2Before, timeEnd) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t2, t3}, tasks) |
- |
- tasks, err = db.GetTasksFromDateRange(t2After, timeEnd) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t3}, tasks) |
- |
- tasks, err = db.GetTasksFromDateRange(t3Before, timeEnd) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{t3}, tasks) |
- |
- tasks, err = db.GetTasksFromDateRange(t3After, timeEnd) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, []*Task{}, tasks) |
-} |
- |
-func TestTooManyUsers(t *testing.T, db DB) { |
- defer testutils.AssertCloses(t, db) |
- |
- // Max out the number of modified-tasks users; ensure that we error out. |
- for i := 0; i < MAX_MODIFIED_TASKS_USERS; i++ { |
- _, err := db.StartTrackingModifiedTasks() |
- assert.NoError(t, err) |
- } |
- _, err := db.StartTrackingModifiedTasks() |
- assert.True(t, IsTooManyUsers(err)) |
-} |
- |
-// Test that PutTask and PutTasks return ErrConcurrentUpdate when a cached Task |
-// has been updated in the DB. |
-func TestConcurrentUpdate(t *testing.T, db DB) { |
- defer testutils.AssertCloses(t, db) |
- |
- // Insert a task. |
- t1 := makeTask(time.Now(), []string{"a", "b", "c", "d"}) |
- assert.NoError(t, db.PutTask(t1)) |
- |
- // Retrieve a copy of the task. |
- t1Cached, err := db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, t1, t1Cached) |
- |
- // Update the original task. |
- t1.Commits = []string{"a", "b"} |
- assert.NoError(t, db.PutTask(t1)) |
- |
- // Update the cached copy; should get concurrent update error. |
- t1Cached.Status = TASK_STATUS_RUNNING |
- err = db.PutTask(t1Cached) |
- assert.True(t, IsConcurrentUpdate(err)) |
- |
- { |
- // DB should still have the old value of t1. |
- t1Again, err := db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, t1, t1Again) |
- } |
- |
- // Insert a second task. |
- t2 := makeTask(time.Now(), []string{"e", "f"}) |
- assert.NoError(t, db.PutTask(t2)) |
- |
- // Update t2 at the same time as t1Cached; should still get an error. |
- t2.Status = TASK_STATUS_MISHAP |
- err = db.PutTasks([]*Task{t2, t1Cached}) |
- assert.True(t, IsConcurrentUpdate(err)) |
- |
- { |
- // DB should still have the old value of t1. |
- t1Again, err := db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, t1, t1Again) |
- |
- // DB should also still have the old value of t2, but to keep InMemoryDB |
- // simple, we don't check that here. |
- } |
-} |
- |
-// Test UpdateWithRetries when no errors or retries. |
-func testUpdateWithRetriesSimple(t *testing.T, db DB) { |
- begin := time.Now() |
- |
- // Test no-op. |
- tasks, err := UpdateWithRetries(db, func() ([]*Task, error) { |
- return nil, nil |
- }) |
- assert.NoError(t, err) |
- assert.Equal(t, 0, len(tasks)) |
- |
- // Create new task t1. (UpdateWithRetries isn't actually useful in this case.) |
- tasks, err = UpdateWithRetries(db, func() ([]*Task, error) { |
- t1 := makeTask(time.Time{}, []string{"a", "b", "c", "d"}) |
- assert.NoError(t, db.AssignId(t1)) |
- t1.Created = time.Now().Add(time.Nanosecond) |
- return []*Task{t1}, nil |
- }) |
- assert.NoError(t, err) |
- assert.Equal(t, 1, len(tasks)) |
- t1 := tasks[0] |
- |
- // Update t1 and create t2. |
- tasks, err = UpdateWithRetries(db, func() ([]*Task, error) { |
- t1, err := db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- t1.Status = TASK_STATUS_RUNNING |
- t2 := makeTask(t1.Created.Add(time.Nanosecond), []string{"e", "f"}) |
- return []*Task{t1, t2}, nil |
- }) |
- assert.NoError(t, err) |
- assert.Equal(t, 2, len(tasks)) |
- assert.Equal(t, t1.Id, tasks[0].Id) |
- assert.Equal(t, TASK_STATUS_RUNNING, tasks[0].Status) |
- assert.Equal(t, []string{"e", "f"}, tasks[1].Commits) |
- |
- // Check that return value matches what's in the DB. |
- t1, err = db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- t2, err := db.GetTaskById(tasks[1].Id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, tasks[0], t1) |
- testutils.AssertDeepEqual(t, tasks[1], t2) |
- |
- // Check no extra tasks in the DB. |
- tasks, err = db.GetTasksFromDateRange(begin, time.Now().Add(3*time.Nanosecond)) |
- assert.NoError(t, err) |
- assert.Equal(t, 2, len(tasks)) |
- assert.Equal(t, t1.Id, tasks[0].Id) |
- assert.Equal(t, t2.Id, tasks[1].Id) |
-} |
- |
-// Test UpdateWithRetries when there are some retries, but eventual success. |
-func testUpdateWithRetriesSuccess(t *testing.T, db DB) { |
- begin := time.Now() |
- |
- // Create and cache. |
- t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"}) |
- assert.NoError(t, db.PutTask(t1)) |
- t1Cached := t1.Copy() |
- |
- // Update original. |
- t1.Status = TASK_STATUS_RUNNING |
- assert.NoError(t, db.PutTask(t1)) |
- |
- // Attempt update. |
- callCount := 0 |
- tasks, err := UpdateWithRetries(db, func() ([]*Task, error) { |
- callCount++ |
- if callCount >= 3 { |
- if task, err := db.GetTaskById(t1.Id); err != nil { |
- return nil, err |
- } else { |
- t1Cached = task |
- } |
- } |
- t1Cached.Status = TASK_STATUS_SUCCESS |
- t2 := makeTask(begin.Add(2*time.Nanosecond), []string{"e", "f"}) |
- return []*Task{t1Cached, t2}, nil |
- }) |
- assert.NoError(t, err) |
- assert.Equal(t, 3, callCount) |
- assert.Equal(t, 2, len(tasks)) |
- assert.Equal(t, t1.Id, tasks[0].Id) |
- assert.Equal(t, TASK_STATUS_SUCCESS, tasks[0].Status) |
- assert.Equal(t, []string{"e", "f"}, tasks[1].Commits) |
- |
- // Check that return value matches what's in the DB. |
- t1, err = db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- t2, err := db.GetTaskById(tasks[1].Id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, tasks[0], t1) |
- testutils.AssertDeepEqual(t, tasks[1], t2) |
- |
- // Check no extra tasks in the DB. |
- tasks, err = db.GetTasksFromDateRange(begin, time.Now().Add(3*time.Nanosecond)) |
- assert.NoError(t, err) |
- assert.Equal(t, 2, len(tasks)) |
- assert.Equal(t, t1.Id, tasks[0].Id) |
- assert.Equal(t, t2.Id, tasks[1].Id) |
-} |
- |
-// Test UpdateWithRetries when f returns an error. |
-func testUpdateWithRetriesErrorInFunc(t *testing.T, db DB) { |
- begin := time.Now() |
- |
- myErr := fmt.Errorf("NO! Bad dog!") |
- callCount := 0 |
- tasks, err := UpdateWithRetries(db, func() ([]*Task, error) { |
- callCount++ |
- // Return a task just for fun. |
- return []*Task{ |
- makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"}), |
- }, myErr |
- }) |
- assert.Error(t, err) |
- assert.Equal(t, myErr, err) |
- assert.Equal(t, 0, len(tasks)) |
- assert.Equal(t, 1, callCount) |
- |
- // Check no tasks in the DB. |
- tasks, err = db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nanosecond)) |
- assert.NoError(t, err) |
- assert.Equal(t, 0, len(tasks)) |
-} |
- |
-// Test UpdateWithRetries when PutTasks returns an error. |
-func testUpdateWithRetriesErrorInPutTasks(t *testing.T, db DB) { |
- begin := time.Now() |
- |
- callCount := 0 |
- tasks, err := UpdateWithRetries(db, func() ([]*Task, error) { |
- callCount++ |
- // Task has zero Created time. |
- return []*Task{ |
- makeTask(time.Time{}, []string{"a", "b", "c", "d"}), |
- }, nil |
- }) |
- assert.Error(t, err) |
- assert.Contains(t, err.Error(), "Created not set.") |
- assert.Equal(t, 0, len(tasks)) |
- assert.Equal(t, 1, callCount) |
- |
- // Check no tasks in the DB. |
- tasks, err = db.GetTasksFromDateRange(begin, time.Now().Add(time.Nanosecond)) |
- assert.NoError(t, err) |
- assert.Equal(t, 0, len(tasks)) |
-} |
- |
-// Test UpdateWithRetries when retries are exhausted. |
-func testUpdateWithRetriesExhausted(t *testing.T, db DB) { |
- begin := time.Now() |
- |
- // Create and cache. |
- t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"}) |
- assert.NoError(t, db.PutTask(t1)) |
- t1Cached := t1.Copy() |
- |
- // Update original. |
- t1.Status = TASK_STATUS_RUNNING |
- assert.NoError(t, db.PutTask(t1)) |
- |
- // Attempt update. |
- callCount := 0 |
- tasks, err := UpdateWithRetries(db, func() ([]*Task, error) { |
- callCount++ |
- t1Cached.Status = TASK_STATUS_SUCCESS |
- t2 := makeTask(begin.Add(2*time.Nanosecond), []string{"e", "f"}) |
- return []*Task{t1Cached, t2}, nil |
- }) |
- assert.True(t, IsConcurrentUpdate(err)) |
- assert.Equal(t, NUM_RETRIES, callCount) |
- assert.Equal(t, 0, len(tasks)) |
- |
- // Check no extra tasks in the DB. |
- tasks, err = db.GetTasksFromDateRange(begin, time.Now().Add(3*time.Nanosecond)) |
- assert.NoError(t, err) |
- assert.Equal(t, 1, len(tasks)) |
- assert.Equal(t, t1.Id, tasks[0].Id) |
- assert.Equal(t, TASK_STATUS_RUNNING, tasks[0].Status) |
-} |
- |
-// Test UpdateTaskWithRetries when no errors or retries. |
-func testUpdateTaskWithRetriesSimple(t *testing.T, db DB) { |
- begin := time.Now() |
- |
- // Create new task t1. |
- t1 := makeTask(time.Time{}, []string{"a", "b", "c", "d"}) |
- assert.NoError(t, db.AssignId(t1)) |
- t1.Created = time.Now().Add(time.Nanosecond) |
- assert.NoError(t, db.PutTask(t1)) |
- |
- // Update t1. |
- t1Updated, err := UpdateTaskWithRetries(db, t1.Id, func(task *Task) error { |
- task.Status = TASK_STATUS_RUNNING |
- return nil |
- }) |
- assert.NoError(t, err) |
- assert.Equal(t, t1.Id, t1Updated.Id) |
- assert.Equal(t, TASK_STATUS_RUNNING, t1Updated.Status) |
- assert.NotEqual(t, t1.DbModified, t1Updated.DbModified) |
- |
- // Check that return value matches what's in the DB. |
- t1Again, err := db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, t1Again, t1Updated) |
- |
- // Check no extra tasks in the DB. |
- tasks, err := db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nanosecond)) |
- assert.NoError(t, err) |
- assert.Equal(t, 1, len(tasks)) |
- assert.Equal(t, t1.Id, tasks[0].Id) |
-} |
- |
-// Test UpdateTaskWithRetries when there are some retries, but eventual success. |
-func testUpdateTaskWithRetriesSuccess(t *testing.T, db DB) { |
- begin := time.Now() |
- |
- // Create new task t1. |
- t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"}) |
- assert.NoError(t, db.PutTask(t1)) |
- |
- // Attempt update. |
- callCount := 0 |
- t1Updated, err := UpdateTaskWithRetries(db, t1.Id, func(task *Task) error { |
- callCount++ |
- if callCount < 3 { |
- // Sneakily make an update in the background. |
- t1.Commits = append(t1.Commits, fmt.Sprintf("z%d", callCount)) |
- assert.NoError(t, db.PutTask(t1)) |
- } |
- task.Status = TASK_STATUS_SUCCESS |
- return nil |
- }) |
- assert.NoError(t, err) |
- assert.Equal(t, 3, callCount) |
- assert.Equal(t, t1.Id, t1Updated.Id) |
- assert.Equal(t, TASK_STATUS_SUCCESS, t1Updated.Status) |
- |
- // Check that return value matches what's in the DB. |
- t1Again, err := db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, t1Again, t1Updated) |
- |
- // Check no extra tasks in the DB. |
- tasks, err := db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nanosecond)) |
- assert.NoError(t, err) |
- assert.Equal(t, 1, len(tasks)) |
- assert.Equal(t, t1.Id, tasks[0].Id) |
-} |
- |
-// Test UpdateTaskWithRetries when f returns an error. |
-func testUpdateTaskWithRetriesErrorInFunc(t *testing.T, db DB) { |
- begin := time.Now() |
- |
- // Create new task t1. |
- t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"}) |
- assert.NoError(t, db.PutTask(t1)) |
- |
- // Update and return an error. |
- myErr := fmt.Errorf("Um, actually, I didn't want to update that task.") |
- callCount := 0 |
- noTask, err := UpdateTaskWithRetries(db, t1.Id, func(task *Task) error { |
- callCount++ |
- // Update task to test nothing changes in DB. |
- task.Status = TASK_STATUS_RUNNING |
- return myErr |
- }) |
- assert.Error(t, err) |
- assert.Equal(t, myErr, err) |
- assert.Nil(t, noTask) |
- assert.Equal(t, 1, callCount) |
- |
- // Check task did not change in the DB. |
- t1Again, err := db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, t1, t1Again) |
- |
- // Check no extra tasks in the DB. |
- tasks, err := db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nanosecond)) |
- assert.NoError(t, err) |
- assert.Equal(t, 1, len(tasks)) |
- assert.Equal(t, t1.Id, tasks[0].Id) |
-} |
- |
-// Test UpdateTaskWithRetries when retries are exhausted. |
-func testUpdateTaskWithRetriesExhausted(t *testing.T, db DB) { |
- begin := time.Now() |
- |
- // Create new task t1. |
- t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"}) |
- assert.NoError(t, db.PutTask(t1)) |
- |
- // Update original. |
- t1.Status = TASK_STATUS_RUNNING |
- assert.NoError(t, db.PutTask(t1)) |
- |
- // Attempt update. |
- callCount := 0 |
- noTask, err := UpdateTaskWithRetries(db, t1.Id, func(task *Task) error { |
- callCount++ |
- // Sneakily make an update in the background. |
- t1.Commits = append(t1.Commits, fmt.Sprintf("z%d", callCount)) |
- assert.NoError(t, db.PutTask(t1)) |
- |
- task.Status = TASK_STATUS_SUCCESS |
- return nil |
- }) |
- assert.True(t, IsConcurrentUpdate(err)) |
- assert.Equal(t, NUM_RETRIES, callCount) |
- assert.Nil(t, noTask) |
- |
- // Check task did not change in the DB. |
- t1Again, err := db.GetTaskById(t1.Id) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, t1, t1Again) |
- |
- // Check no extra tasks in the DB. |
- tasks, err := db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nanosecond)) |
- assert.NoError(t, err) |
- assert.Equal(t, 1, len(tasks)) |
- assert.Equal(t, t1.Id, tasks[0].Id) |
-} |
- |
-// Test UpdateTaskWithRetries when the given ID is not found in the DB. |
-func testUpdateTaskWithRetriesTaskNotFound(t *testing.T, db DB) { |
- begin := time.Now() |
- |
- // Assign ID for a task, but don't put it in the DB. |
- t1 := makeTask(begin.Add(time.Nanosecond), []string{"a", "b", "c", "d"}) |
- assert.NoError(t, db.AssignId(t1)) |
- |
- // Attempt to update non-existent task. Function shouldn't be called. |
- callCount := 0 |
- noTask, err := UpdateTaskWithRetries(db, t1.Id, func(task *Task) error { |
- callCount++ |
- task.Status = TASK_STATUS_RUNNING |
- return nil |
- }) |
- assert.True(t, IsNotFound(err)) |
- assert.Nil(t, noTask) |
- assert.Equal(t, 0, callCount) |
- |
- // Check no tasks in the DB. |
- tasks, err := db.GetTasksFromDateRange(begin, time.Now().Add(2*time.Nanosecond)) |
- assert.NoError(t, err) |
- assert.Equal(t, 0, len(tasks)) |
-} |
- |
-// Test UpdateWithRetries and UpdateTaskWithRetries. |
-func TestUpdateWithRetries(t *testing.T, db DB) { |
- testUpdateWithRetriesSimple(t, db) |
- testUpdateWithRetriesSuccess(t, db) |
- testUpdateWithRetriesErrorInFunc(t, db) |
- testUpdateWithRetriesErrorInPutTasks(t, db) |
- testUpdateWithRetriesExhausted(t, db) |
- testUpdateTaskWithRetriesSimple(t, db) |
- testUpdateTaskWithRetriesSuccess(t, db) |
- testUpdateTaskWithRetriesErrorInFunc(t, db) |
- testUpdateTaskWithRetriesExhausted(t, db) |
- testUpdateTaskWithRetriesTaskNotFound(t, db) |
-} |
- |
-// makeTaskComment creates a comment with its ID fields based on the given repo, |
-// name, commit, and ts, and other fields based on n. |
-func makeTaskComment(n int, repo int, name int, commit int, ts time.Time) *TaskComment { |
- return &TaskComment{ |
- Repo: fmt.Sprintf("r%d", repo), |
- Name: fmt.Sprintf("n%d", name), |
- Commit: fmt.Sprintf("c%d", commit), |
- Timestamp: ts, |
- TaskId: fmt.Sprintf("id%d", n), |
- User: fmt.Sprintf("u%d", n), |
- Message: fmt.Sprintf("m%d", n), |
- } |
-} |
- |
-// makeTaskSpecComment creates a comment with its ID fields based on the given |
-// repo, name, and ts, and other fields based on n. |
-func makeTaskSpecComment(n int, repo int, name int, ts time.Time) *TaskSpecComment { |
- return &TaskSpecComment{ |
- Repo: fmt.Sprintf("r%d", repo), |
- Name: fmt.Sprintf("n%d", name), |
- Timestamp: ts, |
- User: fmt.Sprintf("u%d", n), |
- Flaky: n%2 == 0, |
- IgnoreFailure: n>>1%2 == 0, |
- Message: fmt.Sprintf("m%d", n), |
- } |
-} |
- |
-// makeCommitComment creates a comment with its ID fields based on the given |
-// repo, commit, and ts, and other fields based on n. |
-func makeCommitComment(n int, repo int, commit int, ts time.Time) *CommitComment { |
- return &CommitComment{ |
- Repo: fmt.Sprintf("r%d", repo), |
- Commit: fmt.Sprintf("c%d", commit), |
- Timestamp: ts, |
- User: fmt.Sprintf("u%d", n), |
- Message: fmt.Sprintf("m%d", n), |
- } |
-} |
- |
-// TestCommentDB validates that db correctly implements the CommentDB interface. |
-func TestCommentDB(t *testing.T, db CommentDB) { |
- now := time.Now() |
- |
- // Empty db. |
- { |
- actual, err := db.GetCommentsForRepos([]string{"r0", "r1", "r2"}, now.Add(-10000*time.Hour)) |
- assert.NoError(t, err) |
- assert.Equal(t, 3, len(actual)) |
- assert.Equal(t, "r0", actual[0].Repo) |
- assert.Equal(t, "r1", actual[1].Repo) |
- assert.Equal(t, "r2", actual[2].Repo) |
- for _, rc := range actual { |
- assert.Equal(t, 0, len(rc.TaskComments)) |
- assert.Equal(t, 0, len(rc.TaskSpecComments)) |
- assert.Equal(t, 0, len(rc.CommitComments)) |
- } |
- } |
- |
- // Add some comments. |
- tc1 := makeTaskComment(1, 1, 1, 1, now) |
- tc2 := makeTaskComment(2, 1, 1, 1, now.Add(2*time.Second)) |
- tc3 := makeTaskComment(3, 1, 1, 1, now.Add(time.Second)) |
- tc4 := makeTaskComment(4, 1, 1, 2, now) |
- tc5 := makeTaskComment(5, 1, 2, 2, now) |
- tc6 := makeTaskComment(6, 2, 3, 3, now) |
- tc6copy := tc6.Copy() // Adding identical comment should be ignored. |
- for _, c := range []*TaskComment{tc1, tc2, tc3, tc4, tc5, tc6, tc6copy} { |
- assert.NoError(t, db.PutTaskComment(c)) |
- } |
- tc6.Message = "modifying after Put shouldn't affect stored comment" |
- |
- sc1 := makeTaskSpecComment(1, 1, 1, now) |
- sc2 := makeTaskSpecComment(2, 1, 1, now.Add(2*time.Second)) |
- sc3 := makeTaskSpecComment(3, 1, 1, now.Add(time.Second)) |
- sc4 := makeTaskSpecComment(4, 1, 2, now) |
- sc5 := makeTaskSpecComment(5, 2, 3, now) |
- sc5copy := sc5.Copy() // Adding identical comment should be ignored. |
- for _, c := range []*TaskSpecComment{sc1, sc2, sc3, sc4, sc5, sc5copy} { |
- assert.NoError(t, db.PutTaskSpecComment(c)) |
- } |
- sc5.Message = "modifying after Put shouldn't affect stored comment" |
- |
- cc1 := makeCommitComment(1, 1, 1, now) |
- cc2 := makeCommitComment(2, 1, 1, now.Add(2*time.Second)) |
- cc3 := makeCommitComment(3, 1, 1, now.Add(time.Second)) |
- cc4 := makeCommitComment(4, 1, 2, now) |
- cc5 := makeCommitComment(5, 2, 3, now) |
- cc5copy := cc5.Copy() // Adding identical comment should be ignored. |
- for _, c := range []*CommitComment{cc1, cc2, cc3, cc4, cc5, cc5copy} { |
- assert.NoError(t, db.PutCommitComment(c)) |
- } |
- cc5.Message = "modifying after Put shouldn't affect stored comment" |
- |
- // Check that adding duplicate non-identical comment gives an error. |
- tc1different := tc1.Copy() |
- tc1different.Message = "not the same" |
- assert.True(t, IsAlreadyExists(db.PutTaskComment(tc1different))) |
- sc1different := sc1.Copy() |
- sc1different.Message = "not the same" |
- assert.True(t, IsAlreadyExists(db.PutTaskSpecComment(sc1different))) |
- cc1different := cc1.Copy() |
- cc1different.Message = "not the same" |
- assert.True(t, IsAlreadyExists(db.PutCommitComment(cc1different))) |
- |
- expected := []*RepoComments{ |
- &RepoComments{Repo: "r0"}, |
- &RepoComments{ |
- Repo: "r1", |
- TaskComments: map[string]map[string][]*TaskComment{ |
- "n1": { |
- "c1": {tc1, tc3, tc2}, |
- "c2": {tc4}, |
- }, |
- "n2": { |
- "c2": {tc5}, |
- }, |
- }, |
- TaskSpecComments: map[string][]*TaskSpecComment{ |
- "n1": {sc1, sc3, sc2}, |
- "n2": {sc4}, |
- }, |
- CommitComments: map[string][]*CommitComment{ |
- "c1": {cc1, cc3, cc2}, |
- "c2": {cc4}, |
- }, |
- }, |
- &RepoComments{ |
- Repo: "r2", |
- TaskComments: map[string]map[string][]*TaskComment{ |
- "n3": { |
- "c3": {tc6copy}, |
- }, |
- }, |
- TaskSpecComments: map[string][]*TaskSpecComment{ |
- "n3": {sc5copy}, |
- }, |
- CommitComments: map[string][]*CommitComment{ |
- "c3": {cc5copy}, |
- }, |
- }, |
- } |
- { |
- actual, err := db.GetCommentsForRepos([]string{"r0", "r1", "r2"}, now.Add(-10000*time.Hour)) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, expected, actual) |
- } |
- |
- // Specifying a cutoff time shouldn't drop required comments. |
- { |
- actual, err := db.GetCommentsForRepos([]string{"r1"}, now.Add(time.Second)) |
- assert.NoError(t, err) |
- assert.Equal(t, 1, len(actual)) |
- { |
- tcs := actual[0].TaskComments["n1"]["c1"] |
- assert.True(t, len(tcs) >= 2) |
- offset := 0 |
- if !tcs[0].Timestamp.Equal(tc3.Timestamp) { |
- offset = 1 |
- } |
- testutils.AssertDeepEqual(t, tc3, tcs[offset]) |
- testutils.AssertDeepEqual(t, tc2, tcs[offset+1]) |
- } |
- { |
- scs := actual[0].TaskSpecComments["n1"] |
- assert.True(t, len(scs) >= 2) |
- offset := 0 |
- if !scs[0].Timestamp.Equal(sc3.Timestamp) { |
- offset = 1 |
- } |
- testutils.AssertDeepEqual(t, sc3, scs[offset]) |
- testutils.AssertDeepEqual(t, sc2, scs[offset+1]) |
- } |
- { |
- ccs := actual[0].CommitComments["c1"] |
- assert.True(t, len(ccs) >= 2) |
- offset := 0 |
- if !ccs[0].Timestamp.Equal(cc3.Timestamp) { |
- offset = 1 |
- } |
- testutils.AssertDeepEqual(t, cc3, ccs[offset]) |
- testutils.AssertDeepEqual(t, cc2, ccs[offset+1]) |
- } |
- } |
- |
- // Delete some comments. |
- assert.NoError(t, db.DeleteTaskComment(tc3)) |
- assert.NoError(t, db.DeleteTaskSpecComment(sc3)) |
- assert.NoError(t, db.DeleteCommitComment(cc3)) |
- // Delete should only look at the ID fields. |
- assert.NoError(t, db.DeleteTaskComment(tc1different)) |
- assert.NoError(t, db.DeleteTaskSpecComment(sc1different)) |
- assert.NoError(t, db.DeleteCommitComment(cc1different)) |
- // Delete of nonexistent task should succeed. |
- assert.NoError(t, db.DeleteTaskComment(makeTaskComment(99, 1, 1, 1, now.Add(99*time.Second)))) |
- assert.NoError(t, db.DeleteTaskComment(makeTaskComment(99, 1, 1, 99, now))) |
- assert.NoError(t, db.DeleteTaskComment(makeTaskComment(99, 1, 99, 1, now))) |
- assert.NoError(t, db.DeleteTaskComment(makeTaskComment(99, 99, 1, 1, now))) |
- assert.NoError(t, db.DeleteTaskSpecComment(makeTaskSpecComment(99, 1, 1, now.Add(99*time.Second)))) |
- assert.NoError(t, db.DeleteTaskSpecComment(makeTaskSpecComment(99, 1, 99, now))) |
- assert.NoError(t, db.DeleteTaskSpecComment(makeTaskSpecComment(99, 99, 1, now))) |
- assert.NoError(t, db.DeleteCommitComment(makeCommitComment(99, 1, 1, now.Add(99*time.Second)))) |
- assert.NoError(t, db.DeleteCommitComment(makeCommitComment(99, 1, 99, now))) |
- assert.NoError(t, db.DeleteCommitComment(makeCommitComment(99, 99, 1, now))) |
- |
- expected[1].TaskComments["n1"]["c1"] = []*TaskComment{tc2} |
- expected[1].TaskSpecComments["n1"] = []*TaskSpecComment{sc2} |
- expected[1].CommitComments["c1"] = []*CommitComment{cc2} |
- { |
- actual, err := db.GetCommentsForRepos([]string{"r0", "r1", "r2"}, now.Add(-10000*time.Hour)) |
- assert.NoError(t, err) |
- testutils.AssertDeepEqual(t, expected, actual) |
- } |
- |
- // Delete all the comments. |
- for _, c := range []*TaskComment{tc2, tc4, tc5, tc6} { |
- assert.NoError(t, db.DeleteTaskComment(c)) |
- } |
- for _, c := range []*TaskSpecComment{sc2, sc4, sc5} { |
- assert.NoError(t, db.DeleteTaskSpecComment(c)) |
- } |
- for _, c := range []*CommitComment{cc2, cc4, cc5} { |
- assert.NoError(t, db.DeleteCommitComment(c)) |
- } |
- { |
- actual, err := db.GetCommentsForRepos([]string{"r0", "r1", "r2"}, now.Add(-10000*time.Hour)) |
- assert.NoError(t, err) |
- assert.Equal(t, 3, len(actual)) |
- assert.Equal(t, "r0", actual[0].Repo) |
- assert.Equal(t, "r1", actual[1].Repo) |
- assert.Equal(t, "r2", actual[2].Repo) |
- for _, rc := range actual { |
- assert.Equal(t, 0, len(rc.TaskComments)) |
- assert.Equal(t, 0, len(rc.TaskSpecComments)) |
- assert.Equal(t, 0, len(rc.CommitComments)) |
- } |
- } |
-} |