xref: /aosp_15_r20/external/bazelbuild-rules_python/gazelle/python/python_test.go (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
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