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