Factor out Executable() and FlagPath() into testutils.
Also, have FlagPath() assert the absence of the flag file first. Improve
some naming.
Change-Id: Id6a45c74abab86361762ddd1e045dd6576fec925
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/610662
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Commit-Queue: Erik Rose <erikrose@google.com>
diff --git a/go/testutils/testutils.go b/go/testutils/testutils.go
index eaa927c..1e237cd 100644
--- a/go/testutils/testutils.go
+++ b/go/testutils/testutils.go
@@ -206,3 +206,24 @@
util.RemoveAll(fakeHome)
})
}
+
+// Executable returns the filesystem path to the binary running the current test, panicking on
+// failure.
+func Executable(t sktest.TestingT) string {
+ executable, err := os.Executable()
+ require.NoError(t, err)
+ return executable
+}
+
+// FlagPath returns the path to a temp file for use as a synchronization flag between the test
+// runner and a mocked-out subprocess launched by tested code; see go/flag-files. It lives next to
+// the test executable and has the specified name, which should contain the name of the test for
+// uniqueness across concurrent tests. It also asserts the absence of said file as a safety measure
+// against leftover files from earlier runs—admittedly unlikely given "go test"'s and Bazel's
+// proclivity for running everything in temp dirs.
+func FlagPath(t sktest.TestingT, fileName string) string {
+ path := filepath.Join(filepath.Dir(Executable(t)), fileName)
+ _, err := os.Stat(path)
+ require.True(t, errors.Is(err, os.ErrNotExist), fmt.Sprintf("Flag file %s existed before it was expected.", fileName))
+ return path
+}
diff --git a/machine/go/test_machine_monitor/foundrybotcustodian/BUILD.bazel b/machine/go/test_machine_monitor/foundrybotcustodian/BUILD.bazel
index 01d5642..b0c0d56 100644
--- a/machine/go/test_machine_monitor/foundrybotcustodian/BUILD.bazel
+++ b/machine/go/test_machine_monitor/foundrybotcustodian/BUILD.bazel
@@ -22,6 +22,7 @@
deps = [
"//go/executil",
"//go/recentschannel",
+ "//go/testutils",
"@com_github_stretchr_testify//require",
],
)
diff --git a/machine/go/test_machine_monitor/foundrybotcustodian/foundrybotcustodian_test.go b/machine/go/test_machine_monitor/foundrybotcustodian/foundrybotcustodian_test.go
index 3f4e7b9..877f97b 100644
--- a/machine/go/test_machine_monitor/foundrybotcustodian/foundrybotcustodian_test.go
+++ b/machine/go/test_machine_monitor/foundrybotcustodian/foundrybotcustodian_test.go
@@ -5,13 +5,13 @@
"errors"
"os"
"os/signal"
- "path/filepath"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.skia.org/infra/go/executil"
"go.skia.org/infra/go/recentschannel"
+ "go.skia.org/infra/go/testutils"
)
// launchTimeout is how long we're willing to wait for a process to spin up.
@@ -39,12 +39,6 @@
time.Sleep(2 * launchTimeout)
}
-func testBinary(t *testing.T) string {
- executable, err := os.Executable()
- require.NoError(t, err)
- return executable
-}
-
func TestStart_RelaunchesIfProcessExits(t *testing.T) {
// This also tests the initial launch.
ctx, cancel := context.WithCancel(executil.FakeTestsContext("Test_FakeExe_FoundryBot_ExitsWithZero",
@@ -52,7 +46,7 @@
defer cancel()
wantFoundryBotUpCh := recentschannel.New[bool](1)
wantFoundryBotUpCh.Send(true)
- require.NoError(t, Start(ctx, testBinary(t), "ignored", wantFoundryBotUpCh))
+ require.NoError(t, Start(ctx, testutils.Executable(t), "ignored", wantFoundryBotUpCh))
require.Eventually(t, func() bool {
return executil.FakeCommandsReturned(ctx) >= 2
}, launchTimeout, launchTimeout/10, "Foundry Bot never got relaunched after exiting.")
@@ -63,10 +57,10 @@
require.Contains(t, err.Error(), "Foundry Bot not found")
}
-func gracefulStopTempFile(t *testing.T) string {
- executable, err := os.Executable()
- require.NoError(t, err)
- return filepath.Join(filepath.Dir(executable), "TestStart_GracefullyStopsProcessIfHeartbeatSaysFalse.temp")
+// flagFileForProcessStartAndInterrupt returns the path to the file through which we synchronize the
+// fake Foundry Bot process with the test harness.
+func flagFileForProcessStartAndInterrupt(t *testing.T) string {
+ return testutils.FlagPath(t, "foundryBotStartAndInterrupt.temp")
}
// Test_FakeExe_FoundryBot_RunsUntilInterruptAndMakesFlagFile pretends to be a Foundry Bot which
@@ -80,8 +74,8 @@
require.Contains(t, executil.OriginalArgs(), "session")
// Make flag file.
- tempPath := gracefulStopTempFile(t)
- file, err := os.Create(tempPath)
+ flag := flagFileForProcessStartAndInterrupt(t)
+ file, err := os.Create(flag)
require.NoError(t, err)
require.NoError(t, file.Close())
@@ -91,7 +85,7 @@
timeout := time.NewTicker(launchTimeout)
select {
case <-interrupt:
- require.NoError(t, os.Remove(tempPath))
+ require.NoError(t, os.Remove(flag))
case <-timeout.C:
// Let the file leak. If under Bazel, it's in a temp dir anyway.
}
@@ -103,17 +97,16 @@
wantFoundryBotUpCh := recentschannel.New[bool](1)
wantFoundryBotUpCh.Send(true)
ctx := executil.FakeTestsContext("Test_FakeExe_FoundryBot_RunsUntilInterruptAndMakesFlagFile")
- require.NoError(t, Start(ctx, testBinary(t), "ignored", wantFoundryBotUpCh))
- tempPath := gracefulStopTempFile(t)
+ flag := flagFileForProcessStartAndInterrupt(t)
+ require.NoError(t, Start(ctx, testutils.Executable(t), "ignored", wantFoundryBotUpCh))
- // Wait until TestStart_GracefullyStopsProcessIfHeartbeatSaysFalse.temp exists, showing the
- // process is up.
+ // Wait until foundryBotStartAndInterrupt.temp exists, showing the process is up.
//
// Using the FS (relative to the test executable) as a place to rendezvous and also a
// synchronization mechanism lets us avoid shoehorning extra channels, mutexes, and struct-level
// vars into the implementation just to give visibility to tests.
require.Eventually(t, func() bool {
- _, err := os.Stat(tempPath)
+ _, err := os.Stat(flag)
return err == nil
}, launchTimeout, launchTimeout/10, "Foundry Bot process never came up.")
@@ -122,7 +115,7 @@
// Wait until temp file disappears, indicating the process has received the requisite SIGINT.
require.Eventually(t, func() bool {
- _, err := os.Stat(tempPath)
+ _, err := os.Stat(flag)
return errors.Is(err, os.ErrNotExist)
}, launchTimeout, launchTimeout/10, "Foundry Bot process never caught SIGINT.")
}