blob: 94972eda6940f25c02ea47ab40b1fc8f6be91199 [file] [log] [blame]
Kent Hagerman0ab4cb22019-04-24 13:13:35 -04001// +build integration
2
sslobodrd6e07e72019-01-31 16:07:20 -05003/*
4 * Copyright 2018-present Open Networking Foundation
5
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9
10 * http://www.apache.org/licenses/LICENSE-2.0
11
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18// gRPC affinity router with active/active backends
19
20package main
21
sslobodrd6e07e72019-01-31 16:07:20 -050022import (
sslobodrd6e07e72019-01-31 16:07:20 -050023 "encoding/json"
David Bainbridgeec4ff512019-04-19 18:59:40 +000024 "errors"
25 "flag"
26 "fmt"
sslobodrd6e07e72019-01-31 16:07:20 -050027 "github.com/golang/protobuf/proto"
sslobodrd6e07e72019-01-31 16:07:20 -050028 pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
David Bainbridgeec4ff512019-04-19 18:59:40 +000029 "github.com/opencord/voltha-go/common/log"
30 "io/ioutil"
31 "math"
32 "os"
33 "os/exec"
34 "path"
35 "regexp"
36 "strconv"
37 "text/template"
sslobodrd6e07e72019-01-31 16:07:20 -050038)
39
sslobodr1d1e50b2019-03-14 09:17:40 -040040const MAX_CT = 500
41
sslobodrd6e07e72019-01-31 16:07:20 -050042type TestConfig struct {
43 configFile *string
David Bainbridgeec4ff512019-04-19 18:59:40 +000044 logLevel *int
45 grpcLog *bool
46 Suites []string `json:"suites"`
sslobodrd6e07e72019-01-31 16:07:20 -050047}
48
sslobodrd6e07e72019-01-31 16:07:20 -050049type Connection struct {
50 Name string `json:"name"`
51 Port string `json:"port"`
52}
53
sslobodr1d1e50b2019-03-14 09:17:40 -040054type EndpointList struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +000055 Imports []string `json:"imports"`
sslobodr1d1e50b2019-03-14 09:17:40 -040056 Endpoints []Connection `json:"endPoints"`
57}
58
sslobodrd6e07e72019-01-31 16:07:20 -050059type ProtoFile struct {
60 ImportPath string `json:"importPath"`
David Bainbridgeec4ff512019-04-19 18:59:40 +000061 Service string `json:"service"`
62 Package string `json:"package"`
sslobodrd6e07e72019-01-31 16:07:20 -050063}
64
65type ProtoSubst struct {
66 From string `json:"from"`
David Bainbridgeec4ff512019-04-19 18:59:40 +000067 To string `json:"to"`
sslobodrd6e07e72019-01-31 16:07:20 -050068}
69
70type Environment struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +000071 Command string `json:"cmdLine"`
72 ProtoFiles []ProtoFile `json:"protoFiles"`
73 ProtoDesc string `json:"protoDesc"`
74 Clients EndpointList `json:"clients"`
75 Servers EndpointList `json:"servers"`
76 Imports []string `json:"imports"`
sslobodrd6e07e72019-01-31 16:07:20 -050077 ProtoSubsts []ProtoSubst `json:"protoSubst"`
78}
79
80type Rpc struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +000081 Client string `json:"client"`
82 Method string `json:"method"`
83 Param string `json:"param"`
84 Expect string `json:"expect"`
85 MetaData []MetaD `json:"meta"`
sslobodrd9daabf2019-02-05 13:14:21 -050086 ExpectMeta []MetaD `json:"expectMeta"`
sslobodrd6e07e72019-01-31 16:07:20 -050087}
88
89type MetaD struct {
90 Key string `json:"key"`
91 Val string `json:"value"`
92}
93
94type Server struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +000095 Name string `json:"name"`
sslobodrd6e07e72019-01-31 16:07:20 -050096 Meta []MetaD `json:"meta"`
97}
98
99type Test struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000100 Name string `json:"name"`
101 InfoOnly bool `json:"infoOnly"`
102 Send Rpc `json:"send"`
103 Servers []Server `json:"servers"`
sslobodrd6e07e72019-01-31 16:07:20 -0500104}
105
106type TestSuite struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000107 Env Environment `json:"environment"`
108 Tests []Test `json:"tests"`
sslobodrd6e07e72019-01-31 16:07:20 -0500109}
110
111type ProtoImport struct {
112 Service string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000113 Short string
sslobodrd6e07e72019-01-31 16:07:20 -0500114 Package string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000115 Used bool
sslobodrd6e07e72019-01-31 16:07:20 -0500116}
117
118type SendItem struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000119 Client string
120 Method string
121 Param string
122 ParamType string
123 Expect string
sslobodrd6e07e72019-01-31 16:07:20 -0500124 ExpectType string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000125 MetaData []MetaD
sslobodrd9daabf2019-02-05 13:14:21 -0500126 ExpectMeta []MetaD
sslobodrd6e07e72019-01-31 16:07:20 -0500127}
128
129type TestCase struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000130 Name string
sslobodr1d1e50b2019-03-14 09:17:40 -0400131 InfoOnly bool
David Bainbridgeec4ff512019-04-19 18:59:40 +0000132 Send SendItem
133 Srvr []Server
sslobodrd6e07e72019-01-31 16:07:20 -0500134}
135
sslobodr1d1e50b2019-03-14 09:17:40 -0400136type MethodTypes struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000137 ParamType string
138 ReturnType string
sslobodr1d1e50b2019-03-14 09:17:40 -0400139 CodeGenerated bool
140}
141
142type Import struct {
143 Package string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000144 Used bool
sslobodr1d1e50b2019-03-14 09:17:40 -0400145}
146
sslobodrd6e07e72019-01-31 16:07:20 -0500147type TestList struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000148 ProtoImports []ProtoImport
149 Imports []Import
150 Tests []TestCase
151 Funcs map[string]MethodTypes
sslobodr1d1e50b2019-03-14 09:17:40 -0400152 RunTestsCallList []string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000153 FileNum int
sslobodr1d1e50b2019-03-14 09:17:40 -0400154 //NextFile int
155 HasFuncs bool
David Bainbridgeec4ff512019-04-19 18:59:40 +0000156 Offset int
sslobodrd6e07e72019-01-31 16:07:20 -0500157}
158
159type ClientConfig struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000160 Ct int
161 Name string
162 Port string
163 Imports []string
164 Methods map[string]*mthd
sslobodrd6e07e72019-01-31 16:07:20 -0500165 ProtoImports []ProtoImport
166}
167
168type ServerConfig struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000169 Ct int
170 Name string
171 Port string
172 Imports []string
173 Methods map[string]*mthd
sslobodrd6e07e72019-01-31 16:07:20 -0500174 ProtoImports []ProtoImport
175}
176
177type mthd struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000178 Pkg string
179 Svc string
180 Name string
sslobodrd6e07e72019-01-31 16:07:20 -0500181 Param string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000182 Rtrn string
183 Ss bool // Server streaming
184 Cs bool // Clieent streaming
sslobodrd6e07e72019-01-31 16:07:20 -0500185}
186
sslobodrd6e07e72019-01-31 16:07:20 -0500187func parseCmd() (*TestConfig, error) {
188 config := &TestConfig{}
David Bainbridgeec4ff512019-04-19 18:59:40 +0000189 cmdParse := flag.NewFlagSet(path.Base(os.Args[0]), flag.ContinueOnError)
sslobodrd6e07e72019-01-31 16:07:20 -0500190 config.configFile = cmdParse.String("config", "suites.json", "The configuration file for the affinity router tester")
191 config.logLevel = cmdParse.Int("logLevel", 0, "The log level for the affinity router tester")
192 config.grpcLog = cmdParse.Bool("grpclog", false, "Enable GRPC logging")
193
David Bainbridgeec4ff512019-04-19 18:59:40 +0000194 err := cmdParse.Parse(os.Args[1:])
sslobodrd6e07e72019-01-31 16:07:20 -0500195 if err != nil {
196 //return err
David Bainbridgeec4ff512019-04-19 18:59:40 +0000197 return nil, errors.New("Error parsing the command line")
sslobodrd6e07e72019-01-31 16:07:20 -0500198 }
199 return config, nil
200}
201
David Bainbridgeec4ff512019-04-19 18:59:40 +0000202func (conf *TestConfig) loadConfig() error {
sslobodrd6e07e72019-01-31 16:07:20 -0500203
David Bainbridgeec4ff512019-04-19 18:59:40 +0000204 configF, err := os.Open(*conf.configFile)
sslobodrd6e07e72019-01-31 16:07:20 -0500205 log.Info("Loading configuration from: ", *conf.configFile)
206 if err != nil {
207 log.Error(err)
208 return err
209 }
210
211 defer configF.Close()
212
213 if configBytes, err := ioutil.ReadAll(configF); err != nil {
214 log.Error(err)
215 return err
216 } else if err := json.Unmarshal(configBytes, conf); err != nil {
217 log.Error(err)
218 return err
219 }
220
221 return nil
222}
223
David Bainbridgeec4ff512019-04-19 18:59:40 +0000224func (suite *TestSuite) loadSuite(suiteN string) error {
sslobodr13182842019-02-08 14:40:30 -0500225 // Check if there's a corresponding go file for the suite.
226 // If it is present then the json test suite file is a
227 // template. Compile the go file and run it to process
228 // the template.
David Bainbridge31221742019-04-19 19:49:51 +0000229 if _, err := os.Stat(suiteN + "/" + suiteN + ".go"); err == nil {
sslobodr13182842019-02-08 14:40:30 -0500230 // Compile and run the the go file
231 log.Infof("Suite '%s' is a template, compiling '%s'", suiteN, suiteN+".go")
232 cmd := exec.Command("go", "build", "-o", suiteN+".te", suiteN+".go")
233 cmd.Stdin = os.Stdin
234 cmd.Stdout = os.Stdout
235 cmd.Stderr = os.Stderr
David Bainbridge31221742019-04-19 19:49:51 +0000236 cmd.Dir = suiteN
sslobodr13182842019-02-08 14:40:30 -0500237 if err := cmd.Run(); err != nil {
238 log.Errorf("Error running the compile command:%v", err)
239 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000240 cmd = exec.Command("./" + suiteN + ".te")
sslobodr13182842019-02-08 14:40:30 -0500241 cmd.Stdin = os.Stdin
242 cmd.Stdout = os.Stdout
243 cmd.Stderr = os.Stderr
David Bainbridge31221742019-04-19 19:49:51 +0000244 cmd.Dir = suiteN
sslobodr13182842019-02-08 14:40:30 -0500245 if err := cmd.Run(); err != nil {
246 log.Errorf("Error running the %s command:%v", suiteN+".te", err)
247 }
248 }
David Bainbridge31221742019-04-19 19:49:51 +0000249 suiteF, err := os.Open(suiteN + "/" + suiteN + ".json")
250 log.Infof("Loading test suite from: %s", suiteN+"/"+suiteN+".json")
sslobodrd6e07e72019-01-31 16:07:20 -0500251 if err != nil {
252 log.Error(err)
253 return err
254 }
255
256 defer suiteF.Close()
257
258 if suiteBytes, err := ioutil.ReadAll(suiteF); err != nil {
259 log.Error(err)
260 return err
261 } else if err := json.Unmarshal(suiteBytes, suite); err != nil {
262 log.Error(err)
263 return err
264 }
265
266 return nil
267}
268
269func loadProtoMap(fileName string, pkg string, svc string, substs []ProtoSubst) (map[string]*mthd, error) {
270 var mthds map[string]*mthd = make(map[string]*mthd)
271 var rtrn_err bool
272
273 // Load the protobuf descriptor file
274 protoDescriptor := &pb.FileDescriptorSet{}
David Bainbridgeec4ff512019-04-19 18:59:40 +0000275 fb, err := ioutil.ReadFile(fileName)
sslobodrd6e07e72019-01-31 16:07:20 -0500276 if err != nil {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000277 log.Errorf("Could not open proto file '%s'", fileName)
sslobodrd6e07e72019-01-31 16:07:20 -0500278 rtrn_err = true
279 }
280 err = proto.Unmarshal(fb, protoDescriptor)
281 if err != nil {
282 log.Errorf("Could not unmarshal %s, %v", "proto.pb", err)
283 rtrn_err = true
284 }
285
David Bainbridgeec4ff512019-04-19 18:59:40 +0000286 var substM map[string]string = make(map[string]string)
sslobodrd6e07e72019-01-31 16:07:20 -0500287 // Create a substitution map
288 log.Debugf("Creating import map")
David Bainbridgeec4ff512019-04-19 18:59:40 +0000289 for _, v := range substs {
sslobodrd6e07e72019-01-31 16:07:20 -0500290 log.Debugf("Mapping from %s to %s", v.From, v.To)
291 substM[v.From] = v.To
292 }
293
sslobodrd6e07e72019-01-31 16:07:20 -0500294 // Build the a map containing the method as the key
295 // and the paramter and return types as the fields
David Bainbridgeec4ff512019-04-19 18:59:40 +0000296 for _, f := range protoDescriptor.File {
sslobodrd6e07e72019-01-31 16:07:20 -0500297 if *f.Package == pkg {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000298 for _, s := range f.Service {
sslobodrd6e07e72019-01-31 16:07:20 -0500299 if *s.Name == svc {
300 log.Debugf("Loading package data '%s' for service '%s'", *f.Package, *s.Name)
301 // Now create a map keyed by method name with the value being the
302 // field number of the route selector.
303 //var ok bool
David Bainbridgeec4ff512019-04-19 18:59:40 +0000304 for _, m := range s.Method {
sslobodrd6e07e72019-01-31 16:07:20 -0500305 // Find the input type in the messages and extract the
306 // field number and save it for future reference.
David Bainbridgeec4ff512019-04-19 18:59:40 +0000307 log.Debugf("Processing method (%s(%s) (%s){}", *m.Name, (*m.InputType)[1:], (*m.OutputType)[1:])
308 mthds[*m.Name] = &mthd{Pkg: pkg, Svc: svc, Name: *m.Name, Param: (*m.InputType)[1:],
309 Rtrn: (*m.OutputType)[1:]}
sslobodrd6e07e72019-01-31 16:07:20 -0500310 if m.ClientStreaming != nil && *m.ClientStreaming == true {
311 log.Debugf("Method %s is a client streaming method", *m.Name)
312 mthds[*m.Name].Cs = true
313 }
314 if m.ServerStreaming != nil && *m.ServerStreaming == true {
315 log.Debugf("Method %s is a server streaming method", *m.Name)
316 mthds[*m.Name].Ss = true
317 }
318 // Perform the required substitutions
David Bainbridgeec4ff512019-04-19 18:59:40 +0000319 if _, ok := substM[mthds[*m.Name].Param]; ok == true {
sslobodrd6e07e72019-01-31 16:07:20 -0500320 mthds[*m.Name].Param = substM[mthds[*m.Name].Param]
321 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000322 if _, ok := substM[mthds[*m.Name].Rtrn]; ok == true {
sslobodrd6e07e72019-01-31 16:07:20 -0500323 mthds[*m.Name].Rtrn = substM[mthds[*m.Name].Rtrn]
324 }
325 }
326 }
327 }
328 }
329 }
330 if rtrn_err {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000331 return nil, errors.New(fmt.Sprintf("Failed to load protobuf descriptor file '%s'", fileName))
sslobodrd6e07e72019-01-31 16:07:20 -0500332 }
333 return mthds, nil
334}
335
336// Server source code generation
David Bainbridgeec4ff512019-04-19 18:59:40 +0000337func generateServers(conf *TestConfig, suiteDir string, ts *TestSuite,
338 t *template.Template) error {
sslobodrd6e07e72019-01-31 16:07:20 -0500339 var servers []ServerConfig
340
David Bainbridgeec4ff512019-04-19 18:59:40 +0000341 for k, v := range ts.Env.Servers.Endpoints {
sslobodrd6e07e72019-01-31 16:07:20 -0500342 log.Infof("Generating the code for server[%d]: %s", k, v.Name)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000343 sc := &ServerConfig{Name: v.Name, Port: v.Port, Ct: k, Imports: ts.Env.Servers.Imports,
344 Methods: make(map[string]*mthd)}
345 for k1, v1 := range ts.Env.ProtoFiles {
346 imp := &ProtoImport{Short: "pb" + strconv.Itoa(k1),
347 Package: v1.ImportPath + v1.Package,
348 Service: v1.Service,
349 Used: true}
350 imp = &ProtoImport{Short: v1.Package,
351 Package: v1.ImportPath + v1.Package,
352 Service: v1.Service,
353 Used: true}
sslobodrd6e07e72019-01-31 16:07:20 -0500354 sc.ProtoImports = append(sc.ProtoImports, *imp)
355 // Compile the template from the file
356 log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
357 if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v1.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000358 v1.Service, ts.Env.ProtoSubsts); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500359 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
David Bainbridgeec4ff512019-04-19 18:59:40 +0000360 ts.Env.ProtoDesc, v1.Package, v1.Service)
sslobodrd6e07e72019-01-31 16:07:20 -0500361 return err
362 } else {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000363 //Generate all the function calls required by the
364 for k, v := range mthds {
sslobodr1d1e50b2019-03-14 09:17:40 -0400365 sc.Methods[k] = v
366 }
367 //sc.Methods = mthds
sslobodrd6e07e72019-01-31 16:07:20 -0500368 }
369 }
370 log.Debugf("Server: %v", *sc)
371 // Save this server for the next steop
372 servers = append(servers, *sc)
373 // Open an output file to put the output in.
David Bainbridgeec4ff512019-04-19 18:59:40 +0000374 if f, err := os.Create(suiteDir + "/" + v.Name + ".go"); err == nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500375 defer f.Close()
David Bainbridgeec4ff512019-04-19 18:59:40 +0000376 //if err := t.ExecuteTemplate(os.Stdout, "server.go.tmpl", *sc); err != nil {}
377 if err := t.ExecuteTemplate(f, "server.go.tmpl", *sc); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500378 log.Errorf("Unable to execute template for server[%d]: %s: %v", k, v.Name, err)
379 return err
380 }
381 }
382 }
383 // Generate the server initialization code
David Bainbridgeec4ff512019-04-19 18:59:40 +0000384 if f, err := os.Create(suiteDir + "/serverInit.go"); err == nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500385 defer f.Close()
David Bainbridgeec4ff512019-04-19 18:59:40 +0000386 //if err := t.ExecuteTemplate(os.Stdout, "server.go.tmpl", *sc); err != nil {}
387 if err := t.ExecuteTemplate(f, "serverInit.go.tmpl", servers); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500388 log.Errorf("Unable to execute template for serverInit.go: %v", err)
389 return err
390 }
391 }
392
393 return nil
394}
395
David Bainbridgeec4ff512019-04-19 18:59:40 +0000396func generateClients(conf *TestConfig, suiteDir string, ts *TestSuite,
397 t *template.Template) error {
sslobodrd6e07e72019-01-31 16:07:20 -0500398 var clients []ClientConfig
David Bainbridgeec4ff512019-04-19 18:59:40 +0000399 for k, v := range ts.Env.Clients.Endpoints {
sslobodrd6e07e72019-01-31 16:07:20 -0500400 log.Infof("Generating the code for client[%d]: %s", k, v.Name)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000401 cc := &ClientConfig{Name: v.Name, Port: v.Port, Ct: k, Imports: ts.Env.Clients.Imports,
402 Methods: make(map[string]*mthd)}
403 for _, v1 := range ts.Env.ProtoFiles {
404 imp := &ProtoImport{Short: v1.Package,
405 Package: v1.ImportPath + v1.Package,
406 Service: v1.Service}
sslobodrd6e07e72019-01-31 16:07:20 -0500407 cc.ProtoImports = append(cc.ProtoImports, *imp)
408 // Compile the template from the file
409 log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
410 if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v1.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000411 v1.Service, ts.Env.ProtoSubsts); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500412 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
David Bainbridgeec4ff512019-04-19 18:59:40 +0000413 ts.Env.ProtoDesc, v1.Package, v1.Service)
sslobodrd6e07e72019-01-31 16:07:20 -0500414 return err
415 } else {
sslobodr1d1e50b2019-03-14 09:17:40 -0400416 // Add to the known methods
David Bainbridgeec4ff512019-04-19 18:59:40 +0000417 for k, v := range mthds {
sslobodr1d1e50b2019-03-14 09:17:40 -0400418 cc.Methods[k] = v
419 }
sslobodrd6e07e72019-01-31 16:07:20 -0500420 }
421 }
422 clients = append(clients, *cc)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000423 if f, err := os.Create(suiteDir + "/" + v.Name + "_client.go"); err == nil {
424 _ = f
sslobodrd6e07e72019-01-31 16:07:20 -0500425 defer f.Close()
David Bainbridgeec4ff512019-04-19 18:59:40 +0000426 if err := t.ExecuteTemplate(f, "client.go.tmpl", cc); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500427 log.Errorf("Unable to execute template for client.go: %v", err)
428 return err
429 }
430 } else {
sslobodr1d1e50b2019-03-14 09:17:40 -0400431 log.Errorf("Couldn't create file %s : %v", suiteDir+"/"+v.Name+"_client.go", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500432 return err
433 }
434 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000435 if f, err := os.Create(suiteDir + "/clientInit.go"); err == nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500436 defer f.Close()
David Bainbridgeec4ff512019-04-19 18:59:40 +0000437 //if err := t.ExecuteTemplate(os.Stdout, "server.go.tmpl", *sc); err != nil {}
438 if err := t.ExecuteTemplate(f, "clientInit.go.tmpl", clients); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500439 log.Errorf("Unable to execute template for clientInit.go: %v", err)
440 return err
441 }
442 }
443 return nil
444}
445
David Bainbridgeec4ff512019-04-19 18:59:40 +0000446func serverExists(srvr string, ts *TestSuite) bool {
447 for _, v := range ts.Env.Servers.Endpoints {
sslobodrd6e07e72019-01-31 16:07:20 -0500448 if v.Name == srvr {
449 return true
450 }
451 }
452 return false
453}
sslobodr1d1e50b2019-03-14 09:17:40 -0400454
455func getPackageList(tests []TestCase) map[string]struct{} {
456 var rtrn map[string]struct{} = make(map[string]struct{})
457 var p string
458 var e string
459 r := regexp.MustCompile(`^([^.]+)\..*$`)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000460 for _, v := range tests {
sslobodr1d1e50b2019-03-14 09:17:40 -0400461 pa := r.FindStringSubmatch(v.Send.ParamType)
462 if len(pa) == 2 {
463 p = pa[1]
464 } else {
465 log.Errorf("Internal error regexp returned %v", pa)
466 }
467 ea := r.FindStringSubmatch(v.Send.ExpectType)
468 if len(ea) == 2 {
469 e = ea[1]
470 } else {
471 log.Errorf("Internal error regexp returned %v", pa)
472 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000473 if _, ok := rtrn[p]; ok == false {
sslobodr1d1e50b2019-03-14 09:17:40 -0400474 rtrn[p] = struct{}{}
475 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000476 if _, ok := rtrn[e]; ok == false {
sslobodr1d1e50b2019-03-14 09:17:40 -0400477 rtrn[e] = struct{}{}
478 }
479 }
480 return rtrn
481}
482
483func fixupProtoImports(protoImports []ProtoImport, used map[string]struct{}) []ProtoImport {
484 var rtrn []ProtoImport
485 //log.Infof("Updating imports %v, using %v", protoImports, used)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000486 for _, v := range protoImports {
sslobodr1d1e50b2019-03-14 09:17:40 -0400487 //log.Infof("Looking for package %s", v.Package)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000488 if _, ok := used[v.Short]; ok == true {
489 rtrn = append(rtrn, ProtoImport{
sslobodr1d1e50b2019-03-14 09:17:40 -0400490 Service: v.Service,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000491 Short: v.Short,
sslobodr1d1e50b2019-03-14 09:17:40 -0400492 Package: v.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000493 Used: true,
sslobodr1d1e50b2019-03-14 09:17:40 -0400494 })
495 } else {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000496 rtrn = append(rtrn, ProtoImport{
sslobodr1d1e50b2019-03-14 09:17:40 -0400497 Service: v.Service,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000498 Short: v.Short,
sslobodr1d1e50b2019-03-14 09:17:40 -0400499 Package: v.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000500 Used: false,
sslobodr1d1e50b2019-03-14 09:17:40 -0400501 })
502 }
503 }
504 //log.Infof("After update %v", rtrn)
505 return rtrn
506}
507
508func fixupImports(imports []Import, used map[string]struct{}) []Import {
509 var rtrn []Import
510 var p string
511 re := regexp.MustCompile(`^.*/([^/]+)$`)
512 //log.Infof("Updating imports %v, using %v", protoImports, used)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000513 for _, v := range imports {
sslobodr1d1e50b2019-03-14 09:17:40 -0400514 //log.Infof("Looking for match in %s", v.Package)
515 pa := re.FindStringSubmatch(v.Package)
516 if len(pa) == 2 {
517 p = pa[1]
518 } else {
519 log.Errorf("Substring match failed, regexp returned %v", pa)
520 }
521 //log.Infof("Looking for package %s", v.Package)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000522 if _, ok := used[p]; ok == true {
523 rtrn = append(rtrn, Import{
sslobodr1d1e50b2019-03-14 09:17:40 -0400524 Package: v.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000525 Used: true,
sslobodr1d1e50b2019-03-14 09:17:40 -0400526 })
527 } else {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000528 rtrn = append(rtrn, Import{
sslobodr1d1e50b2019-03-14 09:17:40 -0400529 Package: v.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000530 Used: false,
sslobodr1d1e50b2019-03-14 09:17:40 -0400531 })
532 }
533 }
534 //log.Infof("After update %v", rtrn)
535 return rtrn
536}
537
David Bainbridgeec4ff512019-04-19 18:59:40 +0000538func generateTestCases(conf *TestConfig, suiteDir string, ts *TestSuite,
539 t *template.Template) error {
sslobodrd6e07e72019-01-31 16:07:20 -0500540 var mthdMap map[string]*mthd
sslobodr1d1e50b2019-03-14 09:17:40 -0400541 mthdMap = make(map[string]*mthd)
sslobodrd6e07e72019-01-31 16:07:20 -0500542 // Generate the test cases
543 log.Info("Generating the test cases: runTests.go")
David Bainbridgeec4ff512019-04-19 18:59:40 +0000544 tc := &TestList{Funcs: make(map[string]MethodTypes),
545 FileNum: 0, HasFuncs: false,
546 Offset: 0}
547 for _, v := range ts.Env.Imports {
548 tc.Imports = append(tc.Imports, Import{Package: v, Used: true})
sslobodr1d1e50b2019-03-14 09:17:40 -0400549 }
sslobodrd6e07e72019-01-31 16:07:20 -0500550
551 // Load the proto descriptor file
David Bainbridgeec4ff512019-04-19 18:59:40 +0000552 for _, v := range ts.Env.ProtoFiles {
553 imp := &ProtoImport{Short: v.Package,
554 Package: v.ImportPath + v.Package,
555 Service: v.Service,
556 Used: true}
sslobodrd6e07e72019-01-31 16:07:20 -0500557 tc.ProtoImports = append(tc.ProtoImports, *imp)
558 // Compile the template from the file
559 log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
560 if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000561 v.Service, ts.Env.ProtoSubsts); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500562 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
David Bainbridgeec4ff512019-04-19 18:59:40 +0000563 ts.Env.ProtoDesc, v.Package, v.Service)
sslobodrd6e07e72019-01-31 16:07:20 -0500564 return err
565 } else {
sslobodr1d1e50b2019-03-14 09:17:40 -0400566 // Add to the known methods
David Bainbridgeec4ff512019-04-19 18:59:40 +0000567 for k, v := range mthds {
sslobodr1d1e50b2019-03-14 09:17:40 -0400568 mthdMap[k] = v
569 }
sslobodrd6e07e72019-01-31 16:07:20 -0500570 }
571 }
sslobodr1d1e50b2019-03-14 09:17:40 -0400572 // Since the input file can possibly be a template with loops that
573 // make multiple calls to exactly the same method with potentially
574 // different parameters it is best to try to optimize for that case.
575 // Creating a function for each method used will greatly reduce the
576 // code size and repetition. In the case where a method is used only
577 // once the resulting code will be bigger but this is an acceptable
578 // tradeoff to allow huge suites that call the same method repeatedly
579 // to test for leaks.
580
581 // The go compiler has trouble compiling files that are too large. In order
582 // to mitigate that, It's necessary to create more smaller files than the
583 // single one large one while making sure that the functions for the distinct
584 // methods are only defined once in one of the files.
585
586 // Yet another hiccup with the go compiler, it doesn't like deep function
587 // nesting meaning that each runTests function can't call the next without
588 // eventually blowing the stack check. In order to work around this, the
589 // first runTests function needs to call all the others in sequence to
590 // keep the stack depth constant.
591
592 // Counter limiting the number of tests to output to each file
593 maxCt := MAX_CT
594
595 // Get the list of distinct methods
596 //for _,v := range ts.Tests {
597 // if _,ok := tc.Funcs[v.Send.Method]; ok == false {
598 // tc.Funcs[v.Send.Method] = MethodTypes{ParamType:mthdMap[v.Send.Method].Param,
599 // ReturnType:mthdMap[v.Send.Method].Rtrn}
600 // }
601 //}
David Bainbridgeec4ff512019-04-19 18:59:40 +0000602 for i := 1; i < int(math.Ceil(float64(len(ts.Tests))/float64(MAX_CT))); i++ {
sslobodr1d1e50b2019-03-14 09:17:40 -0400603 tc.RunTestsCallList = append(tc.RunTestsCallList, "runTests"+strconv.Itoa(i))
604 }
605
sslobodrd6e07e72019-01-31 16:07:20 -0500606 // Create the test data structure for the template
David Bainbridgeec4ff512019-04-19 18:59:40 +0000607 for _, v := range ts.Tests {
sslobodrd6e07e72019-01-31 16:07:20 -0500608 var test TestCase
609
David Bainbridgeec4ff512019-04-19 18:59:40 +0000610 if _, ok := tc.Funcs[v.Send.Method]; ok == false {
611 tc.Funcs[v.Send.Method] = MethodTypes{ParamType: mthdMap[v.Send.Method].Param,
612 ReturnType: mthdMap[v.Send.Method].Rtrn,
613 CodeGenerated: false}
sslobodr1d1e50b2019-03-14 09:17:40 -0400614 tc.HasFuncs = true
615 }
616
sslobodrd6e07e72019-01-31 16:07:20 -0500617 test.Name = v.Name
sslobodr1d1e50b2019-03-14 09:17:40 -0400618 test.InfoOnly = v.InfoOnly
sslobodrd6e07e72019-01-31 16:07:20 -0500619 test.Send.Client = v.Send.Client
620 test.Send.Method = v.Send.Method
621 test.Send.Param = v.Send.Param
622 test.Send.ParamType = mthdMap[test.Send.Method].Param
623 test.Send.Expect = v.Send.Expect
624 test.Send.ExpectType = mthdMap[test.Send.Method].Rtrn
David Bainbridgeec4ff512019-04-19 18:59:40 +0000625 for _, v1 := range v.Send.MetaData {
626 test.Send.MetaData = append(test.Send.MetaData, v1)
sslobodrd9daabf2019-02-05 13:14:21 -0500627 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000628 for _, v1 := range v.Send.ExpectMeta {
629 test.Send.ExpectMeta = append(test.Send.ExpectMeta, v1)
sslobodrd9daabf2019-02-05 13:14:21 -0500630 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000631 for _, v1 := range v.Servers {
sslobodrd6e07e72019-01-31 16:07:20 -0500632 var srvr Server
633 if serverExists(v1.Name, ts) == false {
634 log.Errorf("Server '%s' is not defined!!", v1.Name)
635 return errors.New(fmt.Sprintf("Failed to build test case %s", v.Name))
636 }
637 srvr.Name = v1.Name
638 srvr.Meta = v1.Meta
639 test.Srvr = append(test.Srvr, srvr)
640 }
641 tc.Tests = append(tc.Tests, test)
sslobodr1d1e50b2019-03-14 09:17:40 -0400642 if maxCt--; maxCt == 0 {
643 // Get the list of proto pacakges required for this file
644 pkgs := getPackageList(tc.Tests)
645 // Adjust the proto import data accordingly
646 tc.ProtoImports = fixupProtoImports(tc.ProtoImports, pkgs)
647 tc.Imports = fixupImports(tc.Imports, pkgs)
648 //log.Infof("The packages needed are: %v", pkgs)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000649 if f, err := os.Create(suiteDir + "/runTests" + strconv.Itoa(tc.FileNum) + ".go"); err == nil {
650 if err := t.ExecuteTemplate(f, "runTests.go.tmpl", tc); err != nil {
651 log.Errorf("Unable to execute template for runTests.go: %v", err)
sslobodr1d1e50b2019-03-14 09:17:40 -0400652 }
653 f.Close()
654 } else {
655 log.Errorf("Couldn't create file %s : %v",
David Bainbridgeec4ff512019-04-19 18:59:40 +0000656 suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".go", err)
sslobodr1d1e50b2019-03-14 09:17:40 -0400657 }
658 tc.FileNum++
659 //tc.NextFile++
660 maxCt = MAX_CT
661 // Mark the functions as generated.
662 tc.Tests = []TestCase{}
David Bainbridgeec4ff512019-04-19 18:59:40 +0000663 for k, v := range tc.Funcs {
664 tc.Funcs[k] = MethodTypes{ParamType: v.ParamType,
665 ReturnType: v.ReturnType,
666 CodeGenerated: true}
sslobodr1d1e50b2019-03-14 09:17:40 -0400667 }
668 tc.HasFuncs = false
669 tc.Offset += MAX_CT
670 //tmp,_ := strconv.Atoi(tc.Offset)
671 //tc.Offset = strconv.Itoa(tmp+500)
672 }
sslobodrd6e07e72019-01-31 16:07:20 -0500673 }
sslobodr1d1e50b2019-03-14 09:17:40 -0400674 //tc.NextFile = 0
675 // Get the list of proto pacakges required for this file
676 pkgs := getPackageList(tc.Tests)
677 // Adjust the proto import data accordingly
678 tc.ProtoImports = fixupProtoImports(tc.ProtoImports, pkgs)
679 tc.Imports = fixupImports(tc.Imports, pkgs)
680 //log.Infof("The packages needed are: %v", pkgs)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000681 if f, err := os.Create(suiteDir + "/runTests" + strconv.Itoa(tc.FileNum) + ".go"); err == nil {
682 if err := t.ExecuteTemplate(f, "runTests.go.tmpl", tc); err != nil {
683 log.Errorf("Unable to execute template for runTests.go: %v", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500684 }
685 f.Close()
686 } else {
sslobodr1d1e50b2019-03-14 09:17:40 -0400687 log.Errorf("Couldn't create file %s : %v",
David Bainbridgeec4ff512019-04-19 18:59:40 +0000688 suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".go", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500689 }
sslobodr1d1e50b2019-03-14 09:17:40 -0400690
691 //if f,err := os.Create(suiteDir+"/runTests.go"); err == nil {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000692 // if err := t.ExecuteTemplate(f, "runTests.go.tmpl", tc); err != nil {
sslobodr1d1e50b2019-03-14 09:17:40 -0400693 // log.Errorf("Unable to execute template for runTests.go: %v", err)
694 // }
695 // f.Close()
696 //} else {
697 // log.Errorf("Couldn't create file %s : %v", suiteDir+"/runTests.go", err)
698 //}
sslobodrd6e07e72019-01-31 16:07:20 -0500699 return nil
700}
701
702func generateTestSuites(conf *TestConfig, srcDir string, outDir string) error {
703
704 // Create a directory for the tests
705 if err := os.Mkdir(srcDir, 0777); err != nil {
706 log.Errorf("Unable to create directory 'tests':%v\n", err)
707 return err
708 }
709
David Bainbridgeec4ff512019-04-19 18:59:40 +0000710 for k, v := range conf.Suites {
711 var suiteDir string = srcDir + "/" + v
sslobodrd6e07e72019-01-31 16:07:20 -0500712 log.Debugf("Suite[%d] - %s", k, v)
713 ts := &TestSuite{}
714 ts.loadSuite(v)
715 log.Debugf("Suite %s: %v", v, ts)
sslobodr1d1e50b2019-03-14 09:17:40 -0400716 log.Infof("Processing test suite %s", v)
sslobodrd6e07e72019-01-31 16:07:20 -0500717
David Bainbridgeec4ff512019-04-19 18:59:40 +0000718 t := template.Must(template.New("").ParseFiles("../templates/server.go.tmpl",
719 "../templates/serverInit.go.tmpl",
720 "../templates/client.go.tmpl",
721 "../templates/clientInit.go.tmpl",
722 "../templates/runTests.go.tmpl",
723 "../templates/stats.go.tmpl",
724 "../templates/main.go.tmpl"))
sslobodrd6e07e72019-01-31 16:07:20 -0500725 // Create a directory for he source code for this test suite
726 if err := os.Mkdir(suiteDir, 0777); err != nil {
727 log.Errorf("Unable to create directory '%s':%v\n", v, err)
728 return err
729 }
730 // Generate the server source files
731 if err := generateServers(conf, suiteDir, ts, t); err != nil {
732 log.Errorf("Unable to generate server source files: %v", err)
733 return err
734 }
735 // Generate the client source files
736 if err := generateClients(conf, suiteDir, ts, t); err != nil {
737 log.Errorf("Unable to generate client source files: %v", err)
738 return err
739 }
740 // Generate the test case source file
741 if err := generateTestCases(conf, suiteDir, ts, t); err != nil {
742 log.Errorf("Unable to generate test case source file: %v", err)
743 return err
744 }
745
746 // Finally generate the main file
747 log.Info("Generating main.go")
David Bainbridgeec4ff512019-04-19 18:59:40 +0000748 if f, err := os.Create(suiteDir + "/main.go"); err == nil {
749 if err := t.ExecuteTemplate(f, "main.go.tmpl", ts.Env); err != nil {
750 log.Errorf("Unable to execute template for main.go: %v", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500751 }
752 f.Close()
753 } else {
754 log.Errorf("Couldn't create file %s : %v", suiteDir+"/main.go", err)
755 }
756
sslobodr1d1e50b2019-03-14 09:17:40 -0400757 log.Infof("Copying over common modules")
David Bainbridgeec4ff512019-04-19 18:59:40 +0000758 if f, err := os.Create(suiteDir + "/stats.go"); err == nil {
759 if err := t.ExecuteTemplate(f, "stats.go.tmpl", ts.Env); err != nil {
760 log.Errorf("Unable to execute template for stats.go: %v", err)
sslobodr1d1e50b2019-03-14 09:17:40 -0400761 }
762 f.Close()
763 } else {
764 log.Errorf("Couldn't create file %s : %v", suiteDir+"/stats.go", err)
765 }
766
sslobodrd6e07e72019-01-31 16:07:20 -0500767 log.Infof("Compiling test suite: %s in directory %s", v, suiteDir)
768 if err := os.Chdir(suiteDir); err != nil {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000769 log.Errorf("Could not change to directory '%s':%v", suiteDir, err)
sslobodrd6e07e72019-01-31 16:07:20 -0500770 }
sslobodr13182842019-02-08 14:40:30 -0500771 cmd := exec.Command("go", "build", "-o", outDir+"/"+v+".e")
sslobodrd6e07e72019-01-31 16:07:20 -0500772 cmd.Stdin = os.Stdin
773 cmd.Stdout = os.Stdout
774 cmd.Stderr = os.Stderr
775 if err := cmd.Run(); err != nil {
776 log.Errorf("Error running the compile command:%v", err)
777 }
778 if err := os.Chdir("../../suites"); err != nil {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000779 log.Errorf("Could not change to directory '%s':%v", "../../suites", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500780 }
sslobodrd6e07e72019-01-31 16:07:20 -0500781 }
782 return nil
783
784}
785
786func generateTestDriver(conf *TestConfig, srcDir string, outDir string) error {
787 // Generate the main test driver file
788 if err := os.Mkdir(srcDir, 0777); err != nil {
789 log.Errorf("Unable to create directory 'driver':%v\n", err)
790 return err
791 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000792 t := template.Must(template.New("").ParseFiles("../templates/runAll.go.tmpl",
793 "../templates/stats.go.tmpl"))
794 if f, err := os.Create(srcDir + "/runAll.go"); err == nil {
795 if err := t.ExecuteTemplate(f, "runAll.go.tmpl", conf.Suites); err != nil {
796 log.Errorf("Unable to execute template for runAll.go: %v", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500797 }
798 f.Close()
799 } else {
800 log.Errorf("Couldn't create file %s : %v", srcDir+"/runAll.go", err)
801 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000802 if f, err := os.Create(srcDir + "/stats.go"); err == nil {
803 if err := t.ExecuteTemplate(f, "stats.go.tmpl", conf.Suites); err != nil {
804 log.Errorf("Unable to execute template for stats.go: %v", err)
sslobodr1d1e50b2019-03-14 09:17:40 -0400805 }
806 f.Close()
807 } else {
808 log.Errorf("Couldn't create file %s : %v", srcDir+"/stats.go", err)
809 }
sslobodrd6e07e72019-01-31 16:07:20 -0500810
811 // Compile the test driver file
812 log.Info("Compiling the test driver")
813 if err := os.Chdir("../tests/driver"); err != nil {
814 log.Errorf("Could not change to directory 'driver':%v", err)
815 }
816 cmd := exec.Command("go", "build", "-o", outDir+"/runAll")
817 cmd.Stdin = os.Stdin
818 cmd.Stdout = os.Stdout
819 cmd.Stderr = os.Stderr
820 if err := cmd.Run(); err != nil {
821 log.Errorf("Error running the compile command:%v", err)
822 }
823 if err := os.Chdir("../../suites"); err != nil {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000824 log.Errorf("Could not change to directory 'driver':%v", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500825 }
826
827 return nil
828}
829
sslobodrd6e07e72019-01-31 16:07:20 -0500830func main() {
831
David Bainbridgeec4ff512019-04-19 18:59:40 +0000832 conf, err := parseCmd()
sslobodrd6e07e72019-01-31 16:07:20 -0500833 if err != nil {
834 fmt.Printf("Error: %v\n", err)
835 return
836 }
837
838 // Setup logging
839 if _, err := log.SetDefaultLogger(log.JSON, *conf.logLevel, nil); err != nil {
840 log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
841 }
842
843 defer log.CleanUp()
844
845 // Parse the config file
846 if err := conf.loadConfig(); err != nil {
847 log.Error(err)
848 }
849
850 generateTestSuites(conf, "../tests", "/src/tests")
851 generateTestDriver(conf, "../tests/driver", "/src/tests")
852 return
853}