godog/internal/builder/builder_test.go
Arianna Cooper 1a795f1dde
Fix issue#383 (#468)
* Add example about incorrect project structure

This is to help reproduce #383

* Added some debugging statements

* Update go.sum for example project

* Made a cmd_run_test.go file in order to test and run the builderAndRunGodog function in order to see it fail

* added new assertion test

* Matt and I added debugging

Co-authored-by: Matt Wynne <matt@mattwynne.net>

* Matt and I tried to logging through the cobra command

by using cmd.OutOrStdout( )

* Improved some debugging

* Add a failing test for Builder that reproduces #383

* added new test for IncorrectProjectStructure #383

* Revert "Add a failing test for Builder that reproduces #383"

This reverts commit e5b26933b5d4e979009f8f4341448fa8322720d2.

* ignored vscode files

Co-authored-by: Matt Wynne <matt@cucumber.io>

* undid debugging changes

* undid debugging changes

* removed redundant test

* added check for incorrect project structure

we examined the output from running `go test` which tells us if we didn't
find any test files.
we tweaked the error message to follow the capitalization rules

Co-authored-by: Matt Wynne <matt@cucumber.io>

* Update internal/builder/builder_test.go

Co-authored-by: Matt Wynne <matt@mattwynne.net>
Co-authored-by: Matt Wynne <matt@cucumber.io>
Co-authored-by: Viacheslav Poturaev <nanopeni@gmail.com>
2022-04-28 14:33:24 +02:00

362 строки
8,4 КиБ
Go

package builder_test
import (
"bytes"
"go/build"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/cucumber/godog"
"github.com/cucumber/godog/internal/builder"
)
func InitializeScenario(ctx *godog.ScenarioContext) {}
func Test_GodogBuild(t *testing.T) {
t.Run("WithSourceNotInGoPath", testWithSourceNotInGoPath)
t.Run("WithoutSourceNotInGoPath", testWithoutSourceNotInGoPath)
t.Run("WithoutTestSourceNotInGoPath", testWithoutTestSourceNotInGoPath)
t.Run("WithinGopath", testWithinGopath)
t.Run("WithVendoredGodogWithoutModule", testWithVendoredGodogWithoutModule)
t.Run("WithVendoredGodogAndMod", testWithVendoredGodogAndMod)
t.Run("WithIncorrectProjectStructure", testWithIncorrectProjectStructure)
t.Run("WithModule", func(t *testing.T) {
t.Run("OutsideGopathAndHavingOnlyFeature", testOutsideGopathAndHavingOnlyFeature)
t.Run("OutsideGopath", testOutsideGopath)
t.Run("OutsideGopathWithXTest", testOutsideGopathWithXTest)
t.Run("InsideGopath", testInsideGopath)
})
}
var builderFeatureFile = `Feature: eat godogs
In order to be happy
As a hungry gopher
I need to be able to eat godogs
Scenario: Eat 5 out of 12
Given there are 12 godogs
When I eat 5
Then there should be 7 remaining
`
var builderTestFile = `package godogs
import (
"fmt"
"github.com/cucumber/godog"
)
func thereAreGodogs(available int) error {
Godogs = available
return nil
}
func iEat(num int) error {
if Godogs < num {
return fmt.Errorf("you cannot eat %d godogs, there are %d available", num, Godogs)
}
Godogs -= num
return nil
}
func thereShouldBeRemaining(remaining int) error {
if Godogs != remaining {
return fmt.Errorf("expected %d godogs to be remaining, but there is %d", remaining, Godogs)
}
return nil
}
func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Step("^there are (\\d+) godogs$", thereAreGodogs)
ctx.Step("^I eat (\\d+)$", iEat)
ctx.Step("^there should be (\\d+) remaining$", thereShouldBeRemaining)
ctx.BeforeScenario(func(*godog.Scenario) {
Godogs = 0 // clean the state before every scenario
})
}
`
var builderXTestFile = `package godogs_test
import (
"fmt"
"github.com/cucumber/godog"
"godogs"
)
func thereAreGodogs(available int) error {
godogs.Godogs = available
return nil
}
func iEat(num int) error {
if godogs.Godogs < num {
return fmt.Errorf("you cannot eat %d godogs, there are %d available", num, godogs.Godogs)
}
godogs.Godogs -= num
return nil
}
func thereShouldBeRemaining(remaining int) error {
if godogs.Godogs != remaining {
return fmt.Errorf("expected %d godogs to be remaining, but there is %d", remaining, godogs.Godogs)
}
return nil
}
func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Step("^there are (\\d+) godogs$", thereAreGodogs)
ctx.Step("^I eat (\\d+)$", iEat)
ctx.Step("^there should be (\\d+) remaining$", thereShouldBeRemaining)
ctx.BeforeScenario(func(*godog.Scenario) {
godogs.Godogs = 0 // clean the state before every scenario
})
}
`
var builderMainCodeFile = `package godogs
// Godogs available to eat
var Godogs int
func main() {
}
`
var emptyBuilderTestFile = `package godogs
import "github.com/cucumber/godog"
func InitializeScenario(ctx *godog.ScenarioContext) {}
`
var builderModFile = `module godogs`
func buildTestPackage(dir string, files map[string]string) error {
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
for name, content := range files {
if err := ioutil.WriteFile(filepath.Join(dir, name), []byte(content), 0644); err != nil {
return err
}
}
return nil
}
func buildTestCommand(t *testing.T, wd, featureFile string) *exec.Cmd {
testBin := filepath.Join(wd, "godog.test")
testBin, err := filepath.Abs(testBin)
require.Nil(t, err)
if build.Default.GOOS == "windows" {
testBin += ".exe"
}
err = builder.Build(testBin)
require.Nil(t, err)
featureFilePath := filepath.Join(wd, featureFile)
return exec.Command(testBin, featureFilePath)
}
func envVarsWithoutGopath() []string {
var env []string
for _, def := range os.Environ() {
if strings.Index(def, "GOPATH=") == 0 {
continue
}
env = append(env, def)
}
return env
}
func testWithSourceNotInGoPath(t *testing.T) {
dir := filepath.Join(os.TempDir(), t.Name(), "godogs")
files := map[string]string{
"godogs.feature": builderFeatureFile,
"godogs.go": builderMainCodeFile,
"godogs_test.go": builderTestFile,
"go.mod": builderModFile,
}
err := buildTestPackage(dir, files)
defer os.RemoveAll(dir)
require.Nil(t, err)
prevDir, err := os.Getwd()
require.Nil(t, err)
err = os.Chdir(dir)
require.Nil(t, err)
defer os.Chdir(prevDir)
testCmd := buildTestCommand(t, "", "godogs.feature")
testCmd.Env = os.Environ()
var stdout, stderr bytes.Buffer
testCmd.Stdout = &stdout
testCmd.Stderr = &stderr
err = testCmd.Run()
require.Nil(t, err, "stdout:\n%s\nstderr:\n%s", stdout.String(), stderr.String())
}
func testWithoutSourceNotInGoPath(t *testing.T) {
builderTC := builderTestCase{}
builderTC.dir = filepath.Join(os.TempDir(), t.Name(), "godogs")
builderTC.files = map[string]string{
"godogs.feature": builderFeatureFile,
"go.mod": builderModFile,
}
builderTC.run(t)
}
func testWithoutTestSourceNotInGoPath(t *testing.T) {
builderTC := builderTestCase{}
builderTC.dir = filepath.Join(os.TempDir(), t.Name(), "godogs")
builderTC.files = map[string]string{
"godogs.feature": builderFeatureFile,
"godogs.go": builderMainCodeFile,
"go.mod": builderModFile,
}
builderTC.run(t)
}
func testWithIncorrectProjectStructure(t *testing.T) {
dir := filepath.Join(os.TempDir(), t.Name(), "godogs")
files := map[string]string{
"godogs.go": emptyBuilderTestFile,
"go.mod": builderModFile,
}
err := buildTestPackage(dir, files)
defer os.RemoveAll(dir)
require.Nil(t, err)
prevDir, err := os.Getwd()
require.Nil(t, err)
err = os.Chdir(dir)
require.Nil(t, err)
defer os.Chdir(prevDir)
testBin, err := filepath.Abs(filepath.Join(dir, "godog.test"))
require.Nil(t, err)
if build.Default.GOOS == "windows" {
testBin += ".exe"
}
// call the builder - we should get an error
err = builder.Build(testBin)
// check that we even got an error at all
require.NotNil(t, err)
// now check the details of the error message
require.EqualError(t, err, "incorrect project structure: no test files found")
}
func testWithinGopath(t *testing.T) {
builderTC := builderTestCase{}
gopath := filepath.Join(os.TempDir(), t.Name(), "_gp")
defer os.RemoveAll(gopath)
builderTC.dir = filepath.Join(gopath, "src", "godogs")
builderTC.files = map[string]string{
"godogs.feature": builderFeatureFile,
"godogs.go": builderMainCodeFile,
"godogs_test.go": builderTestFile,
"go.mod": builderModFile,
}
pkg := filepath.Join(gopath, "src", "github.com", "cucumber")
err := os.MkdirAll(pkg, 0755)
require.Nil(t, err)
wd, err := os.Getwd()
require.Nil(t, err)
// symlink godog package
err = os.Symlink(wd, filepath.Join(pkg, "godog"))
require.Nil(t, err)
builderTC.testCmdEnv = []string{"GOPATH=" + gopath}
builderTC.run(t)
}
func testWithVendoredGodogWithoutModule(t *testing.T) {
builderTC := builderTestCase{}
gopath := filepath.Join(os.TempDir(), t.Name(), "_gp")
defer os.RemoveAll(gopath)
builderTC.dir = filepath.Join(gopath, "src", "godogs")
builderTC.files = map[string]string{
"godogs.feature": builderFeatureFile,
}
pkg := filepath.Join(builderTC.dir, "vendor", "github.com", "cucumber")
err := os.MkdirAll(pkg, 0755)
require.Nil(t, err)
wd, err := os.Getwd()
require.Nil(t, err)
// symlink godog package
err = os.Symlink(wd, filepath.Join(pkg, "godog"))
require.Nil(t, err)
builderTC.testCmdEnv = append(envVarsWithoutGopath(), "GOPATH="+gopath)
builderTC.run(t)
}
type builderTestCase struct {
dir string
files map[string]string
goModCmds []*exec.Cmd
testCmdEnv []string
}
func (bt builderTestCase) run(t *testing.T) {
err := buildTestPackage(bt.dir, bt.files)
defer os.RemoveAll(bt.dir)
require.Nil(t, err)
for _, c := range bt.goModCmds {
c.Dir = bt.dir
out, err := c.CombinedOutput()
require.Nil(t, err, "%s", string(out))
}
testCmd := buildTestCommand(t, bt.dir, "godogs.feature")
testCmd.Env = append(os.Environ(), bt.testCmdEnv...)
var stdout, stderr bytes.Buffer
testCmd.Stdout = &stdout
testCmd.Stderr = &stderr
err = testCmd.Run()
require.Nil(t, err, "stdout:\n%s\nstderr:\n%s", stdout.String(), stderr.String())
}