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 44func TestGazelleBinary(t *testing.T) { 45 gazellePath := mustFindGazelle() 46 tests := map[string][]bazel.RunfileEntry{} 47 48 runfiles, err := bazel.ListRunfiles() 49 if err != nil { 50 t.Fatalf("bazel.ListRunfiles() error: %v", err) 51 } 52 for _, f := range runfiles { 53 if strings.HasPrefix(f.ShortPath, testDataPath) { 54 relativePath := strings.TrimPrefix(f.ShortPath, testDataPath) 55 parts := strings.SplitN(relativePath, string(os.PathSeparator), 2) 56 if len(parts) < 2 { 57 // This file is not a part of a testcase since it must be in a dir that 58 // is the test case and then have a path inside of that. 59 continue 60 } 61 62 tests[parts[0]] = append(tests[parts[0]], f) 63 } 64 } 65 if len(tests) == 0 { 66 t.Fatal("no tests found") 67 } 68 for testName, files := range tests { 69 testPath(t, gazellePath, testName, files) 70 } 71} 72 73func testPath(t *testing.T, gazellePath, name string, files []bazel.RunfileEntry) { 74 t.Run(name, func(t *testing.T) { 75 t.Parallel() 76 var inputs, goldens []testtools.FileSpec 77 78 var config *testYAML 79 for _, f := range files { 80 path := f.Path 81 trim := filepath.Join(testDataPath, name) + string(os.PathSeparator) 82 shortPath := strings.TrimPrefix(f.ShortPath, trim) 83 info, err := os.Stat(path) 84 if err != nil { 85 t.Fatalf("os.Stat(%q) error: %v", path, err) 86 } 87 88 if info.IsDir() { 89 continue 90 } 91 92 content, err := os.ReadFile(path) 93 if err != nil { 94 t.Errorf("os.ReadFile(%q) error: %v", path, err) 95 } 96 97 if filepath.Base(shortPath) == "test.yaml" { 98 if config != nil { 99 t.Fatal("only 1 test.yaml is supported") 100 } 101 config = new(testYAML) 102 if err := yaml.Unmarshal(content, config); err != nil { 103 t.Fatal(err) 104 } 105 } 106 107 if strings.HasSuffix(shortPath, ".in") { 108 inputs = append(inputs, testtools.FileSpec{ 109 Path: filepath.Join(name, strings.TrimSuffix(shortPath, ".in")), 110 Content: string(content), 111 }) 112 continue 113 } 114 115 if strings.HasSuffix(shortPath, ".out") { 116 goldens = append(goldens, testtools.FileSpec{ 117 Path: filepath.Join(name, strings.TrimSuffix(shortPath, ".out")), 118 Content: string(content), 119 }) 120 continue 121 } 122 123 inputs = append(inputs, testtools.FileSpec{ 124 Path: filepath.Join(name, shortPath), 125 Content: string(content), 126 }) 127 goldens = append(goldens, testtools.FileSpec{ 128 Path: filepath.Join(name, shortPath), 129 Content: string(content), 130 }) 131 } 132 133 testdataDir, cleanup := testtools.CreateFiles(t, inputs) 134 t.Cleanup(cleanup) 135 t.Cleanup(func() { 136 if !t.Failed() { 137 return 138 } 139 140 filepath.Walk(testdataDir, func(path string, info os.FileInfo, err error) error { 141 if err != nil { 142 return err 143 } 144 t.Logf("%q exists", strings.TrimPrefix(path, testdataDir)) 145 return nil 146 }) 147 }) 148 149 workspaceRoot := filepath.Join(testdataDir, name) 150 151 args := []string{"-build_file_name=BUILD,BUILD.bazel"} 152 153 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) 154 t.Cleanup(cancel) 155 cmd := exec.CommandContext(ctx, gazellePath, args...) 156 var stdout, stderr bytes.Buffer 157 cmd.Stdout = &stdout 158 cmd.Stderr = &stderr 159 cmd.Dir = workspaceRoot 160 if err := cmd.Run(); err != nil { 161 var e *exec.ExitError 162 if !errors.As(err, &e) { 163 t.Fatal(err) 164 } 165 } 166 167 actualExitCode := cmd.ProcessState.ExitCode() 168 if config.Expect.ExitCode != actualExitCode { 169 t.Errorf("expected gazelle exit code: %d\ngot: %d", 170 config.Expect.ExitCode, actualExitCode) 171 } 172 actualStdout := stdout.String() 173 if strings.TrimSpace(config.Expect.Stdout) != strings.TrimSpace(actualStdout) { 174 t.Errorf("expected gazelle stdout: %s\ngot: %s", 175 config.Expect.Stdout, actualStdout) 176 } 177 actualStderr := stderr.String() 178 if strings.TrimSpace(config.Expect.Stderr) != strings.TrimSpace(actualStderr) { 179 t.Errorf("expected gazelle stderr: %s\ngot: %s", 180 config.Expect.Stderr, actualStderr) 181 } 182 if t.Failed() { 183 t.FailNow() 184 } 185 186 testtools.CheckFiles(t, testdataDir, goldens) 187 }) 188} 189 190func mustFindGazelle() string { 191 gazellePath, ok := bazel.FindBinary(extensionDir, gazelleBinaryName) 192 if !ok { 193 panic("could not find gazelle binary") 194 } 195 return gazellePath 196} 197 198type testYAML struct { 199 Expect struct { 200 ExitCode int `json:"exit_code"` 201 Stdout string `json:"stdout"` 202 Stderr string `json:"stderr"` 203 } `json:"expect"` 204} 205