# Thumbnails sized for the tile's WIDTH at 2x density, not 512 on the longest # side. The operator on sindra-nude-final, 2026-09-23: "the images look blurry # until they're selected and blown up". Every row is a change # tests/test_thumbs.py or tests/test_thumbs_browser.py claims to forbid. unit = "thumbnails sized for the tile" [[mutation]] label = "the old rule: bound the longest side at 512 (portraits get 256px of width)" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_a_portrait_keeps_its_full_width" old = ''' im.thumbnail((THUMB_WIDTH, THUMB_HEIGHT_MAX))''' new = ''' im.thumbnail((512, 512))''' [[mutation]] label = "the width bound is below what the desktop tile needs at 2x" file = "booth/thumbs.py" test = "tests/test_thumbs_browser.py::test_a_thumbnail_covers_its_tile_at_2x_density" old = ''' THUMB_WIDTH = 768''' new = ''' THUMB_WIDTH = 640''' [[mutation]] label = "no height bound: a long screenshot goes through at full height" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_an_extremely_tall_image_is_bounded_by_height_too" old = ''' im.thumbnail((THUMB_WIDTH, THUMB_HEIGHT_MAX))''' new = ''' im.thumbnail((THUMB_WIDTH, 10 ** 6))''' [[mutation]] label = "fitting in pixels is taken as light in bytes (the megabyte portrait is served whole)" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_a_tile_width_image_that_is_heavy_still_gets_a_thumbnail" old = ''' if fits and (s_stat.st_size <= THUMB_LIGHT_BYTES or getattr(im, "is_animated", False)):''' new = ''' if fits:''' [[mutation]] label = "an already small, light image gets a cache entry that saves nothing" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_an_already_small_image_gets_no_thumbnail" old = ''' if fits and (s_stat.st_size <= THUMB_LIGHT_BYTES or getattr(im, "is_animated", False)):''' new = ''' if fits and getattr(im, "is_animated", False):''' [[mutation]] label = "a heavy animated GIF that fits is flattened to one frame" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_an_animated_gif_that_fits_is_served_as_itself" old = ''' if fits and (s_stat.st_size <= THUMB_LIGHT_BYTES or getattr(im, "is_animated", False)):''' new = ''' if fits and s_stat.st_size <= THUMB_LIGHT_BYTES:''' [[mutation]] label = "an unversioned cache name: a thumbnail cut to the old rule is served forever" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_a_thumbnail_cut_to_the_old_rule_is_not_served" old = ''' return booth / THUMB_DIR / f"{rel}.{rule}.webp"''' new = ''' return booth / THUMB_DIR / (rel + ".webp")''' # ---- the heid bug-hunt on this change (4/4 arms), folded ------------------------ [[mutation]] label = "a cache hit trusts the name and the mtime (a planted directory is served)" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_a_planted_directory_at_the_cache_path_is_not_served" old = ''' return stat.S_ISREG(o.st_mode) and o.st_mtime_ns == s_stat.st_mtime_ns''' new = ''' return o.st_mtime_ns >= s_stat.st_mtime_ns''' [[mutation]] label = "freshness is 'at least as new' (a cp -p'd older source pins the old thumbnail)" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_a_source_replaced_with_an_older_mtime_is_rebuilt" old = ''' return stat.S_ISREG(o.st_mode) and o.st_mtime_ns == s_stat.st_mtime_ns''' new = ''' return stat.S_ISREG(o.st_mode) and o.st_mtime_ns >= s_stat.st_mtime_ns''' [[mutation]] label = "the cache dirs are made by following links (a planted .thumbs link escapes the booth)" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_a_symlinked_cache_dir_is_never_written_through" old = ''' if not _cache_dir(booth, out.parent): return None''' new = ''' out.parent.mkdir(parents=True, exist_ok=True)''' [[mutation]] label = "a predictable temp name the encoder writes through" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_a_planted_link_at_the_old_temp_name_cannot_redirect_the_write" old = ''' fd, tmp = tempfile.mkstemp(prefix=".", suffix=".tmp", dir=out.parent) try: with os.fdopen(fd, "wb") as fh: im.save(fh, "WEBP", quality=THUMB_QUALITY, method=4)''' new = ''' tmp = str(out) + f".{os.getpid()}.tmp" try: im.save(tmp, "WEBP", quality=THUMB_QUALITY, method=4)''' [[mutation]] label = "RGBA chosen by getbands() alone (palette transparency baked opaque)" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_palette_transparency_survives_the_thumbnail" old = ''' alpha = "A" in im.getbands() or "transparency" in im.info''' new = ''' alpha = "A" in im.getbands()''' [[mutation]] label = "EXIF orientation ignored (a camera portrait tiled sideways)" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_a_camera_portrait_is_sized_and_saved_upright" old = ''' orientation = im.getexif().get(0x0112, 1)''' new = ''' orientation = 1''' [[mutation]] label = "no pixel budget: whatever the header claims is decoded" file = "booth/thumbs.py" test = "tests/test_thumbs.py::test_an_image_past_the_pixel_budget_is_never_decoded" old = ''' if w * h > THUMB_MAX_PIXELS: return None''' new = ''' if False: return None'''