Initial commit for the affinity router test framework
added license junk.
Change-Id: I6faad2ca93b0a7bb5108a1ffe42ff82f30451ae9
diff --git a/tests/afrouter/arouter_test.json b/tests/afrouter/arouter_test.json
new file mode 100644
index 0000000..0144478
--- /dev/null
+++ b/tests/afrouter/arouter_test.json
@@ -0,0 +1,184 @@
+{
+ "servers": [
+ {
+ "name": "grpc_command",
+ "port": 5000,
+ "address":"",
+ "type": "grpc",
+ "routers": [
+ {
+ "_TODO":"Suport a router list, remove the package and service from the router",
+ "package":"voltha",
+ "service":"VolthaService",
+ "router":"vcore"
+ }
+ ]
+ }
+ ],
+ "routers": [
+ {
+ "name":"vcore",
+ "package": "voltha",
+ "service": "VolthaService",
+ "routes": [
+ {
+ "name":"dev_manager",
+ "proto_descriptor":"voltha.pb",
+ "type":"rpc_affinity_message",
+ "association":"round_robin",
+ "routing_field": "id",
+ "backend_cluster":"vcore",
+ "_COMMENT":"Methods are naturally southbound affinity binding unless otherwise specified below",
+ "methods":[ "CreateDevice",
+ "GetCoreInstance",
+ "EnableLogicalDevicePort",
+ "DisableLogicalDevicePort",
+ "EnableDevice",
+ "DisableDevice",
+ "RebootDevice",
+ "DeleteDevice",
+ "DownloadImage",
+ "CancelImageDownload",
+ "ActivateImageUpdate",
+ "RevertImageUpdate",
+ "UpdateDevicePmConfigs",
+ "CreateAlarmFilter",
+ "UpdateAlarmFilter",
+ "DeleteAlarmFilter",
+ "SelfTest"],
+ "_COMMENT":"If a method is northbound affinity binding then association is used to route",
+ "_COMMENT":"but affinity is not set southbound but only based on the response",
+ "_COMMENT":"Methods here MUST be specified above, this overrides thier default beahvior",
+ "nb_binding_methods":["CreateDevice"],
+ "_TODO":"Overrides not implemented yet, config ignored",
+ "overrides": [
+ {"methods":["abc","def"], "routing_field":"id"},
+ {"methods":["ghi","jkl"]},
+ {"method":"mno", "routing_field":"id"},
+ {"method":"pqr"}
+ ]
+ },
+ {
+ "name":"read_only",
+ "type":"round_robin",
+ "association":"round_robin",
+ "backend_cluster":"vcore",
+ "methods":[ "ListDevicePorts",
+ "ListDevicePmConfigs",
+ "GetImages",
+ "GetImageDownloadStatus",
+ "GetImageDownload",
+ "ListImageDownloads",
+ "ListDeviceFlows",
+ "ListDeviceFlowGroups",
+ "ListLogicalDeviceFlows",
+ "ListLogicalDeviceFlowGroups",
+ "ListDevices",
+ "GetDevice",
+ "GetDeviceType",
+ "GetDeviceGroup",
+ "GetLogicalDevice",
+ "GetAlarmFilter",
+ "ListLogicalDevicePorts",
+ "GetLogicalDevicePort"
+ ]
+ },
+ {
+ "name":"dev_manager_ofagent",
+ "type":"binding",
+ "_association":"round_robin",
+ "binding": {
+ "type":"header",
+ "field":"voltha_backend_name",
+ "method":"Subscribe",
+ "association":"round_robin"
+ },
+ "backend_cluster":"vcore",
+ "methods":["StreamPacketsOut",
+ "Subscribe",
+ "ListLogicalDevices",
+ "ListDeviceFlowGroups",
+ "ListLogicalDeviceFlowGroups",
+ "ListDeviceFlows",
+ "UpdateLogicalDeviceFlowTable",
+ "UpdateLogicalDeviceFlowGroupTable",
+ "ListLogicalDeviceFlows"
+ ],
+ "_TODO":"Overrides not implemented yet, config ignored",
+ "overrides": [
+ {"methods":["abc","def"], "routing_field":"id"},
+ {"methods":["ghi","jkl"]},
+ {"method":"mno", "routing_field":"id"},
+ {"method":"pqr"}
+ ]
+ }
+ ]
+ }
+ ],
+ "backend_clusters": [
+ {
+ "name":"vcore",
+ "backends":[ {
+ "name":"vcore1",
+ "type":"active_active",
+ "association": {
+ "strategy":"serial_number",
+ "location":"header",
+ "_TODO":"The key below needs to be implemented, currently hard coded",
+ "key":"voltha_serial_number"
+ },
+ "connections": [ {
+ "name":"vcore11",
+ "addr":"127.0.0.1",
+ "port":"5011"
+ },
+ {
+ "name":"vcore12",
+ "addr":"127.0.0.1",
+ "port":"5012"
+ }]
+ },
+ {
+ "name":"vcore2",
+ "type":"active_active",
+ "association": {
+ "strategy":"serial_number",
+ "location":"header"
+ },
+ "connections": [ {
+ "name":"vcore21",
+ "addr":"127.0.0.1",
+ "port":"5021"
+ },
+ {
+ "name":"vcore22",
+ "addr":"127.0.0.1",
+ "port":"5022"
+ }]
+ },
+ {
+ "name":"vcore3",
+ "type":"active_active",
+ "association": {
+ "strategy":"serial_number",
+ "location":"header"
+ },
+ "connections": [ {
+ "name":"vcore31",
+ "addr":"127.0.0.1",
+ "port":"5031"
+ },
+ {
+ "name":"vcore32",
+ "addr":"127.0.0.1",
+ "port":"5032"
+ }]
+ }]
+ }
+ ],
+ "api": {
+ "_comment":"If this isn't defined then no api is available for dynamic configuration and queries",
+ "address":"",
+ "port":55554
+ }
+}
diff --git a/tests/afrouter/suites/main.json b/tests/afrouter/suites/main.json
new file mode 100644
index 0000000..e4862c6
--- /dev/null
+++ b/tests/afrouter/suites/main.json
@@ -0,0 +1,6 @@
+{
+"__COMMENT":"Top level test driver file",
+"suites": [
+ "test1.json"
+]
+}
diff --git a/tests/afrouter/suites/test1.json b/tests/afrouter/suites/test1.json
new file mode 100644
index 0000000..a6791a0
--- /dev/null
+++ b/tests/afrouter/suites/test1.json
@@ -0,0 +1,117 @@
+{
+ "environment":{
+ "cmdLine":"afrouter -config arouter_test.json",
+ "protoFiles": [
+ {
+ "importPath":"github.com/opencord/voltha-go/protos/",
+ "service":"VolthaService",
+ "package":"voltha"
+ }
+ ],
+ "imports": [
+ "github.com/golang/protobuf/ptypes/empty",
+ "github.com/opencord/voltha-go/protos/openflow_13"
+ ],
+ "protoDesc":"voltha.pb",
+ "protoSubst": [
+ {
+ "from":"google.protobuf.Empty",
+ "to":"empty.Empty"
+ }
+ ],
+ "clients": [
+ {
+ "name":"client",
+ "port":"5000"
+ }
+ ],
+ "servers": [
+ {
+ "name":"core11",
+ "port":"5011"
+ },
+ {
+ "name":"core12",
+ "port":"5012"
+ },
+ {
+ "name":"core21",
+ "port":"5021"
+ },
+ {
+ "name":"core22",
+ "port":"5022"
+ },
+ {
+ "name":"core31",
+ "port":"5031"
+ },
+ {
+ "name":"core32",
+ "port":"5032"
+ }
+ ]
+ },
+ "tests":[
+ {
+ "name":"Test CreateDevice",
+ "send": {
+ "client":"client",
+ "method":"CreateDevice",
+ "param":"{Type:\"simulated_olt\"}",
+ "expect":"{Id:\"abcd1234\",Type:\"simulated_olt\"}",
+ "_meta":""
+ },
+ "servers": [
+ {
+ "name":"core11",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"0"
+ }
+ ]
+ },
+ {
+ "name":"core12",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"0"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name":"Test GetDevice",
+ "send": {
+ "client":"client",
+ "method":"GetDevice",
+ "param":"{Id:\"abcd1234\"}",
+ "expect":"{Id:\"abcd1234\",Type:\"simulated_olt\"}",
+ "_meta":""
+ },
+ "servers": [
+ {
+ "name":"core11",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"0"
+ }
+ ]
+ },
+ {
+ "name":"core12",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"0"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/afrouter/suites/test2.json b/tests/afrouter/suites/test2.json
new file mode 100644
index 0000000..2d3d7ad
--- /dev/null
+++ b/tests/afrouter/suites/test2.json
@@ -0,0 +1,87 @@
+{
+ "environment":{
+ "cmdLine":"afrouter -config arouter_test.json",
+ "protoFiles": [
+ {
+ "importPath":"github.com/opencord/voltha-go/protos/",
+ "service":"VolthaService",
+ "package":"voltha"
+ }
+ ],
+ "imports": [
+ "github.com/golang/protobuf/ptypes/empty",
+ "github.com/opencord/voltha-go/protos/openflow_13"
+ ],
+ "protoDesc":"voltha.pb",
+ "protoSubst": [
+ {
+ "from":"google.protobuf.Empty",
+ "to":"empty.Empty"
+ }
+ ],
+ "clients": [
+ {
+ "name":"client",
+ "port":"5000"
+ }
+ ],
+ "servers": [
+ {
+ "name":"core11",
+ "port":"5011"
+ },
+ {
+ "name":"core12",
+ "port":"5012"
+ },
+ {
+ "name":"core21",
+ "port":"5021"
+ },
+ {
+ "name":"core22",
+ "port":"5022"
+ },
+ {
+ "name":"core31",
+ "port":"5031"
+ },
+ {
+ "name":"core32",
+ "port":"5032"
+ }
+ ]
+ },
+ "tests":[
+ {
+ "send": {
+ "client":"client",
+ "method":"CreateDevice",
+ "param":"json struct",
+ "_meta":""
+ },
+ "servers": [
+ {
+ "name":"server11",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"1"
+ }
+ ],
+ "param":"mirror_client"
+ },
+ {
+ "name":"server12",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"1"
+ }
+ ],
+ "param":"mirror_client"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/afrouter/suites/test3.json b/tests/afrouter/suites/test3.json
new file mode 100644
index 0000000..2d3d7ad
--- /dev/null
+++ b/tests/afrouter/suites/test3.json
@@ -0,0 +1,87 @@
+{
+ "environment":{
+ "cmdLine":"afrouter -config arouter_test.json",
+ "protoFiles": [
+ {
+ "importPath":"github.com/opencord/voltha-go/protos/",
+ "service":"VolthaService",
+ "package":"voltha"
+ }
+ ],
+ "imports": [
+ "github.com/golang/protobuf/ptypes/empty",
+ "github.com/opencord/voltha-go/protos/openflow_13"
+ ],
+ "protoDesc":"voltha.pb",
+ "protoSubst": [
+ {
+ "from":"google.protobuf.Empty",
+ "to":"empty.Empty"
+ }
+ ],
+ "clients": [
+ {
+ "name":"client",
+ "port":"5000"
+ }
+ ],
+ "servers": [
+ {
+ "name":"core11",
+ "port":"5011"
+ },
+ {
+ "name":"core12",
+ "port":"5012"
+ },
+ {
+ "name":"core21",
+ "port":"5021"
+ },
+ {
+ "name":"core22",
+ "port":"5022"
+ },
+ {
+ "name":"core31",
+ "port":"5031"
+ },
+ {
+ "name":"core32",
+ "port":"5032"
+ }
+ ]
+ },
+ "tests":[
+ {
+ "send": {
+ "client":"client",
+ "method":"CreateDevice",
+ "param":"json struct",
+ "_meta":""
+ },
+ "servers": [
+ {
+ "name":"server11",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"1"
+ }
+ ],
+ "param":"mirror_client"
+ },
+ {
+ "name":"server12",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"1"
+ }
+ ],
+ "param":"mirror_client"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/afrouter/templates/client.go b/tests/afrouter/templates/client.go
new file mode 100644
index 0000000..d384baf
--- /dev/null
+++ b/tests/afrouter/templates/client.go
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+ "fmt"
+ "errors"
+ "encoding/json"
+ "google.golang.org/grpc"
+ "golang.org/x/net/context"
+ "github.com/opencord/voltha-go/common/log"
+ // Values generated by the go template
+ {{range .Imports}}
+ "{{.}}"
+ {{end}}
+ {{range .ProtoImports}}
+ {{.Short}} "{{.Package}}"
+ {{end}}
+ // End go template values
+)
+
+{{if .Ct}}{{else}}
+type clientCtl struct {
+ send func(string, interface{}, string) error
+ cncl context.CancelFunc
+ ctx context.Context
+}
+{{end}}
+
+type {{.Name}}ClientConn struct {
+ conn * grpc.ClientConn
+ control * clientCtl
+}
+
+
+var {{.Name}}Client *{{.Name}}ClientConn
+
+
+func {{.Name}}Connect() (*{{.Name}}ClientConn, error) {
+ log.Infof("Connecting client {{.Name}} to addr:127.0.0.1, port:{{.Port}}")
+ // Dial doesn't block, it just returns and continues connecting in the background.
+ // Check back later to confirm and increase the connection count.
+ cl := &{{.Name}}ClientConn{control:&clientCtl{}}
+ ctx, cnclFnc := context.WithCancel(context.Background())
+ cl.control.cncl = cnclFnc
+ cl.control.ctx = ctx
+ if conn, err := grpc.Dial("127.0.0.1:{{.Port}}", grpc.WithInsecure()); err != nil {
+ log.Errorf("Dialng connection :%v",err)
+ return nil, err
+ } else {
+ cl.conn = conn
+ }
+ {{.Name}}Client = cl
+ cl.control.send = {{.Name}}Send
+ clients["{{.Name}}"] = cl.control
+ return cl, nil
+}
+
+// This function will make the requested RPC with the supplied
+// parameter and validate that the response matches the expected
+// value provided. It will return nil if successful or an error
+// if not.
+func {{.Name}}Send(mthd string, param interface{}, expect string) error {
+ switch mthd {
+ {{range .Methods}}
+ case "{{.Name}}":
+ switch t := param.(type) {
+ case *{{.Param}}:
+ {{if .Ss}}
+ _=t
+ {{else if .Cs}}
+ _=t
+ {{else}}
+ client := {{.Pkg}}.New{{.Svc}}Client({{$.Name}}Client.conn)
+ res, err := client.{{.Name}}(context.Background(), t)
+ if err != nil {
+ return errors.New("Error sending method {{.Name}}")
+ }
+ // Marshal the result and compare it to the expected
+ // value.
+ if resS,err := json.Marshal(res); err == nil {
+ if string(resS) != expect {
+ return errors.New("Unexpected result on method {{.Name}}")
+ }
+ } else {
+ return errors.New("Error Marshaling the reply for method {{.Name}}")
+ }
+ default:
+ return errors.New("Invalid parameter type for method {{.Name}}")
+ {{end}}
+ }
+ {{end}}
+ default:
+ return errors.New(fmt.Sprintf("Unexpected method %s in send", mthd))
+ }
+ return nil
+}
+
diff --git a/tests/afrouter/templates/clientInit.go b/tests/afrouter/templates/clientInit.go
new file mode 100644
index 0000000..e8f4836
--- /dev/null
+++ b/tests/afrouter/templates/clientInit.go
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+var clients map[string]*clientCtl
+
+func clientInit() error {
+ clients = make(map[string]*clientCtl)
+ {{range .}}
+ if _,err := {{.Name}}Connect(); err != nil {
+ return err
+ }
+ {{end}}
+ return nil
+}
+
+
diff --git a/tests/afrouter/templates/main.go b/tests/afrouter/templates/main.go
new file mode 100644
index 0000000..5c094d9
--- /dev/null
+++ b/tests/afrouter/templates/main.go
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// The template for the tester.
+// This template is filled in by the
+// test driver based on the configuration.
+
+package main
+
+import (
+ "os"
+ "time"
+ "os/exec"
+ "strings"
+ "context"
+ "github.com/opencord/voltha-go/common/log"
+)
+
+var resFile *os.File
+
+func startSut(cmdStr string) (context.CancelFunc, error) {
+ var err error = nil
+
+ cmdAry := strings.Fields(cmdStr)
+ log.Infof("Running the affinity router: %s",cmdStr)
+ //ctx, cncl := context.WithCancel(context.Background())
+ ctx, cncl := context.WithCancel(context.Background())
+ cmd := exec.CommandContext(ctx, cmdAry[0], cmdAry[1:]...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err = cmd.Start(); err != nil {
+ log.Errorf("Failed to run the affinity router: %s %v", cmdStr,err)
+ }
+ time.Sleep(1 * time.Second) // Give the command time to get started
+ return cncl, err
+}
+
+func cleanUp(cncl context.CancelFunc) {
+ cncl()
+ // Give the child processes time to terminate
+ time.Sleep(1 * time.Second)
+}
+
+func main() {
+ var err error
+
+ // Setup logging
+ if _, err = log.SetDefaultLogger(log.JSON, 0, nil); err != nil {
+ log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
+ }
+
+ defer log.CleanUp()
+
+ // Open the results file to write the results to
+ if resFile, err = os.OpenFile(os.Args[1], os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err != nil {
+ log.Errorf("Could not append to the results file")
+ }
+
+ defer resFile.Close()
+
+ // Initialize the servers
+ if err := serverInit(); err != nil {
+ log.Errorf("Error initializing server: %v", err)
+ return
+ }
+
+ // Start the sofware under test
+ cnclFunc, err := startSut("./{{.Command}}");
+ defer cleanUp(cnclFunc)
+ if err != nil {
+ return
+ }
+
+ // Initialize the clients
+ if err := clientInit(); err != nil {
+ log.Errorf("Error initializing client: %v", err)
+ return
+ }
+
+ log.Infof("The servers are: %v",servers)
+
+ // Run all the test cases now
+ log.Infof("Executing tests")
+ resFile.Write([]byte("Executing tests\n"))
+ runTests()
+
+}
diff --git a/tests/afrouter/templates/runAll.go b/tests/afrouter/templates/runAll.go
new file mode 100644
index 0000000..b85e4c7
--- /dev/null
+++ b/tests/afrouter/templates/runAll.go
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// The template for the tester.
+// This template is filled in by the
+// test driver based on the configuration.
+
+package main
+
+import (
+ "os"
+ //"time"
+ "fmt"
+ "os/exec"
+ "io/ioutil"
+ "github.com/opencord/voltha-go/common/log"
+)
+
+func main() {
+ var cmd *exec.Cmd
+ var cmdStr string
+ // Setup logging
+ if _, err := log.SetDefaultLogger(log.JSON, 0, nil); err != nil {
+ log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
+ }
+
+ defer log.CleanUp()
+
+ log.Info("Running tests")
+ {{range .}}
+ if err:= os.Chdir(os.Args[1]); err != nil {
+ log.Error("Could not change directory to %s: %v", os.Args[1], err)
+ }
+ cmdStr = "./"+"{{.}}"[:len("{{.}}")-5]
+ log.Infof("Running test %s",cmdStr)
+ cmd = exec.Command(cmdStr, "results.txt")
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ log.Errorf("Test '%s' failed", cmdStr)
+ }
+ {{end}}
+ // Open the results file and output it.
+ if resFile, err := ioutil.ReadFile("results.txt"); err == nil {
+ fmt.Println(string(resFile))
+ } else {
+ log.Error("Could not load the results file 'results.txt'")
+ }
+ log.Info("Tests complete")
+}
diff --git a/tests/afrouter/templates/runTests.go b/tests/afrouter/templates/runTests.go
new file mode 100644
index 0000000..3b0f6d4
--- /dev/null
+++ b/tests/afrouter/templates/runTests.go
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package main
+
+import (
+ "fmt"
+ "time"
+ "errors"
+ "encoding/json"
+ "github.com/opencord/voltha-go/common/log"
+ {{range .Imports}}
+ _ "{{.}}"
+ {{end}}
+ {{range .ProtoImports}}
+ {{.Short}} "{{.Package}}"
+ {{end}}
+)
+
+func runTests() {
+ {{range $k,$v := .Tests}}
+ if err := test{{$k}}(); err == nil {
+ resFile.Write([]byte("\tTest Successful\n"))
+ } else {
+ resFile.Write([]byte("\tTest Failed\n"))
+ }
+ {{end}}
+ time.Sleep(5 * time.Second)
+}
+
+{{range $k,$v := .Tests}}
+func test{{$k}}() error {
+ var rtrn error = nil
+ // Announce the test being run
+ resFile.Write([]byte("******************** Running test case: {{$v.Name}}\n"))
+ // Acquire the client used to run the test
+ cl := clients["{{$v.Send.Client}}"]
+ // Create the server's reply data structure
+ repl := &reply{repl:&{{$v.Send.ExpectType}}{{$v.Send.Expect}}}
+ // Send the reply data structure to each of the servers
+ {{range $s := .Srvr}}
+ if servers["{{$s.Name}}"] == nil {
+ log.Error("Server '{{$s.Name}}' is nil")
+ return errors.New("GAAK")
+ }
+ servers["{{$s.Name}}"].replyData <- repl
+ {{end}}
+
+ // Now call the RPC with the data provided
+ if expct,err := json.Marshal(repl.repl); err != nil {
+ log.Errorf("Marshaling the reply for test {{$v.Name}}: %v",err)
+ } else {
+ if err := cl.send("{{$v.Send.Method}}",
+ &{{$v.Send.ParamType}}{{$v.Send.Param}},
+ string(expct)); err != nil {
+ log.Errorf("Test case {{$v.Name}} failed!: %v", err)
+
+ }
+ }
+
+ // Now read the servers' information to validate it
+ var s *serverCtl
+ var payload string
+ var i *incoming
+ if pld, err := json.Marshal(&{{$v.Send.ParamType}}{{$v.Send.Param}}); err != nil {
+ log.Errorf("Marshaling paramter for test {{$v.Name}}: %v", err)
+ } else {
+ payload = string(pld)
+ }
+ {{range $s := .Srvr}}
+ s = servers["{{$s.Name}}"]
+ i = <-s.incmg
+ if i.payload != payload {
+ log.Errorf("Mismatched payload for test {{$v.Name}}, %s:%s", i.payload, payload)
+ resFile.Write([]byte(fmt.Sprintf("Mismatched payload expected %s, got %s\n", payload, i.payload)))
+ rtrn = errors.New("Failed")
+ }
+ {{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.Write([]byte(fmt.Sprintf("Mismatched metadata on server %s expected %s, got %s\n", "{{$s.Name}}", "{{$m.Val}}", mv[0])))
+ rtrn = errors.New("Failed")
+ }
+ }
+ {{end}}
+ {{end}}
+
+ return rtrn
+}
+{{end}}
+
+
diff --git a/tests/afrouter/templates/server.go b/tests/afrouter/templates/server.go
new file mode 100644
index 0000000..8461d38
--- /dev/null
+++ b/tests/afrouter/templates/server.go
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// The template for the tester.
+// This template is filled in by the
+// test driver based on the configuration.
+
+package main
+
+import (
+ "fmt"
+ "net"
+ "errors"
+ "encoding/json"
+ "google.golang.org/grpc"
+ "golang.org/x/net/context"
+ "google.golang.org/grpc/metadata"
+ "github.com/opencord/voltha-go/common/log"
+ // Values generated by the go template
+ {{range .Imports}}
+ "{{.}}"
+ {{end}}
+ {{range .ProtoImports}}
+ {{.Short}} "{{.Package}}"
+ {{end}}
+ // End go template values
+)
+
+// The channels to get fed the expected results by the test driver.
+//var {{.Name}}Meta <-chan string
+////var {{.Name}}Payload <-chan string
+
+{{if .Ct}}{{else}}
+type reply struct {
+ repl interface{}
+}
+type incoming struct {
+ meta metadata.MD
+ payload string
+}
+type serverCtl struct {
+ replyData chan * reply
+ incmg chan * incoming
+}
+{{end}}
+
+type {{.Name}}TestServer struct {
+ control *serverCtl
+ srv *grpc.Server
+}
+
+/*
+func init() {
+ {{if .Ct}}{{else}}
+ if _, err := log.SetDefaultLogger(log.JSON, 0, nil); err != nil {
+ log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
+ }
+ {{end}}
+ {{.Name}}ListenAndServe()
+}
+*/
+
+
+func {{.Name}}ListenAndServe() error {
+ var s {{.Name}}TestServer
+
+ s.control = &serverCtl{replyData:make(chan *reply,1), incmg:make(chan *incoming,1)}
+ servers["{{.Name}}"] = s.control
+
+ log.Debugf("Starting server %s on port %d", "{{.Name}}", {{.Port}})
+ // THe test head always uses localhost 127.0.0.1
+ addr := fmt.Sprintf("127.0.0.1:%d", {{.Port}})
+
+ // Create the gRPC server
+ s.srv = grpc.NewServer()
+
+{{range .ProtoImports}}
+ // Register the handler object
+ {{.Short}}.Register{{.Service}}Server(s.srv, s)
+{{end}}
+
+ // Create the channel to listen on
+ lis, err := net.Listen("tcp", addr)
+ if err != nil {
+ log.Errorf("could not listen on %s: %s", addr, err)
+ return err
+ }
+
+ // Serve and Listen
+ go func() {
+ if err = s.srv.Serve(lis); err != nil {
+ log.Errorf("grpc serve error: %s", err)
+ return
+ }
+ }()
+
+ return err
+}
+
+{{range .Methods}}
+{{if .Ss}}
+func (ts {{$.Name}}TestServer) {{.Name}}(in *{{.Param}}, srvr {{.Pkg}}.{{.Svc}}_{{.Name}}Server) error {
+ return nil
+}
+{{else if .Cs}}
+func (ts {{$.Name}}TestServer) {{.Name}}({{.Pkg}}.{{.Svc}}_{{.Name}}Server) error {
+ return nil
+}
+{{else}}
+func (ts {{$.Name}}TestServer) {{.Name}}(ctx context.Context, in *{{.Param}}) (*{{.Rtrn}}, error) {
+ var r * incoming = &incoming{}
+ // Read the metadata
+ if md,ok := metadata.FromIncomingContext(ctx); ok == false {
+ log.Error("Getting matadata during call to {{.Name}}")
+ } else {
+ r.meta = md.Copy()
+ }
+ // Read the data sent to the function
+ if parm,err := json.Marshal(in); err != nil {
+ log.Error("Marshalling the {{.Param}} for {{.Name}}")
+ } else {
+ r.payload = string(parm)
+ }
+ // Send the server information back to the test framework
+ ts.control.incmg <- r
+ // Read the value that needs to be returned from the channel
+ d := <-ts.control.replyData
+ switch r := d.repl.(type) {
+ case *{{.Rtrn}}:
+ return r, nil
+ default:
+ return nil, errors.New("Mismatched type in call to {{.Name}}")
+ }
+ return &{{.Rtrn}}{},nil
+}
+{{end}}
+{{end}}
+
diff --git a/tests/afrouter/templates/serverInit.go b/tests/afrouter/templates/serverInit.go
new file mode 100644
index 0000000..e97e6ff
--- /dev/null
+++ b/tests/afrouter/templates/serverInit.go
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+var servers map[string]*serverCtl
+
+func serverInit() error {
+ servers = make(map[string]*serverCtl)
+ {{range .}}
+ if err := {{.Name}}ListenAndServe(); err != nil {
+ return err
+ }
+ {{end}}
+ return nil
+}
+
+
diff --git a/tests/afrouter/tester.go b/tests/afrouter/tester.go
new file mode 100644
index 0000000..e3d883c
--- /dev/null
+++ b/tests/afrouter/tester.go
@@ -0,0 +1,591 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// gRPC affinity router with active/active backends
+
+package main
+
+// Command line parameters and parsing
+import (
+ "os"
+ "fmt"
+ "flag"
+ "path"
+ //"bufio"
+ "errors"
+ "os/exec"
+ "strconv"
+ "io/ioutil"
+ "encoding/json"
+ "text/template"
+ "github.com/golang/protobuf/proto"
+ "github.com/opencord/voltha-go/common/log"
+ pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
+)
+
+type TestConfig struct {
+ configFile *string
+ logLevel *int
+ grpcLog *bool
+ Suites []string `json:"suites"`
+}
+
+
+type Connection struct {
+ Name string `json:"name"`
+ Port string `json:"port"`
+}
+
+type ProtoFile struct {
+ ImportPath string `json:"importPath"`
+ Service string `json:"service"`
+ Package string `json:"package"`
+}
+
+type ProtoSubst struct {
+ From string `json:"from"`
+ To string `json:"to"`
+}
+
+type Environment struct {
+ Command string `json:"cmdLine"`
+ ProtoFiles []ProtoFile `json:"protoFiles"`
+ ProtoDesc string `json:"protoDesc"`
+ Clients []Connection `json:"clients"`
+ Servers []Connection `json:"servers"`
+ Imports []string `json:"imports"`
+ ProtoSubsts []ProtoSubst `json:"protoSubst"`
+}
+
+type Rpc struct {
+ Client string `json:"client"`
+ Method string `json:"method"`
+ Param string `json:"param"`
+ Expect string `json:"expect"`
+}
+
+type MetaD struct {
+ Key string `json:"key"`
+ Val string `json:"value"`
+}
+
+type Server struct {
+ Name string `json:"name"`
+ Meta []MetaD `json:"meta"`
+}
+
+type Test struct {
+ Name string `json:"name"`
+ Send Rpc `json:"send"`
+ Servers []Server `json:"servers"`
+}
+
+type TestSuite struct {
+ Env Environment `json:"environment"`
+ Tests []Test `json:"tests"`
+}
+
+type ProtoImport struct {
+ Service string
+ Short string
+ Package string
+}
+
+type SendItem struct {
+ Client string
+ Method string
+ Param string
+ ParamType string
+ Expect string
+ ExpectType string
+}
+
+type TestCase struct {
+ Name string
+ Send SendItem
+ Srvr []Server
+}
+
+type TestList struct {
+ ProtoImports []ProtoImport
+ Imports []string
+ Tests []TestCase
+}
+
+type ClientConfig struct {
+ Ct int
+ Name string
+ Port string
+ Imports []string
+ Methods map[string]*mthd
+ ProtoImports []ProtoImport
+}
+
+type ServerConfig struct {
+ Ct int
+ Name string
+ Port string
+ Imports []string
+ Methods map[string]*mthd
+ ProtoImports []ProtoImport
+}
+
+type mthd struct {
+ Pkg string
+ Svc string
+ Name string
+ Param string
+ Rtrn string
+ Ss bool // Server streaming
+ Cs bool // Clieent streaming
+}
+
+
+func parseCmd() (*TestConfig, error) {
+ config := &TestConfig{}
+ cmdParse := flag.NewFlagSet(path.Base(os.Args[0]), flag.ContinueOnError);
+ config.configFile = cmdParse.String("config", "suites.json", "The configuration file for the affinity router tester")
+ config.logLevel = cmdParse.Int("logLevel", 0, "The log level for the affinity router tester")
+ config.grpcLog = cmdParse.Bool("grpclog", false, "Enable GRPC logging")
+
+ err := cmdParse.Parse(os.Args[1:]);
+ if err != nil {
+ //return err
+ return nil, errors.New("Error parsing the command line");
+ }
+ return config, nil
+}
+
+func (conf * TestConfig) loadConfig() error {
+
+ configF, err := os.Open(*conf.configFile);
+ log.Info("Loading configuration from: ", *conf.configFile)
+ if err != nil {
+ log.Error(err)
+ return err
+ }
+
+ defer configF.Close()
+
+ if configBytes, err := ioutil.ReadAll(configF); err != nil {
+ log.Error(err)
+ return err
+ } else if err := json.Unmarshal(configBytes, conf); err != nil {
+ log.Error(err)
+ return err
+ }
+
+ return nil
+}
+
+func (suite * TestSuite) loadSuite(fileN string) error {
+ suiteF, err := os.Open(fileN);
+ log.Info("Loading test suite from: ", fileN)
+ if err != nil {
+ log.Error(err)
+ return err
+ }
+
+ defer suiteF.Close()
+
+ if suiteBytes, err := ioutil.ReadAll(suiteF); err != nil {
+ log.Error(err)
+ return err
+ } else if err := json.Unmarshal(suiteBytes, suite); err != nil {
+ log.Error(err)
+ return err
+ }
+
+ return nil
+}
+
+func loadProtoMap(fileName string, pkg string, svc string, substs []ProtoSubst) (map[string]*mthd, error) {
+ var mthds map[string]*mthd = make(map[string]*mthd)
+ var rtrn_err bool
+
+ // Load the protobuf descriptor file
+ protoDescriptor := &pb.FileDescriptorSet{}
+ fb, err := ioutil.ReadFile(fileName);
+ if err != nil {
+ log.Errorf("Could not open proto file '%s'",fileName)
+ rtrn_err = true
+ }
+ err = proto.Unmarshal(fb, protoDescriptor)
+ if err != nil {
+ log.Errorf("Could not unmarshal %s, %v", "proto.pb", err)
+ rtrn_err = true
+ }
+
+ var substM map[string]string = make(map[string]string)
+ // Create a substitution map
+ log.Debugf("Creating import map")
+ for _,v := range substs {
+ log.Debugf("Mapping from %s to %s", v.From, v.To)
+ substM[v.From] = v.To
+ }
+
+
+ // Build the a map containing the method as the key
+ // and the paramter and return types as the fields
+ for _,f := range protoDescriptor.File {
+ if *f.Package == pkg {
+ for _, s:= range f.Service {
+ if *s.Name == svc {
+ log.Debugf("Loading package data '%s' for service '%s'", *f.Package, *s.Name)
+ // Now create a map keyed by method name with the value being the
+ // field number of the route selector.
+ //var ok bool
+ for _,m := range s.Method {
+ // Find the input type in the messages and extract the
+ // field number and save it for future reference.
+ log.Debugf("Processing method (%s(%s) (%s){}",*m.Name, (*m.InputType)[1:], (*m.OutputType)[1:])
+ mthds[*m.Name] = &mthd{Pkg:pkg, Svc:svc, Name:*m.Name, Param:(*m.InputType)[1:],
+ Rtrn:(*m.OutputType)[1:]}
+ if m.ClientStreaming != nil && *m.ClientStreaming == true {
+ log.Debugf("Method %s is a client streaming method", *m.Name)
+ mthds[*m.Name].Cs = true
+ }
+ if m.ServerStreaming != nil && *m.ServerStreaming == true {
+ log.Debugf("Method %s is a server streaming method", *m.Name)
+ mthds[*m.Name].Ss = true
+ }
+ // Perform the required substitutions
+ if _,ok := substM[mthds[*m.Name].Param]; ok == true {
+ mthds[*m.Name].Param = substM[mthds[*m.Name].Param]
+ }
+ if _,ok := substM[mthds[*m.Name].Rtrn]; ok == true {
+ mthds[*m.Name].Rtrn = substM[mthds[*m.Name].Rtrn]
+ }
+ }
+ }
+ }
+ }
+ }
+ if rtrn_err {
+ return nil,errors.New(fmt.Sprintf("Failed to load protobuf descriptor file '%s'",fileName))
+ }
+ return mthds, nil
+}
+
+// Server source code generation
+func generateServers(conf *TestConfig, suiteDir string, ts * TestSuite,
+ t *template.Template) error {
+ var servers []ServerConfig
+
+ for k,v := range ts.Env.Servers {
+ log.Infof("Generating the code for server[%d]: %s", k, v.Name)
+ sc := &ServerConfig{Name:v.Name, Port:v.Port, Ct:k, Imports:ts.Env.Imports}
+ for k1,v1 := range ts.Env.ProtoFiles {
+ imp := &ProtoImport{Short:"pb"+strconv.Itoa(k1),
+ Package:v1.ImportPath+v1.Package,
+ Service:v1.Service}
+ imp = &ProtoImport{Short:v1.Package,
+ Package:v1.ImportPath+v1.Package,
+ Service:v1.Service}
+ sc.ProtoImports = append(sc.ProtoImports, *imp)
+ // Compile the template from the file
+ log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
+ if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v1.Package,
+ v1.Service, ts.Env.ProtoSubsts); err != nil {
+ log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
+ ts.Env.ProtoDesc, v1.Package, v1.Service)
+ return err
+ } else {
+ //Generate all the function calls required by the
+ sc.Methods = mthds
+ }
+ }
+ log.Debugf("Server: %v", *sc)
+ // Save this server for the next steop
+ servers = append(servers, *sc)
+ // Open an output file to put the output in.
+ if f,err := os.Create(suiteDir+"/"+v.Name+".go"); err == nil {
+ defer f.Close()
+ //if err := t.ExecuteTemplate(os.Stdout, "server.go", *sc); err != nil {}
+ if err := t.ExecuteTemplate(f, "server.go", *sc); err != nil {
+ log.Errorf("Unable to execute template for server[%d]: %s: %v", k, v.Name, err)
+ return err
+ }
+ }
+ }
+ // Generate the server initialization code
+ if f,err := os.Create(suiteDir+"/serverInit.go"); err == nil {
+ defer f.Close()
+ //if err := t.ExecuteTemplate(os.Stdout, "server.go", *sc); err != nil {}
+ if err := t.ExecuteTemplate(f, "serverInit.go", servers); err != nil {
+ log.Errorf("Unable to execute template for serverInit.go: %v", err)
+ return err
+ }
+ }
+
+ return nil
+}
+
+func generateClients(conf *TestConfig, suiteDir string, ts * TestSuite,
+ t *template.Template) error {
+ var clients []ClientConfig
+ for k,v := range ts.Env.Clients {
+ log.Infof("Generating the code for client[%d]: %s", k, v.Name)
+ cc := &ClientConfig{Name:v.Name, Port:v.Port, Ct:k, Imports:ts.Env.Imports}
+ // TODO: This loop makes no sense, the only proto map that would remain
+ // after this loop is the last one loaded. Fix this to load the map
+ // for all services not just the last one.
+ for _,v1 := range ts.Env.ProtoFiles {
+ imp := &ProtoImport{Short:v1.Package,
+ Package:v1.ImportPath+v1.Package,
+ Service:v1.Service}
+ cc.ProtoImports = append(cc.ProtoImports, *imp)
+ // Compile the template from the file
+ log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
+ if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v1.Package,
+ v1.Service, ts.Env.ProtoSubsts); err != nil {
+ log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
+ ts.Env.ProtoDesc, v1.Package, v1.Service)
+ return err
+ } else {
+ //Generate all the function calls required by the
+ cc.Methods = mthds
+ }
+ }
+ clients = append(clients, *cc)
+ if f,err := os.Create(suiteDir+"/"+v.Name+".go"); err == nil {
+ _=f
+ defer f.Close()
+ if err := t.ExecuteTemplate(f, "client.go", cc); err != nil {
+ log.Errorf("Unable to execute template for client.go: %v", err)
+ return err
+ }
+ } else {
+ log.Errorf("Couldn't create file %s : %v", suiteDir+"/client.go", err)
+ return err
+ }
+ }
+ if f,err := os.Create(suiteDir+"/clientInit.go"); err == nil {
+ defer f.Close()
+ //if err := t.ExecuteTemplate(os.Stdout, "server.go", *sc); err != nil {}
+ if err := t.ExecuteTemplate(f, "clientInit.go", clients); err != nil {
+ log.Errorf("Unable to execute template for clientInit.go: %v", err)
+ return err
+ }
+ }
+ return nil
+}
+
+func serverExists(srvr string, ts * TestSuite) bool {
+ for _,v := range ts.Env.Servers {
+ if v.Name == srvr {
+ return true
+ }
+ }
+ return false
+}
+func generateTestCases(conf *TestConfig, suiteDir string, ts * TestSuite,
+ t *template.Template) error {
+ var mthdMap map[string]*mthd
+ // Generate the test cases
+ log.Info("Generating the test cases: runTests.go")
+ tc := &TestList{Imports:ts.Env.Imports}
+
+ // Load the proto descriptor file
+ // TODO: This loop makes no sense, the only proto map that would remain
+ // after this loop is the last one loaded. Fix this to load the map
+ // for all services not just the last one.
+ for _,v := range ts.Env.ProtoFiles {
+ imp := &ProtoImport{Short:v.Package,
+ Package:v.ImportPath+v.Package,
+ Service:v.Service}
+ tc.ProtoImports = append(tc.ProtoImports, *imp)
+ // Compile the template from the file
+ log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
+ if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v.Package,
+ v.Service, ts.Env.ProtoSubsts); err != nil {
+ log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
+ ts.Env.ProtoDesc, v.Package, v.Service)
+ return err
+ } else {
+ mthdMap = mthds
+ }
+ }
+ // Create the test data structure for the template
+ for _,v := range ts.Tests {
+ var test TestCase
+
+ test.Name = v.Name
+ test.Send.Client = v.Send.Client
+ test.Send.Method = v.Send.Method
+ test.Send.Param = v.Send.Param
+ test.Send.ParamType = mthdMap[test.Send.Method].Param
+ test.Send.Expect = v.Send.Expect
+ test.Send.ExpectType = mthdMap[test.Send.Method].Rtrn
+ for _,v1 := range v.Servers {
+ var srvr Server
+ if serverExists(v1.Name, ts) == false {
+ log.Errorf("Server '%s' is not defined!!", v1.Name)
+ return errors.New(fmt.Sprintf("Failed to build test case %s", v.Name))
+ }
+ srvr.Name = v1.Name
+ srvr.Meta = v1.Meta
+ test.Srvr = append(test.Srvr, srvr)
+ }
+ tc.Tests = append(tc.Tests, test)
+ }
+ if f,err := os.Create(suiteDir+"/runTests.go"); err == nil {
+ if err := t.ExecuteTemplate(f, "runTests.go", tc); err != nil {
+ log.Errorf("Unable to execute template for runTests.go: %v", err)
+ }
+ f.Close()
+ } else {
+ log.Errorf("Couldn't create file %s : %v", suiteDir+"/runTests.go", err)
+ }
+ return nil
+}
+
+func generateTestSuites(conf *TestConfig, srcDir string, outDir string) error {
+
+ // Create a directory for the tests
+ if err := os.Mkdir(srcDir, 0777); err != nil {
+ log.Errorf("Unable to create directory 'tests':%v\n", err)
+ return err
+ }
+
+ for k,v := range conf.Suites {
+ var suiteDir string = srcDir+"/"+v
+ log.Debugf("Suite[%d] - %s", k, v)
+ ts := &TestSuite{}
+ ts.loadSuite(v)
+ log.Debugf("Suite %s: %v", v, ts)
+ log.Info("Processing test suite %s", v)
+
+ t := template.Must(template.New("").ParseFiles("../templates/server.go",
+ "../templates/serverInit.go",
+ "../templates/client.go",
+ "../templates/clientInit.go",
+ "../templates/runTests.go",
+ "../templates/main.go"))
+ // Create a directory for he source code for this test suite
+ if err := os.Mkdir(suiteDir, 0777); err != nil {
+ log.Errorf("Unable to create directory '%s':%v\n", v, err)
+ return err
+ }
+ // Generate the server source files
+ if err := generateServers(conf, suiteDir, ts, t); err != nil {
+ log.Errorf("Unable to generate server source files: %v", err)
+ return err
+ }
+ // Generate the client source files
+ if err := generateClients(conf, suiteDir, ts, t); err != nil {
+ log.Errorf("Unable to generate client source files: %v", err)
+ return err
+ }
+ // Generate the test case source file
+ if err := generateTestCases(conf, suiteDir, ts, t); err != nil {
+ log.Errorf("Unable to generate test case source file: %v", err)
+ return err
+ }
+
+ // Finally generate the main file
+ log.Info("Generating main.go")
+ if f,err := os.Create(suiteDir+"/main.go"); err == nil {
+ if err := t.ExecuteTemplate(f, "main.go", ts.Env); err != nil {
+ log.Errorf("Unable to execute template for main.go: %v", err)
+ }
+ f.Close()
+ } else {
+ log.Errorf("Couldn't create file %s : %v", suiteDir+"/main.go", err)
+ }
+
+ log.Infof("Compiling test suite: %s in directory %s", v, suiteDir)
+ if err := os.Chdir(suiteDir); err != nil {
+ log.Errorf("Could not change to directory '%s':%v",suiteDir, err)
+ }
+ cmd := exec.Command("go", "build", "-o", outDir+"/"+v[:len(v)-5])
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ log.Errorf("Error running the compile command:%v", err)
+ }
+ if err := os.Chdir("../../suites"); err != nil {
+ log.Errorf("Could not change to directory '%s':%v","../../suites", err)
+ }
+
+ // Now generate the test cases
+
+ }
+ return nil
+
+}
+
+func generateTestDriver(conf *TestConfig, srcDir string, outDir string) error {
+ // Generate the main test driver file
+ if err := os.Mkdir(srcDir, 0777); err != nil {
+ log.Errorf("Unable to create directory 'driver':%v\n", err)
+ return err
+ }
+ t := template.Must(template.New("").ParseFiles("../templates/runAll.go"))
+ if f,err := os.Create(srcDir+"/runAll.go"); err == nil {
+ if err := t.ExecuteTemplate(f, "runAll.go", conf.Suites); err != nil {
+ log.Errorf("Unable to execute template for main.go: %v", err)
+ }
+ f.Close()
+ } else {
+ log.Errorf("Couldn't create file %s : %v", srcDir+"/runAll.go", err)
+ }
+
+ // Compile the test driver file
+ log.Info("Compiling the test driver")
+ if err := os.Chdir("../tests/driver"); err != nil {
+ log.Errorf("Could not change to directory 'driver':%v", err)
+ }
+ cmd := exec.Command("go", "build", "-o", outDir+"/runAll")
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ log.Errorf("Error running the compile command:%v", err)
+ }
+ if err := os.Chdir("../../suites"); err != nil {
+ log.Errorf("Could not change to directory 'driver':%v",err)
+ }
+
+ return nil
+}
+
+
+func main() {
+
+ conf,err := parseCmd()
+ if err != nil {
+ fmt.Printf("Error: %v\n", err)
+ return
+ }
+
+ // Setup logging
+ if _, err := log.SetDefaultLogger(log.JSON, *conf.logLevel, nil); err != nil {
+ log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
+ }
+
+ defer log.CleanUp()
+
+ // Parse the config file
+ if err := conf.loadConfig(); err != nil {
+ log.Error(err)
+ }
+
+ generateTestSuites(conf, "../tests", "/src/tests")
+ generateTestDriver(conf, "../tests/driver", "/src/tests")
+ return
+}
+
diff --git a/tests/afrouter/tester/main.go b/tests/afrouter/tester/main.go
new file mode 100644
index 0000000..8190fe6
--- /dev/null
+++ b/tests/afrouter/tester/main.go
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// The template for the tester.
+// This template is filled in by the
+// test driver based on the configuration.
+
+package main
+
+import (
+ "github.com/opencord/voltha-go/common/log"
+)
+
+func main() {
+ // Setup logging
+ /*
+ if _, err := log.SetDefaultLogger(log.JSON, 0, nil); err != nil {
+ log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
+ }
+ */
+
+ defer log.CleanUp()
+
+ log.Info("Template runs!")
+}