xref: /aosp_15_r20/external/toolchain-utils/compiler_wrapper/disable_werror_flag_test.go (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package main
6
7import (
8	"encoding/json"
9	"errors"
10	"fmt"
11	"io"
12	"io/ioutil"
13	"os"
14	"path"
15	"path/filepath"
16	"reflect"
17	"regexp"
18	"strings"
19	"testing"
20)
21
22const arbitraryWerrorStderr = "error: foo [-Werror,-Wfoo]"
23
24func TestOmitDoubleBuildForSuccessfulCall(t *testing.T) {
25	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
26		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
27		if ctx.cmdCount != 1 {
28			t.Errorf("expected 1 call. Got: %d", ctx.cmdCount)
29		}
30	})
31}
32
33func TestOmitDoubleBuildForGeneralError(t *testing.T) {
34	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
35		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
36			return errors.New("someerror")
37		}
38		stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
39		if err := verifyInternalError(stderr); err != nil {
40			t.Fatal(err)
41		}
42		if !strings.Contains(stderr, "someerror") {
43			t.Errorf("unexpected error. Got: %s", stderr)
44		}
45		if ctx.cmdCount != 1 {
46			t.Errorf("expected 1 call. Got: %d", ctx.cmdCount)
47		}
48	})
49}
50
51func TestDoubleBuildWithWNoErrorFlag(t *testing.T) {
52	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
53		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
54			switch ctx.cmdCount {
55			case 1:
56				if err := verifyArgCount(cmd, 0, "-Wno-error"); err != nil {
57					return err
58				}
59				fmt.Fprint(stderr, arbitraryWerrorStderr)
60				return newExitCodeError(1)
61			case 2:
62				if err := verifyArgCount(cmd, 1, "-Wno-error"); err != nil {
63					return err
64				}
65				return nil
66			default:
67				t.Fatalf("unexpected command: %#v", cmd)
68				return nil
69			}
70		}
71		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
72		if ctx.cmdCount != 2 {
73			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
74		}
75	})
76}
77
78func TestDoubleBuildUsesSpecificWnoErrorFlagsForWarningsThatDefaultToErrors(t *testing.T) {
79	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
80		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
81			switch ctx.cmdCount {
82			case 1:
83				if err := verifyArgCount(cmd, 0, "-Wno-error"); err != nil {
84					return err
85				}
86				fmt.Fprint(stderr, "error: foo [-Wfoo]")
87				return newExitCodeError(1)
88			case 2:
89				if err := verifyArgCount(cmd, 1, "-Wno-error=foo"); err != nil {
90					return err
91				}
92				if err := verifyArgCount(cmd, 1, "-Wno-error"); err != nil {
93					return err
94				}
95				return nil
96			default:
97				t.Fatalf("unexpected command: %#v", cmd)
98				return nil
99			}
100		}
101		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
102		if ctx.cmdCount != 2 {
103			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
104		}
105	})
106}
107
108func TestDoubleBuildDoesntRecompileIfNoObviousWerrorsExist(t *testing.T) {
109	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
110		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
111			if ctx.cmdCount != 1 {
112				t.Fatalf("unexpected command: %#v", cmd)
113			}
114			if err := verifyArgCount(cmd, 0, "-Wno-error"); err != nil {
115				return err
116			}
117			fmt.Fprint(stderr, "error: foo bar baz\nwarning: foo [-Wfoo]")
118			return newExitCodeError(1)
119		}
120		exitCode := callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc))
121		if exitCode != 1 {
122			t.Errorf("got exit code %d; want 1", exitCode)
123		}
124		if ctx.cmdCount != 1 {
125			t.Errorf("expected 1 call. Got: %d", ctx.cmdCount)
126		}
127	})
128}
129
130func TestKnownConfigureFileParsing(t *testing.T) {
131	withTestContext(t, func(ctx *testContext) {
132		for _, f := range []string{"conftest.c", "conftest.cpp", "/dev/null"} {
133			if !isLikelyAConfTest(ctx.cfg, ctx.newCommand(clangX86_64, f)) {
134				t.Errorf("%q isn't considered a conf test file", f)
135			}
136		}
137	})
138}
139
140func TestDoubleBuildWithKnownConfigureFile(t *testing.T) {
141	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
142		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
143			switch ctx.cmdCount {
144			case 1:
145				if err := verifyArgCount(cmd, 0, "-Wno-error"); err != nil {
146					return err
147				}
148				fmt.Fprint(stderr, arbitraryWerrorStderr)
149				return newExitCodeError(1)
150			default:
151				t.Fatalf("unexpected command: %#v", cmd)
152				return nil
153			}
154		}
155
156		ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, "conftest.c")))
157		if ctx.cmdCount != 1 {
158			t.Errorf("expected 1 call. Got: %d", ctx.cmdCount)
159		}
160	})
161}
162
163func TestDoubleBuildWithWNoErrorAndCCache(t *testing.T) {
164	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
165		ctx.cfg.useCCache = true
166		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
167			switch ctx.cmdCount {
168			case 1:
169				// TODO: This is a bug in the old wrapper that it drops the ccache path
170				// during double build. Fix this once we don't compare to the old wrapper anymore.
171				if err := verifyPath(cmd, "ccache"); err != nil {
172					return err
173				}
174				fmt.Fprint(stderr, arbitraryWerrorStderr)
175				return newExitCodeError(1)
176			case 2:
177				if err := verifyPath(cmd, "ccache"); err != nil {
178					return err
179				}
180				return nil
181			default:
182				t.Fatalf("unexpected command: %#v", cmd)
183				return nil
184			}
185		}
186		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
187		if ctx.cmdCount != 2 {
188			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
189		}
190	})
191}
192
193func TestForwardStdoutAndStderrWhenDoubleBuildSucceeds(t *testing.T) {
194	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
195		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
196			switch ctx.cmdCount {
197			case 1:
198				fmt.Fprint(stdout, "originalmessage")
199				fmt.Fprint(stderr, arbitraryWerrorStderr)
200				return newExitCodeError(1)
201			case 2:
202				fmt.Fprint(stdout, "retrymessage")
203				fmt.Fprint(stderr, "retryerror")
204				return nil
205			default:
206				t.Fatalf("unexpected command: %#v", cmd)
207				return nil
208			}
209		}
210		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
211		if err := verifyNonInternalError(ctx.stderrString(), "retryerror"); err != nil {
212			t.Error(err)
213		}
214		if !strings.Contains(ctx.stdoutString(), "retrymessage") {
215			t.Errorf("unexpected stdout. Got: %s", ctx.stdoutString())
216		}
217	})
218}
219
220func TestForwardStdoutAndStderrWhenDoubleBuildFails(t *testing.T) {
221	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
222		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
223			switch ctx.cmdCount {
224			case 1:
225				fmt.Fprint(stdout, "originalmessage")
226				fmt.Fprint(stderr, arbitraryWerrorStderr)
227				return newExitCodeError(3)
228			case 2:
229				fmt.Fprint(stdout, "retrymessage")
230				fmt.Fprint(stderr, "retryerror")
231				return newExitCodeError(5)
232			default:
233				t.Fatalf("unexpected command: %#v", cmd)
234				return nil
235			}
236		}
237		exitCode := callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc))
238		if exitCode != 3 {
239			t.Errorf("unexpected exitcode. Got: %d", exitCode)
240		}
241		if err := verifyNonInternalError(ctx.stderrString(), regexp.QuoteMeta(arbitraryWerrorStderr)); err != nil {
242			t.Error(err)
243		}
244		if !strings.Contains(ctx.stdoutString(), "originalmessage") {
245			t.Errorf("unexpected stdout. Got: %s", ctx.stdoutString())
246		}
247	})
248}
249
250func TestForwardStdinFromDoubleBuild(t *testing.T) {
251	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
252		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
253			// Note: This is called for the clang syntax call as well as for
254			// the gcc call, and we assert that stdin is cloned and forwarded
255			// to both.
256			stdinStr := ctx.readAllString(stdin)
257			if stdinStr != "someinput" {
258				return fmt.Errorf("unexpected stdin. Got: %s", stdinStr)
259			}
260
261			switch ctx.cmdCount {
262			case 1:
263				fmt.Fprint(stderr, arbitraryWerrorStderr)
264				return newExitCodeError(1)
265			case 2:
266				return nil
267			default:
268				t.Fatalf("unexpected command: %#v", cmd)
269				return nil
270			}
271		}
272		io.WriteString(&ctx.stdinBuffer, "someinput")
273		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, "-", mainCc)))
274	})
275}
276
277func TestForwardGeneralErrorWhenDoubleBuildFails(t *testing.T) {
278	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
279		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
280			switch ctx.cmdCount {
281			case 1:
282				fmt.Fprint(stderr, arbitraryWerrorStderr)
283				return newExitCodeError(3)
284			case 2:
285				return errors.New("someerror")
286			default:
287				t.Fatalf("unexpected command: %#v", cmd)
288				return nil
289			}
290		}
291		stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
292		if err := verifyInternalError(stderr); err != nil {
293			t.Error(err)
294		}
295		if !strings.Contains(stderr, "someerror") {
296			t.Errorf("unexpected stderr. Got: %s", stderr)
297		}
298	})
299}
300
301func TestOmitLogWarningsIfNoDoubleBuild(t *testing.T) {
302	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
303		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
304		if ctx.cmdCount != 1 {
305			t.Errorf("expected 1 call. Got: %d", ctx.cmdCount)
306		}
307		if loggedWarnings := readLoggedWarnings(ctx); loggedWarnings != nil {
308			t.Errorf("expected no logged warnings. Got: %#v", loggedWarnings)
309		}
310	})
311}
312
313func TestLogWarningsWhenDoubleBuildSucceeds(t *testing.T) {
314	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
315		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
316			switch ctx.cmdCount {
317			case 1:
318				fmt.Fprint(stdout, "originalmessage")
319				fmt.Fprint(stderr, arbitraryWerrorStderr)
320				return newExitCodeError(1)
321			case 2:
322				fmt.Fprint(stdout, "retrymessage")
323				fmt.Fprint(stderr, "retryerror")
324				return nil
325			default:
326				t.Fatalf("unexpected command: %#v", cmd)
327				return nil
328			}
329		}
330		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
331		loggedWarnings := readLoggedWarnings(ctx)
332		if loggedWarnings == nil {
333			t.Fatal("expected logged warnings")
334		}
335		if loggedWarnings.Cwd != ctx.getwd() {
336			t.Fatalf("unexpected cwd. Got: %s", loggedWarnings.Cwd)
337		}
338		loggedCmd := &command{
339			Path: loggedWarnings.Command[0],
340			Args: loggedWarnings.Command[1:],
341		}
342		if err := verifyPath(loggedCmd, "usr/bin/clang"); err != nil {
343			t.Error(err)
344		}
345		if err := verifyArgOrder(loggedCmd, "--sysroot=.*", mainCc); err != nil {
346			t.Error(err)
347		}
348	})
349}
350
351func TestLogWarningsWhenDoubleBuildFails(t *testing.T) {
352	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
353		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
354			switch ctx.cmdCount {
355			case 1:
356				fmt.Fprint(stdout, "originalmessage")
357				fmt.Fprint(stderr, arbitraryWerrorStderr)
358				return newExitCodeError(1)
359			case 2:
360				fmt.Fprint(stdout, "retrymessage")
361				fmt.Fprint(stderr, "retryerror")
362				return newExitCodeError(1)
363			default:
364				t.Fatalf("unexpected command: %#v", cmd)
365				return nil
366			}
367		}
368		ctx.mustFail(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
369		loggedWarnings := readLoggedWarnings(ctx)
370		if loggedWarnings != nil {
371			t.Fatal("expected no warnings to be logged")
372		}
373	})
374}
375
376func withForceDisableWErrorTestContext(t *testing.T, work func(ctx *testContext)) {
377	withTestContext(t, func(ctx *testContext) {
378		ctx.NoteTestWritesToUmask()
379
380		ctx.env = []string{
381			"FORCE_DISABLE_WERROR=1",
382			artifactsTmpDirEnvName + "=" + path.Join(ctx.tempDir, "artifacts"),
383		}
384		work(ctx)
385	})
386}
387
388func readLoggedWarnings(ctx *testContext) *warningsJSONData {
389	warningsDir := getForceDisableWerrorDir(ctx, ctx.cfg)
390	files, err := ioutil.ReadDir(warningsDir)
391	if err != nil {
392		if _, ok := err.(*os.PathError); ok {
393			return nil
394		}
395		ctx.t.Fatal(err)
396	}
397	if len(files) != 1 {
398		ctx.t.Fatalf("expected 1 warning log file. Got: %s", files)
399	}
400	data, err := ioutil.ReadFile(filepath.Join(warningsDir, files[0].Name()))
401	if err != nil {
402		ctx.t.Fatal(err)
403	}
404	jsonData := warningsJSONData{}
405	if err := json.Unmarshal(data, &jsonData); err != nil {
406		ctx.t.Fatal(err)
407	}
408	return &jsonData
409}
410
411func TestDoubleBuildWerrorChmodsThingsAppropriately(t *testing.T) {
412	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
413		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
414			switch ctx.cmdCount {
415			case 1:
416				if err := verifyArgCount(cmd, 0, "-Wno-error"); err != nil {
417					return err
418				}
419				fmt.Fprint(stderr, arbitraryWerrorStderr)
420				return newExitCodeError(1)
421			case 2:
422				if err := verifyArgCount(cmd, 1, "-Wno-error"); err != nil {
423					return err
424				}
425				return nil
426			default:
427				t.Fatalf("unexpected command: %#v", cmd)
428				return nil
429			}
430		}
431		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc)))
432		if ctx.cmdCount != 2 {
433			// Later errors are likely senseless if we didn't get called twice.
434			t.Fatalf("expected 2 calls. Got: %d", ctx.cmdCount)
435		}
436
437		warningsDirPath := getForceDisableWerrorDir(ctx, ctx.cfg)
438		t.Logf("Warnings dir is at %q", warningsDirPath)
439		warningsDir, err := os.Open(warningsDirPath)
440		if err != nil {
441			t.Fatalf("failed to open the new warnings dir: %v", err)
442		}
443		defer warningsDir.Close()
444
445		fi, err := warningsDir.Stat()
446		if err != nil {
447			t.Fatalf("failed stat'ing the warnings dir: %v", err)
448		}
449
450		permBits := func(mode os.FileMode) int { return int(mode & 0777) }
451
452		if perms := permBits(fi.Mode()); perms != 0777 {
453			t.Errorf("mode for tempdir are %#o; expected 0777", perms)
454		}
455
456		entries, err := warningsDir.Readdir(0)
457		if err != nil {
458			t.Fatalf("failed reading entries of the tempdir: %v", err)
459		}
460
461		if len(entries) != 1 {
462			t.Errorf("found %d tempfiles in the tempdir; expected 1", len(entries))
463		}
464
465		for _, e := range entries {
466			if perms := permBits(e.Mode()); perms != 0666 {
467				t.Errorf("mode for %q is %#o; expected 0666", e.Name(), perms)
468			}
469		}
470	})
471}
472
473type commandBuilderOpts struct {
474	isGcc  bool
475	cflags []string
476}
477
478func newWerrorCommandBuilderOrDie(t *testing.T, ctx *testContext, opts commandBuilderOpts) *commandBuilder {
479	compiler := "clang"
480	if opts.isGcc {
481		compiler = "gcc"
482	}
483	cmd := ctx.newCommand(compiler, opts.cflags...)
484	b, err := newCommandBuilder(ctx, ctx.cfg, cmd)
485	if err != nil {
486		t.Fatalf("Constructing builder unexpectedly failed: %v", err)
487	}
488	return b
489}
490
491func TestAndroidDisableWerror(t *testing.T) {
492	withTestContext(t, func(ctx *testContext) {
493		ctx.cfg.isAndroidWrapper = true
494
495		builder := newWerrorCommandBuilderOrDie(t, ctx, commandBuilderOpts{})
496
497		// Disable werror ON
498		ctx.cfg.useLlvmNext = true
499		if !processForceDisableWerrorFlag(ctx, ctx.cfg, builder).enabled {
500			t.Errorf("disable Werror not enabled for Android with useLlvmNext")
501		}
502
503		// Disable werror OFF
504		ctx.cfg.useLlvmNext = false
505		if processForceDisableWerrorFlag(ctx, ctx.cfg, builder).enabled {
506			t.Errorf("disable-Werror enabled for Android without useLlvmNext")
507		}
508	})
509}
510
511func TestChromeOSNoForceDisableWerror(t *testing.T) {
512	withTestContext(t, func(ctx *testContext) {
513		builder := newWerrorCommandBuilderOrDie(t, ctx, commandBuilderOpts{})
514		if processForceDisableWerrorFlag(ctx, ctx.cfg, builder).enabled {
515			t.Errorf("disable Werror enabled for ChromeOS without FORCE_DISABLE_WERROR set")
516		}
517	})
518}
519
520func TestChromeOSForceDisableWerrorOnlyAppliesToClang(t *testing.T) {
521	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
522		ctx.env = append(ctx.env, "FORCE_DISABLE_WERROR=1")
523		builder := newWerrorCommandBuilderOrDie(t, ctx, commandBuilderOpts{})
524		if !processForceDisableWerrorFlag(ctx, ctx.cfg, builder).enabled {
525			t.Errorf("Disable -Werror should be enabled for clang.")
526		}
527
528		builder = newWerrorCommandBuilderOrDie(t, ctx, commandBuilderOpts{isGcc: true})
529		if processForceDisableWerrorFlag(ctx, ctx.cfg, builder).enabled {
530			t.Errorf("Disable -Werror should be disabled for gcc.")
531		}
532	})
533}
534
535func TestChromeOSForceDisableWerrorWorksAsFlag(t *testing.T) {
536	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
537		builder := newWerrorCommandBuilderOrDie(t, ctx, commandBuilderOpts{
538			cflags: []string{"-D_CROSTC_FORCE_DISABLE_WERROR=/foo"},
539		})
540		werrorConfig := processForceDisableWerrorFlag(ctx, ctx.cfg, builder)
541		if !werrorConfig.enabled {
542			t.Fatalf("Disable -Werror should be enabled by flag.")
543		}
544
545		if werrorConfig.reportToStdout {
546			t.Errorf("Stdout reporting should be disabled on ChromeOS")
547		} else if werrorConfig.reportDir != "/foo" {
548			t.Errorf("Got werror report dir %s; want /foo", werrorConfig.reportDir)
549		}
550	})
551}
552
553func TestChromeOSForceDisableWerrorIsSuppressedInSrcConfigure(t *testing.T) {
554	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
555		ctx.env = append(ctx.env, "EBUILD_PHASE=configure")
556		builder := newWerrorCommandBuilderOrDie(t, ctx, commandBuilderOpts{
557			cflags: []string{"-D_CROSTC_FORCE_DISABLE_WERROR=/foo"},
558		})
559		werrorConfig := processForceDisableWerrorFlag(ctx, ctx.cfg, builder)
560		if werrorConfig.enabled {
561			t.Fatalf("Disable -Werror should be disabled during src_configure.")
562		}
563	})
564}
565
566func TestChromeOSForceDisableWerrorRemovesClangFlags(t *testing.T) {
567	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
568		builder := newWerrorCommandBuilderOrDie(t, ctx, commandBuilderOpts{
569			cflags: []string{
570				"-D_CROSTC_FORCE_DISABLE_WERROR=/foo",
571				"-D_CROSTC_FORCE_DISABLE_WERROR=/bar",
572				"-Wfoo",
573				"-D_CROSTC_FORCE_DISABLE_WERROR=/baz",
574			},
575		})
576		werrorConfig := processForceDisableWerrorFlag(ctx, ctx.cfg, builder)
577		if !werrorConfig.enabled {
578			t.Fatalf("Disable -Werror should be enabled by flag.")
579		}
580
581		if werrorConfig.reportToStdout {
582			t.Errorf("Stdout reporting should be disabled on ChromeOS")
583		} else if werrorConfig.reportDir != "/baz" {
584			t.Errorf("Got werror report dir %s; want /baz", werrorConfig.reportDir)
585		}
586
587		args := builder.build().Args
588		if want := []string{"-Wfoo"}; !reflect.DeepEqual(args, want) {
589			t.Errorf("Got builder args %#v; want %#v", args, want)
590		}
591	})
592}
593
594func TestChromeOSForceDisableWerrorRemovesGCCFlagsButDoesNotEnableFeature(t *testing.T) {
595	withForceDisableWErrorTestContext(t, func(ctx *testContext) {
596		builder := newWerrorCommandBuilderOrDie(t, ctx, commandBuilderOpts{
597			cflags: []string{
598				"-D_CROSTC_FORCE_DISABLE_WERROR=/foo",
599				"-D_CROSTC_FORCE_DISABLE_WERROR=/bar",
600				"-Wfoo",
601				"-D_CROSTC_FORCE_DISABLE_WERROR=/baz",
602			},
603		})
604		builder.target.compilerType = gccType
605		werrorConfig := processForceDisableWerrorFlag(ctx, ctx.cfg, builder)
606		if werrorConfig.enabled {
607			t.Fatalf("Disable -Werror should not be enabled for GCC.")
608		}
609
610		args := builder.build().Args
611		if want := []string{"-Wfoo"}; !reflect.DeepEqual(args, want) {
612			t.Errorf("Got builder args %#v; want %#v", args, want)
613		}
614	})
615}
616
617func TestClangTidyNoDoubleBuild(t *testing.T) {
618	withTestContext(t, func(ctx *testContext) {
619		ctx.cfg.isAndroidWrapper = true
620		ctx.cfg.useLlvmNext = true
621		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangTidyAndroid, "--", mainCc)))
622		if ctx.cmdCount != 1 {
623			t.Errorf("expected 1 call. Got: %d", ctx.cmdCount)
624		}
625	})
626}
627
628func withAndroidClangTidyTestContext(t *testing.T, work func(ctx *testContext)) {
629	withTestContext(t, func(ctx *testContext) {
630		ctx.cfg.isAndroidWrapper = true
631		ctx.cfg.useLlvmNext = true
632		ctx.env = []string{"OUT_DIR=/tmp"}
633
634		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
635			hasArg := func(s string) bool {
636				for _, e := range cmd.Args {
637					if strings.Contains(e, s) {
638						return true
639					}
640				}
641				return false
642			}
643			switch ctx.cmdCount {
644			case 1:
645				if hasArg("-Werror") {
646					fmt.Fprint(stdout, "clang-diagnostic-")
647					return newExitCodeError(1)
648				}
649				if hasArg("-warnings-as-errors") {
650					fmt.Fprint(stdout, "warnings-as-errors")
651					return newExitCodeError(1)
652				}
653				return nil
654			case 2:
655				if hasArg("warnings-as-errors") {
656					return fmt.Errorf("Unexpected arg warnings-as-errors found.  All args: %s", cmd.Args)
657				}
658				return nil
659			default:
660				t.Fatalf("unexpected command: %#v", cmd)
661				return nil
662			}
663		}
664		work(ctx)
665	})
666}
667
668func TestClangTidyDoubleBuildClangTidyError(t *testing.T) {
669	withAndroidClangTidyTestContext(t, func(ctx *testContext) {
670		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangTidyAndroid, "-warnings-as-errors=*", "--", mainCc)))
671		if ctx.cmdCount != 2 {
672			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
673		}
674	})
675}
676
677func TestClangTidyDoubleBuildClangError(t *testing.T) {
678	withAndroidClangTidyTestContext(t, func(ctx *testContext) {
679		ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangTidyAndroid, "-Werrors=*", "--", mainCc)))
680		if ctx.cmdCount != 2 {
681			t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount)
682		}
683	})
684}
685
686func TestProcPidStatParsingWorksAsIntended(t *testing.T) {
687	t.Parallel()
688
689	type expected struct {
690		parent int
691		ok     bool
692	}
693
694	testCases := []struct {
695		input    string
696		expected expected
697	}{
698		{
699			input: "2556041 (cat) R 2519408 2556041 2519408 34818 2556041 4194304",
700			expected: expected{
701				parent: 2519408,
702				ok:     true,
703			},
704		},
705		{
706			input: "2556041 (c a t) R 2519408 2556041 2519408 34818 2556041 4194304",
707			expected: expected{
708				parent: 2519408,
709				ok:     true,
710			},
711		},
712		{
713			input: "",
714			expected: expected{
715				ok: false,
716			},
717		},
718		{
719			input: "foo (bar)",
720			expected: expected{
721				ok: false,
722			},
723		},
724		{
725			input: "foo (bar) baz",
726			expected: expected{
727				ok: false,
728			},
729		},
730		{
731			input: "foo (bar) baz 1qux2",
732			expected: expected{
733				ok: false,
734			},
735		},
736	}
737
738	for _, tc := range testCases {
739		parent, ok := parseParentPidFromPidStat(tc.input)
740		if tc.expected.ok != ok {
741			t.Errorf("Got ok=%v when parsing %q; expected %v", ok, tc.input, tc.expected.ok)
742			continue
743		}
744
745		if tc.expected.parent != parent {
746			t.Errorf("Got parent=%v when parsing %q; expected %v", parent, tc.input, tc.expected.parent)
747		}
748	}
749}
750