Files
kustomize/kyaml/fn/framework/frameworktestutil/frameworktestutil.go
Phillip Wittrock 82b2d83ede fn/framework support for reading from stdin in standalone
- speficy '-' to read from stdin when running standalone
2020-11-21 09:17:26 -08:00

186 lines
4.9 KiB
Go

// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package frameworktestutil contains utilities for testing functions written using the framework.
package frameworktestutil
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)
// ResultsChecker tests a function by running it with predefined inputs and comparing
// the outputs to expected results.
type ResultsChecker struct {
// TestDataDirectory is the directory containing the testdata subdirectories.
// ResultsChecker will recurse into each test directory and run the Command
// if the directory contains both the ConfigInputFilename and at least one
// of ExpectedOutputFilname or ExpectedErrorFilename.
// Defaults to "testdata"
TestDataDirectory string
// ConfigInputFilename is the name of the config file provided as the first
// argument to the function. Directories without this file will be skipped.
// Defaults to "config.yaml"
ConfigInputFilename string
// InputFilenameGlob matches function inputs
// Defaults to "input*.yaml"
InputFilenameGlob string
// ExpectedOutputFilename is the file with the expected output of the function
// Defaults to "expected.yaml". Directories containing neither this file
// nore ExpectedErrorFilename will be skipped.
ExpectedOutputFilename string
// ExpectedErrorFilename is the file containing part of an expected error message
// Defaults to "error.yaml". Directories containing neither this file
// nore ExpectedOutputFilname will be skipped.
ExpectedErrorFilename string
// Command provides the function to run.
Command func() *cobra.Command
}
// Assert asserts the results for functions
func (rc ResultsChecker) Assert(t *testing.T) bool {
if rc.TestDataDirectory == "" {
rc.TestDataDirectory = "testdata"
}
if rc.ConfigInputFilename == "" {
rc.ConfigInputFilename = "config.yaml"
}
if rc.ExpectedOutputFilename == "" {
rc.ExpectedOutputFilename = "expected.yaml"
}
if rc.ExpectedErrorFilename == "" {
rc.ExpectedErrorFilename = "error.yaml"
}
if rc.InputFilenameGlob == "" {
rc.InputFilenameGlob = "input*.yaml"
}
_ = filepath.Walk(rc.TestDataDirectory, func(path string, info os.FileInfo, err error) error {
if err != nil {
t.FailNow()
}
if !info.IsDir() {
// skip non-directories
return nil
}
rc.compare(t, path)
return nil
})
return true
}
func (rc ResultsChecker) compare(t *testing.T, path string) {
// cd into the directory so we can test functions that refer
// local files by relative paths
d, err := os.Getwd()
if !assert.NoError(t, err) {
t.FailNow()
}
defer func() { _ = os.Chdir(d) }()
if !assert.NoError(t, os.Chdir(path)) {
t.FailNow()
}
// make sure this directory contains test data
_, err = os.Stat(rc.ConfigInputFilename)
if os.IsNotExist(err) {
// missing input
return
}
args := []string{rc.ConfigInputFilename}
expectedOutput, expectedError := rc.getExpected(t)
if expectedError == "" && expectedOutput == "" {
// missing expected
return
}
if !assert.NoError(t, err) {
t.FailNow()
}
// run the test
t.Run(path, func(t *testing.T) {
if rc.InputFilenameGlob != "" {
inputs, err := filepath.Glob(rc.InputFilenameGlob)
if !assert.NoError(t, err) {
t.FailNow()
}
args = append(args, inputs...)
}
var actualOutput, actualError bytes.Buffer
cmd := rc.Command()
cmd.SetArgs(args)
cmd.SetOut(&actualOutput)
cmd.SetErr(&actualError)
err = cmd.Execute()
// Compae the results
if expectedError != "" && !assert.Error(t, err, actualOutput.String()) {
t.FailNow()
}
if expectedError == "" && !assert.NoError(t, err, actualError.String()) {
t.FailNow()
}
if !assert.Equal(t,
strings.TrimSpace(expectedOutput),
strings.TrimSpace(actualOutput.String()), actualError.String()) {
t.FailNow()
}
if !assert.Contains(t,
strings.TrimSpace(actualError.String()),
strings.TrimSpace(expectedError), actualOutput.String()) {
t.FailNow()
}
})
}
// getExpected reads the expected results and error files
func (rc ResultsChecker) getExpected(t *testing.T) (string, string) {
// read the expected results
var expectedOutput, expectedError string
if rc.ExpectedOutputFilename != "" {
_, err := os.Stat(rc.ExpectedOutputFilename)
if !os.IsNotExist(err) && err != nil {
t.FailNow()
}
if err == nil {
// only read the file if it exists
b, err := ioutil.ReadFile(rc.ExpectedOutputFilename)
if !assert.NoError(t, err) {
t.FailNow()
}
expectedOutput = string(b)
}
}
if rc.ExpectedErrorFilename != "" {
_, err := os.Stat(rc.ExpectedErrorFilename)
if !os.IsNotExist(err) && err != nil {
t.FailNow()
}
if err == nil {
// only read the file if it exists
b, err := ioutil.ReadFile(rc.ExpectedErrorFilename)
if !assert.NoError(t, err) {
t.FailNow()
}
expectedError = string(b)
}
}
return expectedOutput, expectedError
}