Updates to the affinity router test framework as
well as bug fixes to the affinity router found by
the test framework.
Change-Id: I90e6baa9e9ee11bd8034498b8651e9e14512e528
diff --git a/tests/afrouter/arouter_test.json b/tests/afrouter/arouter_test.json
index 0829737..d58f26d 100644
--- a/tests/afrouter/arouter_test.json
+++ b/tests/afrouter/arouter_test.json
@@ -30,9 +30,10 @@
"backend_cluster":"vcore",
"_COMMENT":"Methods are naturally southbound affinity binding unless otherwise specified below",
"methods":[ "CreateDevice",
- "GetCoreInstance",
"EnableLogicalDevicePort",
"DisableLogicalDevicePort",
+ "UpdateLogicalDeviceFlowTable",
+ "UpdateLogicalDeviceFlowGroupTable",
"EnableDevice",
"DisableDevice",
"RebootDevice",
@@ -59,6 +60,15 @@
]
},
{
+ "name":"control",
+ "type":"round_robin",
+ "association":"round_robin",
+ "backend_cluster":"vcore",
+ "methods":[
+ "UpdateLogLevel"
+ ]
+ },
+ {
"name":"read_only",
"type":"round_robin",
"association":"round_robin",
@@ -79,8 +89,17 @@
"GetDeviceGroup",
"GetLogicalDevice",
"GetAlarmFilter",
+ "ListLogicalDevices",
"ListLogicalDevicePorts",
- "GetLogicalDevicePort"
+ "GetLogicalDevicePort",
+ "GetVoltha",
+ "ListCoreInstances",
+ "ListAdapters",
+ "ListDeviceIds",
+ "ListDeviceTypes",
+ "ListDeviceGroups",
+ "ListAlarmFilters",
+ "GetCoreInstance"
]
},
{
diff --git a/tests/afrouter/suites/main.json b/tests/afrouter/suites/main.json
index a1bc09c..594bc8a 100644
--- a/tests/afrouter/suites/main.json
+++ b/tests/afrouter/suites/main.json
@@ -2,6 +2,7 @@
"__COMMENT":"Top level test driver file",
"suites": [
"test1",
- "test2"
-]
+ "test2",
+ "test3"
+ ]
}
diff --git a/tests/afrouter/suites/test1.json b/tests/afrouter/suites/test1.json
index 9e749ac..77cc0ec 100644
--- a/tests/afrouter/suites/test1.json
+++ b/tests/afrouter/suites/test1.json
@@ -1,16 +1,26 @@
{
"environment":{
- "cmdLine":"afrouter -config arouter_test.json",
+ "cmdLine":"afrouter -logLevel 1 -config arouter_test.json",
"protoFiles": [
{
"importPath":"github.com/opencord/voltha-go/protos/",
"service":"VolthaService",
"package":"voltha"
+ },
+ {
+ "importPath":"github.com/opencord/voltha-go/protos/",
+ "service":"Configuration",
+ "package":"afrouter"
+ }
+ ],
+ "Junk": [
+ {
+ "importPath":"github.com/opencord/voltha-go/protos/",
+ "service":"Configuration",
+ "package":"afrouter"
}
],
"imports": [
- "github.com/golang/protobuf/ptypes/empty",
- "github.com/opencord/voltha-go/protos/openflow_13"
],
"protoDesc":"voltha.pb",
"protoSubst": [
@@ -19,13 +29,28 @@
"to":"empty.Empty"
}
],
- "clients": [
- {
- "name":"client",
- "port":"5000"
- }
- ],
- "servers": [
+ "clients": {
+ "imports": [
+ "github.com/golang/protobuf/ptypes/empty",
+ "github.com/opencord/voltha-go/protos/openflow_13"
+ ],
+ "endpoints": [
+ {
+ "name":"client",
+ "port":"5000"
+ },
+ {
+ "name":"stats",
+ "port":"55554"
+ }
+ ]
+ },
+ "servers": {
+ "imports": [
+ "github.com/golang/protobuf/ptypes/empty",
+ "github.com/opencord/voltha-go/protos/openflow_13"
+ ],
+ "endpoints": [
{
"name":"core11",
"port":"5011"
@@ -49,8 +74,21 @@
{
"name":"core32",
"port":"5032"
+ },
+ {
+ "name":"roCore1",
+ "port":"5001"
+ },
+ {
+ "name":"roCore2",
+ "port":"5002"
+ },
+ {
+ "name":"roCore3",
+ "port":"5003"
}
- ]
+ ]
+ }
},
"tests":[
{
@@ -150,6 +188,19 @@
]
}
]
+ },
+ {
+ "name":"Get goroutine count",
+ "infoOnly":true,
+ "send": {
+ "client":"stats",
+ "method":"GetGoroutineCount",
+ "param":"{}",
+ "meta": [ ],
+ "expect":"{Count:39}",
+ "expectMeta": [ ]
+ },
+ "servers": [ ]
}
]
}
diff --git a/tests/afrouter/suites/test2.go b/tests/afrouter/suites/test2.go
index fc0332e..80e3af3 100644
--- a/tests/afrouter/suites/test2.go
+++ b/tests/afrouter/suites/test2.go
@@ -33,28 +33,37 @@
//pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
)
+type suite struct {
+ CrTests []test
+ GetTests[]test
+}
+
type test struct {
Core int
+ SerNo int
}
+const SUITE_LEN = 55000
+//const SUITE_LEN = 100
+
func main() {
- var ary []test
+ var ary suite
// Setup logging
if _, err := log.SetDefaultLogger(log.JSON, 0, nil); err != nil {
log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
}
- for i :=0; i<10000; i++ {
-
- ary = append(ary,test{Core:(i%3)+1})
+ for i :=0; i<SUITE_LEN; i++ {
+
+ ary.CrTests = append(ary.CrTests,test{Core:(i%3)+1, SerNo:i})
+ ary.GetTests = append(ary.GetTests,test{Core:(i%3)+1, SerNo:i+SUITE_LEN})
}
// Load the template to execute
t := template.Must(template.New("").ParseFiles("./test2.tmpl.json"))
if f,err := os.Create("test2.json"); err == nil {
- _=f
defer f.Close()
if err := t.ExecuteTemplate(f, "test2.tmpl.json", ary); err != nil {
log.Errorf("Unable to execute template for test2.tmpl.json: %v", err)
diff --git a/tests/afrouter/suites/test2.tmpl.json b/tests/afrouter/suites/test2.tmpl.json
index fc8ef1c..c8d040e 100644
--- a/tests/afrouter/suites/test2.tmpl.json
+++ b/tests/afrouter/suites/test2.tmpl.json
@@ -1,16 +1,20 @@
{
"environment":{
- "cmdLine":"afrouter -config arouter_test.json -grpclog",
+ "cmdLine":"afrouter -config arouter_test.json -logLevel 1",
"protoFiles": [
{
"importPath":"github.com/opencord/voltha-go/protos/",
"service":"VolthaService",
"package":"voltha"
+ },
+ {
+ "importPath":"github.com/opencord/voltha-go/protos/",
+ "service":"Configuration",
+ "package":"afrouter"
}
],
"imports": [
- "github.com/golang/protobuf/ptypes/empty",
- "github.com/opencord/voltha-go/protos/openflow_13"
+ "github.com/golang/protobuf/ptypes/empty"
],
"protoDesc":"voltha.pb",
"protoSubst": [
@@ -19,13 +23,28 @@
"to":"empty.Empty"
}
],
- "clients": [
- {
- "name":"client",
- "port":"5000"
- }
- ],
- "servers": [
+ "clients": {
+ "imports": [
+ "github.com/golang/protobuf/ptypes/empty",
+ "github.com/opencord/voltha-go/protos/openflow_13"
+ ],
+ "endpoints": [
+ {
+ "name":"client",
+ "port":"5000"
+ },
+ {
+ "name":"stats",
+ "port":"55554"
+ }
+ ]
+ },
+ "servers": {
+ "imports": [
+ "github.com/golang/protobuf/ptypes/empty",
+ "github.com/opencord/voltha-go/protos/openflow_13"
+ ],
+ "endpoints": [
{
"name":"core11",
"port":"5011"
@@ -50,10 +69,11 @@
"name":"core32",
"port":"5032"
}
- ]
+ ]
+ }
},
"tests":[
- {{range $k,$v := .}}
+ {{range $k,$v := .CrTests}}
{
"name":"Test CreateDevice",
"send": {
@@ -69,7 +89,7 @@
"meta": [
{
"key":"voltha_serial_number",
- "value":"{{$k}}"
+ "value":"{{$v.SerNo}}"
}
]
},
@@ -78,7 +98,41 @@
"meta": [
{
"key":"voltha_serial_number",
- "value":"{{$k}}"
+ "value":"{{$v.SerNo}}"
+ }
+ ]
+ }
+ ]
+ },
+ {{end}}
+ {{range $k,$v := .GetTests}}
+ {
+ "name":"Test EnableDevice",
+ "send": {
+ "client":"client",
+ "_method":"GetDevice",
+ "method":"EnableDevice",
+ "param":"{Id:\"abcd1234{{$k}}\"}",
+ "expect":"{}",
+ "_expect":"{Id:\"abcd1234{{$k}}\",Type:\"simulated_olt\"}",
+ "_meta":""
+ },
+ "servers": [
+ {
+ "name":"core{{$v.Core}}1",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"{{$v.SerNo}}"
+ }
+ ]
+ },
+ {
+ "name":"core{{$v.Core}}2",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"{{$v.SerNo}}"
}
]
}
@@ -100,7 +154,7 @@
"meta": [
{
"key":"voltha_serial_number",
- "value":"10000"
+ "value":"110000"
}
]
},
@@ -109,7 +163,7 @@
"meta": [
{
"key":"voltha_serial_number",
- "value":"10000"
+ "value":"110000"
}
]
}
@@ -141,7 +195,7 @@
"meta": [
{
"key":"voltha_serial_number",
- "value":"10001"
+ "value":"110001"
},
{
"key":"voltha_backend_name",
@@ -150,6 +204,19 @@
]
}
]
+ },
+ {
+ "_COMMENT":"If this test case fails, there could be a goroutine leak",
+ "name":"Get goroutine count",
+ "send": {
+ "client":"stats",
+ "method":"GetGoroutineCount",
+ "param":"{}",
+ "meta": [ ],
+ "expect":"{Count:39}",
+ "expectMeta": [ ]
+ },
+ "servers": [ ]
}
]
}
diff --git a/tests/afrouter/suites/test3.go b/tests/afrouter/suites/test3.go
new file mode 100644
index 0000000..88b4382
--- /dev/null
+++ b/tests/afrouter/suites/test3.go
@@ -0,0 +1,418 @@
+/*
+ * 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 (
+ "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"
+)
+
+// This test suite validates that the different method types get routed
+// properly. That is, rw methods to the rw cores and ro methods to the
+// ro cores
+type test struct {
+ Name string
+ Core int
+ SerNo int
+ Method string
+ Param string
+ Expect string
+}
+
+type tests struct {
+ RoTests []test
+ RwTests []test
+ CtlTests []test
+}
+
+var roMethods []test = []test{
+ {
+ Name: "Test GetVoltha",
+ Method: "GetVoltha", // rpc GetVoltha(google.protobuf.Empty) returns(Voltha)
+ Param: "{}",
+ Expect:`{Version:\"abcdef\"}`,
+ },
+ /*
+ {
+ Name: "Test ListCoreInstances",
+ Method: "ListCoreInstances", // rpc ListCoreInstances(google.protobuf.Empty) returns(CoreInstances)
+ Param: "{}",
+ Expect: `{Items:[]*voltha.CoreInstance{&voltha.CoreInstance{InstanceId:\"ABC\",Health:&voltha.HealthStatus{State:0}}}}`,
+ },
+ {
+ Name: "Test GetCoreInstance",
+ Method: "GetCoreInstance", // rpc GetCoreInstance(ID) returns(CoreInstance)
+ Param: `{Id:\"arou8390\"}`,
+ Expect: `{InstanceId:\"arou8390\", Health:&voltha.HealthStatus{State:0}}`,
+ },*/
+ {
+ Name: "Test ListAdapters",
+ Method: "ListAdapters", // rpc ListAdapters(google.protobuf.Empty) returns(Adapters)
+ Param: "{}",
+ Expect: `{Items:[]*voltha.Adapter{&voltha.Adapter{Id:\"ABC\", Vendor:\"afrouterTest\", Version:\"Version 1.0\"}}}`,
+ },
+ {
+ Name: "Test ListLogicalDevices",
+ Method: "ListLogicalDevices", // rpc ListLogicalDevices(google.protobuf.Empty) returns(LogicalDevices)
+ Param: "{}",
+ Expect:`{Items:[]*voltha.LogicalDevice{&voltha.LogicalDevice{Id:\"LDevId\", DatapathId:64, RootDeviceId:\"Root\"}}}`,
+ },
+ {
+ Name: "Test GetLogicalDevice",
+ Method: "GetLogicalDevice", // rpc GetLogicalDevice(ID) returns(LogicalDevice)
+ Param: `{Id:\"ABC123XYZ\"}`,
+ Expect:`{Id:\"ABC123XYZ\", DatapathId:64, RootDeviceId:\"Root-of:ABC123XYZ\"}`,
+ },
+ {
+ Name: "Test ListLogicalDevicePorts",
+ Method: "ListLogicalDevicePorts", // rpc ListLogicalDevicePorts(ID) returns(LogicalPorts)
+ Param: `{Id:\"ABC123XYZ\"}`,
+ Expect:`{Items:[]*voltha.LogicalPort{&voltha.LogicalPort{Id:\"PortId-1\", DeviceId:\"ABC12XYZ\", DevicePortNo:64, RootPort:true},&voltha.LogicalPort{Id:\"PortId-2\", DeviceId:\"ABC12XYZ\", DevicePortNo:64, RootPort:false}}}`,
+ },
+ {
+ Name: "Test GetLogicalDevicePort",
+ Method: "GetLogicalDevicePort", // rpc GetLogicalDevicePort(LogicalPortId) returns(LogicalPort)
+ Param: `{Id:\"PortId-1\"}`,
+ Expect:`{Id:\"PortId-1\", DeviceId:\"ABC12XYZ\", DevicePortNo:64, RootPort:true}`,
+ },
+ {
+ Name: "Test ListLogicalDeviceFlows",
+ Method: "ListLogicalDeviceFlows", // rpc ListLogicalDeviceFlows(ID) returns(openflow_13.Flows)
+ Param: `{Id:\"Flow-ABC123XYZ\"}`,
+ Expect:`{Items:[]*openflow_13.OfpFlowStats{&openflow_13.OfpFlowStats{Id:1, TableId:2, DurationSec:12, Priority:0},&openflow_13.OfpFlowStats{Id:1, TableId:2, DurationSec:12, Priority:0}}}`,
+ },
+ {
+ Name: "Test ListLogicalDeviceFlowGroups",
+ Method: "ListLogicalDeviceFlowGroups", // rpc ListLogicalDeviceFlowGroups(ID) returns(openflow_13.FlowGroups)
+ Param: `{Id:\"Flow-ABC123XYZ\"}`,
+ Expect:`{Items:[]*openflow_13.OfpGroupEntry{&openflow_13.OfpGroupEntry{Desc:&openflow_13.OfpGroupDesc{GroupId:12}, Stats:&openflow_13.OfpGroupStats{GroupId:1, RefCount:1, PacketCount:3}}}}`,
+ },
+ {
+ Name: "Test ListDevices",
+ Method: "ListDevices", // rpc ListDevices(google.protobuf.Empty) returns(Devices)
+ Param: `{}`,
+ Expect:`{Items:[]*voltha.Device{&voltha.Device{Id:\"ABC123XYZ\", Type:\"SomeDeviceType\", Root:false, ParentId:\"ZYX321CBA\"},&voltha.Device{Id:\"ZYX321CBA\", Type:\"SomeDeviceType\", Root:true, ParentId:\"ROOT\"}}}`,
+ },
+ {
+ Name: "Test ListDeviceIds",
+ Method: "ListDeviceIds", // rpc ListDeviceIds(google.protobuf.Empty) returns(IDs)
+ Param: `{}`,
+ Expect:`{Items:[]*voltha.ID{&voltha.ID{Id:\"ABC123XYZ\"},&voltha.ID{Id:\"ZYX321CBA\"}}}`,
+ },
+ {
+ Name: "Test GetDevice",
+ Method: "GetDevice", // rpc GetDevice(ID) returns(Device)
+ Param: `{Id:\"ZYX321CBA\"}`,
+ Expect:`{Id:\"ABC123XYZ\", Type:\"SomeDeviceType\", Root:false, ParentId:\"ZYX321CBA\"}`,
+ },
+ {
+ Name: "Test GetImageDownloadStatus",
+ Method: "GetImageDownloadStatus", // rpc GetImageDownloadStatus(ImageDownload) returns(ImageDownload)
+ Param: `{Id:\"Image-ZYX321CBA\"}`,
+ Expect:`{Id:\"Image-ABC123XYZ\", Name:\"SomeName\", Url:\"Some URL\", Crc:123456}`,
+ },
+ {
+ Name: "Test GetImageDownload",
+ Method: "GetImageDownload", // rpc GetImageDownload(ImageDownload) returns(ImageDownload)
+ Param: `{Id:\"Image-ZYX321CBA\"}`,
+ Expect:`{Id:\"Image-ABC123XYZ\", Name:\"SomeName\", Url:\"Some URL\", Crc:123456}`,
+ },
+ {
+ Name: "Test ListImageDownloads",
+ Method: "ListImageDownloads", // rpc ListImageDownloads(ID) returns(ImageDownloads)
+ Param: `{Id:\"ZYX321CBA\"}`,
+ Expect:`{Items:[]*voltha.ImageDownload{&voltha.ImageDownload{Id:\"Image1-ABC123XYZ\", Name:\"SomeName\", Url:\"Some URL\", Crc:123456}, &voltha.ImageDownload{Id:\"Image2-ABC123XYZ\", Name:\"SomeName\", Url:\"Some other URL\", Crc:654321}}}`,
+ },
+ {
+ Name: "Test ListDevicePorts",
+ Method: "ListDevicePorts", // rpc ListDevicePorts(ID) returns(Ports)
+ Param: `{}`,
+ Expect:`{Items:[]*voltha.Port{&voltha.Port{PortNo:100000, Label:\"Port one hundred thousand\", DeviceId:\"ZYX321CBA\"},&voltha.Port{PortNo:100001, Label:\"Port one hundred thousand and one\", DeviceId:\"ZYX321CBA\"}}}`,
+ },
+ {
+ Name: "Test ListDevicePmConfigs",
+ Method: "ListDevicePmConfigs", // rpc ListDevicePmConfigs(ID) returns(PmConfigs)
+ Param: `{}`,
+ Expect:`{Id:\"ABC123XYZ\", DefaultFreq: 10000, Grouped:false, FreqOverride:false, Metrics:[]*voltha.PmConfig{&voltha.PmConfig{Name:\"Speed\", Enabled: true, SampleFreq:10000}, &voltha.PmConfig{Name:\"Errors\", Enabled: true, SampleFreq:10000}}}`,
+ },
+ {
+ Name: "Test ListDeviceFlows",
+ Method: "ListDeviceFlows", // rpc ListDeviceFlows(ID) returns(openflow_13.Flows)
+ Param: `{Id:\"Flow-ABC123XYZ\"}`,
+ Expect:`{Items:[]*openflow_13.OfpFlowStats{&openflow_13.OfpFlowStats{Id:1, TableId:2, DurationSec:12, Priority:0},&openflow_13.OfpFlowStats{Id:1, TableId:2, DurationSec:12, Priority:0}}}`,
+ },
+ {
+ Name: "Test ListDeviceFlowGroups",
+ Method: "ListDeviceFlowGroups", // rpc ListDeviceFlowGroups(ID) returns(openflow_13.FlowGroups)
+ Param: `{Id:\"Flow-ABC123XYZ\"}`,
+ Expect:`{Items:[]*openflow_13.OfpGroupEntry{&openflow_13.OfpGroupEntry{Desc:&openflow_13.OfpGroupDesc{GroupId:12}, Stats:&openflow_13.OfpGroupStats{GroupId:1, RefCount:1, PacketCount:3}}}}`,
+ },
+ {
+ Name: "Test ListDeviceTypes",
+ Method: "ListDeviceTypes", // rpc ListDeviceTypes(google.protobuf.Empty) returns(DeviceTypes)
+ Param: `{}`,
+ Expect:`{Items:[]*voltha.DeviceType{&voltha.DeviceType{Id:\"ABC123XYZ\", VendorId:\"Ciena\", Adapter:\"SAOS\"},&voltha.DeviceType{Id:\"ZYX321CBA\", VendorId:\"Ciena\", Adapter:\"SAOS-Tibit\"}}}`,
+ },
+ {
+ Name: "Test GetDeviceType",
+ Method: "GetDeviceType", // rpc GetDeviceType(ID) returns(DeviceType)
+ Param: `{Id:\"ZYX321CBA\"}`,
+ Expect:`{Id:\"ZYX321CBA\", VendorId:\"Ciena\", Adapter:\"SAOS-Tibit\"}`,
+ },
+ {
+ Name: "Test ListDeviceGroups",
+ Method: "ListDeviceGroups", // rpc ListDeviceGroups(google.protobuf.Empty) returns(DeviceGroups)
+ Param: `{}`,
+ Expect:`{Items:[]*voltha.DeviceGroup{&voltha.DeviceGroup{Id:\"group-ABC123XYZ\", LogicalDevices: []*voltha.LogicalDevice{&voltha.LogicalDevice{Id:\"LDevId\", DatapathId:64, RootDeviceId:\"Root\"}}, Devices: []*voltha.Device{&voltha.Device{Id:\"ABC123XYZ\", Type:\"SomeDeviceType\", Root:false, ParentId:\"ZYX321CBA\"},&voltha.Device{Id:\"ZYX321CBA\", Type:\"SomeDeviceType\", Root:true, ParentId:\"ROOT\"}}}}}`,
+ },
+ {
+ Name: "Test GetDeviceGroup",
+ Method: "GetDeviceGroup", // rpc GetDeviceGroup(ID) returns(DeviceGroup)
+ Param: `{Id:\"ZYX321CBA\"}`,
+ Expect:`{Id:\"group-ABC123XYZ\", LogicalDevices: []*voltha.LogicalDevice{&voltha.LogicalDevice{Id:\"LDevId\", DatapathId:64, RootDeviceId:\"Root\"}}, Devices: []*voltha.Device{&voltha.Device{Id:\"ABC123XYZ\", Type:\"SomeDeviceType\", Root:false, ParentId:\"ZYX321CBA\"},&voltha.Device{Id:\"ZYX321CBA\", Type:\"SomeDeviceType\", Root:true, ParentId:\"ROOT\"}}}`,
+ },
+ {
+ Name: "Test ListAlarmFilters",
+ Method: "ListAlarmFilters", // rpc ListAlarmFilters(google.protobuf.Empty) returns(AlarmFilters)
+ Param: `{}`,
+ Expect:`{Filters:[]*voltha.AlarmFilter{&voltha.AlarmFilter{Id:\"ABC123XYZ\", Rules: []*voltha.AlarmFilterRule{&voltha.AlarmFilterRule{Value:\"Rule Value\"}}}}}`,
+ },
+ {
+ Name: "Test GetAlarmFilter",
+ Method: "GetAlarmFilter", // rpc GetAlarmFilter(ID) returns(AlarmFilter)
+ Param: `{Id:\"ABC123XYZ\"}`,
+ Expect:`{Id:\"ABC123XYZ\", Rules: []*voltha.AlarmFilterRule{&voltha.AlarmFilterRule{Value:\"Rule Value\"}}}`,
+ },
+ {
+ Name: "Test GetImages",
+ Method: "GetImages", // rpc GetImages(ID) returns(Images)
+ Param: `{Id:\"ZYX321CBA\"}`,
+ Expect:`{Image: []*voltha.Image{&voltha.Image{Name:\"Image1\", Version: \"0.1\", Hash:\"123@\", InstallDatetime:\"Yesterday\", IsActive:true, IsCommitted:true, IsValid:false},&voltha.Image{Name:\"Image2\", Version: \"0.2\", Hash:\"ABC@\", InstallDatetime:\"Today\", IsActive:false, IsCommitted:false, IsValid:false}}}`,
+ },
+}
+
+var ctlMethods []test = []test {
+ {
+ Name: "Test UpdateLogLevel",
+ Method: "UpdateLogLevel", // rpc UpdateLogLevel(Logging) returns(google.protobuf.Empty)
+ Param: `{Level: 0, PackageName:\"abc123\"}`,
+ Expect: "{}",
+ },
+}
+
+var rwMethods []test = []test{
+ {
+ Name: "Test EnableLogicalDevicePort",
+ Method: "EnableLogicalDevicePort", // rpc EnableLogicalDevicePort(LogicalPortId) returns(google.protobuf.Empty)
+ Param: `{Id:\"ZYX321CBA\", PortId:\"100,000\"}`,
+ Expect:`{}`,
+ },
+ {
+ Name: "Test DisableLogicalDevicePort",
+ Method: "DisableLogicalDevicePort", // rpc DisableLogicalDevicePort(LogicalPortId) returns(google.protobuf.Empty)
+ Param: `{Id:\"ABC123XYZ\", PortId:\"100,000\"}`,
+ Expect:`{}`,
+ },
+ {
+ Name: "Test UpdateLogicalDeviceFlowTable",
+ Method: "UpdateLogicalDeviceFlowTable", // rpc UpdateLogicalDeviceFlowTable(openflow_13.FlowTableUpdate)
+ Param: `{Id:\"XYZ123ABC\", FlowMod:&openflow_13.OfpFlowMod{Cookie:10, CookieMask:255, TableId:10000, Command:openflow_13.OfpFlowModCommand_OFPFC_ADD}}`,
+ Expect:`{}`,
+ },
+ {
+ Name: "Test UpdateLogicalDeviceFlowGroupTable",
+ Method: "UpdateLogicalDeviceFlowGroupTable", // rpc UpdateLogicalDeviceFlowGroupTable(openflow_13.FlowGroupTableUpdate)
+ Param: `{Id:\"ZYX123ABC\", GroupMod:&openflow_13.OfpGroupMod{Command:openflow_13.OfpGroupModCommand_OFPGC_ADD, Type:openflow_13.OfpGroupType_OFPGT_INDIRECT, GroupId:255}}`,
+ Expect:`{}`,
+ },
+ //"ReconcileDevices", // rpc ReconcileDevices(IDs) returns(google.protobuf.Empty)
+ {
+ Name: "Test CreateDevice",
+ Method: "CreateDevice", // rpc CreateDevice(Device) returns(Device)
+ Param: `{Type:\"simulated_OLT\"}`,
+ Expect:`{Id:\"ZYX321ABC\", Type:\"\"}`,
+ },
+ {
+ Name: "Test EnableDevice",
+ Method: "EnableDevice", // rpc EnableDevice(ID) returns(google.protobuf.Empty)
+ Param: `{Id:\"XYZ321ABC\"}`,
+ Expect:`{}`,
+ },
+ {
+ Name: "Test DisableDevice",
+ Method: "DisableDevice", // rpc DisableDevice(ID) returns(google.protobuf.Empty)
+ Param: `{Id:\"XYZ123CBA\"}`,
+ Expect:`{}`,
+ },
+ {
+ Name: "Test RebootDevice",
+ Method: "RebootDevice", // rpc RebootDevice(ID) returns(google.protobuf.Empty)
+ Param: `{Id:\"ZYX123CBA\"}`,
+ Expect:`{}`,
+ },
+ {
+ Name: "Test DeleteDevice",
+ Method: "DeleteDevice", // rpc DeleteDevice(ID) returns(google.protobuf.Empty)
+ Param: `{Id:\"CBA123XYZ\"}`,
+ Expect:`{}`,
+ },
+ {
+ Name: "Test DownloadImage",
+ Method: "DownloadImage", // rpc DownloadImage(ImageDownload) returns(OperationResp)
+ Param: `{Id:\"CBA321XYZ\", Name:\"Image-1\", Crc: 54321}`,
+ Expect:`{Code:voltha.OperationResp_OPERATION_SUCCESS, AdditionalInfo:\"It worked!\"}`,
+ },
+ {
+ Name: "Test CancelImageDownload",
+ Method: "CancelImageDownload", // rpc CancelImageDownload(ImageDownload) returns(OperationResp)
+ Param: `{Id:\"CBA321ZYX\", Name:\"Image-1\", Crc: 54321}`,
+ Expect:`{Code:voltha.OperationResp_OPERATION_SUCCESS, AdditionalInfo:\"It worked!\"}`,
+ },
+ {
+ Name: "Test ActivateImageUpdate",
+ Method: "ActivateImageUpdate", // rpc ActivateImageUpdate(ImageDownload) returns(OperationResp)
+ Param: `{Id:\"ABC321ZYX\", Name:\"Image-1\", Crc: 54321}`,
+ Expect:`{Code:voltha.OperationResp_OPERATION_FAILURE, AdditionalInfo:\"It bombed!\"}`,
+ },
+ {
+ Name: "Test RevertImageUpdate",
+ Method: "RevertImageUpdate", // rpc RevertImageUpdate(ImageDownload) returns(OperationResp)
+ Param: `{Id:\"ABC123ZYX\", Name:\"Image-1\", Crc: 54321}`,
+ Expect:`{Code:voltha.OperationResp_OPERATION_FAILURE, AdditionalInfo:\"It bombed!\"}`,
+ },
+ {
+ Name: "Test UpdateDevicePmConfigs",
+ Method: "UpdateDevicePmConfigs", // rpc UpdateDevicePmConfigs(voltha.PmConfigs) returns(google.protobuf.Empty)
+ Param: `{Id:\"CBA123ZYX\", DefaultFreq:1000000, Grouped: false, FreqOverride: false}`,
+ Expect:`{}`,
+ },
+ {
+ Name: "Test CreateAlarmFilter",
+ Method: "CreateAlarmFilter", // rpc CreateAlarmFilter(AlarmFilter) returns(AlarmFilter)
+ Param: `{Id:\"abc123xyz\", Rules:[]*voltha.AlarmFilterRule{&voltha.AlarmFilterRule{Key:voltha.AlarmFilterRuleKey_type, Value:\"Type man, it's the type!\"}, &voltha.AlarmFilterRule{Key:voltha.AlarmFilterRuleKey_category, Value:\"Category yeah!\"}}}`,
+ Expect:`{Id:\"abc123xyz\", Rules: []*voltha.AlarmFilterRule{&voltha.AlarmFilterRule{Key:voltha.AlarmFilterRuleKey_type, Value:\"Type man, it's the type!\"}, &voltha.AlarmFilterRule{Key:voltha.AlarmFilterRuleKey_category, Value:\"Category yeah!\"}}}`,
+ },
+ {
+ Name: "Test UpdateAlarmFilter",
+ Method: "UpdateAlarmFilter", // rpc UpdateAlarmFilter(AlarmFilter) returns(AlarmFilter)
+ Param: `{Id:\"ABC321XYZ\", Rules:[]*voltha.AlarmFilterRule{&voltha.AlarmFilterRule{Key:voltha.AlarmFilterRuleKey_type, Value:\"Type man, it's the type!\"}, &voltha.AlarmFilterRule{Key:voltha.AlarmFilterRuleKey_category, Value:\"Category yeah!\"}}}`,
+ Expect:`{Id:\"ABC321XYZ\", Rules: []*voltha.AlarmFilterRule{&voltha.AlarmFilterRule{Key:voltha.AlarmFilterRuleKey_type, Value:\"Type man, it's the type!\"}, &voltha.AlarmFilterRule{Key:voltha.AlarmFilterRuleKey_category, Value:\"Category yeah!\"}}}`,
+ },
+ {
+ Name: "Test DeleteAlarmFilter",
+ Method: "DeleteAlarmFilter", // rpc DeleteAlarmFilter(ID) returns(google.protobuf.Empty)
+ Param: `{Id:\"acb123xyz\"}`,
+ Expect:`{}`,
+ },
+ {
+ Name: "Test SelfTest",
+ Method: "SelfTest", // rpc SelfTest(ID) returns(SelfTestResponse)
+ Param: `{Id:\"bac123xyz\"}`,
+ Expect:`{Result:voltha.SelfTestResponse_UNKNOWN_ERROR}`,
+ },
+} /*
+// "StreamPacketsOut", // rpc StreamPacketsOut(stream openflow_13.PacketOut)
+// "ReceivePacketsIn", // rpc ReceivePacketsIn(google.protobuf.Empty)
+// "ReceiveChangeEvents", // rpc ReceiveChangeEvents(google.protobuf.Empty)
+ "Subscribe ", // rpc Subscribe (OfAgentSubscriber) returns (OfAgentSubscriber)
+
+}
+*/
+
+func dumpFile(fileNm string) error {
+ // Dump the generated file for debugging purposes
+ if c,err := ioutil.ReadFile(fileNm); err == nil {
+ fmt.Print(string(c))
+ } else {
+ e := errors.New(fmt.Sprintf("Could not read the file '%s', %v", fileNm, err))
+ return e
+ }
+ return nil
+}
+
+func main() {
+
+ //var rwAry []test
+ //var roAry []test
+ var serNo int = 0
+
+ // Setup logging
+ if _, err := log.SetDefaultLogger(log.JSON, 0, nil); err != nil {
+ log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
+ }
+
+ core := 0
+ for k,_ := range roMethods {
+ roMethods[k].SerNo = serNo
+ roMethods[k].Core = (core%3) + 1
+ log.Infof("Processing method %s, serNo: %d", roMethods[k].Method, roMethods[k].SerNo)
+ serNo++
+ core++
+ }
+
+ // New round robin starts here because of the different route
+ core = 0
+ for k,_ := range rwMethods {
+ rwMethods[k].SerNo = serNo
+ rwMethods[k].Core = (core%3) + 1
+ log.Infof("Processing method %s, serNo: %d", rwMethods[k].Method, rwMethods[k].SerNo)
+ serNo++
+ core++
+ }
+
+ // New round robin starts here because of the different route
+ core = 0
+ for k,_ := range ctlMethods {
+ ctlMethods[k].SerNo = serNo
+ ctlMethods[k].Core = (core%3) + 1
+ log.Infof("Processing method %s, serNo: %d", ctlMethods[k].Method, ctlMethods[k].SerNo)
+ serNo++
+ core++
+ }
+
+ tsts := tests{RoTests: roMethods, RwTests: rwMethods, CtlTests: ctlMethods}
+
+ t := template.Must(template.New("").ParseFiles("./test3.tmpl.json"))
+ if f,err := os.Create("test3.json"); err == nil {
+ _=f
+ defer f.Close()
+ if err := t.ExecuteTemplate(f, "test3.tmpl.json", tsts); err != nil {
+ log.Errorf("Unable to execute template for test3.tmpl.json: %v", err)
+ }
+ } else {
+ log.Errorf("Couldn't create file test3.json: %v", err)
+ }
+
+ // Dump the generated file for debugging purposes
+ //if err := dumpFile("test3.json"); err != nil {
+ // log.Error(err)
+ //}
+
+
+}
diff --git a/tests/afrouter/suites/test3.json b/tests/afrouter/suites/test3.json
deleted file mode 100644
index 2d3d7ad..0000000
--- a/tests/afrouter/suites/test3.json
+++ /dev/null
@@ -1,87 +0,0 @@
-{
- "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.tmpl.json b/tests/afrouter/suites/test3.tmpl.json
new file mode 100644
index 0000000..b3118ff
--- /dev/null
+++ b/tests/afrouter/suites/test3.tmpl.json
@@ -0,0 +1,235 @@
+{
+ "environment":{
+ "cmdLine":"afrouter -config arouter_test.json -logLevel 1",
+ "protoFiles": [
+ {
+ "importPath":"github.com/opencord/voltha-go/protos/",
+ "service":"VolthaService",
+ "package":"voltha"
+ },
+ {
+ "importPath":"github.com/opencord/voltha-go/protos/",
+ "service":"Configuration",
+ "package":"afrouter"
+ }
+ ],
+ "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": {
+ "imports": [
+ "github.com/golang/protobuf/ptypes/empty",
+ "github.com/opencord/voltha-go/protos/openflow_13"
+ ],
+ "endpoints": [
+ {
+ "name":"client",
+ "port":"5000"
+ },
+ {
+ "name":"stats",
+ "port":"55554"
+ }
+ ]
+ },
+ "servers": {
+ "imports": [
+ "github.com/golang/protobuf/ptypes/empty",
+ "github.com/opencord/voltha-go/protos/openflow_13"
+ ],
+ "endpoints": [
+ {
+ "name":"core11",
+ "port":"5011"
+ },
+ {
+ "name":"core12",
+ "port":"5012"
+ },
+ {
+ "name":"core21",
+ "port":"5021"
+ },
+ {
+ "name":"core22",
+ "port":"5022"
+ },
+ {
+ "name":"core31",
+ "port":"5031"
+ },
+ {
+ "name":"core32",
+ "port":"5032"
+ },
+ {
+ "name":"roCore1",
+ "port":"5001"
+ },
+ {
+ "name":"roCore2",
+ "port":"5002"
+ },
+ {
+ "name":"roCore3",
+ "port":"5003"
+ }
+ ]
+ }
+ },
+ "tests":[
+ {{range $k,$v := .RoTests}}
+ {
+ "name":"{{$v.Name}}",
+ "send": {
+ "client":"client",
+ "method":"{{$v.Method}}",
+ "param":"{{$v.Param}}",
+ "expect":"{{$v.Expect}}",
+ "_meta":""
+ },
+ "servers": [
+ {
+ "name":"core{{$v.Core}}1",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"{{$v.SerNo}}"
+ }
+ ]
+ },
+ {
+ "name":"core{{$v.Core}}2",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"{{$v.SerNo}}"
+ }
+ ]
+ }
+ ]
+ },
+ {{end}}
+ {{range $k,$v := .RwTests}}
+ {
+ "name":"{{$v.Name}}",
+ "send": {
+ "client":"client",
+ "method":"{{$v.Method}}",
+ "param":"{{$v.Param}}",
+ "expect":"{{$v.Expect}}",
+ "_meta":""
+ },
+ "servers": [
+ {
+ "name":"core{{$v.Core}}1",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"{{$v.SerNo}}"
+ }
+ ]
+ },
+ {
+ "name":"core{{$v.Core}}2",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"{{$v.SerNo}}"
+ }
+ ]
+ }
+ ]
+ },
+ {{end}}
+ {{range $k,$v := .CtlTests}}
+ {
+ "name":"{{$v.Name}}",
+ "send": {
+ "client":"client",
+ "method":"{{$v.Method}}",
+ "param":"{{$v.Param}}",
+ "expect":"{{$v.Expect}}",
+ "_meta":""
+ },
+ "servers": [
+ {
+ "name":"core{{$v.Core}}1",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"{{$v.SerNo}}"
+ }
+ ]
+ },
+ {
+ "name":"core{{$v.Core}}2",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"{{$v.SerNo}}"
+ }
+ ]
+ }
+ ]
+ },
+ {{end}}
+ {
+ "name":"Test Subscribe",
+ "send": {
+ "client":"client",
+ "method":"Subscribe",
+ "param":"{OfagentId:\"Agent007\"}",
+ "meta": [
+ {
+ "key":"voltha_backend_name",
+ "value":""
+ }
+ ],
+ "expect":"{OfagentId:\"Agent007\",VolthaId:\"core11\"}",
+ "expectMeta": [
+ {
+ "key":"voltha_backend_name",
+ "value":"vcore1"
+ }
+ ]
+ },
+ "servers": [
+ {
+ "name":"core11",
+ "meta": [
+ {
+ "key":"voltha_serial_number",
+ "value":"44"
+ },
+ {
+ "key":"voltha_backend_name",
+ "value":""
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "_COMMENT":"If this test case fails, there could be a goroutine leak",
+ "name":"Get goroutine count",
+ "send": {
+ "client":"stats",
+ "method":"GetGoroutineCount",
+ "param":"{}",
+ "meta": [ ],
+ "expect":"{Count:39}",
+ "expectMeta": [ ]
+ },
+ "servers": [ ]
+ }
+ ]
+}
diff --git a/tests/afrouter/templates/client.go b/tests/afrouter/templates/client.go
index 8cd3330..db34efd 100644
--- a/tests/afrouter/templates/client.go
+++ b/tests/afrouter/templates/client.go
@@ -99,7 +99,7 @@
// value.
if resS,err := json.Marshal(res); err == nil {
if string(resS) != expect {
- resFile.testLog("Unexpected result returned\n")
+ stats.testLog("Unexpected result returned expected '%s' got '%s'\n", expect, string(resS))
return errors.New("Unexpected result on method {{.Name}}")
}
} else {
@@ -109,11 +109,11 @@
for k,v := range expectMeta {
if rv,ok := hdr[k]; ok == true {
if rv[0] != v[0] {
- resFile.testLog("Mismatch on returned metadata for key '%s' expected '%s' and got '%s'\n", k, v, rv)
+ stats.testLog("Mismatch on returned metadata for key '%s' expected '%s' and got '%s'\n", k, v, rv)
err = errors.New("Failure on returned metadata")
}
} else {
- resFile.testLog("Returned metadata missing key '%s'; expected value '%s' at that key\n", k, v)
+ stats.testLog("Returned metadata missing key '%s'; expected value '%s' at that key\n", k, v)
err = errors.New("Failure on returned metadata")
}
}
diff --git a/tests/afrouter/templates/main.go b/tests/afrouter/templates/main.go
index c2fddb9..0361460 100644
--- a/tests/afrouter/templates/main.go
+++ b/tests/afrouter/templates/main.go
@@ -21,74 +21,15 @@
import (
"os"
- "fmt"
"time"
"os/exec"
"strings"
"context"
- slog "log"
- "io/ioutil"
- "encoding/json"
- "google.golang.org/grpc/grpclog"
+ //slog "log"
+ //"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
- fn string
-}
-
-func (tr * tstLog) testLog(format string, a ...interface{}) {
- var err error
- if tr.fp == nil {
- if tr.fp, err = os.OpenFile(tr.fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err != nil {
- log.Errorf("Could not append to the results file")
- tr.fp = nil
- }
- }
- if tr.fp != nil {
- tr.fp.Write([]byte(fmt.Sprintf(format, a...)))
- }
-}
-
-func (tr * tstLog) testLogOnce(format string, a ...interface{}) {
- var err error
- if tr.fp == nil {
- if tr.fp, err = os.OpenFile(tr.fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err != nil {
- log.Errorf("Could not append to the results file")
- tr.fp = nil
- }
- }
- if tr.fp != nil {
- tr.fp.Write([]byte(fmt.Sprintf(format, a...)))
- }
- tr.fp.Close()
- tr.fp = nil
-}
-
-func (tr * tstLog) close() {
- if tr.fp != nil {
- tr.fp.Close()
- }
-}
-
-
func startSut(cmdStr string) (*exec.Cmd, context.CancelFunc, error) {
var err error = nil
@@ -109,58 +50,35 @@
func cleanUp(cmd *exec.Cmd, cncl context.CancelFunc) {
cncl()
- cmd.Wait() // This seems to hang
- // Give the child processes time to terminate
- //time.Sleep(1 * time.Second)
+ cmd.Wait()
}
-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
// Setup logging
- if _, err = log.SetDefaultLogger(log.JSON, 0, nil); err != nil {
+ if _, err = log.SetDefaultLogger(log.JSON, 1, nil); err != nil {
log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
}
defer log.CleanUp()
- readStats(&stats)
+ if len(os.Args) < 2 {
+ log.Fatalf("Stat file name parameter missing for %s. Aborting...", os.Args[0])
+ } else {
+ statFn = os.Args[1]
+ }
+ if stats,err = readStats(statFn); err != nil {
+ log.Error(err)
+ return
+ }
+ defer writeStats(statFn, stats)
- grpclog.SetLogger(slog.New(os.Stderr, "grpc: ", slog.LstdFlags))
+ // Add a stat entry for this run
- resFile = &tstLog{fn:os.Args[1]}
- defer resFile.close()
+ stats.appendNew()
+ tsIdx := len(stats.TestSuites) - 1
+ stats.TestSuites[tsIdx].Name = os.Args[0]
// Initialize the servers
@@ -186,7 +104,8 @@
// Run all the test cases now
log.Infof("Executing tests")
+
+ //log.Infof("Stats struct: %v", stats)
runTests()
- writeStats(&stats)
}
diff --git a/tests/afrouter/templates/runAll.go b/tests/afrouter/templates/runAll.go
index 03cdab1..5b02376 100644
--- a/tests/afrouter/templates/runAll.go
+++ b/tests/afrouter/templates/runAll.go
@@ -28,44 +28,6 @@
"github.com/opencord/voltha-go/common/log"
)
-type tstLog struct {
- fp * os.File
- fn string
-}
-
-func (tr * tstLog) testLog(format string, a ...interface{}) {
- var err error
- if tr.fp == nil {
- if tr.fp, err = os.OpenFile(tr.fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err != nil {
- log.Errorf("Could not append to the results file")
- tr.fp = nil
- }
- }
- if tr.fp != nil {
- tr.fp.Write([]byte(fmt.Sprintf(format, a...)))
- }
-}
-
-func (tr * tstLog) testLogOnce(format string, a ...interface{}) {
- var err error
- if tr.fp == nil {
- if tr.fp, err = os.OpenFile(tr.fn, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err != nil {
- log.Errorf("Could not append to the results file")
- tr.fp = nil
- }
- }
- if tr.fp != nil {
- tr.fp.Write([]byte(fmt.Sprintf(format, a...)))
- }
- tr.fp.Close()
- tr.fp = nil
-}
-
-func (tr * tstLog) close() {
- if tr.fp != nil {
- tr.fp.Close()
- }
-}
func main() {
var cmd *exec.Cmd
@@ -77,17 +39,23 @@
defer log.CleanUp()
+ statFn = "stats.json"
+
log.Info("Running tests")
if err:= os.Chdir(os.Args[1]); err != nil {
log.Error("Could not change directory to %s: %v", os.Args[1], err)
}
- tl := &tstLog{fn:"results.txt"}
+
+ if err := initStats(statFn); err != nil {
+ log.Error(err)
+ return
+ }
+
{{range .}}
cmdStr = "./"+"{{.}}"+".e"
- tl.testLogOnce("Running test suite '%s'\n", cmdStr[2:])
log.Infof("Running test suite %s",cmdStr)
- cmd = exec.Command(cmdStr, "results.txt")
+ cmd = exec.Command(cmdStr, statFn)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -96,10 +64,36 @@
}
{{end}}
// Open the results file and output it.
- if resFile, err := ioutil.ReadFile("results.txt"); err == nil {
+ if s,err := readStats(statFn); err != nil {
+ log.Error(err)
+ return
+ } else {
+ stats = s
+ }
+
+ //log.Infof("Stats are: %v", stats)
+ if resFile, err := ioutil.ReadFile(statFn); err == nil {
fmt.Println(string(resFile))
} else {
- log.Error("Could not load the results file 'results.txt'")
+ log.Error("Could not load the stats file 'stats.json'")
+ }
+ fmt.Println("Test result summary")
+ for _,v := range stats.TestSuites {
+ fmt.Printf("Test suite: %s\n", v.Name[2:len(v.Name)-2])
+ pass := 0
+ fail := 0
+ total := 0
+ for _,v1 := range v.TestCases {
+ total++
+ if v1.Result == true {
+ pass++
+ } else {
+ fail++
+ }
+ }
+ fmt.Printf("\tTotal test cases: %d\n", total)
+ fmt.Printf("\t\tTotal passed test cases: %d\n", pass)
+ fmt.Printf("\t\tTotal failed test cases: %d\n", fail)
}
log.Info("Tests complete")
}
diff --git a/tests/afrouter/templates/runTests.go b/tests/afrouter/templates/runTests.go
index 61ba5eb..ac58220 100644
--- a/tests/afrouter/templates/runTests.go
+++ b/tests/afrouter/templates/runTests.go
@@ -18,6 +18,7 @@
package main
import (
+{{if .HasFuncs}}
"fmt"
"time"
"errors"
@@ -26,14 +27,22 @@
//"golang.org/x/net/context"
"google.golang.org/grpc/metadata"
"github.com/opencord/voltha-go/common/log"
+{{end}}
{{range .Imports}}
- _ "{{.}}"
+ {{if .Used}}
+ "{{.Package}}"
+ {{end}}
{{end}}
{{range .ProtoImports}}
+ {{if .Used}}
{{.Short}} "{{.Package}}"
{{end}}
+ {{end}}
)
+
+{{if .FileNum}}
+{{else}}
var glCtx context.Context
func resetChannels() {
@@ -54,70 +63,148 @@
}
}
-func runTests() {
- {{range $k,$v := .Tests}}
- if err := test{{$k}}(); err == nil {
- resFile.testLog("\tTest Successful\n")
- } else {
- resFile.testLog("\tTest Failed\n")
- }
- //resetChannels()
- {{end}}
+type testData struct {
+ function func(int, string, string, []string,[]string,[]string,
+ map[string][]string,interface{}, interface{}) error
+ testNum int
+ testName string
+ sendClient string
+ srvrs []string
+ sendMeta []string
+ expectMeta []string // Send Meta
+ srvMeta map[string][]string
+ parm interface{}
+ ret interface{}
}
-{{range $k,$v := .Tests}}
-func test{{$k}}() error {
+func addTestSlot(stats * TestRun) {
+ tsIdx := len(stats.TestSuites) - 1
+ stats.TestSuites[tsIdx].TestCases =
+ append(stats.TestSuites[tsIdx].TestCases, TestCase{Info:[]string{}})
+}
+{{end}}
+
+{{if .FileNum}}
+func runTests{{.FileNum}}() {
+{{else}}
+func runTests() {
+{{end}}
+ tsIdx := len(stats.TestSuites) - 1
+ tests := []testData {
+ {{$ofs := .Offset}}
+ {{range $k,$v := .Tests}}
+ testData {
+ {{$v.Send.Method}}_test,
+ {{$k}} + {{$ofs}},
+ "{{$v.Name}}",
+ "{{$v.Send.Client}}",
+ []string{ {{range $sk,$sv := $v.Srvr}} "{{$sv.Name}}",{{end}} },
+ []string{ {{range $mk,$mv := $v.Send.MetaData}}"{{$mv.Key}}","{{$mv.Val}}",{{end}} },
+ []string{ {{range $mk,$mv := $v.Send.ExpectMeta}}"{{$mv.Key}}","{{$mv.Val}}",{{end}} },
+ map[string][]string {
+ {{range $sk,$sv := $v.Srvr}}
+ "{{$sv.Name}}":[]string {
+ {{range $mk, $mv := $sv.Meta}}
+ "{{$mv.Key}}","{{$mv.Val}}",
+ {{end}}
+ },
+ {{end}}
+ },
+ &{{$v.Send.ParamType}}{{$v.Send.Param}},
+ &{{$v.Send.ExpectType}}{{$v.Send.Expect}},
+ },
+ {{end}}
+ }
+
+ for _,v := range tests {
+ addTestSlot(stats)
+ stats.TestSuites[tsIdx].TestCases[v.testNum].Title = v.testName
+ if err := v.function(
+ v.testNum,
+ v.testName,
+ v.sendClient,
+ v.srvrs,
+ v.sendMeta,
+ v.expectMeta,
+ v.srvMeta,
+ v.parm,
+ v.ret); err != nil {
+ stats.TestSuites[tsIdx].TestCases[v.testNum].Result = false
+ } else {
+ stats.TestSuites[tsIdx].TestCases[v.testNum].Result = true
+ }
+ }
+ {{if .FileNum}}
+ {{else}}
+ {{range $k,$v := .RunTestsCallList}}
+ {{$v}}()
+ {{end}}
+ {{end}}
+ return
+ //resetChannels()
+}
+
+{{range $k,$v := .Funcs }}
+{{if $v.CodeGenerated}}
+{{else}}
+func {{$k}}_test(testNum int, testName string, sendClient string, srvrs []string,
+ sendMeta []string, expectMeta []string, srvrMeta map[string][]string,
+ parm interface{}, ret interface{}) error {
+
var rtrn error = nil
var cancel context.CancelFunc
+ var repl *reply
+ log.Debug("Running Test %d",testNum)
glCtx, cancel = context.WithTimeout(context.Background(), 900*time.Millisecond)
defer cancel()
- // Announce the test being run
- resFile.testLog("******************** Running test case ({{$k}}): {{$v.Name}}\n")
- // Acquire the client used to run the test
- cl := clients["{{$v.Send.Client}}"]
+
+ cl := clients[sendClient]
// 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 {
- err := errors.New("Server '{{$s.Name}}' is nil")
- log.Error(err)
- return err
+ switch r := ret.(type) {
+ case *{{$v.ReturnType}}:
+ repl = &reply{repl:r}
+ default:
+ log.Errorf("Invalid type in call to {{$k}}_test expecting {{$v.ReturnType}} got %T", ret)
}
- // 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())
+ // Send the reply data structure to each of the servers
+ for _,v := range srvrs {
+ if servers[v] == nil {
+ err := errors.New(fmt.Sprintf("Server %s is nil", v))
+ log.Error(err)
+ return err
}
- }(glCtx)
- {{end}}
+ // 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, srv string) {
+ select {
+ case servers[srv].replyData <- repl:
+ case <-ctx.Done():
+ rtrn := errors.New(fmt.Sprintf("Could not provide server %s with reply data",srv))
+ log.Error(rtrn)
+ stats.testLog("%s\n", rtrn.Error())
+ }
+ }(glCtx,v)
+ }
// 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)
+ log.Errorf("Marshaling the reply for test %s: %v",testName, err)
} else {
// Create the context for the call
ctx := context.Background()
- {{range $m := $v.Send.MetaData}}
- ctx = metadata.AppendToOutgoingContext(ctx, "{{$m.Key}}", "{{$m.Val}}")
- {{end}}
+ for i:=0; i<len(sendMeta); i += 2 {
+ ctx = metadata.AppendToOutgoingContext(ctx, sendMeta[i], sendMeta[i+1])
+ }
var md map[string]string = make(map[string]string)
- {{range $m := $v.Send.ExpectMeta}}
- md["{{$m.Key}}"] = "{{$m.Val}}"
- {{end}}
+ for i:=0; i<len(expectMeta); i+=2 {
+ md[expectMeta[i]] = expectMeta[i+1]
+ }
expectMd := metadata.New(md)
- if err := cl.send("{{$v.Send.Method}}", ctx,
- &{{$v.Send.ParamType}}{{$v.Send.Param}},
- string(expct), expectMd); err != nil {
- log.Errorf("Test case {{$v.Name}} failed!: %v", err)
-
+ if err := cl.send("{{$k}}", ctx, parm, string(expct), expectMd); err != nil {
+ log.Errorf("Test case %s failed!: %v", testName, err)
+ rtrn = err
}
}
@@ -125,39 +212,38 @@
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)
+ if pld, err := json.Marshal(parm); err != nil {
+ log.Errorf("Marshaling paramter for test %s: %v", testName, err)
} else {
payload = string(pld)
}
- {{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 {
- 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] {
- rtrn=errors.New(fmt.Sprintf("Mismatched metadata on server '%s' expected '%s', got '%s'", "{{$s.Name}}", "{{$m.Val}}", mv[0]))
+ for _,v := range srvrs {
+ s = servers[v]
+ // 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 {
+ rtrn = errors.New(fmt.Sprintf("Mismatched payload expected '%s', got '%s'", payload, i.payload))
log.Error(rtrn.Error())
- resFile.testLog("%s\n", rtrn.Error())
+ stats.testLog("%s\n", rtrn.Error())
}
+ for j:=0; j<len(srvrMeta[v]); j+=2 {
+ if mv,ok := i.meta[srvrMeta[v][j]]; ok == true {
+ if srvrMeta[v][j+1] != mv[0] {
+ rtrn=errors.New(fmt.Sprintf("Mismatched metadata on server '%s' expected '%s', got '%s'", srvrMeta[v][j], srvrMeta[v][j+1], mv[0]))
+ log.Error(rtrn.Error())
+ stats.testLog("%s\n", rtrn.Error())
+ }
+ }
+ }
+ case <-glCtx.Done():
+ rtrn = errors.New(fmt.Sprintf("Timeout: no response data available for server %s", testName))
+ stats.testLog("%s\n", rtrn.Error())
+ log.Error(rtrn)
}
- {{end}}
- 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}}
return rtrn
}
{{end}}
-
-
+{{end}}
diff --git a/tests/afrouter/templates/server.go b/tests/afrouter/templates/server.go
index ffea3d5..0f9ed48 100644
--- a/tests/afrouter/templates/server.go
+++ b/tests/afrouter/templates/server.go
@@ -123,7 +123,7 @@
{{else}}
func (ts {{$.Name}}TestServer) {{.Name}}(ctx context.Context, in *{{.Param}}) (*{{.Rtrn}}, error) {
var r * incoming = &incoming{}
- log.Debug("Serving {{$.Name}}")
+ //log.Debug("Serving {{$.Name}}")
// Read the metadata
if md,ok := metadata.FromIncomingContext(ctx); ok == false {
log.Error("Getting matadata during call to {{.Name}}")
diff --git a/tests/afrouter/templates/stats.go b/tests/afrouter/templates/stats.go
new file mode 100644
index 0000000..958bea7
--- /dev/null
+++ b/tests/afrouter/templates/stats.go
@@ -0,0 +1,126 @@
+/*
+ * 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"
+ "fmt"
+ //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:"passed"`
+ Info []string `json:"info"`
+}
+
+type TestSuite struct {
+ Name string `json:"name"`
+ Info []string `json:"info"`
+ TestCases []TestCase `json:"testCases"`
+}
+
+type TestRun struct {
+ TestSuites []TestSuite
+}
+
+func (tr * TestRun) testLog(format string, a ...interface{}) {
+
+ tridx := len(tr.TestSuites) - 1
+ tcidx := len(tr.TestSuites[tridx].TestCases) - 1
+
+ tr.TestSuites[tridx].TestCases[tcidx].Info =
+ append(tr.TestSuites[tridx].TestCases[tcidx].Info, fmt.Sprintf(format, a...))
+
+}
+
+func (tr * TestRun) suiteLog(format string, a ...interface{}) {
+
+ tridx := len(tr.TestSuites) - 1
+
+ tr.TestSuites[tridx].Info =
+ append(tr.TestSuites[tridx].Info, fmt.Sprintf(format, a...))
+
+}
+
+func readStats(statFn string) (*TestRun, error) {
+ // Check if the stats file exists
+ if _,err := os.Stat(statFn); err != nil {
+ // Nothing to do, just return
+ return &TestRun{}, nil
+ }
+ rtrn := &TestRun{}
+ // The file is there, read it an unmarshal it into the stats struct
+ if statBytes, err := ioutil.ReadFile(statFn); err != nil {
+ log.Error(err)
+ return nil, err
+ } else if err := json.Unmarshal(statBytes, rtrn); err != nil {
+ log.Error(err)
+ return nil, err
+ }
+ return rtrn, nil
+}
+
+func writeStats(statFn string, stats * TestRun) error {
+ // 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 err
+ } else if err := ioutil.WriteFile(statFn+".new", statBytes, 0644); err != nil {
+ log.Error(err)
+ return err
+ }
+ if err := os.Rename(statFn, statFn+"~"); err != nil {
+ log.Error(err)
+ return err
+ }
+ if err := os.Rename(statFn+".new", statFn); err != nil {
+ log.Error(err)
+ return err
+ }
+ return nil
+}
+
+func (tr * TestRun) appendNew() {
+ tr.TestSuites = append(tr.TestSuites, TestSuite{})
+}
+
+func initStats(statFn string) error {
+ s := &TestRun{}
+
+ if statBytes, err := json.MarshalIndent(s, ""," "); err != nil {
+ log.Error(err)
+ return err
+ } else if err := ioutil.WriteFile(statFn, statBytes, 0644); err != nil {
+ log.Error(err)
+ return err
+ }
+
+ return nil
+}
+
+
+var statFn string
+var stats *TestRun
diff --git a/tests/afrouter/tester.go b/tests/afrouter/tester.go
index f3d975e..fafbbbe 100644
--- a/tests/afrouter/tester.go
+++ b/tests/afrouter/tester.go
@@ -22,8 +22,10 @@
"fmt"
"flag"
"path"
+ "math"
//"bufio"
"errors"
+ "regexp"
"os/exec"
"strconv"
"io/ioutil"
@@ -34,6 +36,8 @@
pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
)
+const MAX_CT = 500
+
type TestConfig struct {
configFile *string
logLevel *int
@@ -47,6 +51,11 @@
Port string `json:"port"`
}
+type EndpointList struct {
+ Imports []string `json:"imports"`
+ Endpoints []Connection `json:"endPoints"`
+}
+
type ProtoFile struct {
ImportPath string `json:"importPath"`
Service string `json:"service"`
@@ -62,8 +71,8 @@
Command string `json:"cmdLine"`
ProtoFiles []ProtoFile `json:"protoFiles"`
ProtoDesc string `json:"protoDesc"`
- Clients []Connection `json:"clients"`
- Servers []Connection `json:"servers"`
+ Clients EndpointList `json:"clients"`
+ Servers EndpointList `json:"servers"`
Imports []string `json:"imports"`
ProtoSubsts []ProtoSubst `json:"protoSubst"`
}
@@ -89,6 +98,7 @@
type Test struct {
Name string `json:"name"`
+ InfoOnly bool `json:"infoOnly"`
Send Rpc `json:"send"`
Servers []Server `json:"servers"`
}
@@ -102,6 +112,7 @@
Service string
Short string
Package string
+ Used bool
}
type SendItem struct {
@@ -117,14 +128,32 @@
type TestCase struct {
Name string
+ InfoOnly bool
Send SendItem
Srvr []Server
}
+type MethodTypes struct {
+ ParamType string
+ ReturnType string
+ CodeGenerated bool
+}
+
+type Import struct {
+ Package string
+ Used bool
+}
+
type TestList struct {
ProtoImports []ProtoImport
- Imports []string
+ Imports []Import
Tests []TestCase
+ Funcs map[string]MethodTypes
+ RunTestsCallList []string
+ FileNum int
+ //NextFile int
+ HasFuncs bool
+ Offset int
}
type ClientConfig struct {
@@ -309,16 +338,19 @@
t *template.Template) error {
var servers []ServerConfig
- for k,v := range ts.Env.Servers {
+ for k,v := range ts.Env.Servers.Endpoints {
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}
+ sc := &ServerConfig{Name:v.Name, Port:v.Port, Ct:k, Imports:ts.Env.Servers.Imports,
+ Methods:make(map[string]*mthd)}
for k1,v1 := range ts.Env.ProtoFiles {
imp := &ProtoImport{Short:"pb"+strconv.Itoa(k1),
Package:v1.ImportPath+v1.Package,
- Service:v1.Service}
+ Service:v1.Service,
+ Used:true}
imp = &ProtoImport{Short:v1.Package,
Package:v1.ImportPath+v1.Package,
- Service:v1.Service}
+ Service:v1.Service,
+ Used:true}
sc.ProtoImports = append(sc.ProtoImports, *imp)
// Compile the template from the file
log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
@@ -329,7 +361,10 @@
return err
} else {
//Generate all the function calls required by the
- sc.Methods = mthds
+ for k,v := range mthds {
+ sc.Methods[k] = v
+ }
+ //sc.Methods = mthds
}
}
log.Debugf("Server: %v", *sc)
@@ -361,12 +396,10 @@
func generateClients(conf *TestConfig, suiteDir string, ts * TestSuite,
t *template.Template) error {
var clients []ClientConfig
- for k,v := range ts.Env.Clients {
+ for k,v := range ts.Env.Clients.Endpoints {
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.
+ cc := &ClientConfig{Name:v.Name, Port:v.Port, Ct:k, Imports:ts.Env.Clients.Imports,
+ Methods:make(map[string]*mthd)}
for _,v1 := range ts.Env.ProtoFiles {
imp := &ProtoImport{Short:v1.Package,
Package:v1.ImportPath+v1.Package,
@@ -380,12 +413,14 @@
ts.Env.ProtoDesc, v1.Package, v1.Service)
return err
} else {
- //Generate all the function calls required by the
- cc.Methods = mthds
+ // Add to the known methods
+ for k,v := range mthds {
+ cc.Methods[k] = v
+ }
}
}
clients = append(clients, *cc)
- if f,err := os.Create(suiteDir+"/"+v.Name+".go"); err == nil {
+ if f,err := os.Create(suiteDir+"/"+v.Name+"_client.go"); err == nil {
_=f
defer f.Close()
if err := t.ExecuteTemplate(f, "client.go", cc); err != nil {
@@ -393,7 +428,7 @@
return err
}
} else {
- log.Errorf("Couldn't create file %s : %v", suiteDir+"/client.go", err)
+ log.Errorf("Couldn't create file %s : %v", suiteDir+"/"+v.Name+"_client.go", err)
return err
}
}
@@ -409,28 +444,117 @@
}
func serverExists(srvr string, ts * TestSuite) bool {
- for _,v := range ts.Env.Servers {
+ for _,v := range ts.Env.Servers.Endpoints {
if v.Name == srvr {
return true
}
}
return false
}
+
+func getPackageList(tests []TestCase) map[string]struct{} {
+ var rtrn map[string]struct{} = make(map[string]struct{})
+ var p string
+ var e string
+ r := regexp.MustCompile(`^([^.]+)\..*$`)
+ for _,v := range tests {
+ pa := r.FindStringSubmatch(v.Send.ParamType)
+ if len(pa) == 2 {
+ p = pa[1]
+ } else {
+ log.Errorf("Internal error regexp returned %v", pa)
+ }
+ ea := r.FindStringSubmatch(v.Send.ExpectType)
+ if len(ea) == 2 {
+ e = ea[1]
+ } else {
+ log.Errorf("Internal error regexp returned %v", pa)
+ }
+ if _,ok := rtrn[p]; ok == false {
+ rtrn[p] = struct{}{}
+ }
+ if _,ok := rtrn[e]; ok == false {
+ rtrn[e] = struct{}{}
+ }
+ }
+ return rtrn
+}
+
+func fixupProtoImports(protoImports []ProtoImport, used map[string]struct{}) []ProtoImport {
+ var rtrn []ProtoImport
+ //log.Infof("Updating imports %v, using %v", protoImports, used)
+ for _,v := range protoImports {
+ //log.Infof("Looking for package %s", v.Package)
+ if _,ok := used[v.Short]; ok == true {
+ rtrn = append(rtrn, ProtoImport {
+ Service: v.Service,
+ Short: v.Short,
+ Package: v.Package,
+ Used: true,
+ })
+ } else {
+ rtrn = append(rtrn, ProtoImport {
+ Service: v.Service,
+ Short: v.Short,
+ Package: v.Package,
+ Used: false,
+ })
+ }
+ }
+ //log.Infof("After update %v", rtrn)
+ return rtrn
+}
+
+func fixupImports(imports []Import, used map[string]struct{}) []Import {
+ var rtrn []Import
+ var p string
+ re := regexp.MustCompile(`^.*/([^/]+)$`)
+ //log.Infof("Updating imports %v, using %v", protoImports, used)
+ for _,v := range imports {
+ //log.Infof("Looking for match in %s", v.Package)
+ pa := re.FindStringSubmatch(v.Package)
+ if len(pa) == 2 {
+ p = pa[1]
+ } else {
+ log.Errorf("Substring match failed, regexp returned %v", pa)
+ }
+ //log.Infof("Looking for package %s", v.Package)
+ if _,ok := used[p]; ok == true {
+ rtrn = append(rtrn, Import {
+ Package: v.Package,
+ Used: true,
+ })
+ } else {
+ rtrn = append(rtrn, Import {
+ Package: v.Package,
+ Used: false,
+ })
+ }
+ }
+ //log.Infof("After update %v", rtrn)
+ return rtrn
+}
+
+
func generateTestCases(conf *TestConfig, suiteDir string, ts * TestSuite,
t *template.Template) error {
var mthdMap map[string]*mthd
+ mthdMap = make(map[string]*mthd)
// Generate the test cases
log.Info("Generating the test cases: runTests.go")
- tc := &TestList{Imports:ts.Env.Imports}
+ tc := &TestList{Funcs:make(map[string]MethodTypes),
+ FileNum:0, HasFuncs: false,
+ Offset:0}
+ for _,v := range ts.Env.Imports {
+ tc.Imports = append(tc.Imports,Import{Package:v, Used:true})
+ }
// 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}
+ Service:v.Service,
+ Used:true}
tc.ProtoImports = append(tc.ProtoImports, *imp)
// Compile the template from the file
log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
@@ -440,14 +564,59 @@
ts.Env.ProtoDesc, v.Package, v.Service)
return err
} else {
- mthdMap = mthds
+ // Add to the known methods
+ for k,v := range mthds {
+ mthdMap[k] = v
+ }
}
}
+ // Since the input file can possibly be a template with loops that
+ // make multiple calls to exactly the same method with potentially
+ // different parameters it is best to try to optimize for that case.
+ // Creating a function for each method used will greatly reduce the
+ // code size and repetition. In the case where a method is used only
+ // once the resulting code will be bigger but this is an acceptable
+ // tradeoff to allow huge suites that call the same method repeatedly
+ // to test for leaks.
+
+ // The go compiler has trouble compiling files that are too large. In order
+ // to mitigate that, It's necessary to create more smaller files than the
+ // single one large one while making sure that the functions for the distinct
+ // methods are only defined once in one of the files.
+
+ // Yet another hiccup with the go compiler, it doesn't like deep function
+ // nesting meaning that each runTests function can't call the next without
+ // eventually blowing the stack check. In order to work around this, the
+ // first runTests function needs to call all the others in sequence to
+ // keep the stack depth constant.
+
+ // Counter limiting the number of tests to output to each file
+ maxCt := MAX_CT
+
+ // Get the list of distinct methods
+ //for _,v := range ts.Tests {
+ // if _,ok := tc.Funcs[v.Send.Method]; ok == false {
+ // tc.Funcs[v.Send.Method] = MethodTypes{ParamType:mthdMap[v.Send.Method].Param,
+ // ReturnType:mthdMap[v.Send.Method].Rtrn}
+ // }
+ //}
+ for i:=1; i<int(math.Ceil(float64(len(ts.Tests))/float64(MAX_CT))); i++ {
+ tc.RunTestsCallList = append(tc.RunTestsCallList, "runTests"+strconv.Itoa(i))
+ }
+
// Create the test data structure for the template
for _,v := range ts.Tests {
var test TestCase
+ if _,ok := tc.Funcs[v.Send.Method]; ok == false {
+ tc.Funcs[v.Send.Method] = MethodTypes{ParamType:mthdMap[v.Send.Method].Param,
+ ReturnType:mthdMap[v.Send.Method].Rtrn,
+ CodeGenerated:false}
+ tc.HasFuncs = true
+ }
+
test.Name = v.Name
+ test.InfoOnly = v.InfoOnly
test.Send.Client = v.Send.Client
test.Send.Method = v.Send.Method
test.Send.Param = v.Send.Param
@@ -471,15 +640,63 @@
test.Srvr = append(test.Srvr, srvr)
}
tc.Tests = append(tc.Tests, test)
+ if maxCt--; maxCt == 0 {
+ // Get the list of proto pacakges required for this file
+ pkgs := getPackageList(tc.Tests)
+ // Adjust the proto import data accordingly
+ tc.ProtoImports = fixupProtoImports(tc.ProtoImports, pkgs)
+ tc.Imports = fixupImports(tc.Imports, pkgs)
+ //log.Infof("The packages needed are: %v", pkgs)
+ if f,err := os.Create(suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".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"+strconv.Itoa(tc.FileNum)+".go", err)
+ }
+ tc.FileNum++
+ //tc.NextFile++
+ maxCt = MAX_CT
+ // Mark the functions as generated.
+ tc.Tests = []TestCase{}
+ for k,v := range tc.Funcs {
+ tc.Funcs[k] = MethodTypes{ParamType:v.ParamType,
+ ReturnType:v.ReturnType,
+ CodeGenerated:true}
+ }
+ tc.HasFuncs = false
+ tc.Offset += MAX_CT
+ //tmp,_ := strconv.Atoi(tc.Offset)
+ //tc.Offset = strconv.Itoa(tmp+500)
+ }
}
- if f,err := os.Create(suiteDir+"/runTests.go"); err == nil {
+ //tc.NextFile = 0
+ // Get the list of proto pacakges required for this file
+ pkgs := getPackageList(tc.Tests)
+ // Adjust the proto import data accordingly
+ tc.ProtoImports = fixupProtoImports(tc.ProtoImports, pkgs)
+ tc.Imports = fixupImports(tc.Imports, pkgs)
+ //log.Infof("The packages needed are: %v", pkgs)
+ if f,err := os.Create(suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".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)
+ log.Errorf("Couldn't create file %s : %v",
+ suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".go", err)
}
+
+ //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
}
@@ -497,13 +714,14 @@
ts := &TestSuite{}
ts.loadSuite(v)
log.Debugf("Suite %s: %v", v, ts)
- log.Info("Processing test suite %s", v)
+ log.Infof("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/stats.go",
"../templates/main.go"))
// Create a directory for he source code for this test suite
if err := os.Mkdir(suiteDir, 0777); err != nil {
@@ -537,6 +755,17 @@
log.Errorf("Couldn't create file %s : %v", suiteDir+"/main.go", err)
}
+ log.Infof("Copying over common modules")
+ if f,err := os.Create(suiteDir+"/stats.go"); err == nil {
+ if err := t.ExecuteTemplate(f, "stats.go", ts.Env); err != nil {
+ log.Errorf("Unable to execute template for stats.go: %v", err)
+ }
+ f.Close()
+ } else {
+ log.Errorf("Couldn't create file %s : %v", suiteDir+"/stats.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)
@@ -562,15 +791,24 @@
log.Errorf("Unable to create directory 'driver':%v\n", err)
return err
}
- t := template.Must(template.New("").ParseFiles("../templates/runAll.go"))
+ t := template.Must(template.New("").ParseFiles("../templates/runAll.go",
+ "../templates/stats.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)
+ log.Errorf("Unable to execute template for runAll.go: %v", err)
}
f.Close()
} else {
log.Errorf("Couldn't create file %s : %v", srcDir+"/runAll.go", err)
}
+ if f,err := os.Create(srcDir+"/stats.go"); err == nil {
+ if err := t.ExecuteTemplate(f, "stats.go", conf.Suites); err != nil {
+ log.Errorf("Unable to execute template for stats.go: %v", err)
+ }
+ f.Close()
+ } else {
+ log.Errorf("Couldn't create file %s : %v", srcDir+"/stats.go", err)
+ }
// Compile the test driver file
log.Info("Compiling the test driver")