diff options
Diffstat (limited to 'parse_test.go')
-rw-r--r-- | parse_test.go | 701 |
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 +} |