aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Bosch <tbosch@google.com>2019-08-16 11:47:00 -0700
committerTobias Bosch <tbosch@google.com>2019-08-20 17:48:54 +0000
commita50a9c16b6a3b2522bea3b6562d023a7b87dbd49 (patch)
tree4d17d8e02962ba4e62c66e49f520cb1b310adc7d
parent36c19215f13c983c2ae0096ed9170fd064744622 (diff)
downloadtoolchain-utils-a50a9c16b6a3b2522bea3b6562d023a7b87dbd49.tar.gz
Forward os.Stdin to child processes.
BUG=chromium:773875 TEST=unit tests, build glibc with compiler wrapper Change-Id: I0b5c1f5adaee18499b72747cd6042b00a9d52c1d Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/1760973 Reviewed-by: George Burgess <gbiv@chromium.org> Tested-by: Tobias Bosch <tbosch@google.com>
-rw-r--r--compiler_wrapper/bisect_flag_test.go4
-rw-r--r--compiler_wrapper/clang_flags.go2
-rw-r--r--compiler_wrapper/clang_flags_test.go4
-rw-r--r--compiler_wrapper/clang_syntax_flag.go18
-rw-r--r--compiler_wrapper/clang_syntax_flag_test.go32
-rw-r--r--compiler_wrapper/clang_tidy_flag.go4
-rw-r--r--compiler_wrapper/clang_tidy_flag_test.go16
-rw-r--r--compiler_wrapper/compiler_wrapper.go13
-rw-r--r--compiler_wrapper/compiler_wrapper_test.go20
-rw-r--r--compiler_wrapper/disable_werror_flag.go6
-rw-r--r--compiler_wrapper/disable_werror_flag_test.go43
-rw-r--r--compiler_wrapper/env.go45
-rw-r--r--compiler_wrapper/goldenutil_test.go2
-rw-r--r--compiler_wrapper/oldwrapper.go14
-rw-r--r--compiler_wrapper/oldwrapper_test.go40
-rw-r--r--compiler_wrapper/rusage_flag.go2
-rw-r--r--compiler_wrapper/rusage_flag_test.go21
-rw-r--r--compiler_wrapper/testutil_test.go24
18 files changed, 238 insertions, 72 deletions
diff --git a/compiler_wrapper/bisect_flag_test.go b/compiler_wrapper/bisect_flag_test.go
index 15021327..11ab2dcf 100644
--- a/compiler_wrapper/bisect_flag_test.go
+++ b/compiler_wrapper/bisect_flag_test.go
@@ -62,7 +62,7 @@ func TestDefaultBisectDir(t *testing.T) {
func TestForwardStdOutAndStdErrAndExitCodeFromBisect(t *testing.T) {
withBisectTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ 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)
@@ -82,7 +82,7 @@ func TestForwardStdOutAndStdErrAndExitCodeFromBisect(t *testing.T) {
func TestForwardGeneralErrorFromBisect(t *testing.T) {
withBisectTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ 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,
diff --git a/compiler_wrapper/clang_flags.go b/compiler_wrapper/clang_flags.go
index 0cb10c7e..30a0b576 100644
--- a/compiler_wrapper/clang_flags.go
+++ b/compiler_wrapper/clang_flags.go
@@ -167,7 +167,7 @@ func getClangResourceDir(env env, clangPath string) (string, error) {
Args: []string{"--print-resource-dir"},
}
stdoutBuffer := bytes.Buffer{}
- if err := env.run(readResourceCmd, &stdoutBuffer, env.stderr()); err != nil {
+ if err := env.run(readResourceCmd, nil, &stdoutBuffer, env.stderr()); err != nil {
return "", wrapErrorwithSourceLocf(err,
"failed to call clang to read the resouce-dir: %#v",
readResourceCmd)
diff --git a/compiler_wrapper/clang_flags_test.go b/compiler_wrapper/clang_flags_test.go
index 5fe13c31..1d651194 100644
--- a/compiler_wrapper/clang_flags_test.go
+++ b/compiler_wrapper/clang_flags_test.go
@@ -94,7 +94,7 @@ func TestClangPathForClangHostWrapper(t *testing.T) {
func TestUseXclangPathAndCalcResourceDirByNestedClangCall(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
ctx.cfg.rootRelPath = "somepath"
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount > 1 {
return nil
}
@@ -121,7 +121,7 @@ func TestUseXclangPathAndCalcResourceDirByNestedClangCall(t *testing.T) {
func TestXclangPathFailIfNestedClangCallFails(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
fmt.Fprint(stderr, "someclangerror")
return errors.New("someerror")
}
diff --git a/compiler_wrapper/clang_syntax_flag.go b/compiler_wrapper/clang_syntax_flag.go
index e305e579..8f38c613 100644
--- a/compiler_wrapper/clang_syntax_flag.go
+++ b/compiler_wrapper/clang_syntax_flag.go
@@ -4,6 +4,11 @@
package main
+import (
+ "bytes"
+ "io"
+)
+
func processClangSyntaxFlag(builder *commandBuilder) (clangSyntax bool) {
builder.transformArgs(func(arg builderArg) string {
if arg.value == "-clang-syntax" {
@@ -15,12 +20,19 @@ func processClangSyntaxFlag(builder *commandBuilder) (clangSyntax bool) {
return clangSyntax
}
-func checkClangSyntax(env env, clangCmd *command) (exitCode int, err error) {
+func checkClangSyntax(env env, clangCmd *command, gccCmd *command) (exitCode int, err error) {
clangSyntaxCmd := &command{
Path: clangCmd.Path,
Args: append(clangCmd.Args, "-fsyntax-only", "-stdlib=libstdc++"),
EnvUpdates: clangCmd.EnvUpdates,
}
- return wrapSubprocessErrorWithSourceLoc(clangSyntaxCmd,
- env.run(clangSyntaxCmd, env.stdout(), env.stderr()))
+
+ stdinBuffer := &bytes.Buffer{}
+ exitCode, err = wrapSubprocessErrorWithSourceLoc(clangSyntaxCmd,
+ env.run(clangSyntaxCmd, io.TeeReader(env.stdin(), stdinBuffer), env.stdout(), env.stderr()))
+ if err != nil || exitCode != 0 {
+ return exitCode, err
+ }
+ return wrapSubprocessErrorWithSourceLoc(gccCmd,
+ env.run(gccCmd, bytes.NewReader(stdinBuffer.Bytes()), env.stdout(), env.stderr()))
}
diff --git a/compiler_wrapper/clang_syntax_flag_test.go b/compiler_wrapper/clang_syntax_flag_test.go
index 822561c2..8ee9c223 100644
--- a/compiler_wrapper/clang_syntax_flag_test.go
+++ b/compiler_wrapper/clang_syntax_flag_test.go
@@ -15,7 +15,7 @@ import (
func TestCheckClangSyntaxByNestedCall(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount == 1 {
if err := verifyPath(cmd, "usr/bin/clang"); err != nil {
return err
@@ -42,7 +42,7 @@ func TestCheckClangSyntaxByNestedCall(t *testing.T) {
func TestForwardStdOutAndStderrFromClangSyntaxCheck(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount == 1 {
fmt.Fprint(stdout, "somemessage")
fmt.Fprint(stderr, "someerror")
@@ -60,9 +60,27 @@ func TestForwardStdOutAndStderrFromClangSyntaxCheck(t *testing.T) {
})
}
+func TestForwardStdinToClangSyntaxCheck(t *testing.T) {
+ withTestContext(t, func(ctx *testContext) {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
+ // Note: This is called for the clang syntax call as well as for
+ // the gcc call, and we assert that stdin is cloned and forwarded
+ // to both.
+ stdinStr := ctx.readAllString(stdin)
+ if stdinStr != "someinput" {
+ return fmt.Errorf("unexpected stdin. Got: %s", stdinStr)
+ }
+ return nil
+ }
+ io.WriteString(&ctx.stdinBuffer, "someinput")
+ ctx.must(callCompiler(ctx, ctx.cfg,
+ ctx.newCommand(gccX86_64, "-clang-syntax", "-", mainCc)))
+ })
+}
+
func TestForwardExitCodeFromClangSyntaxCheck(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount == 1 {
return newExitCodeError(23)
}
@@ -78,7 +96,7 @@ func TestForwardExitCodeFromClangSyntaxCheck(t *testing.T) {
func TestReportGeneralErrorsFromClangSyntaxCheck(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount == 1 {
return errors.New("someerror")
}
@@ -97,7 +115,7 @@ func TestReportGeneralErrorsFromClangSyntaxCheck(t *testing.T) {
func TestIgnoreClangSyntaxCheckWhenCallingClang(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount > 1 {
return fmt.Errorf("Unexpected call %#v", cmd)
}
@@ -117,7 +135,7 @@ func TestUseGomaForClangSyntaxCheck(t *testing.T) {
// Create a file so the gomacc path is valid.
ctx.writeFile(gomaPath, "")
ctx.env = []string{"GOMACC_PATH=" + gomaPath}
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount == 1 {
if err := verifyPath(cmd, gomaPath); err != nil {
return err
@@ -142,7 +160,7 @@ func TestUseGomaForClangSyntaxCheck(t *testing.T) {
func TestPartiallyOmitCCacheForClangSyntaxCheck(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
ctx.cfg.useCCache = true
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount == 1 {
if err := verifyPath(cmd, "usr/bin/clang"); err != nil {
return err
diff --git a/compiler_wrapper/clang_tidy_flag.go b/compiler_wrapper/clang_tidy_flag.go
index 5bd67064..616ff47c 100644
--- a/compiler_wrapper/clang_tidy_flag.go
+++ b/compiler_wrapper/clang_tidy_flag.go
@@ -77,8 +77,10 @@ func runClangTidy(env env, clangCmd *command, cSrcFile string) error {
EnvUpdates: clangCmd.EnvUpdates,
}
+ // Note: We pass nil as stdin as we checked before that the compiler
+ // was invoked with a source file argument.
exitCode, err := wrapSubprocessErrorWithSourceLoc(clangTidyCmd,
- env.run(clangTidyCmd, env.stdout(), env.stderr()))
+ env.run(clangTidyCmd, nil, env.stdout(), env.stderr()))
if err == nil && exitCode != 0 {
// Note: We continue on purpose when clang-tidy fails
// to maintain compatibility with the previous wrapper.
diff --git a/compiler_wrapper/clang_tidy_flag_test.go b/compiler_wrapper/clang_tidy_flag_test.go
index b2578c51..baf5219e 100644
--- a/compiler_wrapper/clang_tidy_flag_test.go
+++ b/compiler_wrapper/clang_tidy_flag_test.go
@@ -24,7 +24,7 @@ func TestClangTidyBasename(t *testing.T) {
}
var clangTidyCmd *command
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount == 2 {
clangTidyCmd = cmd
}
@@ -48,7 +48,7 @@ func TestClangTidyBasename(t *testing.T) {
func TestClangTidyClangResourceDir(t *testing.T) {
withClangTidyTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
switch ctx.cmdCount {
case 1:
if err := verifyPath(cmd, "usr/bin/clang"); err != nil {
@@ -87,7 +87,7 @@ func TestClangTidyClangResourceDir(t *testing.T) {
func TestClangTidyArgOrder(t *testing.T) {
withClangTidyTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount == 2 {
if err := verifyArgOrder(cmd, "-checks=.*", mainCc, "--", "-resource-dir=.*", mainCc, "--some_arg"); err != nil {
return err
@@ -105,7 +105,7 @@ func TestClangTidyArgOrder(t *testing.T) {
func TestForwardStdOutAndStderrFromClangTidyCall(t *testing.T) {
withClangTidyTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount == 2 {
fmt.Fprint(stdout, "somemessage")
fmt.Fprint(stderr, "someerror")
@@ -125,7 +125,7 @@ func TestForwardStdOutAndStderrFromClangTidyCall(t *testing.T) {
func TestIgnoreNonZeroExitCodeFromClangTidy(t *testing.T) {
withClangTidyTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount == 2 {
return newExitCodeError(23)
}
@@ -142,7 +142,7 @@ func TestIgnoreNonZeroExitCodeFromClangTidy(t *testing.T) {
func TestReportGeneralErrorsFromClangTidy(t *testing.T) {
withClangTidyTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if ctx.cmdCount == 2 {
return errors.New("someerror")
}
@@ -212,7 +212,7 @@ func TestOmitCCacheWithClangTidy(t *testing.T) {
withClangTidyTestContext(t, func(ctx *testContext) {
ctx.cfg.useCCache = true
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
switch ctx.cmdCount {
case 1:
if err := verifyPath(cmd, "usr/bin/clang"); err != nil {
@@ -246,7 +246,7 @@ func TestPartiallyOmitGomaWithClangTidy(t *testing.T) {
ctx.writeFile(gomaPath, "")
ctx.env = append(ctx.env, "GOMACC_PATH="+gomaPath)
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
switch ctx.cmdCount {
case 1:
if err := verifyPath(cmd, "usr/bin/clang"); err != nil {
diff --git a/compiler_wrapper/compiler_wrapper.go b/compiler_wrapper/compiler_wrapper.go
index 4aa91c6c..429484c9 100644
--- a/compiler_wrapper/compiler_wrapper.go
+++ b/compiler_wrapper/compiler_wrapper.go
@@ -5,6 +5,7 @@
package main
import (
+ "bytes"
"fmt"
"io"
"path/filepath"
@@ -40,14 +41,16 @@ func callCompiler(env env, cfg *config, inputCmd *command) int {
}
func callCompilerWithRunAndCompareToOldWrapper(env env, cfg *config, inputCmd *command) (exitCode int, err error) {
+ stdinBuffer := &bytes.Buffer{}
recordingEnv := &commandRecordingEnv{
- env: env,
+ env: env,
+ stdinReader: io.TeeReader(env.stdin(), stdinBuffer),
}
// Note: this won't do a real exec as recordingEnv redirects exec to run.
if exitCode, err = callCompilerInternal(recordingEnv, cfg, inputCmd); err != nil {
return 0, err
}
- if err = compareToOldWrapper(env, cfg, inputCmd, recordingEnv.cmdResults, exitCode); err != nil {
+ if err = compareToOldWrapper(env, cfg, inputCmd, stdinBuffer.Bytes(), recordingEnv.cmdResults, exitCode); err != nil {
return exitCode, err
}
return exitCode, nil
@@ -89,10 +92,8 @@ func callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int
if err != nil {
return 0, err
}
- exitCode, err = checkClangSyntax(env, clangCmd)
- if err != nil || exitCode != 0 {
- return exitCode, err
- }
+ gccCmd := calcGccCommand(mainBuilder)
+ return checkClangSyntax(env, clangCmd, gccCmd)
}
compilerCmd = calcGccCommand(mainBuilder)
}
diff --git a/compiler_wrapper/compiler_wrapper_test.go b/compiler_wrapper/compiler_wrapper_test.go
index 57bef951..71cd36df 100644
--- a/compiler_wrapper/compiler_wrapper_test.go
+++ b/compiler_wrapper/compiler_wrapper_test.go
@@ -58,7 +58,7 @@ func TestLogGeneralExecError(t *testing.T) {
ctx.cfg.oldWrapperPath = testOldWrapperPath
// Note: No need to write the old wrapper as we don't execute
// it due to the general error from the new error.
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ 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)))
@@ -75,6 +75,20 @@ func TestLogGeneralExecError(t *testing.T) {
})
}
+func TestForwardStdin(t *testing.T) {
+ withTestContext(t, func(ctx *testContext) {
+ io.WriteString(&ctx.stdinBuffer, "someinput")
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
+ stdinStr := ctx.readAllString(stdin)
+ if stdinStr != "someinput" {
+ return fmt.Errorf("unexpected stdin. Got: %s", stdinStr)
+ }
+ return nil
+ }
+ ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, "-", mainCc)))
+ })
+}
+
func TestLogMissingCCacheExecError(t *testing.T) {
withTestContext(t, func(ctx *testContext) {
ctx.cfg.useCCache = true
@@ -87,7 +101,7 @@ func TestLogMissingCCacheExecError(t *testing.T) {
ctx.cfg.oldWrapperPath = testOldWrapperPath
// Note: No need to write the old wrapper as we don't execute
// it due to the general error from the new error.
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
return syscall.ENOENT
}
ctx.stderrBuffer.Reset()
@@ -104,7 +118,7 @@ func TestLogExitCodeErrorWhenComparingToOldWrapper(t *testing.T) {
ctx.cfg.mockOldWrapperCmds = false
ctx.cfg.oldWrapperPath = filepath.Join(ctx.tempDir, "fakewrapper")
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
writePythonMockWrapper(ctx, &mockWrapperConfig{
Cmds: []*mockWrapperCmd{
{
diff --git a/compiler_wrapper/disable_werror_flag.go b/compiler_wrapper/disable_werror_flag.go
index 063e7571..809b8b6f 100644
--- a/compiler_wrapper/disable_werror_flag.go
+++ b/compiler_wrapper/disable_werror_flag.go
@@ -7,6 +7,7 @@ package main
import (
"bytes"
"encoding/json"
+ "io"
"io/ioutil"
"os"
"strings"
@@ -24,8 +25,9 @@ func doubleBuildWithWNoError(env env, cfg *config, originalCmd *command) (exitCo
if originalCmd.Path == "/usr/bin/ccache" {
originalCmd.Path = "ccache"
}
+ originalStdinBuffer := &bytes.Buffer{}
originalExitCode, err := wrapSubprocessErrorWithSourceLoc(originalCmd,
- env.run(originalCmd, originalStdoutBuffer, originalStderrBuffer))
+ env.run(originalCmd, io.TeeReader(env.stdin(), originalStdinBuffer), originalStdoutBuffer, originalStderrBuffer))
if err != nil {
return 0, err
}
@@ -45,7 +47,7 @@ func doubleBuildWithWNoError(env env, cfg *config, originalCmd *command) (exitCo
EnvUpdates: originalCmd.EnvUpdates,
}
retryExitCode, err := wrapSubprocessErrorWithSourceLoc(retryCommand,
- env.run(retryCommand, retryStdoutBuffer, retryStderrBuffer))
+ env.run(retryCommand, bytes.NewReader(originalStdinBuffer.Bytes()), retryStdoutBuffer, retryStderrBuffer))
if err != nil {
return 0, err
}
diff --git a/compiler_wrapper/disable_werror_flag_test.go b/compiler_wrapper/disable_werror_flag_test.go
index 066157d6..40d0f8c7 100644
--- a/compiler_wrapper/disable_werror_flag_test.go
+++ b/compiler_wrapper/disable_werror_flag_test.go
@@ -27,7 +27,7 @@ func TestOmitDoubleBuildForSuccessfulCall(t *testing.T) {
func TestOmitDoubleBuildForGeneralError(t *testing.T) {
withForceDisableWErrorTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ 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(clangX86_64, mainCc)))
@@ -45,7 +45,7 @@ func TestOmitDoubleBuildForGeneralError(t *testing.T) {
func TestDoubleBuildWithWNoErrorFlag(t *testing.T) {
withForceDisableWErrorTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
switch ctx.cmdCount {
case 1:
if err := verifyArgCount(cmd, 0, "-Wno-error"); err != nil {
@@ -73,7 +73,7 @@ func TestDoubleBuildWithWNoErrorFlag(t *testing.T) {
func TestDoubleBuildWithWNoErrorAndCCache(t *testing.T) {
withForceDisableWErrorTestContext(t, func(ctx *testContext) {
ctx.cfg.useCCache = true
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
switch ctx.cmdCount {
case 1:
// TODO: This is a bug in the old wrapper that it drops the ccache path
@@ -102,7 +102,7 @@ func TestDoubleBuildWithWNoErrorAndCCache(t *testing.T) {
func TestForwardStdoutAndStderrWhenDoubleBuildSucceeds(t *testing.T) {
withForceDisableWErrorTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
switch ctx.cmdCount {
case 1:
fmt.Fprint(stdout, "originalmessage")
@@ -129,7 +129,7 @@ func TestForwardStdoutAndStderrWhenDoubleBuildSucceeds(t *testing.T) {
func TestForwardStdoutAndStderrWhenDoubleBuildFails(t *testing.T) {
withForceDisableWErrorTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
switch ctx.cmdCount {
case 1:
fmt.Fprint(stdout, "originalmessage")
@@ -157,9 +157,36 @@ func TestForwardStdoutAndStderrWhenDoubleBuildFails(t *testing.T) {
})
}
+func TestForwardStdinFromDoubleBuild(t *testing.T) {
+ withForceDisableWErrorTestContext(t, func(ctx *testContext) {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
+ // Note: This is called for the clang syntax call as well as for
+ // the gcc call, and we assert that stdin is cloned and forwarded
+ // to both.
+ stdinStr := ctx.readAllString(stdin)
+ if stdinStr != "someinput" {
+ return fmt.Errorf("unexpected stdin. Got: %s", stdinStr)
+ }
+
+ switch ctx.cmdCount {
+ case 1:
+ fmt.Fprint(stderr, "-Werror originalerror")
+ return newExitCodeError(1)
+ case 2:
+ return nil
+ default:
+ t.Fatalf("unexpected command: %#v", cmd)
+ return nil
+ }
+ }
+ io.WriteString(&ctx.stdinBuffer, "someinput")
+ ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, "-", mainCc)))
+ })
+}
+
func TestForwardGeneralErrorWhenDoubleBuildFails(t *testing.T) {
withForceDisableWErrorTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
switch ctx.cmdCount {
case 1:
fmt.Fprint(stderr, "-Werror originalerror")
@@ -195,7 +222,7 @@ func TestOmitLogWarningsIfNoDoubleBuild(t *testing.T) {
func TestLogWarningsWhenDoubleBuildSucceeds(t *testing.T) {
withForceDisableWErrorTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
switch ctx.cmdCount {
case 1:
fmt.Fprint(stdout, "originalmessage")
@@ -233,7 +260,7 @@ func TestLogWarningsWhenDoubleBuildSucceeds(t *testing.T) {
func TestLogWarningsWhenDoubleBuildFails(t *testing.T) {
withForceDisableWErrorTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
switch ctx.cmdCount {
case 1:
fmt.Fprint(stdout, "originalmessage")
diff --git a/compiler_wrapper/env.go b/compiler_wrapper/env.go
index 40bc2e41..bbf3ec45 100644
--- a/compiler_wrapper/env.go
+++ b/compiler_wrapper/env.go
@@ -17,9 +17,10 @@ type env interface {
getenv(key string) string
environ() []string
getwd() string
+ stdin() io.Reader
stdout() io.Writer
stderr() io.Writer
- run(cmd *command, stdout io.Writer, stderr io.Writer) error
+ run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error
exec(cmd *command) error
}
@@ -49,6 +50,10 @@ func (env *processEnv) getwd() string {
return env.wd
}
+func (env *processEnv) stdin() io.Reader {
+ return os.Stdin
+}
+
func (env *processEnv) stdout() io.Writer {
return os.Stdout
}
@@ -62,16 +67,36 @@ func (env *processEnv) exec(cmd *command) error {
return syscall.Exec(execCmd.Path, execCmd.Args, execCmd.Env)
}
-func (env *processEnv) run(cmd *command, stdout io.Writer, stderr io.Writer) error {
+func (env *processEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
execCmd := newExecCmd(env, cmd)
+ execCmd.Stdin = stdin
execCmd.Stdout = stdout
execCmd.Stderr = stderr
+ _, stdinIsFile := stdin.(*os.File)
+ _, stdinIsBytesReader := stdin.(*bytes.Reader)
+ if stdin != nil && !stdinIsFile && !stdinIsBytesReader {
+ // We can't use execCmd.Run() here as that blocks if stdin blocks,
+ // even if the underlying process has already terminated. We care
+ // especially about the case when stdin is an io.TeeReader for os.Stdin.
+ // See https://github.com/golang/go/issues/7990 for more details.
+ if err := execCmd.Start(); err != nil {
+ return err
+ }
+ if _, err := execCmd.Process.Wait(); err != nil {
+ return err
+ }
+ // Closing Stdin here as we didn't wait for the read to finish via
+ // execCmd.Wait to prevent race conditions.
+ os.Stdin.Close()
+ return nil
+ }
return execCmd.Run()
}
type commandRecordingEnv struct {
env
- cmdResults []*commandResult
+ stdinReader io.Reader
+ cmdResults []*commandResult
}
type commandResult struct {
Cmd *command `json:"cmd"`
@@ -82,16 +107,20 @@ type commandResult struct {
var _ env = (*commandRecordingEnv)(nil)
+func (env *commandRecordingEnv) stdin() io.Reader {
+ return env.stdinReader
+}
+
func (env *commandRecordingEnv) exec(cmd *command) error {
// Note: We treat exec the same as run so that we can do work
// after the call.
- return env.run(cmd, env.stdout(), env.stderr())
+ return env.run(cmd, env.stdin(), env.stdout(), env.stderr())
}
-func (env *commandRecordingEnv) run(cmd *command, stdout io.Writer, stderr io.Writer) error {
+func (env *commandRecordingEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
stdoutBuffer := &bytes.Buffer{}
stderrBuffer := &bytes.Buffer{}
- err := env.env.run(cmd, io.MultiWriter(stdout, stdoutBuffer), io.MultiWriter(stderr, stderrBuffer))
+ err := env.env.run(cmd, stdin, io.MultiWriter(stdout, stdoutBuffer), io.MultiWriter(stderr, stderrBuffer))
if exitCode, ok := getExitCode(err); ok {
env.cmdResults = append(env.cmdResults, &commandResult{
Cmd: cmd,
@@ -114,9 +143,9 @@ func (env *printingEnv) exec(cmd *command) error {
return env.env.exec(cmd)
}
-func (env *printingEnv) run(cmd *command, stdout io.Writer, stderr io.Writer) error {
+func (env *printingEnv) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
printCmd(env, cmd)
- return env.env.run(cmd, stdout, stderr)
+ return env.env.run(cmd, stdin, stdout, stderr)
}
func printCmd(env env, cmd *command) {
diff --git a/compiler_wrapper/goldenutil_test.go b/compiler_wrapper/goldenutil_test.go
index 99d45bb8..9c164442 100644
--- a/compiler_wrapper/goldenutil_test.go
+++ b/compiler_wrapper/goldenutil_test.go
@@ -126,7 +126,7 @@ func fillGoldenResults(ctx *testContext, files []goldenFile) []goldenFile {
newRecords := []goldenRecord{}
for _, record := range file.Records {
newCmds := []commandResult{}
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
if len(newCmds) >= len(record.Cmds) {
ctx.t.Errorf("Not enough commands specified for wrapperCmd %#v and env %#v. Expected: %#v",
record.WrapperCmd.Cmd, record.Env, record.Cmds)
diff --git a/compiler_wrapper/oldwrapper.go b/compiler_wrapper/oldwrapper.go
index 1be4db5a..8ae6f05c 100644
--- a/compiler_wrapper/oldwrapper.go
+++ b/compiler_wrapper/oldwrapper.go
@@ -20,7 +20,7 @@ import (
const compareToOldWrapperFilePattern = "old_wrapper_compare"
-func compareToOldWrapper(env env, cfg *config, inputCmd *command, newCmdResults []*commandResult, newExitCode int) error {
+func compareToOldWrapper(env env, cfg *config, inputCmd *command, stdinBuffer []byte, newCmdResults []*commandResult, newExitCode int) error {
pythonStringEscaper := strings.NewReplacer("\n", "\\n", "'", "\\'")
oldWrapperCfg, err := newOldWrapperConfig(env, cfg, inputCmd)
@@ -41,9 +41,9 @@ func compareToOldWrapper(env env, cfg *config, inputCmd *command, newCmdResults
stderrBuffer := bytes.Buffer{}
oldExitCode := 0
if strings.HasPrefix(oldWrapperCfg.OldWrapperContent, "#!/bin/sh") {
- oldExitCode, err = callOldShellWrapper(env, oldWrapperCfg, inputCmd, compareToOldWrapperFilePattern, &bytes.Buffer{}, &stderrBuffer)
+ oldExitCode, err = callOldShellWrapper(env, oldWrapperCfg, inputCmd, stdinBuffer, compareToOldWrapperFilePattern, &bytes.Buffer{}, &stderrBuffer)
} else {
- oldExitCode, err = callOldPythonWrapper(env, oldWrapperCfg, inputCmd, compareToOldWrapperFilePattern, &bytes.Buffer{}, &stderrBuffer)
+ oldExitCode, err = callOldPythonWrapper(env, oldWrapperCfg, inputCmd, stdinBuffer, compareToOldWrapperFilePattern, &bytes.Buffer{}, &stderrBuffer)
}
if err != nil {
return err
@@ -193,7 +193,7 @@ func newOldWrapperConfig(env env, cfg *config, inputCmd *command) (*oldWrapperCo
}, nil
}
-func callOldShellWrapper(env env, cfg *oldWrapperConfig, inputCmd *command, filepattern string, stdout io.Writer, stderr io.Writer) (exitCode int, err error) {
+func callOldShellWrapper(env env, cfg *oldWrapperConfig, inputCmd *command, stdinBuffer []byte, filepattern string, stdout io.Writer, stderr io.Writer) (exitCode int, err error) {
oldWrapperContent := cfg.OldWrapperContent
oldWrapperContent = regexp.MustCompile(`(?m)^exec\b`).ReplaceAllString(oldWrapperContent, "exec_mock")
oldWrapperContent = regexp.MustCompile(`\$EXEC`).ReplaceAllString(oldWrapperContent, "exec_mock")
@@ -243,10 +243,10 @@ function exec_mock {
Args: append([]string{mockFile.Name()}, inputCmd.Args...),
EnvUpdates: inputCmd.EnvUpdates,
}
- return wrapSubprocessErrorWithSourceLoc(oldWrapperCmd, env.run(oldWrapperCmd, stdout, stderr))
+ return wrapSubprocessErrorWithSourceLoc(oldWrapperCmd, env.run(oldWrapperCmd, bytes.NewReader(stdinBuffer), stdout, stderr))
}
-func callOldPythonWrapper(env env, cfg *oldWrapperConfig, inputCmd *command, filepattern string, stdout io.Writer, stderr io.Writer) (exitCode int, err error) {
+func callOldPythonWrapper(env env, cfg *oldWrapperConfig, inputCmd *command, stdinBuffer []byte, filepattern string, stdout io.Writer, stderr io.Writer) (exitCode int, err error) {
oldWrapperContent := cfg.OldWrapperContent
// TODO: Use strings.ReplaceAll once cros sdk uses golang >= 1.12
oldWrapperContent = strings.Replace(oldWrapperContent, "from __future__ import print_function", "", -1)
@@ -380,5 +380,5 @@ runMain()
Args: append([]string{"-S", mockFile.Name()}, inputCmd.Args...),
EnvUpdates: inputCmd.EnvUpdates,
}
- return wrapSubprocessErrorWithSourceLoc(oldWrapperCmd, env.run(oldWrapperCmd, stdout, stderr))
+ return wrapSubprocessErrorWithSourceLoc(oldWrapperCmd, env.run(oldWrapperCmd, bytes.NewReader(stdinBuffer), stdout, stderr))
}
diff --git a/compiler_wrapper/oldwrapper_test.go b/compiler_wrapper/oldwrapper_test.go
index a07615a9..e1752ce0 100644
--- a/compiler_wrapper/oldwrapper_test.go
+++ b/compiler_wrapper/oldwrapper_test.go
@@ -6,6 +6,7 @@ package main
import (
"bytes"
+ "fmt"
"io"
"path/filepath"
"strings"
@@ -31,7 +32,7 @@ func TestCompareToOldPythonWrapperCompilerCommand(t *testing.T) {
newWrapperExitCode = 0
}
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
writePythonMockWrapper(ctx, &mockWrapperConfig{
Cmds: []*mockWrapperCmd{
{
@@ -92,7 +93,7 @@ func TestCompareToOldPythonWrapperNestedCommand(t *testing.T) {
extraArgs := []string{}
wrapperCfg := &mockWrapperConfig{}
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
isNestedCmd := len(wrapperCfg.Cmds) == 0
var wrapperCmd *mockWrapperCmd
if isNestedCmd {
@@ -165,7 +166,7 @@ func TestCompareToOldShellWrapperCompilerCommand(t *testing.T) {
newWrapperExitCode = 0
}
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
writeShellMockWrapper(ctx, &mockWrapperConfig{
Cmds: []*mockWrapperCmd{
{
@@ -222,7 +223,7 @@ func TestCompareToOldWrapperEscapeStdoutAndStderr(t *testing.T) {
ctx.cfg.mockOldWrapperCmds = false
ctx.cfg.oldWrapperPath = filepath.Join(ctx.tempDir, "fakewrapper")
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
io.WriteString(stdout, "a\n'b'")
io.WriteString(stderr, "c\n'd'")
writePythonMockWrapper(ctx, &mockWrapperConfig{
@@ -282,7 +283,7 @@ func TestCompareToOldPythonWrapperArgumentsWithSpaces(t *testing.T) {
ctx.cfg.mockOldWrapperCmds = false
ctx.cfg.oldWrapperPath = filepath.Join(ctx.tempDir, "fakewrapper")
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
writePythonMockWrapper(ctx, &mockWrapperConfig{
Cmds: []*mockWrapperCmd{
{
@@ -304,7 +305,7 @@ func TestCompareToOldShellWrapperArgumentsWithSpaces(t *testing.T) {
ctx.cfg.mockOldWrapperCmds = false
ctx.cfg.oldWrapperPath = filepath.Join(ctx.tempDir, "fakewrapper")
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
writeShellMockWrapper(ctx, &mockWrapperConfig{
Cmds: []*mockWrapperCmd{
{
@@ -321,6 +322,33 @@ func TestCompareToOldShellWrapperArgumentsWithSpaces(t *testing.T) {
})
}
+func TestForwardStdinWhenUsingOldWrapper(t *testing.T) {
+ withTestContext(t, func(ctx *testContext) {
+ io.WriteString(&ctx.stdinBuffer, "someinput")
+ ctx.cfg.mockOldWrapperCmds = false
+ ctx.cfg.oldWrapperPath = filepath.Join(ctx.tempDir, "fakewrapper")
+
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
+ writeShellMockWrapper(ctx, &mockWrapperConfig{
+ Cmds: []*mockWrapperCmd{
+ {
+ Path: cmd.Path,
+ Args: cmd.Args,
+ },
+ },
+ })
+ stdinStr := ctx.readAllString(stdin)
+ if stdinStr != "someinput" {
+ return fmt.Errorf("unexpected stdin. Got: %s", stdinStr)
+ }
+ return nil
+ }
+
+ ctx.must(callCompiler(ctx, ctx.cfg,
+ ctx.newCommand(clangX86_64, "-", mainCc)))
+ })
+}
+
func writePythonMockWrapper(ctx *testContext, cfg *mockWrapperConfig) {
const mockTemplate = `
from __future__ import print_function
diff --git a/compiler_wrapper/rusage_flag.go b/compiler_wrapper/rusage_flag.go
index b1c2c45e..c3337364 100644
--- a/compiler_wrapper/rusage_flag.go
+++ b/compiler_wrapper/rusage_flag.go
@@ -29,7 +29,7 @@ func logRusage(env env, logFileName string, compilerCmd *command) (exitCode int,
}
startTime := time.Now()
exitCode, err = wrapSubprocessErrorWithSourceLoc(compilerCmdWithoutRusage,
- env.run(compilerCmdWithoutRusage, env.stdout(), env.stderr()))
+ env.run(compilerCmdWithoutRusage, env.stdin(), env.stdout(), env.stderr()))
if err != nil {
return 0, err
}
diff --git a/compiler_wrapper/rusage_flag_test.go b/compiler_wrapper/rusage_flag_test.go
index 916a5332..7acba0c8 100644
--- a/compiler_wrapper/rusage_flag_test.go
+++ b/compiler_wrapper/rusage_flag_test.go
@@ -19,7 +19,7 @@ import (
func TestForwardStdOutAndStdErrAndExitCodeFromLogRusage(t *testing.T) {
withLogRusageTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ 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)
@@ -37,9 +37,26 @@ func TestForwardStdOutAndStdErrAndExitCodeFromLogRusage(t *testing.T) {
})
}
+func TestForwardStdinFromLogRusage(t *testing.T) {
+ withLogRusageTestContext(t, func(ctx *testContext) {
+ ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
+ // Note: This is called for the clang syntax call as well as for
+ // the gcc call, and we assert that stdin is cloned and forwarded
+ // to both.
+ stdinStr := ctx.readAllString(stdin)
+ if stdinStr != "someinput" {
+ return fmt.Errorf("unexpected stdin. Got: %s", stdinStr)
+ }
+ return nil
+ }
+ io.WriteString(&ctx.stdinBuffer, "someinput")
+ ctx.must(callCompiler(ctx, ctx.cfg, ctx.newCommand(clangX86_64, "-", mainCc)))
+ })
+}
+
func TestReportGeneralErrorsFromLogRusage(t *testing.T) {
withLogRusageTestContext(t, func(ctx *testContext) {
- ctx.cmdMock = func(cmd *command, stdout io.Writer, stderr io.Writer) error {
+ 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,
diff --git a/compiler_wrapper/testutil_test.go b/compiler_wrapper/testutil_test.go
index 778c4c26..3e568c77 100644
--- a/compiler_wrapper/testutil_test.go
+++ b/compiler_wrapper/testutil_test.go
@@ -38,7 +38,8 @@ type testContext struct {
inputCmd *command
lastCmd *command
cmdCount int
- cmdMock func(cmd *command, stdout io.Writer, stderr io.Writer) error
+ cmdMock func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error
+ stdinBuffer bytes.Buffer
stdoutBuffer bytes.Buffer
stderrBuffer bytes.Buffer
}
@@ -83,6 +84,10 @@ func (ctx *testContext) getwd() string {
return ctx.wd
}
+func (ctx *testContext) stdin() io.Reader {
+ return &ctx.stdinBuffer
+}
+
func (ctx *testContext) stdout() io.Writer {
return &ctx.stdoutBuffer
}
@@ -99,7 +104,7 @@ func (ctx *testContext) stderrString() string {
return ctx.stderrBuffer.String()
}
-func (ctx *testContext) run(cmd *command, stdout io.Writer, stderr io.Writer) error {
+func (ctx *testContext) run(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
// Keep calling the old wrapper when we are comparing the output of the
// old wrapper to the new wrapper.
if isCompareToOldWrapperCmd(cmd) {
@@ -111,7 +116,7 @@ func (ctx *testContext) run(cmd *command, stdout io.Writer, stderr io.Writer) er
ctx.cmdCount++
ctx.lastCmd = cmd
if ctx.cmdMock != nil {
- return ctx.cmdMock(cmd, stdout, stderr)
+ return ctx.cmdMock(cmd, stdin, stdout, stderr)
}
return nil
}
@@ -120,7 +125,7 @@ func (ctx *testContext) exec(cmd *command) error {
ctx.cmdCount++
ctx.lastCmd = cmd
if ctx.cmdMock != nil {
- return ctx.cmdMock(cmd, ctx.stdout(), ctx.stderr())
+ return ctx.cmdMock(cmd, ctx.stdin(), ctx.stdout(), ctx.stderr())
}
return nil
}
@@ -188,6 +193,17 @@ func (ctx *testContext) symlink(oldname string, newname string) {
}
}
+func (ctx *testContext) readAllString(r io.Reader) string {
+ if r == nil {
+ return ""
+ }
+ bytes, err := ioutil.ReadAll(r)
+ if err != nil {
+ ctx.t.Fatal(err)
+ }
+ return string(bytes)
+}
+
func verifyPath(cmd *command, expectedRegex string) error {
compiledRegex := regexp.MustCompile(matchFullString(expectedRegex))
if !compiledRegex.MatchString(cmd.Path) {