blob: 17d95df27acbd714c691df8ec5e6cddbd545ccbd [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rt_test
import (
"fmt"
"io"
"os"
"testing"
"v.io/v23"
"v.io/x/ref/lib/signals"
"v.io/x/ref/test/v23test"
)
var cstderr io.Writer
func init() {
if testing.Verbose() {
cstderr = os.Stderr
}
}
func writeln(w io.Writer, s string) {
w.Write([]byte(s + "\n"))
}
// TestSimpleServerSignal verifies that sending a signal to the simple server
// causes it to exit cleanly.
func TestSimpleServerSignal(t *testing.T) {
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
c := sh.FuncCmd(simpleServerProgram)
stdin := c.StdinPipe()
c.Start()
c.S.Expect("Ready")
c.Signal(os.Interrupt)
c.S.Expect("Received signal interrupt")
c.S.Expect("Interruptible cleanup")
c.S.Expect("Deferred cleanup")
writeln(stdin, "close")
c.S.ExpectEOF()
}
// TestSimpleServerLocalStop verifies that sending a local stop command to the
// simple server causes it to exit cleanly.
func TestSimpleServerLocalStop(t *testing.T) {
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
c := sh.FuncCmd(simpleServerProgram)
stdin := c.StdinPipe()
c.Start()
c.S.Expect("Ready")
writeln(stdin, "stop")
c.S.Expect(fmt.Sprintf("Received signal %s", v23.LocalStop))
c.S.Expect("Interruptible cleanup")
c.S.Expect("Deferred cleanup")
writeln(stdin, "close")
c.S.ExpectEOF()
}
// TestSimpleServerDoubleSignal verifies that sending a succession of two
// signals to the simple server causes it to initiate the cleanup sequence on
// the first signal and then exit immediately on the second signal.
func TestSimpleServerDoubleSignal(t *testing.T) {
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
c := sh.FuncCmd(simpleServerProgram)
c.ExitErrorIsOk = true
c.Start()
c.S.Expect("Ready")
c.Signal(os.Interrupt)
c.S.Expect("Received signal interrupt")
c.Signal(os.Interrupt)
c.Wait()
if c.Err == nil {
t.Fatalf("expected an error")
}
if got, want := c.Err.Error(), fmt.Sprintf("exit status %d", signals.DoubleStopExitCode); got != want {
t.Errorf("got %q, want %q", got, want)
}
}
// TestSimpleServerLocalForceStop verifies that sending a local ForceStop
// command to the simple server causes it to exit immediately.
func TestSimpleServerLocalForceStop(t *testing.T) {
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
c := sh.FuncCmd(simpleServerProgram)
stdin := c.StdinPipe()
c.ExitErrorIsOk = true
c.Start()
c.S.Expect("Ready")
writeln(stdin, "forcestop")
c.S.Expect("straight exit")
c.Wait()
if c.Err == nil {
t.Fatalf("expected an error")
}
if got, want := c.Err.Error(), fmt.Sprintf("exit status %d", testForceStopExitCode); got != want {
t.Errorf("got %q, want %q", got, want)
}
}
// TestSimpleServerKill demonstrates that a SIGKILL still forces the server
// to exit regardless of our signal handling.
func TestSimpleServerKill(t *testing.T) {
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
c := sh.FuncCmd(simpleServerProgram)
c.ExitErrorIsOk = true
c.Start()
c.S.Expect("Ready")
c.Signal(os.Kill)
c.Wait()
if c.Err == nil {
t.Fatalf("expected an error")
}
if got, want := c.Err.Error(), "signal: killed"; got != want {
t.Errorf("got %q, want %q", got, want)
}
}
// TestComplexServerSignal verifies that sending a signal to the complex server
// initiates the cleanup sequence in that server (we observe the printouts
// corresponding to all the simulated sequential/parallel and
// blocking/interruptible shutdown steps), and then exits cleanly.
func TestComplexServerSignal(t *testing.T) {
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
c := sh.FuncCmd(complexServerProgram)
stdin := c.StdinPipe()
c.Start()
c.S.Expect("Ready")
c.Signal(os.Interrupt)
c.S.Expect("Received signal interrupt")
c.S.ExpectSetRE("Sequential blocking cleanup",
"Sequential interruptible cleanup",
"Parallel blocking cleanup1",
"Parallel blocking cleanup2",
"Parallel interruptible cleanup1",
"Parallel interruptible cleanup2")
writeln(stdin, "close")
c.S.ExpectEOF()
}
// TestComplexServerLocalStop verifies that sending a local stop command to the
// complex server initiates the cleanup sequence in that server (we observe the
// printouts corresponding to all the simulated sequential/parallel and
// blocking/interruptible shutdown steps), and then exits cleanly.
func TestComplexServerLocalStop(t *testing.T) {
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
c := sh.FuncCmd(complexServerProgram)
stdin := c.StdinPipe()
c.Start()
c.S.Expect("Ready")
writeln(stdin, "stop")
c.S.Expect(fmt.Sprintf("Stop %s", v23.LocalStop))
c.S.ExpectSetRE(
"Sequential blocking cleanup",
"Sequential interruptible cleanup",
"Parallel blocking cleanup1",
"Parallel blocking cleanup2",
"Parallel interruptible cleanup1",
"Parallel interruptible cleanup2",
)
writeln(stdin, "close")
c.S.ExpectEOF()
}
// TestComplexServerDoubleSignal verifies that sending a succession of two
// signals to the complex server has the expected effect: the first signal
// initiates the cleanup steps and the second signal kills the process, but only
// after the blocking shutdown steps were allowed to complete (as observed by
// the corresponding printouts from the server). Note that we have no
// expectations on whether or not the interruptible shutdown steps execute.
func TestComplexServerDoubleSignal(t *testing.T) {
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
c := sh.FuncCmd(complexServerProgram)
c.ExitErrorIsOk = true
c.Start()
c.S.Expect("Ready")
c.Signal(os.Interrupt)
c.S.Expect("Received signal interrupt")
c.Signal(os.Interrupt)
c.S.ExpectSetEventuallyRE(
"Sequential blocking cleanup",
"Parallel blocking cleanup1",
"Parallel blocking cleanup2")
c.Wait()
if c.Err == nil {
t.Fatalf("expected an error")
}
if got, want := c.Err.Error(), fmt.Sprintf("exit status %d", signals.DoubleStopExitCode); got != want {
t.Errorf("got %q, want %q", got, want)
}
}
// TestComplexServerLocalForceStop verifies that sending a local ForceStop
// command to the complex server forces it to exit immediately.
func TestComplexServerLocalForceStop(t *testing.T) {
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
c := sh.FuncCmd(complexServerProgram)
stdin := c.StdinPipe()
c.ExitErrorIsOk = true
c.Start()
c.S.Expect("Ready")
writeln(stdin, "forcestop")
c.S.Expect("straight exit")
c.Wait()
if c.Err == nil {
t.Fatalf("expected an error")
}
if got, want := c.Err.Error(), fmt.Sprintf("exit status %d", testForceStopExitCode); got != want {
t.Errorf("got %q, want %q", got, want)
}
}
// TestComplexServerKill demonstrates that a SIGKILL still forces the server to
// exit regardless of our signal handling.
func TestComplexServerKill(t *testing.T) {
sh := v23test.NewShell(t, nil)
defer sh.Cleanup()
c := sh.FuncCmd(complexServerProgram)
c.ExitErrorIsOk = true
c.Start()
c.S.Expect("Ready")
c.Signal(os.Kill)
c.Wait()
if c.Err == nil {
t.Fatalf("expected an error")
}
if got, want := c.Err.Error(), "signal: killed"; got != want {
t.Errorf("got %q, want %q", got, want)
}
}