Skip to content

Commit 89bc385

Browse files
committed
Fix string \N{...} and \U{...} and remove capitalize mutation
1 parent f63029b commit 89bc385

File tree

5 files changed

+27
-12
lines changed

5 files changed

+27
-12
lines changed

e2e_projects/my_lib/src/my_lib/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,9 @@ def from_coords(coords) -> 'Point':
5757

5858
@property
5959
def coords(self):
60-
return self.x, self.y
60+
return self.x, self.y
61+
62+
63+
def escape_sequences():
64+
return "foo" \
65+
"FOO\\\'\"\a\b\f\n\r\t\v\111\x10\N{ghost}\u1234\U0001F51F"

e2e_projects/my_lib/tests/test_my_lib.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from my_lib import hello, Point, badly_tested, make_greeter, fibonacci, cached_fibonacci
1+
from my_lib import hello, Point, badly_tested, make_greeter, fibonacci, cached_fibonacci, escape_sequences
22

33
"""These tests are flawed on purpose, some mutants survive and some are killed."""
44

@@ -31,3 +31,6 @@ def test_point_from_coords():
3131
def test_fibonacci():
3232
assert fibonacci(1) == 1
3333
assert cached_fibonacci(1) == 1
34+
35+
def test_escape_sequences():
36+
assert escape_sequences().lower() == "foofoo\\\'\"\a\b\f\n\r\t\v\111\x10\N{ghost}\u1234\U0001F51F".lower()

mutmut/node_mutation.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
]
1313
]
1414

15-
CHARACTER_AFTER_BACKSLASH = re.compile(r"\\([A-Z])")
15+
# pattern to match (nearly) all chars in a string that are not part of an escape sequence
16+
NON_ESCAPE_SEQUENCE = re.compile(r"((?<!\\)[^\\]+)")
1617

1718
def operator_number(
1819
node: cst.BaseNumber
@@ -42,9 +43,9 @@ def operator_string(
4243

4344
supported_str_mutations: list[Callable[[str], str]] = [
4445
lambda x: "XX" + x + "XX",
45-
lambda x: x.lower(),
46-
lambda x: CHARACTER_AFTER_BACKSLASH.sub(lambda match: f"\\{match.group(1).lower()}", x.upper()),
47-
lambda x: x.capitalize(),
46+
# do not modify escape sequences, as this could break python syntax
47+
lambda x: NON_ESCAPE_SEQUENCE.sub(lambda match: match.group(1).lower(), x),
48+
lambda x: NON_ESCAPE_SEQUENCE.sub(lambda match: match.group(1).upper(), x),
4849
]
4950

5051
for mut_func in supported_str_mutations:

tests/e2e/snapshots/my_lib.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
"my_lib.x\u01c1Point\u01c1to_origin__mutmut_1": 1,
4141
"my_lib.x\u01c1Point\u01c1to_origin__mutmut_2": 1,
4242
"my_lib.x\u01c1Point\u01c1to_origin__mutmut_3": 0,
43-
"my_lib.x\u01c1Point\u01c1to_origin__mutmut_4": 0
43+
"my_lib.x\u01c1Point\u01c1to_origin__mutmut_4": 0,
44+
"my_lib.x_escape_sequences__mutmut_1": 1,
45+
"my_lib.x_escape_sequences__mutmut_2": 0,
46+
"my_lib.x_escape_sequences__mutmut_3": 1,
47+
"my_lib.x_escape_sequences__mutmut_4": 0,
48+
"my_lib.x_escape_sequences__mutmut_5": 0
4449
}
4550
}

tests/test_mutation.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,9 @@ def mutated_module(source: str) -> str:
125125
('x: list[A | None]', []),
126126
('a: Optional[int] = None', 'a: Optional[int] = ""'),
127127
('a: int = 1', ['a: int = 2', 'a: int = None']),
128-
('a: str = "FoO"', ['a: str = "XXFoOXX"', 'a: str = "foo"', 'a: str = "FOO"', 'a: str = "Foo"', 'a: str = None']),
129-
('a: str = "Fo\\t"', ['a: str = "XXFo\\tXX"', 'a: str = "fo\\t"', 'a: str = "FO\\t"', 'a: str = None']),
128+
('a: str = "FoO"', ['a: str = "XXFoOXX"', 'a: str = "foo"', 'a: str = "FOO"', 'a: str = None']),
129+
(r'a: str = "Fo\t"', [r'a: str = "XXFo\tXX"', r'a: str = "FO\t"', r'a: str = "fo\t"', 'a: str = None']),
130+
(r'a: str = "Fo\N{ghost} \U11223344"', [r'a: str = "XXFo\N{ghost} \U11223344XX"', r'a: str = "FO\N{GHOST} \U11223344"', r'a: str = "fo\N{ghost} \U11223344"', 'a: str = None']),
130131
('lambda: 0', ['lambda: 1', 'lambda: None']),
131132
("1 in (1, 2)", ['2 in (1, 2)', '1 not in (1, 2)', '1 in (2, 2)', '1 in (1, 3)']),
132133
('1+1', ['2+1', '1 - 1', '1+2']),
@@ -149,9 +150,9 @@ def mutated_module(source: str) -> str:
149150
('1e-3', '1.001'),
150151
('True', 'False'),
151152
('False', 'True'),
152-
('"FoO"', ['"XXFoOXX"', '"foo"', '"FOO"', '"Foo"']),
153-
("'FoO'", ["'XXFoOXX'", "'foo'", "'FOO'", "'Foo'"]),
154-
("u'FoO'", ["u'XXFoOXX'", "u'foo'", "u'FOO'", "u'Foo'"]),
153+
('"FoO"', ['"XXFoOXX"', '"foo"', '"FOO"']),
154+
("'FoO'", ["'XXFoOXX'", "'foo'", "'FOO'"]),
155+
("u'FoO'", ["u'XXFoOXX'", "u'foo'", "u'FOO'"]),
155156
("10", "11"),
156157
("10.", "11.0"),
157158
("0o10", "9"),

0 commit comments

Comments
 (0)