String mutations: the two case flips are equivalent wherever the consumer is case-insensitive, and no option turns them off
Los mantenedores suelen responder en 3 días
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 55/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- python, sqlite
- Área
- cli, testing-qa, tooling
Línea de trabajo
Comienza con el punto de entrada operator_string y el comportamiento de configuración existente de --disable-mutation-types y do_not_mutate_patterns. Reproduce los mutantes de minúsculas y mayúsculas que sobreviven con example.py, test_example.py y pyproject.toml, y después añade cobertura de regresión que demuestre que los cambios de mayúsculas y minúsculas pueden deshabilitarse sin eliminar los mutantes XX ni los de indexación.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Disclaimer: AI-drafted, verified by me before filing. I answer follow-ups.
operator_string emits three mutants for every string literal: the literal wrapped in XX, lowercased, and uppercased. Where the literal's consumer is case-insensitive -- SQL, codec names, HTTP header names -- the case flips are equivalent mutants by construction.
Expected: a mutation I cannot kill can be switched off, as --disable-mutation-types did in 2.x (#175, added by #225).
Actual: both case flips survive every run, and the only lever in 3.x, do_not_mutate_patterns, matches whole lines, so it drops the XX mutant on that line too.
They were 25% and 11% of all survivors on two of my codebases: re-read on every run, and nothing to be done about them.
Reproduce -- three files, mutmut 3.7.0, pytest 9.1.1, CPython 3.12.12
# example.py
import sqlite3
def note_count(db_path: str) -> int:
con = sqlite3.connect(db_path)
return con.execute("SELECT COUNT(*) FROM notes").fetchone()[0]
# test_example.py
import sqlite3
from example import note_count
def test_note_count(tmp_path):
db = tmp_path / "n.db"
con = sqlite3.connect(db)
con.execute("CREATE TABLE notes (id int)")
con.execute("INSERT INTO notes VALUES (1)")
con.commit()
con.close()
assert note_count(str(db)) == 1
# pyproject.toml
[project]
name = "repro"
version = "0"
requires-python = ">=3.11"
[tool.mutmut]
source_paths = ["example.py"]
$ mutmut run
7/7 🎉 5 🫥 0 ⏰ 0 🤔 0 🙁 2 🔇 0 🧙 0
$ mutmut results
example.x_note_count__mutmut_5: survived
example.x_note_count__mutmut_6: survived
$ mutmut show example.x_note_count__mutmut_5
# example.x_note_count__mutmut_5: survived
--- example.py
+++ example.py
@@ -1,3 +1,3 @@
def note_count(db_path: str) -> int:
con = sqlite3.connect(db_path)
- return con.execute("SELECT COUNT(*) FROM notes").fetchone()[0]
+ return con.execute("select count(*) from notes").fetchone()[0]
$ mutmut show example.x_note_count__mutmut_6 # the same line, uppercased
+ return con.execute("SELECT COUNT(*) FROM NOTES").fetchone()[0]
SQLite parses either casing and resolves table names case-insensitively, so both mutants are equivalent to the original. The only test that would kill them asserts the query string's exact casing, which tests the literal rather than the behaviour.
What the line pattern costs -- 7 mutants become 2
[tool.mutmut]
source_paths = ["example.py"]
do_not_mutate_patterns = ["SELECT"]
$ mutmut run
2/2 🎉 2 🫥 0 ⏰ 0 🤔 0 🙁 0 🔇 0 🧙 0
Five of the seven mutants are gone. Two of them, mutant 4 ("XXSELECT COUNT(*) FROM notesXX") and mutant 7 (.fetchone()[1]), are killed by the test as it stands, so the pattern removes working signal in order to remove two mutants that carry none.
- Lenguaje dominante
- Python
- Estrellas
- 1.5k
- Forks
- 182
- Merge medio
- 2 d 14 h
- PR fusionados (30 d)
- 3
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de boxed/mutmut
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 83/100
Los mantenedores suelen responder en 3 días
-
Removing `not` from a parenthesized multi-line expression drops the parentheses (InvalidGeneratedSyntaxException, 3.8.0)Posiblemente ocupada @jihtsan la tomó hace 1 día. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 3 días
-
`run` with a `name__mutmut_*` pattern runs the whole test suite in the clean-test checkPosiblemente ocupada @Ishaan400 la tomó hace 12 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
boxed/mutmut#592 · 1 comentario ·
Los mantenedores suelen responder en 3 días
-
Invalid unary plus generated in match statementPosiblemente ocupada @GhostCoder6969 la tomó hace 21 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 3 días
-
What do the emoji's mean?Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
boxed/mutmut#560 · 1 comentario ·
Los mantenedores suelen responder en 3 días
Todos los issues de boxed/mutmut
Issues similares
-
documentation good first issue
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
selimfirat/pysad#379 ·
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
open-webui/open-webui#32805 ·
Los mantenedores suelen responder en 1 día
-
fix: [bug description] HTML CVE metric table shows only the last CVE for each productPosiblemente ocupada @Chris-wh233 la tomó hoy. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
ossf/cve-bin-tool#5976 ·
Los mantenedores suelen responder en 1 día
-
needs-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
gke-labs/kube-agents#3615 ·
Los mantenedores suelen responder en 1 día
-
deferred
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
Los mantenedores suelen responder en 1 día