~shulhan/asciidoctor-go

19e2b864ea304f6ab7bd92e97fb1017988f527a7 — Shulhan 2 years ago 5a88e6c
all: rewrite test using lib/test.Data

Previously, to test parser and check the generated HTML, we write AsciDoc
input and expected HTML output using literal string `...`.
The text of input and output sometimes long, take multiple lines, which
makes the test code ugly, hard to write, and read.

Using lib/test.Data we can write the input and output as the AsciiDoc
markup and the HTML markup as is, simplify writing the test and more
readable.
13 files changed, 301 insertions(+), 297 deletions(-)

A .reuse/dep5
M asciidoctor_test.go
M document_test.go
M go.mod
M go.sum
D parser_paragraph_test.go
A testdata/author_test.txt
A testdata/document_title_test.txt
A testdata/header_with_empty_line_test.txt
R document_parser_test.go => testdata/list_description_with_open_block_test.txt
A testdata/meta_doctitle_test.txt
A testdata/meta_showtitle_test.txt
A testdata/paragraph_test.txt
A .reuse/dep5 => .reuse/dep5 +8 -0
@@ 0,0 1,8 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: ciigo
Upstream-Contact: Shulhan <ms@kilabit.info>
Source: https://git.sr.ht/~shulhan/ciigo

Files: testdata/*
Copyright: 2022 Shulhan <ms@kilabit.info>
License: GPL-3.0-or-later

M asciidoctor_test.go => asciidoctor_test.go +74 -3
@@ 4,10 4,81 @@
package asciidoctor

import (
	"os"
	"bytes"
	"testing"

	"github.com/shuLhan/share/lib/test"
)

const (
	outputCallHtmlWriteHeader = "htmlWriteHeader"
	outputCallToHTML          = "ToHTML"
	outputCallToHTMLBody      = "ToHTMLBody"
)

func TestMain(m *testing.M) {
	os.Exit(m.Run())
func TestData(t *testing.T) {
	var (
		listTData    []*test.Data
		tdata        *test.Data
		inputCall    string
		outputCall   string
		inputName    string
		subtestName  string
		inputContent []byte
		err          error
	)

	listTData, err = test.LoadDataDir("testdata")
	if err != nil {
		t.Fatal(err)
	}

	for _, tdata = range listTData {
		inputCall = tdata.Flag["input_call"]
		outputCall = tdata.Flag["output_call"]

		for inputName, inputContent = range tdata.Input {
			subtestName = tdata.Name + "/" + inputName

			t.Run(subtestName, func(t *testing.T) {
				var (
					doc  *Document
					bbuf bytes.Buffer
					exp  []byte
					got  []byte
				)

				exp = tdata.Output[inputName]

				bbuf.Reset()

				switch inputCall {
				default:
					doc = Parse(inputContent)
				}

				switch outputCall {
				case outputCallHtmlWriteHeader:
					htmlWriteHeader(doc, &bbuf)
				case outputCallToHTML:
					err = doc.ToHTML(&bbuf)
				case outputCallToHTMLBody:
					err = doc.ToHTMLBody(&bbuf)
				default:
					err = doc.ToHTMLEmbedded(&bbuf)
				}
				if err != nil {
					got = []byte(err.Error())
				} else {
					// Since the data file is from file it
					// always end with LF, we need to add
					// new line to output.
					bbuf.WriteByte('\n')
					got = bbuf.Bytes()
				}

				test.Assert(t, subtestName, string(exp), string(got))
			})
		}
	}
}

M document_test.go => document_test.go +0 -175
@@ 4,7 4,6 @@
package asciidoctor

import (
	"bytes"
	"os"
	"testing"



@@ 35,111 34,6 @@ func TestOpen(t *testing.T) {
	}
}

func TestParse_metaDocTitle(t *testing.T) {
	type testCase struct {
		content string
	}

	var (
		expHTML string = `
<div class="paragraph">
<p>Abc begins on a bleary Monday morning.</p>
</div>`
	)

	var cases = []testCase{{
		content: `= Abc

{doctitle} begins on a bleary Monday morning.`,
	}, {
		content: `:doctitle: Abc

{doctitle} begins on a bleary Monday morning.`,
	}}

	var (
		doc *Document
		c   testCase
		buf bytes.Buffer
		err error
	)

	for _, c = range cases {
		doc = Parse([]byte(c.content))
		buf.Reset()
		err = doc.ToHTMLEmbedded(&buf)
		if err != nil {
			t.Fatal(err)
		}
		test.Assert(t, "", expHTML, buf.String())
	}
}

func TestParse_metaShowTitle(t *testing.T) {
	type testCase struct {
		desc    string
		content string
		expHTML string
	}

	var cases = []testCase{{
		desc:    "default",
		content: `= Abc`,
		expHTML: `
<div id="header">
<h1>Abc</h1>
<div class="details">
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
</div>
</div>`,
	}, {
		desc: "with showtitle!",
		content: `= Abc
:showtitle!:`,
		expHTML: `
<div id="header">
<div class="details">
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
</div>
</div>`,
	}}

	var (
		doc *Document
		buf bytes.Buffer
		c   testCase
		err error
	)

	for _, c = range cases {
		doc = Parse([]byte(c.content))
		buf.Reset()
		err = doc.ToHTMLBody(&buf)
		if err != nil {
			t.Fatal(err)
		}
		test.Assert(t, c.desc, c.expHTML, buf.String())
	}
}

func TestParse_document_title(t *testing.T) {
	type testCase struct {
		content   string


@@ 197,72 91,3 @@ func TestParse_document_title(t *testing.T) {
		test.Assert(t, "String", c.expString, got.Title.String())
	}
}

func TestParse_author(t *testing.T) {
	type testCase struct {
		desc    string
		content string
		exp     []*Author
	}

	var cases = []testCase{{
		desc: "single author",
		content: `= T
A B`,
		exp: []*Author{{
			FirstName: "A",
			LastName:  "B",
			Initials:  "AB",
		}},
	}, {
		desc: "single author with email",
		content: `= T
A B <a@b>`,
		exp: []*Author{{
			FirstName: "A",
			LastName:  "B",
			Initials:  "AB",
			Email:     "a@b",
		}},
	}, {
		desc: "multiple authors",
		content: `= T
A B <a@b>; C <c@c>; D e_f G <>;`,
		exp: []*Author{{
			FirstName: "A",
			LastName:  "B",
			Initials:  "AB",
			Email:     "a@b",
		}, {
			FirstName: "C",
			Initials:  "C",
			Email:     "c@c",
		}, {
			FirstName:  "D",
			MiddleName: "e f",
			LastName:   "G",
			Initials:   "DeG",
		}},
	}, {
		desc: "meta author",
		content: `= T
:author: A B
:email: a@b`,
		exp: []*Author{{
			FirstName: "A",
			LastName:  "B",
			Initials:  "AB",
			Email:     "a@b",
		}},
	}}

	var (
		c   testCase
		got *Document
	)

	for _, c = range cases {
		got = Parse([]byte(c.content))
		test.Assert(t, c.desc, c.exp, got.Authors)
	}
}

M go.mod => go.mod +1 -1
@@ 5,6 5,6 @@ module git.sr.ht/~shulhan/asciidoctor-go

go 1.18

require github.com/shuLhan/share v0.39.0
require github.com/shuLhan/share v0.39.1-0.20220723102219-eb932c941bd3

//replace github.com/shuLhan/share => ../share

M go.sum => go.sum +2 -2
@@ 1,2 1,2 @@
github.com/shuLhan/share v0.39.0 h1:pga+HQqKeHgPiIs7b5sutf59uM/PsNqO2VlifZFKqTU=
github.com/shuLhan/share v0.39.0/go.mod h1:1phZr6PX+8YLCOeNvx92J14/AV0mTo0Ebbc1SmzzYvk=
github.com/shuLhan/share v0.39.1-0.20220723102219-eb932c941bd3 h1:QZ7p0n/NEcvtiwBeI2mWnVrwO9Hwxp2W0+sUs7dBORU=
github.com/shuLhan/share v0.39.1-0.20220723102219-eb932c941bd3/go.mod h1:hb3Kis5s4jPume4YD15JELE67naFybtuALshhh9TlOg=

D parser_paragraph_test.go => parser_paragraph_test.go +0 -48
@@ 1,48 0,0 @@
// SPDX-FileCopyrightText: 2020 M. Shulhan <ms@kilabit.info>
// SPDX-License-Identifier: GPL-3.0-or-later

package asciidoctor

import (
	"bytes"
	"testing"

	"github.com/shuLhan/share/lib/test"
)

func TestParser_parseParagraph(t *testing.T) {
	type testCase struct {
		desc    string
		exp     string
		content []byte
	}

	var cases = []testCase{{
		desc: "with lead style",
		content: []byte(`[.lead]
This is the ultimate paragraph.`),
		exp: `
<div class="paragraph lead">
<p>This is the ultimate paragraph.</p>
</div>`,
	}}

	var (
		parentDoc = newDocument()
		out       = bytes.Buffer{}

		c      testCase
		subdoc *Document
		err    error
	)

	for _, c = range cases {
		subdoc = parseSub(parentDoc, c.content)
		out.Reset()
		err = subdoc.ToHTMLEmbedded(&out)
		if err != nil {
			t.Fatal(err)
		}
		test.Assert(t, c.desc, c.exp, out.String())
	}
}

A testdata/author_test.txt => testdata/author_test.txt +60 -0
@@ 0,0 1,60 @@
output_call: htmlWriteHeader

>>> single author
= T
A B

<<< single author

<div id="header">
<h1>T</h1>
<div class="details">
<span id="author" class="author">A B</span><br>
</div>
</div>

>>> single author with email
= T
A B <a@b>

<<< single author with email

<div id="header">
<h1>T</h1>
<div class="details">
<span id="author" class="author">A B</span><br>
<span id="email" class="email"><a href="mailto:a@b">a@b</a></span><br>
</div>
</div>

>>> Multiple authors
= T
A B <a@b>; C <c@c>; D e_f G <>;

<<< Multiple authors

<div id="header">
<h1>T</h1>
<div class="details">
<span id="author" class="author">A B</span><br>
<span id="email" class="email"><a href="mailto:a@b">a@b</a></span><br>
<span id="author2" class="author">C</span><br>
<span id="email2" class="email"><a href="mailto:c@c">c@c</a></span><br>
<span id="author3" class="author">D e f G</span><br>
</div>
</div>

>>> Meta author
= T
:author: A B
:email: a@b

<<< Meta author

<div id="header">
<h1>T</h1>
<div class="details">
<span id="author" class="author">A B</span><br>
<span id="email" class="email"><a href="mailto:a@b">a@b</a></span><br>
</div>
</div>

A testdata/document_title_test.txt => testdata/document_title_test.txt +46 -0
@@ 0,0 1,46 @@
output_call: htmlWriteHeader

>>>
= Main: sub

<<<

<div id="header">
<h1>Main: sub</h1>
<div class="details">
</div>
</div>

>>> Without space after separator
= Main:sub

<<< Without space after separator

<div id="header">
<h1>Main:sub</h1>
<div class="details">
</div>
</div>

>>> With multiple separator after separator
= a: b: c

<<< With multiple separator after separator

<div id="header">
<h1>a: b: c</h1>
<div class="details">
</div>
</div>

>>> With custom separator
:title-separator: x
= Mainx sub

<<< With custom separator

<div id="header">
<h1>Mainx sub</h1>
<div class="details">
</div>
</div>

A testdata/header_with_empty_line_test.txt => testdata/header_with_empty_line_test.txt +31 -0
@@ 0,0 1,31 @@
output_call: ToHTMLBody

An empty line break parsing the header.

>>>
//// block
comment.
Below is empty line with spaces.
////
	   
= Title

<<<

<div id="header">
<div class="details">
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>= Title</p>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
</div>
</div>

R document_parser_test.go => testdata/list_description_with_open_block_test.txt +5 -68
@@ 1,55 1,7 @@
// SPDX-FileCopyrightText: 2020 M. Shulhan <ms@kilabit.info>
// SPDX-License-Identifier: GPL-3.0-or-later
List description with open block.

package asciidoctor
>>>

import (
	"bytes"
	"testing"

	"github.com/shuLhan/share/lib/test"
)

func TestDocumentParser_parseHeader(t *testing.T) {
	type testCase struct {
		expDoc      func() *Document
		desc        string
		content     string
		expPreamble string
	}

	var cases = []testCase{{
		desc:    "With empty line contains white spaces",
		content: "//// block\ncomment.\n////\n\t  \n= Title\n",
		expDoc: func() (doc *Document) {
			doc = newDocument()
			return doc
		},
		expPreamble: "= Title",
	}}

	var (
		c      testCase
		expDoc *Document
		gotDoc *Document
	)

	for _, c = range cases {
		t.Log(c.desc)

		expDoc = c.expDoc()
		gotDoc = Parse([]byte(c.content))

		test.Assert(t, "Title", expDoc.Title.raw, gotDoc.Title.raw)
		test.Assert(t, "rawAuthors", expDoc.rawAuthors, gotDoc.rawAuthors)
		test.Assert(t, "rawRevision", expDoc.rawRevision, gotDoc.rawRevision)
		test.Assert(t, "Attributes", expDoc.Attributes, gotDoc.Attributes)
		test.Assert(t, "Preamble text", c.expPreamble, gotDoc.preamble.toText())
	}
}

func TestDocumentParser_parseListDescription_withOpenBlock(t *testing.T) {
	var content = []byte(`
Description:: Description body with open block.
+
--


@@ 60,9 12,9 @@ Paragraph A.
--

Paragraph C.
`)

	var exp string = `
<<<

<div class="dlist">
<dl>
<dt class="hdlist1">Description</dt>


@@ 90,19 42,4 @@ Paragraph C.
</div>
<div class="paragraph">
<p>Paragraph C.</p>
</div>`

	var (
		doc *Document = Parse(content)

		got bytes.Buffer
		err error
	)

	err = doc.ToHTMLEmbedded(&got)
	if err != nil {
		t.Fatal(err)
	}

	test.Assert(t, "parseListDescription with open block", exp, got.String())
}
</div>

A testdata/meta_doctitle_test.txt => testdata/meta_doctitle_test.txt +21 -0
@@ 0,0 1,21 @@
>>>
= Abc

{doctitle} begins on a bleary Monday morning.

<<<

<div class="paragraph">
<p>Abc begins on a bleary Monday morning.</p>
</div>

>>> With meta
:doctitle: Abc

{doctitle} begins on a bleary Monday morning.

<<< With meta

<div class="paragraph">
<p>Abc begins on a bleary Monday morning.</p>
</div>

A testdata/meta_showtitle_test.txt => testdata/meta_showtitle_test.txt +43 -0
@@ 0,0 1,43 @@
output_call: ToHTMLBody

>>>
= Abc

<<<

<div id="header">
<h1>Abc</h1>
<div class="details">
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
</div>
</div>

>>> With meta showtitle off
= Abc
:showtitle!:

<<< With meta showtitle off

<div id="header">
<div class="details">
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
</div>
</div>

A testdata/paragraph_test.txt => testdata/paragraph_test.txt +10 -0
@@ 0,0 1,10 @@
>>> With lead style

[.lead]
This is the ultimate paragraph.

<<< With lead style

<div class="paragraph lead">
<p>This is the ultimate paragraph.</p>
</div>