blob: fafbbbe3eb3d25444e67398e1667af8b867520b6 [file] [log] [blame]
sslobodrd6e07e72019-01-31 16:07:20 -05001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16// gRPC affinity router with active/active backends
17
18package main
19
sslobodrd6e07e72019-01-31 16:07:20 -050020import (
21 "os"
22 "fmt"
23 "flag"
24 "path"
sslobodr1d1e50b2019-03-14 09:17:40 -040025 "math"
sslobodrd6e07e72019-01-31 16:07:20 -050026 //"bufio"
27 "errors"
sslobodr1d1e50b2019-03-14 09:17:40 -040028 "regexp"
sslobodrd6e07e72019-01-31 16:07:20 -050029 "os/exec"
30 "strconv"
31 "io/ioutil"
32 "encoding/json"
33 "text/template"
34 "github.com/golang/protobuf/proto"
35 "github.com/opencord/voltha-go/common/log"
36 pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
37)
38
sslobodr1d1e50b2019-03-14 09:17:40 -040039const MAX_CT = 500
40
sslobodrd6e07e72019-01-31 16:07:20 -050041type TestConfig struct {
42 configFile *string
43 logLevel *int
44 grpcLog *bool
45 Suites []string `json:"suites"`
46}
47
48
49type Connection struct {
50 Name string `json:"name"`
51 Port string `json:"port"`
52}
53
sslobodr1d1e50b2019-03-14 09:17:40 -040054type EndpointList struct {
55 Imports []string `json:"imports"`
56 Endpoints []Connection `json:"endPoints"`
57}
58
sslobodrd6e07e72019-01-31 16:07:20 -050059type ProtoFile struct {
60 ImportPath string `json:"importPath"`
61 Service string `json:"service"`
62 Package string `json:"package"`
63}
64
65type ProtoSubst struct {
66 From string `json:"from"`
67 To string `json:"to"`
68}
69
70type Environment struct {
71 Command string `json:"cmdLine"`
72 ProtoFiles []ProtoFile `json:"protoFiles"`
73 ProtoDesc string `json:"protoDesc"`
sslobodr1d1e50b2019-03-14 09:17:40 -040074 Clients EndpointList `json:"clients"`
75 Servers EndpointList `json:"servers"`
sslobodrd6e07e72019-01-31 16:07:20 -050076 Imports []string `json:"imports"`
77 ProtoSubsts []ProtoSubst `json:"protoSubst"`
78}
79
80type Rpc struct {
81 Client string `json:"client"`
82 Method string `json:"method"`
83 Param string `json:"param"`
84 Expect string `json:"expect"`
sslobodrd9daabf2019-02-05 13:14:21 -050085 MetaData []MetaD `json:"meta"`
86 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 {
95 Name string `json:"name"`
96 Meta []MetaD `json:"meta"`
97}
98
99type Test struct {
100 Name string `json:"name"`
sslobodr1d1e50b2019-03-14 09:17:40 -0400101 InfoOnly bool `json:"infoOnly"`
sslobodrd6e07e72019-01-31 16:07:20 -0500102 Send Rpc `json:"send"`
103 Servers []Server `json:"servers"`
104}
105
106type TestSuite struct {
107 Env Environment `json:"environment"`
108 Tests []Test `json:"tests"`
109}
110
111type ProtoImport struct {
112 Service string
113 Short string
114 Package string
sslobodr1d1e50b2019-03-14 09:17:40 -0400115 Used bool
sslobodrd6e07e72019-01-31 16:07:20 -0500116}
117
118type SendItem struct {
119 Client string
120 Method string
121 Param string
122 ParamType string
123 Expect string
124 ExpectType string
sslobodrd9daabf2019-02-05 13:14:21 -0500125 MetaData []MetaD
126 ExpectMeta []MetaD
sslobodrd6e07e72019-01-31 16:07:20 -0500127}
128
129type TestCase struct {
130 Name string
sslobodr1d1e50b2019-03-14 09:17:40 -0400131 InfoOnly bool
sslobodrd6e07e72019-01-31 16:07:20 -0500132 Send SendItem
133 Srvr []Server
134}
135
sslobodr1d1e50b2019-03-14 09:17:40 -0400136type MethodTypes struct {
137 ParamType string
138 ReturnType string
139 CodeGenerated bool
140}
141
142type Import struct {
143 Package string
144 Used bool
145}
146
sslobodrd6e07e72019-01-31 16:07:20 -0500147type TestList struct {
148 ProtoImports []ProtoImport
sslobodr1d1e50b2019-03-14 09:17:40 -0400149 Imports []Import
sslobodrd6e07e72019-01-31 16:07:20 -0500150 Tests []TestCase
sslobodr1d1e50b2019-03-14 09:17:40 -0400151 Funcs map[string]MethodTypes
152 RunTestsCallList []string
153 FileNum int
154 //NextFile int
155 HasFuncs bool
156 Offset int
sslobodrd6e07e72019-01-31 16:07:20 -0500157}
158
159type ClientConfig struct {
160 Ct int
161 Name string
162 Port string
163 Imports []string
164 Methods map[string]*mthd
165 ProtoImports []ProtoImport
166}
167
168type ServerConfig struct {
169 Ct int
170 Name string
171 Port string
172 Imports []string
173 Methods map[string]*mthd
174 ProtoImports []ProtoImport
175}
176
177type mthd struct {
178 Pkg string
179 Svc string
180 Name string
181 Param string
182 Rtrn string
183 Ss bool // Server streaming
184 Cs bool // Clieent streaming
185}
186
187
188func parseCmd() (*TestConfig, error) {
189 config := &TestConfig{}
190 cmdParse := flag.NewFlagSet(path.Base(os.Args[0]), flag.ContinueOnError);
191 config.configFile = cmdParse.String("config", "suites.json", "The configuration file for the affinity router tester")
192 config.logLevel = cmdParse.Int("logLevel", 0, "The log level for the affinity router tester")
193 config.grpcLog = cmdParse.Bool("grpclog", false, "Enable GRPC logging")
194
195 err := cmdParse.Parse(os.Args[1:]);
196 if err != nil {
197 //return err
198 return nil, errors.New("Error parsing the command line");
199 }
200 return config, nil
201}
202
203func (conf * TestConfig) loadConfig() error {
204
205 configF, err := os.Open(*conf.configFile);
206 log.Info("Loading configuration from: ", *conf.configFile)
207 if err != nil {
208 log.Error(err)
209 return err
210 }
211
212 defer configF.Close()
213
214 if configBytes, err := ioutil.ReadAll(configF); err != nil {
215 log.Error(err)
216 return err
217 } else if err := json.Unmarshal(configBytes, conf); err != nil {
218 log.Error(err)
219 return err
220 }
221
222 return nil
223}
224
sslobodr13182842019-02-08 14:40:30 -0500225func (suite * TestSuite) loadSuite(suiteN string) error {
226 // Check if there's a corresponding go file for the suite.
227 // If it is present then the json test suite file is a
228 // template. Compile the go file and run it to process
229 // the template.
230 if _,err := os.Stat(suiteN+".go"); err == nil {
231 // Compile and run the the go file
232 log.Infof("Suite '%s' is a template, compiling '%s'", suiteN, suiteN+".go")
233 cmd := exec.Command("go", "build", "-o", suiteN+".te", suiteN+".go")
234 cmd.Stdin = os.Stdin
235 cmd.Stdout = os.Stdout
236 cmd.Stderr = os.Stderr
237 if err := cmd.Run(); err != nil {
238 log.Errorf("Error running the compile command:%v", err)
239 }
240 cmd = exec.Command("./"+suiteN+".te")
241 cmd.Stdin = os.Stdin
242 cmd.Stdout = os.Stdout
243 cmd.Stderr = os.Stderr
244 if err := cmd.Run(); err != nil {
245 log.Errorf("Error running the %s command:%v", suiteN+".te", err)
246 }
247 }
248 suiteF, err := os.Open(suiteN+".json");
249 log.Infof("Loading test suite from: %s", suiteN+".json")
sslobodrd6e07e72019-01-31 16:07:20 -0500250 if err != nil {
251 log.Error(err)
252 return err
253 }
254
255 defer suiteF.Close()
256
257 if suiteBytes, err := ioutil.ReadAll(suiteF); err != nil {
258 log.Error(err)
259 return err
260 } else if err := json.Unmarshal(suiteBytes, suite); err != nil {
261 log.Error(err)
262 return err
263 }
264
265 return nil
266}
267
268func loadProtoMap(fileName string, pkg string, svc string, substs []ProtoSubst) (map[string]*mthd, error) {
269 var mthds map[string]*mthd = make(map[string]*mthd)
270 var rtrn_err bool
271
272 // Load the protobuf descriptor file
273 protoDescriptor := &pb.FileDescriptorSet{}
274 fb, err := ioutil.ReadFile(fileName);
275 if err != nil {
276 log.Errorf("Could not open proto file '%s'",fileName)
277 rtrn_err = true
278 }
279 err = proto.Unmarshal(fb, protoDescriptor)
280 if err != nil {
281 log.Errorf("Could not unmarshal %s, %v", "proto.pb", err)
282 rtrn_err = true
283 }
284
285 var substM map[string]string = make(map[string]string)
286 // Create a substitution map
287 log.Debugf("Creating import map")
288 for _,v := range substs {
289 log.Debugf("Mapping from %s to %s", v.From, v.To)
290 substM[v.From] = v.To
291 }
292
293
294 // Build the a map containing the method as the key
295 // and the paramter and return types as the fields
296 for _,f := range protoDescriptor.File {
297 if *f.Package == pkg {
298 for _, s:= range f.Service {
299 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
304 for _,m := range s.Method {
305 // Find the input type in the messages and extract the
306 // field number and save it for future reference.
307 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:]}
310 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
319 if _,ok := substM[mthds[*m.Name].Param]; ok == true {
320 mthds[*m.Name].Param = substM[mthds[*m.Name].Param]
321 }
322 if _,ok := substM[mthds[*m.Name].Rtrn]; ok == true {
323 mthds[*m.Name].Rtrn = substM[mthds[*m.Name].Rtrn]
324 }
325 }
326 }
327 }
328 }
329 }
330 if rtrn_err {
331 return nil,errors.New(fmt.Sprintf("Failed to load protobuf descriptor file '%s'",fileName))
332 }
333 return mthds, nil
334}
335
336// Server source code generation
337func generateServers(conf *TestConfig, suiteDir string, ts * TestSuite,
338 t *template.Template) error {
339 var servers []ServerConfig
340
sslobodr1d1e50b2019-03-14 09:17:40 -0400341 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)
sslobodr1d1e50b2019-03-14 09:17:40 -0400343 sc := &ServerConfig{Name:v.Name, Port:v.Port, Ct:k, Imports:ts.Env.Servers.Imports,
344 Methods:make(map[string]*mthd)}
sslobodrd6e07e72019-01-31 16:07:20 -0500345 for k1,v1 := range ts.Env.ProtoFiles {
346 imp := &ProtoImport{Short:"pb"+strconv.Itoa(k1),
347 Package:v1.ImportPath+v1.Package,
sslobodr1d1e50b2019-03-14 09:17:40 -0400348 Service:v1.Service,
349 Used:true}
sslobodrd6e07e72019-01-31 16:07:20 -0500350 imp = &ProtoImport{Short:v1.Package,
351 Package:v1.ImportPath+v1.Package,
sslobodr1d1e50b2019-03-14 09:17:40 -0400352 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,
358 v1.Service, ts.Env.ProtoSubsts); err != nil {
359 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
360 ts.Env.ProtoDesc, v1.Package, v1.Service)
361 return err
362 } else {
363 //Generate all the function calls required by the
sslobodr1d1e50b2019-03-14 09:17:40 -0400364 for k,v := range mthds {
365 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.
374 if f,err := os.Create(suiteDir+"/"+v.Name+".go"); err == nil {
375 defer f.Close()
376 //if err := t.ExecuteTemplate(os.Stdout, "server.go", *sc); err != nil {}
377 if err := t.ExecuteTemplate(f, "server.go", *sc); err != nil {
378 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
384 if f,err := os.Create(suiteDir+"/serverInit.go"); err == nil {
385 defer f.Close()
386 //if err := t.ExecuteTemplate(os.Stdout, "server.go", *sc); err != nil {}
387 if err := t.ExecuteTemplate(f, "serverInit.go", servers); err != nil {
388 log.Errorf("Unable to execute template for serverInit.go: %v", err)
389 return err
390 }
391 }
392
393 return nil
394}
395
396func generateClients(conf *TestConfig, suiteDir string, ts * TestSuite,
397 t *template.Template) error {
398 var clients []ClientConfig
sslobodr1d1e50b2019-03-14 09:17:40 -0400399 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)
sslobodr1d1e50b2019-03-14 09:17:40 -0400401 cc := &ClientConfig{Name:v.Name, Port:v.Port, Ct:k, Imports:ts.Env.Clients.Imports,
402 Methods:make(map[string]*mthd)}
sslobodrd6e07e72019-01-31 16:07:20 -0500403 for _,v1 := range ts.Env.ProtoFiles {
404 imp := &ProtoImport{Short:v1.Package,
405 Package:v1.ImportPath+v1.Package,
406 Service:v1.Service}
407 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,
411 v1.Service, ts.Env.ProtoSubsts); err != nil {
412 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
413 ts.Env.ProtoDesc, v1.Package, v1.Service)
414 return err
415 } else {
sslobodr1d1e50b2019-03-14 09:17:40 -0400416 // Add to the known methods
417 for k,v := range mthds {
418 cc.Methods[k] = v
419 }
sslobodrd6e07e72019-01-31 16:07:20 -0500420 }
421 }
422 clients = append(clients, *cc)
sslobodr1d1e50b2019-03-14 09:17:40 -0400423 if f,err := os.Create(suiteDir+"/"+v.Name+"_client.go"); err == nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500424 _=f
425 defer f.Close()
426 if err := t.ExecuteTemplate(f, "client.go", cc); err != nil {
427 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 }
435 if f,err := os.Create(suiteDir+"/clientInit.go"); err == nil {
436 defer f.Close()
437 //if err := t.ExecuteTemplate(os.Stdout, "server.go", *sc); err != nil {}
438 if err := t.ExecuteTemplate(f, "clientInit.go", clients); err != nil {
439 log.Errorf("Unable to execute template for clientInit.go: %v", err)
440 return err
441 }
442 }
443 return nil
444}
445
446func serverExists(srvr string, ts * TestSuite) bool {
sslobodr1d1e50b2019-03-14 09:17:40 -0400447 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(`^([^.]+)\..*$`)
460 for _,v := range tests {
461 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 }
473 if _,ok := rtrn[p]; ok == false {
474 rtrn[p] = struct{}{}
475 }
476 if _,ok := rtrn[e]; ok == false {
477 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)
486 for _,v := range protoImports {
487 //log.Infof("Looking for package %s", v.Package)
488 if _,ok := used[v.Short]; ok == true {
489 rtrn = append(rtrn, ProtoImport {
490 Service: v.Service,
491 Short: v.Short,
492 Package: v.Package,
493 Used: true,
494 })
495 } else {
496 rtrn = append(rtrn, ProtoImport {
497 Service: v.Service,
498 Short: v.Short,
499 Package: v.Package,
500 Used: false,
501 })
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)
513 for _,v := range imports {
514 //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)
522 if _,ok := used[p]; ok == true {
523 rtrn = append(rtrn, Import {
524 Package: v.Package,
525 Used: true,
526 })
527 } else {
528 rtrn = append(rtrn, Import {
529 Package: v.Package,
530 Used: false,
531 })
532 }
533 }
534 //log.Infof("After update %v", rtrn)
535 return rtrn
536}
537
538
sslobodrd6e07e72019-01-31 16:07:20 -0500539func generateTestCases(conf *TestConfig, suiteDir string, ts * TestSuite,
540 t *template.Template) error {
541 var mthdMap map[string]*mthd
sslobodr1d1e50b2019-03-14 09:17:40 -0400542 mthdMap = make(map[string]*mthd)
sslobodrd6e07e72019-01-31 16:07:20 -0500543 // Generate the test cases
544 log.Info("Generating the test cases: runTests.go")
sslobodr1d1e50b2019-03-14 09:17:40 -0400545 tc := &TestList{Funcs:make(map[string]MethodTypes),
546 FileNum:0, HasFuncs: false,
547 Offset:0}
548 for _,v := range ts.Env.Imports {
549 tc.Imports = append(tc.Imports,Import{Package:v, Used:true})
550 }
sslobodrd6e07e72019-01-31 16:07:20 -0500551
552 // Load the proto descriptor file
sslobodrd6e07e72019-01-31 16:07:20 -0500553 for _,v := range ts.Env.ProtoFiles {
554 imp := &ProtoImport{Short:v.Package,
555 Package:v.ImportPath+v.Package,
sslobodr1d1e50b2019-03-14 09:17:40 -0400556 Service:v.Service,
557 Used:true}
sslobodrd6e07e72019-01-31 16:07:20 -0500558 tc.ProtoImports = append(tc.ProtoImports, *imp)
559 // Compile the template from the file
560 log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
561 if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v.Package,
562 v.Service, ts.Env.ProtoSubsts); err != nil {
563 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
564 ts.Env.ProtoDesc, v.Package, v.Service)
565 return err
566 } else {
sslobodr1d1e50b2019-03-14 09:17:40 -0400567 // Add to the known methods
568 for k,v := range mthds {
569 mthdMap[k] = v
570 }
sslobodrd6e07e72019-01-31 16:07:20 -0500571 }
572 }
sslobodr1d1e50b2019-03-14 09:17:40 -0400573 // Since the input file can possibly be a template with loops that
574 // make multiple calls to exactly the same method with potentially
575 // different parameters it is best to try to optimize for that case.
576 // Creating a function for each method used will greatly reduce the
577 // code size and repetition. In the case where a method is used only
578 // once the resulting code will be bigger but this is an acceptable
579 // tradeoff to allow huge suites that call the same method repeatedly
580 // to test for leaks.
581
582 // The go compiler has trouble compiling files that are too large. In order
583 // to mitigate that, It's necessary to create more smaller files than the
584 // single one large one while making sure that the functions for the distinct
585 // methods are only defined once in one of the files.
586
587 // Yet another hiccup with the go compiler, it doesn't like deep function
588 // nesting meaning that each runTests function can't call the next without
589 // eventually blowing the stack check. In order to work around this, the
590 // first runTests function needs to call all the others in sequence to
591 // keep the stack depth constant.
592
593 // Counter limiting the number of tests to output to each file
594 maxCt := MAX_CT
595
596 // Get the list of distinct methods
597 //for _,v := range ts.Tests {
598 // if _,ok := tc.Funcs[v.Send.Method]; ok == false {
599 // tc.Funcs[v.Send.Method] = MethodTypes{ParamType:mthdMap[v.Send.Method].Param,
600 // ReturnType:mthdMap[v.Send.Method].Rtrn}
601 // }
602 //}
603 for i:=1; i<int(math.Ceil(float64(len(ts.Tests))/float64(MAX_CT))); i++ {
604 tc.RunTestsCallList = append(tc.RunTestsCallList, "runTests"+strconv.Itoa(i))
605 }
606
sslobodrd6e07e72019-01-31 16:07:20 -0500607 // Create the test data structure for the template
608 for _,v := range ts.Tests {
609 var test TestCase
610
sslobodr1d1e50b2019-03-14 09:17:40 -0400611 if _,ok := tc.Funcs[v.Send.Method]; ok == false {
612 tc.Funcs[v.Send.Method] = MethodTypes{ParamType:mthdMap[v.Send.Method].Param,
613 ReturnType:mthdMap[v.Send.Method].Rtrn,
614 CodeGenerated:false}
615 tc.HasFuncs = true
616 }
617
sslobodrd6e07e72019-01-31 16:07:20 -0500618 test.Name = v.Name
sslobodr1d1e50b2019-03-14 09:17:40 -0400619 test.InfoOnly = v.InfoOnly
sslobodrd6e07e72019-01-31 16:07:20 -0500620 test.Send.Client = v.Send.Client
621 test.Send.Method = v.Send.Method
622 test.Send.Param = v.Send.Param
623 test.Send.ParamType = mthdMap[test.Send.Method].Param
624 test.Send.Expect = v.Send.Expect
625 test.Send.ExpectType = mthdMap[test.Send.Method].Rtrn
sslobodrd9daabf2019-02-05 13:14:21 -0500626 for _,v1 := range v.Send.MetaData {
627 test.Send.MetaData = append(test.Send.MetaData,v1)
628 }
629 for _,v1 := range v.Send.ExpectMeta {
630 test.Send.ExpectMeta = append(test.Send.ExpectMeta,v1)
631 }
sslobodrd6e07e72019-01-31 16:07:20 -0500632 for _,v1 := range v.Servers {
633 var srvr Server
634 if serverExists(v1.Name, ts) == false {
635 log.Errorf("Server '%s' is not defined!!", v1.Name)
636 return errors.New(fmt.Sprintf("Failed to build test case %s", v.Name))
637 }
638 srvr.Name = v1.Name
639 srvr.Meta = v1.Meta
640 test.Srvr = append(test.Srvr, srvr)
641 }
642 tc.Tests = append(tc.Tests, test)
sslobodr1d1e50b2019-03-14 09:17:40 -0400643 if maxCt--; maxCt == 0 {
644 // Get the list of proto pacakges required for this file
645 pkgs := getPackageList(tc.Tests)
646 // Adjust the proto import data accordingly
647 tc.ProtoImports = fixupProtoImports(tc.ProtoImports, pkgs)
648 tc.Imports = fixupImports(tc.Imports, pkgs)
649 //log.Infof("The packages needed are: %v", pkgs)
650 if f,err := os.Create(suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".go"); err == nil {
651 if err := t.ExecuteTemplate(f, "runTests.go", tc); err != nil {
652 log.Errorf("Unable to execute template for runTests.go: %v", err)
653 }
654 f.Close()
655 } else {
656 log.Errorf("Couldn't create file %s : %v",
657 suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".go", err)
658 }
659 tc.FileNum++
660 //tc.NextFile++
661 maxCt = MAX_CT
662 // Mark the functions as generated.
663 tc.Tests = []TestCase{}
664 for k,v := range tc.Funcs {
665 tc.Funcs[k] = MethodTypes{ParamType:v.ParamType,
666 ReturnType:v.ReturnType,
667 CodeGenerated:true}
668 }
669 tc.HasFuncs = false
670 tc.Offset += MAX_CT
671 //tmp,_ := strconv.Atoi(tc.Offset)
672 //tc.Offset = strconv.Itoa(tmp+500)
673 }
sslobodrd6e07e72019-01-31 16:07:20 -0500674 }
sslobodr1d1e50b2019-03-14 09:17:40 -0400675 //tc.NextFile = 0
676 // Get the list of proto pacakges required for this file
677 pkgs := getPackageList(tc.Tests)
678 // Adjust the proto import data accordingly
679 tc.ProtoImports = fixupProtoImports(tc.ProtoImports, pkgs)
680 tc.Imports = fixupImports(tc.Imports, pkgs)
681 //log.Infof("The packages needed are: %v", pkgs)
682 if f,err := os.Create(suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".go"); err == nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500683 if err := t.ExecuteTemplate(f, "runTests.go", tc); err != nil {
684 log.Errorf("Unable to execute template for runTests.go: %v", err)
685 }
686 f.Close()
687 } else {
sslobodr1d1e50b2019-03-14 09:17:40 -0400688 log.Errorf("Couldn't create file %s : %v",
689 suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".go", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500690 }
sslobodr1d1e50b2019-03-14 09:17:40 -0400691
692 //if f,err := os.Create(suiteDir+"/runTests.go"); err == nil {
693 // if err := t.ExecuteTemplate(f, "runTests.go", tc); err != nil {
694 // log.Errorf("Unable to execute template for runTests.go: %v", err)
695 // }
696 // f.Close()
697 //} else {
698 // log.Errorf("Couldn't create file %s : %v", suiteDir+"/runTests.go", err)
699 //}
sslobodrd6e07e72019-01-31 16:07:20 -0500700 return nil
701}
702
703func generateTestSuites(conf *TestConfig, srcDir string, outDir string) error {
704
705 // Create a directory for the tests
706 if err := os.Mkdir(srcDir, 0777); err != nil {
707 log.Errorf("Unable to create directory 'tests':%v\n", err)
708 return err
709 }
710
711 for k,v := range conf.Suites {
712 var suiteDir string = srcDir+"/"+v
713 log.Debugf("Suite[%d] - %s", k, v)
714 ts := &TestSuite{}
715 ts.loadSuite(v)
716 log.Debugf("Suite %s: %v", v, ts)
sslobodr1d1e50b2019-03-14 09:17:40 -0400717 log.Infof("Processing test suite %s", v)
sslobodrd6e07e72019-01-31 16:07:20 -0500718
719 t := template.Must(template.New("").ParseFiles("../templates/server.go",
720 "../templates/serverInit.go",
721 "../templates/client.go",
722 "../templates/clientInit.go",
723 "../templates/runTests.go",
sslobodr1d1e50b2019-03-14 09:17:40 -0400724 "../templates/stats.go",
sslobodrd6e07e72019-01-31 16:07:20 -0500725 "../templates/main.go"))
726 // Create a directory for he source code for this test suite
727 if err := os.Mkdir(suiteDir, 0777); err != nil {
728 log.Errorf("Unable to create directory '%s':%v\n", v, err)
729 return err
730 }
731 // Generate the server source files
732 if err := generateServers(conf, suiteDir, ts, t); err != nil {
733 log.Errorf("Unable to generate server source files: %v", err)
734 return err
735 }
736 // Generate the client source files
737 if err := generateClients(conf, suiteDir, ts, t); err != nil {
738 log.Errorf("Unable to generate client source files: %v", err)
739 return err
740 }
741 // Generate the test case source file
742 if err := generateTestCases(conf, suiteDir, ts, t); err != nil {
743 log.Errorf("Unable to generate test case source file: %v", err)
744 return err
745 }
746
747 // Finally generate the main file
748 log.Info("Generating main.go")
749 if f,err := os.Create(suiteDir+"/main.go"); err == nil {
750 if err := t.ExecuteTemplate(f, "main.go", ts.Env); err != nil {
751 log.Errorf("Unable to execute template for main.go: %v", err)
752 }
753 f.Close()
754 } else {
755 log.Errorf("Couldn't create file %s : %v", suiteDir+"/main.go", err)
756 }
757
sslobodr1d1e50b2019-03-14 09:17:40 -0400758 log.Infof("Copying over common modules")
759 if f,err := os.Create(suiteDir+"/stats.go"); err == nil {
760 if err := t.ExecuteTemplate(f, "stats.go", ts.Env); err != nil {
761 log.Errorf("Unable to execute template for stats.go: %v", err)
762 }
763 f.Close()
764 } else {
765 log.Errorf("Couldn't create file %s : %v", suiteDir+"/stats.go", err)
766 }
767
768
sslobodrd6e07e72019-01-31 16:07:20 -0500769 log.Infof("Compiling test suite: %s in directory %s", v, suiteDir)
770 if err := os.Chdir(suiteDir); err != nil {
771 log.Errorf("Could not change to directory '%s':%v",suiteDir, err)
772 }
sslobodr13182842019-02-08 14:40:30 -0500773 cmd := exec.Command("go", "build", "-o", outDir+"/"+v+".e")
sslobodrd6e07e72019-01-31 16:07:20 -0500774 cmd.Stdin = os.Stdin
775 cmd.Stdout = os.Stdout
776 cmd.Stderr = os.Stderr
777 if err := cmd.Run(); err != nil {
778 log.Errorf("Error running the compile command:%v", err)
779 }
780 if err := os.Chdir("../../suites"); err != nil {
781 log.Errorf("Could not change to directory '%s':%v","../../suites", err)
782 }
sslobodrd6e07e72019-01-31 16:07:20 -0500783 }
784 return nil
785
786}
787
788func generateTestDriver(conf *TestConfig, srcDir string, outDir string) error {
789 // Generate the main test driver file
790 if err := os.Mkdir(srcDir, 0777); err != nil {
791 log.Errorf("Unable to create directory 'driver':%v\n", err)
792 return err
793 }
sslobodr1d1e50b2019-03-14 09:17:40 -0400794 t := template.Must(template.New("").ParseFiles("../templates/runAll.go",
795 "../templates/stats.go"))
sslobodrd6e07e72019-01-31 16:07:20 -0500796 if f,err := os.Create(srcDir+"/runAll.go"); err == nil {
797 if err := t.ExecuteTemplate(f, "runAll.go", conf.Suites); err != nil {
sslobodr1d1e50b2019-03-14 09:17:40 -0400798 log.Errorf("Unable to execute template for runAll.go: %v", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500799 }
800 f.Close()
801 } else {
802 log.Errorf("Couldn't create file %s : %v", srcDir+"/runAll.go", err)
803 }
sslobodr1d1e50b2019-03-14 09:17:40 -0400804 if f,err := os.Create(srcDir+"/stats.go"); err == nil {
805 if err := t.ExecuteTemplate(f, "stats.go", conf.Suites); err != nil {
806 log.Errorf("Unable to execute template for stats.go: %v", err)
807 }
808 f.Close()
809 } else {
810 log.Errorf("Couldn't create file %s : %v", srcDir+"/stats.go", err)
811 }
sslobodrd6e07e72019-01-31 16:07:20 -0500812
813 // Compile the test driver file
814 log.Info("Compiling the test driver")
815 if err := os.Chdir("../tests/driver"); err != nil {
816 log.Errorf("Could not change to directory 'driver':%v", err)
817 }
818 cmd := exec.Command("go", "build", "-o", outDir+"/runAll")
819 cmd.Stdin = os.Stdin
820 cmd.Stdout = os.Stdout
821 cmd.Stderr = os.Stderr
822 if err := cmd.Run(); err != nil {
823 log.Errorf("Error running the compile command:%v", err)
824 }
825 if err := os.Chdir("../../suites"); err != nil {
826 log.Errorf("Could not change to directory 'driver':%v",err)
827 }
828
829 return nil
830}
831
832
833func main() {
834
835 conf,err := parseCmd()
836 if err != nil {
837 fmt.Printf("Error: %v\n", err)
838 return
839 }
840
841 // Setup logging
842 if _, err := log.SetDefaultLogger(log.JSON, *conf.logLevel, nil); err != nil {
843 log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
844 }
845
846 defer log.CleanUp()
847
848 // Parse the config file
849 if err := conf.loadConfig(); err != nil {
850 log.Error(err)
851 }
852
853 generateTestSuites(conf, "../tests", "/src/tests")
854 generateTestDriver(conf, "../tests/driver", "/src/tests")
855 return
856}
857