summaryrefslogtreecommitdiffstats
path: root/parse_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'parse_test.go')
-rw-r--r--parse_test.go701
1 files changed, 701 insertions, 0 deletions
diff --git a/parse_test.go b/parse_test.go
new file mode 100644
index 0000000..b47dc14
--- /dev/null
+++ b/parse_test.go
@@ -0,0 +1,701 @@
+package cnm
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+var parseTests = map[string]*Document{
+ "": &Document{},
+
+ "foo\n\tbar\ntitle\n\ttest": &Document{
+ Title: "test",
+ },
+
+ "foo\n\tbar\ntitle\n\ttest\nfoo\n\tbar": &Document{
+ Title: "test",
+ },
+
+ "title\n\ttest": &Document{
+ Title: "test",
+ },
+
+ "\ntitle\n\ttest\n": &Document{
+ Title: "test",
+ },
+
+ "title\n\ttest\n\n": &Document{
+ Title: "test",
+ },
+
+ "\ntitle\n\t\t\t\t\t\n\t\tfoo bar": &Document{
+ Title: "foo bar",
+ },
+
+ "site\n\tfoo\nsite\n\t\tbar\n": &Document{
+ Site: Site{Children: []Site{
+ Site{Path: "foo"},
+ }},
+ },
+
+ "content\n\ttext\n\t\tfoo\ncontent\n\t\tbar\n": &Document{
+ Content: &ContentBlock{
+ name: "content",
+ children: []Block{
+ &TextBlock{
+ Format: "",
+ Contents: TextPlainContents{
+ Paragraphs: []string{"foo"},
+ },
+ },
+ },
+ },
+ },
+
+ "\n\ttitle\n\t\t\t\t\t\n\tfoo bar": &Document{},
+
+ "\ttitle\n\t\tfoo\n": &Document{},
+
+ "\tsite\n\t\tfoo\n": &Document{},
+
+ "\tlinks\n\t\tfoo\n": &Document{},
+
+ "links\n\tfoo": &Document{
+ Links: []Link{
+ Link{
+ URL: "foo",
+ },
+ },
+ },
+
+ "qwe\ntitle\n\tasd": &Document{
+ Title: "asd",
+ },
+
+ "links\n\t qwe\n\tasd": &Document{
+ Links: []Link{
+ Link{URL: "asd"},
+ },
+ },
+
+ "site\n\t qwe\n\tasd": &Document{
+ Site: Site{Children: []Site{
+ Site{Path: "asd"},
+ }},
+ },
+
+ "site\n\tba\\nr": &Document{
+ Site: Site{
+ Children: []Site{
+ Site{
+ Path: "ba\nr",
+ },
+ },
+ },
+ },
+
+ "site\n\t\t\t\tba\\nr": &Document{},
+
+ "site\n\tfoo\tbar": &Document{
+ Site: Site{
+ Children: []Site{
+ Site{
+ Path: "foo",
+ Name: "bar",
+ },
+ },
+ },
+ },
+
+ "\t\tsite\n\t\t\t\tfoo": &Document{},
+
+ "\tsite\n\tbar": &Document{},
+
+ "content\n\tsection test\n": &Document{
+ Content: &ContentBlock{
+ name: "content",
+ args: nil,
+ children: []Block{
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: []string{"test"},
+ }},
+ },
+ },
+ },
+
+ "content\n\tnosuchblock\n\tsection test\n": &Document{
+ Content: &ContentBlock{
+ name: "content",
+ args: nil,
+ children: []Block{
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: []string{"test"},
+ }},
+ },
+ },
+ },
+
+ "content\n\tnosuchblock\n\tsection test\n\n\tnosuchblock2": &Document{
+ Content: &ContentBlock{
+ name: "content",
+ args: nil,
+ children: []Block{
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: []string{"test"},
+ }},
+ },
+ },
+ },
+
+ "content\n\tsection\n\t\tnosuchblock\n\t\tsection\n\t\t\ttext\n\t\t\t\ttest": &Document{
+ Content: &ContentBlock{
+ name: "content",
+ args: nil,
+ children: []Block{
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: nil,
+ children: []Block{
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: nil,
+ children: []Block{
+ &TextBlock{
+ Format: "",
+ Contents: TextPlainContents{Paragraphs: []string{
+ "test",
+ }},
+ },
+ },
+ }},
+ },
+ }},
+ },
+ },
+ },
+
+ "content\n\ttable\n\t\tnosuchblock\n\t\trow\n\t\t\ttext\n\t\t\t\ttest": &Document{
+ Content: &ContentBlock{
+ name: "content",
+ args: nil,
+ children: []Block{
+ &TableBlock{rows: []Block{
+ &RowBlock{ContentBlock{
+ name: "row",
+ args: nil,
+ children: []Block{
+ &TextBlock{
+ Format: "",
+ Contents: TextPlainContents{Paragraphs: []string{
+ "test",
+ }},
+ },
+ },
+ }},
+ }},
+ },
+ },
+ },
+
+ "site\n\t\ttest\n\tfoo\\ bar baz\n" +
+ "links\n\tfoo\\ bar baz\n\t\t\tquux\n" +
+ "content\n" +
+ "links\n\t\ttest\n" +
+ "content\n" +
+ "\tsection\n\tsection qweasd\n" +
+ "\tsection foo\\ bar baz\n\t\ttext\n\t\t\ttest\n": &Document{
+ Site: Site{
+ Children: []Site{
+ Site{
+ Path: "foo bar",
+ Name: "baz",
+ },
+ },
+ },
+ Links: []Link{
+ Link{
+ URL: "foo bar",
+ Name: "baz",
+ Description: "quux",
+ },
+ },
+ Content: &ContentBlock{
+ name: "content",
+ args: nil,
+ children: []Block{
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: nil,
+ }},
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: []string{"qweasd"},
+ }},
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: []string{"foo bar baz"},
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "test",
+ }},
+ },
+ },
+ }},
+ },
+ },
+ },
+
+ "title\n\tfoo bar\n" +
+ "links\n\tqwe asd\n\tzxc 123\n" +
+ "site\n\tfoo\n\t\tbar\n\t\tbaz/quux\n\t\t\t123\n" +
+ "title\n\tbaz\n" +
+ "links\n\tfoo\n" +
+ "site\n\ttest": &Document{
+ Title: "foo bar baz",
+ Links: []Link{
+ Link{"qwe", "asd", ""},
+ Link{"zxc", "123", ""},
+ Link{"foo", "", ""},
+ },
+ Site: Site{
+ Children: []Site{
+ Site{
+ Path: "foo",
+ Children: []Site{
+ Site{Path: "bar"},
+ Site{
+ Path: "baz/quux",
+ Children: []Site{
+ Site{Path: "123"},
+ },
+ },
+ },
+ },
+ Site{Path: "test"},
+ },
+ },
+ },
+
+ `
+thing stuff
+ whatever
+title blah
+
+
+ Test
+
+title
+ document
+
+content
+ section Test section
+ text
+ This is \n just a
+ text pre
+
+ t e \n s t
+
+
+
+ preformatted text
+
+ raw text/plain
+ of various \n features
+
+ section of\ the
+ table
+ header
+ text
+ Column 1
+ text
+ Column 2
+ row
+ text
+ CNM
+ text
+ document
+
+ format
+ row
+ section
+ lorem
+ list
+ text
+ ipsum
+ list ordered
+ list unordered
+ text
+ dolor
+
+ sit
+ amet
+ embed text/cnm cnp://example.com/ stuff
+ thing
+ whatever
+`: &Document{
+ Title: "Test document",
+ Content: &ContentBlock{
+ name: "content",
+ args: nil,
+ children: []Block{
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: []string{"Test section"},
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "This is \n just a",
+ }},
+ },
+ &TextBlock{
+ Format: "pre",
+ Contents: TextPreContents{
+ " t e \n s t \n\t\n\n\npreformatted text",
+ },
+ },
+ &RawBlock{
+ Syntax: "text/plain",
+ Contents: "of various \\n features",
+ },
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: []string{"of the"},
+ children: []Block{
+ &TableBlock{[]Block{
+ &HeaderBlock{ContentBlock{
+ name: "header",
+ args: nil,
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "Column 1",
+ }},
+ },
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "Column 2",
+ }},
+ },
+ },
+ }},
+ &RowBlock{ContentBlock{
+ name: "row",
+ args: nil,
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "CNM",
+ }},
+ },
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "document",
+ "format",
+ }},
+ },
+ },
+ }},
+ &RowBlock{ContentBlock{
+ name: "row",
+ args: nil,
+ children: []Block{
+ &SectionBlock{ContentBlock{
+ name: "section",
+ args: nil,
+ }},
+ &ListBlock{ContentBlock{
+ name: "list",
+ args: nil,
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "ipsum",
+ }},
+ },
+ &ListBlock{ContentBlock{
+ name: "list",
+ args: []string{"ordered"},
+ children: []Block{
+ &ListBlock{ContentBlock{
+ name: "list",
+ args: []string{"unordered"},
+ children: []Block{
+ &TextBlock{
+ Contents: TextPlainContents{[]string{
+ "dolor",
+ "sit amet",
+ }},
+ },
+ },
+ }},
+ },
+ }},
+ },
+ }},
+ },
+ }},
+ }},
+ },
+ }},
+ },
+ }},
+ &EmbedBlock{
+ Type: "text/cnm",
+ URL: "cnp://example.com/",
+ Description: "thing whatever",
+ },
+ },
+ },
+ },
+}
+
+func TestParse(t *testing.T) {
+ for k, v := range parseTests {
+ t.Run(k, func(t *testing.T) {
+ d, err := ParseDocument(strings.NewReader(k))
+ if err != nil {
+ t.Fatalf("ParseDocument(%q): error: %v", k, err)
+ }
+ if !documentEqual(d, v) {
+ t.Fatalf("ParseDocument(%q):\nexpected:\n%s\n got:\n%s", k, reprDoc(v), reprDoc(d))
+ }
+ })
+ }
+}
+
+func reprDoc(d *Document) string {
+ //return fmt.Sprintf("Document{Title: %q, Links: %+v, Site: %+v, Content: %s}", d.Title, d.Links, d.Site, reprContent(d.Content))
+ return spew.Sdump(d)
+}
+
+func documentEqual(a, b *Document) bool {
+ if a.Title != b.Title {
+ return false
+ }
+ if len(a.Links) != len(b.Links) {
+ return false
+ }
+ for i := range a.Links {
+ if !linkEqual(a.Links[i], b.Links[i]) {
+ return false
+ }
+ }
+ if !siteEqual(a.Site, b.Site) {
+ return false
+ }
+ if !contentBlockEqual(a.Content, b.Content) {
+ return false
+ }
+ return true
+}
+
+func linkEqual(a, b Link) bool {
+ return a == b
+}
+
+func siteEqual(a, b Site) bool {
+ if a.Path != b.Path {
+ return false
+ }
+ if a.Name != b.Name {
+ return false
+ }
+ if len(a.Children) != len(b.Children) {
+ return false
+ }
+ for i := range a.Children {
+ if !siteEqual(a.Children[i], b.Children[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+func blockEqual(a, b Block) bool {
+ switch va := a.(type) {
+ case *SectionBlock:
+ vb, ok := b.(*SectionBlock)
+ if !ok {
+ return false
+ }
+ return sectionBlockEqual(va, vb)
+
+ case *TextBlock:
+ vb, ok := b.(*TextBlock)
+ if !ok {
+ return false
+ }
+ return textBlockEqual(va, vb)
+
+ case *RawBlock:
+ vb, ok := b.(*RawBlock)
+ if !ok {
+ return false
+ }
+ return rawBlockEqual(va, vb)
+
+ case *ListBlock:
+ vb, ok := b.(*ListBlock)
+ if !ok {
+ return false
+ }
+ return listBlockEqual(va, vb)
+
+ case *TableBlock:
+ vb, ok := b.(*TableBlock)
+ if !ok {
+ return false
+ }
+ return tableBlockEqual(va, vb)
+
+ case *HeaderBlock:
+ vb, ok := b.(*HeaderBlock)
+ if !ok {
+ return false
+ }
+ return headerBlockEqual(va, vb)
+
+ case *RowBlock:
+ vb, ok := b.(*RowBlock)
+ if !ok {
+ return false
+ }
+ return rowBlockEqual(va, vb)
+
+ case *EmbedBlock:
+ vb, ok := b.(*EmbedBlock)
+ if !ok {
+ return false
+ }
+ return embedBlockEqual(va, vb)
+
+ case *ContentBlock:
+ vb, ok := b.(*ContentBlock)
+ if !ok {
+ return false
+ }
+ return contentBlockEqual(va, vb)
+
+ default: // shouldn't happen
+ return false
+ }
+}
+
+func contentBlockEqual(a, b *ContentBlock) bool {
+ if (a == nil) != (b == nil) {
+ return false
+ }
+ if a == nil {
+ return true
+ }
+ if a.Name() != b.Name() {
+ return false
+ }
+ aa, ba := a.Args(), b.Args()
+ if len(aa) != len(ba) {
+ return false
+ }
+ for i := range aa {
+ if aa[i] != ba[i] {
+ return false
+ }
+ }
+ ca, cb := a.Children(), b.Children()
+ if len(ca) != len(cb) {
+ return false
+ }
+ for i := range ca {
+ if !blockEqual(ca[i], cb[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+func sectionBlockEqual(a, b *SectionBlock) bool {
+ return contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
+}
+
+func textBlockEqual(a, b *TextBlock) bool {
+ if a.Format != b.Format {
+ return false
+ }
+ return textContentsEqual(a.Contents, b.Contents)
+}
+
+func textContentsEqual(a, b TextContents) bool {
+ switch va := a.(type) {
+ case TextPlainContents:
+ vb, ok := b.(TextPlainContents)
+ if !ok {
+ return false
+ }
+ return textPlainContentsEqual(va, vb)
+
+ case TextPreContents:
+ vb, ok := b.(TextPreContents)
+ if !ok {
+ return false
+ }
+ return textPreContentsEqual(va, vb)
+
+ default:
+ return reflect.TypeOf(a) == reflect.TypeOf(b) && reflect.DeepEqual(a, b)
+ }
+}
+
+func textPlainContentsEqual(a, b TextPlainContents) bool {
+ if len(a.Paragraphs) != len(b.Paragraphs) {
+ return false
+ }
+ for i := range a.Paragraphs {
+ if a.Paragraphs[i] != b.Paragraphs[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func textPreContentsEqual(a, b TextPreContents) bool {
+ return a == b
+}
+
+func rawBlockEqual(a, b *RawBlock) bool {
+ return *a == *b
+}
+
+func listBlockEqual(a, b *ListBlock) bool {
+ return contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
+}
+
+func tableBlockEqual(a, b *TableBlock) bool {
+ ra, rb := a.Rows(), b.Rows()
+ if len(ra) != len(rb) {
+ return false
+ }
+ for i := range ra {
+ if !blockEqual(ra[i], rb[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+func rowBlockEqual(a, b *RowBlock) bool {
+ return contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
+}
+
+func headerBlockEqual(a, b *HeaderBlock) bool {
+ return contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
+}
+
+func embedBlockEqual(a, b *EmbedBlock) bool {
+ return *a == *b
+}