mirror of
https://github.com/yuin/goldmark
synced 2025-03-04 23:04:52 +00:00
Currently, the DoTestCase, DoTestCaseFile, and DoTestCases functions,
are exposed as part of the public interface.
func DoTestCase(m Markdown, testCase MarkdownTestCase, t *testing.T)
func DoTestCaseFile(m Markdown, filename string, t *testing.T)
func DoTestCases(m goldmark.Markdown, cases []MarkdownTestCase, t *testing.T)
Implementing these functions requires importing the `testing` package.
Importing the `testing` package [automatically registers][1] a number of
global [command line flags][2].
[1]: https://golang.org/src/testing/testing.go#L252
[2]: https://golang.org/cmd/go/#hdr-Testing_flags
The effect of this is that any application using the standard `flag`
package with goldmark will automatically get a number of unwanted
command line flags. This is verifiable with the following program,
package main
import (
"flag"
_ "github.com/yuin/goldmark"
)
func main() {
flag.Parse()
}
To fix this, the `testing` import needs to be removed from all non-test
files. There are two ways to go about it,
- If the functions are meant for external use, you can define an
interface with a subset of the methods of `testing.T` and switch the
functions to consume that. This is what testing libraries like
[gomock] and [testify] do.
- If the functions are meant for internal use, you can remove them from
the public interface of the library and use them only from tests.
[gomock]: https://godoc.org/github.com/golang/mock/gomock#TestReporter
[testify]: https://godoc.org/github.com/stretchr/testify/require#TestingT
Since these functions are meant to be used by external extensions, I've
introduced a TestingT interface that is a subset of the functionality
provided by `testing.T`. It supports the standard operations: logging,
skiping, and failing tests,
136 lines
2.6 KiB
Go
136 lines
2.6 KiB
Go
package goldmark
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"runtime/debug"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"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()
|
|
}
|
|
|
|
type MarkdownTestCase struct {
|
|
No int
|
|
Markdown string
|
|
Expected string
|
|
}
|
|
|
|
const attributeSeparator = "//- - - - - - - - -//"
|
|
const caseSeparator = "//= = = = = = = = = = = = = = = = = = = = = = = =//"
|
|
|
|
func DoTestCaseFile(m Markdown, filename string, t TestingT) {
|
|
fp, err := os.Open(filename)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer fp.Close()
|
|
|
|
scanner := bufio.NewScanner(fp)
|
|
c := MarkdownTestCase{
|
|
No: -1,
|
|
Markdown: "",
|
|
Expected: "",
|
|
}
|
|
cases := []MarkdownTestCase{}
|
|
line := 0
|
|
for scanner.Scan() {
|
|
line++
|
|
if util.IsBlank([]byte(scanner.Text())) {
|
|
continue
|
|
}
|
|
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")
|
|
cases = append(cases, c)
|
|
}
|
|
DoTestCases(m, cases, t)
|
|
}
|
|
|
|
func DoTestCases(m Markdown, cases []MarkdownTestCase, t TestingT) {
|
|
for _, testCase := range cases {
|
|
DoTestCase(m, testCase, t)
|
|
}
|
|
}
|
|
|
|
func DoTestCase(m Markdown, testCase MarkdownTestCase, t TestingT) {
|
|
var ok bool
|
|
var out bytes.Buffer
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
format := `============= case %d ================
|
|
Markdown:
|
|
-----------
|
|
%s
|
|
|
|
Expected:
|
|
----------
|
|
%s
|
|
|
|
Actual
|
|
---------
|
|
%v
|
|
%s
|
|
`
|
|
t.Errorf(format, testCase.No, testCase.Markdown, testCase.Expected, err, debug.Stack())
|
|
} else if !ok {
|
|
format := `============= case %d ================
|
|
Markdown:
|
|
-----------
|
|
%s
|
|
|
|
Expected:
|
|
----------
|
|
%s
|
|
|
|
Actual
|
|
---------
|
|
%s
|
|
`
|
|
t.Errorf(format, testCase.No, 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)))
|
|
}
|