1*60517a1eSAndroid Build Coastguard Worker/* Copyright 2020 The Bazel Authors. All rights reserved. 2*60517a1eSAndroid Build Coastguard Worker 3*60517a1eSAndroid Build Coastguard WorkerLicensed under the Apache License, Version 2.0 (the "License"); 4*60517a1eSAndroid Build Coastguard Workeryou may not use this file except in compliance with the License. 5*60517a1eSAndroid Build Coastguard WorkerYou may obtain a copy of the License at 6*60517a1eSAndroid Build Coastguard Worker 7*60517a1eSAndroid Build Coastguard Worker http://www.apache.org/licenses/LICENSE-2.0 8*60517a1eSAndroid Build Coastguard Worker 9*60517a1eSAndroid Build Coastguard WorkerUnless required by applicable law or agreed to in writing, software 10*60517a1eSAndroid Build Coastguard Workerdistributed under the License is distributed on an "AS IS" BASIS, 11*60517a1eSAndroid Build Coastguard WorkerWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*60517a1eSAndroid Build Coastguard WorkerSee the License for the specific language governing permissions and 13*60517a1eSAndroid Build Coastguard Workerlimitations under the License. 14*60517a1eSAndroid Build Coastguard Worker*/ 15*60517a1eSAndroid Build Coastguard Worker 16*60517a1eSAndroid Build Coastguard Worker// This test file was first seen on: 17*60517a1eSAndroid Build Coastguard Worker// https://github.com/bazelbuild/bazel-skylib/blob/f80bc733d4b9f83d427ce3442be2e07427b2cc8d/gazelle/bzl/BUILD. 18*60517a1eSAndroid Build Coastguard Worker// It was modified for the needs of this extension. 19*60517a1eSAndroid Build Coastguard Worker 20*60517a1eSAndroid Build Coastguard Workerpackage python_test 21*60517a1eSAndroid Build Coastguard Worker 22*60517a1eSAndroid Build Coastguard Workerimport ( 23*60517a1eSAndroid Build Coastguard Worker "bytes" 24*60517a1eSAndroid Build Coastguard Worker "context" 25*60517a1eSAndroid Build Coastguard Worker "errors" 26*60517a1eSAndroid Build Coastguard Worker "os" 27*60517a1eSAndroid Build Coastguard Worker "os/exec" 28*60517a1eSAndroid Build Coastguard Worker "path/filepath" 29*60517a1eSAndroid Build Coastguard Worker "strings" 30*60517a1eSAndroid Build Coastguard Worker "testing" 31*60517a1eSAndroid Build Coastguard Worker "time" 32*60517a1eSAndroid Build Coastguard Worker 33*60517a1eSAndroid Build Coastguard Worker "github.com/bazelbuild/bazel-gazelle/testtools" 34*60517a1eSAndroid Build Coastguard Worker "github.com/bazelbuild/rules_go/go/tools/bazel" 35*60517a1eSAndroid Build Coastguard Worker "github.com/ghodss/yaml" 36*60517a1eSAndroid Build Coastguard Worker) 37*60517a1eSAndroid Build Coastguard Worker 38*60517a1eSAndroid Build Coastguard Workerconst ( 39*60517a1eSAndroid Build Coastguard Worker extensionDir = "python" + string(os.PathSeparator) 40*60517a1eSAndroid Build Coastguard Worker testDataPath = extensionDir + "testdata" + string(os.PathSeparator) 41*60517a1eSAndroid Build Coastguard Worker gazelleBinaryName = "gazelle_binary" 42*60517a1eSAndroid Build Coastguard Worker) 43*60517a1eSAndroid Build Coastguard Worker 44*60517a1eSAndroid Build Coastguard Workerfunc TestGazelleBinary(t *testing.T) { 45*60517a1eSAndroid Build Coastguard Worker gazellePath := mustFindGazelle() 46*60517a1eSAndroid Build Coastguard Worker tests := map[string][]bazel.RunfileEntry{} 47*60517a1eSAndroid Build Coastguard Worker 48*60517a1eSAndroid Build Coastguard Worker runfiles, err := bazel.ListRunfiles() 49*60517a1eSAndroid Build Coastguard Worker if err != nil { 50*60517a1eSAndroid Build Coastguard Worker t.Fatalf("bazel.ListRunfiles() error: %v", err) 51*60517a1eSAndroid Build Coastguard Worker } 52*60517a1eSAndroid Build Coastguard Worker for _, f := range runfiles { 53*60517a1eSAndroid Build Coastguard Worker if strings.HasPrefix(f.ShortPath, testDataPath) { 54*60517a1eSAndroid Build Coastguard Worker relativePath := strings.TrimPrefix(f.ShortPath, testDataPath) 55*60517a1eSAndroid Build Coastguard Worker parts := strings.SplitN(relativePath, string(os.PathSeparator), 2) 56*60517a1eSAndroid Build Coastguard Worker if len(parts) < 2 { 57*60517a1eSAndroid Build Coastguard Worker // This file is not a part of a testcase since it must be in a dir that 58*60517a1eSAndroid Build Coastguard Worker // is the test case and then have a path inside of that. 59*60517a1eSAndroid Build Coastguard Worker continue 60*60517a1eSAndroid Build Coastguard Worker } 61*60517a1eSAndroid Build Coastguard Worker 62*60517a1eSAndroid Build Coastguard Worker tests[parts[0]] = append(tests[parts[0]], f) 63*60517a1eSAndroid Build Coastguard Worker } 64*60517a1eSAndroid Build Coastguard Worker } 65*60517a1eSAndroid Build Coastguard Worker if len(tests) == 0 { 66*60517a1eSAndroid Build Coastguard Worker t.Fatal("no tests found") 67*60517a1eSAndroid Build Coastguard Worker } 68*60517a1eSAndroid Build Coastguard Worker for testName, files := range tests { 69*60517a1eSAndroid Build Coastguard Worker testPath(t, gazellePath, testName, files) 70*60517a1eSAndroid Build Coastguard Worker } 71*60517a1eSAndroid Build Coastguard Worker} 72*60517a1eSAndroid Build Coastguard Worker 73*60517a1eSAndroid Build Coastguard Workerfunc testPath(t *testing.T, gazellePath, name string, files []bazel.RunfileEntry) { 74*60517a1eSAndroid Build Coastguard Worker t.Run(name, func(t *testing.T) { 75*60517a1eSAndroid Build Coastguard Worker t.Parallel() 76*60517a1eSAndroid Build Coastguard Worker var inputs, goldens []testtools.FileSpec 77*60517a1eSAndroid Build Coastguard Worker 78*60517a1eSAndroid Build Coastguard Worker var config *testYAML 79*60517a1eSAndroid Build Coastguard Worker for _, f := range files { 80*60517a1eSAndroid Build Coastguard Worker path := f.Path 81*60517a1eSAndroid Build Coastguard Worker trim := filepath.Join(testDataPath, name) + string(os.PathSeparator) 82*60517a1eSAndroid Build Coastguard Worker shortPath := strings.TrimPrefix(f.ShortPath, trim) 83*60517a1eSAndroid Build Coastguard Worker info, err := os.Stat(path) 84*60517a1eSAndroid Build Coastguard Worker if err != nil { 85*60517a1eSAndroid Build Coastguard Worker t.Fatalf("os.Stat(%q) error: %v", path, err) 86*60517a1eSAndroid Build Coastguard Worker } 87*60517a1eSAndroid Build Coastguard Worker 88*60517a1eSAndroid Build Coastguard Worker if info.IsDir() { 89*60517a1eSAndroid Build Coastguard Worker continue 90*60517a1eSAndroid Build Coastguard Worker } 91*60517a1eSAndroid Build Coastguard Worker 92*60517a1eSAndroid Build Coastguard Worker content, err := os.ReadFile(path) 93*60517a1eSAndroid Build Coastguard Worker if err != nil { 94*60517a1eSAndroid Build Coastguard Worker t.Errorf("os.ReadFile(%q) error: %v", path, err) 95*60517a1eSAndroid Build Coastguard Worker } 96*60517a1eSAndroid Build Coastguard Worker 97*60517a1eSAndroid Build Coastguard Worker if filepath.Base(shortPath) == "test.yaml" { 98*60517a1eSAndroid Build Coastguard Worker if config != nil { 99*60517a1eSAndroid Build Coastguard Worker t.Fatal("only 1 test.yaml is supported") 100*60517a1eSAndroid Build Coastguard Worker } 101*60517a1eSAndroid Build Coastguard Worker config = new(testYAML) 102*60517a1eSAndroid Build Coastguard Worker if err := yaml.Unmarshal(content, config); err != nil { 103*60517a1eSAndroid Build Coastguard Worker t.Fatal(err) 104*60517a1eSAndroid Build Coastguard Worker } 105*60517a1eSAndroid Build Coastguard Worker } 106*60517a1eSAndroid Build Coastguard Worker 107*60517a1eSAndroid Build Coastguard Worker if strings.HasSuffix(shortPath, ".in") { 108*60517a1eSAndroid Build Coastguard Worker inputs = append(inputs, testtools.FileSpec{ 109*60517a1eSAndroid Build Coastguard Worker Path: filepath.Join(name, strings.TrimSuffix(shortPath, ".in")), 110*60517a1eSAndroid Build Coastguard Worker Content: string(content), 111*60517a1eSAndroid Build Coastguard Worker }) 112*60517a1eSAndroid Build Coastguard Worker continue 113*60517a1eSAndroid Build Coastguard Worker } 114*60517a1eSAndroid Build Coastguard Worker 115*60517a1eSAndroid Build Coastguard Worker if strings.HasSuffix(shortPath, ".out") { 116*60517a1eSAndroid Build Coastguard Worker goldens = append(goldens, testtools.FileSpec{ 117*60517a1eSAndroid Build Coastguard Worker Path: filepath.Join(name, strings.TrimSuffix(shortPath, ".out")), 118*60517a1eSAndroid Build Coastguard Worker Content: string(content), 119*60517a1eSAndroid Build Coastguard Worker }) 120*60517a1eSAndroid Build Coastguard Worker continue 121*60517a1eSAndroid Build Coastguard Worker } 122*60517a1eSAndroid Build Coastguard Worker 123*60517a1eSAndroid Build Coastguard Worker inputs = append(inputs, testtools.FileSpec{ 124*60517a1eSAndroid Build Coastguard Worker Path: filepath.Join(name, shortPath), 125*60517a1eSAndroid Build Coastguard Worker Content: string(content), 126*60517a1eSAndroid Build Coastguard Worker }) 127*60517a1eSAndroid Build Coastguard Worker goldens = append(goldens, testtools.FileSpec{ 128*60517a1eSAndroid Build Coastguard Worker Path: filepath.Join(name, shortPath), 129*60517a1eSAndroid Build Coastguard Worker Content: string(content), 130*60517a1eSAndroid Build Coastguard Worker }) 131*60517a1eSAndroid Build Coastguard Worker } 132*60517a1eSAndroid Build Coastguard Worker 133*60517a1eSAndroid Build Coastguard Worker testdataDir, cleanup := testtools.CreateFiles(t, inputs) 134*60517a1eSAndroid Build Coastguard Worker t.Cleanup(cleanup) 135*60517a1eSAndroid Build Coastguard Worker t.Cleanup(func() { 136*60517a1eSAndroid Build Coastguard Worker if !t.Failed() { 137*60517a1eSAndroid Build Coastguard Worker return 138*60517a1eSAndroid Build Coastguard Worker } 139*60517a1eSAndroid Build Coastguard Worker 140*60517a1eSAndroid Build Coastguard Worker filepath.Walk(testdataDir, func(path string, info os.FileInfo, err error) error { 141*60517a1eSAndroid Build Coastguard Worker if err != nil { 142*60517a1eSAndroid Build Coastguard Worker return err 143*60517a1eSAndroid Build Coastguard Worker } 144*60517a1eSAndroid Build Coastguard Worker t.Logf("%q exists", strings.TrimPrefix(path, testdataDir)) 145*60517a1eSAndroid Build Coastguard Worker return nil 146*60517a1eSAndroid Build Coastguard Worker }) 147*60517a1eSAndroid Build Coastguard Worker }) 148*60517a1eSAndroid Build Coastguard Worker 149*60517a1eSAndroid Build Coastguard Worker workspaceRoot := filepath.Join(testdataDir, name) 150*60517a1eSAndroid Build Coastguard Worker 151*60517a1eSAndroid Build Coastguard Worker args := []string{"-build_file_name=BUILD,BUILD.bazel"} 152*60517a1eSAndroid Build Coastguard Worker 153*60517a1eSAndroid Build Coastguard Worker ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 154*60517a1eSAndroid Build Coastguard Worker t.Cleanup(cancel) 155*60517a1eSAndroid Build Coastguard Worker cmd := exec.CommandContext(ctx, gazellePath, args...) 156*60517a1eSAndroid Build Coastguard Worker var stdout, stderr bytes.Buffer 157*60517a1eSAndroid Build Coastguard Worker cmd.Stdout = &stdout 158*60517a1eSAndroid Build Coastguard Worker cmd.Stderr = &stderr 159*60517a1eSAndroid Build Coastguard Worker cmd.Dir = workspaceRoot 160*60517a1eSAndroid Build Coastguard Worker if err := cmd.Run(); err != nil { 161*60517a1eSAndroid Build Coastguard Worker var e *exec.ExitError 162*60517a1eSAndroid Build Coastguard Worker if !errors.As(err, &e) { 163*60517a1eSAndroid Build Coastguard Worker t.Fatal(err) 164*60517a1eSAndroid Build Coastguard Worker } 165*60517a1eSAndroid Build Coastguard Worker } 166*60517a1eSAndroid Build Coastguard Worker 167*60517a1eSAndroid Build Coastguard Worker actualExitCode := cmd.ProcessState.ExitCode() 168*60517a1eSAndroid Build Coastguard Worker if config.Expect.ExitCode != actualExitCode { 169*60517a1eSAndroid Build Coastguard Worker t.Errorf("expected gazelle exit code: %d\ngot: %d", 170*60517a1eSAndroid Build Coastguard Worker config.Expect.ExitCode, actualExitCode) 171*60517a1eSAndroid Build Coastguard Worker } 172*60517a1eSAndroid Build Coastguard Worker actualStdout := stdout.String() 173*60517a1eSAndroid Build Coastguard Worker if strings.TrimSpace(config.Expect.Stdout) != strings.TrimSpace(actualStdout) { 174*60517a1eSAndroid Build Coastguard Worker t.Errorf("expected gazelle stdout: %s\ngot: %s", 175*60517a1eSAndroid Build Coastguard Worker config.Expect.Stdout, actualStdout) 176*60517a1eSAndroid Build Coastguard Worker } 177*60517a1eSAndroid Build Coastguard Worker actualStderr := stderr.String() 178*60517a1eSAndroid Build Coastguard Worker if strings.TrimSpace(config.Expect.Stderr) != strings.TrimSpace(actualStderr) { 179*60517a1eSAndroid Build Coastguard Worker t.Errorf("expected gazelle stderr: %s\ngot: %s", 180*60517a1eSAndroid Build Coastguard Worker config.Expect.Stderr, actualStderr) 181*60517a1eSAndroid Build Coastguard Worker } 182*60517a1eSAndroid Build Coastguard Worker if t.Failed() { 183*60517a1eSAndroid Build Coastguard Worker t.FailNow() 184*60517a1eSAndroid Build Coastguard Worker } 185*60517a1eSAndroid Build Coastguard Worker 186*60517a1eSAndroid Build Coastguard Worker testtools.CheckFiles(t, testdataDir, goldens) 187*60517a1eSAndroid Build Coastguard Worker }) 188*60517a1eSAndroid Build Coastguard Worker} 189*60517a1eSAndroid Build Coastguard Worker 190*60517a1eSAndroid Build Coastguard Workerfunc mustFindGazelle() string { 191*60517a1eSAndroid Build Coastguard Worker gazellePath, ok := bazel.FindBinary(extensionDir, gazelleBinaryName) 192*60517a1eSAndroid Build Coastguard Worker if !ok { 193*60517a1eSAndroid Build Coastguard Worker panic("could not find gazelle binary") 194*60517a1eSAndroid Build Coastguard Worker } 195*60517a1eSAndroid Build Coastguard Worker return gazellePath 196*60517a1eSAndroid Build Coastguard Worker} 197*60517a1eSAndroid Build Coastguard Worker 198*60517a1eSAndroid Build Coastguard Workertype testYAML struct { 199*60517a1eSAndroid Build Coastguard Worker Expect struct { 200*60517a1eSAndroid Build Coastguard Worker ExitCode int `json:"exit_code"` 201*60517a1eSAndroid Build Coastguard Worker Stdout string `json:"stdout"` 202*60517a1eSAndroid Build Coastguard Worker Stderr string `json:"stderr"` 203*60517a1eSAndroid Build Coastguard Worker } `json:"expect"` 204*60517a1eSAndroid Build Coastguard Worker} 205