Changes to the test framework to support templating of
the json test files to enable the creation of very large
stress test suites. Still a work in progress.

Change-Id: I1a35e4143a2feb577c9ad6048a0339c7b9dc0f89
diff --git a/tests/afrouter/templates/main.go b/tests/afrouter/templates/main.go
index f539a02..c2fddb9 100644
--- a/tests/afrouter/templates/main.go
+++ b/tests/afrouter/templates/main.go
@@ -26,9 +26,28 @@
 	"os/exec"
 	"strings"
 	"context"
+	slog "log"
+	"io/ioutil"
+	"encoding/json"
+	"google.golang.org/grpc/grpclog"
 	"github.com/opencord/voltha-go/common/log"
 )
 
+type TestCase struct {
+	Title string `json:"title"`
+	Result bool `json:"result"`
+	Info []string `json:"info"`
+}
+
+type TestSuite struct {
+	Name string `json:"name"`
+	TestCases []TestCase `json:"testCases"`
+}
+
+type TestRun struct {
+	TestSuites []TestSuite
+}
+
 var resFile *tstLog
 type tstLog struct {
 	fp * os.File
@@ -95,6 +114,37 @@
 	//time.Sleep(1 * time.Second)
 }
 
+func readStats(stats * TestRun) () {
+	// Check if the  stats file exists
+	if _,err := os.Stat("stats.json"); err != nil {
+		// Nothing to do, just return
+		return
+	}
+	// The file is there, read it an unmarshal it into the stats struct
+	if statBytes, err := ioutil.ReadFile("stats.json"); err != nil {
+		log.Error(err)
+		return
+	} else if err := json.Unmarshal(statBytes, stats); err != nil {
+		log.Error(err)
+		return
+	}
+}
+
+func writeStats(stats * TestRun) () {
+	// Check if the  stats file exists
+	// The file is there, read it an unmarshal it into the stats struct
+	if statBytes, err := json.MarshalIndent(stats, "","    "); err != nil {
+		log.Error(err)
+		return
+	} else if err := ioutil.WriteFile("stats.json.new", statBytes, 0644); err != nil {
+		log.Error(err)
+		return
+	}
+	os.Rename("stats.json", "stats.json~")
+	os.Rename("stats.json.new", "stats.json")
+}
+var stats TestRun
+
 func main() {
 	var err error
 
@@ -102,9 +152,13 @@
 	if _, err = log.SetDefaultLogger(log.JSON, 0, nil); err != nil {
 		log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
 	}
-
 	defer log.CleanUp()
 
+	readStats(&stats)
+
+
+	grpclog.SetLogger(slog.New(os.Stderr, "grpc: ", slog.LstdFlags))
+
 	resFile = &tstLog{fn:os.Args[1]}
 	defer resFile.close()
 
@@ -133,5 +187,6 @@
 	// Run all the test cases now
 	log.Infof("Executing tests")
 	runTests()
+	writeStats(&stats)
 
 }
diff --git a/tests/afrouter/templates/runAll.go b/tests/afrouter/templates/runAll.go
index b4079d1..03cdab1 100644
--- a/tests/afrouter/templates/runAll.go
+++ b/tests/afrouter/templates/runAll.go
@@ -83,7 +83,7 @@
 	}
 	tl := &tstLog{fn:"results.txt"}
 	{{range .}}
-	cmdStr =  "./"+"{{.}}"[:len("{{.}}")-5]
+	cmdStr =  "./"+"{{.}}"+".e"
 	tl.testLogOnce("Running test suite '%s'\n", cmdStr[2:])
 
 	log.Infof("Running test suite %s",cmdStr)
diff --git a/tests/afrouter/templates/runTests.go b/tests/afrouter/templates/runTests.go
index 2c65f09..61ba5eb 100644
--- a/tests/afrouter/templates/runTests.go
+++ b/tests/afrouter/templates/runTests.go
@@ -18,9 +18,12 @@
 package main
 
 import (
+	"fmt"
+	"time"
 	"errors"
+	"context"
 	"encoding/json"
-	"golang.org/x/net/context"
+	//"golang.org/x/net/context"
 	"google.golang.org/grpc/metadata"
 	"github.com/opencord/voltha-go/common/log"
 	{{range .Imports}}
@@ -31,6 +34,8 @@
 	{{end}}
 )
 
+var glCtx context.Context
+
 func resetChannels() {
 	// Drain all channels of data
 	for _,v := range servers {
@@ -56,15 +61,19 @@
 	} else {
 		resFile.testLog("\tTest Failed\n")
 	}
-	resetChannels()
+	//resetChannels()
 	{{end}}
 }
 
 {{range $k,$v := .Tests}}
 func test{{$k}}() error {
 	var rtrn error = nil
+	var cancel context.CancelFunc
+
+	glCtx, cancel = context.WithTimeout(context.Background(), 900*time.Millisecond)
+	defer cancel()
 	// Announce the test being run
-	resFile.testLog("******************** Running test case: {{$v.Name}}\n")
+	resFile.testLog("******************** Running test case ({{$k}}): {{$v.Name}}\n")
 	// Acquire the client used to run the test
 	cl := clients["{{$v.Send.Client}}"]
 	// Create the server's reply data structure
@@ -76,13 +85,18 @@
 		log.Error(err)
 		return err
 	}
-	select {
-	case servers["{{$s.Name}}"].replyData <- repl:
-	default:
-		err := errors.New("Could not provide server {{$s.Name}} with reply data")
-		log.Error(err)
-		return err
-	}
+	// Start a go routine to send the the reply data to the
+	// server. The go routine blocks until the server picks
+	// up the data or the timeout is exceeded.
+	go func (ctx context.Context) {
+		select {
+		case servers["{{$s.Name}}"].replyData <- repl:
+		case <-ctx.Done():
+			rtrn := errors.New("Could not provide server {{$s.Name}} with reply data")
+			log.Error(rtrn)
+			resFile.testLog("%s\n", rtrn.Error())
+		}
+	}(glCtx)
 	{{end}}
 
 	// Now call the RPC with the data provided
@@ -118,25 +132,27 @@
 	}
 	{{range $s := .Srvr}}
 	s = servers["{{$s.Name}}"]
+	// Oddly sometimes the data isn't in the channel yet when we come to read it.
 	select {
 	case i = <-s.incmg:
 		if i.payload != payload {
-			log.Errorf("Mismatched payload for test {{$v.Name}}, %s:%s", i.payload, payload)
-			resFile.testLog("Mismatched payload expected '%s', got '%s'\n", payload, i.payload)
-			rtrn = errors.New("Failed")
+			rtrn = errors.New(fmt.Sprintf("Mismatched payload expected '%s', got '%s'", payload, i.payload))
+			log.Error(rtrn.Error())
+			resFile.testLog("%s\n", rtrn.Error())
 		}
 		{{range $m := $s.Meta}}
 		if mv,ok := i.meta["{{$m.Key}}"]; ok == true {
 			if "{{$m.Val}}" != mv[0] {
-				log.Errorf("Mismatched metadata for test {{$v.Name}}, %s:%s", mv[0], "{{$m.Val}}")
-				resFile.testLog("Mismatched metadata on server '%s' expected '%s', got '%s'\n", "{{$s.Name}}", "{{$m.Val}}", mv[0])
-				rtrn = errors.New("Failed")
+				rtrn=errors.New(fmt.Sprintf("Mismatched metadata on server '%s' expected '%s', got '%s'", "{{$s.Name}}", "{{$m.Val}}", mv[0]))
+				log.Error(rtrn.Error())
+				resFile.testLog("%s\n", rtrn.Error())
 			}
 		}
 		{{end}}
-	default:
-		err := errors.New("No response data available for server {{$s.Name}}")
-		log.Error(err)
+	case <-glCtx.Done():
+		rtrn = errors.New("Timeout: no response data available for server {{$s.Name}}")
+		resFile.testLog("%s\n", rtrn.Error())
+		log.Error(rtrn)
 	}
 	{{end}}
 
diff --git a/tests/afrouter/templates/server.go b/tests/afrouter/templates/server.go
index 85c3b93..ffea3d5 100644
--- a/tests/afrouter/templates/server.go
+++ b/tests/afrouter/templates/server.go
@@ -76,7 +76,7 @@
 func {{.Name}}ListenAndServe() error {
 	var s {{.Name}}TestServer
 
-	s.control = &serverCtl{replyData:make(chan *reply,1), incmg:make(chan *incoming,1)}
+	s.control = &serverCtl{replyData:make(chan *reply), incmg:make(chan *incoming)}
 	servers["{{.Name}}"] = s.control
 
 	log.Debugf("Starting server %s on port %d", "{{.Name}}", {{.Port}})
@@ -112,15 +112,18 @@
 {{range .Methods}}
 {{if .Ss}}
 func (ts {{$.Name}}TestServer) {{.Name}}(in *{{.Param}}, srvr {{.Pkg}}.{{.Svc}}_{{.Name}}Server) error {
+	log.Debug("Serving server streaming {{$.Name}}")
 	return nil
 }
 {{else if .Cs}}
 func (ts {{$.Name}}TestServer) {{.Name}}({{.Pkg}}.{{.Svc}}_{{.Name}}Server) error {
+	log.Debug("Serving client streaming {{$.Name}}")
 	return nil
 }
 {{else}}
 func  (ts {{$.Name}}TestServer) {{.Name}}(ctx context.Context, in *{{.Param}}) (*{{.Rtrn}}, error) {
 	var r * incoming = &incoming{}
+	log.Debug("Serving {{$.Name}}")
 	// Read the metadata
 	if md,ok := metadata.FromIncomingContext(ctx); ok == false {
 		log.Error("Getting matadata during call to {{.Name}}")
@@ -134,7 +137,15 @@
 		r.payload = string(parm)
 	}
 	// Send the server information back to the test framework
-	ts.control.incmg <- r
+	go func(ctx context.Context) {
+		select {
+		case ts.control.incmg <- r:
+			return
+		case <-ctx.Done():
+			return
+
+		}
+	}(glCtx)
 	// Read the value that needs to be returned from the channel
 	select {
 	case d := <-ts.control.replyData:
@@ -144,8 +155,8 @@
 			default:
 				return nil, errors.New("Mismatched type in call to {{.Name}}")
 		}
-	default:
-		return nil, errors.New("Nothing in the reply data channel in call to {{.Name}}, sending nil")
+	case <-glCtx.Done():
+		return nil, errors.New("Timeout: nothing in the reply data channel in call to {{.Name}}, sending nil")
 	}
 	return &{{.Rtrn}}{},nil
 }