Skip to content

Commit 228e3a8

Browse files
Alexey SoshinOnsi Fakhouri
authored andcommitted
Handle -outputdir flag (#364)
* Handle -coverprofile flag Fixes #346 * Add tool for easier contribution * Cast byte[], otherwise failure will be displayed as array of bytes, not very useful * Fix combine not finding files if coverProfile was set * Add test for recursive coverage * Fix test imports to point to correct subpackages * Refactor casts to be semantic functions * Add test for cover profile in parallel mode * Add failing test for outputdir case * Implement outputdir * Add test for moving files to output dir
1 parent 43392d5 commit 228e3a8

File tree

3 files changed

+174
-17
lines changed

3 files changed

+174
-17
lines changed

ginkgo/run_command.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"github.com/onsi/ginkgo/ginkgo/interrupthandler"
1212
"github.com/onsi/ginkgo/ginkgo/testrunner"
1313
"github.com/onsi/ginkgo/types"
14+
"io/ioutil"
15+
"path/filepath"
1416
)
1517

1618
func BuildRunCommand() *Command {
@@ -100,6 +102,18 @@ func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) {
100102
runResult, numSuites = r.suiteRunner.RunSuites(randomizedRunners, r.commandFlags.NumCompilers, r.commandFlags.KeepGoing, nil)
101103
}
102104

105+
if r.isInCoverageMode() {
106+
if r.getOutputDir() != "" {
107+
// If coverprofile is set, combine coverages
108+
if r.getCoverprofile() != "" {
109+
r.combineCoverprofiles(runners)
110+
} else {
111+
// Just move them
112+
r.moveCoverprofiles(runners)
113+
}
114+
}
115+
}
116+
103117
for _, runner := range runners {
104118
runner.CleanUp()
105119
}
@@ -121,6 +135,67 @@ func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) {
121135
}
122136
}
123137

138+
// Moves all generated profiles to specified directory
139+
func (r *SpecRunner) moveCoverprofiles(runners []*testrunner.TestRunner) {
140+
for _, runner := range runners {
141+
_, filename := filepath.Split(runner.CoverageFile)
142+
err := os.Rename(runner.CoverageFile, filepath.Join(r.getOutputDir(), filename))
143+
144+
if err != nil {
145+
fmt.Printf("Unable to move coverprofile %s, %v\n", runner.CoverageFile, err)
146+
return
147+
}
148+
}
149+
}
150+
151+
// Combines all generated profiles in the specified directory
152+
func (r *SpecRunner) combineCoverprofiles(runners []*testrunner.TestRunner) {
153+
154+
path, _ := filepath.Abs(r.getOutputDir())
155+
156+
fmt.Println("path is " + path)
157+
os.MkdirAll(path, os.ModePerm)
158+
159+
combined, err := os.OpenFile(filepath.Join(path, r.getCoverprofile()),
160+
os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666)
161+
162+
if err != nil {
163+
fmt.Printf("Unable to create combined profile, %v\n", err)
164+
return
165+
}
166+
167+
for _, runner := range runners {
168+
contents, err := ioutil.ReadFile(runner.CoverageFile)
169+
170+
if err != nil {
171+
fmt.Printf("Unable to read coverage file %s to combine, %v\n", runner.CoverageFile, err)
172+
return
173+
}
174+
175+
_, err = combined.Write(contents)
176+
177+
if err != nil {
178+
fmt.Printf("Unable to append to coverprofile, %v\n", err)
179+
return
180+
}
181+
}
182+
183+
fmt.Println("All profiles combined")
184+
}
185+
186+
func (r *SpecRunner) isInCoverageMode() bool {
187+
opts := r.commandFlags.GoOpts
188+
return *opts["cover"].(*bool) || *opts["coverpkg"].(*string) != "" || *opts["covermode"].(*string) != ""
189+
}
190+
191+
func (r *SpecRunner) getCoverprofile() string {
192+
return *r.commandFlags.GoOpts["coverprofile"].(*string)
193+
}
194+
195+
func (r *SpecRunner) getOutputDir() string {
196+
return *r.commandFlags.GoOpts["outputdir"].(*string)
197+
}
198+
124199
func (r *SpecRunner) ComputeSuccinctMode(numSuites int) {
125200
if config.DefaultReporterConfig.Verbose {
126201
config.DefaultReporterConfig.Succinct = false

ginkgo/testrunner/test_runner.go

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ type TestRunner struct {
3232
goOpts map[string]interface{}
3333
additionalArgs []string
3434
stderr *bytes.Buffer
35+
36+
CoverageFile string
3537
}
3638

3739
func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, timeout time.Duration, goOpts map[string]interface{}, additionalArgs []string) *TestRunner {
@@ -63,10 +65,10 @@ func (t *TestRunner) Compile() error {
6365
func (t *TestRunner) BuildArgs(path string) []string {
6466
args := []string{"test", "-c", "-i", "-o", path, t.Suite.Path}
6567

66-
if *t.goOpts["covermode"].(*string) != "" {
67-
args = append(args, "-cover", fmt.Sprintf("-covermode=%s", *t.goOpts["covermode"].(*string)))
68+
if t.getCoverMode() != "" {
69+
args = append(args, "-cover", fmt.Sprintf("-covermode=%s", t.getCoverMode()))
6870
} else {
69-
if *t.goOpts["cover"].(*bool) || *t.goOpts["coverpkg"].(*string) != "" {
71+
if t.shouldCover() || t.getCoverPackage() != "" {
7072
args = append(args, "-cover", "-covermode=atomic")
7173
}
7274
}
@@ -298,7 +300,7 @@ func (t *TestRunner) runAndStreamParallelGinkgoSuite() RunResult {
298300

299301
os.Stdout.Sync()
300302

301-
if *t.goOpts["cover"].(*bool) || *t.goOpts["coverpkg"].(*string) != "" || *t.goOpts["covermode"].(*string) != "" {
303+
if t.shouldCombineCoverprofiles() {
302304
t.combineCoverprofiles()
303305
}
304306

@@ -380,7 +382,7 @@ func (t *TestRunner) runParallelGinkgoSuite() RunResult {
380382
os.Stdout.Sync()
381383
}
382384

383-
if *t.goOpts["cover"].(*bool) || *t.goOpts["coverpkg"].(*string) != "" || *t.goOpts["covermode"].(*string) != "" {
385+
if t.shouldCombineCoverprofiles() {
384386
t.combineCoverprofiles()
385387
}
386388

@@ -392,21 +394,24 @@ const CoverProfileSuffix = ".coverprofile"
392394
func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.Cmd {
393395
args := []string{"--test.timeout=" + t.timeout.String()}
394396

395-
coverMode := *t.goOpts["covermode"].(*string)
396-
coverPackage := *t.goOpts["coverpkg"].(*string)
397-
cover := *t.goOpts["cover"].(*bool)
398-
coverProfile := *t.goOpts["coverprofile"].(*string)
397+
coverProfile := t.getCoverProfile()
398+
399+
if t.shouldCombineCoverprofiles() {
399400

400-
if cover || coverPackage != "" || coverMode != "" {
401401
testCoverProfile := "--test.coverprofile="
402402

403+
coverageFile := ""
403404
// Set default name for coverage results
404405
if coverProfile == "" {
405-
testCoverProfile += t.Suite.PackageName + CoverProfileSuffix
406+
coverageFile = t.Suite.PackageName + CoverProfileSuffix
406407
} else {
407-
testCoverProfile += coverProfile
408+
coverageFile = coverProfile
408409
}
409410

411+
testCoverProfile += coverageFile
412+
413+
t.CoverageFile = filepath.Join(t.Suite.Path, coverageFile)
414+
410415
if t.numCPU > 1 {
411416
testCoverProfile = fmt.Sprintf("%s.%d", testCoverProfile, node)
412417
}
@@ -430,6 +435,30 @@ func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.
430435
return cmd
431436
}
432437

438+
func (t *TestRunner) shouldCover() bool {
439+
return *t.goOpts["cover"].(*bool)
440+
}
441+
442+
func (t *TestRunner) shouldRequireSuite() bool {
443+
return *t.goOpts["requireSuite"].(*bool)
444+
}
445+
446+
func (t *TestRunner) getCoverProfile() string {
447+
return *t.goOpts["coverprofile"].(*string)
448+
}
449+
450+
func (t *TestRunner) getCoverPackage() string {
451+
return *t.goOpts["coverpkg"].(*string)
452+
}
453+
454+
func (t *TestRunner) getCoverMode() string {
455+
return *t.goOpts["covermode"].(*string)
456+
}
457+
458+
func (t *TestRunner) shouldCombineCoverprofiles() bool {
459+
return t.shouldCover() || t.getCoverPackage() != "" || t.getCoverMode() != ""
460+
}
461+
433462
func (t *TestRunner) run(cmd *exec.Cmd, completions chan RunResult) RunResult {
434463
var res RunResult
435464

@@ -452,7 +481,7 @@ func (t *TestRunner) run(cmd *exec.Cmd, completions chan RunResult) RunResult {
452481
res.HasProgrammaticFocus = (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
453482

454483
if strings.Contains(t.stderr.String(), "warning: no tests to run") {
455-
if *t.goOpts["requireSuite"].(*bool) {
484+
if t.shouldRequireSuite() {
456485
res.Passed = false
457486
}
458487
fmt.Fprintf(os.Stderr, `Found no test suites, did you forget to run "ginkgo bootstrap"?`)
@@ -464,7 +493,7 @@ func (t *TestRunner) run(cmd *exec.Cmd, completions chan RunResult) RunResult {
464493
func (t *TestRunner) combineCoverprofiles() {
465494
profiles := []string{}
466495

467-
coverProfile := *t.goOpts["coverprofile"].(*string)
496+
coverProfile := t.getCoverProfile()
468497

469498
for cpu := 1; cpu <= t.numCPU; cpu++ {
470499
var coverFile string
@@ -518,6 +547,8 @@ func (t *TestRunner) combineCoverprofiles() {
518547
finalFilename = fmt.Sprintf("%s%s", t.Suite.PackageName, CoverProfileSuffix)
519548
}
520549

521-
ioutil.WriteFile(filepath.Join(t.Suite.Path, finalFilename),
522-
[]byte(finalOutput), 0666)
550+
coverageFilepath := filepath.Join(t.Suite.Path, finalFilename)
551+
ioutil.WriteFile(coverageFilepath, []byte(finalOutput), 0666)
552+
553+
t.CoverageFile = coverageFilepath
523554
}

integration/coverage_test.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import (
44
"os"
55
"os/exec"
66

7+
"fmt"
78
. "github.com/onsi/ginkgo"
89
. "github.com/onsi/gomega"
910
"github.com/onsi/gomega/gexec"
10-
"fmt"
1111
)
1212

1313
var _ = Describe("Coverage Specs", func() {
@@ -97,4 +97,55 @@ var _ = Describe("Coverage Specs", func() {
9797
// Cleanup
9898
os.RemoveAll(coverFile)
9999
})
100+
101+
It("Appends coverages if output dir and coverprofile were set", func() {
102+
session := startGinkgo("./_fixtures/combined_coverage_fixture", "-outputdir=./", "-r", "-cover", "-coverprofile=coverage.txt")
103+
104+
Eventually(session).Should(gexec.Exit(0))
105+
106+
_, err := os.Stat("./_fixtures/combined_coverage_fixture/coverage.txt")
107+
108+
Ω(err).ShouldNot(HaveOccurred())
109+
110+
// Cleanup
111+
os.RemoveAll("./_fixtures/combined_coverage_fixture/coverage.txt")
112+
})
113+
114+
It("Creates directories in path if they don't exist", func() {
115+
session := startGinkgo("./_fixtures/combined_coverage_fixture", "-outputdir=./all/profiles/here", "-r", "-cover", "-coverprofile=coverage.txt")
116+
117+
defer os.RemoveAll("./_fixtures/combined_coverage_fixture/all")
118+
defer os.RemoveAll("./_fixtures/combined_coverage_fixture/coverage.txt")
119+
120+
Eventually(session).Should(gexec.Exit(0))
121+
122+
_, err := os.Stat("./_fixtures/combined_coverage_fixture/all/profiles/here/coverage.txt")
123+
124+
Ω(err).ShouldNot(HaveOccurred())
125+
})
126+
127+
It("Moves coverages if only output dir was set", func() {
128+
session := startGinkgo("./_fixtures/combined_coverage_fixture", "-outputdir=./", "-r", "-cover")
129+
130+
Eventually(session).Should(gexec.Exit(0))
131+
132+
packages := []string{"first_package", "second_package"}
133+
134+
for _, p := range packages {
135+
coverFile := fmt.Sprintf("./_fixtures/combined_coverage_fixture/%s.coverprofile", p)
136+
137+
// Cleanup
138+
defer func (f string) {
139+
os.RemoveAll(f)
140+
} (coverFile)
141+
142+
defer func (f string) {
143+
os.RemoveAll(fmt.Sprintf("./_fixtures/combined_coverage_fixture/%s/coverage.txt", f))
144+
} (p)
145+
146+
_, err := os.Stat(coverFile)
147+
148+
Ω(err).ShouldNot(HaveOccurred())
149+
}
150+
})
100151
})

0 commit comments

Comments
 (0)