Skip to content

Commit 5a22ffe

Browse files
Add option to split files by line count
1 parent 7ff9693 commit 5a22ffe

2 files changed

Lines changed: 63 additions & 12 deletions

File tree

line_count.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"io"
6+
"os"
7+
)
8+
9+
func estimateFileTimesByLineCount(currentFileSet map[string]bool, fileTimes map[string]float64) {
10+
for fileName := range currentFileSet {
11+
file, err := os.Open(fileName)
12+
if err != nil {
13+
printMsg("failed to count lines in file %s: %v\n", file, err)
14+
continue
15+
}
16+
defer file.Close()
17+
lineCount, err := lineCounter(file)
18+
if err != nil {
19+
printMsg("failed to count lines in file %s: %v\n", file, err)
20+
continue
21+
}
22+
fileTimes[fileName] = float64(lineCount)
23+
}
24+
}
25+
26+
// Credit to http://stackoverflow.com/a/24563853/6678
27+
func lineCounter(r io.Reader) (int, error) {
28+
buf := make([]byte, 32*1024)
29+
count := 0
30+
lineSep := []byte{'\n'}
31+
32+
for {
33+
c, err := r.Read(buf)
34+
count += bytes.Count(buf[:c], lineSep)
35+
36+
switch {
37+
case err == io.EOF:
38+
return count, nil
39+
40+
case err != nil:
41+
return count, err
42+
}
43+
}
44+
}

split_tests.go

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
var useCircleCI bool
1414
var useJUnitXML bool
15+
var useLineCount bool
1516
var junitXMLPath string
1617
var testFilePattern = ""
1718
var circleCIProjectPrefix = ""
@@ -56,7 +57,9 @@ func addNewFiles(fileTimes map[string]float64, currentFileSet map[string]bool) {
5657
if _, isSet := fileTimes[file]; isSet {
5758
continue
5859
}
59-
printMsg("missing file time for %s\n", file)
60+
if useCircleCI || useJUnitXML {
61+
printMsg("missing file time for %s\n", file)
62+
}
6063
fileTimes[file] = averageFileTime
6164
}
6265
}
@@ -94,12 +97,14 @@ func parseFlags() {
9497
flag.IntVar(&splitTotal, "split-total", -1, "Total number of containers (or set CIRCLE_NODE_TOTAL)")
9598

9699
flag.StringVar(&circleCIAPIKey, "circleci-key", "", "CircleCI API key (or set CIRCLECI_API_KEY environment variable) - required to use CircleCI")
97-
flag.StringVar(&circleCIProjectPrefix, "circleci-project", "", "CircleCI project name (e.g. github/leonid-shevtsov/circleci-test-balancer) - required to use CircleCI")
100+
flag.StringVar(&circleCIProjectPrefix, "circleci-project", "", "CircleCI project name (e.g. github/leonid-shevtsov/split_tests) - required to use CircleCI")
98101
flag.StringVar(&circleCIBranchName, "circleci-branch", "", "Current branch for CircleCI (or set CIRCLE_BRANCH) - required to use CircleCI")
99102

100103
flag.BoolVar(&useJUnitXML, "junit", false, "Use a JUnit XML report for test times")
101104
flag.StringVar(&junitXMLPath, "junit-path", "", "Path to a JUnit XML report (leave empty to read from stdin)")
102105

106+
flag.BoolVar(&useLineCount, "line-count", false, "Use line count to estimate test times")
107+
103108
var showHelp bool
104109
flag.BoolVar(&showHelp, "help", false, "Show this help text")
105110

@@ -126,7 +131,6 @@ func parseFlags() {
126131
}
127132

128133
useCircleCI = circleCIAPIKey != ""
129-
showHelp = showHelp || !useCircleCI && !useJUnitXML
130134

131135
if showHelp {
132136
printMsg("Splits test files into containers of even duration\n\n")
@@ -144,25 +148,28 @@ func parseFlags() {
144148
func main() {
145149
parseFlags()
146150

147-
fileTimes := make(map[string]float64)
148-
if useJUnitXML {
149-
getFileTimesFromJUnitXML(fileTimes)
150-
} else if useCircleCI {
151-
getFileTimesFromCircleCI(fileTimes)
152-
}
153-
154151
currentFiles, _ := zglob.Glob(testFilePattern)
155152
currentFileSet := make(map[string]bool)
156-
157153
for _, file := range currentFiles {
158154
currentFileSet[file] = true
159155
}
160156

157+
fileTimes := make(map[string]float64)
158+
if useLineCount {
159+
estimateFileTimesByLineCount(currentFileSet, fileTimes)
160+
} else if useJUnitXML {
161+
getFileTimesFromJUnitXML(fileTimes)
162+
} else if useCircleCI {
163+
getFileTimesFromCircleCI(fileTimes)
164+
}
165+
161166
removeDeletedFiles(fileTimes, currentFileSet)
162167
addNewFiles(fileTimes, currentFileSet)
163168

164169
buckets, bucketTimes := splitFiles(fileTimes, splitTotal)
165-
printMsg("expected test time: %0.1fs\n", bucketTimes[splitIndex])
170+
if useCircleCI || useJUnitXML {
171+
printMsg("expected test time: %0.1fs\n", bucketTimes[splitIndex])
172+
}
166173

167174
fmt.Println(strings.Join(buckets[splitIndex], " "))
168175
}

0 commit comments

Comments
 (0)