// testUnfinishedBuild verifies that we can write a build which is not yet
// finished, load the build back from the database, and update it when it
// finishes.
func testUnfinishedBuild(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)

	// Obtain and insert an unfinished build.
	httpClient = testHttpClient
	b, err := getBuildFromMaster("client.skia", "Test-Ubuntu12-ShuttleA-GTX550Ti-x86_64-Release-Valgrind", 152, repos)
	assert.Nil(t, err)
	assert.False(t, b.IsFinished(), fmt.Errorf("Unfinished build thinks it's finished!"))
	dbSerializeAndCompare(t, b, true)

	// Ensure that the build is found by getUnfinishedBuilds.
	unfinished, err := getUnfinishedBuilds()
	assert.Nil(t, err)
	found := false
	for _, u := range unfinished {
		if u.Master == b.Master && u.Builder == b.Builder && u.Number == b.Number {
			found = true
			break
		}
	}
	assert.True(t, found, "Unfinished build was not found by getUnfinishedBuilds!")

	// Add another step to the build to "finish" it, ensure that we can
	// retrieve it as expected.
	b.Finished = b.Started + 1000
	b.Times[1] = b.Finished
	stepStarted := b.Started + 500
	s := &BuildStep{
		BuildID:    b.Id,
		Name:       "LastStep",
		Times:      []float64{stepStarted, b.Finished},
		Number:     len(b.Steps),
		Results:    0,
		ResultsRaw: []interface{}{0.0, []interface{}{}},
		Started:    b.Started + 500.0,
		Finished:   b.Finished,
	}
	b.Steps = append(b.Steps, s)
	assert.True(t, b.IsFinished(), "Finished build thinks it's unfinished!")
	dbSerializeAndCompare(t, b, true)

	// Ensure that the finished build is NOT found by getUnfinishedBuilds.
	unfinished, err = getUnfinishedBuilds()
	assert.Nil(t, err)
	found = false
	for _, u := range unfinished {
		if u.Master == b.Master && u.Builder == b.Builder && u.Number == b.Number {
			found = true
			break
		}
	}
	assert.False(t, found, "Finished build was found by getUnfinishedBuilds!")
}
// testBuildDbSerialization verifies that we can write a build to the DB and
// pull it back out without losing or corrupting the data.
func testBuildDbSerialization(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)

	// Test case: an empty build. Tests null and empty values.
	emptyTime := 0.0
	emptyBuild := &Build{
		Steps:   []*BuildStep{},
		Times:   []float64{emptyTime, emptyTime},
		Commits: []string{},
	}

	// Test case: a completely filled-out build.
	buildFromFullJson, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)

	testCases := []*Build{emptyBuild, buildFromFullJson}
	for _, b := range testCases {
		dbSerializeAndCompare(t, b, true)
	}
}
func testBuildQueue(t *testing.T, timeDecay24Hr float64, expectations []*buildQueueExpect, testInsert bool) {
	testutils.SkipIfShort(t)

	// Initialize the buildbot database.
	d := clearDB(t)
	defer d.Close(t)

	// Load the test repo.
	tr := util.NewTempRepo()
	defer tr.Cleanup()
	repos := gitinfo.NewRepoMap(tr.Dir)
	repo, err := repos.Repo(TEST_REPO)
	assert.Nil(t, err)
	assert.Nil(t, repos.Update())

	// Create the BuildQueue.
	q, err := NewBuildQueue(PERIOD_FOREVER, repos, DEFAULT_SCORE_THRESHOLD, timeDecay24Hr, []*regexp.Regexp{}, d.db)
	assert.Nil(t, err)

	// Fake time.Now()
	details, err := repo.Details(hashes['I'], false)
	assert.Nil(t, err)
	now := details.Timestamp.Add(1 * time.Hour)

	// Update the queue.
	assert.Nil(t, q.update(now))

	// Ensure that we get the expected BuildCandidate at each step. Insert
	// each BuildCandidate into the buildbot database to simulate actually
	// running builds.
	buildNum := 0
	for _, expected := range expectations {
		bc, err := q.Pop([]string{TEST_BUILDER})
		assert.Equal(t, expected.err, err)
		if err != nil {
			break
		}
		glog.Infof("\n%v\n%v", expected.bc, bc)
		assert.True(t, reflect.DeepEqual(expected.bc, bc))
		if testInsert || buildNum == 0 {
			// Actually insert a build, as if we're really using the scheduler.
			// Do this even if we're not testing insertion, because if we don't,
			// the queue won't know about this builder.
			b := &buildbot.Build{
				Builder:     bc.Builder,
				Master:      "fake",
				Number:      buildNum,
				BuildSlave:  "fake",
				Branch:      "master",
				GotRevision: bc.Commit,
				Repository:  TEST_REPO,
			}
			assert.Nil(t, buildbot.IngestBuild(d.db, b, repos))
			buildNum++
			assert.Nil(t, q.update(now))
		}
	}
}
// testUnfinishedBuild verifies that we can write a build which is not yet
// finished, load the build back from the database, and update it when it
// finishes.
func testUnfinishedBuild(t *testing.T, local bool) {
	testutils.SkipIfShort(t)
	d := clearDB(t, local)
	defer d.Close(t)

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

	repos := gitinfo.NewRepoMap(tr.Dir)

	// Obtain and insert an unfinished build.
	httpClient = testHttpClient
	b, err := getBuildFromMaster("client.skia", "Test-Ubuntu12-ShuttleA-GTX550Ti-x86_64-Release-Valgrind", 152, repos)
	assert.Nil(t, err)
	assert.False(t, b.IsFinished(), "Unfinished build thinks it's finished!")
	dbSerializeAndCompare(t, d, b, true)

	// Ensure that the build is found by GetUnfinishedBuilds.
	unfinished, err := d.DB().GetUnfinishedBuilds(b.Master)
	assert.Nil(t, err)
	found := false
	for _, u := range unfinished {
		if u.Master == b.Master && u.Builder == b.Builder && u.Number == b.Number {
			found = true
			break
		}
	}
	assert.True(t, found, "Unfinished build was not found by getUnfinishedBuilds!")

	// Add another step to the build to "finish" it, ensure that we can
	// retrieve it as expected.
	b.Finished = b.Started.Add(30 * time.Second)
	stepStarted := b.Started.Add(500 * time.Millisecond)
	s := &BuildStep{
		Name:     "LastStep",
		Number:   len(b.Steps),
		Results:  0,
		Started:  stepStarted,
		Finished: b.Finished,
	}
	b.Steps = append(b.Steps, s)
	assert.True(t, b.IsFinished(), "Finished build thinks it's unfinished!")
	dbSerializeAndCompare(t, d, b, true)

	// Ensure that the finished build is NOT found by getUnfinishedBuilds.
	unfinished, err = d.DB().GetUnfinishedBuilds(b.Master)
	assert.Nil(t, err)
	found = false
	for _, u := range unfinished {
		if u.Master == b.Master && u.Builder == b.Builder && u.Number == b.Number {
			found = true
			break
		}
	}
	assert.False(t, found, "Finished build was found by getUnfinishedBuilds!")
}
func TestBranchInfo(t *testing.T) {
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	r, err := NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, true)
	if err != nil {
		t.Fatal(err)
	}

	branches, err := r.GetBranches()
	assert.Nil(t, err)
	assert.Equal(t, 2, len(branches))

	// Make sure commits across all branches show up.
	commits := []string{
		"7a669cfa3f4cd3482a4fd03989f75efcc7595f7f",
		"8652a6df7dc8a7e6addee49f6ed3c2308e36bd18",
		"3f5a807d432ac232a952bbf223bc6952e4b49b2c",
	}
	found := r.From(time.Unix(1406721641, 0))
	assert.Equal(t, commits, found)

	// The timestamps of the three commits commits in the entire repository start
	// at timestamp 1406721642.
	testCases := []struct {
		commitHash string
		branchName string
		nBranches  int
	}{
		{
			commitHash: "8652a6df7dc8a7e6addee49f6ed3c2308e36bd18",
			branchName: "master",
			nBranches:  2,
		},
		{
			commitHash: "7a669cfa3f4cd3482a4fd03989f75efcc7595f7f",
			branchName: "master",
			nBranches:  2,
		},
		{
			commitHash: "3f5a807d432ac232a952bbf223bc6952e4b49b2c",
			branchName: "test-branch-1",
			nBranches:  1,
		},
	}

	for _, tc := range testCases {
		details, err := r.Details(tc.commitHash, true)
		assert.Nil(t, err)
		assert.True(t, details.Branches[tc.branchName])
		assert.Equal(t, tc.nBranches, len(details.Branches))
	}

}
func testBuildQueue(t *testing.T, timeDecay24Hr float64, expectations []*buildQueueExpect) {
	testutils.SkipIfShort(t)

	// Initialize the buildbot database.
	d := clearDB(t)
	defer d.Close(t)

	// Load the test repo.
	tr := util.NewTempRepo()
	defer tr.Cleanup()
	repos := gitinfo.NewRepoMap(tr.Dir)
	repo, err := repos.Repo("/skia.git")
	assert.Nil(t, err)
	assert.Nil(t, repos.Update())

	// Create the BuildQueue.
	q, err := NewBuildQueue(PERIOD_FOREVER, tr.Dir, DEFAULT_SCORE_THRESHOLD, timeDecay24Hr, []string{TEST_BUILDER})
	assert.Nil(t, err)

	// Fake time.Now()
	details, err := repo.Details(hashes['I'])
	assert.Nil(t, err)
	now := details.Timestamp.Add(1 * time.Hour)

	// Ensure that we get the expected BuildCandidate at each step. Insert
	// each BuildCandidate into the buildbot database to simulate actually
	// running builds.
	buildNum := 0
	for _, expected := range expectations {
		assert.Nil(t, q.update(now))

		bc, err := q.Pop(TEST_BUILDER)
		assert.Equal(t, expected.err, err)
		if err != nil {
			break
		}
		hash, err := repo.FullHash(bc.Commit)
		assert.Nil(t, err)
		bc.Commit = hash
		assert.True(t, reflect.DeepEqual(expected.bc, bc))
		b := &buildbot.Build{
			Builder:     bc.Builder,
			Master:      "fake",
			Number:      buildNum,
			BuildSlave:  "fake",
			Branch:      "master",
			GotRevision: bc.Commit,
			Repository:  TEST_REPO,
		}
		assert.Nil(t, buildbot.IngestBuild(b, repos))
		buildNum++
	}
}
func TestNumCommits(t *testing.T) {
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	r, err := NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, false)
	if err != nil {
		t.Fatal(err)
	}

	if got, want := r.NumCommits(), 2; got != want {
		t.Errorf("NumCommit wrong number: Got %v Want %v", got, want)
	}
}
func TestIngestCommits(t *testing.T) {
	// Get a known Git repo with 34 commits in it setup.
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	// Create a temporary place for a filetilestore.
	tileDir, err := ioutil.TempDir("", "skiaperf")
	if err != nil {
		t.Fatal("Failed to create testing Tile dir: ", err)
	}
	defer testutils.RemoveAll(t, tileDir)

	git, err := gitinfo.NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, false)
	if err != nil {
		glog.Fatalf("Failed loading Git info: %s\n", err)
	}

	// Construct an Ingestor and have it UpdateCommitInfo.
	i, err := ingester.NewIngester(git, tileDir, config.DATASET_NANO, NewNanoBenchIngester(), 1, time.Second, map[string]string{}, "", "")
	if err != nil {
		t.Fatal("Failed to create ingester:", err)
	}

	if err := i.UpdateCommitInfo(false); err != nil {
		t.Fatal("Failed to ingest commits:", err)
	}

	// Validate the generated Tiles.
	store := filetilestore.NewFileTileStore(tileDir, config.DATASET_NANO, 0)
	if !validator.ValidateDataset(store, false, false) {
		t.Error("Failed to validate the created Tiles:", err)
	}

	// Test TileTracker while were here.
	tt := ingester.NewTileTracker(store, i.HashToNumber())
	err = tt.Move("7a6fe813047d1a84107ef239e81f310f27861473")
	if err != nil {
		t.Fatal(err)
	}
	if got, want := tt.LastTileNum(), 2; got != want {
		t.Errorf("Move failed, wrong tile: Got %d Want %d", got, want)
	}
	err = tt.Move("87709bc360f35de52c2f2bc2fc70962fb234db2d")
	if err != nil {
		t.Fatal(err)
	}
	if got, want := tt.LastTileNum(), 2; got != want {
		t.Errorf("Move failed, wrong tile: Got %d Want %d", got, want)
	}
}
func TestShortList(t *testing.T) {
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	r, err := NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, false)
	if err != nil {
		t.Fatal(err)
	}

	l, err := r.ShortList("8652a6df7dc8a7e6addee49f6ed3c2308e36bd18", "8652a6df7dc8a7e6addee49f6ed3c2308e36bd18")
	if err != nil {
		t.Fatal(err)
	}

	if got, want := len(l.Commits), 0; got != want {
		t.Fatalf("Wrong number of zero results: Got %v Want %v", got, want)
	}

	c, err := r.ShortList("7a669cfa3f4cd3482a4fd03989f75efcc7595f7f", "8652a6df7dc8a7e6addee49f6ed3c2308e36bd18")
	if err != nil {
		t.Fatal(err)
	}

	expected := []struct {
		Hash    string
		Author  string
		Subject string
	}{
		{
			Hash:    "8652a6df7dc8a7e6addee49f6ed3c2308e36bd18",
			Author:  "Joe Gregorio",
			Subject: "Added code. No body.",
		},
	}
	if got, want := len(c.Commits), len(expected); got != want {
		t.Fatalf("Wrong number of results: Got %v Want %v", got, want)
	}
	for i, o := range c.Commits {
		if got, want := o.Hash, expected[i].Hash; got != want {
			t.Errorf("Wrong hash: Got %v Want %v", got, want)
		}
		if got, want := o.Author, expected[i].Author; got != want {
			t.Errorf("Wrong author: Got %v Want %v", got, want)
		}
		if got, want := o.Subject, expected[i].Subject; got != want {
			t.Errorf("Wrong subject: Got %v Want %v", got, want)
		}
	}
}
Example #10
0
func TestRevList(t *testing.T) {
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	r, err := NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, true)
	if err != nil {
		t.Fatal(err)
	}

	revs := []string{
		"8652a6df7dc8a7e6addee49f6ed3c2308e36bd18",
		"7a669cfa3f4cd3482a4fd03989f75efcc7595f7f",
	}
	testCases := []struct {
		Input    []string
		Expected []string
	}{
		{
			Input:    []string{"master"},
			Expected: revs,
		},
		{
			Input:    []string{"HEAD"},
			Expected: revs,
		},
		{
			Input:    []string{"7a669cf..8652a6d"},
			Expected: revs[1:],
		},
		{
			Input:    []string{"8652a6d", "^7a669cf"},
			Expected: revs[1:],
		},
		{
			Input:    []string{"8652a6d..7a669cf"},
			Expected: []string{},
		},
	}
	for _, tc := range testCases {
		actual, err := r.RevList(tc.Input...)
		if err != nil {
			t.Fatal(err)
		}
		if !util.SSliceEqual(actual, tc.Expected) {
			t.Fatalf("Failed test for: git rev-list %s\nGot:  %v\nWant: %v", strings.Join(tc.Input, " "), actual, tc.Expected)
		}
	}
}
// TestGetBuildFromMaster verifies that we can load JSON data from the build master and
// decode it into a Build object.
func TestGetBuildFromMaster(t *testing.T) {
	testutils.SkipIfShort(t)

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

	repos := gitinfo.NewRepoMap(tr.Dir)

	// Default, complete build.
	_, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)
	// Incomplete build.
	_, err = getBuildFromMaster("client.skia", "Test-Ubuntu12-ShuttleA-GTX550Ti-x86_64-Release-Valgrind", 152, repos)
	assert.Nil(t, err)
}
Example #12
0
func TestLog(t *testing.T) {
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	r, err := NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, false)
	if err != nil {
		t.Fatal(err)
	}

	got, err := r.Log("7a669cfa3f4cd3482a4fd03989f75efcc7595f7f", "")
	if err != nil {
		t.Fatal(err)
	}
	want := `commit 7a669cfa3f4cd3482a4fd03989f75efcc7595f7f
Author: Joe Gregorio <*****@*****.**>
Date:   Wed Jul 30 08:00:42 2014 -0400

    First "checkin"
    
    With quotes.

README.txt
`

	if got != want {
		t.Errorf("Log failed: \nGot  %q \nWant %q", got, want)
	}

	got, err = r.Log("7a669cfa3f4cd3482a4fd03989f75efcc7595f7f", "8652a6df7dc8a7e6addee49f6ed3c2308e36bd18")
	if err != nil {
		t.Fatal(err)
	}
	want = `commit 8652a6df7dc8a7e6addee49f6ed3c2308e36bd18
Author: Joe Gregorio <*****@*****.**>
Date:   Wed Jul 30 08:01:55 2014 -0400

    Added code. No body.

README.txt
hello.go
`

	if got != want {
		t.Errorf("Log failed: \nGot  %q \nWant %q", 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)
}
Example #14
0
func TestTileAddressFromHash(t *testing.T) {
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	r, err := NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, false)
	if err != nil {
		t.Fatal(err)
	}

	// The two commits in the repo have timestamps of:
	// 1406721715 and 1406721642.
	testCases := []struct {
		hash   string
		start  time.Time
		num    int
		offset int
	}{
		{
			hash:   "8652a6df7dc8a7e6addee49f6ed3c2308e36bd18",
			start:  time.Unix(1406721642, 0),
			num:    0,
			offset: 1,
		},
		{
			hash:   "8652a6df7dc8a7e6addee49f6ed3c2308e36bd18",
			start:  time.Unix(1406721643, 0),
			num:    0,
			offset: 0,
		},
	}

	for _, tc := range testCases {
		n, o, err := r.TileAddressFromHash(tc.hash, tc.start)
		if err != nil {
			t.Fatal(err)
		}
		if n != tc.num || o != tc.offset {
			t.Errorf("Address unexpected: got (%d, %d), want (%d, %d).", tc.num, tc.offset, n, o)
		}
	}
}
Example #15
0
func TestFrom(t *testing.T) {
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	r, err := NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, false)
	if err != nil {
		t.Fatal(err)
	}

	// The two commits in the repo have timestamps of:
	// 1406721715 and 1406721642.
	testCases := []struct {
		ts     int64
		length int
	}{
		{
			ts:     1406721715,
			length: 0,
		},
		{
			ts:     1406721714,
			length: 1,
		},
		{
			ts:     1406721642,
			length: 1,
		},
		{
			ts:     1406721641,
			length: 2,
		},
	}
	for _, tc := range testCases {
		hashes := r.From(time.Unix(tc.ts, 0))
		if got, want := len(hashes), tc.length; got != want {
			t.Errorf("For ts: %d Length returned is wrong: Got %d Want %d", tc.ts, got, want)
		}
	}
}
Example #16
0
func TestDisplay(t *testing.T) {
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	r, err := NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, false)
	if err != nil {
		t.Fatal(err)
	}
	testCases := []struct {
		hash    string
		author  string
		subject string
	}{
		{
			hash:    "7a669cfa3f4cd3482a4fd03989f75efcc7595f7f",
			author:  "Joe Gregorio ([email protected])",
			subject: "First \"checkin\"",
		},
		{
			hash:    "8652a6df7dc8a7e6addee49f6ed3c2308e36bd18",
			author:  "Joe Gregorio ([email protected])",
			subject: "Added code. No body.",
		},
	}
	for _, tc := range testCases {
		details, err := r.Details(tc.hash, true)
		if err != nil {
			t.Fatal(err)
		}
		if got, want := details.Author, tc.author; got != want {
			t.Errorf("Details author mismatch: Got %q, Want %q", got, want)
		}
		if got, want := details.Subject, tc.subject; got != want {
			t.Errorf("Details subject mismatch: Got %q, Want %q", got, want)
		}
	}
}
Example #17
0
func TestLastN(t *testing.T) {
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	r, err := NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, false)
	if err != nil {
		t.Fatal(err)
	}

	testCases := []struct {
		n      int
		values []string
	}{
		{
			n:      0,
			values: []string{},
		},
		{
			n:      1,
			values: []string{"8652a6df7dc8a7e6addee49f6ed3c2308e36bd18"},
		},
		{
			n:      2,
			values: []string{"7a669cfa3f4cd3482a4fd03989f75efcc7595f7f", "8652a6df7dc8a7e6addee49f6ed3c2308e36bd18"},
		},
		{
			n:      5,
			values: []string{"7a669cfa3f4cd3482a4fd03989f75efcc7595f7f", "8652a6df7dc8a7e6addee49f6ed3c2308e36bd18"},
		},
	}
	for _, tc := range testCases {
		if got, want := r.LastN(tc.n), tc.values; !util.SSliceEqual(got, want) {
			t.Errorf("For N: %d Hashes returned is wrong: Got %#v Want %#v", tc.n, got, want)
		}
	}
}
// testIngestNewBuilds verifies that we can successfully query the masters and
// the database for new and unfinished builds, respectively, and ingest them
// into the database.
func testIngestNewBuilds(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 needs to load a few builds.
	b1, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)
	b1.Master = "client.skia.android"
	b1.Builder = "Perf-Android-Venue8-PowerVR-x86-Release"
	b1.Number = 463
	b1.Steps = []*BuildStep{}
	assert.Nil(t, b1.ReplaceIntoDB())

	// This builder has no new builds, but the last one wasn't finished
	// at its time of ingestion.
	b2, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)
	b2.Master = "client.skia.fyi"
	b2.Builder = "Housekeeper-PerCommit"
	b2.Number = 1035
	b2.Finished = 0.0
	b2.Steps = []*BuildStep{}
	assert.Nil(t, b2.ReplaceIntoDB())

	// Subsequent builders are already up-to-date.
	b3, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)
	b3.Master = "client.skia.fyi"
	b3.Builder = "Housekeeper-Nightly-RecreateSKPs"
	b3.Number = 58
	b3.Steps = []*BuildStep{}
	assert.Nil(t, b3.ReplaceIntoDB())

	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())

	// IngestNewBuilds should process the above Venue8 Perf bot's builds
	// 464-466 as well as Housekeeper-PerCommit's unfinished build #1035.
	assert.Nil(t, ingestNewBuilds(repos))

	// Verify that the expected builds are now in the database.
	expected := []Build{
		Build{
			Master:  b1.Master,
			Builder: b1.Builder,
			Number:  464,
		},
		Build{
			Master:  b1.Master,
			Builder: b1.Builder,
			Number:  465,
		},
		Build{
			Master:  b1.Master,
			Builder: b1.Builder,
			Number:  466,
		},
		Build{
			Master:  b2.Master,
			Builder: b2.Builder,
			Number:  1035,
		},
	}
	for _, e := range expected {
		a, err := GetBuildFromDB(e.Builder, e.Master, e.Number)
		assert.Nil(t, err)
		if !(a.Master == e.Master && a.Builder == e.Builder && a.Number == e.Number) {
			t.Fatalf("Incorrect build was inserted!\n  %s == %s\n  %s == %s\n  %d == %d", a.Master, e.Master, a.Builder, e.Builder, a.Number, e.Number)
		}
		assert.True(t, a.IsFinished(), fmt.Sprintf("Failed to update build properly; it should be finished: %v", a))
	}
}
// 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)
}
// testLastProcessedBuilds verifies that getLastProcessedBuilds gives us
// the expected result.
func testLastProcessedBuilds(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)

	build, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)

	// Ensure that we get the right number for not-yet-processed
	// builder/master pair.
	builds, err := getLastProcessedBuilds()
	assert.Nil(t, err)
	if builds == nil || len(builds) != 0 {
		t.Fatal(fmt.Errorf("getLastProcessedBuilds returned an unacceptable value for no builds: %v", builds))
	}

	// Ensure that we get the right number for a single already-processed
	// builder/master pair.
	assert.Nil(t, build.ReplaceIntoDB())
	builds, err = getLastProcessedBuilds()
	assert.Nil(t, err)
	if builds == nil || len(builds) != 1 {
		t.Fatal(fmt.Errorf("getLastProcessedBuilds returned incorrect number of results: %v", builds))
	}
	if builds[0].Master != build.Master || builds[0].Builder != build.Builder || builds[0].Number != build.Number {
		t.Fatal(fmt.Errorf("getLastProcessedBuilds returned the wrong build: %v", builds[0]))
	}

	// Ensure that we get the correct result for multiple builders.
	build2, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)
	build2.Builder = "Other-Builder"
	build2.Number = build.Number + 10
	assert.Nil(t, build2.ReplaceIntoDB())
	builds, err = getLastProcessedBuilds()
	assert.Nil(t, err)
	compareBuildLists := func(expected []*Build, actual []*BuildID) bool {
		if len(expected) != len(actual) {
			return false
		}
		for _, e := range expected {
			found := false
			for _, a := range actual {
				if e.Builder == a.Builder && e.Master == a.Master && e.Number == a.Number {
					found = true
					break
				}
			}
			if !found {
				return false
			}
		}
		return true
	}
	assert.True(t, compareBuildLists([]*Build{build, build2}, builds), fmt.Sprintf("getLastProcessedBuilds returned incorrect results: %v", builds))

	// Add "older" build, ensure that only the newer ones are returned.
	build3, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)
	build3.Number -= 10
	assert.Nil(t, build3.ReplaceIntoDB())
	builds, err = getLastProcessedBuilds()
	assert.Nil(t, err)
	assert.True(t, compareBuildLists([]*Build{build, build2}, builds), fmt.Sprintf("getLastProcessedBuilds returned incorrect results: %v", builds))
}
// 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))
}
// testBuildDbIdConsistency verifies that we maintain IDs across build updates.
func testBuildDbIdConsistency(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)

	// Retrieve a full build.
	b, err := testGetBuildFromMaster(repos)
	assert.Nil(t, err)

	// Assert that all IDs are zero, since we haven't yet inserted the build.
	assert.Equal(t, 0, b.Id)
	for _, s := range b.Steps {
		assert.Equal(t, 0, s.Id)
	}

	// Insert the build for the first time.
	err = b.ReplaceIntoDB()
	assert.Nil(t, err)

	// Rename a step.
	b.Steps[2].Name = "differentName"
	dbSerializeAndCompare(t, b, false)

	// Remove a step.
	b.Steps = append(b.Steps[:2], b.Steps[3:]...)
	dbSerializeAndCompare(t, b, false)

	// Keep track of the original IDs, verify that IDs are non-zero.
	origBuildId := b.Id
	assert.NotEqual(t, 0, b.Id)
	origStepIds := make([]int, 0, len(b.Steps))
	for _, s := range b.Steps {
		assert.NotEqual(t, 0, s.Id)
		origStepIds = append(origStepIds, s.Id)
	}

	// Add some comments.
	b.Comments = append(b.Comments, &BuildComment{
		BuildId:   b.Id,
		User:      "******",
		Timestamp: float64(time.Now().UTC().Unix()),
		Message:   "Hi.",
	})

	// Insert the build again.
	err = b.ReplaceIntoDB()
	assert.Nil(t, err)

	// Assert that the IDs were maintained.
	assert.Equal(t, origBuildId, b.Id)
	for i, s := range b.Steps {
		assert.Equal(t, origStepIds[i], s.Id)
	}

	// Assert that our comment was given an ID, record it for posterity.
	origCommentId := b.Comments[0].Id
	assert.NotEqual(t, 0, origCommentId)

	// Add another comment.
	b.Comments = append(b.Comments, &BuildComment{
		BuildId:   b.Id,
		User:      "******",
		Timestamp: float64(time.Now().UTC().Unix()),
		Message:   "Here's another comment",
	})

	// Insert the build again.
	err = b.ReplaceIntoDB()
	assert.Nil(t, err)

	// Assert that the IDs were maintained.
	assert.Equal(t, origBuildId, b.Id)
	for i, s := range b.Steps {
		assert.Equal(t, origStepIds[i], s.Id)
	}
	assert.Equal(t, b.Comments[0].Id, origCommentId)
}
func TestBuildScoring(t *testing.T) {
	testutils.SkipIfShort(t)

	// Load the test repo.
	tr := util.NewTempRepo()
	defer tr.Cleanup()
	repos := gitinfo.NewRepoMap(tr.Dir)
	repo, err := repos.Repo(TEST_REPO)
	assert.Nil(t, err)
	assert.Nil(t, repos.Update())

	details := map[string]*vcsinfo.LongCommit{}
	for _, h := range hashes {
		d, err := repo.Details(h, false)
		assert.Nil(t, err)
		details[h] = d
	}

	now := details[hashes['I']].Timestamp.Add(1 * time.Hour)
	build1 := &buildbot.Build{
		GotRevision: hashes['A'],
		Commits:     []string{hashes['A'], hashes['B'], hashes['C']},
	}
	cases := []struct {
		commit        *vcsinfo.LongCommit
		build         *buildbot.Build
		expectedScore float64
		lambda        float64
	}{
		// Built at the given commit.
		{
			commit:        details[hashes['A']],
			build:         build1,
			expectedScore: 1.0,
			lambda:        lambda(1.0),
		},
		// Build included the commit.
		{
			commit:        details[hashes['B']],
			build:         build1,
			expectedScore: 1.0 / 3.0,
			lambda:        lambda(1.0),
		},
		// Build included the commit.
		{
			commit:        details[hashes['C']],
			build:         build1,
			expectedScore: 1.0 / 3.0,
			lambda:        lambda(1.0),
		},
		// Build did not include the commit.
		{
			commit:        details[hashes['D']],
			build:         build1,
			expectedScore: -1.0,
			lambda:        lambda(1.0),
		},
		// Build is nil.
		{
			commit:        details[hashes['A']],
			build:         nil,
			expectedScore: -1.0,
			lambda:        lambda(1.0),
		},
		// Same cases, but with lambda set to something interesting.
		// Built at the given commit.
		{
			commit:        details[hashes['A']],
			build:         build1,
			expectedScore: 0.958902488117383,
			lambda:        lambda(0.5),
		},
		// Build included the commit.
		{
			commit:        details[hashes['B']],
			build:         build1,
			expectedScore: 0.3228038362210165,
			lambda:        lambda(0.5),
		},
		// Build included the commit.
		{
			commit:        details[hashes['C']],
			build:         build1,
			expectedScore: 0.32299553133576475,
			lambda:        lambda(0.5),
		},
		// Build did not include the commit.
		{
			commit:        details[hashes['D']],
			build:         build1,
			expectedScore: -0.9690254634399716,
			lambda:        lambda(0.5),
		},
		// Build is nil.
		{
			commit:        details[hashes['A']],
			build:         nil,
			expectedScore: -0.958902488117383,
			lambda:        lambda(0.5),
		},
		// Same cases, but with an even more agressive lambda.
		// Built at the given commit.
		{
			commit:        details[hashes['A']],
			build:         build1,
			expectedScore: 0.756679619938755,
			lambda:        lambda(0.01),
		},
		// Build included the commit.
		{
			commit:        details[hashes['B']],
			build:         build1,
			expectedScore: 0.269316526502904,
			lambda:        lambda(0.01),
		},
		// Build included the commit.
		{
			commit:        details[hashes['C']],
			build:         build1,
			expectedScore: 0.2703808739655321,
			lambda:        lambda(0.01),
		},
		// Build did not include the commit.
		{
			commit:        details[hashes['D']],
			build:         build1,
			expectedScore: -0.8113588225688924,
			lambda:        lambda(0.01),
		},
		// Build is nil.
		{
			commit:        details[hashes['A']],
			build:         nil,
			expectedScore: -0.756679619938755,
			lambda:        lambda(0.01),
		},
	}
	for _, tc := range cases {
		assert.Equal(t, tc.expectedScore, scoreBuild(tc.commit, tc.build, now, tc.lambda))
	}
}
// TestFindCommitsForBuild verifies that findCommitsForBuild correctly obtains
// the list of commits which were newly built in a given build.
func TestFindCommitsForBuild(t *testing.T) {
	testutils.SkipIfShort(t)
	httpClient = testHttpClient
	d := clearDB(t)
	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['B'], hashes['A']},
			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['F'], hashes['E'], hashes['H']},
			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)
		assert.Nil(t, IngestBuild(build, repos))
		ingested, err := GetBuildFromDB(builder, master, buildNum)
		assert.Nil(t, err)
		assert.True(t, util.SSliceEqual(ingested.Commits, tc.Expected), fmt.Sprintf("Commits for build do not match expectation.\nGot:  %v\nWant: %v", ingested.Commits, tc.Expected))
	}

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

	// First spin up a traceservice server that we wil talk to.
	server, err := traceservice.NewTraceServiceServer(FILENAME)
	if err != nil {
		t.Fatalf("Failed to initialize the tracestore server: %s", err)
	}

	// Start the server on an open port.
	lis, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		t.Fatalf("failed to listen: %v", err)
	}
	port := lis.Addr().String()
	s := grpc.NewServer()
	traceservice.RegisterTraceServiceServer(s, server)
	go func() {
		t.Fatalf("Failed while serving: %s", s.Serve(lis))
	}()

	// Get a Git repo.
	tr := util.NewTempRepo()
	defer tr.Cleanup()

	// Set up a connection to the server.
	conn, err := grpc.Dial(port, grpc.WithInsecure())
	if err != nil {
		t.Fatalf("did not connect: %v", err)
	}
	ts, err := db.NewTraceServiceDB(conn, types.PerfTraceBuilder)
	if err != nil {
		t.Fatalf("Failed to create tracedb.DB: %s", err)
	}
	defer util.Close(ts)

	git, err := gitinfo.NewGitInfo(filepath.Join(tr.Dir, "testrepo"), false, false)
	if err != nil {
		t.Fatal(err)
	}
	hashes := git.LastN(50)
	assert.Equal(t, 50, len(hashes))

	// Populate the tracedb with some data.
	commitID := &db.CommitID{
		Timestamp: git.Timestamp(hashes[1]).Unix(),
		ID:        hashes[1],
		Source:    "master",
	}

	entries := map[string]*db.Entry{
		"key:8888:android": &db.Entry{
			Params: map[string]string{
				"config":   "8888",
				"platform": "android",
				"type":     "skp",
			},
			Value: types.BytesFromFloat64(0.01),
		},
		"key:gpu:win8": &db.Entry{
			Params: map[string]string{
				"config":   "gpu",
				"platform": "win8",
				"type":     "skp",
			},
			Value: types.BytesFromFloat64(1.234),
		},
	}

	err = ts.Add(commitID, entries)
	if err != nil {
		t.Fatalf("Failed to add data to traceservice: %s", err)
	}

	evt := eventbus.New(nil)
	traceDB, err := db.NewTraceServiceDBFromAddress(port, types.PerfTraceBuilder)
	assert.Nil(t, err)

	builder, err := db.NewMasterTileBuilder(traceDB, git, 50, evt)
	if err != nil {
		t.Fatalf("Failed to construct TraceStore: %s", err)
	}
	tile := builder.GetTile()
	assert.Equal(t, 50, len(tile.Commits))
	assert.Equal(t, 2, len(tile.Traces))
	assert.Equal(t, commitID.ID, tile.Commits[1].Hash)
	assert.Equal(t, "Joe Gregorio ([email protected])", tile.Commits[1].Author)

	ptrace := tile.Traces["key:8888:android"].(*types.PerfTrace)
	assert.Equal(t, 0.01, ptrace.Values[1])
	assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[0])

	ptrace = tile.Traces["key:gpu:win8"].(*types.PerfTrace)
	assert.Equal(t, 1.234, ptrace.Values[1])
	assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[0])

	// Now add more data to the tracestore, trigger LoadTile() and check the results.
	commitID = &db.CommitID{
		Timestamp: git.Timestamp(hashes[2]).Unix(),
		ID:        hashes[2],
		Source:    "master",
	}

	entries = map[string]*db.Entry{
		"key:8888:android": &db.Entry{
			Params: map[string]string{
				"config":   "8888",
				"platform": "android",
				"type":     "skp",
			},
			Value: types.BytesFromFloat64(0.02),
		},
		"key:565:ubuntu": &db.Entry{
			Params: map[string]string{
				"config":   "565",
				"platform": "ubuntu",
				"type":     "skp",
			},
			Value: types.BytesFromFloat64(2.345),
		},
	}

	err = ts.Add(commitID, entries)
	if err != nil {
		t.Fatalf("Failed to add data to traceservice: %s", err)
	}

	// Access the LoadTile method without exposing it in the MasterBuilder interface.
	type hasLoadTile interface {
		LoadTile() error
	}
	masterBuilder := builder.(hasLoadTile)
	if err := masterBuilder.LoadTile(); err != nil {
		t.Fatalf("Failed to force load Tile: %s", err)
	}

	tile = builder.GetTile()
	assert.Equal(t, 50, len(tile.Commits))
	assert.Equal(t, 3, len(tile.Traces))
	assert.Equal(t, commitID.ID, tile.Commits[2].Hash)

	ptrace = tile.Traces["key:8888:android"].(*types.PerfTrace)
	assert.Equal(t, 0.01, ptrace.Values[1])
	assert.Equal(t, 0.02, ptrace.Values[2])
	assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[0])
	assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[3])

	ptrace = tile.Traces["key:gpu:win8"].(*types.PerfTrace)
	assert.Equal(t, 1.234, ptrace.Values[1])
	assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[0])

	ptrace = tile.Traces["key:565:ubuntu"].(*types.PerfTrace)
	assert.Equal(t, 2.345, ptrace.Values[2])
	assert.Equal(t, config.MISSING_DATA_SENTINEL, ptrace.Values[3])
}