• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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