xref: /aosp_15_r20/external/toolchain-utils/compiler_wrapper/compiler_wrapper_test.go (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1*760c253cSXin Li// Copyright 2019 The ChromiumOS Authors
2*760c253cSXin Li// Use of this source code is governed by a BSD-style license that can be
3*760c253cSXin Li// found in the LICENSE file.
4*760c253cSXin Li
5*760c253cSXin Lipackage main
6*760c253cSXin Li
7*760c253cSXin Liimport (
8*760c253cSXin Li	"bytes"
9*760c253cSXin Li	"errors"
10*760c253cSXin Li	"fmt"
11*760c253cSXin Li	"io"
12*760c253cSXin Li	"os"
13*760c253cSXin Li	"path"
14*760c253cSXin Li	"path/filepath"
15*760c253cSXin Li	"strings"
16*760c253cSXin Li	"syscall"
17*760c253cSXin Li	"testing"
18*760c253cSXin Li)
19*760c253cSXin Li
20*760c253cSXin Lifunc TestAddCommonFlags(t *testing.T) {
21*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
22*760c253cSXin Li		ctx.cfg.commonFlags = []string{"-someflag"}
23*760c253cSXin Li		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
24*760c253cSXin Li			ctx.newCommand(gccX86_64, mainCc)))
25*760c253cSXin Li		if err := verifyArgOrder(cmd, "-someflag", mainCc); err != nil {
26*760c253cSXin Li			t.Error(err)
27*760c253cSXin Li		}
28*760c253cSXin Li	})
29*760c253cSXin Li}
30*760c253cSXin Li
31*760c253cSXin Lifunc TestAddGccConfigFlags(t *testing.T) {
32*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
33*760c253cSXin Li		ctx.cfg.gccFlags = []string{"-someflag"}
34*760c253cSXin Li		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
35*760c253cSXin Li			ctx.newCommand(gccX86_64, mainCc)))
36*760c253cSXin Li		if err := verifyArgOrder(cmd, "-someflag", mainCc); err != nil {
37*760c253cSXin Li			t.Error(err)
38*760c253cSXin Li		}
39*760c253cSXin Li	})
40*760c253cSXin Li}
41*760c253cSXin Li
42*760c253cSXin Lifunc TestAddClangConfigFlags(t *testing.T) {
43*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
44*760c253cSXin Li		ctx.cfg.clangFlags = []string{"-someflag"}
45*760c253cSXin Li		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
46*760c253cSXin Li			ctx.newCommand(clangX86_64, mainCc)))
47*760c253cSXin Li		if err := verifyArgOrder(cmd, "-someflag", mainCc); err != nil {
48*760c253cSXin Li			t.Error(err)
49*760c253cSXin Li		}
50*760c253cSXin Li	})
51*760c253cSXin Li}
52*760c253cSXin Li
53*760c253cSXin Lifunc TestLogGeneralExecError(t *testing.T) {
54*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
55*760c253cSXin Li		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
56*760c253cSXin Li			return errors.New("someerror")
57*760c253cSXin Li		}
58*760c253cSXin Li		stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc)))
59*760c253cSXin Li		if err := verifyInternalError(stderr); err != nil {
60*760c253cSXin Li			t.Fatal(err)
61*760c253cSXin Li		}
62*760c253cSXin Li		if !strings.Contains(stderr, gccX86_64) {
63*760c253cSXin Li			t.Errorf("could not find compiler path on stderr. Got: %s", stderr)
64*760c253cSXin Li		}
65*760c253cSXin Li		if !strings.Contains(stderr, "someerror") {
66*760c253cSXin Li			t.Errorf("could not find original error on stderr. Got: %s", stderr)
67*760c253cSXin Li		}
68*760c253cSXin Li	})
69*760c253cSXin Li}
70*760c253cSXin Li
71*760c253cSXin Lifunc TestForwardStdin(t *testing.T) {
72*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
73*760c253cSXin Li		io.WriteString(&ctx.stdinBuffer, "someinput")
74*760c253cSXin Li		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
75*760c253cSXin Li			stdinStr := ctx.readAllString(stdin)
76*760c253cSXin Li			if stdinStr != "someinput" {
77*760c253cSXin Li				return fmt.Errorf("unexpected stdin. Got: %s", stdinStr)
78*760c253cSXin Li			}
79*760c253cSXin Li			return nil
80*760c253cSXin Li		}
81*760c253cSXin Li		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, "-", mainCc)))
82*760c253cSXin Li	})
83*760c253cSXin Li}
84*760c253cSXin Li
85*760c253cSXin Lifunc TestLogMissingCCacheExecError(t *testing.T) {
86*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
87*760c253cSXin Li		ctx.cfg.useCCache = true
88*760c253cSXin Li
89*760c253cSXin Li		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
90*760c253cSXin Li			return syscall.ENOENT
91*760c253cSXin Li		}
92*760c253cSXin Li		ctx.stderrBuffer.Reset()
93*760c253cSXin Li		stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc)))
94*760c253cSXin Li		if err := verifyNonInternalError(stderr, "ccache not found under .*. Please install it"); err != nil {
95*760c253cSXin Li			t.Fatal(err)
96*760c253cSXin Li		}
97*760c253cSXin Li	})
98*760c253cSXin Li}
99*760c253cSXin Li
100*760c253cSXin Lifunc TestGomaDisablesRusage(t *testing.T) {
101*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
102*760c253cSXin Li		gomaPath := path.Join(ctx.tempDir, "gomacc")
103*760c253cSXin Li		ctx.writeFile(gomaPath, "")
104*760c253cSXin Li		ctx.env = []string{"GOMACC_PATH=" + gomaPath}
105*760c253cSXin Li		logFileName := filepath.Join(ctx.tempDir, "rusage.log")
106*760c253cSXin Li		ctx.env = []string{
107*760c253cSXin Li			"TOOLCHAIN_RUSAGE_OUTPUT=" + logFileName,
108*760c253cSXin Li			"GOMACC_PATH=" + gomaPath,
109*760c253cSXin Li		}
110*760c253cSXin Li		cmd := ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc)))
111*760c253cSXin Li		// Ensure Goma was used
112*760c253cSXin Li		if err := verifyPath(cmd, gomaPath); err != nil {
113*760c253cSXin Li			t.Fatal(err)
114*760c253cSXin Li		}
115*760c253cSXin Li		if err := verifyArgOrder(cmd, gccX86_64+".real", mainCc); err != nil {
116*760c253cSXin Li			t.Error(err)
117*760c253cSXin Li		}
118*760c253cSXin Li		// Ensure rusage log was not created
119*760c253cSXin Li		if _, err := os.Stat(logFileName); err == nil {
120*760c253cSXin Li			t.Errorf("Logfile shouldn't have been created at TOOLCHAIN_RUSAGE_OUTPUT path %q but was", logFileName)
121*760c253cSXin Li		} else if !os.IsNotExist(err) {
122*760c253cSXin Li			t.Fatalf("error checking for rusage logfile at %q: %v", logFileName, err)
123*760c253cSXin Li		}
124*760c253cSXin Li	})
125*760c253cSXin Li}
126*760c253cSXin Li
127*760c253cSXin Lifunc TestLogRusageAndForceDisableWError(t *testing.T) {
128*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
129*760c253cSXin Li		ctx.NoteTestWritesToUmask()
130*760c253cSXin Li
131*760c253cSXin Li		logFileName := filepath.Join(ctx.tempDir, "rusage.log")
132*760c253cSXin Li		ctx.env = []string{
133*760c253cSXin Li			"FORCE_DISABLE_WERROR=1",
134*760c253cSXin Li			"TOOLCHAIN_RUSAGE_OUTPUT=" + logFileName,
135*760c253cSXin Li		}
136*760c253cSXin Li		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
137*760c253cSXin Li			switch ctx.cmdCount {
138*760c253cSXin Li			case 1:
139*760c253cSXin Li				io.WriteString(stderr, arbitraryWerrorStderr)
140*760c253cSXin Li				return newExitCodeError(1)
141*760c253cSXin Li			case 2:
142*760c253cSXin Li				return nil
143*760c253cSXin Li			default:
144*760c253cSXin Li				t.Fatalf("unexpected command: %#v", cmd)
145*760c253cSXin Li				return nil
146*760c253cSXin Li			}
147*760c253cSXin Li		}
148*760c253cSXin Li		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
149*760c253cSXin Li		if _, err := os.Stat(logFileName); os.IsNotExist(err) {
150*760c253cSXin Li			t.Errorf("no logfile created at TOOLCHAIN_RUSAGE_OUTPUT path %q", logFileName)
151*760c253cSXin Li		} else if err != nil {
152*760c253cSXin Li			t.Fatalf("error checking for rusage logfile at %q: %v", logFileName, err)
153*760c253cSXin Li		}
154*760c253cSXin Li		if ctx.cmdCount != 2 {
155*760c253cSXin Li			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
156*760c253cSXin Li		}
157*760c253cSXin Li	})
158*760c253cSXin Li}
159*760c253cSXin Li
160*760c253cSXin Lifunc TestErrorOnLogRusageAndBisect(t *testing.T) {
161*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
162*760c253cSXin Li		ctx.NoteTestWritesToUmask()
163*760c253cSXin Li
164*760c253cSXin Li		ctx.env = []string{
165*760c253cSXin Li			"BISECT_STAGE=xyz",
166*760c253cSXin Li			"TOOLCHAIN_RUSAGE_OUTPUT=rusage.log",
167*760c253cSXin Li		}
168*760c253cSXin Li		stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc)))
169*760c253cSXin Li		if err := verifyNonInternalError(stderr, "TOOLCHAIN_RUSAGE_OUTPUT is meaningless with BISECT_STAGE"); err != nil {
170*760c253cSXin Li			t.Error(err)
171*760c253cSXin Li		}
172*760c253cSXin Li	})
173*760c253cSXin Li}
174*760c253cSXin Li
175*760c253cSXin Lifunc TestErrorOnBisectAndForceDisableWError(t *testing.T) {
176*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
177*760c253cSXin Li		ctx.NoteTestWritesToUmask()
178*760c253cSXin Li
179*760c253cSXin Li		ctx.env = []string{
180*760c253cSXin Li			"BISECT_STAGE=xyz",
181*760c253cSXin Li			"FORCE_DISABLE_WERROR=1",
182*760c253cSXin Li		}
183*760c253cSXin Li		stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
184*760c253cSXin Li		if err := verifyNonInternalError(stderr, "BISECT_STAGE is meaningless with FORCE_DISABLE_WERROR"); err != nil {
185*760c253cSXin Li			t.Error(err)
186*760c253cSXin Li		}
187*760c253cSXin Li	})
188*760c253cSXin Li}
189*760c253cSXin Li
190*760c253cSXin Lifunc TestPrintUserCompilerError(t *testing.T) {
191*760c253cSXin Li	buffer := bytes.Buffer{}
192*760c253cSXin Li	printCompilerError(&buffer, newUserErrorf("abcd"))
193*760c253cSXin Li	if buffer.String() != "abcd\n" {
194*760c253cSXin Li		t.Errorf("Unexpected string. Got: %s", buffer.String())
195*760c253cSXin Li	}
196*760c253cSXin Li}
197*760c253cSXin Li
198*760c253cSXin Lifunc TestPrintOtherCompilerError(t *testing.T) {
199*760c253cSXin Li	buffer := bytes.Buffer{}
200*760c253cSXin Li	printCompilerError(&buffer, errors.New("abcd"))
201*760c253cSXin Li	if buffer.String() != "Internal error. Please report to [email protected].\nabcd\n" {
202*760c253cSXin Li		t.Errorf("Unexpected string. Got: %s", buffer.String())
203*760c253cSXin Li	}
204*760c253cSXin Li}
205*760c253cSXin Li
206*760c253cSXin Lifunc TestPrintOtherCompilerErrorForAndroidLLVM(t *testing.T) {
207*760c253cSXin Li	buffer := bytes.Buffer{}
208*760c253cSXin Li
209*760c253cSXin Li	oldConfigName := ConfigName
210*760c253cSXin Li	defer func() { ConfigName = oldConfigName }()
211*760c253cSXin Li
212*760c253cSXin Li	ConfigName = "android"
213*760c253cSXin Li	printCompilerError(&buffer, errors.New("abcd"))
214*760c253cSXin Li	if buffer.String() != "Internal error. Please report to [email protected].\nabcd\n" {
215*760c253cSXin Li		t.Errorf("Unexpected string. Got: %s", buffer.String())
216*760c253cSXin Li	}
217*760c253cSXin Li}
218*760c253cSXin Li
219*760c253cSXin Lifunc TestCalculateAndroidWrapperPath(t *testing.T) {
220*760c253cSXin Li	t.Parallel()
221*760c253cSXin Li
222*760c253cSXin Li	testCases := []struct {
223*760c253cSXin Li		mainBuilderPath string
224*760c253cSXin Li		absWrapperPath  string
225*760c253cSXin Li		want            string
226*760c253cSXin Li	}{
227*760c253cSXin Li		{
228*760c253cSXin Li			mainBuilderPath: "/foo/bar",
229*760c253cSXin Li			absWrapperPath:  "/bar/baz",
230*760c253cSXin Li			want:            "/foo/baz.real",
231*760c253cSXin Li		},
232*760c253cSXin Li		{
233*760c253cSXin Li			mainBuilderPath: "/my_wrapper",
234*760c253cSXin Li			absWrapperPath:  "/bar/baz",
235*760c253cSXin Li			want:            "/baz.real",
236*760c253cSXin Li		},
237*760c253cSXin Li		{
238*760c253cSXin Li			mainBuilderPath: "no_seps",
239*760c253cSXin Li			absWrapperPath:  "/bar/baz",
240*760c253cSXin Li			want:            "baz.real",
241*760c253cSXin Li		},
242*760c253cSXin Li		{
243*760c253cSXin Li			mainBuilderPath: "./a_sep",
244*760c253cSXin Li			absWrapperPath:  "/bar/baz",
245*760c253cSXin Li			want:            "./baz.real",
246*760c253cSXin Li		},
247*760c253cSXin Li	}
248*760c253cSXin Li
249*760c253cSXin Li	for _, tc := range testCases {
250*760c253cSXin Li		if result := calculateAndroidWrapperPath(tc.mainBuilderPath, tc.absWrapperPath); result != tc.want {
251*760c253cSXin Li			t.Errorf("Failed calculating the wrapper path with (%q, %q); got %q, want %q", tc.mainBuilderPath, tc.absWrapperPath, result, tc.want)
252*760c253cSXin Li		}
253*760c253cSXin Li	}
254*760c253cSXin Li}
255*760c253cSXin Li
256*760c253cSXin Li// If "crash-diagnostics-dir" flag is not provided, add one in
257*760c253cSXin Lifunc TestCrashDiagDefault(t *testing.T) {
258*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
259*760c253cSXin Li		ctx.env = []string{
260*760c253cSXin Li			"CROS_ARTIFACTS_TMP_DIR=/tmp/foo",
261*760c253cSXin Li		}
262*760c253cSXin Li		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
263*760c253cSXin Li			ctx.newCommand(clangX86_64, mainCc)))
264*760c253cSXin Li
265*760c253cSXin Li		// Verify that we added the default flag
266*760c253cSXin Li		if err := verifyArgCount(cmd, 1, "-fcrash-diagnostics-dir=/tmp/foo/toolchain/clang_crash_diagnostics"); err != nil {
267*760c253cSXin Li			t.Error(err)
268*760c253cSXin Li		}
269*760c253cSXin Li	})
270*760c253cSXin Li}
271*760c253cSXin Li
272*760c253cSXin Li// If "crash-diagnostics-dir" flag is already provided by the user, only use that flag and don't add a dupe
273*760c253cSXin Lifunc TestCrashDiagUserFlag(t *testing.T) {
274*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
275*760c253cSXin Li		ctx.env = []string{
276*760c253cSXin Li			"CROS_ARTIFACTS_TMP_DIR=/tmp/foo",
277*760c253cSXin Li		}
278*760c253cSXin Li		cmd := ctx.must(callCompiler(ctx, ctx.cfg,
279*760c253cSXin Li			ctx.newCommand(clangX86_64, mainCc, "-fcrash-diagnostics-dir=/build/something/foozz")))
280*760c253cSXin Li
281*760c253cSXin Li		// Verify that user flag is not removed
282*760c253cSXin Li		if err := verifyArgCount(cmd, 1, "-fcrash-diagnostics-dir=/build/something/foozz"); err != nil {
283*760c253cSXin Li			t.Error(err)
284*760c253cSXin Li		}
285*760c253cSXin Li
286*760c253cSXin Li		// Verify that we did not add the default flag
287*760c253cSXin Li		if err := verifyArgCount(cmd, 0, "-fcrash-diagnostics-dir=/tmp/foo/toolchain/clang_crash_diagnostics"); err != nil {
288*760c253cSXin Li			t.Error(err)
289*760c253cSXin Li		}
290*760c253cSXin Li	})
291*760c253cSXin Li}
292