goldmark/testutil/testutil.go
2020-07-02 15:28:33 +09:00

183 lines
3.8 KiB
Go

package testutil
import (
"bufio"
"bytes"
"fmt"
"os"
"runtime/debug"
"strconv"
"strings"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/util"
)
// TestingT is a subset of the functionality provided by testing.T.
type TestingT interface {
Logf(string, ...interface{})
Skipf(string, ...interface{})
Errorf(string, ...interface{})
FailNow()
}
// MarkdownTestCase represents a test case.
type MarkdownTestCase struct {
No int
Description string
Markdown string
Expected string
}
const attributeSeparator = "//- - - - - - - - -//"
const caseSeparator = "//= = = = = = = = = = = = = = = = = = = = = = = =//"
// ParseCliCaseArg parses -case command line args.
func ParseCliCaseArg() []int {
ret := []int{}
for _, a := range os.Args {
if strings.HasPrefix(a, "case=") {
parts := strings.Split(a, "=")
for _, cas := range strings.Split(parts[1], ",") {
value, err := strconv.Atoi(strings.TrimSpace(cas))
if err == nil {
ret = append(ret, value)
}
}
}
}
return ret
}
// DoTestCaseFile runs test cases in a given file.
func DoTestCaseFile(m goldmark.Markdown, filename string, t TestingT, no ...int) {
fp, err := os.Open(filename)
if err != nil {
panic(err)
}
defer fp.Close()
scanner := bufio.NewScanner(fp)
c := MarkdownTestCase{
No: -1,
Description: "",
Markdown: "",
Expected: "",
}
cases := []MarkdownTestCase{}
line := 0
for scanner.Scan() {
line++
if util.IsBlank([]byte(scanner.Text())) {
continue
}
header := scanner.Text()
c.Description = ""
if strings.Contains(header, ":") {
parts := strings.Split(header, ":")
c.No, err = strconv.Atoi(strings.TrimSpace(parts[0]))
c.Description = strings.Join(parts[1:], ":")
} else {
c.No, err = strconv.Atoi(scanner.Text())
}
if err != nil {
panic(fmt.Sprintf("%s: invalid case No at line %d", filename, line))
}
if !scanner.Scan() {
panic(fmt.Sprintf("%s: invalid case at line %d", filename, line))
}
line++
if scanner.Text() != attributeSeparator {
panic(fmt.Sprintf("%s: invalid separator '%s' at line %d", filename, scanner.Text(), line))
}
buf := []string{}
for scanner.Scan() {
line++
text := scanner.Text()
if text == attributeSeparator {
break
}
buf = append(buf, text)
}
c.Markdown = strings.Join(buf, "\n")
buf = []string{}
for scanner.Scan() {
line++
text := scanner.Text()
if text == caseSeparator {
break
}
buf = append(buf, text)
}
c.Expected = strings.Join(buf, "\n")
shouldAdd := len(no) == 0
if !shouldAdd {
for _, n := range no {
if n == c.No {
shouldAdd = true
break
}
}
}
if shouldAdd {
cases = append(cases, c)
}
}
DoTestCases(m, cases, t)
}
// DoTestCases runs a set of test cases.
func DoTestCases(m goldmark.Markdown, cases []MarkdownTestCase, t TestingT) {
for _, testCase := range cases {
DoTestCase(m, testCase, t)
}
}
// DoTestCase runs a test case.
func DoTestCase(m goldmark.Markdown, testCase MarkdownTestCase, t TestingT) {
var ok bool
var out bytes.Buffer
defer func() {
description := ""
if len(testCase.Description) != 0 {
description = ": " + testCase.Description
}
if err := recover(); err != nil {
format := `============= case %d%s ================
Markdown:
-----------
%s
Expected:
----------
%s
Actual
---------
%v
%s
`
t.Errorf(format, testCase.No, description, testCase.Markdown, testCase.Expected, err, debug.Stack())
} else if !ok {
format := `============= case %d%s ================
Markdown:
-----------
%s
Expected:
----------
%s
Actual
---------
%s
`
t.Errorf(format, testCase.No, description, testCase.Markdown, testCase.Expected, out.Bytes())
}
}()
if err := m.Convert([]byte(testCase.Markdown), &out); err != nil {
panic(err)
}
ok = bytes.Equal(bytes.TrimSpace(out.Bytes()), bytes.TrimSpace([]byte(testCase.Expected)))
}