Skip to content

Commit

Permalink
Catch2: support v3 (#115)
Browse files Browse the repository at this point in the history
Closes #107
  • Loading branch information
jnewb1 committed Sep 12, 2023
1 parent 9f677bd commit bd997b4
Show file tree
Hide file tree
Showing 8 changed files with 24,035 additions and 32 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# UNRELEASED

- Catch2: add support for catch version 3 ([#115](https://github.com/pytest-dev/pytest-cpp/pull/115))

# 2.4.0 (2023-09-08)

- Catch2: fix issue with multiple test failures, and support multiple "SECTION" tests. ([#112](https://github.com/pytest-dev/pytest-cpp/pull/112))
Expand Down
57 changes: 48 additions & 9 deletions src/pytest_cpp/catch2.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

import enum
import os
import subprocess
import tempfile
from typing import Optional
from typing import Sequence
from xml.etree import ElementTree

Expand All @@ -14,17 +16,22 @@
from pytest_cpp.helpers import make_cmdline


class Catch2Version(enum.Enum):
V2 = "v2"
V3 = "v3"


class Catch2Facade(AbstractFacade):
"""
Facade for Catch2.
"""

@classmethod
def is_test_suite(
def get_catch_version(
cls,
executable: str,
harness_collect: Sequence[str] = (),
) -> bool:
) -> Optional[Catch2Version]:
args = make_cmdline(harness_collect, executable, ["--help"])
try:
output = subprocess.check_output(
Expand All @@ -33,25 +40,47 @@ def is_test_suite(
universal_newlines=True,
)
except (subprocess.CalledProcessError, OSError):
return False
return None
else:
return "--list-test-names-only" in output
return (
Catch2Version.V2
if "--list-test-names-only" in output
else Catch2Version.V3
if "--list-tests" in output
else None
)

@classmethod
def is_test_suite(
cls,
executable: str,
harness_collect: Sequence[str] = (),
) -> bool:
return cls.get_catch_version(executable, harness_collect) in [
Catch2Version.V2,
Catch2Version.V3,
]

def list_tests(
self,
executable: str,
harness_collect: Sequence[str] = (),
) -> list[str]:
"""
Executes test with "--list-test-names-only" and gets list of tests
Executes test with "--list-test-names-only" (v2) or "--list-tests --verbosity quiet" (v3) and gets list of tests
parsing output like this:
1: All test cases reside in other .cpp files (empty)
2: Factorial of 0 is 1 (fail)
2: Factorials of 1 and higher are computed (pass)
"""
# This will return an exit code with the number of tests available
args = make_cmdline(harness_collect, executable, ["--list-test-names-only"])
exec_args = (
["--list-test-names-only"]
if self.get_catch_version(executable, harness_collect) == Catch2Version.V2
else ["--list-tests", "--verbosity quiet"]
)
args = make_cmdline(harness_collect, executable, exec_args)
try:
output = subprocess.check_output(
args,
Expand All @@ -77,6 +106,11 @@ def run_test(
On Windows, ValueError is raised when path and start are on different drives.
In this case failing back to the absolute path.
"""
catch_version = self.get_catch_version(executable, harness)

if catch_version is None:
raise Exception("Invalid Catch Version")

try:
xml_filename = os.path.join(os.path.relpath(temp_dir), "cpp-report.xml")
except ValueError:
Expand All @@ -97,7 +131,7 @@ def run_test(
except subprocess.CalledProcessError as e:
output = e.output

results = self._parse_xml(xml_filename)
results = self._parse_xml(xml_filename, catch_version)

for executed_test_id, failures, skipped in results:
if executed_test_id == test_id:
Expand All @@ -123,11 +157,16 @@ def run_test(
return [failure], output

def _parse_xml(
self, xml_filename: str
self, xml_filename: str, catch_version: Catch2Version
) -> Sequence[tuple[str, Sequence[tuple[str, int, str]], bool]]:
root = ElementTree.parse(xml_filename)
result = []
for test_suite in root.findall("Group"):
test_suites = (
root.findall("Group")
if catch_version == Catch2Version.V2
else root.iter("Catch2TestRun")
)
for test_suite in test_suites:
for test_case in test_suite.findall("TestCase"):
test_name = test_case.attrib["name"]
test_result = test_case.find("OverallResult")
Expand Down
5 changes: 4 additions & 1 deletion tests/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
!.gitignore
!*.h
!*.cpp
!*.hpp
!*.cc
!*.py
!*.xml
!README*
!SCons*
!catch.hpp

!catch2_v2
!catch2_v3
16 changes: 12 additions & 4 deletions tests/SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ if 'CXX' in os.environ:

env = Environment(**kwargs)
genv = env.Clone(LIBS=['gtest'] + LIBS)
c2env = env.Clone(CPPPATH=['.'])
c2env = env.Clone(CPPPATH=['.', 'catch2_v2'])

Export('env genv c2env')
catch2_v3 = env.Library('catch2_v3', ['catch2_v3/catch.cpp'])

c3env = env.Clone(CPPPATH=['.', 'catch2_v3'], LIBS=[catch2_v3])

Export('env genv c2env c3env')

genv.Program('gtest.cpp')
genv.Program('gtest_args.cpp')
Expand All @@ -39,8 +43,12 @@ boost_files = [
for filename in boost_files:
env.Program(filename)

c2env.Program('catch2_success.cpp')
c2env.Program('catch2_failure.cpp')
for env, label in [(c3env, "_v3"), (c2env, "")]:
catch2_success = env.Object(f'catch2_success{label}', 'catch2_success.cpp')
catch2_failure = env.Object(f'catch2_failure{label}', 'catch2_failure.cpp')

env.Program(catch2_success)
env.Program(catch2_failure)

SConscript('acceptance/googletest-samples/SConscript')
SConscript('acceptance/boosttest-samples/SConscript')
Expand Down
File renamed without changes.
Loading

0 comments on commit bd997b4

Please sign in to comment.