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

Unified Diff: build_scheduler/go/task_scheduler/task_scheduler_test.go

Issue 2296763008: [task scheduler] Move files from build_scheduler/ to task_scheduler/ (Closed) Base URL: https://skia.googlesource.com/buildbot@master
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: build_scheduler/go/task_scheduler/task_scheduler_test.go
diff --git a/build_scheduler/go/task_scheduler/task_scheduler_test.go b/build_scheduler/go/task_scheduler/task_scheduler_test.go
deleted file mode 100644
index ac5b934eaa9548dfe7b22f40b03ed72c0224c86f..0000000000000000000000000000000000000000
--- a/build_scheduler/go/task_scheduler/task_scheduler_test.go
+++ /dev/null
@@ -1,1393 +0,0 @@
-package task_scheduler
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "math"
- "os"
- "path"
- "path/filepath"
- "runtime"
- "sort"
- "strings"
- "testing"
- "time"
-
- swarming_api "github.com/luci/luci-go/common/api/swarming/swarming/v1"
- assert "github.com/stretchr/testify/require"
- "go.skia.org/infra/build_scheduler/go/db"
- "go.skia.org/infra/go/buildbot"
- "go.skia.org/infra/go/exec"
- "go.skia.org/infra/go/gitinfo"
- "go.skia.org/infra/go/gitrepo"
- "go.skia.org/infra/go/isolate"
- "go.skia.org/infra/go/swarming"
- "go.skia.org/infra/go/testutils"
- "go.skia.org/infra/go/util"
-)
-
-func makeTask(name, repo, revision string) *db.Task {
- return &db.Task{
- Commits: []string{revision},
- Created: time.Now(),
- Name: name,
- Repo: repo,
- Revision: revision,
- }
-}
-
-func makeSwarmingRpcsTaskRequestMetadata(t *testing.T, task *db.Task) *swarming_api.SwarmingRpcsTaskRequestMetadata {
- tag := func(k, v string) string {
- return fmt.Sprintf("%s:%s", k, v)
- }
- ts := func(t time.Time) string {
- return t.Format(swarming.TIMESTAMP_FORMAT)
- }
- abandoned := ""
- state := db.SWARMING_STATE_PENDING
- failed := false
- switch task.Status {
- case db.TASK_STATUS_MISHAP:
- state = db.SWARMING_STATE_BOT_DIED
- abandoned = ts(task.Finished)
- case db.TASK_STATUS_RUNNING:
- state = db.SWARMING_STATE_RUNNING
- case db.TASK_STATUS_FAILURE:
- state = db.SWARMING_STATE_COMPLETED
- failed = true
- case db.TASK_STATUS_SUCCESS:
- state = db.SWARMING_STATE_COMPLETED
- case db.TASK_STATUS_PENDING:
- // noop
- default:
- assert.FailNow(t, "Unknown task status: %s", task.Status)
- }
- return &swarming_api.SwarmingRpcsTaskRequestMetadata{
- Request: &swarming_api.SwarmingRpcsTaskRequest{},
- TaskId: task.SwarmingTaskId,
- TaskResult: &swarming_api.SwarmingRpcsTaskResult{
- AbandonedTs: abandoned,
- CreatedTs: ts(task.Created),
- CompletedTs: ts(task.Finished),
- Failure: failed,
- OutputsRef: &swarming_api.SwarmingRpcsFilesRef{
- Isolated: "???",
- },
- StartedTs: ts(task.Started),
- State: state,
- Tags: []string{
- tag(db.SWARMING_TAG_ID, task.Id),
- tag(db.SWARMING_TAG_NAME, task.Name),
- tag(db.SWARMING_TAG_REPO, task.Repo),
- tag(db.SWARMING_TAG_REVISION, task.Revision),
- },
- TaskId: task.SwarmingTaskId,
- },
- }
-}
-
-func TestFindTaskCandidates(t *testing.T) {
- testutils.SkipIfShort(t)
-
- // Setup.
- tr := util.NewTempRepo()
- defer tr.Cleanup()
- d := db.NewInMemoryDB()
- cache, err := db.NewTaskCache(d, time.Hour)
- assert.NoError(t, err)
-
- // The test repo has two commits. The first commit adds a tasks.cfg file
- // with two task specs: a build task and a test task, the test task
- // depending on the build task. The second commit adds a perf task spec,
- // which also depends on the build task. Therefore, there are five total
- // possible tasks we could run:
- //
- // Build@c1, Test@c1, Build@c2, Test@c2, Perf@c2
- //
- c1 := "c06ac6093d3029dffe997e9d85e8e61fee5f87b9"
- c2 := "0f87799ac791b8d8573e93694d05b05a65e09668"
- buildTask := "Build-Ubuntu-GCC-Arm7-Release-Android"
- testTask := "Test-Android-GCC-Nexus7-GPU-Tegra3-Arm7-Release"
- perfTask := "Perf-Android-GCC-Nexus7-GPU-Tegra3-Arm7-Release"
- repo := "skia.git"
- commits := map[string][]string{
- repo: []string{c1, c2},
- }
-
- assert.NoError(t, err)
- isolateClient, err := isolate.NewClient(tr.Dir)
- assert.NoError(t, err)
- isolateClient.ServerUrl = isolate.FAKE_SERVER_URL
- swarmingClient := swarming.NewTestClient()
- s, err := NewTaskScheduler(d, cache, time.Duration(math.MaxInt64), tr.Dir, []string{"skia.git"}, isolateClient, swarmingClient)
- assert.NoError(t, err)
-
- // Check the initial set of task candidates. The two Build tasks
- // should be the only ones available.
- c, err := s.findTaskCandidates(commits)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(c))
- key := fmt.Sprintf("%s|%s", repo, buildTask)
- assert.Equal(t, 2, len(c[key]))
- for _, candidate := range c[key] {
- assert.Equal(t, candidate.Name, buildTask)
- }
-
- // Insert a the Build task at c1 (1 dependent) into the database,
- // transition through various states.
- var t1 *db.Task
- for _, candidates := range c { // Order not guaranteed; find the right candidate.
- for _, candidate := range candidates {
- if candidate.Revision == c1 {
- t1 = makeTask(candidate.Name, candidate.Repo, candidate.Revision)
- break
- }
- }
- }
- assert.NotNil(t, t1)
-
- // We shouldn't duplicate pending or running tasks.
- for _, status := range []db.TaskStatus{db.TASK_STATUS_PENDING, db.TASK_STATUS_RUNNING} {
- t1.Status = status
- assert.NoError(t, d.PutTask(t1))
- assert.NoError(t, cache.Update())
-
- c, err = s.findTaskCandidates(commits)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(c))
- for _, candidates := range c {
- for _, candidate := range candidates {
- assert.Equal(t, candidate.Name, buildTask)
- assert.Equal(t, c2, candidate.Revision)
- }
- }
- }
-
- // The task failed. Ensure that its dependents are not candidates, but
- // the task itself is back in the list of candidates, in case we want
- // to retry.
- t1.Status = db.TASK_STATUS_FAILURE
- assert.NoError(t, d.PutTask(t1))
- assert.NoError(t, cache.Update())
-
- c, err = s.findTaskCandidates(commits)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(c))
- for _, candidates := range c {
- assert.Equal(t, 2, len(candidates))
- for _, candidate := range candidates {
- assert.Equal(t, candidate.Name, buildTask)
- }
- }
-
- // The task succeeded. Ensure that its dependents are candidates and
- // the task itself is not.
- t1.Status = db.TASK_STATUS_SUCCESS
- t1.IsolatedOutput = "fake isolated hash"
- assert.NoError(t, d.PutTask(t1))
- assert.NoError(t, cache.Update())
-
- c, err = s.findTaskCandidates(commits)
- assert.NoError(t, err)
- assert.Equal(t, 2, len(c))
- for _, candidates := range c {
- for _, candidate := range candidates {
- assert.False(t, t1.Name == candidate.Name && t1.Revision == candidate.Revision)
- }
- }
-
- // Create the other Build task.
- var t2 *db.Task
- for _, candidates := range c {
- for _, candidate := range candidates {
- if candidate.Revision == c2 && strings.HasPrefix(candidate.Name, "Build-") {
- t2 = makeTask(candidate.Name, candidate.Repo, candidate.Revision)
- break
- }
- }
- }
- assert.NotNil(t, t2)
- t2.Status = db.TASK_STATUS_SUCCESS
- t2.IsolatedOutput = "fake isolated hash"
- assert.NoError(t, d.PutTask(t2))
- assert.NoError(t, cache.Update())
-
- // All test and perf tasks are now candidates, no build tasks.
- c, err = s.findTaskCandidates(commits)
- assert.NoError(t, err)
- assert.Equal(t, 2, len(c))
- assert.Equal(t, 2, len(c[fmt.Sprintf("%s|%s", repo, testTask)]))
- assert.Equal(t, 1, len(c[fmt.Sprintf("%s|%s", repo, perfTask)]))
- for _, candidates := range c {
- for _, candidate := range candidates {
- assert.NotEqual(t, candidate.Name, buildTask)
- }
- }
-}
-
-func TestTestedness(t *testing.T) {
- tc := []struct {
- in int
- out float64
- }{
- {
- in: -1,
- out: -1.0,
- },
- {
- in: 0,
- out: 0.0,
- },
- {
- in: 1,
- out: 1.0,
- },
- {
- in: 2,
- out: 1.0 + 1.0/2.0,
- },
- {
- in: 3,
- out: 1.0 + float64(2.0)/float64(3.0),
- },
- {
- in: 4,
- out: 1.0 + 3.0/4.0,
- },
- {
- in: 4096,
- out: 1.0 + float64(4095)/float64(4096),
- },
- }
- for i, c := range tc {
- assert.Equal(t, c.out, testedness(c.in), fmt.Sprintf("test case #%d", i))
- }
-}
-
-func TestTestednessIncrease(t *testing.T) {
- tc := []struct {
- a int
- b int
- out float64
- }{
- // Invalid cases.
- {
- a: -1,
- b: 10,
- out: -1.0,
- },
- {
- a: 10,
- b: -1,
- out: -1.0,
- },
- {
- a: 0,
- b: -1,
- out: -1.0,
- },
- {
- a: 0,
- b: 0,
- out: -1.0,
- },
- // Invalid because if we're re-running at already-tested commits
- // then we should have a blamelist which is at most the size of
- // the blamelist of the previous task. We naturally get negative
- // testedness increase in these cases.
- {
- a: 2,
- b: 1,
- out: -0.5,
- },
- // Testing only new commits.
- {
- a: 1,
- b: 0,
- out: 1.0 + 1.0,
- },
- {
- a: 2,
- b: 0,
- out: 2.0 + (1.0 + 1.0/2.0),
- },
- {
- a: 3,
- b: 0,
- out: 3.0 + (1.0 + float64(2.0)/float64(3.0)),
- },
- {
- a: 4096,
- b: 0,
- out: 4096.0 + (1.0 + float64(4095.0)/float64(4096.0)),
- },
- // Retries.
- {
- a: 1,
- b: 1,
- out: 0.0,
- },
- {
- a: 2,
- b: 2,
- out: 0.0,
- },
- {
- a: 3,
- b: 3,
- out: 0.0,
- },
- {
- a: 4096,
- b: 4096,
- out: 0.0,
- },
- // Bisect/backfills.
- {
- a: 1,
- b: 2,
- out: 0.5, // (1 + 1) - (1 + 1/2)
- },
- {
- a: 1,
- b: 3,
- out: float64(2.5) - (1.0 + float64(2.0)/float64(3.0)),
- },
- {
- a: 5,
- b: 10,
- out: 2.0*(1.0+float64(4.0)/float64(5.0)) - (1.0 + float64(9.0)/float64(10.0)),
- },
- }
- for i, c := range tc {
- assert.Equal(t, c.out, testednessIncrease(c.a, c.b), fmt.Sprintf("test case #%d", i))
- }
-}
-
-func TestComputeBlamelist(t *testing.T) {
- testutils.SkipIfShort(t)
-
- // Setup.
- _, filename, _, _ := runtime.Caller(0)
- // Use the test repo from the buildbot package, since it's already set
- // up for this type of test.
- zipfile := filepath.Join(filepath.Dir(filename), "..", "..", "..", "go", "buildbot", "testdata", "testrepo.zip")
- tr := util.NewTempRepoFrom(zipfile)
- defer tr.Cleanup()
- d := db.NewInMemoryDB()
- cache, err := db.NewTaskCache(d, time.Hour)
- assert.NoError(t, err)
-
- // The test repo is laid out like this:
- //
- // * 06eb2a58139d3ff764f10232d5c8f9362d55e20f I (HEAD, master, Task #4)
- // * ecb424466a4f3b040586a062c15ed58356f6590e F (Task #3)
- // |\
- // | * d30286d2254716d396073c177a754f9e152bbb52 H
- // | * 8d2d1247ef5d2b8a8d3394543df6c12a85881296 G (Task #2)
- // * | 67635e7015d74b06c00154f7061987f426349d9f E
- // * | 6d4811eddfa637fac0852c3a0801b773be1f260d D (Task #1)
- // * | d74dfd42a48325ab2f3d4a97278fc283036e0ea4 C (Task #6)
- // |/
- // * 4b822ebb7cedd90acbac6a45b897438746973a87 B (Task #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 {
- Revision string
- Expected []string
- StoleFromIdx int
- }{
- // 0. The first task.
- {
- Revision: hashes['B'],
- Expected: []string{hashes['B']}, // Task #0 is limited to a single commit.
- StoleFromIdx: -1,
- },
- // 1. On a linear set of commits, with at least one previous task.
- {
- Revision: hashes['D'],
- Expected: []string{hashes['D'], hashes['C']},
- StoleFromIdx: -1,
- },
- // 2. The first task on a new branch.
- {
- Revision: hashes['G'],
- Expected: []string{hashes['G']},
- StoleFromIdx: -1,
- },
- // 3. After a merge.
- {
- Revision: hashes['F'],
- Expected: []string{hashes['E'], hashes['H'], hashes['F']},
- StoleFromIdx: -1,
- },
- // 4. One last "normal" task.
- {
- Revision: hashes['I'],
- Expected: []string{hashes['I']},
- StoleFromIdx: -1,
- },
- // 5. No Revision.
- {
- Revision: "",
- Expected: []string{},
- StoleFromIdx: -1,
- },
- // 6. Steal commits from a previously-ingested task.
- {
- Revision: hashes['C'],
- Expected: []string{hashes['C']},
- StoleFromIdx: 1,
- },
- }
- name := "Test-Ubuntu12-ShuttleA-GTX660-x86-Release"
- repoName := "skia.git"
- repo, err := gitrepo.NewRepo(repoName, path.Join(tr.Dir, repoName))
- assert.NoError(t, err)
- ids := make([]string, len(testCases))
- commitsBuf := make([]*gitrepo.Commit, 0, buildbot.MAX_BLAMELIST_COMMITS)
- for i, tc := range testCases {
- // Ensure that we get the expected blamelist.
- commits, stoleFrom, err := ComputeBlamelist(cache, repo, name, repoName, tc.Revision, commitsBuf)
- if tc.Revision == "" {
- assert.Error(t, err)
- continue
- } else {
- assert.NoError(t, err)
- }
- sort.Strings(commits)
- testutils.AssertDeepEqual(t, tc.Expected, commits)
- if tc.StoleFromIdx >= 0 {
- assert.NotNil(t, stoleFrom)
- assert.Equal(t, ids[tc.StoleFromIdx], stoleFrom.Id)
- } else {
- assert.Nil(t, stoleFrom)
- }
-
- // Insert the task into the DB.
- c := &taskCandidate{
- Name: name,
- Repo: repoName,
- Revision: tc.Revision,
- }
- task := c.MakeTask()
- task.Commits = commits
- task.Created = time.Now()
- if stoleFrom != nil {
- // Re-insert the stoleFrom task without the commits
- // which were stolen from it.
- stoleFromCommits := make([]string, 0, len(stoleFrom.Commits)-len(commits))
- for _, commit := range stoleFrom.Commits {
- if !util.In(commit, task.Commits) {
- stoleFromCommits = append(stoleFromCommits, commit)
- }
- }
- stoleFrom.Commits = stoleFromCommits
- assert.NoError(t, d.PutTasks([]*db.Task{task, stoleFrom}))
- } else {
- assert.NoError(t, d.PutTask(task))
- }
- ids[i] = task.Id
- assert.NoError(t, cache.Update())
- }
-
- // Extra: ensure that task #6 really stole the commit from #1.
- task, err := cache.GetTask(ids[1])
- assert.NoError(t, err)
- assert.False(t, util.In(hashes['C'], task.Commits), fmt.Sprintf("Expected not to find %s in %v", hashes['C'], task.Commits))
-}
-
-func TestTimeDecay24Hr(t *testing.T) {
- tc := []struct {
- decayAmt24Hr float64
- elapsed time.Duration
- out float64
- }{
- {
- decayAmt24Hr: 1.0,
- elapsed: 10 * time.Hour,
- out: 1.0,
- },
- {
- decayAmt24Hr: 0.5,
- elapsed: 0 * time.Hour,
- out: 1.0,
- },
- {
- decayAmt24Hr: 0.5,
- elapsed: 24 * time.Hour,
- out: 0.5,
- },
- {
- decayAmt24Hr: 0.5,
- elapsed: 12 * time.Hour,
- out: 0.75,
- },
- {
- decayAmt24Hr: 0.5,
- elapsed: 36 * time.Hour,
- out: 0.25,
- },
- {
- decayAmt24Hr: 0.5,
- elapsed: 48 * time.Hour,
- out: 0.0,
- },
- {
- decayAmt24Hr: 0.5,
- elapsed: 72 * time.Hour,
- out: 0.0,
- },
- }
- for i, c := range tc {
- assert.Equal(t, c.out, timeDecay24Hr(c.decayAmt24Hr, c.elapsed), fmt.Sprintf("test case #%d", i))
- }
-}
-
-func TestRegenerateTaskQueue(t *testing.T) {
- testutils.SkipIfShort(t)
-
- // Setup.
- tr := util.NewTempRepo()
- defer tr.Cleanup()
- d := db.NewInMemoryDB()
- cache, err := db.NewTaskCache(d, time.Hour)
- assert.NoError(t, err)
-
- // The test repo has two commits. The first commit adds a tasks.cfg file
- // with two task specs: a build task and a test task, the test task
- // depending on the build task. The second commit adds a perf task spec,
- // which also depends on the build task. Therefore, there are five total
- // possible tasks we could run:
- //
- // Build@c1, Test@c1, Build@c2, Test@c2, Perf@c2
- //
- c1 := "c06ac6093d3029dffe997e9d85e8e61fee5f87b9"
- c2 := "0f87799ac791b8d8573e93694d05b05a65e09668"
- buildTask := "Build-Ubuntu-GCC-Arm7-Release-Android"
- testTask := "Test-Android-GCC-Nexus7-GPU-Tegra3-Arm7-Release"
- perfTask := "Perf-Android-GCC-Nexus7-GPU-Tegra3-Arm7-Release"
- repoName := "skia.git"
-
- assert.NoError(t, err)
- isolateClient, err := isolate.NewClient(tr.Dir)
- assert.NoError(t, err)
- isolateClient.ServerUrl = isolate.FAKE_SERVER_URL
- swarmingClient := swarming.NewTestClient()
- s, err := NewTaskScheduler(d, cache, time.Duration(math.MaxInt64), tr.Dir, []string{repoName}, isolateClient, swarmingClient)
- assert.NoError(t, err)
-
- // Ensure that the queue is initially empty.
- assert.Equal(t, 0, len(s.queue))
-
- // Regenerate the task queue.
- assert.NoError(t, s.regenerateTaskQueue())
- assert.Equal(t, 2, len(s.queue)) // Two Build tasks.
-
- testSort := func() {
- // Ensure that we sorted correctly.
- if len(s.queue) == 0 {
- return
- }
- highScore := s.queue[0].Score
- for _, c := range s.queue {
- assert.True(t, highScore >= c.Score)
- highScore = c.Score
- }
- }
- testSort()
-
- // Since we haven't run any task yet, we should have the two Build
- // tasks, each with a blamelist of 1 commit (since we don't go past
- // taskCandidate.Revision when computing blamelists when we haven't run
- // a given task spec before), and a score of 2.0.
- for _, c := range s.queue {
- assert.Equal(t, buildTask, c.Name)
- assert.Equal(t, []string{c.Revision}, c.Commits)
- assert.Equal(t, 2.0, c.Score)
- }
-
- // Insert one of the tasks.
- var t1 *db.Task
- for _, c := range s.queue { // Order not guaranteed; find the right candidate.
- if c.Revision == c1 {
- t1 = makeTask(c.Name, c.Repo, c.Revision)
- break
- }
- }
- assert.NotNil(t, t1)
- t1.Status = db.TASK_STATUS_SUCCESS
- t1.IsolatedOutput = "fake isolated hash"
- assert.NoError(t, d.PutTask(t1))
- assert.NoError(t, cache.Update())
-
- // Regenerate the task queue.
- assert.NoError(t, s.regenerateTaskQueue())
-
- // Now we expect the queue to contain the other Build task and the one
- // Test task we unblocked by running the first Build task.
- assert.Equal(t, 2, len(s.queue))
- testSort()
- for _, c := range s.queue {
- assert.Equal(t, 2.0, c.Score)
- assert.Equal(t, []string{c.Revision}, c.Commits)
- }
- buildIdx := 0
- testIdx := 1
- if s.queue[1].Name == buildTask {
- buildIdx = 1
- testIdx = 0
- }
- assert.Equal(t, buildTask, s.queue[buildIdx].Name)
- assert.Equal(t, c2, s.queue[buildIdx].Revision)
-
- assert.Equal(t, testTask, s.queue[testIdx].Name)
- assert.Equal(t, c1, s.queue[testIdx].Revision)
-
- // Run the other Build task.
- t2 := makeTask(s.queue[buildIdx].Name, s.queue[buildIdx].Repo, s.queue[buildIdx].Revision)
- t2.Status = db.TASK_STATUS_SUCCESS
- t2.IsolatedOutput = "fake isolated hash"
- assert.NoError(t, d.PutTask(t2))
- assert.NoError(t, cache.Update())
-
- // Regenerate the task queue.
- assert.NoError(t, s.regenerateTaskQueue())
- assert.Equal(t, 3, len(s.queue))
- testSort()
- perfIdx := -1
- for i, c := range s.queue {
- if c.Name == perfTask {
- perfIdx = i
- } else {
- assert.Equal(t, c.Name, testTask)
- }
- assert.Equal(t, 2.0, c.Score)
- assert.Equal(t, []string{c.Revision}, c.Commits)
- }
- assert.True(t, perfIdx > -1)
-
- // Run the Test task at tip of tree, but make its blamelist cover both
- // commits.
- t3 := makeTask(testTask, repoName, c2)
- t3.Commits = append(t3.Commits, c1)
- t3.Status = db.TASK_STATUS_SUCCESS
- t3.IsolatedOutput = "fake isolated hash"
- assert.NoError(t, d.PutTask(t3))
- assert.NoError(t, cache.Update())
-
- // Regenerate the task queue.
- assert.NoError(t, s.regenerateTaskQueue())
-
- // Now we expect the queue to contain one Test and one Perf task. The
- // Test task is a backfill, and should have a score of 0.5.
- assert.Equal(t, 2, len(s.queue))
- testSort()
- // First candidate should be the perf task.
- assert.Equal(t, perfTask, s.queue[0].Name)
- assert.Equal(t, 2.0, s.queue[0].Score)
- // The test task is next, a backfill.
- assert.Equal(t, testTask, s.queue[1].Name)
- assert.Equal(t, 0.5, s.queue[1].Score)
-}
-
-func makeTaskCandidate(name string, dims []string) *taskCandidate {
- return &taskCandidate{
- Name: name,
- TaskSpec: &TaskSpec{
- Dimensions: dims,
- },
- }
-}
-
-func makeSwarmingBot(id string, dims []string) *swarming_api.SwarmingRpcsBotInfo {
- d := make([]*swarming_api.SwarmingRpcsStringListPair, 0, len(dims))
- for _, s := range dims {
- split := strings.SplitN(s, ":", 2)
- d = append(d, &swarming_api.SwarmingRpcsStringListPair{
- Key: split[0],
- Value: []string{split[1]},
- })
- }
- return &swarming_api.SwarmingRpcsBotInfo{
- BotId: id,
- Dimensions: d,
- }
-}
-
-func TestGetCandidatesToSchedule(t *testing.T) {
- // Empty lists.
- rv := getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{}, []*taskCandidate{})
- assert.Equal(t, 0, len(rv))
-
- t1 := makeTaskCandidate("task1", []string{"k:v"})
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{}, []*taskCandidate{t1})
- assert.Equal(t, 0, len(rv))
-
- b1 := makeSwarmingBot("bot1", []string{"k:v"})
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b1}, []*taskCandidate{})
- assert.Equal(t, 0, len(rv))
-
- // Single match.
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b1}, []*taskCandidate{t1})
- testutils.AssertDeepEqual(t, []*taskCandidate{t1}, rv)
-
- // No match.
- t1.TaskSpec.Dimensions[0] = "k:v2"
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b1}, []*taskCandidate{t1})
- assert.Equal(t, 0, len(rv))
-
- // Add a task candidate to match b1.
- t1 = makeTaskCandidate("task1", []string{"k:v2"})
- t2 := makeTaskCandidate("task2", []string{"k:v"})
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b1}, []*taskCandidate{t1, t2})
- testutils.AssertDeepEqual(t, []*taskCandidate{t2}, rv)
-
- // Switch the task order.
- t1 = makeTaskCandidate("task1", []string{"k:v2"})
- t2 = makeTaskCandidate("task2", []string{"k:v"})
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b1}, []*taskCandidate{t2, t1})
- testutils.AssertDeepEqual(t, []*taskCandidate{t2}, rv)
-
- // Make both tasks match the bot, ensure that we pick the first one.
- t1 = makeTaskCandidate("task1", []string{"k:v"})
- t2 = makeTaskCandidate("task2", []string{"k:v"})
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b1}, []*taskCandidate{t1, t2})
- testutils.AssertDeepEqual(t, []*taskCandidate{t1}, rv)
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b1}, []*taskCandidate{t2, t1})
- testutils.AssertDeepEqual(t, []*taskCandidate{t2}, rv)
-
- // Multiple dimensions. Ensure that different permutations of the bots
- // and tasks lists give us the expected results.
- dims := []string{"k:v", "k2:v2", "k3:v3"}
- b1 = makeSwarmingBot("bot1", dims)
- b2 := makeSwarmingBot("bot2", t1.TaskSpec.Dimensions)
- t1 = makeTaskCandidate("task1", []string{"k:v"})
- t2 = makeTaskCandidate("task2", dims)
- // In the first two cases, the task with fewer dimensions has the
- // higher priority. It gets the bot with more dimensions because it
- // is first in sorted order. The second task does not get scheduled
- // because there is no bot available which can run it.
- // TODO(borenet): Use a more optimal solution to avoid this case.
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b1, b2}, []*taskCandidate{t1, t2})
- testutils.AssertDeepEqual(t, []*taskCandidate{t1}, rv)
- t1 = makeTaskCandidate("task1", []string{"k:v"})
- t2 = makeTaskCandidate("task2", dims)
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b2, b1}, []*taskCandidate{t1, t2})
- testutils.AssertDeepEqual(t, []*taskCandidate{t1}, rv)
- // In these two cases, the task with more dimensions has the higher
- // priority. Both tasks get scheduled.
- t1 = makeTaskCandidate("task1", []string{"k:v"})
- t2 = makeTaskCandidate("task2", dims)
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b1, b2}, []*taskCandidate{t2, t1})
- testutils.AssertDeepEqual(t, []*taskCandidate{t2, t1}, rv)
- t1 = makeTaskCandidate("task1", []string{"k:v"})
- t2 = makeTaskCandidate("task2", dims)
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b2, b1}, []*taskCandidate{t2, t1})
- testutils.AssertDeepEqual(t, []*taskCandidate{t2, t1}, rv)
-
- // Matching dimensions. More bots than tasks.
- b2 = makeSwarmingBot("bot2", dims)
- b3 := makeSwarmingBot("bot3", dims)
- t1 = makeTaskCandidate("task1", dims)
- t2 = makeTaskCandidate("task2", dims)
- t3 := makeTaskCandidate("task3", dims)
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b1, b2, b3}, []*taskCandidate{t1, t2})
- testutils.AssertDeepEqual(t, []*taskCandidate{t1, t2}, rv)
-
- // More tasks than bots.
- t1 = makeTaskCandidate("task1", dims)
- t2 = makeTaskCandidate("task2", dims)
- t3 = makeTaskCandidate("task3", dims)
- rv = getCandidatesToSchedule([]*swarming_api.SwarmingRpcsBotInfo{b1, b2}, []*taskCandidate{t1, t2, t3})
- testutils.AssertDeepEqual(t, []*taskCandidate{t1, t2}, rv)
-}
-
-func makeBot(id string, dims map[string]string) *swarming_api.SwarmingRpcsBotInfo {
- dimensions := make([]*swarming_api.SwarmingRpcsStringListPair, 0, len(dims))
- for k, v := range dims {
- dimensions = append(dimensions, &swarming_api.SwarmingRpcsStringListPair{
- Key: k,
- Value: []string{v},
- })
- }
- return &swarming_api.SwarmingRpcsBotInfo{
- BotId: id,
- Dimensions: dimensions,
- }
-}
-
-func TestSchedulingE2E(t *testing.T) {
- testutils.SkipIfShort(t)
-
- // Setup.
- tr := util.NewTempRepo()
- defer tr.Cleanup()
- d := db.NewInMemoryDB()
- cache, err := db.NewTaskCache(d, time.Hour)
- assert.NoError(t, err)
-
- // The test repo has two commits. The first commit adds a tasks.cfg file
- // with two task specs: a build task and a test task, the test task
- // depending on the build task. The second commit adds a perf task spec,
- // which also depends on the build task. Therefore, there are five total
- // possible tasks we could run:
- //
- // Build@c1, Test@c1, Build@c2, Test@c2, Perf@c2
- //
- c1 := "c06ac6093d3029dffe997e9d85e8e61fee5f87b9"
- c2 := "0f87799ac791b8d8573e93694d05b05a65e09668"
-
- repoName := "skia.git"
-
- isolateClient, err := isolate.NewClient(tr.Dir)
- assert.NoError(t, err)
- isolateClient.ServerUrl = isolate.FAKE_SERVER_URL
- swarmingClient := swarming.NewTestClient()
- s, err := NewTaskScheduler(d, cache, time.Duration(math.MaxInt64), tr.Dir, []string{repoName}, isolateClient, swarmingClient)
-
- // Start testing. No free bots, so we get a full queue with nothing
- // scheduled.
- assert.NoError(t, s.MainLoop())
- tasks, err := cache.GetTasksForCommits(repoName, []string{c1, c2})
- assert.NoError(t, err)
- expect := map[string]map[string]*db.Task{
- c1: map[string]*db.Task{},
- c2: map[string]*db.Task{},
- }
- testutils.AssertDeepEqual(t, expect, tasks)
-
- // A bot is free but doesn't have all of the right dimensions to run a task.
- bot1 := makeBot("bot1", map[string]string{"pool": "Skia"})
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1})
- assert.NoError(t, s.MainLoop())
- tasks, err = cache.GetTasksForCommits(repoName, []string{c1, c2})
- assert.NoError(t, err)
- expect = map[string]map[string]*db.Task{
- c1: map[string]*db.Task{},
- c2: map[string]*db.Task{},
- }
- testutils.AssertDeepEqual(t, expect, tasks)
- assert.Equal(t, 2, len(s.queue))
-
- // One bot free, schedule a task, ensure it's not in the queue.
- bot1.Dimensions = append(bot1.Dimensions, &swarming_api.SwarmingRpcsStringListPair{
- Key: "os",
- Value: []string{"Ubuntu"},
- })
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1})
- assert.NoError(t, s.MainLoop())
- tasks, err = cache.GetTasksForCommits(repoName, []string{c1, c2})
- assert.NoError(t, err)
- var t1 *db.Task
- for _, v := range tasks {
- for _, t := range v {
- t1 = t
- break
- }
- }
- assert.NotNil(t, t1)
- assert.Equal(t, 1, len(s.queue))
-
- // The task is complete.
- t1.Status = db.TASK_STATUS_SUCCESS
- t1.Finished = time.Now()
- t1.IsolatedOutput = "abc123"
- assert.NoError(t, d.PutTask(t1))
- swarmingClient.MockTasks([]*swarming_api.SwarmingRpcsTaskRequestMetadata{
- makeSwarmingRpcsTaskRequestMetadata(t, t1),
- })
-
- // No bots free. Ensure that the queue is correct.
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{})
- assert.NoError(t, s.MainLoop())
- tasks, err = cache.GetTasksForCommits(repoName, []string{c1, c2})
- assert.NoError(t, err)
- // The tests don't use any time-based score scaling, because the commits
- // in the test repo have fixed timestamps and would eventually result in
- // zero scores. The side effect is that we don't know which of c1 or c2
- // will be chosen because they end up with the same score.
- expectLen := 2 // One remaining build task, plus one test task.
- if t1.Revision == c2 {
- expectLen = 3 // c2 adds a perf task.
- }
- assert.Equal(t, expectLen, len(s.queue))
-
- // More bots than tasks free, ensure the queue is correct.
- bot2 := makeBot("bot2", map[string]string{
- "pool": "Skia",
- "os": "Android",
- "device_type": "grouper",
- })
- bot3 := makeBot("bot3", map[string]string{
- "pool": "Skia",
- "os": "Android",
- "device_type": "grouper",
- })
- bot4 := makeBot("bot4", map[string]string{
- "pool": "Skia",
- "os": "Ubuntu",
- })
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1, bot2, bot3, bot4})
- assert.NoError(t, s.MainLoop())
- _, err = cache.GetTasksForCommits(repoName, []string{c1, c2})
- assert.NoError(t, err)
- assert.Equal(t, 0, len(s.queue))
-
- // Second compile task finished.
- var t2 *db.Task
- var t3 *db.Task
- var t4 *db.Task
- tasks, err = cache.GetTasksForCommits(repoName, []string{c1, c2})
- assert.NoError(t, err)
- for _, v := range tasks {
- for _, task := range v {
- if task.Name == t1.Name {
- if (t1.Revision == c1 && task.Revision == c2) || (t1.Revision == c2 && task.Revision == c1) {
- t2 = task
- }
- } else {
- if t3 == nil {
- t3 = task
- } else {
- t4 = task
- }
- }
- }
- }
- assert.NotNil(t, t2)
- assert.NotNil(t, t3)
- t2.Status = db.TASK_STATUS_SUCCESS
- t2.Finished = time.Now()
- t2.IsolatedOutput = "abc123"
-
- // No new bots free; ensure that the newly-available tasks are in the queue.
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{})
- mockTasks := []*swarming_api.SwarmingRpcsTaskRequestMetadata{
- makeSwarmingRpcsTaskRequestMetadata(t, t2),
- makeSwarmingRpcsTaskRequestMetadata(t, t3),
- }
- if t4 != nil {
- mockTasks = append(mockTasks, makeSwarmingRpcsTaskRequestMetadata(t, t4))
- }
- swarmingClient.MockTasks(mockTasks)
- assert.NoError(t, s.MainLoop())
- expectLen = 1 // Test task from c1
- if t2.Revision == c2 {
- expectLen = 2 // Test and perf tasks from c2
- }
- assert.Equal(t, expectLen, len(s.queue))
-
- // Finish the other tasks.
- t3, err = cache.GetTask(t3.Id)
- assert.NoError(t, err)
- t3.Status = db.TASK_STATUS_SUCCESS
- t3.Finished = time.Now()
- t3.IsolatedOutput = "abc123"
- if t4 != nil {
- t4, err = cache.GetTask(t4.Id)
- assert.NoError(t, err)
- t4.Status = db.TASK_STATUS_SUCCESS
- t4.Finished = time.Now()
- t4.IsolatedOutput = "abc123"
- }
-
- // Ensure that we finally run all of the tasks and insert into the DB.
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1, bot2, bot3, bot4})
- mockTasks = []*swarming_api.SwarmingRpcsTaskRequestMetadata{
- makeSwarmingRpcsTaskRequestMetadata(t, t3),
- }
- if t4 != nil {
- mockTasks = append(mockTasks, makeSwarmingRpcsTaskRequestMetadata(t, t4))
- }
- assert.NoError(t, s.MainLoop())
- tasks, err = cache.GetTasksForCommits(repoName, []string{c1, c2})
- assert.NoError(t, err)
- assert.Equal(t, 2, len(tasks[c1]))
- assert.Equal(t, 3, len(tasks[c2]))
- assert.Equal(t, 0, len(s.queue))
-
- // Mark everything as finished. Ensure that the queue still ends up empty.
- tasksList := []*db.Task{}
- for _, v := range tasks {
- for _, task := range v {
- if task.Status != db.TASK_STATUS_SUCCESS {
- task.Status = db.TASK_STATUS_SUCCESS
- task.Finished = time.Now()
- task.IsolatedOutput = "abc123"
- tasksList = append(tasksList, task)
- }
- }
- }
- mockTasks = make([]*swarming_api.SwarmingRpcsTaskRequestMetadata, 0, len(tasksList))
- for _, task := range tasksList {
- mockTasks = append(mockTasks, makeSwarmingRpcsTaskRequestMetadata(t, task))
- }
- swarmingClient.MockTasks(mockTasks)
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1, bot2, bot3, bot4})
- assert.NoError(t, s.MainLoop())
- assert.Equal(t, 0, len(s.queue))
-}
-
-func makeDummyCommits(t *testing.T, repoDir string, numCommits int) {
- _, err := exec.RunCwd(repoDir, "git", "config", "user.email", "test@skia.org")
- assert.NoError(t, err)
- _, err = exec.RunCwd(repoDir, "git", "config", "user.name", "Skia Tester")
- assert.NoError(t, err)
- _, err = exec.RunCwd(repoDir, "git", "checkout", "master")
- assert.NoError(t, err)
- dummyFile := path.Join(repoDir, "dummyfile.txt")
- for i := 0; i < numCommits; i++ {
- title := fmt.Sprintf("Dummy #%d", i)
- assert.NoError(t, ioutil.WriteFile(dummyFile, []byte(title), os.ModePerm))
- _, err = exec.RunCwd(repoDir, "git", "add", dummyFile)
- assert.NoError(t, err)
- _, err = exec.RunCwd(repoDir, "git", "commit", "-m", title)
- assert.NoError(t, err)
- _, err = exec.RunCwd(repoDir, "git", "push", "origin", "master")
- assert.NoError(t, err)
- }
-}
-
-func TestSchedulerStealingFrom(t *testing.T) {
- testutils.SkipIfShort(t)
-
- // Setup.
- tr := util.NewTempRepo()
- d := db.NewInMemoryDB()
- cache, err := db.NewTaskCache(d, time.Hour)
- assert.NoError(t, err)
-
- // The test repo has two commits. The first commit adds a tasks.cfg file
- // with two task specs: a build task and a test task, the test task
- // depending on the build task. The second commit adds a perf task spec,
- // which also depends on the build task. Therefore, there are five total
- // possible tasks we could run:
- //
- // Build@c1, Test@c1, Build@c2, Test@c2, Perf@c2
- //
- c1 := "c06ac6093d3029dffe997e9d85e8e61fee5f87b9"
- c2 := "0f87799ac791b8d8573e93694d05b05a65e09668"
- buildTask := "Build-Ubuntu-GCC-Arm7-Release-Android"
- repoName := "skia.git"
- repoDir := path.Join(tr.Dir, repoName)
-
- repos := gitinfo.NewRepoMap(tr.Dir)
- repo, err := repos.Repo(repoName)
- assert.NoError(t, err)
- isolateClient, err := isolate.NewClient(tr.Dir)
- assert.NoError(t, err)
- isolateClient.ServerUrl = isolate.FAKE_SERVER_URL
- swarmingClient := swarming.NewTestClient()
- s, err := NewTaskScheduler(d, cache, time.Duration(math.MaxInt64), tr.Dir, []string{"skia.git"}, isolateClient, swarmingClient)
- assert.NoError(t, err)
-
- // Run both available compile tasks.
- bot1 := makeBot("bot1", map[string]string{"pool": "Skia", "os": "Ubuntu"})
- bot2 := makeBot("bot2", map[string]string{"pool": "Skia", "os": "Ubuntu"})
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1, bot2})
- assert.NoError(t, s.MainLoop())
- tasks, err := cache.GetTasksForCommits(repoName, []string{c1, c2})
- assert.NoError(t, err)
- assert.Equal(t, 1, len(tasks[c1]))
- assert.Equal(t, 1, len(tasks[c2]))
- tasksList := []*db.Task{}
- for _, v := range tasks {
- for _, task := range v {
- if task.Status != db.TASK_STATUS_SUCCESS {
- task.Status = db.TASK_STATUS_SUCCESS
- task.Finished = time.Now()
- task.IsolatedOutput = "abc123"
- tasksList = append(tasksList, task)
- }
- }
- }
- assert.NoError(t, d.PutTasks(tasksList))
- assert.NoError(t, cache.Update())
-
- // Add some commits.
- makeDummyCommits(t, repoDir, 10)
- commits, err := repo.RevList("HEAD")
- assert.NoError(t, err)
-
- // Run one task. Ensure that it's at tip-of-tree.
- head, err := repo.FullHash("HEAD")
- assert.NoError(t, err)
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1})
- assert.NoError(t, s.MainLoop())
- tasks, err = cache.GetTasksForCommits(repoName, commits)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(tasks[head]))
- task := tasks[head][buildTask]
- assert.Equal(t, head, task.Revision)
- expect := commits[:len(commits)-2]
- sort.Strings(expect)
- sort.Strings(task.Commits)
- testutils.AssertDeepEqual(t, expect, task.Commits)
-
- task.Status = db.TASK_STATUS_SUCCESS
- task.Finished = time.Now()
- task.IsolatedOutput = "abc123"
- assert.NoError(t, d.PutTask(task))
- assert.NoError(t, cache.Update())
-
- oldTasksByCommit := tasks
-
- // Run backfills, ensuring that each one steals the right set of commits
- // from previous builds, until all of the build task candidates have run.
- for i := 0; i < 9; i++ {
- // Now, run another task. The new task should bisect the old one.
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1})
- assert.NoError(t, s.MainLoop())
- tasks, err = cache.GetTasksForCommits(repoName, commits)
- assert.NoError(t, err)
- var newTask *db.Task
- for _, v := range tasks {
- for _, task := range v {
- if task.Status == db.TASK_STATUS_PENDING {
- assert.True(t, newTask == nil || task.Id == newTask.Id)
- newTask = task
- }
- }
- }
- assert.NotNil(t, newTask)
-
- oldTask := oldTasksByCommit[newTask.Revision][newTask.Name]
- assert.NotNil(t, oldTask)
- assert.True(t, util.In(newTask.Revision, oldTask.Commits))
-
- // Find the updated old task.
- updatedOldTask, err := cache.GetTask(oldTask.Id)
- assert.NoError(t, err)
- assert.NotNil(t, updatedOldTask)
-
- // Ensure that the blamelists are correct.
- old := util.NewStringSet(oldTask.Commits)
- new := util.NewStringSet(newTask.Commits)
- updatedOld := util.NewStringSet(updatedOldTask.Commits)
-
- testutils.AssertDeepEqual(t, old, new.Union(updatedOld))
- assert.Equal(t, 0, len(new.Intersect(updatedOld)))
- // Finish the new task.
- newTask.Status = db.TASK_STATUS_SUCCESS
- newTask.Finished = time.Now()
- newTask.IsolatedOutput = "abc123"
- assert.NoError(t, d.PutTask(newTask))
- assert.NoError(t, cache.Update())
- oldTasksByCommit = tasks
-
- }
-
- // Ensure that we're really done.
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1})
- assert.NoError(t, s.MainLoop())
- tasks, err = cache.GetTasksForCommits(repoName, commits)
- assert.NoError(t, err)
- var newTask *db.Task
- for _, v := range tasks {
- for _, task := range v {
- if task.Status == db.TASK_STATUS_PENDING {
- assert.True(t, newTask == nil || task.Id == newTask.Id)
- newTask = task
- }
- }
- }
- assert.Nil(t, newTask)
-}
-
-func TestMultipleCandidatesBackfillingEachOther(t *testing.T) {
- testutils.SkipIfShort(t)
-
- workdir, err := ioutil.TempDir("", "")
- assert.NoError(t, err)
- defer testutils.RemoveAll(t, workdir)
-
- run := func(dir string, cmd ...string) {
- _, err := exec.RunCwd(dir, cmd...)
- assert.NoError(t, err)
- }
-
- addFile := func(repoDir, subPath, contents string) {
- assert.NoError(t, ioutil.WriteFile(path.Join(repoDir, subPath), []byte(contents), os.ModePerm))
- run(repoDir, "git", "add", subPath)
- }
-
- repoName := "skia.git"
- repoDir := path.Join(workdir, repoName)
-
- assert.NoError(t, ioutil.WriteFile(path.Join(workdir, ".gclient"), []byte("dummy"), os.ModePerm))
-
- assert.NoError(t, os.Mkdir(path.Join(workdir, repoName), os.ModePerm))
- run(repoDir, "git", "init")
- run(repoDir, "git", "remote", "add", "origin", ".")
-
- infraBotsSubDir := path.Join("infra", "bots")
- infraBotsDir := path.Join(repoDir, infraBotsSubDir)
- assert.NoError(t, os.MkdirAll(infraBotsDir, os.ModePerm))
-
- addFile(repoDir, "somefile.txt", "dummy3")
- addFile(repoDir, path.Join(infraBotsSubDir, "dummy.isolate"), `{
- 'variables': {
- 'command': [
- 'python', 'recipes.py', 'run',
- ],
- 'files': [
- '../../somefile.txt',
- ],
- },
-}`)
-
- // Create a single task in the config.
- taskName := "dummytask"
- cfg := &TasksCfg{
- Tasks: map[string]*TaskSpec{
- taskName: &TaskSpec{
- CipdPackages: []*CipdPackage{},
- Dependencies: []string{},
- Dimensions: []string{"pool:Skia"},
- Isolate: "dummy.isolate",
- Priority: 1.0,
- },
- },
- }
- f, err := os.Create(path.Join(repoDir, TASKS_CFG_FILE))
- assert.NoError(t, err)
- assert.NoError(t, json.NewEncoder(f).Encode(&cfg))
- assert.NoError(t, f.Close())
- run(repoDir, "git", "add", TASKS_CFG_FILE)
- run(repoDir, "git", "commit", "-m", "Add more tasks!")
- run(repoDir, "git", "push", "origin", "master")
- run(repoDir, "git", "branch", "-u", "origin/master")
-
- // Setup the scheduler.
- repos := gitinfo.NewRepoMap(workdir)
- repo, err := repos.Repo(repoName)
- assert.NoError(t, err)
- d := db.NewInMemoryDB()
- cache, err := db.NewTaskCache(d, time.Hour)
- assert.NoError(t, err)
- isolateClient, err := isolate.NewClient(workdir)
- assert.NoError(t, err)
- isolateClient.ServerUrl = isolate.FAKE_SERVER_URL
- swarmingClient := swarming.NewTestClient()
- s, err := NewTaskScheduler(d, cache, time.Duration(math.MaxInt64), workdir, []string{repoName}, isolateClient, swarmingClient)
- assert.NoError(t, err)
-
- mockTasks := []*swarming_api.SwarmingRpcsTaskRequestMetadata{}
- mock := func(task *db.Task) {
- mockTasks = append(mockTasks, makeSwarmingRpcsTaskRequestMetadata(t, task))
- swarmingClient.MockTasks(mockTasks)
- }
-
- // Cycle once.
- bot1 := makeBot("bot1", map[string]string{"pool": "Skia"})
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1})
- assert.NoError(t, s.MainLoop())
- assert.Equal(t, 0, len(s.queue))
- head, err := repo.FullHash("HEAD")
- assert.NoError(t, err)
- tasks, err := cache.GetTasksForCommits(repoName, []string{head})
- assert.NoError(t, err)
- assert.Equal(t, 1, len(tasks[head]))
- mock(tasks[head][taskName])
-
- // Add some commits to the repo.
- makeDummyCommits(t, repoDir, 8)
- commits, err := repo.RevList(fmt.Sprintf("%s..HEAD", head))
- assert.Nil(t, err)
- assert.Equal(t, 8, len(commits))
-
- // Trigger builds simultaneously.
- bot2 := makeBot("bot2", map[string]string{"pool": "Skia"})
- bot3 := makeBot("bot3", map[string]string{"pool": "Skia"})
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1, bot2, bot3})
- assert.NoError(t, s.MainLoop())
- assert.Equal(t, 5, len(s.queue))
- tasks, err = cache.GetTasksForCommits(repoName, commits)
- assert.NoError(t, err)
-
- // If we're queueing correctly, we should've triggered tasks at
- // commits[0], commits[4], and either commits[2] or commits[6].
- var t1, t2, t3 *db.Task
- for _, byName := range tasks {
- for _, task := range byName {
- if task.Revision == commits[0] {
- t1 = task
- } else if task.Revision == commits[4] {
- t2 = task
- } else if task.Revision == commits[2] || task.Revision == commits[6] {
- t3 = task
- } else {
- assert.FailNow(t, fmt.Sprintf("Task has unknown revision: %v", task))
- }
- }
- }
- assert.NotNil(t, t1)
- assert.NotNil(t, t2)
- assert.NotNil(t, t3)
- mock(t1)
- mock(t2)
- mock(t3)
-
- // Ensure that we got the blamelists right.
- mkCopy := func(orig []string) []string {
- rv := make([]string, len(orig))
- copy(rv, orig)
- return rv
- }
- var expect1, expect2, expect3 []string
- if t3.Revision == commits[2] {
- expect1 = mkCopy(commits[:2])
- expect2 = mkCopy(commits[4:])
- expect3 = mkCopy(commits[2:4])
- } else {
- expect1 = mkCopy(commits[:4])
- expect2 = mkCopy(commits[4:6])
- expect3 = mkCopy(commits[6:])
- }
- sort.Strings(expect1)
- sort.Strings(expect2)
- sort.Strings(expect3)
- sort.Strings(t1.Commits)
- sort.Strings(t2.Commits)
- sort.Strings(t3.Commits)
- testutils.AssertDeepEqual(t, expect1, t1.Commits)
- testutils.AssertDeepEqual(t, expect2, t2.Commits)
- testutils.AssertDeepEqual(t, expect3, t3.Commits)
-
- // Just for good measure, check the task at the head of the queue.
- expectIdx := 2
- if t3.Revision == commits[expectIdx] {
- expectIdx = 6
- }
- assert.Equal(t, commits[expectIdx], s.queue[0].Revision)
-
- // Run again with 5 bots to check the case where we bisect the same
- // task twice.
- bot4 := makeBot("bot4", map[string]string{"pool": "Skia"})
- bot5 := makeBot("bot5", map[string]string{"pool": "Skia"})
- swarmingClient.MockBots([]*swarming_api.SwarmingRpcsBotInfo{bot1, bot2, bot3, bot4, bot5})
- assert.NoError(t, s.MainLoop())
- assert.Equal(t, 0, len(s.queue))
- tasks, err = cache.GetTasksForCommits(repoName, commits)
- assert.NoError(t, err)
- for _, byName := range tasks {
- for _, task := range byName {
- assert.Equal(t, 1, len(task.Commits))
- }
- }
-}
« no previous file with comments | « build_scheduler/go/task_scheduler/task_scheduler.go ('k') | build_scheduler/go/task_scheduler/testdata/testrepo.zip » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698