aboutsummaryrefslogtreecommitdiff
path: root/compiler_wrapper/bisect_flag_test.go
blob: 2071a5b29cf8dd42fd1c183cccede510cc53e873 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Copyright 2019 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 (
	"errors"
	"fmt"
	"io"
	"path/filepath"
	"strings"
	"testing"
)

func TestCallBisectDriver(t *testing.T) {
	withBisectTestContext(t, func(ctx *testContext) {
		ctx.env = []string{
			"BISECT_STAGE=someBisectStage",
			"BISECT_DIR=someBisectDir",
		}
		cmd := mustCallBisectDriver(ctx, callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc)))
		if err := verifyPath(cmd, "bisect_driver"); err != nil {
			t.Error(err)
		}
		if err := verifyArgOrder(cmd,
			"someBisectStage", "someBisectDir", filepath.Join(ctx.tempDir, gccX86_64+".real"), "--sysroot=.*", mainCc); err != nil {
			t.Error(err)
		}
	})
}

func TestCallBisectDriverWithParamsFile(t *testing.T) {
	withBisectTestContext(t, func(ctx *testContext) {
		ctx.env = []string{
			"BISECT_STAGE=someBisectStage",
			"BISECT_DIR=someBisectDir",
		}
		paramsFile1 := filepath.Join(ctx.tempDir, "params1")
		ctx.writeFile(paramsFile1, "a\n#comment\n@params2")
		paramsFile2 := filepath.Join(ctx.tempDir, "params2")
		ctx.writeFile(paramsFile2, "b\n"+mainCc)

		cmd := mustCallBisectDriver(ctx, callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, "@"+paramsFile1)))
		if err := verifyArgOrder(cmd,
			"a", "b", mainCc); err != nil {
			t.Error(err)
		}
	})
}

func TestCallBisectDriverWithCCache(t *testing.T) {
	withBisectTestContext(t, func(ctx *testContext) {
		ctx.cfg.useCCache = true
		cmd := ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc)))
		if err := verifyPath(cmd, "/usr/bin/env"); err != nil {
			t.Error(err)
		}
		if err := verifyArgOrder(cmd, "python3", "/usr/bin/ccache"); err != nil {
			t.Error(err)
		}
		if err := verifyEnvUpdate(cmd, "CCACHE_DIR=.*"); err != nil {
			t.Error(err)
		}
	})
}

func TestDefaultBisectDirCros(t *testing.T) {
	withBisectTestContext(t, func(ctx *testContext) {
		ctx.env = []string{
			"BISECT_STAGE=someBisectStage",
		}
		cmd := mustCallBisectDriver(ctx, callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc)))
		if err := verifyArgOrder(cmd,
			"someBisectStage", "/tmp/sysroot_bisect"); err != nil {
			t.Error(err)
		}
	})
}

func TestDefaultBisectDirAndroid(t *testing.T) {
	withBisectTestContext(t, func(ctx *testContext) {
		ctx.env = []string{
			"BISECT_STAGE=someBisectStage",
			"HOME=/somehome",
		}
		ctx.cfg.isAndroidWrapper = true
		cmd := mustCallBisectDriver(ctx, callCompiler(ctx, ctx.cfg, ctx.newCommand(clangAndroid, mainCc)))
		if err := verifyArgOrder(cmd,
			"someBisectStage", filepath.Join("/somehome", "ANDROID_BISECT")); err != nil {
			t.Error(err)
		}
	})
}

func TestForwardStdOutAndStdErrAndExitCodeFromBisect(t *testing.T) {
	withBisectTestContext(t, func(ctx *testContext) {
		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
			fmt.Fprint(stdout, "somemessage")
			fmt.Fprint(stderr, "someerror")
			return newExitCodeError(23)
		}
		exitCode := callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc))
		if exitCode != 23 {
			t.Errorf("unexpected exit code. Got: %d", exitCode)
		}
		if ctx.stdoutString() != "somemessage" {
			t.Errorf("stdout was not forwarded. Got: %s", ctx.stdoutString())
		}
		if ctx.stderrString() != "someerror" {
			t.Errorf("stderr was not forwarded. Got: %s", ctx.stderrString())
		}
	})
}

func TestForwardGeneralErrorFromBisect(t *testing.T) {
	withBisectTestContext(t, func(ctx *testContext) {
		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
			return errors.New("someerror")
		}
		stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg,
			ctx.newCommand(gccX86_64, mainCc)))
		if err := verifyInternalError(stderr); err != nil {
			t.Fatal(err)
		}
		if !strings.Contains(stderr, "someerror") {
			t.Errorf("unexpected error. Got: %s", stderr)
		}
	})
}

func withBisectTestContext(t *testing.T, work func(ctx *testContext)) {
	withTestContext(t, func(ctx *testContext) {
		ctx.env = []string{"BISECT_STAGE=xyz"}
		// We execute the python script but replace the call to the bisect_driver with
		// a mock that logs the data.
		ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
			if err := verifyPath(cmd, "/usr/bin/env"); err != nil {
				return err
			}
			if cmd.Args[0] != "python3" {
				return fmt.Errorf("expected a call to python. Got: %s", cmd.Args[0])
			}
			if cmd.Args[1] != "-c" {
				return fmt.Errorf("expected an inline python script. Got: %s", cmd.Args)
			}
			script := cmd.Args[2]
			mock := `
class BisectDriver:
	def __init__(self):
		self.VALID_MODES = ['POPULATE_GOOD', 'POPULATE_BAD', 'TRIAGE']
	def bisect_driver(self, bisect_stage, bisect_dir, execargs):
		print('command bisect_driver')
		print('arg %s' % bisect_stage)
		print('arg %s' % bisect_dir)
		for arg in execargs:
			print('arg %s' % arg)

bisect_driver = BisectDriver()
`
			script = mock + script
			script = strings.Replace(script, "import bisect_driver", "", -1)
			cmdCopy := *cmd
			cmdCopy.Args = append(append(cmd.Args[:2], script), cmd.Args[3:]...)
			// Evaluate the python script, but replace the call to the bisect_driver
			// with a log statement so that we can assert it.
			return runCmd(ctx, &cmdCopy, nil, stdout, stderr)
		}
		work(ctx)
	})
}

func mustCallBisectDriver(ctx *testContext, exitCode int) *command {
	ctx.must(exitCode)
	cmd := &command{}
	for _, line := range strings.Split(ctx.stdoutString(), "\n") {
		if prefix := "command "; strings.HasPrefix(line, prefix) {
			cmd.Path = line[len(prefix):]
		} else if prefix := "arg "; strings.HasPrefix(line, prefix) {
			cmd.Args = append(cmd.Args, line[len(prefix):])
		}
	}
	return cmd
}