Exemple #1
0
// TestGetModeHistory verifies that we correctly track mode history.
func TestGetModeHistory(t *testing.T) {
	testutils.SkipIfShort(t)
	d := newTestDB(t)
	defer d.cleanup(t)

	// Single mode.
	m1 := &ModeChange{
		Message: "Starting!",
		Mode:    MODE_RUNNING,
		Time:    time.Now().UTC(),
		User:    "******",
	}
	assert.Nil(t, d.db.SetMode(m1))
	history, err := d.db.GetModeHistory(10)
	assert.Nil(t, err)
	assert.Equal(t, 1, len(history))
	testutils.AssertDeepEqual(t, m1, history[0])

	// Add more modes, ensuring that we retrieve them consistently.
	m2 := &ModeChange{
		Message: "Stoppit",
		Mode:    MODE_STOPPED,
		Time:    time.Now().UTC().Add(time.Minute),
		User:    "******",
	}
	m3 := &ModeChange{
		Message: "Dry run",
		Mode:    MODE_DRY_RUN,
		Time:    time.Now().UTC().Add(2 * time.Minute),
		User:    "******",
	}
	m4 := &ModeChange{
		Message: "Dry run",
		Mode:    MODE_DRY_RUN,
		Time:    time.Now().UTC().Add(3 * time.Minute),
		User:    "******",
	}

	assert.Nil(t, d.db.SetMode(m2))
	history, err = d.db.GetModeHistory(10)
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, []*ModeChange{m2, m1}, history)

	assert.Nil(t, d.db.SetMode(m3))
	history, err = d.db.GetModeHistory(10)
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, []*ModeChange{m3, m2, m1}, history)

	assert.Nil(t, d.db.SetMode(m4))
	history, err = d.db.GetModeHistory(10)
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, []*ModeChange{m4, m3, m2, m1}, history)

	// Only three changes?
	history, err = d.db.GetModeHistory(3)
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, []*ModeChange{m4, m3, m2}, history)
}
// dbSerializeAndCompare is a helper function used by TestDbBuild which takes
// a Build object, writes it into the database, reads it back out, and compares
// the structs. Returns any errors encountered including a comparison failure.
func dbSerializeAndCompare(t *testing.T, d testDB, b1 *Build, ignoreIds bool) {
	assert.Nil(t, d.DB().PutBuild(b1))
	b2, err := d.DB().GetBuildFromDB(b1.Master, b1.Builder, b1.Number)
	assert.Nil(t, err)
	assert.NotNil(t, b2)

	testutils.AssertDeepEqual(t, b1, b2)
}
// TestAlertJsonSerialization verifies that we properly serialize and
// deserialize Alerts to JSON.
func TestAlertJsonSerialization(t *testing.T) {
	cases := []*Alert{
		&Alert{},    // Empty Alert.
		makeAlert(), // Realistic case.
	}

	for _, want := range cases {
		b, err := json.Marshal(want)
		assert.Nil(t, err)
		got := &Alert{}
		assert.Nil(t, json.Unmarshal(b, got))
		testutils.AssertDeepEqual(t, got, want)
	}
}
// TestBuildJsonSerialization verifies that we can serialize a build to JSON
// and back without losing or corrupting the data.
func TestBuildJsonSerialization(t *testing.T) {
	testutils.SkipIfShort(t)

	// Load the test repo.
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	repos := gitinfo.NewRepoMap(tr.Dir)

	b1, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)
	bytes, err := json.Marshal(b1)
	assert.Nil(t, err)
	b2 := &Build{}
	assert.Nil(t, json.Unmarshal(bytes, b2))
	testutils.AssertDeepEqual(t, b1, b2)
}
// TestGetLatestBuilds verifies that getLatestBuilds gives us
// the expected results.
func TestGetLatestBuilds(t *testing.T) {
	// Note: Masters with no builders shouldn't be in the map.
	expected := map[string]map[string]int{
		"client.skia.fyi": map[string]int{
			"Housekeeper-PerCommit":            1035,
			"Housekeeper-Nightly-RecreateSKPs": 58,
		},
		"client.skia.android": map[string]int{
			"Perf-Android-Venue8-PowerVR-x86-Release": 466,
			"Test-Android-Venue8-PowerVR-x86-Debug":   532,
		},
	}

	httpClient = testHttpClient
	actual, err := getLatestBuilds()
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, expected, actual)
}
// dbSerializeAndCompare is a helper function used by TestDbBuild which takes
// a Build object, writes it into the database, reads it back out, and compares
// the structs. Returns any errors encountered including a comparison failure.
func dbSerializeAndCompare(t *testing.T, b1 *Build, ignoreIds bool) {
	assert.Nil(t, b1.ReplaceIntoDB())
	b2, err := GetBuildFromDB(b1.Builder, b1.Master, b1.Number)
	assert.Nil(t, err)

	// Force the IDs to be equal, since the DB assigns ID, and we
	// don't care to try to predict them.
	if ignoreIds {
		b2.Id = b1.Id
		assert.Equal(t, len(b1.Steps), len(b2.Steps), "Got incorrect number of steps.")
		for i, s := range b2.Steps {
			s.Id = b1.Steps[i].Id
		}
		assert.Equal(t, len(b1.Comments), len(b2.Comments), "Got incorrect number of comments.")
		for i, c := range b2.Comments {
			c.Id = b1.Comments[i].Id
		}
	}

	testutils.AssertDeepEqual(t, b1, b2)
}
// TestAlertDBSerialization verifies that we properly serialize and
// deserialize Alerts into the DB.
func TestAlertDBSerialization(t *testing.T) {
	testutils.SkipIfShort(t)
	d := clearDB(t)
	defer d.Close(t)

	cases := []*Alert{
		&Alert{},    // Empty Alert.
		makeAlert(), // Realistic case.
	}

	for _, want := range cases {
		assert.Nil(t, want.retryReplaceIntoDB())
		a, err := GetActiveAlerts()
		assert.Nil(t, err)
		assert.Equal(t, 1, len(a))
		got := a[0]
		testutils.AssertDeepEqual(t, got, want)
		// Dismiss the Alert, so that it doesn't show up later.
		got.DismissedAt = 1000
		assert.Nil(t, got.retryReplaceIntoDB())
	}
}
Exemple #8
0
// checkStatus verifies that we get the expected status from the roller.
func checkStatus(t *testing.T, r *AutoRoller, rv *mockRietveld, rm *mockRepoManager, expectedStatus string, current *rietveld.Issue, currentTrybots []*buildbucket.Build, currentDryRun bool, last *rietveld.Issue, lastTrybots []*buildbucket.Build, lastDryRun bool) {
	rv.assertMocksEmpty()
	rm.assertForceUpdate()
	s := r.GetStatus(true)
	assert.Equal(t, expectedStatus, s.Status)
	assert.Equal(t, s.Error, "")
	checkRoll := func(t *testing.T, expect *rietveld.Issue, actual *autoroll.AutoRollIssue, expectTrybots []*buildbucket.Build, dryRun bool) {
		if expect != nil {
			assert.NotNil(t, actual)
			ari, err := autoroll.FromRietveldIssue(expect, rm.FullSkiaHash)
			assert.Nil(t, err)
			tryResults := make([]*autoroll.TryResult, 0, len(expectTrybots))
			for _, b := range expectTrybots {
				tryResult, err := autoroll.TryResultFromBuildbucket(b)
				assert.Nil(t, err)
				tryResults = append(tryResults, tryResult)
			}
			ari.TryResults = tryResults

			// This is kind of a hack to prevent having to pass the
			// expected dry run result around.
			if dryRun {
				if ari.AllTrybotsFinished() {
					ari.Result = autoroll.ROLL_RESULT_DRY_RUN_FAILURE
					if ari.AllTrybotsSucceeded() {
						ari.Result = autoroll.ROLL_RESULT_DRY_RUN_SUCCESS
					}
				}
			}

			assert.Nil(t, ari.Validate())
			testutils.AssertDeepEqual(t, ari, actual)
		} else {
			assert.Nil(t, actual)
		}
	}
	checkRoll(t, current, s.CurrentRoll, currentTrybots, currentDryRun)
	checkRoll(t, last, s.LastRoll, lastTrybots, lastDryRun)
}
// testGetUningestedBuilds verifies that getUningestedBuilds works as expected.
func testGetUningestedBuilds(t *testing.T) {
	d := clearDB(t)
	defer d.Close(t)

	// Load the test repo.
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	repos := gitinfo.NewRepoMap(tr.Dir)

	// This builder is no longer found on the master.
	b1, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)
	b1.Master = "client.skia.compile"
	b1.Builder = "My-Builder"
	b1.Number = 115
	b1.Steps = []*BuildStep{}
	assert.Nil(t, b1.ReplaceIntoDB())

	// This builder needs to load a few builds.
	b2, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)
	b2.Master = "client.skia.android"
	b2.Builder = "Perf-Android-Venue8-PowerVR-x86-Release"
	b2.Number = 463
	b2.Steps = []*BuildStep{}
	assert.Nil(t, b2.ReplaceIntoDB())

	// This builder is already up-to-date.
	b3, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)
	b3.Master = "client.skia.fyi"
	b3.Builder = "Housekeeper-PerCommit"
	b3.Number = 1035
	b3.Steps = []*BuildStep{}
	assert.Nil(t, b3.ReplaceIntoDB())

	// This builder is already up-to-date.
	b4, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)
	b4.Master = "client.skia.android"
	b4.Builder = "Test-Android-Venue8-PowerVR-x86-Debug"
	b4.Number = 532
	b4.Steps = []*BuildStep{}
	assert.Nil(t, b4.ReplaceIntoDB())

	// Expectations. If the master or builder has no uningested builds,
	// we expect it not to be in the results, even with an empty map/slice.
	expected := map[string]map[string][]int{
		"client.skia.fyi": map[string][]int{
			"Housekeeper-Nightly-RecreateSKPs": []int{ // No already-ingested builds.
				0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
			},
		},
		"client.skia.android": map[string][]int{ // Some already-ingested builds.
			"Perf-Android-Venue8-PowerVR-x86-Release": []int{
				464, 465, 466,
			},
		},
	}
	httpClient = testHttpClient
	actual, err := getUningestedBuilds()
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, expected, actual)
}
Exemple #10
0
// Test that we insert, update, delete, and retrieve rolls as expected.
func TestRolls(t *testing.T) {
	testutils.SkipIfShort(t)
	d := newTestDB(t)
	defer d.cleanup(t)

	now := time.Now().UTC()
	roll1 := &autoroll.AutoRollIssue{
		Closed:      false,
		Committed:   false,
		CommitQueue: true,
		Created:     now,
		Issue:       101101101,
		Modified:    now,
		Patchsets:   []int64{1},
		Result:      autoroll.ROLL_RESULT_IN_PROGRESS,
		Subject:     "Roll asdfdasf",
		TryResults:  []*autoroll.TryResult{},
	}

	// Insert.
	assert.Nil(t, d.db.InsertRoll(roll1))
	test1, err := d.db.GetRoll(roll1.Issue)
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, roll1, test1)
	recent, err := d.db.GetRecentRolls(10)
	assert.Nil(t, err)
	assert.Equal(t, 1, len(recent))
	testutils.AssertDeepEqual(t, roll1, recent[0])

	// Update.
	roll1.Closed = true
	roll1.Committed = true
	roll1.Result = autoroll.ROLL_RESULT_SUCCESS

	assert.Nil(t, d.db.UpdateRoll(roll1))
	test1, err = d.db.GetRoll(roll1.Issue)
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, roll1, test1)
	recent, err = d.db.GetRecentRolls(10)
	assert.Nil(t, err)
	assert.Equal(t, 1, len(recent))
	testutils.AssertDeepEqual(t, roll1, recent[0])

	// Delete.
	assert.Nil(t, d.db.DeleteRoll(roll1))
	test1, err = d.db.GetRoll(roll1.Issue)
	assert.Nil(t, err)
	assert.Nil(t, test1)
	recent, err = d.db.GetRecentRolls(10)
	assert.Nil(t, err)
	assert.Equal(t, 0, len(recent))

	// Multiple rolls.
	now = time.Now().UTC().Add(time.Minute)
	roll2 := &autoroll.AutoRollIssue{
		Closed:      false,
		Committed:   false,
		CommitQueue: true,
		Created:     now,
		Issue:       101101102,
		Modified:    now,
		Patchsets:   []int64{1},
		Result:      autoroll.ROLL_RESULT_IN_PROGRESS,
		Subject:     "Roll #2",
		TryResults:  []*autoroll.TryResult{},
	}
	now = time.Now().UTC().Add(30 * time.Minute)
	roll3 := &autoroll.AutoRollIssue{
		Closed:      false,
		Committed:   false,
		CommitQueue: true,
		Created:     now,
		Issue:       101101103,
		Modified:    now,
		Patchsets:   []int64{1},
		Result:      autoroll.ROLL_RESULT_IN_PROGRESS,
		Subject:     "Roll #3",
		TryResults:  []*autoroll.TryResult{},
	}
	now = time.Now().UTC().Add(10 * time.Minute)
	roll4 := &autoroll.AutoRollIssue{
		Closed:      false,
		Committed:   false,
		CommitQueue: true,
		Created:     now,
		Issue:       1001101, // Lower issue number, verify that we order correctly by date.
		Modified:    now,
		Patchsets:   []int64{1},
		Result:      autoroll.ROLL_RESULT_IN_PROGRESS,
		Subject:     "Roll #4",
		TryResults:  []*autoroll.TryResult{},
	}
	for _, roll := range []*autoroll.AutoRollIssue{roll1, roll2, roll3, roll4} {
		assert.Nil(t, d.db.InsertRoll(roll))
	}

	// Ensure that we get the rolls back most recent first.
	expect := []*autoroll.AutoRollIssue{roll3, roll4, roll2, roll1}
	recent, err = d.db.GetRecentRolls(10)
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, recent, expect)

	// Ensure that we get a maximum of the number of rolls we requested.
	recent, err = d.db.GetRecentRolls(3)
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, recent, expect[:3])
}
// TestRecentRolls verifies that we correctly track mode history.
func TestRecentRolls(t *testing.T) {
	testutils.SkipIfShort(t)

	// Create the RecentRolls.
	tmpDir, err := ioutil.TempDir("", "test_autoroll_recent_")
	assert.Nil(t, err)
	defer func() {
		assert.Nil(t, os.RemoveAll(tmpDir))
	}()
	r, err := NewRecentRolls(path.Join(tmpDir, "test.db"))
	assert.Nil(t, err)
	defer func() {
		assert.Nil(t, r.Close())
	}()

	// Use this function for checking expectations.
	check := func(current, last *autoroll.AutoRollIssue, history []*autoroll.AutoRollIssue) {
		testutils.AssertDeepEqual(t, current, r.CurrentRoll())
		testutils.AssertDeepEqual(t, last, r.LastRoll())
		testutils.AssertDeepEqual(t, history, r.GetRecentRolls())
	}

	// Add one issue.
	now := time.Now().UTC()
	ari1 := &autoroll.AutoRollIssue{
		Closed:      false,
		Committed:   false,
		CommitQueue: true,
		Created:     now,
		Issue:       1010101,
		Modified:    now,
		Patchsets:   []int64{1},
		Result:      autoroll.ROLL_RESULT_IN_PROGRESS,
		Subject:     "FAKE DEPS ROLL 1",
		TryResults:  []*autoroll.TryResult{},
	}
	expect := []*autoroll.AutoRollIssue{ari1}
	assert.Nil(t, r.Add(ari1))
	check(ari1, nil, expect)

	// Try to add it again. Ensure that we throw an error.
	assert.Error(t, r.Add(ari1))
	check(ari1, nil, expect)

	// Close the issue as successful. Ensure that it's now the last roll
	// instead of the current roll.
	ari1.Closed = true
	ari1.Committed = true
	ari1.CommitQueue = false
	ari1.Result = autoroll.ROLL_RESULT_SUCCESS
	assert.Nil(t, r.Update(ari1))
	check(nil, ari1, expect)

	// Add another issue. Ensure that it's the current roll with the
	// previously-added roll as the last roll.
	now = time.Now().UTC()
	ari2 := &autoroll.AutoRollIssue{
		Closed:      false,
		Committed:   false,
		CommitQueue: true,
		Created:     now,
		Issue:       1010102,
		Modified:    now,
		Patchsets:   []int64{1},
		Result:      autoroll.ROLL_RESULT_IN_PROGRESS,
		Subject:     "FAKE DEPS ROLL 2",
		TryResults:  []*autoroll.TryResult{},
	}
	assert.Nil(t, r.Add(ari2))
	expect = []*autoroll.AutoRollIssue{ari2, ari1}
	check(ari2, ari1, expect)

	// Try to add another active issue. Ensure that the RecentRolls complains.
	now = time.Now().UTC()
	bad1 := &autoroll.AutoRollIssue{
		Closed:      false,
		Committed:   false,
		CommitQueue: true,
		Created:     now,
		Issue:       1010103,
		Modified:    now,
		Patchsets:   []int64{1},
		Result:      autoroll.ROLL_RESULT_IN_PROGRESS,
		Subject:     "FAKE DEPS ROLL 3",
		TryResults:  []*autoroll.TryResult{},
	}
	assert.Error(t, r.Add(bad1))

	// Close the issue as failed. Ensure that it's now the last roll
	// instead of the current roll.
	ari2.Closed = true
	ari2.Committed = false
	ari2.CommitQueue = false
	ari2.Result = autoroll.ROLL_RESULT_FAILURE
	assert.Nil(t, r.Update(ari2))
	check(nil, ari2, expect)

	// Try to add a bogus issue.
	now = time.Now().UTC()
	bad2 := &autoroll.AutoRollIssue{
		Closed:      false,
		Committed:   true,
		CommitQueue: true,
		Created:     now,
		Issue:       1010104,
		Modified:    now,
		Patchsets:   []int64{1},
		Result:      autoroll.ROLL_RESULT_FAILURE,
		Subject:     "FAKE DEPS ROLL 4",
		TryResults:  []*autoroll.TryResult{},
	}
	assert.Error(t, r.Add(bad2))

	// Add one more roll. Ensure that it's the current roll.
	now = time.Now().UTC()
	ari3 := &autoroll.AutoRollIssue{
		Closed:      false,
		Committed:   false,
		CommitQueue: true,
		Created:     now,
		Issue:       1010105,
		Modified:    now,
		Patchsets:   []int64{1},
		Result:      autoroll.ROLL_RESULT_IN_PROGRESS,
		Subject:     "FAKE DEPS ROLL 5",
		TryResults:  []*autoroll.TryResult{},
	}
	assert.Nil(t, r.Add(ari3))
	expect = []*autoroll.AutoRollIssue{ari3, ari2, ari1}
	check(ari3, ari2, expect)
}
// testCommitComments ensures that we properly handle builder comments.
func testCommitComments(t *testing.T, local bool) {
	testutils.SkipIfShort(t)
	d := clearDB(t, local)
	defer d.Close(t)

	c := "3e9eff3518fe26312c0e1f5bd5f49e17cf270d9a"
	u := "*****@*****.**"

	test := func(expect []*CommitComment) {
		comments, err := d.DB().GetCommitComments(c)
		assert.Nil(t, err)
		testutils.AssertDeepEqual(t, expect, comments)
	}

	// Check empty.
	test([]*CommitComment{})

	// Add a comment.
	c1 := &CommitComment{
		Commit:    c,
		User:      u,
		Timestamp: time.Now(),
		Message:   "Here's a message!",
	}
	assert.Nil(t, d.DB().PutCommitComment(c1))
	c1.Id = 1
	test([]*CommitComment{c1})

	// Ensure that we can't update a comment that doesn't exist.
	c2 := &CommitComment{
		Id:        30,
		Commit:    c,
		User:      u,
		Timestamp: time.Now(),
		Message:   "This comment doesn't exist, but it has an ID!",
	}
	assert.NotNil(t, d.DB().PutCommitComment(c2))
	test([]*CommitComment{c1})

	// Fix the second comment, insert it, and ensure that we get both comments back.
	c2.Id = 0
	assert.Nil(t, d.DB().PutCommitComment(c2))
	c2.Id = 2
	test([]*CommitComment{c1, c2})

	// Ensure that we don't get the two comments for a commit which has the first commit as a prefix.
	comments, err := d.DB().GetCommitComments("3e9eff3518fe26312c0e1f5bd5f49e17cf270d9asuffix")
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, []*CommitComment{}, comments)

	// Ensure that we don't get the two comments for a commit which is a prefix of the first commit.
	comments, err = d.DB().GetCommitComments("3e9eff3518fe26312c0e1f5bd5f49e17cf27")
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, []*CommitComment{}, comments)

	// Delete the first comment.
	assert.Nil(t, d.DB().DeleteCommitComment(c1.Id))
	test([]*CommitComment{c2})

	// Try to re-insert the first comment. Ensure that we can't.
	assert.NotNil(t, d.DB().PutCommitComment(c1))

	// Try to delete the no-longer-existing first comment.
	assert.NotNil(t, d.DB().DeleteCommitComment(c1.Id))
}
// testBuilderComments ensures that we properly handle builder comments.
func testBuilderComments(t *testing.T, local bool) {
	testutils.SkipIfShort(t)
	d := clearDB(t, local)
	defer d.Close(t)

	b := "Perf-Android-Venue8-PowerVR-x86-Release"
	u := "*****@*****.**"

	test := func(expect []*BuilderComment) {
		c, err := d.DB().GetBuilderComments(b)
		assert.Nil(t, err)
		testutils.AssertDeepEqual(t, expect, c)
	}

	// Check empty.
	test([]*BuilderComment{})

	// Add a comment.
	c1 := &BuilderComment{
		Builder:       b,
		User:          u,
		Timestamp:     time.Now(),
		Flaky:         true,
		IgnoreFailure: true,
		Message:       "Here's a message!",
	}
	assert.Nil(t, d.DB().PutBuilderComment(c1))
	c1.Id = 1
	test([]*BuilderComment{c1})

	// Ensure that we can't update a comment that doesn't exist.
	c2 := &BuilderComment{
		Id:            30,
		Builder:       b,
		User:          u,
		Timestamp:     time.Now(),
		Flaky:         false,
		IgnoreFailure: true,
		Message:       "This comment doesn't exist, but it has an ID!",
	}
	assert.NotNil(t, d.DB().PutBuilderComment(c2))
	test([]*BuilderComment{c1})

	// Fix the second comment, insert it, and ensure that we get both comments back.
	c2.Id = 0
	assert.Nil(t, d.DB().PutBuilderComment(c2))
	c2.Id = 2
	test([]*BuilderComment{c1, c2})

	// Ensure that we don't get the two comments for a bot which has the first bot as a prefix.
	c, err := d.DB().GetBuilderComments("Perf-Android-Venue8-PowerVR-x86-Release-Suffix")
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, []*BuilderComment{}, c)

	// Ensure that we don't get the two comments for a bot which is a prefix of the first bot.
	c, err = d.DB().GetBuilderComments("Perf-Android-Venue8-PowerVR-x86")
	assert.Nil(t, err)
	testutils.AssertDeepEqual(t, []*BuilderComment{}, c)

	// Delete the first comment.
	assert.Nil(t, d.DB().DeleteBuilderComment(c1.Id))
	test([]*BuilderComment{c2})

	// Try to re-insert the first comment. Ensure that we can't.
	assert.NotNil(t, d.DB().PutBuilderComment(c1))

	// Try to delete the no-longer-existing first comment.
	assert.NotNil(t, d.DB().DeleteBuilderComment(c1.Id))
}
// testFindCommitsForBuild verifies that findCommitsForBuild correctly obtains
// the list of commits which were newly built in a given build.
func testFindCommitsForBuild(t *testing.T, local bool) {
	testutils.SkipIfShort(t)
	httpClient = testHttpClient
	d := clearDB(t, local)
	defer d.Close(t)

	// Load the test repo.
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	repos := gitinfo.NewRepoMap(tr.Dir)

	// The test repo is laid out like this:
	//
	// *   06eb2a58139d3ff764f10232d5c8f9362d55e20f I (HEAD, master, Build #4)
	// *   ecb424466a4f3b040586a062c15ed58356f6590e F (Build #3)
	// |\
	// | * d30286d2254716d396073c177a754f9e152bbb52 H
	// | * 8d2d1247ef5d2b8a8d3394543df6c12a85881296 G (Build #2)
	// * | 67635e7015d74b06c00154f7061987f426349d9f E
	// * | 6d4811eddfa637fac0852c3a0801b773be1f260d D (Build #1)
	// * | d74dfd42a48325ab2f3d4a97278fc283036e0ea4 C (Build #6)
	// |/
	// *   4b822ebb7cedd90acbac6a45b897438746973a87 B (Build #0)
	// *   051955c355eb742550ddde4eccc3e90b6dc5b887 A
	//
	hashes := map[rune]string{
		'A': "051955c355eb742550ddde4eccc3e90b6dc5b887",
		'B': "4b822ebb7cedd90acbac6a45b897438746973a87",
		'C': "d74dfd42a48325ab2f3d4a97278fc283036e0ea4",
		'D': "6d4811eddfa637fac0852c3a0801b773be1f260d",
		'E': "67635e7015d74b06c00154f7061987f426349d9f",
		'F': "ecb424466a4f3b040586a062c15ed58356f6590e",
		'G': "8d2d1247ef5d2b8a8d3394543df6c12a85881296",
		'H': "d30286d2254716d396073c177a754f9e152bbb52",
		'I': "06eb2a58139d3ff764f10232d5c8f9362d55e20f",
	}

	// Test cases. Each test case builds on the previous cases.
	testCases := []struct {
		GotRevision string
		Expected    []string
		StoleFrom   int
		Stolen      []string
	}{
		// 0. The first build.
		{
			GotRevision: hashes['B'],
			Expected:    []string{hashes['A'], hashes['B']},
			StoleFrom:   -1,
			Stolen:      []string{},
		},
		// 1. On a linear set of commits, with at least one previous build.
		{
			GotRevision: hashes['D'],
			Expected:    []string{hashes['D'], hashes['C']},
			StoleFrom:   -1,
			Stolen:      []string{},
		},
		// 2. The first build on a new branch.
		{
			GotRevision: hashes['G'],
			Expected:    []string{hashes['G']},
			StoleFrom:   -1,
			Stolen:      []string{},
		},
		// 3. After a merge.
		{
			GotRevision: hashes['F'],
			Expected:    []string{hashes['E'], hashes['H'], hashes['F']},
			StoleFrom:   -1,
			Stolen:      []string{},
		},
		// 4. One last "normal" build.
		{
			GotRevision: hashes['I'],
			Expected:    []string{hashes['I']},
			StoleFrom:   -1,
			Stolen:      []string{},
		},
		// 5. No GotRevision.
		{
			GotRevision: "",
			Expected:    []string{},
			StoleFrom:   -1,
			Stolen:      []string{},
		},
		// 6. Steal commits from a previously-ingested build.
		{
			GotRevision: hashes['C'],
			Expected:    []string{hashes['C']},
			StoleFrom:   1,
			Stolen:      []string{hashes['C']},
		},
	}
	master := "client.skia"
	builder := "Test-Ubuntu12-ShuttleA-GTX660-x86-Release"
	for buildNum, tc := range testCases {
		build, err := getBuildFromMaster(master, builder, buildNum, repos)
		assert.Nil(t, err)
		// Sanity check. Make sure we have the GotRevision we expect.
		assert.Equal(t, tc.GotRevision, build.GotRevision)
		gotRevProp, err := build.GetStringProperty("got_revision")
		assert.Nil(t, err)
		assert.Equal(t, tc.GotRevision, gotRevProp)

		assert.Nil(t, IngestBuild(d.DB(), build, repos))

		ingested, err := d.DB().GetBuildFromDB(master, builder, buildNum)
		assert.Nil(t, err)
		assert.NotNil(t, ingested)

		// Double-check the inserted build's GotRevision.
		assert.Equal(t, tc.GotRevision, ingested.GotRevision)
		gotRevProp, err = ingested.GetStringProperty("got_revision")
		assert.Nil(t, err)
		assert.Equal(t, tc.GotRevision, gotRevProp)

		// Verify that we got the build (and commits list) we expect.
		build.Commits = tc.Expected
		testutils.AssertDeepEqual(t, build, ingested)

		// Ensure that we can search by commit to find the build we inserted.
		for _, c := range hashes {
			expectBuild := util.In(c, tc.Expected)
			builds, err := d.DB().GetBuildsForCommits([]string{c}, nil)
			assert.Nil(t, err)
			if expectBuild {
				// Assert that we get the build we inserted.
				assert.Equal(t, 1, len(builds))
				assert.Equal(t, 1, len(builds[c]))
				testutils.AssertDeepEqual(t, ingested, builds[c][0])
			} else {
				// Assert that we didn't get the build we inserted.
				for _, gotBuild := range builds[c] {
					assert.NotEqual(t, ingested.Id(), gotBuild.Id())
				}
			}

			n, err := d.DB().GetBuildNumberForCommit(build.Master, build.Builder, c)
			assert.Nil(t, err)
			if expectBuild {
				assert.Equal(t, buildNum, n)
			} else {
				assert.NotEqual(t, buildNum, n)
			}
		}
	}

	// Extra: ensure that build #6 really stole the commit from #1.
	b, err := d.DB().GetBuildFromDB(master, builder, 1)
	assert.Nil(t, err)
	assert.NotNil(t, b)
	assert.False(t, util.In(hashes['C'], b.Commits), fmt.Sprintf("Expected not to find %s in %v", hashes['C'], b.Commits))
}