Skip to content

Commit d736212

Browse files
committed
Implement hybrid comparisons
1 parent 42484d1 commit d736212

File tree

6 files changed

+94
-28
lines changed

6 files changed

+94
-28
lines changed

pytest_mpl/plugin.py

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ def pytest_configure(config):
121121
"mpl_image_compare: Compares matplotlib figures "
122122
"against a baseline image")
123123

124-
if (config.getoption("--mpl") or # noqa
125-
config.getoption("--mpl-generate-path") is not None or # noqa
126-
config.getoption("--mpl-generate-hash-library") is not None): # noqa
124+
if (config.getoption("--mpl") or
125+
config.getoption("--mpl-generate-path") is not None or
126+
config.getoption("--mpl-generate-hash-library") is not None):
127127

128128
baseline_dir = config.getoption("--mpl-baseline-path")
129129
generate_dir = config.getoption("--mpl-generate-path")
@@ -257,6 +257,14 @@ def make_results_dir(self, item):
257257
"""
258258
return Path(tempfile.mkdtemp(dir=self.results_dir))
259259

260+
def baseline_directory_specified(self, item):
261+
"""
262+
Returns `True` if a non-default baseline directory is specified.
263+
"""
264+
compare = self.get_compare(item)
265+
item_baseline_dir = compare.kwargs.get('baseline_dir', None)
266+
return item_baseline_dir or self.baseline_dir or self.baseline_relative_dir
267+
260268
def get_baseline_directory(self, item):
261269
"""
262270
Return a full path to the baseline directory, either local or remote.
@@ -409,25 +417,32 @@ def compare_image_to_hash_library(self, item, fig, result_dir):
409417
if test_hash == hash_library[hash_name]:
410418
return
411419

420+
error_message = (f"hash {test_hash} doesn't match hash "
421+
f"{hash_library[hash_name]} in library "
422+
f"{hash_library_filename} for test {hash_name}.")
423+
424+
# If the compare has only been specified with hash and not baseline
425+
# dir, don't attempt to find a baseline image at the default path.
426+
if not self.baseline_directory_specified(item):
427+
return error_message
428+
429+
baseline_image_path = self.obtain_baseline_image(item, result_dir)
412430
try:
413-
baseline_image = self.obtain_baseline_image(item, result_dir)
431+
baseline_image = baseline_image_path
414432
baseline_image = None if not baseline_image.exists() else baseline_image
415433
except Exception:
416434
baseline_image = None
417435

418-
hash_error = (f"hash {test_hash} doesn't match hash "
419-
f"{hash_library[hash_name]} in library "
420-
f"{hash_library_filename} for test {hash_name}.")
421-
422436
if baseline_image is None:
423-
return hash_error
437+
error_message += f"\nUnable to find baseline image {baseline_image_path}."
424438

425-
comparison_error = self.compare_image_to_baseline(item, fig, result_dir)
439+
if baseline_image is None:
440+
return error_message
426441

427-
if not comparison_error:
428-
return hash_error + "\nHowever, the comparison to the baseline image succeeded."
442+
comparison_error = (self.compare_image_to_baseline(item, fig, result_dir) or
443+
"\nHowever, the comparison to the baseline image succeeded.")
429444

430-
return f"{hash_error}\n{comparison_error}"
445+
return f"{error_message}\n{comparison_error}"
431446

432447
def pytest_runtest_setup(self, item): # noqa
433448

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ testpaths = "tests"
4040

4141
[flake8]
4242
max-line-length = 100
43+
ignore = W504
4344

4445
[pycodestyle]
4546
max_line_length = 100
12.4 KB
Loading
17.1 KB
Loading

tests/baseline/test_hash_lib.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"test_pytest_mpl.test_hash_succeeds": "cd01b7a39330033b18d54b507635236214cae5e24f9e09d84b56462a9ac81052",
3-
"test.test_hash_fails": "FAIL"
3+
"test.test_hash_fails": "FAIL",
4+
"test.test_hash_fail_hybrid": "FAIL"
45
}

tests/test_pytest_mpl.py

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
"hashes" / hash_filename)
3131

3232
fail_hash_library = Path(__file__).parent / "baseline" / "test_hash_lib.json"
33+
baseline_dir_abs = Path(__file__).parent / "baseline" / baseline_subdir
34+
hash_baseline_dir_abs = Path(__file__).parent / "baseline" / "hybrid"
3335

3436

3537
WIN = sys.platform.startswith('win')
@@ -42,6 +44,15 @@ def call_pytest(args):
4244
return subprocess.call([sys.executable, '-m', 'pytest', '-s'] + args)
4345

4446

47+
def assert_pytest_fails_with(args, output_substring):
48+
try:
49+
subprocess.check_output([sys.executable, '-m', 'pytest', '-s'] + args)
50+
except subprocess.CalledProcessError as exc:
51+
output = exc.output.decode()
52+
assert output_substring in output, output
53+
return output
54+
55+
4556
@pytest.mark.mpl_image_compare(baseline_dir=baseline_dir_local,
4657
tolerance=DEFAULT_TOLERANCE)
4758
def test_succeeds():
@@ -172,10 +183,7 @@ def test_generate(tmpdir):
172183
gen_dir = tmpdir.mkdir('spam').mkdir('egg').strpath
173184

174185
# If we don't generate, the test will fail
175-
try:
176-
subprocess.check_output([sys.executable, '-m', 'pytest', '-s', '--mpl', test_file])
177-
except subprocess.CalledProcessError as exc:
178-
assert b'Image file not found for comparison test' in exc.output, exc.output.decode()
186+
assert_pytest_fails_with(['--mpl', test_file], 'Image file not found for comparison test')
179187

180188
# If we do generate, the test should succeed and a new file will appear
181189
code = call_pytest([f'--mpl-generate-path={gen_dir}', test_file])
@@ -282,10 +290,50 @@ def test_hash_fails(tmpdir):
282290
f.write(TEST_FAILING_HASH)
283291

284292
# If we use --mpl, it should detect that the figure is wrong
285-
try:
286-
subprocess.check_output([sys.executable, '-m', 'pytest', '-s', '--mpl', test_file])
287-
except subprocess.CalledProcessError as exc:
288-
assert b"doesn't match hash FAIL in library" in exc.output, exc.output.decode()
293+
output = assert_pytest_fails_with(['--mpl', test_file], "doesn't match hash FAIL in library")
294+
# We didn't specify a baseline dir so we shouldn't attempt to find one
295+
assert "Unable to find baseline image" not in output, output
296+
297+
# If we don't use --mpl option, the test should succeed
298+
code = call_pytest([test_file])
299+
assert code == 0
300+
301+
302+
TEST_FAILING_HYBRID = f"""
303+
import pytest
304+
import matplotlib.pyplot as plt
305+
@pytest.mark.mpl_image_compare(hash_library="{fail_hash_library}")
306+
def test_hash_fail_hybrid():
307+
fig = plt.figure()
308+
ax = fig.add_subplot(1,1,1)
309+
ax.plot([1,2,3])
310+
return fig
311+
"""
312+
313+
314+
def test_hash_fail_hybrid(tmpdir):
315+
316+
test_file = tmpdir.join('test.py').strpath
317+
with open(test_file, 'w', encoding='ascii') as f:
318+
f.write(TEST_FAILING_HYBRID)
319+
320+
# Assert that image comparison runs and fails
321+
output = assert_pytest_fails_with(['--mpl', test_file,
322+
f'--mpl-baseline-path={hash_baseline_dir_abs / "fail"}'],
323+
"doesn't match hash FAIL in library")
324+
assert "Error: Image files did not match." in output, output
325+
326+
# Assert reports missing baseline image
327+
output = assert_pytest_fails_with(['--mpl', test_file,
328+
'--mpl-baseline-path=/not/a/path'],
329+
"doesn't match hash FAIL in library")
330+
assert "Unable to find baseline image" in output, output
331+
332+
# Assert reports image comparison succeeds
333+
output = assert_pytest_fails_with(['--mpl', test_file,
334+
f'--mpl-baseline-path={hash_baseline_dir_abs / "succeed"}'],
335+
"doesn't match hash FAIL in library")
336+
assert "However, the comparison to the baseline image succeeded." in output, output
289337

290338
# If we don't use --mpl option, the test should succeed
291339
code = call_pytest([test_file])
@@ -310,12 +358,13 @@ def test_hash_missing(tmpdir):
310358
with open(test_file, 'w') as f:
311359
f.write(TEST_MISSING_HASH)
312360

313-
# If we use --mpl, it should detect that the figure is wrong
314-
try:
315-
subprocess.check_output([sys.executable, '-m', 'pytest', '-s', '--mpl', test_file,
316-
f'--mpl-hash-library={os.path.join(baseline_dir, "test_hash_lib.json")}']) # noqa
317-
except subprocess.CalledProcessError as exc:
318-
assert b"Can't find hash library at path" in exc.output, exc.output.decode()
361+
# Assert fails if hash library missing
362+
assert_pytest_fails_with(['--mpl', test_file, '--mpl-hash-library=/not/a/path'],
363+
"Can't find hash library at path")
364+
365+
# Assert fails if hash not in library
366+
assert_pytest_fails_with(['--mpl', test_file, f'--mpl-hash-library={fail_hash_library}'],
367+
"Hash for test 'test.test_hash_missing' not found in")
319368

320369
# If we don't use --mpl option, the test should succeed
321370
code = call_pytest([test_file])

0 commit comments

Comments
 (0)