blob: f3ba2aa1f8a1ac6156281bdc9cf787a70abf15ff [file] [log] [blame]
/*
* Copyright 2018-present Open Networking Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// gRPC affinity router with active/active backends
package main
import (
"encoding/json"
"errors"
"flag"
"fmt"
"github.com/golang/protobuf/proto"
pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/opencord/voltha-go/common/log"
"io/ioutil"
"math"
"os"
"os/exec"
"path"
"regexp"
"strconv"
"text/template"
)
const MAX_CT = 500
type TestConfig struct {
configFile *string
logLevel *int
grpcLog *bool
Suites []string `json:"suites"`
}
type Connection struct {
Name string `json:"name"`
Port string `json:"port"`
}
type EndpointList struct {
Imports []string `json:"imports"`
Endpoints []Connection `json:"endPoints"`
}
type ProtoFile struct {
ImportPath string `json:"importPath"`
Service string `json:"service"`
Package string `json:"package"`
}
type ProtoSubst struct {
From string `json:"from"`
To string `json:"to"`
}
type Environment struct {
Command string `json:"cmdLine"`
ProtoFiles []ProtoFile `json:"protoFiles"`
ProtoDesc string `json:"protoDesc"`
Clients EndpointList `json:"clients"`
Servers EndpointList `json:"servers"`
Imports []string `json:"imports"`
ProtoSubsts []ProtoSubst `json:"protoSubst"`
}
type Rpc struct {
Client string `json:"client"`
Method string `json:"method"`
Param string `json:"param"`
Expect string `json:"expect"`
MetaData []MetaD `json:"meta"`
ExpectMeta []MetaD `json:"expectMeta"`
}
type MetaD struct {
Key string `json:"key"`
Val string `json:"value"`
}
type Server struct {
Name string `json:"name"`
Meta []MetaD `json:"meta"`
}
type Test struct {
Name string `json:"name"`
InfoOnly bool `json:"infoOnly"`
Send Rpc `json:"send"`
Servers []Server `json:"servers"`
}
type TestSuite struct {
Env Environment `json:"environment"`
Tests []Test `json:"tests"`
}
type ProtoImport struct {
Service string
Short string
Package string
Used bool
}
type SendItem struct {
Client string
Method string
Param string
ParamType string
Expect string
ExpectType string
MetaData []MetaD
ExpectMeta []MetaD
}
type TestCase struct {
Name string
InfoOnly bool
Send SendItem
Srvr []Server
}
type MethodTypes struct {
ParamType string
ReturnType string
CodeGenerated bool
}
type Import struct {
Package string
Used bool
}
type TestList struct {
ProtoImports []ProtoImport
Imports []Import
Tests []TestCase
Funcs map[string]MethodTypes
RunTestsCallList []string
FileNum int
//NextFile int
HasFuncs bool
Offset int
}
type ClientConfig struct {
Ct int
Name string
Port string
Imports []string
Methods map[string]*mthd
ProtoImports []ProtoImport
}
type ServerConfig struct {
Ct int
Name string
Port string
Imports []string
Methods map[string]*mthd
ProtoImports []ProtoImport
}
type mthd struct {
Pkg string
Svc string
Name string
Param string
Rtrn string
Ss bool // Server streaming
Cs bool // Clieent streaming
}
func parseCmd() (*TestConfig, error) {
config := &TestConfig{}
cmdParse := flag.NewFlagSet(path.Base(os.Args[0]), flag.ContinueOnError)
config.configFile = cmdParse.String("config", "suites.json", "The configuration file for the affinity router tester")
config.logLevel = cmdParse.Int("logLevel", 0, "The log level for the affinity router tester")
config.grpcLog = cmdParse.Bool("grpclog", false, "Enable GRPC logging")
err := cmdParse.Parse(os.Args[1:])
if err != nil {
//return err
return nil, errors.New("Error parsing the command line")
}
return config, nil
}
func (conf *TestConfig) loadConfig() error {
configF, err := os.Open(*conf.configFile)
log.Info("Loading configuration from: ", *conf.configFile)
if err != nil {
log.Error(err)
return err
}
defer configF.Close()
if configBytes, err := ioutil.ReadAll(configF); err != nil {
log.Error(err)
return err
} else if err := json.Unmarshal(configBytes, conf); err != nil {
log.Error(err)
return err
}
return nil
}
func (suite *TestSuite) loadSuite(suiteN string) error {
// Check if there's a corresponding go file for the suite.
// If it is present then the json test suite file is a
// template. Compile the go file and run it to process
// the template.
if _, err := os.Stat(suiteN + "/" + suiteN + ".go"); err == nil {
// Compile and run the the go file
log.Infof("Suite '%s' is a template, compiling '%s'", suiteN, suiteN+".go")
cmd := exec.Command("go", "build", "-o", suiteN+".te", suiteN+".go")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = suiteN
if err := cmd.Run(); err != nil {
log.Errorf("Error running the compile command:%v", err)
}
cmd = exec.Command("./" + suiteN + ".te")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = suiteN
if err := cmd.Run(); err != nil {
log.Errorf("Error running the %s command:%v", suiteN+".te", err)
}
}
suiteF, err := os.Open(suiteN + "/" + suiteN + ".json")
log.Infof("Loading test suite from: %s", suiteN+"/"+suiteN+".json")
if err != nil {
log.Error(err)
return err
}
defer suiteF.Close()
if suiteBytes, err := ioutil.ReadAll(suiteF); err != nil {
log.Error(err)
return err
} else if err := json.Unmarshal(suiteBytes, suite); err != nil {
log.Error(err)
return err
}
return nil
}
func loadProtoMap(fileName string, pkg string, svc string, substs []ProtoSubst) (map[string]*mthd, error) {
var mthds map[string]*mthd = make(map[string]*mthd)
var rtrn_err bool
// Load the protobuf descriptor file
protoDescriptor := &pb.FileDescriptorSet{}
fb, err := ioutil.ReadFile(fileName)
if err != nil {
log.Errorf("Could not open proto file '%s'", fileName)
rtrn_err = true
}
err = proto.Unmarshal(fb, protoDescriptor)
if err != nil {
log.Errorf("Could not unmarshal %s, %v", "proto.pb", err)
rtrn_err = true
}
var substM map[string]string = make(map[string]string)
// Create a substitution map
log.Debugf("Creating import map")
for _, v := range substs {
log.Debugf("Mapping from %s to %s", v.From, v.To)
substM[v.From] = v.To
}
// Build the a map containing the method as the key
// and the paramter and return types as the fields
for _, f := range protoDescriptor.File {
if *f.Package == pkg {
for _, s := range f.Service {
if *s.Name == svc {
log.Debugf("Loading package data '%s' for service '%s'", *f.Package, *s.Name)
// Now create a map keyed by method name with the value being the
// field number of the route selector.
//var ok bool
for _, m := range s.Method {
// Find the input type in the messages and extract the
// field number and save it for future reference.
log.Debugf("Processing method (%s(%s) (%s){}", *m.Name, (*m.InputType)[1:], (*m.OutputType)[1:])
mthds[*m.Name] = &mthd{Pkg: pkg, Svc: svc, Name: *m.Name, Param: (*m.InputType)[1:],
Rtrn: (*m.OutputType)[1:]}
if m.ClientStreaming != nil && *m.ClientStreaming == true {
log.Debugf("Method %s is a client streaming method", *m.Name)
mthds[*m.Name].Cs = true
}
if m.ServerStreaming != nil && *m.ServerStreaming == true {
log.Debugf("Method %s is a server streaming method", *m.Name)
mthds[*m.Name].Ss = true
}
// Perform the required substitutions
if _, ok := substM[mthds[*m.Name].Param]; ok == true {
mthds[*m.Name].Param = substM[mthds[*m.Name].Param]
}
if _, ok := substM[mthds[*m.Name].Rtrn]; ok == true {
mthds[*m.Name].Rtrn = substM[mthds[*m.Name].Rtrn]
}
}
}
}
}
}
if rtrn_err {
return nil, errors.New(fmt.Sprintf("Failed to load protobuf descriptor file '%s'", fileName))
}
return mthds, nil
}
// Server source code generation
func generateServers(conf *TestConfig, suiteDir string, ts *TestSuite,
t *template.Template) error {
var servers []ServerConfig
for k, v := range ts.Env.Servers.Endpoints {
log.Infof("Generating the code for server[%d]: %s", k, v.Name)
sc := &ServerConfig{Name: v.Name, Port: v.Port, Ct: k, Imports: ts.Env.Servers.Imports,
Methods: make(map[string]*mthd)}
for k1, v1 := range ts.Env.ProtoFiles {
imp := &ProtoImport{Short: "pb" + strconv.Itoa(k1),
Package: v1.ImportPath + v1.Package,
Service: v1.Service,
Used: true}
imp = &ProtoImport{Short: v1.Package,
Package: v1.ImportPath + v1.Package,
Service: v1.Service,
Used: true}
sc.ProtoImports = append(sc.ProtoImports, *imp)
// Compile the template from the file
log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v1.Package,
v1.Service, ts.Env.ProtoSubsts); err != nil {
log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
ts.Env.ProtoDesc, v1.Package, v1.Service)
return err
} else {
//Generate all the function calls required by the
for k, v := range mthds {
sc.Methods[k] = v
}
//sc.Methods = mthds
}
}
log.Debugf("Server: %v", *sc)
// Save this server for the next steop
servers = append(servers, *sc)
// Open an output file to put the output in.
if f, err := os.Create(suiteDir + "/" + v.Name + ".go"); err == nil {
defer f.Close()
//if err := t.ExecuteTemplate(os.Stdout, "server.go.tmpl", *sc); err != nil {}
if err := t.ExecuteTemplate(f, "server.go.tmpl", *sc); err != nil {
log.Errorf("Unable to execute template for server[%d]: %s: %v", k, v.Name, err)
return err
}
}
}
// Generate the server initialization code
if f, err := os.Create(suiteDir + "/serverInit.go"); err == nil {
defer f.Close()
//if err := t.ExecuteTemplate(os.Stdout, "server.go.tmpl", *sc); err != nil {}
if err := t.ExecuteTemplate(f, "serverInit.go.tmpl", servers); err != nil {
log.Errorf("Unable to execute template for serverInit.go: %v", err)
return err
}
}
return nil
}
func generateClients(conf *TestConfig, suiteDir string, ts *TestSuite,
t *template.Template) error {
var clients []ClientConfig
for k, v := range ts.Env.Clients.Endpoints {
log.Infof("Generating the code for client[%d]: %s", k, v.Name)
cc := &ClientConfig{Name: v.Name, Port: v.Port, Ct: k, Imports: ts.Env.Clients.Imports,
Methods: make(map[string]*mthd)}
for _, v1 := range ts.Env.ProtoFiles {
imp := &ProtoImport{Short: v1.Package,
Package: v1.ImportPath + v1.Package,
Service: v1.Service}
cc.ProtoImports = append(cc.ProtoImports, *imp)
// Compile the template from the file
log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v1.Package,
v1.Service, ts.Env.ProtoSubsts); err != nil {
log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
ts.Env.ProtoDesc, v1.Package, v1.Service)
return err
} else {
// Add to the known methods
for k, v := range mthds {
cc.Methods[k] = v
}
}
}
clients = append(clients, *cc)
if f, err := os.Create(suiteDir + "/" + v.Name + "_client.go"); err == nil {
_ = f
defer f.Close()
if err := t.ExecuteTemplate(f, "client.go.tmpl", cc); err != nil {
log.Errorf("Unable to execute template for client.go: %v", err)
return err
}
} else {
log.Errorf("Couldn't create file %s : %v", suiteDir+"/"+v.Name+"_client.go", err)
return err
}
}
if f, err := os.Create(suiteDir + "/clientInit.go"); err == nil {
defer f.Close()
//if err := t.ExecuteTemplate(os.Stdout, "server.go.tmpl", *sc); err != nil {}
if err := t.ExecuteTemplate(f, "clientInit.go.tmpl", clients); err != nil {
log.Errorf("Unable to execute template for clientInit.go: %v", err)
return err
}
}
return nil
}
func serverExists(srvr string, ts *TestSuite) bool {
for _, v := range ts.Env.Servers.Endpoints {
if v.Name == srvr {
return true
}
}
return false
}
func getPackageList(tests []TestCase) map[string]struct{} {
var rtrn map[string]struct{} = make(map[string]struct{})
var p string
var e string
r := regexp.MustCompile(`^([^.]+)\..*$`)
for _, v := range tests {
pa := r.FindStringSubmatch(v.Send.ParamType)
if len(pa) == 2 {
p = pa[1]
} else {
log.Errorf("Internal error regexp returned %v", pa)
}
ea := r.FindStringSubmatch(v.Send.ExpectType)
if len(ea) == 2 {
e = ea[1]
} else {
log.Errorf("Internal error regexp returned %v", pa)
}
if _, ok := rtrn[p]; ok == false {
rtrn[p] = struct{}{}
}
if _, ok := rtrn[e]; ok == false {
rtrn[e] = struct{}{}
}
}
return rtrn
}
func fixupProtoImports(protoImports []ProtoImport, used map[string]struct{}) []ProtoImport {
var rtrn []ProtoImport
//log.Infof("Updating imports %v, using %v", protoImports, used)
for _, v := range protoImports {
//log.Infof("Looking for package %s", v.Package)
if _, ok := used[v.Short]; ok == true {
rtrn = append(rtrn, ProtoImport{
Service: v.Service,
Short: v.Short,
Package: v.Package,
Used: true,
})
} else {
rtrn = append(rtrn, ProtoImport{
Service: v.Service,
Short: v.Short,
Package: v.Package,
Used: false,
})
}
}
//log.Infof("After update %v", rtrn)
return rtrn
}
func fixupImports(imports []Import, used map[string]struct{}) []Import {
var rtrn []Import
var p string
re := regexp.MustCompile(`^.*/([^/]+)$`)
//log.Infof("Updating imports %v, using %v", protoImports, used)
for _, v := range imports {
//log.Infof("Looking for match in %s", v.Package)
pa := re.FindStringSubmatch(v.Package)
if len(pa) == 2 {
p = pa[1]
} else {
log.Errorf("Substring match failed, regexp returned %v", pa)
}
//log.Infof("Looking for package %s", v.Package)
if _, ok := used[p]; ok == true {
rtrn = append(rtrn, Import{
Package: v.Package,
Used: true,
})
} else {
rtrn = append(rtrn, Import{
Package: v.Package,
Used: false,
})
}
}
//log.Infof("After update %v", rtrn)
return rtrn
}
func generateTestCases(conf *TestConfig, suiteDir string, ts *TestSuite,
t *template.Template) error {
var mthdMap map[string]*mthd
mthdMap = make(map[string]*mthd)
// Generate the test cases
log.Info("Generating the test cases: runTests.go")
tc := &TestList{Funcs: make(map[string]MethodTypes),
FileNum: 0, HasFuncs: false,
Offset: 0}
for _, v := range ts.Env.Imports {
tc.Imports = append(tc.Imports, Import{Package: v, Used: true})
}
// Load the proto descriptor file
for _, v := range ts.Env.ProtoFiles {
imp := &ProtoImport{Short: v.Package,
Package: v.ImportPath + v.Package,
Service: v.Service,
Used: true}
tc.ProtoImports = append(tc.ProtoImports, *imp)
// Compile the template from the file
log.Debugf("Proto substs: %v", ts.Env.ProtoSubsts)
if mthds, err := loadProtoMap(ts.Env.ProtoDesc, v.Package,
v.Service, ts.Env.ProtoSubsts); err != nil {
log.Errorf("Unable to process proto descriptor file %s for package: %s, service: %s",
ts.Env.ProtoDesc, v.Package, v.Service)
return err
} else {
// Add to the known methods
for k, v := range mthds {
mthdMap[k] = v
}
}
}
// Since the input file can possibly be a template with loops that
// make multiple calls to exactly the same method with potentially
// different parameters it is best to try to optimize for that case.
// Creating a function for each method used will greatly reduce the
// code size and repetition. In the case where a method is used only
// once the resulting code will be bigger but this is an acceptable
// tradeoff to allow huge suites that call the same method repeatedly
// to test for leaks.
// The go compiler has trouble compiling files that are too large. In order
// to mitigate that, It's necessary to create more smaller files than the
// single one large one while making sure that the functions for the distinct
// methods are only defined once in one of the files.
// Yet another hiccup with the go compiler, it doesn't like deep function
// nesting meaning that each runTests function can't call the next without
// eventually blowing the stack check. In order to work around this, the
// first runTests function needs to call all the others in sequence to
// keep the stack depth constant.
// Counter limiting the number of tests to output to each file
maxCt := MAX_CT
// Get the list of distinct methods
//for _,v := range ts.Tests {
// if _,ok := tc.Funcs[v.Send.Method]; ok == false {
// tc.Funcs[v.Send.Method] = MethodTypes{ParamType:mthdMap[v.Send.Method].Param,
// ReturnType:mthdMap[v.Send.Method].Rtrn}
// }
//}
for i := 1; i < int(math.Ceil(float64(len(ts.Tests))/float64(MAX_CT))); i++ {
tc.RunTestsCallList = append(tc.RunTestsCallList, "runTests"+strconv.Itoa(i))
}
// Create the test data structure for the template
for _, v := range ts.Tests {
var test TestCase
if _, ok := tc.Funcs[v.Send.Method]; ok == false {
tc.Funcs[v.Send.Method] = MethodTypes{ParamType: mthdMap[v.Send.Method].Param,
ReturnType: mthdMap[v.Send.Method].Rtrn,
CodeGenerated: false}
tc.HasFuncs = true
}
test.Name = v.Name
test.InfoOnly = v.InfoOnly
test.Send.Client = v.Send.Client
test.Send.Method = v.Send.Method
test.Send.Param = v.Send.Param
test.Send.ParamType = mthdMap[test.Send.Method].Param
test.Send.Expect = v.Send.Expect
test.Send.ExpectType = mthdMap[test.Send.Method].Rtrn
for _, v1 := range v.Send.MetaData {
test.Send.MetaData = append(test.Send.MetaData, v1)
}
for _, v1 := range v.Send.ExpectMeta {
test.Send.ExpectMeta = append(test.Send.ExpectMeta, v1)
}
for _, v1 := range v.Servers {
var srvr Server
if serverExists(v1.Name, ts) == false {
log.Errorf("Server '%s' is not defined!!", v1.Name)
return errors.New(fmt.Sprintf("Failed to build test case %s", v.Name))
}
srvr.Name = v1.Name
srvr.Meta = v1.Meta
test.Srvr = append(test.Srvr, srvr)
}
tc.Tests = append(tc.Tests, test)
if maxCt--; maxCt == 0 {
// Get the list of proto pacakges required for this file
pkgs := getPackageList(tc.Tests)
// Adjust the proto import data accordingly
tc.ProtoImports = fixupProtoImports(tc.ProtoImports, pkgs)
tc.Imports = fixupImports(tc.Imports, pkgs)
//log.Infof("The packages needed are: %v", pkgs)
if f, err := os.Create(suiteDir + "/runTests" + strconv.Itoa(tc.FileNum) + ".go"); err == nil {
if err := t.ExecuteTemplate(f, "runTests.go.tmpl", tc); err != nil {
log.Errorf("Unable to execute template for runTests.go: %v", err)
}
f.Close()
} else {
log.Errorf("Couldn't create file %s : %v",
suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".go", err)
}
tc.FileNum++
//tc.NextFile++
maxCt = MAX_CT
// Mark the functions as generated.
tc.Tests = []TestCase{}
for k, v := range tc.Funcs {
tc.Funcs[k] = MethodTypes{ParamType: v.ParamType,
ReturnType: v.ReturnType,
CodeGenerated: true}
}
tc.HasFuncs = false
tc.Offset += MAX_CT
//tmp,_ := strconv.Atoi(tc.Offset)
//tc.Offset = strconv.Itoa(tmp+500)
}
}
//tc.NextFile = 0
// Get the list of proto pacakges required for this file
pkgs := getPackageList(tc.Tests)
// Adjust the proto import data accordingly
tc.ProtoImports = fixupProtoImports(tc.ProtoImports, pkgs)
tc.Imports = fixupImports(tc.Imports, pkgs)
//log.Infof("The packages needed are: %v", pkgs)
if f, err := os.Create(suiteDir + "/runTests" + strconv.Itoa(tc.FileNum) + ".go"); err == nil {
if err := t.ExecuteTemplate(f, "runTests.go.tmpl", tc); err != nil {
log.Errorf("Unable to execute template for runTests.go: %v", err)
}
f.Close()
} else {
log.Errorf("Couldn't create file %s : %v",
suiteDir+"/runTests"+strconv.Itoa(tc.FileNum)+".go", err)
}
//if f,err := os.Create(suiteDir+"/runTests.go"); err == nil {
// if err := t.ExecuteTemplate(f, "runTests.go.tmpl", tc); err != nil {
// log.Errorf("Unable to execute template for runTests.go: %v", err)
// }
// f.Close()
//} else {
// log.Errorf("Couldn't create file %s : %v", suiteDir+"/runTests.go", err)
//}
return nil
}
func generateTestSuites(conf *TestConfig, srcDir string, outDir string) error {
// Create a directory for the tests
if err := os.Mkdir(srcDir, 0777); err != nil {
log.Errorf("Unable to create directory 'tests':%v\n", err)
return err
}
for k, v := range conf.Suites {
var suiteDir string = srcDir + "/" + v
log.Debugf("Suite[%d] - %s", k, v)
ts := &TestSuite{}
ts.loadSuite(v)
log.Debugf("Suite %s: %v", v, ts)
log.Infof("Processing test suite %s", v)
t := template.Must(template.New("").ParseFiles("../templates/server.go.tmpl",
"../templates/serverInit.go.tmpl",
"../templates/client.go.tmpl",
"../templates/clientInit.go.tmpl",
"../templates/runTests.go.tmpl",
"../templates/stats.go.tmpl",
"../templates/main.go.tmpl"))
// Create a directory for he source code for this test suite
if err := os.Mkdir(suiteDir, 0777); err != nil {
log.Errorf("Unable to create directory '%s':%v\n", v, err)
return err
}
// Generate the server source files
if err := generateServers(conf, suiteDir, ts, t); err != nil {
log.Errorf("Unable to generate server source files: %v", err)
return err
}
// Generate the client source files
if err := generateClients(conf, suiteDir, ts, t); err != nil {
log.Errorf("Unable to generate client source files: %v", err)
return err
}
// Generate the test case source file
if err := generateTestCases(conf, suiteDir, ts, t); err != nil {
log.Errorf("Unable to generate test case source file: %v", err)
return err
}
// Finally generate the main file
log.Info("Generating main.go")
if f, err := os.Create(suiteDir + "/main.go"); err == nil {
if err := t.ExecuteTemplate(f, "main.go.tmpl", ts.Env); err != nil {
log.Errorf("Unable to execute template for main.go: %v", err)
}
f.Close()
} else {
log.Errorf("Couldn't create file %s : %v", suiteDir+"/main.go", err)
}
log.Infof("Copying over common modules")
if f, err := os.Create(suiteDir + "/stats.go"); err == nil {
if err := t.ExecuteTemplate(f, "stats.go.tmpl", ts.Env); err != nil {
log.Errorf("Unable to execute template for stats.go: %v", err)
}
f.Close()
} else {
log.Errorf("Couldn't create file %s : %v", suiteDir+"/stats.go", err)
}
log.Infof("Compiling test suite: %s in directory %s", v, suiteDir)
if err := os.Chdir(suiteDir); err != nil {
log.Errorf("Could not change to directory '%s':%v", suiteDir, err)
}
cmd := exec.Command("go", "build", "-o", outDir+"/"+v+".e")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Errorf("Error running the compile command:%v", err)
}
if err := os.Chdir("../../suites"); err != nil {
log.Errorf("Could not change to directory '%s':%v", "../../suites", err)
}
}
return nil
}
func generateTestDriver(conf *TestConfig, srcDir string, outDir string) error {
// Generate the main test driver file
if err := os.Mkdir(srcDir, 0777); err != nil {
log.Errorf("Unable to create directory 'driver':%v\n", err)
return err
}
t := template.Must(template.New("").ParseFiles("../templates/runAll.go.tmpl",
"../templates/stats.go.tmpl"))
if f, err := os.Create(srcDir + "/runAll.go"); err == nil {
if err := t.ExecuteTemplate(f, "runAll.go.tmpl", conf.Suites); err != nil {
log.Errorf("Unable to execute template for runAll.go: %v", err)
}
f.Close()
} else {
log.Errorf("Couldn't create file %s : %v", srcDir+"/runAll.go", err)
}
if f, err := os.Create(srcDir + "/stats.go"); err == nil {
if err := t.ExecuteTemplate(f, "stats.go.tmpl", conf.Suites); err != nil {
log.Errorf("Unable to execute template for stats.go: %v", err)
}
f.Close()
} else {
log.Errorf("Couldn't create file %s : %v", srcDir+"/stats.go", err)
}
// Compile the test driver file
log.Info("Compiling the test driver")
if err := os.Chdir("../tests/driver"); err != nil {
log.Errorf("Could not change to directory 'driver':%v", err)
}
cmd := exec.Command("go", "build", "-o", outDir+"/runAll")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Errorf("Error running the compile command:%v", err)
}
if err := os.Chdir("../../suites"); err != nil {
log.Errorf("Could not change to directory 'driver':%v", err)
}
return nil
}
func main() {
conf, err := parseCmd()
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Setup logging
if _, err := log.SetDefaultLogger(log.JSON, *conf.logLevel, nil); err != nil {
log.With(log.Fields{"error": err}).Fatal("Cannot setup logging")
}
defer log.CleanUp()
// Parse the config file
if err := conf.loadConfig(); err != nil {
log.Error(err)
}
generateTestSuites(conf, "../tests", "/src/tests")
generateTestDriver(conf, "../tests/driver", "/src/tests")
return
}