1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
package approvaltests
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"runtime"
"strings"
)
type approvalName struct {
pc uintptr
fullName string
name string
fileName string
fileLine int
}
func newApprovalName(pc uintptr, f *runtime.Func) (*approvalName, error) {
namer := &approvalName{
pc: pc,
fullName: f.Name(),
}
namer.fileName, namer.fileLine = f.FileLine(pc)
splits := strings.Split(namer.fullName, ".")
namer.name = splits[len(splits)-1]
return namer, nil
}
// Walk the call stack, and try to find the test method that was executed.
// The test method is identified by looking for the test runner, which is
// *assumed* to be common across all callers. The test runner has a Name() of
// 'testing.tRunner'. The method immediately previous to this is the test
// method.
func getApprovalName() (*approvalName, error) {
pc := make([]uintptr, 100)
count := runtime.Callers(0, pc)
i := 0
var lastFunc *runtime.Func
for ; i < count; i++ {
lastFunc = runtime.FuncForPC(pc[i])
if isTestRunner(lastFunc) {
break
}
}
if i == 0 || !isTestRunner(lastFunc) {
return nil, fmt.Errorf("approvals: could not find the test method")
}
testMethod := runtime.FuncForPC(pc[i-1])
return newApprovalName(pc[i-1], testMethod)
}
func isTestRunner(f *runtime.Func) bool {
return f != nil && f.Name() == "testing.tRunner"
}
func (s *approvalName) compare(approvalFile, receivedFile string, reader io.Reader) error {
received, err := ioutil.ReadAll(reader)
if err != nil {
return err
}
// Ideally, this should only be written if
// 1. the approval file does not exist
// 2. the results differ
err = s.dumpReceivedTestResult(received, receivedFile)
if err != nil {
return err
}
fh, err := os.Open(approvalFile)
if err != nil {
return err
}
defer fh.Close()
approved, err := ioutil.ReadAll(fh)
if err != nil {
return err
}
received = s.normalizeLineEndings(received)
approved = s.normalizeLineEndings(approved)
// The two sides are identical, nothing more to do.
if bytes.Compare(received, approved) == 0 {
return nil
}
return fmt.Errorf("failed to approved %s", s.name)
}
func (s *approvalName) normalizeLineEndings(bs []byte) []byte {
return bytes.Replace(bs, []byte("\r\n"), []byte("\n"), -1)
}
func (s *approvalName) dumpReceivedTestResult(bs []byte, receivedFile string) error {
err := ioutil.WriteFile(receivedFile, bs, 0644)
return err
}
func (s *approvalName) getFileName(extWithDot string, suffix string) string {
if !strings.HasPrefix(extWithDot, ".") {
extWithDot = fmt.Sprintf(".%s", extWithDot)
}
baseName := s.fileName
baseWithoutExt := baseName[:len(baseName)-len(path.Ext(s.fileName))]
return fmt.Sprintf("%s.%s.%s%s", baseWithoutExt, s.name, suffix, extWithDot)
}
func (s *approvalName) getReceivedFile(extWithDot string) string {
return s.getFileName(extWithDot, "received")
}
func (s *approvalName) getApprovalFile(extWithDot string) string {
return s.getFileName(extWithDot, "approved")
}
|