Skip to content

Commit 5958c51

Browse files
committed
review actions
1 parent fa630de commit 5958c51

9 files changed

+205
-66
lines changed

Diff for: pytest_mpl/kernels.py

+25-3
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,19 @@ def update_summary(self, summary):
118118
# The "name" class property *must* be defined in derived child class.
119119
summary["kernel"] = self.name
120120

121+
@property
122+
def metadata(self):
123+
"""
124+
The kernel metadata to be archived in a hash library with results.
125+
126+
Returns
127+
-------
128+
dict
129+
The kernel metadata.
130+
131+
"""
132+
return dict(name=self.name)
133+
121134

122135
class KernelPHash(Kernel):
123136
"""
@@ -138,15 +151,17 @@ def __init__(self, plugin):
138151
self.hamming_distance = None
139152
# Value may be overridden by py.test marker kwarg.
140153
arg = self._plugin.hamming_tolerance
141-
self.hamming_tolerance = arg if arg is not None else DEFAULT_HAMMING_TOLERANCE
154+
self.hamming_tolerance = (
155+
int(arg) if arg is not None else DEFAULT_HAMMING_TOLERANCE
156+
)
142157
# The hash-size (N) defines the resultant N**2 bits hash size.
143158
arg = self._plugin.hash_size
144-
self.hash_size = arg if arg is not None else DEFAULT_HASH_SIZE
159+
self.hash_size = int(arg) if arg is not None else DEFAULT_HASH_SIZE
145160
# The level of image detail (high freq) or structure (low freq)
146161
# represented in perceptual hash thru discrete cosine transform.
147162
arg = self._plugin.high_freq_factor
148163
self.high_freq_factor = (
149-
arg if arg is not None else DEFAULT_HIGH_FREQUENCY_FACTOR
164+
int(arg) if arg is not None else DEFAULT_HIGH_FREQUENCY_FACTOR
150165
)
151166
# py.test marker kwarg.
152167
self.option = "hamming_tolerance"
@@ -197,6 +212,13 @@ def update_summary(self, summary):
197212
summary["hamming_distance"] = self.hamming_distance
198213
summary["hamming_tolerance"] = self.hamming_tolerance
199214

215+
@property
216+
def metadata(self):
217+
result = super().metadata
218+
result["hash_size"] = self.hash_size
219+
result["high_freq_factor"] = self.high_freq_factor
220+
return result
221+
200222

201223
class KernelSHA256(Kernel):
202224
"""

Diff for: pytest_mpl/plugin.py

+68-42
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@
4343

4444
import pytest
4545

46-
from .kernels import (DEFAULT_HAMMING_TOLERANCE, DEFAULT_HASH_SIZE,
47-
DEFAULT_HIGH_FREQUENCY_FACTOR, KERNEL_SHA256, kernel_factory)
46+
from .kernels import KERNEL_SHA256, kernel_factory
4847
from .summary.html import generate_summary_basic_html, generate_summary_html
4948

5049
#: The default matplotlib backend.
@@ -65,8 +64,8 @@
6564
#: The default matplotlib plot style.
6665
DEFAULT_STYLE = "classic"
6766

68-
#: Metadata entry in the JSON hash library defining the source kernel of the hashes.
69-
META_HASH_LIBRARY_KERNEL = 'pytest-mpl-kernel'
67+
#: JSON metadata entry defining the source kernel of the hashes.
68+
META_HASH_KERNEL = 'pytest-mpl-kernel'
7069

7170
#: Valid formats for generate summary.
7271
SUPPORTED_FORMATS = {'html', 'json', 'basic-html'}
@@ -326,20 +325,18 @@ def __init__(self,
326325

327326
# Configure hashing kernel options.
328327
option = 'mpl-hash-size'
329-
hash_size = int(config.getoption(f'--{option}') or
330-
config.getini(option) or DEFAULT_HASH_SIZE)
328+
hash_size = (config.getoption(f'--{option}') or
329+
config.getini(option) or None)
331330
self.hash_size = hash_size
332331

333332
option = 'mpl-hamming-tolerance'
334-
hamming_tolerance = int(config.getoption(f'--{option}') or
335-
config.getini(option) or
336-
DEFAULT_HAMMING_TOLERANCE)
333+
hamming_tolerance = (config.getoption(f'--{option}') or
334+
config.getini(option) or None)
337335
self.hamming_tolerance = hamming_tolerance
338336

339337
option = 'mpl-high-freq-factor'
340-
high_freq_factor = int(config.getoption(f'--{option}') or
341-
config.getini(option) or
342-
DEFAULT_HIGH_FREQUENCY_FACTOR)
338+
high_freq_factor = (config.getoption(f'--{option}') or
339+
config.getini(option) or None)
343340
self.high_freq_factor = high_freq_factor
344341

345342
# Configure the hashing kernel - must be done *after* kernel options.
@@ -351,12 +348,8 @@ def __init__(self,
351348
emsg = f'Unrecognised hashing kernel {kernel!r} not supported.'
352349
raise ValueError(emsg)
353350
kernel = requested
354-
# Flag that the kernel has been user configured.
355-
self.kernel_default = False
356351
else:
357352
kernel = DEFAULT_KERNEL
358-
# Flag that the kernel has been configured by default.
359-
self.kernel_default = True
360353
# Create the kernel.
361354
self.kernel = kernel_factory[kernel](self)
362355

@@ -600,9 +593,63 @@ def compare_image_to_baseline(self, item, fig, result_dir, summary=None):
600593
summary['status_msg'] = error_message
601594
return error_message
602595

603-
def load_hash_library(self, library_path):
604-
with open(str(library_path)) as fp:
605-
return json.load(fp)
596+
def load_hash_library(self, fname):
597+
with open(str(fname)) as fi:
598+
hash_library = json.load(fi)
599+
kernel_metadata = hash_library.get(META_HASH_KERNEL)
600+
if kernel_metadata is None:
601+
msg = (f'Hash library {str(fname)!r} missing a '
602+
f'{META_HASH_KERNEL!r} entry. Assuming that a '
603+
f'{self.kernel.name!r} kernel generated the library.')
604+
self.logger.info(msg)
605+
else:
606+
if "name" not in kernel_metadata:
607+
emsg = (f"Missing kernel 'name' in the {META_HASH_KERNEL!r} entry, "
608+
f'for the hash library {str(fname)!r}.')
609+
pytest.fail(emsg)
610+
kernel_name = kernel_metadata["name"]
611+
if kernel_name not in kernel_factory:
612+
emsg = (f'Unrecognised hashing kernel {kernel_name!r} specified '
613+
f'in the hash library {str(fname)!r}.')
614+
pytest.fail(emsg)
615+
if kernel_name != self.kernel.name:
616+
option = 'mpl-kernel'
617+
if (self.config.getoption(f'--{option}') is None and
618+
len(self.config.getini(option)) == 0):
619+
# Override the default kernel with the kernel configured
620+
# within the hash library.
621+
self.kernel = kernel_factory[kernel_name](self)
622+
else:
623+
emsg = (f'Hash library {str(fname)!r} kernel '
624+
f'{kernel_name!r} does not match configured runtime '
625+
f'kernel {self.kernel.name!r}.')
626+
pytest.fail(emsg)
627+
628+
def check_metadata(key):
629+
if key not in kernel_metadata:
630+
emsg = (f'Missing kernel {key!r} in the '
631+
f'{META_HASH_KERNEL!r} entry, for the hash '
632+
f'library {str(fname)!r}.')
633+
pytest.fail(emsg)
634+
value = kernel_metadata[key]
635+
if value != getattr(self.kernel, key):
636+
option = f'mpl-{key.replace("_", "-")}'
637+
if (self.config.getoption(f'--{option}') is None and
638+
len(self.config.getini(option)) == 0):
639+
# Override the default kernel value with the
640+
# configured value within the hash library.
641+
setattr(self.kernel, key, value)
642+
else:
643+
emsg = (f"Hash library {str(fname)!r} '{key}={value}' "
644+
'does not match configured runtime kernel '
645+
f"'{key}={getattr(self.kernel, key)}'.")
646+
pytest.fail(emsg)
647+
648+
for key in self.kernel.metadata:
649+
if key != "name":
650+
check_metadata(key)
651+
652+
return hash_library
606653

607654
def compare_image_to_hash_library(self, item, fig, result_dir, summary=None):
608655
hash_comparison_pass = False
@@ -623,27 +670,6 @@ def compare_image_to_hash_library(self, item, fig, result_dir, summary=None):
623670
pytest.fail(f"Can't find hash library at path {str(hash_library_filename)!r}.")
624671

625672
hash_library = self.load_hash_library(hash_library_filename)
626-
kernel_name = hash_library.get(META_HASH_LIBRARY_KERNEL)
627-
if kernel_name is None:
628-
msg = (f'Hash library {str(hash_library_filename)!r} missing a '
629-
f'{META_HASH_LIBRARY_KERNEL!r} entry. Assuming that a '
630-
f'{self.kernel.name!r} kernel generated the library.')
631-
self.logger.info(msg)
632-
else:
633-
if kernel_name not in kernel_factory:
634-
emsg = (f'Unrecognised hashing kernel {kernel_name!r} specified '
635-
f'in the hash library {str(hash_library_filename)!r}.')
636-
pytest.fail(emsg)
637-
if kernel_name != self.kernel.name:
638-
if self.kernel_default:
639-
# Override the default kernel with the kernel configured
640-
# within the hash library.
641-
self.kernel = kernel_factory[kernel_name](self)
642-
else:
643-
emsg = (f'Hash library {str(hash_library_filename)!r} kernel '
644-
f'{kernel_name!r} does not match configured runtime '
645-
f'kernel {self.kernel.name!r}.')
646-
pytest.fail(emsg)
647673

648674
hash_name = self.generate_test_name(item)
649675
baseline_hash = hash_library.get(hash_name, None)
@@ -838,7 +864,7 @@ def pytest_unconfigure(self, config):
838864
# It's safe to inject this metadata, as the key is an invalid Python
839865
# class/function/method name, therefore there's no possible
840866
# namespace conflict with user py.test marker decorated tokens.
841-
self._generated_hash_library[META_HASH_LIBRARY_KERNEL] = self.kernel.name
867+
self._generated_hash_library[META_HASH_KERNEL] = self.kernel.metadata
842868
with open(hash_library_path, "w") as fp:
843869
json.dump(self._generated_hash_library, fp, indent=2)
844870
if self.results_always: # Make accessible in results directory
@@ -849,7 +875,7 @@ def pytest_unconfigure(self, config):
849875
result_hashes = {k: v['result_hash'] for k, v in self._test_results.items()
850876
if v['result_hash']}
851877
if len(result_hashes) > 0: # At least one hash comparison test
852-
result_hashes[META_HASH_LIBRARY_KERNEL] = self.kernel.name
878+
result_hashes[META_HASH_KERNEL] = self.kernel.metadata
853879
with open(result_hash_library, "w") as fp:
854880
json.dump(result_hashes, fp, indent=2)
855881

Diff for: tests/baseline/hashes/test_phash.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{
22
"test.test_gen": "8fe8c01f7a45b01f6645ac1fe9572b2a807e2ae0d41a7ab085967aac87997eab",
3-
"pytest-mpl-kernel": "phash"
3+
"pytest-mpl-kernel": {
4+
"name": "phash",
5+
"hash_size": 16,
6+
"high_freq_factor": 4
7+
}
48
}

Diff for: tests/baseline/hashes/test_phash_bad_kernel.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"test.test_gen": "8fe8c01f7a45b01f6645ac1fe9572b2a807e2ae0d41a7ab085967aac87997eab",
3+
"pytest-mpl-kernel": {
4+
"name": "wibble",
5+
"hash_size": 16,
6+
"high_freq_factor": 4
7+
}
8+
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"test.test_gen": "8fe8c01f7a45b01f6645ac1fe9572b2a807e2ae0d41a7ab085967aac87997eab",
3+
"pytest-mpl-kernel": {
4+
"name": "phash",
5+
"high_freq_factor": 4
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"test.test_gen": "8fe8c01f7a45b01f6645ac1fe9572b2a807e2ae0d41a7ab085967aac87997eab",
3+
"pytest-mpl-kernel": {
4+
"name": "phash",
5+
"hash_size": 16
6+
}
7+
}

Diff for: tests/baseline/hashes/test_phash_missing_name.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"test.test_gen": "8fe8c01f7a45b01f6645ac1fe9572b2a807e2ae0d41a7ab085967aac87997eab",
3+
"pytest-mpl-kernel": {
4+
"hash_size": 16,
5+
"high_freq_factor": 4
6+
}
7+
}

Diff for: tests/test_kernels.py

+30-13
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
from pathlib import Path
2-
from unittest.mock import sentinel
32

43
import pytest
54

65
from pytest_mpl.kernels import (DEFAULT_HAMMING_TOLERANCE, DEFAULT_HASH_SIZE,
7-
DEFAULT_HIGH_FREQUENCY_FACTOR, KERNEL_PHASH, KERNEL_SHA256, Kernel,
6+
DEFAULT_HIGH_FREQUENCY_FACTOR, Kernel,
87
KernelPHash, KernelSHA256, kernel_factory)
98

10-
HASH_SIZE = sentinel.hash_size
11-
HAMMING_TOLERANCE = sentinel.hamming_tolerance
12-
HIGH_FREQUENCY_FACTOR = sentinel.high_freq_factor
13-
149
#: baseline hash (32-bit)
1510
HASH_BASE_32 = "01234567"
1611

@@ -69,15 +64,16 @@ def test_phash_name():
6964

7065

7166
def test_phash_init__set():
67+
hash_size, hamming_tolerance, high_freq_factor = -1, -2, -3
7268
plugin = DummyPlugin(
73-
hash_size=HASH_SIZE,
74-
hamming_tolerance=HAMMING_TOLERANCE,
75-
high_freq_factor=HIGH_FREQUENCY_FACTOR,
69+
hash_size=hash_size,
70+
hamming_tolerance=hamming_tolerance,
71+
high_freq_factor=high_freq_factor,
7672
)
7773
kernel = KernelPHash(plugin)
78-
assert kernel.hash_size == HASH_SIZE
79-
assert kernel.hamming_tolerance == HAMMING_TOLERANCE
80-
assert kernel.high_freq_factor == HIGH_FREQUENCY_FACTOR
74+
assert kernel.hash_size == hash_size
75+
assert kernel.hamming_tolerance == hamming_tolerance
76+
assert kernel.high_freq_factor == high_freq_factor
8177
assert kernel.equivalent is None
8278
assert kernel.hamming_distance is None
8379

@@ -209,6 +205,20 @@ def test_phash_update_summary(summary, distance, tolerance, count):
209205
assert len(summary) == count
210206

211207

208+
@pytest.mark.parametrize(
209+
"hash_size,hff",
210+
[(DEFAULT_HASH_SIZE, DEFAULT_HIGH_FREQUENCY_FACTOR), (32, 8)],
211+
)
212+
def test_phash_metadata(hash_size, hff):
213+
plugin = DummyPlugin(hash_size=hash_size, high_freq_factor=hff)
214+
kernel = KernelPHash(plugin)
215+
metadata = kernel.metadata
216+
assert {"name", "hash_size", "high_freq_factor"} == set(metadata)
217+
assert metadata["name"] == KernelPHash.name
218+
assert metadata["hash_size"] == hash_size
219+
assert metadata["high_freq_factor"] == hff
220+
221+
212222
#
213223
# KernelSHA256
214224
#
@@ -234,7 +244,7 @@ def test_sha256_generate_hash():
234244

235245
def test_sha256_update_status():
236246
kernel = KernelSHA256(DummyPlugin())
237-
message = sentinel.message
247+
message = "nop"
238248
result = kernel.update_status(message)
239249
assert result is message
240250

@@ -246,3 +256,10 @@ def test_sha256_update_summary():
246256
assert len(summary) == 1
247257
assert "kernel" in summary
248258
assert summary["kernel"] == KernelSHA256.name
259+
260+
261+
def test_sha256_metadata():
262+
kernel = KernelSHA256(DummyPlugin())
263+
metadata = kernel.metadata
264+
assert {"name"} == set(metadata)
265+
assert metadata["name"] == KernelSHA256.name

0 commit comments

Comments
 (0)