github.com/onsi/ginkgo@v1.16.6-0.20211118180735-4e1925ba4c95/ginkgo/internal/compile.go (about) 1 package internal 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "path/filepath" 8 "strings" 9 "sync" 10 11 "github.com/onsi/ginkgo/types" 12 ) 13 14 func CompileSuite(suite TestSuite, goFlagsConfig types.GoFlagsConfig) TestSuite { 15 if suite.PathToCompiledTest != "" { 16 return suite 17 } 18 19 suite.CompilationError = nil 20 21 path, err := filepath.Abs(filepath.Join(suite.Path, suite.PackageName+".test")) 22 if err != nil { 23 suite.State = TestSuiteStateFailedToCompile 24 suite.CompilationError = fmt.Errorf("Failed to compute compilation target path:\n%s", err.Error()) 25 return suite 26 } 27 28 args, err := types.GenerateGoTestCompileArgs(goFlagsConfig, path, "./") 29 if err != nil { 30 suite.State = TestSuiteStateFailedToCompile 31 suite.CompilationError = fmt.Errorf("Failed to generate go test compile flags:\n%s", err.Error()) 32 return suite 33 } 34 35 cmd := exec.Command("go", args...) 36 cmd.Dir = suite.Path 37 output, err := cmd.CombinedOutput() 38 if err != nil { 39 if len(output) > 0 { 40 suite.State = TestSuiteStateFailedToCompile 41 suite.CompilationError = fmt.Errorf("Failed to compile %s:\n\n%s", suite.PackageName, output) 42 } else { 43 suite.State = TestSuiteStateFailedToCompile 44 suite.CompilationError = fmt.Errorf("Failed to compile %s\n%s", suite.PackageName, err.Error()) 45 } 46 return suite 47 } 48 49 if strings.Contains(string(output), "[no test files]") { 50 suite.State = TestSuiteStateSkippedDueToEmptyCompilation 51 return suite 52 } 53 54 if len(output) > 0 { 55 fmt.Println(string(output)) 56 } 57 58 if !FileExists(path) { 59 suite.State = TestSuiteStateFailedToCompile 60 suite.CompilationError = fmt.Errorf("Failed to compile %s:\nOutput file %s could not be found", suite.PackageName, path) 61 return suite 62 } 63 64 suite.State = TestSuiteStateCompiled 65 suite.PathToCompiledTest = path 66 return suite 67 } 68 69 func Cleanup(goFlagsConfig types.GoFlagsConfig, suites ...TestSuite) { 70 if goFlagsConfig.BinaryMustBePreserved() { 71 return 72 } 73 for _, suite := range suites { 74 if !suite.Precompiled { 75 os.Remove(suite.PathToCompiledTest) 76 } 77 } 78 } 79 80 type parallelSuiteBundle struct { 81 suite TestSuite 82 compiled chan TestSuite 83 } 84 85 type OrderedParallelCompiler struct { 86 mutex *sync.Mutex 87 stopped bool 88 numCompilers int 89 90 idx int 91 numSuites int 92 completionChannels []chan TestSuite 93 } 94 95 func NewOrderedParallelCompiler(numCompilers int) *OrderedParallelCompiler { 96 return &OrderedParallelCompiler{ 97 mutex: &sync.Mutex{}, 98 numCompilers: numCompilers, 99 } 100 } 101 102 func (opc *OrderedParallelCompiler) StartCompiling(suites TestSuites, goFlagsConfig types.GoFlagsConfig) { 103 opc.stopped = false 104 opc.idx = 0 105 opc.numSuites = len(suites) 106 opc.completionChannels = make([]chan TestSuite, opc.numSuites) 107 108 toCompile := make(chan parallelSuiteBundle, opc.numCompilers) 109 for compiler := 0; compiler < opc.numCompilers; compiler++ { 110 go func() { 111 for bundle := range toCompile { 112 c, suite := bundle.compiled, bundle.suite 113 opc.mutex.Lock() 114 stopped := opc.stopped 115 opc.mutex.Unlock() 116 if !stopped { 117 suite = CompileSuite(suite, goFlagsConfig) 118 } 119 c <- suite 120 } 121 }() 122 } 123 124 for idx, suite := range suites { 125 opc.completionChannels[idx] = make(chan TestSuite, 1) 126 toCompile <- parallelSuiteBundle{suite, opc.completionChannels[idx]} 127 if idx == 0 { //compile first suite serially 128 suite = <-opc.completionChannels[0] 129 opc.completionChannels[0] <- suite 130 } 131 } 132 133 close(toCompile) 134 } 135 136 func (opc *OrderedParallelCompiler) Next() (int, TestSuite) { 137 if opc.idx >= opc.numSuites { 138 return opc.numSuites, TestSuite{} 139 } 140 141 idx := opc.idx 142 suite := <-opc.completionChannels[idx] 143 opc.idx = opc.idx + 1 144 145 return idx, suite 146 } 147 148 func (opc *OrderedParallelCompiler) StopAndDrain() { 149 opc.mutex.Lock() 150 opc.stopped = true 151 opc.mutex.Unlock() 152 }