blob: 4bd5bab7c38b23bd07a7b1b17819c11e88c39eb2 [file] [log] [blame]
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package generator
import (
"bytes"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
"golang.org/x/tools/internal/testenv"
)
func mkGenState() *genstate {
return &genstate{
GenConfig: GenConfig{
Tag: "gen",
OutDir: "/tmp",
NumTestPackages: 1,
NumTestFunctions: 10,
},
ipref: "foo/",
derefFuncs: make(map[string]string),
assignFuncs: make(map[string]string),
allocFuncs: make(map[string]string),
globVars: make(map[string]string),
}
}
func TestBasic(t *testing.T) {
checkTunables(tunables)
s := mkGenState()
for i := 0; i < 1000; i++ {
s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
fp := s.GenFunc(i, i)
var buf bytes.Buffer
var b *bytes.Buffer = &buf
wr := NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
s.wr = wr
s.emitCaller(fp, b, i)
s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
s.emitChecker(fp, b, i, true)
wr.Check(s.wr)
}
if s.errs != 0 {
t.Errorf("%d errors during Generate", s.errs)
}
}
func TestMoreComplicated(t *testing.T) {
saveit := tunables
defer func() { tunables = saveit }()
checkTunables(tunables)
s := mkGenState()
for i := 0; i < 10000; i++ {
s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
fp := s.GenFunc(i, i)
var buf bytes.Buffer
var b *bytes.Buffer = &buf
wr := NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
s.wr = wr
s.emitCaller(fp, b, i)
verb(1, "finished iter %d caller", i)
s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic)
s.emitChecker(fp, b, i, true)
verb(1, "finished iter %d checker", i)
wr.Check(s.wr)
if s.errs != 0 {
t.Errorf("%d errors during Generate iter %d", s.errs, i)
}
}
}
func TestIsBuildable(t *testing.T) {
testenv.NeedsTool(t, "go")
if runtime.GOOS == "android" {
t.Skipf("the dependencies are not available on android")
}
td := t.TempDir()
verb(1, "generating into temp dir %s", td)
checkTunables(tunables)
pack := filepath.Base(td)
s := GenConfig{
Tag: "x",
OutDir: td,
PkgPath: pack,
NumTestFunctions: 10,
NumTestPackages: 10,
MaxFail: 10,
RandCtl: RandCtlChecks | RandCtlPanic,
}
errs := Generate(s)
if errs != 0 {
t.Errorf("%d errors during Generate", errs)
}
verb(1, "building %s\n", td)
cmd := exec.Command("go", "run", ".")
cmd.Dir = td
coutput, cerr := cmd.CombinedOutput()
if cerr != nil {
t.Errorf("go build command failed: %s\n", string(coutput))
}
verb(1, "output is: %s\n", string(coutput))
}
// TestExhaustive does a series of code genreation runs, starting with
// (relatively) simple code and then getting progressively more
// complex (more params, deeper structs, turning on additional
// features such as address-taken vars and reflect testing). The
// intent here is mainly to insure that the tester still works if you
// turn things on and off, e.g. that each feature is separately
// controllable and not linked to other things.
func TestExhaustive(t *testing.T) {
testenv.NeedsTool(t, "go")
if runtime.GOOS == "android" {
t.Skipf("the dependencies are not available on android")
}
if testing.Short() {
t.Skip("skipping test in short mode.")
}
td := t.TempDir()
verb(1, "generating into temp dir %s", td)
scenarios := []struct {
name string
adjuster func()
}{
{
"minimal",
func() {
tunables.nParmRange = 3
tunables.nReturnRange = 3
tunables.structDepth = 1
tunables.recurPerc = 0
tunables.methodPerc = 0
tunables.doReflectCall = false
tunables.doDefer = false
tunables.takeAddress = false
tunables.doFuncCallValues = false
tunables.doSkipCompare = false
checkTunables(tunables)
},
},
{
"moreparms",
func() {
tunables.nParmRange = 15
tunables.nReturnRange = 7
tunables.structDepth = 3
checkTunables(tunables)
},
},
{
"addrecur",
func() {
tunables.recurPerc = 20
checkTunables(tunables)
},
},
{
"addmethod",
func() {
tunables.methodPerc = 25
tunables.pointerMethodCallPerc = 30
checkTunables(tunables)
},
},
{
"addtakeaddr",
func() {
tunables.takeAddress = true
tunables.takenFraction = 20
checkTunables(tunables)
},
},
{
"addreflect",
func() {
tunables.doReflectCall = true
checkTunables(tunables)
},
},
{
"adddefer",
func() {
tunables.doDefer = true
checkTunables(tunables)
},
},
{
"addfuncval",
func() {
tunables.doFuncCallValues = true
checkTunables(tunables)
},
},
{
"addfuncval",
func() {
tunables.doSkipCompare = true
checkTunables(tunables)
},
},
}
// Loop over scenarios and make sure each one works properly.
for i, s := range scenarios {
t.Logf("running %s\n", s.name)
s.adjuster()
os.RemoveAll(td)
pack := filepath.Base(td)
c := GenConfig{
Tag: "x",
OutDir: td,
PkgPath: pack,
NumTestFunctions: 10,
NumTestPackages: 10,
Seed: int64(i + 9),
MaxFail: 10,
RandCtl: RandCtlChecks | RandCtlPanic,
}
errs := Generate(c)
if errs != 0 {
t.Errorf("%d errors during scenarios %q Generate", errs, s.name)
}
cmd := exec.Command("go", "run", ".")
cmd.Dir = td
coutput, cerr := cmd.CombinedOutput()
if cerr != nil {
t.Fatalf("run failed for scenario %q: %s\n", s.name, string(coutput))
}
verb(1, "output is: %s\n", string(coutput))
}
}
func TestEmitBadBuildFailure(t *testing.T) {
testenv.NeedsTool(t, "go")
if runtime.GOOS == "android" {
t.Skipf("the dependencies are not available on android")
}
td := t.TempDir()
verb(1, "generating into temp dir %s", td)
checkTunables(tunables)
pack := filepath.Base(td)
s := GenConfig{
Tag: "x",
OutDir: td,
PkgPath: pack,
NumTestFunctions: 10,
NumTestPackages: 10,
MaxFail: 10,
RandCtl: RandCtlChecks | RandCtlPanic,
EmitBad: 1,
}
errs := Generate(s)
if errs != 0 {
t.Errorf("%d errors during Generate", errs)
}
cmd := exec.Command("go", "build", ".")
cmd.Dir = td
coutput, cerr := cmd.CombinedOutput()
if cerr == nil {
t.Errorf("go build command passed, expected failure. output: %s\n", string(coutput))
}
}
func TestEmitBadRunFailure(t *testing.T) {
testenv.NeedsTool(t, "go")
if runtime.GOOS == "android" {
t.Skipf("the dependencies are not available on android")
}
td := t.TempDir()
verb(1, "generating into temp dir %s", td)
checkTunables(tunables)
pack := filepath.Base(td)
s := GenConfig{
Tag: "x",
OutDir: td,
PkgPath: pack,
NumTestFunctions: 10,
NumTestPackages: 10,
MaxFail: 10,
RandCtl: RandCtlChecks | RandCtlPanic,
EmitBad: 2,
}
errs := Generate(s)
if errs != 0 {
t.Errorf("%d errors during Generate", errs)
}
// build
cmd := exec.Command("go", "build", ".")
cmd.Dir = td
coutput, cerr := cmd.CombinedOutput()
if cerr != nil {
t.Fatalf("build failed: %s\n", string(coutput))
}
// run
cmd = exec.Command("./" + pack)
cmd.Dir = td
coutput, cerr = cmd.CombinedOutput()
if cerr == nil {
t.Fatalf("run passed, expected failure -- run output: %s", string(coutput))
}
}