Imported Upstream version 2.4.2
[scm/test.git] / script / integration.go
1 package main
2
3 import (
4         "bytes"
5         "errors"
6         "fmt"
7         "os"
8         "os/exec"
9         "path/filepath"
10         "regexp"
11         "runtime"
12         "strconv"
13         "strings"
14         "sync"
15         "time"
16 )
17
18 var (
19         bashPath    string
20         debugging   = false
21         erroring    = false
22         maxprocs    = 4
23         testPattern = regexp.MustCompile(`test[/\\]test-([a-z\-]+)\.sh$`)
24 )
25
26 func mainIntegration() {
27         if len(os.Getenv("DEBUG")) > 0 {
28                 debugging = true
29         }
30
31         setBash()
32
33         if max, _ := strconv.Atoi(os.Getenv("GIT_LFS_TEST_MAXPROCS")); max > 0 {
34                 maxprocs = max
35         }
36
37         fmt.Println("Running this maxprocs", maxprocs)
38
39         files := testFiles()
40
41         if len(files) == 0 {
42                 fmt.Println("no tests to run")
43                 os.Exit(1)
44         }
45
46         var wg sync.WaitGroup
47         tests := make(chan string, len(files))
48         output := make(chan string, len(files))
49
50         for _, file := range files {
51                 tests <- file
52         }
53         close(tests)
54
55         outputDone := make(chan bool)
56         go func() {
57                 for out := range output {
58                         fmt.Println(out)
59                 }
60                 outputDone <- true
61         }()
62
63         for i := 0; i < maxprocs; i++ {
64                 wg.Add(1)
65                 go worker(tests, output, &wg)
66         }
67
68         wg.Wait()
69         close(output)
70         <-outputDone
71
72         if erroring {
73                 os.Exit(1)
74         }
75 }
76
77 func runTest(output chan string, testname string) {
78         buf := &bytes.Buffer{}
79         cmd := exec.Command(bashPath, testname)
80         cmd.Stdout = buf
81         cmd.Stderr = buf
82
83         err := cmd.Start()
84         if err != nil {
85                 sendTestOutput(output, testname, buf, err)
86                 return
87         }
88
89         done := make(chan error)
90         go func() {
91                 if err := cmd.Wait(); err != nil {
92                         done <- err
93                 }
94                 close(done)
95         }()
96
97         select {
98         case err = <-done:
99                 sendTestOutput(output, testname, buf, err)
100                 return
101         case <-time.After(3 * time.Minute):
102                 sendTestOutput(output, testname, buf, errors.New("Timed out"))
103                 cmd.Process.Kill()
104                 return
105         }
106 }
107
108 func sendTestOutput(output chan string, testname string, buf *bytes.Buffer, err error) {
109         cli := strings.TrimSpace(buf.String())
110         if len(cli) == 0 {
111                 cli = fmt.Sprintf("<no output for %s>", testname)
112         }
113
114         if err == nil {
115                 output <- cli
116         } else {
117                 basetestname := filepath.Base(testname)
118                 if debugging {
119                         fmt.Printf("Error on %s: %s\n", basetestname, err)
120                 }
121                 erroring = true
122                 output <- fmt.Sprintf("error: %s => %s\n%s", basetestname, err, cli)
123         }
124 }
125
126 func worker(tests <-chan string, output chan string, wg *sync.WaitGroup) {
127         defer wg.Done()
128         for {
129                 select {
130                 case testname, ok := <-tests:
131                         if !ok {
132                                 return
133                         }
134                         runTest(output, testname)
135                 }
136         }
137 }
138
139 func testFiles() []string {
140         if len(os.Args) < 4 {
141                 return allTestFiles()
142         }
143
144         fileMap := make(map[string]bool)
145         for _, file := range allTestFiles() {
146                 fileMap[file] = true
147         }
148
149         files := make([]string, 0, len(os.Args)-3)
150         for _, arg := range os.Args {
151                 fullname := "test/test-" + arg + ".sh"
152                 if fileMap[fullname] {
153                         files = append(files, fullname)
154                 }
155         }
156
157         return files
158 }
159
160 func allTestFiles() []string {
161         files := make([]string, 0, 100)
162         filepath.Walk("test", func(path string, info os.FileInfo, err error) error {
163                 if debugging {
164                         fmt.Println("FOUND:", path)
165                 }
166                 if err != nil || info.IsDir() || !testPattern.MatchString(path) {
167                         return nil
168                 }
169
170                 if debugging {
171                         fmt.Println("MATCHING:", path)
172                 }
173                 files = append(files, path)
174                 return nil
175         })
176         return files
177 }
178
179 func setBash() {
180         findcmd := "which"
181         if runtime.GOOS == "windows" {
182                 // Can't use paths returned from which even if it's on PATH in Windows
183                 // Because our Go binary is a separate Windows app & not MinGW, it
184                 // can't understand paths like '/usr/bin/bash', needs Windows version
185                 findcmd = "where"
186         }
187
188         out, err := exec.Command(findcmd, "bash").Output()
189         if err != nil {
190                 fmt.Println("Unable to find bash:", err)
191                 os.Exit(1)
192         }
193         if len(out) == 0 {
194                 fmt.Printf("No output from '%s bash'\n", findcmd)
195                 os.Exit(1)
196         }
197
198         bashPath = strings.TrimSpace(strings.Split(string(out), "\n")[0])
199         if debugging {
200                 fmt.Println("Using", bashPath)
201         }
202
203         // Test
204         _, err = exec.Command(bashPath, "--version").CombinedOutput()
205         if err != nil {
206                 fmt.Println("Error calling bash:", err)
207                 os.Exit(1)
208         }
209 }