blob: 92f59ba5328eb671c43532808f4fffe61f4bdb28 [file]
// Copyright 2020 The Chromium OS 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 genparams
import (
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/kylelemons/godebug/diff"
"chromiumos/tast/testutil"
)
// fakeTestingT is a fake implementation of TestingT.
// The zero value of fakeTestingT is ready to use.
type fakeTestingT struct {
// Errors is a list of error messages reported so far.
Errors []string
}
func (t *fakeTestingT) Helper() {}
func (t *fakeTestingT) Logf(format string, args ...interface{}) {}
func (t *fakeTestingT) Errorf(format string, args ...interface{}) {
t.Errors = append(t.Errors, fmt.Sprintf(format, args...))
}
func (t *fakeTestingT) Fatalf(format string, args ...interface{}) {
t.Errorf(format, args...)
panic("fakeTestingT.Fatalf called")
}
// callEnsure is a helper function to call Ensure.
// It calls Ensure twice, without TAST_GENERATE_UPDATE and with
// TAST_GENERATE_UPDATE. It returns errors reported from the first call and
// generated code from the second call.
func callEnsure(t *testing.T, code, params string) (errors []string, newCode string) {
// Create a temporary dir and chdir to it to make error messages deterministic.
wd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
td := testutil.TempDir(t)
defer os.RemoveAll(td)
os.Chdir(td)
defer os.Chdir(wd)
const filename = "code.go"
if err := testutil.WriteFiles(td, map[string]string{
filename: code,
}); err != nil {
t.Fatal(t)
}
origEnv := os.Getenv(envName)
defer os.Setenv(envName, origEnv)
var ft0 fakeTestingT
// Return errors even if ft0.Fatalf is called.
defer func() {
if recover() != nil {
errors = ft0.Errors
}
}()
// First, call Ensure in the check mode.
os.Setenv(envName, "")
Ensure(&ft0, filename, params)
// Second, call Ensure in the update mode.
var ft1 fakeTestingT
os.Setenv(envName, "1")
Ensure(&ft1, filename, params)
if len(ft1.Errors) > 0 {
t.Errorf("Ensure failed: %v", ft1.Errors)
}
newBytes, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatal(err)
}
return ft0.Errors, string(newBytes)
}
const (
testCode = `package example
import "chromiumos/tast/testing"
func init() {
testing.AddTest(&testing.Test{
Name: "Test",
Params: []testing.Param{
// Parameters generated by params_test.go. DO NOT EDIT.
{Name: "a", Val: 1},
{Name: "b", Val: 2},
},
})
}
`
testParams = `{Name: "a", Val: 1},
{Name: "b", Val: 2},
`
)
func TestEnsureNoChange(t *testing.T) {
errors, newCode := callEnsure(t, testCode, testParams)
if diff := cmp.Diff(errors, []string(nil)); diff != "" {
t.Errorf("Errors mismatch (-got +want):\n%s", diff)
}
if diff := diff.Diff(newCode, testCode); diff != "" {
t.Errorf("Generated code mismatch (-got +want):\n%s", diff)
}
}
func TestEnsureChange(t *testing.T) {
const oldCode = `package example
import "chromiumos/tast/testing"
func init() {
testing.AddTest(&testing.Test{
Name: "Test",
Params: []testing.Param{
{Name: "x", Val: 28},
},
})
}
`
errors, newCode := callEnsure(t, oldCode, testParams)
wantErrors := []string{
`code.go: Params is stale; run the following command to update:
TAST_GENERATE_UPDATE=1 ~/trunk/src/platform/tast/tools/go.sh test -count=1 chromiumos/tast/common/genparams`,
}
if diff := cmp.Diff(errors, wantErrors); diff != "" {
t.Errorf("Errors mismatch (-got +want):\n%s", diff)
}
if diff := diff.Diff(newCode, testCode); diff != "" {
t.Errorf("Generated code mismatch (-got +want):\n%s", diff)
}
}
func TestEnsureMissingParams(t *testing.T) {
const oldCode = `package example
import "chromiumos/tast/testing"
func init() {
testing.AddTest(&testing.Test{
Name: "Test",
})
}
`
errors, newCode := callEnsure(t, oldCode, testParams)
wantErrors := []string{
`code.go: Params is stale; run the following command to update:
TAST_GENERATE_UPDATE=1 ~/trunk/src/platform/tast/tools/go.sh test -count=1 chromiumos/tast/common/genparams`,
}
if diff := cmp.Diff(errors, wantErrors); diff != "" {
t.Errorf("Errors mismatch (-got +want):\n%s", diff)
}
if diff := diff.Diff(newCode, testCode); diff != "" {
t.Errorf("Generated code mismatch (-got +want):\n%s", diff)
}
}
func TestEnsureNoRegistration(t *testing.T) {
const oldCode = `package example
func init() {
setUpSomething()
}
`
errors, _ := callEnsure(t, oldCode, testParams)
wantErrors := []string{
"code.go: found 0 testing.AddTest calls; want 1",
}
if diff := cmp.Diff(errors, wantErrors); diff != "" {
t.Errorf("Errors mismatch (-got +want):\n%s", diff)
}
}
func TestEnsureDuplicatedRegistration(t *testing.T) {
const oldCode = `package example
func init() {
testing.AddTest(&testing.Test{
Name: "Test1",
})
testing.AddTest(&testing.Test{
Name: "Test2",
})
}
`
errors, _ := callEnsure(t, oldCode, testParams)
wantErrors := []string{
"code.go: found 2 testing.AddTest calls; want 1",
}
if diff := cmp.Diff(errors, wantErrors); diff != "" {
t.Errorf("Errors mismatch (-got +want):\n%s", diff)
}
}
func TestEnsureTestingAlias(t *testing.T) {
const oldCode = `package example
import mytesting "chromiumos/tast/testing"
func init() {
mytesting.AddTest(&mytesting.Test{
Name: "Test",
})
}
`
errors, _ := callEnsure(t, oldCode, testParams)
wantErrors := []string{
"chromiumos/tast/testing must be imported without alias",
}
if diff := cmp.Diff(errors, wantErrors); diff != "" {
t.Errorf("Errors mismatch (-got +want):\n%s", diff)
}
}