diff options
-rw-r--r-- | content.go | 42 | ||||
-rw-r--r-- | document.go | 7 | ||||
-rw-r--r-- | parse_test.go | 348 | ||||
-rw-r--r-- | write_test.go | 150 |
4 files changed, 297 insertions, 250 deletions
@@ -28,6 +28,9 @@ type ContainerBlock interface { // Children returns the child blocks. Children() []Block + + // AppendChild adds a new child block to the end of the list of children. + AppendChild(Block) } // ContentBlock represents a block that holds other content blocks. @@ -467,45 +470,12 @@ func parseContentList(p *Parser, block *TokenBlock) (*ListBlock, error) { // TableBlock represents a "table" content block. type TableBlock struct { - rows []Block + ContentBlock } // NewTableBlock creates a new TableBlock. func NewTableBlock() *TableBlock { - return &TableBlock{} -} - -// Name returns the block name "table". -func (t *TableBlock) Name() string { - return "table" -} - -// Args returns the block's nil arguments. -func (t *TableBlock) Args() []string { - return nil -} - -// WriteIndent writes the table header and contents indented by n tabs. -func (t *TableBlock) WriteIndent(w io.Writer, n int) error { - if err := WriteIndent(w, t.Name(), n); err != nil { - return err - } - for _, row := range t.rows { - if err := row.WriteIndent(w, n+1); err != nil { - return err - } - } - return nil -} - -// Children returns the table's rows. -func (t *TableBlock) Children() []Block { - return t.rows -} - -// AppendRow adds a new row to the end of the table. -func (t *TableBlock) AppendRow(row Block) { - t.rows = append(t.rows, row) + return &TableBlock{*NewContentBlock("table", "")} } func (t *TableBlock) parse(p *Parser, block *TokenBlock) (err error) { @@ -526,7 +496,7 @@ func (t *TableBlock) parse(p *Parser, block *TokenBlock) (err error) { err = parseUnknown(p, blk) } if b != nil { - t.AppendRow(b) + t.AppendChild(b) } } else if err = p.Next(); err != nil { break diff --git a/document.go b/document.go index c01837c..65eac3e 100644 --- a/document.go +++ b/document.go @@ -23,10 +23,15 @@ type Document struct { Content *ContentBlock } +// NewDocument creates a new Document. +func NewDocument() *Document { + return &Document{} +} + // ParseDocument parses a CNM document from r. func ParseDocument(r io.Reader) (doc *Document, err error) { p := NewParser(r) - doc = &Document{} + doc = NewDocument() err = p.Next() for err == nil { token := p.Block() diff --git a/parse_test.go b/parse_test.go index fc0ef87..e70b833 100644 --- a/parse_test.go +++ b/parse_test.go @@ -196,19 +196,23 @@ var parseTests = map[string]*Document{ name: "content", args: nil, children: []Block{ - &TableBlock{rows: []Block{ - &RowBlock{ContentBlock{ - name: "row", - args: nil, - children: []Block{ - &TextBlock{ - Format: "", - Contents: TextPlainContents{Paragraphs: []string{ - "test", - }}, + &TableBlock{ContentBlock{ + name: "table", + args: nil, + children: []Block{ + &RowBlock{ContentBlock{ + name: "row", + args: nil, + children: []Block{ + &TextBlock{ + Format: "", + Contents: TextPlainContents{Paragraphs: []string{ + "test", + }}, + }, }, - }, - }}, + }}, + }, }}, }, }, @@ -379,79 +383,83 @@ content 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", - }}, + &TableBlock{ContentBlock{ + name: "table", + args: nil, + children: []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", - }}, + }}, + &RowBlock{ContentBlock{ + name: "row", + args: nil, + children: []Block{ + &TextBlock{ + Contents: TextPlainContents{[]string{ + "CNM", + }}, + }, + &TextBlock{ + Contents: TextPlainContents{[]string{ + "document", + "format", + }}, + }, }, - &TextBlock{ - Contents: TextPlainContents{[]string{ - "document", - "format", + }}, + &RowBlock{ContentBlock{ + name: "row", + args: nil, + children: []Block{ + &SectionBlock{ContentBlock{ + name: "section", + args: nil, }}, - }, - }, - }}, - &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", - }}, - }, - }, + &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", + }}, + }, + }, + }}, + }, + }}, + }, + }}, + }, + }}, + }, }}, }, }}, @@ -474,251 +482,311 @@ func TestParse(t *testing.T) { if err != nil { t.Fatalf("ParseDocument(%q): error: %v", k, err) } - if !documentEqual(d, v) { - t.Fatalf("ParseDocument(%q):\nexpected:\n%#v\n got:\n%#v\n\n%#v\n%#v", k, v, d, v.Content.Children()[0], d.Content.Children()[0]) + if !documentEqual(t, d, v) { + t.Fatalf("ParseDocument(%q):\nexpected:\n%#v\n got:\n%#v", k, v, d) } }) } } -func documentEqual(a, b *Document) bool { +func documentEqual(t *testing.T, a, b *Document) bool { if a.Title != b.Title { + t.Log("titles differ") return false } if len(a.Links) != len(b.Links) { + t.Log("links count differ") return false } for i := range a.Links { - if !linkEqual(a.Links[i], b.Links[i]) { + if !linkEqual(t, a.Links[i], b.Links[i]) { return false } } - if !siteEqual(a.Site, b.Site) { + if !siteEqual(t, a.Site, b.Site) { return false } - if !contentBlockEqual(a.Content, b.Content) { + if !contentBlockEqual(t, a.Content, b.Content) { return false } return true } -func linkEqual(a, b Link) bool { - return a == b +func linkEqual(t *testing.T, a, b Link) bool { + if a != b { + t.Log("link differs") + return false + } + return true } -func siteEqual(a, b Site) bool { +func siteEqual(t *testing.T, a, b Site) bool { if a.Path != b.Path { + t.Log("site path differs") return false } if a.Name != b.Name { + t.Log("site name differs") return false } if len(a.Children) != len(b.Children) { + t.Log("site children count differs") return false } for i := range a.Children { - if !siteEqual(a.Children[i], b.Children[i]) { + if !siteEqual(t, a.Children[i], b.Children[i]) { return false } } return true } -func blockEqual(a, b Block) bool { +func blockEqual(t *testing.T, a, b Block) bool { switch va := a.(type) { + case *Document: + vb, ok := b.(*Document) + if !ok { + t.Log("type mismatch") + return false + } + return documentEqual(t, va, vb) + case *SectionBlock: vb, ok := b.(*SectionBlock) if !ok { + t.Log("type mismatch") return false } - return sectionBlockEqual(va, vb) + return sectionBlockEqual(t, va, vb) case *TextBlock: vb, ok := b.(*TextBlock) if !ok { + t.Log("type mismatch") return false } - return textBlockEqual(va, vb) + return textBlockEqual(t, va, vb) case *RawBlock: vb, ok := b.(*RawBlock) if !ok { + t.Log("type mismatch") return false } - return rawBlockEqual(va, vb) + return rawBlockEqual(t, va, vb) case *ListBlock: vb, ok := b.(*ListBlock) if !ok { + t.Log("type mismatch") return false } - return listBlockEqual(va, vb) + return listBlockEqual(t, va, vb) case *TableBlock: vb, ok := b.(*TableBlock) if !ok { + t.Log("type mismatch") return false } - return tableBlockEqual(va, vb) + return tableBlockEqual(t, va, vb) case *HeaderBlock: vb, ok := b.(*HeaderBlock) if !ok { + t.Log("type mismatch") return false } - return headerBlockEqual(va, vb) + return headerBlockEqual(t, va, vb) case *RowBlock: vb, ok := b.(*RowBlock) if !ok { + t.Log("type mismatch") return false } - return rowBlockEqual(va, vb) + return rowBlockEqual(t, va, vb) case *EmbedBlock: vb, ok := b.(*EmbedBlock) if !ok { + t.Log("type mismatch") return false } - return embedBlockEqual(va, vb) + return embedBlockEqual(t, va, vb) case *ContentBlock: vb, ok := b.(*ContentBlock) if !ok { + t.Logf("type mismatch: %T, %T", a, b) return false } - return contentBlockEqual(va, vb) + return contentBlockEqual(t, va, vb) + + case nil: + return b == nil default: // shouldn't happen + t.Logf("unknown type: %T", a) return false } } -func contentBlockEqual(a, b *ContentBlock) bool { +func contentBlockEqual(t *testing.T, a, b *ContentBlock) bool { if (a == nil) != (b == nil) { + t.Log("content blocks are not both nil or non-nil") return false } if a == nil { return true } if a.Name() != b.Name() { + t.Log("content block names differ") return false } - if !argsEqual(a.Args(), b.Args()) { + if !argsEqual(t, a.Args(), b.Args()) { return false } ca, cb := a.Children(), b.Children() if len(ca) != len(cb) { + t.Log("content block children count differs") return false } for i := range ca { - if !blockEqual(ca[i], cb[i]) { + if !blockEqual(t, ca[i], cb[i]) { return false } } return true } -func argsEqual(a, b []string) bool { +func argsEqual(t *testing.T, a, b []string) bool { if len(a) != len(b) { + t.Log("args count differs") return false } for i := range a { if a[i] != b[i] { + t.Log("arg differs") return false } } return true } -func sectionBlockEqual(a, b *SectionBlock) bool { - return a.Title() == b.Title() && contentBlockEqual(&a.ContentBlock, &b.ContentBlock) +func sectionBlockEqual(t *testing.T, a, b *SectionBlock) bool { + if a.Title() != b.Title() { + t.Log("section titles differ") + return false + } + if !contentBlockEqual(t, &a.ContentBlock, &b.ContentBlock) { + return false + } + return true } -func textBlockEqual(a, b *TextBlock) bool { - if !argsEqual(a.Args(), b.Args()) { +func textBlockEqual(t *testing.T, a, b *TextBlock) bool { + if !argsEqual(t, a.Args(), b.Args()) { + return false + } + if !textContentsEqual(t, a.Contents, b.Contents) { return false } - return textContentsEqual(a.Contents, b.Contents) + return true } -func textContentsEqual(a, b TextContents) bool { +func textContentsEqual(t *testing.T, a, b TextContents) bool { switch va := a.(type) { case TextPlainContents: vb, ok := b.(TextPlainContents) if !ok { + t.Log("text content type differs") return false } - return textPlainContentsEqual(va, vb) + return textPlainContentsEqual(t, va, vb) case TextPreContents: vb, ok := b.(TextPreContents) if !ok { + t.Log("text content type differs") return false } - return textPreContentsEqual(va, vb) + return textPreContentsEqual(t, va, vb) case TextRawContents: vb, ok := b.(TextRawContents) if !ok { + t.Log("text content type differs") return false } - return textRawContentsEqual(va, vb) + return textRawContentsEqual(t, va, vb) default: // handle unknown contents, e.g. text fmt - return reflect.TypeOf(a) == reflect.TypeOf(b) && reflect.DeepEqual(a, b) + if reflect.TypeOf(a) != reflect.TypeOf(b) { + t.Log("unknown text content format type differs") + return false + } + if !reflect.DeepEqual(a, b) { + t.Log("unknown text content format contents differ") + return false + } + return true } } -func textPlainContentsEqual(a, b TextPlainContents) bool { +func textPlainContentsEqual(t *testing.T, a, b TextPlainContents) bool { if len(a.Paragraphs) != len(b.Paragraphs) { + t.Log("text plain paragraph count differs") return false } for i := range a.Paragraphs { if a.Paragraphs[i] != b.Paragraphs[i] { + t.Log("text plain paragraph differs") return false } } return true } -func textPreContentsEqual(a, b TextPreContents) bool { - return a == b +func textPreContentsEqual(t *testing.T, a, b TextPreContents) bool { + if a.Text != b.Text { + t.Log("text pre contents differ") + return false + } + return true } -func textRawContentsEqual(a, b TextRawContents) bool { - return a.Text == b.Text +func textRawContentsEqual(t *testing.T, a, b TextRawContents) bool { + if a.Text != b.Text { + t.Log("text raw contents differ") + return false + } + return true } -func rawBlockEqual(a, b *RawBlock) bool { - return argsEqual(a.Args(), b.Args()) && textRawContentsEqual(a.Contents, b.Contents) +func rawBlockEqual(t *testing.T, a, b *RawBlock) bool { + return argsEqual(t, a.Args(), b.Args()) && textRawContentsEqual(t, a.Contents, b.Contents) } -func listBlockEqual(a, b *ListBlock) bool { - return a.Ordered() == b.Ordered() && contentBlockEqual(&a.ContentBlock, &b.ContentBlock) +func listBlockEqual(t *testing.T, a, b *ListBlock) bool { + if a.Ordered() != b.Ordered() { + t.Log("list block mode differs") + } + return contentBlockEqual(t, &a.ContentBlock, &b.ContentBlock) } -func tableBlockEqual(a, b *TableBlock) bool { - ra, rb := a.Children(), b.Children() - if len(ra) != len(rb) { - return false - } - for i := range ra { - if !blockEqual(ra[i], rb[i]) { - return false - } - } - return argsEqual(a.Args(), b.Args()) +func tableBlockEqual(t *testing.T, a, b *TableBlock) bool { + return contentBlockEqual(t, &a.ContentBlock, &b.ContentBlock) } -func rowBlockEqual(a, b *RowBlock) bool { - return contentBlockEqual(&a.ContentBlock, &b.ContentBlock) +func rowBlockEqual(t *testing.T, a, b *RowBlock) bool { + return contentBlockEqual(t, &a.ContentBlock, &b.ContentBlock) } -func headerBlockEqual(a, b *HeaderBlock) bool { - return contentBlockEqual(&a.ContentBlock, &b.ContentBlock) +func headerBlockEqual(t *testing.T, a, b *HeaderBlock) bool { + return contentBlockEqual(t, &a.ContentBlock, &b.ContentBlock) } -func embedBlockEqual(a, b *EmbedBlock) bool { - return argsEqual(a.Args(), b.Args()) +func embedBlockEqual(t *testing.T, a, b *EmbedBlock) bool { + return argsEqual(t, a.Args(), b.Args()) } diff --git a/write_test.go b/write_test.go index bf829a0..18809b1 100644 --- a/write_test.go +++ b/write_test.go @@ -108,79 +108,83 @@ content name: "section", args: []string{"of the"}, children: []Block{ - &TableBlock{[]Block{ - &HeaderBlock{ContentBlock{ - name: "header", - args: []string{}, - children: []Block{ - &TextBlock{ - Contents: TextPlainContents{[]string{ - "Column 1", - }}, - }, - &TextBlock{ - Contents: TextPlainContents{[]string{ - "Column 2", - }}, + &TableBlock{ContentBlock{ + name: "table", + args: nil, + children: []Block{ + &HeaderBlock{ContentBlock{ + name: "header", + args: []string{}, + children: []Block{ + &TextBlock{ + Contents: TextPlainContents{[]string{ + "Column 1", + }}, + }, + &TextBlock{ + Contents: TextPlainContents{[]string{ + "Column 2", + }}, + }, }, - }, - }}, - &RowBlock{ContentBlock{ - name: "row", - args: []string{}, - children: []Block{ - &TextBlock{ - Contents: TextPlainContents{[]string{ - "CNM", - }}, + }}, + &RowBlock{ContentBlock{ + name: "row", + args: []string{}, + children: []Block{ + &TextBlock{ + Contents: TextPlainContents{[]string{ + "CNM", + }}, + }, + &TextBlock{ + Contents: TextPlainContents{[]string{ + "document", + "format", + }}, + }, }, - &TextBlock{ - Contents: TextPlainContents{[]string{ - "document", - "format", + }}, + &RowBlock{ContentBlock{ + name: "row", + args: []string{""}, + children: []Block{ + &SectionBlock{ContentBlock{ + name: "section", + args: []string{"", "", ""}, }}, - }, - }, - }}, - &RowBlock{ContentBlock{ - name: "row", - args: []string{""}, - children: []Block{ - &SectionBlock{ContentBlock{ - name: "section", - args: []string{"", "", ""}, - }}, - &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", - }}, - }, - }, + &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", + }}, + }, + }, + }}, + }, + }}, + }, + }}, + }, + }}, + }, }}, }, }}, @@ -205,12 +209,12 @@ func TestWrite(t *testing.T) { t.Fatalf("Write error: %v", err) } w := buf.String() - t.Log("====================") - t.Log("expected:\n" + k) - t.Log("--------------------") - t.Log(" got:\n" + w) - t.Log("====================") if k != w { + t.Log("====================") + t.Log("expected:\n" + k) + t.Log("--------------------") + t.Log(" got:\n" + w) + t.Log("====================") t.Fatal("Write: output did not match expected document") } }) |