Repository navigation
test: coverage and mutation gates; fix Ctrl+C, La, I/O messages - #9
Merged
Merged
Conversation
Collection never calls into the package: a broken build, or a mutant of can_decode(), then fails the tests that ask instead of the whole session. mutmut verifies its setup by breaking every function and needs the tests, not their collection, to fail.
With threads=1 libphash runs the callbacks on the calling thread, and KeyboardInterrupt arrives as a callback is entered, before its own try. cffi printed "Exception ignored" and returned 0: the batch stopped, and hash_files() returned the cancelled results instead of raising. The callbacks now leave escaping exceptions to an onerror handler, which keeps them per thread for finish() to re-raise. The Ctrl+C test runs with one thread and with workers, with and without a progress callback. The unused interrupted and previous_wakeup attributes are removed.
ph_get_last_error_message() never returns NULL: it gives an empty string when the last load set no message. os.path.isdir() and exists() answer False for a path they cannot check, so the try around them caught nothing. on_progress is registered only with a progress function, so its check for None never ran.
The tests cover what was reachable but untested: algorithms given as their C value, an empty algorithm list, ImageContext.config, file objects whose read() returns a memoryview or a str, digest-only hashes(), comparisons with other types, memoryview pixel layouts, array interfaces without data, a batch without the wakeup fd or alongside an event loop's, and a progress callback that is not called after it stops the batch. C errors that the binding's own checks leave unreachable are reached through a stand-in lib that fails one function: the caller gets the matching exception, and a batch frees its config template.
- Pixels loses its keepalive: ffi.from_buffer() keeps its object alive, and an array passed by address is held by the caller for the call. - A memoryview is taken of every buffer exporter, memoryview included; formats 'B' and 'c' are one byte, so the itemsize check is gone. - Palette and gray-with-alpha Pillow images become RGBA whether or not they carry transparency: libphash blends an opaque pixel to its own value exactly, so an opaque RGBA image hashes as RGB. - read_file() raises the ImageIOError itself, for ImageContext and for batches alike, and a batch chunk's progress is shifted by _shifted().
Where the binding reads files itself (Windows), a missing or empty file gave "No such file or directory: <path>" and "empty file: <path>", and a bytes path printed as b'...'. read_file() now words them as libphash does, "Cannot open '<path>': <reason>" and "Cannot read '<path>': file is empty", with the path decoded. The error and batch tests run with libphash opening the files and with the binding reading them, and check the message, errno and filename of each failure. Batch tests check every result's source, the progress total of hash_buffers(), that threads=0 runs on workers, and that an exception from progress stops the batch; Ctrl+C tests clear a wakeup fd the test runner may have set. ImageIOError.__str__ drops its check for empty args, which a message always fills.
Pillow converts premultiplied gray-with-alpha (La) only to LA, so
img.convert("RGBA") raised ValueError and ph.hash() of such an image
failed. La now goes through LA to RGBA.
The pixel tests compare each converted Pillow mode with Pillow's own
conversion, on an image whose alpha changes the hashes; check that
arrays and memoryviews reach C at their own address with their row
step; cover overlapping rows and reversed channels; and check the full
text of each pixel error.
ph_load_from_pixels() clears the context's message and never sets one, so reading it after a failed pixel load always gave an empty string.
Digest tests take the kind from whichever digest states it, name the right metric for each wrong kind, hash distinct digests apart, quote the text a hex parser refuses (non-ASCII included) and give the full text of the 64-bit range checks. The stand-in lib rejects each digest metric in turn. ImageContext's pickle and copy errors are checked in full, and a missing path that is not UTF-8 reads as U+FFFD in the message.
mutmut mutates src/libphash/ and runs the tests against each mutant. tools/mutants.py passes only when every mutant is caught (killed, timed out or crashed) or listed in tests/equivalent_mutants.json with the reason no test can tell it apart: typing.cast() arguments, strict= on zips of equal length, codec name case, buffers larger than C fills, the chunk size of the Python file reader. It fails on a listed mutant that no longer survives. Entries name a mutant by its function and changed lines, not mutmut's number, which shifts as the code changes. mutation.yml runs it weekly, by hand, and when the package or the gate changes. pytest collects only tests/, since mutmut keeps a copy of them in mutants/.
pytest turns every warning into an error, refuses unknown markers and settings, and stops a test after five minutes (pytest-timeout, now installed wherever the tests run). The development environment runs the tests in a random order with pytest-randomly, which prints its seed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
coveragejob that combines the matrix's data (HTML report as an artifact, summary in the job summary).mutation.yml: mutmut oversrc/libphash/;tools/mutants.pypasses when every mutant is caught or listed intests/equivalent_mutants.jsonwith its reason.KeyboardInterruptfrom a batch withthreads=1; Pillow images in modeLahash; I/O errors read the same when the binding reads the file (Windows) as when libphash does.🤖 Generated with Claude Code