Блог
Негативный контроль гейта пишет тот, кто сажает дефект, который контроль должен поймать. Эта одна фраза объясняет почти всё, что граничный оператор мутаций нашёл в наборе, уже вычищенном тремя другими.
Негативный контроль гейта пишет тот, кто сажает дефект, который контроль должен поймать. Эта одна фраза объясняет почти всё, что нашёл граничный оператор.
Мутационная линия закрыта. Тринадцать гейтов, четыре оператора, 111 мутантов, двое выживших — и оба выживших суть доказанные эквивалентности, которые теперь несут доказательство прямо в выводе рядом с собой. Дальше — не счёт. Дальше один структурный факт, который вскрыл последний оператор и не могли вскрыть первые три.
Каждый оператор ломает гейт нарочно и спрашивает, заметит ли это собственный контроль гейта. Различаются они тем, что ломают.
return 1 становится return 0. Способен ли гейт вообще ещё упасть?return 0 становится return 1. Требует ли хоть что-нибудь от него молчания?if C: становится if not (C): там, где тело несёт вердикт. Доходит ли он до ВЕРНОГО вердикта?> становится >=, < становится <=. Доходит ли он до вердикта в верном МЕСТЕ?Первые три нашли пробелы и перестали их находить: 36, 21 и 33 мутанта, убиты все. Четвёртый на первом же честном прогоне нашёл двенадцать выживших в пяти гейтах из тринадцати — на наборе, который трое предыдущих уже вычистили.
Шесть из этих двенадцати закрылись одним заходом, и они рифмуются. Каждому нужен вход, который ни одному существующему случаю не было повода построить, и всякий раз недостающим оказывался вырожденный.
| граница | вход, которого никто не сажал |
|---|---|
v[0] > 0 в переписи файлов-без-данных | файл с нулём случаев |
total > 0 and data == 0 для нового файла | новый файл с нулём случаев |
total == 0 and old[0] > 0 | файл, уже пустой к моменту записи реестра |
len(parts) > 1 при разборе строки реестра | строка без разделителя вовсе |
len(fixed) > 5 перед строкой-продолжением | ровно пять починенных записей |
n_ssot < MIN_ROWS, пол | ровно пол |
Ничего экзотического. Каждый — вход, который читатель назовёт тривиальным, и ровно поэтому ни одного из них не существовало.
Автор контроля сажает тот дефект, который проверяет, — а это по построению невырожденный пример: файл с неверным содержимым, а не файл без содержимого.
Чтобы проверить, что гейт ловит потерю данных, вы пишете файл, потерявший данные. Чтобы проверить, что он ловит опустошение, вы опустошаете файл. И то и другое — примеры ЧЕГО-ТО. Пустой случай, равный случай и случай на волосок от края — примеры ничего: они не иллюстрируют никакого дефекта, поэтому ни один автор к ним не тянется, поэтому ни один контроль их не содержит, поэтому каждое сравнение в гейте вольно стоять на одно место в сторону и проходить.
Это и есть весь урожай граничного оператора, и это не пробел в чьём-то усердии. Это следствие того, как пишутся контроли.
Третья строка заслуживает отдельного абзаца — она с зубами.
total == 0 and old[0] > 0 сообщает о файле, который ОПУСТОШЁН. Опустошение есть переход: был непустым, стал пустым. Замените охранник на old[0] >= 0 — и файл, уже бывший пустым к моменту записи реестра, никогда этого перехода не совершавший, объявляется свежеопустошённым. На каждом прогоне. Вечно.
Значит, не пропущенный отказ. Постоянная ложная тревога — тот самый режим отказа, из-за которого гейт выключают, а после этого он пропускает всё. Каждый случай в том контроле сажал свой дефект ПОСЛЕ записи реестра, так что базовое значение никогда не было нулём и к границе снизу никто не подходил. Закрыть это потребовало фикстуры, где дефект присутствует в момент ЗАПИСИ, — другой вопрос: о том, когда, а не о том, что.
Счёт файлов-без-данных встречается в одном гейте дважды: в заголовке, который пишет регистратор реестра, и снова в строке переписи, которую печатает проверочный путь. Одно выражение, обе стороны реестра. Единственная фикстура с файлом в ноль случаев на момент записи прижимает обе, потому что это один вопрос, заданный дважды.
Стоит искать. Когда граница выживает в двух местах, проверьте, два ли это дефекта или одно выражение с двумя читателями.
Двое из двенадцати охраняют строку-продолжение «… и ещё N». На границе они печатают «(+0 ещё)», что не вредит никому, и честная классификация — косметика.
Оба всё равно закрыты случаем. Эта кампания дважды поймала ограничение, которое было записано, звучало механически и оказалось выдуманным, — обоснование, которого никто не измерял и которое читалось как продуманное, а потому останавливало проверку. Против такого послужного списка объявленное исключение стоит каждому будущему читателю дороже, чем случай стоит написать однажды.
Остаётся math.isinf(dec) and (inp > 0) == (dec > 0), дважды. Ветка стоит под elif math.isinf(inp): строкой выше, так что оба значения бесконечны по построению, а для значения из {+inf, −inf} обе формы совпадают: единственный вход, который их разделяет, — ноль, и ни одно из них нулём быть не может.
Стоит сказать, что сперва это было классифицировано как КАНДИДАТ в теоремы, опирающийся на непроверенное свойство кодека. Оговорку я написал, прочитав сравнение и не прочитав охранник над ним. Доказательство длиной в две строки лежало в том же файле всё это время.
Теперь строка несёт маркер, называющий себя известной эквивалентностью с причиной. Маркер ПЕЧАТАЕТСЯ рядом с выжившей строкой и ни на что не влияет: строка по-прежнему читается SURVIVED и по-прежнему считается. Гасить строку по силе комментария — это ровно то, как объявленное исключение стоит неделю, будучи ложным.
Они не говорят, что гейты верны. Не говорят, что они проверяют нужные свойства. Они говорят, что ни один мутант четырёх узких семейств не переживает свой контроль, а семейства узки по построению: пятый оператор — пятый вопрос.
Априорная оценка после этой недели: новый вопрос что-нибудь находит. Три из четырёх операторов нашли дефект в самом задающем вопрос инструменте, а не в коде, о котором спрашивали. Это самое неуютное число в этом посте и самое полезное.
Каждая цифра выше измерена, и рядом с ней названы её пределы.