blob: f3ba2aa1f8a1ac6156281bdc9cf787a70abf15ff [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 (
sslobodrd6e07e72019-01-31 16:07:20 -050021 "encoding/json"
David Bainbridgeec4ff512019-04-19 18:59:40 +000022 "errors"
23 "flag"
24 "fmt"
sslobodrd6e07e72019-01-31 16:07:20 -050025 "github.com/golang/protobuf/proto"
sslobodrd6e07e72019-01-31 16:07:20 -050026 pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
David Bainbridgeec4ff512019-04-19 18:59:40 +000027 "github.com/opencord/voltha-go/common/log"
28 "io/ioutil"
29 "math"
30 "os"
31 "os/exec"
32 "path"
33 "regexp"
34 "strconv"
35 "text/template"
sslobodrd6e07e72019-01-31 16:07:20 -050036)
37
sslobodr1d1e50b2019-03-14 09:17:40 -040038const MAX_CT = 500
39
sslobodrd6e07e72019-01-31 16:07:20 -050040type TestConfig struct {
41 configFile *string
David Bainbridgeec4ff512019-04-19 18:59:40 +000042 logLevel *int
43 grpcLog *bool
44 Suites []string `json:"suites"`
sslobodrd6e07e72019-01-31 16:07:20 -050045}
46
sslobodrd6e07e72019-01-31 16:07:20 -050047type Connection struct {
48 Name string `json:"name"`
49 Port string `json:"port"`
50}
51
sslobodr1d1e50b2019-03-14 09:17:40 -040052type EndpointList struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +000053 Imports []string `json:"imports"`
sslobodr1d1e50b2019-03-14 09:17:40 -040054 Endpoints []Connection `json:"endPoints"`
55}
56
sslobodrd6e07e72019-01-31 16:07:20 -050057type ProtoFile struct {
58 ImportPath string `json:"importPath"`
David Bainbridgeec4ff512019-04-19 18:59:40 +000059 Service string `json:"service"`
60 Package string `json:"package"`
sslobodrd6e07e72019-01-31 16:07:20 -050061}
62
63type ProtoSubst struct {
64 From string `json:"from"`
David Bainbridgeec4ff512019-04-19 18:59:40 +000065 To string `json:"to"`
sslobodrd6e07e72019-01-31 16:07:20 -050066}
67
68type Environment struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +000069 Command string `json:"cmdLine"`
70 ProtoFiles []ProtoFile `json:"protoFiles"`
71 ProtoDesc string `json:"protoDesc"`
72 Clients EndpointList `json:"clients"`
73 Servers EndpointList `json:"servers"`
74 Imports []string `json:"imports"`
sslobodrd6e07e72019-01-31 16:07:20 -050075 ProtoSubsts []ProtoSubst `json:"protoSubst"`
76}
77
78type Rpc struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +000079 Client string `json:"client"`
80 Method string `json:"method"`
81 Param string `json:"param"`
82 Expect string `json:"expect"`
83 MetaData []MetaD `json:"meta"`
sslobodrd9daabf2019-02-05 13:14:21 -050084 ExpectMeta []MetaD `json:"expectMeta"`
sslobodrd6e07e72019-01-31 16:07:20 -050085}
86
87type MetaD struct {
88 Key string `json:"key"`
89 Val string `json:"value"`
90}
91
92type Server struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +000093 Name string `json:"name"`
sslobodrd6e07e72019-01-31 16:07:20 -050094 Meta []MetaD `json:"meta"`
95}
96
97type Test struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +000098 Name string `json:"name"`
99 InfoOnly bool `json:"infoOnly"`
100 Send Rpc `json:"send"`
101 Servers []Server `json:"servers"`
sslobodrd6e07e72019-01-31 16:07:20 -0500102}
103
104type TestSuite struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000105 Env Environment `json:"environment"`
106 Tests []Test `json:"tests"`
sslobodrd6e07e72019-01-31 16:07:20 -0500107}
108
109type ProtoImport struct {
110 Service string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000111 Short string
sslobodrd6e07e72019-01-31 16:07:20 -0500112 Package string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000113 Used bool
sslobodrd6e07e72019-01-31 16:07:20 -0500114}
115
116type SendItem struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000117 Client string
118 Method string
119 Param string
120 ParamType string
121 Expect string
sslobodrd6e07e72019-01-31 16:07:20 -0500122 ExpectType string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000123 MetaData []MetaD
sslobodrd9daabf2019-02-05 13:14:21 -0500124 ExpectMeta []MetaD
sslobodrd6e07e72019-01-31 16:07:20 -0500125}
126
127type TestCase struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000128 Name string
sslobodr1d1e50b2019-03-14 09:17:40 -0400129 InfoOnly bool
David Bainbridgeec4ff512019-04-19 18:59:40 +0000130 Send SendItem
131 Srvr []Server
sslobodrd6e07e72019-01-31 16:07:20 -0500132}
133
sslobodr1d1e50b2019-03-14 09:17:40 -0400134type MethodTypes struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000135 ParamType string
136 ReturnType string
sslobodr1d1e50b2019-03-14 09:17:40 -0400137 CodeGenerated bool
138}
139
140type Import struct {
141 Package string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000142 Used bool
sslobodr1d1e50b2019-03-14 09:17:40 -0400143}
144
sslobodrd6e07e72019-01-31 16:07:20 -0500145type TestList struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000146 ProtoImports []ProtoImport
147 Imports []Import
148 Tests []TestCase
149 Funcs map[string]MethodTypes
sslobodr1d1e50b2019-03-14 09:17:40 -0400150 RunTestsCallList []string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000151 FileNum int
sslobodr1d1e50b2019-03-14 09:17:40 -0400152 //NextFile int
153 HasFuncs bool
David Bainbridgeec4ff512019-04-19 18:59:40 +0000154 Offset int
sslobodrd6e07e72019-01-31 16:07:20 -0500155}
156
157type ClientConfig struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000158 Ct int
159 Name string
160 Port string
161 Imports []string
162 Methods map[string]*mthd
sslobodrd6e07e72019-01-31 16:07:20 -0500163 ProtoImports []ProtoImport
164}
165
166type ServerConfig struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000167 Ct int
168 Name string
169 Port string
170 Imports []string
171 Methods map[string]*mthd
sslobodrd6e07e72019-01-31 16:07:20 -0500172 ProtoImports []ProtoImport
173}
174
175type mthd struct {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000176 Pkg string
177 Svc string
178 Name string
sslobodrd6e07e72019-01-31 16:07:20 -0500179 Param string
David Bainbridgeec4ff512019-04-19 18:59:40 +0000180 Rtrn string
181 Ss bool // Server streaming
182 Cs bool // Clieent streaming
sslobodrd6e07e72019-01-31 16:07:20 -0500183}
184
sslobodrd6e07e72019-01-31 16:07:20 -0500185func parseCmd() (*TestConfig, error) {
186 config := &TestConfig{}
David Bainbridgeec4ff512019-04-19 18:59:40 +0000187 cmdParse := flag.NewFlagSet(path.Base(os.Args[0]), flag.ContinueOnError)
sslobodrd6e07e72019-01-31 16:07:20 -0500188 config.configFile = cmdParse.String("config", "suites.json", "The configuration file for the affinity router tester")
189 config.logLevel = cmdParse.Int("logLevel", 0, "The log level for the affinity router tester")
190 config.grpcLog = cmdParse.Bool("grpclog", false, "Enable GRPC logging")
191
David Bainbridgeec4ff512019-04-19 18:59:40 +0000192 err := cmdParse.Parse(os.Args[1:])
sslobodrd6e07e72019-01-31 16:07:20 -0500193 if err != nil {
194 //return err
David Bainbridgeec4ff512019-04-19 18:59:40 +0000195 return nil, errors.New("Error parsing the command line")
sslobodrd6e07e72019-01-31 16:07:20 -0500196 }
197 return config, nil
198}
199
David Bainbridgeec4ff512019-04-19 18:59:40 +0000200func (conf *TestConfig) loadConfig() error {
sslobodrd6e07e72019-01-31 16:07:20 -0500201
David Bainbridgeec4ff512019-04-19 18:59:40 +0000202 configF, err := os.Open(*conf.configFile)
sslobodrd6e07e72019-01-31 16:07:20 -0500203 log.Info("Loading configuration from: ", *conf.configFile)
204 if err != nil {
205 log.Error(err)
206 return err
207 }
208
209 defer configF.Close()
210
211 if configBytes, err := ioutil.ReadAll(configF); err != nil {
212 log.Error(err)
213 return err
214 } else if err := json.Unmarshal(configBytes, conf); err != nil {
215 log.Error(err)
216 return err
217 }
218
219 return nil
220}
221
David Bainbridgeec4ff512019-04-19 18:59:40 +0000222func (suite *TestSuite) loadSuite(suiteN string) error {
sslobodr13182842019-02-08 14:40:30 -0500223 // Check if there's a corresponding go file for the suite.
224 // If it is present then the json test suite file is a
225 // template. Compile the go file and run it to process
226 // the template.
David Bainbridge31221742019-04-19 19:49:51 +0000227 if _, err := os.Stat(suiteN + "/" + suiteN + ".go"); err == nil {
sslobodr13182842019-02-08 14:40:30 -0500228 // Compile and run the the go file
229 log.Infof("Suite '%s' is a template, compiling '%s'", suiteN, suiteN+".go")
230 cmd := exec.Command("go", "build", "-o", suiteN+".te", suiteN+".go")
231 cmd.Stdin = os.Stdin
232 cmd.Stdout = os.Stdout
233 cmd.Stderr = os.Stderr
David Bainbridge31221742019-04-19 19:49:51 +0000234 cmd.Dir = suiteN
sslobodr13182842019-02-08 14:40:30 -0500235 if err := cmd.Run(); err != nil {
236 log.Errorf("Error running the compile command:%v", err)
237 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000238 cmd = exec.Command("./" + suiteN + ".te")
sslobodr13182842019-02-08 14:40:30 -0500239 cmd.Stdin = os.Stdin
240 cmd.Stdout = os.Stdout
241 cmd.Stderr = os.Stderr
David Bainbridge31221742019-04-19 19:49:51 +0000242 cmd.Dir = suiteN
sslobodr13182842019-02-08 14:40:30 -0500243 if err := cmd.Run(); err != nil {
244 log.Errorf("Error running the %s command:%v", suiteN+".te", err)
245 }
246 }
David Bainbridge31221742019-04-19 19:49:51 +0000247 suiteF, err := os.Open(suiteN + "/" + suiteN + ".json")
248 log.Infof("Loading test suite from: %s", suiteN+"/"+suiteN+".json")
sslobodrd6e07e72019-01-31 16:07:20 -0500249 if err != nil {
250 log.Error(err)
251 return err
252 }
253
254 defer suiteF.Close()
255
256 if suiteBytes, err := ioutil.ReadAll(suiteF); err != nil {
257 log.Error(err)
258 return err
259 } else if err := json.Unmarshal(suiteBytes, suite); err != nil {
260 log.Error(err)
261 return err
262 }
263
264 return nil
265}
266
267func loadProtoMap(fileName string, pkg string, svc string, substs []ProtoSubst) (map[string]*mthd, error) {
268 var mthds map[string]*mthd = make(map[string]*mthd)
269 var rtrn_err bool
270
271 // Load the protobuf descriptor file
272 protoDescriptor := &pb.FileDescriptorSet{}
David Bainbridgeec4ff512019-04-19 18:59:40 +0000273 fb, err := ioutil.ReadFile(fileName)
sslobodrd6e07e72019-01-31 16:07:20 -0500274 if err != nil {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000275 log.Errorf("Could not open proto file '%s'", fileName)
sslobodrd6e07e72019-01-31 16:07:20 -0500276 rtrn_err = true
277 }
278 err = proto.Unmarshal(fb, protoDescriptor)
279 if err != nil {
280 log.Errorf("Could not unmarshal %s, %v", "proto.pb", err)
281 rtrn_err = true
282 }
283
David Bainbridgeec4ff512019-04-19 18:59:40 +0000284 var substM map[string]string = make(map[string]string)
sslobodrd6e07e72019-01-31 16:07:20 -0500285 // Create a substitution map
286 log.Debugf("Creating import map")
David Bainbridgeec4ff512019-04-19 18:59:40 +0000287 for _, v := range substs {
sslobodrd6e07e72019-01-31 16:07:20 -0500288 log.Debugf("Mapping from %s to %s", v.From, v.To)
289 substM[v.From] = v.To
290 }
291
sslobodrd6e07e72019-01-31 16:07:20 -0500292 // Build the a map containing the method as the key
293 // and the paramter and return types as the fields
David Bainbridgeec4ff512019-04-19 18:59:40 +0000294 for _, f := range protoDescriptor.File {
sslobodrd6e07e72019-01-31 16:07:20 -0500295 if *f.Package == pkg {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000296 for _, s := range f.Service {
sslobodrd6e07e72019-01-31 16:07:20 -0500297 if *s.Name == svc {
298 log.Debugf("Loading package data '%s' for service '%s'", *f.Package, *s.Name)
299 // Now create a map keyed by method name with the value being the
300 // field number of the route selector.
301 //var ok bool
David Bainbridgeec4ff512019-04-19 18:59:40 +0000302 for _, m := range s.Method {
sslobodrd6e07e72019-01-31 16:07:20 -0500303 // Find the input type in the messages and extract the
304 // field number and save it for future reference.
David Bainbridgeec4ff512019-04-19 18:59:40 +0000305 log.Debugf("Processing method (%s(%s) (%s){}", *m.Name, (*m.InputType)[1:], (*m.OutputType)[1:])
306 mthds[*m.Name] = &mthd{Pkg: pkg, Svc: svc, Name: *m.Name, Param: (*m.InputType)[1:],
307 Rtrn: (*m.OutputType)[1:]}
sslobodrd6e07e72019-01-31 16:07:20 -0500308 if m.ClientStreaming != nil && *m.ClientStreaming == true {
309 log.Debugf("Method %s is a client streaming method", *m.Name)
310 mthds[*m.Name].Cs = true
311 }
312 if m.ServerStreaming != nil && *m.ServerStreaming == true {
313 log.Debugf("Method %s is a server streaming method", *m.Name)
314 mthds[*m.Name].Ss = true
315 }
316 // Perform the required substitutions
David Bainbridgeec4ff512019-04-19 18:59:40 +0000317 if _, ok := substM[mthds[*m.Name].Param]; ok == true {
sslobodrd6e07e72019-01-31 16:07:20 -0500318 mthds[*m.Name].Param = substM[mthds[*m.Name].Param]
319 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000320 if _, ok := substM[mthds[*m.Name].Rtrn]; ok == true {
sslobodrd6e07e72019-01-31 16:07:20 -0500321 mthds[*m.Name].Rtrn = substM[mthds[*m.Name].Rtrn]
322 }
323 }
324 }
325 }
326 }
327 }
328 if rtrn_err {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000329 return nil, errors.New(fmt.Sprintf("Failed to load protobuf descriptor file '%s'", fileName))
sslobodrd6e07e72019-01-31 16:07:20 -0500330 }
331 return mthds, nil
332}
333
334// Server source code generation
David Bainbridgeec4ff512019-04-19 18:59:40 +0000335func generateServers(conf *TestConfig, suiteDir string, ts *TestSuite,
336 t *template.Template) error {
sslobodrd6e07e72019-01-31 16:07:20 -0500337 var servers []ServerConfig
338
David Bainbridgeec4ff512019-04-19 18:59:40 +0000339 for k, v := range ts.Env.Servers.Endpoints {
sslobodrd6e07e72019-01-31 16:07:20 -0500340 log.Infof("Generating the code for server[%d]: %s", k, v.Name)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000341 sc := &ServerConfig{Name: v.Name, Port: v.Port, Ct: k, Imports: ts.Env.Servers.Imports,
342 Methods: make(map[string]*mthd)}
343 for k1, v1 := range ts.Env.ProtoFiles {
344 imp := &ProtoImport{Short: "pb" + strconv.Itoa(k1),
345 Package: v1.ImportPath + v1.Package,
346 Service: v1.Service,
347 Used: true}
348 imp = &ProtoImport{Short: v1.Package,
349 Package: v1.ImportPath + v1.Package,
350 Service: v1.Service,
351 Used: true}
sslobodrd6e07e72019-01-31 16:07:20 -0500352 sc.ProtoImports = append(sc.ProtoImports, *imp)
353 // Compile the template from the file
354 log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
355 if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v1.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000356 v1.Service, ts.Env.ProtoSubsts); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500357 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
David Bainbridgeec4ff512019-04-19 18:59:40 +0000358 ts.Env.ProtoDesc, v1.Package, v1.Service)
sslobodrd6e07e72019-01-31 16:07:20 -0500359 return err
360 } else {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000361 //Generate all the function calls required by the
362 for k, v := range mthds {
sslobodr1d1e50b2019-03-14 09:17:40 -0400363 sc.Methods[k] = v
364 }
365 //sc.Methods = mthds
sslobodrd6e07e72019-01-31 16:07:20 -0500366 }
367 }
368 log.Debugf("Server: %v", *sc)
369 // Save this server for the next steop
370 servers = append(servers, *sc)
371 // Open an output file to put the output in.
David Bainbridgeec4ff512019-04-19 18:59:40 +0000372 if f, err := os.Create(suiteDir + "/" + v.Name + ".go"); err == nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500373 defer f.Close()
David Bainbridgeec4ff512019-04-19 18:59:40 +0000374 //if err := t.ExecuteTemplate(os.Stdout, "server.go.tmpl", *sc); err != nil {}
375 if err := t.ExecuteTemplate(f, "server.go.tmpl", *sc); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500376 log.Errorf("Unable to execute template for server[%d]: %s: %v", k, v.Name, err)
377 return err
378 }
379 }
380 }
381 // Generate the server initialization code
David Bainbridgeec4ff512019-04-19 18:59:40 +0000382 if f, err := os.Create(suiteDir + "/serverInit.go"); err == nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500383 defer f.Close()
David Bainbridgeec4ff512019-04-19 18:59:40 +0000384 //if err := t.ExecuteTemplate(os.Stdout, "server.go.tmpl", *sc); err != nil {}
385 if err := t.ExecuteTemplate(f, "serverInit.go.tmpl", servers); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500386 log.Errorf("Unable to execute template for serverInit.go: %v", err)
387 return err
388 }
389 }
390
391 return nil
392}
393
David Bainbridgeec4ff512019-04-19 18:59:40 +0000394func generateClients(conf *TestConfig, suiteDir string, ts *TestSuite,
395 t *template.Template) error {
sslobodrd6e07e72019-01-31 16:07:20 -0500396 var clients []ClientConfig
David Bainbridgeec4ff512019-04-19 18:59:40 +0000397 for k, v := range ts.Env.Clients.Endpoints {
sslobodrd6e07e72019-01-31 16:07:20 -0500398 log.Infof("Generating the code for client[%d]: %s", k, v.Name)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000399 cc := &ClientConfig{Name: v.Name, Port: v.Port, Ct: k, Imports: ts.Env.Clients.Imports,
400 Methods: make(map[string]*mthd)}
401 for _, v1 := range ts.Env.ProtoFiles {
402 imp := &ProtoImport{Short: v1.Package,
403 Package: v1.ImportPath + v1.Package,
404 Service: v1.Service}
sslobodrd6e07e72019-01-31 16:07:20 -0500405 cc.ProtoImports = append(cc.ProtoImports, *imp)
406 // Compile the template from the file
407 log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
408 if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v1.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000409 v1.Service, ts.Env.ProtoSubsts); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500410 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
David Bainbridgeec4ff512019-04-19 18:59:40 +0000411 ts.Env.ProtoDesc, v1.Package, v1.Service)
sslobodrd6e07e72019-01-31 16:07:20 -0500412 return err
413 } else {
sslobodr1d1e50b2019-03-14 09:17:40 -0400414 // Add to the known methods
David Bainbridgeec4ff512019-04-19 18:59:40 +0000415 for k, v := range mthds {
sslobodr1d1e50b2019-03-14 09:17:40 -0400416 cc.Methods[k] = v
417 }
sslobodrd6e07e72019-01-31 16:07:20 -0500418 }
419 }
420 clients = append(clients, *cc)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000421 if f, err := os.Create(suiteDir + "/" + v.Name + "_client.go"); err == nil {
422 _ = f
sslobodrd6e07e72019-01-31 16:07:20 -0500423 defer f.Close()
David Bainbridgeec4ff512019-04-19 18:59:40 +0000424 if err := t.ExecuteTemplate(f, "client.go.tmpl", cc); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500425 log.Errorf("Unable to execute template for client.go: %v", err)
426 return err
427 }
428 } else {
sslobodr1d1e50b2019-03-14 09:17:40 -0400429 log.Errorf("Couldn't create file %s : %v", suiteDir+"/"+v.Name+"_client.go", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500430 return err
431 }
432 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000433 if f, err := os.Create(suiteDir + "/clientInit.go"); err == nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500434 defer f.Close()
David Bainbridgeec4ff512019-04-19 18:59:40 +0000435 //if err := t.ExecuteTemplate(os.Stdout, "server.go.tmpl", *sc); err != nil {}
436 if err := t.ExecuteTemplate(f, "clientInit.go.tmpl", clients); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500437 log.Errorf("Unable to execute template for clientInit.go: %v", err)
438 return err
439 }
440 }
441 return nil
442}
443
David Bainbridgeec4ff512019-04-19 18:59:40 +0000444func serverExists(srvr string, ts *TestSuite) bool {
445 for _, v := range ts.Env.Servers.Endpoints {
sslobodrd6e07e72019-01-31 16:07:20 -0500446 if v.Name == srvr {
447 return true
448 }
449 }
450 return false
451}
sslobodr1d1e50b2019-03-14 09:17:40 -0400452
453func getPackageList(tests []TestCase) map[string]struct{} {
454 var rtrn map[string]struct{} = make(map[string]struct{})
455 var p string
456 var e string
457 r := regexp.MustCompile(`^([^.]+)\..*$`)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000458 for _, v := range tests {
sslobodr1d1e50b2019-03-14 09:17:40 -0400459 pa := r.FindStringSubmatch(v.Send.ParamType)
460 if len(pa) == 2 {
461 p = pa[1]
462 } else {
463 log.Errorf("Internal error regexp returned %v", pa)
464 }
465 ea := r.FindStringSubmatch(v.Send.ExpectType)
466 if len(ea) == 2 {
467 e = ea[1]
468 } else {
469 log.Errorf("Internal error regexp returned %v", pa)
470 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000471 if _, ok := rtrn[p]; ok == false {
sslobodr1d1e50b2019-03-14 09:17:40 -0400472 rtrn[p] = struct{}{}
473 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000474 if _, ok := rtrn[e]; ok == false {
sslobodr1d1e50b2019-03-14 09:17:40 -0400475 rtrn[e] = struct{}{}
476 }
477 }
478 return rtrn
479}
480
481func fixupProtoImports(protoImports []ProtoImport, used map[string]struct{}) []ProtoImport {
482 var rtrn []ProtoImport
483 //log.Infof("Updating imports %v, using %v", protoImports, used)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000484 for _, v := range protoImports {
sslobodr1d1e50b2019-03-14 09:17:40 -0400485 //log.Infof("Looking for package %s", v.Package)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000486 if _, ok := used[v.Short]; ok == true {
487 rtrn = append(rtrn, ProtoImport{
sslobodr1d1e50b2019-03-14 09:17:40 -0400488 Service: v.Service,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000489 Short: v.Short,
sslobodr1d1e50b2019-03-14 09:17:40 -0400490 Package: v.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000491 Used: true,
sslobodr1d1e50b2019-03-14 09:17:40 -0400492 })
493 } else {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000494 rtrn = append(rtrn, ProtoImport{
sslobodr1d1e50b2019-03-14 09:17:40 -0400495 Service: v.Service,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000496 Short: v.Short,
sslobodr1d1e50b2019-03-14 09:17:40 -0400497 Package: v.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000498 Used: false,
sslobodr1d1e50b2019-03-14 09:17:40 -0400499 })
500 }
501 }
502 //log.Infof("After update %v", rtrn)
503 return rtrn
504}
505
506func fixupImports(imports []Import, used map[string]struct{}) []Import {
507 var rtrn []Import
508 var p string
509 re := regexp.MustCompile(`^.*/([^/]+)$`)
510 //log.Infof("Updating imports %v, using %v", protoImports, used)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000511 for _, v := range imports {
sslobodr1d1e50b2019-03-14 09:17:40 -0400512 //log.Infof("Looking for match in %s", v.Package)
513 pa := re.FindStringSubmatch(v.Package)
514 if len(pa) == 2 {
515 p = pa[1]
516 } else {
517 log.Errorf("Substring match failed, regexp returned %v", pa)
518 }
519 //log.Infof("Looking for package %s", v.Package)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000520 if _, ok := used[p]; ok == true {
521 rtrn = append(rtrn, Import{
sslobodr1d1e50b2019-03-14 09:17:40 -0400522 Package: v.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000523 Used: true,
sslobodr1d1e50b2019-03-14 09:17:40 -0400524 })
525 } else {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000526 rtrn = append(rtrn, Import{
sslobodr1d1e50b2019-03-14 09:17:40 -0400527 Package: v.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000528 Used: false,
sslobodr1d1e50b2019-03-14 09:17:40 -0400529 })
530 }
531 }
532 //log.Infof("After update %v", rtrn)
533 return rtrn
534}
535
David Bainbridgeec4ff512019-04-19 18:59:40 +0000536func generateTestCases(conf *TestConfig, suiteDir string, ts *TestSuite,
537 t *template.Template) error {
sslobodrd6e07e72019-01-31 16:07:20 -0500538 var mthdMap map[string]*mthd
sslobodr1d1e50b2019-03-14 09:17:40 -0400539 mthdMap = make(map[string]*mthd)
sslobodrd6e07e72019-01-31 16:07:20 -0500540 // Generate the test cases
541 log.Info("Generating the test cases: runTests.go")
David Bainbridgeec4ff512019-04-19 18:59:40 +0000542 tc := &TestList{Funcs: make(map[string]MethodTypes),
543 FileNum: 0, HasFuncs: false,
544 Offset: 0}
545 for _, v := range ts.Env.Imports {
546 tc.Imports = append(tc.Imports, Import{Package: v, Used: true})
sslobodr1d1e50b2019-03-14 09:17:40 -0400547 }
sslobodrd6e07e72019-01-31 16:07:20 -0500548
549 // Load the proto descriptor file
David Bainbridgeec4ff512019-04-19 18:59:40 +0000550 for _, v := range ts.Env.ProtoFiles {
551 imp := &ProtoImport{Short: v.Package,
552 Package: v.ImportPath + v.Package,
553 Service: v.Service,
554 Used: true}
sslobodrd6e07e72019-01-31 16:07:20 -0500555 tc.ProtoImports = append(tc.ProtoImports, *imp)
556 // Compile the template from the file
557 log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
558 if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v.Package,
David Bainbridgeec4ff512019-04-19 18:59:40 +0000559 v.Service, ts.Env.ProtoSubsts); err != nil {
sslobodrd6e07e72019-01-31 16:07:20 -0500560 log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
David Bainbridgeec4ff512019-04-19 18:59:40 +0000561 ts.Env.ProtoDesc, v.Package, v.Service)
sslobodrd6e07e72019-01-31 16:07:20 -0500562 return err
563 } else {
sslobodr1d1e50b2019-03-14 09:17:40 -0400564 // Add to the known methods
David Bainbridgeec4ff512019-04-19 18:59:40 +0000565 for k, v := range mthds {
sslobodr1d1e50b2019-03-14 09:17:40 -0400566 mthdMap[k] = v
567 }
sslobodrd6e07e72019-01-31 16:07:20 -0500568 }
569 }
sslobodr1d1e50b2019-03-14 09:17:40 -0400570 // Since the input file can possibly be a template with loops that
571 // make multiple calls to exactly the same method with potentially
572 // different parameters it is best to try to optimize for that case.
573 // Creating a function for each method used will greatly reduce the
574 // code size and repetition. In the case where a method is used only
575 // once the resulting code will be bigger but this is an acceptable
576 // tradeoff to allow huge suites that call the same method repeatedly
577 // to test for leaks.
578
579 // The go compiler has trouble compiling files that are too large. In order
580 // to mitigate that, It's necessary to create more smaller files than the
581 // single one large one while making sure that the functions for the distinct
582 // methods are only defined once in one of the files.
583
584 // Yet another hiccup with the go compiler, it doesn't like deep function
585 // nesting meaning that each runTests function can't call the next without
586 // eventually blowing the stack check. In order to work around this, the
587 // first runTests function needs to call all the others in sequence to
588 // keep the stack depth constant.
589
590 // Counter limiting the number of tests to output to each file
591 maxCt := MAX_CT
592
593 // Get the list of distinct methods
594 //for _,v := range ts.Tests {
595 // if _,ok := tc.Funcs[v.Send.Method]; ok == false {
596 // tc.Funcs[v.Send.Method] = MethodTypes{ParamType:mthdMap[v.Send.Method].Param,
597 // ReturnType:mthdMap[v.Send.Method].Rtrn}
598 // }
599 //}
David Bainbridgeec4ff512019-04-19 18:59:40 +0000600 for i := 1; i < int(math.Ceil(float64(len(ts.Tests))/float64(MAX_CT))); i++ {
sslobodr1d1e50b2019-03-14 09:17:40 -0400601 tc.RunTestsCallList = append(tc.RunTestsCallList, "runTests"+strconv.Itoa(i))
602 }
603
sslobodrd6e07e72019-01-31 16:07:20 -0500604 // Create the test data structure for the template
David Bainbridgeec4ff512019-04-19 18:59:40 +0000605 for _, v := range ts.Tests {
sslobodrd6e07e72019-01-31 16:07:20 -0500606 var test TestCase
607
David Bainbridgeec4ff512019-04-19 18:59:40 +0000608 if _, ok := tc.Funcs[v.Send.Method]; ok == false {
609 tc.Funcs[v.Send.Method] = MethodTypes{ParamType: mthdMap[v.Send.Method].Param,
610 ReturnType: mthdMap[v.Send.Method].Rtrn,
611 CodeGenerated: false}
sslobodr1d1e50b2019-03-14 09:17:40 -0400612 tc.HasFuncs = true
613 }
614
sslobodrd6e07e72019-01-31 16:07:20 -0500615 test.Name = v.Name
sslobodr1d1e50b2019-03-14 09:17:40 -0400616 test.InfoOnly = v.InfoOnly
sslobodrd6e07e72019-01-31 16:07:20 -0500617 test.Send.Client = v.Send.Client
618 test.Send.Method = v.Send.Method
619 test.Send.Param = v.Send.Param
620 test.Send.ParamType = mthdMap[test.Send.Method].Param
621 test.Send.Expect = v.Send.Expect
622 test.Send.ExpectType = mthdMap[test.Send.Method].Rtrn
David Bainbridgeec4ff512019-04-19 18:59:40 +0000623 for _, v1 := range v.Send.MetaData {
624 test.Send.MetaData = append(test.Send.MetaData, v1)
sslobodrd9daabf2019-02-05 13:14:21 -0500625 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000626 for _, v1 := range v.Send.ExpectMeta {
627 test.Send.ExpectMeta = append(test.Send.ExpectMeta, v1)
sslobodrd9daabf2019-02-05 13:14:21 -0500628 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000629 for _, v1 := range v.Servers {
sslobodrd6e07e72019-01-31 16:07:20 -0500630 var srvr Server
631 if serverExists(v1.Name, ts) == false {
632 log.Errorf("Server '%s' is not defined!!", v1.Name)
633 return errors.New(fmt.Sprintf("Failed to build test case %s", v.Name))
634 }
635 srvr.Name = v1.Name
636 srvr.Meta = v1.Meta
637 test.Srvr = append(test.Srvr, srvr)
638 }
639 tc.Tests = append(tc.Tests, test)
sslobodr1d1e50b2019-03-14 09:17:40 -0400640 if maxCt--; maxCt == 0 {
641 // Get the list of proto pacakges required for this file
642 pkgs := getPackageList(tc.Tests)
643 // Adjust the proto import data accordingly
644 tc.ProtoImports = fixupProtoImports(tc.ProtoImports, pkgs)
645 tc.Imports = fixupImports(tc.Imports, pkgs)
646 //log.Infof("The packages needed are: %v", pkgs)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000647 if f, err := os.Create(suiteDir + "/runTests" + strconv.Itoa(tc.FileNum) + ".go"); err == nil {
648 if err := t.ExecuteTemplate(f, "runTests.go.tmpl", tc); err != nil {
649 log.Errorf("Unable to execute template for runTests.go: %v", err)
sslobodr1d1e50b2019-03-14 09:17:40 -0400650 }
651 f.Close()
652 } else {
653 log.Errorf("Couldn't create file %s : %v",
David Bainbridgeec4ff512019-04-19 18:59:40 +0000654 suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".go", err)
sslobodr1d1e50b2019-03-14 09:17:40 -0400655 }
656 tc.FileNum++
657 //tc.NextFile++
658 maxCt = MAX_CT
659 // Mark the functions as generated.
660 tc.Tests = []TestCase{}
David Bainbridgeec4ff512019-04-19 18:59:40 +0000661 for k, v := range tc.Funcs {
662 tc.Funcs[k] = MethodTypes{ParamType: v.ParamType,
663 ReturnType: v.ReturnType,
664 CodeGenerated: true}
sslobodr1d1e50b2019-03-14 09:17:40 -0400665 }
666 tc.HasFuncs = false
667 tc.Offset += MAX_CT
668 //tmp,_ := strconv.Atoi(tc.Offset)
669 //tc.Offset = strconv.Itoa(tmp+500)
670 }
sslobodrd6e07e72019-01-31 16:07:20 -0500671 }
sslobodr1d1e50b2019-03-14 09:17:40 -0400672 //tc.NextFile = 0
673 // Get the list of proto pacakges required for this file
674 pkgs := getPackageList(tc.Tests)
675 // Adjust the proto import data accordingly
676 tc.ProtoImports = fixupProtoImports(tc.ProtoImports, pkgs)
677 tc.Imports = fixupImports(tc.Imports, pkgs)
678 //log.Infof("The packages needed are: %v", pkgs)
David Bainbridgeec4ff512019-04-19 18:59:40 +0000679 if f, err := os.Create(suiteDir + "/runTests" + strconv.Itoa(tc.FileNum) + ".go"); err == nil {
680 if err := t.ExecuteTemplate(f, "runTests.go.tmpl", tc); err != nil {
681 log.Errorf("Unable to execute template for runTests.go: %v", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500682 }
683 f.Close()
684 } else {
sslobodr1d1e50b2019-03-14 09:17:40 -0400685 log.Errorf("Couldn't create file %s : %v",
David Bainbridgeec4ff512019-04-19 18:59:40 +0000686 suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".go", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500687 }
sslobodr1d1e50b2019-03-14 09:17:40 -0400688
689 //if f,err := os.Create(suiteDir+"/runTests.go"); err == nil {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000690 // if err := t.ExecuteTemplate(f, "runTests.go.tmpl", tc); err != nil {
sslobodr1d1e50b2019-03-14 09:17:40 -0400691 // log.Errorf("Unable to execute template for runTests.go: %v", err)
692 // }
693 // f.Close()
694 //} else {
695 // log.Errorf("Couldn't create file %s : %v", suiteDir+"/runTests.go", err)
696 //}
sslobodrd6e07e72019-01-31 16:07:20 -0500697 return nil
698}
699
700func generateTestSuites(conf *TestConfig, srcDir string, outDir string) error {
701
702 // Create a directory for the tests
703 if err := os.Mkdir(srcDir, 0777); err != nil {
704 log.Errorf("Unable to create directory 'tests':%v\n", err)
705 return err
706 }
707
David Bainbridgeec4ff512019-04-19 18:59:40 +0000708 for k, v := range conf.Suites {
709 var suiteDir string = srcDir + "/" + v
sslobodrd6e07e72019-01-31 16:07:20 -0500710 log.Debugf("Suite[%d] - %s", k, v)
711 ts := &TestSuite{}
712 ts.loadSuite(v)
713 log.Debugf("Suite %s: %v", v, ts)
sslobodr1d1e50b2019-03-14 09:17:40 -0400714 log.Infof("Processing test suite %s", v)
sslobodrd6e07e72019-01-31 16:07:20 -0500715
David Bainbridgeec4ff512019-04-19 18:59:40 +0000716 t := template.Must(template.New("").ParseFiles("../templates/server.go.tmpl",
717 "../templates/serverInit.go.tmpl",
718 "../templates/client.go.tmpl",
719 "../templates/clientInit.go.tmpl",
720 "../templates/runTests.go.tmpl",
721 "../templates/stats.go.tmpl",
722 "../templates/main.go.tmpl"))
sslobodrd6e07e72019-01-31 16:07:20 -0500723 // Create a directory for he source code for this test suite
724 if err := os.Mkdir(suiteDir, 0777); err != nil {
725 log.Errorf("Unable to create directory '%s':%v\n", v, err)
726 return err
727 }
728 // Generate the server source files
729 if err := generateServers(conf, suiteDir, ts, t); err != nil {
730 log.Errorf("Unable to generate server source files: %v", err)
731 return err
732 }
733 // Generate the client source files
734 if err := generateClients(conf, suiteDir, ts, t); err != nil {
735 log.Errorf("Unable to generate client source files: %v", err)
736 return err
737 }
738 // Generate the test case source file
739 if err := generateTestCases(conf, suiteDir, ts, t); err != nil {
740 log.Errorf("Unable to generate test case source file: %v", err)
741 return err
742 }
743
744 // Finally generate the main file
745 log.Info("Generating main.go")
David Bainbridgeec4ff512019-04-19 18:59:40 +0000746 if f, err := os.Create(suiteDir + "/main.go"); err == nil {
747 if err := t.ExecuteTemplate(f, "main.go.tmpl", ts.Env); err != nil {
748 log.Errorf("Unable to execute template for main.go: %v", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500749 }
750 f.Close()
751 } else {
752 log.Errorf("Couldn't create file %s : %v", suiteDir+"/main.go", err)
753 }
754
sslobodr1d1e50b2019-03-14 09:17:40 -0400755 log.Infof("Copying over common modules")
David Bainbridgeec4ff512019-04-19 18:59:40 +0000756 if f, err := os.Create(suiteDir + "/stats.go"); err == nil {
757 if err := t.ExecuteTemplate(f, "stats.go.tmpl", ts.Env); err != nil {
758 log.Errorf("Unable to execute template for stats.go: %v", err)
sslobodr1d1e50b2019-03-14 09:17:40 -0400759 }
760 f.Close()
761 } else {
762 log.Errorf("Couldn't create file %s : %v", suiteDir+"/stats.go", err)
763 }
764
sslobodrd6e07e72019-01-31 16:07:20 -0500765 log.Infof("Compiling test suite: %s in directory %s", v, suiteDir)
766 if err := os.Chdir(suiteDir); err != nil {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000767 log.Errorf("Could not change to directory '%s':%v", suiteDir, err)
sslobodrd6e07e72019-01-31 16:07:20 -0500768 }
sslobodr13182842019-02-08 14:40:30 -0500769 cmd := exec.Command("go", "build", "-o", outDir+"/"+v+".e")
sslobodrd6e07e72019-01-31 16:07:20 -0500770 cmd.Stdin = os.Stdin
771 cmd.Stdout = os.Stdout
772 cmd.Stderr = os.Stderr
773 if err := cmd.Run(); err != nil {
774 log.Errorf("Error running the compile command:%v", err)
775 }
776 if err := os.Chdir("../../suites"); err != nil {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000777 log.Errorf("Could not change to directory '%s':%v", "../../suites", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500778 }
sslobodrd6e07e72019-01-31 16:07:20 -0500779 }
780 return nil
781
782}
783
784func generateTestDriver(conf *TestConfig, srcDir string, outDir string) error {
785 // Generate the main test driver file
786 if err := os.Mkdir(srcDir, 0777); err != nil {
787 log.Errorf("Unable to create directory 'driver':%v\n", err)
788 return err
789 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000790 t := template.Must(template.New("").ParseFiles("../templates/runAll.go.tmpl",
791 "../templates/stats.go.tmpl"))
792 if f, err := os.Create(srcDir + "/runAll.go"); err == nil {
793 if err := t.ExecuteTemplate(f, "runAll.go.tmpl", conf.Suites); err != nil {
794 log.Errorf("Unable to execute template for runAll.go: %v", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500795 }
796 f.Close()
797 } else {
798 log.Errorf("Couldn't create file %s : %v", srcDir+"/runAll.go", err)
799 }
David Bainbridgeec4ff512019-04-19 18:59:40 +0000800 if f, err := os.Create(srcDir + "/stats.go"); err == nil {
801 if err := t.ExecuteTemplate(f, "stats.go.tmpl", conf.Suites); err != nil {
802 log.Errorf("Unable to execute template for stats.go: %v", err)
sslobodr1d1e50b2019-03-14 09:17:40 -0400803 }
804 f.Close()
805 } else {
806 log.Errorf("Couldn't create file %s : %v", srcDir+"/stats.go", err)
807 }
sslobodrd6e07e72019-01-31 16:07:20 -0500808
809 // Compile the test driver file
810 log.Info("Compiling the test driver")
811 if err := os.Chdir("../tests/driver"); err != nil {
812 log.Errorf("Could not change to directory 'driver':%v", err)
813 }
814 cmd := exec.Command("go", "build", "-o", outDir+"/runAll")
815 cmd.Stdin = os.Stdin
816 cmd.Stdout = os.Stdout
817 cmd.Stderr = os.Stderr
818 if err := cmd.Run(); err != nil {
819 log.Errorf("Error running the compile command:%v", err)
820 }
821 if err := os.Chdir("../../suites"); err != nil {
David Bainbridgeec4ff512019-04-19 18:59:40 +0000822 log.Errorf("Could not change to directory 'driver':%v", err)
sslobodrd6e07e72019-01-31 16:07:20 -0500823 }
824
825 return nil
826}
827
sslobodrd6e07e72019-01-31 16:07:20 -0500828func main() {
829
David Bainbridgeec4ff512019-04-19 18:59:40 +0000830 conf, err := parseCmd()
sslobodrd6e07e72019-01-31 16:07:20 -0500831 if err != nil {
832 fmt.Printf("Error: %v\n", err)
833 return
834 }
835
836 // Setup logging
837 if _, err := log.SetDefaultLogger(log.JSON, *conf.logLevel, nil); err != nil {
838 log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
839 }
840
841 defer log.CleanUp()
842
843 // Parse the config file
844 if err := conf.loadConfig(); err != nil {
845 log.Error(err)
846 }
847
848 generateTestSuites(conf, "../tests", "/src/tests")
849 generateTestDriver(conf, "../tests/driver", "/src/tests")
850 return
851}