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