comparison tests/conftest.py @ 1:1d09e1dec1d9 upstream

ADD: PyMuPDF v1.26.4: the original sdist. It does not yet contain MuPDF. This normally will be downloaded when building PyMuPDF.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:37:51 +0200
parents
children a6bc019ac0b2
comparison
equal deleted inserted replaced
-1:000000000000 1:1d09e1dec1d9
1 import copy
2 import os
3 import platform
4 import sys
5
6 import pymupdf
7
8 import pytest
9
10 PYMUPDF_PYTEST_RESUME = os.environ.get('PYMUPDF_PYTEST_RESUME')
11
12 @pytest.fixture(autouse=True)
13 def wrap(request):
14 '''
15 Check that tests return with empty MuPDF warnings buffer. For example this
16 detects failure to call fz_close_output() before fz_drop_output(), which
17 (as of 2024-4-12) generates a warning from MuPDF.
18
19 As of 2024-09-12 we also detect whether tests leave fds open; but for now
20 do not fail tests, because many tests need fixing.
21 '''
22 global PYMUPDF_PYTEST_RESUME
23 if PYMUPDF_PYTEST_RESUME:
24 # Skip all tests until we reach a matching name.
25 if PYMUPDF_PYTEST_RESUME == request.function.__name__:
26 print(f'### {PYMUPDF_PYTEST_RESUME=}: resuming at {request.function.__name__=}.')
27 PYMUPDF_PYTEST_RESUME = None
28 else:
29 print(f'### {PYMUPDF_PYTEST_RESUME=}: Skipping {request.function.__name__=}.')
30 return
31
32 wt = pymupdf.TOOLS.mupdf_warnings()
33 assert not wt, f'{wt=}'
34 if platform.python_implementation() == 'GraalVM':
35 pymupdf.TOOLS.set_small_glyph_heights()
36 else:
37 assert not pymupdf.TOOLS.set_small_glyph_heights()
38 next_fd_before = os.open(__file__, os.O_RDONLY)
39 os.close(next_fd_before)
40
41 if platform.system() == 'Linux' and platform.python_implementation() != 'GraalVM':
42 test_fds = True
43 else:
44 test_fds = False
45
46 if test_fds:
47 # Gather detailed information about leaked fds.
48 def get_fds():
49 import subprocess
50 path = 'PyMuPDF-linx-fds'
51 path_l = 'PyMuPDF-linx-fds-l'
52 command = f'ls /proc/{os.getpid()}/fd > {path}'
53 command_l = f'ls -l /proc/{os.getpid()}/fd > {path_l}'
54 subprocess.run(command, shell=1)
55 subprocess.run(command_l, shell=1)
56 with open(path) as f:
57 ret = f.read()
58 ret = ret.replace('\n', ' ')
59 with open(path_l) as f:
60 ret_l = f.read()
61 return ret, ret_l
62 open_fds_before, open_fds_before_l = get_fds()
63
64 pymupdf._log_items_clear()
65 pymupdf._log_items_active(True)
66
67 JM_annot_id_stem = pymupdf.JM_annot_id_stem
68
69 def get_members(a):
70 ret = dict()
71 for n in dir(a):
72 if not n.startswith('_'):
73 v = getattr(a, n)
74 ret[n] = v
75 return ret
76
77 # Allow post-test checking that pymupdf._globals has not changed.
78 _globals_pre = get_members(pymupdf._globals)
79
80 # Run the test.
81 rep = yield
82
83 sys.stdout.flush()
84
85 # Test has run; check it did not create any MuPDF warnings etc.
86 wt = pymupdf.TOOLS.mupdf_warnings()
87 if not hasattr(pymupdf, 'mupdf'):
88 print(f'Not checking mupdf_warnings on classic.')
89 else:
90 assert not wt, f'Warnings text not empty: {wt=}'
91
92 assert not pymupdf.TOOLS.set_small_glyph_heights()
93
94 _globals_post = get_members(pymupdf._globals)
95 if _globals_post != _globals_pre:
96 print(f'Test has changed pymupdf._globals from {_globals_pre=} to {_globals_post=}')
97 assert 0
98
99 log_items = pymupdf._log_items()
100 assert not log_items, f'log() was called; {len(log_items)=}.'
101
102 assert pymupdf.JM_annot_id_stem == JM_annot_id_stem, \
103 f'pymupdf.JM_annot_id_stem has changed from {JM_annot_id_stem!r} to {pymupdf.JM_annot_id_stem!r}'
104
105 if test_fds:
106 # Show detailed information about leaked fds.
107 open_fds_after, open_fds_after_l = get_fds()
108 if open_fds_after != open_fds_before:
109 import textwrap
110 print(f'Test has changed process fds:')
111 print(f' {open_fds_before=}')
112 print(f' {open_fds_after=}')
113 print(f'open_fds_before_l:')
114 print(textwrap.indent(open_fds_before_l, ' '))
115 print(f'open_fds_after_l:')
116 print(textwrap.indent(open_fds_after_l, ' '))
117 #assert 0
118
119 next_fd_after = os.open(__file__, os.O_RDONLY)
120 os.close(next_fd_after)
121
122 if test_fds and next_fd_after != next_fd_before:
123 print(f'Test has leaked fds, {next_fd_before=} {next_fd_after=}.')
124 #assert 0, f'Test has leaked fds, {next_fd_before=} {next_fd_after=}. {args=} {kwargs=}.'
125
126 if 0:
127 # This code can be useful to track down test failures caused by other
128 # tests modifying global state.
129 #
130 # We run a particular test menually after each test returns.
131 sys.path.insert(0, os.path.dirname(__file__))
132 try:
133 import test_tables
134 finally:
135 del sys.path[0]
136 print(f'### Calling test_tables.test_md_styles().')
137 try:
138 test_tables.test_md_styles()
139 except Exception as e:
140 print(f'### test_tables.test_md_styles() failed: {e}')
141 raise
142 else:
143 print(f'### test_tables.test_md_styles() passed.')