1*760c253cSXin Li// Copyright 2019 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 "errors" 9*760c253cSXin Li "fmt" 10*760c253cSXin Li "io" 11*760c253cSXin Li "path/filepath" 12*760c253cSXin Li "strings" 13*760c253cSXin Li "testing" 14*760c253cSXin Li) 15*760c253cSXin Li 16*760c253cSXin Lifunc TestCallBisectDriver(t *testing.T) { 17*760c253cSXin Li withBisectTestContext(t, func(ctx *testContext) { 18*760c253cSXin Li ctx.env = []string{ 19*760c253cSXin Li "BISECT_STAGE=someBisectStage", 20*760c253cSXin Li "BISECT_DIR=someBisectDir", 21*760c253cSXin Li } 22*760c253cSXin Li cmd := mustCallBisectDriver(ctx, callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc))) 23*760c253cSXin Li if err := verifyPath(cmd, "bisect_driver"); err != nil { 24*760c253cSXin Li t.Error(err) 25*760c253cSXin Li } 26*760c253cSXin Li if err := verifyArgOrder(cmd, 27*760c253cSXin Li "someBisectStage", "someBisectDir", filepath.Join(ctx.tempDir, gccX86_64+".real"), "--sysroot=.*", mainCc); err != nil { 28*760c253cSXin Li t.Error(err) 29*760c253cSXin Li } 30*760c253cSXin Li }) 31*760c253cSXin Li} 32*760c253cSXin Li 33*760c253cSXin Lifunc TestCallBisectDriverWithParamsFile(t *testing.T) { 34*760c253cSXin Li withBisectTestContext(t, func(ctx *testContext) { 35*760c253cSXin Li ctx.env = []string{ 36*760c253cSXin Li "BISECT_STAGE=someBisectStage", 37*760c253cSXin Li "BISECT_DIR=someBisectDir", 38*760c253cSXin Li } 39*760c253cSXin Li paramsFile1 := filepath.Join(ctx.tempDir, "params1") 40*760c253cSXin Li ctx.writeFile(paramsFile1, "a\n#comment\n@params2") 41*760c253cSXin Li paramsFile2 := filepath.Join(ctx.tempDir, "params2") 42*760c253cSXin Li ctx.writeFile(paramsFile2, "b\n"+mainCc) 43*760c253cSXin Li 44*760c253cSXin Li cmd := mustCallBisectDriver(ctx, callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, "@"+paramsFile1))) 45*760c253cSXin Li if err := verifyArgOrder(cmd, 46*760c253cSXin Li "a", "b", mainCc); err != nil { 47*760c253cSXin Li t.Error(err) 48*760c253cSXin Li } 49*760c253cSXin Li }) 50*760c253cSXin Li} 51*760c253cSXin Li 52*760c253cSXin Lifunc TestCallBisectDriverWithCCache(t *testing.T) { 53*760c253cSXin Li withBisectTestContext(t, func(ctx *testContext) { 54*760c253cSXin Li ctx.cfg.useCCache = true 55*760c253cSXin Li cmd := ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc))) 56*760c253cSXin Li if err := verifyPath(cmd, "/usr/bin/env"); err != nil { 57*760c253cSXin Li t.Error(err) 58*760c253cSXin Li } 59*760c253cSXin Li if err := verifyArgOrder(cmd, "python3", "/usr/bin/ccache"); err != nil { 60*760c253cSXin Li t.Error(err) 61*760c253cSXin Li } 62*760c253cSXin Li if err := verifyEnvUpdate(cmd, "CCACHE_DIR=.*"); err != nil { 63*760c253cSXin Li t.Error(err) 64*760c253cSXin Li } 65*760c253cSXin Li }) 66*760c253cSXin Li} 67*760c253cSXin Li 68*760c253cSXin Lifunc TestDefaultBisectDirCros(t *testing.T) { 69*760c253cSXin Li withBisectTestContext(t, func(ctx *testContext) { 70*760c253cSXin Li ctx.env = []string{ 71*760c253cSXin Li "BISECT_STAGE=someBisectStage", 72*760c253cSXin Li } 73*760c253cSXin Li cmd := mustCallBisectDriver(ctx, callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc))) 74*760c253cSXin Li if err := verifyArgOrder(cmd, 75*760c253cSXin Li "someBisectStage", "/tmp/sysroot_bisect"); err != nil { 76*760c253cSXin Li t.Error(err) 77*760c253cSXin Li } 78*760c253cSXin Li }) 79*760c253cSXin Li} 80*760c253cSXin Li 81*760c253cSXin Lifunc TestDefaultBisectDirAndroid(t *testing.T) { 82*760c253cSXin Li withBisectTestContext(t, func(ctx *testContext) { 83*760c253cSXin Li ctx.env = []string{ 84*760c253cSXin Li "BISECT_STAGE=someBisectStage", 85*760c253cSXin Li "HOME=/somehome", 86*760c253cSXin Li } 87*760c253cSXin Li ctx.cfg.isAndroidWrapper = true 88*760c253cSXin Li cmd := mustCallBisectDriver(ctx, callCompiler(ctx, ctx.cfg, ctx.newCommand(clangAndroid, mainCc))) 89*760c253cSXin Li if err := verifyArgOrder(cmd, 90*760c253cSXin Li "someBisectStage", filepath.Join("/somehome", "ANDROID_BISECT")); err != nil { 91*760c253cSXin Li t.Error(err) 92*760c253cSXin Li } 93*760c253cSXin Li }) 94*760c253cSXin Li} 95*760c253cSXin Li 96*760c253cSXin Lifunc TestForwardStdOutAndStdErrAndExitCodeFromBisect(t *testing.T) { 97*760c253cSXin Li withBisectTestContext(t, func(ctx *testContext) { 98*760c253cSXin Li ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 99*760c253cSXin Li fmt.Fprint(stdout, "somemessage") 100*760c253cSXin Li fmt.Fprint(stderr, "someerror") 101*760c253cSXin Li return newExitCodeError(23) 102*760c253cSXin Li } 103*760c253cSXin Li exitCode := callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc)) 104*760c253cSXin Li if exitCode != 23 { 105*760c253cSXin Li t.Errorf("unexpected exit code. Got: %d", exitCode) 106*760c253cSXin Li } 107*760c253cSXin Li if ctx.stdoutString() != "somemessage" { 108*760c253cSXin Li t.Errorf("stdout was not forwarded. Got: %s", ctx.stdoutString()) 109*760c253cSXin Li } 110*760c253cSXin Li if ctx.stderrString() != "someerror" { 111*760c253cSXin Li t.Errorf("stderr was not forwarded. Got: %s", ctx.stderrString()) 112*760c253cSXin Li } 113*760c253cSXin Li }) 114*760c253cSXin Li} 115*760c253cSXin Li 116*760c253cSXin Lifunc TestForwardGeneralErrorFromBisect(t *testing.T) { 117*760c253cSXin Li withBisectTestContext(t, func(ctx *testContext) { 118*760c253cSXin Li ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 119*760c253cSXin Li return errors.New("someerror") 120*760c253cSXin Li } 121*760c253cSXin Li stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, 122*760c253cSXin Li ctx.newCommand(gccX86_64, mainCc))) 123*760c253cSXin Li if err := verifyInternalError(stderr); err != nil { 124*760c253cSXin Li t.Fatal(err) 125*760c253cSXin Li } 126*760c253cSXin Li if !strings.Contains(stderr, "someerror") { 127*760c253cSXin Li t.Errorf("unexpected error. Got: %s", stderr) 128*760c253cSXin Li } 129*760c253cSXin Li }) 130*760c253cSXin Li} 131*760c253cSXin Li 132*760c253cSXin Lifunc withBisectTestContext(t *testing.T, work func(ctx *testContext)) { 133*760c253cSXin Li withTestContext(t, func(ctx *testContext) { 134*760c253cSXin Li ctx.env = []string{"BISECT_STAGE=xyz"} 135*760c253cSXin Li // We execute the python script but replace the call to the bisect_driver with 136*760c253cSXin Li // a mock that logs the data. 137*760c253cSXin Li ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 138*760c253cSXin Li if err := verifyPath(cmd, "/usr/bin/env"); err != nil { 139*760c253cSXin Li return err 140*760c253cSXin Li } 141*760c253cSXin Li if cmd.Args[0] != "python3" { 142*760c253cSXin Li return fmt.Errorf("expected a call to python. Got: %s", cmd.Args[0]) 143*760c253cSXin Li } 144*760c253cSXin Li if cmd.Args[1] != "-c" { 145*760c253cSXin Li return fmt.Errorf("expected an inline python script. Got: %s", cmd.Args) 146*760c253cSXin Li } 147*760c253cSXin Li script := cmd.Args[2] 148*760c253cSXin Li mock := ` 149*760c253cSXin Liclass BisectDriver: 150*760c253cSXin Li def __init__(self): 151*760c253cSXin Li self.VALID_MODES = ['POPULATE_GOOD', 'POPULATE_BAD', 'TRIAGE'] 152*760c253cSXin Li def bisect_driver(self, bisect_stage, bisect_dir, execargs): 153*760c253cSXin Li print('command bisect_driver') 154*760c253cSXin Li print('arg %s' % bisect_stage) 155*760c253cSXin Li print('arg %s' % bisect_dir) 156*760c253cSXin Li for arg in execargs: 157*760c253cSXin Li print('arg %s' % arg) 158*760c253cSXin Li 159*760c253cSXin Libisect_driver = BisectDriver() 160*760c253cSXin Li` 161*760c253cSXin Li script = mock + script 162*760c253cSXin Li script = strings.Replace(script, "import bisect_driver", "", -1) 163*760c253cSXin Li cmdCopy := *cmd 164*760c253cSXin Li cmdCopy.Args = append(append(cmd.Args[:2], script), cmd.Args[3:]...) 165*760c253cSXin Li // Evaluate the python script, but replace the call to the bisect_driver 166*760c253cSXin Li // with a log statement so that we can assert it. 167*760c253cSXin Li return runCmd(ctx, &cmdCopy, nil, stdout, stderr) 168*760c253cSXin Li } 169*760c253cSXin Li work(ctx) 170*760c253cSXin Li }) 171*760c253cSXin Li} 172*760c253cSXin Li 173*760c253cSXin Lifunc mustCallBisectDriver(ctx *testContext, exitCode int) *command { 174*760c253cSXin Li ctx.must(exitCode) 175*760c253cSXin Li cmd := &command{} 176*760c253cSXin Li for _, line := range strings.Split(ctx.stdoutString(), "\n") { 177*760c253cSXin Li if prefix := "command "; strings.HasPrefix(line, prefix) { 178*760c253cSXin Li cmd.Path = line[len(prefix):] 179*760c253cSXin Li } else if prefix := "arg "; strings.HasPrefix(line, prefix) { 180*760c253cSXin Li cmd.Args = append(cmd.Args, line[len(prefix):]) 181*760c253cSXin Li } 182*760c253cSXin Li } 183*760c253cSXin Li return cmd 184*760c253cSXin Li} 185