xref: /aosp_15_r20/external/toolchain-utils/compiler_wrapper/crash_builds_test.go (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1*760c253cSXin Li// Copyright 2022 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	"io"
10*760c253cSXin Li	"strings"
11*760c253cSXin Li	"testing"
12*760c253cSXin Li)
13*760c253cSXin Li
14*760c253cSXin Lifunc TestBuildWithAutoCrashDoesNothingIfCrashIsNotRequested(t *testing.T) {
15*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
16*760c253cSXin Li		neverAutoCrash := buildWithAutocrashPredicates{
17*760c253cSXin Li			allowInConfigure: true,
18*760c253cSXin Li			shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool {
19*760c253cSXin Li				return false
20*760c253cSXin Li			},
21*760c253cSXin Li		}
22*760c253cSXin Li
23*760c253cSXin Li		exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc), neverAutoCrash)
24*760c253cSXin Li		if err != nil {
25*760c253cSXin Li			t.Fatalf("unexpectedly failed with %v", err)
26*760c253cSXin Li		}
27*760c253cSXin Li		ctx.must(exitCode)
28*760c253cSXin Li		if ctx.cmdCount != 1 {
29*760c253cSXin Li			t.Errorf("expected 1 call. Got: %d", ctx.cmdCount)
30*760c253cSXin Li		}
31*760c253cSXin Li	})
32*760c253cSXin Li}
33*760c253cSXin Li
34*760c253cSXin Lifunc TestBuildWithAutoCrashSkipsAutocrashLogicIfInConfigureAndConfigureChecksDisabled(t *testing.T) {
35*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
36*760c253cSXin Li		alwaysAutocrash := buildWithAutocrashPredicates{
37*760c253cSXin Li			allowInConfigure: false,
38*760c253cSXin Li			shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool {
39*760c253cSXin Li				return true
40*760c253cSXin Li			},
41*760c253cSXin Li		}
42*760c253cSXin Li
43*760c253cSXin Li		ctx.env = append(ctx.env, "EBUILD_PHASE=configure")
44*760c253cSXin Li		exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc), alwaysAutocrash)
45*760c253cSXin Li		if err != nil {
46*760c253cSXin Li			t.Fatalf("unexpectedly failed with %v", err)
47*760c253cSXin Li		}
48*760c253cSXin Li		ctx.must(exitCode)
49*760c253cSXin Li		if ctx.cmdCount != 1 {
50*760c253cSXin Li			t.Errorf("expected 1 call. Got: %d", ctx.cmdCount)
51*760c253cSXin Li		}
52*760c253cSXin Li	})
53*760c253cSXin Li}
54*760c253cSXin Li
55*760c253cSXin Lifunc TestBuildWithAutoCrashRerunsIfPredicateRequestsCrash(t *testing.T) {
56*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
57*760c253cSXin Li		autocrashPostCmd := buildWithAutocrashPredicates{
58*760c253cSXin Li			allowInConfigure: true,
59*760c253cSXin Li			shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool {
60*760c253cSXin Li				return true
61*760c253cSXin Li			},
62*760c253cSXin Li		}
63*760c253cSXin Li
64*760c253cSXin Li		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
65*760c253cSXin Li			hasDash := false
66*760c253cSXin Li			for _, arg := range cmd.Args {
67*760c253cSXin Li				if arg == "-" {
68*760c253cSXin Li					hasDash = true
69*760c253cSXin Li					break
70*760c253cSXin Li				}
71*760c253cSXin Li			}
72*760c253cSXin Li
73*760c253cSXin Li			switch ctx.cmdCount {
74*760c253cSXin Li			case 1:
75*760c253cSXin Li				if hasDash {
76*760c253cSXin Li					t.Error("Got `-` on command 1; didn't want that.")
77*760c253cSXin Li				}
78*760c253cSXin Li				return nil
79*760c253cSXin Li			case 2:
80*760c253cSXin Li				if !hasDash {
81*760c253cSXin Li					t.Error("Didn't get `-` on command 2; wanted that.")
82*760c253cSXin Li				} else {
83*760c253cSXin Li					input := stdin.(*bytes.Buffer)
84*760c253cSXin Li					if s := input.String(); !strings.Contains(s, autocrashProgramLine) {
85*760c253cSXin Li						t.Errorf("Input was %q; expected %q to be in it", s, autocrashProgramLine)
86*760c253cSXin Li					}
87*760c253cSXin Li				}
88*760c253cSXin Li				return nil
89*760c253cSXin Li			default:
90*760c253cSXin Li				t.Fatalf("Unexpected command count: %d", ctx.cmdCount)
91*760c253cSXin Li				panic("Unreachable")
92*760c253cSXin Li			}
93*760c253cSXin Li		}
94*760c253cSXin Li
95*760c253cSXin Li		exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc), autocrashPostCmd)
96*760c253cSXin Li		if err != nil {
97*760c253cSXin Li			t.Fatalf("unexpectedly failed with %v", err)
98*760c253cSXin Li		}
99*760c253cSXin Li		ctx.must(exitCode)
100*760c253cSXin Li
101*760c253cSXin Li		if ctx.cmdCount != 2 {
102*760c253cSXin Li			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
103*760c253cSXin Li		}
104*760c253cSXin Li	})
105*760c253cSXin Li}
106*760c253cSXin Li
107*760c253cSXin Lifunc TestBuildWithAutoCrashAddsDashAndWritesToStdinIfInputFileIsNotStdin(t *testing.T) {
108*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
109*760c253cSXin Li		autocrashPostCmd := buildWithAutocrashPredicates{
110*760c253cSXin Li			allowInConfigure: true,
111*760c253cSXin Li			shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool {
112*760c253cSXin Li				return true
113*760c253cSXin Li			},
114*760c253cSXin Li		}
115*760c253cSXin Li
116*760c253cSXin Li		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
117*760c253cSXin Li			numDashes := 0
118*760c253cSXin Li			for _, arg := range cmd.Args {
119*760c253cSXin Li				if arg == "-" {
120*760c253cSXin Li					numDashes++
121*760c253cSXin Li				}
122*760c253cSXin Li			}
123*760c253cSXin Li
124*760c253cSXin Li			switch ctx.cmdCount {
125*760c253cSXin Li			case 1:
126*760c253cSXin Li				if numDashes != 0 {
127*760c253cSXin Li					t.Errorf("Got %d dashes on command 1; want 0", numDashes)
128*760c253cSXin Li				}
129*760c253cSXin Li				return nil
130*760c253cSXin Li			case 2:
131*760c253cSXin Li				if numDashes != 1 {
132*760c253cSXin Li					t.Errorf("Got %d dashes on command 2; want 1", numDashes)
133*760c253cSXin Li				}
134*760c253cSXin Li
135*760c253cSXin Li				input := stdin.(*bytes.Buffer).String()
136*760c253cSXin Li				stdinHasAutocrashLine := strings.Contains(input, autocrashProgramLine)
137*760c253cSXin Li				if !stdinHasAutocrashLine {
138*760c253cSXin Li					t.Error("Got no autocrash line on the second command; wanted that")
139*760c253cSXin Li				}
140*760c253cSXin Li				return nil
141*760c253cSXin Li			default:
142*760c253cSXin Li				t.Fatalf("Unexpected command count: %d", ctx.cmdCount)
143*760c253cSXin Li				panic("Unreachable")
144*760c253cSXin Li			}
145*760c253cSXin Li		}
146*760c253cSXin Li
147*760c253cSXin Li		exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc), autocrashPostCmd)
148*760c253cSXin Li		if err != nil {
149*760c253cSXin Li			t.Fatalf("unexpectedly failed with %v", err)
150*760c253cSXin Li		}
151*760c253cSXin Li		ctx.must(exitCode)
152*760c253cSXin Li
153*760c253cSXin Li		if ctx.cmdCount != 2 {
154*760c253cSXin Li			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
155*760c253cSXin Li		}
156*760c253cSXin Li	})
157*760c253cSXin Li}
158*760c253cSXin Li
159*760c253cSXin Lifunc TestBuildWithAutoCrashAppendsToStdinIfStdinIsTheOnlyInputFile(t *testing.T) {
160*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
161*760c253cSXin Li		autocrashPostCmd := buildWithAutocrashPredicates{
162*760c253cSXin Li			allowInConfigure: true,
163*760c253cSXin Li			shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool {
164*760c253cSXin Li				return true
165*760c253cSXin Li			},
166*760c253cSXin Li		}
167*760c253cSXin Li
168*760c253cSXin Li		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
169*760c253cSXin Li			numDashes := 0
170*760c253cSXin Li			for _, arg := range cmd.Args {
171*760c253cSXin Li				if arg == "-" {
172*760c253cSXin Li					numDashes++
173*760c253cSXin Li				}
174*760c253cSXin Li			}
175*760c253cSXin Li
176*760c253cSXin Li			if numDashes != 1 {
177*760c253cSXin Li				t.Errorf("Got %d dashes on command %d (args: %#v); want 1", numDashes, ctx.cmdCount, cmd.Args)
178*760c253cSXin Li			}
179*760c253cSXin Li
180*760c253cSXin Li			input := stdin.(*bytes.Buffer).String()
181*760c253cSXin Li			stdinHasAutocrashLine := strings.Contains(input, autocrashProgramLine)
182*760c253cSXin Li
183*760c253cSXin Li			switch ctx.cmdCount {
184*760c253cSXin Li			case 1:
185*760c253cSXin Li				if stdinHasAutocrashLine {
186*760c253cSXin Li					t.Error("Got autocrash line on the first command; did not want that")
187*760c253cSXin Li				}
188*760c253cSXin Li				return nil
189*760c253cSXin Li			case 2:
190*760c253cSXin Li				if !stdinHasAutocrashLine {
191*760c253cSXin Li					t.Error("Got no autocrash line on the second command; wanted that")
192*760c253cSXin Li				}
193*760c253cSXin Li				return nil
194*760c253cSXin Li			default:
195*760c253cSXin Li				t.Fatalf("Unexpected command count: %d", ctx.cmdCount)
196*760c253cSXin Li				panic("Unreachable")
197*760c253cSXin Li			}
198*760c253cSXin Li		}
199*760c253cSXin Li
200*760c253cSXin Li		exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, "-x", "c", "-"), autocrashPostCmd)
201*760c253cSXin Li		if err != nil {
202*760c253cSXin Li			t.Fatalf("unexpectedly failed with %v", err)
203*760c253cSXin Li		}
204*760c253cSXin Li		ctx.must(exitCode)
205*760c253cSXin Li
206*760c253cSXin Li		if ctx.cmdCount != 2 {
207*760c253cSXin Li			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
208*760c253cSXin Li		}
209*760c253cSXin Li	})
210*760c253cSXin Li}
211*760c253cSXin Li
212*760c253cSXin Lifunc TestCrashBuildFiltersObjectFileOptionOnCrashes(t *testing.T) {
213*760c253cSXin Li	withTestContext(t, func(ctx *testContext) {
214*760c253cSXin Li		autocrashPostCmd := buildWithAutocrashPredicates{
215*760c253cSXin Li			allowInConfigure: true,
216*760c253cSXin Li			shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool {
217*760c253cSXin Li				return true
218*760c253cSXin Li			},
219*760c253cSXin Li		}
220*760c253cSXin Li
221*760c253cSXin Li		const outputFileName = "/path/to/foo.o"
222*760c253cSXin Li
223*760c253cSXin Li		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
224*760c253cSXin Li			cmdOutputArg := (*string)(nil)
225*760c253cSXin Li			for i, e := range cmd.Args {
226*760c253cSXin Li				if e == "-o" {
227*760c253cSXin Li					// Assume something follows. If not, we'll crash and the
228*760c253cSXin Li					// test will fail.
229*760c253cSXin Li					cmdOutputArg = &cmd.Args[i+1]
230*760c253cSXin Li				}
231*760c253cSXin Li			}
232*760c253cSXin Li
233*760c253cSXin Li			switch ctx.cmdCount {
234*760c253cSXin Li			case 1:
235*760c253cSXin Li				if cmdOutputArg == nil || *cmdOutputArg != outputFileName {
236*760c253cSXin Li					t.Errorf("Got command args %q; want `-o %q` in them", cmd.Args, outputFileName)
237*760c253cSXin Li				}
238*760c253cSXin Li				return nil
239*760c253cSXin Li			case 2:
240*760c253cSXin Li				if cmdOutputArg != nil {
241*760c253cSXin Li					t.Errorf("Got command args %q; want no mention of `-o %q` in them", cmd.Args, outputFileName)
242*760c253cSXin Li				}
243*760c253cSXin Li				return nil
244*760c253cSXin Li			default:
245*760c253cSXin Li				t.Fatalf("Unexpected command count: %d", ctx.cmdCount)
246*760c253cSXin Li				panic("Unreachable")
247*760c253cSXin Li			}
248*760c253cSXin Li		}
249*760c253cSXin Li
250*760c253cSXin Li		exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, "-o", outputFileName, mainCc), autocrashPostCmd)
251*760c253cSXin Li		if err != nil {
252*760c253cSXin Li			t.Fatalf("unexpectedly failed with %v", err)
253*760c253cSXin Li		}
254*760c253cSXin Li		ctx.must(exitCode)
255*760c253cSXin Li
256*760c253cSXin Li		if ctx.cmdCount != 2 {
257*760c253cSXin Li			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
258*760c253cSXin Li		}
259*760c253cSXin Li	})
260*760c253cSXin Li}
261