github.com/mmatczuk/gohan@v0.0.0-20170206152520-30e45d9bdb69/extension/framework/framework.go (about) 1 // Copyright (C) 2015 NTT Innovation Institute, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 // implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package framework 17 18 import ( 19 "fmt" 20 "io" 21 "os" 22 "path/filepath" 23 "regexp" 24 "sync" 25 "sync/atomic" 26 27 "github.com/cloudwan/gohan/extension/framework/runner" 28 l "github.com/cloudwan/gohan/log" 29 "github.com/cloudwan/gohan/schema" 30 "github.com/cloudwan/gohan/util" 31 "github.com/codegangsta/cli" 32 ) 33 34 var ( 35 logWriter io.Writer = os.Stderr 36 log = l.NewLoggerForModule("extest") 37 ) 38 39 // TestExtensions runs extension tests when invoked from Gohan CLI 40 func TestExtensions(c *cli.Context) { 41 l.SetUpBasicLogging(logWriter, l.DefaultFormat, "", l.DEBUG) 42 43 var config *util.Config 44 configFilePath := c.String("config-file") 45 46 if configFilePath != "" && !c.Bool("verbose") { 47 config = util.GetConfig() 48 err := config.ReadConfig(configFilePath) 49 if err != nil { 50 log.Error(fmt.Sprintf("Failed to read config from path %s: %v", configFilePath, err)) 51 os.Exit(1) 52 } 53 54 err = l.SetUpLogging(config) 55 if err != nil { 56 log.Error(fmt.Sprintf("Failed to set up logging: %v", err)) 57 os.Exit(1) 58 } 59 } 60 61 testFiles := getTestFiles(c.Args()) 62 63 //logging from config is a limited printAllLogs option 64 returnCode := RunTests(testFiles, c.Bool("verbose") || config != nil, c.String("run-test"), c.Int("parallel")) 65 os.Exit(returnCode) 66 } 67 68 // RunTests runs extension tests for CLI. 69 func RunTests(testFiles []string, printAllLogs bool, testFilter string, workers int) (returnCode int) { 70 if !printAllLogs { 71 l.SetUpBasicLogging(l.BufWritter{}, l.DefaultFormat, "", l.DEBUG) 72 } 73 74 if workers <= 0 { 75 panic("Workers must be greater than 0") 76 } 77 78 if len(testFiles) < workers { 79 workers = len(testFiles) 80 } 81 82 var ( 83 maxIdx = int64(len(testFiles) - 1) 84 idx int64 = -1 85 errors = make(map[string]runner.TestRunnerErrors) 86 errorsMu sync.Mutex 87 wg sync.WaitGroup 88 ) 89 90 worker := func() { 91 for { 92 i := atomic.AddInt64(&idx, 1) 93 if i > maxIdx { 94 break 95 } 96 97 fileName := testFiles[i] 98 testErr := runner.NewTestRunner(fileName, printAllLogs, testFilter).Run() 99 100 errorsMu.Lock() 101 errors[fileName] = testErr 102 errorsMu.Unlock() 103 104 if err, ok := testErr[runner.GeneralError]; ok { 105 log.Error(fmt.Sprintf("\t ERROR (%s): %v", fileName, err)) 106 } 107 } 108 wg.Done() 109 } 110 111 // force goroutine local manager 112 schema.SetManagerScope(schema.ScopeGLSSingleton) 113 114 for workers > 0 { 115 wg.Add(1) 116 go worker() 117 workers -= 1 118 } 119 wg.Wait() 120 121 summary := makeSummary(errors) 122 printSummary(summary, printAllLogs) 123 124 for _, err := range summary { 125 if err != nil { 126 return 1 127 } 128 } 129 return 0 130 } 131 132 func makeSummary(errors map[string]runner.TestRunnerErrors) (summary map[string]error) { 133 summary = map[string]error{} 134 for testFile, errors := range errors { 135 if err, ok := errors[runner.GeneralError]; ok { 136 summary[testFile] = err 137 continue 138 } 139 140 failed := 0 141 for _, err := range errors { 142 if err != nil { 143 failed++ 144 } 145 } 146 summary[testFile] = nil 147 if failed > 0 { 148 summary[testFile] = fmt.Errorf("%d/%d tests failed", failed, len(errors)) 149 } 150 } 151 return 152 } 153 154 func printSummary(summary map[string]error, printAllLogs bool) { 155 l.SetUpBasicLogging(logWriter, l.DefaultFormat, "", l.DEBUG) 156 157 log.Info("Run %d test files.", len(summary)) 158 159 allPassed := true 160 for testFile, err := range summary { 161 if err != nil { 162 log.Error(fmt.Sprintf("\tFAIL\t%s: %s", testFile, err.Error())) 163 allPassed = false 164 } else if printAllLogs { 165 log.Notice("\tOK\t%s", testFile) 166 } 167 } 168 if allPassed { 169 log.Notice("All tests have passed.") 170 } 171 } 172 173 func getTestFiles(args cli.Args) []string { 174 paths := args 175 if len(paths) == 0 { 176 paths = append(paths, ".") 177 } 178 179 pattern := regexp.MustCompile(`^test_.*\.js$`) 180 seen := map[string]bool{} 181 testFiles := []string{} 182 for _, path := range paths { 183 filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error { 184 if err != nil { 185 log.Error(fmt.Sprintf("Failed to process '%s': %s", filePath, err.Error())) 186 return nil 187 } 188 189 if info.IsDir() { 190 return nil 191 } 192 193 if !pattern.MatchString(info.Name()) { 194 return nil 195 } 196 197 filePath = filepath.Clean(filePath) 198 199 if !seen[filePath] { 200 testFiles = append(testFiles, filePath) 201 seen[filePath] = true 202 } 203 204 return nil 205 }) 206 } 207 208 return testFiles 209 }