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 Markdown string Expected string } const attributeSeparator = "//- - - - - - - - -//" const caseSeparator = "//= = = = = = = = = = = = = = = = = = = = = = = =//" // DoTestCaseFile runs test cases in a given file. func DoTestCaseFile(m goldmark.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) } // 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() { 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))) }