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