From 5a7d3f0942dd7e661f63bb59be883e6e245c0ddd Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 8 Sep 2017 19:45:15 -0400 Subject: test: clean up, create a parameterized testDriver() --- nslcd_systemd/misc_test.go | 176 ++++++++++++++++++++++++++------------------- 1 file changed, 104 insertions(+), 72 deletions(-) diff --git a/nslcd_systemd/misc_test.go b/nslcd_systemd/misc_test.go index a910cd9..8ef697a 100644 --- a/nslcd_systemd/misc_test.go +++ b/nslcd_systemd/misc_test.go @@ -22,6 +22,7 @@ import ( "io/ioutil" "net" "os" + "runtime" "strings" "sync" "syscall" @@ -34,13 +35,39 @@ import ( "golang.org/x/sys/unix" ) +func init() { + if fdIsDevNull(3) == nil { + return + } + + devnull, err := os.OpenFile("/dev/null", os.O_RDWR, 0666) + if err != nil { + panic(err) + } + if devnull.Fd() == 3 { + return + } + + fmt.Fprintln(os.Stderr, "Could not open /dev/null on FD 3; calling dup2 and re-exec()ing") + // shell out to do the FD manipulation--If we made it here, + // there's a good chance that FD3 was managed by the go + // runtime, and would be changed before we execve(2). + panic(syscall.Exec("/bin/sh", append([]string{"sh", "-c", "exec -- \"$@\" 3<>/dev/null"}, os.Args...), os.Environ())) +} + type testContext struct { *testing.T tmpdir string status chan<- string } -func testBadClient(t *testContext, backend nslcd_systemd.Backend, toclose bool) { +func testDriver( + t *testContext, + backend nslcd_systemd.Backend, + limits nslcd_server.Limits, + notifyHandler func(dat []byte, oob []byte) error, + client func(sockname string)) { + t.status <- "setting up" errfatal := func(err error) { @@ -51,72 +78,31 @@ func testBadClient(t *testContext, backend nslcd_systemd.Backend, toclose bool) evExitSupervisor := make(chan error) evExitServer := make(chan uint8) - evReload := make(chan bool) // supervisor ////////////////////////////////////////////////////////// notify_sock, err := sdNotifyListen(t.tmpdir + "/notify.sock") errfatal(err) go func() { - reloading := false - evExitSupervisor <- sdNotifyHandle(notify_sock, func(dat []byte, oob []byte) error { - for _, line := range strings.Split(string(dat), "\n") { - switch line { - case "RELOADING=1": - reloading = true - case "READY=1": - if reloading { - evReload <- true - } - reloading = false - } - } - return nil - }) + evExitSupervisor <- sdNotifyHandle(notify_sock, notifyHandler) }() // server ////////////////////////////////////////////////////////////// errfatal(sdActivatedStream(t.tmpdir + "/nslcd.sock")) go func() { - evExitServer <- nslcd_systemd.Main(backend, nslcd_server.Limits{ - Timeout: 1 * time.Second, - }) + evExitServer <- nslcd_systemd.Main(backend, limits) }() // client/driver /////////////////////////////////////////////////////// + t.status <- "running client" + client(t.tmpdir + "/nslcd.sock") - t.status <- "talking with server" - conn, err := net.Dial("unix", t.tmpdir+"/nslcd.sock") - errfatal(err) - errfatal(nslcd_proto.Write(conn, nslcd_proto.NSLCD_VERSION)) - errfatal(nslcd_proto.Write(conn, nslcd_proto.NSLCD_ACTION_PASSWD_ALL)) - // Wait for NSLCD_RESULT_*, to make sure the server has made - // it in to backend code. - var n int32 - errfatal(nslcd_proto.Read(conn, &n)) - if n != nslcd_proto.NSLCD_VERSION { - t.Fatal("server version wrong") - } - errfatal(nslcd_proto.Read(conn, &n)) - if n != nslcd_proto.NSLCD_ACTION_PASSWD_ALL { - t.Fatal("server action wrong") - } - errfatal(nslcd_proto.Read(conn, &n)) - if n != nslcd_proto.NSLCD_RESULT_BEGIN && n != nslcd_proto.NSLCD_RESULT_END { - t.Fatal("server result malformed") - } - if toclose { - errfatal(conn.Close()) - } - - t.status <- "waiting for server reload" - errfatal(unix.Kill(unix.Getpid(), unix.SIGHUP)) - <-evReload + // exit //////////////////////////////////////////////////////////////// // A limitation of Unix sockets is that some may get dropped // if they arrive close together. So give it some (a half // second is probably generous by a couple orders of - // magnitude) time to handle SIGHUP before sending SIGTERM, so - // that we are sure it gets both. + // magnitude) time between the client sending any signals and + // us sending SIGTERM, so that we are sure it gets both. time.Sleep(time.Second / 2) t.status <- "waiting for server exit" @@ -136,6 +122,9 @@ func testBadClient(t *testContext, backend nslcd_systemd.Backend, toclose bool) errfatal(notify_sock.Close()) } +func testClientHang(t *testContext, backend nslcd_systemd.Backend, toclose bool) { +} + type NonLockingBackend struct { nslcd_server.NilBackend } @@ -203,27 +192,7 @@ func (o *LockingBackend) Passwd_All(cred unix.Ucred, req nslcd_proto.Request_Pas return ret } -func init() { - if fdIsDevNull(3) == nil { - return - } - - devnull, err := os.OpenFile("/dev/null", os.O_RDWR, 0666) - if err != nil { - panic(err) - } - if devnull.Fd() == 3 { - return - } - - fmt.Fprintln(os.Stderr, "Could not open /dev/null on FD 3; calling dup2 and re-exec()ing") - // shell out to do the FD manipulation--If we made it here, - // there's a good chance that FD3 was managed by the go - // runtime, and would be changed before we execve(2). - panic(syscall.Exec("/bin/sh", append([]string{"sh", "-c", "exec -- \"$@\" 3<>/dev/null"}, os.Args...), os.Environ())) -} - -func TestBadClient(t *testing.T) { +func TestClientHang(t *testing.T) { testcases := []struct { name string backend nslcd_systemd.Backend @@ -236,7 +205,7 @@ func TestBadClient(t *testing.T) { } for _, testcase := range testcases { func() { - tmpdir, err := ioutil.TempDir("", "go-test-libnslcd-bad-client.") + tmpdir, err := ioutil.TempDir("", "go-test-libnslcd-client-hang.") if err != nil { t.Fatal(err) } @@ -245,7 +214,70 @@ func TestBadClient(t *testing.T) { t.Run(testcase.name, func(t *testing.T) { testWithTimeout(t, 2*time.Second, func(t *testing.T, s chan<- string) { ctx := &testContext{T: t, tmpdir: tmpdir, status: s} - testBadClient(ctx, testcase.backend, testcase.toclose) + + backend := testcase.backend + + limits := nslcd_server.Limits{ + Timeout: 1 * time.Second, + } + + evReload := make(chan bool) + + notifyHandler := func() func(dat []byte, oob []byte) error { + reloading := false + return func(dat []byte, oob []byte) error { + for _, line := range strings.Split(string(dat), "\n") { + switch line { + case "RELOADING=1": + reloading = true + case "READY=1": + if reloading { + evReload <- true + } + reloading = false + } + } + return nil + } + }() + + client := func(sockname string) { + errfatal := func(err error) { + if err != nil { + t.Fatal(err) + } + } + + ctx.status <- "talking with server" + conn, err := net.Dial("unix", sockname) + errfatal(err) + errfatal(nslcd_proto.Write(conn, nslcd_proto.NSLCD_VERSION)) + errfatal(nslcd_proto.Write(conn, nslcd_proto.NSLCD_ACTION_PASSWD_ALL)) + // Wait for NSLCD_RESULT_*, to make sure the server has made + // it in to backend code. + var n int32 + errfatal(nslcd_proto.Read(conn, &n)) + if n != nslcd_proto.NSLCD_VERSION { + ctx.Fatal("server version wrong") + } + errfatal(nslcd_proto.Read(conn, &n)) + if n != nslcd_proto.NSLCD_ACTION_PASSWD_ALL { + ctx.Fatal("server action wrong") + } + errfatal(nslcd_proto.Read(conn, &n)) + if n != nslcd_proto.NSLCD_RESULT_BEGIN && n != nslcd_proto.NSLCD_RESULT_END { + ctx.Fatal("server result malformed") + } + if testcase.toclose { + errfatal(conn.Close()) + } + + ctx.status <- "waiting for server reload" + errfatal(unix.Kill(unix.Getpid(), unix.SIGHUP)) + <-evReload + } + + testDriver(ctx, backend, limits, notifyHandler, client) }) }) }() -- cgit v1.2.3