diff --git a/.gitignore b/.gitignore index b1564eb..af12fb9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .cutekit/ __pycache__/ +tmp/ .vscode/ .DS_Store .python-version diff --git a/ck b/ck index 0c9cfa1..9cf80c3 100755 --- a/ck +++ b/ck @@ -65,7 +65,7 @@ if [ ! -f .cutekit/tools-ready ]; then mkdir -p .cutekit if [ ! -d .cutekit/venv ]; then echo "Setting up Python virtual environment..." - python3 -m venv .cutekit/venv + python3.11 -m venv .cutekit/venv fi source .cutekit/venv/bin/activate diff --git a/meta/plugins/reftest.py b/meta/plugins/reftest.py index fde12c0..904ad5b 100644 --- a/meta/plugins/reftest.py +++ b/meta/plugins/reftest.py @@ -1,8 +1,8 @@ from cutekit import shell, vt100, cli, builder, model from pathlib import Path -import dataclasses as dt -from dataclasses_json import DataClassJsonMixin -import tempfile +import re +import textwrap +import difflib def buildPaperMuncher(args: model.TargetArgs) -> builder.ProductScope: @@ -17,24 +17,108 @@ def buildPaperMuncher(args: model.TargetArgs) -> builder.ProductScope: def _(args: model.TargetArgs): paperMuncher = buildPaperMuncher(args) - for file in shell.find("tests", ["*.html", "*.xhtml"]): - print(f"Running reftest {file}...") - path = Path(file) + test_tmp_folder = Path(__file__).parent.parent.parent / 'tests/tmp' + test_tmp_folder.mkdir(parents=True, exist_ok=True) + for temp in test_tmp_folder.glob('*.*'): + temp.unlink() - refPath = path.parent / ".ref" / (path.name + ".ref") - output = paperMuncher.popen("print", "-sdlpo", "/dev/null", file) + temp_file = test_tmp_folder / 'reftest.xhtml' + def update_temp_file(container, rendering): + # write xhtml into the temporary file + xhtml = container.replace("", rendering) if container else rendering + with temp_file.open("w") as f: + f.write(f"\n{textwrap.dedent(xhtml)}") - if not refPath.exists(): - vt100.warning(f"{refPath} not found, creating reference") - refPath.parent.mkdir(parents=True, exist_ok=True) - with refPath.open("x") as ref: - ref.write(output) + for file in shell.find("tests", ["*.xhtml"]): + if '/tmp/' in file: continue - with refPath.open() as ref: - refContent = ref.read() + print(f"Running comparison test {file}...") - if refContent == output: - print(f"{vt100.GREEN}Passed{vt100.RESET}") - else: - print(f"{vt100.RED}Failed{vt100.RESET}") + with Path(file).open() as f: + content = f.read() + + Num = 0 + for id, name, test in re.findall(r"""([\w\W]+?)""", content): + print(f"{vt100.WHITE}Test {name!r}{vt100.RESET}") + Num += 1 + temp_file_name = re.sub(r"[^\w.-]", "_", f"{file}-{id or Num}") + + search = re.search(r"""([\w\W]+?)""", content) + container = search and search.group(1) + + expected_xhtml = None + expected_image = None + if id: + ref_image = Path(file).parent / f'{id}.bmp' + if ref_image.exists(): + with ref_image.open('rb') as f: + expected_image = f.read() + with (test_tmp_folder / f"{temp_file_name}.expected.bmp").open("wb") as f: + f.write(expected_image) + + num = 0 + for tag, info, rendering in re.findall(r"""<(rendering|error)([^>]*)>([\w\W]+?)""", test): + num += 1 + if "skip" in info: + print(f"{vt100.YELLOW}Skip test{vt100.RESET}") + continue + + update_temp_file(container, rendering) + + # generate temporary bmp + img_path = test_tmp_folder / f"{temp_file_name}-{num}.bmp" + paperMuncher.popen("render", "-sdlpo", img_path, temp_file) + with img_path.open('rb') as f: + output_image = f.read() + + # the first template is the expected value + if not expected_xhtml: + expected_xhtml = rendering + expected_pdf = paperMuncher.popen("print", "-sdlpo", test_tmp_folder / f"{temp_file_name}.expected.pdf", temp_file) + if not expected_image: + expected_image = output_image + with (test_tmp_folder / f"{temp_file_name}.expected.bmp").open("wb") as f: + f.write(expected_image) + continue + + # check if the rendering is different + if (expected_image == output_image) == (tag == "rendering"): + img_path.unlink() + print(f"{vt100.GREEN}Passed{vt100.RESET}") + else: + # generate temporary file for debugging + output_pdf = paperMuncher.popen("print", "-sdlpo", test_tmp_folder / f"{temp_file_name}-{num}.pdf", temp_file) + + if tag == "error": + print(f"{vt100.RED}Failed {name!r} (The result should be different){vt100.RESET}") + print(f"{vt100.WHITE}{expected_xhtml[1:].rstrip()}{vt100.RESET}") + print(f"{vt100.WHITE}{test_tmp_folder / f'{temp_file_name}-{num}.pdf'}{vt100.RESET}") + print(f"{vt100.WHITE}{test_tmp_folder / f'{temp_file_name}-{num}.bmp'}{vt100.RESET}") + print(f"{vt100.BLUE}{rendering[1:].rstrip()}{vt100.RESET}") + else: + print(f"{vt100.RED}Failed {name!r}{vt100.RESET}") + print(f"{vt100.WHITE}{expected_xhtml[1:].rstrip()}{vt100.RESET}") + print(f"{vt100.WHITE}{test_tmp_folder / f'{temp_file_name}.expected.pdf'}{vt100.RESET}") + print(f"{vt100.WHITE}{test_tmp_folder / f'{temp_file_name}.expected.bmp'}{vt100.RESET}") + print(f"{vt100.BLUE}{rendering[1:].rstrip()}{vt100.RESET}") + print(f"{vt100.BLUE}{test_tmp_folder / f'{temp_file_name}-{num}.pdf'}{vt100.RESET}") + print(f"{vt100.BLUE}{test_tmp_folder / f'{temp_file_name}-{num}.bmp'}{vt100.RESET}") + + # print rendering diff + output = output_pdf.split("---")[-3] + expected = expected_pdf.split('---')[-3] + if expected == output: + continue + diff_html = [] + theDiffs = difflib.ndiff(expected.splitlines(), output.splitlines()) + for eachDiff in theDiffs: + if eachDiff[0] == "-": + diff_html.append(f"{vt100.RED}{eachDiff}{vt100.RESET}") + elif eachDiff[0] == "+": + diff_html.append(f"{vt100.GREEN}{eachDiff}{vt100.RESET}") + elif eachDiff[0] != "?": + diff_html.append(eachDiff) + print('\n'.join(diff_html)) + + temp_file.unlink() diff --git a/tests/css/.ref/cascade.xhtml.ref b/tests/css/.ref/cascade.xhtml.ref deleted file mode 100644 index b361559..0000000 --- a/tests/css/.ref/cascade.xhtml.ref +++ /dev/null @@ -1,132 +0,0 @@ ---- START OF DOM --- -(DOCUMENT - (DOCUMENT_TYPE name="html" publicId="" systemId="") - (ELEMENT tagName=html - (ATTRIBUTE localName=HTML:lang value="en") - (TEXT data="\n\n") - (ELEMENT tagName=body - (TEXT data="\n ") - (ELEMENT tagName=div) - (TEXT data="\n") - ) - (TEXT data="\n\n") - (ELEMENT tagName=style - (TEXT data="\n div {\n width: 100px;\n height: 100px;\n background-color: red;\n }\n\n div {\n background-color: green;\n }\n\n div {\n background-color: blue;\n }\n") - ) - (TEXT data="\n\n") - ) -) - ---- END OF DOM --- - ---- START OF STYLE --- -(style-book [(style-sheet text/css . - rules: [ - (style-rule - selector: html - props: [ - (display (display box: CONTENTS)) - ] - ) - (style-rule - selector: (OR [head, style]) - props: [ - (display (display box: NONE)) - ] - ) - (style-rule - selector: table - props: [ - (display (display inside: TABLE, outside: BLOCK, item: NO)) - ] - ) - (style-rule - selector: caption - props: [ - (display (display internal: TABLE_CAPTION)) - ] - ) - (style-rule - selector: colgroup - props: [ - (display (display internal: TABLE_COLUMN_GROUP)) - ] - ) - (style-rule - selector: col - props: [ - (display (display internal: TABLE_COLUMN)) - ] - ) - (style-rule - selector: thead - props: [ - (display (display internal: TABLE_HEADER_GROUP)) - ] - ) - (style-rule - selector: tbody - props: [ - (display (display internal: TABLE_ROW_GROUP)) - ] - ) - (style-rule - selector: tfoot - props: [ - (display (display internal: TABLE_FOOTER_GROUP)) - ] - ) - (style-rule - selector: tr - props: [ - (display (display internal: TABLE_ROW)) - ] - ) - (style-rule - selector: (OR [td, th]) - props: [ - (display (display internal: TABLE_CELL)) - ] - ) - ] -), (style-sheet text/css . - rules: [ - (style-rule - selector: div - props: [ - (width 100PX) - (height 100PX) - (background-color [#00000000, #ff0000ff]) - ] - ) - (style-rule - selector: div - props: [ - (background-color [#00000000, #008000ff]) - ] - ) - (style-rule - selector: div - props: [ - (background-color [#00000000, #0000ffff]) - ] - ) - ] -)]) ---- END OF STYLE --- - ---- START OF LAYOUT --- -(flow (display inside: FLOW, outside: INLINE, item: NO) (rect 0 0 210 100) - (flow (display inside: FLOW, outside: INLINE, item: NO) (rect 0 0 210 100) - (frag (display inside: FLOW, outside: INLINE, item: NO) (rect 0 0 100 100)) - ) -) ---- END OF LAYOUT --- - ---- START OF PAINT --- -(stack - (box (rect 0 0 210 100) (radii 0) [(color #00000000)]) - (box (rect 0 0 210 100) (radii 0) [(color #00000000)]) - (box (rect 0 0 100 100) (radii 0) [(color #00000000), (color #0000ffff)]) -) ---- END OF PAINT --- \ No newline at end of file diff --git a/tests/css/cascade.xhtml b/tests/css/cascade.xhtml deleted file mode 100644 index 4fe7133..0000000 --- a/tests/css/cascade.xhtml +++ /dev/null @@ -1,24 +0,0 @@ - - - - -
- - - - - diff --git a/tests/css/display-block-basic.bmp b/tests/css/display-block-basic.bmp new file mode 100644 index 0000000..4af0773 Binary files /dev/null and b/tests/css/display-block-basic.bmp differ diff --git a/tests/css/display-block.xhtml b/tests/css/display-block.xhtml new file mode 100644 index 0000000..57c93ee --- /dev/null +++ b/tests/css/display-block.xhtml @@ -0,0 +1,259 @@ + + + + + + + + + + + + + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+
+ + + + + + + + + + + + + + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+
+ + + + + + + + + + + + + + +
+
+
+
+
+ + +
+
+
+
+
+
diff --git a/tests/css/display-flex.xhtml b/tests/css/display-flex.xhtml new file mode 100644 index 0000000..6bb2786 --- /dev/null +++ b/tests/css/display-flex.xhtml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+
+ diff --git a/tests/css/text.xhtml b/tests/css/text.xhtml new file mode 100644 index 0000000..005fdf3 --- /dev/null +++ b/tests/css/text.xhtml @@ -0,0 +1,13 @@ + + +
abc def ghi
+
+ + +
abc def ghi
+
+ + +
abc def ghi
+
+
diff --git a/tests/sale-order/sale-order-basic.pdf b/tests/sale-order/sale-order-basic.pdf new file mode 100644 index 0000000..ad7c2a1 Binary files /dev/null and b/tests/sale-order/sale-order-basic.pdf differ diff --git a/tests/sale-order/sale-order-basic_001.bmp b/tests/sale-order/sale-order-basic_001.bmp new file mode 100644 index 0000000..5f060c9 Binary files /dev/null and b/tests/sale-order/sale-order-basic_001.bmp differ diff --git a/tests/sale-order/sale-order-basic_002.bmp b/tests/sale-order/sale-order-basic_002.bmp new file mode 100644 index 0000000..51c93f6 Binary files /dev/null and b/tests/sale-order/sale-order-basic_002.bmp differ diff --git a/tests/sale-order/sale-order-basic_003.bmp b/tests/sale-order/sale-order-basic_003.bmp new file mode 100644 index 0000000..60aea5a Binary files /dev/null and b/tests/sale-order/sale-order-basic_003.bmp differ diff --git a/tests/sale-order/sale-order.xhtml b/tests/sale-order/sale-order.xhtml new file mode 100644 index 0000000..a2b8fb3 --- /dev/null +++ b/tests/sale-order/sale-order.xhtml @@ -0,0 +1,198 @@ + + + + + + + + + + + + + +
+
+
Sale Order
+
No: 2024-8976
+
+
+
+

Sold to: ...........................................

+

.......................................................

+

.......................................................

+

.......................................................

+
+
+

Ship to: ...........................................

+

.......................................................

+

.......................................................

+

.......................................................

+
+
+
+

Order date: .............

+

Due date: .............

+
+
+
+
Quantity
+
Item
+
Description
+
Unit Price
+
Amount
+
+
+
3
+
Eleifend
+
Maecenas condimentum gravida massa, eu iaculis mauris suscipit nec. Vivamus varius nisl mauris.
+
€ 123.99
+
€ 371.97
+
+
+
1
+
Auctor
+
Auctor adipisci velit.
+
€ 0.0
+
€ 0.0
+
+
+
7
+
Aenean
+
Aenean tellus lacus, eleifend quis auctor id, lacinia.
+
€ 11.0
+
€ 77.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Vestibulum nibh orci, mollis eget euismod in.
+
Total
+
€ 448,97
+
+
+
+
+

1) Curabitur eleifend tellus a maximus scelerisque.

+

1.1) Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pellentesque a orci nec vestibulum. Phasellus eu purus dignissim, ullamcorper nisi ac, auctor elit. Pellentesque sit amet porttitor justo. Donec a enim eget risus faucibus dapibus ultricies eu urna. Nunc et mi in quam posuere tincidunt non id leo. Sed eu nunc quam. Donec laoreet suscipit orci, ac pellentesque risus efficitur vitae. Curabitur eget ullamcorper odio.

+

1.2) In ultrices id massa nec porta. Sed accumsan dapibus neque eget gravida. Phasellus tempor euismod est, eget eleifend leo aliquam ac. Quisque lacinia dolor felis, vel pellentesque velit viverra id. In consequat eros sodales, mollis turpis id, tempus arcu. Nam consectetur, tellus quis porttitor vulputate, diam libero sollicitudin erat, ut vulputate velit eros sed mauris. Curabitur bibendum aliquet orci a tincidunt. Sed gravida in nunc ut gravida. Ut luctus neque eget sodales malesuada. Curabitur mattis ultricies sollicitudin.

+

1.3) Aenean placerat justo diam. Suspendisse potenti. In ut pharetra tellus. Suspendisse vel elementum ante, a faucibus orci. Nulla fringilla lacinia nisi, quis molestie risus maximus id. Duis sollicitudin felis sed tellus accumsan rhoncus ac vel quam. Fusce viverra quam vel turpis blandit, eget porta arcu sodales. Duis ultrices mi eu ex viverra ultrices. Vestibulum congue laoreet risus id sagittis. Donec ornare ac nulla a consequat. Proin condimentum enim et turpis semper, eu efficitur turpis dictum.

+

2) Nulla ac eros ultrices nisi congue viverra eget sagittis orci.

+

2.1) Mauris tincidunt dolor vitae iaculis tempus. Nulla facilisi. Integer a interdum sapien, eget dapibus nisl. Etiam id tellus ac dui fringilla bibendum. Vivamus et enim ex. Sed et neque dolor. Sed sed semper nulla. Donec varius metus a ex aliquet, non elementum ante vehicula. Fusce tempus volutpat sem malesuada cursus. In venenatis purus sapien, in ultricies nulla porttitor et. Donec a pellentesque felis. Vivamus lorem ante, suscipit venenatis egestas id, vulputate at tortor. Aliquam hendrerit arcu et lacus eleifend, a egestas arcu volutpat. Duis accumsan volutpat magna, non hendrerit felis auctor ut.

+

2.2) Etiam aliquam, lectus ac vestibulum blandit, justo nulla egestas nibh, sed sodales orci eros et mi. Sed euismod scelerisque ipsum. Pellentesque a sodales lectus. Praesent at condimentum urna, at fringilla erat. Nunc tristique dolor vitae porta ullamcorper. Integer quis felis et ex scelerisque imperdiet. Integer laoreet mauris sit amet neque molestie, sed volutpat ex consequat. Proin nec neque pulvinar dui dapibus gravida. Duis laoreet justo ac mauris maximus volutpat. Duis lobortis auctor malesuada. Phasellus tincidunt efficitur tellus eget finibus. Sed ornare vitae ipsum eu cursus. Donec vitae mauris nec dui ornare rhoncus nec ut risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

+

3) Proin quis purus at orci efficitur cursus at vel justo.

+

3.1) Quisque vitae mi consectetur, porta libero in, faucibus erat. Donec scelerisque sollicitudin dolor. Aenean non interdum magna. Vivamus rutrum mattis fringilla. Cras nec diam sit amet augue venenatis egestas sit amet non sapien. Suspendisse euismod massa eget mauris fermentum, ac fringilla neque aliquet. Nullam pulvinar nec dui efficitur tempor. Cras ultrices massa felis, quis pharetra risus interdum vitae. Donec quis luctus mi. Nullam et suscipit orci. Phasellus imperdiet viverra nunc at bibendum. Cras consectetur tempor risus vel rhoncus. Sed elementum felis sem, vitae feugiat est pharetra ut.

+

3.2) Phasellus a faucibus neque. Proin a felis sit amet eros semper finibus non a lectus. Ut ornare consectetur lectus nec vulputate. Nullam non auctor sem, eget congue lacus. Pellentesque consectetur euismod nunc at placerat. Vivamus augue elit, lacinia in quam quis, pretium viverra nisi. Pellentesque ac molestie magna. Vivamus vel pretium velit. In ultrices gravida enim, non viverra nunc interdum ut. Proin tristique vestibulum quam, at laoreet orci aliquam et. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; In in augue vel purus lobortis sodales. Praesent convallis, sapien eget posuere viverra, lacus dolor volutpat ex, ac convallis nibh sem non odio. Donec aliquam mollis aliquam. Maecenas posuere neque convallis, tempus lorem vel, scelerisque dui. Cras tristique erat vitae diam lacinia euismod.

+

3.3) In tempor diam ornare, commodo lectus commodo, vestibulum enim. Ut rutrum, nunc a commodo dignissim, augue nibh mollis ligula, in lobortis justo leo eu tellus. Suspendisse ipsum neque, ornare sed erat in, efficitur ornare arcu. Curabitur pulvinar lectus justo, non feugiat metus dignissim sed. Praesent euismod metus nec tellus lacinia, sed dictum diam vehicula. Vivamus convallis euismod sapien id vestibulum. Nunc justo felis, feugiat vitae malesuada vulputate, hendrerit ut diam. Etiam porta iaculis ipsum, non pretium nisl dignissim nec. Aenean id tempus mauris. Quisque venenatis eros in eleifend feugiat. Curabitur dapibus tellus non dui mattis scelerisque. Donec imperdiet eget nunc vitae sodales. Mauris ut lacus dui.

+

4) Vulputate

+

4.1) Donec eget lacus vel purus semper ullamcorper eu id nisl. Curabitur mollis vel odio ut eleifend. Sed sed mollis urna. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse potenti. Nulla sit amet pharetra orci. Aliquam tempus, magna quis congue faucibus, est elit ullamcorper arcu, at pellentesque augue dolor at tellus. Nam venenatis dolor sapien, tincidunt vehicula risus pulvinar vel. Integer malesuada odio a consectetur sollicitudin. Pellentesque at dignissim dolor. Aliquam pharetra ullamcorper leo, vitae vestibulum odio fermentum ut. Curabitur vulputate nulla eget est cursus, eu consectetur magna rhoncus. Donec at tristique enim, a molestie dui. Maecenas porta sit amet sapien ut dapibus. Integer interdum massa diam, nec sagittis tellus vestibulum quis. Donec quis nisi consequat, commodo metus nec, sollicitudin odio.

+

4.2) Vestibulum luctus ex et turpis bibendum, ut faucibus erat ultrices. Cras id congue risus. Ut ultricies porta quam a elementum. In elementum ante at lectus ultricies, sit amet mattis turpis aliquam. Vestibulum rhoncus orci sodales eros facilisis imperdiet. Praesent euismod elit erat, quis feugiat erat gravida eget. Mauris consequat neque sit amet porttitor imperdiet.

+

4.3) Maecenas quis nisl tellus. Nunc cursus bibendum consectetur. Donec sagittis a magna et viverra. Aenean viverra aliquet pretium. Vivamus convallis sem at ante euismod, a dictum felis aliquam. Phasellus sed risus purus. Nunc in accumsan dui, varius sollicitudin justo. Vestibulum ultrices ultricies enim nec lobortis. Praesent non scelerisque mauris. Vivamus quis diam urna. Nunc a gravida felis.

+
+
+ +