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