// Package progtest provides utilities for testing subprograms. // // This package intentionally has no test file; it is excluded from test // coverage.
package progtest import ( ) // Fixture is a test fixture suitable for testing programs. type Fixture struct { pipes [3]*pipe dirCleanup func() } func ( *pipe) { , := ioutil.ReadAll(.r) if != nil { panic() } .output <- } // Setup sets up a test fixture. The caller is responsible for calling the // Cleanup method of the returned Fixture. func () *Fixture { , := testutil.InTestDir() := [3]*pipe{makePipe(false), makePipe(true), makePipe(true)} return &Fixture{, } } // Cleanup cleans up the test fixture. func ( *Fixture) () { .pipes[0].close() .pipes[1].close() .pipes[2].close() .dirCleanup() } // Fds returns the file descriptors in the fixture. func ( *Fixture) () [3]*os.File { return [3]*os.File{.pipes[0].r, .pipes[1].w, .pipes[2].w} } // FeedIn feeds input to the standard input. func ( *Fixture) ( string) { , := .pipes[0].w.WriteString() if != nil { panic() } .pipes[0].w.Close() .pipes[0].wClosed = true } // TestOut tests that the output on the given FD matches the given text. func ( *Fixture) ( *testing.T, int, string) { .Helper() if := .pipes[].get(); != { .Errorf("got out %q, want %q", , ) } } // TestOutSnippet tests that the output on the given FD contains the given text. func ( *Fixture) ( *testing.T, int, string) { .Helper() if := .pipes[].get(); !strings.Contains(, ) { .Errorf("got out %q, want string containing %q", , ) } } type pipe struct { r, w *os.File rClosed, wClosed bool saved string output chan []byte } func ( bool) *pipe { , , := os.Pipe() if != nil { panic() } if ! { return &pipe{r: , w: } } := make(chan []byte, 1) := pipe{r: , w: , output: } go captureOutput(&) return & } func ( *pipe) () string { if !.wClosed { // Close the write side so captureOutput goroutine sees EOF and // terminates allowing us to capture and cache the output. .w.Close() .wClosed = true if .output != nil { .saved = string(<-.output) } } return .saved } func ( *pipe) () { if !.wClosed { .w.Close() .wClosed = true if .output != nil { .saved = string(<-.output) } } if !.rClosed { .r.Close() .rClosed = true } if .output != nil { close(.output) .output = nil } } // MustWriteFile writes a file with the given name and content. It panics if the // write fails. func (, string) { := ioutil.WriteFile(, []byte(), 0600) if != nil { panic() } } // Elvish returns an argument slice starting with "elvish". func ( ...string) []string { return append([]string{"elvish"}, ...) } // TestError tests the error result of a program. func ( *testing.T, *Fixture, int, string) { .Helper() if != 2 { .Errorf("got exit %v, want 2", ) } .TestOutSnippet(, 2, ) }