Skip to content

Commit bcbc184

Browse files
authored
Escape special characters in Catch2 test names (#124)
Fixes #123
1 parent c046f19 commit bcbc184

File tree

5 files changed

+69
-10
lines changed

5 files changed

+69
-10
lines changed

CHANGELOG.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# UNRELEASED
22

3-
- Catch2: add support for catch version 3 ([#115](https://github.com/pytest-dev/pytest-cpp/pull/115))
4-
- Catch2: support exception handling ([#117](https://github.com/pytest-dev/pytest-cpp/pull/117))
3+
- Catch2: add support for catch version 3 ([#115](https://github.com/pytest-dev/pytest-cpp/pull/115)).
4+
- Catch2: support exception handling ([#117](https://github.com/pytest-dev/pytest-cpp/pull/117)).
5+
- Catch2: Correctly handle test names that contain special characters ([#123](https://github.com/pytest-dev/pytest-cpp/issues/123)).
56
- Added support for Python 3.12.
67

78
# 2.4.0 (2023-09-08)

src/pytest_cpp/catch2.py

+17-8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@
1616
from pytest_cpp.helpers import make_cmdline
1717

1818

19+
# Map each special character's Unicode ordinal to the escaped character.
20+
_special_chars_map: dict[int, str] = {i: "\\" + chr(i) for i in b'[]*,~\\"'}
21+
22+
23+
def escape(test_id: str) -> str:
24+
"""Escape special characters in test names (see #123)."""
25+
return test_id.translate(_special_chars_map)
26+
27+
1928
class Catch2Version(enum.Enum):
2029
V2 = "v2"
2130
V3 = "v3"
@@ -115,14 +124,14 @@ def run_test(
115124
xml_filename = os.path.join(os.path.relpath(temp_dir), "cpp-report.xml")
116125
except ValueError:
117126
xml_filename = os.path.join(temp_dir, "cpp-report.xml")
118-
args = list(
119-
make_cmdline(
120-
harness,
121-
executable,
122-
[test_id, "--success", "--reporter=xml", f"--out {xml_filename}"],
123-
)
124-
)
125-
args.extend(test_args)
127+
exec_args = [
128+
escape(test_id),
129+
"--success",
130+
"--reporter=xml",
131+
f"--out={xml_filename}",
132+
]
133+
exec_args.extend(test_args)
134+
args = make_cmdline(harness, executable, exec_args)
126135

127136
try:
128137
output = subprocess.check_output(

tests/SConstruct

+5
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,15 @@ for env, label in [(c3env, "_v3"), (c2env, "")]:
4747
catch2_success = env.Object(f'catch2_success{label}', 'catch2_success.cpp')
4848
catch2_failure = env.Object(f'catch2_failure{label}', 'catch2_failure.cpp')
4949
catch2_error = env.Object(f'catch2_error{label}', 'catch2_error.cpp')
50+
catch2_special_chars = env.Object(
51+
f'catch2_special_chars{label}',
52+
'catch2_special_chars.cpp'
53+
)
5054

5155
env.Program(catch2_success)
5256
env.Program(catch2_failure)
5357
env.Program(catch2_error)
58+
env.Program(catch2_special_chars)
5459

5560
SConscript('acceptance/googletest-samples/SConscript')
5661
SConscript('acceptance/boosttest-samples/SConscript')

tests/catch2_special_chars.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
2+
#include "catch.hpp"
3+
4+
TEST_CASE( "Brackets in [test] name" ) {
5+
REQUIRE( true );
6+
}
7+
8+
TEST_CASE( "**Star in test name**" ) {
9+
REQUIRE( true );
10+
}
11+
12+
TEST_CASE( "~Tilde in test name" ) {
13+
REQUIRE( true );
14+
}
15+
16+
TEST_CASE( "Comma, in, test, name" ) {
17+
REQUIRE( true );
18+
}
19+
20+
TEST_CASE( R"(Backslash\ in\ test\ name)" ) {
21+
REQUIRE( true );
22+
}
23+
24+
TEST_CASE( "\"Quotes\" in test name" ) {
25+
REQUIRE( true );
26+
}

tests/test_pytest_cpp.py

+18
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,24 @@ def test_catch2_failure(exes):
635635
assert_catch2_failure(fail1.get_lines()[1], "a runtime error", colors)
636636

637637

638+
@pytest.mark.parametrize("suffix", ["", "_v3"])
639+
@pytest.mark.parametrize(
640+
"test_id",
641+
[
642+
"Brackets in [test] name",
643+
"**Star in test name**",
644+
"~Tilde in test name",
645+
"Comma, in, test, name",
646+
r"Backslash\ in\ test\ name",
647+
'"Quotes" in test name',
648+
],
649+
)
650+
def test_catch2_special_chars(suffix, test_id, exes):
651+
facade = Catch2Facade()
652+
exe = exes.get("catch2_special_chars" + suffix)
653+
assert facade.run_test(exe, test_id)[0] is None
654+
655+
638656
class TestError:
639657
def test_get_whitespace(self):
640658
assert error.get_left_whitespace(" foo") == " "

0 commit comments

Comments
 (0)