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")  			}  		}) |