Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catch2: support v3 #115

Merged
merged 7 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading