Skip to content

Commit

Permalink
Implement `webrpc-test -waitForServer -timeout=10s' (#208)
Browse files Browse the repository at this point in the history
* Implement `webrpc-test -waitForServer -timeout=10s'

Will remove the need for waiting for the test server readiness via custom scripts, ie.
https://github.com/webrpc/gen-typescript/blob/a80789817830a8e55480745111eb259da5eeedf6/tests/test.sh#L13-L14

* PR feedback from @holowcost, add delay & use select{}

* PR feedback: Remove unnecessary os.Exit(0)
  • Loading branch information
VojtechVitek committed Jun 6, 2023
1 parent 0d39e64 commit 3d8704f
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 56 deletions.
93 changes: 52 additions & 41 deletions cmd/webrpc-test/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,77 +18,88 @@ var (
flags = flag.NewFlagSet("webrpc-test", flag.ContinueOnError)
clientFlag = flags.Bool("client", false, "run client tests")
serverFlag = flags.Bool("server", false, "run test server")
waitFlag = flags.Bool("waitForServer", false, "wait for server to be ready")
versionFlag = flags.Bool("version", false, "print version and exit")
printSchema = flags.Bool("print-schema", false, "obsolete flag (use -printRIDL)") // Obsolete.
printRIDL = flags.Bool("printRIDL", false, "print schema in RIDL")
printJSON = flags.Bool("printJSON", false, "print schema in JSON")

// webrpc-test -client -url=http://localhost:9988
clientFlags = flag.NewFlagSet("webrpc-test -client", flag.ExitOnError)
urlFlag = clientFlags.String("url", "http://localhost:9988", "run client against given server URL")
clientFlags = flag.NewFlagSet("webrpc-test -client", flag.ExitOnError)
clientUrlFlag = clientFlags.String("url", "http://localhost:9988", "run client against given server URL")

// webrpc-test -server -port=9988 -timeout=1m
serverFlags = flag.NewFlagSet("webrpc-test -server", flag.ExitOnError)
portFlag = serverFlags.Int("port", 9988, "run server at given port")
timeoutFlag = serverFlags.Duration("timeout", time.Minute, "exit after given timeout")
serverFlags = flag.NewFlagSet("webrpc-test -server", flag.ExitOnError)
serverPortFlag = serverFlags.Int("port", 9988, "run server at given port")
serverTimeoutFlag = serverFlags.Duration("timeout", time.Minute, "exit after given timeout")

// webrpc-test -waitForServer -url=http://localhost:9988 -timeout=1m
waitFlags = flag.NewFlagSet("webrpc-test -waitForServer", flag.ExitOnError)
waitUrlFlag = waitFlags.String("url", "http://localhost:9988", "run client against given server URL")
waitTimeoutFlag = waitFlags.Duration("timeout", time.Minute, "exit after given timeout")
)

func main() {
if len(os.Args) < 2 {
fmt.Fprintf(os.Stderr, "-server or -client flag is required\n")
os.Exit(1)
if len(os.Args) >= 2 {
if err := flags.Parse(os.Args[1:2]); err != nil {
os.Exit(1)
}
}

_ = flags.Parse(os.Args[1:2])

if *versionFlag {
switch {
case *versionFlag:
fmt.Println("webrpc-test", webrpc.VERSION)
os.Exit(0)
}

if *printRIDL || *printSchema {
case *printRIDL, *printSchema:
fmt.Println(tests.GetRIDLSchema())
os.Exit(0)
}

if *printJSON {
case *printJSON:
fmt.Println(tests.GetJSONSchema())
os.Exit(0)
}

if !*serverFlag && !*clientFlag {
fmt.Fprintf(os.Stderr, "-server or -client flag is required\n")
os.Exit(1)
}

if *clientFlag {
case *clientFlag:
if err := clientFlags.Parse(os.Args[2:]); err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

if err := client.RunTests(context.Background(), *clientUrlFlag); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

case *serverFlag:
if err := serverFlags.Parse(os.Args[2:]); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

err := client.RunTests(context.Background(), *urlFlag)
server, err := server.RunTestServer(fmt.Sprintf("0.0.0.0:%v", *serverPortFlag), *serverTimeoutFlag)
if err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

os.Exit(0)
}
if err := server.Wait(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

if err := serverFlags.Parse(os.Args[2:]); err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(1)
}
case *waitFlag:
if err := waitFlags.Parse(os.Args[2:]); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

server, err := server.RunTestServer(fmt.Sprintf("0.0.0.0:%v", *portFlag), *timeoutFlag)
if err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(1)
}
start := time.Now()
if err := client.Wait(*waitUrlFlag, *waitTimeoutFlag); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

fmt.Fprintf(os.Stdout, "wait: test server ready in %v\n", time.Since(start).Round(time.Millisecond))

if err := server.Wait(); err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
default:
flags.Usage()
os.Exit(1)
}
}
23 changes: 23 additions & 0 deletions tests/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,31 @@ import (
"fmt"
"net/http"
"strings"
"time"
)

func Wait(serverURL string, timeout time.Duration) error {
testApi := NewTestApiClient(serverURL, &http.Client{})

ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

for {
err := testApi.GetEmpty(ctx)
if err == nil {
return nil // Success.
}

select {
case <-ctx.Done():
return fmt.Errorf("wait: test server (%v) still not ready after %v", serverURL, timeout)

case <-time.After(100 * time.Millisecond):
// Add a delay between retry attempts.
}
}
}

func RunTests(ctx context.Context, serverURL string) error {
var errs []error // Note: We can't use Go 1.20's errors.Join() until we drop support for older Go versions.

Expand Down
1 change: 0 additions & 1 deletion tests/embed.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package tests

import (
"embed"
_ "embed"

"github.com/webrpc/webrpc/schema/ridl"
)
Expand Down
27 changes: 13 additions & 14 deletions tests/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,23 +186,22 @@ func RunTestServer(addr string, timeout time.Duration) (*testServer, error) {
closed: make(chan struct{}),
}

if timeout > 0 {
go func() {
timeoutCtx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
go func() {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

select {
case <-srv.closed:
select {
case <-srv.closed:

case <-timeoutCtx.Done():
gracefulShutdownCtx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
case <-ctx.Done():
// 1s graceful shutdown
gracefulShutdownCtx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

srv.err = srv.Shutdown(gracefulShutdownCtx)
close(srv.closed)
}
}()
}
srv.err = srv.Shutdown(gracefulShutdownCtx)
close(srv.closed)
}
}()

l, err := net.Listen("tcp", addr)
if err != nil {
Expand Down

0 comments on commit 3d8704f

Please sign in to comment.