| package emulators |
| |
| import ( |
| "fmt" |
| "os" |
| "os/exec" |
| "syscall" |
| "time" |
| |
| "go.skia.org/infra/bazel/go/bazel" |
| "go.skia.org/infra/go/skerr" |
| "go.skia.org/infra/go/sktest" |
| ) |
| |
| type EmulatorLauncherFn func() (bool, error) |
| |
| // RequireEmulator fails the test when running outside of RBE if the corresponding *_EMULATOR_HOST |
| // environment variable wasn't set, or starts a new emulator instance under RBE if necessary. |
| // This should not be called by unit tests directly, but rather by the subpackages of this module. |
| func RequireEmulator(t sktest.TestingT, emulator Emulator, fn EmulatorLauncherFn) { |
| if bazel.InBazelTestOnRBE() { |
| setUpEmulatorBazelRBEOnly(t, emulator, fn) |
| return |
| } |
| |
| // When running locally, the developer is responsible for running any necessary emulators. |
| host := GetEmulatorHostEnvVar(emulator) |
| if host == "" { |
| t.Fatalf(`This test requires the %s emulator, which you can start with |
| |
| $ ./scripts/run_emulators/run_emulators start |
| (in the skia-perf/buildbot directory) |
| |
| and then set the environment variables it prints out.`, emulator) |
| } |
| } |
| |
| // setUpEmulatorBazelRBEOnly starts an instance of the given emulator, or reuses an existing |
| // instance if one was started by an earlier test case. It then sets the appropriate environment |
| // variable with the port number where the emulator is listening. |
| // |
| // If a test case-specific instance is requested, it will always start a new emulator instance, |
| // regardless of whether one was started by an earlier test case. The test case-specific instance |
| // will only be visible to the current test case (i.e. it will not be visible to any subsequent test |
| // cases). |
| // |
| // Test case-specific instances should only be used in the presence of hard-to-diagnose bugs that |
| // only occur under RBE. |
| func setUpEmulatorBazelRBEOnly(t sktest.TestingT, emulator Emulator, fn EmulatorLauncherFn) { |
| started, err := fn() |
| if err != nil { |
| t.Fatalf("Could not start %s Emulator: %s", emulator, err) |
| return |
| } |
| if err = setEmulatorHostEnvVar(emulator); err != nil { |
| t.Fatalf("Could not set env var for %s Emulator: %s", emulator, err) |
| return |
| } |
| |
| // If the above code actually started the emulator, give the emulator time to boot. |
| // |
| // Empirically chosen: A delay of 5 seconds seems OK for all emulators; shorter delays tend to |
| // cause flakes. |
| if started { |
| // TODO(kjlubick) use emulator health checks instead of just sleeping |
| time.Sleep(5 * time.Second) |
| fmt.Println("Finished sleeping waiting for emulator to boot") |
| } |
| |
| t.Cleanup(func() { |
| // By unsetting any *_EMULATOR_HOST environment variables set by the current test case, we |
| // ensure that any subsequent test cases only "see" the emulators they request via any of the |
| // unittest.Requires*Emulator functions. This makes dependencies on emulators explicit at the |
| // test case level, and makes individual test cases more self-documenting. |
| if err := unsetAllEmulatorHostEnvVars(); err != nil { |
| t.Fatalf("Error while unsetting the emulator host environment variables: %v", err) |
| } |
| }) |
| } |
| |
| // StartEmulatorCmd performs some shared-setup to starting emulators. It should not be called by |
| // anything other than subpackages of this package. |
| func StartEmulatorCmd(cmd *exec.Cmd) error { |
| cmd.Stdout = os.Stdout |
| cmd.Stderr = os.Stderr |
| if bazel.InBazelTestOnRBE() { |
| // Force emulator child processes to die as soon as the parent process (e.g. the Go test runner) |
| // dies. If we don't do this, the emulators will continue running indefinitely after the parent |
| // process dies, eventually timing out. |
| // |
| // Note that this is only possible under Linux. The below function call will panic under |
| // non-Linux operating systems. Running emulator tests under RBE on non-Linux OSes is therefore |
| // not supported. This is OK because our RBE instance is currently Linux-only. See the comments |
| // in the function body for alternative approaches if we ever decide to run emulator tests under |
| // RBE on other operating systems. |
| cmd.SysProcAttr = makeSysProcAttrWithPdeathsigSIGKILL() |
| } |
| |
| // Start the emulator. |
| fmt.Printf("Starting emulator: %s %s\n", cmd.Path, cmd.Args) |
| if err := cmd.Start(); err != nil { |
| return skerr.Wrap(err) |
| } |
| |
| // Log the emulator's exit status. |
| go func() { |
| err := cmd.Wait() |
| if err != nil { |
| fmt.Printf("Emulator %s finished with error: %v\n", cmd.Path, err) |
| return |
| } |
| fmt.Printf("Emulator %s finished with exit status: %d\n", cmd.Path, cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()) |
| }() |
| |
| return nil |
| } |