1
0
treerack/cmd/treerack/main_test.go
2018-01-09 00:21:35 +01:00

233 lines
3.6 KiB
Go

package main
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"strings"
"testing"
)
type mainTest struct {
title string
args []string
failingOutput bool
exit int
stdin string
stdout []string
stderr []string
}
type failingWriter struct{}
var errWriteFailed = errors.New("write failed")
func (w failingWriter) Write([]byte) (int, error) {
return 0, errWriteFailed
}
func init() {
isTest = true
}
func convertTest(cmd string, t mainTest) mainTest {
args := make([]string, len(t.args))
copy(args, t.args)
args[1] = cmd
t.args = args
return t
}
func convertTests(cmd string, t []mainTest) []mainTest {
tt := make([]mainTest, len(t))
for i := range t {
tt[i] = convertTest(cmd, t[i])
}
return tt
}
func mockArgs(args ...string) (reset func()) {
original := os.Args
os.Args = args
reset = func() {
os.Args = original
}
return
}
func mockStdin(in string) (reset func()) {
original := rin
if in == "" {
rin = nil
} else {
rin = bytes.NewBufferString(in)
}
reset = func() {
rin = original
}
return
}
func mockOutput(w *io.Writer, failing bool) (out fmt.Stringer, reset func()) {
original := *w
reset = func() { *w = original }
if failing {
*w = failingWriter{}
return
}
var buf bytes.Buffer
*w = &buf
out = &buf
return
}
func mockStdout() (out fmt.Stringer, reset func()) {
return mockOutput(&wout, false)
}
func mockStderr() (out fmt.Stringer, reset func()) {
return mockOutput(&werr, false)
}
func mockFailingOutput() (reset func()) {
_, reset = mockOutput(&wout, true)
return
}
func mockExit() (code *int, reset func()) {
var exitCode int
code = &exitCode
original := exit
exit = func(c int) { exitCode = c }
reset = func() { exit = original }
return
}
func (mt mainTest) run(t *testing.T) {
test := func(t *testing.T) {
defer mockArgs(mt.args...)()
defer mockStdin(mt.stdin)()
var stdout fmt.Stringer
if mt.failingOutput {
defer mockFailingOutput()()
} else {
var reset func()
stdout, reset = mockStdout()
defer reset()
}
stderr, resetStderr := mockStderr()
defer resetStderr()
code, resetExit := mockExit()
defer resetExit()
main()
if *code != mt.exit {
t.Error("invalid exit code")
}
if stdout != nil {
var failed bool
for i := range mt.stdout {
if !strings.Contains(stdout.String(), mt.stdout[i]) {
t.Error("invalid output")
failed = true
}
}
if failed {
t.Log(stdout.String())
}
}
var failed bool
for i := range mt.stderr {
if !strings.Contains(stderr.String(), mt.stderr[i]) {
t.Error("invalid error output")
failed = true
}
}
if failed {
t.Log(stderr.String())
}
}
if mt.title == "" {
test(t)
} else {
t.Run(mt.title, test)
}
}
func runMainTest(t *testing.T, mt ...mainTest) {
for i := range mt {
mt[i].run(t)
}
}
func TestMissingCommand(t *testing.T) {
runMainTest(t,
mainTest{
args: []string{"treerack"},
exit: -1,
stderr: []string{
"missing command",
commandsHelp,
docRef,
},
},
)
}
func TestInvalidCommand(t *testing.T) {
runMainTest(t,
mainTest{
args: []string{
"treerack", "foo",
},
exit: -1,
stderr: []string{
"invalid command",
commandsHelp,
},
},
)
}
func TestHelp(t *testing.T) {
runMainTest(t,
mainTest{
title: "without dash",
args: []string{
"treerack", "help",
},
stdout: []string{
summary, commandsHelp, docRef,
},
},
mainTest{
title: "with dash",
args: []string{
"treerack", "-help",
},
stdout: []string{
summary, commandsHelp, docRef,
},
},
)
}