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  }