| // We intentionally use the _test package here so that the tests import executil like client code |
| // would, making this demo of the API more realistic. |
| package executil_test |
| |
| import ( |
| "context" |
| "fmt" |
| "os" |
| "testing" |
| "time" |
| |
| "github.com/stretchr/testify/assert" |
| "github.com/stretchr/testify/require" |
| "go.skia.org/infra/go/executil" |
| "go.skia.org/infra/go/testutils/unittest" |
| ) |
| |
| // runSomething would normally be in the code under test, where it has to invoke a command. |
| func runSomething(ctx context.Context) (string, error) { |
| // Notice how the only functional difference in the code that uses the context is a call to |
| // executil.CommandContext instead of os/exec.CommandContext. |
| cmd := executil.CommandContext(ctx, "cowsay", "moo", "moooo") |
| b, err := cmd.CombinedOutput() |
| return string(b), err |
| } |
| |
| func TestFakeTestsContext_SingleFakeTest_Success(t *testing.T) { |
| unittest.SmallTest(t) |
| ctx := executil.FakeTestsContext("Test_FakeExe_Cowsay_ReturnsASCIIArt") |
| |
| out, err := runSomething(ctx) |
| // normally, require.NoError is what I would do here, but doing so would mask the outputs of |
| // asserts made in the faked executable (which show up in the combined stdout/stderr. |
| assert.NoError(t, err) |
| assert.Equal(t, asciiArt, out) |
| } |
| |
| func TestFakeTestsContext_SingleFakeTest_ReturnsErrorIfWrongArgumentsPassed(t *testing.T) { |
| unittest.SmallTest(t) |
| ctx := executil.FakeTestsContext("Test_FakeExe_Cowsay_ReturnsASCIIArt") |
| |
| cmd := executil.CommandContext(ctx, "cowsay", "wrong arguments") |
| _, err := cmd.CombinedOutput() |
| require.Error(t, err) |
| } |
| |
| func TestFakeTestsContext_SingleFakeTest_CowsayFails_ReturnsError(t *testing.T) { |
| unittest.SmallTest(t) |
| ctx := executil.FakeTestsContext("Test_FakeExe_Cowsay_Crashes") |
| |
| out, err := runSomething(ctx) |
| assert.Error(t, err) |
| assert.Contains(t, err.Error(), "2") // exited code 2 |
| assert.Contains(t, out, "moo") // some of the art was posted before it crashed |
| } |
| |
| func TestFakeTestsContext_MultipleFakeTests_FirstSucceedsSecondReturnsError(t *testing.T) { |
| unittest.SmallTest(t) |
| ctx := executil.FakeTestsContext( |
| "Test_FakeExe_Cowsay_ReturnsASCIIArt", // should be run first |
| "Test_FakeExe_Cowsay_Crashes") // should be run second |
| |
| out, err := runSomething(ctx) |
| assert.NoError(t, err) |
| assert.Contains(t, out, asciiArt) |
| |
| _, err = runSomething(ctx) |
| require.Error(t, err) |
| |
| assert.Equal(t, 2, executil.FakeCommandsReturned(ctx)) |
| } |
| |
| func TestWithFakeTests_ParentContextTimeoutRespected(t *testing.T) { |
| unittest.SmallTest(t) |
| |
| ctx, cancel := context.WithTimeout(context.Background(), time.Second) |
| defer cancel() |
| ctx = executil.WithFakeTests(ctx, "Test_FakeExe_Cowsay_Hangs") |
| |
| // This is an error because the context timed out. On Linux, this error is "signal: killed". |
| _, err := runSomething(ctx) |
| require.Error(t, err) |
| } |
| |
| // This is not a real test, but a fake implementation of the executable in question (i.e. cowsay). |
| // By convention, we prefix these with FakeExe to make that clear. |
| func Test_FakeExe_Cowsay_ReturnsASCIIArt(t *testing.T) { |
| unittest.FakeExeTest(t) |
| // Since this is a normal go test, it will get run on the usual test suite. We check for the |
| // special environment variable and if it is not set, we do nothing. |
| if os.Getenv(executil.OverrideEnvironmentVariable) == "" { |
| return |
| } |
| |
| // Check the input arguments to make sure they were as expected. |
| args := executil.OriginalArgs() |
| require.Equal(t, []string{"cowsay", "moo", "moooo"}, args) |
| |
| fmt.Printf(asciiArt) |
| os.Exit(0) // exit 0 prevents golang from outputting test stuff like "=== RUN", "---Fail". |
| } |
| |
| func Test_FakeExe_Cowsay_Crashes(t *testing.T) { |
| unittest.FakeExeTest(t) |
| if os.Getenv(executil.OverrideEnvironmentVariable) == "" { |
| return |
| } |
| |
| args := executil.OriginalArgs() |
| require.Equal(t, []string{"cowsay", "moo", "moooo"}, args) |
| |
| fmt.Println(asciiArt[:20]) |
| os.Exit(2) |
| } |
| |
| func Test_FakeExe_Cowsay_Hangs(t *testing.T) { |
| unittest.FakeExeTest(t) |
| if os.Getenv(executil.OverrideEnvironmentVariable) == "" { |
| return |
| } |
| // block forever. Hopefully this is called with a timeout. |
| select {} |
| } |
| |
| const asciiArt = ` ___________ |
| < moo moooo > |
| ----------- |
| \ ^__^ |
| \ (oo)\_______ |
| (__)\ )\/\ |
| ||----w | |
| || || |
| ` |