Skip to content

Commit

Permalink
Wrap all tests to check no MuPDF warning text is generated.
Browse files Browse the repository at this point in the history
In particular this detects warnings about not calling fz_close_output() before
fz_drop_output() etc, which have been fixed in this commit.

For some tests that are still expected to generate warnings, we consume
these warnings before returning.

We disable these new tests if using classic implementation of PyMuPDF, or rebased with mupdf version < 1.25.
  • Loading branch information
julian-smith-artifex-com committed Apr 13, 2024
1 parent 6c9c56e commit 0a0ecf1
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 8 deletions.
9 changes: 8 additions & 1 deletion src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4647,6 +4647,7 @@ def journal_save(self, filename):
else:
out = JM_new_output_fileptr(filename)
mupdf.pdf_write_journal(pdf, out)
out.fz_close_output()

def journal_start_op(self, name=None):
"""Begin a journalling operation."""
Expand Down Expand Up @@ -5225,6 +5226,7 @@ def obj_string(obj):
buffer = mupdf.fz_new_buffer(512)
output = mupdf.FzOutput(buffer)
mupdf.pdf_print_obj(output, obj, 1, 0)
output.fz_close_output()
return JM_UnicodeFromBuffer(buffer)

def get_array(val):
Expand Down Expand Up @@ -5398,6 +5400,7 @@ def save(
out = JM_new_output_fileptr(filename)
#log( f'{type(out)=} {type(out.this)=}')
mupdf.pdf_write_document(pdf, out, opts)
out.fz_close_output()

def save_snapshot(self, filename):
"""Save a file snapshot suitable for journalling."""
Expand Down Expand Up @@ -9123,6 +9126,7 @@ def get_svg_image(self, matrix=None, text_as_path=1):
)
mupdf.fz_run_page(self.this, dev, ctm, mupdf.FzCookie())
mupdf.fz_close_device(dev)
out.fz_close_output()
text = JM_EscapeStrFromBuffer(res)
return text

Expand Down Expand Up @@ -9875,7 +9879,7 @@ def _tobytes(self, format_, jpg_quality):
mupdf.fz_write_pixmap_as_jpeg(out, pm, jpg_quality, 0)
else:
mupdf.fz_write_pixmap_as_png(out, pm)

out.fz_close_output()
barray = JM_BinFromBuffer(res)
return barray

Expand Down Expand Up @@ -10039,6 +10043,7 @@ def pdfocr_save(self, filename, compress=1, language=None, tessdata=None):
else:
out = JM_new_output_fileptr( filename)
mupdf.fz_write_pixmap_as_pdfocr( out, pix, opts)
out.fz_close_output()

def pdfocr_tobytes(self, compress=True, language="eng", tessdata=None):
"""Save pixmap as an OCR-ed PDF page.
Expand Down Expand Up @@ -15088,6 +15093,7 @@ def JM_convert_to_pdf(doc, fp, tp, rotate):
res = mupdf.fz_new_buffer(8192)
out = mupdf.FzOutput(res)
mupdf.pdf_write_document(pdfout, out, opts)
out.fz_close_output()
c = mupdf.fz_buffer_extract_copy(res)
assert isinstance(c, bytes)
return c
Expand Down Expand Up @@ -16951,6 +16957,7 @@ def JM_object_to_buffer(what, compress, ascii):
res = mupdf.fz_new_buffer(512)
out = mupdf.FzOutput(res)
mupdf.pdf_print_obj(out, what, compress, ascii)
out.fz_close_output()
mupdf.fz_terminate_buffer(res)
return res

Expand Down
1 change: 1 addition & 0 deletions src/extra.i
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,7 @@ static mupdf::FzBuffer JM_object_to_buffer(const mupdf::PdfObj& what, int compre
mupdf::FzBuffer res = mupdf::fz_new_buffer(512);
mupdf::FzOutput out(res);
mupdf::pdf_print_obj(out, what, compress, ascii);
out.fz_close_output();
mupdf::fz_terminate_buffer(res);
return res;
}
Expand Down
22 changes: 22 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import fitz
import pytest

@pytest.fixture(autouse=True)
def wrap(*args, **kwargs):
'''
Check that tests return with empty MuPDF warnings buffer. For example this
detects failure to call fz_close_output() before fz_drop_output(), which
(as of 2024-4-12) generates a warning from MuPDF.
'''
wt = fitz.TOOLS.mupdf_warnings()
assert not wt, f'{wt=}'

# Run the test.
rep = yield

# Test has run; check it did not create any MuPDF warnings.
wt = fitz.TOOLS.mupdf_warnings()
if not hasattr(fitz, 'mupdf'):
print(f'Not checking mupdf_warnings on classic.')
else:
assert not wt, f'Warnings text not empty: {wt=}'
11 changes: 7 additions & 4 deletions tests/test_2548.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,18 @@ def test_2548():

# This checks that PyMuPDF 1.23.7 fixes this bug, and also that earlier
# versions with updated MuPDF also fix the bug.

rebased = hasattr(fitz, 'mupdf')
if fitz.mupdf_version_tuple >= (1, 24):
expected = 'Loop found in structure tree. Ignoring structure.'
assert wt == expected, f'expected:\n {expected!r}\nwt:\n {wt!r}\n'
if rebased:
assert wt == expected, f'expected:\n {expected!r}\nwt:\n {wt!r}\n'
assert not e
elif fitz.mupdf_version_tuple >= (1, 23, 7):
expected = 'structure tree broken, assume tree is missing: cycle in structure tree'
assert wt == expected, f'expected:\n {expected!r}\nwt:\n {wt!r}\n'
if rebased:
assert wt == expected, f'expected:\n {expected!r}\nwt:\n {wt!r}\n'
assert not e
else:
assert e
assert not wt
if rebased:
assert not wt
4 changes: 3 additions & 1 deletion tests/test_2904.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ def test_2904():
assert str(e) == 'Failed to read JPX header'
else:
assert not e


# Clear warnings, as we will have generated many.
fitz.TOOLS.mupdf_warnings()
4 changes: 4 additions & 0 deletions tests/test_2907.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ def test_2907():
pdf_pages = list(fitz_document.pages())
(page,) = pdf_pages
page.clean_contents()
if fitz.mupdf_version_tuple < (1, 25):
# We expect 'dropping unclosed PDF processor' warnings.
wt = fitz.TOOLS.mupdf_warnings()
assert wt
27 changes: 25 additions & 2 deletions tests/test_general.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ def test_wrapcontents():
page.set_contents(xref)
assert len(page.get_contents()) == 1
page.clean_contents()
rebased = hasattr(fitz, 'mupdf')
if rebased:
wt = fitz.TOOLS.mupdf_warnings()
assert wt == 'PDF stream Length incorrect'


def test_page_clean_contents():
Expand Down Expand Up @@ -373,7 +377,14 @@ def test_2108():
def test_2238():
filepath = f'{scriptdir}/resources/test2238.pdf'
doc = fitz.open(filepath)

rebased = hasattr(fitz, 'mupdf')
if rebased:
wt = fitz.TOOLS.mupdf_warnings()
assert wt == (
'format error: cannot recognize version marker\n'
'trying to repair broken xref\n'
'repairing PDF document'
), f'{wt=}'
first_page = doc.load_page(0).get_text('text', fitz.INFINITE_RECT())
last_page = doc.load_page(-1).get_text('text', fitz.INFINITE_RECT())

Expand Down Expand Up @@ -550,7 +561,7 @@ def test_2692():


def test_2596():
"""Cconfirm correctly abandoning cache when reloading a page."""
"""Confirm correctly abandoning cache when reloading a page."""
doc = fitz.Document(f"{scriptdir}/resources/test_2596.pdf")
page = doc[0]
pix0 = page.get_pixmap() # render the page
Expand All @@ -562,6 +573,10 @@ def test_2596():
page = doc.reload_page(page)
pix1 = page.get_pixmap()
assert pix1.samples == pix0.samples
rebased = hasattr(fitz, 'mupdf')
if rebased:
wt = fitz.TOOLS.mupdf_warnings()
assert wt == 'too many indirections (possible indirection cycle involving 24 0 R)'


def test_2730():
Expand Down Expand Up @@ -698,6 +713,14 @@ def assert_rects_approx_eq(a, b):
else:
# 2023-11-05: Currently broken in mupdf master.
print(f'test_2710(): Not Checking page.rect and rect.')
rebased = hasattr(fitz, 'mupdf')
if rebased:
wt = fitz.TOOLS.mupdf_warnings()
assert wt == (
"syntax error: cannot find ExtGState resource 'GS7'\n"
"syntax error: cannot find ExtGState resource 'GS8'\n"
"encountered syntax errors; page may not be correct"
)


def test_2736():
Expand Down
12 changes: 12 additions & 0 deletions tests/test_pixmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ def product(x, y):
pix.save(os.path.abspath(f'{__file__}/../../tests/test_3050_out.png'))
assert digest1 != digest0
assert digest1 == digest_expected
rebased = hasattr(fitz, 'mupdf')
if rebased:
wt = fitz.TOOLS.mupdf_warnings()
assert wt == 'PDF stream Length incorrect'

def test_3058():
doc = fitz.Document(os.path.abspath(f'{__file__}/../../tests/resources/test_3058.pdf'))
Expand Down Expand Up @@ -233,6 +237,14 @@ def test_3072():
pix = page_49.get_pixmap(clip=rect, matrix=zoom)
image_save_path = f'{out}/2.jpg'
pix.save(image_save_path, jpg_quality=95)

wt = fitz.TOOLS.mupdf_warnings()
assert wt == (
"syntax error: cannot find ExtGState resource 'BlendMode0'\n"
"encountered syntax errors; page may not be correct\n"
"syntax error: cannot find ExtGState resource 'BlendMode0'\n"
"encountered syntax errors; page may not be correct"
)

def test_3134():
doc = fitz.Document()
Expand Down
8 changes: 8 additions & 0 deletions tests/test_showpdfpage.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,11 @@ def test_2742():

dest.save(os.path.abspath(f'{__file__}/../../tests/test_2742-out.pdf'))
print("The end!")

rebased = hasattr(fitz, 'mupdf')
if rebased:
wt = fitz.TOOLS.mupdf_warnings()
assert wt == (
'Circular dependencies! Consider page cleaning.\n'
'... repeated 3 times...'
)
12 changes: 12 additions & 0 deletions tests/test_tesseract.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,15 @@ def test_tesseract():
assert type(e) == e_expected_type, f'{type(e)=} != {e_expected_type=}.'
else:
assert 0, f'Expected exception {e_expected!r}'
rebased = hasattr(fitz, 'mupdf')
if rebased:
wt = fitz.TOOLS.mupdf_warnings()
if fitz.mupdf_version_tuple < (1, 25):
assert wt
else:
assert wt == (
'UNHANDLED EXCEPTION!\n'
'library error: Tesseract initialisation failed\n'
'dropping unclosed output'
)

10 changes: 10 additions & 0 deletions tests/test_toc.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ def test_circular():
"""The test file contains circular bookmarks."""
doc = fitz.open(circular)
toc = doc.get_toc(False) # this must not loop
rebased = hasattr(fitz, 'mupdf')
if rebased:
wt = fitz.TOOLS.mupdf_warnings()
assert wt == 'Bad or missing prev pointer in outline tree, repairing'

def test_2355():

Expand Down Expand Up @@ -122,3 +126,9 @@ def test_2788():
# Also test Page.get_links() bugfix from #2817.
for page in document:
page.get_links()
wt = fitz.TOOLS.mupdf_warnings()
assert wt == (
"syntax error: expected 'obj' keyword (0 3 ?)\n"
"trying to repair broken xref\n"
"repairing PDF document"
)

0 comments on commit 0a0ecf1

Please sign in to comment.