# Mutations the check tests must catch. One row each: file | exact text | replacement | why
# `python3 .github/scripts/run_tests.py --mutants --only check` applies each row to a scratch copy
# of check/ and fails when the tests still pass. The text must occur exactly once in the file.
gwt_check/links.py | if len(w) >= MIN_WORD] | if len(w) >= 0] | words of fewer than 3 letters never count as a name
gwt_check/links.py | if len([1 for h in ch.get("parent_family_list") or [] if h in O["families"]]) > 1: | if False: | a child with several parental families gets one row per family and a flag
gwt_check/links.py | if x.get("ref") in E and kind(x.get("role")) == "primary"] | if x.get("ref") in E] | an event where the child is only a godparent is not the child's record
gwt_check/links.py | if any(x not in BIOLOGICAL for x in rel): | if any(x and x != "birth" for x in rel): | an Unknown relation counts as by birth, as in the other checks
gwt_check/links.py | return [C[h] for h in handles or [] if h in C and not C[h].get("private")] | return [C[h] for h in handles or [] if h in C] | a private citation is not read
gwt_check/links.py | if n in N and not N[n].get("private")] | if n in N] | a private note is not read
gwt_check/links.py | (w & given and w & FILIATION) | (w & given) | one given name of several counts only beside a filiation word
gwt_check/links.py | if _public_cits(tree, r.get("citation_list")): | if False: | a citation on the link itself is the top level
gwt_check/links.py | flags = [f"the link is marked {mark}"] if mark in FLAG_MARKS else [] | flags = [] | an open [probable] or [wrong link] mark is flagged
gwt_check/links.py | rows = [r for r in rep["rows"] if only is None or r["level"] == only or (only == "flagged" and r["flags"])] | rows = rep["rows"] | --level prints only the rows of that level
gwt_check/links.py | for r in rows[:limit]: | for r in rows: | --limit caps the printed rows
gwt_check/act.py | elif len(rows) > 1: | elif False: | an act whose subject fits several people is never matched to one
gwt_check/act.py | if registered: | if False: | a tree date that is the registration day is reported as such, not as a plain difference
gwt_check/act.py | if r.get("role") == "Primary" and r.get("ref") in E | if r.get("ref") in E | a godparent's or witness's ref to an event is never read as the person's own event
gwt_check/act.py | if p.get("private"):  # counted | if False:  # counted | a person marked private is counted but nothing of them is shown
gwt_check/act.py | if not c or c.get("private"): | if not c: | a private citation is never searched for the act's reference
gwt_check/act.py | and all(isinstance(who.get(k) or "", str) for k in ("given", "surname"))) | and True) | a name given as a list or a number is refused as bad input, not a crash
gwt_check/act.py | cands = wed or cands | cands = cands | a marriage act counts only the namesakes who have a family with the named spouse
gwt_check/act.py | p.get("gramps_id") == person] | True] | --person picks exactly the named person
gwt_check/act.py | if want == names[0]: | if want in names: | a place that only encloses the tree's place is not reported as the same place
gwt_check/act.py | r"(?<!\w)" + re.escape(want) | re.escape(want) | a reference is not found inside a longer number ("1" in "folio 21")
gwt_check/act.py | x in P and x != p["handle"] and _fits | x in P and _fits | a spouse name that fits the subject is never the subject himself
gwt_check/cites.py | if want and acts and want not in acts: | if want and acts: | a citation whose page names the event's own kind of act is not other-act
gwt_check/cites.py | (?:s|es)?(?!\w)", f) | ", f) | an act word matches whole, never inside a longer word such as birthplace
gwt_check/cites.py | "boda", | "bodx", | a marriage cited by a «Bodas» page is not another kind of act
gwt_check/cites.py | if want in ("death", "marriage") and any(m in _fold(page) for m in MARGINAL): | if False: | a death or marriage cited by a marginal mention is flagged
gwt_check/fill.py | (flagged if prob else parts).extend(text) | parts.extend(text) | fill-places does not propose a place that only a flagged citation names
gwt_check/fill.py | if only: | if False: | a place only a flagged citation names is listed under held_back
gwt_check/cites.py | if want and acts and want not in acts: | if acts and want not in acts: | an event of no act kind (a residence) is never another act
gwt_check/cites.py | "baptism": "birth", | "baptismx": "birth", | a baptism is read as a birth act
gwt_check/cites.py | "burial": "death", | "burialx": "death", | a burial is read as a death act
gwt_check/cites.py | rf"(?<!\w){w} | rf"{w} | an act word inside a longer word (renacimiento) is not that act
gwt_check/cites.py | if want in ("death", "marriage") and any | if any | a birth cited by a marginal mention is not flagged
gwt_check/cites.py | prob = citation_problem(e, c) if c else None | prob = citation_problem(e, c) | a citation missing from the dump is skipped
gwt_check/cli.py | raise BadInput(f"{tree.source}: a .gramps backup has no citation pages; use a dump") | pass | cites refuses a .gramps backup
gwt_check/fill.py | if rep.get("held_back"): | if False: | the text output counts the held-back events
gwt_check/client.py | got += pool.map(first_page, TYPES[1:]) | got += [first_page(t) for t in TYPES[1:]] | the first pages of the types are read together, not one type after another
gwt_check/checks.py | if len(group) > 1 and any(in_scope("media", h) for h, _ in group): | if len(group) > 1: | dup-media reports a group only when one member is in scope
gwt_check/checks.py | if o.get("checksum"): | if True: | a media object with no checksum is never a duplicate
gwt_check/checks.py | if any(not NOTE_DISCARDED_AFTER.match(text, m.end()) | if any(True | a hypothesis discarded right after it is not reported
gwt_check/checks.py | and not NOTE_DISCARDED_BEFORE.search(text[:m.start()]) for | and True for | a hypothesis discarded right before it is not reported
gwt_check/checks.py | _GAP = rf"(?:(?!(?:no|ni|sin|nada)\b)\w+{_SEP}){{0,3}}" | _GAP = rf"(?:\w+{_SEP}){{0,3}}" | a «no» between the marker and the discard word keeps the hypothesis open
gwt_check/checks.py | for m in word.finditer(text)): | for m in list(word.finditer(text))[:1]): | one discarded hypothesis does not hide another still open in the same note
gwt_check/checks.py | _SEP = r"[^\w.;:!?]+" | _SEP = r"\W+" | a discard word in another sentence does not discard the hypothesis
gwt_check/checks.py | if not wanted: | if False: | a tree with no such tag is never blamed for missing it
gwt_check/checks.py | if not in_scope("notes", n["handle"]) or wanted & set(n.get("tag_list") or []): | if wanted & set(n.get("tag_list") or []): | a note out of scope is not reported
gwt_check/checks.py | NOTE_DISCARDED_BEFORE = re.compile(rf"\b{_DISCARD}{_SEP}{_GAP}$") | NOTE_DISCARDED_BEFORE = re.compile(rf"\b{_DISCARD}{_SEP}{_GAP}") | a discard word before the marker counts only when it is within three words of it
gwt_check/checks.py | _GAP = rf"(?:(?!(?:no|ni|sin|nada)\b)\w+{_SEP}){{0,3}}" | _GAP = rf"(?:(?!(?:no|ni|sin|nada)\b)\w+{_SEP}){{0,30}}" | a discard word more than three words from the marker does not discard it
gwt_check/checks.py | _DISCARD = r"(?:descartad|refutad|superad|sustituid|desmentid)[oa]s?" | _DISCARD = r"(?:descartad|refutad|superad|sustituid)[oa]s?" | «desmentida» discards a hypothesis like «descartada»
gwt_check/checks.py | if len(n) >= 4: | if len(n) >= 0: | place names under four letters are skipped
gwt_check/checks.py | if any(f" {n} " in padded for n in own): | if False: | a description that names the event's own place is quiet
gwt_check/checks.py | todo += [r.get("ref") for r in pl.get("placeref_list") or []] | todo += [] | a description naming a place that encloses the event's place is quiet
gwt_check/checks.py | if h not in T["places"] or not desc or not in_scope("events", e["handle"]): | if h not in T["places"] or not desc: | place-vs-description reports only events in the run scope
gwt_check/checks.py | hits = {oh: n for oh, n in hits.items() if not any(f" {n} " in f" {m} " and n != m for m in hits.values())} | hits = hits | place-vs-description names a place once, by its longest match
gwt_check/checks.py | if n not in own and n not in people and | if n not in own and | place-vs-description skips a given name or surname of someone on the event
gwt_check/checks.py | on_event[r.get("ref")] += [T["people"][x] for x in | on_event[r.get("ref")] += [] and [T["people"][x] for x in | both spouses of a family are on the family's event
gwt_check/checks.py | for part in [n.get("first_name") or ""] + [ | for part in [ | a given name of someone on the event is not a place
gwt_check/checks.py | (residence or not origin(raw, words[j][1])) | (residence or True) | a place named as where someone is from or lives is not the event's place
gwt_check/checks.py | return ((k == 0 and before[0] in ("de", "del", "d") and | return ((False and | a clause that opens with «de» («Juan, de Otropueblo») names an origin
gwt_check/checks.py | if t in ("'", "’"): | if False: | an elided «d'» before a place still reads as «de»
gwt_check/checks.py | if k == len(before): | if False: | a place right after punctuation, or with no filler word before it, is not an origin
gwt_check/checks.py | and (stop is None or stop in ORIGIN_CLAUSE_BREAK)) | and True) | a «de» after an abbreviation dot («parr. de X») does not open a clause
gwt_check/checks.py | (stop is None or stop in ORIGIN_CLAUSE_BREAK) | (stop in ORIGIN_CLAUSE_BREAK) | a description that opens with «De X» names an origin
gwt_check/checks.py | residence = e.get("type") == "Residence" | residence = False | on a residence, where someone lives is the event's own place
gwt_check/checks.py | "vezina", "residente", | "vezina", | «residente en» names where someone lives
gwt_check/checks.py | "vecina", "vezino", "vezina", | "vecina", | the old spelling «vezino» names where someone lives
gwt_check/checks.py | "vecino", "vecina", "vezino", | "vecin", "vezino", | «en la vecindad de X» is not where someone lives
gwt_check/checks.py | "domicilie", "domiciliad", | "domicili", | «en su domicilio de X» is not where someone lives
gwt_check/checks.py | "vecino", "vecina", | "vecino", | «vecina de X» names where someone lives
gwt_check/checks.py | "vezino", "vezina", | "vezino", | the old spelling «vezina» names where someone lives
gwt_check/checks.py | "domicilie", "domiciliad", | "domicilie", | «domiciliado en X» names where someone lives
gwt_check/checks.py | {",", ";", ":", | {",", ":", | a «de» after a semicolon opens a clause
gwt_check/checks.py | ";", ":", "(", | ";", "(", | a «de» after a colon opens a clause
gwt_check/checks.py | [w for w, _ in words[j:j + len(ns)]] == ns | words[j][0] == ns[0] | a place of several words matches only as a whole
gwt_check/public.py | if owner.tree_id and guest.tree_id and owner.tree_id != guest.tree_id: | if False: | public refuses a guest dump read from another tree
gwt_check/public.py | if not any(guest.objs[t] for t in TYPES): | if False: | public refuses an empty guest dump
gwt_check/public.py | if extra: | if False: | public refuses a guest dump with objects the owner dump lacks
gwt_check/public.py | if o.get("private") is True} for t in TYPES} | if False} for t in TYPES} | a private object the guest still gets is private-visible
gwt_check/public.py | if path and x.get("private") is True: | if False: | a private sub-object the guest still gets is private-visible
gwt_check/public.py | return id_order(owner.gid(t, h)) | return owner.gid(t, h) | public rows follow numeric Gramps ID order
gwt_check/public.py | if k != "handle": | if True: | a visible private object is not also a reference to itself
gwt_check/public.py | return norm(re.sub(r"[^\w]+", " ", s)) | return norm(s) | a private name before punctuation still matches
gwt_check/public.py | if len(n) >= MIN_NAME_CHARS and " " in n | if " " in n | a short private name is not matched
gwt_check/public.py | and " " in n and n not in visible_names: | and n not in visible_names: | a one-word private name is not matched
gwt_check/public.py | if f" {n} " in padded} | } | the first word of a private name alone is not a match
gwt_check/public.py | possibly_living(owner.objs["people"][h], owner.objs["events"] | possibly_living(guest.objs["people"][h], guest.objs["events"] | a death or burial hidden from the guest still means dead
gwt_check/public.py | leaks = sum(r["check"] in LEAKS for r in rows) | leaks = len(rows) | living-visible alone does not set the exit code
gwt_check/checks.py | return undated | return True | public does not count a person with no dated birth or baptism as living
gwt_check/checks.py | if any(e and e.get("type") in ("Death", "Burial") for e in own): | if False: | a person buried or dead is not possibly living
gwt_check/checks.py | own = [events.get(r.get("ref")) for r in p.get("event_ref_list", []) if r.get("role") == "Primary"] | own = [events.get(r.get("ref")) for r in p.get("event_ref_list", [])] | only a person's own events say when they were born
gwt_check/cli.py | if owner.partial or guest.partial: | if False: | public refuses a .gramps backup
gwt_check/cli.py | return 1 if rep["leaks"] else 0 | return 0 | public exits 1 on a leak
gwt_check/cli.py | print_public(rep, args.limit or len(rep["rows"])) | print_public(rep, len(rep["rows"])) | public prints only --limit findings
gwt_check/public.py | for r in rep["rows"][:limit]: | for r in rep["rows"]: | print_public stops at the limit
gwt_check/public.py | LEAKS = ("private-visible", "private-reference", "private-name") | LEAKS = ("private-visible", "private-reference") | a private name alone sets the exit code of public
gwt_check/public.py | LEAKS = ("private-visible", "private-reference", "private-name") | LEAKS = ("private-visible", "private-name") | a private reference alone sets the exit code of public
gwt_check/public.py | if path and x.get("private") is True: | if x.get("private") is True: | a visible private object is one row, not also a sub-object row with no path
gwt_check/checks.py | if e and e.get("type") in ("Birth", "Baptism")] | if e and e.get("type") in ("Birth",)] | a recent baptism alone makes a person possibly living
gwt_check/public.py | if not rep["rows"]: | if False: | a clean comparison says nothing found
gwt_check/checks.py | and not parent_and_child(p["handle"], q["handle"]) and not apart(p, q): | and not parent_and_child(p["handle"], q["handle"]): | namesakes whose births cannot overlap get no same-relative hint
gwt_check/checks.py | if near or (kin_shared and not (yp and yq) and not apart(P[h], P[c])): | if near or (kin_shared and not (yp and yq)): | namesakes whose births cannot overlap get no fuzzy same-relative hint
gwt_check/checks.py | return bool(a and b) and (a[1] < b[0] or b[1] < a[0]) | return bool(a and b) and (a[1] <= b[0] or b[1] <= a[0]) | two births on the same day overlap
gwt_check/checks.py | if readable and len(readable) == len(alts) else None | if readable else None | one unreadable birth date leaves the birth unknown
gwt_check/checks.py | spans[p["handle"]] = (min(s.lo for s in readable), max(s.hi for s in readable)) \ | spans[p["handle"]] = (readable[-1].lo, readable[-1].hi) \ | every birth alternative widens the interval
gwt_check/checks.py | and dates.kind(E[r["ref"]].get("type")) in dates.BIRTH] | and dates.kind(E[r["ref"]].get("type")) in dates.BIRTH.union(dates.BAPTISM)] | a baptism alone never sets two namesakes apart
gwt_check/checks.py | if r.get("ref") in E and r.get("role") == "Primary" | if r.get("ref") in E | a birth the person only witnessed never sets two namesakes apart
gwt_check/checks.py | if r.get("ref") in E and r.get("role") == "Primary" | if r.get("role") == "Primary" | a dangling event reference never crashes the namesake check
gwt_check/checks.py | max(s.hi for s in readable)) | min(s.hi for s in readable)) | the latest birth alternative widens the interval too
gwt_check/media.py | or mimetypes.guess_type(o.get("path") or "")[0] or "").lower() | or "").lower() | a media object with no MIME stored is judged by its file name
gwt_check/media.py | return sum(hist[max(0, top - TONE_WINDOW):top + TONE_WINDOW + 1]) / total | return sum(hist[max(0, top - TONE_WINDOW):top + TONE_WINDOW]) / total | the tone window reaches as far above the top tone as below it
gwt_check/media.py | (a > b) | (a < b) | the dHash bit is set where the left neighbour is brighter
gwt_check/media.py | got["file_missing"] = o["handle"] in missing | got["file_missing"] = False | a file the server lists as missing is marked missing
gwt_check/media.py | todo = [o for o in media if is_image(o) and not status[o["handle"]].get("file_missing")] | todo = list(media) | no thumbnail is asked for a missing file or one that is not an image
gwt_check/media.py | if not in_scope("media", h): | if False: | a run is not blamed for old media
gwt_check/media.py | if "file_missing" not in got: | if False: | media the server was never asked about are counted
gwt_check/media.py | max(got["w"], got["h"]) < MIN_SIDE: | min(got["w"], got["h"]) < MIN_SIDE: | tiny is the longer side, so a long thin strip is not tiny
gwt_check/media.py | got["h"]) < MIN_SIDE: | got["h"]) <= MIN_SIDE: | an image of exactly MIN_SIDE px is not tiny
gwt_check/media.py | got["tone"] >= UNIFORM_SHARE: | got["tone"] > UNIFORM_SHARE: | a share of exactly UNIFORM_SHARE is uniform
gwt_check/media.py | if M[a].get("checksum") and M[a].get("checksum") == M[b].get("checksum"): | if False: | the same file twice is dup-media, not a same-page pair
gwt_check/media.py | if not (in_scope("media", a) or in_scope("media", b)): | if False: | a same-page pair needs one side in scope
gwt_check/media.py | if bits <= DUP_BITS: | if bits < DUP_BITS: | two images exactly DUP_BITS apart are the same page
gwt_check/media.py | if bits <= DUP_BITS: | if True: | two images more than DUP_BITS apart are not a pair
gwt_check/cli.py | f["info"] or f["check"] in media.AFTER_READ | f["info"] | what the server says about files survives a read that settled while someone wrote
gwt_check/media.py | "pagesize": random.randrange(1, 2**62)}) | "pagesize": 1}) | a file deleted after an earlier run is not hidden by the server's cached answer
gwt_check/media.py | frozenset(range(400, 500)) - {401, 429} | frozenset({404, 413, 415}) - {401, 429} | a thumbnail the server cannot make (422, 501) is counted, not the end of the run
gwt_check/client.py | if status in none_on: | if False: | a status the caller expects answers None instead of ending the run
gwt_check/media.py | unread += bool(got.get("unread")) | unread += 0 | thumbnails --download could not read are counted in the report
gwt_check/cli.py | if hasattr(args, "download"): | if True: | only check and dump ask the server which media files are missing
gwt_check/media.py | except ApiError as exc: | except ImportError as exc: | a failing list of missing files is a note, not the end of the check
gwt_check/media.py | return _mime(o).startswith(( | return (o.get("mime") or "").lower().startswith(( | an image with no MIME stored is measured by its file name
gwt_check/media.py | if mime.startswith(NON_VIEWABLE_PREFIXES) else | if True else | an archive or JSON file is not told to go into a Transcript note
gwt_check/media.py | if not isinstance(h, str) or len(h) != HASH_SIDE * HASH_SIDE // 4: | if not isinstance(h, str): | a dHash of the wrong length in a hand-edited dump is skipped, not compared
gwt_check/media.py | .items() if k != "file_missing"} | .items()} | a failed list of missing files leaves an earlier dump's answer out, not reported again
gwt_check/media.py | got.pop(k, None) | pass | a thumbnail not read now drops the measures an earlier dump had for it
gwt_check/media.py | got.pop("unread", None) | pass | a thumbnail measured now is no longer counted as not read
gwt_check/media.py | S = {h: status.get(h) if isinstance(status.get(h), dict) else {} for h in M} | S = {h: status.get(h) or {} for h in M} | a media status that is not a dict is skipped, not a crash
gwt_check/media.py | frozenset(range(400, 500)) - {401, 429} | frozenset(range(400, 500)) | a 429 on a thumbnail is asked again, not counted as unread
gwt_check/tree.py | media_status=status if isinstance(status, dict) else None) | media_status=status) | a dump whose media_status is not a dict is read without it, not a crash
gwt_check/cli.py | seed.media_status if seed else None | None | check --from keeps the measures of the earlier dump
