// Copyright 2022 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package main import ( "bytes" "io" "strings" "testing" ) func TestBuildWithAutoCrashDoesNothingIfCrashIsNotRequested(t *testing.T) { withTestContext(t, func(ctx *testContext) { neverAutoCrash := buildWithAutocrashPredicates{ allowInConfigure: true, shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool { return false }, } exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc), neverAutoCrash) if err != nil { t.Fatalf("unexpectedly failed with %v", err) } ctx.must(exitCode) if ctx.cmdCount != 1 { t.Errorf("expected 1 call. Got: %d", ctx.cmdCount) } }) } func TestBuildWithAutoCrashSkipsAutocrashLogicIfInConfigureAndConfigureChecksDisabled(t *testing.T) { withTestContext(t, func(ctx *testContext) { alwaysAutocrash := buildWithAutocrashPredicates{ allowInConfigure: false, shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool { return true }, } ctx.env = append(ctx.env, "EBUILD_PHASE=configure") exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc), alwaysAutocrash) if err != nil { t.Fatalf("unexpectedly failed with %v", err) } ctx.must(exitCode) if ctx.cmdCount != 1 { t.Errorf("expected 1 call. Got: %d", ctx.cmdCount) } }) } func TestBuildWithAutoCrashRerunsIfPredicateRequestsCrash(t *testing.T) { withTestContext(t, func(ctx *testContext) { autocrashPostCmd := buildWithAutocrashPredicates{ allowInConfigure: true, shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool { return true }, } ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { hasDash := false for _, arg := range cmd.Args { if arg == "-" { hasDash = true break } } switch ctx.cmdCount { case 1: if hasDash { t.Error("Got `-` on command 1; didn't want that.") } return nil case 2: if !hasDash { t.Error("Didn't get `-` on command 2; wanted that.") } else { input := stdin.(*bytes.Buffer) if s := input.String(); !strings.Contains(s, autocrashProgramLine) { t.Errorf("Input was %q; expected %q to be in it", s, autocrashProgramLine) } } return nil default: t.Fatalf("Unexpected command count: %d", ctx.cmdCount) panic("Unreachable") } } exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc), autocrashPostCmd) if err != nil { t.Fatalf("unexpectedly failed with %v", err) } ctx.must(exitCode) if ctx.cmdCount != 2 { t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount) } }) } func TestBuildWithAutoCrashAddsDashAndWritesToStdinIfInputFileIsNotStdin(t *testing.T) { withTestContext(t, func(ctx *testContext) { autocrashPostCmd := buildWithAutocrashPredicates{ allowInConfigure: true, shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool { return true }, } ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { numDashes := 0 for _, arg := range cmd.Args { if arg == "-" { numDashes++ } } switch ctx.cmdCount { case 1: if numDashes != 0 { t.Errorf("Got %d dashes on command 1; want 0", numDashes) } return nil case 2: if numDashes != 1 { t.Errorf("Got %d dashes on command 2; want 1", numDashes) } input := stdin.(*bytes.Buffer).String() stdinHasAutocrashLine := strings.Contains(input, autocrashProgramLine) if !stdinHasAutocrashLine { t.Error("Got no autocrash line on the second command; wanted that") } return nil default: t.Fatalf("Unexpected command count: %d", ctx.cmdCount) panic("Unreachable") } } exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, mainCc), autocrashPostCmd) if err != nil { t.Fatalf("unexpectedly failed with %v", err) } ctx.must(exitCode) if ctx.cmdCount != 2 { t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount) } }) } func TestBuildWithAutoCrashAppendsToStdinIfStdinIsTheOnlyInputFile(t *testing.T) { withTestContext(t, func(ctx *testContext) { autocrashPostCmd := buildWithAutocrashPredicates{ allowInConfigure: true, shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool { return true }, } ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { numDashes := 0 for _, arg := range cmd.Args { if arg == "-" { numDashes++ } } if numDashes != 1 { t.Errorf("Got %d dashes on command %d (args: %#v); want 1", numDashes, ctx.cmdCount, cmd.Args) } input := stdin.(*bytes.Buffer).String() stdinHasAutocrashLine := strings.Contains(input, autocrashProgramLine) switch ctx.cmdCount { case 1: if stdinHasAutocrashLine { t.Error("Got autocrash line on the first command; did not want that") } return nil case 2: if !stdinHasAutocrashLine { t.Error("Got no autocrash line on the second command; wanted that") } return nil default: t.Fatalf("Unexpected command count: %d", ctx.cmdCount) panic("Unreachable") } } exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, "-x", "c", "-"), autocrashPostCmd) if err != nil { t.Fatalf("unexpectedly failed with %v", err) } ctx.must(exitCode) if ctx.cmdCount != 2 { t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount) } }) } func TestCrashBuildFiltersObjectFileOptionOnCrashes(t *testing.T) { withTestContext(t, func(ctx *testContext) { autocrashPostCmd := buildWithAutocrashPredicates{ allowInConfigure: true, shouldAutocrash: func(env, *config, *command, compilerExecInfo) bool { return true }, } const outputFileName = "/path/to/foo.o" ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { cmdOutputArg := (*string)(nil) for i, e := range cmd.Args { if e == "-o" { // Assume something follows. If not, we'll crash and the // test will fail. cmdOutputArg = &cmd.Args[i+1] } } switch ctx.cmdCount { case 1: if cmdOutputArg == nil || *cmdOutputArg != outputFileName { t.Errorf("Got command args %q; want `-o %q` in them", cmd.Args, outputFileName) } return nil case 2: if cmdOutputArg != nil { t.Errorf("Got command args %q; want no mention of `-o %q` in them", cmd.Args, outputFileName) } return nil default: t.Fatalf("Unexpected command count: %d", ctx.cmdCount) panic("Unreachable") } } exitCode, err := buildWithAutocrashImpl(ctx, ctx.cfg, ctx.newCommand(clangX86_64, "-o", outputFileName, mainCc), autocrashPostCmd) if err != nil { t.Fatalf("unexpectedly failed with %v", err) } ctx.must(exitCode) if ctx.cmdCount != 2 { t.Errorf("expected 2 calls. Got: %d", ctx.cmdCount) } }) }