| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build go1.13 |
| // +build linux darwin |
| |
| package main |
| |
| import ( |
| "bytes" |
| "io/ioutil" |
| "log" |
| "net/http" |
| "net/http/httptest" |
| "reflect" |
| "strings" |
| "testing" |
| "time" |
| |
| "golang.org/x/build/buildenv" |
| "golang.org/x/build/dashboard" |
| "golang.org/x/build/internal/buildgo" |
| "golang.org/x/build/internal/coordinator/pool" |
| "golang.org/x/build/maintner/maintnerd/apipb" |
| ) |
| |
| type Seconds float64 |
| |
| func (s Seconds) Duration() time.Duration { |
| return time.Duration(float64(s) * float64(time.Second)) |
| } |
| |
| var fixedTestDuration = map[string]Seconds{ |
| "go_test:a": 1, |
| "go_test:b": 1.5, |
| "go_test:c": 2, |
| "go_test:d": 2.50, |
| "go_test:e": 3, |
| "go_test:f": 3.5, |
| "go_test:g": 4, |
| "go_test:h": 4.5, |
| "go_test:i": 5, |
| "go_test:j": 5.5, |
| "go_test:k": 6.5, |
| } |
| |
| func TestPartitionGoTests(t *testing.T) { |
| var in []string |
| for name := range fixedTestDuration { |
| in = append(in, name) |
| } |
| testDuration := func(builder, testName string) time.Duration { |
| if s, ok := fixedTestDuration[testName]; ok { |
| return s.Duration() |
| } |
| return 3 * time.Second |
| } |
| sets := partitionGoTests(testDuration, "", in) |
| want := [][]string{ |
| {"go_test:a", "go_test:b", "go_test:c", "go_test:d", "go_test:e"}, |
| {"go_test:f", "go_test:g"}, |
| {"go_test:h", "go_test:i"}, |
| {"go_test:j"}, |
| {"go_test:k"}, |
| } |
| if !reflect.DeepEqual(sets, want) { |
| t.Errorf(" got: %v\nwant: %v", sets, want) |
| } |
| } |
| |
| func TestTryStatusJSON(t *testing.T) { |
| testCases := []struct { |
| desc string |
| method string |
| ts *trySet |
| tss trySetState |
| status int |
| body string |
| }{ |
| { |
| "pre-flight CORS header", |
| "OPTIONS", |
| nil, |
| trySetState{}, |
| http.StatusOK, |
| ``, |
| }, |
| { |
| "nil trySet", |
| "GET", |
| nil, |
| trySetState{}, |
| http.StatusNotFound, |
| `{"success":false,"error":"TryBot result not found (already done, invalid, or not yet discovered from Gerrit). Check Gerrit for results."}` + "\n", |
| }, |
| {"non-nil trySet", |
| "GET", |
| &trySet{ |
| tryKey: tryKey{ |
| Commit: "deadbeef", |
| ChangeID: "Ifoo", |
| }, |
| }, |
| trySetState{ |
| builds: []*buildStatus{ |
| { |
| BuilderRev: buildgo.BuilderRev{Name: "linux"}, |
| startTime: time.Time{}.Add(24 * time.Hour), |
| done: time.Time{}.Add(48 * time.Hour), |
| succeeded: true, |
| }, |
| { |
| BuilderRev: buildgo.BuilderRev{Name: "macOS"}, |
| startTime: time.Time{}.Add(24 * time.Hour), |
| }, |
| }, |
| }, |
| http.StatusOK, |
| `{"success":true,"payload":{"changeId":"Ifoo","commit":"deadbeef","builds":[{"name":"linux","startTime":"0001-01-02T00:00:00Z","done":true,"succeeded":true},{"name":"macOS","startTime":"0001-01-02T00:00:00Z","done":false,"succeeded":false}]}}` + "\n"}, |
| } |
| |
| for _, tc := range testCases { |
| t.Run(tc.desc, func(t *testing.T) { |
| w := httptest.NewRecorder() |
| r, err := http.NewRequest(tc.method, "", nil) |
| if err != nil { |
| t.Fatalf("could not create http.Request: %v", err) |
| } |
| serveTryStatusJSON(w, r, tc.ts, tc.tss) |
| resp := w.Result() |
| hdr := "Access-Control-Allow-Origin" |
| if got, want := resp.Header.Get(hdr), "*"; got != want { |
| t.Errorf("unexpected %q header: got %q; want %q", hdr, got, want) |
| } |
| if got, want := resp.StatusCode, tc.status; got != want { |
| t.Errorf("response status code: got %d; want %d", got, want) |
| } |
| defer resp.Body.Close() |
| b, err := ioutil.ReadAll(resp.Body) |
| if err != nil { |
| t.Fatalf("ioutil.ReadAll: %v", err) |
| } |
| if got, want := string(b), tc.body; got != want { |
| t.Errorf("body: got\n%v\nwant\n%v", got, want) |
| } |
| }) |
| } |
| } |
| |
| func TestStagingClusterBuilders(t *testing.T) { |
| // Just test that it doesn't panic: |
| stagingClusterBuilders() |
| } |
| |
| // Test that trybot on release-branch.go1.N branch of a golang.org/x repo |
| // uses the Go revision from Go repository's release-branch.go1.N branch. |
| // See golang.org/issue/28891. |
| func TestIssue28891(t *testing.T) { |
| testingKnobSkipBuilds = true |
| |
| work := &apipb.GerritTryWorkItem{ // Roughly based on https://go-review.googlesource.com/c/tools/+/150577/1. |
| Project: "tools", |
| Branch: "release-branch.go1.11", |
| ChangeId: "Ice719ab807ce3922b885a800ac873cdbf165a8f7", |
| Commit: "9d66f1bfdbed72f546df963194a19d56180c4ce7", |
| GoCommit: []string{"a2e79571a9d3dbe3cf10dcaeb1f9c01732219869", "e39e43d7349555501080133bb426f1ead4b3ef97", "f5ff72d62301c4e9d0a78167fab5914ca12919bd"}, |
| GoBranch: []string{"master", "release-branch.go1.11", "release-branch.go1.10"}, |
| GoVersion: []*apipb.MajorMinor{{1, 12}, {1, 11}, {1, 10}}, |
| } |
| ts := newTrySet(work) |
| if len(ts.builds) == 0 { |
| t.Fatal("no builders in try set, want at least 1") |
| } |
| for i, bs := range ts.builds { |
| const go111Revision = "e39e43d7349555501080133bb426f1ead4b3ef97" |
| if bs.BuilderRev.Rev != go111Revision { |
| t.Errorf("build[%d]: %s: x/tools on release-branch.go1.11 branch should be tested with Go 1.11, but isn't", i, bs.NameAndBranch()) |
| } |
| } |
| } |
| |
| // tests that we don't test Go 1.10 for the build repo |
| func TestNewTrySetBuildRepoGo110(t *testing.T) { |
| testingKnobSkipBuilds = true |
| |
| work := &apipb.GerritTryWorkItem{ |
| Project: "build", |
| Branch: "master", |
| ChangeId: "I6f05da2186b38dc8056081252563a82c50f0ce05", |
| Commit: "a62e6a3ab11cc9cc2d9e22a50025dd33fc35d22f", |
| GoCommit: []string{"a2e79571a9d3dbe3cf10dcaeb1f9c01732219869", "e39e43d7349555501080133bb426f1ead4b3ef97", "f5ff72d62301c4e9d0a78167fab5914ca12919bd"}, |
| GoBranch: []string{"master", "release-branch.go1.11", "release-branch.go1.10"}, |
| GoVersion: []*apipb.MajorMinor{{1, 12}, {1, 11}, {1, 10}}, |
| } |
| ts := newTrySet(work) |
| for i, bs := range ts.builds { |
| v := bs.NameAndBranch() |
| if strings.Contains(v, "Go 1.10.x") { |
| t.Errorf("unexpected builder: %v", v) |
| } |
| t.Logf("build[%d]: %s", i, v) |
| } |
| } |
| |
| // Tests that TryBots run on branches of the x/ repositories, other than |
| // "master" and "release-branch.go1.N". See golang.org/issue/37512. |
| func TestXRepoBranches(t *testing.T) { |
| testingKnobSkipBuilds = true |
| |
| work := &apipb.GerritTryWorkItem{ |
| Project: "tools", |
| Branch: "gopls-release-branch.0.4", |
| ChangeId: "Ica799fcf117bf607c0c59f41b08a78552339dc53", |
| Commit: "6af4ce83c61d0f3e616b410b53b51982798c4d73", |
| GoVersion: []*apipb.MajorMinor{{1, 15}}, |
| GoCommit: []string{"74d6de03fd7db2c6faa7794620a9bcf0c4f018f2"}, |
| GoBranch: []string{"master"}, |
| } |
| ts := newTrySet(work) |
| for i, bs := range ts.builds { |
| v := bs.NameAndBranch() |
| t.Logf("build[%d]: %s", i, v) |
| } |
| if len(ts.builds) < 3 { |
| t.Fatalf("expected at least 3 builders, got %v", len(ts.builds)) |
| } |
| } |
| |
| func TestFindWork(t *testing.T) { |
| if testing.Short() { |
| t.Skip("skipping in short mode") |
| } |
| gce := pool.NewGCEConfiguration() |
| buildEnv := gce.BuildEnv() |
| defer func(old *buildenv.Environment) { gce.SetBuildEnv(old) }(buildEnv) |
| gce.SetBuildEnv(buildenv.Production) |
| defer func() { buildgo.TestHookSnapshotExists = nil }() |
| buildgo.TestHookSnapshotExists = func(br *buildgo.BuilderRev) bool { |
| if strings.Contains(br.Name, "android") { |
| log.Printf("snapshot check for %+v", br) |
| } |
| return false |
| } |
| |
| addWorkTestHook = func(work buildgo.BuilderRev, d *commitDetail) { |
| t.Logf("Got: %v, %+v", work, d) |
| } |
| defer func() { addWorkTestHook = nil }() |
| |
| err := findWork() |
| if err != nil { |
| t.Error(err) |
| } |
| } |
| |
| func TestBuildersJSON(t *testing.T) { |
| rec := httptest.NewRecorder() |
| handleBuilders(rec, httptest.NewRequest("GET", "https://farmer.tld/builders?mode=json", nil)) |
| res := rec.Result() |
| if res.Header.Get("Content-Type") != "application/json" || res.StatusCode != 200 { |
| var buf bytes.Buffer |
| res.Write(&buf) |
| t.Error(buf.String()) |
| } |
| } |
| |
| func mustConf(t *testing.T, name string) *dashboard.BuildConfig { |
| conf, ok := dashboard.Builders[name] |
| if !ok { |
| t.Fatalf("unknown builder %q", name) |
| } |
| return conf |
| } |
| |
| func TestSlowBotsFromComments(t *testing.T) { |
| work := &apipb.GerritTryWorkItem{ |
| Version: 2, |
| TryMessage: []*apipb.TryVoteMessage{ |
| { |
| Version: 1, |
| Message: "ios", |
| }, |
| { |
| Version: 2, |
| Message: "arm64, mac aix ", |
| }, |
| { |
| Version: 1, |
| Message: "aix", |
| }, |
| }, |
| } |
| slowBots := slowBotsFromComments(work) |
| var got []string |
| for _, bc := range slowBots { |
| got = append(got, bc.Name) |
| } |
| want := []string{"aix-ppc64", "darwin-amd64-10_14", "linux-arm64-packet"} |
| if !reflect.DeepEqual(got, want) { |
| t.Errorf("mismatch:\n got: %q\nwant: %q\n", got, want) |
| } |
| } |
| |
| func TestSubreposFromComments(t *testing.T) { |
| work := &apipb.GerritTryWorkItem{ |
| Version: 2, |
| TryMessage: []*apipb.TryVoteMessage{ |
| { |
| Version: 2, |
| Message: "x/build, x/sync x/tools, x/sync", |
| }, |
| }, |
| } |
| got := xReposFromComments(work) |
| want := map[string]bool{ |
| "build": true, |
| "sync": true, |
| "tools": true, |
| } |
| if !reflect.DeepEqual(got, want) { |
| t.Errorf("mismatch:\n got: %v\nwant: %v\n", got, want) |
| } |
| } |
| |
| func TestBuildStatusFormat(t *testing.T) { |
| for i, tt := range []struct { |
| st *buildStatus |
| want string |
| }{ |
| { |
| st: &buildStatus{ |
| trySet: &trySet{ |
| tryKey: tryKey{ |
| Project: "go", |
| }, |
| }, |
| BuilderRev: buildgo.BuilderRev{ |
| Name: "linux-amd64", |
| SubName: "tools", |
| }, |
| }, |
| want: "(x/tools) linux-amd64", |
| }, |
| { |
| st: &buildStatus{ |
| trySet: &trySet{ |
| tryKey: tryKey{ |
| Project: "tools", |
| }, |
| }, |
| BuilderRev: buildgo.BuilderRev{ |
| Name: "linux-amd64", |
| SubName: "tools", |
| }, |
| goBranch: "release-branch.go1.15", |
| }, |
| want: "linux-amd64 (Go 1.15.x)", |
| }, |
| { |
| st: &buildStatus{ |
| trySet: &trySet{ |
| tryKey: tryKey{ |
| Project: "go", |
| }, |
| }, |
| BuilderRev: buildgo.BuilderRev{ |
| Name: "linux-amd64", |
| SubName: "tools", |
| }, |
| }, |
| want: "(x/tools) linux-amd64", |
| }, |
| { |
| st: &buildStatus{ |
| BuilderRev: buildgo.BuilderRev{ |
| Name: "darwin-amd64-10_14", |
| }, |
| }, |
| want: "darwin-amd64-10_14", |
| }, |
| { |
| st: &buildStatus{ |
| BuilderRev: buildgo.BuilderRev{ |
| Name: "darwin-amd64-10_14", |
| }, |
| goBranch: "release-branch.go1.15", |
| }, |
| want: "darwin-amd64-10_14 (Go 1.15.x)", |
| }, |
| } { |
| if got := tt.st.NameAndBranch(); got != tt.want { |
| t.Errorf("%d: NameAndBranch = %q; want %q", i, got, tt.want) |
| } |
| } |
| } |