From 9020e5e2494e97998aca254554ccd910cd01c33e Mon Sep 17 00:00:00 2001 From: Rylie Pavlik Date: Fri, 25 Oct 2024 16:39:58 -0500 Subject: [PATCH 1/2] Changes from OpenXR 1.1.42. GitOrigin-RevId: 38dd67e911b3578f6d1c2da497b8c3d3b605c354 --- .github/workflows/android-cts-build.yml | 2 +- .github/workflows/android-cts-pr.yml | 2 +- .github/workflows/msvc-build-preset.yml | 2 +- .reuse/dep5 | 2 +- changes/conformance/mr.2578.gl.md | 2 + changes/conformance/mr.3017.gl.md | 1 + changes/conformance/mr.3351.gl.md | 4 + changes/conformance/mr.3366.gl.md | 1 + changes/conformance/mr.3489.gl.md | 1 + changes/conformance/mr.3493.gl.md | 1 + changes/conformance/mr.3499.gl.md | 4 + changes/conformance/mr.3506.gl.md | 1 + changes/conformance/mr.3507.gl.1.md | 1 + changes/conformance/mr.3507.gl.2.md | 5 + changes/conformance/mr.3507.gl.md | 4 + changes/conformance/mr.3517.gl.md | 6 + changes/conformance/mr.3521.gl.md | 1 + changes/conformance/mr.3523.gl.md | 1 + changes/conformance/mr.3524.gl.md | 4 + changes/conformance/mr.3527.gl.md | 1 + changes/conformance/mr.3534.gl.md | 5 + changes/conformance/mr.3547.gl.md | 1 + changes/conformance/mr.3553.gl.md | 2 + changes/conformance/mr.3562.gl.md | 1 + changes/registry/mr.3513.gl.md | 5 - specification/Makefile | 8 +- specification/registry/xr.xml | 169 +- specification/scripts/docgenerator.py | 31 +- specification/scripts/update_version.py | 19 +- src/common/platform_utils.hpp | 2 + .../gradient_linearity.png | 3 + .../gradient_linearity.png.license | 3 + .../conformance_test/conformance_test.cpp | 77 +- .../test_GradientFormatsLinearVsNonLinear.cpp | 299 ++ .../conformance_test/test_SessionState.cpp | 275 +- .../test_ViewConfigurations.cpp | 56 +- .../test_XR_FB_space_warp.cpp | 45 +- .../conformance_test/test_actions.cpp | 164 +- .../conformance_test/test_xrLocateViews.cpp | 223 +- .../test_xrRequestExitSession.cpp | 36 - src/conformance/framework/CMakeLists.txt | 8 +- .../framework/conformance_framework.cpp | 81 +- .../framework/conformance_framework.h | 19 +- .../framework/conformance_utils.cpp | 36 +- src/conformance/framework/conformance_utils.h | 18 +- src/conformance/framework/cts_tinygltf.cpp | 5 - .../framework/graphics_plugin_metal.cpp | 295 +- .../framework/graphics_plugin_opengl.cpp | 134 +- .../framework/graphics_plugin_opengles.cpp | 195 +- .../framework/graphics_plugin_vulkan.cpp | 283 +- .../framework/input_testinputdevice.cpp | 36 +- .../framework/xml_test_environment.cpp | 4 +- src/conformance/usage/Makefile | 2 +- src/conformance/usage/asciidoctor-targets.mk | 2 +- src/conformance/usage/selftests.adoc | 61 +- .../usage/submissions_criteria.adoc | 11 +- .../usage/submissions_package.adoc | 11 +- .../usage/submissions_testing_steps.adoc | 52 +- src/conformance/utilities/CMakeLists.txt | 1 + src/conformance/utilities/bitmask_to_string.h | 3 +- src/conformance/utilities/d3d_common.cpp | 213 +- .../utilities/swapchain_format_data.cpp | 24 +- .../utilities/swapchain_format_data.h | 75 +- .../utilities/swapchain_parameters.cpp | 63 + .../utilities/swapchain_parameters.h | 99 +- src/external/stb/stb_image.h | 487 +-- src/external/stb/stb_truetype.h | 14 +- src/external/tinygltf/README.md | 6 +- src/external/tinygltf/tiny_gltf.h | 2684 ++++++++++------- src/scripts/conformance_generator.py | 7 +- src/scripts/interaction_profile_processor.py | 329 +- src/scripts/template_function_info.cpp | 2 +- .../template_interaction_info_generated.cpp | 23 +- .../template_interaction_info_generated.h | 2 +- 74 files changed, 4230 insertions(+), 2525 deletions(-) create mode 100644 changes/conformance/mr.2578.gl.md create mode 100644 changes/conformance/mr.3017.gl.md create mode 100644 changes/conformance/mr.3351.gl.md create mode 100644 changes/conformance/mr.3366.gl.md create mode 100644 changes/conformance/mr.3489.gl.md create mode 100644 changes/conformance/mr.3493.gl.md create mode 100644 changes/conformance/mr.3499.gl.md create mode 100644 changes/conformance/mr.3506.gl.md create mode 100644 changes/conformance/mr.3507.gl.1.md create mode 100644 changes/conformance/mr.3507.gl.2.md create mode 100644 changes/conformance/mr.3507.gl.md create mode 100644 changes/conformance/mr.3517.gl.md create mode 100644 changes/conformance/mr.3521.gl.md create mode 100644 changes/conformance/mr.3523.gl.md create mode 100644 changes/conformance/mr.3524.gl.md create mode 100644 changes/conformance/mr.3527.gl.md create mode 100644 changes/conformance/mr.3534.gl.md create mode 100644 changes/conformance/mr.3547.gl.md create mode 100644 changes/conformance/mr.3553.gl.md create mode 100644 changes/conformance/mr.3562.gl.md delete mode 100644 changes/registry/mr.3513.gl.md create mode 100644 src/conformance/conformance_test/composition_examples/gradient_linearity.png create mode 100644 src/conformance/conformance_test/composition_examples/gradient_linearity.png.license create mode 100644 src/conformance/conformance_test/test_GradientFormatsLinearVsNonLinear.cpp delete mode 100644 src/conformance/conformance_test/test_xrRequestExitSession.cpp create mode 100644 src/conformance/utilities/swapchain_parameters.cpp diff --git a/.github/workflows/android-cts-build.yml b/.github/workflows/android-cts-build.yml index 100154ea..43635ba7 100644 --- a/.github/workflows/android-cts-build.yml +++ b/.github/workflows/android-cts-build.yml @@ -26,7 +26,7 @@ jobs: fetch-depth: "${{ github.event.release && '0' || '1'}}" - name: Get modern CMake and Ninja - uses: lukka/get-cmake@v3.30.3 + uses: lukka/get-cmake@v3.30.5 - name: set up JDK 11 uses: actions/setup-java@v4 diff --git a/.github/workflows/android-cts-pr.yml b/.github/workflows/android-cts-pr.yml index acae4f12..c9bc83c7 100644 --- a/.github/workflows/android-cts-pr.yml +++ b/.github/workflows/android-cts-pr.yml @@ -19,7 +19,7 @@ jobs: lfs: true - name: Get modern CMake and Ninja - uses: lukka/get-cmake@v3.30.3 + uses: lukka/get-cmake@v3.30.5 - name: set up JDK 11 uses: actions/setup-java@v4 diff --git a/.github/workflows/msvc-build-preset.yml b/.github/workflows/msvc-build-preset.yml index 877b9cc5..5b94b950 100644 --- a/.github/workflows/msvc-build-preset.yml +++ b/.github/workflows/msvc-build-preset.yml @@ -36,7 +36,7 @@ jobs: fetch-depth: "${{ github.event.release && '0' || '1'}}" - name: Get modern CMake and Ninja - uses: lukka/get-cmake@v3.30.3 + uses: lukka/get-cmake@v3.30.5 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v2 diff --git a/.reuse/dep5 b/.reuse/dep5 index ac74aa84..ac059d99 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -50,7 +50,7 @@ Files: src/external/tinygltf/README.md src/external/tinygltf/tiny_gltf.h Copyright: 2015-Present, Syoyo Fujita, Aurélien Chatelain and many contributors License: MIT -Comment: Unmodified, vendored copy of a subset of the tiny-gltf repo v2.8.9 +Comment: Unmodified, vendored copy of a subset of the tiny-gltf repo v2.9.3 Files: src/external/d3dx12/* Copyright: Copyright (c) Microsoft Corporation. diff --git a/changes/conformance/mr.2578.gl.md b/changes/conformance/mr.2578.gl.md new file mode 100644 index 00000000..6b860722 --- /dev/null +++ b/changes/conformance/mr.2578.gl.md @@ -0,0 +1,2 @@ +- Improvement: Update `stb_image` from 2.27 to 2.30 (latest upstream) for fixes, security improvements, and optimizations. +- Improvement: Update `stb_typetype` to latest upstream for float comparison fixes. diff --git a/changes/conformance/mr.3017.gl.md b/changes/conformance/mr.3017.gl.md new file mode 100644 index 00000000..aa564c9e --- /dev/null +++ b/changes/conformance/mr.3017.gl.md @@ -0,0 +1 @@ +Improvement: Reorganize session state tests and use test sections, annotating assertions with spec links. diff --git a/changes/conformance/mr.3351.gl.md b/changes/conformance/mr.3351.gl.md new file mode 100644 index 00000000..fc9ec4a6 --- /dev/null +++ b/changes/conformance/mr.3351.gl.md @@ -0,0 +1,4 @@ +--- +- issue.2239.gl +--- +- New test: Show a perceptively-smooth gradient in two swapchain formats or two layer types (projection vs quad), to do basic rendering verification. This primarily catches issues where non-linear textures are being interpreted as linear or vice-versa in a runtime's compositor. diff --git a/changes/conformance/mr.3366.gl.md b/changes/conformance/mr.3366.gl.md new file mode 100644 index 00000000..6d6c98b4 --- /dev/null +++ b/changes/conformance/mr.3366.gl.md @@ -0,0 +1 @@ +Improvement: Update tinygltf to version 2.9.3. diff --git a/changes/conformance/mr.3489.gl.md b/changes/conformance/mr.3489.gl.md new file mode 100644 index 00000000..b414ea44 --- /dev/null +++ b/changes/conformance/mr.3489.gl.md @@ -0,0 +1 @@ +Improvement: List failed test cases, and unmatched test specs, at end of CTS run. diff --git a/changes/conformance/mr.3493.gl.md b/changes/conformance/mr.3493.gl.md new file mode 100644 index 00000000..b6afca7f --- /dev/null +++ b/changes/conformance/mr.3493.gl.md @@ -0,0 +1 @@ +- Fix: Update generated code to allow dash in interaction profile paths. diff --git a/changes/conformance/mr.3499.gl.md b/changes/conformance/mr.3499.gl.md new file mode 100644 index 00000000..af0bcd80 --- /dev/null +++ b/changes/conformance/mr.3499.gl.md @@ -0,0 +1,4 @@ +--- +- mr.3499.gl +--- +Improvement: Add warnings if supporting OpenXR 1.1 but not supporting optional, but recommended, extensions providing compatibility for OpenXR 1.0 apps/engines. diff --git a/changes/conformance/mr.3506.gl.md b/changes/conformance/mr.3506.gl.md new file mode 100644 index 00000000..ed72abca --- /dev/null +++ b/changes/conformance/mr.3506.gl.md @@ -0,0 +1 @@ +Fix: Allow interaction profiles which do not expose boolean paths to be tested. diff --git a/changes/conformance/mr.3507.gl.1.md b/changes/conformance/mr.3507.gl.1.md new file mode 100644 index 00000000..ad188b5d --- /dev/null +++ b/changes/conformance/mr.3507.gl.1.md @@ -0,0 +1 @@ +- Improvement: Make generated interaction profile sources reproducible/deterministic. diff --git a/changes/conformance/mr.3507.gl.2.md b/changes/conformance/mr.3507.gl.2.md new file mode 100644 index 00000000..96dcd82e --- /dev/null +++ b/changes/conformance/mr.3507.gl.2.md @@ -0,0 +1,5 @@ +--- +- mr.3507.gl.2 +- mr.3521.gl +--- +- Fix: Correctly parse and process interaction profile path and binding path dependencies from XML, and clean up code in `interaction_profile_processor.py`. diff --git a/changes/conformance/mr.3507.gl.md b/changes/conformance/mr.3507.gl.md new file mode 100644 index 00000000..9369faca --- /dev/null +++ b/changes/conformance/mr.3507.gl.md @@ -0,0 +1,4 @@ +--- +- issue.2350.gl +--- +- Fix: Include interaction profile dependencies in generated binding path dependencies. diff --git a/changes/conformance/mr.3517.gl.md b/changes/conformance/mr.3517.gl.md new file mode 100644 index 00000000..618ebadd --- /dev/null +++ b/changes/conformance/mr.3517.gl.md @@ -0,0 +1,6 @@ +--- +- mr.3517.gl +- mr.3541.gl +- mr.3544.gl +--- +- Fix: Add missing XML description of `EXT_hand_interaction` additions to several interaction profiles, affecting generated interaction profile tests. diff --git a/changes/conformance/mr.3521.gl.md b/changes/conformance/mr.3521.gl.md new file mode 100644 index 00000000..f77c5879 --- /dev/null +++ b/changes/conformance/mr.3521.gl.md @@ -0,0 +1 @@ +- Improvement: Correct the build-time dependencies of the generated interaction profile data. diff --git a/changes/conformance/mr.3523.gl.md b/changes/conformance/mr.3523.gl.md new file mode 100644 index 00000000..372f9359 --- /dev/null +++ b/changes/conformance/mr.3523.gl.md @@ -0,0 +1 @@ +New test: When using `XR_FB_space_warp`, verify runtime returns `XR_ERROR_VALIDATION_FAILURE` if `nearZ` is equal to `farZ` diff --git a/changes/conformance/mr.3524.gl.md b/changes/conformance/mr.3524.gl.md new file mode 100644 index 00000000..dda7f881 --- /dev/null +++ b/changes/conformance/mr.3524.gl.md @@ -0,0 +1,4 @@ +--- +- mr.3506.gl +--- +Improvement: Note in the Usage document that `/interaction_profiles/ext/hand_interaction_ext` and `/interaction_profiles/ext/eye_gaze_interaction` are generic interaction profiles that should be tested if supported. diff --git a/changes/conformance/mr.3527.gl.md b/changes/conformance/mr.3527.gl.md new file mode 100644 index 00000000..098d2da2 --- /dev/null +++ b/changes/conformance/mr.3527.gl.md @@ -0,0 +1 @@ +Improvement: added support for `PRIMARY_STEREO_WITH_FOVEATED_INSET` in `xrLocateViews` tests. diff --git a/changes/conformance/mr.3534.gl.md b/changes/conformance/mr.3534.gl.md new file mode 100644 index 00000000..b6484234 --- /dev/null +++ b/changes/conformance/mr.3534.gl.md @@ -0,0 +1,5 @@ +--- +- mr.3534.gl +- mr.3559.gl +--- +Improvement: Add additional tests for `xrGetInputSourceLocalizedName` diff --git a/changes/conformance/mr.3547.gl.md b/changes/conformance/mr.3547.gl.md new file mode 100644 index 00000000..940c0ca2 --- /dev/null +++ b/changes/conformance/mr.3547.gl.md @@ -0,0 +1 @@ +- Fix: remove unused `functionPtr` field in global `FunctionInfoMap` as it is invalid if called with a different instance diff --git a/changes/conformance/mr.3553.gl.md b/changes/conformance/mr.3553.gl.md new file mode 100644 index 00000000..c6c3c4fc --- /dev/null +++ b/changes/conformance/mr.3553.gl.md @@ -0,0 +1,2 @@ +- Improvement: Add command lines for Android for the self tests in the Usage document. +- Fix: Correct an outdated description of the self tests in the Usage document, glTF files now load asynchronously in those tests. diff --git a/changes/conformance/mr.3562.gl.md b/changes/conformance/mr.3562.gl.md new file mode 100644 index 00000000..73492d2b --- /dev/null +++ b/changes/conformance/mr.3562.gl.md @@ -0,0 +1 @@ +- Fix: Internal cross-references in the "Submission Package" section of the Usage document. diff --git a/changes/registry/mr.3513.gl.md b/changes/registry/mr.3513.gl.md deleted file mode 100644 index dab7f51f..00000000 --- a/changes/registry/mr.3513.gl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -- issue.2350.gl -- issue.2375.gl ---- -Fix: Correct XML description of OpenXR 1.1 related additions to the promoted Meta Touch Plus, Touch Pro, and Touch (Rift CV1) controller interaction profiles. diff --git a/specification/Makefile b/specification/Makefile index f9e92a09..76ae74a7 100644 --- a/specification/Makefile +++ b/specification/Makefile @@ -39,7 +39,7 @@ endif VERSIONS := XR_VERSION_1_0 XR_VERSION_1_1 XR_LOADER_VERSION_1_0 VERSIONOPTIONS := $(foreach version,$(VERSIONS),-feature $(version)) -SPECREVISION = 1.1.41 +SPECREVISION = 1.1.42 REVISION_COMPONENTS = $(subst ., ,$(SPECREVISION)) MAJORMINORVER = $(word 1,$(REVISION_COMPONENTS)).$(word 2,$(REVISION_COMPONENTS)) @@ -334,7 +334,8 @@ html: $(HTMLSPEC) ASCIIDOCTOR_TARGETS += $(HTMLSPEC) # Target-specific variables and deps customizing the AsciiDoctor rule -$(HTMLSPEC): ATTRIBOPTS += -a sectanchors +# EXTRAATRIBS is for build-time customization +$(HTMLSPEC): ATTRIBOPTS += -a sectanchors $(EXTRAATTRIBS) $(HTMLSPEC): ADOCOPTS += $(ADOCHTMLOPTS) $(HTMLSPEC): $(COMMONDOCS) @@ -349,7 +350,8 @@ pdfA4: $(PDFA4SPEC) ASCIIDOCTOR_TARGETS += $(PDFSPEC) $(PDFA4SPEC) # Target-specific variables and deps customizing the AsciiDoctor rule -$(PDFSPEC) $(PDFA4SPEC): BACKEND_ARGS=--backend pdf --require asciidoctor-pdf -a compress --require ./scripts/pdf-index-customizer.rb +# EXTRAATRIBS is for build-time customization +$(PDFSPEC) $(PDFA4SPEC): BACKEND_ARGS=--backend pdf --require asciidoctor-pdf -a compress --require ./scripts/pdf-index-customizer.rb $(EXTRAATTRIBS) $(PDFSPEC): PAGESIZE=LETTER $(PDFA4SPEC): PAGESIZE=A4 $(PDFSPEC) $(PDFA4SPEC): $(COMMONDOCS) diff --git a/specification/registry/xr.xml b/specification/registry/xr.xml index d7a4b1c8..17ea6811 100644 --- a/specification/registry/xr.xml +++ b/specification/registry/xr.xml @@ -135,7 +135,7 @@ maintained in the default branch of the Khronos OpenXR GitHub project. updates them automatically by processing a line at a time. --> // OpenXR current version number. -#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 41) +#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 42) + + + + + + + + + + + + + + + + + + + + + + + - + @@ -8474,8 +8499,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - - @@ -8491,15 +8514,13 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - - - + @@ -8588,14 +8609,19 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - + + + + + + - + @@ -8623,9 +8649,8 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - - + @@ -8689,7 +8714,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8718,7 +8742,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8747,9 +8770,8 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - - + @@ -8781,7 +8803,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8810,7 +8831,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8835,7 +8855,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8860,7 +8879,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8915,7 +8933,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -8924,7 +8941,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - @@ -9763,6 +9779,11 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + @@ -10208,7 +10229,7 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - + @@ -10229,6 +10250,12 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + + @@ -10659,8 +10686,20 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - + + + + + + + + + + + + + @@ -11242,6 +11281,17 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + + + + + + + @@ -11349,6 +11399,11 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + @@ -11367,12 +11422,6 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( - - - - - - @@ -12367,6 +12416,17 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + + + + + + + @@ -12595,6 +12655,40 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -15873,6 +15967,11 @@ typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + + + + + diff --git a/specification/scripts/docgenerator.py b/specification/scripts/docgenerator.py index 72960e49..e31f5a6e 100644 --- a/specification/scripts/docgenerator.py +++ b/specification/scripts/docgenerator.py @@ -34,6 +34,7 @@ class _Enumerant: comment: str extname: Optional[str] = None deprecated: Optional[str] = None + alias: Optional[str] = None def orgLevelKey(name): @@ -447,7 +448,6 @@ def genStruct(self, typeinfo, typeName, alias): self.writeInclude('structs', typeName, body) - def _maybe_return_enumerant_object_for_table( self, elems, elem, missing_comments: List[str] ) -> Optional[_Enumerant]: @@ -466,15 +466,21 @@ def _maybe_return_enumerant_object_for_table( return comment = elem.get("comment") + alias = elem.get("alias") if comment is None: if name.endswith("_UNKNOWN") and num_val == 0: # This is a placeholder for 0-initialization to be clearly invalid. # Just skip this silently return - # Skip but record this in case it is an odd-one-out missing - # a comment. - missing_comments.append(name) - return + if alias is not None: + # oh it's an alias. That's fine. We can generate a comment. + comment = f"Alias for ename:{alias}" + + else: + # Skip but record this in case it is an odd-one-out missing + # a comment. + missing_comments.append(name) + return assert num_val is not None @@ -531,13 +537,18 @@ def genEnumTable(self, groupinfo, groupName): if maybe_data: values.append(maybe_data) - if values: - # If any had a comment, output it. + if values and any(v.alias is None for v in values): + # If any had a (non-alias) comment, output it. if missing_comments: - self.logMsg('warn', 'The following values for', groupName, - 'were omitted from the table due to missing comment attributes:', - ', '.join(missing_comments)) + # Warn if it looks like we have comments, but some were missed. + if len(missing_comments) < len(values): + self.logMsg('warn', 'The following value(s) for', groupName, + 'were omitted from the table due to missing comment attributes:', + ', '.join(missing_comments)) + else: + self.logMsg('warn', 'The enumeration ', groupName, + 'appears to be missing comments for most of its elements') group_type = groupinfo.elem.get('type') if groupName == self.result_type: diff --git a/specification/scripts/update_version.py b/specification/scripts/update_version.py index 1c4ba8c9..a6d4bbc9 100755 --- a/specification/scripts/update_version.py +++ b/specification/scripts/update_version.py @@ -17,12 +17,10 @@ def update_makefile(fn, spec_version): for line in fileinput.input(fn, inplace=True): - printed = False if 'SPECREVISION = ' in line: - printed = True print('SPECREVISION = %s.%s.%s' % spec_version) - if not printed: - print(f"{line}", end='') + else: + print(line, end='') if __name__ == "__main__": @@ -32,8 +30,6 @@ def update_makefile(fn, spec_version): config = configparser.ConfigParser() config.read_file(fp) versions = config['Version'] - major_version = versions['MAJOR'] - minor_version = versions['MINOR'] spec_version = (versions['MAJOR'], versions['MINOR'], versions['PATCH']) # Now update the version in the appropriate places in the @@ -41,13 +37,10 @@ def update_makefile(fn, spec_version): # print('Replacing version lines in the registry') for line in fileinput.input('registry/xr.xml', inplace=True): - printed = False - if 'XR_CURRENT_API_VERSION' in line: - if 'XR_MAKE_VERSION' in line: - printed = True - print('#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(%s, %s, %s)' % spec_version) - if not printed: - print(f"{line}", end='') + if 'XR_CURRENT_API_VERSION' in line and 'XR_MAKE_VERSION' in line: + print('#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(%s, %s, %s)' % spec_version) + else: + print(line, end='') # Now update the version in the appropriate places in the # specification make file (Makefile). diff --git a/src/common/platform_utils.hpp b/src/common/platform_utils.hpp index 19cd41cb..59b5bf2f 100644 --- a/src/common/platform_utils.hpp +++ b/src/common/platform_utils.hpp @@ -71,6 +71,8 @@ #define XR_ARCH_ABI "riscv64" #elif defined(__sparc__) && defined(__arch64__) #define XR_ARCH_ABI "sparc64" +#elif defined(__loongarch64) +#define XR_ARCH_ABI "loong64" #else #error "No architecture string known!" #endif diff --git a/src/conformance/conformance_test/composition_examples/gradient_linearity.png b/src/conformance/conformance_test/composition_examples/gradient_linearity.png new file mode 100644 index 00000000..32220f75 --- /dev/null +++ b/src/conformance/conformance_test/composition_examples/gradient_linearity.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fda1ad7752044e8a8087f415c9cb6df65b7a5dbdd77db80b2e97736508151caa +size 39900 diff --git a/src/conformance/conformance_test/composition_examples/gradient_linearity.png.license b/src/conformance/conformance_test/composition_examples/gradient_linearity.png.license new file mode 100644 index 00000000..c94a118e --- /dev/null +++ b/src/conformance/conformance_test/composition_examples/gradient_linearity.png.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/conformance_test.cpp b/src/conformance/conformance_test/conformance_test.cpp index efed4359..a3eec100 100644 --- a/src/conformance/conformance_test/conformance_test.cpp +++ b/src/conformance/conformance_test/conformance_test.cpp @@ -177,6 +177,65 @@ namespace } } + TEST_CASE("ValidateExpectedFeatures") + { + // Create and destroy an XrInstance with a particular apiVersion to validate that + // it is possible and supported. + auto validateOpenXRVersionSupported = [](XrVersion apiVersion) -> bool { + GlobalData& globalData = GetGlobalData(); + + XrInstanceCreateInfo createInfo{XR_TYPE_INSTANCE_CREATE_INFO}; + createInfo.applicationInfo.applicationVersion = 1; + strcpy(createInfo.applicationInfo.applicationName, "conformance test"); + createInfo.applicationInfo.apiVersion = apiVersion; + createInfo.enabledApiLayerCount = (uint32_t)globalData.enabledAPILayerNames.size(); + createInfo.enabledApiLayerNames = globalData.enabledAPILayerNames.data(); + + StringVec extensions(globalData.enabledInstanceExtensionNames); + createInfo.enabledExtensionCount = (uint32_t)extensions.size(); + createInfo.enabledExtensionNames = extensions.data(); + + if (globalData.requiredPlatformInstanceCreateStruct != nullptr) { + createInfo.next = globalData.requiredPlatformInstanceCreateStruct; + } + + XrInstance instance{XR_NULL_HANDLE}; + XrResult result = xrCreateInstance(&createInfo, &instance); + if (XR_FAILED(result)) { + return false; + } + + xrDestroyInstance(instance); + return true; + }; + + SECTION("OpenXR 1.1") + { + GlobalData& globalData = GetGlobalData(); + + FeatureSet available; + globalData.PopulateVersionAndAvailableExtensions(available); + + if (available.Get(FeatureBitIndex::BIT_XR_VERSION_1_1)) { + bool openxr1_1_supported = validateOpenXRVersionSupported(XR_API_VERSION_1_1); + bool openxr1_0_supported = validateOpenXRVersionSupported(XR_API_VERSION_1_0); + REQUIRE(openxr1_1_supported); + + if (!available.Get(FeatureBitIndex::BIT_XR_KHR_locate_spaces)) { + WARN( + "Runtime supports OpenXR 1.1 but does not support XR_KHR_locate_spaces; this is not strictly required but is surprising."); + } + if (!available.Get(FeatureBitIndex::BIT_XR_KHR_maintenance1)) { + WARN( + "Runtime supports OpenXR 1.1 but does not support XR_KHR_maintenance1; this is not strictly required but is surprising."); + } + if (!openxr1_0_supported) { + WARN("Runtime supports OpenXR 1.1 but does not support OpenXR 1.0; this is not strictly required but is surprising."); + } + } + } + } + // Ensure conformance is configured correctly. TEST_CASE("ValidateEnvironment") { @@ -339,6 +398,8 @@ namespace globalData.options.viewConfiguration = arg; if (striequal(globalData.options.viewConfiguration.c_str(), "stereo")) globalData.options.viewConfigurationValue = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + else if (striequal(globalData.options.viewConfiguration.c_str(), "stereoFoveated")) + globalData.options.viewConfigurationValue = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET; else if (striequal(globalData.options.viewConfiguration.c_str(), "mono")) globalData.options.viewConfigurationValue = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO; else { @@ -410,8 +471,8 @@ namespace ("Choose which hands to test: left, right, or both. Default is both.") .optional() - | Opt(parseViewConfig, "Stereo|Mono") // view configuration - ["-V"]["--viewConfiguration"] // + | Opt(parseViewConfig, "Stereo|StereoFoveated|Mono") // view configuration + ["-V"]["--viewConfiguration"] // ("Specify view configuration. Default is Stereo.") .optional() @@ -519,8 +580,8 @@ namespace Base::testCaseEnded(testCaseStats); Conformance::GlobalData& globalData = Conformance::GetGlobalData(); - globalData.conformanceReport.testSuccessCount += testCaseStats.totals.testCases.passed; - globalData.conformanceReport.testFailureCount += testCaseStats.totals.testCases.failed; + globalData.conformanceReport.results[testCaseStats.testInfo->name].testSuccessCount += testCaseStats.totals.testCases.passed; + globalData.conformanceReport.results[testCaseStats.testInfo->name].testFailureCount += testCaseStats.totals.testCases.failed; } void sectionStarting(Catch::SectionInfo const& sectionInfo) override @@ -547,10 +608,10 @@ namespace m_sectionIndent--; } - void noMatchingTestCases(Catch::StringRef /* unmatchedSpec */) override + void noMatchingTestCases(Catch::StringRef unmatchedSpec) override { Conformance::GlobalData& globalData = Conformance::GetGlobalData(); - globalData.conformanceReport.unmatchedTestSpecs = true; + globalData.conformanceReport.unmatchedTestSpecs.push_back(unmatchedSpec.data()); } void testRunEnded(Catch::TestRunStats const& testRunStats) override @@ -704,7 +765,7 @@ XrcResult XRAPI_CALL xrcRunConformanceTests(const ConformanceLaunchSettings* con int exitCode = CreateOrGetCatchSession().run(); Conformance::GlobalData& globalData = Conformance::GetGlobalData(); - *failureCount = globalData.conformanceReport.testFailureCount; + *failureCount = globalData.conformanceReport.TestFailureCount(); const auto& totals = globalData.conformanceReport.totals; conformanceTestsRun = true; @@ -712,7 +773,7 @@ XrcResult XRAPI_CALL xrcRunConformanceTests(const ConformanceLaunchSettings* con if (skipActuallyTesting) { *testResult = XRC_TEST_RESULT_SUCCESS; } - else if (globalData.conformanceReport.unmatchedTestSpecs && catchConfig.warnAboutUnmatchedTestSpecs()) { + else if (!globalData.conformanceReport.unmatchedTestSpecs.empty() && catchConfig.warnAboutUnmatchedTestSpecs()) { *testResult = XRC_TEST_RESULT_UNMATCHED_TEST_SPEC; } else if (totals.testCases.total() == 0 && !catchConfig.zeroTestsCountAsSuccess()) { diff --git a/src/conformance/conformance_test/test_GradientFormatsLinearVsNonLinear.cpp b/src/conformance/conformance_test/test_GradientFormatsLinearVsNonLinear.cpp new file mode 100644 index 00000000..b4dac66e --- /dev/null +++ b/src/conformance/conformance_test/test_GradientFormatsLinearVsNonLinear.cpp @@ -0,0 +1,299 @@ +// Copyright (c) 2019-2024, The Khronos Group Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "composition_utils.h" +#include "graphics_plugin.h" +#include "utilities/array_size.h" +#include "utilities/bitmask_to_string.h" +#include "utilities/swapchain_parameters.h" +#include "utilities/throw_helpers.h" +#include "utilities/types_and_constants.h" +#include "utilities/xr_math_operators.h" + +#include + +#include + +#include +#include + +namespace Conformance +{ + using namespace openxr::math_operators; + + static XrSwapchainCreateInfo MakeDefaultSwapchainCreateInfo(int64_t imageFormat, const SwapchainCreateTestParameters& tp) + { + XrSwapchainCreateInfo createInfo{XR_TYPE_SWAPCHAIN_CREATE_INFO}; + createInfo.faceCount = 1; + createInfo.format = imageFormat; + XrSwapchainCreateFlagsRefCPP(createInfo.createFlags) = tp.createFlagsVector[0]; + XrSwapchainUsageFlagsRefCPP(createInfo.usageFlags) = tp.usageFlagsVector[0]; + createInfo.sampleCount = 1; + createInfo.width = 64; + createInfo.height = 64; + createInfo.arraySize = tp.arrayCountVector[0]; + createInfo.mipCount = tp.mipCountVector[0]; + return createInfo; + } + + namespace + { + + constexpr int32_t labelImageWidth = 512; + constexpr int32_t labelImageHeight = 32; + constexpr int32_t labelFontSize = 18; // some format names are long! + constexpr float labelWidth = 2.0f; + constexpr float labelHeight = labelWidth * labelImageHeight / labelImageWidth; + constexpr float labelMargin = 0.2f; + inline XrCompositionLayerQuad* MakeFormatLabel(CompositionHelper& compositionHelper, XrSpace viewSpace, const std::string& label, + const XrVector3f& position) + { + + return compositionHelper.CreateQuadLayer( + compositionHelper.CreateStaticSwapchainImage( + CreateTextImage(labelImageWidth, labelImageHeight, label.c_str(), labelFontSize, WordWrap::Disabled)), + viewSpace, labelWidth, {Quat::Identity, position}); + } + + constexpr int32_t aspectRatio = 8; + constexpr int32_t gradientImageHeight = 32; + constexpr int32_t gradientImageWidth = gradientImageHeight * aspectRatio; + constexpr float gradientWidth = 1.0f; + constexpr float gradientHeight = gradientWidth / aspectRatio; + constexpr float quadZ = -3; // How far away quads are placed. + constexpr float margin = 0.02f; // the gap between quads + constexpr float yOffset = (gradientHeight + margin) / 2; + + inline MeshHandle MakeGradientMesh(IGraphicsPlugin& graphicsPlugin, SwapchainFormat::RawColorComponents referenceComponents) + { + // 0-2 + // |/| + // 1-3 + std::vector vertices; + vertices.reserve(2 * gradientImageWidth); + for (int col = 0; col < gradientImageWidth; col++) { + float value = col / (float)gradientImageWidth; + + // if any of the other format's color channels aren't present, + // they should be sampled as zero, so also zero them here to match + using Components = SwapchainFormat::RawColorComponents; + XrColor4f color{ + referenceComponents & Components::r ? value : 0.0f, + referenceComponents & Components::g ? value : 0.0f, + referenceComponents & Components::b ? value : 0.0f, + 1.0, + }; + + color = ColorUtils::FromSRGB(color); // perceptual gradient instead of linear + float x = -(gradientWidth / 2) + gradientWidth * value; + vertices.push_back(Geometry::Vertex{{x, gradientHeight / 2, 0}, {color.r, color.g, color.b}}); + vertices.push_back(Geometry::Vertex{{x, -gradientHeight / 2, 0}, {color.r, color.g, color.b}}); + } + const uint16_t quadIndices[] = {1, 0, 2, 2, 3, 1}; + std::vector indices; + indices.reserve((gradientImageWidth - 1) * ArraySize(quadIndices)); + for (int col = 0; col < gradientImageWidth - 1; col++) { + for (const uint16_t index : quadIndices) { + indices.push_back((uint16_t)(col * 2 + index)); + } + } + + return graphicsPlugin.MakeSimpleMesh(indices, vertices); + } + } // namespace + + TEST_CASE("GradientFormatsLinearVsNonLinear", "[composition][interactive]") + { + const GlobalData& globalData = GetGlobalData(); + if (!globalData.IsUsingGraphicsPlugin()) { + // Nothing to check - no graphics plugin means no swapchain + SKIP("Cannot test swapchains without a graphics plugin"); + } + + CompositionHelper compositionHelper("Linear vs Non-Linear"); + InteractiveLayerManager interactiveLayerManager(compositionHelper, "gradient_linearity.png", + "Both gradients should match both each other" + " and the example image, except for banding artifacts," + " and should appear perceptually linear." + " Banding may introduce small color artifacts, but" + " the gradients should be broadly the same color."); + + XrSession session = compositionHelper.GetSession(); + + const XrSpace viewSpace = compositionHelper.CreateReferenceSpace(XR_REFERENCE_SPACE_TYPE_VIEW, Pose::Identity); + + // Enumerate formats + std::vector imageFormatArray; + { + uint32_t countOutput = 0; + XRC_CHECK_THROW_XRCMD_UNQUALIFIED_SUCCESS(xrEnumerateSwapchainFormats(session, 0, &countOutput, nullptr)); + if (countOutput > 0) { + imageFormatArray.resize(countOutput, XRC_INVALID_IMAGE_FORMAT); + XRC_CHECK_THROW_XRCMD_UNQUALIFIED_SUCCESS( + xrEnumerateSwapchainFormats(session, countOutput, &countOutput, imageFormatArray.data())); + } + } + + int64_t defaultFormat = globalData.graphicsPlugin->GetSRGBA8Format(); + SwapchainCreateTestParameters defaultTestParameters; + REQUIRE(globalData.graphicsPlugin->GetSwapchainCreateTestParameters(defaultFormat, &defaultTestParameters)); + + compositionHelper.GetInteractionManager().AttachActionSets(); + compositionHelper.BeginSession(); + + auto doInteractiveRenderTest = [&](int64_t projFormat, const SwapchainCreateTestParameters& projTestParameters, // + int64_t quadFormat, const SwapchainCreateTestParameters& quadTestParameters) { + // Set up composition projection layer and swapchains (one swapchain per view). + std::vector swapchains; + XrCompositionLayerProjection* const projLayer = compositionHelper.CreateProjectionLayer(viewSpace); + { + const std::vector viewProperties = compositionHelper.EnumerateConfigurationViews(); + XrSwapchainCreateInfo createInfo = MakeDefaultSwapchainCreateInfo(projFormat, projTestParameters); + for (uint32_t j = 0; j < projLayer->viewCount; j++) { + // this may need to be rounded up for compressed formats (maybe compensated for with a subimage?) + createInfo.width = viewProperties[j].recommendedImageRectWidth; + createInfo.height = viewProperties[j].recommendedImageRectHeight; + const XrSwapchain swapchain = compositionHelper.CreateSwapchain(createInfo); + const_cast(projLayer->views[j].subImage) = compositionHelper.MakeDefaultSubImage(swapchain, 0); + swapchains.push_back(swapchain); + } + } + + // Set up swapchain for composition quad layer + XrSwapchain gradientQuadSwapchain; + { + XrSwapchainCreateInfo createInfo = MakeDefaultSwapchainCreateInfo(quadFormat, quadTestParameters); + // this may need to be rounded up for compressed formats (maybe compensated for with a subimage?) + createInfo.width = gradientImageWidth; + createInfo.height = gradientImageHeight; + gradientQuadSwapchain = compositionHelper.CreateSwapchain(createInfo); + } + + const float labelYOffset = gradientHeight + (labelHeight / 2) + labelMargin; + + XrCompositionLayerQuad* const projFormatLabelQuad = MakeFormatLabel( + compositionHelper, viewSpace, "Projection: " + projTestParameters.imageFormatName, {0, labelYOffset, quadZ}); + interactiveLayerManager.AddLayer(projFormatLabelQuad); + + XrCompositionLayerQuad* const quadFormatLabelQuad = + MakeFormatLabel(compositionHelper, viewSpace, "Quad: " + quadTestParameters.imageFormatName, {0, -labelYOffset, quadZ}); + interactiveLayerManager.AddLayer(quadFormatLabelQuad); + + XrCompositionLayerQuad* gradientQuad = compositionHelper.CreateQuadLayer(gradientQuadSwapchain, viewSpace, gradientWidth, + XrPosef{Quat::Identity, {0, -yOffset, quadZ}}); + interactiveLayerManager.AddLayer(gradientQuad); + + // Each mesh should only write non-zero to the color components that the **other** format supports, which is why the below looks backwards. + // We could do an intersection of the two format components, but e.g. writing all white to a format with fewer channels + // may expose errors that writing something more limited would not. + MeshHandle quadMesh = MakeGradientMesh(*globalData.graphicsPlugin, projTestParameters.colorComponents); + MeshHandle projMesh = MakeGradientMesh(*globalData.graphicsPlugin, quadTestParameters.colorComponents); + + auto updateLayers = [&](const XrFrameState& frameState) { + auto viewData = compositionHelper.LocateViews(viewSpace, frameState.predictedDisplayTime); + const auto& viewState = std::get(viewData); + + std::vector layers; + if (viewState.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT && + viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) { + const auto& views = std::get>(viewData); + + const uint32_t imageArrayIndex = 0; + // Render the gradient into the quad's swapchain + compositionHelper.AcquireWaitReleaseImage(gradientQuadSwapchain, [&](const XrSwapchainImageBaseHeader* swapchainImage) { + GetGlobalData().graphicsPlugin->ClearImageSlice(swapchainImage, imageArrayIndex, Colors::Black); + + XrCompositionLayerProjectionView view = {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}; + const float quadRenderDistance = 1.0; + const float halfAngleX = std::atan2(gradientWidth / 2, quadRenderDistance); + const float halfAngleY = std::atan2(gradientHeight / 2, quadRenderDistance); + view.fov = {-halfAngleX, halfAngleX, halfAngleY, -halfAngleY}; + view.pose = Pose::Identity; + view.subImage = compositionHelper.MakeDefaultSubImage(gradientQuadSwapchain, imageArrayIndex); + + auto quadMeshList = {MeshDrawable{quadMesh, {Quat::Identity, {0, 0, -quadRenderDistance}}}}; + GetGlobalData().graphicsPlugin->RenderView(view, swapchainImage, RenderParams().Draw(quadMeshList)); + }); + + auto projMeshList = {MeshDrawable{projMesh, {Quat::Identity, {0, yOffset, quadZ}}}}; + // Render into each of the separate swapchains using the projection layer view fov and pose. + for (size_t view = 0; view < views.size(); view++) { + compositionHelper.AcquireWaitReleaseImage(swapchains[view], [&](const XrSwapchainImageBaseHeader* swapchainImage) { + GetGlobalData().graphicsPlugin->ClearImageSlice(swapchainImage, imageArrayIndex, Colors::Black); + const_cast(projLayer->views[view].fov) = views[view].fov; + const_cast(projLayer->views[view].pose) = views[view].pose; + GetGlobalData().graphicsPlugin->RenderView(projLayer->views[view], swapchainImage, + RenderParams().Draw(projMeshList)); + }); + } + + layers.push_back({reinterpret_cast(projLayer)}); + } + return interactiveLayerManager.EndFrame(frameState, layers); + }; + + RenderLoop(session, updateLayers).Loop(); + }; + + for (int64_t imageFormat : imageFormatArray) { + + SwapchainCreateTestParameters tp; + REQUIRE(globalData.graphicsPlugin->GetSwapchainCreateTestParameters(imageFormat, &tp)); + + if (!tp.supportsRendering) { + // currently, we render to the format, + // but we could generate the image + // or render to another format and copy. + continue; + } + if (!tp.colorFormat) { + // we are testing by visual inspection + continue; + } + if (tp.colorIntegerRange != SwapchainFormat::ColorIntegerRange::NoIntegerColor) { + // unsure whether and how non-normalized + // integer formats map to the screen + continue; + } + + DYNAMIC_SECTION(tp.imageFormatName) + { + CAPTURE(tp.imageFormatName); + + SECTION("Custom projection layer format") + { + INFO("Formats: projection: " << tp.imageFormatName << ", quad: " << defaultTestParameters.imageFormatName + << " (default)"); + doInteractiveRenderTest(imageFormat, tp, defaultFormat, defaultTestParameters); + } + if (imageFormat == defaultFormat) { + // compare proj to quad, but no point doing it twice + continue; + } + SECTION("Custom quad layer format") + { + INFO("Formats: projection: " << defaultTestParameters.imageFormatName << " (default), quad: " << tp.imageFormatName); + doInteractiveRenderTest(defaultFormat, defaultTestParameters, imageFormat, tp); + } + } + + // SwapchainScoped will have xrDestroySwapchain + // now we need to flush + GetGlobalData().graphicsPlugin->ClearSwapchainCache(); + GetGlobalData().graphicsPlugin->Flush(); + } + } +} // namespace Conformance diff --git a/src/conformance/conformance_test/test_SessionState.cpp b/src/conformance/conformance_test/test_SessionState.cpp index 43a24ccc..da379cf4 100644 --- a/src/conformance/conformance_test/test_SessionState.cpp +++ b/src/conformance/conformance_test/test_SessionState.cpp @@ -16,8 +16,7 @@ #include "conformance_utils.h" #include "conformance_framework.h" -#include "matchers.h" -#include "utilities/utils.h" +#include "utilities/types_and_constants.h" #include "utilities/throw_helpers.h" #include @@ -26,168 +25,182 @@ #include #include -#include #include -#include -#include -#include #include -#include - -#define AS_LIST(name, val) name, -constexpr XrViewConfigurationType KnownViewTypes[] = {XR_LIST_ENUM_XrViewConfigurationType(AS_LIST)}; -#undef AS_LIST namespace Conformance { + static bool tryReadEvent(XrInstance instance, XrEventDataBuffer* evt) + { + *evt = {XR_TYPE_EVENT_DATA_BUFFER}; + XrResult res; + XRC_CHECK_THROW_XRCMD(res = xrPollEvent(instance, evt)); + return res == XR_SUCCESS; + } - TEST_CASE("SessionState", "") + static bool tryGetNextSessionState(XrInstance instance, XrEventDataSessionStateChanged* evt) { - AutoBasicSession session(AutoBasicSession::createSession); - REQUIRE_MSG(session != XR_NULL_HANDLE_CPP, - "If this (XrSession creation) fails, ensure the runtime is configured and the AR/VR device is present."); - - XrInstance instance = session.GetInstance(); - XrSystemId systemId = session.GetSystemId(); - - auto tryReadEvent = [&](XrEventDataBuffer* evt) { - *evt = {XR_TYPE_EVENT_DATA_BUFFER}; - XrResult res; - XRC_CHECK_THROW_XRCMD(res = xrPollEvent(session.GetInstance(), evt)); - return res == XR_SUCCESS; - }; - - auto tryGetNextSessionState = [&](XrEventDataSessionStateChanged* evt) { - XrEventDataBuffer buffer; - while (tryReadEvent(&buffer)) { - if (buffer.type == XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED) { - *evt = *reinterpret_cast(&buffer); - return true; - } + XrEventDataBuffer buffer; + while (tryReadEvent(instance, &buffer)) { + if (buffer.type == XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED) { + *evt = *reinterpret_cast(&buffer); + return true; } + } + return false; + } - return false; - }; + static bool waitForNextSessionState(XrInstance instance, XrEventDataSessionStateChanged* evt, std::chrono::nanoseconds duration = 1s) + { + CountdownTimer countdown(duration); + while (!countdown.IsTimeUp()) { + if (tryGetNextSessionState(instance, evt)) { + return true; + } - auto waitForNextSessionState = [&](XrEventDataSessionStateChanged* evt, std::chrono::nanoseconds duration = 1s) { - CountdownTimer countdown(duration); - while (!countdown.IsTimeUp()) { - if (tryGetNextSessionState(evt)) { - return true; - } + std::this_thread::sleep_for(5ms); + } - std::this_thread::sleep_for(5ms); - } + return false; + } - return false; - }; + static void submitFrame(XrSession session) + { + XrFrameState frameState{XR_TYPE_FRAME_STATE}; + XRC_CHECK_THROW_XRCMD(xrWaitFrame(session, nullptr, &frameState)); + XRC_CHECK_THROW_XRCMD(xrBeginFrame(session, nullptr)); + + XrFrameEndInfo frameEndInfo{XR_TYPE_FRAME_END_INFO}; + frameEndInfo.displayTime = frameState.predictedDisplayTime; + frameEndInfo.environmentBlendMode = GetGlobalData().GetOptions().environmentBlendModeValue; + XRC_CHECK_THROW_XRCMD(xrEndFrame(session, &frameEndInfo)); + } + + static void submitFramesUntilSessionState(XrInstance instance, XrSession session, XrSessionState expectedSessionState, + std::chrono::nanoseconds duration = 30s) + { + CAPTURE(expectedSessionState); + + CountdownTimer countdown(duration); + while (!countdown.IsTimeUp()) { + XrEventDataSessionStateChanged evt; + if (tryGetNextSessionState(instance, &evt)) { + REQUIRE(evt.state == expectedSessionState); + return; + } + submitFrame(session); + } - XrEventDataSessionStateChanged evt{}; - REQUIRE(waitForNextSessionState(&evt) == true); - REQUIRE_MSG(evt.state == XR_SESSION_STATE_IDLE, "Unexpected session state " << evt.state); + FAIL("Failed to reach expected session state"); + } - REQUIRE(waitForNextSessionState(&evt) == true); - REQUIRE_MSG(evt.state == XR_SESSION_STATE_READY, "Unexpected session state " << evt.state); + TEST_CASE("SessionState", "") + { + AutoBasicInstance instance; - // Ensure unsupported view configuration types fail. + SECTION("Cycle through all states") { - // Get the list of supported view configurations - uint32_t viewCount = 0; - REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, 0, &viewCount, nullptr)); - std::vector runtimeViewTypes(viewCount); - REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, viewCount, &viewCount, runtimeViewTypes.data())); + AutoBasicSession session(AutoBasicSession::createSession, instance); - for (XrViewConfigurationType viewType : KnownViewTypes) { - CAPTURE(viewType); + REQUIRE(session != XR_NULL_HANDLE_CPP); - // Is this enum valid, check against enabled extensions. - bool valid = IsViewConfigurationTypeEnumValid(viewType); + XrEventDataSessionStateChanged evt{}; + REQUIRE(waitForNextSessionState(instance, &evt) == true); + REQUIRE_MSG(evt.state == XR_SESSION_STATE_IDLE, "Unexpected session state " << evt.state); - if (!IsViewConfigurationTypeEnumValid(viewType)) { - INFO("Must not enumerate invalid view configuration type"); - CHECK_THAT(runtimeViewTypes, !Catch::Matchers::VectorContains(viewType)); - } + REQUIRE(waitForNextSessionState(instance, &evt) == true); + REQUIRE_MSG(evt.state == XR_SESSION_STATE_READY, "Unexpected session state " << evt.state); - // Skip this view config if it is supported, since we cannot test correct handling of unsupported values with it. - if (Catch::Matchers::VectorContains(viewType).match(runtimeViewTypes)) { - continue; - } + XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; + beginInfo.primaryViewConfigurationType = GetGlobalData().GetOptions().viewConfigurationValue; + REQUIRE(XR_SUCCESS == xrBeginSession(session, &beginInfo)); - XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; - beginInfo.primaryViewConfigurationType = viewType; - XrResult result = xrBeginSession(session, &beginInfo); - REQUIRE_THAT(result, In({XR_ERROR_VALIDATION_FAILURE, XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED})); - if (!valid && result == XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED) { - WARN( - "On receiving an 'invalid' enum value " - << viewType - << ", the runtime returned as XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED instead of XR_ERROR_VALIDATION_FAILURE, which may make it harder for apps to reason about the error."); - } - else if (valid && result == XR_ERROR_VALIDATION_FAILURE) { - WARN( - "On receiving a 'valid' but not supported enum value " - << viewType - << ", the runtime returned as XR_ERROR_VALIDATION_FAILURE instead of XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED, which may make it harder for apps to reason about the error."); - } - } - } + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_SYNCHRONIZED); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_VISIBLE); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_FOCUSED); - XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; - beginInfo.primaryViewConfigurationType = GetGlobalData().GetOptions().viewConfigurationValue; - XRC_CHECK_THROW_XRCMD(xrBeginSession(session, &beginInfo)); + // Runtime should only allow ending a session in the STOPPING state. + REQUIRE(XR_ERROR_SESSION_NOT_STOPPING == xrEndSession(session)); - if (GetGlobalData().IsUsingGraphicsPlugin()) { - // Runtime should not transition from READY to SYNCHRONIZED until one or more frames have been submitted. - // The exception is if the runtime is transitioning to STOPPING, which should not happen - // during conformance testing. This will wait 1 second before assuming no such incorrect event will come. - REQUIRE_MSG(tryGetNextSessionState(&evt) == false, "Premature progression from READY to SYNCHRONIZED state"); + REQUIRE(XR_SUCCESS == xrRequestExitSession(session)); + + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_VISIBLE); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_SYNCHRONIZED); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_STOPPING); + + // Runtime should not transition from STOPPING to IDLE until the session has been ended. + // This will wait 1 second before assuming no such incorrect event will come. + REQUIRE_MSG(waitForNextSessionState(instance, &evt) == false, "Premature progression from STOPPING to IDLE state"); + + REQUIRE(XR_SUCCESS == xrEndSession(session)); + + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_IDLE); + + // https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#session-lifecycle + // If the runtime determines that its use of this XR session has + // concluded, it will transition the session state from + // XR_SESSION_STATE_IDLE to XR_SESSION_STATE_EXITING. + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_EXITING); + + // When the application receives the XR_SESSION_STATE_EXITING + // event, it releases the resources related to the session and + // calls xrDestroySession. } - auto submitFrame = [&]() { - XrFrameState frameState{XR_TYPE_FRAME_STATE}; - XRC_CHECK_THROW_XRCMD(xrWaitFrame(session, nullptr, &frameState)); - XRC_CHECK_THROW_XRCMD(xrBeginFrame(session, nullptr)); - - XrFrameEndInfo frameEndInfo{XR_TYPE_FRAME_END_INFO}; - frameEndInfo.displayTime = frameState.predictedDisplayTime; - frameEndInfo.environmentBlendMode = GetGlobalData().GetOptions().environmentBlendModeValue; - XRC_CHECK_THROW_XRCMD(xrEndFrame(session, &frameEndInfo)); - }; - - auto submitFramesUntilSessionState = [&](XrSessionState expectedSessionState, std::chrono::nanoseconds duration = 30s) { - CAPTURE(expectedSessionState); - - CountdownTimer countdown(duration); - while (!countdown.IsTimeUp()) { - if (tryGetNextSessionState(&evt)) { - REQUIRE(evt.state == expectedSessionState); - return; - } - submitFrame(); + SECTION("xrRequestExitSession Session Not Running") + { + SECTION("Session not running before starting") + { + AutoBasicSession session(AutoBasicSession::createSession, instance); + + REQUIRE(XR_ERROR_SESSION_NOT_RUNNING == xrRequestExitSession(session)); } - FAIL("Failed to reach expected session state"); - }; + SECTION("Session not running after ending") + { + // A session is considered running after a successful call to + // xrBeginSession and remains running until any call is made to + // xrEndSession. Certain functions are only valid to call when + // a session is running, such as xrWaitFrame, or else the + // XR_ERROR_SESSION_NOT_RUNNING error must be returned by the + // runtime. - submitFramesUntilSessionState(XR_SESSION_STATE_SYNCHRONIZED); - submitFramesUntilSessionState(XR_SESSION_STATE_VISIBLE); - submitFramesUntilSessionState(XR_SESSION_STATE_FOCUSED); + // If session is not running when xrRequestExitSession is + // called, XR_ERROR_SESSION_NOT_RUNNING must be returned. - // Runtime should only allow ending a session in the STOPPING state. - REQUIRE(XR_ERROR_SESSION_NOT_STOPPING == xrEndSession(session)); + AutoBasicSession session( + AutoBasicSession::beginSession | AutoBasicSession::createSpaces | AutoBasicSession::createSwapchains, instance); + REQUIRE(XR_SUCCESS == xrRequestExitSession(session)); - XRC_CHECK_THROW_XRCMD(xrRequestExitSession(session)); + FrameIterator frameIterator(&session); + frameIterator.RunToSessionState(XR_SESSION_STATE_STOPPING); + REQUIRE(XR_SUCCESS == xrEndSession(session)); - submitFramesUntilSessionState(XR_SESSION_STATE_VISIBLE); - submitFramesUntilSessionState(XR_SESSION_STATE_SYNCHRONIZED); - submitFramesUntilSessionState(XR_SESSION_STATE_STOPPING); + // Actually test what we want to test! + REQUIRE(XR_ERROR_SESSION_NOT_RUNNING == xrRequestExitSession(session)); + } + } - // Runtime should not transition from STOPPING to IDLE until the session has been ended. - REQUIRE_MSG(tryGetNextSessionState(&evt) == false, "Premature progression from STOPPING to IDLE state"); + // Runtime should not transition from READY to SYNCHRONIZED until one + // or more frames have been submitted. + // The exception is if the runtime is transitioning to STOPPING, which + // should not happen during conformance testing. + if (GetGlobalData().IsUsingGraphicsPlugin()) { + SECTION("Advance without frame submission") + { + AutoBasicSession session(AutoBasicSession::createSession, instance); - XRC_CHECK_THROW_XRCMD(xrEndSession(session)); + FrameIterator frameIterator(&session); + frameIterator.RunToSessionState(XR_SESSION_STATE_READY); - submitFramesUntilSessionState(XR_SESSION_STATE_IDLE); - submitFramesUntilSessionState(XR_SESSION_STATE_EXITING); + XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; + beginInfo.primaryViewConfigurationType = GetGlobalData().GetOptions().viewConfigurationValue; + REQUIRE(XR_SUCCESS == xrBeginSession(session, &beginInfo)); + + // This will wait 1 second before assuming no such incorrect event will come. + XrEventDataSessionStateChanged evt; + REQUIRE_MSG(waitForNextSessionState(instance, &evt) == false, "Premature progression from READY to SYNCHRONIZED state"); + } + } } } // namespace Conformance diff --git a/src/conformance/conformance_test/test_ViewConfigurations.cpp b/src/conformance/conformance_test/test_ViewConfigurations.cpp index 14d80dc4..46de14bc 100644 --- a/src/conformance/conformance_test/test_ViewConfigurations.cpp +++ b/src/conformance/conformance_test/test_ViewConfigurations.cpp @@ -19,6 +19,8 @@ #include "matchers.h" #include +#include +#include #include #include @@ -29,7 +31,6 @@ namespace Conformance { - TEST_CASE("ViewConfigurations", "") { // XrResult xrEnumerateViewConfigurations(XrInstance instance, XrSystemId systemId, uint32_t viewConfigurationTypeCapacityInput, @@ -71,6 +72,59 @@ namespace Conformance } } + // Ensure unsupported view configuration types fail. + { +#define AS_LIST(name, val) name, + constexpr XrViewConfigurationType KnownViewTypes[] = {XR_LIST_ENUM_XrViewConfigurationType(AS_LIST)}; +#undef AS_LIST + + XrSystemId systemId = instance.systemId; + + // Get the list of supported view configurations + uint32_t viewCount = 0; + REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, 0, &viewCount, nullptr)); + std::vector runtimeViewTypes(viewCount); + REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, viewCount, &viewCount, runtimeViewTypes.data())); + + AutoBasicSession session(AutoBasicSession::createSession, instance); + FrameIterator frameIterator(&session); + frameIterator.RunToSessionState(XR_SESSION_STATE_READY); + + for (XrViewConfigurationType viewType : KnownViewTypes) { + CAPTURE(viewType); + + // Is this enum valid, check against enabled extensions. + bool valid = IsViewConfigurationTypeEnumValid(viewType); + + if (!IsViewConfigurationTypeEnumValid(viewType)) { + INFO("Must not enumerate invalid view configuration type"); + CHECK_THAT(runtimeViewTypes, !Catch::Matchers::VectorContains(viewType)); + } + + // Skip this view config if it is supported, since we cannot test correct handling of unsupported values with it. + if (Catch::Matchers::VectorContains(viewType).match(runtimeViewTypes)) { + continue; + } + + XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; + beginInfo.primaryViewConfigurationType = viewType; + XrResult result = xrBeginSession(session, &beginInfo); + REQUIRE_THAT(result, In({XR_ERROR_VALIDATION_FAILURE, XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED})); + if (!valid && result == XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED) { + WARN( + "On receiving an 'invalid' enum value " + << viewType + << ", the runtime returned as XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED instead of XR_ERROR_VALIDATION_FAILURE, which may make it harder for apps to reason about the error."); + } + else if (valid && result == XR_ERROR_VALIDATION_FAILURE) { + WARN( + "On receiving a 'valid' but not supported enum value " + << viewType + << ", the runtime returned as XR_ERROR_VALIDATION_FAILURE instead of XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED, which may make it harder for apps to reason about the error."); + } + } + } + // xrGetViewConfigurationProperties { // XrResult xrGetViewConfigurationProperties(XrInstance instance, XrSystemId systemId, XrViewConfigurationType diff --git a/src/conformance/conformance_test/test_XR_FB_space_warp.cpp b/src/conformance/conformance_test/test_XR_FB_space_warp.cpp index 11d9e275..945bfc53 100644 --- a/src/conformance/conformance_test/test_XR_FB_space_warp.cpp +++ b/src/conformance/conformance_test/test_XR_FB_space_warp.cpp @@ -95,28 +95,43 @@ namespace Conformance float maxDepth; float nearZ; float farZ; + XrResult result; }; constexpr float minimum_useful_z = 0.01f; std::vector varyingInfoTestArray{ - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, minimum_useful_z, 100.0f}, - SpaceWarpVaryingInfo{0, 0.5f, 0.6f, minimum_useful_z, 100.0f}, - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, minimum_useful_z, std::numeric_limits::infinity()}, - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, 100.0f, minimum_useful_z}, - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::infinity(), minimum_useful_z}, - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::max(), minimum_useful_z}, - SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::max(), minimum_useful_z}, - SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, minimum_useful_z, 100.0f}, - SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.5f, 0.6f, minimum_useful_z, 100.0f}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, minimum_useful_z, 100.0f, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.5f, 0.6f, minimum_useful_z, 100.0f, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, minimum_useful_z, std::numeric_limits::infinity(), XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, 100.0f, minimum_useful_z, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::infinity(), minimum_useful_z, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::max(), minimum_useful_z, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::max(), minimum_useful_z, XR_SUCCESS}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, minimum_useful_z, 100.0f, + XR_SUCCESS}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.5f, 0.6f, minimum_useful_z, 100.0f, + XR_SUCCESS}, SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, minimum_useful_z, - std::numeric_limits::infinity()}, - SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, 100.0f, minimum_useful_z}, + std::numeric_limits::infinity(), XR_SUCCESS}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, 100.0f, minimum_useful_z, + XR_SUCCESS}, SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, - std::numeric_limits::infinity(), minimum_useful_z}, + std::numeric_limits::infinity(), minimum_useful_z, XR_SUCCESS}, SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, std::numeric_limits::max(), - minimum_useful_z}, + minimum_useful_z, XR_SUCCESS}, SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, std::numeric_limits::max(), - minimum_useful_z}}; + minimum_useful_z, XR_SUCCESS}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, minimum_useful_z, minimum_useful_z, XR_ERROR_VALIDATION_FAILURE}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, 100.0f, 100.0f, XR_ERROR_VALIDATION_FAILURE}, + SpaceWarpVaryingInfo{0, 0.0f, 1.0f, std::numeric_limits::infinity(), std::numeric_limits::infinity(), + XR_ERROR_VALIDATION_FAILURE}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, minimum_useful_z, minimum_useful_z, + XR_ERROR_VALIDATION_FAILURE}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, 100.0f, 100.0f, + XR_ERROR_VALIDATION_FAILURE}, + SpaceWarpVaryingInfo{XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0.0f, 1.0f, + std::numeric_limits::infinity(), std::numeric_limits::infinity(), + XR_ERROR_VALIDATION_FAILURE}}; for (const SpaceWarpVaryingInfo& varyingInfo : varyingInfoTestArray) { REQUIRE(frameIterator.PrepareSubmitFrame() == FrameIterator::RunResult::Success); @@ -160,7 +175,7 @@ namespace Conformance // xrEndFrame requires the XR_FB_space_warp extension to be // enabled or else it must return XR_ERROR_LAYER_INVALID. XrResult result = xrEndFrame(session.GetSession(), &frameIterator.frameEndInfo); - CHECK(result == XR_SUCCESS); + CHECK(result == varyingInfo.result); } } diff --git a/src/conformance/conformance_test/test_actions.cpp b/src/conformance/conformance_test/test_actions.cpp index 344efb63..b4bc19a1 100644 --- a/src/conformance/conformance_test/test_actions.cpp +++ b/src/conformance/conformance_test/test_actions.cpp @@ -34,7 +34,9 @@ #include #include #include +#include #include +#include #include #include @@ -1047,19 +1049,18 @@ namespace Conformance XrBoundSourcesForActionEnumerateInfo info{XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO}; info.action = selectAction; uint32_t sourceCountOutput; - XrPath buffer; - REQUIRE_RESULT(xrEnumerateBoundSourcesForAction(session, &info, 0, &sourceCountOutput, &buffer), - XR_ERROR_ACTIONSET_NOT_ATTACHED); - } - SECTION("Get localized source name") - { - XrInputSourceLocalizedNameGetInfo getInfo{XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; - getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT; - getInfo.sourcePath = StringToPath(instance, "/user/hand/left/input/select/click"); - uint32_t sourceCountOutput; - char buffer; - REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, &buffer), + REQUIRE_RESULT(xrEnumerateBoundSourcesForAction(session, &info, 0, &sourceCountOutput, nullptr), XR_ERROR_ACTIONSET_NOT_ATTACHED); + + REQUIRE_RESULT(xrAttachSessionActionSets(session, &attachInfo), XR_SUCCESS); + + std::vector boundSourcesPaths = REQUIRE_TWO_CALL(XrPath, {}, xrEnumerateBoundSourcesForAction, session, &info); + sourceCountOutput = static_cast(boundSourcesPaths.size()); + + // should not get a null path, not really much else we can assert here. + REQUIRE_THAT(boundSourcesPaths, !Catch::Matchers::VectorContains(XrPath{})); + // Can we assert that we don't enumerate duplicates? Would be weird to return duplicates but may not be strictly forbidden. + REQUIRE_THAT(boundSourcesPaths, VectorHasOnlyUniqueElements{}); } } SECTION("Unattached action sets") @@ -3925,65 +3926,94 @@ namespace Conformance REQUIRE(enumerateResult.size() > 0); + // should not get a null path, not really much else we can assert here. + REQUIRE_THAT(enumerateResult, !Catch::Matchers::VectorContains(XrPath{})); + // Can we assert that we don't enumerate duplicates? Would be weird to return duplicates but may not be strictly forbidden. + REQUIRE_THAT(enumerateResult, VectorHasOnlyUniqueElements{}); + XrInputSourceLocalizedNameGetInfo getInfo{XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; + getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT; getInfo.sourcePath = enumerateResult[0]; - SECTION("xrGetInputSourceLocalizedName") + + uint32_t sourceCountOutput; + SECTION("Invalid components") + { + getInfo.whichComponents = 0; + REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, nullptr), + XR_ERROR_VALIDATION_FAILURE); + } + SECTION("Invalid path") { - getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT; - std::vector localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT; - localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; - localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - getInfo.whichComponents = - XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT; - localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; - localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - getInfo.whichComponents = - XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; - localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); - - getInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | - XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | - XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; - localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); - CHECK(localizedStringResult.size() > 1); // more than null terminator - CHECK_THAT(localizedStringResult, NullTerminatedVec()); + getInfo.sourcePath = XR_NULL_PATH; + REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, nullptr), XR_ERROR_PATH_INVALID); + getInfo.sourcePath = (XrPath)0x1234; + REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, nullptr), XR_ERROR_PATH_INVALID); + } - uint32_t sourceCountOutput; - char buffer; - SECTION("Invalid components") - { - getInfo.whichComponents = 0; - REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, &buffer), - XR_ERROR_VALIDATION_FAILURE); - } - SECTION("Invalid path") - { - getInfo.sourcePath = XR_NULL_PATH; - REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, &buffer), - XR_ERROR_PATH_INVALID); - getInfo.sourcePath = (XrPath)0x1234; - REQUIRE_RESULT(xrGetInputSourceLocalizedName(session, &getInfo, 0, &sourceCountOutput, &buffer), - XR_ERROR_PATH_INVALID); + SECTION("xrGetInputSourceLocalizedName-on-each") + { + for (size_t i = 0; i < enumerateResult.size(); ++i) { + getInfo.sourcePath = enumerateResult[i]; + CAPTURE(i); + + auto s = PathToString(instance, getInfo.sourcePath); + CHECK(!s.empty()); + CAPTURE(s); + std::vector localizedStringResult; + + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } + { + CAPTURE(XrInputSourceLocalizedNameFlagsRefCPP(getInfo.whichComponents) = + XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT); + localizedStringResult = REQUIRE_TWO_CALL(char, {}, xrGetInputSourceLocalizedName, session, &getInfo); + CHECK(localizedStringResult.size() > 1); // more than null terminator + CHECK_THAT(localizedStringResult, NullTerminatedVec()); + } } } } diff --git a/src/conformance/conformance_test/test_xrLocateViews.cpp b/src/conformance/conformance_test/test_xrLocateViews.cpp index 2fbc527e..8466527c 100644 --- a/src/conformance/conformance_test/test_xrLocateViews.cpp +++ b/src/conformance/conformance_test/test_xrLocateViews.cpp @@ -33,84 +33,95 @@ namespace Conformance { GlobalData& globalData = GetGlobalData(); - // Get a session started. - AutoBasicSession session(AutoBasicSession::createInstance | AutoBasicSession::createSession | AutoBasicSession::beginSession | - AutoBasicSession::createSwapchains | AutoBasicSession::createSpaces); - - // Get frames iterating to the point of app focused state. This will draw frames along the way. - FrameIterator frameIterator(&session); - frameIterator.RunToSessionState(XR_SESSION_STATE_FOCUSED); - - // Render one frame to get a predicted display time for the xrLocateViews calls. - FrameIterator::RunResult runResult = frameIterator.SubmitFrame(); - REQUIRE(runResult == FrameIterator::RunResult::Success); - - XrResult result; + // Get a session, but do not start it yet, we might pick a different view config type. + // Swapchain will be sized based on default view config type, but that is OK. + AutoBasicSession session(AutoBasicSession::createInstance | AutoBasicSession::createSession | AutoBasicSession::createSwapchains | + AutoBasicSession::createSpaces); XrViewLocateInfo locateInfo{XR_TYPE_VIEW_LOCATE_INFO}; locateInfo.space = session.spaceVector.front(); - XrTime time = frameIterator.frameState.predictedDisplayTime; - CHECK(time != 0); - locateInfo.displayTime = time; - locateInfo.viewConfigurationType = globalData.GetOptions().viewConfigurationValue; - - XrViewState viewState{XR_TYPE_VIEW_STATE}; - uint32_t viewCount = (uint32_t)session.viewConfigurationViewVector.size(); + auto viewConfigDependentSetup = [&](XrViewConfigurationType configType) { + locateInfo.viewConfigurationType = configType; + session.viewConfigurationType = configType; + { + CAPTURE(configType); + session.BeginSession(); + } - CAPTURE(viewCount); - SECTION("valid inputs") - { - std::vector views(viewCount, {XR_TYPE_VIEW}); - uint32_t viewCountOut = 0; + // Get frames iterating to the point of app focused state. This will draw frames along the way. + FrameIterator frameIterator(&session); + frameIterator.RunToSessionState(XR_SESSION_STATE_FOCUSED); - CAPTURE(locateInfo.displayTime); - CHECK(XR_SUCCESS == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); + // Render one frame to get a predicted display time for the xrLocateViews calls. + // FrameIterator::RunResult runResult = frameIterator.SubmitFrame(); + // REQUIRE(runResult == FrameIterator::RunResult::Success); - CHECK(viewCountOut == viewCount); - } - SECTION("invalid inputs") + const XrTime time = frameIterator.frameState.predictedDisplayTime; + REQUIRE(time != 0); + locateInfo.displayTime = time; + }; + SECTION("Selected view config") { - std::vector views(viewCount, {XR_TYPE_VIEW}); - uint32_t viewCountOut = 0; + viewConfigDependentSetup(globalData.GetOptions().viewConfigurationValue); - OPTIONAL_INVALID_HANDLE_VALIDATION_SECTION - { - // Exercise NULL session handle. - CHECK(xrLocateViews(XR_NULL_HANDLE_CPP, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()) == - XR_ERROR_HANDLE_INVALID); - - // Exercise invalid session handle. - CHECK(xrLocateViews(GlobalData().invalidSession, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()) == - XR_ERROR_HANDLE_INVALID); - } + XrViewState viewState{XR_TYPE_VIEW_STATE}; + uint32_t viewCount = (uint32_t)session.viewConfigurationViewVector.size(); - SECTION("Exercise 0 as an invalid time") + CAPTURE(viewCount); + SECTION("valid inputs") { - locateInfo.displayTime = 0; - CAPTURE(locateInfo.displayTime); - CHECK(XR_ERROR_TIME_INVALID == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); - } + std::vector views(viewCount, {XR_TYPE_VIEW}); + uint32_t viewCountOut = 0; - SECTION("Exercise negative values as an invalid time") - { - locateInfo.displayTime = (XrTime)-42; CAPTURE(locateInfo.displayTime); - CHECK(XR_ERROR_TIME_INVALID == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); - } + CHECK(XR_SUCCESS == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); - OPTIONAL_INVALID_TYPE_VALIDATION_SECTION + CHECK(viewCountOut == viewCount); + } + SECTION("invalid inputs") { - std::vector invalidViews(viewCount, {XR_TYPE_UNKNOWN}); - REQUIRE(xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, invalidViews.data()) == - XR_ERROR_VALIDATION_FAILURE); + std::vector views(viewCount, {XR_TYPE_VIEW}); + uint32_t viewCountOut = 0; + + OPTIONAL_INVALID_HANDLE_VALIDATION_SECTION + { + // Exercise NULL session handle. + CHECK(xrLocateViews(XR_NULL_HANDLE_CPP, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()) == + XR_ERROR_HANDLE_INVALID); + + // Exercise invalid session handle. + CHECK(xrLocateViews(GlobalData().invalidSession, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()) == + XR_ERROR_HANDLE_INVALID); + } + + SECTION("Exercise 0 as an invalid time") + { + locateInfo.displayTime = 0; + CAPTURE(locateInfo.displayTime); + CHECK(XR_ERROR_TIME_INVALID == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); + } + + SECTION("Exercise negative values as an invalid time") + { + locateInfo.displayTime = (XrTime)-42; + CAPTURE(locateInfo.displayTime); + CHECK(XR_ERROR_TIME_INVALID == xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data())); + } + + OPTIONAL_INVALID_TYPE_VALIDATION_SECTION + { + std::vector invalidViews(viewCount, {XR_TYPE_UNKNOWN}); + REQUIRE(xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, invalidViews.data()) == + XR_ERROR_VALIDATION_FAILURE); + } } } SECTION("all known view types") { // Ensure unsupported view configuration types fail and supported types pass - XrInstance instance = session.GetInstance(); - XrSystemId systemId = session.GetSystemId(); + const XrInstance instance = session.GetInstance(); + const XrSystemId systemId = session.GetSystemId(); // Get the list of supported view configurations uint32_t viewConfigCount = 0; @@ -119,61 +130,65 @@ namespace Conformance REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurations(instance, systemId, viewConfigCount, &viewConfigCount, runtimeViewTypes.data())); - CAPTURE(locateInfo.displayTime); - + XrViewState viewState{XR_TYPE_VIEW_STATE}; for (auto viewTypeAndName : KnownViewTypes) { - XrViewConfigurationType viewType = viewTypeAndName.first; - CAPTURE(viewType); - CAPTURE(viewTypeAndName.second); + DYNAMIC_SECTION("View type " << viewTypeAndName.second) + { + XrViewConfigurationType viewType = viewTypeAndName.first; + CAPTURE(viewType); + CAPTURE(viewTypeAndName.second); + + // Is this enum valid, check against enabled extensions. + bool valid = IsViewConfigurationTypeEnumValid(viewType); + + const bool isSupportedType = Catch::Matchers::VectorContains(viewType).match(runtimeViewTypes); + CAPTURE(valid); + CAPTURE(isSupportedType); + + if (!valid) { + INFO("Not a valid view configuration type given the enabled extensions"); + CHECK_MSG(!isSupportedType, "Cannot support invalid view configuration type"); + } - // Is this enum valid, check against enabled extensions. - bool valid = IsViewConfigurationTypeEnumValid(viewType); + if (isSupportedType) { - const bool isSupportedType = Catch::Matchers::VectorContains(viewType).match(runtimeViewTypes); - CAPTURE(valid); - CAPTURE(isSupportedType); + // Supported but we don't have the corresponding view count immediately at hand + // So, we look it up. + uint32_t expectedViewCount = 0; + REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurationViews(session.GetInstance(), session.GetSystemId(), viewType, 0, + &expectedViewCount, nullptr)); - if (!valid) { - INFO("Not a valid view configuration type given the enabled extensions"); - CHECK_MSG(!isSupportedType, "Cannot support invalid view configuration type"); - } + viewConfigDependentSetup(viewType); - locateInfo.viewConfigurationType = viewType; - if (isSupportedType) { - // Supported but we don't have the corresponding view count immediately at hand - // So, we look it up. - uint32_t expectedViewCount = 0; - REQUIRE(XR_SUCCESS == xrEnumerateViewConfigurationViews(session.GetInstance(), session.GetSystemId(), viewType, 0, - &expectedViewCount, nullptr)); - INFO("Calling xrLocateViews with the noted viewType, which is claimed to be supported"); - uint32_t viewCountOut = 0; - std::vector views(expectedViewCount, {XR_TYPE_VIEW}); - CHECK(XR_SUCCESS == xrLocateViews(session, &locateInfo, &viewState, expectedViewCount, &viewCountOut, views.data())); - } - else { - // Not a supported type, so call should fail, regardless of the array size. - INFO("Calling xrLocateViews with the noted viewType, which is claimed to be not supported"); - uint32_t viewCountOut = 0; - std::vector views(viewCount, {XR_TYPE_VIEW}); - - result = xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()); - REQUIRE_THAT(result, In({XR_ERROR_VALIDATION_FAILURE, XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED})); - if (!valid && result == XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED) { - WARN( - "Runtime accepted an invalid enum value as unsupported, which makes it harder for apps to reason about the error."); + CAPTURE(locateInfo.displayTime); + + INFO("Calling xrLocateViews with the noted viewType, which is claimed to be supported"); + uint32_t viewCountOut = 0; + std::vector views(expectedViewCount, {XR_TYPE_VIEW}); + CHECK(XR_SUCCESS == + xrLocateViews(session, &locateInfo, &viewState, expectedViewCount, &viewCountOut, views.data())); } - else if (valid && result == XR_ERROR_VALIDATION_FAILURE) { - WARN( - "Runtime accepted an valid but unsupported enum value as unsupported, which makes it harder for apps to reason about the error."); + else { + // Not a supported type, so call should fail, regardless of the array size. + INFO("Calling xrLocateViews with the noted viewType, which is claimed to be not supported"); + uint32_t viewCount = static_cast(session.viewConfigurationViewVector.size()); + uint32_t viewCountOut = 0; + std::vector views(viewCount, {XR_TYPE_VIEW}); + + XrResult result = xrLocateViews(session, &locateInfo, &viewState, viewCount, &viewCountOut, views.data()); + REQUIRE_THAT(result, In({XR_ERROR_VALIDATION_FAILURE, XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED})); + if (!valid && result == XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED) { + WARN( + "Runtime accepted an invalid enum value as unsupported, which makes it harder for apps to reason about the error."); + } + else if (valid && result == XR_ERROR_VALIDATION_FAILURE) { + WARN( + "Runtime accepted an valid but unsupported enum value as unsupported, which makes it harder for apps to reason about the error."); + } } } } } - - // Leave - result = xrRequestExitSession(session); - CHECK(result == XR_SUCCESS); - - frameIterator.RunToSessionState(XR_SESSION_STATE_STOPPING); } + } // namespace Conformance diff --git a/src/conformance/conformance_test/test_xrRequestExitSession.cpp b/src/conformance/conformance_test/test_xrRequestExitSession.cpp deleted file mode 100644 index a9e36175..00000000 --- a/src/conformance/conformance_test/test_xrRequestExitSession.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2019-2024, The Khronos Group Inc. -// -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "conformance_utils.h" - -#include -#include - -namespace Conformance -{ - TEST_CASE("xrRequestExitSession", "") - { - AutoBasicSession session(AutoBasicSession::createSession); - - SECTION("Session Not Running") - { - REQUIRE(XR_ERROR_SESSION_NOT_RUNNING == xrRequestExitSession(session)); - } - - // Successful case is tested in test_SessionState.cpp - } - -} // namespace Conformance diff --git a/src/conformance/framework/CMakeLists.txt b/src/conformance/framework/CMakeLists.txt index 8adc3a50..9f89b954 100644 --- a/src/conformance/framework/CMakeLists.txt +++ b/src/conformance/framework/CMakeLists.txt @@ -50,12 +50,16 @@ run_xr_xml_generate( ${PROJECT_SOURCE_DIR}/src/scripts/template_function_info.cpp ) run_xr_xml_generate( - conformance_generator.py interaction_info_generated.cpp + conformance_generator.py + interaction_info_generated.cpp "${PROJECT_SOURCE_DIR}/src/scripts/template_interaction_info_generated.cpp" + "${PROJECT_SOURCE_DIR}/src/scripts/interaction_profile_processor.py" ) run_xr_xml_generate( - conformance_generator.py interaction_info_generated.h + conformance_generator.py + interaction_info_generated.h "${PROJECT_SOURCE_DIR}/src/scripts/template_interaction_info_generated.h" + "${PROJECT_SOURCE_DIR}/src/scripts/interaction_profile_processor.py" ) add_library( diff --git a/src/conformance/framework/conformance_framework.cpp b/src/conformance/framework/conformance_framework.cpp index 6738032e..fce1ff4c 100644 --- a/src/conformance/framework/conformance_framework.cpp +++ b/src/conformance/framework/conformance_framework.cpp @@ -133,12 +133,46 @@ namespace Conformance AppendSprintf(reportString, "Handle invalidation tested: %s\n", globalData.options.invalidHandleValidation ? "yes" : "no"); AppendSprintf(reportString, "Type invalidation tested: %s\n", globalData.options.invalidTypeValidation ? "yes" : "no"); AppendSprintf(reportString, "Non-disconnectable devices: %s\n", globalData.options.nonDisconnectableDevices ? "yes" : "no"); - AppendSprintf(reportString, "Test Success Count: %d\n", (int)testSuccessCount); - AppendSprintf(reportString, "Test Failure Count: %d\n", (int)testFailureCount); + AppendSprintf(reportString, "Test Success Count: %zu\n", TestSuccessCount()); + AppendSprintf(reportString, "Test Failure Count: %zu\n", TestFailureCount()); + if (TestFailureCount() > 0) { + AppendSprintf(reportString, "Tests with failures:\n"); + for (auto const& pair : results) { + const auto& name = pair.first; + const auto& score = pair.second; + if (score.testFailureCount > 0) { + AppendSprintf(reportString, " %s\n", name.c_str()); + } + } + } + if (!unmatchedTestSpecs.empty()) { + AppendSprintf(reportString, "Unmatched Test Specs:\n"); + for (auto const& spec : unmatchedTestSpecs) { + AppendSprintf(reportString, " %s\n", spec.c_str()); + } + } return reportString; } + uint64_t ConformanceReport::TestSuccessCount() const + { + uint64_t success{}; + for (auto const& pair : results) { + success += pair.second.testSuccessCount; + } + return success; + } + + uint64_t ConformanceReport::TestFailureCount() const + { + uint64_t failure{}; + for (auto const& pair : results) { + failure += pair.second.testFailureCount; + } + return failure; + } + bool GlobalData::Initialize() { // NOTE: Runs *after* population of command-line options. @@ -301,49 +335,6 @@ namespace Conformance } } } - // Fill out the functions in functionInfoMap. - // Keep trying all functions, only failing out at end if one of them failed. - bool functionMapInitialized = true; - const FunctionInfoMap& functionInfoMap = GetFunctionInfoMap(); - for (auto& functionInfo : functionInfoMap) { - // We need to poke the address pointer into map entries. - result = xrGetInstanceProcAddr(autoInstance, functionInfo.first.c_str(), - &const_cast(functionInfo.second).functionPtr); - - if (XR_SUCCEEDED(result)) { - // This doesn't actually prove the pointer is correct. However, we will exercise that later. - if (functionInfo.second.functionPtr == nullptr) { - ReportF("GlobalData::Initialize: xrGetInstanceProcAddr for '%s' failed to return valid addr.", - functionInfo.first.c_str()); - functionMapInitialized = false; - } - } - else { - if (!ValidateResultAllowed("xrGetInstanceProcAddr", result)) { - ReportF("GlobalData::Initialize: xrGetInstanceProcAddr for '%s' returned invalid XrResult.", - functionInfo.first.c_str()); - functionMapInitialized = false; - } - - // If we could not get a pointer to this function, then it should be because we didn't - // enable the extension required by the function. - if (result != XR_ERROR_FUNCTION_UNSUPPORTED) { - ReportF("GlobalData::Initialize: xrGetInstanceProcAddr for '%s' failed with result: %s.", functionInfo.first.c_str(), - ResultToString(result)); - functionMapInitialized = false; - } - - // At this point, result is XR_ERROR_FUNCTION_UNSUPPORTED. - // Verify that the extension was *not* enabled. - // functionInfo.second.requiredExtension - // To do. - } - } - - if (!functionMapInitialized) { - ReportF("GlobalData::Initialize: xrGetInstanceProcAddr failed for one or more functions."); - return false; - } // Find XrSystemId (for later use and to ensure device is connected/available for whatever that means in a given runtime) XrSystemId systemId = XR_NULL_SYSTEM_ID; diff --git a/src/conformance/framework/conformance_framework.h b/src/conformance/framework/conformance_framework.h index 6febb8d0..55faa770 100644 --- a/src/conformance/framework/conformance_framework.h +++ b/src/conformance/framework/conformance_framework.h @@ -167,7 +167,7 @@ namespace Conformance /// or 0 when auto skip is disabled. std::chrono::milliseconds autoSkipTimeout{0}; - /// Options include "stereo" "mono". See enum XrViewConfigurationType. + /// Options include "stereo" "mono" "foveatedInset" "firstPersonObserver". See enum XrViewConfigurationType. /// Default is stereo. std::string viewConfiguration{"Stereo"}; XrViewConfigurationType viewConfigurationValue{XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO}; @@ -310,10 +310,21 @@ namespace Conformance std::string GetReportString() const; public: + class Score + { + public: + uint64_t testSuccessCount{}; + uint64_t testFailureCount{}; + }; + /// The total successful test case runs across all test cases. + uint64_t TestSuccessCount() const; + + /// The total failed test case runs across all test cases. + uint64_t TestFailureCount() const; + XrVersion apiVersion{XR_CURRENT_API_VERSION}; - uint64_t testSuccessCount{}; - uint64_t testFailureCount{}; - bool unmatchedTestSpecs{false}; + std::unordered_map results; + std::vector unmatchedTestSpecs; Catch::Totals totals{}; TimedSubmissionResults timedSubmission; std::vector> swapchainFormats; diff --git a/src/conformance/framework/conformance_utils.cpp b/src/conformance/framework/conformance_utils.cpp index 99fa2cd2..412f57dd 100644 --- a/src/conformance/framework/conformance_utils.cpp +++ b/src/conformance/framework/conformance_utils.cpp @@ -440,12 +440,13 @@ namespace Conformance //////////////////////////////////////////////////////////////////////////////////////////////// // AutoBasicSession //////////////////////////////////////////////////////////////////////////////////////////////// - AutoBasicSession::AutoBasicSession(int optionFlags_, XrInstance instance_) : optionFlags(optionFlags_) + AutoBasicSession::AutoBasicSession(int optionFlags_, XrInstance instance_, XrViewConfigurationType viewConfigType_) + : optionFlags(optionFlags_) { - Init(optionFlags_, instance_); + Init(optionFlags_, instance_, viewConfigType_); } - void AutoBasicSession::Init(int optionFlags_, XrInstance instance_) + void AutoBasicSession::Init(int optionFlags_, XrInstance instance_, XrViewConfigurationType viewConfigType_) { GlobalData& globalData = GetGlobalData(); @@ -467,6 +468,8 @@ namespace Conformance instance = instance_; optionFlags = optionFlags_; + viewConfigurationType = + viewConfigType_ == XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM ? globalData.options.viewConfigurationValue : viewConfigType_; if ((optionFlags & createInstance) == 0) { // cannot proceed further without an instance @@ -503,16 +506,13 @@ namespace Conformance XRC_CHECK_THROW_XRCMD(xrStringToPath(instance, "/user/hand/left", &handSubactionArray[0])); XRC_CHECK_THROW_XRCMD(xrStringToPath(instance, "/user/hand/right", &handSubactionArray[1])); - // Note that while we are enumerating this, normally our testing is done via a pre-chosen one (globalData.options.viewConfigurationValue). - XRC_CHECK_THROW_XRCMD(doTwoCallInPlace(viewConfigurationTypeVector, xrEnumerateViewConfigurations, instance, systemId)); - - // We use globalData.options.viewConfigurationValue as the type we enumerate with, despite that the runtime may support others. + // We use viewConfigurationType as the type we enumerate with, despite that the runtime may support others. XRC_CHECK_THROW_XRCMD(doTwoCallInPlaceWithEmptyElement(viewConfigurationViewVector, {XR_TYPE_VIEW_CONFIGURATION_VIEW}, xrEnumerateViewConfigurationViews, instance, systemId, - globalData.options.viewConfigurationValue)); + viewConfigurationType)); - XRC_CHECK_THROW_XRCMD(doTwoCallInPlace(environmentBlendModeVector, xrEnumerateEnvironmentBlendModes, instance, systemId, - globalData.options.viewConfigurationValue)); + XRC_CHECK_THROW_XRCMD( + doTwoCallInPlace(environmentBlendModeVector, xrEnumerateEnvironmentBlendModes, instance, systemId, viewConfigurationType)); if ((optionFlags & createSwapchains) && globalData.IsUsingGraphicsPlugin()) { auto graphicsPlugin = globalData.GetGraphicsPlugin(); @@ -604,8 +604,13 @@ namespace Conformance XrSessionBeginInfo sessionBeginInfo{XR_TYPE_SESSION_BEGIN_INFO, globalData.GetPlatformPlugin()->PopulateNextFieldForStruct(XR_TYPE_SESSION_BEGIN_INFO), - globalData.options.viewConfigurationValue}; + viewConfigurationType}; XRC_CHECK_THROW_XRCMD(xrBeginSession(session, &sessionBeginInfo)); + + // We potentially changed the view configuration so we need to update the viewConfigurationViewVector + XRC_CHECK_THROW_XRCMD(doTwoCallInPlaceWithEmptyElement(viewConfigurationViewVector, {XR_TYPE_VIEW_CONFIGURATION_VIEW}, + xrEnumerateViewConfigurationViews, instance, systemId, + viewConfigurationType)); } AutoBasicSession::~AutoBasicSession() @@ -626,9 +631,11 @@ namespace Conformance actionSet = XR_NULL_HANDLE; // Let parent session destroy this. actionVector.clear(); // Let parent session destroy this. spaceTypeVector.clear(); // Let parent session destroy this. - viewConfigurationTypeVector.clear(); + spaceVector.clear(); viewConfigurationViewVector.clear(); environmentBlendModeVector.clear(); + viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM; + sessionState = XR_SESSION_STATE_UNKNOWN; if (session != XR_NULL_HANDLE) { xrDestroySession(session); @@ -675,7 +682,6 @@ namespace Conformance XRC_CHECK_THROW(autoBasicSession); XRC_CHECK_THROW(autoBasicSession->GetInstance()); XRC_CHECK_THROW(autoBasicSession->GetSession()); - XRC_CHECK_THROW(!autoBasicSession->viewConfigurationTypeVector.empty()); XRC_CHECK_THROW(!autoBasicSession->environmentBlendModeVector.empty()); } @@ -762,7 +768,7 @@ namespace Conformance return RunResult::Error; XrViewLocateInfo viewLocateInfo{XR_TYPE_VIEW_LOCATE_INFO}; - viewLocateInfo.viewConfigurationType = GetGlobalData().options.viewConfigurationValue; + viewLocateInfo.viewConfigurationType = autoBasicSession->viewConfigurationType; viewLocateInfo.displayTime = frameState.predictedDisplayTime; viewLocateInfo.space = autoBasicSession->spaceVector[0]; XrViewState viewState{XR_TYPE_VIEW_STATE}; @@ -906,7 +912,7 @@ namespace Conformance GlobalData& globalData = GetGlobalData(); XrSessionBeginInfo sessionBeginInfo{ XR_TYPE_SESSION_BEGIN_INFO, globalData.GetPlatformPlugin()->PopulateNextFieldForStruct(XR_TYPE_SESSION_BEGIN_INFO), - globalData.options.viewConfigurationValue}; + autoBasicSession->viewConfigurationType}; REQUIRE(xrBeginSession(autoBasicSession->GetSession(), &sessionBeginInfo) == XR_SUCCESS); } diff --git a/src/conformance/framework/conformance_utils.h b/src/conformance/framework/conformance_utils.h index 43340daf..1f4d6d06 100644 --- a/src/conformance/framework/conformance_utils.h +++ b/src/conformance/framework/conformance_utils.h @@ -582,15 +582,17 @@ namespace Conformance }; /// If instance is valid then we inherit it instead of create one ourselves. - AutoBasicSession(int optionFlags = 0, XrInstance instance = XR_NULL_HANDLE); + AutoBasicSession(int optionFlags = 0, XrInstance instance = XR_NULL_HANDLE, + XrViewConfigurationType viewConfigType_ = XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM); /// Calls Shutdown if not shut down already. ~AutoBasicSession(); /// If instance is valid then we inherit it instead of create one ourselves. - void Init(int optionFlags, XrInstance instance = XR_NULL_HANDLE); + void Init(int optionFlags, XrInstance instance = XR_NULL_HANDLE, + XrViewConfigurationType viewConfigType_ = XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM); - /// Begin the session. + /// Begin the session with specific view configuration type void BeginSession(); /// Restores the class instance to a pre-initialized state. @@ -666,9 +668,9 @@ namespace Conformance // Enumerated types. std::vector swapchainFormatVector; std::vector spaceTypeVector; - std::vector viewConfigurationTypeVector; std::vector viewConfigurationViewVector; std::vector environmentBlendModeVector; + XrViewConfigurationType viewConfigurationType{XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM}; }; /// Output operator for the `XrSession` handle in a @ref AutoBasicSession @@ -685,17 +687,13 @@ namespace Conformance /// Identifies conformance-related information about individual OpenXR functions. struct FunctionInfo { - PFN_xrVoidFunction functionPtr; bool nullInstanceOk; const char* requiredExtension; std::vector validResults; - FunctionInfo(PFN_xrVoidFunction functionPtr = nullptr, bool nullInstanceOk = false, const char* requiredExtension = nullptr, + FunctionInfo(bool nullInstanceOk = false, const char* requiredExtension = nullptr, std::vector validResults = std::vector()) - : functionPtr(functionPtr) - , nullInstanceOk(nullInstanceOk) - , requiredExtension(requiredExtension) - , validResults(std::move(validResults)) + : nullInstanceOk(nullInstanceOk), requiredExtension(requiredExtension), validResults(std::move(validResults)) { } }; diff --git a/src/conformance/framework/cts_tinygltf.cpp b/src/conformance/framework/cts_tinygltf.cpp index 6150d106..8e893f7e 100644 --- a/src/conformance/framework/cts_tinygltf.cpp +++ b/src/conformance/framework/cts_tinygltf.cpp @@ -9,9 +9,4 @@ #pragma warning(disable : 4189) // local variable is initialized but not referenced #endif // defined(_MSC_VER) -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic ignored "-Wunused-variable" -#pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" -#endif - #include "cts_tinygltf.h" diff --git a/src/conformance/framework/graphics_plugin_metal.cpp b/src/conformance/framework/graphics_plugin_metal.cpp index 4f261f03..ae18e64d 100644 --- a/src/conformance/framework/graphics_plugin_metal.cpp +++ b/src/conformance/framework/graphics_plugin_metal.cpp @@ -427,76 +427,78 @@ namespace Conformance static const SwapchainFormatDataMap& GetSwapchainFormatData() { + using namespace SwapchainFormat; + // Add SwapchainCreateTestParameters for other Vulkan formats if they are supported by a runtime static SwapchainFormatDataMap map{{ - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Unorm_sRGB).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGRA8Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGRA8Unorm_sRGB).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Unorm).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Unorm_sRGB).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGRA8Unorm).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGRA8Unorm_sRGB).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Unorm_sRGB).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Unorm).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Unorm_sRGB).rg().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Unorm_sRGB).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Unorm).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Unorm_sRGB).r().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Snorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Snorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Snorm).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Snorm).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Snorm).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Snorm).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Sint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Uint).r().Int(ColorIntegerRange::u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Uint).rg().Int(ColorIntegerRange::u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Uint).rgba().Int(ColorIntegerRange::u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR8Sint).r().Int(ColorIntegerRange::s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG8Sint).rg().Int(ColorIntegerRange::s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA8Sint).rgba().Int(ColorIntegerRange::s8).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Snorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Snorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Snorm).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Unorm).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Unorm).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Unorm).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Snorm).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Snorm).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Snorm).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Uint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Uint).r().Int(ColorIntegerRange::u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Uint).rg().Int(ColorIntegerRange::u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Uint).rgba().Int(ColorIntegerRange::u16).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Sint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Sint).r().Int(ColorIntegerRange::s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Sint).rg().Int(ColorIntegerRange::s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Sint).rgba().Int(ColorIntegerRange::s16).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Float).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR16Float).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG16Float).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Float).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Sint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Sint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Sint).r().Int(ColorIntegerRange::s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Sint).rg().Int(ColorIntegerRange::s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Sint).rgba().Int(ColorIntegerRange::s32).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Uint).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Uint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Uint).r().Int(ColorIntegerRange::u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Uint).rg().Int(ColorIntegerRange::u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Uint).rgba().Int(ColorIntegerRange::u32).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Float).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatR32Float).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG32Float).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA32Float).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatB5G6R5Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatA1BGR5Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGR5A1Unorm).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatB5G6R5Unorm).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatA1BGR5Unorm).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGR5A1Unorm).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatABGR4Unorm).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatABGR4Unorm).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB10A2Unorm).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGR10A2Unorm).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB10A2Unorm).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBGR10A2Unorm).rgba().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB10A2Uint).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB10A2Uint).rgba().Int(ColorIntegerRange::uRGB10A2).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG11B10Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB9E5Float).ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Float).ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRG11B10Float).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGB9E5Float).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatRGBA16Float).rgba().ToPair(), XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatDepth16Unorm).Depth().ToPair(), XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatDepth24Unorm_Stencil8).DepthStencil().ToPair(), @@ -504,77 +506,77 @@ namespace Conformance XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatDepth32Float).Depth().ToPair(), XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatDepth32Float_Stencil8).DepthStencil().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8A1).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8A1_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_R11Unorm).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RG11Unorm).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_R11Snorm).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RG11Snorm).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RGBA8).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RGBA8_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_sRGB).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_LDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_LDR).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_HDR).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_HDR).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC1_RGBA).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC1_RGBA_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC2_RGBA).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC2_RGBA_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC3_RGBA).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC3_RGBA_sRGB).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC6H_RGBFloat).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC6H_RGBUfloat).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC7_RGBAUnorm).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC7_RGBAUnorm_sRGB).Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8A1).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8_sRGB).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatETC2_RGB8A1_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_R11Unorm).r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RG11Unorm).rg().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_R11Snorm).r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RG11Snorm).rg().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RGBA8).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatEAC_RGBA8_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_sRGB).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_LDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_LDR).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_4x4_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x4_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_5x5_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x5_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_6x6_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x5_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x6_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_8x8_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x5_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x6_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_10x10_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x10_HDR).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatASTC_12x12_HDR).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC1_RGBA).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC1_RGBA_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC2_RGBA).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC2_RGBA_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC3_RGBA).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC3_RGBA_sRGB).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC6H_RGBFloat).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC6H_RGBUfloat).rgb().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC7_RGBAUnorm).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(MTL::PixelFormatBC7_RGBAUnorm_sRGB).rgba().Compressed().ToPair(), }}; return map; } @@ -941,36 +943,35 @@ namespace Conformance using NS::StringEncoding::UTF8StringEncoding; const char* shaderSrc = R"( - #include +#include using namespace metal; - struct VertexBuffer { - float4 position; - float4 color; - }; + struct VertexBuffer + { + float4 position; + float4 color; + }; - struct v2f - { - float4 position [[position]]; - half4 color; - }; + struct v2f + { + float4 position [[position]]; + half4 color; + }; - v2f vertex vertexMain( uint vertexId [[vertex_id]], - uint instanceId [[instance_id]], - device const VertexBuffer* vertexBuffer [[buffer(0)]], - device const float4x4* matricesBuffer [[buffer(1)]] ) - { - v2f o; - float4 pos = vertexBuffer[vertexId].position; - o.position = matricesBuffer[instanceId] * pos; - o.color = half4(vertexBuffer[vertexId].color); - return o; - } + v2f vertex vertexMain(uint vertexId [[vertex_id]], uint instanceId [[instance_id]], + device const VertexBuffer* vertexBuffer [[buffer(0)]], device const float4x4* matricesBuffer [[buffer(1)]]) + { + v2f o; + float4 pos = vertexBuffer[vertexId].position; + o.position = matricesBuffer[instanceId] * pos; + o.color = half4(vertexBuffer[vertexId].color); + return o; + } - half4 fragment fragmentMain( v2f in [[stage_in]] ) - { - return in.color; - } + half4 fragment fragmentMain(v2f in [[stage_in]]) + { + return in.color; + } )"; NS::Error* pError = nullptr; diff --git a/src/conformance/framework/graphics_plugin_opengl.cpp b/src/conformance/framework/graphics_plugin_opengl.cpp index 5a510f10..95f4d5d2 100644 --- a/src/conformance/framework/graphics_plugin_opengl.cpp +++ b/src/conformance/framework/graphics_plugin_opengl.cpp @@ -93,54 +93,56 @@ namespace Conformance #define WORKAROUND NotMutable() static const SwapchainFormatDataMap& GetSwapchainFormatData() { + using namespace SwapchainFormat; + static SwapchainFormatDataMap map{ - XRC_SWAPCHAIN_FORMAT(GL_RGBA8).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA16).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2).WORKAROUND.ToPair(), - - XRC_SWAPCHAIN_FORMAT(GL_R8).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R16).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG8).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG16).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R16F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG16F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGB16F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA16F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R32F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG32F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA32F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R11F_G11F_B10F).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R8_SNORM).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG8_SNORM).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGB8_SNORM).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA8_SNORM).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R8I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R8UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R16I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R16UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R32I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_R32UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG8I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG8UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG16I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG16UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG32I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RG32UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA8I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA8UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA16I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA16UI).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA32I).WORKAROUND.ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGBA32UI).WORKAROUND.ToPair(), - - XRC_SWAPCHAIN_FORMAT(GL_RGBA4).WORKAROUND.NotMutable().NoUnorderedAccess().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_RGB5_A1).WORKAROUND.NotMutable().NoUnorderedAccess().ToPair(), - - XRC_SWAPCHAIN_FORMAT(GL_SRGB8).WORKAROUND.NoUnorderedAccess().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_SRGB8_ALPHA8).WORKAROUND.NoUnorderedAccess().ToPair(), - - XRC_SWAPCHAIN_FORMAT(GL_RGB565).WORKAROUND.NotMutable().NoUnorderedAccess().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA8).WORKAROUND.rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA16).WORKAROUND.rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2).WORKAROUND.rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(GL_R8).WORKAROUND.r().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R16).WORKAROUND.r().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG8).WORKAROUND.rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG16).WORKAROUND.rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2UI).WORKAROUND.rgba().Int(uRGB10A2).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R16F).WORKAROUND.r().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG16F).WORKAROUND.rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGB16F).WORKAROUND.rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA16F).WORKAROUND.rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R32F).WORKAROUND.r().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG32F).WORKAROUND.rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA32F).WORKAROUND.rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R11F_G11F_B10F).WORKAROUND.rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R8_SNORM).WORKAROUND.r().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG8_SNORM).WORKAROUND.rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGB8_SNORM).WORKAROUND.rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA8_SNORM).WORKAROUND.rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R8I).WORKAROUND.r().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R8UI).WORKAROUND.r().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R16I).WORKAROUND.r().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R16UI).WORKAROUND.r().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R32I).WORKAROUND.r().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_R32UI).WORKAROUND.r().Int(u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG8I).WORKAROUND.rg().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG8UI).WORKAROUND.rg().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG16I).WORKAROUND.rg().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG16UI).WORKAROUND.rg().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG32I).WORKAROUND.rg().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RG32UI).WORKAROUND.rg().Int(u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA8I).WORKAROUND.rgba().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA8UI).WORKAROUND.rgba().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA16I).WORKAROUND.rgba().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA16UI).WORKAROUND.rgba().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA32I).WORKAROUND.rgba().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGBA32UI).WORKAROUND.rgba().Int(u32).ToPair(), + + XRC_SWAPCHAIN_FORMAT(GL_RGBA4).WORKAROUND.rgba().NotMutable().NoUnorderedAccess().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_RGB5_A1).WORKAROUND.rgba().NotMutable().NoUnorderedAccess().ToPair(), + + XRC_SWAPCHAIN_FORMAT(GL_SRGB8).WORKAROUND.rgb().NoUnorderedAccess().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_SRGB8_ALPHA8).WORKAROUND.rgba().NoUnorderedAccess().ToPair(), + + XRC_SWAPCHAIN_FORMAT(GL_RGB565).WORKAROUND.rgb().NotMutable().NoUnorderedAccess().ToPair(), XRC_SWAPCHAIN_FORMAT(GL_DEPTH_COMPONENT16).WORKAROUND.Depth().ToPair(), XRC_SWAPCHAIN_FORMAT(GL_DEPTH_COMPONENT24).WORKAROUND.Depth().ToPair(), @@ -150,25 +152,25 @@ namespace Conformance XRC_SWAPCHAIN_FORMAT(GL_DEPTH32F_STENCIL8).WORKAROUND.DepthStencil().ToPair(), XRC_SWAPCHAIN_FORMAT(GL_STENCIL_INDEX8).WORKAROUND.Stencil().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RED_RGTC1).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RED_RGTC1).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG_RGTC2).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG_RGTC2).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_BPTC_UNORM).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT).WORKAROUND.Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT).WORKAROUND.Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_ETC2).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ETC2).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA8_ETC2_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_R11_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_R11_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG11_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG11_EAC).WORKAROUND.Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RED_RGTC1).WORKAROUND.r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RED_RGTC1).WORKAROUND.r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG_RGTC2).WORKAROUND.rg().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG_RGTC2).WORKAROUND.rg().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_BPTC_UNORM).WORKAROUND.rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM).WORKAROUND.rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT).WORKAROUND.rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT).WORKAROUND.rgb().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_ETC2).WORKAROUND.rgb().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ETC2).WORKAROUND.rgb().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2).WORKAROUND.rgba().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2).WORKAROUND.rgba().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA8_ETC2_EAC).WORKAROUND.rgba().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC).WORKAROUND.rgba().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_R11_EAC).WORKAROUND.r().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_R11_EAC).WORKAROUND.r().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG11_EAC).WORKAROUND.rg().Compressed().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG11_EAC).WORKAROUND.rg().Compressed().NotMutable().ToPair(), }; return map; } diff --git a/src/conformance/framework/graphics_plugin_opengles.cpp b/src/conformance/framework/graphics_plugin_opengles.cpp index 10bb7335..e6255159 100644 --- a/src/conformance/framework/graphics_plugin_opengles.cpp +++ b/src/conformance/framework/graphics_plugin_opengles.cpp @@ -620,204 +620,243 @@ namespace Conformance static const SwapchainFormatDataMap& GetSwapchainFormatData() { + using namespace SwapchainFormat; + using C = SwapchainFormat::RawColorComponents; + static SwapchainFormatDataMap map{ // // 8 bits per component // - XRC_SWAPCHAIN_FORMAT(GL_R8).WORKAROUND.ToPair(), // 1-component, 8-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RG8).WORKAROUND.ToPair(), // 2-component, 8-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB8).WORKAROUND.ToPair(), // 3-component, 8-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGBA8).WORKAROUND.ToPair(), // 4-component, 8-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_R8_SNORM).WORKAROUND.ToPair(), // 1-component, 8-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RG8_SNORM).WORKAROUND.ToPair(), // 2-component, 8-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB8_SNORM).WORKAROUND.ToPair(), // 3-component, 8-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RGBA8_SNORM).WORKAROUND.ToPair(), // 4-component, 8-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_R8UI).WORKAROUND.ToPair(), // 1-component, 8-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RG8UI).WORKAROUND.ToPair(), // 2-component, 8-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGB8UI).WORKAROUND.ToPair(), // 3-component, 8-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA8UI).WORKAROUND.ToPair(), // 4-component, 8-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_R8I).WORKAROUND.ToPair(), // 1-component, 8-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RG8I).WORKAROUND.ToPair(), // 2-component, 8-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGB8I).WORKAROUND.ToPair(), // 3-component, 8-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA8I).WORKAROUND.ToPair(), // 4-component, 8-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_SR8).WORKAROUND.ToPair(), // 1-component, 8-bit sRGB - XRC_SWAPCHAIN_FORMAT(GL_SRG8).WORKAROUND.ToPair(), // 2-component, 8-bit sRGB - XRC_SWAPCHAIN_FORMAT(GL_SRGB8).WORKAROUND.ToPair(), // 3-component, 8-bit sRGB - XRC_SWAPCHAIN_FORMAT(GL_SRGB8_ALPHA8).WORKAROUND.ToPair(), // 4-component, 8-bit sRGB + XRC_SWAPCHAIN_FORMAT(GL_R8).WORKAROUND.r().ToPair(), // 1-component, 8-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RG8).WORKAROUND.rg().ToPair(), // 2-component, 8-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB8).WORKAROUND.rgb().ToPair(), // 3-component, 8-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGBA8).WORKAROUND.rgba().ToPair(), // 4-component, 8-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_R8_SNORM).WORKAROUND.r().ToPair(), // 1-component, 8-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RG8_SNORM).WORKAROUND.rg().ToPair(), // 2-component, 8-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB8_SNORM).WORKAROUND.rgb().ToPair(), // 3-component, 8-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RGBA8_SNORM).WORKAROUND.rgba().ToPair(), // 4-component, 8-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_R8UI).WORKAROUND.r().Int(u8).ToPair(), // 1-component, 8-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RG8UI).WORKAROUND.rg().Int(u8).ToPair(), // 2-component, 8-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGB8UI).WORKAROUND.rgb().Int(u8).ToPair(), // 3-component, 8-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA8UI).WORKAROUND.rgba().Int(u8).ToPair(), // 4-component, 8-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_R8I).WORKAROUND.r().Int(s8).ToPair(), // 1-component, 8-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RG8I).WORKAROUND.rg().Int(s8).ToPair(), // 2-component, 8-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGB8I).WORKAROUND.rgb().Int(s8).ToPair(), // 3-component, 8-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA8I).WORKAROUND.rgba().Int(s8).ToPair(), // 4-component, 8-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_SR8).WORKAROUND.r().ToPair(), // 1-component, 8-bit sRGB + XRC_SWAPCHAIN_FORMAT(GL_SRG8).WORKAROUND.rg().ToPair(), // 2-component, 8-bit sRGB + XRC_SWAPCHAIN_FORMAT(GL_SRGB8).WORKAROUND.rgb().ToPair(), // 3-component, 8-bit sRGB + XRC_SWAPCHAIN_FORMAT(GL_SRGB8_ALPHA8).WORKAROUND.rgba().ToPair(), // 4-component, 8-bit sRGB // // 16 bits per component // - XRC_SWAPCHAIN_FORMAT(GL_R16).WORKAROUND.ToPair(), // 1-component, 16-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RG16).WORKAROUND.ToPair(), // 2-component, 16-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB16).WORKAROUND.ToPair(), // 3-component, 16-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGBA16).WORKAROUND.ToPair(), // 4-component, 16-bit unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_R16_SNORM).WORKAROUND.ToPair(), // 1-component, 16-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RG16_SNORM).WORKAROUND.ToPair(), // 2-component, 16-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB16_SNORM).WORKAROUND.ToPair(), // 3-component, 16-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_RGBA16_SNORM).WORKAROUND.ToPair(), // 4-component, 16-bit signed normalized - XRC_SWAPCHAIN_FORMAT(GL_R16UI).WORKAROUND.ToPair(), // 1-component, 16-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RG16UI).WORKAROUND.ToPair(), // 2-component, 16-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGB16UI).WORKAROUND.ToPair(), // 3-component, 16-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA16UI).WORKAROUND.ToPair(), // 4-component, 16-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_R16I).WORKAROUND.ToPair(), // 1-component, 16-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RG16I).WORKAROUND.ToPair(), // 2-component, 16-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGB16I).WORKAROUND.ToPair(), // 3-component, 16-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA16I).WORKAROUND.ToPair(), // 4-component, 16-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_R16F).WORKAROUND.ToPair(), // 1-component, 16-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RG16F).WORKAROUND.ToPair(), // 2-component, 16-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RGB16F).WORKAROUND.ToPair(), // 3-component, 16-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RGBA16F).WORKAROUND.ToPair(), // 4-component, 16-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_R16).WORKAROUND.r().ToPair(), // 1-component, 16-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RG16).WORKAROUND.rg().ToPair(), // 2-component, 16-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB16).WORKAROUND.rgb().ToPair(), // 3-component, 16-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGBA16).WORKAROUND.rgba().ToPair(), // 4-component, 16-bit unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_R16_SNORM).WORKAROUND.r().ToPair(), // 1-component, 16-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RG16_SNORM).WORKAROUND.rg().ToPair(), // 2-component, 16-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB16_SNORM).WORKAROUND.rgb().ToPair(), // 3-component, 16-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_RGBA16_SNORM).WORKAROUND.rgba().ToPair(), // 4-component, 16-bit signed normalized + XRC_SWAPCHAIN_FORMAT(GL_R16UI).WORKAROUND.r().Int(u16).ToPair(), // 1-component, 16-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RG16UI).WORKAROUND.rg().Int(u16).ToPair(), // 2-component, 16-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGB16UI).WORKAROUND.rgb().Int(u16).ToPair(), // 3-component, 16-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA16UI).WORKAROUND.rgba().Int(u16).ToPair(), // 4-component, 16-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_R16I).WORKAROUND.r().Int(s16).ToPair(), // 1-component, 16-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RG16I).WORKAROUND.rg().Int(s16).ToPair(), // 2-component, 16-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGB16I).WORKAROUND.rgb().Int(s16).ToPair(), // 3-component, 16-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA16I).WORKAROUND.rgba().Int(s16).ToPair(), // 4-component, 16-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_R16F).WORKAROUND.r().ToPair(), // 1-component, 16-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RG16F).WORKAROUND.rg().ToPair(), // 2-component, 16-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGB16F).WORKAROUND.rgb().ToPair(), // 3-component, 16-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGBA16F).WORKAROUND.rgba().ToPair(), // 4-component, 16-bit floating-point // // 32 bits per component // - XRC_SWAPCHAIN_FORMAT(GL_R32UI).WORKAROUND.ToPair(), // 1-component, 32-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RG32UI).WORKAROUND.ToPair(), // 2-component, 32-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGB32UI).WORKAROUND.ToPair(), // 3-component, 32-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA32UI).WORKAROUND.ToPair(), // 4-component, 32-bit unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_R32I).WORKAROUND.ToPair(), // 1-component, 32-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RG32I).WORKAROUND.ToPair(), // 2-component, 32-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGB32I).WORKAROUND.ToPair(), // 3-component, 32-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_RGBA32I).WORKAROUND.ToPair(), // 4-component, 32-bit signed integer - XRC_SWAPCHAIN_FORMAT(GL_R32F).WORKAROUND.ToPair(), // 1-component, 32-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RG32F).WORKAROUND.ToPair(), // 2-component, 32-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RGB32F).WORKAROUND.ToPair(), // 3-component, 32-bit floating-point - XRC_SWAPCHAIN_FORMAT(GL_RGBA32F).WORKAROUND.ToPair(), // 4-component, 32-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_R32UI).WORKAROUND.r().Int(u32).ToPair(), // 1-component, 32-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RG32UI).WORKAROUND.rg().Int(u32).ToPair(), // 2-component, 32-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGB32UI).WORKAROUND.rgb().Int(u32).ToPair(), // 3-component, 32-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA32UI).WORKAROUND.rgba().Int(u32).ToPair(), // 4-component, 32-bit unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_R32I).WORKAROUND.r().Int(s32).ToPair(), // 1-component, 32-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RG32I).WORKAROUND.rg().Int(s32).ToPair(), // 2-component, 32-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGB32I).WORKAROUND.rgb().Int(s32).ToPair(), // 3-component, 32-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_RGBA32I).WORKAROUND.rgba().Int(s32).ToPair(), // 4-component, 32-bit signed integer + XRC_SWAPCHAIN_FORMAT(GL_R32F).WORKAROUND.r().ToPair(), // 1-component, 32-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RG32F).WORKAROUND.rg().ToPair(), // 2-component, 32-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGB32F).WORKAROUND.rgb().ToPair(), // 3-component, 32-bit floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGBA32F).WORKAROUND.rgba().ToPair(), // 4-component, 32-bit floating-point // // Packed // - XRC_SWAPCHAIN_FORMAT(GL_RGB5).WORKAROUND.ToPair(), // 3-component 5:5:5, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB565).WORKAROUND.ToPair(), // 3-component 5:6:5, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB10).WORKAROUND.ToPair(), // 3-component 10:10:10, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGBA4).WORKAROUND.ToPair(), // 4-component 4:4:4:4, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB5_A1).WORKAROUND.ToPair(), // 4-component 5:5:5:1, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2).WORKAROUND.ToPair(), // 4-component 10:10:10:2, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2UI).WORKAROUND.ToPair(), // 4-component 10:10:10:2, unsigned integer - XRC_SWAPCHAIN_FORMAT(GL_R11F_G11F_B10F).WORKAROUND.ToPair(), // 3-component 11:11:10, floating-point - XRC_SWAPCHAIN_FORMAT(GL_RGB9_E5).WORKAROUND.ToPair(), // 3-component/exp 9:9:9/5, floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGB5).WORKAROUND.rgb().ToPair(), // 3-component 5:5:5, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB565).WORKAROUND.rgb().ToPair(), // 3-component 5:6:5, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB10).WORKAROUND.rgb().ToPair(), // 3-component 10:10:10, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGBA4).WORKAROUND.rgba().ToPair(), // 4-component 4:4:4:4, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB5_A1).WORKAROUND.rgba().ToPair(), // 4-component 5:5:5:1, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2).WORKAROUND.rgba().ToPair(), // 4-component 10:10:10:2, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_RGB10_A2UI).WORKAROUND.rgba().Int(uRGB10A2).ToPair(), // 4-component 10:10:10:2, unsigned integer + XRC_SWAPCHAIN_FORMAT(GL_R11F_G11F_B10F).WORKAROUND.rgb().ToPair(), // 3-component 11:11:10, floating-point + XRC_SWAPCHAIN_FORMAT(GL_RGB9_E5).WORKAROUND.rgb().ToPair(), // 3-component/exp 9:9:9/5, floating-point // // S3TC/DXT/BC // XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_S3TC_DXT1_EXT) + .rgb() .Compressed() .NotMutable() .ToPair(), // line through 3D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_S3TC_DXT1_EXT) + .rgb() .Compressed() .NotMutable() .ToPair(), // line through 3D space, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus line through 1D space, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) + .rgba() .Compressed() .NotMutable() .ToPair(), // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_LUMINANCE_LATC1_EXT) + .r() .Compressed() .NotMutable() .ToPair(), // line through 1D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) + .Color(C(r | a)) .Compressed() .NotMutable() .ToPair(), // two lines through 1D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT) + .r() .Compressed() .NotMutable() .ToPair(), // line through 1D space, 4x4 blocks, signed normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT) + .Color(C(r | a)) .Compressed() .NotMutable() .ToPair(), // two lines through 1D space, 4x4 blocks, signed normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RED_RGTC1) + .r() .Compressed() .NotMutable() .ToPair(), // line through 1D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG_RGTC2) + .rg() .Compressed() .NotMutable() .ToPair(), // two lines through 1D space, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RED_RGTC1) + .r() .Compressed() .NotMutable() .ToPair(), // line through 1D space, 4x4 blocks, signed normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG_RGTC2) + .rg() .Compressed() .NotMutable() .ToPair(), // two lines through 1D space, 4x4 blocks, signed normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT) + .rgb() .Compressed() .NotMutable() .ToPair(), // 3-component, 4x4 blocks, unsigned floating-point XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT) + .rgb() .Compressed() .NotMutable() .ToPair(), // 3-component, 4x4 blocks, signed floating-point XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_BPTC_UNORM) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component, 4x4 blocks, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM).Compressed().NotMutable().ToPair(), // 4-component, 4x4 blocks, sRGB + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM) + .rgba() + .Compressed() + .NotMutable() + .ToPair(), // 4-component, 4x4 blocks, sRGB // // ETC // - XRC_SWAPCHAIN_FORMAT(GL_ETC1_RGB8_OES).Compressed().NotMutable().ToPair(), // 3-component ETC1, 4x4 blocks, unsigned normalized + XRC_SWAPCHAIN_FORMAT(GL_ETC1_RGB8_OES) + .rgb() + .Compressed() + .NotMutable() + .ToPair(), // 3-component ETC1, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_ETC2) + .rgb() .Compressed() .NotMutable() .ToPair(), // 3-component ETC2, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA8_ETC2_EAC) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ETC2, 4x4 blocks, unsigned normalized - XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ETC2).Compressed().NotMutable().ToPair(), // 3-component ETC2, 4x4 blocks, sRGB + XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ETC2).rgb().Compressed().NotMutable().ToPair(), // 3-component ETC2, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ETC2, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_R11_EAC) + .r() .Compressed() .NotMutable() .ToPair(), // 1-component ETC, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RG11_EAC) + .rg() .Compressed() .NotMutable() .ToPair(), // 2-component ETC, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_R11_EAC) + .r() .Compressed() .NotMutable() .ToPair(), // 1-component ETC, 4x4 blocks, signed normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SIGNED_RG11_EAC) + .rg() .Compressed() .NotMutable() .ToPair(), // 2-component ETC, 4x4 blocks, signed normalized @@ -826,114 +865,142 @@ namespace Conformance // ASTC // XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_4x4_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 4x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_5x4_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 5x4 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_5x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 5x5 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_6x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 6x5 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_6x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 6x6 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_8x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x5 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_8x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x6 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_8x8_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x8 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_10x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x5 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_10x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x6 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_10x8_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x8 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_10x10_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x10 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_12x10_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 12x10 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_RGBA_ASTC_12x12_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 12x12 blocks, unsigned normalized XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 4x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 5x4 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 5x5 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 6x5 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 6x6 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x5 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x6 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 8x8 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x5 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x6 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x8 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 10x10 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 12x10 blocks, sRGB XRC_SWAPCHAIN_FORMAT(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR) + .rgba() .Compressed() .NotMutable() .ToPair(), // 4-component ASTC, 12x12 blocks, sRGB diff --git a/src/conformance/framework/graphics_plugin_vulkan.cpp b/src/conformance/framework/graphics_plugin_vulkan.cpp index 987a5c68..657abf20 100644 --- a/src/conformance/framework/graphics_plugin_vulkan.cpp +++ b/src/conformance/framework/graphics_plugin_vulkan.cpp @@ -1429,97 +1429,98 @@ namespace Conformance static const SwapchainFormatDataMap& GetSwapchainFormatData() { + using namespace SwapchainFormat; // Add SwapchainCreateTestParameters for other Vulkan formats if they are supported by a runtime static SwapchainFormatDataMap map{{ - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SRGB).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8A8_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8A8_SRGB).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SRGB).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8_SRGB).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_UNORM).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UNORM).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SNORM).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SRGB).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SRGB).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_UNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_UNORM).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SNORM).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SNORM).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_UINT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SINT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SFLOAT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_SINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_SINT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_UINT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_UINT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_SFLOAT).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_SFLOAT).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R5G5B5A1_UNORM_PACK16).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R5G6B5_UNORM_PACK16).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UNORM_PACK32).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R4G4B4A4_UNORM_PACK16).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A1R5G5B5_UNORM_PACK16).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2R10G10B10_UNORM_PACK32).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2R10G10B10_UINT_PACK32).ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UNORM_PACK32).ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UINT_PACK32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_UNORM).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SRGB).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8A8_UNORM).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8A8_SRGB).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_UNORM).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SRGB).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8_UNORM).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B8G8R8_SRGB).rgb().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_UNORM).rg().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UNORM).r().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SNORM).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SNORM).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SNORM).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SNORM).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UINT).r().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_UINT).rg().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_UINT).rgb().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_UINT).rgba().Int(u8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SINT).r().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SINT).rg().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8_SINT).rgb().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_UNORM).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8B8A8_SINT).rgba().Int(s8).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8_SRGB).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R8G8_SRGB).rg().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_UNORM).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_UNORM).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_UNORM).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_UNORM).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SNORM).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SNORM).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SNORM).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SNORM).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_UINT).r().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_UINT).rg().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_UINT).rgb().Int(u16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_UINT).rgba().Int(u16).ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SINT).r().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SINT).rg().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SINT).rgb().Int(s16).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SINT).rgba().Int(s16).ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16_SFLOAT).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16_SFLOAT).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16_SFLOAT).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SFLOAT).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_SINT).r().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_SINT).rg().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_SINT).rgb().Int(s32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_SINT).rgba().Int(s32).ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_UINT).r().Int(u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_UINT).rg().Int(u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_UINT).rgb().Int(u32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_UINT).rgba().Int(u32).ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32_SFLOAT).r().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32_SFLOAT).rg().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32_SFLOAT).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R32G32B32A32_SFLOAT).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R5G5B5A1_UNORM_PACK16).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R5G6B5_UNORM_PACK16).rgb().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UNORM_PACK32).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R4G4B4A4_UNORM_PACK16).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A1R5G5B5_UNORM_PACK16).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2R10G10B10_UNORM_PACK32).rgba().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2R10G10B10_UINT_PACK32).rgba().Int(uRGB10A2).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UNORM_PACK32).rgba().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_A2B10G10R10_UINT_PACK32).rgba().Int(uRGB10A2).ToPair(), // Runtimes with D3D11 back-ends map VK_FORMAT_B10G11R11_UFLOAT_PACK32 to DXGI_FORMAT_R11G11B10_FLOAT and that format doesn't have a TYPELESS equivalent. - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B10G11R11_UFLOAT_PACK32).NotMutable().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_B10G11R11_UFLOAT_PACK32).rgb().NotMutable().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32).rgb().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SFLOAT).ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_R16G16B16A16_SFLOAT).rgba().ToPair(), XRC_SWAPCHAIN_FORMAT(VK_FORMAT_D16_UNORM).Depth().ToPair(), XRC_SWAPCHAIN_FORMAT(VK_FORMAT_D24_UNORM_S8_UINT).DepthStencil().ToPair(), @@ -1530,62 +1531,62 @@ namespace Conformance XRC_SWAPCHAIN_FORMAT(VK_FORMAT_D32_SFLOAT).Depth().ToPair(), XRC_SWAPCHAIN_FORMAT(VK_FORMAT_D32_SFLOAT_S8_UINT).DepthStencil().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11G11_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11_SNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11G11_SNORM_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_4x4_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x4_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x5_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x5_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x6_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x5_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x6_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x8_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x5_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x6_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x8_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x10_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x10_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x12_UNORM_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_4x4_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x4_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x5_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x5_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x6_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x5_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x6_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x8_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x5_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x6_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x8_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x10_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x10_SRGB_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x12_SRGB_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC1_RGBA_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC1_RGBA_SRGB_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC2_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC2_SRGB_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC3_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC3_SRGB_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC6H_UFLOAT_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC6H_SFLOAT_BLOCK).Compressed().ToPair(), - - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC7_UNORM_BLOCK).Compressed().ToPair(), - XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC7_SRGB_BLOCK).Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11_UNORM_BLOCK).r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11G11_UNORM_BLOCK).rg().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11_SNORM_BLOCK).r().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_EAC_R11G11_SNORM_BLOCK).rg().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_4x4_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x4_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x5_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x5_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x6_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x5_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x6_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x8_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x5_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x6_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x8_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x10_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x10_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x12_UNORM_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_4x4_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x4_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_5x5_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x5_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_6x6_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x5_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x6_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_8x8_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x5_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x6_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x8_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_10x10_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x10_SRGB_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_ASTC_12x12_SRGB_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC1_RGBA_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC1_RGBA_SRGB_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC2_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC2_SRGB_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC3_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC3_SRGB_BLOCK).rgba().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC6H_UFLOAT_BLOCK).rgb().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC6H_SFLOAT_BLOCK).rgb().Compressed().ToPair(), + + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC7_UNORM_BLOCK).rgba().Compressed().ToPair(), + XRC_SWAPCHAIN_FORMAT(VK_FORMAT_BC7_SRGB_BLOCK).rgba().Compressed().ToPair(), }}; return map; } diff --git a/src/conformance/framework/input_testinputdevice.cpp b/src/conformance/framework/input_testinputdevice.cpp index 6aabb4e1..0ea05a63 100644 --- a/src/conformance/framework/input_testinputdevice.cpp +++ b/src/conformance/framework/input_testinputdevice.cpp @@ -76,6 +76,7 @@ namespace Conformance m_actionSet = actionSet; m_actionMap = actionMap; m_firstBooleanAction = firstBooleanAction; // will be used for testing active controller + m_firstTrackerAction = XR_NULL_HANDLE; // will be used for testing active controller if m_firstBooleanAction is not set m_shouldDestroyActionSet = false; // actions and action sets are handled by the test, so do not destroy } @@ -129,6 +130,9 @@ namespace Conformance if (m_firstBooleanAction == XR_NULL_PATH && inputSourceData.Type == XR_ACTION_TYPE_BOOLEAN_INPUT) { m_firstBooleanAction = action; } + if (m_firstTrackerAction == XR_NULL_PATH && inputSourceData.Type == XR_ACTION_TYPE_POSE_INPUT) { + m_firstTrackerAction = action; + } const XrPath bindingPath = StringToPath(instance, inputSourceData.Path); m_actionMap.insert({bindingPath, action}); @@ -182,6 +186,10 @@ namespace Conformance XrActionSet detectionActionSet{waitCondition.detectionActionSet}; XrAction detectionBoolAction{waitCondition.detectionBoolAction}; + // The selected controller has no boolean actions so... + XRC_CHECK_THROW(detectionBoolAction != XR_NULL_HANDLE || m_firstBooleanAction != XR_NULL_HANDLE || + m_firstTrackerAction != XR_NULL_HANDLE); + // Checks the isActive on a boolean action to determine if a controller is on auto findController = [&]() -> ControllerState { XrActiveActionSet activeActionSets[] = { @@ -201,15 +209,28 @@ namespace Conformance } } - XrActionStateBoolean booleanActionData{XR_TYPE_ACTION_STATE_BOOLEAN}; - XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; - getInfo.action = detectionBoolAction == XR_NULL_HANDLE ? m_firstBooleanAction : detectionBoolAction; - const XrResult res = xrGetActionStateBoolean(m_session, &getInfo, &booleanActionData); - if (res != XR_SUCCESS) { - XRC_THROW_XRRESULT(res, xrGetActionStateBoolean); + if (detectionBoolAction != XR_NULL_HANDLE || m_firstBooleanAction != XR_NULL_HANDLE) { + XrActionStateBoolean booleanActionData{XR_TYPE_ACTION_STATE_BOOLEAN}; + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = detectionBoolAction != XR_NULL_HANDLE ? detectionBoolAction : m_firstBooleanAction; + const XrResult res = xrGetActionStateBoolean(m_session, &getInfo, &booleanActionData); + if (res != XR_SUCCESS) { + XRC_THROW_XRRESULT(res, xrGetActionStateBoolean); + } + + return booleanActionData.isActive ? ControllerState::Active : ControllerState::Inactive; } + else { + XrActionStatePose trackerActionData{XR_TYPE_ACTION_STATE_POSE}; + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = m_firstTrackerAction; + const XrResult res = xrGetActionStatePose(m_session, &getInfo, &trackerActionData); + if (res != XR_SUCCESS) { + XRC_THROW_XRRESULT(res, xrGetActionStatePose); + } - return booleanActionData.isActive ? ControllerState::Active : ControllerState::Inactive; + return trackerActionData.isActive ? ControllerState::Active : ControllerState::Inactive; + } }; const ControllerState desiredControllerState = state ? ControllerState::Active : ControllerState::Inactive; @@ -532,6 +553,7 @@ namespace Conformance std::map m_actionMap; XrAction m_firstBooleanAction{XR_NULL_PATH}; // Used to detect controller state + XrAction m_firstTrackerAction{XR_NULL_PATH}; // Used to detect controller state bool m_shouldDestroyActionSet = true; // Don't destroy the action set if the test provided one }; diff --git a/src/conformance/framework/xml_test_environment.cpp b/src/conformance/framework/xml_test_environment.cpp index af10edf2..802076bf 100644 --- a/src/conformance/framework/xml_test_environment.cpp +++ b/src/conformance/framework/xml_test_environment.cpp @@ -31,8 +31,8 @@ namespace Conformance .writeAttribute("patch", XR_VERSION_PATCH(cr.apiVersion)) .writeText(to_hex(cr.apiVersion)); xml.scopedElement(CTS_XML_NS_PREFIX_QUALIFIER "results") - .writeAttribute("testSuccessCount", cr.testSuccessCount) - .writeAttribute("testFailureCount", cr.testFailureCount); + .writeAttribute("testSuccessCount", cr.TestSuccessCount()) + .writeAttribute("testFailureCount", cr.TestFailureCount()); if (cr.timedSubmission.IsValid()) { const auto& timing = cr.timedSubmission; using ms = std::chrono::duration; diff --git a/src/conformance/usage/Makefile b/src/conformance/usage/Makefile index 17ebd9eb..067ef105 100644 --- a/src/conformance/usage/Makefile +++ b/src/conformance/usage/Makefile @@ -85,7 +85,7 @@ reflow: ################################################ # Meta builds for releases. Also copies into approximately the right layout for the OpenXR-Registry repo. -REGISTRYOUTDIR = $(GENDIR)/out/registry-release/cts-usage +REGISTRYOUTDIR := $(GENDIR)/out/registry-release/conformance $(REGISTRYOUTDIR)/cts_usage.pdf: $(CTSUSAGEPDF) $(ECHO) "[hexapdf] $(call MAKE_RELATIVE,$@)" diff --git a/src/conformance/usage/asciidoctor-targets.mk b/src/conformance/usage/asciidoctor-targets.mk index 7c81eda0..c14319d8 100644 --- a/src/conformance/usage/asciidoctor-targets.mk +++ b/src/conformance/usage/asciidoctor-targets.mk @@ -14,7 +14,7 @@ ADOCOPTS = --doctype book \ $(ADOC_FAILURE_LEVEL) \ $(ATTRIBOPTS) -SPECREVISION = 1.1.41 +SPECREVISION = 1.1.42 ifneq (,$(strip $(RELEASE))) # No dates or internal commit hashes in release builds for reproducibility diff --git a/src/conformance/usage/selftests.adoc b/src/conformance/usage/selftests.adoc index 222f33f5..bece57e9 100644 --- a/src/conformance/usage/selftests.adoc +++ b/src/conformance/usage/selftests.adoc @@ -14,8 +14,8 @@ with tests that build on them, submission of a CTS results package does not require them. Currently, the only self-tests tagged in this way are for the PBR/glTF rendering subsystem. -They synchronously load very large, artificial test assets, originally from -the "glTF-Sample-Models" repository, to test specific details of the +They (asynchronously) load very large, artificial test assets, originally +from the "glTF-Sample-Models" repository, to test specific details of the renderer. To run the self-tests, commands similar to the following can be used: @@ -50,3 +50,60 @@ conformance_cli "[self_test][interactive]" -G opengl --apiVersion 1.1 --reporter ---- ==== +.Corresponding ADB commands to launch on Android for OpenXR 1.0 +[%collapsible] +==== +Omit any graphics API binding extensions your runtime does not support. +These commands do not match one-to-one with the desktop examples due to +different graphics API availability on Android. + +[source,sh] +---- +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin vulkan -e apiVersion 1.0 -e xmlFilename interactive_self_test_vulkan_1_0.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_vulkan_1_0.xml + + +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin vulkan2 -e apiVersion 1.0 -e xmlFilename interactive_self_test_vulkan2_1_0.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_vulkan2_1_0.xml + + +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin opengles -e apiVersion 1.0 -e xmlFilename interactive_self_test_opengles_1_0.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_opengles_1_0.xml + +---- +==== + +.Corresponding ADB commands to launch on Android for OpenXR 1.1 +[%collapsible] +==== +Omit any graphics API binding extensions your runtime does not support. +These commands do not match one-to-one with the desktop examples due to +different graphics API availability on Android. + +[source,sh] +---- +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin vulkan -e apiVersion 1.1 -e xmlFilename interactive_self_test_vulkan_1_1.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_vulkan_1_1.xml + + +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin vulkan2 -e apiVersion 1.1 -e xmlFilename interactive_self_test_vulkan2_1_1.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_vulkan2_1_1.xml + + +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[self_test][interactive]" -e graphicsPlugin opengles -e apiVersion 1.1 -e xmlFilename interactive_self_test_opengles_1_1.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_self_test_opengles_1_1.xml + +---- +==== diff --git a/src/conformance/usage/submissions_criteria.adoc b/src/conformance/usage/submissions_criteria.adoc index 415bc112..532e22e5 100644 --- a/src/conformance/usage/submissions_criteria.adoc +++ b/src/conformance/usage/submissions_criteria.adoc @@ -33,14 +33,14 @@ or [source,xml] ---- - + ---- or [source,xml] ---- - + ---- With the results of the entire run summarized in `testsuite` tag (listing @@ -59,8 +59,11 @@ the number of top level test cases): ---- -**Any error or failure when testing core or KHR extension functionality -means your runtime is not conformant.** +[IMPORTANT] +==== +Any error or failure when testing **core or KHR extension** functionality +means your runtime is **not conformant**. Any warnings **may** indicate non-conformance and **must** be explained in the submission package. +==== diff --git a/src/conformance/usage/submissions_package.adoc b/src/conformance/usage/submissions_package.adoc index 6093b7b4..cecb382a 100644 --- a/src/conformance/usage/submissions_package.adoc +++ b/src/conformance/usage/submissions_package.adoc @@ -7,10 +7,10 @@ The submission package **must** include each of the following: -. <> +. <> . The console output produced by the CTS runs above. -. <> -. <> +. <> +. <> When you submit for OpenXR 1.1 conformance, you **must** also submit for each earlier OpenXR minor version that your runtime supports in the same @@ -95,15 +95,14 @@ Can we drop this requirement? ==== Each test suite run starts by printing test configuration data, and ends by -printing a ``Report'' showing details of the runtime and environment +printing a "Report" showing details of the runtime and environment (extensions, etc) used in that run. A few tests produce console output in-between that does not show up in the result XML. It is important to have this data for the interpretation of the results. [[submissions-package-build-info]] -=== Information on the build of conformance used in generating the -results +=== Information on the build of conformance used in generating the results Files containing the result of the commands `git status` and `git log` from the CTS directory: diff --git a/src/conformance/usage/submissions_testing_steps.adoc b/src/conformance/usage/submissions_testing_steps.adoc index a58a6b6f..52d8ba12 100644 --- a/src/conformance/usage/submissions_testing_steps.adoc +++ b/src/conformance/usage/submissions_testing_steps.adoc @@ -354,6 +354,12 @@ For any supported interaction profiles that are valid for `/user/gamepad` rather than `/user/hand/left` and `/user/hand/right`, further filter the tests by specifying the tag `[gamepad]`. +To clarify, if you support providing input for all components of one of the +following interaction profiles, specify them as well. + +* `/interaction_profiles/khr/simple_controller` +* `/interaction_profiles/ext/hand_interaction_ext` +* `/interaction_profiles/ext/eye_gaze_interaction` .Example command lines for OpenXR 1.0 [%collapsible] @@ -362,9 +368,14 @@ Select the interaction profiles to test based on the preceding description. [source,sh] ---- -# Generic profile +## Generic: Simple controller conformance_cli "[actions][interactive]" -G d3d11 -I "khr/simple_controller" --apiVersion 1.0 --reporter ctsxml::out=interactive_action_simple_controller_1_0.xml +## Generic: Hand interaction (whether via hand tracking or controller) +conformance_cli "[actions][interactive]" -G d3d11 -I "ext/hand_interaction_ext" --apiVersion 1.0 --reporter ctsxml::out=interactive_action_ext_hand_interaction_interaction_1_0.xml + +## Generic: Eye gaze +conformance_cli "[actions][interactive]" -G d3d11 -I "ext/eye_gaze_interaction" --apiVersion 1.0 --reporter ctsxml::out=interactive_action_ext_eye_gaze_interaction_1_0.xml # Sample device-associated profiles conformance_cli "[actions][interactive]" -G d3d11 -I "microsoft/motion_controller" --apiVersion 1.0 --reporter ctsxml::out=interactive_action_microsoft_motion_controller_1_0.xml @@ -382,9 +393,14 @@ conformance_cli "[actions][interactive]" -G d3d11 -I "htc/vive_controller" --api Select the interaction profiles to test based on the preceding description. [source,sh] ---- -# Generic profile +## Generic: Simple controller conformance_cli "[actions][interactive]" -G d3d11 -I "khr/simple_controller" --apiVersion 1.1 --reporter ctsxml::out=interactive_action_simple_controller_1_1.xml +## Generic: Hand interaction (whether via hand tracking or controller) +conformance_cli "[actions][interactive]" -G d3d11 -I "ext/hand_interaction_ext" --apiVersion 1.1 --reporter ctsxml::out=interactive_action_ext_hand_interaction_interaction_1_1.xml + +## Generic: Eye gaze +conformance_cli "[actions][interactive]" -G d3d11 -I "ext/eye_gaze_interaction" --apiVersion 1.1 --reporter ctsxml::out=interactive_action_ext_eye_gaze_interaction_1_1.xml # Sample device-associated profiles conformance_cli "[actions][interactive]" -G d3d11 -I "microsoft/motion_controller" --apiVersion 1.1 --reporter ctsxml::out=interactive_action_microsoft_motion_controller_1_1.xml @@ -423,13 +439,27 @@ Select the interaction profiles to test based on the preceding description. [source,sh] ---- -## Generic profile +## Generic: Simple controller adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,khr/simple_controller" -e apiVersion 1.0 -e xmlFilename interactive_action_simple_controller_1_0.xml # Wait until tests complete, then retrieve results with adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_simple_controller_1_0.xml +## Generic: Hand interaction (whether via hand tracking or controller) +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,ext/hand_interaction_ext" -e apiVersion 1.0 -e xmlFilename interactive_action_ext_hand_interaction_1_0.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_ext_hand_interaction_1_0.xml + + +## Generic: Eye gaze interaction +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,ext/eye_gaze_interaction" -e apiVersion 1.0 -e xmlFilename interactive_action_ext_eye_gaze_interaction_1_0.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_ext_eye_gaze_interaction_1_0.xml + + ## Sample device-associated profile adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,oculus/touch_controller" -e apiVersion 1.0 -e xmlFilename interactive_action_oculus_touch_controller_1_0.xml @@ -451,13 +481,27 @@ adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_mi ==== [source,sh] ---- -## Generic profile +## Generic: Simple controller adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,khr/simple_controller" -e apiVersion 1.1 -e xmlFilename interactive_action_simple_controller_1_1.xml # Wait until tests complete, then retrieve results with adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_simple_controller_1_1.xml +## Generic: Hand interaction (whether via hand tracking or controller) +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,ext/hand_interaction_ext" -e apiVersion 1.1 -e xmlFilename interactive_action_ext_hand_interaction_1_1.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_ext_hand_interaction_1_1.xml + + +## Generic: Eye gaze interaction +adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,ext/eye_gaze_interaction" -e apiVersion 1.1 -e xmlFilename interactive_action_ext_eye_gaze_interaction_1_1.xml + +# Wait until tests complete, then retrieve results with +adb pull /sdcard/Android/data/org.khronos.openxr.cts/files/interactive_action_ext_eye_gaze_interaction_1_1.xml + + ## Sample device-associated profile adb shell am start-activity -S -n org.khronos.openxr.cts/android.app.NativeActivity --esa args "[actions][interactive],-I,oculus/touch_controller" -e apiVersion 1.1 -e xmlFilename interactive_action_oculus_touch_controller_1_1.xml diff --git a/src/conformance/utilities/CMakeLists.txt b/src/conformance/utilities/CMakeLists.txt index aa539975..9a5e4ea1 100644 --- a/src/conformance/utilities/CMakeLists.txt +++ b/src/conformance/utilities/CMakeLists.txt @@ -59,6 +59,7 @@ add_library( string_utils.cpp stringification.cpp swapchain_format_data.cpp + swapchain_parameters.cpp throw_helpers.cpp types_and_constants.cpp utils.cpp diff --git a/src/conformance/utilities/bitmask_to_string.h b/src/conformance/utilities/bitmask_to_string.h index 06dec935..1b69bc85 100644 --- a/src/conformance/utilities/bitmask_to_string.h +++ b/src/conformance/utilities/bitmask_to_string.h @@ -22,7 +22,8 @@ namespace Conformance _(XrSwapchainUsageFlags) \ _(XrCompositionLayerFlags) \ _(XrViewStateFlags) \ - _(XrSpaceLocationFlags) + _(XrSpaceLocationFlags) \ + _(XrInputSourceLocalizedNameFlags) namespace detail { diff --git a/src/conformance/utilities/d3d_common.cpp b/src/conformance/utilities/d3d_common.cpp index e4268e5d..82876409 100644 --- a/src/conformance/utilities/d3d_common.cpp +++ b/src/conformance/utilities/d3d_common.cpp @@ -90,141 +90,152 @@ namespace Conformance SwapchainTestMap& GetDxgiSwapchainTestMap() { + using namespace SwapchainFormat; + static SwapchainTestMap dxgiSwapchainTestMap{ - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_FLOAT).ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_UINT).ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_SINT).ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_TYPELESS).rgba().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_FLOAT).rgba().ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_UINT).rgba().Int(u32).ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32A32_SINT).rgba().Int(s32).ExpectedFormat(DXGI_FORMAT_R32G32B32A32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_FLOAT).ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_UINT).ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_SINT).ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_TYPELESS).rgb().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_FLOAT).rgb().ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_UINT).rgb().Int(u32).ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32B32_SINT).rgb().Int(s32).ExpectedFormat(DXGI_FORMAT_R32G32B32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_FLOAT).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_UINT).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_SINT).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_UNORM).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_SNORM).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_TYPELESS).rgba().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_FLOAT).rgba().ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_UINT).rgba().Int(u16).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_SINT).rgba().Int(s16).ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_UNORM).rgba().ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16B16A16_SNORM).rgba().ExpectedFormat(DXGI_FORMAT_R16G16B16A16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_FLOAT).ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_UINT).ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_SINT).ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_TYPELESS).rg().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_FLOAT).rg().ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_UINT).rg().Int(u32).ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G32_SINT).rg().Int(s32).ExpectedFormat(DXGI_FORMAT_R32G32_TYPELESS).Build(), // 32bit channel, 8bit channel, 24bit ignored. All typeless. - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G8X24_TYPELESS).Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32G8X24_TYPELESS).rg().Typeless().Build(), // 32bit float depth, 8 bit uint stencil, 24bit ignored. XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_D32_FLOAT_S8X24_UINT).ExpectedFormat(DXGI_FORMAT_R32G8X24_TYPELESS).DepthStencil().Build(), // 32bit float red, 8bit ignored, 24bit ignored. Not typeless because used parts are typed? - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS).Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS).r().Typeless().Build(), // typeless unused 32bit component, 8bit uint green, and 24bit unused. Not typeless because used parts are typed? - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_X32_TYPELESS_G8X24_UINT).ExpectedFormat(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS).Build(), - - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_UNORM).ExpectedFormat(DXGI_FORMAT_R10G10B10A2_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_UINT).ExpectedFormat(DXGI_FORMAT_R10G10B10A2_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_X32_TYPELESS_G8X24_UINT) + .Color(g) + .Int(u8) + .ExpectedFormat(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS) + .Build(), + + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_TYPELESS).rgba().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_UNORM).rgba().ExpectedFormat(DXGI_FORMAT_R10G10B10A2_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10A2_UINT) + .rgba() + .Int(uRGB10A2) + .ExpectedFormat(DXGI_FORMAT_R10G10B10A2_TYPELESS) + .Build(), // This doesn't have a typeless equivalent, so it's created as-is by the runtime. - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R11G11B10_FLOAT).NotMutable().Build(), - - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UNORM).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UINT).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_SINT).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_SNORM).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), - - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_FLOAT).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_UINT).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_SINT).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_UNORM).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_SNORM).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), - - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_FLOAT).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R11G11B10_FLOAT).rgb().NotMutable().Build(), + + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_TYPELESS).rgba().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UNORM).rgba().ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).rgba().ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_UINT).rgba().Int(u8).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_SINT).rgba().Int(s8).ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8B8A8_SNORM).rgba().ExpectedFormat(DXGI_FORMAT_R8G8B8A8_TYPELESS).Build(), + + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_TYPELESS).rg().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_FLOAT).rg().ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_UINT).rg().Int(u16).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_SINT).rg().Int(s16).ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_UNORM).rg().ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16G16_SNORM).rg().ExpectedFormat(DXGI_FORMAT_R16G16_TYPELESS).Build(), + + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_TYPELESS).r().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_FLOAT).r().ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_D32_FLOAT).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Depth().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_UINT).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_SINT).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_UINT).r().Int(u32).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R32_SINT).r().Int(s32).ExpectedFormat(DXGI_FORMAT_R32_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R24G8_TYPELESS).Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R24G8_TYPELESS).rg().Typeless().Build(), XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_D24_UNORM_S8_UINT).ExpectedFormat(DXGI_FORMAT_R24G8_TYPELESS).Depth().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R24_UNORM_X8_TYPELESS).ExpectedFormat(DXGI_FORMAT_R24G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_X24_TYPELESS_G8_UINT).ExpectedFormat(DXGI_FORMAT_R24G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R24_UNORM_X8_TYPELESS).r().ExpectedFormat(DXGI_FORMAT_R24G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_X24_TYPELESS_G8_UINT).Color(g).Int(u8).ExpectedFormat(DXGI_FORMAT_R24G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_UINT).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_SINT).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_UNORM).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_SNORM).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_TYPELESS).rg().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_UINT).rg().Int(u8).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_SINT).rg().Int(s8).ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_UNORM).rg().ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_SNORM).rg().ExpectedFormat(DXGI_FORMAT_R8G8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_FLOAT).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_TYPELESS).r().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_FLOAT).r().ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_D16_UNORM).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Depth().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_UINT).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_SINT).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_UNORM).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_SNORM).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), - - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_UINT).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_SINT).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_UNORM).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_SNORM).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_A8_UNORM).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_UINT).r().Int(u16).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_SINT).r().Int(s16).ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_UNORM).r().ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R16_SNORM).r().ExpectedFormat(DXGI_FORMAT_R16_TYPELESS).Build(), + + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_TYPELESS).r().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_UINT).r().Int(u8).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_SINT).r().Int(s8).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_UNORM).r().ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8_SNORM).r().ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_A8_UNORM).Color(a).ExpectedFormat(DXGI_FORMAT_R8_TYPELESS).Build(), // These don't have typeless equivalents, so they are created as-is by the runtime. - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R1_UNORM).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R9G9B9E5_SHAREDEXP).NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_B8G8_UNORM).NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_G8R8_G8B8_UNORM).NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R1_UNORM).r().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R9G9B9E5_SHAREDEXP).rgb().NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R8G8_B8G8_UNORM).rgb().NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_G8R8_G8B8_UNORM).rgb().NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC1_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_UNORM_SRGB).Compressed().ExpectedFormat(DXGI_FORMAT_BC1_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_TYPELESS).rgba().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_UNORM).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC1_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC1_UNORM_SRGB).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC1_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC2_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_UNORM_SRGB).Compressed().ExpectedFormat(DXGI_FORMAT_BC2_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_TYPELESS).rgba().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_UNORM).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC2_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC2_UNORM_SRGB).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC2_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC3_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_UNORM_SRGB).Compressed().ExpectedFormat(DXGI_FORMAT_BC3_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_TYPELESS).rgba().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_UNORM).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC3_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC3_UNORM_SRGB).rgba().Compressed().ExpectedFormat(DXGI_FORMAT_BC3_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC4_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_SNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC4_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_TYPELESS).r().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_UNORM).r().Compressed().ExpectedFormat(DXGI_FORMAT_BC4_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC4_SNORM).r().Compressed().ExpectedFormat(DXGI_FORMAT_BC4_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC5_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_SNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC5_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_TYPELESS).rg().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_UNORM).rg().Compressed().ExpectedFormat(DXGI_FORMAT_BC5_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC5_SNORM).rg().Compressed().ExpectedFormat(DXGI_FORMAT_BC5_TYPELESS).Build(), // These don't have typeless equivalents, so they are created as-is by the runtime. - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B5G6R5_UNORM).NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B5G5R5A1_UNORM).NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM).NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B5G6R5_UNORM).rgb().NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B5G5R5A1_UNORM).rgba().NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM).rgba().NotMutable().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_UNORM).ExpectedFormat(DXGI_FORMAT_B8G8R8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB).ExpectedFormat(DXGI_FORMAT_B8G8R8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_TYPELESS).rgba().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_UNORM).rgba().ExpectedFormat(DXGI_FORMAT_B8G8R8A8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB).rgba().ExpectedFormat(DXGI_FORMAT_B8G8R8A8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_TYPELESS).Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_UNORM).ExpectedFormat(DXGI_FORMAT_B8G8R8X8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_UNORM_SRGB).ExpectedFormat(DXGI_FORMAT_B8G8R8X8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_TYPELESS).rgb().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_UNORM).rgb().ExpectedFormat(DXGI_FORMAT_B8G8R8X8_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B8G8R8X8_UNORM_SRGB).rgb().ExpectedFormat(DXGI_FORMAT_B8G8R8X8_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_UF16).Compressed().ExpectedFormat(DXGI_FORMAT_BC6H_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_SF16).Compressed().ExpectedFormat(DXGI_FORMAT_BC6H_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_TYPELESS).rgb().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_UF16).rgb().Compressed().ExpectedFormat(DXGI_FORMAT_BC6H_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC6H_SF16).rgb().Compressed().ExpectedFormat(DXGI_FORMAT_BC6H_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_TYPELESS).Compressed().Typeless().Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_UNORM).Compressed().ExpectedFormat(DXGI_FORMAT_BC7_TYPELESS).Build(), - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_UNORM_SRGB).Compressed().ExpectedFormat(DXGI_FORMAT_BC7_TYPELESS).Build(), + // The alpha channel is optional for these + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_TYPELESS).rgb().Compressed().Typeless().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_UNORM).rgb().Compressed().ExpectedFormat(DXGI_FORMAT_BC7_TYPELESS).Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_BC7_UNORM_SRGB).rgb().Compressed().ExpectedFormat(DXGI_FORMAT_BC7_TYPELESS).Build(), // This doesn't have a typeless equivalent, so it's created as-is by the runtime. - XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B4G4R4A4_UNORM).NotMutable().Build(), + XRC_SWAPCHAIN_FORMAT(DXGI_FORMAT_B4G4R4A4_UNORM).rgba().NotMutable().Build(), }; diff --git a/src/conformance/utilities/swapchain_format_data.cpp b/src/conformance/utilities/swapchain_format_data.cpp index 49ffcb8f..59be12ae 100644 --- a/src/conformance/utilities/swapchain_format_data.cpp +++ b/src/conformance/utilities/swapchain_format_data.cpp @@ -246,6 +246,13 @@ namespace Conformance SwapchainCreateTestParameters SwapchainFormatData::ToTestParameters() const { + if (!m_colorFormat && !m_depthFormat && !m_stencilFormat) { + std::ostringstream oss; + oss << "Format " << m_imageFormatName << " (" << to_hex(m_imageFormat) << ")" + << "has no known aspect (color, depth, stencil);" + " if this is correct, code may need to be updated."; + throw std::logic_error(oss.str()); + } span mipCountVector{kArrayOf1.begin(), kArrayOf1.end()}; if (m_colorFormat && !m_compressedFormat) { @@ -253,16 +260,17 @@ namespace Conformance } span arrayCountVector{kArrayOf1And2.begin(), kArrayOf1And2.end()}; + namespace FormatFlags = SwapchainFormat::Flags; return SwapchainCreateTestParameters{ - std::string{m_imageFormatName}, // - m_isTypeless ? SwapchainFormatMutability::MUTABLE : SwapchainFormatMutability::IMMUTABLE, // - m_supportsMutableFormat ? SwapchainFormatSupportsMutability::MUT_SUPPORT - : SwapchainFormatSupportsMutability::NO_MUT_SUPPORT, // - m_colorFormat ? SwapchainFormatIsColor::COLOR : SwapchainFormatIsColor::NON_COLOR, - m_compressedFormat ? SwapchainFormatIsCompressed::COMPRESSED : SwapchainFormatIsCompressed::UNCOMPRESSED, - m_compressedFormat ? SwapchainFormatSupportsRendering::NO_RENDERING_SUPPORT - : SwapchainFormatSupportsRendering::RENDERING_SUPPORT, + std::string{m_imageFormatName}, // + m_isTypeless ? FormatFlags::Mutability::MUTABLE : FormatFlags::Mutability::IMMUTABLE, // + m_supportsMutableFormat ? FormatFlags::SupportsMutability::MUT_SUPPORT : FormatFlags::SupportsMutability::NO_MUT_SUPPORT, // + m_colorFormat ? FormatFlags::IsColor::COLOR : FormatFlags::IsColor::NON_COLOR, + m_compressedFormat ? FormatFlags::IsCompressed::COMPRESSED : FormatFlags::IsCompressed::UNCOMPRESSED, + m_compressedFormat ? FormatFlags::SupportsRendering::NO_RENDERING_SUPPORT : FormatFlags::SupportsRendering::RENDERING_SUPPORT, m_expectedCreatedImageFormat, + m_colorComponents, + m_integerRange, {m_usageFlagsVector.begin(), m_usageFlagsVector.end()}, {m_createFlagsVector.begin(), m_createFlagsVector.end()}, {arrayCountVector.begin(), arrayCountVector.end()}, diff --git a/src/conformance/utilities/swapchain_format_data.h b/src/conformance/utilities/swapchain_format_data.h index 46bac4a0..18be78a6 100644 --- a/src/conformance/utilities/swapchain_format_data.h +++ b/src/conformance/utilities/swapchain_format_data.h @@ -30,7 +30,6 @@ namespace Conformance { using nonstd::span; - /// Minimal data structure storing details about a swapchain image format. /// /// May eventually replace @ref SwapchainImageTestParam @@ -145,7 +144,10 @@ namespace Conformance bool m_supportsMutableFormat = true; /// Whether the format is a color-specific format - bool m_colorFormat = true; + bool m_colorFormat = false; + + /// Which color components the format resolves to (including alpha) + SwapchainFormat::RawColorComponents m_colorComponents = SwapchainFormat::RawColorComponents::Unknown; /// Whether the format can be use as a depth buffer: implies not color bool m_depthFormat = false; @@ -156,6 +158,9 @@ namespace Conformance /// Whether the format is a compressed format (and thus cannot be rendered to) bool m_compressedFormat = false; + /// The signed-ness and integer range of this format. + SwapchainFormat::ColorIntegerRange m_integerRange = SwapchainFormat::ColorIntegerRange::NoIntegerColor; + /// XrSwapchainUsageFlags to exercise for this format. /// Defaults to all combinations, including 0, of the core flags. /// @todo Stop making so many copies of this, generate it from the other data instead @@ -188,6 +193,60 @@ namespace Conformance using Self = SwapchainCreateTestParametersBuilder; + /// Mark this as supporting color buffer usage with the channels/components @param components + /// + /// Also sets some default usage flags. + Self& Color(SwapchainFormat::RawColorComponents components) + { + m_data.m_colorFormat = true; + m_data.m_colorComponents = components; + UpdateDefaultUsageFlagVector(); + return *this; + } + + /// Mark this as supporting color buffer usage with the channels R + /// + /// Also sets some default usage flags. + Self& r() + { + using C = SwapchainFormat::RawColorComponents; + return Color(C::r); + } + + /// Mark this as supporting color buffer usage with the channels R, G + /// + /// Also sets some default usage flags. + Self& rg() + { + using C = SwapchainFormat::RawColorComponents; + return Color((C)(C::r | C::g)); + } + + /// Mark this as supporting color buffer usage with the channels R, G, B + /// + /// Also sets some default usage flags. + Self& rgb() + { + using C = SwapchainFormat::RawColorComponents; + return Color((C)(C::r | C::g | C::b)); + } + + /// Mark this as supporting color buffer usage with the channels R, G, B, A + /// + /// Also sets some default usage flags. + Self& rgba() + { + using C = SwapchainFormat::RawColorComponents; + return Color((C)(C::r | C::g | C::b | C::a)); + } + + /// Mark this as a color integer format with the color/alpha depth specified + Self& Int(SwapchainFormat::ColorIntegerRange range) + { + m_data.m_integerRange = range; + return *this; + } + /// Mark this as not supporting "unordered access" Self& NoUnorderedAccess() { @@ -214,7 +273,7 @@ namespace Conformance Self& Depth() { m_data.m_depthFormat = true; - NotColor(); + UpdateDefaultUsageFlagVector(); return *this; } @@ -224,7 +283,7 @@ namespace Conformance Self& Stencil() { m_data.m_stencilFormat = true; - NotColor(); + UpdateDefaultUsageFlagVector(); return *this; } @@ -237,7 +296,7 @@ namespace Conformance { m_data.m_stencilFormat = true; m_data.m_depthFormat = true; - NotColor(); + UpdateDefaultUsageFlagVector(); return *this; } @@ -292,12 +351,6 @@ namespace Conformance std::string ToString() const; private: - void NotColor() - { - m_data.m_colorFormat = false; - UpdateDefaultUsageFlagVector(); - } - void UpdateDefaultUsageFlagVector(); SwapchainFormatData m_data; }; diff --git a/src/conformance/utilities/swapchain_parameters.cpp b/src/conformance/utilities/swapchain_parameters.cpp new file mode 100644 index 00000000..8f93d0df --- /dev/null +++ b/src/conformance/utilities/swapchain_parameters.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2019-2024, The Khronos Group Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "swapchain_parameters.h" + +namespace Conformance +{ + namespace SwapchainFormat + { + uint8_t ColorIntegerRangeBits(ColorIntegerRange colorIntegerRange) + { + switch (colorIntegerRange) { + case ColorIntegerRange::NoIntegerColor: + throw std::logic_error("ColorIntegerRangeBits only valid for integer colors"); + case ColorIntegerRange::u8: + case ColorIntegerRange::s8: + return 8; + case ColorIntegerRange::u16: + case ColorIntegerRange::s16: + return 16; + case ColorIntegerRange::u32: + case ColorIntegerRange::s32: + return 32; + case ColorIntegerRange::uRGB10A2: + return 10; + default: + throw std::logic_error("Missing case in implementation of ColorIntegerRangeBits"); + } + } + + bool ColorIntegerRangeIsSigned(ColorIntegerRange colorIntegerRange) + { + switch (colorIntegerRange) { + case ColorIntegerRange::NoIntegerColor: + throw std::logic_error("ColorIntegerRangeIsSigned only valid for integer colors"); + case ColorIntegerRange::u8: + case ColorIntegerRange::u16: + case ColorIntegerRange::u32: + case ColorIntegerRange::uRGB10A2: + return false; + case ColorIntegerRange::s8: + case ColorIntegerRange::s16: + case ColorIntegerRange::s32: + return true; + default: + throw std::logic_error("Missing case in implementation of ColorIntegerRangeIsSigned"); + } + } + } // namespace SwapchainFormat +} // namespace Conformance diff --git a/src/conformance/utilities/swapchain_parameters.h b/src/conformance/utilities/swapchain_parameters.h index a16db1c7..b9c61dd6 100644 --- a/src/conformance/utilities/swapchain_parameters.h +++ b/src/conformance/utilities/swapchain_parameters.h @@ -22,31 +22,66 @@ namespace Conformance { - enum SwapchainFormatMutability + namespace SwapchainFormat { - IMMUTABLE, - MUTABLE, - }; - enum SwapchainFormatSupportsMutability - { - NO_MUT_SUPPORT, - MUT_SUPPORT, - }; - enum SwapchainFormatIsColor - { - NON_COLOR, - COLOR, - }; - enum SwapchainFormatIsCompressed - { - UNCOMPRESSED, - COMPRESSED, - }; - enum SwapchainFormatSupportsRendering - { - NO_RENDERING_SUPPORT, - RENDERING_SUPPORT, - }; + namespace Flags + { + enum Mutability + { + IMMUTABLE, + MUTABLE, + }; + enum SupportsMutability + { + NO_MUT_SUPPORT, + MUT_SUPPORT, + }; + enum IsColor + { + NON_COLOR, + COLOR, + }; + enum IsCompressed + { + UNCOMPRESSED, + COMPRESSED, + }; + enum SupportsRendering + { + NO_RENDERING_SUPPORT, + RENDERING_SUPPORT, + }; + } // namespace Flags + + /// The components defined by the texture during sampling + /// (i.e. not just returned as the default value) + enum RawColorComponents : uint8_t + { + Unknown = 0, + r = 1 << 0, + g = 1 << 1, + b = 1 << 2, + a = 1 << 3, + }; + + /// Textures whose output is interpreted as an integer + /// and not mapped to a fixed- or floating-point value. + /// This does not yet account for integer aspects of + /// non-color formats, but could be renamed and extended. + enum ColorIntegerRange + { + NoIntegerColor = 0, + u8, + s8, + u16, + s16, + u32, + s32, + uRGB10A2, + }; + uint8_t ColorIntegerRangeBits(ColorIntegerRange colorIntegerRange); + bool ColorIntegerRangeIsSigned(ColorIntegerRange colorIntegerRange); + } // namespace SwapchainFormat /// Defines XrSwapchainCreateInfo test parameters for a single given image format. /// Sometimes values are zeroed, for the case that use of them is invalid or unsupportable. @@ -58,23 +93,29 @@ namespace Conformance std::string imageFormatName; /// Whether the image format is a mutable (a.k.a. typeless) type. - SwapchainFormatMutability mutableFormat; + SwapchainFormat::Flags::Mutability mutableFormat; /// Whether the image format supports creation with XR_SWAPCHAIN_USAGE_MUTABLE_FORMAT_BIT. - SwapchainFormatSupportsMutability supportsMutableFormat; + SwapchainFormat::Flags::SupportsMutability supportsMutableFormat; /// Whether the format is a color-specific format, as opposed to a depth-specific format. - SwapchainFormatIsColor colorFormat; + SwapchainFormat::Flags::IsColor colorFormat; /// Whether the format is a compressed format. - SwapchainFormatIsCompressed compressedFormat; + SwapchainFormat::Flags::IsCompressed compressedFormat; /// Whether the image format can be rendered to. - SwapchainFormatSupportsRendering supportsRendering; + SwapchainFormat::Flags::SupportsRendering supportsRendering; /// The graphics-specific created image format returned by xrCreateSwapchain, may be different from imageFormat in some cases. int64_t expectedCreatedImageFormat; + /// The color components that, when sampled, will not just be set to default values. + SwapchainFormat::RawColorComponents colorComponents; + + /// For integer (not floating point or normalised) color images, the bit depth of each color/alpha component. + SwapchainFormat::ColorIntegerRange colorIntegerRange; + /// XrSwapchainUsageFlags to exercise for this format. std::vector usageFlagsVector; diff --git a/src/external/stb/stb_image.h b/src/external/stb/stb_image.h index d60371b9..9eedabed 100644 --- a/src/external/stb/stb_image.h +++ b/src/external/stb/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,9 @@ LICENSE RECENT REVISION HISTORY: + 2.30 (2024-05-31) avoid erroneous gcc warning + 2.29 (2023-05-xx) optimizations + 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes 2.26 (2020-07-13) many minor fixes 2.25 (2020-02-02) fix warnings @@ -108,7 +111,7 @@ RECENT REVISION HISTORY: Cass Everitt Ryamond Barbiero github:grim210 Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Matthew Gregan github:poppolopoppo + Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo Julian Raschke Gregory Mullen Christian Floisand github:darealshinji Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 Brad Weinberger Matvey Cherevko github:mosra @@ -140,7 +143,7 @@ RECENT REVISION HISTORY: // // ... x = width, y = height, n = # 8-bit components per pixel ... // // ... replace '0' with '1'..'4' to force that many components per pixel // // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) +// stbi_image_free(data); // // Standard parameters: // int *x -- outputs image width in pixels @@ -635,7 +638,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #endif #endif -#ifdef _MSC_VER +#if defined(_MSC_VER) || defined(__SYMBIAN32__) typedef unsigned short stbi__uint16; typedef signed short stbi__int16; typedef unsigned int stbi__uint32; @@ -1063,6 +1066,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) } #endif +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two ints fits in a signed short, 0 on overflow. +static int stbi__mul2shorts_valid(int a, int b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char @@ -1985,9 +2005,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) int i,j,k=0; unsigned int code; // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } h->size[k] = 0; // compute actual symbols (from jpeg spec) @@ -2112,6 +2135,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); // convert the id to a symbol @@ -2130,6 +2155,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) unsigned int k; int sgn; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) k = stbi_lrot(j->code_buffer, n); @@ -2144,6 +2170,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { unsigned int k; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; @@ -2155,6 +2182,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) { unsigned int k; if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing k = j->code_buffer; j->code_buffer <<= 1; --j->code_bits; @@ -2192,8 +2220,10 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman memset(data,0,64*sizeof(data[0])); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); data[0] = (short) (dc * dequant[0]); // decode AC components, see JPEG spec @@ -2207,6 +2237,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; // decode into unzigzag'd location @@ -2246,8 +2277,10 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); data[0] = (short) (dc * (1 << j->succ_low)); } else { // refinement scan for DC coefficient @@ -2282,6 +2315,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; @@ -3102,6 +3136,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m) sizes[i] = stbi__get8(z->s); n += sizes[i]; } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! L -= 17; if (tc == 0) { if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; @@ -3351,6 +3386,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) return 1; } +static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + stbi_uc x = stbi__get8(j->s); + while (x == 0xff) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + // decode image to YCbCr format static int stbi__decode_jpeg_image(stbi__jpeg *j) { @@ -3367,25 +3424,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) if (!stbi__process_scan_header(j)) return 0; if (!stbi__parse_entropy_coded_data(j)) return 0; if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } + j->marker = stbi__skip_jpeg_junk_at_end(j); // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); } else { - if (!stbi__process_marker(j, m)) return 0; + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); } - m = stbi__get_marker(j); } if (j->progressive) stbi__jpeg_finish(j); @@ -3976,6 +4030,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); @@ -3989,6 +4044,7 @@ static int stbi__jpeg_test(stbi__context *s) int r; stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); @@ -4014,6 +4070,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) int result; stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); @@ -4121,6 +4178,7 @@ typedef struct { stbi_uc *zbuffer, *zbuffer_end; int num_bits; + int hit_zeof_once; stbi__uint32 code_buffer; char *zout; @@ -4187,9 +4245,20 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) int b,s; if (a->num_bits < 16) { if (stbi__zeof(a)) { - return -1; /* report error for unexpected end of data. */ + if (!a->hit_zeof_once) { + // This is the first time we hit eof, insert 16 extra padding btis + // to allow us to keep going; if we actually consume any of them + // though, that is invalid data. This is caught later. + a->hit_zeof_once = 1; + a->num_bits += 16; // add 16 implicit zero bits + } else { + // We already inserted our extra 16 padding bits and are again + // out, this stream is actually prematurely terminated. + return -1; + } + } else { + stbi__fill_bits(a); } - stbi__fill_bits(a); } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { @@ -4254,17 +4323,25 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) int len,dist; if (z == 256) { a->zout = zout; + if (a->hit_zeof_once && a->num_bits < 16) { + // The first time we hit zeof, we inserted 16 extra zero bits into our bit + // buffer so the decoder can just do its speculative decoding. But if we + // actually consumed any of those bits (which is the case when num_bits < 16), + // the stream actually read past the end so it is malformed. + return stbi__err("unexpected end","Corrupt PNG"); + } return 1; } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data z -= 257; len = stbi__zlength_base[z]; if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data dist = stbi__zdist_base[z]; if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { + if (len > a->zout_end - zout) { if (!stbi__zexpand(a, zout, len)) return 0; zout = a->zout; } @@ -4408,6 +4485,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) if (!stbi__parse_zlib_header(a)) return 0; a->num_bits = 0; a->code_buffer = 0; + a->hit_zeof_once = 0; do { final = stbi__zreceive(a,1); type = stbi__zreceive(a,2); @@ -4563,9 +4641,8 @@ enum { STBI__F_up=2, STBI__F_avg=3, STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first + // synthetic filter used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first }; static stbi_uc first_row_filter[5] = @@ -4574,29 +4651,56 @@ static stbi_uc first_row_filter[5] = STBI__F_sub, STBI__F_none, STBI__F_avg_first, - STBI__F_paeth_first + STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub }; static int stbi__paeth(int a, int b, int c) { - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; + // This formulation looks very different from the reference in the PNG spec, but is + // actually equivalent and has favorable data dependencies and admits straightforward + // generation of branch-free code, which helps performance significantly. + int thresh = c*3 - (a + b); + int lo = a < b ? a : b; + int hi = a < b ? b : a; + int t0 = (hi <= thresh) ? lo : c; + int t1 = (thresh <= lo) ? hi : t0; + return t1; } static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; +// adds an extra all-255 alpha channel +// dest == src is legal +// img_n must be 1 or 3 +static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) +{ + int i; + // must process data backwards since we allow dest==src + if (img_n == 1) { + for (i=x-1; i >= 0; --i) { + dest[i*2+1] = 255; + dest[i*2+0] = src[i]; + } + } else { + STBI_ASSERT(img_n == 3); + for (i=x-1; i >= 0; --i) { + dest[i*4+3] = 255; + dest[i*4+2] = src[i*3+2]; + dest[i*4+1] = src[i*3+1]; + dest[i*4+0] = src[i*3+0]; + } + } +} + // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { - int bytes = (depth == 16? 2 : 1); + int bytes = (depth == 16 ? 2 : 1); stbi__context *s = a->s; stbi__uint32 i,j,stride = x*out_n*bytes; stbi__uint32 img_len, img_width_bytes; + stbi_uc *filter_buf; + int all_ok = 1; int k; int img_n = s->img_n; // copy it into a local for later @@ -4608,8 +4712,11 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); + // note: error exits here don't need to clean up a->out individually, + // stbi__do_png always does on error. if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); + if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG"); img_len = (img_width_bytes + 1) * y; // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, @@ -4617,189 +4724,137 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r // so just check for raw_len < img_len always. if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + // Allocate two scan lines worth of filter workspace buffer. + filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0); + if (!filter_buf) return stbi__err("outofmem", "Out of memory"); + + // Filtering for low-bit-depth images + if (depth < 8) { + filter_bytes = 1; + width = img_width_bytes; + } + for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; + // cur/prior filter buffers alternate + stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes; + stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes; + stbi_uc *dest = a->out + stride*j; + int nk = width * filter_bytes; int filter = *raw++; - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; + // check filter type + if (filter > 4) { + all_ok = stbi__err("invalid filter","Corrupt PNG"); + break; } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; + // perform actual filtering + switch (filter) { + case STBI__F_none: + memcpy(cur, raw, nk); + break; + case STBI__F_sub: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); + break; + case STBI__F_up: + for (k = 0; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + case STBI__F_avg: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); + break; + case STBI__F_paeth: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes])); + break; + case STBI__F_avg_first: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); + break; } - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } + raw += nk; - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + // expand decoded bits in cur to dest, also adding an extra alpha channel if desired + if (depth < 8) { stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + stbi_uc *in = cur; + stbi_uc *out = dest; + stbi_uc inb = 0; + stbi__uint32 nsmp = x*img_n; - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - + // expand bits to bytes first if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); + for (i=0; i < nsmp; ++i) { + if ((i & 1) == 0) inb = *in++; + *out++ = scale * (inb >> 4); + inb <<= 4; } - if (k > 0) *cur++ = scale * ((*in >> 4) ); } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); + for (i=0; i < nsmp; ++i) { + if ((i & 3) == 0) inb = *in++; + *out++ = scale * (inb >> 6); + inb <<= 2; } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); + } else { + STBI_ASSERT(depth == 1); + for (i=0; i < nsmp; ++i) { + if ((i & 7) == 0) inb = *in++; + *out++ = scale * (inb >> 7); + inb <<= 1; } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; + + // insert alpha=255 values if desired + if (img_n != out_n) + stbi__create_png_alpha_expand8(dest, dest, x, img_n); + } else if (depth == 8) { + if (img_n == out_n) + memcpy(dest, cur, x*img_n); + else + stbi__create_png_alpha_expand8(dest, cur, x, img_n); + } else if (depth == 16) { + // convert the image data from big-endian to platform-native + stbi__uint16 *dest16 = (stbi__uint16*)dest; + stbi__uint32 nsmp = x*img_n; + + if (img_n == out_n) { + for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) + *dest16 = (cur[0] << 8) | cur[1]; + } else { + STBI_ASSERT(img_n+1 == out_n); if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; + for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = 0xffff; } } else { STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; + for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = (cur[2] << 8) | cur[3]; + dest16[2] = (cur[4] << 8) | cur[5]; + dest16[3] = 0xffff; } } } } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } } + STBI_FREE(filter_buf); + if (!all_ok) return 0; + return 1; } @@ -4955,7 +5010,7 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; -STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) { stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; stbi__unpremultiply_on_load_set = 1; @@ -5064,14 +5119,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!pal_img_n) { s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; } else { // if paletted, then pal_n is our final components, and // img_n is # components to decompress/filter. s->img_n = 1; if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS } + // even with SCAN_header, have to scan to see if we have a tRNS break; } @@ -5103,10 +5157,14 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning + tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + for (k = 0; k < s->img_n && k < 3; ++k) + tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger } } break; @@ -5115,7 +5173,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) case STBI__PNG_TYPE('I','D','A','T'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { stbi__uint32 idata_limit_old = idata_limit; @@ -5498,8 +5562,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req psize = (info.offset - info.extra_read - info.hsz) >> 2; } if (psize == 0) { - if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); } } @@ -7187,12 +7265,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re // Run value = stbi__get8(s); count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } @@ -7446,10 +7524,17 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } if (out == NULL) return out; // stbi__convert_format frees input on failure } return out; @@ -7486,6 +7571,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { value = value*10 + (*c - '0'); *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); } return value; @@ -7516,9 +7603,13 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) stbi__pnm_skip_whitespace(s, &c); *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value diff --git a/src/external/stb/stb_truetype.h b/src/external/stb/stb_truetype.h index bbf2284b..90a5c2e2 100644 --- a/src/external/stb/stb_truetype.h +++ b/src/external/stb/stb_truetype.h @@ -54,7 +54,7 @@ // Hou Qiming Derek Vinyard // Rob Loach Cort Stratton // Kenney Phillis Jr. Brian Costabile -// Ken Voskuil (kaesve) +// Ken Voskuil (kaesve) Yakov Galka // // VERSION HISTORY // @@ -4604,6 +4604,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc scale_y = -scale_y; { + // distance from singular values (in the same units as the pixel grid) + const float eps = 1./1024, eps2 = eps*eps; int x,y,i,j; float *precompute; stbtt_vertex *verts; @@ -4616,15 +4618,15 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); - precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + precompute[i] = (dist < eps) ? 0.0f : 1.0f / dist; } else if (verts[i].type == STBTT_vcurve) { float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; float len2 = bx*bx + by*by; - if (len2 != 0.0f) - precompute[i] = 1.0f / (bx*bx + by*by); + if (len2 >= eps2) + precompute[i] = 1.0f / len2; else precompute[i] = 0.0f; } else @@ -4689,8 +4691,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc float a = 3*(ax*bx + ay*by); float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); float c = mx*ax+my*ay; - if (a == 0.0) { // if a is 0, it's linear - if (b != 0.0) { + if (STBTT_fabs(a) < eps2) { // if a is 0, it's linear + if (STBTT_fabs(b) >= eps2) { res[num++] = -c/b; } } else { diff --git a/src/external/tinygltf/README.md b/src/external/tinygltf/README.md index 557734a1..5e706d9c 100644 --- a/src/external/tinygltf/README.md +++ b/src/external/tinygltf/README.md @@ -9,7 +9,7 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch (b ## Status Currently TinyGLTF is stable and maintenance mode. No drastic changes and feature additions planned. - + - v2.9.0 Various fixes and improvements. Filesystem callback API change. - v2.8.0 Add URICallbacks for custom URI handling in Buffer and Image. PR#397 - v2.7.0 Change WriteImageDataFunction user callback function signature. PR#393 - v2.6.0 Support serializing sparse accessor(Thanks to @fynv). @@ -26,8 +26,6 @@ Currently TinyGLTF is stable and maintenance mode. No drastic changes and featur ## Builds -[![Build Status](https://travis-ci.org/syoyo/tinygltf.svg?branch=devel)](https://travis-ci.org/syoyo/tinygltf) - [![Build status](https://ci.appveyor.com/api/projects/status/warngenu9wjjhlm8?svg=true)](https://ci.appveyor.com/project/syoyo/tinygltf) ![C/C++ CI](https://github.com/syoyo/tinygltf/workflows/C/C++%20CI/badge.svg) @@ -109,6 +107,7 @@ WASI build example is located in [wasm](wasm) . * [SanityEngine](https://github.com/DethRaid/SanityEngine) - A C++/D3D12 renderer focused on the personal and professional development of its developer * [Open3D](http://www.open3d.org/) - A Modern Library for 3D Data Processing * [Supernova Engine](https://github.com/supernovaengine/supernova) - Game engine for 2D and 3D projects with Lua or C++ in data oriented design. +* [Wicked Engine](https://github.com/turanszkij/WickedEngine) - 3D engine with modern graphics * Your projects here! (Please send PR) ## TODOs @@ -197,6 +196,7 @@ if (!ret) { * `TINYGLTF_USE_RAPIDJSON` : Use RapidJSON as a JSON parser/serializer. RapidJSON files are not included in TinyGLTF repo. Please set an include path to RapidJSON if you enable this feature. * `TINYGLTF_USE_CPP14` : Use C++14 feature(requires C++14 compiler). This may give better performance than C++11. + ## CMake options You can add tinygltf using `add_subdirectory` feature. diff --git a/src/external/tinygltf/tiny_gltf.h b/src/external/tinygltf/tiny_gltf.h index 0f69ad27..6d8c3ffb 100644 --- a/src/external/tinygltf/tiny_gltf.h +++ b/src/external/tinygltf/tiny_gltf.h @@ -25,32 +25,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -// Version: -// - v2.8.1 Missed serialization texture sampler name fixed. PR#399. -// - v2.8.0 Add URICallbacks for custom URI handling in Buffer and Image. PR#397. -// - v2.7.0 Change WriteImageDataFunction user callback function signature. PR#393. -// - v2.6.3 Fix GLB file with empty BIN chunk was not handled. PR#382 and PR#383. -// - v2.6.2 Fix out-of-bounds access of accessors. PR#379. -// - v2.6.1 Better GLB validation check when loading. -// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv). -// Disable expanding file path for security(no use of awkward `wordexp` anymore). -// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is. -// - v2.4.3 Fix null object output when material has all default -// parameters. -// - v2.4.2 Decode percent-encoded URI. -// - v2.4.1 Fix some glTF object class does not have `extensions` and/or -// `extras` property. -// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone). -// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1. -// - v2.3.0 Modified Material representation according to glTF 2.0 schema -// (and introduced TextureInfo class) -// Change the behavior of `Value::IsNumber`. It return true either the -// value is int or real. -// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks -// to @Ybalrid) -// - v2.1.0 Add draco compression. -// - v2.0.1 Add comparison feature(Thanks to @Selmar). -// - v2.0.0 glTF 2.0!. +// Version: - v2.9.* +// See https://github.com/syoyo/tinygltf/releases for release history. // // Tiny glTF loader is using following third party libraries: // @@ -67,20 +43,19 @@ #include #include #include +#include #include #include #include +#include #include -//Auto-detect C++14 standard version -#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L) +// Auto-detect C++14 standard version +#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && \ + (__cplusplus >= 201402L) #define TINYGLTF_USE_CPP14 #endif -#ifndef TINYGLTF_USE_CPP14 -#include -#endif - #ifdef __ANDROID__ #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS #include @@ -222,6 +197,11 @@ typedef enum { OBJECT_TYPE } Type; +typedef enum { + Permissive, + Strict +} ParseStrictness; + static inline int32_t GetComponentSizeInBytes(uint32_t componentType) { if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) { return 1; @@ -284,11 +264,7 @@ class Value { typedef std::vector Array; typedef std::map Object; - Value() - : type_(NULL_TYPE), - int_value_(0), - real_value_(0.0), - boolean_value_(false) {} + Value() = default; explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; } explicit Value(int i) : type_(INT_TYPE) { @@ -301,9 +277,7 @@ class Value { } explicit Value(std::string &&s) : type_(STRING_TYPE), string_value_(std::move(s)) {} - explicit Value(const char *s) : type_(STRING_TYPE) { - string_value_ = s; - } + explicit Value(const char *s) : type_(STRING_TYPE) { string_value_ = s; } explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) { binary_value_.resize(n); memcpy(binary_value_.data(), p, n); @@ -365,12 +339,11 @@ class Value { T &Get(); // Lookup value from an array - const Value &Get(int idx) const { + const Value &Get(size_t idx) const { static Value null_value; assert(IsArray()); - assert(idx >= 0); - return (static_cast(idx) < array_value_.size()) - ? array_value_[static_cast(idx)] + return (idx < array_value_.size()) + ? array_value_[idx] : null_value; } @@ -549,28 +522,30 @@ typedef std::map ParameterMap; typedef std::map ExtensionMap; struct AnimationChannel { - int sampler; // required - int target_node; // optional index of the node to target (alternative + int sampler{-1}; // required + int target_node{-1}; // optional index of the node to target (alternative // target should be provided by extension) std::string target_path; // required with standard values of ["translation", // "rotation", "scale", "weights"] Value extras; ExtensionMap extensions; + Value target_extras; ExtensionMap target_extensions; // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. std::string extras_json_string; std::string extensions_json_string; + std::string target_extras_json_string; std::string target_extensions_json_string; - AnimationChannel() : sampler(-1), target_node(-1) {} + AnimationChannel() = default; DEFAULT_METHODS(AnimationChannel) bool operator==(const AnimationChannel &) const; }; struct AnimationSampler { - int input; // required - int output; // required + int input{-1}; // required + int output{-1}; // required std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined // string. default "LINEAR" Value extras; @@ -580,7 +555,7 @@ struct AnimationSampler { std::string extras_json_string; std::string extensions_json_string; - AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {} + AnimationSampler() : interpolation("LINEAR") {} DEFAULT_METHODS(AnimationSampler) bool operator==(const AnimationSampler &) const; }; @@ -603,9 +578,9 @@ struct Animation { struct Skin { std::string name; - int inverseBindMatrices; // required here but not in the spec - int skeleton; // The index of the node used as a skeleton root - std::vector joints; // Indices of skeleton nodes + int inverseBindMatrices{-1}; // required here but not in the spec + int skeleton{-1}; // The index of the node used as a skeleton root + std::vector joints; // Indices of skeleton nodes Value extras; ExtensionMap extensions; @@ -614,10 +589,7 @@ struct Skin { std::string extras_json_string; std::string extensions_json_string; - Skin() { - inverseBindMatrices = -1; - skeleton = -1; - } + Skin() = default; DEFAULT_METHODS(Skin) bool operator==(const Skin &) const; }; @@ -648,25 +620,21 @@ struct Sampler { std::string extras_json_string; std::string extensions_json_string; - Sampler() - : minFilter(-1), - magFilter(-1), - wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT), - wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {} + Sampler() = default; DEFAULT_METHODS(Sampler) bool operator==(const Sampler &) const; }; struct Image { std::string name; - int width; - int height; - int component; - int bits; // bit depth per channel. 8(byte), 16 or 32. - int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually - // UBYTE(bits = 8) or USHORT(bits = 16) + int width{-1}; + int height{-1}; + int component{-1}; + int bits{-1}; // bit depth per channel. 8(byte), 16 or 32. + int pixel_type{-1}; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually + // UBYTE(bits = 8) or USHORT(bits = 16) std::vector image; - int bufferView; // (required if no uri) + int bufferView{-1}; // (required if no uri) std::string mimeType; // (required if no uri) ["image/jpeg", "image/png", // "image/bmp", "image/gif"] std::string uri; // (required if no mimeType) uri is not decoded(e.g. @@ -681,19 +649,10 @@ struct Image { // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg // compressed for "image/jpeg" mime) This feature is good if you use custom // image loader function. (e.g. delayed decoding of images for faster glTF - // parsing) Default parser for Image does not provide as-is loading feature at - // the moment. (You can manipulate this by providing your own LoadImageData - // function) - bool as_is; - - Image() : as_is(false) { - bufferView = -1; - width = -1; - height = -1; - component = -1; - bits = -1; - pixel_type = -1; - } + // parsing). + bool as_is{false}; + + Image() = default; DEFAULT_METHODS(Image) bool operator==(const Image &) const; @@ -702,8 +661,8 @@ struct Image { struct Texture { std::string name; - int sampler; - int source; + int sampler{-1}; + int source{-1}; Value extras; ExtensionMap extensions; @@ -711,16 +670,16 @@ struct Texture { std::string extras_json_string; std::string extensions_json_string; - Texture() : sampler(-1), source(-1) {} + Texture() = default; DEFAULT_METHODS(Texture) bool operator==(const Texture &) const; }; struct TextureInfo { - int index = -1; // required. - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. + int index{-1}; // required. + int texCoord{0}; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. Value extras; ExtensionMap extensions; @@ -729,17 +688,18 @@ struct TextureInfo { std::string extras_json_string; std::string extensions_json_string; - TextureInfo() : index(-1), texCoord(0) {} + TextureInfo() = default; DEFAULT_METHODS(TextureInfo) bool operator==(const TextureInfo &) const; }; struct NormalTextureInfo { - int index = -1; // required - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. - double scale; // scaledNormal = normalize(( - // * 2.0 - 1.0) * vec3(, , 1.0)) + int index{-1}; // required + int texCoord{0}; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. + double scale{ + 1.0}; // scaledNormal = normalize(( + // * 2.0 - 1.0) * vec3(, , 1.0)) Value extras; ExtensionMap extensions; @@ -748,17 +708,17 @@ struct NormalTextureInfo { std::string extras_json_string; std::string extensions_json_string; - NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {} + NormalTextureInfo() = default; DEFAULT_METHODS(NormalTextureInfo) bool operator==(const NormalTextureInfo &) const; }; struct OcclusionTextureInfo { - int index = -1; // required - int texCoord; // The set index of texture's TEXCOORD attribute used for + int index{-1}; // required + int texCoord{0}; // The set index of texture's TEXCOORD attribute used for // texture coordinate mapping. - double strength; // occludedColor = lerp(color, color * , ) + double strength{1.0}; // occludedColor = lerp(color, color * , ) Value extras; ExtensionMap extensions; @@ -767,17 +727,17 @@ struct OcclusionTextureInfo { std::string extras_json_string; std::string extensions_json_string; - OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {} + OcclusionTextureInfo() = default; DEFAULT_METHODS(OcclusionTextureInfo) bool operator==(const OcclusionTextureInfo &) const; }; // pbrMetallicRoughness class defined in glTF 2.0 spec. struct PbrMetallicRoughness { - std::vector baseColorFactor; // len = 4. default [1,1,1,1] + std::vector baseColorFactor{1.0, 1.0, 1.0, 1.0}; // len = 4. default [1,1,1,1] TextureInfo baseColorTexture; - double metallicFactor; // default 1 - double roughnessFactor; // default 1 + double metallicFactor{1.0}; // default 1 + double roughnessFactor{1.0}; // default 1 TextureInfo metallicRoughnessTexture; Value extras; @@ -787,11 +747,9 @@ struct PbrMetallicRoughness { std::string extras_json_string; std::string extensions_json_string; - PbrMetallicRoughness() - : baseColorFactor(std::vector{1.0, 1.0, 1.0, 1.0}), - metallicFactor(1.0), - roughnessFactor(1.0) {} + PbrMetallicRoughness() = default; DEFAULT_METHODS(PbrMetallicRoughness) + bool operator==(const PbrMetallicRoughness &) const; }; @@ -801,10 +759,11 @@ struct PbrMetallicRoughness { struct Material { std::string name; - std::vector emissiveFactor; // length 3. default [0, 0, 0] - std::string alphaMode; // default "OPAQUE" - double alphaCutoff; // default 0.5 - bool doubleSided; // default false; + std::vector emissiveFactor{0.0, 0.0, 0.0}; // length 3. default [0, 0, 0] + std::string alphaMode{"OPAQUE"}; // default "OPAQUE" + double alphaCutoff{0.5}; // default 0.5 + bool doubleSided{false}; // default false + std::vector lods; // level of detail materials (MSFT_lod) PbrMetallicRoughness pbrMetallicRoughness; @@ -824,7 +783,7 @@ struct Material { std::string extras_json_string; std::string extensions_json_string; - Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {} + Material() = default; DEFAULT_METHODS(Material) bool operator==(const Material &) const; @@ -848,26 +807,20 @@ struct BufferView { bool dracoDecoded{false}; // Flag indicating this has been draco decoded - BufferView() - : buffer(-1), - byteOffset(0), - byteLength(0), - byteStride(0), - target(0), - dracoDecoded(false) {} + BufferView() = default; DEFAULT_METHODS(BufferView) bool operator==(const BufferView &) const; }; struct Accessor { - int bufferView; // optional in spec but required here since sparse accessor - // are not supported + int bufferView{-1}; // optional in spec but required here since sparse + // accessor are not supported std::string name; - size_t byteOffset; - bool normalized; // optional. - int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_*** - size_t count; // required - int type; // (required) One of TINYGLTF_TYPE_*** .. + size_t byteOffset{0}; + bool normalized{false}; // optional. + int componentType{-1}; // (required) One of TINYGLTF_COMPONENT_TYPE_*** + size_t count{0}; // required + int type{-1}; // (required) One of TINYGLTF_TYPE_*** .. Value extras; ExtensionMap extensions; @@ -880,19 +833,33 @@ struct Accessor { std::vector maxValues; // optional. integer value is promoted to double - struct { + struct Sparse { int count; bool isSparse; struct { - int byteOffset; + size_t byteOffset; int bufferView; int componentType; // a TINYGLTF_COMPONENT_TYPE_ value + Value extras; + ExtensionMap extensions; + std::string extras_json_string; + std::string extensions_json_string; } indices; struct { int bufferView; - int byteOffset; + size_t byteOffset; + Value extras; + ExtensionMap extensions; + std::string extras_json_string; + std::string extensions_json_string; } values; - } sparse; + Value extras; + ExtensionMap extensions; + std::string extras_json_string; + std::string extensions_json_string; + }; + + Sparse sparse; /// /// Utility function to compute byteStride for a given bufferView object. @@ -914,8 +881,8 @@ struct Accessor { return componentSizeInBytes * numComponents; } else { - // Check if byteStride is a multiple of the size of the accessor's component - // type. + // Check if byteStride is a multiple of the size of the accessor's + // component type. int componentSizeInBytes = GetComponentSizeInBytes(static_cast(componentType)); if (componentSizeInBytes <= 0) { @@ -932,12 +899,8 @@ struct Accessor { } Accessor() - : bufferView(-1), - byteOffset(0), - normalized(false), - componentType(-1), - count(0), - type(-1) { + + { sparse.isSparse = false; } DEFAULT_METHODS(Accessor) @@ -945,17 +908,12 @@ struct Accessor { }; struct PerspectiveCamera { - double aspectRatio; // min > 0 - double yfov; // required. min > 0 - double zfar; // min > 0 - double znear; // required. min > 0 - - PerspectiveCamera() - : aspectRatio(0.0), - yfov(0.0), - zfar(0.0) // 0 = use infinite projection matrix - , - znear(0.0) {} + double aspectRatio{0.0}; // min > 0 + double yfov{0.0}; // required. min > 0 + double zfar{0.0}; // min > 0 + double znear{0.0}; // required. min > 0 + + PerspectiveCamera() = default; DEFAULT_METHODS(PerspectiveCamera) bool operator==(const PerspectiveCamera &) const; @@ -968,12 +926,12 @@ struct PerspectiveCamera { }; struct OrthographicCamera { - double xmag; // required. must not be zero. - double ymag; // required. must not be zero. - double zfar; // required. `zfar` must be greater than `znear`. - double znear; // required + double xmag{0.0}; // required. must not be zero. + double ymag{0.0}; // required. must not be zero. + double zfar{0.0}; // required. `zfar` must be greater than `znear`. + double znear{0.0}; // required - OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {} + OrthographicCamera() = default; DEFAULT_METHODS(OrthographicCamera) bool operator==(const OrthographicCamera &) const; @@ -992,7 +950,7 @@ struct Camera { PerspectiveCamera perspective; OrthographicCamera orthographic; - Camera() {} + Camera() = default; DEFAULT_METHODS(Camera) bool operator==(const Camera &) const; @@ -1009,10 +967,10 @@ struct Primitive { // integer, where each integer // is the index of the accessor // containing an attribute. - int material; // The index of the material to apply to this primitive - // when rendering. - int indices; // The index of the accessor that contains the indices. - int mode; // one of TINYGLTF_MODE_*** + int material{-1}; // The index of the material to apply to this primitive + // when rendering. + int indices{-1}; // The index of the accessor that contains the indices. + int mode{-1}; // one of TINYGLTF_MODE_*** std::vector > targets; // array of morph targets, // where each target is a dict with attributes in ["POSITION, "NORMAL", // "TANGENT"] pointing @@ -1024,11 +982,7 @@ struct Primitive { std::string extras_json_string; std::string extensions_json_string; - Primitive() { - material = -1; - indices = -1; - mode = -1; - } + Primitive() = default; DEFAULT_METHODS(Primitive) bool operator==(const Primitive &) const; }; @@ -1051,17 +1005,20 @@ struct Mesh { class Node { public: - Node() : camera(-1), skin(-1), mesh(-1) {} + Node() = default; DEFAULT_METHODS(Node) bool operator==(const Node &) const; - int camera; // the index of the camera referenced by this node + int camera{-1}; // the index of the camera referenced by this node std::string name; - int skin; - int mesh; + int skin{-1}; + int mesh{-1}; + int light{-1}; // light source index (KHR_lights_punctual) + int emitter{-1}; // audio emitter index (KHR_audio) + std::vector lods; // level of detail nodes (MSFT_lod) std::vector children; std::vector rotation; // length must be 0 or 4 std::vector scale; // length must be 0 or 3 @@ -1115,6 +1072,7 @@ struct Asset { struct Scene { std::string name; std::vector nodes; + std::vector audioEmitters; // KHR_audio global emitters ExtensionMap extensions; Value extras; @@ -1129,10 +1087,10 @@ struct Scene { }; struct SpotLight { - double innerConeAngle; - double outerConeAngle; + double innerConeAngle{0.0}; + double outerConeAngle{0.7853981634}; - SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {} + SpotLight() = default; DEFAULT_METHODS(SpotLight) bool operator==(const SpotLight &) const; @@ -1152,7 +1110,7 @@ struct Light { double range{0.0}; // 0.0 = infinite SpotLight spot; - Light() : intensity(1.0), range(0.0) {} + Light() = default; DEFAULT_METHODS(Light) bool operator==(const Light &) const; @@ -1165,6 +1123,89 @@ struct Light { std::string extensions_json_string; }; +struct PositionalEmitter { + double coneInnerAngle{6.283185307179586}; + double coneOuterAngle{6.283185307179586}; + double coneOuterGain{0.0}; + double maxDistance{100.0}; + double refDistance{1.0}; + double rolloffFactor{1.0}; + + PositionalEmitter() = default; + DEFAULT_METHODS(PositionalEmitter) + bool operator==(const PositionalEmitter &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct AudioEmitter { + std::string name; + double gain{1.0}; + bool loop{false}; + bool playing{false}; + std::string + type; // positional - Positional audio emitters. Using sound cones, the + // orientation is +Z having the same front side for a glTF asset. + // global - Global audio emitters are not affected by the position + // of audio listeners. coneInnerAngle, coneOuterAngle, + // coneOuterGain, distanceModel, maxDistance, refDistance, and + // rolloffFactor should all be ignored when set. + std::string + distanceModel; // linear - A linear distance model calculating the + // gain induced by the distance according to: 1.0 + // - rolloffFactor * (distance - refDistance) / + // (maxDistance - refDistance) + // inverse - (default) An inverse distance model + // calculating the gain induced by the distance according + // to: refDistance / (refDistance + rolloffFactor * + // (Math.max(distance, refDistance) - refDistance)) + // exponential - An exponential distance model calculating + // the gain induced by the distance according to: + // pow((Math.max(distance, refDistance) / refDistance, + // -rolloffFactor)) + PositionalEmitter positional; + int source{-1}; + + AudioEmitter() : type("global"), distanceModel("inverse") {} + DEFAULT_METHODS(AudioEmitter) + + bool operator==(const AudioEmitter &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct AudioSource { + std::string name; + std::string uri; + int bufferView{-1}; // (required if no uri) + std::string + mimeType; // (required if no uri) The audio's MIME type. Required if + // bufferView is defined. Unless specified by another + // extension, the only supported mimeType is audio/mpeg. + + AudioSource() = default; + DEFAULT_METHODS(AudioSource) + + bool operator==(const AudioSource &) const; + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + class Model { public: Model() = default; @@ -1186,8 +1227,10 @@ class Model { std::vector cameras; std::vector scenes; std::vector lights; + std::vector audioEmitters; + std::vector audioSources; - int defaultScene = -1; + int defaultScene{-1}; std::vector extensionsUsed; std::vector extensionsRequired; @@ -1220,17 +1263,18 @@ enum SectionCheck { /// image URIs differently, for example. See /// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris /// -typedef bool (*URIEncodeFunction)(const std::string &in_uri, - const std::string &object_type, - std::string *out_uri, void *user_data); +using URIEncodeFunction = std::function; /// /// URIDecodeFunction type. Signature for custom URI decoding of external /// resources such as .bin and image files. Used by tinygltf when computing /// filenames to write resources. /// -typedef bool (*URIDecodeFunction)(const std::string &in_uri, - std::string *out_uri, void *user_data); +using URIDecodeFunction = + std::function; // Declaration of default uri decode function bool URIDecode(const std::string &in_uri, std::string *out_uri, @@ -1247,68 +1291,36 @@ struct URICallbacks { }; /// -/// LoadImageDataFunction type. Signature for custom image loading callbacks. -/// -typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, - std::string *, int, int, - const unsigned char *, int, - void *user_pointer); - -/// -/// WriteImageDataFunction type. Signature for custom image writing callbacks. -/// The out_uri parameter becomes the URI written to the gltf and may reference -/// a file or contain a data URI. -/// -typedef bool (*WriteImageDataFunction)(const std::string *basepath, - const std::string *filename, - const Image *image, bool embedImages, - const URICallbacks *uri_cb, - std::string *out_uri, - void *user_pointer); - -#ifndef TINYGLTF_NO_STB_IMAGE -// Declaration of default image loader callback -bool LoadImageData(Image *image, const int image_idx, std::string *err, - std::string *warn, int req_width, int req_height, - const unsigned char *bytes, int size, void *); -#endif - -#ifndef TINYGLTF_NO_STB_IMAGE_WRITE -// Declaration of default image writer callback -bool WriteImageData(const std::string *basepath, const std::string *filename, - const Image *image, bool embedImages, - const URICallbacks *uri_cb, std::string *out_uri, void *); -#endif - -/// -/// FilExistsFunction type. Signature for custom filesystem callbacks. +/// FileExistsFunction type. Signature for custom filesystem callbacks. /// -typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *); +using FileExistsFunction = std::function; /// /// ExpandFilePathFunction type. Signature for custom filesystem callbacks. /// -typedef std::string (*ExpandFilePathFunction)(const std::string &, void *); +using ExpandFilePathFunction = + std::function; /// /// ReadWholeFileFunction type. Signature for custom filesystem callbacks. /// -typedef bool (*ReadWholeFileFunction)(std::vector *, - std::string *, const std::string &, - void *); +using ReadWholeFileFunction = std::function *, std::string *, const std::string &, void *)>; /// /// WriteWholeFileFunction type. Signature for custom filesystem callbacks. /// -typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &, - const std::vector &, - void *); +using WriteWholeFileFunction = + std::function &, void *)>; /// /// GetFileSizeFunction type. Signature for custom filesystem callbacks. /// -typedef bool (*GetFileSizeFunction)(size_t *filesize_out, std::string *err, const std::string &abs_filename, - void *userdata); +using GetFileSizeFunction = + std::function; /// /// A structure containing all required filesystem callbacks and a pointer to @@ -1319,7 +1331,8 @@ struct FsCallbacks { ExpandFilePathFunction ExpandFilePath; ReadWholeFileFunction ReadWholeFile; WriteWholeFileFunction WriteWholeFile; - GetFileSizeFunction GetFileSizeInBytes; // To avoid GetFileSize Win32 API, add `InBytes` suffix. + GetFileSizeFunction GetFileSizeInBytes; // To avoid GetFileSize Win32 API, + // add `InBytes` suffix. void *user_data; // An argument that is passed to all fs callbacks }; @@ -1344,8 +1357,42 @@ bool ReadWholeFile(std::vector *out, std::string *err, bool WriteWholeFile(std::string *err, const std::string &filepath, const std::vector &contents, void *); -bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, const std::string &filepath, - void *); +bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, + const std::string &filepath, void *); +#endif + +/// +/// LoadImageDataFunction type. Signature for custom image loading callbacks. +/// +using LoadImageDataFunction = std::function; + +/// +/// WriteImageDataFunction type. Signature for custom image writing callbacks. +/// The out_uri parameter becomes the URI written to the gltf and may reference +/// a file or contain a data URI. +/// +using WriteImageDataFunction = std::function; + +#ifndef TINYGLTF_NO_STB_IMAGE +// Declaration of default image loader callback +bool LoadImageData(Image *image, const int image_idx, std::string *err, + std::string *warn, int req_width, int req_height, + const unsigned char *bytes, int size, void *); +#endif + +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE +// Declaration of default image writer callback +bool WriteImageData(const std::string *basepath, const std::string *filename, + const Image *image, bool embedImages, + const FsCallbacks* fs_cb, const URICallbacks *uri_cb, + std::string *out_uri, void *); #endif /// @@ -1358,13 +1405,13 @@ class TinyGLTF { #pragma clang diagnostic ignored "-Wc++98-compat" #endif - TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {} + TinyGLTF() = default; #ifdef __clang__ #pragma clang diagnostic pop #endif - ~TinyGLTF() {} + ~TinyGLTF() = default; /// /// Loads glTF ASCII asset from a file. @@ -1425,7 +1472,13 @@ class TinyGLTF { bool prettyPrint, bool writeBinary); /// - /// Set callback to use for loading image data + /// Sets the parsing strictness. + /// + void SetParseStrictness(ParseStrictness strictness); + + /// + /// Set callback to use for loading image data. Passing the nullptr is akin to + /// calling RemoveImageLoader(). /// void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data); @@ -1440,14 +1493,18 @@ class TinyGLTF { void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data); /// - /// Set callbacks to use for URI encoding and decoding and their user data + /// Set callbacks to use for URI encoding and decoding and their user data. + /// Returns false if there is an error with the callbacks. If err is not + /// nullptr, explanation will be written there. /// - void SetURICallbacks(URICallbacks callbacks); + bool SetURICallbacks(URICallbacks callbacks, std::string* err = nullptr); /// - /// Set callbacks to use for filesystem (fs) access and their user data + /// Set callbacks to use for filesystem (fs) access and their user data. + /// Returns false if there is an error with the callbacks. If err is not + /// nullptr, explanation will be written there. /// - void SetFsCallbacks(FsCallbacks callbacks); + bool SetFsCallbacks(FsCallbacks callbacks, std::string* err = nullptr); /// /// Set serializing default values(default = false). @@ -1485,6 +1542,17 @@ class TinyGLTF { preserve_image_channels_ = onoff; } + bool GetPreserveImageChannels() const { return preserve_image_channels_; } + + /// + /// Specifiy whether image data is decoded/decompressed during load, or left as is + /// + void SetImagesAsIs(bool onoff) { + images_as_is_ = onoff; + } + + bool GetImagesAsIs() const { return images_as_is_; } + /// /// Set maximum allowed external file size in bytes. /// Default: 2GB @@ -1494,11 +1562,7 @@ class TinyGLTF { max_external_file_size_ = max_bytes; } - size_t GetMaxExternalFileSize() const { - return max_external_file_size_; - } - - bool GetPreserveImageChannels() const { return preserve_image_channels_; } + size_t GetMaxExternalFileSize() const { return max_external_file_size_; } private: /// @@ -1515,6 +1579,8 @@ class TinyGLTF { size_t bin_size_ = 0; bool is_binary_ = false; + ParseStrictness strictness_ = ParseStrictness::Strict; + bool serialize_default_values_ = false; ///< Serialize default values? bool store_original_json_for_extras_and_extensions_ = false; @@ -1522,7 +1588,10 @@ class TinyGLTF { bool preserve_image_channels_ = false; /// Default false(expand channels to /// RGBA) for backward compatibility. - size_t max_external_file_size_{size_t((std::numeric_limits::max)())}; // Default 2GB + bool images_as_is_ = false; /// Default false (decode/decompress images) + + size_t max_external_file_size_{ + size_t((std::numeric_limits::max)())}; // Default 2GB // Warning & error messages std::string warn_; @@ -1530,8 +1599,11 @@ class TinyGLTF { FsCallbacks fs = { #ifndef TINYGLTF_NO_FS - &tinygltf::FileExists, &tinygltf::ExpandFilePath, - &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile, &tinygltf::GetFileSizeInBytes, + &tinygltf::FileExists, + &tinygltf::ExpandFilePath, + &tinygltf::ReadWholeFile, + &tinygltf::WriteWholeFile, + &tinygltf::GetFileSizeInBytes, nullptr // Fs callback user data #else @@ -1578,11 +1650,12 @@ class TinyGLTF { #if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__) #include -//#include +// #include #ifndef TINYGLTF_NO_FS +#include // for is_directory check + #include #include -#include // for is_directory check #endif #include @@ -1723,7 +1796,7 @@ class TinyGLTF { #endif #elif !defined(__ANDROID__) && !defined(__OpenBSD__) -//#include +// #include #endif #if defined(__sparcv9) || defined(__powerpc__) @@ -1743,6 +1816,7 @@ namespace detail { // documents may be active at once. using json = rapidjson::GenericValue, rapidjson::CrtAllocator>; +using json_iterator = json::MemberIterator; using json_const_iterator = json::ConstMemberIterator; using json_const_array_iterator = json const *; using JsonDocument = @@ -1754,6 +1828,7 @@ rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; } // not thread safe. Only a single JsonDocument may be active at any one time, // meaning only a single gltf load/save can be active any one time. using json = rapidjson::Value; +using json_iterator = json::MemberIterator; using json_const_iterator = json::ConstMemberIterator; using json_const_array_iterator = json const *; rapidjson::Document *s_pActiveDocument = nullptr; @@ -1800,6 +1875,7 @@ struct JsonDocument : public rapidjson::Document { #else using nlohmann::json; +using json_iterator = json::iterator; using json_const_iterator = json::const_iterator; using json_const_array_iterator = json_const_iterator; using JsonDocument = json; @@ -1814,8 +1890,8 @@ void JsonParse(JsonDocument &doc, const char *str, size_t length, doc = detail::json::parse(str, str + length, nullptr, throwExc); #endif } -} // namespace -} +} // namespace detail +} // namespace tinygltf #ifdef __APPLE__ #include "TargetConditionals.h" @@ -1839,6 +1915,9 @@ struct LoadImageDataOption { // channels) default `false`(channels are expanded to RGBA for backward // compatibility). bool preserve_channels{false}; + // true: do not decode/decompress image data. + // default `false`: decode/decompress image data. + bool as_is{false}; }; // Equals function for Value, for recursivity @@ -1868,7 +1947,7 @@ static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) { } case ARRAY_TYPE: { if (one.Size() != other.Size()) return false; - for (int i = 0; i < int(one.Size()); ++i) + for (size_t i = 0; i < one.Size(); ++i) if (!Equals(one.Get(i), other.Get(i))) return false; return true; } @@ -1958,6 +2037,17 @@ bool Light::operator==(const Light &other) const { return Equals(this->color, other.color) && this->name == other.name && this->type == other.type; } +bool AudioEmitter::operator==(const AudioEmitter &other) const { + return this->name == other.name && + TINYGLTF_DOUBLE_EQUAL(this->gain, other.gain) && + this->loop == other.loop && this->playing == other.playing && + this->type == other.type && + this->distanceModel == other.distanceModel && + this->source == other.source; +} +bool AudioSource::operator==(const AudioSource &other) const { + return this->name == other.name && this->uri == other.uri; +} bool Material::operator==(const Material &other) const { return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) && (this->normalTexture == other.normalTexture) && @@ -1997,6 +2087,7 @@ bool Node::operator==(const Node &other) const { return this->camera == other.camera && this->children == other.children && this->extensions == other.extensions && this->extras == other.extras && Equals(this->matrix, other.matrix) && this->mesh == other.mesh && + (this->light == other.light) && (this->emitter == other.emitter) && this->name == other.name && Equals(this->rotation, other.rotation) && Equals(this->scale, other.scale) && this->skin == other.skin && Equals(this->translation, other.translation) && @@ -2007,6 +2098,15 @@ bool SpotLight::operator==(const SpotLight &other) const { TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) && TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle); } +bool PositionalEmitter::operator==(const PositionalEmitter &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + TINYGLTF_DOUBLE_EQUAL(this->coneInnerAngle, other.coneInnerAngle) && + TINYGLTF_DOUBLE_EQUAL(this->coneOuterAngle, other.coneOuterAngle) && + TINYGLTF_DOUBLE_EQUAL(this->coneOuterGain, other.coneOuterGain) && + TINYGLTF_DOUBLE_EQUAL(this->maxDistance, other.maxDistance) && + TINYGLTF_DOUBLE_EQUAL(this->refDistance, other.refDistance) && + TINYGLTF_DOUBLE_EQUAL(this->rolloffFactor, other.rolloffFactor); +} bool OrthographicCamera::operator==(const OrthographicCamera &other) const { return this->extensions == other.extensions && this->extras == other.extras && TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) && @@ -2137,8 +2237,9 @@ static std::string FindFile(const std::vector &paths, } // https://github.com/syoyo/tinygltf/issues/416 - // Use strlen() since std::string's size/length reports the number of elements in the buffer, not the length of string(null-terminated) - // strip null-character in the middle of string. + // Use strlen() since std::string's size/length reports the number of elements + // in the buffer, not the length of string(null-terminated) strip + // null-character in the middle of string. size_t slength = strlen(filepath.c_str()); if (slength == 0) { return std::string(); @@ -2165,7 +2266,7 @@ static std::string GetFilePathExtension(const std::string &FileName) { static std::string GetBaseDir(const std::string &filepath) { if (filepath.find_last_of("/\\") != std::string::npos) - return filepath.substr(0, filepath.find_last_of("/\\")); + return filepath.substr(0, filepath.find_last_of("/\\") + 1); return ""; } @@ -2394,7 +2495,8 @@ bool URIDecode(const std::string &in_uri, std::string *out_uri, static bool LoadExternalFile(std::vector *out, std::string *err, std::string *warn, const std::string &filename, const std::string &basedir, bool required, - size_t reqBytes, bool checkSize, size_t maxFileSize, FsCallbacks *fs) { + size_t reqBytes, bool checkSize, + size_t maxFileSize, FsCallbacks *fs) { if (fs == nullptr || fs->FileExists == nullptr || fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) { // This is a developer error, assert() ? @@ -2422,14 +2524,15 @@ static bool LoadExternalFile(std::vector *out, std::string *err, // Check file size if (fs->GetFileSizeInBytes) { - size_t file_size{0}; std::string _err; - bool ok = fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data); + bool ok = + fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data); if (!ok) { if (_err.size()) { if (failMsgOut) { - (*failMsgOut) += "Getting file size failed : " + filename + ", err = " + _err + "\n"; + (*failMsgOut) += "Getting file size failed : " + filename + + ", err = " + _err + "\n"; } } return false; @@ -2437,7 +2540,9 @@ static bool LoadExternalFile(std::vector *out, std::string *err, if (file_size > maxFileSize) { if (failMsgOut) { - (*failMsgOut) += "File size " + std::to_string(file_size) + " exceeds maximum allowed file size " + std::to_string(maxFileSize) + " : " + filepath + "\n"; + (*failMsgOut) += "File size " + std::to_string(file_size) + + " exceeds maximum allowed file size " + + std::to_string(maxFileSize) + " : " + filepath + "\n"; } return false; } @@ -2482,8 +2587,16 @@ static bool LoadExternalFile(std::vector *out, std::string *err, return true; } +void TinyGLTF::SetParseStrictness(ParseStrictness strictness) { + strictness_ = strictness; +} + void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) { - LoadImageData = func; + if (func == nullptr) { + RemoveImageLoader(); + return; + } + LoadImageData = std::move(func); load_image_user_data_ = user_data; user_image_loader_ = true; } @@ -2513,48 +2626,65 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, int w = 0, h = 0, comp = 0, req_comp = 0; - unsigned char *data = nullptr; + // Try to decode image header + if (!stbi_info_from_memory(bytes, size, &w, &h, &comp)) { + // On failure, if we load images as is, we just warn. + std::string* msgOut = option.as_is ? warn : err; + if (msgOut) { + (*msgOut) += + "Unknown image format. STB cannot decode image header for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; + } + if (!option.as_is) { + // If we decode images, error out. + return false; + } else { + // If we load images as is, we copy the image data, + // set all image properties to invalid, and report success. + image->width = image->height = image->component = -1; + image->bits = image->pixel_type = -1; + image->image.resize(static_cast(size)); + std::copy(bytes, bytes + size, image->image.begin()); + return true; + } + } - // preserve_channels true: Use channels stored in the image file. - // false: force 32-bit textures for common Vulkan compatibility. It appears - // that some GPU drivers do not support 24-bit images for Vulkan - req_comp = option.preserve_channels ? 0 : 4; int bits = 8; int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; - // It is possible that the image we want to load is a 16bit per channel image - // We are going to attempt to load it as 16bit per channel, and if it worked, - // set the image data accordingly. We are casting the returned pointer into - // unsigned char, because we are representing "bytes". But we are updating - // the Image metadata to signal that this image uses 2 bytes (16bits) per - // channel: if (stbi_is_16_bit_from_memory(bytes, size)) { - data = reinterpret_cast( - stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp)); - if (data) { - bits = 16; - pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; - } - } - - // at this point, if data is still NULL, it means that the image wasn't - // 16bit per channel, we are going to load it as a normal 8bit per channel - // image as we used to do: - // if image cannot be decoded, ignore parsing and keep it by its path - // don't break in this case - // FIXME we should only enter this function if the image is embedded. If - // image->uri references - // an image file, it should be left as it is. Image loading should not be - // mandatory (to support other formats) - if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp); - if (!data) { - // NOTE: you can use `warn` instead of `err` - if (err) { - (*err) += - "Unknown image format. STB cannot decode image data for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; + bits = 16; + pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; + } + + // preserve_channels true: Use channels stored in the image file. + // false: force 32-bit textures for common Vulkan compatibility. It appears + // that some GPU drivers do not support 24-bit images for Vulkan + req_comp = (option.preserve_channels || option.as_is) ? 0 : 4; + + unsigned char* data = nullptr; + // Perform image decoding if requested + if (!option.as_is) { + // If the image is marked as 16 bit per channel, attempt to decode it as such first. + // If that fails, we are going to attempt to load it as 8 bit per channel image. + if (bits == 16) { + data = reinterpret_cast(stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp)); + } + // Load as 8 bit per channel data + if (!data) { + data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp); + if (!data) { + if (err) { + (*err) += + "Unknown image format. STB cannot decode image data for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; + } + return false; + } + // If we were succesful, mark as 8 bit + bits = 8; + pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; } - return false; } if ((w < 1) || (h < 1)) { @@ -2600,16 +2730,26 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, image->component = comp; image->bits = bits; image->pixel_type = pixel_type; - image->image.resize(static_cast(w * h * comp) * size_t(bits / 8)); - std::copy(data, data + w * h * comp * (bits / 8), image->image.begin()); - stbi_image_free(data); + image->as_is = option.as_is; + if (option.as_is) { + // Store the original image data + image->image.resize(static_cast(size)); + std::copy(bytes, bytes + size, image->image.begin()); + } + else { + // Store the decoded image data + image->image.resize(static_cast(w * h * comp) * size_t(bits / 8)); + std::copy(data, data + w * h * comp * (bits / 8), image->image.begin()); + } + + stbi_image_free(data); return true; } #endif void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) { - WriteImageData = func; + WriteImageData = std::move(func); write_image_user_data_ = user_data; } @@ -2625,36 +2765,51 @@ static void WriteToMemory_stbi(void *context, void *data, int size) { bool WriteImageData(const std::string *basepath, const std::string *filename, const Image *image, bool embedImages, - const URICallbacks *uri_cb, std::string *out_uri, - void *fsPtr) { + const FsCallbacks* fs_cb, const URICallbacks *uri_cb, + std::string *out_uri, void *) { + // Early out on empty images, report the original uri if the image was not written. + if (image->image.empty()) { + *out_uri = *filename; + return true; + } + const std::string ext = GetFilePathExtension(*filename); // Write image to temporary buffer std::string header; std::vector data; + // If the image data is already encoded, take it as is + if (image->as_is) { + data = image->image; + } + if (ext == "png") { - if ((image->bits != 8) || - (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) { - // Unsupported pixel format - return false; - } + if (!image->as_is) { + if ((image->bits != 8) || + (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) { + // Unsupported pixel format + return false; + } - if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width, - image->height, image->component, - &image->image[0], 0)) { - return false; + if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width, + image->height, image->component, + &image->image[0], 0)) { + return false; + } } header = "data:image/png;base64,"; } else if (ext == "jpg") { - if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width, + if (!image->as_is && + !stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width, image->height, image->component, &image->image[0], 100)) { return false; } header = "data:image/jpeg;base64,"; } else if (ext == "bmp") { - if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width, + if (!image->as_is && + !stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width, image->height, image->component, &image->image[0])) { return false; @@ -2668,19 +2823,18 @@ bool WriteImageData(const std::string *basepath, const std::string *filename, if (embedImages) { // Embed base64-encoded image into URI if (data.size()) { - *out_uri = header + - base64_encode(&data[0], static_cast(data.size())); + *out_uri = header + base64_encode(&data[0], + static_cast(data.size())); } else { // Throw error? } } else { // Write image to disc - FsCallbacks *fs = reinterpret_cast(fsPtr); - if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) { + if ((fs_cb != nullptr) && (fs_cb->WriteWholeFile != nullptr)) { const std::string imagefilepath = JoinPath(*basepath, *filename); std::string writeError; - if (!fs->WriteWholeFile(&writeError, imagefilepath, data, - fs->user_data)) { + if (!fs_cb->WriteWholeFile(&writeError, imagefilepath, data, + fs_cb->user_data)) { // Could not write image file to disc; Throw error ? return false; } @@ -2700,14 +2854,36 @@ bool WriteImageData(const std::string *basepath, const std::string *filename, } #endif -void TinyGLTF::SetURICallbacks(URICallbacks callbacks) { - assert(callbacks.decode); +bool TinyGLTF::SetURICallbacks(URICallbacks callbacks, std::string* err) { + if (callbacks.decode == nullptr) { + if (err != nullptr) { + *err = "URI Callback require a non-null decode function."; + } + return false; + } + if (callbacks.decode) { - uri_cb = callbacks; + uri_cb = std::move(callbacks); } + return true; } -void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; } +bool TinyGLTF::SetFsCallbacks(FsCallbacks callbacks, std::string *err) { + // If callbacks are defined at all, they must all be defined. + if (callbacks.FileExists == nullptr || callbacks.ExpandFilePath == nullptr || + callbacks.ReadWholeFile == nullptr || + callbacks.WriteWholeFile == nullptr || + callbacks.GetFileSizeInBytes == nullptr) { + if (err != nullptr) { + *err = + "FS Callbacks must be completely defined. At least one callback is " + "null."; + } + return false; + } + fs = std::move(callbacks); + return true; +} #ifdef _WIN32 static inline std::wstring UTF8ToWchar(const std::string &str) { @@ -2748,7 +2924,7 @@ bool FileExists(const std::string &abs_filename, void *) { } #else #ifdef _WIN32 -#if defined(_MSC_VER) || defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) +#if defined(_MSC_VER) || defined(_LIBCPP_VERSION) // First check if a file is a directory. DWORD result = GetFileAttributesW(UTF8ToWchar(abs_filename).c_str()); @@ -2764,6 +2940,11 @@ bool FileExists(const std::string &abs_filename, void *) { if (err != 0) { return false; } +#elif defined(__GLIBCXX__) + FILE *fp = fopen(abs_filename.c_str(), "rb"); + if (!fp) { + return false; + } #else // TODO: is_directory check FILE *fp = nullptr; @@ -2859,7 +3040,7 @@ std::string ExpandFilePath(const std::string &filepath, void *) { } bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, - const std::string &filepath, void *userdata) { + const std::string &filepath, void *userdata) { (void)userdata; #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS @@ -2915,27 +3096,29 @@ bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, } // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only) - int buf = f.peek(); + f.peek(); if (!f) { if (err) { - (*err) += "File read error. Maybe empty file or invalid file : " + filepath + "\n"; + (*err) += + "File read error. Maybe empty file or invalid file : " + filepath + + "\n"; } return false; } f.seekg(0, f.end); - size_t sz = static_cast(f.tellg()); + const auto sz = f.tellg(); - //std::cout << "sz = " << sz << "\n"; + // std::cout << "sz = " << sz << "\n"; f.seekg(0, f.beg); - if (int64_t(sz) < 0) { + if (sz < 0) { if (err) { (*err) += "Invalid file size : " + filepath + " (does the path point to a directory?)"; } return false; - } else if (sz == 0) { + } else if (sz == std::streamoff(0)) { if (err) { (*err) += "File is empty : " + filepath + "\n"; } @@ -2947,7 +3130,7 @@ bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, return false; } - (*filesize_out) = sz; + (*filesize_out) = static_cast(sz); return true; #endif } @@ -2972,7 +3155,7 @@ bool ReadWholeFile(std::vector *out, std::string *err, } return false; } - out->resize(size); + out->resize(static_cast(size)); AAsset_read(asset, reinterpret_cast(&out->at(0)), size); AAsset_close(asset); return true; @@ -3008,27 +3191,29 @@ bool ReadWholeFile(std::vector *out, std::string *err, } // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only) - int buf = f.peek(); + f.peek(); if (!f) { if (err) { - (*err) += "File read error. Maybe empty file or invalid file : " + filepath + "\n"; + (*err) += + "File read error. Maybe empty file or invalid file : " + filepath + + "\n"; } return false; } f.seekg(0, f.end); - size_t sz = static_cast(f.tellg()); + const auto sz = f.tellg(); - //std::cout << "sz = " << sz << "\n"; + // std::cout << "sz = " << sz << "\n"; f.seekg(0, f.beg); - if (int64_t(sz) < 0) { + if (sz < 0) { if (err) { (*err) += "Invalid file size : " + filepath + " (does the path point to a directory?)"; } return false; - } else if (sz == 0) { + } else if (sz == std::streamoff(0)) { if (err) { (*err) += "File is empty : " + filepath + "\n"; } @@ -3053,7 +3238,7 @@ bool WriteWholeFile(std::string *err, const std::string &filepath, #ifdef _WIN32 #if defined(__GLIBCXX__) // mingw int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE); __gnu_cxx::stdio_filebuf wfile_buf( file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream f(&wfile_buf); @@ -3102,8 +3287,9 @@ static std::string MimeToExt(const std::string &mimeType) { static bool UpdateImageObject(const Image &image, std::string &baseDir, int index, bool embedImages, + const FsCallbacks *fs_cb, const URICallbacks *uri_cb, - WriteImageDataFunction *WriteImageData, + const WriteImageDataFunction& WriteImageData, void *user_data, std::string *out_uri) { std::string filename; std::string ext; @@ -3129,13 +3315,14 @@ static bool UpdateImageObject(const Image &image, std::string &baseDir, filename = std::to_string(index) + "." + ext; } - // If callback is set and image data exists, modify image data object. If - // image data does not exist, this is not considered a failure and the - // original uri should be maintained. + // If callback is set, modify image data object. + // Note that the callback is also invoked for images without data. + // The default callback implementation simply returns true for + // empty images and sets the out URI to filename. bool imageWritten = false; - if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) { - imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages, - uri_cb, out_uri, user_data); + if (WriteImageData != nullptr && !filename.empty()) { + imageWritten = WriteImageData(&baseDir, &filename, &image, embedImages, + fs_cb, uri_cb, out_uri, user_data); if (!imageWritten) { return false; } @@ -3398,7 +3585,22 @@ std::string GetKey(detail::json_const_iterator &it) { #endif } -bool FindMember(const detail::json &o, const char *member, detail::json_const_iterator &it) { +bool FindMember(const detail::json &o, const char *member, + detail::json_const_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (!o.IsObject()) { + return false; + } + it = o.FindMember(member); + return it != o.MemberEnd(); +#else + it = o.find(member); + return it != o.end(); +#endif +} + +bool FindMember(detail::json &o, const char *member, + detail::json_iterator &it) { #ifdef TINYGLTF_USE_RAPIDJSON if (!o.IsObject()) { return false; @@ -3411,6 +3613,22 @@ bool FindMember(const detail::json &o, const char *member, detail::json_const_it #endif } +void Erase(detail::json &o, detail::json_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + o.EraseMember(it); +#else + o.erase(it); +#endif +} + +bool IsEmpty(const detail::json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.ObjectEmpty(); +#else + return o.empty(); +#endif +} + const detail::json &GetValue(detail::json_const_iterator &it) { #ifdef TINYGLTF_USE_RAPIDJSON return it->value; @@ -3419,6 +3637,14 @@ const detail::json &GetValue(detail::json_const_iterator &it) { #endif } +detail::json &GetValue(detail::json_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + return it->value; +#else + return it.value(); +#endif +} + std::string JsonToString(const detail::json &o, int spacing = -1) { #ifdef TINYGLTF_USE_RAPIDJSON using namespace rapidjson; @@ -3443,7 +3669,7 @@ std::string JsonToString(const detail::json &o, int spacing = -1) { #endif } -} // namespace +} // namespace detail static bool ParseJsonAsValue(Value *ret, const detail::json &o) { Value val{}; @@ -3552,7 +3778,8 @@ static bool ParseExtrasProperty(Value *ret, const detail::json &o) { return ParseJsonAsValue(ret, detail::GetValue(it)); } -static bool ParseBooleanProperty(bool *ret, std::string *err, const detail::json &o, +static bool ParseBooleanProperty(bool *ret, std::string *err, + const detail::json &o, const std::string &property, const bool required, const std::string &parent_node = "") { @@ -3601,7 +3828,8 @@ static bool ParseBooleanProperty(bool *ret, std::string *err, const detail::json return true; } -static bool ParseIntegerProperty(int *ret, std::string *err, const detail::json &o, +static bool ParseIntegerProperty(int *ret, std::string *err, + const detail::json &o, const std::string &property, const bool required, const std::string &parent_node = "") { @@ -3637,7 +3865,8 @@ static bool ParseIntegerProperty(int *ret, std::string *err, const detail::json return true; } -static bool ParseUnsignedProperty(size_t *ret, std::string *err, const detail::json &o, +static bool ParseUnsignedProperty(size_t *ret, std::string *err, + const detail::json &o, const std::string &property, const bool required, const std::string &parent_node = "") { @@ -3690,7 +3919,8 @@ static bool ParseUnsignedProperty(size_t *ret, std::string *err, const detail::j return true; } -static bool ParseNumberProperty(double *ret, std::string *err, const detail::json &o, +static bool ParseNumberProperty(double *ret, std::string *err, + const detail::json &o, const std::string &property, const bool required, const std::string &parent_node = "") { @@ -3729,8 +3959,8 @@ static bool ParseNumberProperty(double *ret, std::string *err, const detail::jso } static bool ParseNumberArrayProperty(std::vector *ret, std::string *err, - const detail::json &o, const std::string &property, - bool required, + const detail::json &o, + const std::string &property, bool required, const std::string &parent_node = "") { detail::json_const_iterator it; if (!detail::FindMember(o, property.c_str(), it)) { @@ -3965,8 +4195,8 @@ static bool ParseJSONProperty(std::map *ret, } static bool ParseParameterProperty(Parameter *param, std::string *err, - const detail::json &o, const std::string &prop, - bool required) { + const detail::json &o, + const std::string &prop, bool required) { // A parameter value can either be a string or an array of either a boolean or // a number. Booleans of any kind aren't supported here. Granted, it // complicates the Parameter structure and breaks it semantically in the sense @@ -4011,7 +4241,8 @@ static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err, return false; } ExtensionMap extensions; - detail::json_const_iterator extIt = detail::ObjectBegin(obj); // it.value().begin(); + detail::json_const_iterator extIt = + detail::ObjectBegin(obj); // it.value().begin(); detail::json_const_iterator extEnd = detail::ObjectEnd(obj); for (; extIt != extEnd; ++extIt) { auto &itObj = detail::GetValue(extIt); @@ -4031,42 +4262,49 @@ static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err, return true; } -static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - ParseStringProperty(&asset->version, err, o, "version", true, "Asset"); - ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset"); - ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset"); - ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset"); - - ParseExtensionsProperty(&asset->extensions, err, o); - - // Unity exporter version is added as extra here - ParseExtrasProperty(&(asset->extras), o); +template +static bool ParseExtrasAndExtensions(GltfType *target, std::string *err, + const detail::json &o, + bool store_json_strings) { + ParseExtensionsProperty(&target->extensions, err, o); + ParseExtrasProperty(&target->extras, o); - if (store_original_json_for_extras_and_extensions) { + if (store_json_strings) { { detail::json_const_iterator it; if (detail::FindMember(o, "extensions", it)) { - asset->extensions_json_string = detail::JsonToString(detail::GetValue(it)); + target->extensions_json_string = + detail::JsonToString(detail::GetValue(it)); } } { detail::json_const_iterator it; if (detail::FindMember(o, "extras", it)) { - asset->extras_json_string = detail::JsonToString(detail::GetValue(it)); + target->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } + return true; +} +static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&asset->version, err, o, "version", true, "Asset"); + ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset"); + ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset"); + ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset"); + + ParseExtrasAndExtensions(asset, err, o, + store_original_json_for_extras_and_extensions); return true; } static bool ParseImage(Image *image, const int image_idx, std::string *err, std::string *warn, const detail::json &o, bool store_original_json_for_extras_and_extensions, - const std::string &basedir, const size_t max_file_size, FsCallbacks *fs, - const URICallbacks *uri_cb, - LoadImageDataFunction *LoadImageData = nullptr, + const std::string &basedir, const size_t max_file_size, + FsCallbacks *fs, const URICallbacks *uri_cb, + const LoadImageDataFunction& LoadImageData = nullptr, void *load_image_user_data = nullptr) { // A glTF image must either reference a bufferView or an image uri @@ -4098,23 +4336,8 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, return false; } - ParseExtensionsProperty(&image->extensions, err, o); - ParseExtrasProperty(&image->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extensions", eit)) { - image->extensions_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extras", eit)) { - image->extras_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - } + ParseExtrasAndExtensions(image, err, o, + store_original_json_for_extras_and_extensions); if (hasBufferView) { int bufferView = -1; @@ -4171,7 +4394,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, } } else { // Assume external file - // Keep texture path (for textures that cannot be decoded) + // Unconditionally keep the external URI of the image image->uri = uri; #ifdef TINYGLTF_NO_EXTERNAL_IMAGE return true; @@ -4190,7 +4413,8 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir, /* required */ false, /* required bytes */ 0, - /* checksize */ false, /* max file size */ max_file_size, fs)) { + /* checksize */ false, + /* max file size */ max_file_size, fs)) { if (warn) { (*warn) += "Failed to load external 'uri' for image[" + std::to_string(image_idx) + "] name = \"" + decoded_uri + @@ -4211,17 +4435,19 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, #endif } - if (*LoadImageData == nullptr) { + if (LoadImageData == nullptr) { if (err) { (*err) += "No LoadImageData callback specified.\n"; } return false; } - return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0), - static_cast(img.size()), load_image_user_data); + + return LoadImageData(image, image_idx, err, warn, 0, 0, &img.at(0), + static_cast(img.size()), load_image_user_data); } -static bool ParseTexture(Texture *texture, std::string *err, const detail::json &o, +static bool ParseTexture(Texture *texture, std::string *err, + const detail::json &o, bool store_original_json_for_extras_and_extensions, const std::string &basedir) { (void)basedir; @@ -4234,23 +4460,8 @@ static bool ParseTexture(Texture *texture, std::string *err, const detail::json texture->sampler = sampler; texture->source = source; - ParseExtensionsProperty(&texture->extensions, err, o); - ParseExtrasProperty(&texture->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texture->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texture->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(texture, err, o, + store_original_json_for_extras_and_extensions); ParseStringProperty(&texture->name, err, o, "name", false); @@ -4271,23 +4482,8 @@ static bool ParseTextureInfo( ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(texinfo, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -4307,23 +4503,8 @@ static bool ParseNormalTextureInfo( ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); ParseNumberProperty(&texinfo->scale, err, o, "scale", false); - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(texinfo, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -4343,23 +4524,8 @@ static bool ParseOcclusionTextureInfo( ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); ParseNumberProperty(&texinfo->strength, err, o, "strength", false); - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(texinfo, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -4367,7 +4533,8 @@ static bool ParseOcclusionTextureInfo( static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions, FsCallbacks *fs, const URICallbacks *uri_cb, - const std::string &basedir, const size_t max_buffer_size, bool is_binary = false, + const std::string &basedir, + const size_t max_buffer_size, bool is_binary = false, const unsigned char *bin_data = nullptr, size_t bin_size = 0) { size_t byteLength; @@ -4419,7 +4586,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, } if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri, basedir, /* required */ true, - byteLength, /* checkSize */ true, /* max_file_size */max_buffer_size, fs)) { + byteLength, /* checkSize */ true, + /* max_file_size */ max_buffer_size, fs)) { return false; } } @@ -4428,7 +4596,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, if ((bin_size == 0) || (bin_data == nullptr)) { if (err) { - (*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n"; + (*err) += + "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n"; } return false; } @@ -4467,7 +4636,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, } if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri, basedir, /* required */ true, byteLength, - /* checkSize */ true, /* max file size */max_buffer_size, fs)) { + /* checkSize */ true, + /* max file size */ max_buffer_size, fs)) { return false; } } @@ -4475,23 +4645,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, ParseStringProperty(&buffer->name, err, o, "name", false); - ParseExtensionsProperty(&buffer->extensions, err, o); - ParseExtrasProperty(&buffer->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - buffer->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - buffer->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(buffer, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -4547,23 +4702,8 @@ static bool ParseBufferView( ParseStringProperty(&bufferView->name, err, o, "name", false); - ParseExtensionsProperty(&bufferView->extensions, err, o); - ParseExtrasProperty(&bufferView->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - bufferView->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - bufferView->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(bufferView, err, o, + store_original_json_for_extras_and_extensions); bufferView->buffer = buffer; bufferView->byteOffset = byteOffset; @@ -4572,15 +4712,19 @@ static bool ParseBufferView( return true; } -static bool ParseSparseAccessor(Accessor *accessor, std::string *err, - const detail::json &o) { - accessor->sparse.isSparse = true; +static bool ParseSparseAccessor( + Accessor::Sparse *sparse, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + sparse->isSparse = true; int count = 0; if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) { return false; } + ParseExtrasAndExtensions(sparse, err, o, + store_original_json_for_extras_and_extensions); + detail::json_const_iterator indices_iterator; detail::json_const_iterator values_iterator; if (!detail::FindMember(o, "indices", indices_iterator)) { @@ -4596,37 +4740,45 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err, const detail::json &indices_obj = detail::GetValue(indices_iterator); const detail::json &values_obj = detail::GetValue(values_iterator); - int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0; + int indices_buffer_view = 0, component_type = 0; + size_t indices_byte_offset = 0; if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView", true, "SparseAccessor")) { return false; } - ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset", + ParseUnsignedProperty(&indices_byte_offset, err, indices_obj, "byteOffset", false); if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType", true, "SparseAccessor")) { return false; } - int values_buffer_view = 0, values_byte_offset = 0; + int values_buffer_view = 0; + size_t values_byte_offset = 0; if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView", true, "SparseAccessor")) { return false; } - ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset", + ParseUnsignedProperty(&values_byte_offset, err, values_obj, "byteOffset", false); - accessor->sparse.count = count; - accessor->sparse.indices.bufferView = indices_buffer_view; - accessor->sparse.indices.byteOffset = indices_byte_offset; - accessor->sparse.indices.componentType = component_type; - accessor->sparse.values.bufferView = values_buffer_view; - accessor->sparse.values.byteOffset = values_byte_offset; + sparse->count = count; + sparse->indices.bufferView = indices_buffer_view; + sparse->indices.byteOffset = indices_byte_offset; + sparse->indices.componentType = component_type; + ParseExtrasAndExtensions(&sparse->indices, err, indices_obj, + store_original_json_for_extras_and_extensions); + + sparse->values.bufferView = values_buffer_view; + sparse->values.byteOffset = values_byte_offset; + ParseExtrasAndExtensions(&sparse->values, err, values_obj, + store_original_json_for_extras_and_extensions); return true; } -static bool ParseAccessor(Accessor *accessor, std::string *err, const detail::json &o, +static bool ParseAccessor(Accessor *accessor, std::string *err, + const detail::json &o, bool store_original_json_for_extras_and_extensions) { int bufferView = -1; ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor"); @@ -4706,29 +4858,16 @@ static bool ParseAccessor(Accessor *accessor, std::string *err, const detail::js } } - ParseExtensionsProperty(&(accessor->extensions), err, o); - ParseExtrasProperty(&(accessor->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - accessor->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - accessor->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(accessor, err, o, + store_original_json_for_extras_and_extensions); // check if accessor has a "sparse" object: detail::json_const_iterator iterator; if (detail::FindMember(o, "sparse", iterator)) { // here this accessor has a "sparse" subobject - return ParseSparseAccessor(accessor, err, detail::GetValue(iterator)); + return ParseSparseAccessor(&accessor->sparse, err, + detail::GetValue(iterator), + store_original_json_for_extras_and_extensions); } return true; @@ -4828,8 +4967,9 @@ static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh, } static bool ParseDracoExtension(Primitive *primitive, Model *model, - std::string *err, - const Value &dracoExtensionValue) { + std::string *err, std::string *warn, + const Value &dracoExtensionValue, + ParseStrictness strictness) { (void)err; auto bufferViewValue = dracoExtensionValue.Get("bufferView"); if (!bufferViewValue.IsInt()) return false; @@ -4861,6 +5001,33 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, // create new bufferView for indices if (primitive->indices >= 0) { + if (strictness == ParseStrictness::Permissive) { + const draco::PointIndex::ValueType numPoint = mesh->num_points(); + // handle the situation where the stored component type does not match the + // required type for the actual number of stored points + int supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + if (numPoint < static_cast( + std::numeric_limits::max())) { + supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + } else if ( + numPoint < static_cast( + std::numeric_limits::max())) { + supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; + } else { + supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT; + } + + if (supposedComponentType > model->accessors[primitive->indices].componentType) { + if (warn) { + (*warn) += + "GLTF component type " + std::to_string(model->accessors[primitive->indices].componentType) + + " is not sufficient for number of stored points," + " treating as " + std::to_string(supposedComponentType) + "\n"; + } + model->accessors[primitive->indices].componentType = supposedComponentType; + } + } + int32_t componentSize = GetComponentSizeInBytes( model->accessors[primitive->indices].componentType); Buffer decodedIndexBuffer; @@ -4926,9 +5093,11 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, } #endif -static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, +static bool ParsePrimitive(Primitive *primitive, Model *model, + std::string *err, std::string *warn, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { + bool store_original_json_for_extras_and_extensions, + ParseStrictness strictness) { int material = -1; ParseIntegerProperty(&material, err, o, "material", false); primitive->material = material; @@ -4950,7 +5119,8 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, if (detail::FindMember(o, "targets", targetsObject) && detail::IsArray(detail::GetValue(targetsObject))) { auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject)); - for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(targetsObject)); + for (detail::json_const_array_iterator i = + detail::ArrayBegin(detail::GetValue(targetsObject)); i != targetsObjectEnd; ++i) { std::map targetAttribues; @@ -4969,51 +5139,44 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, } } - ParseExtrasProperty(&(primitive->extras), o); - ParseExtensionsProperty(&primitive->extensions, err, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - primitive->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - primitive->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(primitive, err, o, + store_original_json_for_extras_and_extensions); #ifdef TINYGLTF_ENABLE_DRACO auto dracoExtension = primitive->extensions.find("KHR_draco_mesh_compression"); if (dracoExtension != primitive->extensions.end()) { - ParseDracoExtension(primitive, model, err, dracoExtension->second); + ParseDracoExtension(primitive, model, err, warn, dracoExtension->second, strictness); } #else (void)model; + (void)warn; + (void)strictness; #endif return true; } -static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { +static bool ParseMesh(Mesh *mesh, Model *model, + std::string *err, std::string *warn, + const detail::json &o, + bool store_original_json_for_extras_and_extensions, + ParseStrictness strictness) { ParseStringProperty(&mesh->name, err, o, "name", false); mesh->primitives.clear(); detail::json_const_iterator primObject; if (detail::FindMember(o, "primitives", primObject) && detail::IsArray(detail::GetValue(primObject))) { - detail::json_const_array_iterator primEnd = detail::ArrayEnd(detail::GetValue(primObject)); - for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(primObject)); + detail::json_const_array_iterator primEnd = + detail::ArrayEnd(detail::GetValue(primObject)); + for (detail::json_const_array_iterator i = + detail::ArrayBegin(detail::GetValue(primObject)); i != primEnd; ++i) { Primitive primitive; - if (ParsePrimitive(&primitive, model, err, *i, - store_original_json_for_extras_and_extensions)) { + if (ParsePrimitive(&primitive, model, err, warn, *i, + store_original_json_for_extras_and_extensions, + strictness)) { // Only add the primitive if the parsing succeeds. mesh->primitives.emplace_back(std::move(primitive)); } @@ -5023,23 +5186,8 @@ static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const detail:: // Should probably check if has targets and if dimensions fit ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false); - ParseExtensionsProperty(&mesh->extensions, err, o); - ParseExtrasProperty(&(mesh->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - mesh->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - mesh->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(mesh, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -5072,21 +5220,87 @@ static bool ParseNode(Node *node, std::string *err, const detail::json &o, ParseNumberArrayProperty(&node->weights, err, o, "weights", false); - ParseExtensionsProperty(&node->extensions, err, o); - ParseExtrasProperty(&(node->extras), o); + ParseExtrasAndExtensions(node, err, o, + store_original_json_for_extras_and_extensions); - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - node->extensions_json_string = detail::JsonToString(detail::GetValue(it)); + // KHR_lights_punctual: parse light source reference + int light = -1; + if (node->extensions.count("KHR_lights_punctual") != 0) { + auto const &light_ext = node->extensions["KHR_lights_punctual"]; + if (light_ext.Has("light")) { + light = light_ext.Get("light").GetNumberAsInt(); + } else { + if (err) { + *err += + "Node has extension KHR_lights_punctual, but does not reference " + "a light source.\n"; } + return false; } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - node->extras_json_string = detail::JsonToString(detail::GetValue(it)); + } + node->light = light; + + // KHR_audio: parse audio source reference + int emitter = -1; + if (node->extensions.count("KHR_audio") != 0) { + auto const &audio_ext = node->extensions["KHR_audio"]; + if (audio_ext.Has("emitter")) { + emitter = audio_ext.Get("emitter").GetNumberAsInt(); + } else { + if (err) { + *err += + "Node has extension KHR_audio, but does not reference " + "a audio emitter.\n"; + } + return false; + } + } + node->emitter = emitter; + + node->lods.clear(); + if (node->extensions.count("MSFT_lod") != 0) { + auto const &msft_lod_ext = node->extensions["MSFT_lod"]; + if (msft_lod_ext.Has("ids")) { + auto idsArr = msft_lod_ext.Get("ids"); + for (size_t i = 0; i < idsArr.ArrayLen(); ++i) { + node->lods.emplace_back(idsArr.Get(i).GetNumberAsInt()); + } + } else { + if (err) { + *err += + "Node has extension MSFT_lod, but does not reference " + "other nodes via their ids.\n"; + } + return false; + } + } + + return true; +} + +static bool ParseScene(Scene *scene, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&scene->name, err, o, "name", false); + ParseIntegerArrayProperty(&scene->nodes, err, o, "nodes", false); + + ParseExtrasAndExtensions(scene, err, o, + store_original_json_for_extras_and_extensions); + + // Parse KHR_audio global emitters + if (scene->extensions.count("KHR_audio") != 0) { + auto const &audio_ext = scene->extensions["KHR_audio"]; + if (audio_ext.Has("emitters")) { + auto emittersArr = audio_ext.Get("emitters"); + for (size_t i = 0; i < emittersArr.ArrayLen(); ++i) { + scene->audioEmitters.emplace_back(emittersArr.Get(i).GetNumberAsInt()); + } + } else { + if (err) { + *err += + "Node has extension KHR_audio, but does not reference " + "a audio emitter.\n"; } + return false; } } @@ -5126,7 +5340,8 @@ static bool ParsePbrMetallicRoughness( { detail::json_const_iterator it; if (detail::FindMember(o, "metallicRoughnessTexture", it)) { - ParseTextureInfo(&pbr->metallicRoughnessTexture, err, detail::GetValue(it), + ParseTextureInfo(&pbr->metallicRoughnessTexture, err, + detail::GetValue(it), store_original_json_for_extras_and_extensions); } } @@ -5134,35 +5349,30 @@ static bool ParsePbrMetallicRoughness( ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false); ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false); - ParseExtensionsProperty(&pbr->extensions, err, o); - ParseExtrasProperty(&pbr->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - pbr->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - pbr->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(pbr, err, o, + store_original_json_for_extras_and_extensions); return true; } -static bool ParseMaterial(Material *material, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { +static bool ParseMaterial(Material *material, std::string *err, std::string *warn, + const detail::json &o, + bool store_original_json_for_extras_and_extensions, + ParseStrictness strictness) { ParseStringProperty(&material->name, err, o, "name", /* required */ false); if (ParseNumberArrayProperty(&material->emissiveFactor, err, o, "emissiveFactor", /* required */ false)) { - if (material->emissiveFactor.size() != 3) { + if (strictness==ParseStrictness::Permissive && material->emissiveFactor.size() == 4) { + if (warn) { + (*warn) += + "Array length of `emissiveFactor` parameter in " + "material must be 3, but got 4\n"; + } + material->emissiveFactor.resize(3); + } + else if (material->emissiveFactor.size() != 3) { if (err) { (*err) += "Array length of `emissiveFactor` parameter in " @@ -5195,7 +5405,8 @@ static bool ParseMaterial(Material *material, std::string *err, const detail::js { detail::json_const_iterator it; if (detail::FindMember(o, "normalTexture", it)) { - ParseNormalTextureInfo(&material->normalTexture, err, detail::GetValue(it), + ParseNormalTextureInfo(&material->normalTexture, err, + detail::GetValue(it), store_original_json_for_extras_and_extensions); } } @@ -5203,7 +5414,8 @@ static bool ParseMaterial(Material *material, std::string *err, const detail::js { detail::json_const_iterator it; if (detail::FindMember(o, "occlusionTexture", it)) { - ParseOcclusionTextureInfo(&material->occlusionTexture, err, detail::GetValue(it), + ParseOcclusionTextureInfo(&material->occlusionTexture, err, + detail::GetValue(it), store_original_json_for_extras_and_extensions); } } @@ -5238,8 +5450,8 @@ static bool ParseMaterial(Material *material, std::string *err, const detail::js for (; itVal != itValEnd; ++itVal) { Parameter param; - if (ParseParameterProperty(¶m, err, values_object, detail::GetKey(itVal), - false)) { + if (ParseParameterProperty(¶m, err, values_object, + detail::GetKey(itVal), false)) { material->values.emplace(detail::GetKey(itVal), std::move(param)); } } @@ -5258,22 +5470,25 @@ static bool ParseMaterial(Material *material, std::string *err, const detail::js } } - material->extensions.clear(); - ParseExtensionsProperty(&material->extensions, err, o); - ParseExtrasProperty(&(material->extras), o); + material->extensions.clear(); // Note(agnat): Why? + ParseExtrasAndExtensions(material, err, o, + store_original_json_for_extras_and_extensions); - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extensions", eit)) { - material->extensions_json_string = detail::JsonToString(detail::GetValue(eit)); + material->lods.clear(); + if (material->extensions.count("MSFT_lod") != 0) { + auto const &msft_lod_ext = material->extensions["MSFT_lod"]; + if (msft_lod_ext.Has("ids")) { + auto idsArr = msft_lod_ext.Get("ids"); + for (size_t i = 0; i < idsArr.ArrayLen(); ++i) { + material->lods.emplace_back(idsArr.Get(i).GetNumberAsInt()); } - } - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extras", eit)) { - material->extras_json_string = detail::JsonToString(detail::GetValue(eit)); + } else { + if (err) { + *err += + "Material has extension MSFT_lod, but does not reference " + "other materials via their ids.\n"; } + return false; } } @@ -5294,7 +5509,8 @@ static bool ParseAnimationChannel( } detail::json_const_iterator targetIt; - if (detail::FindMember(o, "target", targetIt) && detail::IsObject(detail::GetValue(targetIt))) { + if (detail::FindMember(o, "target", targetIt) && + detail::IsObject(detail::GetValue(targetIt))) { const detail::json &target_object = detail::GetValue(targetIt); ParseIntegerProperty(&targetIndex, err, target_object, "node", false); @@ -5307,10 +5523,21 @@ static bool ParseAnimationChannel( return false; } ParseExtensionsProperty(&channel->target_extensions, err, target_object); + ParseExtrasProperty(&channel->target_extras, target_object); if (store_original_json_for_extras_and_extensions) { - detail::json_const_iterator it; - if (detail::FindMember(target_object, "extensions", it)) { - channel->target_extensions_json_string = detail::JsonToString(detail::GetValue(it)); + { + detail::json_const_iterator it; + if (detail::FindMember(target_object, "extensions", it)) { + channel->target_extensions_json_string = + detail::JsonToString(detail::GetValue(it)); + } + } + { + detail::json_const_iterator it; + if (detail::FindMember(target_object, "extras", it)) { + channel->target_extras_json_string = + detail::JsonToString(detail::GetValue(it)); + } } } } @@ -5318,23 +5545,8 @@ static bool ParseAnimationChannel( channel->sampler = samplerIndex; channel->target_node = targetIndex; - ParseExtensionsProperty(&channel->extensions, err, o); - ParseExtrasProperty(&(channel->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - channel->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - channel->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(channel, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -5346,8 +5558,10 @@ static bool ParseAnimation(Animation *animation, std::string *err, detail::json_const_iterator channelsIt; if (detail::FindMember(o, "channels", channelsIt) && detail::IsArray(detail::GetValue(channelsIt))) { - detail::json_const_array_iterator channelEnd = detail::ArrayEnd(detail::GetValue(channelsIt)); - for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(channelsIt)); + detail::json_const_array_iterator channelEnd = + detail::ArrayEnd(detail::GetValue(channelsIt)); + for (detail::json_const_array_iterator i = + detail::ArrayBegin(detail::GetValue(channelsIt)); i != channelEnd; ++i) { AnimationChannel channel; if (ParseAnimationChannel( @@ -5362,7 +5576,8 @@ static bool ParseAnimation(Animation *animation, std::string *err, { detail::json_const_iterator samplerIt; - if (detail::FindMember(o, "samplers", samplerIt) && detail::IsArray(detail::GetValue(samplerIt))) { + if (detail::FindMember(o, "samplers", samplerIt) && + detail::IsArray(detail::GetValue(samplerIt))) { const detail::json &sampler_array = detail::GetValue(samplerIt); detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array); @@ -5390,23 +5605,8 @@ static bool ParseAnimation(Animation *animation, std::string *err, } sampler.input = inputIndex; sampler.output = outputIndex; - ParseExtensionsProperty(&(sampler.extensions), err, o); - ParseExtrasProperty(&(sampler.extras), s); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extensions", eit)) { - sampler.extensions_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - { - detail::json_const_iterator eit; - if (detail::FindMember(o, "extras", eit)) { - sampler.extras_json_string = detail::JsonToString(detail::GetValue(eit)); - } - } - } + ParseExtrasAndExtensions(&sampler, err, s, + store_original_json_for_extras_and_extensions); animation->samplers.emplace_back(std::move(sampler)); } @@ -5415,28 +5615,14 @@ static bool ParseAnimation(Animation *animation, std::string *err, ParseStringProperty(&animation->name, err, o, "name", false); - ParseExtensionsProperty(&animation->extensions, err, o); - ParseExtrasProperty(&(animation->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - animation->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - animation->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(animation, err, o, + store_original_json_for_extras_and_extensions); return true; } -static bool ParseSampler(Sampler *sampler, std::string *err, const detail::json &o, +static bool ParseSampler(Sampler *sampler, std::string *err, + const detail::json &o, bool store_original_json_for_extras_and_extensions) { ParseStringProperty(&sampler->name, err, o, "name", false); @@ -5461,23 +5647,8 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const detail::json sampler->wrapT = wrapT; // sampler->wrapR = wrapR; - ParseExtensionsProperty(&(sampler->extensions), err, o); - ParseExtrasProperty(&(sampler->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - sampler->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - sampler->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(sampler, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -5497,26 +5668,11 @@ static bool ParseSkin(Skin *skin, std::string *err, const detail::json &o, skin->skeleton = skeleton; int invBind = -1; - ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin"); + ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", false, "Skin"); skin->inverseBindMatrices = invBind; - ParseExtensionsProperty(&(skin->extensions), err, o); - ParseExtrasProperty(&(skin->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - skin->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - skin->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(skin, err, o, + store_original_json_for_extras_and_extensions); return true; } @@ -5547,51 +5703,22 @@ static bool ParsePerspectiveCamera( camera->yfov = yfov; camera->znear = znear; - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(camera, err, o, + store_original_json_for_extras_and_extensions); // TODO(syoyo): Validate parameter values. return true; } -static bool ParseSpotLight(SpotLight *light, std::string *err, const detail::json &o, +static bool ParseSpotLight(SpotLight *light, std::string *err, + const detail::json &o, bool store_original_json_for_extras_and_extensions) { ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false); ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false); - ParseExtensionsProperty(&light->extensions, err, o); - ParseExtrasProperty(&light->extras, o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - light->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - light->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(light, err, o, + store_original_json_for_extras_and_extensions); // TODO(syoyo): Validate parameter values. @@ -5622,23 +5749,8 @@ static bool ParseOrthographicCamera( return false; } - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); - - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - } + ParseExtrasAndExtensions(camera, err, o, + store_original_json_for_extras_and_extensions); camera->xmag = xmag; camera->ymag = ymag; @@ -5720,85 +5832,160 @@ static bool ParseCamera(Camera *camera, std::string *err, const detail::json &o, ParseStringProperty(&camera->name, err, o, "name", false); - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); + ParseExtrasAndExtensions(camera, err, o, + store_original_json_for_extras_and_extensions); - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); + return true; +} + +static bool ParseLight(Light *light, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + if (!ParseStringProperty(&light->type, err, o, "type", true)) { + return false; + } + + if (light->type == "spot") { + detail::json_const_iterator spotIt; + if (!detail::FindMember(o, "spot", spotIt)) { + if (err) { + std::stringstream ss; + ss << "Spot light description not found." << std::endl; + (*err) += ss.str(); } + return false; } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); + + const detail::json &v = detail::GetValue(spotIt); + if (!detail::IsObject(v)) { + if (err) { + std::stringstream ss; + ss << "\"spot\" is not a JSON object." << std::endl; + (*err) += ss.str(); } + return false; + } + + if (!ParseSpotLight(&light->spot, err, v, + store_original_json_for_extras_and_extensions)) { + return false; } } + ParseStringProperty(&light->name, err, o, "name", false); + ParseNumberArrayProperty(&light->color, err, o, "color", false); + ParseNumberProperty(&light->range, err, o, "range", false); + ParseNumberProperty(&light->intensity, err, o, "intensity", false); + + ParseExtrasAndExtensions(light, err, o, + store_original_json_for_extras_and_extensions); + + return true; +} + +static bool ParsePositionalEmitter( + PositionalEmitter *positional, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + ParseNumberProperty(&positional->coneInnerAngle, err, o, "coneInnerAngle", + false); + ParseNumberProperty(&positional->coneOuterAngle, err, o, "coneOuterAngle", + false); + ParseNumberProperty(&positional->coneOuterGain, err, o, "coneOuterGain", + false); + ParseNumberProperty(&positional->maxDistance, err, o, "maxDistance", false); + ParseNumberProperty(&positional->refDistance, err, o, "refDistance", false); + ParseNumberProperty(&positional->rolloffFactor, err, o, "rolloffFactor", + false); + + ParseExtrasAndExtensions(positional, err, o, + store_original_json_for_extras_and_extensions); + return true; } -static bool ParseLight(Light *light, std::string *err, const detail::json &o, - bool store_original_json_for_extras_and_extensions) { - if (!ParseStringProperty(&light->type, err, o, "type", true)) { +static bool ParseAudioEmitter( + AudioEmitter *emitter, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + if (!ParseStringProperty(&emitter->type, err, o, "type", true)) { return false; } - if (light->type == "spot") { - detail::json_const_iterator spotIt; - if (!detail::FindMember(o, "spot", spotIt)) { + if (emitter->type == "positional") { + detail::json_const_iterator positionalIt; + if (!detail::FindMember(o, "positional", positionalIt)) { if (err) { std::stringstream ss; - ss << "Spot light description not found." << std::endl; + ss << "Positional emitter description not found." << std::endl; (*err) += ss.str(); } return false; } - const detail::json &v = detail::GetValue(spotIt); + const detail::json &v = detail::GetValue(positionalIt); if (!detail::IsObject(v)) { if (err) { std::stringstream ss; - ss << "\"spot\" is not a JSON object." << std::endl; + ss << "\"positional\" is not a JSON object." << std::endl; (*err) += ss.str(); } return false; } - if (!ParseSpotLight(&light->spot, err, v, - store_original_json_for_extras_and_extensions)) { + if (!ParsePositionalEmitter( + &emitter->positional, err, v, + store_original_json_for_extras_and_extensions)) { return false; } } - ParseStringProperty(&light->name, err, o, "name", false); - ParseNumberArrayProperty(&light->color, err, o, "color", false); - ParseNumberProperty(&light->range, err, o, "range", false); - ParseNumberProperty(&light->intensity, err, o, "intensity", false); - ParseExtensionsProperty(&light->extensions, err, o); - ParseExtrasProperty(&(light->extras), o); + ParseStringProperty(&emitter->name, err, o, "name", false); + ParseNumberProperty(&emitter->gain, err, o, "gain", false); + ParseBooleanProperty(&emitter->loop, err, o, "loop", false); + ParseBooleanProperty(&emitter->playing, err, o, "playing", false); + ParseStringProperty(&emitter->distanceModel, err, o, "distanceModel", false); + ParseIntegerProperty(&emitter->source, err, o, "source", true); - if (store_original_json_for_extras_and_extensions) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - light->extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - light->extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } + ParseExtrasAndExtensions(emitter, err, o, + store_original_json_for_extras_and_extensions); + + return true; +} + +static bool ParseAudioSource( + AudioSource *source, std::string *err, const detail::json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&source->name, err, o, "name", false); + ParseStringProperty(&source->uri, err, o, "uri", false); + + if (source->uri.empty()) { + ParseIntegerProperty(&source->bufferView, err, o, "bufferView", true); + ParseStringProperty(&source->mimeType, err, o, "mimeType", true); } + ParseExtrasAndExtensions(source, err, o, + store_original_json_for_extras_and_extensions); + return true; } +namespace detail { + +template +bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) { + detail::json_const_iterator itm; + if (detail::FindMember(_v, member, itm) && + detail::IsArray(detail::GetValue(itm))) { + const detail::json &root = detail::GetValue(itm); + auto it = detail::ArrayBegin(root); + auto end = detail::ArrayEnd(root); + for (; it != end; ++it) { + if (!cb(*it)) return false; + } + } + return true; +}; + +} // end of namespace detail + bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, const char *json_str, unsigned int json_str_length, @@ -5850,7 +6037,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, { bool version_found = false; detail::json_const_iterator it; - if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) { + if (detail::FindMember(v, "asset", it) && + detail::IsObject(detail::GetValue(it))) { auto &itObj = detail::GetValue(it); detail::json_const_iterator version_it; std::string versionStr; @@ -5872,9 +6060,11 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // scene is not mandatory. // FIXME Maybe a better way to handle it than removing the code - auto IsArrayMemberPresent = [](const detail::json &_v, const char *name) -> bool { + auto IsArrayMemberPresent = [](const detail::json &_v, + const char *name) -> bool { detail::json_const_iterator it; - return detail::FindMember(_v, name, it) && detail::IsArray(detail::GetValue(it)); + return detail::FindMember(_v, name, it) && + detail::IsArray(detail::GetValue(it)); }; { @@ -5926,21 +6116,14 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } } - model->buffers.clear(); - model->bufferViews.clear(); - model->accessors.clear(); - model->meshes.clear(); - model->cameras.clear(); - model->nodes.clear(); - model->extensionsUsed.clear(); - model->extensionsRequired.clear(); - model->extensions.clear(); - model->defaultScene = -1; + // Reset the model + (*model) = Model(); // 1. Parse Asset { detail::json_const_iterator it; - if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) { + if (detail::FindMember(v, "asset", it) && + detail::IsObject(detail::GetValue(it))) { const detail::json &root = detail::GetValue(it); ParseAsset(&model->asset, err, root, @@ -5948,28 +6131,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } } -#ifdef TINYGLTF_USE_CPP14 - auto ForEachInArray = [](const detail::json &_v, const char *member, - const auto &cb) -> bool -#else - // The std::function<> implementation can be less efficient because it will - // allocate heap when the size of the captured lambda is above 16 bytes with - // clang and gcc, but it does not require C++14. - auto ForEachInArray = [](const detail::json &_v, const char *member, - const std::function &cb) -> bool -#endif - { - detail::json_const_iterator itm; - if (detail::FindMember(_v, member, itm) && detail::IsArray(detail::GetValue(itm))) { - const detail::json &root = detail::GetValue(itm); - auto it = detail::ArrayBegin(root); - auto end = detail::ArrayEnd(root); - for (; it != end; ++it) { - if (!cb(*it)) return false; - } - } - return true; - }; + using detail::ForEachInArray; // 2. Parse extensionUsed { @@ -6002,7 +6164,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, Buffer buffer; if (!ParseBuffer(&buffer, err, o, store_original_json_for_extras_and_extensions_, &fs, - &uri_cb, base_dir, max_external_file_size_, is_binary_, bin_data_, bin_size_)) { + &uri_cb, base_dir, max_external_file_size_, is_binary_, + bin_data_, bin_size_)) { return false; } @@ -6072,8 +6235,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, return false; } Mesh mesh; - if (!ParseMesh(&mesh, model, err, o, - store_original_json_for_extras_and_extensions_)) { + if (!ParseMesh(&mesh, model, err, warn, o, + store_original_json_for_extras_and_extensions_, + strictness_)) { return false; } @@ -6101,20 +6265,22 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, return false; } - auto bufferView = + const auto bufferView = model->accessors[size_t(primitive.indices)].bufferView; - if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) { + if (bufferView < 0) { + // skip, bufferView could be null(-1) for certain extensions + } else if (size_t(bufferView) >= model->bufferViews.size()) { if (err) { (*err) += "accessor[" + std::to_string(primitive.indices) + "] invalid bufferView"; } return false; + } else { + model->bufferViews[size_t(bufferView)].target = + TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; + // we could optionally check if accessors' bufferView type is Scalar, as + // it should be } - - model->bufferViews[size_t(bufferView)].target = - TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; - // we could optionally check if accessors' bufferView type is Scalar, as - // it should be } for (auto &attribute : primitive.attributes) { @@ -6135,7 +6301,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, if (accessorsIndex < model->accessors.size()) { const auto bufferView = model->accessors[accessorsIndex].bufferView; // bufferView could be null(-1) for sparse morph target - if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) { + if (bufferView >= 0 && + bufferView < (int)model->bufferViews.size()) { model->bufferViews[size_t(bufferView)].target = TINYGLTF_TARGET_ARRAY_BUFFER; } @@ -6178,30 +6345,11 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } return false; } - std::vector nodes; - ParseIntegerArrayProperty(&nodes, err, o, "nodes", false); Scene scene; - scene.nodes = std::move(nodes); - - ParseStringProperty(&scene.name, err, o, "name", false); - - ParseExtensionsProperty(&scene.extensions, err, o); - ParseExtrasProperty(&scene.extras, o); - - if (store_original_json_for_extras_and_extensions_) { - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extensions", it)) { - scene.extensions_json_string = detail::JsonToString(detail::GetValue(it)); - } - } - { - detail::json_const_iterator it; - if (detail::FindMember(o, "extras", it)) { - scene.extras_json_string = detail::JsonToString(detail::GetValue(it)); - } - } + if (!ParseScene(&scene, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; } model->scenes.emplace_back(std::move(scene)); @@ -6217,7 +6365,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, { detail::json_const_iterator rootIt; int iVal; - if (detail::FindMember(v, "scene", rootIt) && detail::GetInt(detail::GetValue(rootIt), iVal)) { + if (detail::FindMember(v, "scene", rootIt) && + detail::GetInt(detail::GetValue(rootIt), iVal)) { model->defaultScene = iVal; } } @@ -6234,8 +6383,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, Material material; ParseStringProperty(&material.name, err, o, "name", false); - if (!ParseMaterial(&material, err, o, - store_original_json_for_extras_and_extensions_)) { + if (!ParseMaterial(&material, err, warn, o, + store_original_json_for_extras_and_extensions_, + strictness_)) { return false; } @@ -6258,6 +6408,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, load_image_user_data = load_image_user_data_; } else { load_image_option.preserve_channels = preserve_image_channels_; + load_image_option.as_is = images_as_is_; load_image_user_data = reinterpret_cast(&load_image_option); } @@ -6273,8 +6424,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, Image image; if (!ParseImage(&image, idx, err, warn, o, store_original_json_for_extras_and_extensions_, base_dir, - max_external_file_size_, &fs, &uri_cb, &this->LoadImageData, - load_image_user_data)) { + max_external_file_size_, &fs, &uri_cb, + this->LoadImageData, load_image_user_data)) { return false; } @@ -6303,7 +6454,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } const Buffer &buffer = model->buffers[size_t(bufferView.buffer)]; - if (*LoadImageData == nullptr) { + if (LoadImageData == nullptr) { if (err) { (*err) += "No LoadImageData callback specified.\n"; } @@ -6449,13 +6600,15 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } } - // 17. Parse Extensions - ParseExtensionsProperty(&model->extensions, err, v); + // 17. Parse Extras & Extensions + ParseExtrasAndExtensions(model, err, v, + store_original_json_for_extras_and_extensions_); // 18. Specific extension implementations { detail::json_const_iterator rootIt; - if (detail::FindMember(v, "extensions", rootIt) && detail::IsObject(detail::GetValue(rootIt))) { + if (detail::FindMember(v, "extensions", rootIt) && + detail::IsObject(detail::GetValue(rootIt))) { const detail::json &root = detail::GetValue(rootIt); detail::json_const_iterator it(detail::ObjectBegin(root)); @@ -6463,7 +6616,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, for (; it != itEnd; ++it) { // parse KHR_lights_punctual extension std::string key(detail::GetKey(it)); - if ((key == "KHR_lights_punctual") && detail::IsObject(detail::GetValue(it))) { + if ((key == "KHR_lights_punctual") && + detail::IsObject(detail::GetValue(it))) { const detail::json &object = detail::GetValue(it); detail::json_const_iterator itLight; if (detail::FindMember(object, "lights", itLight)) { @@ -6484,16 +6638,50 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } } } - } - } - } + // parse KHR_audio extension + if ((key == "KHR_audio") && detail::IsObject(detail::GetValue(it))) { + const detail::json &object = detail::GetValue(it); + detail::json_const_iterator itKhrAudio; + if (detail::FindMember(object, "emitters", itKhrAudio)) { + const detail::json &emitters = detail::GetValue(itKhrAudio); + if (!detail::IsArray(emitters)) { + continue; + } + + auto arrayIt(detail::ArrayBegin(emitters)); + auto arrayItEnd(detail::ArrayEnd(emitters)); + for (; arrayIt != arrayItEnd; ++arrayIt) { + AudioEmitter emitter; + if (!ParseAudioEmitter( + &emitter, err, *arrayIt, + store_original_json_for_extras_and_extensions_)) { + return false; + } + model->audioEmitters.emplace_back(std::move(emitter)); + } + } - // 19. Parse Extras - ParseExtrasProperty(&model->extras, v); + if (detail::FindMember(object, "sources", itKhrAudio)) { + const detail::json &sources = detail::GetValue(itKhrAudio); + if (!detail::IsArray(sources)) { + continue; + } - if (store_original_json_for_extras_and_extensions_) { - model->extras_json_string = detail::JsonToString(v["extras"]); - model->extensions_json_string = detail::JsonToString(v["extensions"]); + auto arrayIt(detail::ArrayBegin(sources)); + auto arrayItEnd(detail::ArrayEnd(sources)); + for (; arrayIt != arrayItEnd; ++arrayIt) { + AudioSource source; + if (!ParseAudioSource( + &source, err, *arrayIt, + store_original_json_for_extras_and_extensions_)) { + return false; + } + model->audioSources.emplace_back(std::move(source)); + } + } + } + } + } } return true; @@ -6578,16 +6766,16 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, return false; } - unsigned int version; // 4 bytes - unsigned int length; // 4 bytes + unsigned int version; // 4 bytes + unsigned int length; // 4 bytes unsigned int chunk0_length; // 4 bytes unsigned int chunk0_format; // 4 bytes; memcpy(&version, bytes + 4, 4); swap4(&version); - memcpy(&length, bytes + 8, 4); + memcpy(&length, bytes + 8, 4); // Total glb size, including header and all chunks. swap4(&length); - memcpy(&chunk0_length, bytes + 12, 4); // JSON data length + memcpy(&chunk0_length, bytes + 12, 4); // JSON data length swap4(&chunk0_length); memcpy(&chunk0_format, bytes + 16, 4); swap4(&chunk0_format); @@ -6602,13 +6790,16 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, // Use 64bit uint to avoid integer overflow. uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length); - if (header_and_json_size > std::numeric_limits::max()) { + if (header_and_json_size > (std::numeric_limits::max)()) { // Do not allow 4GB or more GLB data. - (*err) = "Invalid glTF binary. GLB data exceeds 4GB."; + if (err) { + (*err) = "Invalid glTF binary. GLB data exceeds 4GB."; + } + return false; } - if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) || - (header_and_json_size > uint64_t(length)) || + if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || + (length > size) || (header_and_json_size > uint64_t(length)) || (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format. if (err) { (*err) = "Invalid glTF binary."; @@ -6623,77 +6814,105 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, if (err) { (*err) = "JSON Chunk end does not aligned to a 4-byte boundary."; } + return false; } - //std::cout << "header_and_json_size = " << header_and_json_size << "\n"; - //std::cout << "length = " << length << "\n"; + // std::cout << "header_and_json_size = " << header_and_json_size << "\n"; + // std::cout << "length = " << length << "\n"; // Chunk1(BIN) data - // The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted. - // So when header + JSON data == binary size, Chunk1 is omitted. + // The spec says: When the binary buffer is empty or when it is stored by + // other means, this chunk SHOULD be omitted. So when header + JSON data == + // binary size, Chunk1 is omitted. if (header_and_json_size == uint64_t(length)) { - bin_data_ = nullptr; bin_size_ = 0; } else { // Read Chunk1 info(BIN data) - // At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aligned to 4 bytes) - if ((header_and_json_size + 12ull) > uint64_t(length)) { + // + // issue-440: + // 'SHOULD' in glTF spec means 'RECOMMENDED', + // So there is a situation that Chunk1(BIN) is composed of zero-sized BIN data + // (chunksize(0) + binformat(BIN) = 8bytes). + // + if ((header_and_json_size + 8ull) > uint64_t(length)) { if (err) { - (*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n"; + (*err) = + "Insufficient storage space for Chunk1(BIN data). At least Chunk1 " + "Must have 8 or more bytes, but got " + + std::to_string((header_and_json_size + 8ull) - uint64_t(length)) + + ".\n"; } return false; } - unsigned int chunk1_length; // 4 bytes - unsigned int chunk1_format; // 4 bytes; - memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length + unsigned int chunk1_length{0}; // 4 bytes + unsigned int chunk1_format{0}; // 4 bytes; + memcpy(&chunk1_length, bytes + header_and_json_size, + 4); // Bin data length swap4(&chunk1_length); memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4); swap4(&chunk1_format); - //std::cout << "chunk1_length = " << chunk1_length << "\n"; - - if (chunk1_length < 4) { + if (chunk1_format != 0x004e4942) { if (err) { - (*err) = "Insufficient Chunk1(BIN) data size."; + (*err) = "Invalid chunkType for Chunk1."; } return false; } - if ((chunk1_length % 4) != 0) { - if (err) { - (*err) = "BIN Chunk end does not aligned to a 4-byte boundary."; + if (chunk1_length == 0) { + + if (header_and_json_size + 8 > uint64_t(length)) { + if (err) { + (*err) = "BIN Chunk header location exceeds the GLB size."; + } + return false; } - return false; - } - if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) { - if (err) { - (*err) = "BIN Chunk data length exceeds the GLB size."; + bin_data_ = nullptr; + + } else { + + // When BIN chunk size is not zero, at least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin + // payload could be 1~3 bytes, but need to be aligned to 4 bytes) + + if (chunk1_length < 4) { + if (err) { + (*err) = "Insufficient Chunk1(BIN) data size."; + } + return false; } - return false; - } - if (chunk1_format != 0x004e4942) { - if (err) { - (*err) = "Invalid type for chunk1 data."; + if ((chunk1_length % 4) != 0) { + if (strictness_==ParseStrictness::Permissive) { + if (warn) { + (*warn) += "BIN Chunk end is not aligned to a 4-byte boundary.\n"; + } + } + else { + if (err) { + (*err) = "BIN Chunk end is not aligned to a 4-byte boundary."; + } + return false; + } } - return false; - } - //std::cout << "chunk1_length = " << chunk1_length << "\n"; + // +8 chunk1 header size. + if (uint64_t(chunk1_length) + header_and_json_size + 8 > uint64_t(length)) { + if (err) { + (*err) = "BIN Chunk data length exceeds the GLB size."; + } + return false; + } - bin_data_ = bytes + header_and_json_size + - 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format) + bin_data_ = bytes + header_and_json_size + + 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format) + } bin_size_ = size_t(chunk1_length); } - // Extract JSON string. - std::string jsonString(reinterpret_cast(&bytes[20]), - chunk0_length); - is_binary_ = true; bool ret = LoadFromString(model, err, warn, @@ -6767,7 +6986,18 @@ void JsonAddMember(detail::json &o, const char *key, detail::json &&value) { if (!o.IsObject()) { o.SetObject(); } - o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value), detail::GetAllocator()); + + // Issue 420. + // AddMember may create duplicated key, so use [] API when a key already + // exists. + // https://github.com/Tencent/rapidjson/issues/771#issuecomment-254386863 + detail::json_const_iterator it; + if (detail::FindMember(o, key, it)) { + o[key] = std::move(value); // replace + } else { + o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value), + detail::GetAllocator()); + } #else o[key] = std::move(value); #endif @@ -6805,7 +7035,7 @@ void JsonReserveArray(detail::json &o, size_t s) { (void)(o); (void)(s); } -} // namespace +} // namespace detail // typedef std::pair json_object_pair; @@ -6820,8 +7050,10 @@ static void SerializeNumberProperty(const std::string &key, T number, #ifdef TINYGLTF_USE_RAPIDJSON template <> -void SerializeNumberProperty(const std::string &key, size_t number, detail::json &obj) { - detail::JsonAddMember(obj, key.c_str(), detail::json(static_cast(number))); +void SerializeNumberProperty(const std::string &key, size_t number, + detail::json &obj) { + detail::JsonAddMember(obj, key.c_str(), + detail::json(static_cast(number))); } #endif @@ -6840,8 +7072,10 @@ static void SerializeNumberArrayProperty(const std::string &key, } static void SerializeStringProperty(const std::string &key, - const std::string &value, detail::json &obj) { - detail::JsonAddMember(obj, key.c_str(), detail::JsonFromString(value.c_str())); + const std::string &value, + detail::json &obj) { + detail::JsonAddMember(obj, key.c_str(), + detail::JsonFromString(value.c_str())); } static void SerializeStringArrayProperty(const std::string &key, @@ -6919,10 +7153,10 @@ static bool ValueToJson(const Value &value, detail::json *ret) { obj = detail::json(value.Get()); break; case ARRAY_TYPE: { - for (unsigned int i = 0; i < value.ArrayLen(); ++i) { - Value elementValue = value.Get(int(i)); + for (size_t i = 0; i < value.ArrayLen(); ++i) { + Value elementValue = value.Get(i); detail::json elementJson; - if (ValueToJson(value.Get(int(i)), &elementJson)) + if (ValueToJson(value.Get(i), &elementJson)) obj.push_back(elementJson); } break; @@ -6976,7 +7210,7 @@ static bool SerializeGltfBufferData(const std::vector &data, #ifdef _WIN32 #if defined(__GLIBCXX__) // mingw int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE); __gnu_cxx::stdio_filebuf wfile_buf( file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream output(&wfile_buf); @@ -7034,7 +7268,8 @@ static void SerializeParameterMap(ParameterMap ¶m, detail::json &o) { } #endif -static void SerializeExtensionMap(const ExtensionMap &extensions, detail::json &o) { +static void SerializeExtensionMap(const ExtensionMap &extensions, + detail::json &o) { if (!extensions.size()) return; detail::json extMap; @@ -7060,12 +7295,22 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, detail::json & detail::JsonAddMember(o, "extensions", std::move(extMap)); } +static void SerializeExtras(const Value &extras, detail::json &o) { + if (extras.Type() != NULL_TYPE) SerializeValue("extras", extras, o); +} + +template +void SerializeExtrasAndExtensions(const GltfType &obj, detail::json &o) { + SerializeExtensionMap(obj.extensions, o); + SerializeExtras(obj.extras, o); +} + static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) { if (accessor.bufferView >= 0) SerializeNumberProperty("bufferView", accessor.bufferView, o); if (accessor.byteOffset != 0) - SerializeNumberProperty("byteOffset", int(accessor.byteOffset), o); + SerializeNumberProperty("byteOffset", accessor.byteOffset, o); SerializeNumberProperty("componentType", accessor.componentType, o); SerializeNumberProperty("count", accessor.count, o); @@ -7126,29 +7371,34 @@ static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) { SerializeStringProperty("type", type, o); if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o); - if (accessor.extras.Type() != NULL_TYPE) { - SerializeValue("extras", accessor.extras, o); - } + SerializeExtrasAndExtensions(accessor, o); // sparse - if (accessor.sparse.isSparse) - { - detail::json sparse; - SerializeNumberProperty("count", accessor.sparse.count, sparse); - { - detail::json indices; - SerializeNumberProperty("bufferView", accessor.sparse.indices.bufferView, indices); - SerializeNumberProperty("byteOffset", accessor.sparse.indices.byteOffset, indices); - SerializeNumberProperty("componentType", accessor.sparse.indices.componentType, indices); - detail::JsonAddMember(sparse, "indices", std::move(indices)); - } - { - detail::json values; - SerializeNumberProperty("bufferView", accessor.sparse.values.bufferView, values); - SerializeNumberProperty("byteOffset", accessor.sparse.values.byteOffset, values); - detail::JsonAddMember(sparse, "values", std::move(values)); - } - detail::JsonAddMember(o, "sparse", std::move(sparse)); + if (accessor.sparse.isSparse) { + detail::json sparse; + SerializeNumberProperty("count", accessor.sparse.count, sparse); + { + detail::json indices; + SerializeNumberProperty("bufferView", + accessor.sparse.indices.bufferView, indices); + SerializeNumberProperty("byteOffset", + accessor.sparse.indices.byteOffset, indices); + SerializeNumberProperty( + "componentType", accessor.sparse.indices.componentType, indices); + SerializeExtrasAndExtensions(accessor.sparse.indices, indices); + detail::JsonAddMember(sparse, "indices", std::move(indices)); + } + { + detail::json values; + SerializeNumberProperty("bufferView", + accessor.sparse.values.bufferView, values); + SerializeNumberProperty("byteOffset", + accessor.sparse.values.byteOffset, values); + SerializeExtrasAndExtensions(accessor.sparse.values, values); + detail::JsonAddMember(sparse, "values", std::move(values)); + } + SerializeExtrasAndExtensions(accessor.sparse, sparse); + detail::JsonAddMember(o, "sparse", std::move(sparse)); } } @@ -7165,15 +7415,12 @@ static void SerializeGltfAnimationChannel(const AnimationChannel &channel, SerializeStringProperty("path", channel.target_path, target); SerializeExtensionMap(channel.target_extensions, target); + SerializeExtras(channel.target_extras, target); detail::JsonAddMember(o, "target", std::move(target)); } - if (channel.extras.Type() != NULL_TYPE) { - SerializeValue("extras", channel.extras, o); - } - - SerializeExtensionMap(channel.extensions, o); + SerializeExtrasAndExtensions(channel, o); } static void SerializeGltfAnimationSampler(const AnimationSampler &sampler, @@ -7182,12 +7429,11 @@ static void SerializeGltfAnimationSampler(const AnimationSampler &sampler, SerializeNumberProperty("output", sampler.output, o); SerializeStringProperty("interpolation", sampler.interpolation, o); - if (sampler.extras.Type() != NULL_TYPE) { - SerializeValue("extras", sampler.extras, o); - } + SerializeExtrasAndExtensions(sampler, o); } -static void SerializeGltfAnimation(const Animation &animation, detail::json &o) { +static void SerializeGltfAnimation(const Animation &animation, + detail::json &o) { if (!animation.name.empty()) SerializeStringProperty("name", animation.name, o); @@ -7216,11 +7462,7 @@ static void SerializeGltfAnimation(const Animation &animation, detail::json &o) detail::JsonAddMember(o, "samplers", std::move(samplers)); } - if (animation.extras.Type() != NULL_TYPE) { - SerializeValue("extras", animation.extras, o); - } - - SerializeExtensionMap(animation.extensions, o); + SerializeExtrasAndExtensions(animation, o); } static void SerializeGltfAsset(const Asset &asset, detail::json &o) { @@ -7242,11 +7484,7 @@ static void SerializeGltfAsset(const Asset &asset, detail::json &o) { // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0? SerializeStringProperty("version", version, o); - if (asset.extras.Keys().size()) { - SerializeValue("extras", asset.extras, o); - } - - SerializeExtensionMap(asset.extensions, o); + SerializeExtrasAndExtensions(asset, o); } static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o, @@ -7256,9 +7494,7 @@ static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o, if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); - if (buffer.extras.Type() != NULL_TYPE) { - SerializeValue("extras", buffer.extras, o); - } + SerializeExtrasAndExtensions(buffer, o); } static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) { @@ -7267,9 +7503,7 @@ static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) { if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); - if (buffer.extras.Type() != NULL_TYPE) { - SerializeValue("extras", buffer.extras, o); - } + SerializeExtrasAndExtensions(buffer, o); } static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o, @@ -7281,13 +7515,12 @@ static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o, if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); - if (buffer.extras.Type() != NULL_TYPE) { - SerializeValue("extras", buffer.extras, o); - } + SerializeExtrasAndExtensions(buffer, o); return true; } -static void SerializeGltfBufferView(const BufferView &bufferView, detail::json &o) { +static void SerializeGltfBufferView(const BufferView &bufferView, + detail::json &o) { SerializeNumberProperty("buffer", bufferView.buffer, o); SerializeNumberProperty("byteLength", bufferView.byteLength, o); @@ -7308,9 +7541,7 @@ static void SerializeGltfBufferView(const BufferView &bufferView, detail::json & SerializeStringProperty("name", bufferView.name, o); } - if (bufferView.extras.Type() != NULL_TYPE) { - SerializeValue("extras", bufferView.extras, o); - } + SerializeExtrasAndExtensions(bufferView, o); } static void SerializeGltfImage(const Image &image, const std::string &uri, @@ -7328,25 +7559,18 @@ static void SerializeGltfImage(const Image &image, const std::string &uri, SerializeStringProperty("name", image.name, o); } - if (image.extras.Type() != NULL_TYPE) { - SerializeValue("extras", image.extras, o); - } - - SerializeExtensionMap(image.extensions, o); + SerializeExtrasAndExtensions(image, o); } -static void SerializeGltfTextureInfo(const TextureInfo &texinfo, detail::json &o) { +static void SerializeGltfTextureInfo(const TextureInfo &texinfo, + detail::json &o) { SerializeNumberProperty("index", texinfo.index, o); if (texinfo.texCoord != 0) { SerializeNumberProperty("texCoord", texinfo.texCoord, o); } - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); + SerializeExtrasAndExtensions(texinfo, o); } static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo, @@ -7361,11 +7585,7 @@ static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo, SerializeNumberProperty("scale", texinfo.scale, o); } - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); + SerializeExtrasAndExtensions(texinfo, o); } static void SerializeGltfOcclusionTextureInfo( @@ -7380,11 +7600,7 @@ static void SerializeGltfOcclusionTextureInfo( SerializeNumberProperty("strength", texinfo.strength, o); } - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); + SerializeExtrasAndExtensions(texinfo, o); } static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr, @@ -7415,11 +7631,7 @@ static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr, detail::JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo)); } - SerializeExtensionMap(pbr.extensions, o); - - if (pbr.extras.Type() != NULL_TYPE) { - SerializeValue("extras", pbr.extras, o); - } + SerializeExtrasAndExtensions(pbr, o); } static void SerializeGltfMaterial(const Material &material, detail::json &o) { @@ -7475,7 +7687,8 @@ static void SerializeGltfMaterial(const Material &material, detail::json &o) { // importers (and validators). // if (!detail::JsonIsNull(pbrMetallicRoughness)) { - detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); + detail::JsonAddMember(o, "pbrMetallicRoughness", + std::move(pbrMetallicRoughness)); } } @@ -7487,14 +7700,39 @@ static void SerializeGltfMaterial(const Material &material, detail::json &o) { } SerializeParameterMap(material.additionalValues, o); -#else - #endif - SerializeExtensionMap(material.extensions, o); - - if (material.extras.Type() != NULL_TYPE) { - SerializeValue("extras", material.extras, o); + SerializeExtrasAndExtensions(material, o); + + // MSFT_lod + if (!material.lods.empty()) { + detail::json_iterator it; + if (!detail::FindMember(o, "extensions", it)) { + detail::json extensions; + detail::JsonSetObject(extensions); + detail::JsonAddMember(o, "extensions", std::move(extensions)); + detail::FindMember(o, "extensions", it); + } + auto &extensions = detail::GetValue(it); + if (!detail::FindMember(extensions, "MSFT_lod", it)) { + detail::json lod; + detail::JsonSetObject(lod); + detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod)); + detail::FindMember(extensions, "MSFT_lod", it); + } + SerializeNumberArrayProperty("ids", material.lods, detail::GetValue(it)); + } else { + detail::json_iterator ext_it; + if (detail::FindMember(o, "extensions", ext_it)) { + auto &extensions = detail::GetValue(ext_it); + detail::json_iterator lp_it; + if (detail::FindMember(extensions, "MSFT_lod", lp_it)) { + detail::Erase(extensions, lp_it); + } + if (detail::IsEmpty(extensions)) { + detail::Erase(o, ext_it); + } + } } } @@ -7542,11 +7780,7 @@ static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) { detail::JsonAddMember(primitive, "targets", std::move(targets)); } - SerializeExtensionMap(gltfPrimitive.extensions, primitive); - - if (gltfPrimitive.extras.Type() != NULL_TYPE) { - SerializeValue("extras", gltfPrimitive.extras, primitive); - } + SerializeExtrasAndExtensions(gltfPrimitive, primitive); detail::JsonPushBack(primitives, std::move(primitive)); } @@ -7561,19 +7795,13 @@ static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) { SerializeStringProperty("name", mesh.name, o); } - SerializeExtensionMap(mesh.extensions, o); - if (mesh.extras.Type() != NULL_TYPE) { - SerializeValue("extras", mesh.extras, o); - } + SerializeExtrasAndExtensions(mesh, o); } static void SerializeSpotLight(const SpotLight &spot, detail::json &o) { SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o); SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o); - SerializeExtensionMap(spot.extensions, o); - if (spot.extras.Type() != NULL_TYPE) { - SerializeValue("extras", spot.extras, o); - } + SerializeExtrasAndExtensions(spot, o); } static void SerializeGltfLight(const Light &light, detail::json &o) { @@ -7589,10 +7817,61 @@ static void SerializeGltfLight(const Light &light, detail::json &o) { SerializeSpotLight(light.spot, spot); detail::JsonAddMember(o, "spot", std::move(spot)); } - SerializeExtensionMap(light.extensions, o); - if (light.extras.Type() != NULL_TYPE) { - SerializeValue("extras", light.extras, o); + SerializeExtrasAndExtensions(light, o); +} + +static void SerializeGltfPositionalEmitter(const PositionalEmitter &positional, + detail::json &o) { + if (!TINYGLTF_DOUBLE_EQUAL(positional.coneInnerAngle, 6.283185307179586)) + SerializeNumberProperty("coneInnerAngle", positional.coneInnerAngle, o); + if (!TINYGLTF_DOUBLE_EQUAL(positional.coneOuterAngle, 6.283185307179586)) + SerializeNumberProperty("coneOuterAngle", positional.coneOuterAngle, o); + if (positional.coneOuterGain > 0.0) + SerializeNumberProperty("coneOuterGain", positional.coneOuterGain, o); + if (!TINYGLTF_DOUBLE_EQUAL(positional.maxDistance, 100.0)) + SerializeNumberProperty("maxDistance", positional.maxDistance, o); + if (!TINYGLTF_DOUBLE_EQUAL(positional.refDistance, 1.0)) + SerializeNumberProperty("refDistance", positional.refDistance, o); + if (!TINYGLTF_DOUBLE_EQUAL(positional.rolloffFactor, 1.0)) + SerializeNumberProperty("rolloffFactor", positional.rolloffFactor, o); + + SerializeExtrasAndExtensions(positional, o); +} + +static void SerializeGltfAudioEmitter(const AudioEmitter &emitter, + detail::json &o) { + if (!emitter.name.empty()) SerializeStringProperty("name", emitter.name, o); + if (!TINYGLTF_DOUBLE_EQUAL(emitter.gain, 1.0)) + SerializeNumberProperty("gain", emitter.gain, o); + if (emitter.loop) SerializeNumberProperty("loop", emitter.loop, o); + if (emitter.playing) SerializeNumberProperty("playing", emitter.playing, o); + if (!emitter.type.empty()) SerializeStringProperty("type", emitter.type, o); + if (!emitter.distanceModel.empty()) + SerializeStringProperty("distanceModel", emitter.distanceModel, o); + if (emitter.type == "positional") { + detail::json positional; + SerializeGltfPositionalEmitter(emitter.positional, positional); + detail::JsonAddMember(o, "positional", std::move(positional)); + } + SerializeNumberProperty("source", emitter.source, o); + SerializeExtrasAndExtensions(emitter, o); +} + +static void SerializeGltfAudioSource(const AudioSource &source, + detail::json &o) { + std::string name; + std::string uri; + std::string mimeType; // (required if no uri) ["audio/mp3", "audio/ogg", + // "audio/wav", "audio/m4a"] + + if (!source.name.empty()) SerializeStringProperty("name", source.name, o); + if (source.uri.empty()) { + SerializeStringProperty("mimeType", source.mimeType, o); + SerializeNumberProperty("bufferView", source.bufferView, o); + } else { + SerializeStringProperty("uri", source.uri, o); } + SerializeExtrasAndExtensions(source, o); } static void SerializeGltfNode(const Node &node, detail::json &o) { @@ -7624,11 +7903,107 @@ static void SerializeGltfNode(const Node &node, detail::json &o) { SerializeNumberArrayProperty("weights", node.weights, o); } - if (node.extras.Type() != NULL_TYPE) { - SerializeValue("extras", node.extras, o); + SerializeExtrasAndExtensions(node, o); + + // Note(agnat): If the asset was loaded from disk, the node may already + // contain the KHR_lights_punctual extension. If it was constructed in + // memory it does not. In any case we update the JSON property using + // the value from the struct. Last, if the node does not have a light + // reference but the extension is still present, we remove it. + if (node.light != -1) { + detail::json_iterator it; + if (!detail::FindMember(o, "extensions", it)) { + detail::json extensions; + detail::JsonSetObject(extensions); + detail::JsonAddMember(o, "extensions", std::move(extensions)); + detail::FindMember(o, "extensions", it); + } + auto &extensions = detail::GetValue(it); + if (!detail::FindMember(extensions, "KHR_lights_punctual", it)) { + detail::json lights_punctual; + detail::JsonSetObject(lights_punctual); + detail::JsonAddMember(extensions, "KHR_lights_punctual", + std::move(lights_punctual)); + detail::FindMember(extensions, "KHR_lights_punctual", it); + } + SerializeNumberProperty("light", node.light, detail::GetValue(it)); + } else { + // node has no light ref (any longer)... so we clean up + detail::json_iterator ext_it; + if (detail::FindMember(o, "extensions", ext_it)) { + auto &extensions = detail::GetValue(ext_it); + detail::json_iterator lp_it; + if (detail::FindMember(extensions, "KHR_lights_punctual", lp_it)) { + detail::Erase(extensions, lp_it); + } + if (detail::IsEmpty(extensions)) { + detail::Erase(o, ext_it); + } + } + } + + // KHR_audio + if (node.emitter != -1) { + detail::json_iterator it; + if (!detail::FindMember(o, "extensions", it)) { + detail::json extensions; + detail::JsonSetObject(extensions); + detail::JsonAddMember(o, "extensions", std::move(extensions)); + detail::FindMember(o, "extensions", it); + } + auto &extensions = detail::GetValue(it); + if (!detail::FindMember(extensions, "KHR_audio", it)) { + detail::json audio; + detail::JsonSetObject(audio); + detail::JsonAddMember(extensions, "KHR_audio", std::move(audio)); + detail::FindMember(extensions, "KHR_audio", it); + } + SerializeNumberProperty("emitter", node.emitter, detail::GetValue(it)); + } else { + detail::json_iterator ext_it; + if (detail::FindMember(o, "extensions", ext_it)) { + auto &extensions = detail::GetValue(ext_it); + detail::json_iterator lp_it; + if (detail::FindMember(extensions, "KHR_audio", lp_it)) { + detail::Erase(extensions, lp_it); + } + if (detail::IsEmpty(extensions)) { + detail::Erase(o, ext_it); + } + } + } + + // MSFT_lod + if (!node.lods.empty()) { + detail::json_iterator it; + if (!detail::FindMember(o, "extensions", it)) { + detail::json extensions; + detail::JsonSetObject(extensions); + detail::JsonAddMember(o, "extensions", std::move(extensions)); + detail::FindMember(o, "extensions", it); + } + auto &extensions = detail::GetValue(it); + if (!detail::FindMember(extensions, "MSFT_lod", it)) { + detail::json lod; + detail::JsonSetObject(lod); + detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod)); + detail::FindMember(extensions, "MSFT_lod", it); + } + SerializeNumberArrayProperty("ids", node.lods, detail::GetValue(it)); + } else { + detail::json_iterator ext_it; + if (detail::FindMember(o, "extensions", ext_it)) { + auto &extensions = detail::GetValue(ext_it); + detail::json_iterator lp_it; + if (detail::FindMember(extensions, "MSFT_lod", lp_it)) { + detail::Erase(extensions, lp_it); + } + if (detail::IsEmpty(extensions)) { + detail::Erase(o, ext_it); + } + } } - SerializeExtensionMap(node.extensions, o); if (!node.name.empty()) SerializeStringProperty("name", node.name, o); SerializeNumberArrayProperty("children", node.children, o); } @@ -7647,9 +8022,7 @@ static void SerializeGltfSampler(const Sampler &sampler, detail::json &o) { SerializeNumberProperty("wrapS", sampler.wrapS, o); SerializeNumberProperty("wrapT", sampler.wrapT, o); - if (sampler.extras.Type() != NULL_TYPE) { - SerializeValue("extras", sampler.extras, o); - } + SerializeExtrasAndExtensions(sampler, o); } static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera, @@ -7659,9 +8032,7 @@ static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera, SerializeNumberProperty("xmag", camera.xmag, o); SerializeNumberProperty("ymag", camera.ymag, o); - if (camera.extras.Type() != NULL_TYPE) { - SerializeValue("extras", camera.extras, o); - } + SerializeExtrasAndExtensions(camera, o); } static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera, @@ -7676,9 +8047,7 @@ static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera, SerializeNumberProperty("yfov", camera.yfov, o); } - if (camera.extras.Type() != NULL_TYPE) { - SerializeValue("extras", camera.extras, o); - } + SerializeExtrasAndExtensions(camera, o); } static void SerializeGltfCamera(const Camera &camera, detail::json &o) { @@ -7699,10 +8068,7 @@ static void SerializeGltfCamera(const Camera &camera, detail::json &o) { // ??? } - if (camera.extras.Type() != NULL_TYPE) { - SerializeValue("extras", camera.extras, o); - } - SerializeExtensionMap(camera.extensions, o); + SerializeExtrasAndExtensions(camera, o); } static void SerializeGltfScene(const Scene &scene, detail::json &o) { @@ -7711,10 +8077,39 @@ static void SerializeGltfScene(const Scene &scene, detail::json &o) { if (scene.name.size()) { SerializeStringProperty("name", scene.name, o); } - if (scene.extras.Type() != NULL_TYPE) { - SerializeValue("extras", scene.extras, o); + SerializeExtrasAndExtensions(scene, o); + + // KHR_audio + if (!scene.audioEmitters.empty()) { + detail::json_iterator it; + if (!detail::FindMember(o, "extensions", it)) { + detail::json extensions; + detail::JsonSetObject(extensions); + detail::JsonAddMember(o, "extensions", std::move(extensions)); + detail::FindMember(o, "extensions", it); + } + auto &extensions = detail::GetValue(it); + if (!detail::FindMember(extensions, "KHR_audio", it)) { + detail::json audio; + detail::JsonSetObject(audio); + detail::JsonAddMember(extensions, "KHR_audio", std::move(audio)); + detail::FindMember(o, "KHR_audio", it); + } + SerializeNumberArrayProperty("emitters", scene.audioEmitters, + detail::GetValue(it)); + } else { + detail::json_iterator ext_it; + if (detail::FindMember(o, "extensions", ext_it)) { + auto &extensions = detail::GetValue(ext_it); + detail::json_iterator lp_it; + if (detail::FindMember(extensions, "KHR_audio", lp_it)) { + detail::Erase(extensions, lp_it); + } + if (detail::IsEmpty(extensions)) { + detail::Erase(o, ext_it); + } + } } - SerializeExtensionMap(scene.extensions, o); } static void SerializeGltfSkin(const Skin &skin, detail::json &o) { @@ -7732,6 +8127,8 @@ static void SerializeGltfSkin(const Skin &skin, detail::json &o) { if (skin.name.size()) { SerializeStringProperty("name", skin.name, o); } + + SerializeExtrasAndExtensions(skin, o); } static void SerializeGltfTexture(const Texture &texture, detail::json &o) { @@ -7744,10 +8141,7 @@ static void SerializeGltfTexture(const Texture &texture, detail::json &o) { if (texture.name.size()) { SerializeStringProperty("name", texture.name, o); } - if (texture.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texture.extras, o); - } - SerializeExtensionMap(texture.extensions, o); + SerializeExtrasAndExtensions(texture, o); } /// @@ -7845,6 +8239,16 @@ static void SerializeGltfModel(const Model *model, detail::json &o) { for (unsigned int i = 0; i < model->nodes.size(); ++i) { detail::json node; SerializeGltfNode(model->nodes[i], node); + + if (detail::JsonIsNull(node)) { + // Issue 457. + // `node` does not have any required parameters, + // so the result may be null(unmodified) when all node parameters + // have default value. + // + // null is not allowed thus we create an empty JSON object. + detail::JsonSetObject(node); + } detail::JsonPushBack(nodes, std::move(node)); } detail::JsonAddMember(o, "nodes", std::move(nodes)); @@ -7862,6 +8266,15 @@ static void SerializeGltfModel(const Model *model, detail::json &o) { for (unsigned int i = 0; i < model->scenes.size(); ++i) { detail::json currentScene; SerializeGltfScene(model->scenes[i], currentScene); + if (detail::JsonIsNull(currentScene)) { + // Issue 464. + // `scene` does not have any required parameters, + // so the result may be null(unmodified) when all scene parameters + // have default value. + // + // null is not allowed thus we create an empty JSON object. + detail::JsonSetObject(currentScene); + } detail::JsonPushBack(scenes, std::move(currentScene)); } detail::JsonAddMember(o, "scenes", std::move(scenes)); @@ -7915,8 +8328,8 @@ static void SerializeGltfModel(const Model *model, detail::json &o) { detail::JsonAddMember(o, "cameras", std::move(cameras)); } - // EXTENSIONS - SerializeExtensionMap(model->extensions, o); + // EXTRAS & EXTENSIONS + SerializeExtrasAndExtensions(*model, o); auto extensionsUsed = model->extensionsUsed; @@ -7940,7 +8353,8 @@ static void SerializeGltfModel(const Model *model, detail::json &o) { } } - detail::JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn)); + detail::JsonAddMember(ext_j, "KHR_lights_punctual", + std::move(khr_lights_cmn)); detail::JsonAddMember(o, "extensions", std::move(ext_j)); @@ -7958,15 +8372,81 @@ static void SerializeGltfModel(const Model *model, detail::json &o) { } } + // KHR_audio + if (!model->audioEmitters.empty() || !model->audioSources.empty()) { + detail::json emitters; + detail::JsonReserveArray(emitters, model->audioEmitters.size()); + for (unsigned int i = 0; i < model->audioEmitters.size(); ++i) { + detail::json emitter; + SerializeGltfAudioEmitter(model->audioEmitters[i], emitter); + detail::JsonPushBack(emitters, std::move(emitter)); + } + detail::json khr_audio_cmn; + detail::JsonAddMember(khr_audio_cmn, "emitters", std::move(emitters)); + + detail::json sources; + detail::JsonReserveArray(sources, model->audioSources.size()); + for (unsigned int i = 0; i < model->audioSources.size(); ++i) { + detail::json source; + SerializeGltfAudioSource(model->audioSources[i], source); + detail::JsonPushBack(sources, std::move(source)); + } + detail::JsonAddMember(khr_audio_cmn, "sources", std::move(sources)); + + detail::json ext_j; + { + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + detail::JsonAssign(ext_j, detail::GetValue(it)); + } + } + + detail::JsonAddMember(ext_j, "KHR_audio", std::move(khr_audio_cmn)); + + detail::JsonAddMember(o, "extensions", std::move(ext_j)); + + // Also add "KHR_audio" to `extensionsUsed` + { + auto has_khr_audio = std::find_if( + extensionsUsed.begin(), extensionsUsed.end(), + [](const std::string &s) { return (s.compare("KHR_audio") == 0); }); + + if (has_khr_audio == extensionsUsed.end()) { + extensionsUsed.push_back("KHR_audio"); + } + } + } + + // MSFT_lod + + // Look if there is a node that employs MSFT_lod + auto msft_lod_nodes_it = std::find_if( + model->nodes.begin(), model->nodes.end(), + [](const Node& node) { return !node.lods.empty(); }); + + // Look if there is a material that employs MSFT_lod + auto msft_lod_materials_it = std::find_if( + model->materials.begin(), model->materials.end(), + [](const Material& material) {return !material.lods.empty(); }); + + // If either a node or a material employ MSFT_lod, then we need + // to add MSFT_lod to the list of used extensions. + if (msft_lod_nodes_it != model->nodes.end() || msft_lod_materials_it != model->materials.end()) { + // First check if MSFT_lod is already registered as used extension + auto has_msft_lod = std::find_if( + extensionsUsed.begin(), extensionsUsed.end(), + [](const std::string &s) { return (s.compare("MSFT_lod") == 0); }); + + // If MSFT_lod is not registered yet, add it + if (has_msft_lod == extensionsUsed.end()) { + extensionsUsed.push_back("MSFT_lod"); + } + } + // Extensions used if (extensionsUsed.size()) { SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o); } - - // EXTRAS - if (model->extras.Type() != NULL_TYPE) { - SerializeValue("extras", model->extras, o); - } } static bool WriteGltfStream(std::ostream &stream, const std::string &content) { @@ -7981,7 +8461,7 @@ static bool WriteGltfFile(const std::string &output, std::ofstream gltfFile(UTF8ToWchar(output).c_str()); #elif defined(__GLIBCXX__) int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE); __gnu_cxx::stdio_filebuf wfile_buf( file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream gltfFile(&wfile_buf); @@ -8066,7 +8546,7 @@ static bool WriteBinaryGltfFile(const std::string &output, std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary); #elif defined(__GLIBCXX__) int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), - _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE); __gnu_cxx::stdio_filebuf wfile_buf( file_descriptor, std::ios_base::out | std::ios_base::binary); std::ostream gltfFile(&wfile_buf); @@ -8111,13 +8591,13 @@ bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream, for (unsigned int i = 0; i < model->images.size(); ++i) { detail::json image; - std::string dummystring = ""; + std::string dummystring; // UpdateImageObject need baseDir but only uses it if embeddedImages is // enabled, since we won't write separate images when writing to a stream // we std::string uri; if (!UpdateImageObject(model->images[i], dummystring, int(i), true, - &uri_cb, &this->WriteImageData, + &fs, &uri_cb, this->WriteImageData, this->write_image_user_data_, &uri)) { return false; } @@ -8128,9 +8608,11 @@ bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream, } if (writeBinary) { - return WriteBinaryGltfStream(stream, detail::JsonToString(output), binBuffer); + return WriteBinaryGltfStream(stream, detail::JsonToString(output), + binBuffer); } else { - return WriteGltfStream(stream, detail::JsonToString(output, prettyPrint ? 2 : -1)); + return WriteGltfStream(stream, + detail::JsonToString(output, prettyPrint ? 2 : -1)); } } @@ -8223,7 +8705,7 @@ bool TinyGLTF::WriteGltfSceneToFile(const Model *model, std::string uri; if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages, - &uri_cb, &this->WriteImageData, + &fs, &uri_cb, this->WriteImageData, this->write_image_user_data_, &uri)) { return false; } @@ -8234,9 +8716,11 @@ bool TinyGLTF::WriteGltfSceneToFile(const Model *model, } if (writeBinary) { - return WriteBinaryGltfFile(filename, detail::JsonToString(output), binBuffer); + return WriteBinaryGltfFile(filename, detail::JsonToString(output), + binBuffer); } else { - return WriteGltfFile(filename, detail::JsonToString(output, (prettyPrint ? 2 : -1))); + return WriteGltfFile(filename, + detail::JsonToString(output, (prettyPrint ? 2 : -1))); } } diff --git a/src/scripts/conformance_generator.py b/src/scripts/conformance_generator.py index b8a3dfff..d51815c2 100644 --- a/src/scripts/conformance_generator.py +++ b/src/scripts/conformance_generator.py @@ -79,15 +79,16 @@ def _compute_avail_symbols(self) -> List[Tuple[str, FrozenAvailability]]: temp = AvailabilitySymbols() for profile in self.interaction_profiles.interaction_profiles.values(): temp.add(profile.availability) - for component in profile.components.values(): - temp.add(component.availability) + for _, component in profile.yield_user_path_and_component_pairs(): + avail = profile.compute_component_availability(component) + temp.add(avail) return temp.make_frozen() # Write out all the information for the appropriate file, # and then call down to the base class to wrap everything up. # self the ConformanceLayerBaseGenerator object def endFile(self): - self.interaction_profiles.process_deferred() + self.interaction_profiles.finish_processing() avail_syms = self._compute_avail_symbols() sorted_cmds = self.core_commands + self.ext_commands file_data = self.template.render( diff --git a/src/scripts/interaction_profile_processor.py b/src/scripts/interaction_profile_processor.py index 6313248c..7dcaad49 100644 --- a/src/scripts/interaction_profile_processor.py +++ b/src/scripts/interaction_profile_processor.py @@ -19,23 +19,33 @@ def _format_conjunction(and_terms): if len(and_terms) > 1: - return f"({'+'.join(and_terms)})" + return f"({'+'.join(sorted(and_terms))})" assert and_terms return tuple(and_terms)[0] def _repr_conjunction(and_terms): - terms = ", ".join(f"'{t}'" for t in and_terms) + terms = ", ".join(f"'{t}'" for t in sorted(and_terms)) return "".join(("{", terms, "}")) FrozenAvailability = Tuple[Tuple[str, ...], ...] + class Availability: """ Information on when something is available. In 'disjunctive normal form' - an OR of ANDs. + + >>> Availability([{'a'}, {'b'}]) + [{'a'}, {'b'}] + + >>> Availability([{'a'}, {'a', 'b'}]) + [{'a'}] + + >>> Availability([{'a', 'b'}, {'a'}]) + [{'a'}] """ def __init__(self, conjunctions: Iterable[Set[str]]): @@ -54,16 +64,107 @@ def add(self, features: Set[str]) -> bool: Add a new set of features that would make this available. Return true if this feature set is redundant given what already exists. + + >>> Availability([{'a'}]).add({'a', 'b'}) + True + + >>> Availability([{'a'}]).add({'b'}) + False + + >>> a = Availability([{'a'}]) + >>> a.add({'b'}) + False + >>> a + [{'a'}, {'b'}] + + >>> a = Availability([{'a'}]) + >>> a.add({'a', 'b'}) + True + >>> a + [{'a'}] + + >>> a = Availability([{'a', 'b'}]) + >>> a.add({'a'}) + False + >>> a + [{'a'}] """ return self._add_impl(frozenset(features)) def merge(self, other: 'Availability') -> bool: - """Merge two availabilities.""" + """Merge two availabilities (by OR).""" redundant = [self._add_impl(condition) for condition in other.conjunctions] return all(redundant) + def merged(self, other: 'Availability') -> 'Availability': + """ + Return the results of merging without changing this object. + + + >>> Availability([{'a'}]).merged(Availability([{'b', 'c'}])) + [{'a'}, {'b', 'c'}] + + >>> Availability([{'a'}]).merged(Availability([{'a', 'b'}, {'b', 'c'}])) + [{'a'}, {'b', 'c'}] + + >>> Availability([{'a'}, {'b', 'c'}]).merged(Availability([{'a', 'b'}])) + [{'a'}, {'b', 'c'}] + """ + clone = deepcopy(self) + clone.merge(other) + return clone + + def anded(self, other: 'Availability') -> 'Availability': + """ + Return the boolean AND of this and another availability. + + >>> Availability([{'a'}]).anded(Availability([{'b', 'c'}])) + [{'a', 'b', 'c'}] + + + >>> Availability([{'a'}]).anded(Availability([{'a', 'b'}])) + [{'a', 'b'}] + + >>> # a+b+c drops out because a+b is sufficient + >>> Availability([{'a'}]).anded(Availability([{'a', 'b'}, {'b', 'c'}])) + [{'a', 'b'}] + + >>> Availability([{'a'}]).anded(Availability([{'b'}, {'c'}])) + [{'a', 'b'}, {'a', 'c'}] + + >>> Availability([{'a'}]).anded(Availability([{'b', 'c'}, {'d', 'e'}])) + [{'a', 'b', 'c'}, {'a', 'd', 'e'}] + + >>> Availability([{'a', 'b'}]).anded(Availability([{'c'}, {'d'}])) + [{'a', 'b', 'c'}, {'a', 'b', 'd'}] + + >>> Availability([{'a'}]).anded(Availability([])) + [] + + >>> Availability([]).anded(Availability([{'a'}])) + [] + """ + ret = Availability([]) + + for self_term in self.conjunctions: + for other_term in other.conjunctions: + ret._add_impl(self_term.union(other_term)) + + return ret + def test(self, present_features: Set[str]) -> bool: - """See if the provided features satisfy any of the conjunctions.""" + """ + See if the provided features satisfy any of the conjunctions. + + >>> Availability([{'a'}]).test({'b'}) + False + + >>> Availability([{'a'}]).test({'a'}) + True + + >>> Availability([]).test({'a'}) + False + """ for condition in self.conjunctions: if condition.issubset(present_features): return True @@ -76,27 +177,14 @@ def is_consistent(self) -> bool: if len(set(self.conjunctions)) < len(self.conjunctions): # got a dupe return False - for a, b in itertools.permutations(self.conjunctions): + for a, b in itertools.permutations(self.conjunctions, 2): if a.issubset(b): # One condition is a subset of another return False return True - def issubset(self, potential_superset: "Availability") -> bool: - for condition in self.conjunctions: - for other_condition in potential_superset.conjunctions: - if condition.issubset(other_condition): - return True - return False - - def issuperset(self, other: "Availability") -> bool: - for condition in self.conjunctions: - for other_condition in other.conjunctions: - if condition.issuperset(other_condition): - return True - return False - def make_frozen(self) -> FrozenAvailability: + """Convert to a tuple of term tuples.""" terms = [] for condition in self.conjunctions: terms.append(tuple(sorted(condition))) @@ -107,21 +195,34 @@ def as_normalized_symbol(self) -> str: for c in sorted(self.conjunctions)] return "_or_".join(conjs) + def cleaned(self, cleaner) -> 'Availability': + """Return an availability where each term has been filtered by your function.""" + ret = Availability([]) + for term in self.conjunctions: + ret.add(set(cleaner(term))) + return ret + def _add_impl(self, features: FrozenSet[str]) -> bool: if features in self.conjunctions: return True - # these we can't be sure which is right - redundant = any(features.issubset(c) or features.issuperset(c) for c in self.conjunctions) - self.conjunctions.append(features) - return redundant + # Do not add this term if we are a superset of any existing term + # e.g. A, A+B -> A + if any(features.issuperset(c) for c in self.conjunctions): + return True + + # Drop any terms that are a superset of the new term + remaining = [term for term in self.conjunctions if not term.issuperset(features)] + remaining.append(features) + self.conjunctions = remaining + return False def __str__(self): - conjs = [_format_conjunction(c) for c in self.conjunctions] + conjs = [_format_conjunction(c) for c in sorted(self.conjunctions)] return f'({" OR ".join(conjs)})' def __repr__(self) -> str: - conjs = [_repr_conjunction(c) for c in self.conjunctions] + conjs = [_repr_conjunction(c) for c in sorted(self.conjunctions)] return f"[{ ', '.join(conjs)}]" @classmethod @@ -144,6 +245,38 @@ def make_frozen(self) -> List[Tuple[str, FrozenAvailability]]: for key in sorted(self.syms.keys())] +def _version_cleaner(term: FrozenSet[str]) -> FrozenSet[str]: + versions = list(sorted((s for s in term if s.startswith("XR_VERSION_")))) + if not versions: + return term + + mutable_term = set(term) + # Saying the default version is redundant if we have more interesting dependencies. + if len(versions) == 1 and versions[0] == _DEFAULT_VER and len(term) > 1: + mutable_term.discard(_DEFAULT_VER) + + # keep the last one + redundant_versions = versions[:-1] + if redundant_versions: + mutable_term.difference_update(redundant_versions) + + return frozenset(mutable_term) + + +_DEFAULT_VER = 'XR_VERSION_1_0' + + +def _process_depends_string(s: Optional[str]) -> Availability: + if not s: + return Availability.create({_DEFAULT_VER}) + + terms = [{feat.strip() for feat in t.split("+")} for t in s.split(',')] + for t in terms: + if not any(feat.startswith('XR_VERSION_') for feat in t): + t.add(_DEFAULT_VER) + return Availability(terms) + + @dataclass class InteractionProfileComponent: """A component of an interaction profile""" @@ -200,16 +333,13 @@ def add_component(self, limit_to_user_path: Optional[str] = None, verbose: bool = _VERBOSE_INTERACTION_PROFILE_PROCESSING ) -> InteractionProfileComponent: - # if not avail.issuperset(self.availability): - # # the component availability does not include all the requirements of the profile availability? - # print(f"{self.name}: Add {subpath}: component availability {avail} is not a superset of profile availability {self.availability}") if limit_to_user_path: user_paths = {limit_to_user_path} else: - user_paths = deepcopy(self.valid_user_paths) - if subpath in self.components: - component = self.components[subpath] + user_paths = self.valid_user_paths + component = self.components.get(subpath) + if component: if action_type != component.action_type: raise RuntimeError(f"{self.name}: Add {subpath} again: Action type mismatch") if system != component.system: @@ -217,11 +347,9 @@ def add_component(self, if user_paths != component.valid_user_paths: raise RuntimeError(f"{self.name}: Add {subpath} again: Valid user paths mismatch") - assert len(avail.conjunctions) == 1 - # Just extend the availability - redundant = component.availability.merge(avail) - if redundant and verbose: - print(f"{self.name}{subpath}: Redundant availability {avail}") + # Just extend the availability. Not caring about return value (whether the avail is redundant) + component.availability.merge(avail) + return component ret = InteractionProfileComponent( @@ -235,14 +363,39 @@ def add_component(self, self.components[subpath] = ret return ret + def yield_user_path_and_component_pairs(self): + """ + Yield a user path and a component object. + + Outer iteration is over user paths. + """ + for user_path in sorted(self.valid_user_paths): + for subpath in sorted(self.components.keys()): + component = self.components[subpath] + if user_path in component.valid_user_paths: + yield user_path, component + + def generate_binding_paths(self): + """ + Yield a full binding path, availability, and the component object. + + Outer iteration is over user paths. + """ + for user_path, component in self.yield_user_path_and_component_pairs(): + yield (f"{user_path}{component.subpath}", + self.compute_component_availability(component), + component) + + def compute_component_availability(self, component: InteractionProfileComponent) -> Availability: + return self.availability.anded(component.availability).cleaned(_version_cleaner) + class InteractionProfileProcessor: """Encapsulates the logic to process interaction profiles in the XML.""" def __init__(self): self.interaction_profiles: Dict[str, InteractionProfile] = dict() - self.deferred_added_components = defaultdict(list) - self.availabilities: Set[FrozenAvailability] = set() + self.processed_features: List[et.Element] = [] self.finished = False self.verbose = _VERBOSE_INTERACTION_PROFILE_PROCESSING @@ -255,12 +408,12 @@ def process_feature(self, root: et.Element, interface: et.Element, emit: bool): root is the root of the registry XML tree. interface is either a `` tag or `` tag. - This may queue added components, if a feature requiring their profile - is not yet processed. Call process_deferred() to resolve this once all features - are processed. + This logs the features processed, in order, to perform additional passes + when finishing processing. Call `finish_processing()` once all features + are processed to perform these subsequent processing passes. """ if self.finished: - print("Hey, we think we are already finished!") + raise ValueError("Cannot process more features once we are finished!") interface_name = interface.get('name') assert interface_name @@ -268,11 +421,12 @@ def process_feature(self, root: et.Element, interface: et.Element, emit: bool): if self.verbose: print(f"Handling {interface.tag}: {interface_name} {emit}") + # Only grab interaction profiles in this first pass for require in interface.findall('./require[interaction_profile]'): - required_features = self._compute_deps_for_interaction_profile(interface, require) + avail = self._compute_deps_for_interaction_profile(interface, require) if self.verbose: - print(interface.tag, interface_name, required_features) + print(interface.tag, interface_name, avail) for include_ipp in require.findall('./interaction_profile'): # Path @@ -282,61 +436,58 @@ def process_feature(self, root: et.Element, interface: et.Element, emit: bool): if self.verbose: print(interface_name, name) - self._include_interaction_profile(root, name, required_features) + self._include_interaction_profile(root, name, avail) + self.processed_features.append(interface) + def _process_extending_bindings(self, interface: et.Element): + + interface_name = interface.get('name') + assert interface_name + + if self.verbose: + print(f"Handling {interface.tag}: {interface_name}: Pass 2, additional binding paths") for require in interface.findall('./require[extend]'): - required_features = self._compute_deps_for_interaction_profile(interface, require) + avail = self._compute_deps_for_interaction_profile(interface, require) for extend in require.findall('./extend[@interaction_profile_path]'): profile_name = extend.get("interaction_profile_path") assert profile_name - profile = self.interaction_profiles.get(profile_name) - if profile: - self._add_interaction_profile_components(profile, extend, required_features) - else: - self.deferred_added_components[profile_name].append((extend, required_features)) + profile = self.interaction_profiles[profile_name] + self._add_interaction_profile_components(profile, extend, avail) - def process_deferred(self): + def finish_processing(self): """ - Process components originating in features processed before their profile. + Perform the second pass over features and other finish-up work. - This is idempotent: you can call it multiple times safely. - However, you must have processed the profiles referred to by deferred components - first! So, just call it once from endFile() if you are using the common - Generator classes. + To guarantee correct processing, only call this once, after all + calls to `process_feature()` are complete. + Call it once from `endFile()` if you are using the common + `Generator` classes. """ - for profile_name, components in self.deferred_added_components.items(): - for extend, deps in components: - profile = self.interaction_profiles.get(profile_name) - assert profile - self._add_interaction_profile_components(profile, extend, deps) - self.deferred_added_components.clear() - - for profile in self.interaction_profiles.values(): - self.availabilities.add(profile.availability.make_frozen()) - for component in profile.components.values(): - self.availabilities.add(component.availability.make_frozen()) + # Second pass: extend binding paths (components) + for feature in self.processed_features: + self._process_extending_bindings(feature) + + self.processed_features.clear() self.finished = True - # for ip in self.interaction_profiles.values(): - # for component in ip.components: - def _include_interaction_profile(self, root: et.Element, name: str, required_features: Set[str]): + def _include_interaction_profile(self, root: et.Element, name: str, avail: Availability): if name in self.interaction_profiles: profile = self.interaction_profiles[name] - redundant = profile.availability.add(required_features) + redundant = profile.availability.merge(avail) if redundant and self.verbose: - print(f"{name}: Adding redundant set of required features! {required_features}") + print(f"{name}: Adding redundant availability! {avail}") # Add our new route of availability to all components defined inline, without # going through the XML again. for component in profile.components.values(): if component.integral: - redundant = component.availability.add(required_features) + redundant = component.availability.merge(avail) if redundant and self.verbose: - print(f"{name}{component.subpath}: Adding redundant set of required features!") - print(f" new: {required_features} existing availability: {component.availability}") + print(f"{name}{component.subpath}: Adding redundant availability!") + print(f" new: {avail} existing availability: {component.availability}") return @@ -348,37 +499,27 @@ def _include_interaction_profile(self, root: et.Element, name: str, required_fea title = profile_elt.get("title") assert title - avail = Availability.create(required_features) profile = InteractionProfile(valid_user_paths=user_paths, name=name, title=title, availability=avail) self.interaction_profiles[name] = profile - self._add_interaction_profile_components(profile, profile_elt, required_features, integral=True) + self._add_interaction_profile_components(profile, profile_elt, avail, integral=True) - def _compute_deps_for_interaction_profile(self, feature_elt: et.Element, require_elt: et.Element) -> Set[str]: + def _compute_deps_for_interaction_profile(self, feature_elt: et.Element, require_elt: et.Element) -> Availability: feature_name = feature_elt.get("name") assert feature_name - deps = {feature_name} - - # TODO finish fixing for schema update - if feature_elt.tag == "extension": - requiresCore = feature_elt.get("requiresCore", "1.0") - deps.add(f"XR_VERSION_{requiresCore.replace('.', '_')}") - - additional_extension = require_elt.get('extension') - if additional_extension is None: - additional_extension = require_elt.get('depends') - if additional_extension is not None: - deps.add(additional_extension) + deps = Availability.create({feature_name}) - return deps + require_depends = require_elt.get('depends') + if require_depends: + deps = deps.anded(_process_depends_string(require_depends)) - def _add_interaction_profile_components(self, profile: InteractionProfile, component_parent, required_features: Set[str], integral: bool = False): + return deps.cleaned(_version_cleaner) - avail = Availability.create(required_features) + def _add_interaction_profile_components(self, profile: InteractionProfile, component_parent, avail: Availability, integral: bool = False): for component in component_parent.findall("./component"): system = False if component.get("system") is not None: diff --git a/src/scripts/template_function_info.cpp b/src/scripts/template_function_info.cpp index bb02e710..07daa06a 100644 --- a/src/scripts/template_function_info.cpp +++ b/src/scripts/template_function_info.cpp @@ -13,7 +13,7 @@ static const FunctionInfoMap functionInfoMapInternal{ //# for cur_cmd in sorted_cmds //# if cur_cmd.ext_name != "XR_LOADER_VERSION_1_0" {/*{ cur_cmd.name | quote_string }*/, - FunctionInfo(PFN_xrVoidFunction(nullptr), + FunctionInfo( //# if cur_cmd.name in null_instance_ok true, /* null instance OK */ //# else diff --git a/src/scripts/template_interaction_info_generated.cpp b/src/scripts/template_interaction_info_generated.cpp index d9385aa2..e6edc388 100644 --- a/src/scripts/template_interaction_info_generated.cpp +++ b/src/scripts/template_interaction_info_generated.cpp @@ -9,12 +9,12 @@ namespace Conformance { -//# macro make_qualified_path_entry(user_path, component) +//# macro make_path_entry(binding_path, avail, component) InputSourcePathAvailData{ - /*{ (user_path + component.subpath) | quote_string }*/, + /*{ binding_path | quote_string }*/, /*{ component.action_type }*/, - InteractionProfileAvailability::Avail_/*{- component.availability.as_normalized_symbol() }*/ - //# if component.system + InteractionProfileAvailability::Avail_/*{- avail.as_normalized_symbol() }*/ + //#- if component.system , true //# endif } @@ -24,13 +24,14 @@ const std::vector& GetAllInteractionProfiles() // // Generated lists of component paths for interaction profiles, with metadata and availability expressions. // + //# for path, profile in interaction_profiles.items() - static const InputSourcePathAvailCollection /*{'c' + (path | replace("/", "_")) }*/{ -//# for component in profile.components.values() -//# for user_path in component.valid_user_paths - /*{ make_qualified_path_entry(user_path, component) | collapse_whitespace }*/, -//# endfor + // Interaction profile path: /*{ path }*/ + // Availability: /*{ profile.availability }*/ + static const InputSourcePathAvailCollection /*{'c' + (path | replace("/", "_") | replace("-", "_")) }*/{ +//# for binding_path, avail, component in profile.generate_binding_paths() + /*{ make_path_entry(binding_path, avail, component) | collapse_whitespace }*/, //# endfor }; @@ -46,12 +47,12 @@ const std::vector& GetAllInteractionProfiles() /*{ profile.name | quote_string }*/, /*{ profile.name | replace("/interaction_profiles/", "") | quote_string }*/, { - //# for user_path in profile.valid_user_paths + //# for user_path in profile.valid_user_paths | sort /*{ user_path | quote_string }*/, //# endfor }, InteractionProfileAvailability::Avail_/*{- profile.availability.as_normalized_symbol() -}*/, - /*{'c' + (path |replace("/", "_")) }*/ + /*{'c' + (path | replace("/", "_") | replace("-", "_")) }*/ }, //# endfor }; diff --git a/src/scripts/template_interaction_info_generated.h b/src/scripts/template_interaction_info_generated.h index 65ba0856..8f8fc467 100644 --- a/src/scripts/template_interaction_info_generated.h +++ b/src/scripts/template_interaction_info_generated.h @@ -61,7 +61,7 @@ static const std::array kInteract /// This is a generated list of all interaction profiles in the order returned by GetAllInteractionProfiles. enum class InteractionProfileIndex { //# for path, profile in interaction_profiles.items() - Profile_/*{ (path | replace("/interaction_profiles/", "") | replace("/", "_")) }*/, + Profile_/*{ (path | replace("/interaction_profiles/", "") | replace("/", "_") | replace("-", "_")) }*/, //# endfor }; From 3db438ce431ed4c8b6f9af9e14674c9410a33d85 Mon Sep 17 00:00:00 2001 From: Rylie Pavlik Date: Fri, 8 Nov 2024 15:40:37 -0600 Subject: [PATCH 2/2] OpenXR CTS 1.1.42.0 (2024-11-07) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This release, like the previous one, has a separate “Usage Guide” document with instructions and command lines, instead of a complex README. See the previous release notes for more info. There is one known issue in this release, but it only affects the self-tests (not required for conformance) under OpenGL. Passing exclude:[self_test] when running interactive tests with OpenGL will work around the issue. A fix is known and in review, expected by next release. - Conformance Tests - Fix: Update generated code to allow - in interaction profile paths. (internal MR 3493) - Fix: Allow interaction profiles which do not expose boolean paths to be tested. (internal MR 3506) - Fix: Include interaction profile dependencies in generated binding path dependencies. (internal MR 3507, internal issue 2350) - Fix: Correctly parse and process interaction profile path and binding path dependencies from XML, and clean up code in interaction_profile_processor.py. (internal MR 3507, internal MR 3521) - Fix: Add missing XML description of EXT_hand_interaction additions to several interaction profiles, affecting generated interaction profile tests. (internal MR 3517, internal MR 3541, internal MR 3544) - Fix: remove unused functionPtr field in global FunctionInfoMap as it is invalid if called with a different instance. (internal MR 3547) - Fix: Correct an outdated description of the self tests in the Usage document: glTF files now load asynchronously in those tests. (internal MR 3553) - Fix: Internal cross-references in the “Submission Package” section of the Usage document. (internal MR 3562) - Improvement: Update stb_image from 2.27 to 2.30 (latest upstream) for fixes, security improvements, and optimizations. (internal MR 2578) - Improvement: Update stb_typetype to latest upstream for float comparison fixes. (internal MR 2578) - Improvement: Update Catch2 from v3.3.2 to v3.7.1, including our custom fork of the jUnit reporter. Provides build-time and runtime performance improvements, among other changes. (internal MR 2893) - Improvement: Reorganize session state tests and use test sections, annotating assertions with spec links. (internal MR 3017, internal MR 3468) - Improvement: Update tinygltf to version 2.9.3. (internal MR 3366) - Improvement: List failed test cases, and unmatched test specs, at end of CTS run. (internal MR 3489) - Improvement: Add warnings if supporting OpenXR 1.1 but not supporting optional, but recommended, extensions providing compatibility for OpenXR 1.0 apps/engines. (internal MR 3499) - Improvement: Make generated interaction profile sources reproducible/deterministic. (internal MR 3507) - Improvement: Correct the build-time dependencies of the generated interaction profile data. (internal MR 3521) - Improvement: Note in the Usage document that /interaction_profiles/ext/hand_interaction_ext and /interaction_profiles/ext/eye_gaze_interaction are generic interaction profiles that should be tested if supported. (internal MR 3524, internal MR 3506) - Improvement: Added support for PRIMARY_STEREO_WITH_FOVEATED_INSET in xrLocateViews tests. (internal MR 3527, internal MR 3571) - Improvement: Add command lines for Android for the self tests in the Usage document. (internal MR 3553) - New test: Show a perceptively-smooth gradient in two swapchain formats or two layer types (projection vs quad), to do basic rendering verification. This primarily catches issues where non-linear textures are being interpreted as linear or vice-versa in a runtime’s compositor. (internal MR 3351, internal issue 2239) - New test: Check xrCreateInstance patterns required by API layers. (internal MR 3415, internal issue 2333, OpenXR-CTS PR 94) - New test: Verify behavior when session state related calls occur out of order. (internal MR 3468, internal MR 3579) - New test: When using XR_FB_space_warp, verify runtime returns XR_ERROR_VALIDATION_FAILURE if nearZ is equal to farZ (internal MR 3523) - New test: Add additional assertions for xrGetInputSourceLocalizedName, after removing incorrect portions of the test. (internal MR 3534, internal MR 3559) GitOrigin-RevId: f71e0129647d748aa9d876189d465a01e91965f7 --- .reuse/dep5 | 4 +- CHANGELOG.CTS.md | 101 + changes/conformance/mr.2578.gl.md | 2 - changes/conformance/mr.3017.gl.md | 1 - changes/conformance/mr.3351.gl.md | 4 - changes/conformance/mr.3366.gl.md | 1 - changes/conformance/mr.3489.gl.md | 1 - changes/conformance/mr.3493.gl.md | 1 - changes/conformance/mr.3499.gl.md | 4 - changes/conformance/mr.3506.gl.md | 1 - changes/conformance/mr.3507.gl.1.md | 1 - changes/conformance/mr.3507.gl.2.md | 5 - changes/conformance/mr.3507.gl.md | 4 - changes/conformance/mr.3517.gl.md | 6 - changes/conformance/mr.3521.gl.md | 1 - changes/conformance/mr.3523.gl.md | 1 - changes/conformance/mr.3524.gl.md | 4 - changes/conformance/mr.3527.gl.md | 1 - changes/conformance/mr.3534.gl.md | 5 - changes/conformance/mr.3547.gl.md | 1 - changes/conformance/mr.3553.gl.md | 2 - changes/conformance/mr.3562.gl.md | 1 - specification/Makefile | 2 +- .../conformance_layer/Negotiate.cpp | 12 +- .../conformance_test/CMakeLists.txt | 47 + ...ILAYER_KHRONOS_conformance_test_layer.json | 14 + ...HRONOS_conformance_test_layer.json.license | 3 + .../conformance_test/conformance_test.cpp | 3 +- .../conformance_test/conformance_test.def | 9 + .../conformance_test/conformance_test.expsym | 8 + .../conformance_test/conformance_test.map | 15 + .../conformance_test_layer.json | 14 + .../conformance_test_layer.json.license | 3 + .../conformance_test/test_SessionState.cpp | 211 +- .../conformance_test/test_apiLayer.cpp | 264 + .../test_xrCreateInstance.cpp | 3 +- .../conformance_test/test_xrLocateViews.cpp | 7 + .../framework/catch_reporter_cts.cpp | 10 +- src/conformance/framework/two_call.h | 4 +- src/external/catch2/.clang-tidy | 81 + src/external/catch2/.gitignore | 3 + src/external/catch2/BUILD.bazel | 3 + .../catch2/CMake/CatchConfigOptions.cmake | 12 +- .../catch2/CMake/CatchMiscFunctions.cmake | 6 +- src/external/catch2/CMakeLists.txt | 10 +- src/external/catch2/CMakePresets.json | 3 +- src/external/catch2/Doxyfile | 308 +- src/external/catch2/MODULE.bazel | 3 + src/external/catch2/WORKSPACE.bazel | 7 +- src/external/catch2/appveyor.yml | 6 +- src/external/catch2/conanfile.py | 131 +- src/external/catch2/docs/assertions.md | 2 +- src/external/catch2/docs/benchmarks.md | 2 +- src/external/catch2/docs/ci-and-misc.md | 2 +- src/external/catch2/docs/cmake-integration.md | 34 +- src/external/catch2/docs/command-line.md | 116 +- src/external/catch2/docs/configuration.md | 29 +- src/external/catch2/docs/contributing.md | 3 +- src/external/catch2/docs/deprecations.md | 13 + src/external/catch2/docs/faq.md | 27 +- src/external/catch2/docs/generators.md | 59 +- src/external/catch2/docs/limitations.md | 20 +- src/external/catch2/docs/list-of-examples.md | 1 + src/external/catch2/docs/logging.md | 4 + src/external/catch2/docs/matchers.md | 80 +- src/external/catch2/docs/opensource-users.md | 11 +- src/external/catch2/docs/other-macros.md | 24 - src/external/catch2/docs/release-notes.md | 213 +- src/external/catch2/docs/reporter-events.md | 12 +- src/external/catch2/docs/reporters.md | 4 +- .../catch2/docs/skipping-passing-failing.md | 8 +- .../catch2/docs/test-cases-and-sections.md | 4 +- src/external/catch2/docs/test-fixtures.md | 137 +- src/external/catch2/docs/tostring.md | 2 +- src/external/catch2/docs/tutorial.md | 11 +- src/external/catch2/docs/why-catch.md | 6 +- src/external/catch2/examples/010-TestCase.cpp | 8 + .../catch2/examples/020-TestCase-1.cpp | 8 + .../catch2/examples/020-TestCase-2.cpp | 8 + .../catch2/examples/030-Asn-Require-Check.cpp | 8 + .../catch2/examples/100-Fix-Section.cpp | 8 + .../catch2/examples/110-Fix-ClassFixture.cpp | 8 + .../examples/111-Fix-PersistentFixture.cpp | 74 + .../120-Bdd-ScenarioGivenWhenThen.cpp | 8 + .../examples/210-Evt-EventListeners.cpp | 11 +- .../catch2/examples/231-Cfg-OutputStreams.cpp | 10 +- .../catch2/examples/232-Cfg-CustomMain.cpp | 41 + .../catch2/examples/300-Gen-OwnGenerator.cpp | 10 +- .../examples/301-Gen-MapTypeConversion.cpp | 25 +- .../catch2/examples/302-Gen-Table.cpp | 12 +- .../310-Gen-VariablesInGenerators.cpp | 8 + .../catch2/examples/311-Gen-CustomCapture.cpp | 8 + src/external/catch2/examples/CMakeLists.txt | 7 +- src/external/catch2/extras/Catch.cmake | 189 +- .../catch2/extras/CatchAddTests.cmake | 301 +- .../catch2/extras/CatchShardTests.cmake | 10 +- .../catch2/extras/ParseAndAddCatchTests.cmake | 2 +- .../catch2/extras/catch_amalgamated.cpp | 2942 +++++++--- .../catch2/extras/catch_amalgamated.hpp | 5020 ++++++++++------- src/external/catch2/fuzzing/NullOStream.cpp | 8 + src/external/catch2/fuzzing/NullOStream.h | 8 + .../catch2/fuzzing/fuzz_TestSpecParser.cpp | 8 +- .../catch2/fuzzing/fuzz_XmlWriter.cpp | 8 +- src/external/catch2/fuzzing/fuzz_textflow.cpp | 8 +- src/external/catch2/meson.build | 8 +- src/external/catch2/meson_options.txt | 1 + src/external/catch2/src/CMakeLists.txt | 75 +- .../src/catch2/benchmark/catch_benchmark.hpp | 30 +- .../catch2/benchmark/catch_benchmark_all.hpp | 2 + .../catch2/benchmark/catch_chronometer.hpp | 6 +- .../src/catch2/benchmark/catch_clock.hpp | 16 +- .../catch2/benchmark/catch_environment.hpp | 14 +- .../src/catch2/benchmark/catch_estimate.hpp | 13 +- .../catch2/benchmark/catch_execution_plan.hpp | 40 +- .../src/catch2/benchmark/catch_optimizer.hpp | 13 +- .../catch_outlier_classification.hpp | 2 +- .../benchmark/catch_sample_analysis.hpp | 26 +- .../catch2/benchmark/detail/catch_analyse.cpp | 85 + .../catch2/benchmark/detail/catch_analyse.hpp | 59 +- .../detail/catch_benchmark_function.cpp | 6 + .../detail/catch_benchmark_function.hpp | 26 +- .../detail/catch_benchmark_stats.hpp | 48 + .../detail/catch_benchmark_stats_fwd.hpp | 23 + .../detail/catch_complete_invoke.hpp | 5 - .../benchmark/detail/catch_estimate_clock.hpp | 70 +- .../catch2/benchmark/detail/catch_measure.hpp | 5 +- .../detail/catch_run_for_at_least.cpp | 3 +- .../detail/catch_run_for_at_least.hpp | 8 +- .../catch2/benchmark/detail/catch_stats.cpp | 458 +- .../catch2/benchmark/detail/catch_stats.hpp | 126 +- .../catch2/benchmark/detail/catch_timing.hpp | 8 +- src/external/catch2/src/catch2/catch_all.hpp | 11 +- .../catch2/src/catch2/catch_approx.cpp | 6 +- .../src/catch2/catch_assertion_result.cpp | 2 +- .../{internal => }/catch_case_sensitive.hpp | 0 .../catch2/src/catch2/catch_config.cpp | 14 +- .../catch2/src/catch2/catch_config.hpp | 2 +- .../catch2/src/catch2/catch_message.cpp | 8 +- .../catch2/src/catch2/catch_message.hpp | 16 +- .../catch2/src/catch2/catch_registry_hub.cpp | 4 +- .../catch2/src/catch2/catch_session.cpp | 41 +- .../src/catch2/catch_test_case_info.cpp | 45 +- .../src/catch2/catch_test_case_info.hpp | 19 +- .../catch2/src/catch2/catch_test_macros.hpp | 4 + .../catch2/src/catch2/catch_test_spec.cpp | 20 +- .../catch2/src/catch2/catch_timer.cpp | 2 +- .../catch2/src/catch2/catch_tostring.cpp | 14 +- .../catch2/src/catch2/catch_tostring.hpp | 11 +- .../src/catch2/catch_translate_exception.cpp | 20 + .../src/catch2/catch_translate_exception.hpp | 12 +- .../src/catch2/catch_user_config.hpp.in | 12 +- .../catch2/src/catch2/catch_version.cpp | 2 +- .../src/catch2/catch_version_macros.hpp | 4 +- .../catch2/generators/catch_generators.hpp | 6 - .../generators/catch_generators_random.cpp | 32 +- .../generators/catch_generators_random.hpp | 35 +- .../generators/catch_generators_range.hpp | 9 +- .../interfaces/catch_interfaces_all.hpp | 2 +- .../interfaces/catch_interfaces_capture.hpp | 9 +- .../interfaces/catch_interfaces_exception.hpp | 1 - .../catch_interfaces_registry_hub.hpp | 4 +- .../interfaces/catch_interfaces_reporter.cpp | 10 - .../interfaces/catch_interfaces_reporter.hpp | 52 +- .../catch_interfaces_reporter_registry.cpp | 13 - .../catch_interfaces_reporter_registry.hpp | 42 - .../catch_interfaces_test_invoker.hpp | 23 + .../interfaces/catch_interfaces_testcase.cpp | 1 - .../interfaces/catch_interfaces_testcase.hpp | 13 - .../internal/catch_assertion_handler.cpp | 22 +- .../internal/catch_assertion_handler.hpp | 8 +- .../src/catch2/internal/catch_clara.cpp | 124 +- .../src/catch2/internal/catch_clara.hpp | 134 +- .../src/catch2/internal/catch_commandline.cpp | 11 +- .../internal/catch_compiler_capabilities.hpp | 41 +- .../catch2/internal/catch_config_counter.hpp | 2 + .../internal/catch_config_prefix_messages.hpp | 29 + .../catch_config_static_analysis_support.hpp | 34 + .../catch_config_uncaught_exceptions.hpp | 2 + .../catch2/internal/catch_config_wchar.hpp | 2 + .../catch2/internal/catch_console_colour.cpp | 18 +- .../src/catch2/internal/catch_context.cpp | 48 +- .../src/catch2/internal/catch_context.hpp | 48 +- .../src/catch2/internal/catch_decomposer.cpp | 7 +- .../src/catch2/internal/catch_decomposer.hpp | 211 +- .../internal/catch_enum_values_registry.cpp | 2 +- .../internal/catch_enum_values_registry.hpp | 2 +- .../catch_exception_translator_registry.cpp | 34 +- .../catch_exception_translator_registry.hpp | 1 - .../catch_fatal_condition_handler.cpp | 1 + .../catch_fatal_condition_handler.hpp | 3 - .../internal/catch_floating_point_helpers.cpp | 11 + .../internal/catch_floating_point_helpers.hpp | 5 + .../catch2/internal/catch_is_permutation.hpp | 27 +- .../src/catch2/internal/catch_istream.cpp | 13 +- .../src/catch2/internal/catch_jsonwriter.cpp | 148 + .../src/catch2/internal/catch_jsonwriter.hpp | 120 + .../src/catch2/internal/catch_lazy_expr.hpp | 6 +- .../catch2/internal/catch_leak_detector.cpp | 2 +- .../catch2/src/catch2/internal/catch_list.cpp | 9 +- .../catch2/internal/catch_message_info.hpp | 2 +- .../src/catch2/internal/catch_optional.hpp | 55 +- .../catch2/internal/catch_output_redirect.cpp | 389 +- .../catch2/internal/catch_output_redirect.hpp | 137 +- .../src/catch2/internal/catch_platform.hpp | 3 + .../src/catch2/internal/catch_polyfills.cpp | 8 + .../src/catch2/internal/catch_polyfills.hpp | 5 + .../catch_preprocessor_internal_stringify.hpp | 19 + .../catch_random_floating_point_helpers.hpp | 94 + .../internal/catch_random_integer_helpers.hpp | 224 + .../internal/catch_random_seed_generation.cpp | 9 +- .../internal/catch_reporter_registry.cpp | 86 +- .../internal/catch_reporter_registry.hpp | 45 +- .../internal/catch_reporter_spec_parser.cpp | 8 +- .../internal/catch_reporter_spec_parser.hpp | 2 +- .../src/catch2/internal/catch_result_type.cpp | 26 - .../src/catch2/internal/catch_result_type.hpp | 23 +- .../src/catch2/internal/catch_run_context.cpp | 394 +- .../src/catch2/internal/catch_run_context.hpp | 20 +- .../src/catch2/internal/catch_section.cpp | 2 +- .../src/catch2/internal/catch_section.hpp | 73 +- .../src/catch2/internal/catch_sharding.hpp | 3 +- .../catch2/internal/catch_stream_end_stop.hpp | 4 +- .../catch2/internal/catch_string_manip.cpp | 34 +- .../src/catch2/internal/catch_stringref.hpp | 8 +- .../internal/catch_tag_alias_registry.cpp | 3 +- .../catch_test_case_registry_impl.cpp | 69 +- .../catch_test_case_registry_impl.hpp | 25 +- .../internal/catch_test_case_tracker.hpp | 2 +- .../internal/catch_test_failure_exception.cpp | 8 + .../internal/catch_test_failure_exception.hpp | 10 +- .../catch2/internal/catch_test_macro_impl.hpp | 26 +- .../catch2/internal/catch_test_registry.cpp | 18 +- .../catch2/internal/catch_test_registry.hpp | 120 +- .../catch2/internal/catch_test_run_info.hpp | 22 + .../src/catch2/internal/catch_textflow.cpp | 250 +- .../src/catch2/internal/catch_textflow.hpp | 167 +- .../internal/catch_uncaught_exceptions.cpp | 1 - ...ch_uniform_floating_point_distribution.hpp | 131 + .../catch_uniform_integer_distribution.hpp | 108 + .../internal/catch_wildcard_pattern.hpp | 2 +- .../src/catch2/internal/catch_xmlwriter.cpp | 24 +- .../src/catch2/internal/catch_xmlwriter.hpp | 19 +- .../catch_matchers_floating_point.cpp | 17 +- .../matchers/catch_matchers_range_equals.hpp | 10 +- .../catch2/matchers/catch_matchers_string.hpp | 4 +- .../catch2/matchers/catch_matchers_vector.hpp | 9 +- .../matchers/internal/catch_matchers_impl.hpp | 36 +- src/external/catch2/src/catch2/meson.build | 35 +- .../reporters/catch_reporter_automake.cpp | 2 +- .../reporters/catch_reporter_automake.hpp | 1 - .../reporters/catch_reporter_compact.cpp | 4 +- .../reporters/catch_reporter_console.cpp | 43 +- .../catch_reporter_cumulative_base.cpp | 3 +- .../catch_reporter_cumulative_base.hpp | 3 +- .../catch2/reporters/catch_reporter_json.cpp | 372 ++ .../catch2/reporters/catch_reporter_json.hpp | 95 + .../catch2/reporters/catch_reporter_junit.cpp | 10 +- .../catch2/reporters/catch_reporter_junit.hpp | 2 - .../catch2/reporters/catch_reporter_multi.cpp | 1 - .../catch2/reporters/catch_reporter_multi.hpp | 2 +- .../reporters/catch_reporter_registrars.cpp | 6 + .../reporters/catch_reporter_registrars.hpp | 7 +- .../reporters/catch_reporter_sonarqube.cpp | 6 +- .../reporters/catch_reporter_sonarqube.hpp | 6 +- .../catch_reporter_streaming_base.hpp | 1 - .../catch2/reporters/catch_reporter_tap.cpp | 3 +- .../catch2/reporters/catch_reporter_tap.hpp | 1 - .../reporters/catch_reporter_teamcity.cpp | 2 +- .../reporters/catch_reporter_teamcity.hpp | 4 +- .../catch2/reporters/catch_reporter_xml.cpp | 21 +- .../catch2/reporters/catch_reporters_all.hpp | 1 + src/external/catch2/tests/BUILD.bazel | 83 + src/external/catch2/tests/CMakeLists.txt | 24 +- .../catch2/tests/ExtraTests/CMakeLists.txt | 26 +- .../tests/ExtraTests/X02-DisabledMacros.cpp | 46 +- ...ingEventGoesBeforeAssertionIsEvaluated.cpp | 77 + .../X22-BenchmarksInCumulativeReporter.cpp | 2 +- .../X29-CustomArgumentsForReporters.cpp | 1 + .../X36-ReportingCrashWithJunitReporter.cpp | 32 + .../tests/ExtraTests/X91-AmalgamatedCatch.cpp | 4 +- .../Baselines/automake.sw.approved.txt | 14 +- .../Baselines/automake.sw.multi.approved.txt | 14 +- .../Baselines/compact.sw.approved.txt | 620 +- .../Baselines/compact.sw.multi.approved.txt | 620 +- .../Baselines/console.std.approved.txt | 105 +- .../Baselines/console.sw.approved.txt | 1205 +++- .../Baselines/console.sw.multi.approved.txt | 1205 +++- .../SelfTest/Baselines/junit.sw.approved.txt | 293 +- .../Baselines/junit.sw.multi.approved.txt | 293 +- .../Baselines/sonarqube.sw.approved.txt | 284 +- .../Baselines/sonarqube.sw.multi.approved.txt | 284 +- .../SelfTest/Baselines/tap.sw.approved.txt | 406 +- .../Baselines/tap.sw.multi.approved.txt | 406 +- .../Baselines/teamcity.sw.approved.txt | 58 +- .../Baselines/teamcity.sw.multi.approved.txt | 58 +- .../SelfTest/Baselines/xml.sw.approved.txt | 1734 ++++-- .../Baselines/xml.sw.multi.approved.txt | 1734 ++++-- .../AssertionHandler.tests.cpp | 17 + .../IntrospectiveTests/Clara.tests.cpp | 15 + .../IntrospectiveTests/Details.tests.cpp | 41 + .../FloatingPoint.tests.cpp | 65 + .../GeneratorsImpl.tests.cpp | 31 +- .../IntrospectiveTests/Integer.tests.cpp | 224 + .../InternalBenchmark.tests.cpp | 60 +- .../IntrospectiveTests/Json.tests.cpp | 152 + .../RandomNumberGeneration.tests.cpp | 547 ++ .../IntrospectiveTests/Reporters.tests.cpp | 11 +- .../IntrospectiveTests/String.tests.cpp | 2 +- .../IntrospectiveTests/StringManip.tests.cpp | 11 + .../SelfTest/IntrospectiveTests/Tag.tests.cpp | 19 +- .../IntrospectiveTests/TestSpec.tests.cpp | 2 +- .../IntrospectiveTests/TextFlow.tests.cpp | 202 +- .../tests/SelfTest/TestRegistrations.cpp | 1 - .../SelfTest/UsageTests/Benchmark.tests.cpp | 14 +- .../tests/SelfTest/UsageTests/Class.tests.cpp | 28 +- .../SelfTest/UsageTests/Compilation.tests.cpp | 118 +- .../SelfTest/UsageTests/Exception.tests.cpp | 6 +- .../SelfTest/UsageTests/Generators.tests.cpp | 20 +- .../SelfTest/UsageTests/Matchers.tests.cpp | 35 +- .../UsageTests/MatchersRanges.tests.cpp | 12 +- .../SelfTest/UsageTests/Message.tests.cpp | 41 +- .../tests/SelfTest/UsageTests/Misc.tests.cpp | 29 +- .../tests/SelfTest/UsageTests/Skip.tests.cpp | 27 + .../UsageTests/ToStringGeneral.tests.cpp | 33 +- .../UsageTests/ToStringOptional.tests.cpp | 4 + .../UsageTests/ToStringTuple.tests.cpp | 8 +- .../SelfTest/UsageTests/Tricky.tests.cpp | 4 +- .../helpers/type_with_lit_0_comparisons.hpp | 29 +- .../TestScripts/DiscoverTests/CMakeLists.txt | 22 + .../DiscoverTests/VerifyRegistration.py | 153 + .../DiscoverTests/register-tests.cpp | 16 + .../tests/TestScripts/testBazelReporter.py | 2 +- .../testConfigureDefaultReporter.py | 19 +- src/external/catch2/tests/meson.build | 1 + .../tools/misc/appveyorTestRunScript.bat | 2 +- .../catch2/tools/scripts/buildAndTest.cmd | 3 +- .../catch2/tools/scripts/buildAndTest.sh | 3 +- .../catch2/tools/scripts/checkLicense.py | 3 +- .../tools/scripts/generateAmalgamatedFiles.py | 10 + .../catch2/tools/scripts/releaseCommon.py | 6 +- .../catch2/tools/scripts/updateDocumentToC.py | 2 +- 341 files changed, 21188 insertions(+), 7641 deletions(-) delete mode 100644 changes/conformance/mr.2578.gl.md delete mode 100644 changes/conformance/mr.3017.gl.md delete mode 100644 changes/conformance/mr.3351.gl.md delete mode 100644 changes/conformance/mr.3366.gl.md delete mode 100644 changes/conformance/mr.3489.gl.md delete mode 100644 changes/conformance/mr.3493.gl.md delete mode 100644 changes/conformance/mr.3499.gl.md delete mode 100644 changes/conformance/mr.3506.gl.md delete mode 100644 changes/conformance/mr.3507.gl.1.md delete mode 100644 changes/conformance/mr.3507.gl.2.md delete mode 100644 changes/conformance/mr.3507.gl.md delete mode 100644 changes/conformance/mr.3517.gl.md delete mode 100644 changes/conformance/mr.3521.gl.md delete mode 100644 changes/conformance/mr.3523.gl.md delete mode 100644 changes/conformance/mr.3524.gl.md delete mode 100644 changes/conformance/mr.3527.gl.md delete mode 100644 changes/conformance/mr.3534.gl.md delete mode 100644 changes/conformance/mr.3547.gl.md delete mode 100644 changes/conformance/mr.3553.gl.md delete mode 100644 changes/conformance/mr.3562.gl.md create mode 100644 src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json create mode 100644 src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json.license create mode 100644 src/conformance/conformance_test/conformance_test.def create mode 100644 src/conformance/conformance_test/conformance_test.expsym create mode 100644 src/conformance/conformance_test/conformance_test.map create mode 100644 src/conformance/conformance_test/conformance_test_layer.json create mode 100644 src/conformance/conformance_test/conformance_test_layer.json.license create mode 100644 src/conformance/conformance_test/test_apiLayer.cpp create mode 100644 src/external/catch2/.clang-tidy create mode 100644 src/external/catch2/MODULE.bazel mode change 100644 => 100755 src/external/catch2/conanfile.py create mode 100644 src/external/catch2/examples/111-Fix-PersistentFixture.cpp create mode 100644 src/external/catch2/examples/232-Cfg-CustomMain.cpp create mode 100644 src/external/catch2/meson_options.txt create mode 100644 src/external/catch2/src/catch2/benchmark/detail/catch_analyse.cpp create mode 100644 src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp create mode 100644 src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp rename src/external/catch2/src/catch2/{internal => }/catch_case_sensitive.hpp (100%) create mode 100644 src/external/catch2/src/catch2/catch_translate_exception.cpp delete mode 100644 src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.cpp delete mode 100644 src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.hpp create mode 100644 src/external/catch2/src/catch2/interfaces/catch_interfaces_test_invoker.hpp create mode 100644 src/external/catch2/src/catch2/internal/catch_config_prefix_messages.hpp create mode 100644 src/external/catch2/src/catch2/internal/catch_config_static_analysis_support.hpp create mode 100644 src/external/catch2/src/catch2/internal/catch_jsonwriter.cpp create mode 100644 src/external/catch2/src/catch2/internal/catch_jsonwriter.hpp create mode 100644 src/external/catch2/src/catch2/internal/catch_preprocessor_internal_stringify.hpp create mode 100644 src/external/catch2/src/catch2/internal/catch_random_floating_point_helpers.hpp create mode 100644 src/external/catch2/src/catch2/internal/catch_random_integer_helpers.hpp delete mode 100644 src/external/catch2/src/catch2/internal/catch_result_type.cpp create mode 100644 src/external/catch2/src/catch2/internal/catch_test_run_info.hpp create mode 100644 src/external/catch2/src/catch2/internal/catch_uniform_floating_point_distribution.hpp create mode 100644 src/external/catch2/src/catch2/internal/catch_uniform_integer_distribution.hpp create mode 100644 src/external/catch2/src/catch2/reporters/catch_reporter_json.cpp create mode 100644 src/external/catch2/src/catch2/reporters/catch_reporter_json.hpp create mode 100644 src/external/catch2/tests/BUILD.bazel create mode 100644 src/external/catch2/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp create mode 100644 src/external/catch2/tests/ExtraTests/X36-ReportingCrashWithJunitReporter.cpp create mode 100644 src/external/catch2/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp create mode 100644 src/external/catch2/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp create mode 100644 src/external/catch2/tests/SelfTest/IntrospectiveTests/Json.tests.cpp create mode 100644 src/external/catch2/tests/TestScripts/DiscoverTests/CMakeLists.txt create mode 100644 src/external/catch2/tests/TestScripts/DiscoverTests/VerifyRegistration.py create mode 100644 src/external/catch2/tests/TestScripts/DiscoverTests/register-tests.cpp diff --git a/.reuse/dep5 b/.reuse/dep5 index ac059d99..51db8c8d 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -36,9 +36,9 @@ Comment: Based on a Material Icons asset ("emoji-people") with added text Rasterized with Android Studio. Files: src/external/catch2/* -Copyright: Copyright (c) 2023 Two Blue Cubes Ltd. +Copyright: Copyright Catch2 Authors License: BSL-1.0 -Comment: Unmodified, vendored copy of Catch2 v3.3.2 +Comment: Unmodified, vendored copy of Catch2 v3.7.1 Files: src/external/jsoncpp/* Copyright: 2007-2010 Baptiste Lepilleur and The JsonCpp Authors diff --git a/CHANGELOG.CTS.md b/CHANGELOG.CTS.md index 77dabfa1..21a16161 100644 --- a/CHANGELOG.CTS.md +++ b/CHANGELOG.CTS.md @@ -17,6 +17,107 @@ particular, since it is primarily software, pull requests may be integrated as they are accepted even between periodic updates. However, versions that are not signed tags on the `approved` branch are not valid for conformance submission. +## OpenXR CTS 1.1.42.0 (2024-11-07) + +This release, like the previous one, has a separate "Usage Guide" document with +instructions and command lines, instead of a complex README. See the previous +release notes for more info. + +There is one known issue in this release, but it only affects the self-tests +(not required for conformance) under OpenGL. Passing `exclude:[self_test]` when +running interactive tests with OpenGL will work around the issue. A fix is known +and in review, expected by next release. + +- Conformance Tests + - Fix: Update generated code to allow `-` in interaction profile paths. + ([internal MR 3493](https://gitlab.khronos.org/openxr/openxr/merge_requests/3493)) + - Fix: Allow interaction profiles which do not expose boolean paths to be tested. + ([internal MR 3506](https://gitlab.khronos.org/openxr/openxr/merge_requests/3506)) + - Fix: Include interaction profile dependencies in generated binding path + dependencies. + ([internal MR 3507](https://gitlab.khronos.org/openxr/openxr/merge_requests/3507), + [internal issue 2350](https://gitlab.khronos.org/openxr/openxr/issues/2350)) + - Fix: Correctly parse and process interaction profile path and binding path + dependencies from XML, and clean up code in `interaction_profile_processor.py`. + ([internal MR 3507](https://gitlab.khronos.org/openxr/openxr/merge_requests/3507), + [internal MR 3521](https://gitlab.khronos.org/openxr/openxr/merge_requests/3521)) + - Fix: Add missing XML description of `EXT_hand_interaction` additions to several + interaction profiles, affecting generated interaction profile tests. + ([internal MR 3517](https://gitlab.khronos.org/openxr/openxr/merge_requests/3517), + [internal MR 3541](https://gitlab.khronos.org/openxr/openxr/merge_requests/3541), + [internal MR 3544](https://gitlab.khronos.org/openxr/openxr/merge_requests/3544)) + - Fix: remove unused `functionPtr` field in global `FunctionInfoMap` as it is + invalid if called with a different instance. + ([internal MR 3547](https://gitlab.khronos.org/openxr/openxr/merge_requests/3547)) + - Fix: Correct an outdated description of the self tests in the Usage document: + glTF files now load asynchronously in those tests. + ([internal MR 3553](https://gitlab.khronos.org/openxr/openxr/merge_requests/3553)) + - Fix: Internal cross-references in the "Submission Package" section of the Usage + document. + ([internal MR 3562](https://gitlab.khronos.org/openxr/openxr/merge_requests/3562)) + - Improvement: Update `stb_image` from 2.27 to 2.30 (latest upstream) for fixes, + security improvements, and optimizations. + ([internal MR 2578](https://gitlab.khronos.org/openxr/openxr/merge_requests/2578)) + - Improvement: Update `stb_typetype` to latest upstream for float comparison + fixes. + ([internal MR 2578](https://gitlab.khronos.org/openxr/openxr/merge_requests/2578)) + - Improvement: Update Catch2 from v3.3.2 to v3.7.1, including our custom fork of + the jUnit reporter. Provides build-time and runtime performance improvements, + among other changes. + ([internal MR 2893](https://gitlab.khronos.org/openxr/openxr/merge_requests/2893)) + - Improvement: Reorganize session state tests and use test sections, annotating + assertions with spec links. + ([internal MR 3017](https://gitlab.khronos.org/openxr/openxr/merge_requests/3017), + [internal MR 3468](https://gitlab.khronos.org/openxr/openxr/merge_requests/3468)) + - Improvement: Update tinygltf to version 2.9.3. + ([internal MR 3366](https://gitlab.khronos.org/openxr/openxr/merge_requests/3366)) + - Improvement: List failed test cases, and unmatched test specs, at end of CTS + run. + ([internal MR 3489](https://gitlab.khronos.org/openxr/openxr/merge_requests/3489)) + - Improvement: Add warnings if supporting OpenXR 1.1 but not supporting optional, + but recommended, extensions providing compatibility for OpenXR 1.0 + apps/engines. + ([internal MR 3499](https://gitlab.khronos.org/openxr/openxr/merge_requests/3499)) + - Improvement: Make generated interaction profile sources + reproducible/deterministic. + ([internal MR 3507](https://gitlab.khronos.org/openxr/openxr/merge_requests/3507)) + - Improvement: Correct the build-time dependencies of the generated interaction + profile data. + ([internal MR 3521](https://gitlab.khronos.org/openxr/openxr/merge_requests/3521)) + - Improvement: Note in the Usage document that + `/interaction_profiles/ext/hand_interaction_ext` and + `/interaction_profiles/ext/eye_gaze_interaction` are generic interaction + profiles that should be tested if supported. + ([internal MR 3524](https://gitlab.khronos.org/openxr/openxr/merge_requests/3524), + [internal MR 3506](https://gitlab.khronos.org/openxr/openxr/merge_requests/3506)) + - Improvement: Added support for `PRIMARY_STEREO_WITH_FOVEATED_INSET` in + `xrLocateViews` tests. + ([internal MR 3527](https://gitlab.khronos.org/openxr/openxr/merge_requests/3527), + [internal MR 3571](https://gitlab.khronos.org/openxr/openxr/merge_requests/3571)) + - Improvement: Add command lines for Android for the self tests in the Usage + document. + ([internal MR 3553](https://gitlab.khronos.org/openxr/openxr/merge_requests/3553)) + - New test: Show a perceptively-smooth gradient in two swapchain formats or two + layer types (projection vs quad), to do basic rendering verification. This + primarily catches issues where non-linear textures are being interpreted as + linear or vice-versa in a runtime's compositor. + ([internal MR 3351](https://gitlab.khronos.org/openxr/openxr/merge_requests/3351), + [internal issue 2239](https://gitlab.khronos.org/openxr/openxr/issues/2239)) + - New test: Check `xrCreateInstance` patterns required by API layers. + ([internal MR 3415](https://gitlab.khronos.org/openxr/openxr/merge_requests/3415), + [internal issue 2333](https://gitlab.khronos.org/openxr/openxr/issues/2333), + [OpenXR-CTS PR 94](https://github.com/KhronosGroup/OpenXR-CTS/pull/94)) + - New test: Verify behavior when session state related calls occur out of order. + ([internal MR 3468](https://gitlab.khronos.org/openxr/openxr/merge_requests/3468), + [internal MR 3579](https://gitlab.khronos.org/openxr/openxr/merge_requests/3579)) + - New test: When using `XR_FB_space_warp`, verify runtime returns + `XR_ERROR_VALIDATION_FAILURE` if `nearZ` is equal to `farZ` + ([internal MR 3523](https://gitlab.khronos.org/openxr/openxr/merge_requests/3523)) + - New test: Add additional assertions for `xrGetInputSourceLocalizedName`, after + removing incorrect portions of the test. + ([internal MR 3534](https://gitlab.khronos.org/openxr/openxr/merge_requests/3534), + [internal MR 3559](https://gitlab.khronos.org/openxr/openxr/merge_requests/3559)) + ## OpenXR CTS 1.1.41.0 (2024-10-10) An important feature of this release is the new usage guide for the Conformance diff --git a/changes/conformance/mr.2578.gl.md b/changes/conformance/mr.2578.gl.md deleted file mode 100644 index 6b860722..00000000 --- a/changes/conformance/mr.2578.gl.md +++ /dev/null @@ -1,2 +0,0 @@ -- Improvement: Update `stb_image` from 2.27 to 2.30 (latest upstream) for fixes, security improvements, and optimizations. -- Improvement: Update `stb_typetype` to latest upstream for float comparison fixes. diff --git a/changes/conformance/mr.3017.gl.md b/changes/conformance/mr.3017.gl.md deleted file mode 100644 index aa564c9e..00000000 --- a/changes/conformance/mr.3017.gl.md +++ /dev/null @@ -1 +0,0 @@ -Improvement: Reorganize session state tests and use test sections, annotating assertions with spec links. diff --git a/changes/conformance/mr.3351.gl.md b/changes/conformance/mr.3351.gl.md deleted file mode 100644 index fc9ec4a6..00000000 --- a/changes/conformance/mr.3351.gl.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -- issue.2239.gl ---- -- New test: Show a perceptively-smooth gradient in two swapchain formats or two layer types (projection vs quad), to do basic rendering verification. This primarily catches issues where non-linear textures are being interpreted as linear or vice-versa in a runtime's compositor. diff --git a/changes/conformance/mr.3366.gl.md b/changes/conformance/mr.3366.gl.md deleted file mode 100644 index 6d6c98b4..00000000 --- a/changes/conformance/mr.3366.gl.md +++ /dev/null @@ -1 +0,0 @@ -Improvement: Update tinygltf to version 2.9.3. diff --git a/changes/conformance/mr.3489.gl.md b/changes/conformance/mr.3489.gl.md deleted file mode 100644 index b414ea44..00000000 --- a/changes/conformance/mr.3489.gl.md +++ /dev/null @@ -1 +0,0 @@ -Improvement: List failed test cases, and unmatched test specs, at end of CTS run. diff --git a/changes/conformance/mr.3493.gl.md b/changes/conformance/mr.3493.gl.md deleted file mode 100644 index b6afca7f..00000000 --- a/changes/conformance/mr.3493.gl.md +++ /dev/null @@ -1 +0,0 @@ -- Fix: Update generated code to allow dash in interaction profile paths. diff --git a/changes/conformance/mr.3499.gl.md b/changes/conformance/mr.3499.gl.md deleted file mode 100644 index af0bcd80..00000000 --- a/changes/conformance/mr.3499.gl.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -- mr.3499.gl ---- -Improvement: Add warnings if supporting OpenXR 1.1 but not supporting optional, but recommended, extensions providing compatibility for OpenXR 1.0 apps/engines. diff --git a/changes/conformance/mr.3506.gl.md b/changes/conformance/mr.3506.gl.md deleted file mode 100644 index ed72abca..00000000 --- a/changes/conformance/mr.3506.gl.md +++ /dev/null @@ -1 +0,0 @@ -Fix: Allow interaction profiles which do not expose boolean paths to be tested. diff --git a/changes/conformance/mr.3507.gl.1.md b/changes/conformance/mr.3507.gl.1.md deleted file mode 100644 index ad188b5d..00000000 --- a/changes/conformance/mr.3507.gl.1.md +++ /dev/null @@ -1 +0,0 @@ -- Improvement: Make generated interaction profile sources reproducible/deterministic. diff --git a/changes/conformance/mr.3507.gl.2.md b/changes/conformance/mr.3507.gl.2.md deleted file mode 100644 index 96dcd82e..00000000 --- a/changes/conformance/mr.3507.gl.2.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -- mr.3507.gl.2 -- mr.3521.gl ---- -- Fix: Correctly parse and process interaction profile path and binding path dependencies from XML, and clean up code in `interaction_profile_processor.py`. diff --git a/changes/conformance/mr.3507.gl.md b/changes/conformance/mr.3507.gl.md deleted file mode 100644 index 9369faca..00000000 --- a/changes/conformance/mr.3507.gl.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -- issue.2350.gl ---- -- Fix: Include interaction profile dependencies in generated binding path dependencies. diff --git a/changes/conformance/mr.3517.gl.md b/changes/conformance/mr.3517.gl.md deleted file mode 100644 index 618ebadd..00000000 --- a/changes/conformance/mr.3517.gl.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -- mr.3517.gl -- mr.3541.gl -- mr.3544.gl ---- -- Fix: Add missing XML description of `EXT_hand_interaction` additions to several interaction profiles, affecting generated interaction profile tests. diff --git a/changes/conformance/mr.3521.gl.md b/changes/conformance/mr.3521.gl.md deleted file mode 100644 index f77c5879..00000000 --- a/changes/conformance/mr.3521.gl.md +++ /dev/null @@ -1 +0,0 @@ -- Improvement: Correct the build-time dependencies of the generated interaction profile data. diff --git a/changes/conformance/mr.3523.gl.md b/changes/conformance/mr.3523.gl.md deleted file mode 100644 index 372f9359..00000000 --- a/changes/conformance/mr.3523.gl.md +++ /dev/null @@ -1 +0,0 @@ -New test: When using `XR_FB_space_warp`, verify runtime returns `XR_ERROR_VALIDATION_FAILURE` if `nearZ` is equal to `farZ` diff --git a/changes/conformance/mr.3524.gl.md b/changes/conformance/mr.3524.gl.md deleted file mode 100644 index dda7f881..00000000 --- a/changes/conformance/mr.3524.gl.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -- mr.3506.gl ---- -Improvement: Note in the Usage document that `/interaction_profiles/ext/hand_interaction_ext` and `/interaction_profiles/ext/eye_gaze_interaction` are generic interaction profiles that should be tested if supported. diff --git a/changes/conformance/mr.3527.gl.md b/changes/conformance/mr.3527.gl.md deleted file mode 100644 index 098d2da2..00000000 --- a/changes/conformance/mr.3527.gl.md +++ /dev/null @@ -1 +0,0 @@ -Improvement: added support for `PRIMARY_STEREO_WITH_FOVEATED_INSET` in `xrLocateViews` tests. diff --git a/changes/conformance/mr.3534.gl.md b/changes/conformance/mr.3534.gl.md deleted file mode 100644 index b6484234..00000000 --- a/changes/conformance/mr.3534.gl.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -- mr.3534.gl -- mr.3559.gl ---- -Improvement: Add additional tests for `xrGetInputSourceLocalizedName` diff --git a/changes/conformance/mr.3547.gl.md b/changes/conformance/mr.3547.gl.md deleted file mode 100644 index 940c0ca2..00000000 --- a/changes/conformance/mr.3547.gl.md +++ /dev/null @@ -1 +0,0 @@ -- Fix: remove unused `functionPtr` field in global `FunctionInfoMap` as it is invalid if called with a different instance diff --git a/changes/conformance/mr.3553.gl.md b/changes/conformance/mr.3553.gl.md deleted file mode 100644 index c6c3c4fc..00000000 --- a/changes/conformance/mr.3553.gl.md +++ /dev/null @@ -1,2 +0,0 @@ -- Improvement: Add command lines for Android for the self tests in the Usage document. -- Fix: Correct an outdated description of the self tests in the Usage document, glTF files now load asynchronously in those tests. diff --git a/changes/conformance/mr.3562.gl.md b/changes/conformance/mr.3562.gl.md deleted file mode 100644 index 73492d2b..00000000 --- a/changes/conformance/mr.3562.gl.md +++ /dev/null @@ -1 +0,0 @@ -- Fix: Internal cross-references in the "Submission Package" section of the Usage document. diff --git a/specification/Makefile b/specification/Makefile index 76ae74a7..1efa7270 100644 --- a/specification/Makefile +++ b/specification/Makefile @@ -351,7 +351,7 @@ ASCIIDOCTOR_TARGETS += $(PDFSPEC) $(PDFA4SPEC) # Target-specific variables and deps customizing the AsciiDoctor rule # EXTRAATRIBS is for build-time customization -$(PDFSPEC) $(PDFA4SPEC): BACKEND_ARGS=--backend pdf --require asciidoctor-pdf -a compress --require ./scripts/pdf-index-customizer.rb $(EXTRAATTRIBS) +$(PDFSPEC) $(PDFA4SPEC): BACKEND_ARGS=--backend pdf --require asciidoctor-pdf -a compress --require ./scripts/pdf-index-customizer.rb $(EXTRAATTRIBS) $(PDFSPEC): PAGESIZE=LETTER $(PDFA4SPEC): PAGESIZE=A4 $(PDFSPEC) $(PDFA4SPEC): $(COMMONDOCS) diff --git a/src/conformance/conformance_layer/Negotiate.cpp b/src/conformance/conformance_layer/Negotiate.cpp index dad921c0..b05be924 100644 --- a/src/conformance/conformance_layer/Negotiate.cpp +++ b/src/conformance/conformance_layer/Negotiate.cpp @@ -19,8 +19,12 @@ #include "HandleState.h" #include "gen_dispatch.h" +#include + namespace { + static const char* LAYER_NAME = "XR_APILAYER_KHRONOS_runtime_conformance"; + XRAPI_ATTR XrResult XRAPI_CALL ConformanceLayer_RegisterInstance(const XrInstanceCreateInfo* createInfo, const XrApiLayerCreateInfo* apiLayerInfo, XrInstance* instance) { @@ -69,7 +73,7 @@ namespace // Function used to negotiate an interface betewen the loader and an API layer. Each library exposing one or // more API layers needs to expose at least this function. extern "C" LAYER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrNegotiateLoaderApiLayerInterface(const XrNegotiateLoaderInfo* loaderInfo, - const char* /*apiLayerName*/, + const char* apiLayerName, XrNegotiateApiLayerRequest* apiLayerRequest) { if (loaderInfo == nullptr || loaderInfo->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO || @@ -92,6 +96,12 @@ extern "C" LAYER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrNegotiateLoaderApiLayer return XR_ERROR_INITIALIZATION_FAILED; } + if (strcmp(apiLayerName, LAYER_NAME) != 0) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loader layer name does not match expected name"); + return XR_ERROR_INITIALIZATION_FAILED; + } + if (apiLayerRequest == nullptr || apiLayerRequest->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST || apiLayerRequest->structVersion != XR_API_LAYER_INFO_STRUCT_VERSION || apiLayerRequest->structSize != sizeof(XrNegotiateApiLayerRequest)) { diff --git a/src/conformance/conformance_test/CMakeLists.txt b/src/conformance/conformance_test/CMakeLists.txt index c8ca23d2..593672dc 100644 --- a/src/conformance/conformance_test/CMakeLists.txt +++ b/src/conformance/conformance_test/CMakeLists.txt @@ -41,6 +41,11 @@ file( ) file(GLOB VULKAN_SHADERS "vulkan_shaders/*.glsl") +configure_file( + conformance_test_layer.json + ${CMAKE_CURRENT_BINARY_DIR}/XrApiLayer_conformance_test_layer.json @ONLY +) + # Check to see if git-lfs is working right file(STRINGS gltf_examples/VertexColorTest.glb LFS_CHECK_STRINGS) if(LFS_CHECK_STRINGS MATCHES "https://git-lfs[.]github[.]com/spec/v1") @@ -153,6 +158,38 @@ if(ANDROID) ) endif() +# Dynamic Library: +# - Make build depend on the module definition/version script/export map +# - Add the linker flag (except windows) +if(WIN32) + target_sources( + conformance_test + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/conformance_test.def" + ) +elseif(APPLE) + set_target_properties( + conformance_test + PROPERTIES + LINK_FLAGS + "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/conformance_test.expsym" + ) + target_sources( + conformance_test + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/conformance_test.expsym" + ) +else() + set_target_properties( + conformance_test + PROPERTIES + LINK_FLAGS + "-Wl,--version-script=\"${CMAKE_CURRENT_SOURCE_DIR}/conformance_test.map\"" + ) + target_sources( + conformance_test + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/conformance_test.map" + ) +endif() + if(BUILD_CONFORMANCE_CLI) # Copy conformance_test assets files to the conformance_cli binary folder foreach(ASSET ${ASSETS}) @@ -174,6 +211,16 @@ if(BUILD_CONFORMANCE_CLI) ${CMAKE_COMMAND} -E copy $ $ ) + + # Copy test layer files to conformance_cli binary folder + add_custom_command( + TARGET conformance_test + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_BINARY_DIR}/XrApiLayer_conformance_test_layer.json + $ + ) endif() set_target_properties( diff --git a/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json b/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json new file mode 100644 index 00000000..c5d59ea2 --- /dev/null +++ b/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json @@ -0,0 +1,14 @@ +{ + "file_format_version": "1.0.0", + "api_layer": { + "name": "XR_APILAYER_KHRONOS_conformance_test_layer", + "library_path": "libconformance_test.so", + "api_version": "1.1", + "implementation_version": "1", + "description": "API Layer to validate OpenXR runtime conformance", + "disable_environment": "KHRONOS_conformance_test_layer_disabled", + "functions": { + "xrNegotiateLoaderApiLayerInterface": "testLayer_xrNegotiateLoaderApiLayerInterface" + } + } +} diff --git a/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json.license b/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/android_assets/openxr/1/api_layers/explicit.d/XR_APILAYER_KHRONOS_conformance_test_layer.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/conformance_test.cpp b/src/conformance/conformance_test/conformance_test.cpp index a3eec100..0bead759 100644 --- a/src/conformance/conformance_test/conformance_test.cpp +++ b/src/conformance/conformance_test/conformance_test.cpp @@ -32,7 +32,8 @@ #include #include #include -#include // for customizing arg parsing +#include // for customizing arg parsing +#include // for getAllTestCasesSorted #include #include diff --git a/src/conformance/conformance_test/conformance_test.def b/src/conformance/conformance_test/conformance_test.def new file mode 100644 index 00000000..aeec7e2a --- /dev/null +++ b/src/conformance/conformance_test/conformance_test.def @@ -0,0 +1,9 @@ +; Copyright (c) 2019-2024, The Khronos Group Inc. +; +; SPDX-License-Identifier: Apache-2.0 + +EXPORTS + testLayer_xrNegotiateLoaderApiLayerInterface + xrcCleanup + xrcEnumerateTestCases + xrcRunConformanceTests diff --git a/src/conformance/conformance_test/conformance_test.expsym b/src/conformance/conformance_test/conformance_test.expsym new file mode 100644 index 00000000..41c5eaaa --- /dev/null +++ b/src/conformance/conformance_test/conformance_test.expsym @@ -0,0 +1,8 @@ +# Copyright (c) 2019-2024, The Khronos Group Inc. +# +# SPDX-License-Identifier: Apache-2.0 + +_testLayer_xrNegotiateLoaderApiLayerInterface +_xrcCleanup +_xrcEnumerateTestCases +_xrcRunConformanceTests diff --git a/src/conformance/conformance_test/conformance_test.map b/src/conformance/conformance_test/conformance_test.map new file mode 100644 index 00000000..301ae00e --- /dev/null +++ b/src/conformance/conformance_test/conformance_test.map @@ -0,0 +1,15 @@ +/* +Copyright (c) 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 +*/ + +{ + global: + testLayer_xrNegotiateLoaderApiLayerInterface; + xrcCleanup; + xrcEnumerateTestCases; + xrcRunConformanceTests; + local: + *; +}; diff --git a/src/conformance/conformance_test/conformance_test_layer.json b/src/conformance/conformance_test/conformance_test_layer.json new file mode 100644 index 00000000..f1b391bc --- /dev/null +++ b/src/conformance/conformance_test/conformance_test_layer.json @@ -0,0 +1,14 @@ +{ + "file_format_version": "1.0.0", + "api_layer": { + "name": "XR_APILAYER_KHRONOS_conformance_test_layer", + "library_path": "./@CMAKE_SHARED_MODULE_PREFIX@conformance_test@CMAKE_SHARED_MODULE_SUFFIX@", + "api_version": "1.0", + "implementation_version": "1", + "description": "API Layer to validate OpenXR runtime conformance", + "disable_environment": "KHRONOS_conformance_test_layer_disabled", + "functions": { + "xrNegotiateLoaderApiLayerInterface": "testLayer_xrNegotiateLoaderApiLayerInterface" + } + } +} diff --git a/src/conformance/conformance_test/conformance_test_layer.json.license b/src/conformance/conformance_test/conformance_test_layer.json.license new file mode 100644 index 00000000..cca73d04 --- /dev/null +++ b/src/conformance/conformance_test/conformance_test_layer.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2019-2024, The Khronos Group Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/src/conformance/conformance_test/test_SessionState.cpp b/src/conformance/conformance_test/test_SessionState.cpp index da379cf4..0cd24319 100644 --- a/src/conformance/conformance_test/test_SessionState.cpp +++ b/src/conformance/conformance_test/test_SessionState.cpp @@ -105,48 +105,195 @@ namespace Conformance REQUIRE(session != XR_NULL_HANDLE_CPP); XrEventDataSessionStateChanged evt{}; - REQUIRE(waitForNextSessionState(instance, &evt) == true); - REQUIRE_MSG(evt.state == XR_SESSION_STATE_IDLE, "Unexpected session state " << evt.state); - - REQUIRE(waitForNextSessionState(instance, &evt) == true); - REQUIRE_MSG(evt.state == XR_SESSION_STATE_READY, "Unexpected session state " << evt.state); - XrSessionBeginInfo beginInfo{XR_TYPE_SESSION_BEGIN_INFO}; beginInfo.primaryViewConfigurationType = GetGlobalData().GetOptions().viewConfigurationValue; - REQUIRE(XR_SUCCESS == xrBeginSession(session, &beginInfo)); - submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_SYNCHRONIZED); - submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_VISIBLE); - submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_FOCUSED); + SECTION("Normal operation") + { + { + INFO("Advancing to IDLE"); + REQUIRE(waitForNextSessionState(instance, &evt) == true); + REQUIRE(evt.state == XR_SESSION_STATE_IDLE); + } + + { + INFO("Advancing to READY"); + REQUIRE(waitForNextSessionState(instance, &evt) == true); + REQUIRE(evt.state == XR_SESSION_STATE_READY); + } - // Runtime should only allow ending a session in the STOPPING state. - REQUIRE(XR_ERROR_SESSION_NOT_STOPPING == xrEndSession(session)); + REQUIRE(XR_SUCCESS == xrBeginSession(session, &beginInfo)); - REQUIRE(XR_SUCCESS == xrRequestExitSession(session)); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_SYNCHRONIZED); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_VISIBLE); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_FOCUSED); - submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_VISIBLE); - submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_SYNCHRONIZED); - submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_STOPPING); + // Runtime should only allow ending a session in the STOPPING state. + REQUIRE(XR_ERROR_SESSION_NOT_STOPPING == xrEndSession(session)); - // Runtime should not transition from STOPPING to IDLE until the session has been ended. - // This will wait 1 second before assuming no such incorrect event will come. - REQUIRE_MSG(waitForNextSessionState(instance, &evt) == false, "Premature progression from STOPPING to IDLE state"); + REQUIRE(XR_SUCCESS == xrRequestExitSession(session)); - REQUIRE(XR_SUCCESS == xrEndSession(session)); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_VISIBLE); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_SYNCHRONIZED); + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_STOPPING); - submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_IDLE); + // Runtime should not transition from STOPPING to IDLE until the session has been ended. + // This will wait 1 second before assuming no such incorrect event will come. + REQUIRE_MSG(waitForNextSessionState(instance, &evt) == false, "Premature progression from STOPPING to IDLE state"); - // https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#session-lifecycle - // If the runtime determines that its use of this XR session has - // concluded, it will transition the session state from - // XR_SESSION_STATE_IDLE to XR_SESSION_STATE_EXITING. - submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_EXITING); + REQUIRE(XR_SUCCESS == xrEndSession(session)); - // When the application receives the XR_SESSION_STATE_EXITING - // event, it releases the resources related to the session and - // calls xrDestroySession. - } + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_IDLE); + + // https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#session-lifecycle + // If the runtime determines that its use of this XR session has + // concluded, it will transition the session state from + // XR_SESSION_STATE_IDLE to XR_SESSION_STATE_EXITING. + + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_EXITING); + } + + SECTION("Try calls out of turn") + { + XrFrameState frameState{XR_TYPE_FRAME_STATE}; + + SECTION("xrWaitFrame before polling session state IDLE") + { + INFO("xrWaitFrame only valid when session running"); + REQUIRE(XR_ERROR_SESSION_NOT_RUNNING == xrWaitFrame(session, nullptr, &frameState)); + } + + // We have not polled state IDLE yet, but the session may have already moved to that state. + // So, cannot assert anything about behaviour under various XrSessionState here: + // session might be in mystery unknown state before IDLE, IDLE, or READY. + + INFO("Polling events until receiving IDLE"); + REQUIRE(waitForNextSessionState(instance, &evt) == true); + REQUIRE(evt.state == XR_SESSION_STATE_IDLE); + + SECTION("xrWaitFrame after polling session state IDLE") + { + INFO("xrWaitFrame only valid when session running"); + CHECK(XR_ERROR_SESSION_NOT_RUNNING == xrWaitFrame(session, nullptr, &frameState)); + } + + // We have polled state IDLE but not READY yet. However,the session may have already moved to that state. + // So, cannot assert anything about behaviour under various XrSessionState here, since we may be in either + // IDLE or READY. + + INFO("Polling events until receiving READY"); + REQUIRE(waitForNextSessionState(instance, &evt) == true); + REQUIRE(evt.state == XR_SESSION_STATE_READY); + + SECTION("xrWaitFrame in READY") + { + INFO("xrWaitFrame only valid when session running"); + // If test hangs here, this is an error in the runtime! + // It should not actually wait, but error out immediately. + REQUIRE(xrWaitFrame(session, nullptr, &frameState) == XR_ERROR_SESSION_NOT_RUNNING); + } + + INFO("xrBeginSession"); + REQUIRE(XR_SUCCESS == xrBeginSession(session, &beginInfo)); + + if (GetGlobalData().IsUsingGraphicsPlugin()) { + // Runtime should not transition from READY to SYNCHRONIZED until one or more frames have been submitted. + // The exception is if the runtime is transitioning to STOPPING, which should not happen + // during conformance testing. This will wait 1 second before assuming no such incorrect event will come. + REQUIRE_MSG(waitForNextSessionState(instance, &evt) == false, "Premature progression from READY to SYNCHRONIZED state"); + } + SECTION("Second call to xrBeginSession in READY") + { + REQUIRE(XR_ERROR_SESSION_RUNNING == xrBeginSession(session, &beginInfo)); + } + + // READY -> SYNCHRONIZED + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_SYNCHRONIZED); + SECTION("xrBeginSession in SYNCHRONIZED") + { + REQUIRE(XR_ERROR_SESSION_RUNNING == xrBeginSession(session, &beginInfo)); + } + + // SYNCHRONIZED -> VISIBLE + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_VISIBLE); + SECTION("xrBeginSession in VISIBLE") + { + REQUIRE(XR_ERROR_SESSION_RUNNING == xrBeginSession(session, &beginInfo)); + } + + // VISIBLE -> FOCUSED + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_FOCUSED); + SECTION("xrBeginSession in FOCUSED") + { + REQUIRE(XR_ERROR_SESSION_RUNNING == xrBeginSession(session, &beginInfo)); + } + + // Runtime should only allow ending a session in the STOPPING state. + SECTION("xrEndSession in FOCUSED") + { + REQUIRE(XR_ERROR_SESSION_NOT_STOPPING == xrEndSession(session)); + } + + INFO("xrRequestExitSession"); + REQUIRE(XR_SUCCESS == xrRequestExitSession(session)); + SECTION("xrBeginSession in FOCUSED due to xrRequestExitSession") + { + REQUIRE(XR_ERROR_SESSION_RUNNING == xrBeginSession(session, &beginInfo)); + } + + // FOCUSED -> VISIBLE + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_VISIBLE); + SECTION("xrBeginSession in VISIBLE due to xrRequestExitSession") + { + REQUIRE(XR_ERROR_SESSION_RUNNING == xrBeginSession(session, &beginInfo)); + } + + // VISIBLE -> SYNCHRONIZED + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_SYNCHRONIZED); + SECTION("xrBeginSession in SYNCHRONIZED due to xrRequestExitSession") + { + REQUIRE(XR_ERROR_SESSION_RUNNING == xrBeginSession(session, &beginInfo)); + } + + // SYNCHRONIZED -> STOPPING + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_STOPPING); + SECTION("xrBeginSession in STOPPING due to xrRequestExitSession") + { + REQUIRE(XR_ERROR_SESSION_RUNNING == xrBeginSession(session, &beginInfo)); + } + + { + INFO( + "Runtime should not transition from STOPPING to IDLE until the session has been ended. Wait 1s for incorrect event."); + // Runtime should not transition from STOPPING to IDLE until the session has been ended. + // This will wait 1 second before assuming no such incorrect event will come. + REQUIRE(waitForNextSessionState(instance, &evt) == false); + } + + INFO("xrEndSession"); + REQUIRE(XR_SUCCESS == xrEndSession(session)); + + SECTION("xrWaitFrame after xrEndSession but before IDLE") + { + CHECK(XR_ERROR_SESSION_NOT_RUNNING == xrWaitFrame(session, nullptr, &frameState)); + } + + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_IDLE); + + SECTION("xrWaitFrame in IDLE while shutting down") + { + CHECK(XR_ERROR_SESSION_NOT_RUNNING == xrWaitFrame(session, nullptr, &frameState)); + } + + submitFramesUntilSessionState(instance, session, XR_SESSION_STATE_EXITING); + + SECTION("xrWaitFrame in EXITING while shutting down") + { + CHECK(XR_ERROR_SESSION_NOT_RUNNING == xrWaitFrame(session, nullptr, &frameState)); + } + } + } SECTION("xrRequestExitSession Session Not Running") { SECTION("Session not running before starting") @@ -198,9 +345,11 @@ namespace Conformance REQUIRE(XR_SUCCESS == xrBeginSession(session, &beginInfo)); // This will wait 1 second before assuming no such incorrect event will come. + INFO("When using graphics, must not move from READY to SYNCHRONIZED without submittting frames."); XrEventDataSessionStateChanged evt; - REQUIRE_MSG(waitForNextSessionState(instance, &evt) == false, "Premature progression from READY to SYNCHRONIZED state"); + REQUIRE(waitForNextSessionState(instance, &evt) == false); } } } + } // namespace Conformance diff --git a/src/conformance/conformance_test/test_apiLayer.cpp b/src/conformance/conformance_test/test_apiLayer.cpp new file mode 100644 index 00000000..8f5b11e0 --- /dev/null +++ b/src/conformance/conformance_test/test_apiLayer.cpp @@ -0,0 +1,264 @@ +// Copyright (c) 2019-2024, The Khronos Group Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "conformance_framework.h" +#include "conformance_utils.h" +#include "utilities/types_and_constants.h" +#include "utilities/utils.h" + +#include +#include +#include + +#include +#include +#include +#include + +#if defined(__GNUC__) && __GNUC__ >= 4 +#define LAYER_EXPORT __attribute__((visibility("default"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define LAYER_EXPORT __attribute__((visibility("default"))) +#else +#define LAYER_EXPORT +#endif + +namespace +{ + std::mutex g_instance_dispatch_mutex; + std::unordered_map g_instance_dispatch_map; + + static const char* LAYER_NAME = "XR_APILAYER_KHRONOS_conformance_test_layer"; + + XRAPI_ATTR XrResult XRAPI_CALL TestLayer_CreateApiLayerInstance(const XrInstanceCreateInfo* createInfo, + const XrApiLayerCreateInfo* apiLayerInfo, XrInstance* instance) + { + // + // 1. Validate input data from Loader / previous layer + // + { + if (apiLayerInfo == nullptr || apiLayerInfo->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO || + apiLayerInfo->structVersion != XR_API_LAYER_CREATE_INFO_STRUCT_VERSION || + apiLayerInfo->structSize != sizeof(XrApiLayerCreateInfo)) { + return XR_ERROR_INITIALIZATION_FAILED; + } + + // apiLayerInfo->loaderInstance is deprecated and must be ignored + // apiLayerInfo->settings_file_location is currently unused. + + if (apiLayerInfo->nextInfo == nullptr || apiLayerInfo->nextInfo->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO || + apiLayerInfo->nextInfo->structVersion != XR_API_LAYER_NEXT_INFO_STRUCT_VERSION || + apiLayerInfo->nextInfo->structSize != sizeof(XrApiLayerNextInfo)) { + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (strcmp(apiLayerInfo->nextInfo->layerName, LAYER_NAME) != 0) { + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (apiLayerInfo->nextInfo->nextGetInstanceProcAddr == nullptr || + apiLayerInfo->nextInfo->nextCreateApiLayerInstance == nullptr) { + return XR_ERROR_INITIALIZATION_FAILED; + } + } + + // + // 2.A. Checks associated with https://gitlab.khronos.org/openxr/openxr/-/issues/2333 + // API Layers may need to know information about the other layers or the runtime, + // and to do so has to query information from the next chain. + // + { + PFN_xrEnumerateInstanceExtensionProperties xrEnumerateInstanceExtensionProperties{nullptr}; + XrResult res = apiLayerInfo->nextInfo->nextGetInstanceProcAddr( + XR_NULL_HANDLE, "xrEnumerateInstanceExtensionProperties", + reinterpret_cast(&xrEnumerateInstanceExtensionProperties)); + if (res != XR_SUCCESS) { + return res; + } + + uint32_t extensionsCount = 0; + res = xrEnumerateInstanceExtensionProperties(nullptr, 0, &extensionsCount, nullptr); + if (res != XR_SUCCESS) { + return res; + } + + std::vector extensions(extensionsCount, {XR_TYPE_EXTENSION_PROPERTIES}); + res = xrEnumerateInstanceExtensionProperties(nullptr, extensionsCount, &extensionsCount, extensions.data()); + if (res != XR_SUCCESS) { + return res; + } + + // The API layer would now cache extensions vector or use it for validation. + } + + // + // 2.B. Checks for feature availability in instances. + // + { + XrInstance temporaryInstance{XR_NULL_HANDLE}; + + XrApiLayerCreateInfo temporaryNextApiLayerInfo = *apiLayerInfo; + temporaryNextApiLayerInfo.nextInfo = apiLayerInfo->nextInfo->next; + + XrResult res = apiLayerInfo->nextInfo->nextCreateApiLayerInstance(createInfo, &temporaryNextApiLayerInfo, &temporaryInstance); + + if (res != XR_SUCCESS) { + return res; + } + + // The API layer would now query the instance using other functions to validate feature availability. + + PFN_xrDestroyInstance xrDestroyInstance{nullptr}; + res = apiLayerInfo->nextInfo->nextGetInstanceProcAddr(temporaryInstance, "xrDestroyInstance", + reinterpret_cast(&xrDestroyInstance)); + res = xrDestroyInstance(temporaryInstance); + if (res != XR_SUCCESS) { + return res; + } + } + + // + // 3. Call down to the next layer's xrCreateApiLayerInstance and record next gipa. + // + { + + // Clone the XrApiLayerCreateInfo, but move to the next XrApiLayerNextInfo in the chain. nextInfo will be null + // if the loader's terminator function is going to be called (between the layer and the runtime) but this is OK + // because the loader's terminator function won't use it. + XrApiLayerCreateInfo newApiLayerInfo = *apiLayerInfo; + newApiLayerInfo.nextInfo = apiLayerInfo->nextInfo->next; + + XrResult nextLayerCreateRes = apiLayerInfo->nextInfo->nextCreateApiLayerInstance(createInfo, &newApiLayerInfo, instance); + if (XR_FAILED(nextLayerCreateRes)) { + // Some layer higher the chain failed - we return the error. + return nextLayerCreateRes; + } + + { + // Record the get instance proc addr for the next layer in the chain. + std::unique_lock lock(g_instance_dispatch_mutex); + g_instance_dispatch_map[*instance] = apiLayerInfo->nextInfo->nextGetInstanceProcAddr; + } + + return XR_SUCCESS; + } + } + + XRAPI_ATTR XrResult XRAPI_CALL TestLayer_GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function) + { + if (instance == XR_NULL_HANDLE) { + // assert(false); + return XR_SUCCESS; + } + + PFN_xrGetInstanceProcAddr nextGetProcAddr = nullptr; + { + std::unique_lock lock(g_instance_dispatch_mutex); + auto it = g_instance_dispatch_map.find(instance); + if (it == g_instance_dispatch_map.end()) { + return XR_ERROR_HANDLE_INVALID; + } + nextGetProcAddr = it->second; + } + return nextGetProcAddr(instance, name, function); + } +} // namespace + +extern "C" LAYER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL testLayer_xrNegotiateLoaderApiLayerInterface( + const XrNegotiateLoaderInfo* loaderInfo, const char* apiLayerName, XrNegotiateApiLayerRequest* apiLayerRequest) +{ + if (loaderInfo == nullptr || loaderInfo->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO || + loaderInfo->structVersion != XR_LOADER_INFO_STRUCT_VERSION || loaderInfo->structSize != sizeof(XrNegotiateLoaderInfo)) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loaderInfo struct is not valid"); + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (loaderInfo->minInterfaceVersion > XR_CURRENT_LOADER_API_LAYER_VERSION || + loaderInfo->maxInterfaceVersion < XR_CURRENT_LOADER_API_LAYER_VERSION) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loader interface version is not in the range [minInterfaceVersion, maxInterfaceVersion]"); + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (loaderInfo->minApiVersion > XR_CURRENT_API_VERSION || loaderInfo->maxApiVersion < XR_CURRENT_API_VERSION) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loader api version is not in the range [minApiVersion, maxApiVersion]"); + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (strcmp(apiLayerName, LAYER_NAME) != 0) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("loader layer name does not match expected name"); + return XR_ERROR_INITIALIZATION_FAILED; + } + + if (apiLayerRequest == nullptr || apiLayerRequest->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST || + apiLayerRequest->structVersion != XR_API_LAYER_INFO_STRUCT_VERSION || + apiLayerRequest->structSize != sizeof(XrNegotiateApiLayerRequest)) { + // TODO: Log reason somehow. + // LogPlatformUtilsError("apiLayerRequest is not valid"); + return XR_ERROR_INITIALIZATION_FAILED; + } + + apiLayerRequest->layerInterfaceVersion = XR_CURRENT_LOADER_API_LAYER_VERSION; + apiLayerRequest->layerApiVersion = XR_CURRENT_API_VERSION; + apiLayerRequest->getInstanceProcAddr = TestLayer_GetInstanceProcAddr; + apiLayerRequest->createApiLayerInstance = TestLayer_CreateApiLayerInstance; + + return XR_SUCCESS; +} + +namespace Conformance +{ + TEST_CASE("validApiLayer", "") + { + GlobalData& globalData = GetGlobalData(); + + // Layer for run-time conformance (and anything else global) + StringVec enabledApiLayers = globalData.enabledAPILayerNames; + // plus our test layer + enabledApiLayers.push_back(LAYER_NAME); + + // Enable only the required platform extensions by default + auto enabledExtensions = StringVec(globalData.requiredPlatformInstanceExtensions); + + XrInstance instance = XR_NULL_HANDLE_CPP; + CleanupInstanceOnScopeExit cleanup(instance); + + XrInstanceCreateInfo createInfo{XR_TYPE_INSTANCE_CREATE_INFO}; + + strcpy(createInfo.applicationInfo.applicationName, "conformance test"); + createInfo.applicationInfo.applicationVersion = 1; + // Leave engineName and engineVersion empty, which is valid usage. + createInfo.applicationInfo.apiVersion = globalData.options.desiredApiVersionValue; + + if (globalData.requiredPlatformInstanceCreateStruct) { + createInfo.next = globalData.requiredPlatformInstanceCreateStruct; + } + + createInfo.enabledApiLayerCount = (uint32_t)enabledApiLayers.size(); + createInfo.enabledApiLayerNames = enabledApiLayers.data(); + createInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size(); + createInfo.enabledExtensionNames = enabledExtensions.data(); + + SECTION("XR_SUCCESS, only platform-required extensions enabled") + { + REQUIRE(XR_SUCCESS == xrCreateInstance(&createInfo, &instance)); + } + } + +} // namespace Conformance diff --git a/src/conformance/conformance_test/test_xrCreateInstance.cpp b/src/conformance/conformance_test/test_xrCreateInstance.cpp index 66438f23..f927ec56 100644 --- a/src/conformance/conformance_test/test_xrCreateInstance.cpp +++ b/src/conformance/conformance_test/test_xrCreateInstance.cpp @@ -46,8 +46,9 @@ namespace Conformance // Leave engineName and engineVersion empty, which is valid usage. createInfo.applicationInfo.apiVersion = globalData.options.desiredApiVersionValue; - if (globalData.requiredPlatformInstanceCreateStruct) + if (globalData.requiredPlatformInstanceCreateStruct) { createInfo.next = globalData.requiredPlatformInstanceCreateStruct; + } // Layers enabled at least for run-time conformance StringVec enabledApiLayers = globalData.enabledAPILayerNames; diff --git a/src/conformance/conformance_test/test_xrLocateViews.cpp b/src/conformance/conformance_test/test_xrLocateViews.cpp index 8466527c..f368610b 100644 --- a/src/conformance/conformance_test/test_xrLocateViews.cpp +++ b/src/conformance/conformance_test/test_xrLocateViews.cpp @@ -171,6 +171,13 @@ namespace Conformance else { // Not a supported type, so call should fail, regardless of the array size. INFO("Calling xrLocateViews with the noted viewType, which is claimed to be not supported"); + + // Start the session with the default view config, so we get a reasonable display time. + viewConfigDependentSetup(globalData.GetOptions().viewConfigurationValue); + + // but use our not-supported view config type for xrLocateViews + locateInfo.viewConfigurationType = viewType; + uint32_t viewCount = static_cast(session.viewConfigurationViewVector.size()); uint32_t viewCountOut = 0; std::vector views(viewCount, {XR_TYPE_VIEW}); diff --git a/src/conformance/framework/catch_reporter_cts.cpp b/src/conformance/framework/catch_reporter_cts.cpp index 3a443cf7..7457c714 100644 --- a/src/conformance/framework/catch_reporter_cts.cpp +++ b/src/conformance/framework/catch_reporter_cts.cpp @@ -42,6 +42,8 @@ namespace Catch gmtime_s(&timeInfo, &rawtime); #elif defined(CATCH_PLATFORM_PLAYSTATION) gmtime_s(&rawtime, &timeInfo); +#elif defined(__IAR_SYSTEMS_ICC__) + timeInfo = *std::gmtime(&rawtime); #else gmtime_r(&rawtime, &timeInfo); #endif @@ -79,7 +81,7 @@ namespace Catch static void normalizeNamespaceMarkers(std::string& str) { std::size_t pos = str.find("::"); - while (pos != str.npos) { + while (pos != std::string::npos) { str.replace(pos, 2, "."); pos += 1; pos = str.find("::", pos); @@ -91,7 +93,7 @@ namespace Catch CTSReporter::CTSReporter(ReporterConfig&& _config) : CumulativeReporterBase(CATCH_MOVE(_config)), xml(m_stream) { m_preferences.shouldRedirectStdOut = true; - m_preferences.shouldReportAllAssertions = true; + m_preferences.shouldReportAllAssertions = false; m_shouldStoreSuccesfulAssertions = false; } @@ -214,7 +216,7 @@ namespace Catch if (!rootName.empty()) name = rootName + '/' + name; - if (sectionNode.hasAnyAssertions() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) { + if (sectionNode.stats.assertions.total() > 0 || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) { XmlWriter::ScopedElement e = xml.scopedElement("testcase"); if (className.empty()) { xml.writeAttribute("classname"_sr, name); @@ -317,7 +319,7 @@ namespace Catch rss << TextFlow::Column(result.getExpandedExpression()).indent(2) << '\n'; } } - if (!result.getMessage().empty()) + if (result.hasMessage()) rss << result.getMessage() << '\n'; for (auto const& msg : stats.infoMessages) if (msg.type == ResultWas::Info) diff --git a/src/conformance/framework/two_call.h b/src/conformance/framework/two_call.h index ada3089f..16e4c95c 100644 --- a/src/conformance/framework/two_call.h +++ b/src/conformance/framework/two_call.h @@ -83,7 +83,7 @@ namespace Conformance catchAssertionHandler.handleExpr(Catch::ExprLhs(XR_SUCCESS) == result); } INTERNAL_CATCH_CATCH(catchAssertionHandler) - INTERNAL_CATCH_REACT(catchAssertionHandler) + catchAssertionHandler.complete(); } if (Catch::getResultCapture().lastAssertionPassed() && count > 0) { @@ -110,7 +110,7 @@ namespace Conformance } } INTERNAL_CATCH_CATCH(catchAssertionHandler) - INTERNAL_CATCH_REACT(catchAssertionHandler) + catchAssertionHandler.complete(); } return ret; } diff --git a/src/external/catch2/.clang-tidy b/src/external/catch2/.clang-tidy new file mode 100644 index 00000000..539010d9 --- /dev/null +++ b/src/external/catch2/.clang-tidy @@ -0,0 +1,81 @@ +--- +# Note: Alas, `Checks` is a string, not an array. +# Comments in the block string are not parsed and are passed in the value. +# They must thus be delimited by ',' from either side - then they are +# harmless. It's terrible, but it works. +Checks: >- + clang-diagnostic-*, + clang-analyzer-*, + -clang-analyzer-optin.core.EnumCastOutOfRange, + + bugprone-*, + -bugprone-unchecked-optional-access, + ,# This is ridiculous, as it triggers on constants, + -bugprone-implicit-widening-of-multiplication-result, + -bugprone-easily-swappable-parameters, + ,# Is not really useful, has false positives, triggers for no-noexcept move constructors ..., + -bugprone-exception-escape, + -bugprone-narrowing-conversions, + -bugprone-chained-comparison,# RIP decomposers, + + modernize-*, + -modernize-avoid-c-arrays, + -modernize-use-auto, + -modernize-use-emplace, + -modernize-use-nullptr,# it went crazy with three-way comparison operators, + -modernize-use-trailing-return-type, + -modernize-return-braced-init-list, + -modernize-concat-nested-namespaces, + -modernize-use-nodiscard, + -modernize-use-default-member-init, + -modernize-type-traits,# we need to support C++14, + -modernize-deprecated-headers, + ,# There's a lot of these and most of them are probably not useful, + -modernize-pass-by-value, + + performance-*, + -performance-enum-size, + + portability-*, + + readability-*, + -readability-braces-around-statements, + -readability-container-size-empty, + -readability-convert-member-functions-to-static, + -readability-else-after-return, + -readability-function-cognitive-complexity, + -readability-function-size, + -readability-identifier-length, + -readability-implicit-bool-conversion, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-named-parameter, + -readability-qualified-auto, + -readability-redundant-access-specifiers, + -readability-simplify-boolean-expr, + -readability-static-definition-in-anonymous-namespace, + -readability-uppercase-literal-suffix, + -readability-use-anyofallof, + -readability-avoid-return-with-void-value, + + ,# time hogs, + -bugprone-throw-keyword-missing, + -modernize-replace-auto-ptr, + -readability-identifier-naming, + + ,# We cannot use this until clang-tidy supports custom unique_ptr, + -bugprone-use-after-move, + ,# Doesn't recognize unevaluated context in CATCH_MOVE and CATCH_FORWARD, + -bugprone-macro-repeated-side-effects, +WarningsAsErrors: >- + clang-analyzer-core.*, + clang-analyzer-cplusplus.*, + clang-analyzer-security.*, + clang-analyzer-unix.*, + performance-move-const-arg, + performance-unnecessary-value-param, + readability-duplicate-include, +HeaderFilterRegex: '.*\.(c|cxx|cpp)$' +FormatStyle: none +CheckOptions: {} +... diff --git a/src/external/catch2/.gitignore b/src/external/catch2/.gitignore index 27f6bc0b..dbf9f40a 100644 --- a/src/external/catch2/.gitignore +++ b/src/external/catch2/.gitignore @@ -25,7 +25,9 @@ Build cmake-build-* benchmark-dir .conan/test_package/build +**/CMakeUserPresets.json bazel-* +MODULE.bazel.lock build-fuzzers debug-build .vscode @@ -35,3 +37,4 @@ msvc-sln* docs/doxygen *.cache compile_commands.json +**/*.unapproved.txt diff --git a/src/external/catch2/BUILD.bazel b/src/external/catch2/BUILD.bazel index 3125e7c5..c51bf57e 100644 --- a/src/external/catch2/BUILD.bazel +++ b/src/external/catch2/BUILD.bazel @@ -43,12 +43,15 @@ expand_template( "#cmakedefine CATCH_CONFIG_NO_GLOBAL_NEXTAFTER": "", "#cmakedefine CATCH_CONFIG_NO_POSIX_SIGNALS": "", "#cmakedefine CATCH_CONFIG_NO_USE_ASYNC": "", + "#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT": "", "#cmakedefine CATCH_CONFIG_NO_WCHAR": "", "#cmakedefine CATCH_CONFIG_NO_WINDOWS_SEH": "", "#cmakedefine CATCH_CONFIG_NOSTDOUT": "", "#cmakedefine CATCH_CONFIG_POSIX_SIGNALS": "", "#cmakedefine CATCH_CONFIG_PREFIX_ALL": "", + "#cmakedefine CATCH_CONFIG_PREFIX_MESSAGES": "", "#cmakedefine CATCH_CONFIG_SHARED_LIBRARY": "", + "#cmakedefine CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT": "", "#cmakedefine CATCH_CONFIG_USE_ASYNC": "", "#cmakedefine CATCH_CONFIG_WCHAR": "", "#cmakedefine CATCH_CONFIG_WINDOWS_CRTDBG": "", diff --git a/src/external/catch2/CMake/CatchConfigOptions.cmake b/src/external/catch2/CMake/CatchConfigOptions.cmake index 733ec65e..6eae220d 100644 --- a/src/external/catch2/CMake/CatchConfigOptions.cmake +++ b/src/external/catch2/CMake/CatchConfigOptions.cmake @@ -18,10 +18,12 @@ macro(AddOverridableConfigOption OptionBaseName) option(CATCH_CONFIG_${OptionBaseName} "Read docs/configuration.md for details" OFF) option(CATCH_CONFIG_NO_${OptionBaseName} "Read docs/configuration.md for details" OFF) + mark_as_advanced(CATCH_CONFIG_${OptionBaseName} CATCH_CONFIG_NO_${OptionBaseName}) endmacro() macro(AddConfigOption OptionBaseName) option(CATCH_CONFIG_${OptionBaseName} "Read docs/configuration.md for details" OFF) + mark_as_advanced(CATCH_CONFIG_${OptionBaseName}) endmacro() set(_OverridableOptions @@ -41,6 +43,7 @@ set(_OverridableOptions "WCHAR" "WINDOWS_SEH" "GETENV" + "EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT" ) foreach(OptionName ${_OverridableOptions}) @@ -61,6 +64,7 @@ set(_OtherConfigOptions "FAST_COMPILE" "NOSTDOUT" "PREFIX_ALL" + "PREFIX_MESSAGES" "WINDOWS_CRTDBG" ) @@ -68,11 +72,17 @@ set(_OtherConfigOptions foreach(OptionName ${_OtherConfigOptions}) AddConfigOption(${OptionName}) endforeach() -set(CATCH_CONFIG_SHARED_LIBRARY ${BUILD_SHARED_LIBS}) +if(DEFINED BUILD_SHARED_LIBS) + set(CATCH_CONFIG_SHARED_LIBRARY ${BUILD_SHARED_LIBS}) +else() + set(CATCH_CONFIG_SHARED_LIBRARY "") +endif() set(CATCH_CONFIG_DEFAULT_REPORTER "console" CACHE STRING "Read docs/configuration.md for details. The name of the reporter should be without quotes.") set(CATCH_CONFIG_CONSOLE_WIDTH "80" CACHE STRING "Read docs/configuration.md for details. Must form a valid integer literal.") +mark_as_advanced(CATCH_CONFIG_SHARED_LIBRARY CATCH_CONFIG_DEFAULT_REPORTER CATCH_CONFIG_CONSOLE_WIDTH) + # There is no good way to both turn this into a CMake cache variable, # and keep reasonable default semantics inside the project. Thus we do # not define it and users have to provide it as an outside variable. diff --git a/src/external/catch2/CMake/CatchMiscFunctions.cmake b/src/external/catch2/CMake/CatchMiscFunctions.cmake index 3758d956..05bc83c0 100644 --- a/src/external/catch2/CMake/CatchMiscFunctions.cmake +++ b/src/external/catch2/CMake/CatchMiscFunctions.cmake @@ -46,7 +46,6 @@ function(add_warnings_to_targets targets) set(CHECKED_WARNING_FLAGS "-Wabsolute-value" "-Wall" - "-Wc++20-compat" "-Wcall-to-pure-virtual-from-ctor-dtor" "-Wcast-align" "-Wcatch-value" @@ -69,21 +68,24 @@ function(add_warnings_to_targets targets) "-Wmissing-noreturn" "-Wmissing-prototypes" "-Wmissing-variable-declarations" + "-Wnon-virtual-dtor" "-Wnull-dereference" "-Wold-style-cast" "-Woverloaded-virtual" "-Wparentheses" "-Wpedantic" + "-Wredundant-decls" "-Wreorder" "-Wreturn-std-move" "-Wshadow" "-Wstrict-aliasing" + "-Wsubobject-linkage" "-Wsuggest-destructor-override" "-Wsuggest-override" "-Wundef" "-Wuninitialized" "-Wunneeded-internal-declaration" - "-Wunreachable-code" + "-Wunreachable-code-aggressive" "-Wunused" "-Wunused-function" "-Wunused-parameter" diff --git a/src/external/catch2/CMakeLists.txt b/src/external/catch2/CMakeLists.txt index 6d381d8d..7bad26c3 100644 --- a/src/external/catch2/CMakeLists.txt +++ b/src/external/catch2/CMakeLists.txt @@ -11,6 +11,7 @@ endif() option(CATCH_INSTALL_DOCS "Install documentation alongside library" ON) option(CATCH_INSTALL_EXTRAS "Install extras (CMake scripts, debugger helpers) alongside library" ON) option(CATCH_DEVELOPMENT_BUILD "Build tests, enable warnings, enable Werror, etc" OFF) +option(CATCH_ENABLE_REPRODUCIBLE_BUILD "Add compiler flags for improving build reproducibility" ON) include(CMakeDependentOption) cmake_dependent_option(CATCH_BUILD_TESTING "Build the SelfTest project" ON "CATCH_DEVELOPMENT_BUILD" OFF) @@ -21,6 +22,7 @@ cmake_dependent_option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" cmake_dependent_option(CATCH_ENABLE_WERROR "Enables Werror during build" ON "CATCH_DEVELOPMENT_BUILD" OFF) cmake_dependent_option(CATCH_BUILD_SURROGATES "Enable generating and building surrogate TUs for the main headers" OFF "CATCH_DEVELOPMENT_BUILD" OFF) cmake_dependent_option(CATCH_ENABLE_CONFIGURE_TESTS "Enable CMake configuration tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF) +cmake_dependent_option(CATCH_ENABLE_CMAKE_HELPER_TESTS "Enable CMake helper tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF) # Catch2's build breaks if done in-tree. You probably should not build @@ -31,7 +33,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() project(Catch2 - VERSION 3.3.2 # CML version placeholder, don't delete + VERSION 3.7.1 # CML version placeholder, don't delete LANGUAGES CXX # HOMEPAGE_URL is not supported until CMake version 3.12, which # we do not target yet. @@ -74,8 +76,6 @@ endif() set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(SOURCES_DIR ${CATCH_DIR}/src/catch2) set(SELF_TEST_DIR ${CATCH_DIR}/tests/SelfTest) -set(BENCHMARK_DIR ${CATCH_DIR}/tests/Benchmark) -set(EXAMPLES_DIR ${CATCH_DIR}/examples) # We need to bring-in the variables defined there to this scope add_subdirectory(src) @@ -148,6 +148,8 @@ if (NOT_SUBPROJECT) "extras/ParseAndAddCatchTests.cmake" "extras/Catch.cmake" "extras/CatchAddTests.cmake" + "extras/CatchShardTests.cmake" + "extras/CatchShardTestsImpl.cmake" DESTINATION ${CATCH_CMAKE_CONFIG_DESTINATION} ) @@ -196,4 +198,4 @@ if (NOT_SUBPROJECT) include( CPack ) -endif(NOT_SUBPROJECT) +endif() diff --git a/src/external/catch2/CMakePresets.json b/src/external/catch2/CMakePresets.json index 00f3a6d3..88541285 100644 --- a/src/external/catch2/CMakePresets.json +++ b/src/external/catch2/CMakePresets.json @@ -18,7 +18,8 @@ "CATCH_BUILD_EXAMPLES": "ON", "CATCH_BUILD_EXTRA_TESTS": "ON", "CATCH_BUILD_SURROGATES": "ON", - "CATCH_ENABLE_CONFIGURE_TESTS": "ON" + "CATCH_ENABLE_CONFIGURE_TESTS": "ON", + "CATCH_ENABLE_CMAKE_HELPER_TESTS": "ON" } } ] diff --git a/src/external/catch2/Doxyfile b/src/external/catch2/Doxyfile index 07b385ec..914e5984 100644 --- a/src/external/catch2/Doxyfile +++ b/src/external/catch2/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.16 +# Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Catch2" +PROJECT_NAME = Catch2 # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -51,6 +51,7 @@ PROJECT_BRIEF = "Popular C++ unit testing framework" # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. +PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is @@ -216,6 +217,14 @@ QT_AUTOBRIEF = YES MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. @@ -251,13 +260,7 @@ TAB_SIZE = 4 # a double escape (\\{ and \\}) ALIASES = "complexity=@par Complexity:" \ - "noexcept=**Noexcept**" - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = + noexcept=**Noexcept** # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For @@ -299,19 +302,22 @@ OPTIMIZE_OUTPUT_SLICE = NO # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is -# Fortran), use: inc=Fortran f=C. +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = @@ -445,6 +451,19 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -508,6 +527,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -525,8 +551,8 @@ HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO @@ -545,11 +571,18 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) ands Mac users are advised to set this option to NO. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. # The default value is: system dependent. CASE_SENSE_NAMES = NO @@ -788,7 +821,10 @@ WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO @@ -819,13 +855,13 @@ WARN_LOGFILE = doxygen.errors # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = "src/catch2" +INPUT = src/catch2 # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 @@ -838,13 +874,61 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, -# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. - -# FILE_PATTERNS = +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -968,6 +1052,7 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. +USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -1055,6 +1140,44 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1066,13 +1189,6 @@ VERBATIM_HEADERS = YES ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored @@ -1211,9 +1327,9 @@ HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that -# are dynamically created via Javascript. If disabled, the navigation index will +# are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have Javascript, +# page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1243,10 +1359,11 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/xcode/), introduced with OSX -# 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. @@ -1288,8 +1405,8 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# (see: +# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1364,7 +1481,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1372,8 +1490,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1381,16 +1499,16 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = @@ -1402,9 +1520,9 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1481,6 +1599,17 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML @@ -1501,8 +1630,14 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1514,7 +1649,7 @@ USE_MATHJAX = YES # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. @@ -1530,7 +1665,7 @@ MATHJAX_FORMAT = HTML-CSS # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -1545,7 +1680,8 @@ MATHJAX_EXTENSIONS = TeX/AMSmath \ # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1573,7 +1709,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1592,7 +1728,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1605,8 +1742,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1770,9 +1908,11 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES, to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2204,7 +2344,7 @@ HIDE_UNDOC_RELATIONS = YES # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO -# The default value is: NO. +# The default value is: YES. HAVE_DOT = YES @@ -2283,10 +2423,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2360,7 +2522,9 @@ DIRECTORY_GRAPH = NO # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, +# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, +# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo, # png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and # png:gdiplus:gdiplus. # The default value is: png. @@ -2476,9 +2640,11 @@ DOT_MULTI_TARGETS = YES GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc and +# plantuml temporary files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES diff --git a/src/external/catch2/MODULE.bazel b/src/external/catch2/MODULE.bazel new file mode 100644 index 00000000..4d7ec860 --- /dev/null +++ b/src/external/catch2/MODULE.bazel @@ -0,0 +1,3 @@ +module(name = "catch2") + +bazel_dep(name = "bazel_skylib", version = "1.7.1") diff --git a/src/external/catch2/WORKSPACE.bazel b/src/external/catch2/WORKSPACE.bazel index 6fd2ffa5..e48080a4 100644 --- a/src/external/catch2/WORKSPACE.bazel +++ b/src/external/catch2/WORKSPACE.bazel @@ -4,12 +4,13 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "bazel_skylib", + sha256 = "bc283cdfcd526a52c3201279cda4bc298652efa898b10b4db0837dc51652756f", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.7.1/bazel-skylib-1.7.1.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.7.1/bazel-skylib-1.7.1.tar.gz", ], - sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506", ) load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") + bazel_skylib_workspace() diff --git a/src/external/catch2/appveyor.yml b/src/external/catch2/appveyor.yml index 3b6580d8..ba4556ea 100644 --- a/src/external/catch2/appveyor.yml +++ b/src/external/catch2/appveyor.yml @@ -5,10 +5,10 @@ version: "{build}-{branch}" clone_depth: 20 # We want to build everything, except for branches that are explicitly -# for messing around with travis. +# for messing around with Github Actions. branches: except: - - /dev-travis.+/ + - /devel-gha.+/ # We need a more up to date pip because Python 2.7 is EOL soon @@ -80,4 +80,4 @@ environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 coverage: 1 platform: x64 - configuration: Debug \ No newline at end of file + configuration: Debug diff --git a/src/external/catch2/conanfile.py b/src/external/catch2/conanfile.py old mode 100644 new mode 100755 index 7aa27ef5..7a3ac7c3 --- a/src/external/catch2/conanfile.py +++ b/src/external/catch2/conanfile.py @@ -1,5 +1,14 @@ #!/usr/bin/env python -from conans import ConanFile, CMake, tools +from conan import ConanFile +from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout +from conan.tools.files import copy, rmdir +from conan.tools.build import check_min_cppstd +from conan.tools.scm import Version +from conan.errors import ConanInvalidConfiguration +import os +import re + +required_conan_version = ">=1.53.0" class CatchConan(ConanFile): name = "catch2" @@ -8,53 +17,113 @@ class CatchConan(ConanFile): url = "https://github.com/catchorg/Catch2" homepage = url license = "BSL-1.0" + version = "latest" + settings = "os", "compiler", "build_type", "arch" + extension_properties = {"compatibility_cppstd": False} - exports = "LICENSE.txt" - exports_sources = ("src/*", "CMakeLists.txt", "CMake/*", "extras/*") + options = { + "shared": [True, False], + "fPIC": [True, False], + } + default_options = { + "shared": False, + "fPIC": True, + } - settings = "os", "compiler", "build_type", "arch" + @property + def _min_cppstd(self): + return "14" - generators = "cmake" + @property + def _compilers_minimum_version(self): + return { + "gcc": "7", + "Visual Studio": "15", + "msvc": "191", + "clang": "5", + "apple-clang": "10", + } - def _configure_cmake(self): - cmake = CMake(self) - cmake.definitions["BUILD_TESTING"] = "OFF" - cmake.definitions["CATCH_INSTALL_DOCS"] = "OFF" - cmake.definitions["CATCH_INSTALL_EXTRAS"] = "ON" - cmake.configure(build_folder="build") - return cmake + + def set_version(self): + pattern = re.compile(r"\w*VERSION (\d+\.\d+\.\d+) # CML version placeholder, don't delete") + with open("CMakeLists.txt") as file: + for line in file: + result = pattern.search(line) + if result: + self.version = result.group(1) + + self.output.info(f'Using version: {self.version}') + + def export(self): + copy(self, "LICENSE.txt", src=self.recipe_folder, dst=self.export_folder) + + def export_sources(self): + copy(self, "CMakeLists.txt", src=self.recipe_folder, dst=self.export_sources_folder) + copy(self, "src/*", src=self.recipe_folder, dst=self.export_sources_folder) + copy(self, "extras/*", src=self.recipe_folder, dst=self.export_sources_folder) + copy(self, "CMake/*", src=self.recipe_folder, dst=self.export_sources_folder) + + def config_options(self): + if self.settings.os == "Windows": + del self.options.fPIC + + def configure(self): + if self.options.shared: + self.options.rm_safe("fPIC") + + def layout(self): + cmake_layout(self) + + def validate(self): + if self.settings.compiler.get_safe("cppstd"): + check_min_cppstd(self, self._min_cppstd) + # INFO: Conan 1.x does not specify cppstd by default, so we need to check the compiler version instead. + minimum_version = self._compilers_minimum_version.get(str(self.settings.compiler), False) + if minimum_version and Version(self.settings.compiler.version) < minimum_version: + raise ConanInvalidConfiguration(f"{self.ref} requires C++{self._min_cppstd}, which your compiler doesn't support") + + def generate(self): + tc = CMakeToolchain(self) + tc.cache_variables["BUILD_TESTING"] = False + tc.cache_variables["CATCH_INSTALL_DOCS"] = False + tc.cache_variables["CATCH_INSTALL_EXTRAS"] = True + tc.generate() + + deps = CMakeDeps(self) + deps.generate() def build(self): - # We need this workaround until the toolchains feature - # to inject stuff like MD/MT - line_to_replace = 'list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")' - tools.replace_in_file("CMakeLists.txt", line_to_replace, - '''{} -include("{}/conanbuildinfo.cmake") -conan_basic_setup()'''.format(line_to_replace, self.install_folder.replace("\\", "/"))) - - cmake = self._configure_cmake() + cmake = CMake(self) + cmake.configure() cmake.build() def package(self): - self.copy(pattern="LICENSE.txt", dst="licenses") - cmake = self._configure_cmake() + copy(self, "LICENSE.txt", src=str(self.recipe_folder), dst=os.path.join(self.package_folder, "licenses")) + cmake = CMake(self) cmake.install() + rmdir(self, os.path.join(self.package_folder, "share")) + rmdir(self, os.path.join(self.package_folder, "lib", "cmake")) + copy(self, "*.cmake", src=os.path.join(self.export_sources_folder, "extras"), + dst=os.path.join(self.package_folder, "lib", "cmake", "Catch2")) def package_info(self): lib_suffix = "d" if self.settings.build_type == "Debug" else "" - self.cpp_info.names["cmake_find_package"] = "Catch2" - self.cpp_info.names["cmake_find_package_multi"] = "Catch2" + self.cpp_info.set_property("cmake_file_name", "Catch2") + self.cpp_info.set_property("cmake_target_name", "Catch2::Catch2WithMain") + self.cpp_info.set_property("pkg_config_name", "catch2-with-main") + # Catch2 - self.cpp_info.components["catch2base"].names["cmake_find_package"] = "Catch2" - self.cpp_info.components["catch2base"].names["cmake_find_package_multi"] = "Catch2" - self.cpp_info.components["catch2base"].names["pkg_config"] = "Catch2" + self.cpp_info.components["catch2base"].set_property("cmake_file_name", "Catch2::Catch2") + self.cpp_info.components["catch2base"].set_property("cmake_target_name", "Catch2::Catch2") + self.cpp_info.components["catch2base"].set_property("pkg_config_name", "catch2") self.cpp_info.components["catch2base"].libs = ["Catch2" + lib_suffix] self.cpp_info.components["catch2base"].builddirs.append("lib/cmake/Catch2") + # Catch2WithMain - self.cpp_info.components["catch2main"].names["cmake_find_package"] = "Catch2WithMain" - self.cpp_info.components["catch2main"].names["cmake_find_package_multi"] = "Catch2WithMain" - self.cpp_info.components["catch2main"].names["pkg_config"] = "Catch2WithMain" + self.cpp_info.components["catch2main"].set_property("cmake_file_name", "Catch2::Catch2WithMain") + self.cpp_info.components["catch2main"].set_property("cmake_target_name", "Catch2::Catch2WithMain") + self.cpp_info.components["catch2main"].set_property("pkg_config_name", "catch2-with-main") self.cpp_info.components["catch2main"].libs = ["Catch2Main" + lib_suffix] self.cpp_info.components["catch2main"].requires = ["catch2base"] diff --git a/src/external/catch2/docs/assertions.md b/src/external/catch2/docs/assertions.md index 40faa5eb..f3dcdd48 100644 --- a/src/external/catch2/docs/assertions.md +++ b/src/external/catch2/docs/assertions.md @@ -110,7 +110,7 @@ Expects that an exception is thrown that, when converted to a string, matches th e.g. ```cpp -REQUIRE_THROWS_WITH( openThePodBayDoors(), Contains( "afraid" ) && Contains( "can't do that" ) ); +REQUIRE_THROWS_WITH( openThePodBayDoors(), ContainsSubstring( "afraid" ) && ContainsSubstring( "can't do that" ) ); REQUIRE_THROWS_WITH( dismantleHal(), "My mind is going" ); ``` diff --git a/src/external/catch2/docs/benchmarks.md b/src/external/catch2/docs/benchmarks.md index 548913c7..9edbb93c 100644 --- a/src/external/catch2/docs/benchmarks.md +++ b/src/external/catch2/docs/benchmarks.md @@ -93,7 +93,7 @@ Fibonacci ------------------------------------------------------------------------------- C:\path\to\Catch2\Benchmark.tests.cpp(10) ............................................................................... -benchmark name samples iterations estimated +benchmark name samples iterations est run time mean low mean high mean std dev low std dev high std dev ------------------------------------------------------------------------------- diff --git a/src/external/catch2/docs/ci-and-misc.md b/src/external/catch2/docs/ci-and-misc.md index c07da29f..49bbd989 100644 --- a/src/external/catch2/docs/ci-and-misc.md +++ b/src/external/catch2/docs/ci-and-misc.md @@ -82,7 +82,7 @@ variable set to "1". ### CodeCoverage module (GCOV, LCOV...) -If you are using GCOV tool to get testing coverage of your code, and are not sure how to integrate it with CMake and Catch, there should be an external example over at https://github.com/fkromer/catch_cmake_coverage +If you are using GCOV tool to get testing coverage of your code, and are not sure how to integrate it with CMake and Catch, there should be an external example over at https://github.com/claremacrae/catch_cmake_coverage ### pkg-config diff --git a/src/external/catch2/docs/cmake-integration.md b/src/external/catch2/docs/cmake-integration.md index 0720a95b..ad6ca004 100644 --- a/src/external/catch2/docs/cmake-integration.md +++ b/src/external/catch2/docs/cmake-integration.md @@ -8,6 +8,7 @@ [`CATCH_CONFIG_*` customization options in CMake](#catch_config_-customization-options-in-cmake)
[Installing Catch2 from git repository](#installing-catch2-from-git-repository)
[Installing Catch2 from vcpkg](#installing-catch2-from-vcpkg)
+[Installing Catch2 from Bazel](#installing-catch2-from-bazel)
Because we use CMake to build Catch2, we also provide a couple of integration points for our users. @@ -51,7 +52,7 @@ Include(FetchContent) FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.0.1 # or a later release + GIT_TAG v3.4.0 # or a later release ) FetchContent_MakeAvailable(Catch2) @@ -126,6 +127,7 @@ catch_discover_tests(target [OUTPUT_DIR dir] [OUTPUT_PREFIX prefix] [OUTPUT_SUFFIX suffix] + [DISCOVERY_MODE ] ) ``` @@ -198,6 +200,16 @@ If specified, `suffix` is added to each output file name, like so `--out dir/suffix`. This can be used to add a file extension to the output file name e.g. ".xml". +* `DISCOVERY_MODE mode` + +If specified allows control over when test discovery is performed. +For a value of `POST_BUILD` (default) test discovery is performed at build time. +For a value of `PRE_TEST` test discovery is delayed until just prior to test +execution (useful e.g. in cross-compilation environments). +``DISCOVERY_MODE`` defaults to the value of the +``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when +calling ``catch_discover_tests``. This provides a mechanism for globally +selecting a preferred test discovery behavior. ### `ParseAndAddCatchTests.cmake` @@ -373,7 +385,7 @@ install it to the default location, like so: ``` $ git clone https://github.com/catchorg/Catch2.git $ cd Catch2 -$ cmake -Bbuild -H. -DBUILD_TESTING=OFF +$ cmake -B build -S . -DBUILD_TESTING=OFF $ sudo cmake --build build/ --target install ``` @@ -397,6 +409,24 @@ cd vcpkg The catch2 port in vcpkg is kept up to date by microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. +## Installing Catch2 from Bazel + +Catch2 is now a supported module in the Bazel Central Registry. You only need to add one line to your MODULE.bazel file; +please see https://registry.bazel.build/modules/catch2 for the latest supported version. + +You can then add `catch2_main` to each of your C++ test build rules as follows: + +``` +cc_test( + name = "example_test", + srcs = ["example_test.cpp"], + deps = [ + ":example", + "@catch2//:catch2_main", + ], +) +``` + --- [Home](Readme.md#top) diff --git a/src/external/catch2/docs/command-line.md b/src/external/catch2/docs/command-line.md index a15a2131..7e69bf12 100644 --- a/src/external/catch2/docs/command-line.md +++ b/src/external/catch2/docs/command-line.md @@ -85,43 +85,102 @@ Click one of the following links to take you straight to that option - or scroll
<test-spec> ...
-Test cases, wildcarded test cases, tags and tag expressions are all passed directly as arguments. Tags are distinguished by being enclosed in square brackets. +By providing a test spec, you filter which tests will be run. If you call +Catch2 without any test spec, then it will run all non-hidden test +cases. A test case is hidden if it has the `[!benchmark]` tag, any tag +with a dot at the start, e.g. `[.]` or `[.foo]`. -If no test specs are supplied then all test cases, except "hidden" tests, are run. -A test is hidden by giving it any tag starting with (or just) a period (```.```) - or, in the deprecated case, tagged ```[hide]``` or given name starting with `'./'`. To specify hidden tests from the command line ```[.]``` or ```[hide]``` can be used *regardless of how they were declared*. +There are three basic test specs that can then be combined into more +complex specs: -Specs must be enclosed in quotes if they contain spaces. If they do not contain spaces the quotes are optional. + * Full test name, e.g. `"Test 1"`. -Wildcards consist of the `*` character at the beginning and/or end of test case names and can substitute for any number of any characters (including none). + This allows only test cases whose name is "Test 1". -Test specs are case insensitive. + * Wildcarded test name, e.g. `"*Test"`, or `"Test*"`, or `"*Test*"`. -If a spec is prefixed with `exclude:` or the `~` character then the pattern matches an exclusion. This means that tests matching the pattern are excluded from the set - even if a prior inclusion spec included them. Subsequent inclusion specs will take precedence, however. -Inclusions and exclusions are evaluated in left-to-right order. + This allows any test case whose name ends with, starts with, or contains + in the middle the string "Test". Note that the wildcard can only be at + the start or end. -Test case examples: + * Tag name, e.g. `[some-tag]`. + This allows any test case tagged with "[some-tag]". Remember that some + tags are special, e.g. those that start with "." or with "!". + + +You can also combine the basic test specs to create more complex test +specs. You can: + + * Concatenate specs to apply all of them, e.g. `[some-tag][other-tag]`. + + This allows test cases that are tagged with **both** "[some-tag]" **and** + "[other-tag]". A test case with just "[some-tag]" will not pass the filter, + nor will test case with just "[other-tag]". + + * Comma-join specs to apply any of them, e.g. `[some-tag],[other-tag]`. + + This allows test cases that are tagged with **either** "[some-tag]" **or** + "[other-tag]". A test case with both will obviously also pass the filter. + + Note that commas take precendence over simple concatenation. This means + that `[a][b],[c]` accepts tests that are tagged with either both "[a]" and + "[b]", or tests that are tagged with just "[c]". + + * Negate the spec by prepending it with `~`, e.g. `~[some-tag]`. + + This rejects any test case that is tagged with "[some-tag]". Note that + rejection takes precedence over other filters. + + Note that negations always binds to the following _basic_ test spec. + This means that `~[foo][bar]` negates only the "[foo]" tag and not the + "[bar]" tag. + +Note that when Catch2 is deciding whether to include a test, first it +checks whether the test matches any negative filters. If it does, +the test is rejected. After that, the behaviour depends on whether there +are positive filters as well. If there are no positive filters, all +remaining non-hidden tests are included. If there are positive filters, +only tests that match the positive filters are included. + +You can also match test names with special characters by escaping them +with a backslash (`"\"`), e.g. a test named `"Do A, then B"` is matched +by `"Do A\, then B"` test spec. Backslash also escapes itself. + + +### Examples + +Given these TEST_CASEs, ``` -thisTestOnly Matches the test case called, 'thisTestOnly' -"this test only" Matches the test case called, 'this test only' -these* Matches all cases starting with 'these' -exclude:notThis Matches all tests except, 'notThis' -~notThis Matches all tests except, 'notThis' -~*private* Matches all tests except those that contain 'private' -a* ~ab* abc Matches all tests that start with 'a', except those that - start with 'ab', except 'abc', which is included -~[tag1] Matches all tests except those tagged with '[tag1]' --# [#somefile] Matches all tests from the file 'somefile.cpp' +TEST_CASE("Test 1") {} + +TEST_CASE("Test 2", "[.foo]") {} + +TEST_CASE("Test 3", "[.bar]") {} + +TEST_CASE("Test 4", "[.][foo][bar]") {} ``` -Names within square brackets are interpreted as tags. -A series of tags form an AND expression whereas a comma-separated sequence forms an OR expression. e.g.: +this is the result of these filters +``` +./tests # Selects only the first test, others are hidden +./tests "Test 1" # Selects only the first test, other do not match +./tests ~"Test 1" # Selects no tests. Test 1 is rejected, other tests are hidden +./tests "Test *" # Selects all tests. +./tests [bar] # Selects tests 3 and 4. Other tests are not tagged [bar] +./tests ~[foo] # Selects test 1, because it is the only non-hidden test without [foo] tag +./tests [foo][bar] # Selects test 4. +./tests [foo],[bar] # Selects tests 2, 3, 4. +./tests ~[foo][bar] # Selects test 3. 2 and 4 are rejected due to having [foo] tag +./tests ~"Test 2"[foo] # Selects test 4, because test 2 is explicitly rejected +./tests [foo][bar],"Test 1" # Selects tests 1 and 4. +./tests "Test 1*" # Selects test 1, wildcard can match zero characters +``` -
[one][two],[three]
-This matches all tests tagged `[one]` and `[two]`, as well as all tests tagged `[three]` +_Note: Using plain asterisk on a command line can cause issues with shell +expansion. Make sure that the asterisk is passed to Catch2 and is not +interpreted by the shell._ -Test names containing special characters, such as `,` or `[` can specify them on the command line using `\`. -`\` also escapes itself. ## Choosing a reporter to use @@ -135,7 +194,8 @@ verbose and human-friendly output. Reporters are also individually configurable. To pass configuration options to the reporter, you append `::key=value` to the reporter specification -as many times as you want, e.g. `--reporter xml::out=someFile.xml`. +as many times as you want, e.g. `--reporter xml::out=someFile.xml` or +`--reporter custom::colour-mode=ansi::Xoption=2`. The keys must either be prefixed by "X", in which case they are not parsed by Catch2 and are only passed down to the reporter, or one of options @@ -306,14 +366,14 @@ There are currently two warnings implemented: ## Reporting timings
-d, --durations <yes/no>
-When set to ```yes``` Catch will report the duration of each test case, in milliseconds. Note that it does this regardless of whether a test case passes or fails. Note, also, the certain reporters (e.g. Junit) always report test case durations regardless of this option being set or not. +When set to ```yes``` Catch will report the duration of each test case, in seconds with millisecond precision. Note that it does this regardless of whether a test case passes or fails. Note, also, the certain reporters (e.g. Junit) always report test case durations regardless of this option being set or not.
-D, --min-duration <value>
> `--min-duration` was [introduced](https://github.com/catchorg/Catch2/pull/1910) in Catch2 2.13.0 When set, Catch will report the duration of each test case that took more -than <value> seconds, in milliseconds. This option is overridden by both +than <value> seconds, in seconds with millisecond precision. This option is overridden by both `-d yes` and `-d no`, so that either all durations are reported, or none are. diff --git a/src/external/catch2/docs/configuration.md b/src/external/catch2/docs/configuration.md index d4421f3c..8a3ddfab 100644 --- a/src/external/catch2/docs/configuration.md +++ b/src/external/catch2/docs/configuration.md @@ -15,6 +15,7 @@ [Enabling stringification](#enabling-stringification)
[Disabling exceptions](#disabling-exceptions)
[Overriding Catch's debug break (`-b`)](#overriding-catchs-debug-break--b)
+[Static analysis support](#static-analysis-support)
Catch2 is designed to "just work" as much as possible, and most of the configuration options below are changed automatically during compilation, @@ -25,7 +26,8 @@ with the same name. ## Prefixing Catch macros - CATCH_CONFIG_PREFIX_ALL + CATCH_CONFIG_PREFIX_ALL // Prefix all macros with CATCH_ + CATCH_CONFIG_PREFIX_MESSAGES // Prefix only INFO, UNSCOPED_INFO, WARN and CAPTURE To keep test code clean and uncluttered Catch uses short macro names (e.g. ```TEST_CASE``` and ```REQUIRE```). Occasionally these may conflict with identifiers from platform headers or the system under test. In this case the above identifier can be defined. This will cause all the Catch user macros to be prefixed with ```CATCH_``` (e.g. ```CATCH_TEST_CASE``` and ```CATCH_REQUIRE```). @@ -264,6 +266,31 @@ The macro will be used as is, that is, `CATCH_BREAK_INTO_DEBUGGER();` must compile and must break into debugger. +## Static analysis support + +> Introduced in Catch2 3.4.0. + +Some parts of Catch2, e.g. `SECTION`s, can be hard for static analysis +tools to reason about. Catch2 can change its internals to help static +analysis tools reason about the tests. + +Catch2 automatically detects some static analysis tools (initial +implementation checks for clang-tidy and Coverity), but you can override +its detection (in either direction) via + +``` +CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT // force enables static analysis help +CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT // force disables static analysis help +``` + +_As the name suggests, this is currently experimental, and thus we provide +no backwards compatibility guarantees._ + +**DO NOT ENABLE THIS FOR BUILDS YOU INTEND TO RUN.** The changed internals +are not meant to be runnable, only "scannable". + + + --- [Home](Readme.md#top) diff --git a/src/external/catch2/docs/contributing.md b/src/external/catch2/docs/contributing.md index d9f87fc1..d21323d9 100644 --- a/src/external/catch2/docs/contributing.md +++ b/src/external/catch2/docs/contributing.md @@ -107,8 +107,7 @@ cmake -B debug-build -S . -DCMAKE_BUILD_TYPE=Debug --preset all-tests cmake --build debug-build # 4. Run the tests using CTest -cd debug-build -ctest -j 4 --output-on-failure -C Debug +ctest -j 4 --output-on-failure -C Debug --test-dir debug-build ``` snippet source | anchor diff --git a/src/external/catch2/docs/deprecations.md b/src/external/catch2/docs/deprecations.md index 1fb79aaa..0b5bee13 100644 --- a/src/external/catch2/docs/deprecations.md +++ b/src/external/catch2/docs/deprecations.md @@ -35,6 +35,19 @@ being aborted (when using `--abort` or `--abortx`). It is however **NOT** invoked for test cases that are [explicitly skipped using the `SKIP` macro](skipping-passing-failing.md#top). + +### Non-const function for `TEST_CASE_METHOD` + +> Deprecated in Catch2 vX.Y.Z + +Currently, the member function generated for `TEST_CASE_METHOD` is +not `const` qualified. In the future, the generated member function will +be `const` qualified, just as `TEST_CASE_PERSISTENT_FIXTURE` does. + +If you are mutating the fixture instance from within the test case, and +want to keep doing so in the future, mark the mutated members as `mutable`. + + --- [Home](Readme.md#top) diff --git a/src/external/catch2/docs/faq.md b/src/external/catch2/docs/faq.md index 0f303ee5..80923d26 100644 --- a/src/external/catch2/docs/faq.md +++ b/src/external/catch2/docs/faq.md @@ -10,6 +10,7 @@ [Does Catch2 support running tests in parallel?](#does-catch2-support-running-tests-in-parallel)
[Can I compile Catch2 into a dynamic library?](#can-i-compile-catch2-into-a-dynamic-library)
[What repeatability guarantees does Catch2 provide?](#what-repeatability-guarantees-does-catch2-provide)
+[My build cannot find `catch2/catch_user_config.hpp`, how can I fix it?](#my-build-cannot-find-catch2catch_user_confighpp-how-can-i-fix-it)
## How do I run global setup/teardown only if tests will be run? @@ -28,7 +29,7 @@ depending on how often the cleanup needs to happen. ## Why cannot I derive from the built-in reporters? They are not made to be overridden, in that we do not attempt to maintain -a consistent internal state if a member function is overriden, and by +a consistent internal state if a member function is overridden, and by forbidding users from using them as a base class, we can refactor them as needed later. @@ -83,12 +84,30 @@ and it is also generally repeatable across versions, but we might break it from time to time. E.g. we broke repeatability with previous versions in v2.13.4 so that test cases with similar names are shuffled better. -Random generators currently rely on platform's stdlib, specifically -the distributions from ``. We thus provide no extra guarantee -above what your platform does. **Important: ``'s distributions +Since Catch2 3.5.0 the random generators use custom distributions, +that should be repeatable across different platforms, with few caveats. +For details see the section on random generators in the [Generator +documentation](generators.md#random-number-generators-details). + +Before this version, random generators relied on distributions from +platform's stdlib. We thus can provide no extra guarantee on top of the +ones given by your platform. **Important: ``'s distributions are not specified to be repeatable across different platforms.** +## My build cannot find `catch2/catch_user_config.hpp`, how can I fix it? + +`catch2/catch_user_config.hpp` is a generated header that contains user +compile time configuration. It is generated by CMake/Meson/Bazel during +build. If you are not using either of these, your three options are to + +1) Build Catch2 separately using build tool that will generate the header +2) Use the amalgamated files to build Catch2 +3) Use CMake to configure a build. This will generate the header and you + can copy it into your own checkout of Catch2. + + + --- [Home](Readme.md#top) diff --git a/src/external/catch2/docs/generators.md b/src/external/catch2/docs/generators.md index 69d1a02d..eb1a255a 100644 --- a/src/external/catch2/docs/generators.md +++ b/src/external/catch2/docs/generators.md @@ -134,7 +134,7 @@ type, making their usage much nicer. These are * `map(func, GeneratorWrapper&&)` for `MapGenerator` (map `U` to `T`) * `chunk(chunk-size, GeneratorWrapper&&)` for `ChunkGenerator` * `random(IntegerOrFloat a, IntegerOrFloat b)` for `RandomIntegerGenerator` or `RandomFloatGenerator` -* `range(Arithemtic start, Arithmetic end)` for `RangeGenerator` with a step size of `1` +* `range(Arithmetic start, Arithmetic end)` for `RangeGenerator` with a step size of `1` * `range(Arithmetic start, Arithmetic end, Arithmetic step)` for `RangeGenerator` with a custom step size * `from_range(InputIterator from, InputIterator to)` for `IteratorGenerator` * `from_range(Container const&)` for `IteratorGenerator` @@ -189,6 +189,45 @@ TEST_CASE("type conversion", "[generators]") { } ``` + +### Random number generators: details + +> This section applies from Catch2 3.5.0. Before that, random generators +> were a thin wrapper around distributions from ``. + +All of the `random(a, b)` generators in Catch2 currently generate uniformly +distributed number in closed interval \[a; b\]. This is different from +`std::uniform_real_distribution`, which should return numbers in interval +\[a; b) (but due to rounding can end up returning b anyway), but the +difference is intentional, so that `random(a, a)` makes sense. If there is +enough interest from users, we can provide API to pick any of CC, CO, OC, +or OO ranges. + +Unlike `std::uniform_int_distribution`, Catch2's generators also support +various single-byte integral types, such as `char` or `bool`. + + +#### Reproducibility + +Given the same seed, the output from the integral generators is fully +reproducible across different platforms. + +For floating point generators, the situation is much more complex. +Generally Catch2 only promises reproducibility (or even just correctness!) +on platforms that obey the IEEE-754 standard. Furthermore, reproducibility +only applies between binaries that perform floating point math in the +same way, e.g. if you compile a binary targetting the x87 FPU and another +one targetting SSE2 for floating point math, their results will vary. +Similarly, binaries compiled with compiler flags that relax the IEEE-754 +adherence, e.g. `-ffast-math`, might provide different results than those +compiled for strict IEEE-754 adherence. + +Finally, we provide zero guarantees on the reproducibility of generating +`long double`s, as the internals of `long double` varies across different +platforms. + + + ## Generator interface You can also implement your own generators, by deriving from the @@ -221,3 +260,21 @@ For full example of implementing your own generator, look into Catch2's examples, specifically [Generators: Create your own generator](../examples/300-Gen-OwnGenerator.cpp). + +### Handling empty generators + +The generator interface assumes that a generator always has at least one +element. This is not always true, e.g. if the generator depends on an external +datafile, the file might be missing. + +There are two ways to handle this, depending on whether you want this +to be an error or not. + + * If empty generator **is** an error, throw an exception in constructor. + * If empty generator **is not** an error, use the [`SKIP`](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor. + + + +--- + +[Home](Readme.md#top) diff --git a/src/external/catch2/docs/limitations.md b/src/external/catch2/docs/limitations.md index cc0ed05d..f5f60ba8 100644 --- a/src/external/catch2/docs/limitations.md +++ b/src/external/catch2/docs/limitations.md @@ -174,12 +174,18 @@ TEST_CASE("b") { If you are seeing a problem like this, i.e. weird test paths that trigger only under Clang with `libc++`, or only under very specific version of `libstdc++`, it is very likely you are seeing this. The only known workaround is to use a fixed version of your standard library. -### libstdc++, `_GLIBCXX_DEBUG` macro and random ordering of tests +### Visual Studio 2022 -- can't compile assertion with the spaceship operator -Running a Catch2 binary compiled against libstdc++ with `_GLIBCXX_DEBUG` -macro defined with `--order rand` will cause a debug check to trigger and -abort the run due to self-assignment. -[This is a known bug inside libstdc++](https://stackoverflow.com/questions/22915325/avoiding-self-assignment-in-stdshuffle/23691322) +[The C++ standard requires that `std::foo_ordering` is only comparable with +a literal 0](https://eel.is/c++draft/cmp#categories.pre-3). There are +multiple strategies a stdlib implementation can take to achieve this, and +MSVC's STL has changed the strategy they use between two releases of VS 2022. + +With the new strategy, `REQUIRE((a <=> b) == 0)` no longer compiles under +MSVC. Note that Catch2 can compile code using MSVC STL's new strategy, +but only when compiled with a C++20 conforming compiler. MSVC is currently +not conformant enough, but `clang-cl` will compile the assertion above +using MSVC STL without problem. + +This change got in with MSVC v19.37](https://godbolt.org/z/KG9obzdvE). -Workaround: Don't use `--order rand` when compiling against debug-enabled -libstdc++. diff --git a/src/external/catch2/docs/list-of-examples.md b/src/external/catch2/docs/list-of-examples.md index a919408a..40d3f711 100644 --- a/src/external/catch2/docs/list-of-examples.md +++ b/src/external/catch2/docs/list-of-examples.md @@ -8,6 +8,7 @@ - Assertion: [REQUIRE, CHECK](../examples/030-Asn-Require-Check.cpp) - Fixture: [Sections](../examples/100-Fix-Section.cpp) - Fixture: [Class-based fixtures](../examples/110-Fix-ClassFixture.cpp) +- Fixture: [Persistent fixtures](../examples/111-Fix-PersistentFixture.cpp) - BDD: [SCENARIO, GIVEN, WHEN, THEN](../examples/120-Bdd-ScenarioGivenWhenThen.cpp) - Listener: [Listeners](../examples/210-Evt-EventListeners.cpp) - Configuration: [Provide your own output streams](../examples/231-Cfg-OutputStreams.cpp) diff --git a/src/external/catch2/docs/logging.md b/src/external/catch2/docs/logging.md index dbd4f912..79709386 100644 --- a/src/external/catch2/docs/logging.md +++ b/src/external/catch2/docs/logging.md @@ -114,6 +114,10 @@ Similar to `INFO`, but messages are not limited to their own scope: They are rem The message is always reported but does not fail the test. +**SUCCEED(** _message expression_ **)** + +The message is reported and the test case succeeds. + **FAIL(** _message expression_ **)** The message is reported and the test case fails. diff --git a/src/external/catch2/docs/matchers.md b/src/external/catch2/docs/matchers.md index 14c15898..4b9445ae 100644 --- a/src/external/catch2/docs/matchers.md +++ b/src/external/catch2/docs/matchers.md @@ -50,25 +50,43 @@ Both of the string matchers used in the examples above live in the `catch_matchers_string.hpp` header, so to compile the code above also requires `#include `. +### Combining operators and lifetimes + **IMPORTANT**: The combining operators do not take ownership of the -matcher objects being combined. This means that if you store combined -matcher object, you have to ensure that the matchers being combined -outlive its last use. What this means is that the following code leads -to a use-after-free (UAF): +matcher objects being combined. + +This means that if you store combined matcher object, you have to ensure +that the individual matchers being combined outlive the combined matcher. +Note that the negation matcher from `!` also counts as combining matcher +for this. +Explained on an example, this is fine ```cpp -#include -#include +CHECK_THAT(value, WithinAbs(0, 2e-2) && !WithinULP(0., 1)); +``` -TEST_CASE("Bugs, bugs, bugs", "[Bug]"){ - std::string str = "Bugs as a service"; +and so is this +```cpp +auto is_close_to_zero = WithinAbs(0, 2e-2); +auto is_zero = WithinULP(0., 1); - auto match_expression = Catch::Matchers::EndsWith( "as a service" ) || - (Catch::Matchers::StartsWith( "Big data" ) && !Catch::Matchers::ContainsSubstring( "web scale" ) ); - REQUIRE_THAT(str, match_expression); -} +CHECK_THAT(value, is_close_to_zero && !is_zero); ``` +but this is not +```cpp +auto is_close_to_zero = WithinAbs(0, 2e-2); +auto is_zero = WithinULP(0., 1); +auto is_close_to_but_not_zero = is_close_to_zero && !is_zero; + +CHECK_THAT(a_value, is_close_to_but_not_zero); // UAF +``` + +because `!is_zero` creates a temporary instance of Negation matcher, +which the `is_close_to_but_not_zero` refers to. After the line ends, +the temporary is destroyed and the combined `is_close_to_but_not_zero` +matcher now refers to non-existent object, so using it causes use-after-free. + ## Built-in matchers @@ -192,15 +210,36 @@ The other miscellaneous matcher utility is exception matching. #### Matching exceptions -Catch2 provides a utility macro for asserting that an expression -throws exception of specific type, and that the exception has desired -properties. The macro is `REQUIRE_THROWS_MATCHES(expr, ExceptionType, Matcher)`. +Because exceptions are a bit special, Catch2 has a separate macro for them. + + +The basic form is + +``` +REQUIRE_THROWS_MATCHES(expr, ExceptionType, Matcher) +``` + +and it checks that the `expr` throws an exception, that exception is derived +from the `ExceptionType` type, and then `Matcher::match` is called on +the caught exception. > `REQUIRE_THROWS_MATCHES` macro lives in `catch2/matchers/catch_matchers.hpp` +For one-off checks you can use the `Predicate` matcher above, e.g. + +```cpp +REQUIRE_THROWS_MATCHES(parse(...), + parse_error, + Predicate([] (parse_error const& err) -> bool { return err.line() == 1; }) +); +``` -Catch2 currently provides two matchers for exceptions. -These are: +but if you intend to thoroughly test your error reporting, I recommend +defining a specialized matcher. + + +Catch2 also provides 2 built-in matchers for checking the error message +inside an exception (it must be derived from `std::exception`): * `Message(std::string message)`. * `MessageMatches(Matcher matcher)`. @@ -218,10 +257,7 @@ REQUIRE_THROWS_MATCHES(throwsDerivedException(), DerivedException, Message("De REQUIRE_THROWS_MATCHES(throwsDerivedException(), DerivedException, MessageMatches(StartsWith("DerivedException"))); ``` -Note that `DerivedException` in the example above has to derive from -`std::exception` for the example to work. - -> the exception message matcher lives in `catch2/matchers/catch_matchers_exception.hpp` +> the exception message matchers live in `catch2/matchers/catch_matchers_exception.hpp` ### Generic range Matchers @@ -286,7 +322,7 @@ comparable. (e.g. you may compare `std::vector` to `std::array`). `UnorderedRangeEquals` is similar to `RangeEquals`, but the order does not matter. For example "1, 2, 3" would match "3, 2, 1", but not "1, 1, 2, 3" As with `RangeEquals`, `UnorderedRangeEquals` compares -the individual elements using using `operator==` by default. +the individual elements using `operator==` by default. Both `RangeEquals` and `UnorderedRangeEquals` optionally accept a predicate which can be used to compare the containers element-wise. diff --git a/src/external/catch2/docs/opensource-users.md b/src/external/catch2/docs/opensource-users.md index 12b4551c..a02d0b98 100644 --- a/src/external/catch2/docs/opensource-users.md +++ b/src/external/catch2/docs/opensource-users.md @@ -95,6 +95,9 @@ A C++ client library for Consul. Consul is a distributed tool for discovering an ### [Reactive-Extensions/ RxCpp](https://github.com/Reactive-Extensions/RxCpp) A library of algorithms for values-distributed-in-time. +### [SFML](https://github.com/SFML/SFML) +Simple and Fast Multimedia Library. + ### [SOCI](https://github.com/SOCI/soci) The C++ Database Access Library. @@ -110,6 +113,12 @@ A header-only TOML parser and serializer for modern C++. ### [Trompeloeil](https://github.com/rollbear/trompeloeil) A thread-safe header-only mocking framework for C++14. +### [wxWidgets](https://www.wxwidgets.org/) +Cross-Platform C++ GUI Library. + +### [xmlwrapp](https://github.com/vslavik/xmlwrapp) +C++ XML parsing library using libxml2. + ## Applications & Tools ### [App Mesh](https://github.com/laoshanxi/app-mesh) @@ -137,7 +146,7 @@ Newsbeuter is an open-source RSS/Atom feed reader for text terminals. A 2D, Zombie, RPG game which is being made on our own engine. ### [raspigcd](https://github.com/pantadeusz/raspigcd) -Low level CLI app and library for execution of GCODE on Raspberry Pi without any additional microcontrolers (just RPi + Stepsticks). +Low level CLI app and library for execution of GCODE on Raspberry Pi without any additional microcontrollers (just RPi + Stepsticks). ### [SpECTRE](https://github.com/sxs-collaboration/spectre) SpECTRE is a code for multi-scale, multi-physics problems in astrophysics and gravitational physics. diff --git a/src/external/catch2/docs/other-macros.md b/src/external/catch2/docs/other-macros.md index 24a0fb6e..79990a6a 100644 --- a/src/external/catch2/docs/other-macros.md +++ b/src/external/catch2/docs/other-macros.md @@ -93,30 +93,6 @@ TEST_CASE("STATIC_CHECK showcase", "[traits]") { ## Test case related macros -* `METHOD_AS_TEST_CASE` - -`METHOD_AS_TEST_CASE( member-function-pointer, description )` lets you -register a member function of a class as a Catch2 test case. The class -will be separately instantiated for each method registered in this way. - -```cpp -class TestClass { - std::string s; - -public: - TestClass() - :s( "hello" ) - {} - - void testCase() { - REQUIRE( s == "hello" ); - } -}; - - -METHOD_AS_TEST_CASE( TestClass::testCase, "Use class's method as a test case", "[class]" ) -``` - * `REGISTER_TEST_CASE` `REGISTER_TEST_CASE( function, description )` let's you register diff --git a/src/external/catch2/docs/release-notes.md b/src/external/catch2/docs/release-notes.md index 1fa37da4..5f2d92ae 100644 --- a/src/external/catch2/docs/release-notes.md +++ b/src/external/catch2/docs/release-notes.md @@ -2,6 +2,15 @@ # Release notes **Contents**
+[3.7.1](#371)
+[3.7.0](#370)
+[3.6.0](#360)
+[3.5.4](#354)
+[3.5.3](#353)
+[3.5.2](#352)
+[3.5.1](#351)
+[3.5.0](#350)
+[3.4.0](#340)
[3.3.2](#332)
[3.3.1](#331)
[3.3.0](#330)
@@ -56,6 +65,200 @@ [Even Older versions](#even-older-versions)
+## 3.7.1 + +### Improvements +* Applied the JUnit reporter's optimization from last release to the SonarQube reporter +* Suppressed `-Wuseless-cast` in `CHECK_THROWS_MATCHES` (#2904) +* Standardize exit codes for various failures + * Running no tests is now guaranteed to exit with 2 (without the `--allow-running-no-tests` flag) + * All tests skipped is now always 4 (...) + * Assertion failures are now always 42 + * and so on + +### Fixes +* Fixed out-of-bounds access when the arg parser encounters single `-` as an argument (#2905) + +### Miscellaneous +* Added `catch_config_prefix_messages.hpp` to meson build (#2903) +* `catch_discover_tests` now supports skipped tests (#2873) + * You can get the old behaviour by calling `catch_discover_tests` with `SKIP_IS_FAILURE` option. + + +## 3.7.0 + +### Improvements +* Slightly improved compile times of benchmarks +* Made the resolution estimation in benchmarks slightly more precise +* Added new test case macro, `TEST_CASE_PERSISTENT_FIXTURE` (#2885, #1602) + * Unlike `TEST_CASE_METHOD`, the same underlying instance is used for all partial runs of that test case +* **MASSIVELY** improved performance of the JUnit reporter when handling successful assertions (#2897) + * For 1 test case and 10M assertions, the new reporter runs 3x faster and uses up only 8 MB of memory, while the old one needs 7 GB of memory. +* Reworked how output redirects works. + * Combining a reporter writing to stdout with capturing reporter no longer leads to the capturing reporter seeing all of the other reporter's output. + * The file based redirect no longer opens up a new temporary file for each partial test case run, so it will not run out of temporary files when running many tests in single process. + +### Miscellaneous +* Better documentation for matchers on thrown exceptions (`REQUIRE_THROWS_MATCHES`) +* Improved `catch_discover_tests`'s handling of environment paths (#2878) + * It won't reorder paths in `DL_PATHS` or `DYLD_FRAMEWORK_PATHS` args + * It won't overwrite the environment paths for test discovery + + +## 3.6.0 + +### Fixes +* Fixed Windows ARM64 build by fixing the preprocessor condition guarding use `_umul128` intrinsic. +* Fixed Windows ARM64EC build by removing intrinsic pragma it does not understand. (#2858) + * Why doesn't the x64-emulation build mode understand x64 pragmas? Don't ask me, ask the MSVC guys. +* Fixed the JUnit reporter sometimes crashing when reporting a fatal error. (#1210, #2855) + * The binary will still exit, but through the original error, rather than secondary error inside the reporter. + * The underlying fix applies to all reporters, not just the JUnit one, but only JUnit was currently causing troubles. + +### Improvements +* Disable `-Wnon-virtual-dtor` in Decomposer and Matchers (#2854) +* `precision` in floating point stringmakers defaults to `max_digits10`. + * This means that floating point values will be printed with enough precision to disambiguate any two floats. +* Column wrapping ignores ansi colour codes when calculating string width (#2833, #2849) + * This makes the output much more readable when the provided messages contain colour codes. + +### Miscellaneous +* Conan support improvements + * `compatibility_cppstr` is set to False. (#2860) + * This means that Conan won't let you mix library and project with different C++ standard settings. + * The implementation library CMake target name through Conan is properly set to `Catch2::Catch2` (#2861) +* `SelfTest` target can be built through Bazel (#2857) + + +## 3.5.4 + +### Fixes +* Fixed potential compilation error when asked to generate random integers whose type did not match `std::(u)int*_t`. + * This manifested itself when generating random `size_t`s on MacOS +* Added missing outlined destructor causing `Wdelete-incomplete` when compiling against libstdc++ in C++23 mode (#2852) +* Fixed regression where decomposing assertion with const instance of `std::foo_ordering` would not compile + +### Improvements +* Reintroduced support for GCC 5 and 6 (#2836) + * As with VS2017, if they start causing trouble again, they will be dropped again. +* Added workaround for targetting newest MacOS (Sonoma) using GCC (#2837, #2839) +* `CATCH_CONFIG_DEFAULT_REPORTER` can now be an arbitrary reporter spec + * Previously it could only be a plain reporter name, so it was impossible to compile in custom arguments to the reporter. +* Improved performance of generating 64bit random integers by 20+% + +### Miscellaneous +* Significantly improved Conan in-tree recipe (#2831) +* `DL_PATHS` in `catch_discover_tests` now supports multiple arguments (#2852, #2736) +* Fixed preprocessor logic for checking whether we expect reproducible floating point results in tests. +* Improved the floating point tests structure to avoid `Wunused` when the reproducibility tests are disabled (#2845) + + +## 3.5.3 + +### Fixes +* Fixed OOB access when computing filename tag (from the `-#` flag) for file without extension (#2798) +* Fixed the linking against `log` on Android to be `PRIVATE` (#2815) +* Fixed `Wuseless-cast` in benchmarking internals (#2823) + +### Improvements +* Restored compatibility with VS2017 (#2792, #2822) + * The baseline for Catch2 is still C++14 with some reasonable workarounds for specific compilers, so if VS2017 starts acting up again, the support will be dropped again. +* Suppressed clang-tidy's `bugprone-chained-comparison` in assertions (#2801) +* Improved the static analysis mode to evaluate arguments to `TEST_CASE` and `SECTION` (#2817) + * Clang-tidy should no longer warn about runtime arguments to these macros being unused in static analysis mode. + * Clang-tidy can warn on issues involved arguments to these macros. +* Added support for literal-zero detectors based on `consteval` constructors + * This is required for compiling `REQUIRE((a <=> b) == 0)` against MSVC's stdlib. + * Sadly, MSVC still cannot compile this assertion as it does not implement C++20 correctly. + * You can use `clang-cl` with MSVC's stdlib instead. + * If for some godforsaken reasons you want to understand this better, read the two relevant commits: [`dc51386b9fd61f99ea9c660d01867e6ad489b403`](https://github.com/catchorg/Catch2/commit/dc51386b9fd61f99ea9c660d01867e6ad489b403), and [`0787132fc82a75e3fb255aa9484ca1dc1eff2a30`](https://github.com/catchorg/Catch2/commit/0787132fc82a75e3fb255aa9484ca1dc1eff2a30). + +### Miscellaneous +* Disabled tests for FP random generator reproducibility on non-SSE2 x86 targets (#2796) +* Modified the in-tree Conan recipe to support Conan 2 (#2805) + + +## 3.5.2 + +### Fixes +* Fixed `-Wsubobject-linkage` in the Console reporter (#2794) +* Fixed adding new CLI Options to lvalue parser using `|` (#2787) + + +## 3.5.1 + +### Improvements +* Significantly improved performance of the CLI parsing. + * This includes the cost of preparing the CLI parser, so Catch2's binaries start much faster. + +### Miscellaneous +* Added support for Bazel modules (#2781) +* Added CMake option to disable the build reproducibility settings (#2785) +* Added `log` library linking to the Meson build (#2784) + + +## 3.5.0 + +### Improvements +* Introduced `CATCH_CONFIG_PREFIX_MESSAGES` to prefix only logging macros (#2544) + * This means `INFO`, `UNSCOPED_INFO`, `WARN` and `CAPTURE`. +* Section hints in static analysis mode are now `const` + * This prevents Clang-Tidy from complaining about `misc-const-correctness`. +* `from_range` generator supports C arrays and ranges that require ADL (#2737) +* Stringification support for `std::optional` now also includes `std::nullopt` (#2740) +* The Console reporter flushes output after writing benchmark runtime estimate. + * This means that you can immediately see for how long the benchmark is expected to run. +* Added workaround to enable compilation with ICC 19.1 (#2551, #2766) +* Compiling Catch2 for XBox should work out of the box (#2772) + * Catch2 should automatically disable getenv when compiled for XBox. +* Compiling Catch2 with exceptions disabled no longer triggers `Wunused-function` (#2726) +* **`random` Generators for integral types are now reproducible across different platforms** + * Unlike ``, Catch2's generators also support 1 byte integral types (`char`, `bool`, ...) +* **`random` Generators for `float` and `double` are now reproducible across different platforms** + * `long double` varies across different platforms too much to be reproducible + * This guarantee applies only to platforms with IEEE 754 floats. + +### Fixes +* UDL declaration inside Catch2 are now strictly conforming to the standard + * `operator "" _a` is UB, `operator ""_a` is fine. Seriously. +* Fixed `CAPTURE` tests failing to compile in C++23 mode (#2744) +* Fixed missing include in `catch_message.hpp` (#2758) +* Fixed `CHECK_ELSE` suppressing failure from uncaught exceptions(#2723) + +### Miscellaneous +* The documentation for specifying which tests to run through commandline has been completely rewritten (#2738) +* Fixed installation when building Catch2 with meson (#2722, #2742) +* Fixed `catch_discover_tests` when using custom reporter and `PRE_TEST` discovery mode (#2747) +* `catch_discover_tests` supports multi-config CMake generator in `PRE_TEST` discovery mode (#2739, #2746) + + +## 3.4.0 + +### Improvements +* `VectorEquals` supports elements that provide only `==` and not `!=` (#2648) +* Catch2 supports compiling with IAR compiler (#2651) +* Various small internal performance improvements +* Various small internal compilation time improvements +* XMLReporter now reports location info for INFO and WARN (#1251) + * This bumps up the xml format version to 3 +* Documented that `SKIP` in generator constructor can be used to handle empty generator (#1593) +* Added experimental static analysis support to `TEST_CASE` and `SECTION` macros (#2681) + * The two macros are redefined in a way that helps the SA tools reason about the possible paths through a test case with sections. + * The support is controlled by the `CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT` option and autodetects clang-tidy and Coverity. +* `*_THROWS`, `*_THROWS_AS`, etc now suppress warning coming from `__attribute__((warn_unused_result))` on GCC (#2691) + * Unlike plain `[[nodiscard]]`, this warning is not silenced by void cast. WTF GCC? + +### Fixes +* Fixed `assertionStarting` events being sent after the expr is evaluated (#2678) +* Errors in `TEST_CASE` tags are now reported nicely (#2650) + +### Miscellaneous +* Bunch of improvements to `catch_discover_tests` + * Added DISCOVERY_MODE option, so the discovery can happen either post build or pre-run. + * Fixed handling of semicolons and backslashes in test names (#2674, #2676) +* meson build can disable building tests (#2693) +* meson build properly sets meson version 0.54.1 as the minimal supported version (#2688) + ## 3.3.2 @@ -149,7 +352,7 @@ ### Fixes * Cleaned out some warnings and static analysis issues - * Suppressed `-Wcomma` warning rarely occuring in templated test cases (#2543) + * Suppressed `-Wcomma` warning rarely occurring in templated test cases (#2543) * Constified implementation details in `INFO` (#2564) * Made `MatcherGenericBase` copy constructor const (#2566) * Fixed serialization of test filters so the output roundtrips @@ -352,7 +555,7 @@ v3 releases. * Added `STATIC_CHECK` macro, similar to `STATIC_REQUIRE` (#2318) * When deferred tu runtime, it behaves like `CHECK`, and not like `REQUIRE`. * You can have multiple tests with the same name, as long as other parts of the test identity differ (#1915, #1999, #2175) - * Test identity includes test's name, test's tags and and test's class name if applicable. + * Test identity includes test's name, test's tags and test's class name if applicable. * Added new warning, `UnmatchedTestSpec`, to error on test specs with no matching tests * The `-w`, `--warn` warning flags can now be provided multiple times to enable multiple warnings * The case-insensitive handling of tags is now more reliable and takes up less memory @@ -517,7 +720,7 @@ v3 releases. * The `SECTION`(s) before the `GENERATE` will not be run multiple times, the following ones will. * Added `-D`/`--min-duration` command line flag (#1910) * If a test takes longer to finish than the provided value, its name and duration will be printed. - * This flag is overriden by setting `-d`/`--duration`. + * This flag is overridden by setting `-d`/`--duration`. ### Fixes * `TAPReporter` no longer skips successful assertions (#1983) @@ -585,7 +788,7 @@ v3 releases. ### Fixes * Fixed computation of benchmarking column widths in ConsoleReporter (#1885, #1886) * Suppressed clang-tidy's `cppcoreguidelines-pro-type-vararg` in assertions (#1901) - * It was a false positive trigered by the new warning support workaround + * It was a false positive triggered by the new warning support workaround * Fixed bug in test specification parser handling of OR'd patterns using escaping (#1905) ### Miscellaneous @@ -922,7 +1125,7 @@ v3 releases. ### Contrib * `ParseAndAddCatchTests` has learned how to use `DISABLED` CTest property (#1452) -* `ParseAndAddCatchTests` now works when there is a whitspace before the test name (#1493) +* `ParseAndAddCatchTests` now works when there is a whitespace before the test name (#1493) ### Miscellaneous diff --git a/src/external/catch2/docs/reporter-events.md b/src/external/catch2/docs/reporter-events.md index 32a0ae50..015f67be 100644 --- a/src/external/catch2/docs/reporter-events.md +++ b/src/external/catch2/docs/reporter-events.md @@ -96,12 +96,12 @@ void assertionStarting( AssertionInfo const& assertionInfo ); void assertionEnded( AssertionStats const& assertionStats ); ``` -`assertionStarting` is called after the expression is captured, but before -the assertion expression is evaluated. This might seem like a minor -distinction, but what it means is that if you have assertion like -`REQUIRE( a + b == c + d )`, then what happens is that `a + b` and `c + d` -are evaluated before `assertionStarting` is emitted, while the `==` is -evaluated after the event. +The `assertionStarting` event is emitted before the expression in the +assertion is captured or evaluated and `assertionEnded` is emitted +afterwards. This means that given assertion like `REQUIRE(a + b == c + d)`, +Catch2 first emits `assertionStarting` event, then `a + b` and `c + d` +are evaluated, then their results are captured, the comparison is evaluated, +and then `assertionEnded` event is emitted. ## Benchmarking events diff --git a/src/external/catch2/docs/reporters.md b/src/external/catch2/docs/reporters.md index 496c61a9..20ef5e55 100644 --- a/src/external/catch2/docs/reporters.md +++ b/src/external/catch2/docs/reporters.md @@ -5,7 +5,7 @@ Reporters are a customization point for most of Catch2's output, e.g. formatting and writing out [assertions (whether passing or failing), sections, test cases, benchmarks, and so on](reporter-events.md#top). -Catch2 comes with a bunch of reporters by default (currently 8), and +Catch2 comes with a bunch of reporters by default (currently 9), and you can also write your own reporter. Because multiple reporters can be active at the same time, your own reporters do not even have to handle all reporter event, just the ones you are interested in, e.g. benchmarks. @@ -52,7 +52,7 @@ its machine-readable XML output to file `result-junit.xml`, and the uses ANSI colour codes for colouring the output. Using multiple reporters (or one reporter and one-or-more [event -listeners](event-listener.md#top)) can have surprisingly complex semantics +listeners](event-listeners.md#top)) can have surprisingly complex semantics when using customization points provided to reporters by Catch2, namely capturing stdout/stderr from test cases. diff --git a/src/external/catch2/docs/skipping-passing-failing.md b/src/external/catch2/docs/skipping-passing-failing.md index 4300d9d3..52bb18f7 100644 --- a/src/external/catch2/docs/skipping-passing-failing.md +++ b/src/external/catch2/docs/skipping-passing-failing.md @@ -9,7 +9,7 @@ In some situations it may not be possible to meaningfully execute a test case, for example when the system under test is missing certain hardware capabilities. If the required conditions can only be determined at runtime, it often doesn't make sense to consider such a test case as either passed or failed, -because it simply can not run at all. +because it simply cannot run at all. To properly express such scenarios, Catch2 provides a way to explicitly _skip_ test cases, using the `SKIP` macro: @@ -84,6 +84,12 @@ exit code, same as it does if no test cases have run. This behaviour can be overridden using the [--allow-running-no-tests](command-line.md#no-tests-override) flag. +### `SKIP` inside generators + +You can also use the `SKIP` macro inside generator's constructor to handle +cases where the generator is empty, but you do not want to fail the test +case. + ## Passing and failing test cases diff --git a/src/external/catch2/docs/test-cases-and-sections.md b/src/external/catch2/docs/test-cases-and-sections.md index acebcc51..14db55ae 100644 --- a/src/external/catch2/docs/test-cases-and-sections.md +++ b/src/external/catch2/docs/test-cases-and-sections.md @@ -48,7 +48,7 @@ For more detail on command line selection see [the command line docs](command-li Tag names are not case sensitive and can contain any ASCII characters. This means that tags `[tag with spaces]` and `[I said "good day"]` are both allowed tags and can be filtered on. However, escapes are not -supported however and `[\]]` is not a valid tag. +supported and `[\]]` is not a valid tag. The same tag can be specified multiple times for a single test case, but only one of the instances of identical tags will be kept. Which one @@ -231,7 +231,7 @@ TEMPLATE_TEST_CASE( "vectors can be sized and resized", "[vector][template]", in > [Introduced](https://github.com/catchorg/Catch2/issues/1468) in Catch2 2.6.0. -_template-type1_ through _template-typen_ is list of template template +_template-type1_ through _template-typen_ is list of template types which should be combined with each of _template-arg1_ through _template-argm_, resulting in _n * m_ test cases. Inside the test case, the resulting type is available under the name of `TestType`. diff --git a/src/external/catch2/docs/test-fixtures.md b/src/external/catch2/docs/test-fixtures.md index 9c9eaa18..653b50e0 100644 --- a/src/external/catch2/docs/test-fixtures.md +++ b/src/external/catch2/docs/test-fixtures.md @@ -1,9 +1,30 @@ # Test fixtures -## Defining test fixtures +**Contents**
+[Non-Templated test fixtures](#non-templated-test-fixtures)
+[Templated test fixtures](#templated-test-fixtures)
+[Signature-based parameterised test fixtures](#signature-based-parametrised-test-fixtures)
+[Template fixtures with types specified in template type lists](#template-fixtures-with-types-specified-in-template-type-lists)
-Although Catch allows you to group tests together as [sections within a test case](test-cases-and-sections.md), it can still be convenient, sometimes, to group them using a more traditional test fixture. Catch fully supports this too. You define the test fixture as a simple structure: +## Non-Templated test fixtures + +Although Catch2 allows you to group tests together as +[sections within a test case](test-cases-and-sections.md), it can still +be convenient, sometimes, to group them using a more traditional test. +Catch2 fully supports this too with 3 different macros for +non-templated test fixtures. They are: + +| Macro | Description | +|----------|-------------| +|1. `TEST_CASE_METHOD(className, ...)`| Creates a uniquely named class which inherits from the class specified by `className`. The test function will be a member of this derived class. An instance of the derived class will be created for every partial run of the test case. | +|2. `METHOD_AS_TEST_CASE(member-function, ...)`| Uses `member-function` as the test function. An instance of the class will be created for each partial run of the test case. | +|3. `TEST_CASE_PERSISTENT_FIXTURE(className, ...)`| Creates a uniquely named class which inherits from the class specified by `className`. The test function will be a member of this derived class. An instance of the derived class will be created at the start of the test run. That instance will be destroyed once the entire test case has ended. | + +### 1. `TEST_CASE_METHOD` + + +You define a `TEST_CASE_METHOD` test fixture as a simple structure: ```c++ class UniqueTestsFixture { @@ -30,8 +51,116 @@ class UniqueTestsFixture { } ``` -The two test cases here will create uniquely-named derived classes of UniqueTestsFixture and thus can access the `getID()` protected method and `conn` member variables. This ensures that both the test cases are able to create a DBConnection using the same method (DRY principle) and that any ID's created are unique such that the order that tests are executed does not matter. +The two test cases here will create uniquely-named derived classes of +UniqueTestsFixture and thus can access the `getID()` protected method +and `conn` member variables. This ensures that both the test cases +are able to create a DBConnection using the same method +(DRY principle) and that any ID's created are unique such that the +order that tests are executed does not matter. + +### 2. `METHOD_AS_TEST_CASE` + +`METHOD_AS_TEST_CASE` lets you register a member function of a class +as a Catch2 test case. The class will be separately instantiated +for each method registered in this way. + +```cpp +class TestClass { + std::string s; + +public: + TestClass() + :s( "hello" ) + {} + + void testCase() { + REQUIRE( s == "hello" ); + } +}; + + +METHOD_AS_TEST_CASE( TestClass::testCase, "Use class's method as a test case", "[class]" ) +``` + +This type of fixture is similar to [TEST_CASE_METHOD](#1-test_case_method) except in this +case it will directly use the provided class to create an object rather than a derived +class. + +### 3. `TEST_CASE_PERSISTENT_FIXTURE` + +> [Introduced](https://github.com/catchorg/Catch2/pull/2885) in Catch2 3.7.0 + +`TEST_CASE_PERSISTENT_FIXTURE` behaves in the same way as +[TEST_CASE_METHOD](#1-test_case_method) except that there will only be +one instance created throughout the entire run of a test case. To +demonstrate this have a look at the following example: + +```cpp +class ClassWithExpensiveSetup { +public: + ClassWithExpensiveSetup() { + // expensive construction + std::this_thread::sleep_for( std::chrono::seconds( 2 ) ); + } + + ~ClassWithExpensiveSetup() noexcept { + // expensive destruction + std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); + } + + int getInt() const { return 42; } +}; + +struct MyFixture { + mutable int myInt = 0; + ClassWithExpensiveSetup expensive; +}; + +TEST_CASE_PERSISTENT_FIXTURE( MyFixture, "Tests with MyFixture" ) { + + const int val = myInt++; + + SECTION( "First partial run" ) { + const auto otherValue = expensive.getInt(); + REQUIRE( val == 0 ); + REQUIRE( otherValue == 42 ); + } + + SECTION( "Second partial run" ) { REQUIRE( val == 1 ); } +} +``` + +This example demonstates two possible use-cases of this fixture type: +1. Improve test run times by reducing the amount of expensive and +redundant setup and tear-down required. +2. Reusing results from the previous partial run, in the current +partial run. + +This test case will be executed twice as there are two leaf sections. +On the first run `val` will be `0` and on the second run `val` will be +`1`. This demonstrates that we were able to use the results of the +previous partial run in subsequent partial runs. + +Additionally, we are simulating an expensive object using +`std::this_thread::sleep_for`, but real world use-cases could be: +1. Creating a D3D12/Vulkan device +2. Connecting to a database +3. Loading a file. + +The fixture object (`MyFixture`) will be constructed just before the +test case begins, and it will be destroyed just after the test case +ends. Therefore, this expensive object will only be created and +destroyed once during the execution of this test case. If we had used +`TEST_CASE_METHOD`, `MyFixture` would have been created and destroyed +twice during the execution of this test case. + +NOTE: The member function which runs the test case is `const`. Therefore +if you want to mutate any member of the fixture it must be marked as +`mutable` as shown in this example. This is to make it clear that +the initial state of the fixture is intended to mutate during the +execution of the test case. +## Templated test fixtures Catch2 also provides `TEMPLATE_TEST_CASE_METHOD` and `TEMPLATE_PRODUCT_TEST_CASE_METHOD` that can be used together @@ -93,7 +222,7 @@ _While there is an upper limit on the number of types you can specify in single `TEMPLATE_TEST_CASE_METHOD` or `TEMPLATE_PRODUCT_TEST_CASE_METHOD`, the limit is very high and should not be encountered in practice._ -## Signature-based parametrised test fixtures +## Signature-based parameterised test fixtures > [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch2 2.8.0. diff --git a/src/external/catch2/docs/tostring.md b/src/external/catch2/docs/tostring.md index adce3cc7..b99b6742 100644 --- a/src/external/catch2/docs/tostring.md +++ b/src/external/catch2/docs/tostring.md @@ -75,7 +75,7 @@ CATCH_TRANSLATE_EXCEPTION( MyType const& ex ) { Enums that already have a `<<` overload for `std::ostream` will convert to strings as expected. If you only need to convert enums to strings for test reporting purposes you can provide a `StringMaker` specialisations as any other type. -However, as a convenience, Catch provides the `REGISTER_ENUM` helper macro that will generate the `StringMaker` specialiation for you with minimal code. +However, as a convenience, Catch provides the `REGISTER_ENUM` helper macro that will generate the `StringMaker` specialisation for you with minimal code. Simply provide it the (qualified) enum name, followed by all the enum values, and you're done! E.g. diff --git a/src/external/catch2/docs/tutorial.md b/src/external/catch2/docs/tutorial.md index 342c7381..fb5a5b37 100644 --- a/src/external/catch2/docs/tutorial.md +++ b/src/external/catch2/docs/tutorial.md @@ -16,7 +16,7 @@ Ideally you should be using Catch2 through its [CMake integration](cmake-integra Catch2 also provides pkg-config files and two file (header + cpp) distribution, but this documentation will assume you are using CMake. If you are using the two file distribution instead, remember to replace -the included header with `catch_amalgamated.hpp`. +the included header with `catch_amalgamated.hpp` ([step by step instructions](migrate-v2-to-v3.md#how-to-migrate-projects-from-v2-to-v3)). ## Writing tests @@ -119,7 +119,7 @@ This is best explained through an example ([code](../examples/100-Fix-Section.cp ```c++ TEST_CASE( "vectors can be sized and resized", "[vector]" ) { - + // This setup will be done 4 times in total, once for each section std::vector v( 5 ); REQUIRE( v.size() == 5 ); @@ -152,11 +152,12 @@ TEST_CASE( "vectors can be sized and resized", "[vector]" ) { } ``` -For each `SECTION` the `TEST_CASE` is executed from the start. This means +For each `SECTION` the `TEST_CASE` is **executed from the start**. This means that each section is entered with a freshly constructed vector `v`, that we know has size 5 and capacity at least 5, because the two assertions -are also checked before the section is entered. Each run through a test -case will execute one, and only one, leaf section. +are also checked before the section is entered. This behaviour may not be +ideal for tests where setup is expensive. Each run through a test case will +execute one, and only one, leaf section. Section can also be nested, in which case the parent section can be entered multiple times, once for each leaf section. Nested sections are diff --git a/src/external/catch2/docs/why-catch.md b/src/external/catch2/docs/why-catch.md index 2c0178ca..b7367496 100644 --- a/src/external/catch2/docs/why-catch.md +++ b/src/external/catch2/docs/why-catch.md @@ -30,7 +30,7 @@ So what does Catch2 bring to the party that differentiates it from these? Apart * Output is through modular reporter objects. Basic textual and XML reporters are included. Custom reporters can easily be added. * JUnit xml output is supported for integration with third-party tools, such as CI servers. * A default main() function is provided, but you can supply your own for complete control (e.g. integration into your own test runner GUI). -* A command line parser is provided and can still be used if you choose to provided your own main() function. +* A command line parser is provided and can still be used if you choose to provide your own main() function. * Alternative assertion macro(s) report failures but don't abort the test case * Good set of facilities for floating point comparisons (`Catch::Approx` and full set of matchers) * Internal and friendly macros are isolated so name clashes can be managed @@ -41,8 +41,8 @@ So what does Catch2 bring to the party that differentiates it from these? Apart ## Who else is using Catch2? -A whole lot of people. According to the 2021 JetBrains C++ ecosystem survey, -about 11% of C++ programmers use Catch2 for unit testing, making it the +A whole lot of people. According to [the 2022 JetBrains C++ ecosystem survey](https://www.jetbrains.com/lp/devecosystem-2022/cpp/#Which-unit-testing-frameworks-do-you-regularly-use), +about 12% of C++ programmers use Catch2 for unit testing, making it the second most popular unit testing framework. You can also take a look at the (incomplete) list of [open source projects](opensource-users.md#top) diff --git a/src/external/catch2/examples/010-TestCase.cpp b/src/external/catch2/examples/010-TestCase.cpp index 7ec208d5..9e5cd8cd 100644 --- a/src/external/catch2/examples/010-TestCase.cpp +++ b/src/external/catch2/examples/010-TestCase.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 010-TestCase.cpp // And write tests in the same file: #include diff --git a/src/external/catch2/examples/020-TestCase-1.cpp b/src/external/catch2/examples/020-TestCase-1.cpp index cec55799..a9d87dbc 100644 --- a/src/external/catch2/examples/020-TestCase-1.cpp +++ b/src/external/catch2/examples/020-TestCase-1.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 020-TestCase-1.cpp #include diff --git a/src/external/catch2/examples/020-TestCase-2.cpp b/src/external/catch2/examples/020-TestCase-2.cpp index 3f5767b3..72dd0ffb 100644 --- a/src/external/catch2/examples/020-TestCase-2.cpp +++ b/src/external/catch2/examples/020-TestCase-2.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 020-TestCase-2.cpp // main() provided by Catch in file 020-TestCase-1.cpp. diff --git a/src/external/catch2/examples/030-Asn-Require-Check.cpp b/src/external/catch2/examples/030-Asn-Require-Check.cpp index 0d027ca9..62cd3cfc 100644 --- a/src/external/catch2/examples/030-Asn-Require-Check.cpp +++ b/src/external/catch2/examples/030-Asn-Require-Check.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 030-Asn-Require-Check.cpp // Catch has two natural expression assertion macro's: diff --git a/src/external/catch2/examples/100-Fix-Section.cpp b/src/external/catch2/examples/100-Fix-Section.cpp index cfbfa79f..7c8d8aa8 100644 --- a/src/external/catch2/examples/100-Fix-Section.cpp +++ b/src/external/catch2/examples/100-Fix-Section.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 100-Fix-Section.cpp // Catch has two ways to express fixtures: diff --git a/src/external/catch2/examples/110-Fix-ClassFixture.cpp b/src/external/catch2/examples/110-Fix-ClassFixture.cpp index 75c10da6..614c3797 100644 --- a/src/external/catch2/examples/110-Fix-ClassFixture.cpp +++ b/src/external/catch2/examples/110-Fix-ClassFixture.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 110-Fix-ClassFixture.cpp // Catch has two ways to express fixtures: diff --git a/src/external/catch2/examples/111-Fix-PersistentFixture.cpp b/src/external/catch2/examples/111-Fix-PersistentFixture.cpp new file mode 100644 index 00000000..2bef90ff --- /dev/null +++ b/src/external/catch2/examples/111-Fix-PersistentFixture.cpp @@ -0,0 +1,74 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +// Fixture.cpp + +// Catch2 has three ways to express fixtures: +// - Sections +// - Traditional class-based fixtures that are created and destroyed on every +// partial run +// - Traditional class-based fixtures that are created at the start of a test +// case and destroyed at the end of a test case (this file) + +// main() provided by linkage to Catch2WithMain + +#include + +#include + +class ClassWithExpensiveSetup { +public: + ClassWithExpensiveSetup() { + // Imagine some really expensive set up here. + // e.g. + // setting up a D3D12/Vulkan Device, + // connecting to a database, + // loading a file + // etc etc etc + std::this_thread::sleep_for( std::chrono::seconds( 2 ) ); + } + + ~ClassWithExpensiveSetup() noexcept { + // We can do any clean up of the expensive class in the destructor + // e.g. + // destroy D3D12/Vulkan Device, + // disconnecting from a database, + // release file handle + // etc etc etc + std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); + } + + int getInt() const { return 42; } +}; + +struct MyFixture { + + // The test case member function is const. + // Therefore we need to mark any member of the fixture + // that needs to mutate as mutable. + mutable int myInt = 0; + ClassWithExpensiveSetup expensive; +}; + +// Only one object of type MyFixture will be instantiated for the run +// of this test case even though there are two leaf sections. +// This is useful if your test case requires an object that is +// expensive to create and could be reused for each partial run of the +// test case. +TEST_CASE_PERSISTENT_FIXTURE( MyFixture, "Tests with MyFixture" ) { + + const int val = myInt++; + + SECTION( "First partial run" ) { + const auto otherValue = expensive.getInt(); + REQUIRE( val == 0 ); + REQUIRE( otherValue == 42 ); + } + + SECTION( "Second partial run" ) { REQUIRE( val == 1 ); } +} \ No newline at end of file diff --git a/src/external/catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp b/src/external/catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp index 99cdf9ab..345d53c3 100644 --- a/src/external/catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp +++ b/src/external/catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 120-Bdd-ScenarioGivenWhenThen.cpp // main() provided by linkage with Catch2WithMain diff --git a/src/external/catch2/examples/210-Evt-EventListeners.cpp b/src/external/catch2/examples/210-Evt-EventListeners.cpp index 6cedb885..d05dfaaa 100644 --- a/src/external/catch2/examples/210-Evt-EventListeners.cpp +++ b/src/external/catch2/examples/210-Evt-EventListeners.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 210-Evt-EventListeners.cpp // Contents: @@ -377,8 +385,7 @@ struct MyListener : Catch::EventListenerBase { CATCH_REGISTER_LISTENER( MyListener ) // Get rid of Wweak-tables -MyListener::~MyListener() {} - +MyListener::~MyListener() = default; // ----------------------------------------------------------------------- // 3. Test cases: diff --git a/src/external/catch2/examples/231-Cfg-OutputStreams.cpp b/src/external/catch2/examples/231-Cfg-OutputStreams.cpp index b77c1273..5aee38bc 100644 --- a/src/external/catch2/examples/231-Cfg-OutputStreams.cpp +++ b/src/external/catch2/examples/231-Cfg-OutputStreams.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 231-Cfg-OutputStreams.cpp // Show how to replace the streams with a simple custom made streambuf. @@ -14,7 +22,7 @@ class out_buff : public std::stringbuf { std::FILE* m_stream; public: out_buff(std::FILE* stream):m_stream(stream) {} - ~out_buff(); + ~out_buff() override; int sync() override { int ret = 0; for (unsigned char c : str()) { diff --git a/src/external/catch2/examples/232-Cfg-CustomMain.cpp b/src/external/catch2/examples/232-Cfg-CustomMain.cpp new file mode 100644 index 00000000..27040993 --- /dev/null +++ b/src/external/catch2/examples/232-Cfg-CustomMain.cpp @@ -0,0 +1,41 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +// 232-Cfg-CustomMain.cpp +// Show how to use custom main and add a custom option to the CLI parser + +#include + +#include + +int main(int argc, char** argv) { + Catch::Session session; // There must be exactly one instance + + int height = 0; // Some user variable you want to be able to set + + // Build a new parser on top of Catch2's + using namespace Catch::Clara; + auto cli + = session.cli() // Get Catch2's command line parser + | Opt( height, "height" ) // bind variable to a new option, with a hint string + ["--height"] // the option names it will respond to + ("how high?"); // description string for the help output + + // Now pass the new composite back to Catch2 so it uses that + session.cli( cli ); + + // Let Catch2 (using Clara) parse the command line + int returnCode = session.applyCommandLine( argc, argv ); + if( returnCode != 0 ) // Indicates a command line error + return returnCode; + + // if set on the command line then 'height' is now set at this point + std::cout << "height: " << height << '\n'; + + return session.run(); +} diff --git a/src/external/catch2/examples/300-Gen-OwnGenerator.cpp b/src/external/catch2/examples/300-Gen-OwnGenerator.cpp index 09643d6f..9cb02e39 100644 --- a/src/external/catch2/examples/300-Gen-OwnGenerator.cpp +++ b/src/external/catch2/examples/300-Gen-OwnGenerator.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 300-Gen-OwnGenerator.cpp // Shows how to define a custom generator. @@ -13,7 +21,7 @@ namespace { // This class shows how to implement a simple generator for Catch tests -class RandomIntGenerator : public Catch::Generators::IGenerator { +class RandomIntGenerator final : public Catch::Generators::IGenerator { std::minstd_rand m_rand; std::uniform_int_distribution<> m_dist; int current_number; diff --git a/src/external/catch2/examples/301-Gen-MapTypeConversion.cpp b/src/external/catch2/examples/301-Gen-MapTypeConversion.cpp index ba55f65f..0a284483 100644 --- a/src/external/catch2/examples/301-Gen-MapTypeConversion.cpp +++ b/src/external/catch2/examples/301-Gen-MapTypeConversion.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 301-Gen-MapTypeConversion.cpp // Shows how to use map to modify generator's return type. @@ -16,12 +24,12 @@ namespace { // Returns a line from a stream. You could have it e.g. read lines from // a file, but to avoid problems with paths in examples, we will use // a fixed stringstream. -class LineGenerator : public Catch::Generators::IGenerator { +class LineGenerator final : public Catch::Generators::IGenerator { std::string m_line; std::stringstream m_stream; public: - LineGenerator() { - m_stream.str("1\n2\n3\n4\n"); + explicit LineGenerator( std::string const& lines ) { + m_stream.str( lines ); if (!next()) { Catch::Generators::Detail::throw_generator_exception("Couldn't read a single line"); } @@ -41,18 +49,19 @@ std::string const& LineGenerator::get() const { // This helper function provides a nicer UX when instantiating the generator // Notice that it returns an instance of GeneratorWrapper, which // is a value-wrapper around std::unique_ptr>. -Catch::Generators::GeneratorWrapper lines(std::string /* ignored for example */) { +Catch::Generators::GeneratorWrapper +lines( std::string const& lines ) { return Catch::Generators::GeneratorWrapper( - new LineGenerator() - ); + new LineGenerator( lines ) ); } } // end anonymous namespace TEST_CASE("filter can convert types inside the generator expression", "[example][generator]") { - auto num = GENERATE(map([](std::string const& line) { return std::stoi(line); }, - lines("fake-file"))); + auto num = GENERATE( + map( []( std::string const& line ) { return std::stoi( line ); }, + lines( "1\n2\n3\n4\n" ) ) ); REQUIRE(num > 0); } diff --git a/src/external/catch2/examples/302-Gen-Table.cpp b/src/external/catch2/examples/302-Gen-Table.cpp index 74319518..3cdb1430 100644 --- a/src/external/catch2/examples/302-Gen-Table.cpp +++ b/src/external/catch2/examples/302-Gen-Table.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 302-Gen-Table.cpp // Shows how to use table to run a test many times with different inputs. Lifted from examples on // issue #850. @@ -44,11 +52,11 @@ TEST_CASE("Table allows pre-computed test inputs and outputs", "[example][genera /* Possible simplifications where less legacy toolchain support is needed: * - * - With libstdc++6 or newer, the make_tuple() calls can be ommitted + * - With libstdc++6 or newer, the make_tuple() calls can be omitted * (technically C++17 but does not require -std in GCC/Clang). See * https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list * - * - In C++17 mode std::tie() and the preceding variable delcarations can be + * - In C++17 mode std::tie() and the preceding variable declarations can be * replaced by structured bindings: auto [test_input, expected] = GENERATE( * table({ ... */ diff --git a/src/external/catch2/examples/310-Gen-VariablesInGenerators.cpp b/src/external/catch2/examples/310-Gen-VariablesInGenerators.cpp index 0339c5f1..5d24d45a 100644 --- a/src/external/catch2/examples/310-Gen-VariablesInGenerators.cpp +++ b/src/external/catch2/examples/310-Gen-VariablesInGenerators.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 310-Gen-VariablesInGenerator.cpp // Shows how to use variables when creating generators. diff --git a/src/external/catch2/examples/311-Gen-CustomCapture.cpp b/src/external/catch2/examples/311-Gen-CustomCapture.cpp index d12ee709..ee310383 100644 --- a/src/external/catch2/examples/311-Gen-CustomCapture.cpp +++ b/src/external/catch2/examples/311-Gen-CustomCapture.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + // 311-Gen-CustomCapture.cpp // Shows how to provide custom capture list to the generator expression diff --git a/src/external/catch2/examples/CMakeLists.txt b/src/external/catch2/examples/CMakeLists.txt index f9933341..4647df1d 100644 --- a/src/external/catch2/examples/CMakeLists.txt +++ b/src/external/catch2/examples/CMakeLists.txt @@ -28,8 +28,10 @@ set( SOURCES_IDIOMATIC_EXAMPLES 030-Asn-Require-Check.cpp 100-Fix-Section.cpp 110-Fix-ClassFixture.cpp + 111-Fix-PersistentFixture.cpp 120-Bdd-ScenarioGivenWhenThen.cpp 210-Evt-EventListeners.cpp + 232-Cfg-CustomMain.cpp 300-Gen-OwnGenerator.cpp 301-Gen-MapTypeConversion.cpp 302-Gen-Table.cpp @@ -42,8 +44,7 @@ set( TARGETS_IDIOMATIC_EXAMPLES ${BASENAMES_IDIOMATIC_EXAMPLES} ) foreach( name ${TARGETS_IDIOMATIC_EXAMPLES} ) - add_executable( ${name} - ${EXAMPLES_DIR}/${name}.cpp ) + add_executable( ${name} ${name}.cpp ) endforeach() set(ALL_EXAMPLE_TARGETS @@ -53,7 +54,7 @@ set(ALL_EXAMPLE_TARGETS ) foreach( name ${ALL_EXAMPLE_TARGETS} ) - target_link_libraries( ${name} Catch2 Catch2WithMain ) + target_link_libraries( ${name} Catch2WithMain ) endforeach() diff --git a/src/external/catch2/extras/Catch.cmake b/src/external/catch2/extras/Catch.cmake index bc553591..c1712885 100644 --- a/src/external/catch2/extras/Catch.cmake +++ b/src/external/catch2/extras/Catch.cmake @@ -35,8 +35,10 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``. [TEST_LIST var] [REPORTER reporter] [OUTPUT_DIR dir] - [OUTPUT_PREFIX prefix} + [OUTPUT_PREFIX prefix] [OUTPUT_SUFFIX suffix] + [DISCOVERY_MODE ] + [SKIP_IS_FAILURE] ) ``catch_discover_tests`` sets up a post-build command on the test executable @@ -123,15 +125,39 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``. test executable and when the tests are executed themselves. This requires cmake/ctest >= 3.22. + ``DL_FRAMEWORK_PATHS path...`` + Specifies paths that need to be set for the dynamic linker to find libraries + packaged as frameworks on Apple platforms when running the test executable + (DYLD_FRAMEWORK_PATH). These paths will both be set when retrieving the list + of test cases from the test executable and when the tests are executed themselves. + This requires cmake/ctest >= 3.22. + + ``DISCOVERY_MODE mode`` + Provides control over when ``catch_discover_tests`` performs test discovery. + By default, ``POST_BUILD`` sets up a post-build command to perform test discovery + at build time. In certain scenarios, like cross-compiling, this ``POST_BUILD`` + behavior is not desirable. By contrast, ``PRE_TEST`` delays test discovery until + just prior to test execution. This way test discovery occurs in the target environment + where the test has a better chance at finding appropriate runtime dependencies. + + ``DISCOVERY_MODE`` defaults to the value of the + ``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when + calling ``catch_discover_tests``. This provides a mechanism for globally selecting + a preferred test discovery behavior without having to modify each call site. + + ``SKIP_IS_FAILURE`` + Disables skipped test detection. + #]=======================================================================] #------------------------------------------------------------------------------ function(catch_discover_tests TARGET) + cmake_parse_arguments( "" - "" - "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX" - "TEST_SPEC;EXTRA_ARGS;PROPERTIES;DL_PATHS" + "SKIP_IS_FAILURE" + "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX;DISCOVERY_MODE" + "TEST_SPEC;EXTRA_ARGS;PROPERTIES;DL_PATHS;DL_FRAMEWORK_PATHS" ${ARGN} ) @@ -141,11 +167,20 @@ function(catch_discover_tests TARGET) if(NOT _TEST_LIST) set(_TEST_LIST ${TARGET}_TESTS) endif() - - if (_DL_PATHS) - if(${CMAKE_VERSION} VERSION_LESS "3.22.0") - message(FATAL_ERROR "The DL_PATHS option requires at least cmake 3.22") + if(_DL_PATHS AND ${CMAKE_VERSION} VERSION_LESS "3.22.0") + message(FATAL_ERROR "The DL_PATHS option requires at least cmake 3.22") + endif() + if(_DL_FRAMEWORK_PATHS AND ${CMAKE_VERSION} VERSION_LESS "3.22.0") + message(FATAL_ERROR "The DL_FRAMEWORK_PATHS option requires at least cmake 3.22") + endif() + if(NOT _DISCOVERY_MODE) + if(NOT CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE) + set(CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD") endif() + set(_DISCOVERY_MODE ${CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE}) + endif() + if (NOT _DISCOVERY_MODE MATCHES "^(POST_BUILD|PRE_TEST)$") + message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}") endif() ## Generate a unique name based on the extra arguments @@ -153,45 +188,113 @@ function(catch_discover_tests TARGET) string(SUBSTRING ${args_hash} 0 7 args_hash) # Define rule to generate test list for aforementioned test executable - set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake") - set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake") + set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-${args_hash}") + set(ctest_include_file "${ctest_file_base}_include.cmake") + set(ctest_tests_file "${ctest_file_base}_tests.cmake") + get_property(crosscompiling_emulator TARGET ${TARGET} PROPERTY CROSSCOMPILING_EMULATOR ) - add_custom_command( - TARGET ${TARGET} POST_BUILD - BYPRODUCTS "${ctest_tests_file}" - COMMAND "${CMAKE_COMMAND}" - -D "TEST_TARGET=${TARGET}" - -D "TEST_EXECUTABLE=$" - -D "TEST_EXECUTOR=${crosscompiling_emulator}" - -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" - -D "TEST_SPEC=${_TEST_SPEC}" - -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" - -D "TEST_PROPERTIES=${_PROPERTIES}" - -D "TEST_PREFIX=${_TEST_PREFIX}" - -D "TEST_SUFFIX=${_TEST_SUFFIX}" - -D "TEST_LIST=${_TEST_LIST}" - -D "TEST_REPORTER=${_REPORTER}" - -D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}" - -D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}" - -D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}" - -D "TEST_DL_PATHS=${_DL_PATHS}" - -D "CTEST_FILE=${ctest_tests_file}" - -P "${_CATCH_DISCOVER_TESTS_SCRIPT}" - VERBATIM - ) + if (NOT _SKIP_IS_FAILURE) + set(_PROPERTIES ${_PROPERTIES} SKIP_RETURN_CODE 4) + endif() - file(WRITE "${ctest_include_file}" - "if(EXISTS \"${ctest_tests_file}\")\n" - " include(\"${ctest_tests_file}\")\n" - "else()\n" - " add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n" - "endif()\n" - ) + if(_DISCOVERY_MODE STREQUAL "POST_BUILD") + add_custom_command( + TARGET ${TARGET} POST_BUILD + BYPRODUCTS "${ctest_tests_file}" + COMMAND "${CMAKE_COMMAND}" + -D "TEST_TARGET=${TARGET}" + -D "TEST_EXECUTABLE=$" + -D "TEST_EXECUTOR=${crosscompiling_emulator}" + -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" + -D "TEST_SPEC=${_TEST_SPEC}" + -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" + -D "TEST_PROPERTIES=${_PROPERTIES}" + -D "TEST_PREFIX=${_TEST_PREFIX}" + -D "TEST_SUFFIX=${_TEST_SUFFIX}" + -D "TEST_LIST=${_TEST_LIST}" + -D "TEST_REPORTER=${_REPORTER}" + -D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}" + -D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}" + -D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}" + -D "TEST_DL_PATHS=${_DL_PATHS}" + -D "TEST_DL_FRAMEWORK_PATHS=${_DL_FRAMEWORK_PATHS}" + -D "CTEST_FILE=${ctest_tests_file}" + -P "${_CATCH_DISCOVER_TESTS_SCRIPT}" + VERBATIM + ) + + file(WRITE "${ctest_include_file}" + "if(EXISTS \"${ctest_tests_file}\")\n" + " include(\"${ctest_tests_file}\")\n" + "else()\n" + " add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n" + "endif()\n" + ) - if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") + elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST") + + get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL + PROPERTY GENERATOR_IS_MULTI_CONFIG + ) + + if(GENERATOR_IS_MULTI_CONFIG) + set(ctest_tests_file "${ctest_file_base}_tests-$.cmake") + endif() + + string(CONCAT ctest_include_content + "if(EXISTS \"$\")" "\n" + " if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n" + " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$\" OR\n" + " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n" + " include(\"${_CATCH_DISCOVER_TESTS_SCRIPT}\")" "\n" + " catch_discover_tests_impl(" "\n" + " TEST_EXECUTABLE" " [==[" "$" "]==]" "\n" + " TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n" + " TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n" + " TEST_SPEC" " [==[" "${_TEST_SPEC}" "]==]" "\n" + " TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n" + " TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n" + " TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n" + " TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n" + " TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n" + " TEST_REPORTER" " [==[" "${_REPORTER}" "]==]" "\n" + " TEST_OUTPUT_DIR" " [==[" "${_OUTPUT_DIR}" "]==]" "\n" + " TEST_OUTPUT_PREFIX" " [==[" "${_OUTPUT_PREFIX}" "]==]" "\n" + " TEST_OUTPUT_SUFFIX" " [==[" "${_OUTPUT_SUFFIX}" "]==]" "\n" + " CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n" + " TEST_DL_PATHS" " [==[" "${_DL_PATHS}" "]==]" "\n" + " TEST_DL_FRAMEWORK_PATHS" " [==[" "${_DL_FRAMEWORK_PATHS}" "]==]" "\n" + " CTEST_FILE" " [==[" "${CTEST_FILE}" "]==]" "\n" + " )" "\n" + " endif()" "\n" + " include(\"${ctest_tests_file}\")" "\n" + "else()" "\n" + " add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n" + "endif()" "\n" + ) + + if(GENERATOR_IS_MULTI_CONFIG) + foreach(_config ${CMAKE_CONFIGURATION_TYPES}) + file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $) + endforeach() + string(CONCAT ctest_include_multi_content + "if(NOT CTEST_CONFIGURATION_TYPE)" "\n" + " message(\"No configuration for testing specified, use '-C '.\")" "\n" + "else()" "\n" + " include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")" "\n" + "endif()" "\n" + ) + file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_multi_content}") + else() + file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}") + file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")") + endif() + endif() + + if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") # Add discovered tests to directory TEST_INCLUDE_FILES set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" @@ -204,9 +307,7 @@ function(catch_discover_tests TARGET) PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}" ) else() - message(FATAL_ERROR - "Cannot set more than one TEST_INCLUDE_FILE" - ) + message(FATAL_ERROR "Cannot set more than one TEST_INCLUDE_FILE") endif() endif() diff --git a/src/external/catch2/extras/CatchAddTests.cmake b/src/external/catch2/extras/CatchAddTests.cmake index beec3aed..399a839d 100644 --- a/src/external/catch2/extras/CatchAddTests.cmake +++ b/src/external/catch2/extras/CatchAddTests.cmake @@ -1,28 +1,6 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. -set(prefix "${TEST_PREFIX}") -set(suffix "${TEST_SUFFIX}") -set(spec ${TEST_SPEC}) -set(extra_args ${TEST_EXTRA_ARGS}) -set(properties ${TEST_PROPERTIES}) -set(reporter ${TEST_REPORTER}) -set(output_dir ${TEST_OUTPUT_DIR}) -set(output_prefix ${TEST_OUTPUT_PREFIX}) -set(output_suffix ${TEST_OUTPUT_SUFFIX}) -set(dl_paths ${TEST_DL_PATHS}) -set(script) -set(suite) -set(tests) - -if(WIN32) - set(dl_paths_variable_name PATH) -elseif(APPLE) - set(dl_paths_variable_name DYLD_LIBRARY_PATH) -else() - set(dl_paths_variable_name LD_LIBRARY_PATH) -endif() - function(add_command NAME) set(_args "") # use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments @@ -38,119 +16,196 @@ function(add_command NAME) set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE) endfunction() -# Run test executable to get list of available tests -if(NOT EXISTS "${TEST_EXECUTABLE}") - message(FATAL_ERROR - "Specified test executable '${TEST_EXECUTABLE}' does not exist" +function(catch_discover_tests_impl) + + cmake_parse_arguments( + "" + "" + "TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_OUTPUT_DIR;TEST_OUTPUT_PREFIX;TEST_OUTPUT_SUFFIX;TEST_PREFIX;TEST_REPORTER;TEST_SPEC;TEST_SUFFIX;TEST_LIST;CTEST_FILE" + "TEST_EXTRA_ARGS;TEST_PROPERTIES;TEST_EXECUTOR;TEST_DL_PATHS;TEST_DL_FRAMEWORK_PATHS" + ${ARGN} ) -endif() -if(dl_paths) - cmake_path(CONVERT "${dl_paths}" TO_NATIVE_PATH_LIST paths) - set(ENV{${dl_paths_variable_name}} "${paths}") -endif() + set(prefix "${_TEST_PREFIX}") + set(suffix "${_TEST_SUFFIX}") + set(spec ${_TEST_SPEC}) + set(extra_args ${_TEST_EXTRA_ARGS}) + set(properties ${_TEST_PROPERTIES}) + set(reporter ${_TEST_REPORTER}) + set(output_dir ${_TEST_OUTPUT_DIR}) + set(output_prefix ${_TEST_OUTPUT_PREFIX}) + set(output_suffix ${_TEST_OUTPUT_SUFFIX}) + set(dl_paths ${_TEST_DL_PATHS}) + set(dl_framework_paths ${_TEST_DL_FRAMEWORK_PATHS}) + set(environment_modifications "") + set(script) + set(suite) + set(tests) + + if(WIN32) + set(dl_paths_variable_name PATH) + elseif(APPLE) + set(dl_paths_variable_name DYLD_LIBRARY_PATH) + else() + set(dl_paths_variable_name LD_LIBRARY_PATH) + endif() -execute_process( - COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-tests --verbosity quiet - OUTPUT_VARIABLE output - RESULT_VARIABLE result - WORKING_DIRECTORY "${TEST_WORKING_DIR}" -) -if(NOT ${result} EQUAL 0) - message(FATAL_ERROR - "Error running test executable '${TEST_EXECUTABLE}':\n" - " Result: ${result}\n" - " Output: ${output}\n" - ) -endif() + # Run test executable to get list of available tests + if(NOT EXISTS "${_TEST_EXECUTABLE}") + message(FATAL_ERROR + "Specified test executable '${_TEST_EXECUTABLE}' does not exist" + ) + endif() -string(REPLACE "\n" ";" output "${output}") - -# Run test executable to get list of available reporters -execute_process( - COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-reporters - OUTPUT_VARIABLE reporters_output - RESULT_VARIABLE reporters_result - WORKING_DIRECTORY "${TEST_WORKING_DIR}" -) -if(NOT ${reporters_result} EQUAL 0) - message(FATAL_ERROR - "Error running test executable '${TEST_EXECUTABLE}':\n" - " Result: ${reporters_result}\n" - " Output: ${reporters_output}\n" - ) -endif() -string(FIND "${reporters_output}" "${reporter}" reporter_is_valid) -if(reporter AND ${reporter_is_valid} EQUAL -1) - message(FATAL_ERROR - "\"${reporter}\" is not a valid reporter!\n" + if(dl_paths) + cmake_path(CONVERT "$ENV{${dl_paths_variable_name}}" TO_NATIVE_PATH_LIST env_dl_paths) + list(PREPEND env_dl_paths "${dl_paths}") + cmake_path(CONVERT "${env_dl_paths}" TO_NATIVE_PATH_LIST paths) + set(ENV{${dl_paths_variable_name}} "${paths}") + endif() + + if(APPLE AND dl_framework_paths) + cmake_path(CONVERT "$ENV{DYLD_FRAMEWORK_PATH}" TO_NATIVE_PATH_LIST env_dl_framework_paths) + list(PREPEND env_dl_framework_paths "${dl_framework_paths}") + cmake_path(CONVERT "${env_dl_framework_paths}" TO_NATIVE_PATH_LIST paths) + set(ENV{DYLD_FRAMEWORK_PATH} "${paths}") + endif() + + execute_process( + COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} --list-tests --verbosity quiet + OUTPUT_VARIABLE output + RESULT_VARIABLE result + WORKING_DIRECTORY "${_TEST_WORKING_DIR}" ) -endif() + if(NOT ${result} EQUAL 0) + message(FATAL_ERROR + "Error running test executable '${_TEST_EXECUTABLE}':\n" + " Result: ${result}\n" + " Output: ${output}\n" + ) + endif() -# Prepare reporter -if(reporter) - set(reporter_arg "--reporter ${reporter}") -endif() + # Make sure to escape ; (semicolons) in test names first, because + # that'd break the foreach loop for "Parse output" later and create + # wrongly splitted and thus failing test cases (false positives) + string(REPLACE ";" "\;" output "${output}") + string(REPLACE "\n" ";" output "${output}") + + # Prepare reporter + if(reporter) + set(reporter_arg "--reporter ${reporter}") + + # Run test executable to check whether reporter is available + # note that the use of --list-reporters is not the important part, + # we only want to check whether the execution succeeds with ${reporter_arg} + execute_process( + COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} ${reporter_arg} --list-reporters + OUTPUT_VARIABLE reporter_check_output + RESULT_VARIABLE reporter_check_result + WORKING_DIRECTORY "${_TEST_WORKING_DIR}" + ) + if(${reporter_check_result} EQUAL 255) + message(FATAL_ERROR + "\"${reporter}\" is not a valid reporter!\n" + ) + elseif(NOT ${reporter_check_result} EQUAL 0) + message(FATAL_ERROR + "Error running test executable '${_TEST_EXECUTABLE}':\n" + " Result: ${reporter_check_result}\n" + " Output: ${reporter_check_output}\n" + ) + endif() + endif() -# Prepare output dir -if(output_dir AND NOT IS_ABSOLUTE ${output_dir}) - set(output_dir "${TEST_WORKING_DIR}/${output_dir}") - if(NOT EXISTS ${output_dir}) - file(MAKE_DIRECTORY ${output_dir}) + # Prepare output dir + if(output_dir AND NOT IS_ABSOLUTE ${output_dir}) + set(output_dir "${_TEST_WORKING_DIR}/${output_dir}") + if(NOT EXISTS ${output_dir}) + file(MAKE_DIRECTORY ${output_dir}) + endif() endif() -endif() -if(dl_paths) - foreach(path ${dl_paths}) - cmake_path(NATIVE_PATH path native_path) - list(APPEND environment_modifications "${dl_paths_variable_name}=path_list_prepend:${native_path}") - endforeach() -endif() + if(dl_paths) + foreach(path ${dl_paths}) + cmake_path(NATIVE_PATH path native_path) + list(PREPEND environment_modifications "${dl_paths_variable_name}=path_list_prepend:${native_path}") + endforeach() + endif() -# Parse output -foreach(line ${output}) - set(test ${line}) - # Escape characters in test case names that would be parsed by Catch2 - set(test_name ${test}) - foreach(char , [ ]) - string(REPLACE ${char} "\\${char}" test_name ${test_name}) - endforeach(char) - # ...add output dir - if(output_dir) - string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean ${test_name}) - set(output_dir_arg "--out ${output_dir}/${output_prefix}${test_name_clean}${output_suffix}") + if(APPLE AND dl_framework_paths) + foreach(path ${dl_framework_paths}) + cmake_path(NATIVE_PATH path native_path) + list(PREPEND environment_modifications "DYLD_FRAMEWORK_PATH=path_list_prepend:${native_path}") + endforeach() endif() - - # ...and add to script - add_command(add_test - "${prefix}${test}${suffix}" - ${TEST_EXECUTOR} - "${TEST_EXECUTABLE}" - "${test_name}" - ${extra_args} - "${reporter_arg}" - "${output_dir_arg}" - ) - add_command(set_tests_properties - "${prefix}${test}${suffix}" - PROPERTIES - WORKING_DIRECTORY "${TEST_WORKING_DIR}" - ${properties} - ) - if(environment_modifications) - add_command(set_tests_properties - "${prefix}${test}${suffix}" - PROPERTIES - ENVIRONMENT_MODIFICATION "${environment_modifications}") - endif() + # Parse output + foreach(line ${output}) + set(test "${line}") + # Escape characters in test case names that would be parsed by Catch2 + # Note that the \ escaping must happen FIRST! Do not change the order. + set(test_name "${test}") + foreach(char \\ , [ ]) + string(REPLACE ${char} "\\${char}" test_name "${test_name}") + endforeach(char) + # ...add output dir + if(output_dir) + string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean "${test_name}") + set(output_dir_arg "--out ${output_dir}/${output_prefix}${test_name_clean}${output_suffix}") + endif() + + # ...and add to script + add_command(add_test + "${prefix}${test}${suffix}" + ${_TEST_EXECUTOR} + "${_TEST_EXECUTABLE}" + "${test_name}" + ${extra_args} + "${reporter_arg}" + "${output_dir_arg}" + ) + add_command(set_tests_properties + "${prefix}${test}${suffix}" + PROPERTIES + WORKING_DIRECTORY "${_TEST_WORKING_DIR}" + ${properties} + ) + + if(environment_modifications) + add_command(set_tests_properties + "${prefix}${test}${suffix}" + PROPERTIES + ENVIRONMENT_MODIFICATION "${environment_modifications}") + endif() + + list(APPEND tests "${prefix}${test}${suffix}") + endforeach() - list(APPEND tests "${prefix}${test}${suffix}") -endforeach() + # Create a list of all discovered tests, which users may use to e.g. set + # properties on the tests + add_command(set ${_TEST_LIST} ${tests}) -# Create a list of all discovered tests, which users may use to e.g. set -# properties on the tests -add_command(set ${TEST_LIST} ${tests}) + # Write CTest script + file(WRITE "${_CTEST_FILE}" "${script}") +endfunction() -# Write CTest script -file(WRITE "${CTEST_FILE}" "${script}") +if(CMAKE_SCRIPT_MODE_FILE) + catch_discover_tests_impl( + TEST_EXECUTABLE ${TEST_EXECUTABLE} + TEST_EXECUTOR ${TEST_EXECUTOR} + TEST_WORKING_DIR ${TEST_WORKING_DIR} + TEST_SPEC ${TEST_SPEC} + TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS} + TEST_PROPERTIES ${TEST_PROPERTIES} + TEST_PREFIX ${TEST_PREFIX} + TEST_SUFFIX ${TEST_SUFFIX} + TEST_LIST ${TEST_LIST} + TEST_REPORTER ${TEST_REPORTER} + TEST_OUTPUT_DIR ${TEST_OUTPUT_DIR} + TEST_OUTPUT_PREFIX ${TEST_OUTPUT_PREFIX} + TEST_OUTPUT_SUFFIX ${TEST_OUTPUT_SUFFIX} + TEST_DL_PATHS ${TEST_DL_PATHS} + TEST_DL_FRAMEWORK_PATHS ${TEST_DL_FRAMEWORK_PATHS} + CTEST_FILE ${CTEST_FILE} + ) +endif() diff --git a/src/external/catch2/extras/CatchShardTests.cmake b/src/external/catch2/extras/CatchShardTests.cmake index 5e043cf0..68228f5a 100644 --- a/src/external/catch2/extras/CatchShardTests.cmake +++ b/src/external/catch2/extras/CatchShardTests.cmake @@ -46,7 +46,7 @@ function(catch_add_sharded_tests TARGET) APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" ) - set(shard_impl_script_file "${CMAKE_CURRENT_LIST_DIR}/CatchShardTestsImpl.cmake") + set(shard_impl_script_file "${_CATCH_DISCOVER_SHARD_TESTS_IMPL_SCRIPT}") add_custom_command( TARGET ${TARGET} POST_BUILD @@ -64,3 +64,11 @@ function(catch_add_sharded_tests TARGET) endfunction() + + +############################################################################### + +set(_CATCH_DISCOVER_SHARD_TESTS_IMPL_SCRIPT + ${CMAKE_CURRENT_LIST_DIR}/CatchShardTestsImpl.cmake + CACHE INTERNAL "Catch2 full path to CatchShardTestsImpl.cmake helper file" +) diff --git a/src/external/catch2/extras/ParseAndAddCatchTests.cmake b/src/external/catch2/extras/ParseAndAddCatchTests.cmake index 4771e029..31fc193a 100644 --- a/src/external/catch2/extras/ParseAndAddCatchTests.cmake +++ b/src/external/catch2/extras/ParseAndAddCatchTests.cmake @@ -187,7 +187,7 @@ function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget) if(result) set(HiddenTagFound ON) break() - endif(result) + endif() endforeach(label) if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_LESS "3.9") ParseAndAddCatchTests_PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label") diff --git a/src/external/catch2/extras/catch_amalgamated.cpp b/src/external/catch2/extras/catch_amalgamated.cpp index a81b1b6a..f45c18a0 100644 --- a/src/external/catch2/extras/catch_amalgamated.cpp +++ b/src/external/catch2/extras/catch_amalgamated.cpp @@ -1,3 +1,4 @@ + // Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at @@ -5,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.3.2 -// Generated: 2023-02-26 10:28:48.270752 +// Catch v3.7.1 +// Generated: 2024-09-17 10:36:45.608896 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -48,18 +49,99 @@ namespace Catch { } // namespace Catch +// Adapted from donated nonius code. + + +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last) { + if (!cfg.benchmarkNoAnalysis()) { + std::vector samples; + samples.reserve(static_cast(last - first)); + for (auto current = first; current != last; ++current) { + samples.push_back( current->count() ); + } + + auto analysis = Catch::Benchmark::Detail::analyse_samples( + cfg.benchmarkConfidenceInterval(), + cfg.benchmarkResamples(), + samples.data(), + samples.data() + samples.size() ); + auto outliers = Catch::Benchmark::Detail::classify_outliers( + samples.data(), samples.data() + samples.size() ); + + auto wrap_estimate = [](Estimate e) { + return Estimate { + FDuration(e.point), + FDuration(e.lower_bound), + FDuration(e.upper_bound), + e.confidence_interval, + }; + }; + std::vector samples2; + samples2.reserve(samples.size()); + for (auto s : samples) { + samples2.push_back( FDuration( s ) ); + } + + return { + CATCH_MOVE(samples2), + wrap_estimate(analysis.mean), + wrap_estimate(analysis.standard_deviation), + outliers, + analysis.outlier_variance, + }; + } else { + std::vector samples; + samples.reserve(static_cast(last - first)); + + FDuration mean = FDuration(0); + int i = 0; + for (auto it = first; it < last; ++it, ++i) { + samples.push_back(*it); + mean += *it; + } + mean /= i; + + return SampleAnalysis{ + CATCH_MOVE(samples), + Estimate{ mean, mean, mean, 0.0 }, + Estimate{ FDuration( 0 ), + FDuration( 0 ), + FDuration( 0 ), + 0.0 }, + OutlierClassification{}, + 0.0 + }; + } + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + + namespace Catch { namespace Benchmark { namespace Detail { + struct do_nothing { + void operator()() const {} + }; + BenchmarkFunction::callable::~callable() = default; + BenchmarkFunction::BenchmarkFunction(): + f( new model{ {} } ){} } // namespace Detail } // namespace Benchmark } // namespace Catch + #include namespace Catch { @@ -86,9 +168,11 @@ namespace Catch { +#include #include +#include #include -#include +#include #include @@ -96,139 +180,199 @@ namespace Catch { #include #endif -namespace { +namespace Catch { + namespace Benchmark { + namespace Detail { + namespace { + + template + static sample + resample( URng& rng, + unsigned int resamples, + double const* first, + double const* last, + Estimator& estimator ) { + auto n = static_cast( last - first ); + Catch::uniform_integer_distribution dist( 0, n - 1 ); + + sample out; + out.reserve( resamples ); + std::vector resampled; + resampled.reserve( n ); + for ( size_t i = 0; i < resamples; ++i ) { + resampled.clear(); + for ( size_t s = 0; s < n; ++s ) { + resampled.push_back( first[dist( rng )] ); + } + const auto estimate = + estimator( resampled.data(), resampled.data() + resampled.size() ); + out.push_back( estimate ); + } + std::sort( out.begin(), out.end() ); + return out; + } -using Catch::Benchmark::Detail::sample; - - template - sample resample(URng& rng, unsigned int resamples, std::vector::iterator first, std::vector::iterator last, Estimator& estimator) { - auto n = static_cast(last - first); - std::uniform_int_distribution dist(0, n - 1); - - sample out; - out.reserve(resamples); - std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] { - std::vector resampled; - resampled.reserve(n); - std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[static_cast(dist(rng))]; }); - return estimator(resampled.begin(), resampled.end()); - }); - std::sort(out.begin(), out.end()); - return out; - } - - - double erf_inv(double x) { - // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2 - double w, p; - - w = -log((1.0 - x) * (1.0 + x)); - - if (w < 6.250000) { - w = w - 3.125000; - p = -3.6444120640178196996e-21; - p = -1.685059138182016589e-19 + p * w; - p = 1.2858480715256400167e-18 + p * w; - p = 1.115787767802518096e-17 + p * w; - p = -1.333171662854620906e-16 + p * w; - p = 2.0972767875968561637e-17 + p * w; - p = 6.6376381343583238325e-15 + p * w; - p = -4.0545662729752068639e-14 + p * w; - p = -8.1519341976054721522e-14 + p * w; - p = 2.6335093153082322977e-12 + p * w; - p = -1.2975133253453532498e-11 + p * w; - p = -5.4154120542946279317e-11 + p * w; - p = 1.051212273321532285e-09 + p * w; - p = -4.1126339803469836976e-09 + p * w; - p = -2.9070369957882005086e-08 + p * w; - p = 4.2347877827932403518e-07 + p * w; - p = -1.3654692000834678645e-06 + p * w; - p = -1.3882523362786468719e-05 + p * w; - p = 0.0001867342080340571352 + p * w; - p = -0.00074070253416626697512 + p * w; - p = -0.0060336708714301490533 + p * w; - p = 0.24015818242558961693 + p * w; - p = 1.6536545626831027356 + p * w; - } else if (w < 16.000000) { - w = sqrt(w) - 3.250000; - p = 2.2137376921775787049e-09; - p = 9.0756561938885390979e-08 + p * w; - p = -2.7517406297064545428e-07 + p * w; - p = 1.8239629214389227755e-08 + p * w; - p = 1.5027403968909827627e-06 + p * w; - p = -4.013867526981545969e-06 + p * w; - p = 2.9234449089955446044e-06 + p * w; - p = 1.2475304481671778723e-05 + p * w; - p = -4.7318229009055733981e-05 + p * w; - p = 6.8284851459573175448e-05 + p * w; - p = 2.4031110387097893999e-05 + p * w; - p = -0.0003550375203628474796 + p * w; - p = 0.00095328937973738049703 + p * w; - p = -0.0016882755560235047313 + p * w; - p = 0.0024914420961078508066 + p * w; - p = -0.0037512085075692412107 + p * w; - p = 0.005370914553590063617 + p * w; - p = 1.0052589676941592334 + p * w; - p = 3.0838856104922207635 + p * w; - } else { - w = sqrt(w) - 5.000000; - p = -2.7109920616438573243e-11; - p = -2.5556418169965252055e-10 + p * w; - p = 1.5076572693500548083e-09 + p * w; - p = -3.7894654401267369937e-09 + p * w; - p = 7.6157012080783393804e-09 + p * w; - p = -1.4960026627149240478e-08 + p * w; - p = 2.9147953450901080826e-08 + p * w; - p = -6.7711997758452339498e-08 + p * w; - p = 2.2900482228026654717e-07 + p * w; - p = -9.9298272942317002539e-07 + p * w; - p = 4.5260625972231537039e-06 + p * w; - p = -1.9681778105531670567e-05 + p * w; - p = 7.5995277030017761139e-05 + p * w; - p = -0.00021503011930044477347 + p * w; - p = -0.00013871931833623122026 + p * w; - p = 1.0103004648645343977 + p * w; - p = 4.8499064014085844221 + p * w; - } - return p * x; - } - - double standard_deviation(std::vector::iterator first, std::vector::iterator last) { - auto m = Catch::Benchmark::Detail::mean(first, last); - double variance = std::accumulate( first, - last, - 0., - [m]( double a, double b ) { - double diff = b - m; - return a + diff * diff; - } ) / - ( last - first ); - return std::sqrt( variance ); - } + static double outlier_variance( Estimate mean, + Estimate stddev, + int n ) { + double sb = stddev.point; + double mn = mean.point / n; + double mg_min = mn / 2.; + double sg = (std::min)( mg_min / 4., sb / std::sqrt( n ) ); + double sg2 = sg * sg; + double sb2 = sb * sb; + + auto c_max = [n, mn, sb2, sg2]( double x ) -> double { + double k = mn - x; + double d = k * k; + double nd = n * d; + double k0 = -n * nd; + double k1 = sb2 - n * sg2 + nd; + double det = k1 * k1 - 4 * sg2 * k0; + return static_cast( -2. * k0 / + ( k1 + std::sqrt( det ) ) ); + }; + + auto var_out = [n, sb2, sg2]( double c ) { + double nc = n - c; + return ( nc / n ) * ( sb2 - nc * sg2 ); + }; + + return (std::min)( var_out( 1 ), + var_out( + (std::min)( c_max( 0. ), + c_max( mg_min ) ) ) ) / + sb2; + } -} + static double erf_inv( double x ) { + // Code accompanying the article "Approximating the erfinv + // function" in GPU Computing Gems, Volume 2 + double w, p; + + w = -log( ( 1.0 - x ) * ( 1.0 + x ) ); + + if ( w < 6.250000 ) { + w = w - 3.125000; + p = -3.6444120640178196996e-21; + p = -1.685059138182016589e-19 + p * w; + p = 1.2858480715256400167e-18 + p * w; + p = 1.115787767802518096e-17 + p * w; + p = -1.333171662854620906e-16 + p * w; + p = 2.0972767875968561637e-17 + p * w; + p = 6.6376381343583238325e-15 + p * w; + p = -4.0545662729752068639e-14 + p * w; + p = -8.1519341976054721522e-14 + p * w; + p = 2.6335093153082322977e-12 + p * w; + p = -1.2975133253453532498e-11 + p * w; + p = -5.4154120542946279317e-11 + p * w; + p = 1.051212273321532285e-09 + p * w; + p = -4.1126339803469836976e-09 + p * w; + p = -2.9070369957882005086e-08 + p * w; + p = 4.2347877827932403518e-07 + p * w; + p = -1.3654692000834678645e-06 + p * w; + p = -1.3882523362786468719e-05 + p * w; + p = 0.0001867342080340571352 + p * w; + p = -0.00074070253416626697512 + p * w; + p = -0.0060336708714301490533 + p * w; + p = 0.24015818242558961693 + p * w; + p = 1.6536545626831027356 + p * w; + } else if ( w < 16.000000 ) { + w = sqrt( w ) - 3.250000; + p = 2.2137376921775787049e-09; + p = 9.0756561938885390979e-08 + p * w; + p = -2.7517406297064545428e-07 + p * w; + p = 1.8239629214389227755e-08 + p * w; + p = 1.5027403968909827627e-06 + p * w; + p = -4.013867526981545969e-06 + p * w; + p = 2.9234449089955446044e-06 + p * w; + p = 1.2475304481671778723e-05 + p * w; + p = -4.7318229009055733981e-05 + p * w; + p = 6.8284851459573175448e-05 + p * w; + p = 2.4031110387097893999e-05 + p * w; + p = -0.0003550375203628474796 + p * w; + p = 0.00095328937973738049703 + p * w; + p = -0.0016882755560235047313 + p * w; + p = 0.0024914420961078508066 + p * w; + p = -0.0037512085075692412107 + p * w; + p = 0.005370914553590063617 + p * w; + p = 1.0052589676941592334 + p * w; + p = 3.0838856104922207635 + p * w; + } else { + w = sqrt( w ) - 5.000000; + p = -2.7109920616438573243e-11; + p = -2.5556418169965252055e-10 + p * w; + p = 1.5076572693500548083e-09 + p * w; + p = -3.7894654401267369937e-09 + p * w; + p = 7.6157012080783393804e-09 + p * w; + p = -1.4960026627149240478e-08 + p * w; + p = 2.9147953450901080826e-08 + p * w; + p = -6.7711997758452339498e-08 + p * w; + p = 2.2900482228026654717e-07 + p * w; + p = -9.9298272942317002539e-07 + p * w; + p = 4.5260625972231537039e-06 + p * w; + p = -1.9681778105531670567e-05 + p * w; + p = 7.5995277030017761139e-05 + p * w; + p = -0.00021503011930044477347 + p * w; + p = -0.00013871931833623122026 + p * w; + p = 1.0103004648645343977 + p * w; + p = 4.8499064014085844221 + p * w; + } + return p * x; + } + + static double + standard_deviation( double const* first, double const* last ) { + auto m = Catch::Benchmark::Detail::mean( first, last ); + double variance = + std::accumulate( first, + last, + 0., + [m]( double a, double b ) { + double diff = b - m; + return a + diff * diff; + } ) / + ( last - first ); + return std::sqrt( variance ); + } + + static sample jackknife( double ( *estimator )( double const*, + double const* ), + double* first, + double* last ) { + const auto second = first + 1; + sample results; + results.reserve( static_cast( last - first ) ); + + for ( auto it = first; it != last; ++it ) { + std::iter_swap( it, first ); + results.push_back( estimator( second, last ) ); + } + + return results; + } + + + } // namespace + } // namespace Detail + } // namespace Benchmark +} // namespace Catch namespace Catch { namespace Benchmark { namespace Detail { -#if defined( __GNUC__ ) || defined( __clang__ ) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - bool directCompare( double lhs, double rhs ) { return lhs == rhs; } -#if defined( __GNUC__ ) || defined( __clang__ ) -# pragma GCC diagnostic pop -#endif - - double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last) { + double weighted_average_quantile( int k, + int q, + double* first, + double* last ) { auto count = last - first; double idx = (count - 1) * k / static_cast(q); int j = static_cast(idx); double g = idx - j; std::nth_element(first, first + j, last); auto xj = first[j]; - if ( directCompare( g, 0 ) ) { + if ( Catch::Detail::directCompare( g, 0 ) ) { return xj; } @@ -236,6 +380,48 @@ namespace Catch { return xj + g * (xj1 - xj); } + OutlierClassification + classify_outliers( double const* first, double const* last ) { + std::vector copy( first, last ); + + auto q1 = weighted_average_quantile( 1, 4, copy.data(), copy.data() + copy.size() ); + auto q3 = weighted_average_quantile( 3, 4, copy.data(), copy.data() + copy.size() ); + auto iqr = q3 - q1; + auto los = q1 - ( iqr * 3. ); + auto lom = q1 - ( iqr * 1.5 ); + auto him = q3 + ( iqr * 1.5 ); + auto his = q3 + ( iqr * 3. ); + + OutlierClassification o; + for ( ; first != last; ++first ) { + const double t = *first; + if ( t < los ) { + ++o.low_severe; + } else if ( t < lom ) { + ++o.low_mild; + } else if ( t > his ) { + ++o.high_severe; + } else if ( t > him ) { + ++o.high_mild; + } + ++o.samples_seen; + } + return o; + } + + double mean( double const* first, double const* last ) { + auto count = last - first; + double sum = 0.; + while (first != last) { + sum += *first; + ++first; + } + return sum / static_cast(count); + } + + double normal_cdf( double x ) { + return std::erfc( -x / std::sqrt( 2.0 ) ) / 2.0; + } double erfc_inv(double x) { return erf_inv(1.0 - x); @@ -257,50 +443,77 @@ namespace Catch { return result; } + Estimate + bootstrap( double confidence_level, + double* first, + double* last, + sample const& resample, + double ( *estimator )( double const*, double const* ) ) { + auto n_samples = last - first; + + double point = estimator( first, last ); + // Degenerate case with a single sample + if ( n_samples == 1 ) + return { point, point, point, confidence_level }; + + sample jack = jackknife( estimator, first, last ); + double jack_mean = + mean( jack.data(), jack.data() + jack.size() ); + double sum_squares = 0, sum_cubes = 0; + for ( double x : jack ) { + auto difference = jack_mean - x; + auto square = difference * difference; + auto cube = square * difference; + sum_squares += square; + sum_cubes += cube; + } - double outlier_variance(Estimate mean, Estimate stddev, int n) { - double sb = stddev.point; - double mn = mean.point / n; - double mg_min = mn / 2.; - double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); - double sg2 = sg * sg; - double sb2 = sb * sb; + double accel = sum_cubes / ( 6 * std::pow( sum_squares, 1.5 ) ); + long n = static_cast( resample.size() ); + double prob_n = + std::count_if( resample.begin(), + resample.end(), + [point]( double x ) { return x < point; } ) / + static_cast( n ); + // degenerate case with uniform samples + if ( Catch::Detail::directCompare( prob_n, 0. ) ) { + return { point, point, point, confidence_level }; + } - auto c_max = [n, mn, sb2, sg2](double x) -> double { - double k = mn - x; - double d = k * k; - double nd = n * d; - double k0 = -n * nd; - double k1 = sb2 - n * sg2 + nd; - double det = k1 * k1 - 4 * sg2 * k0; - return static_cast(-2. * k0 / (k1 + std::sqrt(det))); - }; + double bias = normal_quantile( prob_n ); + double z1 = normal_quantile( ( 1. - confidence_level ) / 2. ); - auto var_out = [n, sb2, sg2](double c) { - double nc = n - c; - return (nc / n) * (sb2 - nc * sg2); + auto cumn = [n]( double x ) -> long { + return std::lround( normal_cdf( x ) * + static_cast( n ) ); }; - - return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; - } - - - bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector::iterator first, std::vector::iterator last) { - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS - static std::random_device entropy; - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - - auto n = static_cast(last - first); // seriously, one can't use integral types without hell in C++ - - auto mean = &Detail::mean::iterator>; + auto a = [bias, accel]( double b ) { + return bias + b / ( 1. - accel * b ); + }; + double b1 = bias + z1; + double b2 = bias - z1; + double a1 = a( b1 ); + double a2 = a( b2 ); + auto lo = static_cast( (std::max)( cumn( a1 ), 0l ) ); + auto hi = + static_cast( (std::min)( cumn( a2 ), n - 1 ) ); + + return { point, resample[lo], resample[hi], confidence_level }; + } + + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + double* first, + double* last) { + auto mean = &Detail::mean; auto stddev = &standard_deviation; #if defined(CATCH_CONFIG_USE_ASYNC) - auto Estimate = [=](double(*f)(std::vector::iterator, std::vector::iterator)) { - auto seed = entropy(); + auto Estimate = [=](double(*f)(double const*, double const*)) { + std::random_device rd; + auto seed = rd(); return std::async(std::launch::async, [=] { - std::mt19937 rng(seed); + SimplePcg32 rng( seed ); auto resampled = resample(rng, n_resamples, first, last, f); return bootstrap(confidence_level, first, last, resampled, f); }); @@ -312,9 +525,10 @@ namespace Catch { auto mean_estimate = mean_future.get(); auto stddev_estimate = stddev_future.get(); #else - auto Estimate = [=](double(*f)(std::vector::iterator, std::vector::iterator)) { - auto seed = entropy(); - std::mt19937 rng(seed); + auto Estimate = [=](double(*f)(double const* , double const*)) { + std::random_device rd; + auto seed = rd(); + SimplePcg32 rng( seed ); auto resampled = resample(rng, n_resamples, first, last, f); return bootstrap(confidence_level, first, last, resampled, f); }; @@ -323,6 +537,7 @@ namespace Catch { auto stddev_estimate = Estimate(stddev); #endif // CATCH_USE_ASYNC + auto n = static_cast(last - first); // seriously, one can't use integral types without hell in C++ double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); return { mean_estimate, stddev_estimate, outlier_variance }; @@ -349,7 +564,7 @@ bool marginComparison(double lhs, double rhs, double margin) { namespace Catch { Approx::Approx ( double value ) - : m_epsilon( std::numeric_limits::epsilon()*100. ), + : m_epsilon( static_cast(std::numeric_limits::epsilon())*100. ), m_margin( 0.0 ), m_scale( 0.0 ), m_value( value ) @@ -394,10 +609,10 @@ namespace Catch { } namespace literals { - Approx operator "" _a(long double val) { + Approx operator ""_a(long double val) { return Approx(val); } - Approx operator "" _a(unsigned long long val) { + Approx operator ""_a(unsigned long long val) { return Approx(val); } } // end namespace literals @@ -412,7 +627,7 @@ std::string StringMaker::convert(Catch::Approx const& value) { namespace Catch { - AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const& _lazyExpression): lazyExpression(_lazyExpression), resultType(_resultType) {} @@ -596,16 +811,18 @@ namespace Catch { elem = trim(elem); } - // Insert the default reporter if user hasn't asked for a specfic one + // Insert the default reporter if user hasn't asked for a specific one if ( m_data.reporterSpecifications.empty() ) { - m_data.reporterSpecifications.push_back( { #if defined( CATCH_CONFIG_DEFAULT_REPORTER ) - CATCH_CONFIG_DEFAULT_REPORTER, + const auto default_spec = CATCH_CONFIG_DEFAULT_REPORTER; #else - "console", + const auto default_spec = "console"; #endif - {}, {}, {} - } ); + auto parsed = parseReporterSpec(default_spec); + CATCH_ENFORCE( parsed, + "Cannot parse the provided default reporter spec: '" + << default_spec << '\'' ); + m_data.reporterSpecifications.push_back( std::move( *parsed ) ); } if ( enableBazelEnvSupport() ) { @@ -775,7 +992,11 @@ namespace Catch { } - Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { + Capturer::Capturer( StringRef macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType resultType, + StringRef names ): + m_resultCapture( getResultCapture() ) { auto trimmed = [&] (size_t start, size_t end) { while (names[start] == ',' || isspace(static_cast(names[start]))) { ++start; @@ -825,6 +1046,8 @@ namespace Catch { m_messages.back().message += " := "; start = pos; } + break; + default:; // noop } } assert(openings.empty() && "Mismatched openings"); @@ -852,6 +1075,8 @@ namespace Catch { +#include + namespace Catch { namespace { @@ -862,7 +1087,7 @@ namespace Catch { public: // IRegistryHub RegistryHub() = default; - IReporterRegistry const& getReporterRegistry() const override { + ReporterRegistry const& getReporterRegistry() const override { return m_reporterRegistry; } ITestCaseRegistry const& getTestCaseRegistry() const override { @@ -938,13 +1163,20 @@ namespace Catch { #include #include +#include #include #include namespace Catch { namespace { - const int MaxExitCode = 255; + static constexpr int TestFailureExitCode = 42; + static constexpr int UnspecifiedErrorExitCode = 1; + static constexpr int AllTestsSkippedExitCode = 4; + static constexpr int NoTestsRunExitCode = 2; + static constexpr int UnmatchedTestSpecExitCode = 3; + static constexpr int InvalidTestSpecExitCode = 5; + IEventListenerPtr createReporter(std::string const& reporterName, ReporterConfig&& config) { auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, CATCH_MOVE(config)); @@ -1108,8 +1340,7 @@ namespace Catch { } int Session::applyCommandLine( int argc, char const * const * argv ) { - if( m_startupExceptions ) - return 1; + if ( m_startupExceptions ) { return UnspecifiedErrorExitCode; } auto result = m_cli.parse( Clara::Args( argc, argv ) ); @@ -1125,7 +1356,7 @@ namespace Catch { << TextFlow::Column( result.errorMessage() ).indent( 2 ) << "\n\n"; errStream->stream() << "Run with -? for usage\n\n" << std::flush; - return MaxExitCode; + return UnspecifiedErrorExitCode; } if( m_configData.showHelp ) @@ -1195,8 +1426,7 @@ namespace Catch { } int Session::runInternal() { - if( m_startupExceptions ) - return 1; + if ( m_startupExceptions ) { return UnspecifiedErrorExitCode; } if (m_configData.showHelp || m_configData.libIdentify) { return 0; @@ -1207,7 +1437,7 @@ namespace Catch { << ") must be greater than the shard index (" << m_configData.shardIndex << ")\n" << std::flush; - return 1; + return UnspecifiedErrorExitCode; } CATCH_TRY { @@ -1230,7 +1460,7 @@ namespace Catch { for ( auto const& spec : invalidSpecs ) { reporter->reportInvalidTestSpec( spec ); } - return 1; + return InvalidTestSpecExitCode; } @@ -1244,29 +1474,29 @@ namespace Catch { if ( tests.hadUnmatchedTestSpecs() && m_config->warnAboutUnmatchedTestSpecs() ) { - return 3; + // UnmatchedTestSpecExitCode + return UnmatchedTestSpecExitCode; } if ( totals.testCases.total() == 0 && !m_config->zeroTestsCountAsSuccess() ) { - return 2; + return NoTestsRunExitCode; } if ( totals.testCases.total() > 0 && totals.testCases.total() == totals.testCases.skipped && !m_config->zeroTestsCountAsSuccess() ) { - return 4; + return AllTestsSkippedExitCode; } - // Note that on unices only the lower 8 bits are usually used, clamping - // the return value to 255 prevents false negative when some multiple - // of 256 tests has failed - return (std::min) (MaxExitCode, static_cast(totals.assertions.failed)); + if ( totals.assertions.failed ) { return TestFailureExitCode; } + return 0; + } #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) catch( std::exception& ex ) { Catch::cerr() << ex.what() << '\n' << std::flush; - return MaxExitCode; + return UnspecifiedErrorExitCode; } #endif } @@ -1302,26 +1532,26 @@ namespace Catch { static_assert(sizeof(TestCaseProperties) == sizeof(TCP_underlying_type), "The size of the TestCaseProperties is different from the assumed size"); - TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) { + constexpr TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) { return static_cast( static_cast(lhs) | static_cast(rhs) ); } - TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) { + constexpr TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) { lhs = static_cast( static_cast(lhs) | static_cast(rhs) ); return lhs; } - TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) { + constexpr TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) { return static_cast( static_cast(lhs) & static_cast(rhs) ); } - bool applies(TestCaseProperties tcp) { + constexpr bool applies(TestCaseProperties tcp) { static_assert(static_cast(TestCaseProperties::None) == 0, "TestCaseProperties::None must be equal to 0"); return tcp != TestCaseProperties::None; @@ -1360,13 +1590,15 @@ namespace Catch { return "Anonymous test case " + std::to_string(++counter); } - StringRef extractFilenamePart(StringRef filename) { + constexpr StringRef extractFilenamePart(StringRef filename) { size_t lastDot = filename.size(); while (lastDot > 0 && filename[lastDot - 1] != '.') { --lastDot; } - --lastDot; + // In theory we could have filename without any extension in it + if ( lastDot == 0 ) { return StringRef(); } + --lastDot; size_t nameStart = lastDot; while (nameStart > 0 && filename[nameStart - 1] != '/' && filename[nameStart - 1] != '\\') { --nameStart; @@ -1376,7 +1608,7 @@ namespace Catch { } // Returns the upper bound on size of extra tags ([#file]+[.]) - size_t sizeOfExtraTags(StringRef filepath) { + constexpr size_t sizeOfExtraTags(StringRef filepath) { // [.] is 3, [#] is another 3 const size_t extras = 3 + 3; return extractFilenamePart(filepath).size() + extras; @@ -1420,12 +1652,20 @@ namespace Catch { for (size_t idx = 0; idx < originalTags.size(); ++idx) { auto c = originalTags[idx]; if (c == '[') { - assert(!inTag); + CATCH_ENFORCE( + !inTag, + "Found '[' inside a tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + inTag = true; tagStart = idx; } if (c == ']') { - assert(inTag); + CATCH_ENFORCE( + inTag, + "Found unmatched ']' while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + inTag = false; tagEnd = idx; assert(tagStart < tagEnd); @@ -1434,7 +1674,11 @@ namespace Catch { // it over to backing storage and actually reference the // backing storage in the saved tags StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1); - CATCH_ENFORCE(!tagStr.empty(), "Empty tags are not allowed"); + CATCH_ENFORCE( !tagStr.empty(), + "Found an empty tag while registering test case '" + << _nameAndTags.name << "' at " + << _lineInfo ); + enforceNotReservedTag(tagStr, lineInfo); properties |= parseSpecialTag(tagStr); // When copying a tag to the backing storage, we need to @@ -1448,8 +1692,12 @@ namespace Catch { // the tags. internalAppendTag(tagStr); } - (void)inTag; // Silence "set-but-unused" warning in release mode. } + CATCH_ENFORCE( !inTag, + "Found an unclosed tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + + // Add [.] if relevant if (isHidden()) { internalAppendTag("."_sr); @@ -1521,10 +1769,6 @@ namespace Catch { return lhs.tags < rhs.tags; } - TestCaseInfo const& TestCaseHandle::getTestCaseInfo() const { - return *m_info; - } - } // end namespace Catch @@ -1625,16 +1869,18 @@ namespace Catch { return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } ); } - TestSpec::Matches TestSpec::matchesByFilter( std::vector const& testCases, IConfig const& config ) const - { - Matches matches( m_filters.size() ); - std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){ + TestSpec::Matches TestSpec::matchesByFilter( std::vector const& testCases, IConfig const& config ) const { + Matches matches; + matches.reserve( m_filters.size() ); + for ( auto const& filter : m_filters ) { std::vector currentMatches; - for( auto const& test : testCases ) - if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) ) + for ( auto const& test : testCases ) + if ( isThrowSafe( test, config ) && + filter.matches( test.getTestCaseInfo() ) ) currentMatches.emplace_back( &test ); - return FilterMatch{ extractFilterName(filter), currentMatches }; - } ); + matches.push_back( + FilterMatch{ extractFilterName( filter ), currentMatches } ); + } return matches; } @@ -1663,7 +1909,7 @@ namespace Catch { namespace { static auto getCurrentNanosecondsSinceEpoch() -> uint64_t { - return std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); } } // end unnamed namespace @@ -1732,13 +1978,13 @@ namespace Detail { } } // end unnamed namespace - std::string convertIntoString(StringRef string, bool escape_invisibles) { + std::string convertIntoString(StringRef string, bool escapeInvisibles) { std::string ret; // This is enough for the "don't escape invisibles" case, and a good // lower bound on the "escape invisibles" case. ret.reserve(string.size() + 2); - if (!escape_invisibles) { + if (!escapeInvisibles) { ret += '"'; ret += string; ret += '"'; @@ -1816,7 +2062,7 @@ std::string StringMaker::convert(char const* str) { return{ "{null string}" }; } } -std::string StringMaker::convert(char* str) { +std::string StringMaker::convert(char* str) { // NOLINT(readability-non-const-parameter) if (str) { return Detail::convertIntoString( str ); } else { @@ -1913,17 +2159,17 @@ std::string StringMaker::convert(signed char value) { std::string StringMaker::convert(char c) { return ::Catch::Detail::stringify(static_cast(c)); } -std::string StringMaker::convert(unsigned char c) { - return ::Catch::Detail::stringify(static_cast(c)); +std::string StringMaker::convert(unsigned char value) { + return ::Catch::Detail::stringify(static_cast(value)); } -int StringMaker::precision = 5; +int StringMaker::precision = std::numeric_limits::max_digits10; std::string StringMaker::convert(float value) { return Detail::fpToString(value, precision) + 'f'; } -int StringMaker::precision = 10; +int StringMaker::precision = std::numeric_limits::max_digits10; std::string StringMaker::convert(double value) { return Detail::fpToString(value, precision); @@ -1991,6 +2237,19 @@ namespace Catch { } + + +namespace Catch { + namespace Detail { + void registerTranslatorImpl( + Detail::unique_ptr&& translator ) { + getMutableRegistryHub().registerTranslator( + CATCH_MOVE( translator ) ); + } + } // namespace Detail +} // namespace Catch + + #include namespace Catch { @@ -2021,7 +2280,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 3, 2, "", 0 ); + static Version version( 3, 7, 1, "", 0 ); return version; } @@ -2074,8 +2333,36 @@ namespace Detail { +#include + +namespace Catch { + namespace Generators { + namespace Detail { + std::uint32_t getSeed() { return sharedRng()(); } + } // namespace Detail + + struct RandomFloatingGenerator::PImpl { + PImpl( long double a, long double b, uint32_t seed ): + rng( seed ), dist( a, b ) {} + + Catch::SimplePcg32 rng; + std::uniform_real_distribution dist; + }; + + RandomFloatingGenerator::RandomFloatingGenerator( + long double a, long double b, std::uint32_t seed) : + m_pimpl(Catch::Detail::make_unique(a, b, seed)) { + static_cast( next() ); + } -std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); } + RandomFloatingGenerator::~RandomFloatingGenerator() = + default; + bool RandomFloatingGenerator::next() { + m_current_number = m_pimpl->dist( m_pimpl->rng ); + return true; + } + } // namespace Generators +} // namespace Catch @@ -2135,9 +2422,7 @@ namespace Catch { -#include #include -#include namespace Catch { @@ -2172,8 +2457,6 @@ namespace Catch { infoMessages( _infoMessages ), totals( _totals ) { - assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; - if( assertionResult.hasMessage() ) { // Copy message into messages list. // !TBD This should have been done earlier, somewhere @@ -2232,14 +2515,6 @@ namespace Catch { namespace Catch { - IReporterRegistry::~IReporterRegistry() = default; -} - - - - -namespace Catch { - ITestInvoker::~ITestInvoker() = default; ITestCaseRegistry::~ITestCaseRegistry() = default; } @@ -2254,13 +2529,15 @@ namespace Catch { ResultDisposition::Flags resultDisposition ) : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, m_resultCapture( getResultCapture() ) - {} + { + m_resultCapture.notifyAssertionStarted( m_assertionInfo ); + } void AssertionHandler::handleExpr( ITransientExpression const& expr ) { m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); } - void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef message) { - m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); + void AssertionHandler::handleMessage(ResultWas::OfType resultType, std::string&& message) { + m_resultCapture.handleMessage( m_assertionInfo, resultType, CATCH_MOVE(message), m_reaction ); } auto AssertionHandler::allowThrows() const -> bool { @@ -2268,7 +2545,7 @@ namespace Catch { } void AssertionHandler::complete() { - setCompleted(); + m_completed = true; if( m_reaction.shouldDebugBreak ) { // If you find your debugger stopping you here then go one level up on the @@ -2281,16 +2558,9 @@ namespace Catch { throw_test_failure_exception(); } if ( m_reaction.shouldSkip ) { -#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) - throw Catch::TestSkipException(); -#else - CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); -#endif + throw_test_skip_exception(); } } - void AssertionHandler::setCompleted() { - m_completed = true; - } void AssertionHandler::handleUnexpectedInflightException() { m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); @@ -2362,13 +2632,29 @@ namespace { ; } - std::string normaliseOpt( std::string const& optName ) { -#ifdef CATCH_PLATFORM_WINDOWS - if ( optName[0] == '/' ) - return "-" + optName.substr( 1 ); - else + Catch::StringRef normaliseOpt( Catch::StringRef optName ) { + if ( optName[0] == '-' +#if defined(CATCH_PLATFORM_WINDOWS) + || optName[0] == '/' #endif - return optName; + ) { + return optName.substr( 1, optName.size() ); + } + + return optName; + } + + static size_t find_first_separator(Catch::StringRef sr) { + auto is_separator = []( char c ) { + return c == ' ' || c == ':' || c == '='; + }; + size_t pos = 0; + while (pos < sr.size()) { + if (is_separator(sr[pos])) { return pos; } + ++pos; + } + + return Catch::StringRef::npos; } } // namespace @@ -2386,23 +2672,23 @@ namespace Catch { } if ( it != itEnd ) { - auto const& next = *it; + StringRef next = *it; if ( isOptPrefix( next[0] ) ) { - auto delimiterPos = next.find_first_of( " :=" ); - if ( delimiterPos != std::string::npos ) { + auto delimiterPos = find_first_separator(next); + if ( delimiterPos != StringRef::npos ) { m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); m_tokenBuffer.push_back( { TokenType::Argument, - next.substr( delimiterPos + 1 ) } ); + next.substr( delimiterPos + 1, next.size() ) } ); } else { - if ( next[1] != '-' && next.size() > 2 ) { - std::string opt = "- "; + if ( next.size() > 1 && next[1] != '-' && next.size() > 2 ) { + // Combined short args, e.g. "-ab" for "-a -b" for ( size_t i = 1; i < next.size(); ++i ) { - opt[1] = next[i]; m_tokenBuffer.push_back( - { TokenType::Option, opt } ); + { TokenType::Option, + next.substr( i, 1 ) } ); } } else { m_tokenBuffer.push_back( @@ -2462,12 +2748,12 @@ namespace Catch { size_t ParserBase::cardinality() const { return 1; } InternalParseResult ParserBase::parse( Args const& args ) const { - return parse( args.exeName(), TokenStream( args ) ); + return parse( static_cast(args.exeName()), TokenStream( args ) ); } ParseState::ParseState( ParseResultType type, - TokenStream const& remainingTokens ): - m_type( type ), m_remainingTokens( remainingTokens ) {} + TokenStream remainingTokens ): + m_type( type ), m_remainingTokens( CATCH_MOVE(remainingTokens) ) {} ParserResult BoundFlagRef::setFlag( bool flag ) { m_ref = flag; @@ -2485,34 +2771,34 @@ namespace Catch { } // namespace Detail Detail::InternalParseResult Arg::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream tokens) const { auto validationResult = validate(); if (!validationResult) return Detail::InternalParseResult(validationResult); - auto remainingTokens = tokens; - auto const& token = *remainingTokens; + auto token = *tokens; if (token.type != Detail::TokenType::Argument) return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::NoMatch, remainingTokens)); + ParseResultType::NoMatch, CATCH_MOVE(tokens))); assert(!m_ref->isFlag()); auto valueRef = static_cast(m_ref.get()); - auto result = valueRef->setValue(remainingTokens->token); - if (!result) - return Detail::InternalParseResult(result); + auto result = valueRef->setValue(static_cast(token.token)); + if ( !result ) + return Detail::InternalParseResult( result ); else - return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::Matched, ++remainingTokens)); + return Detail::InternalParseResult::ok( + Detail::ParseState( ParseResultType::Matched, + CATCH_MOVE( ++tokens ) ) ); } Opt::Opt(bool& ref) : ParserRefImpl(std::make_shared(ref)) {} - std::vector Opt::getHelpColumns() const { - std::ostringstream oss; + Detail::HelpColumns Opt::getHelpColumns() const { + ReusableStringStream oss; bool first = true; for (auto const& opt : m_optNames) { if (first) @@ -2523,10 +2809,10 @@ namespace Catch { } if (!m_hint.empty()) oss << " <" << m_hint << '>'; - return { { oss.str(), m_description } }; + return { oss.str(), m_description }; } - bool Opt::isMatch(std::string const& optToken) const { + bool Opt::isMatch(StringRef optToken) const { auto normalisedToken = normaliseOpt(optToken); for (auto const& name : m_optNames) { if (normaliseOpt(name) == normalisedToken) @@ -2536,15 +2822,14 @@ namespace Catch { } Detail::InternalParseResult Opt::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream tokens) const { auto validationResult = validate(); if (!validationResult) return Detail::InternalParseResult(validationResult); - auto remainingTokens = tokens; - if (remainingTokens && - remainingTokens->type == Detail::TokenType::Option) { - auto const& token = *remainingTokens; + if (tokens && + tokens->type == Detail::TokenType::Option) { + auto const& token = *tokens; if (isMatch(token.token)) { if (m_ref->isFlag()) { auto flagRef = @@ -2556,35 +2841,35 @@ namespace Catch { if (result.value() == ParseResultType::ShortCircuitAll) return Detail::InternalParseResult::ok(Detail::ParseState( - result.value(), remainingTokens)); + result.value(), CATCH_MOVE(tokens))); } else { auto valueRef = static_cast( m_ref.get()); - ++remainingTokens; - if (!remainingTokens) + ++tokens; + if (!tokens) return Detail::InternalParseResult::runtimeError( "Expected argument following " + token.token); - auto const& argToken = *remainingTokens; + auto const& argToken = *tokens; if (argToken.type != Detail::TokenType::Argument) return Detail::InternalParseResult::runtimeError( "Expected argument following " + token.token); - const auto result = valueRef->setValue(argToken.token); + const auto result = valueRef->setValue(static_cast(argToken.token)); if (!result) return Detail::InternalParseResult(result); if (result.value() == ParseResultType::ShortCircuitAll) return Detail::InternalParseResult::ok(Detail::ParseState( - result.value(), remainingTokens)); + result.value(), CATCH_MOVE(tokens))); } return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::Matched, ++remainingTokens)); + ParseResultType::Matched, CATCH_MOVE(++tokens))); } } return Detail::InternalParseResult::ok( - Detail::ParseState(ParseResultType::NoMatch, remainingTokens)); + Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); } Detail::Result Opt::validate() const { @@ -2616,9 +2901,9 @@ namespace Catch { Detail::InternalParseResult ExeName::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream tokens) const { return Detail::InternalParseResult::ok( - Detail::ParseState(ParseResultType::NoMatch, tokens)); + Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); } ParserResult ExeName::set(std::string const& newName) { @@ -2648,9 +2933,9 @@ namespace Catch { std::vector Parser::getHelpColumns() const { std::vector cols; + cols.reserve( m_options.size() ); for ( auto const& o : m_options ) { - auto childCols = o.getHelpColumns(); - cols.insert( cols.end(), childCols.begin(), childCols.end() ); + cols.push_back(o.getHelpColumns()); } return cols; } @@ -2688,12 +2973,12 @@ namespace Catch { optWidth = ( std::min )( optWidth, consoleWidth / 2 ); - for ( auto const& cols : rows ) { - auto row = TextFlow::Column( cols.left ) + for ( auto& cols : rows ) { + auto row = TextFlow::Column( CATCH_MOVE(cols.left) ) .width( optWidth ) .indent( 2 ) + TextFlow::Spacer( 4 ) + - TextFlow::Column( cols.right ) + TextFlow::Column( static_cast(cols.descriptions) ) .width( consoleWidth - 7 - optWidth ); os << row << '\n'; } @@ -2715,7 +3000,7 @@ namespace Catch { Detail::InternalParseResult Parser::parse( std::string const& exeName, - Detail::TokenStream const& tokens ) const { + Detail::TokenStream tokens ) const { struct ParserInfo { ParserBase const* parser = nullptr; @@ -2733,7 +3018,7 @@ namespace Catch { m_exeName.set( exeName ); auto result = Detail::InternalParseResult::ok( - Detail::ParseState( ParseResultType::NoMatch, tokens ) ); + Detail::ParseState( ParseResultType::NoMatch, CATCH_MOVE(tokens) ) ); while ( result.value().remainingTokens() ) { bool tokenParsed = false; @@ -2741,7 +3026,7 @@ namespace Catch { if ( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { result = parseInfo.parser->parse( - exeName, result.value().remainingTokens() ); + exeName, CATCH_MOVE(result).value().remainingTokens() ); if ( !result ) return result; if ( result.value().type() != @@ -2767,7 +3052,7 @@ namespace Catch { Args::Args(int argc, char const* const* argv) : m_exeName(argv[0]), m_args(argv + 1, argv + argc) {} - Args::Args(std::initializer_list args) : + Args::Args(std::initializer_list args) : m_exeName(*args.begin()), m_args(args.begin() + 1, args.end()) {} @@ -2819,7 +3104,7 @@ namespace Catch { line = trim(line); if( !line.empty() && !startsWith( line, '#' ) ) { if( !startsWith( line, '"' ) ) - line = '"' + line + '"'; + line = '"' + CATCH_MOVE(line) + '"'; config.testsOrTags.push_back( line ); config.testsOrTags.emplace_back( "," ); } @@ -2917,7 +3202,7 @@ namespace Catch { auto const& reporterSpec = *parsed; - IReporterRegistry::FactoryMap const& factories = + auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); auto result = factories.find( reporterSpec.name() ); @@ -3073,8 +3358,8 @@ namespace Catch { ( "split the tests to execute into this many groups" ) | Opt( setShardIndex, "shard index" ) ["--shard-index"] - ( "index of the group of tests to execute (see --shard-count)" ) | - Opt( config.allowZeroTests ) + ( "index of the group of tests to execute (see --shard-count)" ) + | Opt( config.allowZeroTests ) ["--allow-running-no-tests"] ( "Treat 'No tests run' as a success" ) | Arg( config.testsOrTags, "test name|pattern|tags" ) @@ -3155,7 +3440,7 @@ namespace Catch { namespace { //! A do-nothing implementation of colour, used as fallback for unknown //! platforms, and when the user asks to deactivate all colours. - class NoColourImpl : public ColourImpl { + class NoColourImpl final : public ColourImpl { public: NoColourImpl( IStream* stream ): ColourImpl( stream ) {} @@ -3173,7 +3458,7 @@ namespace Catch { namespace Catch { namespace { - class Win32ColourImpl : public ColourImpl { + class Win32ColourImpl final : public ColourImpl { public: Win32ColourImpl(IStream* stream): ColourImpl(stream) { @@ -3239,7 +3524,7 @@ namespace { namespace Catch { namespace { - class ANSIColourImpl : public ColourImpl { + class ANSIColourImpl final : public ColourImpl { public: ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {} @@ -3300,21 +3585,21 @@ namespace { namespace Catch { - Detail::unique_ptr makeColourImpl( ColourMode implSelection, + Detail::unique_ptr makeColourImpl( ColourMode colourSelection, IStream* stream ) { #if defined( CATCH_CONFIG_COLOUR_WIN32 ) - if ( implSelection == ColourMode::Win32 ) { + if ( colourSelection == ColourMode::Win32 ) { return Detail::make_unique( stream ); } #endif - if ( implSelection == ColourMode::ANSI ) { + if ( colourSelection == ColourMode::ANSI ) { return Detail::make_unique( stream ); } - if ( implSelection == ColourMode::None ) { + if ( colourSelection == ColourMode::None ) { return Detail::make_unique( stream ); } - if ( implSelection == ColourMode::PlatformDefault) { + if ( colourSelection == ColourMode::PlatformDefault) { #if defined( CATCH_CONFIG_COLOUR_WIN32 ) if ( Win32ColourImpl::useImplementationForStream( *stream ) ) { return Detail::make_unique( stream ); @@ -3326,7 +3611,7 @@ namespace Catch { return Detail::make_unique( stream ); } - CATCH_ERROR( "Could not create colour impl for selection " << static_cast(implSelection) ); + CATCH_ERROR( "Could not create colour impl for selection " << static_cast(colourSelection) ); } bool isColourImplAvailable( ColourMode colourSelection ) { @@ -3355,49 +3640,21 @@ namespace Catch { namespace Catch { - class Context : public IMutableContext, private Detail::NonCopyable { - - public: // IContext - IResultCapture* getResultCapture() override { - return m_resultCapture; - } - - IConfig const* getConfig() const override { - return m_config; - } - - ~Context() override; - - public: // IMutableContext - void setResultCapture( IResultCapture* resultCapture ) override { - m_resultCapture = resultCapture; - } - void setConfig( IConfig const* config ) override { - m_config = config; - } - - friend IMutableContext& getCurrentMutableContext(); - - private: - IConfig const* m_config = nullptr; - IResultCapture* m_resultCapture = nullptr; - }; - - IMutableContext *IMutableContext::currentContext = nullptr; + Context* Context::currentContext = nullptr; - void IMutableContext::createContext() - { + void cleanUpContext() { + delete Context::currentContext; + Context::currentContext = nullptr; + } + void Context::createContext() { currentContext = new Context(); } - void cleanUpContext() { - delete IMutableContext::currentContext; - IMutableContext::currentContext = nullptr; + Context& getCurrentMutableContext() { + if ( !Context::currentContext ) { Context::createContext(); } + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *Context::currentContext; } - IContext::~IContext() = default; - IMutableContext::~IMutableContext() = default; - Context::~Context() = default; - SimplePcg32& sharedRng() { static SimplePcg32 s_rng; @@ -3556,7 +3813,12 @@ namespace Catch { namespace Catch { - ITransientExpression::~ITransientExpression() = default; + void ITransientExpression::streamReconstructedExpression( + std::ostream& os ) const { + // We can't make this function pure virtual to keep ITransientExpression + // constexpr, so we write error message instead + os << "Some class derived from ITransientExpression without overriding streamReconstructedExpression"; + } void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { if( lhs.size() + rhs.size() < 40 && @@ -3635,7 +3897,7 @@ namespace Catch { return parsed; } - EnumInfo::~EnumInfo() {} + EnumInfo::~EnumInfo() = default; StringRef EnumInfo::lookup( int value ) const { for( auto const& valueToName : m_values ) { @@ -3680,10 +3942,27 @@ namespace Catch { +#include + namespace Catch { - ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + namespace { + static std::string tryTranslators( + std::vector< + Detail::unique_ptr> const& translators ) { + if ( translators.empty() ) { + std::rethrow_exception( std::current_exception() ); + } else { + return translators[0]->translate( translators.begin() + 1, + translators.end() ); + } + } + } +#endif //!defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() = default; void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr&& translator ) { m_translators.push_back( CATCH_MOVE( translator ) ); @@ -3706,7 +3985,7 @@ namespace Catch { // First we try user-registered translators. If none of them can // handle the exception, it will be rethrown handled by our defaults. try { - return tryTranslators(); + return tryTranslators(m_translators); } // To avoid having to handle TFE explicitly everywhere, we just // rethrow it so that it goes back up the caller. @@ -3730,25 +4009,12 @@ namespace Catch { } } - std::string ExceptionTranslatorRegistry::tryTranslators() const { - if (m_translators.empty()) { - std::rethrow_exception(std::current_exception()); - } else { - return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); - } - } - #else // ^^ Exceptions are enabled // Exceptions are disabled vv std::string ExceptionTranslatorRegistry::translateActiveException() const { CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); } - - std::string ExceptionTranslatorRegistry::tryTranslators() const { - CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); - } #endif - } @@ -4005,6 +4271,17 @@ namespace Catch { return i; } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + bool directCompare( float lhs, float rhs ) { return lhs == rhs; } + bool directCompare( double lhs, double rhs ) { return lhs == rhs; } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + + } // end namespace Detail } // end namespace Catch @@ -4053,7 +4330,7 @@ namespace Catch { namespace Detail { namespace { template - class StreamBufImpl : public std::streambuf { + class StreamBufImpl final : public std::streambuf { char data[bufferSize]; WriterF m_writer; @@ -4101,7 +4378,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class FileStream : public IStream { + class FileStream final : public IStream { std::ofstream m_ofs; public: FileStream( std::string const& filename ) { @@ -4109,7 +4386,6 @@ namespace Detail { CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << '\'' ); m_ofs << std::unitbuf; } - ~FileStream() override = default; public: // IStream std::ostream& stream() override { return m_ofs; @@ -4118,13 +4394,12 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class CoutStream : public IStream { + class CoutStream final : public IStream { std::ostream m_os; public: // Store the streambuf from cout up-front because // cout may get redirected when running tests CoutStream() : m_os( Catch::cout().rdbuf() ) {} - ~CoutStream() override = default; public: // IStream std::ostream& stream() override { return m_os; } @@ -4138,7 +4413,6 @@ namespace Detail { // Store the streambuf from cerr up-front because // cout may get redirected when running tests CerrStream(): m_os( Catch::cerr().rdbuf() ) {} - ~CerrStream() override = default; public: // IStream std::ostream& stream() override { return m_os; } @@ -4147,7 +4421,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class DebugOutStream : public IStream { + class DebugOutStream final : public IStream { Detail::unique_ptr> m_streamBuf; std::ostream m_os; public: @@ -4156,8 +4430,6 @@ namespace Detail { m_os( m_streamBuf.get() ) {} - ~DebugOutStream() override = default; - public: // IStream std::ostream& stream() override { return m_os; } }; @@ -4189,6 +4461,147 @@ namespace Detail { +namespace Catch { + void JsonUtils::indent( std::ostream& os, std::uint64_t level ) { + for ( std::uint64_t i = 0; i < level; ++i ) { + os << " "; + } + } + void JsonUtils::appendCommaNewline( std::ostream& os, + bool& should_comma, + std::uint64_t level ) { + if ( should_comma ) { os << ','; } + should_comma = true; + os << '\n'; + indent( os, level ); + } + + JsonObjectWriter::JsonObjectWriter( std::ostream& os ): + JsonObjectWriter{ os, 0 } {} + + JsonObjectWriter::JsonObjectWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } { + m_os << '{'; + } + JsonObjectWriter::JsonObjectWriter( JsonObjectWriter&& source ) noexcept: + m_os{ source.m_os }, + m_indent_level{ source.m_indent_level }, + m_should_comma{ source.m_should_comma }, + m_active{ source.m_active } { + source.m_active = false; + } + + JsonObjectWriter::~JsonObjectWriter() { + if ( !m_active ) { return; } + + m_os << '\n'; + JsonUtils::indent( m_os, m_indent_level ); + m_os << '}'; + } + + JsonValueWriter JsonObjectWriter::write( StringRef key ) { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + + m_os << '"' << key << "\": "; + return JsonValueWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter::JsonArrayWriter( std::ostream& os ): + JsonArrayWriter{ os, 0 } {} + JsonArrayWriter::JsonArrayWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } { + m_os << '['; + } + JsonArrayWriter::JsonArrayWriter( JsonArrayWriter&& source ) noexcept: + m_os{ source.m_os }, + m_indent_level{ source.m_indent_level }, + m_should_comma{ source.m_should_comma }, + m_active{ source.m_active } { + source.m_active = false; + } + JsonArrayWriter::~JsonArrayWriter() { + if ( !m_active ) { return; } + + m_os << '\n'; + JsonUtils::indent( m_os, m_indent_level ); + m_os << ']'; + } + + JsonObjectWriter JsonArrayWriter::writeObject() { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + return JsonObjectWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter JsonArrayWriter::writeArray() { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + return JsonArrayWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter& JsonArrayWriter::write( bool value ) { + return writeImpl( value ); + } + + JsonValueWriter::JsonValueWriter( std::ostream& os ): + JsonValueWriter{ os, 0 } {} + + JsonValueWriter::JsonValueWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } {} + + JsonObjectWriter JsonValueWriter::writeObject() && { + return JsonObjectWriter{ m_os, m_indent_level }; + } + + JsonArrayWriter JsonValueWriter::writeArray() && { + return JsonArrayWriter{ m_os, m_indent_level }; + } + + void JsonValueWriter::write( Catch::StringRef value ) && { + writeImpl( value, true ); + } + + void JsonValueWriter::write( bool value ) && { + writeImpl( value ? "true"_sr : "false"_sr, false ); + } + + void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) { + if ( quote ) { m_os << '"'; } + for (char c : value) { + // Escape list taken from https://www.json.org/json-en.html, + // string definition. + // Note that while forward slash _can_ be escaped, it does + // not have to be, if JSON is not further embedded somewhere + // where forward slash is meaningful. + if ( c == '"' ) { + m_os << "\\\""; + } else if ( c == '\\' ) { + m_os << "\\\\"; + } else if ( c == '\b' ) { + m_os << "\\b"; + } else if ( c == '\f' ) { + m_os << "\\f"; + } else if ( c == '\n' ) { + m_os << "\\n"; + } else if ( c == '\r' ) { + m_os << "\\r"; + } else if ( c == '\t' ) { + m_os << "\\t"; + } else { + m_os << c; + } + } + if ( quote ) { m_os << '"'; } + } + +} // namespace Catch + + + namespace Catch { @@ -4231,7 +4644,7 @@ namespace Catch { #else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv - Catch::LeakDetector::LeakDetector() {} + Catch::LeakDetector::LeakDetector() = default; #endif // CATCH_CONFIG_WINDOWS_CRTDBG @@ -4242,7 +4655,6 @@ Catch::LeakDetector::~LeakDetector() { - namespace Catch { namespace { @@ -4277,7 +4689,7 @@ namespace Catch { void listReporters(IEventListener& reporter) { std::vector descriptions; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); descriptions.reserve(factories.size()); for (auto const& fac : factories) { descriptions.push_back({ fac.first, fac.second->getDescription() }); @@ -4397,138 +4809,328 @@ namespace Catch { #include #include +#include #include -#if defined(CATCH_CONFIG_NEW_CAPTURE) - #if defined(_MSC_VER) - #include //_dup and _dup2 - #define dup _dup - #define dup2 _dup2 - #define fileno _fileno - #else - #include // dup and dup2 - #endif +#if defined( CATCH_CONFIG_NEW_CAPTURE ) +# if defined( _MSC_VER ) +# include //_dup and _dup2 +# define dup _dup +# define dup2 _dup2 +# define fileno _fileno +# else +# include // dup and dup2 +# endif #endif - namespace Catch { - RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) - : m_originalStream( originalStream ), - m_redirectionStream( redirectionStream ), - m_prevBuf( m_originalStream.rdbuf() ) - { - m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); - } + namespace { + //! A no-op implementation, used if no reporter wants output + //! redirection. + class NoopRedirect : public OutputRedirect { + void activateImpl() override {} + void deactivateImpl() override {} + std::string getStdout() override { return {}; } + std::string getStderr() override { return {}; } + void clearBuffers() override {} + }; - RedirectedStream::~RedirectedStream() { - m_originalStream.rdbuf( m_prevBuf ); - } + /** + * Redirects specific stream's rdbuf with another's. + * + * Redirection can be stopped and started on-demand, assumes + * that the underlying stream's rdbuf aren't changed by other + * users. + */ + class RedirectedStreamNew { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; - RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} - auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + public: + RedirectedStreamNew( std::ostream& originalStream, + std::ostream& redirectionStream ): + m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) {} - RedirectedStdErr::RedirectedStdErr() - : m_cerr( Catch::cerr(), m_rss.get() ), - m_clog( Catch::clog(), m_rss.get() ) - {} - auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + void startRedirect() { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + void stopRedirect() { m_originalStream.rdbuf( m_prevBuf ); } + }; - RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr) - : m_redirectedCout(redirectedCout), - m_redirectedCerr(redirectedCerr) - {} + /** + * Redirects the `std::cout`, `std::cerr`, `std::clog` streams, + * but does not touch the actual `stdout`/`stderr` file descriptors. + */ + class StreamRedirect : public OutputRedirect { + ReusableStringStream m_redirectedOut, m_redirectedErr; + RedirectedStreamNew m_cout, m_cerr, m_clog; - RedirectedStreams::~RedirectedStreams() { - m_redirectedCout += m_redirectedStdOut.str(); - m_redirectedCerr += m_redirectedStdErr.str(); - } + public: + StreamRedirect(): + m_cout( Catch::cout(), m_redirectedOut.get() ), + m_cerr( Catch::cerr(), m_redirectedErr.get() ), + m_clog( Catch::clog(), m_redirectedErr.get() ) {} + + void activateImpl() override { + m_cout.startRedirect(); + m_cerr.startRedirect(); + m_clog.startRedirect(); + } + void deactivateImpl() override { + m_cout.stopRedirect(); + m_cerr.stopRedirect(); + m_clog.stopRedirect(); + } + std::string getStdout() override { return m_redirectedOut.str(); } + std::string getStderr() override { return m_redirectedErr.str(); } + void clearBuffers() override { + m_redirectedOut.str( "" ); + m_redirectedErr.str( "" ); + } + }; -#if defined(CATCH_CONFIG_NEW_CAPTURE) +#if defined( CATCH_CONFIG_NEW_CAPTURE ) -#if defined(_MSC_VER) - TempFile::TempFile() { - if (tmpnam_s(m_buffer)) { - CATCH_RUNTIME_ERROR("Could not get a temp filename"); - } - if (fopen_s(&m_file, m_buffer, "w+")) { - char buffer[100]; - if (strerror_s(buffer, errno)) { - CATCH_RUNTIME_ERROR("Could not translate errno to a string"); + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile( TempFile const& ) = delete; + TempFile& operator=( TempFile const& ) = delete; + TempFile( TempFile&& ) = delete; + TempFile& operator=( TempFile&& ) = delete; + +# if defined( _MSC_VER ) + TempFile() { + if ( tmpnam_s( m_buffer ) ) { + CATCH_RUNTIME_ERROR( "Could not get a temp filename" ); + } + if ( fopen_s( &m_file, m_buffer, "wb+" ) ) { + char buffer[100]; + if ( strerror_s( buffer, errno ) ) { + CATCH_RUNTIME_ERROR( + "Could not translate errno to a string" ); + } + CATCH_RUNTIME_ERROR( "Could not open the temp file: '" + << m_buffer + << "' because: " << buffer ); + } + } +# else + TempFile() { + m_file = std::tmpfile(); + if ( !m_file ) { + CATCH_RUNTIME_ERROR( "Could not create a temp file." ); + } + } +# endif + + ~TempFile() { + // TBD: What to do about errors here? + std::fclose( m_file ); + // We manually create the file on Windows only, on Linux + // it will be autodeleted +# if defined( _MSC_VER ) + std::remove( m_buffer ); +# endif } - CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); + + std::FILE* getFile() { return m_file; } + std::string getContents() { + ReusableStringStream sstr; + constexpr long buffer_size = 100; + char buffer[buffer_size + 1] = {}; + long current_pos = ftell( m_file ); + CATCH_ENFORCE( current_pos >= 0, + "ftell failed, errno: " << errno ); + std::rewind( m_file ); + while ( current_pos > 0 ) { + auto read_characters = + std::fread( buffer, + 1, + std::min( buffer_size, current_pos ), + m_file ); + buffer[read_characters] = '\0'; + sstr << buffer; + current_pos -= static_cast( read_characters ); + } + return sstr.str(); + } + + void clear() { std::rewind( m_file ); } + + private: + std::FILE* m_file = nullptr; + char m_buffer[L_tmpnam] = { 0 }; + }; + + /** + * Redirects the actual `stdout`/`stderr` file descriptors. + * + * Works by replacing the file descriptors numbered 1 and 2 + * with an open temporary file. + */ + class FileRedirect : public OutputRedirect { + TempFile m_outFile, m_errFile; + int m_originalOut = -1; + int m_originalErr = -1; + + // Flushes cout/cerr/clog streams and stdout/stderr FDs + void flushEverything() { + Catch::cout() << std::flush; + fflush( stdout ); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush( stderr ); + } + + public: + FileRedirect(): + m_originalOut( dup( fileno( stdout ) ) ), + m_originalErr( dup( fileno( stderr ) ) ) { + CATCH_ENFORCE( m_originalOut >= 0, "Could not dup stdout" ); + CATCH_ENFORCE( m_originalErr >= 0, "Could not dup stderr" ); + } + + std::string getStdout() override { return m_outFile.getContents(); } + std::string getStderr() override { return m_errFile.getContents(); } + void clearBuffers() override { + m_outFile.clear(); + m_errFile.clear(); + } + + void activateImpl() override { + // We flush before starting redirect, to ensure that we do + // not capture the end of message sent before activation. + flushEverything(); + + int ret; + ret = dup2( fileno( m_outFile.getFile() ), fileno( stdout ) ); + CATCH_ENFORCE( ret >= 0, + "dup2 to stdout has failed, errno: " << errno ); + ret = dup2( fileno( m_errFile.getFile() ), fileno( stderr ) ); + CATCH_ENFORCE( ret >= 0, + "dup2 to stderr has failed, errno: " << errno ); + } + void deactivateImpl() override { + // We flush before ending redirect, to ensure that we + // capture all messages sent while the redirect was active. + flushEverything(); + + int ret; + ret = dup2( m_originalOut, fileno( stdout ) ); + CATCH_ENFORCE( + ret >= 0, + "dup2 of original stdout has failed, errno: " << errno ); + ret = dup2( m_originalErr, fileno( stderr ) ); + CATCH_ENFORCE( + ret >= 0, + "dup2 of original stderr has failed, errno: " << errno ); + } + }; + +#endif // CATCH_CONFIG_NEW_CAPTURE + + } // end namespace + + bool isRedirectAvailable( OutputRedirect::Kind kind ) { + switch ( kind ) { + // These two are always available + case OutputRedirect::None: + case OutputRedirect::Streams: + return true; +#if defined( CATCH_CONFIG_NEW_CAPTURE ) + case OutputRedirect::FileDescriptors: + return true; +#endif + default: + return false; } } + + Detail::unique_ptr makeOutputRedirect( bool actual ) { + if ( actual ) { + // TODO: Clean this up later +#if defined( CATCH_CONFIG_NEW_CAPTURE ) + return Detail::make_unique(); #else - TempFile::TempFile() { - m_file = std::tmpfile(); - if (!m_file) { - CATCH_RUNTIME_ERROR("Could not create a temp file."); + return Detail::make_unique(); +#endif + } else { + return Detail::make_unique(); } } -#endif + RedirectGuard scopedActivate( OutputRedirect& redirectImpl ) { + return RedirectGuard( true, redirectImpl ); + } - TempFile::~TempFile() { - // TBD: What to do about errors here? - std::fclose(m_file); - // We manually create the file on Windows only, on Linux - // it will be autodeleted -#if defined(_MSC_VER) - std::remove(m_buffer); -#endif + RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl ) { + return RedirectGuard( false, redirectImpl ); } + OutputRedirect::~OutputRedirect() = default; - FILE* TempFile::getFile() { - return m_file; - } + RedirectGuard::RedirectGuard( bool activate, OutputRedirect& redirectImpl ): + m_redirect( &redirectImpl ), + m_activate( activate ), + m_previouslyActive( redirectImpl.isActive() ) { - std::string TempFile::getContents() { - std::stringstream sstr; - char buffer[100] = {}; - std::rewind(m_file); - while (std::fgets(buffer, sizeof(buffer), m_file)) { - sstr << buffer; - } - return sstr.str(); - } + // Skip cases where there is no actual state change. + if ( m_activate == m_previouslyActive ) { return; } - OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : - m_originalStdout(dup(1)), - m_originalStderr(dup(2)), - m_stdoutDest(stdout_dest), - m_stderrDest(stderr_dest) { - dup2(fileno(m_stdoutFile.getFile()), 1); - dup2(fileno(m_stderrFile.getFile()), 2); + if ( m_activate ) { + m_redirect->activate(); + } else { + m_redirect->deactivate(); + } } - OutputRedirect::~OutputRedirect() { - Catch::cout() << std::flush; - fflush(stdout); - // Since we support overriding these streams, we flush cerr - // even though std::cerr is unbuffered - Catch::cerr() << std::flush; - Catch::clog() << std::flush; - fflush(stderr); + RedirectGuard::~RedirectGuard() noexcept( false ) { + if ( m_moved ) { return; } + // Skip cases where there is no actual state change. + if ( m_activate == m_previouslyActive ) { return; } - dup2(m_originalStdout, 1); - dup2(m_originalStderr, 2); + if ( m_activate ) { + m_redirect->deactivate(); + } else { + m_redirect->activate(); + } + } - m_stdoutDest += m_stdoutFile.getContents(); - m_stderrDest += m_stderrFile.getContents(); + RedirectGuard::RedirectGuard( RedirectGuard&& rhs ) noexcept: + m_redirect( rhs.m_redirect ), + m_activate( rhs.m_activate ), + m_previouslyActive( rhs.m_previouslyActive ), + m_moved( false ) { + rhs.m_moved = true; } -#endif // CATCH_CONFIG_NEW_CAPTURE + RedirectGuard& RedirectGuard::operator=( RedirectGuard&& rhs ) noexcept { + m_redirect = rhs.m_redirect; + m_activate = rhs.m_activate; + m_previouslyActive = rhs.m_previouslyActive; + m_moved = false; + rhs.m_moved = true; + return *this; + } } // namespace Catch -#if defined(CATCH_CONFIG_NEW_CAPTURE) - #if defined(_MSC_VER) - #undef dup - #undef dup2 - #undef fileno - #endif +#if defined( CATCH_CONFIG_NEW_CAPTURE ) +# if defined( _MSC_VER ) +# undef dup +# undef dup2 +# undef fileno +# endif #endif @@ -4599,6 +5201,14 @@ namespace Catch { } #endif +#if !defined( CATCH_CONFIG_GLOBAL_NEXTAFTER ) + float nextafter( float x, float y ) { return std::nextafter( x, y ); } + double nextafter( double x, double y ) { return std::nextafter( x, y ); } +#else + float nextafter( float x, float y ) { return ::nextafterf( x, y ); } + double nextafter( double x, double y ) { return ::nextafter( x, y ); } +#endif + } // end namespace Catch @@ -4680,10 +5290,10 @@ namespace Catch { return static_cast( std::time( nullptr ) ); case GenerateFrom::Default: - case GenerateFrom::RandomDevice: - // In theory, a platform could have random_device that returns just - // 16 bits. That is still some randomness, so we don't care too much - return static_cast( std::random_device{}() ); + case GenerateFrom::RandomDevice: { + std::random_device rd; + return Detail::fillBitsFrom( rd ); + } default: CATCH_ERROR("Unknown generation method"); @@ -4696,49 +5306,73 @@ namespace Catch { namespace Catch { + struct ReporterRegistry::ReporterRegistryImpl { + std::vector> listeners; + std::map + factories; + }; - ReporterRegistry::ReporterRegistry() { + ReporterRegistry::ReporterRegistry(): + m_impl( Detail::make_unique() ) { // Because it is impossible to move out of initializer list, // we have to add the elements manually - m_factories["Automake"] = Detail::make_unique>(); - m_factories["compact"] = Detail::make_unique>(); - m_factories["console"] = Detail::make_unique>(); - m_factories["JUnit"] = Detail::make_unique>(); - m_factories["SonarQube"] = Detail::make_unique>(); - m_factories["TAP"] = Detail::make_unique>(); - m_factories["TeamCity"] = Detail::make_unique>(); - m_factories["XML"] = Detail::make_unique>(); + m_impl->factories["Automake"] = + Detail::make_unique>(); + m_impl->factories["compact"] = + Detail::make_unique>(); + m_impl->factories["console"] = + Detail::make_unique>(); + m_impl->factories["JUnit"] = + Detail::make_unique>(); + m_impl->factories["SonarQube"] = + Detail::make_unique>(); + m_impl->factories["TAP"] = + Detail::make_unique>(); + m_impl->factories["TeamCity"] = + Detail::make_unique>(); + m_impl->factories["XML"] = + Detail::make_unique>(); + m_impl->factories["JSON"] = + Detail::make_unique>(); } ReporterRegistry::~ReporterRegistry() = default; - - IEventListenerPtr ReporterRegistry::create( std::string const& name, ReporterConfig&& config ) const { - auto it = m_factories.find( name ); - if( it == m_factories.end() ) - return nullptr; - return it->second->create( CATCH_MOVE(config) ); + IEventListenerPtr + ReporterRegistry::create( std::string const& name, + ReporterConfig&& config ) const { + auto it = m_impl->factories.find( name ); + if ( it == m_impl->factories.end() ) return nullptr; + return it->second->create( CATCH_MOVE( config ) ); } - void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) { + void ReporterRegistry::registerReporter( std::string const& name, + IReporterFactoryPtr factory ) { CATCH_ENFORCE( name.find( "::" ) == name.npos, - "'::' is not allowed in reporter name: '" + name + '\'' ); - auto ret = m_factories.emplace(name, CATCH_MOVE(factory)); - CATCH_ENFORCE( ret.second, "reporter using '" + name + "' as name was already registered" ); + "'::' is not allowed in reporter name: '" + name + + '\'' ); + auto ret = m_impl->factories.emplace( name, CATCH_MOVE( factory ) ); + CATCH_ENFORCE( ret.second, + "reporter using '" + name + + "' as name was already registered" ); } void ReporterRegistry::registerListener( Detail::unique_ptr factory ) { - m_listeners.push_back( CATCH_MOVE(factory) ); + m_impl->listeners.push_back( CATCH_MOVE( factory ) ); } - IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { - return m_factories; - } - IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { - return m_listeners; + std::map const& + ReporterRegistry::getFactories() const { + return m_impl->factories; } -} + std::vector> const& + ReporterRegistry::getListeners() const { + return m_impl->listeners; + } +} // namespace Catch @@ -4754,9 +5388,9 @@ namespace Catch { }; kvPair splitKVPair(StringRef kvString) { - auto splitPos = static_cast( std::distance( - kvString.begin(), - std::find( kvString.begin(), kvString.end(), '=' ) ) ); + auto splitPos = static_cast( + std::find( kvString.begin(), kvString.end(), '=' ) - + kvString.begin() ); return { kvString.substr( 0, splitPos ), kvString.substr( splitPos + 1, kvString.size() ) }; @@ -4850,7 +5484,7 @@ namespace Catch { auto kv = splitKVPair( parts[i] ); auto key = kv.key, value = kv.value; - if ( key.empty() || value.empty() ) { + if ( key.empty() || value.empty() ) { // NOLINT(bugprone-branch-clone) return {}; } else if ( key[0] == 'X' ) { // This is a reporter-specific option, we don't check these @@ -4907,26 +5541,6 @@ ReporterSpec::ReporterSpec( -namespace Catch { - - bool isOk( ResultWas::OfType resultType ) { - return ( resultType & ResultWas::FailureBit ) == 0; - } - bool isJustInfo( int flags ) { - return flags == ResultWas::Info; - } - - ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { - return static_cast( static_cast( lhs ) | static_cast( rhs ) ); - } - - bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } - bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } - -} // end namespace Catch - - - #include #include #include @@ -4988,146 +5602,152 @@ namespace Catch { namespace Catch { namespace Generators { - struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { - GeneratorBasePtr m_generator; + namespace { + struct GeneratorTracker final : TestCaseTracking::TrackerBase, + IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker( + TestCaseTracking::NameAndLocation&& nameAndLocation, + TrackerContext& ctx, + ITracker* parent ): + TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {} + + static GeneratorTracker* + acquire( TrackerContext& ctx, + TestCaseTracking::NameAndLocationRef const& + nameAndLocation ) { + GeneratorTracker* tracker; + + ITracker& currentTracker = ctx.currentTracker(); + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if ( currentTracker.nameAndLocation() == nameAndLocation ) { + auto thisTracker = currentTracker.parent()->findChild( + nameAndLocation ); + assert( thisTracker ); + assert( thisTracker->isGeneratorTracker() ); + tracker = static_cast( thisTracker ); + } else if ( ITracker* childTracker = + currentTracker.findChild( + nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isGeneratorTracker() ); + tracker = + static_cast( childTracker ); + } else { + return nullptr; + } - GeneratorTracker( TestCaseTracking::NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ) - {} - ~GeneratorTracker() override; - - static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocationRef const& nameAndLocation ) { - GeneratorTracker* tracker; - - ITracker& currentTracker = ctx.currentTracker(); - // Under specific circumstances, the generator we want - // to acquire is also the current tracker. If this is - // the case, we have to avoid looking through current - // tracker's children, and instead return the current - // tracker. - // A case where this check is important is e.g. - // for (int i = 0; i < 5; ++i) { - // int n = GENERATE(1, 2); - // } - // - // without it, the code above creates 5 nested generators. - if ( currentTracker.nameAndLocation() == nameAndLocation ) { - auto thisTracker = - currentTracker.parent()->findChild( nameAndLocation ); - assert( thisTracker ); - assert( thisTracker->isGeneratorTracker() ); - tracker = static_cast( thisTracker ); - } else if ( ITracker* childTracker = - currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isGeneratorTracker() ); - tracker = static_cast( childTracker ); - } else { - return nullptr; - } + if ( !tracker->isComplete() ) { tracker->open(); } - if( !tracker->isComplete() ) { - tracker->open(); + return tracker; } - return tracker; - } - - // TrackerBase interface - bool isGeneratorTracker() const override { return true; } - auto hasGenerator() const -> bool override { - return !!m_generator; - } - void close() override { - TrackerBase::close(); - // If a generator has a child (it is followed by a section) - // and none of its children have started, then we must wait - // until later to start consuming its values. - // This catches cases where `GENERATE` is placed between two - // `SECTION`s. - // **The check for m_children.empty cannot be removed**. - // doing so would break `GENERATE` _not_ followed by `SECTION`s. - const bool should_wait_for_child = [&]() { - // No children -> nobody to wait for - if ( m_children.empty() ) { - return false; - } - // If at least one child started executing, don't wait - if ( std::find_if( - m_children.begin(), - m_children.end(), - []( TestCaseTracking::ITrackerPtr const& tracker ) { - return tracker->hasStarted(); - } ) != m_children.end() ) { - return false; - } - - // No children have started. We need to check if they _can_ - // start, and thus we should wait for them, or they cannot - // start (due to filters), and we shouldn't wait for them - ITracker* parent = m_parent; - // This is safe: there is always at least one section - // tracker in a test case tracking tree - while ( !parent->isSectionTracker() ) { - parent = parent->parent(); - } - assert( parent && - "Missing root (test case) level section" ); - - auto const& parentSection = - static_cast( *parent ); - auto const& filters = parentSection.getFilters(); - // No filters -> no restrictions on running sections - if ( filters.empty() ) { - return true; - } + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by + // `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if ( m_children.empty() ) { return false; } + // If at least one child started executing, don't wait + if ( std::find_if( + m_children.begin(), + m_children.end(), + []( TestCaseTracking::ITrackerPtr const& + tracker ) { + return tracker->hasStarted(); + } ) != m_children.end() ) { + return false; + } - for ( auto const& child : m_children ) { - if ( child->isSectionTracker() && - std::find( - filters.begin(), - filters.end(), - static_cast( *child ) - .trimmedName() ) != filters.end() ) { - return true; + // No children have started. We need to check if they + // _can_ start, and thus we should wait for them, or + // they cannot start (due to filters), and we shouldn't + // wait for them + ITracker* parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while ( !parent->isSectionTracker() ) { + parent = parent->parent(); + } + assert( parent && + "Missing root (test case) level section" ); + + auto const& parentSection = + static_cast( *parent ); + auto const& filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if ( filters.empty() ) { return true; } + + for ( auto const& child : m_children ) { + if ( child->isSectionTracker() && + std::find( filters.begin(), + filters.end(), + static_cast( + *child ) + .trimmedName() ) != + filters.end() ) { + return true; + } } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + assert( m_generator && "Tracker without generator" ); + if ( should_wait_for_child || + ( m_runState == CompletedSuccessfully && + m_generator->countedNext() ) ) { + m_children.clear(); + m_runState = Executing; } - return false; - }(); - - // This check is a bit tricky, because m_generator->next() - // has a side-effect, where it consumes generator's current - // value, but we do not want to invoke the side-effect if - // this generator is still waiting for any child to start. - assert( m_generator && "Tracker without generator" ); - if ( should_wait_for_child || - ( m_runState == CompletedSuccessfully && - m_generator->countedNext() ) ) { - m_children.clear(); - m_runState = Executing; } - } - // IGeneratorTracker interface - auto getGenerator() const -> GeneratorBasePtr const& override { - return m_generator; - } - void setGenerator( GeneratorBasePtr&& generator ) override { - m_generator = CATCH_MOVE( generator ); - } - }; - GeneratorTracker::~GeneratorTracker() = default; + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = CATCH_MOVE( generator ); + } + }; + } // namespace } RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter) : m_runInfo(_config->name()), - m_context(getCurrentMutableContext()), m_config(_config), m_reporter(CATCH_MOVE(reporter)), m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, + m_outputRedirect( makeOutputRedirect( m_reporter->getPreferences().shouldRedirectStdOut ) ), m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) { - m_context.setResultCapture(this); + getCurrentMutableContext().setResultCapture( this ); m_reporter->testRunStarting(m_runInfo); } @@ -5140,6 +5760,7 @@ namespace Catch { auto const& testInfo = testCase.getTestCaseInfo(); m_reporter->testCaseStarting(testInfo); + testCase.prepareTestCase(); m_activeTestCase = &testCase; @@ -5190,15 +5811,17 @@ namespace Catch { m_reporter->testCasePartialStarting(testInfo, testRuns); const auto beforeRunTotals = m_totals; - std::string oneRunCout, oneRunCerr; - runCurrentTest(oneRunCout, oneRunCerr); + runCurrentTest(); + std::string oneRunCout = m_outputRedirect->getStdout(); + std::string oneRunCerr = m_outputRedirect->getStderr(); + m_outputRedirect->clearBuffers(); redirectedCout += oneRunCout; redirectedCerr += oneRunCerr; const auto singleRunTotals = m_totals.delta(beforeRunTotals); auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting()); - m_reporter->testCasePartialEnded(statsForOneRun, testRuns); + ++testRuns; } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); @@ -5209,6 +5832,7 @@ namespace Catch { deltaTotals.testCases.failed++; } m_totals.testCases += deltaTotals.testCases; + testCase.tearDownTestCase(); m_reporter->testCaseEnded(TestCaseStats(testInfo, deltaTotals, CATCH_MOVE(redirectedCout), @@ -5222,7 +5846,7 @@ namespace Catch { } - void RunContext::assertionEnded(AssertionResult const & result) { + void RunContext::assertionEnded(AssertionResult&& result) { if (result.getResultType() == ResultWas::Ok) { m_totals.assertions.passed++; m_lastAssertionPassed = true; @@ -5242,21 +5866,33 @@ namespace Catch { m_lastAssertionPassed = true; } - m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)); + { + auto _ = scopedDeactivate( *m_outputRedirect ); + m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ); + } - if (result.getResultType() != ResultWas::Warning) + if ( result.getResultType() != ResultWas::Warning ) { m_messageScopes.clear(); + } - // Reset working state - resetAssertionInfo(); - m_lastResult = result; + // Reset working state. assertion info will be reset after + // populateReaction is run if it is needed + m_lastResult = CATCH_MOVE( result ); } void RunContext::resetAssertionInfo() { m_lastAssertionInfo.macroName = StringRef(); m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + m_lastAssertionInfo.resultDisposition = ResultDisposition::Normal; + } + + void RunContext::notifyAssertionStarted( AssertionInfo const& info ) { + auto _ = scopedDeactivate( *m_outputRedirect ); + m_reporter->assertionStarting( info ); } - bool RunContext::sectionStarted(StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts & assertions) { + bool RunContext::sectionStarted( StringRef sectionName, + SourceLineInfo const& sectionLineInfo, + Counts& assertions ) { ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocationRef( @@ -5269,7 +5905,10 @@ namespace Catch { SectionInfo sectionInfo( sectionLineInfo, static_cast(sectionName) ); m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; - m_reporter->sectionStarting(sectionInfo); + { + auto _ = scopedDeactivate( *m_outputRedirect ); + m_reporter->sectionStarting( sectionInfo ); + } assertions = m_totals.assertions; @@ -5329,7 +5968,15 @@ namespace Catch { m_activeSections.pop_back(); } - m_reporter->sectionEnded(SectionStats(CATCH_MOVE(endInfo.sectionInfo), assertions, endInfo.durationInSeconds, missingAssertions)); + { + auto _ = scopedDeactivate( *m_outputRedirect ); + m_reporter->sectionEnded( + SectionStats( CATCH_MOVE( endInfo.sectionInfo ), + assertions, + endInfo.durationInSeconds, + missingAssertions ) ); + } + m_messages.clear(); m_messageScopes.clear(); } @@ -5346,15 +5993,19 @@ namespace Catch { } void RunContext::benchmarkPreparing( StringRef name ) { - m_reporter->benchmarkPreparing(name); + auto _ = scopedDeactivate( *m_outputRedirect ); + m_reporter->benchmarkPreparing( name ); } void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + auto _ = scopedDeactivate( *m_outputRedirect ); m_reporter->benchmarkStarting( info ); } void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { + auto _ = scopedDeactivate( *m_outputRedirect ); m_reporter->benchmarkEnded( stats ); } void RunContext::benchmarkFailed( StringRef error ) { + auto _ = scopedDeactivate( *m_outputRedirect ); m_reporter->benchmarkFailed( error ); } @@ -5385,8 +6036,13 @@ namespace Catch { } void RunContext::handleFatalErrorCondition( StringRef message ) { + // TODO: scoped deactivate here? Just give up and do best effort? + // the deactivation can break things further, OTOH so can the + // capture + auto _ = scopedDeactivate( *m_outputRedirect ); + // First notify reporter that bad things happened - m_reporter->fatalErrorEncountered(message); + m_reporter->fatalErrorEncountered( message ); // Don't rebuild the result -- the stringification itself can cause more fatal errors // Instead, fake a result data. @@ -5394,8 +6050,16 @@ namespace Catch { tempResult.message = static_cast(message); AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult)); - assertionEnded(result); + assertionEnded(CATCH_MOVE(result) ); + resetAssertionInfo(); + // Best effort cleanup for sections that have not been destructed yet + // Since this is a fatal error, we have not had and won't have the opportunity to destruct them properly + while (!m_activeSections.empty()) { + auto nl = m_activeSections.back()->nameAndLocation(); + SectionEndInfo endInfo{ SectionInfo(CATCH_MOVE(nl.location), CATCH_MOVE(nl.name)), {}, 0.0 }; + sectionEndedEarly(CATCH_MOVE(endInfo)); + } handleUnfinishedSections(); // Recreate section for test case (as we will lose the one that was in scope) @@ -5405,7 +6069,7 @@ namespace Catch { Counts assertions; assertions.failed = 1; SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false); - m_reporter->sectionEnded(testCaseSectionStats); + m_reporter->sectionEnded( testCaseSectionStats ); auto const& testInfo = m_activeTestCase->getTestCaseInfo(); @@ -5436,7 +6100,7 @@ namespace Catch { return m_totals.assertions.failed >= static_cast(m_config->abortAfter()); } - void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + void RunContext::runCurrentTest() { auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); m_reporter->sectionStarting(testCaseSection); @@ -5447,18 +6111,8 @@ namespace Catch { Timer timer; CATCH_TRY { - if (m_reporter->getPreferences().shouldRedirectStdOut) { -#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) - RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); - - timer.start(); - invokeActiveTestCase(); -#else - OutputRedirect r(redirectedCout, redirectedCerr); - timer.start(); - invokeActiveTestCase(); -#endif - } else { + { + auto _ = scopedActivate( *m_outputRedirect ); timer.start(); invokeActiveTestCase(); } @@ -5503,11 +6157,12 @@ namespace Catch { void RunContext::handleUnfinishedSections() { // If sections ended prematurely due to an exception we stored their // infos here so we can tear them down outside the unwind process. - for (auto it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it) - sectionEnded(CATCH_MOVE(*it)); + for ( auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it ) { + sectionEnded( CATCH_MOVE( *it ) ); + } m_unfinishedSections.clear(); } @@ -5516,8 +6171,6 @@ namespace Catch { ITransientExpression const& expr, AssertionReaction& reaction ) { - m_reporter->assertionStarting( info ); - bool negated = isFalseTest( info.resultDisposition ); bool result = expr.getResult() != negated; @@ -5533,6 +6186,7 @@ namespace Catch { reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); populateReaction( reaction ); } + resetAssertionInfo(); } void RunContext::reportExpr( AssertionInfo const &info, @@ -5546,31 +6200,32 @@ namespace Catch { AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); } void RunContext::handleMessage( AssertionInfo const& info, ResultWas::OfType resultType, - StringRef message, + std::string&& message, AssertionReaction& reaction ) { - m_reporter->assertionStarting( info ); - m_lastAssertionInfo = info; AssertionResultData data( resultType, LazyExpression( false ) ); - data.message = static_cast(message); + data.message = CATCH_MOVE( message ); AssertionResult assertionResult{ m_lastAssertionInfo, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); - if ( !assertionResult.isOk() ) { + + const auto isOk = assertionResult.isOk(); + assertionEnded( CATCH_MOVE(assertionResult) ); + if ( !isOk ) { populateReaction( reaction ); } else if ( resultType == ResultWas::ExplicitSkip ) { // TODO: Need to handle this explicitly, as ExplicitSkip is // considered "OK" reaction.shouldSkip = true; } + resetAssertionInfo(); } void RunContext::handleUnexpectedExceptionNotThrown( AssertionInfo const& info, @@ -5581,16 +6236,17 @@ namespace Catch { void RunContext::handleUnexpectedInflightException( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) { m_lastAssertionInfo = info; AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); - data.message = message; + data.message = CATCH_MOVE(message); AssertionResult assertionResult{ info, CATCH_MOVE(data) }; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); populateReaction( reaction ); + resetAssertionInfo(); } void RunContext::populateReaction( AssertionReaction& reaction ) { @@ -5607,7 +6263,8 @@ namespace Catch { AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s; AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); + resetAssertionInfo(); } void RunContext::handleNonExpr( AssertionInfo const &info, @@ -5618,10 +6275,11 @@ namespace Catch { AssertionResultData data( resultType, LazyExpression( false ) ); AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); - if( !assertionResult.isOk() ) - populateReaction( reaction ); + const auto isOk = assertionResult.isOk(); + assertionEnded( CATCH_MOVE(assertionResult) ); + if ( !isOk ) { populateReaction( reaction ); } + resetAssertionInfo(); } @@ -5790,7 +6448,6 @@ namespace Catch { -#include #include #include #include @@ -5814,9 +6471,9 @@ namespace Catch { return s.find( infix ) != std::string::npos; } void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), []( char c ) { - return toLower( c ); - } ); + for ( char& c : s ) { + c = toLower( c ); + } } std::string toLower( std::string const& s ) { std::string lc = s; @@ -5848,17 +6505,29 @@ namespace Catch { } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { - bool replaced = false; std::size_t i = str.find( replaceThis ); - while( i != std::string::npos ) { - replaced = true; - str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); - if( i < str.size()-withThis.size() ) - i = str.find( replaceThis, i+withThis.size() ); + if (i == std::string::npos) { + return false; + } + std::size_t copyBegin = 0; + std::string origStr = CATCH_MOVE(str); + str.clear(); + // There is at least one replacement, so reserve with the best guess + // we can make without actually counting the number of occurences. + str.reserve(origStr.size() - replaceThis.size() + withThis.size()); + do { + str.append(origStr, copyBegin, i-copyBegin ); + str += withThis; + copyBegin = i + replaceThis.size(); + if( copyBegin < origStr.size() ) + i = origStr.find( replaceThis, copyBegin ); else i = std::string::npos; + } while( i != std::string::npos ); + if ( copyBegin < origStr.size() ) { + str.append(origStr, copyBegin, origStr.size() ); } - return replaced; + return true; } std::vector splitStringRef( StringRef str, char delimiter ) { @@ -5949,7 +6618,7 @@ namespace Catch { namespace Catch { - TagAliasRegistry::~TagAliasRegistry() {} + TagAliasRegistry::~TagAliasRegistry() = default; TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { auto it = m_registry.find( alias ); @@ -6030,6 +6699,38 @@ namespace Catch { namespace Catch { + namespace { + static void enforceNoDuplicateTestCases( + std::vector const& tests ) { + auto testInfoCmp = []( TestCaseInfo const* lhs, + TestCaseInfo const* rhs ) { + return *lhs < *rhs; + }; + std::set seenTests( + testInfoCmp ); + for ( auto const& test : tests ) { + const auto infoPtr = &test.getTestCaseInfo(); + const auto prev = seenTests.insert( infoPtr ); + CATCH_ENFORCE( prev.second, + "error: test case \"" + << infoPtr->name << "\", with tags \"" + << infoPtr->tagsAsString() + << "\" already defined.\n" + << "\tFirst seen at " + << ( *prev.first )->lineInfo << "\n" + << "\tRedefined at " << infoPtr->lineInfo ); + } + } + + static bool matchTest( TestCaseHandle const& testCase, + TestSpec const& testSpec, + IConfig const& config ) { + return testSpec.matches( testCase.getTestCaseInfo() ) && + isThrowSafe( testCase, config ); + } + + } // end unnamed namespace + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { switch (config.runOrder()) { case TestRunOrder::Declared: @@ -6047,7 +6748,6 @@ namespace Catch { return sorted; } case TestRunOrder::Randomized: { - seedRng(config); using TestWithHash = std::pair; TestCaseInfoHasher h{ config.rngSeed() }; @@ -6086,29 +6786,6 @@ namespace Catch { return !testCase.getTestCaseInfo().throws() || config.allowThrows(); } - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ) { - return testSpec.matches( testCase.getTestCaseInfo() ) && isThrowSafe( testCase, config ); - } - - void - enforceNoDuplicateTestCases( std::vector const& tests ) { - auto testInfoCmp = []( TestCaseInfo const* lhs, - TestCaseInfo const* rhs ) { - return *lhs < *rhs; - }; - std::set seenTests(testInfoCmp); - for ( auto const& test : tests ) { - const auto infoPtr = &test.getTestCaseInfo(); - const auto prev = seenTests.insert( infoPtr ); - CATCH_ENFORCE( - prev.second, - "error: test case \"" << infoPtr->name << "\", with tags \"" - << infoPtr->tagsAsString() << "\" already defined.\n" - << "\tFirst seen at " << ( *prev.first )->lineInfo << "\n" - << "\tRedefined at " << infoPtr->lineInfo ); - } - } - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { std::vector filtered; filtered.reserve( testCases.size() ); @@ -6124,6 +6801,8 @@ namespace Catch { return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); } + TestRegistry::~TestRegistry() = default; + void TestRegistry::registerTest(Detail::unique_ptr testInfo, Detail::unique_ptr testInvoker) { m_handles.emplace_back(testInfo.get(), testInvoker.get()); m_viewed_test_infos.push_back(testInfo.get()); @@ -6149,13 +6828,6 @@ namespace Catch { return m_sortedFunctions; } - - - /////////////////////////////////////////////////////////////////////////// - void TestInvokerAsFunction::invoke() const { - m_testAsFunction(); - } - } // end namespace Catch @@ -6401,6 +7073,14 @@ namespace Catch { #endif } + void throw_test_skip_exception() { +#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) + throw Catch::TestSkipException(); +#else + CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); +#endif + } + } // namespace Catch @@ -6409,9 +7089,12 @@ namespace Catch { #include namespace Catch { + void ITestInvoker::prepareTestCase() {} + void ITestInvoker::tearDownTestCase() {} + ITestInvoker::~ITestInvoker() = default; namespace { - StringRef extractClassName( StringRef classOrMethodName ) { + static StringRef extractClassName( StringRef classOrMethodName ) { if ( !startsWith( classOrMethodName, '&' ) ) { return classOrMethodName; } @@ -6438,6 +7121,18 @@ namespace Catch { static_cast( startIdx ), static_cast( classNameSize ) ); } + + class TestInvokerAsFunction final : public ITestInvoker { + using TestType = void ( * )(); + TestType m_testAsFunction; + + public: + constexpr TestInvokerAsFunction( TestType testAsFunction ) noexcept: + m_testAsFunction( testAsFunction ) {} + + void invoke() const override { m_testAsFunction(); } + }; + } // namespace Detail::unique_ptr makeTestInvoker( void(*testAsFunction)() ) { @@ -6712,117 +7407,228 @@ namespace { return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr; } - bool isBoundary( std::string const& line, size_t at ) { - assert( at > 0 ); - assert( at <= line.size() ); - - return at == line.size() || - ( isWhitespace( line[at] ) && !isWhitespace( line[at - 1] ) ) || - isBreakableBefore( line[at] ) || - isBreakableAfter( line[at - 1] ); - } - } // namespace namespace Catch { namespace TextFlow { + void AnsiSkippingString::preprocessString() { + for ( auto it = m_string.begin(); it != m_string.end(); ) { + // try to read through an ansi sequence + while ( it != m_string.end() && *it == '\033' && + it + 1 != m_string.end() && *( it + 1 ) == '[' ) { + auto cursor = it + 2; + while ( cursor != m_string.end() && + ( isdigit( *cursor ) || *cursor == ';' ) ) { + ++cursor; + } + if ( cursor == m_string.end() || *cursor != 'm' ) { + break; + } + // 'm' -> 0xff + *cursor = AnsiSkippingString::sentinel; + // if we've read an ansi sequence, set the iterator and + // return to the top of the loop + it = cursor + 1; + } + if ( it != m_string.end() ) { + ++m_size; + ++it; + } + } + } + + AnsiSkippingString::AnsiSkippingString( std::string const& text ): + m_string( text ) { + preprocessString(); + } + + AnsiSkippingString::AnsiSkippingString( std::string&& text ): + m_string( CATCH_MOVE( text ) ) { + preprocessString(); + } + + AnsiSkippingString::const_iterator AnsiSkippingString::begin() const { + return const_iterator( m_string ); + } + + AnsiSkippingString::const_iterator AnsiSkippingString::end() const { + return const_iterator( m_string, const_iterator::EndTag{} ); + } + + std::string AnsiSkippingString::substring( const_iterator begin, + const_iterator end ) const { + // There's one caveat here to an otherwise simple substring: when + // making a begin iterator we might have skipped ansi sequences at + // the start. If `begin` here is a begin iterator, skipped over + // initial ansi sequences, we'll use the true beginning of the + // string. Lastly: We need to transform any chars we replaced with + // 0xff back to 'm' + auto str = std::string( begin == this->begin() ? m_string.begin() + : begin.m_it, + end.m_it ); + std::transform( str.begin(), str.end(), str.begin(), []( char c ) { + return c == AnsiSkippingString::sentinel ? 'm' : c; + } ); + return str; + } + + void AnsiSkippingString::const_iterator::tryParseAnsiEscapes() { + // check if we've landed on an ansi sequence, and if so read through + // it + while ( m_it != m_string->end() && *m_it == '\033' && + m_it + 1 != m_string->end() && *( m_it + 1 ) == '[' ) { + auto cursor = m_it + 2; + while ( cursor != m_string->end() && + ( isdigit( *cursor ) || *cursor == ';' ) ) { + ++cursor; + } + if ( cursor == m_string->end() || + *cursor != AnsiSkippingString::sentinel ) { + break; + } + // if we've read an ansi sequence, set the iterator and + // return to the top of the loop + m_it = cursor + 1; + } + } + + void AnsiSkippingString::const_iterator::advance() { + assert( m_it != m_string->end() ); + m_it++; + tryParseAnsiEscapes(); + } + + void AnsiSkippingString::const_iterator::unadvance() { + assert( m_it != m_string->begin() ); + m_it--; + // if *m_it is 0xff, scan back to the \033 and then m_it-- once more + // (and repeat check) + while ( *m_it == AnsiSkippingString::sentinel ) { + while ( *m_it != '\033' ) { + assert( m_it != m_string->begin() ); + m_it--; + } + // if this happens, we must have been a begin iterator that had + // skipped over ansi sequences at the start of a string + assert( m_it != m_string->begin() ); + assert( *m_it == '\033' ); + m_it--; + } + } + + static bool isBoundary( AnsiSkippingString const& line, + AnsiSkippingString::const_iterator it ) { + return it == line.end() || + ( isWhitespace( *it ) && + !isWhitespace( *it.oneBefore() ) ) || + isBreakableBefore( *it ) || + isBreakableAfter( *it.oneBefore() ); + } void Column::const_iterator::calcLength() { m_addHyphen = false; m_parsedTo = m_lineStart; + AnsiSkippingString const& current_line = m_column.m_string; - std::string const& current_line = m_column.m_string; - if ( current_line[m_lineStart] == '\n' ) { - ++m_parsedTo; + if ( m_parsedTo == current_line.end() ) { + m_lineEnd = m_parsedTo; + return; } + assert( m_lineStart != current_line.end() ); + if ( *m_lineStart == '\n' ) { ++m_parsedTo; } + const auto maxLineLength = m_column.m_width - indentSize(); - const auto maxParseTo = std::min(current_line.size(), m_lineStart + maxLineLength); - while ( m_parsedTo < maxParseTo && - current_line[m_parsedTo] != '\n' ) { + std::size_t lineLength = 0; + while ( m_parsedTo != current_line.end() && + lineLength < maxLineLength && *m_parsedTo != '\n' ) { ++m_parsedTo; + ++lineLength; } // If we encountered a newline before the column is filled, // then we linebreak at the newline and consider this line // finished. - if ( m_parsedTo < m_lineStart + maxLineLength ) { - m_lineLength = m_parsedTo - m_lineStart; + if ( lineLength < maxLineLength ) { + m_lineEnd = m_parsedTo; } else { // Look for a natural linebreak boundary in the column // (We look from the end, so that the first found boundary is // the right one) - size_t newLineLength = maxLineLength; - while ( newLineLength > 0 && !isBoundary( current_line, m_lineStart + newLineLength ) ) { - --newLineLength; + m_lineEnd = m_parsedTo; + while ( lineLength > 0 && + !isBoundary( current_line, m_lineEnd ) ) { + --lineLength; + --m_lineEnd; } - while ( newLineLength > 0 && - isWhitespace( current_line[m_lineStart + newLineLength - 1] ) ) { - --newLineLength; + while ( lineLength > 0 && + isWhitespace( *m_lineEnd.oneBefore() ) ) { + --lineLength; + --m_lineEnd; } - // If we found one, then that is where we linebreak - if ( newLineLength > 0 ) { - m_lineLength = newLineLength; - } else { - // Otherwise we have to split text with a hyphen + // If we found one, then that is where we linebreak, otherwise + // we have to split text with a hyphen + if ( lineLength == 0 ) { m_addHyphen = true; - m_lineLength = maxLineLength - 1; + m_lineEnd = m_parsedTo.oneBefore(); } } } size_t Column::const_iterator::indentSize() const { - auto initial = - m_lineStart == 0 ? m_column.m_initialIndent : std::string::npos; + auto initial = m_lineStart == m_column.m_string.begin() + ? m_column.m_initialIndent + : std::string::npos; return initial == std::string::npos ? m_column.m_indent : initial; } - std::string - Column::const_iterator::addIndentAndSuffix( size_t position, - size_t length ) const { + std::string Column::const_iterator::addIndentAndSuffix( + AnsiSkippingString::const_iterator start, + AnsiSkippingString::const_iterator end ) const { std::string ret; const auto desired_indent = indentSize(); - ret.reserve( desired_indent + length + m_addHyphen ); + // ret.reserve( desired_indent + (end - start) + m_addHyphen ); ret.append( desired_indent, ' ' ); - ret.append( m_column.m_string, position, length ); - if ( m_addHyphen ) { - ret.push_back( '-' ); - } + // ret.append( start, end ); + ret += m_column.m_string.substring( start, end ); + if ( m_addHyphen ) { ret.push_back( '-' ); } return ret; } - Column::const_iterator::const_iterator( Column const& column ): m_column( column ) { + Column::const_iterator::const_iterator( Column const& column ): + m_column( column ), + m_lineStart( column.m_string.begin() ), + m_lineEnd( column.m_string.begin() ), + m_parsedTo( column.m_string.begin() ) { assert( m_column.m_width > m_column.m_indent ); assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); calcLength(); - if ( m_lineLength == 0 ) { - m_lineStart = m_column.m_string.size(); + if ( m_lineStart == m_lineEnd ) { + m_lineStart = m_column.m_string.end(); } } std::string Column::const_iterator::operator*() const { assert( m_lineStart <= m_parsedTo ); - return addIndentAndSuffix( m_lineStart, m_lineLength ); + return addIndentAndSuffix( m_lineStart, m_lineEnd ); } Column::const_iterator& Column::const_iterator::operator++() { - m_lineStart += m_lineLength; - std::string const& current_line = m_column.m_string; - if ( m_lineStart < current_line.size() && current_line[m_lineStart] == '\n' ) { - m_lineStart += 1; + m_lineStart = m_lineEnd; + AnsiSkippingString const& current_line = m_column.m_string; + if ( m_lineStart != current_line.end() && *m_lineStart == '\n' ) { + m_lineStart++; } else { - while ( m_lineStart < current_line.size() && - isWhitespace( current_line[m_lineStart] ) ) { + while ( m_lineStart != current_line.end() && + isWhitespace( *m_lineStart ) ) { ++m_lineStart; } } - if ( m_lineStart != current_line.size() ) { - calcLength(); - } + if ( m_lineStart != current_line.end() ) { calcLength(); } return *this; } @@ -6919,23 +7725,36 @@ namespace Catch { return os; } - Columns Column::operator+( Column const& other ) { + Columns operator+( Column const& lhs, Column const& rhs ) { Columns cols; - cols += *this; - cols += other; + cols += lhs; + cols += rhs; return cols; } - - Columns& Columns::operator+=( Column const& col ) { - m_columns.push_back( col ); - return *this; + Columns operator+( Column&& lhs, Column&& rhs ) { + Columns cols; + cols += CATCH_MOVE( lhs ); + cols += CATCH_MOVE( rhs ); + return cols; } - Columns Columns::operator+( Column const& col ) { - Columns combined = *this; - combined += col; + Columns& operator+=( Columns& lhs, Column const& rhs ) { + lhs.m_columns.push_back( rhs ); + return lhs; + } + Columns& operator+=( Columns& lhs, Column&& rhs ) { + lhs.m_columns.push_back( CATCH_MOVE( rhs ) ); + return lhs; + } + Columns operator+( Columns const& lhs, Column const& rhs ) { + auto combined( lhs ); + combined += rhs; return combined; } + Columns operator+( Columns&& lhs, Column&& rhs ) { + lhs += CATCH_MOVE( rhs ); + return CATCH_MOVE( lhs ); + } } // namespace TextFlow } // namespace Catch @@ -7043,36 +7862,16 @@ namespace { os.flags(f); } - bool shouldNewline(XmlFormatting fmt) { + constexpr bool shouldNewline(XmlFormatting fmt) { return !!(static_cast>(fmt & XmlFormatting::Newline)); } - bool shouldIndent(XmlFormatting fmt) { + constexpr bool shouldIndent(XmlFormatting fmt) { return !!(static_cast>(fmt & XmlFormatting::Indent)); } } // anonymous namespace - XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) { - return static_cast( - static_cast>(lhs) | - static_cast>(rhs) - ); - } - - XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) { - return static_cast( - static_cast>(lhs) & - static_cast>(rhs) - ); - } - - - XmlEncode::XmlEncode( StringRef str, ForWhat forWhat ) - : m_str( str ), - m_forWhat( forWhat ) - {} - void XmlEncode::encodeTo( std::ostream& os ) const { // Apostrophe escaping not necessary if we always use " to write attributes // (see: http://www.w3.org/TR/xml/#syntax) @@ -7431,26 +8230,11 @@ namespace { return ulpDist <= maxUlpDiff; } -#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) - - float nextafter(float x, float y) { - return ::nextafterf(x, y); - } - - double nextafter(double x, double y) { - return ::nextafter(x, y); - } - -#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^ template FP step(FP start, FP direction, uint64_t steps) { for (uint64_t i = 0; i < steps; ++i) { -#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) start = Catch::nextafter(start, direction); -#else - start = std::nextafter(start, direction); -#endif } return start; } @@ -7584,7 +8368,7 @@ namespace Detail { std::string WithinRelMatcher::describe() const { Catch::ReusableStringStream sstr; - sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other"; + sstr << "and " << ::Catch::Detail::stringify(m_target) << " are within " << m_epsilon * 100. << "% of each other"; return sstr.str(); } @@ -7824,7 +8608,7 @@ namespace Catch { namespace Catch { - AutomakeReporter::~AutomakeReporter() {} + AutomakeReporter::~AutomakeReporter() = default; void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. @@ -8046,7 +8830,7 @@ class AssertionPrinter { return; const auto itEnd = messages.cend(); - const auto N = static_cast(std::distance(itMessage, itEnd)); + const auto N = static_cast(itEnd - itMessage); stream << colourImpl->guardColour( colour ) << " with " << pluralise( N, "message"_sr ) << ':'; @@ -8124,7 +8908,7 @@ class AssertionPrinter { StreamingReporterBase::testRunEnded( _testRunStats ); } - CompactReporter::~CompactReporter() {} + CompactReporter::~CompactReporter() = default; } // end namespace Catch @@ -8319,15 +9103,9 @@ findMax( std::size_t& i, std::size_t& j, std::size_t& k, std::size_t& l ) { return l; } -enum class Justification { Left, Right }; - -struct ColumnInfo { - std::string name; - std::size_t width; - Justification justification; -}; struct ColumnBreak {}; struct RowBreak {}; +struct OutputFlush {}; class Duration { enum class Unit { @@ -8402,6 +9180,14 @@ class Duration { }; } // end anon namespace +enum class Justification { Left, Right }; + +struct ColumnInfo { + std::string name; + std::size_t width; + Justification justification; +}; + class TablePrinter { std::ostream& m_os; std::vector m_columnInfos; @@ -8424,11 +9210,10 @@ class TablePrinter { *this << RowBreak(); TextFlow::Columns headerCols; - auto spacer = TextFlow::Spacer(2); for (auto const& info : m_columnInfos) { assert(info.width > 2); headerCols += TextFlow::Column(info.name).width(info.width - 2); - headerCols += spacer; + headerCols += TextFlow::Spacer( 2 ); } m_os << headerCols << '\n'; @@ -8444,12 +9229,12 @@ class TablePrinter { } template - friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + friend TablePrinter& operator<< (TablePrinter& tp, T const& value) { tp.m_oss << value; return tp; } - friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + friend TablePrinter& operator<< (TablePrinter& tp, ColumnBreak) { auto colStr = tp.m_oss.str(); const auto strSize = colStr.size(); tp.m_oss.str(""); @@ -8471,13 +9256,18 @@ class TablePrinter { return tp; } - friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + friend TablePrinter& operator<< (TablePrinter& tp, RowBreak) { if (tp.m_currentColumn > 0) { tp.m_os << '\n'; tp.m_currentColumn = -1; } return tp; } + + friend TablePrinter& operator<<(TablePrinter& tp, OutputFlush) { + tp.m_os << std::flush; + return tp; + } }; ConsoleReporter::ConsoleReporter(ReporterConfig&& config): @@ -8499,7 +9289,7 @@ ConsoleReporter::ConsoleReporter(ReporterConfig&& config): { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left }, { "samples mean std dev", 14, Justification::Right }, { "iterations low mean low std dev", 14, Justification::Right }, - { "estimated high mean high std dev", 14, Justification::Right } + { "est run time high mean high std dev", 14, Justification::Right } }; } }())) {} @@ -8583,8 +9373,11 @@ void ConsoleReporter::benchmarkPreparing( StringRef name ) { void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { (*m_tablePrinter) << info.samples << ColumnBreak() << info.iterations << ColumnBreak(); - if (!m_config->benchmarkNoAnalysis()) - (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak(); + if ( !m_config->benchmarkNoAnalysis() ) { + ( *m_tablePrinter ) + << Duration( info.estimatedDuration ) << ColumnBreak(); + } + ( *m_tablePrinter ) << OutputFlush{}; } void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { if (m_config->benchmarkNoAnalysis()) @@ -8621,8 +9414,8 @@ void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { m_stream << '\n' << std::flush; StreamingReporterBase::testRunEnded(_testRunStats); } -void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) { - StreamingReporterBase::testRunStarting(_testInfo); +void ConsoleReporter::testRunStarting(TestRunInfo const& _testRunInfo) { + StreamingReporterBase::testRunStarting(_testRunInfo); if ( m_config->testSpec().hasFilters() ) { m_stream << m_colour->guardColour( Colour::BrightYellow ) << "Filters: " << m_config->testSpec() << '\n'; @@ -8775,8 +9568,7 @@ namespace Catch { namespace { struct BySectionInfo { BySectionInfo( SectionInfo const& other ): m_other( other ) {} - BySectionInfo( BySectionInfo const& other ): - m_other( other.m_other ) {} + BySectionInfo( BySectionInfo const& other ) = default; bool operator()( Detail::unique_ptr const& node ) const { @@ -9280,6 +10072,366 @@ namespace Catch { } // namespace Catch +// + +namespace Catch { + namespace { + void writeSourceInfo( JsonObjectWriter& writer, + SourceLineInfo const& sourceInfo ) { + auto source_location_writer = + writer.write( "source-location"_sr ).writeObject(); + source_location_writer.write( "filename"_sr ) + .write( sourceInfo.file ); + source_location_writer.write( "line"_sr ).write( sourceInfo.line ); + } + + void writeTags( JsonArrayWriter writer, std::vector const& tags ) { + for ( auto const& tag : tags ) { + writer.write( tag.original ); + } + } + + void writeProperties( JsonArrayWriter writer, + TestCaseInfo const& info ) { + if ( info.isHidden() ) { writer.write( "is-hidden"_sr ); } + if ( info.okToFail() ) { writer.write( "ok-to-fail"_sr ); } + if ( info.expectedToFail() ) { + writer.write( "expected-to-fail"_sr ); + } + if ( info.throws() ) { writer.write( "throws"_sr ); } + } + + } // namespace + + JsonReporter::JsonReporter( ReporterConfig&& config ): + StreamingReporterBase{ CATCH_MOVE( config ) } { + + m_preferences.shouldRedirectStdOut = true; + // TBD: Do we want to report all assertions? XML reporter does + // not, but for machine-parseable reporters I think the answer + // should be yes. + m_preferences.shouldReportAllAssertions = true; + + m_objectWriters.emplace( m_stream ); + m_writers.emplace( Writer::Object ); + auto& writer = m_objectWriters.top(); + + writer.write( "version"_sr ).write( 1 ); + + { + auto metadata_writer = writer.write( "metadata"_sr ).writeObject(); + metadata_writer.write( "name"_sr ).write( m_config->name() ); + metadata_writer.write( "rng-seed"_sr ).write( m_config->rngSeed() ); + metadata_writer.write( "catch2-version"_sr ) + .write( libraryVersion() ); + if ( m_config->testSpec().hasFilters() ) { + metadata_writer.write( "filters"_sr ) + .write( m_config->testSpec() ); + } + } + } + + JsonReporter::~JsonReporter() { + endListing(); + // TODO: Ensure this closes the top level object, add asserts + assert( m_writers.size() == 1 && "Only the top level object should be open" ); + assert( m_writers.top() == Writer::Object ); + endObject(); + m_stream << '\n' << std::flush; + assert( m_writers.empty() ); + } + + JsonArrayWriter& JsonReporter::startArray() { + m_arrayWriters.emplace( m_arrayWriters.top().writeArray() ); + m_writers.emplace( Writer::Array ); + return m_arrayWriters.top(); + } + JsonArrayWriter& JsonReporter::startArray( StringRef key ) { + m_arrayWriters.emplace( + m_objectWriters.top().write( key ).writeArray() ); + m_writers.emplace( Writer::Array ); + return m_arrayWriters.top(); + } + + JsonObjectWriter& JsonReporter::startObject() { + m_objectWriters.emplace( m_arrayWriters.top().writeObject() ); + m_writers.emplace( Writer::Object ); + return m_objectWriters.top(); + } + JsonObjectWriter& JsonReporter::startObject( StringRef key ) { + m_objectWriters.emplace( + m_objectWriters.top().write( key ).writeObject() ); + m_writers.emplace( Writer::Object ); + return m_objectWriters.top(); + } + + void JsonReporter::endObject() { + assert( isInside( Writer::Object ) ); + m_objectWriters.pop(); + m_writers.pop(); + } + void JsonReporter::endArray() { + assert( isInside( Writer::Array ) ); + m_arrayWriters.pop(); + m_writers.pop(); + } + + bool JsonReporter::isInside( Writer writer ) { + return !m_writers.empty() && m_writers.top() == writer; + } + + void JsonReporter::startListing() { + if ( !m_startedListing ) { startObject( "listings"_sr ); } + m_startedListing = true; + } + void JsonReporter::endListing() { + if ( m_startedListing ) { endObject(); } + m_startedListing = false; + } + + std::string JsonReporter::getDescription() { + return "Outputs listings as JSON. Test listing is Work-in-Progress!"; + } + + void JsonReporter::testRunStarting( TestRunInfo const& runInfo ) { + StreamingReporterBase::testRunStarting( runInfo ); + endListing(); + + assert( isInside( Writer::Object ) ); + startObject( "test-run"_sr ); + startArray( "test-cases"_sr ); + } + + static void writeCounts( JsonObjectWriter&& writer, Counts const& counts ) { + writer.write( "passed"_sr ).write( counts.passed ); + writer.write( "failed"_sr ).write( counts.failed ); + writer.write( "fail-but-ok"_sr ).write( counts.failedButOk ); + writer.write( "skipped"_sr ).write( counts.skipped ); + } + + void JsonReporter::testRunEnded(TestRunStats const& runStats) { + assert( isInside( Writer::Array ) ); + // End "test-cases" + endArray(); + + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + runStats.totals.assertions ); + writeCounts( totals.write( "test-cases"_sr ).writeObject(), + runStats.totals.testCases ); + } + + // End the "test-run" object + endObject(); + } + + void JsonReporter::testCaseStarting( TestCaseInfo const& tcInfo ) { + StreamingReporterBase::testCaseStarting( tcInfo ); + + assert( isInside( Writer::Array ) && + "We should be in the 'test-cases' array" ); + startObject(); + // "test-info" prelude + { + auto testInfo = + m_objectWriters.top().write( "test-info"_sr ).writeObject(); + // TODO: handle testName vs className!! + testInfo.write( "name"_sr ).write( tcInfo.name ); + writeSourceInfo(testInfo, tcInfo.lineInfo); + writeTags( testInfo.write( "tags"_sr ).writeArray(), tcInfo.tags ); + writeProperties( testInfo.write( "properties"_sr ).writeArray(), + tcInfo ); + } + + + // Start the array for individual test runs (testCasePartial pairs) + startArray( "runs"_sr ); + } + + void JsonReporter::testCaseEnded( TestCaseStats const& tcStats ) { + StreamingReporterBase::testCaseEnded( tcStats ); + + // We need to close the 'runs' array before finishing the test case + assert( isInside( Writer::Array ) ); + endArray(); + + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + tcStats.totals.assertions ); + // We do not write the test case totals, because there will always be just one test case here. + // TODO: overall "result" -> success, skip, fail here? Or in partial result? + } + // We do not write out stderr/stdout, because we instead wrote those out in partial runs + + // TODO: aborting? + + // And we also close this test case's object + assert( isInside( Writer::Object ) ); + endObject(); + } + + void JsonReporter::testCasePartialStarting( TestCaseInfo const& /*tcInfo*/, + uint64_t index ) { + startObject(); + m_objectWriters.top().write( "run-idx"_sr ).write( index ); + startArray( "path"_sr ); + // TODO: we want to delay most of the printing to the 'root' section + // TODO: childSection key name? + } + + void JsonReporter::testCasePartialEnded( TestCaseStats const& tcStats, + uint64_t /*index*/ ) { + // Fixme: the top level section handles this. + //// path object + endArray(); + if ( !tcStats.stdOut.empty() ) { + m_objectWriters.top() + .write( "captured-stdout"_sr ) + .write( tcStats.stdOut ); + } + if ( !tcStats.stdErr.empty() ) { + m_objectWriters.top() + .write( "captured-stderr"_sr ) + .write( tcStats.stdErr ); + } + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + tcStats.totals.assertions ); + // We do not write the test case totals, because there will + // always be just one test case here. + // TODO: overall "result" -> success, skip, fail here? Or in + // partial result? + } + // TODO: aborting? + // run object + endObject(); + } + + void JsonReporter::sectionStarting( SectionInfo const& sectionInfo ) { + assert( isInside( Writer::Array ) && + "Section should always start inside an object" ); + // We want to nest top level sections, even though it shares name + // and source loc with the TEST_CASE + auto& sectionObject = startObject(); + sectionObject.write( "kind"_sr ).write( "section"_sr ); + sectionObject.write( "name"_sr ).write( sectionInfo.name ); + writeSourceInfo( m_objectWriters.top(), sectionInfo.lineInfo ); + + + // TBD: Do we want to create this event lazily? It would become + // rather complex, but we could do it, and it would look + // better for empty sections. OTOH, empty sections should + // be rare. + startArray( "path"_sr ); + } + void JsonReporter::sectionEnded( SectionStats const& /*sectionStats */) { + // End the subpath array + endArray(); + // TODO: metadata + // TODO: what info do we have here? + + // End the section object + endObject(); + } + + void JsonReporter::assertionStarting( AssertionInfo const& /*assertionInfo*/ ) {} + void JsonReporter::assertionEnded( AssertionStats const& assertionStats ) { + // TODO: There is lot of different things to handle here, but + // we can fill it in later, after we show that the basic + // outline and streaming reporter impl works well enough. + //if ( !m_config->includeSuccessfulResults() + // && assertionStats.assertionResult.isOk() ) { + // return; + //} + assert( isInside( Writer::Array ) ); + auto assertionObject = m_arrayWriters.top().writeObject(); + + assertionObject.write( "kind"_sr ).write( "assertion"_sr ); + writeSourceInfo( assertionObject, + assertionStats.assertionResult.getSourceInfo() ); + assertionObject.write( "status"_sr ) + .write( assertionStats.assertionResult.isOk() ); + // TODO: handling of result. + // TODO: messages + // TODO: totals? + } + + + void JsonReporter::benchmarkPreparing( StringRef name ) { (void)name; } + void JsonReporter::benchmarkStarting( BenchmarkInfo const& ) {} + void JsonReporter::benchmarkEnded( BenchmarkStats<> const& ) {} + void JsonReporter::benchmarkFailed( StringRef error ) { (void)error; } + + void JsonReporter::listReporters( + std::vector const& descriptions ) { + startListing(); + + auto writer = + m_objectWriters.top().write( "reporters"_sr ).writeArray(); + for ( auto const& desc : descriptions ) { + auto desc_writer = writer.writeObject(); + desc_writer.write( "name"_sr ).write( desc.name ); + desc_writer.write( "description"_sr ).write( desc.description ); + } + } + void JsonReporter::listListeners( + std::vector const& descriptions ) { + startListing(); + + auto writer = + m_objectWriters.top().write( "listeners"_sr ).writeArray(); + + for ( auto const& desc : descriptions ) { + auto desc_writer = writer.writeObject(); + desc_writer.write( "name"_sr ).write( desc.name ); + desc_writer.write( "description"_sr ).write( desc.description ); + } + } + void JsonReporter::listTests( std::vector const& tests ) { + startListing(); + + auto writer = m_objectWriters.top().write( "tests"_sr ).writeArray(); + + for ( auto const& test : tests ) { + auto desc_writer = writer.writeObject(); + auto const& info = test.getTestCaseInfo(); + + desc_writer.write( "name"_sr ).write( info.name ); + desc_writer.write( "class-name"_sr ).write( info.className ); + { + auto tag_writer = desc_writer.write( "tags"_sr ).writeArray(); + for ( auto const& tag : info.tags ) { + tag_writer.write( tag.original ); + } + } + writeSourceInfo( desc_writer, info.lineInfo ); + } + } + void JsonReporter::listTags( std::vector const& tags ) { + startListing(); + + auto writer = m_objectWriters.top().write( "tags"_sr ).writeArray(); + for ( auto const& tag : tags ) { + auto tag_writer = writer.writeObject(); + { + auto aliases_writer = + tag_writer.write( "aliases"_sr ).writeArray(); + for ( auto alias : tag.spellings ) { + aliases_writer.write( alias ); + } + } + tag_writer.write( "count"_sr ).write( tag.count ); + } + } +} // namespace Catch + + #include @@ -9299,6 +10451,8 @@ namespace Catch { gmtime_s(&timeInfo, &rawtime); #elif defined (CATCH_PLATFORM_PLAYSTATION) gmtime_s(&rawtime, &timeInfo); +#elif defined (__IAR_SYSTEMS_ICC__) + timeInfo = *std::gmtime(&rawtime); #else gmtime_r(&rawtime, &timeInfo); #endif @@ -9338,7 +10492,7 @@ namespace Catch { static void normalizeNamespaceMarkers(std::string& str) { std::size_t pos = str.find( "::" ); - while ( pos != str.npos ) { + while ( pos != std::string::npos ) { str.replace( pos, 2, "." ); pos += 1; pos = str.find( "::", pos ); @@ -9352,7 +10506,7 @@ namespace Catch { xml( m_stream ) { m_preferences.shouldRedirectStdOut = true; - m_preferences.shouldReportAllAssertions = true; + m_preferences.shouldReportAllAssertions = false; m_shouldStoreSuccesfulAssertions = false; } @@ -9462,7 +10616,7 @@ namespace Catch { if( !rootName.empty() ) name = rootName + '/' + name; - if( sectionNode.hasAnyAssertions() + if ( sectionNode.stats.assertions.total() > 0 || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty() ) { XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); @@ -9559,7 +10713,7 @@ namespace Catch { } } - if( !result.getMessage().empty() ) + if( result.hasMessage() ) rss << result.getMessage() << '\n'; for( auto const& msg : stats.infoMessages ) if( msg.type == ResultWas::Info ) @@ -9678,7 +10832,6 @@ namespace Catch { } } - // The return value indicates if the messages buffer should be cleared: void MultiReporter::assertionEnded( AssertionStats const& assertionStats ) { const bool reportByDefault = assertionStats.assertionResult.getResultType() != ResultWas::Ok || @@ -9781,6 +10934,11 @@ namespace Catch { } } + void registerListenerImpl( Detail::unique_ptr listenerFactory ) { + getMutableRegistryHub().registerListener( CATCH_MOVE(listenerFactory) ); + } + + } // namespace Detail } // namespace Catch @@ -9846,9 +11004,9 @@ namespace Catch { if (!rootName.empty()) name = rootName + '/' + name; - if ( sectionNode.hasAnyAssertions() + if ( sectionNode.stats.assertions.total() > 0 || !sectionNode.stdOut.empty() - || !sectionNode.stdErr.empty() ) { + || !sectionNode.stdErr.empty() ) { XmlWriter::ScopedElement e = xml.scopedElement("testCase"); xml.writeAttribute("name"_sr, name); xml.writeAttribute("duration"_sr, static_cast(sectionNode.stats.durationInSeconds * 1000)); @@ -9920,7 +11078,7 @@ namespace Catch { } } - if (!result.getMessage().empty()) + if (result.hasMessage()) textRss << result.getMessage() << '\n'; for (auto const& msg : stats.infoMessages) @@ -9954,7 +11112,6 @@ namespace Catch { #include -#include #include namespace Catch { @@ -10105,7 +11262,7 @@ namespace Catch { // using messages.end() directly (or auto) yields compilation error: std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + const std::size_t N = static_cast(itEnd - itMessage); stream << colourImpl->guardColour( colour ) << " with " << pluralise( N, "message"_sr ) << ':'; @@ -10203,7 +11360,7 @@ namespace Catch { } // end anonymous namespace - TeamCityReporter::~TeamCityReporter() {} + TeamCityReporter::~TeamCityReporter() = default; void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) { m_stream << "##teamcity[testSuiteStarted name='" << escape( runInfo.name ) @@ -10377,7 +11534,7 @@ namespace Catch { m_xml.startElement("Catch2TestRun") .writeAttribute("name"_sr, m_config->name()) .writeAttribute("rng-seed"_sr, m_config->rngSeed()) - .writeAttribute("xml-format-version"_sr, 2) + .writeAttribute("xml-format-version"_sr, 3) .writeAttribute("catch2-version"_sr, libraryVersion()); if ( m_config->testSpec().hasFilters() ) { m_xml.writeAttribute( "filters"_sr, m_config->testSpec() ); @@ -10419,11 +11576,13 @@ namespace Catch { // Print any info messages in tags. for( auto const& msg : assertionStats.infoMessages ) { if( msg.type == ResultWas::Info && includeResults ) { - m_xml.scopedElement( "Info" ) - .writeText( msg.message ); + auto t = m_xml.scopedElement( "Info" ); + writeSourceInfo( msg.lineInfo ); + t.writeText( msg.message ); } else if ( msg.type == ResultWas::Warning ) { - m_xml.scopedElement( "Warning" ) - .writeText( msg.message ); + auto t = m_xml.scopedElement( "Warning" ); + writeSourceInfo( msg.lineInfo ); + t.writeText( msg.message ); } } } @@ -10553,26 +11712,23 @@ namespace Catch { } void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { - m_xml.startElement("mean") + m_xml.scopedElement("mean") .writeAttribute("value"_sr, benchmarkStats.mean.point.count()) .writeAttribute("lowerBound"_sr, benchmarkStats.mean.lower_bound.count()) .writeAttribute("upperBound"_sr, benchmarkStats.mean.upper_bound.count()) .writeAttribute("ci"_sr, benchmarkStats.mean.confidence_interval); - m_xml.endElement(); - m_xml.startElement("standardDeviation") + m_xml.scopedElement("standardDeviation") .writeAttribute("value"_sr, benchmarkStats.standardDeviation.point.count()) .writeAttribute("lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count()) .writeAttribute("upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count()) .writeAttribute("ci"_sr, benchmarkStats.standardDeviation.confidence_interval); - m_xml.endElement(); - m_xml.startElement("outliers") + m_xml.scopedElement("outliers") .writeAttribute("variance"_sr, benchmarkStats.outlierVariance) .writeAttribute("lowMild"_sr, benchmarkStats.outliers.low_mild) .writeAttribute("lowSevere"_sr, benchmarkStats.outliers.low_severe) .writeAttribute("highMild"_sr, benchmarkStats.outliers.high_mild) .writeAttribute("highSevere"_sr, benchmarkStats.outliers.high_severe); m_xml.endElement(); - m_xml.endElement(); } void XmlReporter::benchmarkFailed(StringRef error) { diff --git a/src/external/catch2/extras/catch_amalgamated.hpp b/src/external/catch2/extras/catch_amalgamated.hpp index 321cec5d..b7c768b8 100644 --- a/src/external/catch2/extras/catch_amalgamated.hpp +++ b/src/external/catch2/extras/catch_amalgamated.hpp @@ -1,3 +1,4 @@ + // Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at @@ -5,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.3.2 -// Generated: 2023-02-26 10:28:46.785908 +// Catch v3.7.1 +// Generated: 2024-09-17 10:36:40.974985 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -59,238 +60,6 @@ -#ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED -#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED - - - -#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED -#define CATCH_NONCOPYABLE_HPP_INCLUDED - -namespace Catch { - namespace Detail { - - //! Deriving classes become noncopyable and nonmovable - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable&& ) = delete; - NonCopyable& operator=( NonCopyable const& ) = delete; - NonCopyable& operator=( NonCopyable&& ) = delete; - - protected: - NonCopyable() noexcept = default; - }; - - } // namespace Detail -} // namespace Catch - -#endif // CATCH_NONCOPYABLE_HPP_INCLUDED - - -#ifndef CATCH_STRINGREF_HPP_INCLUDED -#define CATCH_STRINGREF_HPP_INCLUDED - -#include -#include -#include -#include - -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; - - private: - static constexpr char const* const s_empty = ""; - - char const* m_start = s_empty; - size_type m_size = 0; - - public: // construction - constexpr StringRef() noexcept = default; - - StringRef( char const* rawChars ) noexcept; - - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} - - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} - - explicit operator std::string() const { - return std::string(m_start, m_size); - } - - public: // operators - auto operator == ( StringRef other ) const noexcept -> bool { - return m_size == other.m_size - && (std::memcmp( m_start, other.m_start, m_size ) == 0); - } - auto operator != (StringRef other) const noexcept -> bool { - return !(*this == other); - } - - constexpr auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } - - bool operator<(StringRef rhs) const noexcept; - - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } - - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, start + size()). - // If start > size(), then the substring is empty. - constexpr StringRef substr(size_type start, size_type length) const noexcept { - if (start < m_size) { - const auto shortened_size = m_size - start; - return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length); - } else { - return StringRef(); - } - } - - // Returns the current start pointer. May not be null-terminated. - constexpr char const* data() const noexcept { - return m_start; - } - - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - - - friend std::string& operator += (std::string& lhs, StringRef sr); - friend std::ostream& operator << (std::ostream& os, StringRef sr); - friend std::string operator+(StringRef lhs, StringRef rhs); - - /** - * Provides a three-way comparison with rhs - * - * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive - * number if lhs > rhs - */ - int compare( StringRef rhs ) const; - }; - - - constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -} // namespace Catch - -constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); -} - -#endif // CATCH_STRINGREF_HPP_INCLUDED - -#include -#include -#include -#include - -namespace Catch { - - enum class Verbosity { - Quiet = 0, - Normal, - High - }; - - struct WarnAbout { enum What { - Nothing = 0x00, - //! A test case or leaf section did not run any assertions - NoAssertions = 0x01, - //! A command line test spec matched no test cases - UnmatchedTestSpec = 0x02, - }; }; - - enum class ShowDurations { - DefaultForReporter, - Always, - Never - }; - enum class TestRunOrder { - Declared, - LexicographicallySorted, - Randomized - }; - enum class ColourMode : std::uint8_t { - //! Let Catch2 pick implementation based on platform detection - PlatformDefault, - //! Use ANSI colour code escapes - ANSI, - //! Use Win32 console colour API - Win32, - //! Don't use any colour - None - }; - struct WaitForKeypress { enum When { - Never, - BeforeStart = 1, - BeforeExit = 2, - BeforeStartAndExit = BeforeStart | BeforeExit - }; }; - - class TestSpec; - class IStream; - - class IConfig : public Detail::NonCopyable { - public: - virtual ~IConfig(); - - virtual bool allowThrows() const = 0; - virtual StringRef name() const = 0; - virtual bool includeSuccessfulResults() const = 0; - virtual bool shouldDebugBreak() const = 0; - virtual bool warnAboutMissingAssertions() const = 0; - virtual bool warnAboutUnmatchedTestSpecs() const = 0; - virtual bool zeroTestsCountAsSuccess() const = 0; - virtual int abortAfter() const = 0; - virtual bool showInvisibles() const = 0; - virtual ShowDurations showDurations() const = 0; - virtual double minDuration() const = 0; - virtual TestSpec const& testSpec() const = 0; - virtual bool hasTestFilters() const = 0; - virtual std::vector const& getTestsOrTags() const = 0; - virtual TestRunOrder runOrder() const = 0; - virtual uint32_t rngSeed() const = 0; - virtual unsigned int shardCount() const = 0; - virtual unsigned int shardIndex() const = 0; - virtual ColourMode defaultColourMode() const = 0; - virtual std::vector const& getSectionsToRun() const = 0; - virtual Verbosity verbosity() const = 0; - - virtual bool skipBenchmarks() const = 0; - virtual bool benchmarkNoAnalysis() const = 0; - virtual unsigned int benchmarkSamples() const = 0; - virtual double benchmarkConfidenceInterval() const = 0; - virtual unsigned int benchmarkResamples() const = 0; - virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; - }; -} - -#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED - - #ifndef CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED #define CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED @@ -318,6 +87,9 @@ namespace Catch { // See e.g.: // https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ +# ifndef __has_extension +# define __has_extension(x) 0 +# endif # include # if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) @@ -345,14 +117,14 @@ namespace Catch { #ifdef __cplusplus -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif - # if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) # define CATCH_CPP17_OR_GREATER # endif +# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +# define CATCH_CPP20_OR_GREATER +# endif + #endif // Only GCC compiler should be used in this block, so other compilers trying to @@ -366,12 +138,18 @@ namespace Catch { # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ + _Pragma( "GCC diagnostic ignored \"-Wunused-result\"" ) + # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" ) # define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) + # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) #endif @@ -444,6 +222,9 @@ namespace Catch { # define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wcomma\"" ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) + #endif // __clang__ @@ -463,7 +244,9 @@ namespace Catch { //////////////////////////////////////////////////////////////////////////////// // Assume that some platforms do not support getenv. -#if defined(CATCH_PLATFORM_WINDOWS_UWP) || defined(CATCH_PLATFORM_PLAYSTATION) +#if defined( CATCH_PLATFORM_WINDOWS_UWP ) || \ + defined( CATCH_PLATFORM_PLAYSTATION ) || \ + defined( _GAMING_XBOX ) # define CATCH_INTERNAL_CONFIG_NO_GETENV #else # define CATCH_INTERNAL_CONFIG_GETENV @@ -681,6 +464,9 @@ namespace Catch { #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS #endif @@ -690,6 +476,16 @@ namespace Catch { #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif +#if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif +#if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS +#endif +#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS +#endif + // The goal of this macro is to avoid evaluation of the arguments, but // still have the compiler warn on problems inside... @@ -703,13 +499,6 @@ namespace Catch { # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS -#endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #define CATCH_TRY if ((true)) @@ -755,38 +544,36 @@ namespace Catch { class IResultCapture; class IConfig; - class IContext { - public: - virtual ~IContext(); // = default + class Context { + IConfig const* m_config = nullptr; + IResultCapture* m_resultCapture = nullptr; - virtual IResultCapture* getResultCapture() = 0; - virtual IConfig const* getConfig() const = 0; - }; + CATCH_EXPORT static Context* currentContext; + friend Context& getCurrentMutableContext(); + friend Context const& getCurrentContext(); + static void createContext(); + friend void cleanUpContext(); - class IMutableContext : public IContext { public: - ~IMutableContext() override; // = default - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setConfig( IConfig const* config ) = 0; + constexpr IResultCapture* getResultCapture() const { + return m_resultCapture; + } + constexpr IConfig const* getConfig() const { return m_config; } + constexpr void setResultCapture( IResultCapture* resultCapture ) { + m_resultCapture = resultCapture; + } + constexpr void setConfig( IConfig const* config ) { m_config = config; } - private: - CATCH_EXPORT static IMutableContext* currentContext; - friend IMutableContext& getCurrentMutableContext(); - friend void cleanUpContext(); - static void createContext(); }; - inline IMutableContext& getCurrentMutableContext() - { - if( !IMutableContext::currentContext ) - IMutableContext::createContext(); - // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) - return *IMutableContext::currentContext; - } + Context& getCurrentMutableContext(); - inline IContext& getCurrentContext() - { - return getCurrentMutableContext(); + inline Context const& getCurrentContext() { + // We duplicate the logic from `getCurrentMutableContext` here, + // to avoid paying the call overhead in debug mode. + if ( !Context::currentContext ) { Context::createContext(); } + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *Context::currentContext; } void cleanUpContext(); @@ -798,16 +585,6 @@ namespace Catch { #endif // CATCH_CONTEXT_HPP_INCLUDED -#ifndef CATCH_INTERFACES_REPORTER_HPP_INCLUDED -#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED - - - -#ifndef CATCH_SECTION_INFO_HPP_INCLUDED -#define CATCH_SECTION_INFO_HPP_INCLUDED - - - #ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED #define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED @@ -822,110 +599,200 @@ namespace Catch { #endif // CATCH_MOVE_AND_FORWARD_HPP_INCLUDED -#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED -#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED - -#include -#include +#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED +#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED namespace Catch { - struct SourceLineInfo { + //! Used to signal that an assertion macro failed + struct TestFailureException{}; + //! Used to signal that the remainder of a test should be skipped + struct TestSkipException {}; - SourceLineInfo() = delete; - constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept: - file( _file ), - line( _line ) - {} + /** + * Outlines throwing of `TestFailureException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_failure_exception(); - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; + /** + * Outlines throwing of `TestSkipException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_skip_exception(); - char const* file; - std::size_t line; +} // namespace Catch - friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); - }; -} +#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) -#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED +#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED +#define CATCH_UNIQUE_NAME_HPP_INCLUDED -#ifndef CATCH_TOTALS_HPP_INCLUDED -#define CATCH_TOTALS_HPP_INCLUDED -#include -namespace Catch { +/** \file + * Wrapper for the CONFIG configuration option + * + * When generating internal unique names, there are two options. Either + * we mix in the current line number, or mix in an incrementing number. + * We prefer the latter, using `__COUNTER__`, but users might want to + * use the former. + */ - struct Counts { - Counts operator - ( Counts const& other ) const; - Counts& operator += ( Counts const& other ); +#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED +#define CATCH_CONFIG_COUNTER_HPP_INCLUDED - std::uint64_t total() const; - bool allPassed() const; - bool allOk() const; - std::uint64_t passed = 0; - std::uint64_t failed = 0; - std::uint64_t failedButOk = 0; - std::uint64_t skipped = 0; - }; +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif - struct Totals { +#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ + !defined( CATCH_CONFIG_NO_COUNTER ) && \ + !defined( CATCH_CONFIG_COUNTER ) +# define CATCH_CONFIG_COUNTER +#endif - Totals operator - ( Totals const& other ) const; - Totals& operator += ( Totals const& other ); - Totals delta( Totals const& prevTotals ) const; +#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif - Counts assertions; - Counts testCases; - }; -} +#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED + + +#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED +#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED + +#include -#endif // CATCH_TOTALS_HPP_INCLUDED + +#ifndef CATCH_STRINGREF_HPP_INCLUDED +#define CATCH_STRINGREF_HPP_INCLUDED + +#include #include +#include +#include + +#include namespace Catch { - struct SectionInfo { - // The last argument is ignored, so that people can write - // SECTION("ShortName", "Proper description that is long") and - // still use the `-c` flag comfortably. - SectionInfo( SourceLineInfo const& _lineInfo, std::string _name, - const char* const = nullptr ): - name(CATCH_MOVE(_name)), - lineInfo(_lineInfo) - {} + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; - std::string name; - SourceLineInfo lineInfo; - }; + static constexpr size_type npos{ static_cast( -1 ) }; - struct SectionEndInfo { - SectionInfo sectionInfo; - Counts prevAssertions; - double durationInSeconds; - }; + private: + static constexpr char const* const s_empty = ""; -} // end namespace Catch + char const* m_start = s_empty; + size_type m_size = 0; -#endif // CATCH_SECTION_INFO_HPP_INCLUDED + public: // construction + constexpr StringRef() noexcept = default; + StringRef( char const* rawChars ) noexcept; -#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED -#define CATCH_ASSERTION_RESULT_HPP_INCLUDED + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + explicit operator std::string() const { + return std::string(m_start, m_size); + } -#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED -#define CATCH_ASSERTION_INFO_HPP_INCLUDED + public: // operators + auto operator == ( StringRef other ) const noexcept -> bool { + return m_size == other.m_size + && (std::memcmp( m_start, other.m_start, m_size ) == 0); + } + auto operator != (StringRef other) const noexcept -> bool { + return !(*this == other); + } + + constexpr auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + bool operator<(StringRef rhs) const noexcept; + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + constexpr StringRef substr(size_type start, size_type length) const noexcept { + if (start < m_size) { + const auto shortened_size = m_size - start; + return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length); + } else { + return StringRef(); + } + } + + // Returns the current start pointer. May not be null-terminated. + constexpr char const* data() const noexcept { + return m_start; + } + + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + + + friend std::string& operator += (std::string& lhs, StringRef rhs); + friend std::ostream& operator << (std::ostream& os, StringRef str); + friend std::string operator+(StringRef lhs, StringRef rhs); + + /** + * Provides a three-way comparison with rhs + * + * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive + * number if lhs > rhs + */ + int compare( StringRef rhs ) const; + }; + + + constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +#endif // CATCH_STRINGREF_HPP_INCLUDED #ifndef CATCH_RESULT_TYPE_HPP_INCLUDED @@ -956,8 +823,10 @@ namespace Catch { }; }; - bool isOk( ResultWas::OfType resultType ); - bool isJustInfo( int flags ); + constexpr bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + constexpr bool isJustInfo( int flags ) { return flags == ResultWas::Info; } // ResultDisposition::Flags enum @@ -969,130 +838,29 @@ namespace Catch { SuppressFail = 0x08 // Failures are reported but do not fail the test }; }; - ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); + constexpr ResultDisposition::Flags operator|( ResultDisposition::Flags lhs, + ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | + static_cast( rhs ) ); + } - bool shouldContinueOnFailure( int flags ); - inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } - bool shouldSuppressFailure( int flags ); + constexpr bool isFalseTest( int flags ) { + return ( flags & ResultDisposition::FalseTest ) != 0; + } + constexpr bool shouldSuppressFailure( int flags ) { + return ( flags & ResultDisposition::SuppressFail ) != 0; + } } // end namespace Catch #endif // CATCH_RESULT_TYPE_HPP_INCLUDED -namespace Catch { - - struct AssertionInfo { - // AssertionInfo() = delete; - - StringRef macroName; - SourceLineInfo lineInfo; - StringRef capturedExpression; - ResultDisposition::Flags resultDisposition; - }; -} // end namespace Catch +#ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED +#define CATCH_UNIQUE_PTR_HPP_INCLUDED -#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED - - -#ifndef CATCH_LAZY_EXPR_HPP_INCLUDED -#define CATCH_LAZY_EXPR_HPP_INCLUDED - -#include - -namespace Catch { - - class ITransientExpression; - - class LazyExpression { - friend class AssertionHandler; - friend struct AssertionStats; - friend class RunContext; - - ITransientExpression const* m_transientExpression = nullptr; - bool m_isNegated; - public: - LazyExpression( bool isNegated ): - m_isNegated(isNegated) - {} - LazyExpression(LazyExpression const& other) = default; - LazyExpression& operator = ( LazyExpression const& ) = delete; - - explicit operator bool() const { - return m_transientExpression != nullptr; - } - - friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; - }; - -} // namespace Catch - -#endif // CATCH_LAZY_EXPR_HPP_INCLUDED - -#include - -namespace Catch { - - struct AssertionResultData - { - AssertionResultData() = delete; - - AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); - - std::string message; - mutable std::string reconstructedExpression; - LazyExpression lazyExpression; - ResultWas::OfType resultType; - - std::string reconstructExpression() const; - }; - - class AssertionResult { - public: - AssertionResult() = delete; - AssertionResult( AssertionInfo const& info, AssertionResultData&& data ); - - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - StringRef getMessage() const; - SourceLineInfo getSourceInfo() const; - StringRef getTestMacroName() const; - - //protected: - AssertionInfo m_info; - AssertionResultData m_resultData; - }; - -} // end namespace Catch - -#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED - - -#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED -#define CATCH_MESSAGE_INFO_HPP_INCLUDED - - - -#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED -#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED - -#include -#include - - - -#ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED -#define CATCH_UNIQUE_PTR_HPP_INCLUDED - -#include -#include +#include +#include namespace Catch { @@ -1199,6 +967,45 @@ namespace Detail { #endif // CATCH_UNIQUE_PTR_HPP_INCLUDED + +#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED +#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_CLOCK_HPP_INCLUDED +#define CATCH_CLOCK_HPP_INCLUDED + +#include + +namespace Catch { + namespace Benchmark { + using IDuration = std::chrono::nanoseconds; + using FDuration = std::chrono::duration; + + template + using TimePoint = typename Clock::time_point; + + using default_clock = std::chrono::steady_clock; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_CLOCK_HPP_INCLUDED + +namespace Catch { + + // We cannot forward declare the type with default template argument + // multiple times, so it is split out into a separate header so that + // we can prevent multiple declarations in dependees + template + struct BenchmarkStats; + +} // end namespace Catch + +#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED + namespace Catch { class AssertionResult; @@ -1215,8 +1022,6 @@ namespace Catch { class IGeneratorTracker; struct BenchmarkInfo; - template > - struct BenchmarkStats; namespace Generators { class GeneratorUntypedBase; @@ -1228,6 +1033,7 @@ namespace Catch { public: virtual ~IResultCapture(); + virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0; virtual bool sectionStarted( StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts& assertions ) = 0; @@ -1261,14 +1067,14 @@ namespace Catch { virtual void handleMessage ( AssertionInfo const& info, ResultWas::OfType resultType, - StringRef message, + std::string&& message, AssertionReaction& reaction ) = 0; virtual void handleUnexpectedExceptionNotThrown ( AssertionInfo const& info, AssertionReaction& reaction ) = 0; virtual void handleUnexpectedInflightException ( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) = 0; virtual void handleIncomplete ( AssertionInfo const& info ) = 0; @@ -1293,415 +1099,308 @@ namespace Catch { #endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED -#include - -namespace Catch { - - struct MessageInfo { - MessageInfo( StringRef _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ); - - StringRef macroName; - std::string message; - SourceLineInfo lineInfo; - ResultWas::OfType type; - unsigned int sequence; - - bool operator == (MessageInfo const& other) const { - return sequence == other.sequence; - } - bool operator < (MessageInfo const& other) const { - return sequence < other.sequence; - } - private: - static unsigned int globalCount; - }; - -} // end namespace Catch -#endif // CATCH_MESSAGE_INFO_HPP_INCLUDED +#ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED +#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED -// Adapted from donated nonius code. -#ifndef CATCH_ESTIMATE_HPP_INCLUDED -#define CATCH_ESTIMATE_HPP_INCLUDED +#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED +#define CATCH_NONCOPYABLE_HPP_INCLUDED namespace Catch { - namespace Benchmark { - template - struct Estimate { - Duration point; - Duration lower_bound; - Duration upper_bound; - double confidence_interval; - - template - operator Estimate() const { - return { point, lower_bound, upper_bound, confidence_interval }; - } - }; - } // namespace Benchmark -} // namespace Catch - -#endif // CATCH_ESTIMATE_HPP_INCLUDED - - -// Adapted from donated nonius code. - -#ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED -#define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED + namespace Detail { -namespace Catch { - namespace Benchmark { - struct OutlierClassification { - int samples_seen = 0; - int low_severe = 0; // more than 3 times IQR below Q1 - int low_mild = 0; // 1.5 to 3 times IQR below Q1 - int high_mild = 0; // 1.5 to 3 times IQR above Q3 - int high_severe = 0; // more than 3 times IQR above Q3 + //! Deriving classes become noncopyable and nonmovable + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable&& ) = delete; + NonCopyable& operator=( NonCopyable const& ) = delete; + NonCopyable& operator=( NonCopyable&& ) = delete; - int total() const { - return low_severe + low_mild + high_mild + high_severe; - } + protected: + NonCopyable() noexcept = default; }; - } // namespace Benchmark -} // namespace Catch -#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED + } // namespace Detail +} // namespace Catch +#endif // CATCH_NONCOPYABLE_HPP_INCLUDED -#include +#include +#include #include #include -#include namespace Catch { - struct ReporterDescription; - struct ListenerDescription; - struct TagInfo; - struct TestCaseInfo; - class TestCaseHandle; - class IConfig; - class IStream; - enum class ColourMode : std::uint8_t; - - struct ReporterConfig { - ReporterConfig( IConfig const* _fullConfig, - Detail::unique_ptr _stream, - ColourMode colourMode, - std::map customOptions ); - - ReporterConfig( ReporterConfig&& ) = default; - ReporterConfig& operator=( ReporterConfig&& ) = default; - ~ReporterConfig(); // = default - - Detail::unique_ptr takeStream() &&; - IConfig const* fullConfig() const; - ColourMode colourMode() const; - std::map const& customOptions() const; - - private: - Detail::unique_ptr m_stream; - IConfig const* m_fullConfig; - ColourMode m_colourMode; - std::map m_customOptions; - }; - - struct TestRunInfo { - constexpr TestRunInfo(StringRef _name) : name(_name) {} - StringRef name; + enum class Verbosity { + Quiet = 0, + Normal, + High }; - struct AssertionStats { - AssertionStats( AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals ); + struct WarnAbout { enum What { + Nothing = 0x00, + //! A test case or leaf section did not run any assertions + NoAssertions = 0x01, + //! A command line test spec matched no test cases + UnmatchedTestSpec = 0x02, + }; }; - AssertionStats( AssertionStats const& ) = default; - AssertionStats( AssertionStats && ) = default; - AssertionStats& operator = ( AssertionStats const& ) = delete; - AssertionStats& operator = ( AssertionStats && ) = delete; - - AssertionResult assertionResult; - std::vector infoMessages; - Totals totals; + enum class ShowDurations { + DefaultForReporter, + Always, + Never }; + enum class TestRunOrder { + Declared, + LexicographicallySorted, + Randomized + }; + enum class ColourMode : std::uint8_t { + //! Let Catch2 pick implementation based on platform detection + PlatformDefault, + //! Use ANSI colour code escapes + ANSI, + //! Use Win32 console colour API + Win32, + //! Don't use any colour + None + }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; - struct SectionStats { - SectionStats( SectionInfo&& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ); + class TestSpec; + class IStream; - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; - }; + class IConfig : public Detail::NonCopyable { + public: + virtual ~IConfig(); - struct TestCaseStats { - TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string&& _stdOut, - std::string&& _stdErr, - bool _aborting ); + virtual bool allowThrows() const = 0; + virtual StringRef name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutUnmatchedTestSpecs() const = 0; + virtual bool zeroTestsCountAsSuccess() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations showDurations() const = 0; + virtual double minDuration() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual std::vector const& getTestsOrTags() const = 0; + virtual TestRunOrder runOrder() const = 0; + virtual uint32_t rngSeed() const = 0; + virtual unsigned int shardCount() const = 0; + virtual unsigned int shardIndex() const = 0; + virtual ColourMode defaultColourMode() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; - TestCaseInfo const * testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; + virtual bool skipBenchmarks() const = 0; + virtual bool benchmarkNoAnalysis() const = 0; + virtual unsigned int benchmarkSamples() const = 0; + virtual double benchmarkConfidenceInterval() const = 0; + virtual unsigned int benchmarkResamples() const = 0; + virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; }; +} - struct TestRunStats { - TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ); +#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED - TestRunInfo runInfo; - Totals totals; - bool aborting; - }; +#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED +#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED - struct BenchmarkInfo { - std::string name; - double estimatedDuration; - int iterations; - unsigned int samples; - unsigned int resamples; - double clockResolution; - double clockCost; - }; - template - struct BenchmarkStats { - BenchmarkInfo info; +#include - std::vector samples; - Benchmark::Estimate mean; - Benchmark::Estimate standardDeviation; - Benchmark::OutlierClassification outliers; - double outlierVariance; +namespace Catch { - template - operator BenchmarkStats() const { - std::vector samples2; - samples2.reserve(samples.size()); - for (auto const& sample : samples) { - samples2.push_back(Duration2(sample)); - } - return { - info, - CATCH_MOVE(samples2), - mean, - standardDeviation, - outliers, - outlierVariance, - }; - } + class TestCaseHandle; + struct TestCaseInfo; + class ITestCaseRegistry; + class IExceptionTranslatorRegistry; + class IExceptionTranslator; + class ReporterRegistry; + class IReporterFactory; + class ITagAliasRegistry; + class ITestInvoker; + class IMutableEnumValuesRegistry; + struct SourceLineInfo; + + class StartupExceptionRegistry; + class EventListenerFactory; + + using IReporterFactoryPtr = Detail::unique_ptr; + + class IRegistryHub { + public: + virtual ~IRegistryHub(); // = default + + virtual ReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; + + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; }; - //! By setting up its preferences, a reporter can modify Catch2's behaviour - //! in some regards, e.g. it can request Catch2 to capture writes to - //! stdout/stderr during test execution, and pass them to the reporter. - struct ReporterPreferences { - //! Catch2 should redirect writes to stdout and pass them to the - //! reporter - bool shouldRedirectStdOut = false; - //! Catch2 should call `Reporter::assertionEnded` even for passing - //! assertions - bool shouldReportAllAssertions = false; + class IMutableRegistryHub { + public: + virtual ~IMutableRegistryHub(); // = default + virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0; + virtual void registerListener( Detail::unique_ptr factory ) = 0; + virtual void registerTest(Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker) = 0; + virtual void registerTranslator( Detail::unique_ptr&& translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; + virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; }; - /** - * The common base for all reporters and event listeners - * - * Implementing classes must also implement: - * - * //! User-friendly description of the reporter/listener type - * static std::string getDescription() - * - * Generally shouldn't be derived from by users of Catch2 directly, - * instead they should derive from one of the utility bases that - * derive from this class. - */ - class IEventListener { - protected: - //! Derived classes can set up their preferences here - ReporterPreferences m_preferences; - //! The test run's config as filled in from CLI and defaults - IConfig const* m_config; + IRegistryHub const& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); - public: - IEventListener( IConfig const* config ): m_config( config ) {} +} - virtual ~IEventListener(); // = default; +#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED - // Implementing class must also provide the following static methods: - // static std::string getDescription(); - ReporterPreferences const& getPreferences() const { - return m_preferences; - } +#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED +#define CATCH_BENCHMARK_STATS_HPP_INCLUDED - //! Called when no test cases match provided test spec - virtual void noMatchingTestCases( StringRef unmatchedSpec ) = 0; - //! Called for all invalid test specs from the cli - virtual void reportInvalidTestSpec( StringRef invalidArgument ) = 0; - /** - * Called once in a testing run before tests are started - * - * Not called if tests won't be run (e.g. only listing will happen) - */ - virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; - //! Called _once_ for each TEST_CASE, no matter how many times it is entered - virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; - //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) - virtual void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) = 0; - //! Called when a `SECTION` is being entered. Not called for skipped sections - virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; +// Adapted from donated nonius code. - //! Called when user-code is being probed before the actual benchmark runs - virtual void benchmarkPreparing( StringRef benchmarkName ) = 0; - //! Called after probe but before the user-code is being benchmarked - virtual void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) = 0; - //! Called with the benchmark results if benchmark successfully finishes - virtual void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) = 0; - //! Called if running the benchmarks fails for any reason - virtual void benchmarkFailed( StringRef benchmarkName ) = 0; +#ifndef CATCH_ESTIMATE_HPP_INCLUDED +#define CATCH_ESTIMATE_HPP_INCLUDED - //! Called before assertion success/failure is evaluated - virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; +namespace Catch { + namespace Benchmark { + template + struct Estimate { + Type point; + Type lower_bound; + Type upper_bound; + double confidence_interval; + }; + } // namespace Benchmark +} // namespace Catch - //! Called after assertion was fully evaluated - virtual void assertionEnded( AssertionStats const& assertionStats ) = 0; +#endif // CATCH_ESTIMATE_HPP_INCLUDED - //! Called after a `SECTION` has finished running - virtual void sectionEnded( SectionStats const& sectionStats ) = 0; - //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) - virtual void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber ) = 0; - //! Called _once_ for each TEST_CASE, no matter how many times it is entered - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; - /** - * Called once after all tests in a testing run are finished - * - * Not called if tests weren't run (e.g. only listings happened) - */ - virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; - /** - * Called with test cases that are skipped due to the test run aborting. - * NOT called for test cases that are explicitly skipped using the `SKIP` macro. - * - * Deprecated - will be removed in the next major release. - */ - virtual void skipTest( TestCaseInfo const& testInfo ) = 0; +// Adapted from donated nonius code. - //! Called if a fatal error (signal/structured exception) occured - virtual void fatalErrorEncountered( StringRef error ) = 0; +#ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED +#define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED - //! Writes out information about provided reporters using reporter-specific format - virtual void listReporters(std::vector const& descriptions) = 0; - //! Writes out the provided listeners descriptions using reporter-specific format - virtual void listListeners(std::vector const& descriptions) = 0; - //! Writes out information about provided tests using reporter-specific format - virtual void listTests(std::vector const& tests) = 0; - //! Writes out information about the provided tags using reporter-specific format - virtual void listTags(std::vector const& tags) = 0; - }; - using IEventListenerPtr = Detail::unique_ptr; +namespace Catch { + namespace Benchmark { + struct OutlierClassification { + int samples_seen = 0; + int low_severe = 0; // more than 3 times IQR below Q1 + int low_mild = 0; // 1.5 to 3 times IQR below Q1 + int high_mild = 0; // 1.5 to 3 times IQR above Q3 + int high_severe = 0; // more than 3 times IQR above Q3 -} // end namespace Catch + constexpr int total() const { + return low_severe + low_mild + high_mild + high_severe; + } + }; + } // namespace Benchmark +} // namespace Catch -#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED +#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED +// The fwd decl & default specialization needs to be seen by VS2017 before +// BenchmarkStats itself, or VS2017 will report compilation error. + +#include +#include +namespace Catch { + + struct BenchmarkInfo { + std::string name; + double estimatedDuration; + int iterations; + unsigned int samples; + unsigned int resamples; + double clockResolution; + double clockCost; + }; -#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED -#define CATCH_UNIQUE_NAME_HPP_INCLUDED + // We need to keep template parameter for backwards compatibility, + // but we also do not want to use the template paraneter. + template + struct BenchmarkStats { + BenchmarkInfo info; + std::vector samples; + Benchmark::Estimate mean; + Benchmark::Estimate standardDeviation; + Benchmark::OutlierClassification outliers; + double outlierVariance; + }; +} // end namespace Catch -/** \file - * Wrapper for the CONFIG configuration option - * - * When generating internal unique names, there are two options. Either - * we mix in the current line number, or mix in an incrementing number. - * We prefer the latter, using `__COUNTER__`, but users might want to - * use the former. - */ +#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED -#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED -#define CATCH_CONFIG_COUNTER_HPP_INCLUDED -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif +// Adapted from donated nonius code. -#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ - !defined( CATCH_CONFIG_NO_COUNTER ) && \ - !defined( CATCH_CONFIG_COUNTER ) -# define CATCH_CONFIG_COUNTER -#endif +#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED +#define CATCH_ENVIRONMENT_HPP_INCLUDED -#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif +namespace Catch { + namespace Benchmark { + struct EnvironmentEstimate { + FDuration mean; + OutlierClassification outliers; + }; + struct Environment { + EnvironmentEstimate clock_resolution; + EnvironmentEstimate clock_cost; + }; + } // namespace Benchmark +} // namespace Catch -#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED +#endif // CATCH_ENVIRONMENT_HPP_INCLUDED // Adapted from donated nonius code. -#ifndef CATCH_CHRONOMETER_HPP_INCLUDED -#define CATCH_CHRONOMETER_HPP_INCLUDED +#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED +#define CATCH_EXECUTION_PLAN_HPP_INCLUDED // Adapted from donated nonius code. -#ifndef CATCH_CLOCK_HPP_INCLUDED -#define CATCH_CLOCK_HPP_INCLUDED - -#include -#include - -namespace Catch { - namespace Benchmark { - template - using ClockDuration = typename Clock::duration; - template - using FloatDuration = std::chrono::duration; +#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED +#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED - template - using TimePoint = typename Clock::time_point; - using default_clock = std::chrono::steady_clock; - template - struct now { - TimePoint operator()() const { - return Clock::now(); - } - }; +// Adapted from donated nonius code. - using fp_seconds = std::chrono::duration>; - } // namespace Benchmark -} // namespace Catch +#ifndef CATCH_CHRONOMETER_HPP_INCLUDED +#define CATCH_CHRONOMETER_HPP_INCLUDED -#endif // CATCH_CLOCK_HPP_INCLUDED // Adapted from donated nonius code. @@ -1709,7 +1408,7 @@ namespace Catch { #ifndef CATCH_OPTIMIZER_HPP_INCLUDED #define CATCH_OPTIMIZER_HPP_INCLUDED -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) # include // atomic_thread_fence #endif @@ -1730,16 +1429,23 @@ namespace Catch { namespace Detail { inline void optimizer_barrier() { keep_memory(); } } // namespace Detail -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) +#if defined(_MSVC_VER) #pragma optimize("", off) +#elif defined(__IAR_SYSTEMS_ICC__) +// For IAR the pragma only affects the following function +#pragma optimize=disable +#endif template inline void keep_memory(T* p) { // thanks @milleniumbug *reinterpret_cast(p) = *reinterpret_cast(p); } // TODO equivalent keep_memory() +#if defined(_MSVC_VER) #pragma optimize("", on) +#endif namespace Detail { inline void optimizer_barrier() { @@ -1761,7 +1467,7 @@ namespace Catch { template inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { - CATCH_FORWARD(fn) (CATCH_FORWARD(args)...); + CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...); } } // namespace Benchmark } // namespace Catch @@ -1769,36 +1475,6 @@ namespace Catch { #endif // CATCH_OPTIMIZER_HPP_INCLUDED -// Adapted from donated nonius code. - -#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED -#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED - - - -#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED -#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED - -namespace Catch { - - //! Used to signal that an assertion macro failed - struct TestFailureException{}; - - /** - * Outlines throwing of `TestFailureException` into a single TU - * - * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. - */ - [[noreturn]] void throw_test_failure_exception(); - - //! Used to signal that the remainder of a test should be skipped - struct TestSkipException{}; - -} // namespace Catch - -#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED - - #ifndef CATCH_META_HPP_INCLUDED #define CATCH_META_HPP_INCLUDED @@ -1840,112 +1516,6 @@ namespace mpl_{ #endif // CATCH_META_HPP_INCLUDED - -#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED -#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED - - -#include - -namespace Catch { - - class TestCaseHandle; - struct TestCaseInfo; - class ITestCaseRegistry; - class IExceptionTranslatorRegistry; - class IExceptionTranslator; - class IReporterRegistry; - class IReporterFactory; - class ITagAliasRegistry; - class ITestInvoker; - class IMutableEnumValuesRegistry; - struct SourceLineInfo; - - class StartupExceptionRegistry; - class EventListenerFactory; - - using IReporterFactoryPtr = Detail::unique_ptr; - - class IRegistryHub { - public: - virtual ~IRegistryHub(); // = default - - virtual IReporterRegistry const& getReporterRegistry() const = 0; - virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; - virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; - virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; - - - virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; - }; - - class IMutableRegistryHub { - public: - virtual ~IMutableRegistryHub(); // = default - virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0; - virtual void registerListener( Detail::unique_ptr factory ) = 0; - virtual void registerTest(Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker) = 0; - virtual void registerTranslator( Detail::unique_ptr&& translator ) = 0; - virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; - virtual void registerStartupException() noexcept = 0; - virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; - }; - - IRegistryHub const& getRegistryHub(); - IMutableRegistryHub& getMutableRegistryHub(); - void cleanUp(); - std::string translateActiveException(); - -} - -#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED - -#include - -namespace Catch { - namespace Benchmark { - namespace Detail { - template - struct CompleteType { using type = T; }; - template <> - struct CompleteType { struct type {}; }; - - template - using CompleteType_t = typename CompleteType::type; - - template - struct CompleteInvoker { - template - static Result invoke(Fun&& fun, Args&&... args) { - return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); - } - }; - template <> - struct CompleteInvoker { - template - static CompleteType_t invoke(Fun&& fun, Args&&... args) { - CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); - return {}; - } - }; - - // invoke and not return void :( - template - CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { - return CompleteInvoker>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); - } - - } // namespace Detail - - template - Detail::CompleteType_t> user_code(Fun&& fun) { - return Detail::complete_invoke(CATCH_FORWARD(fun)); - } - } // namespace Benchmark -} // namespace Catch - -#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED - namespace Catch { namespace Benchmark { namespace Detail { @@ -1963,7 +1533,10 @@ namespace Catch { void start() override { started = Clock::now(); } void finish() override { finished = Clock::now(); } - ClockDuration elapsed() const { return finished - started; } + IDuration elapsed() const { + return std::chrono::duration_cast( + finished - started ); + } TimePoint started; TimePoint finished; @@ -1996,57 +1569,13 @@ namespace Catch { Detail::optimizer_barrier(); } - Detail::ChronometerConcept* impl; - int repeats; - }; - } // namespace Benchmark -} // namespace Catch - -#endif // CATCH_CHRONOMETER_HPP_INCLUDED - - -// Adapted from donated nonius code. - -#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED -#define CATCH_ENVIRONMENT_HPP_INCLUDED - - -namespace Catch { - namespace Benchmark { - template - struct EnvironmentEstimate { - Duration mean; - OutlierClassification outliers; - - template - operator EnvironmentEstimate() const { - return { mean, outliers }; - } - }; - template - struct Environment { - using clock_type = Clock; - EnvironmentEstimate> clock_resolution; - EnvironmentEstimate> clock_cost; - }; - } // namespace Benchmark -} // namespace Catch - -#endif // CATCH_ENVIRONMENT_HPP_INCLUDED - - -// Adapted from donated nonius code. - -#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED -#define CATCH_EXECUTION_PLAN_HPP_INCLUDED - - - -// Adapted from donated nonius code. - -#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED -#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED + Detail::ChronometerConcept* impl; + int repeats; + }; + } // namespace Benchmark +} // namespace Catch +#endif // CATCH_CHRONOMETER_HPP_INCLUDED #include @@ -2068,22 +1597,17 @@ namespace Catch { private: struct callable { virtual void call(Chronometer meter) const = 0; - virtual Catch::Detail::unique_ptr clone() const = 0; virtual ~callable(); // = default; callable() = default; - callable(callable const&) = default; - callable& operator=(callable const&) = default; + callable(callable&&) = default; + callable& operator=(callable&&) = default; }; template struct model : public callable { model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {} model(Fun const& fun_) : fun(fun_) {} - Catch::Detail::unique_ptr clone() const override { - return Catch::Detail::make_unique>( *this ); - } - void call(Chronometer meter) const override { call(meter, is_callable()); } @@ -2097,14 +1621,8 @@ namespace Catch { Fun fun; }; - struct do_nothing { void operator()() const {} }; - - template - BenchmarkFunction(model* c) : f(c) {} - public: - BenchmarkFunction() - : f(new model{ {} }) {} + BenchmarkFunction(); template ::value, int> = 0> @@ -2114,20 +1632,12 @@ namespace Catch { BenchmarkFunction( BenchmarkFunction&& that ) noexcept: f( CATCH_MOVE( that.f ) ) {} - BenchmarkFunction(BenchmarkFunction const& that) - : f(that.f->clone()) {} - BenchmarkFunction& operator=( BenchmarkFunction&& that ) noexcept { f = CATCH_MOVE( that.f ); return *this; } - BenchmarkFunction& operator=(BenchmarkFunction const& that) { - f = that.f->clone(); - return *this; - } - void operator()(Chronometer meter) const { f->call(meter); } private: @@ -2184,6 +1694,57 @@ namespace Catch { +// Adapted from donated nonius code. + +#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED +#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED + + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + struct CompleteType { using type = T; }; + template <> + struct CompleteType { struct type {}; }; + + template + using CompleteType_t = typename CompleteType::type; + + template + struct CompleteInvoker { + template + static Result invoke(Fun&& fun, Args&&... args) { + return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); + } + }; + template <> + struct CompleteInvoker { + template + static CompleteType_t invoke(Fun&& fun, Args&&... args) { + CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); + return {}; + } + }; + + // invoke and not return void :( + template + CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { + return CompleteInvoker>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); + } + + } // namespace Detail + + template + Detail::CompleteType_t> user_code(Fun&& fun) { + return Detail::complete_invoke(CATCH_FORWARD(fun)); + } + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED + + // Adapted from donated nonius code. #ifndef CATCH_TIMING_HPP_INCLUDED @@ -2194,14 +1755,14 @@ namespace Catch { namespace Catch { namespace Benchmark { - template + template struct Timing { - Duration elapsed; + IDuration elapsed; Result result; int iterations; }; - template - using TimingOf = Timing, Detail::CompleteType_t>>; + template + using TimingOf = Timing>>; } // namespace Benchmark } // namespace Catch @@ -2211,9 +1772,9 @@ namespace Catch { namespace Benchmark { namespace Detail { template - TimingOf measure(Fun&& fun, Args&&... args) { + TimingOf measure(Fun&& fun, Args&&... args) { auto start = Clock::now(); - auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...); + auto&& r = Detail::complete_invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); auto end = Clock::now(); auto delta = end - start; return { delta, CATCH_FORWARD(r), 1 }; @@ -2230,11 +1791,11 @@ namespace Catch { namespace Benchmark { namespace Detail { template - TimingOf measure_one(Fun&& fun, int iters, std::false_type) { + TimingOf measure_one(Fun&& fun, int iters, std::false_type) { return Detail::measure(fun, iters); } template - TimingOf measure_one(Fun&& fun, int iters, std::true_type) { + TimingOf measure_one(Fun&& fun, int iters, std::true_type) { Detail::ChronometerModel meter; auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); @@ -2249,8 +1810,8 @@ namespace Catch { void throw_optimized_away_error(); template - TimingOf> - run_for_at_least(ClockDuration how_long, + TimingOf> + run_for_at_least(IDuration how_long, const int initial_iterations, Fun&& fun) { auto iters = initial_iterations; @@ -2270,38 +1831,38 @@ namespace Catch { #endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED -#include -#include +#include namespace Catch { namespace Benchmark { - template struct ExecutionPlan { int iterations_per_sample; - Duration estimated_duration; + FDuration estimated_duration; Detail::BenchmarkFunction benchmark; - Duration warmup_time; + FDuration warmup_time; int warmup_iterations; - template - operator ExecutionPlan() const { - return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations }; - } - template - std::vector> run(const IConfig &cfg, Environment> env) const { + std::vector run(const IConfig &cfg, Environment env) const { // warmup a bit - Detail::run_for_at_least(std::chrono::duration_cast>(warmup_time), warmup_iterations, Detail::repeat(now{})); + Detail::run_for_at_least( + std::chrono::duration_cast( warmup_time ), + warmup_iterations, + Detail::repeat( []() { return Clock::now(); } ) + ); - std::vector> times; - times.reserve(cfg.benchmarkSamples()); - std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] { + std::vector times; + const auto num_samples = cfg.benchmarkSamples(); + times.reserve( num_samples ); + for ( size_t i = 0; i < num_samples; ++i ) { Detail::ChronometerModel model; - this->benchmark(Chronometer(model, iterations_per_sample)); + this->benchmark( Chronometer( model, iterations_per_sample ) ); auto sample_time = model.elapsed() - env.clock_cost.mean; - if (sample_time < FloatDuration::zero()) sample_time = FloatDuration::zero(); - return sample_time / iterations_per_sample; - }); + if ( sample_time < FDuration::zero() ) { + sample_time = FDuration::zero(); + } + times.push_back(sample_time / iterations_per_sample); + } return times; } }; @@ -2324,122 +1885,35 @@ namespace Catch { #define CATCH_STATS_HPP_INCLUDED -#include #include -#include -#include -#include namespace Catch { namespace Benchmark { namespace Detail { using sample = std::vector; - // Used when we know we want == comparison of two doubles - // to centralize warning suppression - bool directCompare( double lhs, double rhs ); - - double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last); - - template - OutlierClassification classify_outliers(Iterator first, Iterator last) { - std::vector copy(first, last); - - auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end()); - auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end()); - auto iqr = q3 - q1; - auto los = q1 - (iqr * 3.); - auto lom = q1 - (iqr * 1.5); - auto him = q3 + (iqr * 1.5); - auto his = q3 + (iqr * 3.); - - OutlierClassification o; - for (; first != last; ++first) { - auto&& t = *first; - if (t < los) ++o.low_severe; - else if (t < lom) ++o.low_mild; - else if (t > his) ++o.high_severe; - else if (t > him) ++o.high_mild; - ++o.samples_seen; - } - return o; - } - - template - double mean(Iterator first, Iterator last) { - auto count = last - first; - double sum = std::accumulate(first, last, 0.); - return sum / static_cast(count); - } - - template - sample jackknife(Estimator&& estimator, Iterator first, Iterator last) { - auto n = static_cast(last - first); - auto second = first; - ++second; - sample results; - results.reserve(n); + double weighted_average_quantile( int k, + int q, + double* first, + double* last ); - for (auto it = first; it != last; ++it) { - std::iter_swap(it, first); - results.push_back(estimator(second, last)); - } + OutlierClassification + classify_outliers( double const* first, double const* last ); - return results; - } + double mean( double const* first, double const* last ); - inline double normal_cdf(double x) { - return std::erfc(-x / std::sqrt(2.0)) / 2.0; - } + double normal_cdf( double x ); double erfc_inv(double x); double normal_quantile(double p); - template - Estimate bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) { - auto n_samples = last - first; - - double point = estimator(first, last); - // Degenerate case with a single sample - if (n_samples == 1) return { point, point, point, confidence_level }; - - sample jack = jackknife(estimator, first, last); - double jack_mean = mean(jack.begin(), jack.end()); - double sum_squares, sum_cubes; - std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair sqcb, double x) -> std::pair { - auto d = jack_mean - x; - auto d2 = d * d; - auto d3 = d2 * d; - return { sqcb.first + d2, sqcb.second + d3 }; - }); - - double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); - long n = static_cast(resample.size()); - double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / static_cast(n); - // degenerate case with uniform samples - if ( directCompare( prob_n, 0. ) ) { - return { point, point, point, confidence_level }; - } - - double bias = normal_quantile(prob_n); - double z1 = normal_quantile((1. - confidence_level) / 2.); - - auto cumn = [n]( double x ) -> long { - return std::lround( normal_cdf( x ) * static_cast(n) ); - }; - auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); }; - double b1 = bias + z1; - double b2 = bias - z1; - double a1 = a(b1); - double a2 = a(b2); - auto lo = static_cast((std::max)(cumn(a1), 0l)); - auto hi = static_cast((std::min)(cumn(a2), n - 1)); - - return { point, resample[lo], resample[hi], confidence_level }; - } - - double outlier_variance(Estimate mean, Estimate stddev, int n); + Estimate + bootstrap( double confidence_level, + double* first, + double* last, + sample const& resample, + double ( *estimator )( double const*, double const* ) ); struct bootstrap_analysis { Estimate mean; @@ -2447,7 +1921,10 @@ namespace Catch { double outlier_variance; }; - bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector::iterator first, std::vector::iterator last); + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + double* first, + double* last); } // namespace Detail } // namespace Benchmark } // namespace Catch @@ -2455,7 +1932,6 @@ namespace Catch { #endif // CATCH_STATS_HPP_INCLUDED #include -#include #include #include @@ -2464,48 +1940,53 @@ namespace Catch { namespace Detail { template std::vector resolution(int k) { - std::vector> times; - times.reserve(static_cast(k + 1)); - std::generate_n(std::back_inserter(times), k + 1, now{}); + const size_t points = static_cast( k + 1 ); + // To avoid overhead from the branch inside vector::push_back, + // we allocate them all and then overwrite. + std::vector> times(points); + for ( auto& time : times ) { + time = Clock::now(); + } std::vector deltas; deltas.reserve(static_cast(k)); - std::transform(std::next(times.begin()), times.end(), times.begin(), - std::back_inserter(deltas), - [](TimePoint a, TimePoint b) { return static_cast((a - b).count()); }); + for ( size_t idx = 1; idx < points; ++idx ) { + deltas.push_back( static_cast( + ( times[idx] - times[idx - 1] ).count() ) ); + } return deltas; } - const auto warmup_iterations = 10000; - const auto warmup_time = std::chrono::milliseconds(100); - const auto minimum_ticks = 1000; - const auto warmup_seed = 10000; - const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); - const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); - const auto clock_cost_estimation_tick_limit = 100000; - const auto clock_cost_estimation_time = std::chrono::milliseconds(10); - const auto clock_cost_estimation_iterations = 10000; + constexpr auto warmup_iterations = 10000; + constexpr auto warmup_time = std::chrono::milliseconds(100); + constexpr auto minimum_ticks = 1000; + constexpr auto warmup_seed = 10000; + constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500); + constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1); + constexpr auto clock_cost_estimation_tick_limit = 100000; + constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10); + constexpr auto clock_cost_estimation_iterations = 10000; template int warmup() { - return run_for_at_least(std::chrono::duration_cast>(warmup_time), warmup_seed, &resolution) + return run_for_at_least(warmup_time, warmup_seed, &resolution) .iterations; } template - EnvironmentEstimate> estimate_clock_resolution(int iterations) { - auto r = run_for_at_least(std::chrono::duration_cast>(clock_resolution_estimation_time), iterations, &resolution) + EnvironmentEstimate estimate_clock_resolution(int iterations) { + auto r = run_for_at_least(clock_resolution_estimation_time, iterations, &resolution) .result; return { - FloatDuration(mean(r.begin(), r.end())), - classify_outliers(r.begin(), r.end()), + FDuration(mean(r.data(), r.data() + r.size())), + classify_outliers(r.data(), r.data() + r.size()), }; } template - EnvironmentEstimate> estimate_clock_cost(FloatDuration resolution) { + EnvironmentEstimate estimate_clock_cost(FDuration resolution) { auto time_limit = (std::min)( resolution * clock_cost_estimation_tick_limit, - FloatDuration(clock_cost_estimation_time_limit)); + FDuration(clock_cost_estimation_time_limit)); auto time_clock = [](int k) { return Detail::measure([k] { for (int i = 0; i < k; ++i) { @@ -2516,26 +1997,28 @@ namespace Catch { }; time_clock(1); int iters = clock_cost_estimation_iterations; - auto&& r = run_for_at_least(std::chrono::duration_cast>(clock_cost_estimation_time), iters, time_clock); + auto&& r = run_for_at_least(clock_cost_estimation_time, iters, time_clock); std::vector times; int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); times.reserve(static_cast(nsamples)); - std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { - return static_cast((time_clock(r.iterations) / r.iterations).count()); - }); + for ( int s = 0; s < nsamples; ++s ) { + times.push_back( static_cast( + ( time_clock( r.iterations ) / r.iterations ) + .count() ) ); + } return { - FloatDuration(mean(times.begin(), times.end())), - classify_outliers(times.begin(), times.end()), + FDuration(mean(times.data(), times.data() + times.size())), + classify_outliers(times.data(), times.data() + times.size()), }; } template - Environment> measure_environment() { + Environment measure_environment() { #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wexit-time-destructors" #endif - static Catch::Detail::unique_ptr>> env; + static Catch::Detail::unique_ptr env; #if defined(__clang__) # pragma clang diagnostic pop #endif @@ -2547,7 +2030,7 @@ namespace Catch { auto resolution = Detail::estimate_clock_resolution(iters); auto cost = Detail::estimate_clock_cost(resolution.mean); - env = Catch::Detail::make_unique>>( Environment>{resolution, cost} ); + env = Catch::Detail::make_unique( Environment{resolution, cost} ); return *env; } } // namespace Detail @@ -2570,95 +2053,29 @@ namespace Catch { #define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED -#include #include -#include namespace Catch { namespace Benchmark { - template struct SampleAnalysis { - std::vector samples; - Estimate mean; - Estimate standard_deviation; + std::vector samples; + Estimate mean; + Estimate standard_deviation; OutlierClassification outliers; double outlier_variance; - - template - operator SampleAnalysis() const { - std::vector samples2; - samples2.reserve(samples.size()); - std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); - return { - CATCH_MOVE(samples2), - mean, - standard_deviation, - outliers, - outlier_variance, - }; - } }; } // namespace Benchmark } // namespace Catch #endif // CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED -#include -#include -#include namespace Catch { + class IConfig; + namespace Benchmark { namespace Detail { - template - SampleAnalysis analyse(const IConfig &cfg, Environment, Iterator first, Iterator last) { - if (!cfg.benchmarkNoAnalysis()) { - std::vector samples; - samples.reserve(static_cast(last - first)); - std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); }); - - auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end()); - auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end()); - - auto wrap_estimate = [](Estimate e) { - return Estimate { - Duration(e.point), - Duration(e.lower_bound), - Duration(e.upper_bound), - e.confidence_interval, - }; - }; - std::vector samples2; - samples2.reserve(samples.size()); - std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); }); - return { - CATCH_MOVE(samples2), - wrap_estimate(analysis.mean), - wrap_estimate(analysis.standard_deviation), - outliers, - analysis.outlier_variance, - }; - } else { - std::vector samples; - samples.reserve(static_cast(last - first)); - - Duration mean = Duration(0); - int i = 0; - for (auto it = first; it < last; ++it, ++i) { - samples.push_back(Duration(*it)); - mean += Duration(*it); - } - mean /= i; - - return { - CATCH_MOVE(samples), - Estimate{mean, mean, mean, 0.0}, - Estimate{Duration(0), Duration(0), Duration(0), 0.0}, - OutlierClassification{}, - 0.0 - }; - } - } + SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last); } // namespace Detail } // namespace Benchmark } // namespace Catch @@ -2666,9 +2083,9 @@ namespace Catch { #endif // CATCH_ANALYSE_HPP_INCLUDED #include -#include +#include +#include #include -#include #include namespace Catch { @@ -2682,16 +2099,18 @@ namespace Catch { : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} template - ExecutionPlan> prepare(const IConfig &cfg, Environment> env) const { + ExecutionPlan prepare(const IConfig &cfg, Environment env) { auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; auto run_time = std::max(min_time, std::chrono::duration_cast(cfg.benchmarkWarmupTime())); - auto&& test = Detail::run_for_at_least(std::chrono::duration_cast>(run_time), 1, fun); + auto&& test = Detail::run_for_at_least(std::chrono::duration_cast(run_time), 1, fun); int new_iters = static_cast(std::ceil(min_time * test.iterations / test.elapsed)); - return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; + return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; } template void run() { + static_assert( Clock::is_steady, + "Benchmarking clock should be steady" ); auto const* cfg = getCurrentContext().getConfig(); auto env = Detail::measure_environment(); @@ -2718,10 +2137,10 @@ namespace Catch { return plan.template run(*cfg, env); }); - auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); - BenchmarkStats> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; + auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size()); + BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; getResultCapture().benchmarkEnded(stats); - } CATCH_CATCH_ANON (TestFailureException) { + } CATCH_CATCH_ANON (TestFailureException const&) { getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); } CATCH_CATCH_ALL{ getResultCapture().benchmarkFailed(translateActiveException()); @@ -2889,6 +2308,7 @@ namespace Catch { #ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED #define CATCH_CONFIG_WCHAR_HPP_INCLUDED + // We assume that WCHAR should be enabled by default, and only disabled // for a shortlist (so far only DJGPP) of compilers. @@ -3112,7 +2532,6 @@ namespace Catch { } // namespace Detail - // If we decide for C++14, change these to enable_if_ts template struct StringMaker { template @@ -3276,11 +2695,11 @@ namespace Catch { }; template<> struct StringMaker { - static std::string convert(signed char c); + static std::string convert(signed char value); }; template<> struct StringMaker { - static std::string convert(unsigned char c); + static std::string convert(unsigned char value); }; template<> @@ -3395,6 +2814,12 @@ namespace Catch { } } }; + template <> + struct StringMaker { + static std::string convert(const std::nullopt_t&) { + return "{ }"; + } + }; } #endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER @@ -3727,58 +3152,207 @@ namespace Catch { return lhs.m_value < static_cast(rhs) || lhs == rhs; } - template ::value>> - friend bool operator >= ( T const& lhs, Approx const& rhs ) { - return static_cast(lhs) > rhs.m_value || lhs == rhs; + template ::value>> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template ::value>> + Approx& epsilon( T const& newEpsilon ) { + const auto epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template ::value>> + Approx& margin( T const& newMargin ) { + const auto marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template ::value>> + Approx& scale( T const& newScale ) { + m_scale = static_cast(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; + +namespace literals { + Approx operator ""_a(long double val); + Approx operator ""_a(unsigned long long val); +} // end namespace literals + +template<> +struct StringMaker { + static std::string convert(Catch::Approx const& value); +}; + +} // end namespace Catch + +#endif // CATCH_APPROX_HPP_INCLUDED + + +#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED +#define CATCH_ASSERTION_INFO_HPP_INCLUDED + + + +#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED +#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED + +#include +#include + +namespace Catch { + + struct SourceLineInfo { + + SourceLineInfo() = delete; + constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept: + file( _file ), + line( _line ) + {} + + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + + friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); + }; +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED + +namespace Catch { + + struct AssertionInfo { + // AssertionInfo() = delete; + + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + }; + +} // end namespace Catch + +#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED + + +#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED +#define CATCH_ASSERTION_RESULT_HPP_INCLUDED + + + +#ifndef CATCH_LAZY_EXPR_HPP_INCLUDED +#define CATCH_LAZY_EXPR_HPP_INCLUDED + +#include + +namespace Catch { + + class ITransientExpression; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + constexpr LazyExpression( bool isNegated ): + m_isNegated(isNegated) + {} + constexpr LazyExpression(LazyExpression const& other) = default; + LazyExpression& operator = ( LazyExpression const& ) = delete; + + constexpr explicit operator bool() const { + return m_transientExpression != nullptr; } - template ::value>> - friend bool operator >= ( Approx const& lhs, T const& rhs ) { - return lhs.m_value > static_cast(rhs) || lhs == rhs; - } + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; - template ::value>> - Approx& epsilon( T const& newEpsilon ) { - const auto epsilonAsDouble = static_cast(newEpsilon); - setEpsilon(epsilonAsDouble); - return *this; - } +} // namespace Catch - template ::value>> - Approx& margin( T const& newMargin ) { - const auto marginAsDouble = static_cast(newMargin); - setMargin(marginAsDouble); - return *this; - } +#endif // CATCH_LAZY_EXPR_HPP_INCLUDED - template ::value>> - Approx& scale( T const& newScale ) { - m_scale = static_cast(newScale); - return *this; - } +#include - std::string toString() const; +namespace Catch { - private: - double m_epsilon; - double m_margin; - double m_scale; - double m_value; + struct AssertionResultData + { + AssertionResultData() = delete; + + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); + + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; + + std::string reconstructExpression() const; }; -namespace literals { - Approx operator ""_a(long double val); - Approx operator ""_a(unsigned long long val); -} // end namespace literals + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData&& data ); -template<> -struct StringMaker { - static std::string convert(Catch::Approx const& value); -}; + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + StringRef getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; } // end namespace Catch -#endif // CATCH_APPROX_HPP_INCLUDED +#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED + + +#ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED +#define CATCH_CASE_SENSITIVE_HPP_INCLUDED + +namespace Catch { + + enum class CaseSensitive { Yes, No }; + +} // namespace Catch + +#endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED #ifndef CATCH_CONFIG_HPP_INCLUDED @@ -3800,18 +3374,6 @@ struct StringMaker { #define CATCH_WILDCARD_PATTERN_HPP_INCLUDED - -#ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED -#define CATCH_CASE_SENSITIVE_HPP_INCLUDED - -namespace Catch { - - enum class CaseSensitive { Yes, No }; - -} // namespace Catch - -#endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED - #include namespace Catch @@ -3945,6 +3507,7 @@ namespace Catch { #ifndef CATCH_OPTIONAL_HPP_INCLUDED #define CATCH_OPTIONAL_HPP_INCLUDED + #include namespace Catch { @@ -3953,35 +3516,50 @@ namespace Catch { template class Optional { public: - Optional() : nullableValue( nullptr ) {} - Optional( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Optional( Optional const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) - {} + Optional(): nullableValue( nullptr ) {} + ~Optional() { reset(); } + + Optional( T const& _value ): + nullableValue( new ( storage ) T( _value ) ) {} + Optional( T&& _value ): + nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {} - ~Optional() { + Optional& operator=( T const& _value ) { reset(); + nullableValue = new ( storage ) T( _value ); + return *this; + } + Optional& operator=( T&& _value ) { + reset(); + nullableValue = new ( storage ) T( CATCH_MOVE( _value ) ); + return *this; } - Optional& operator= ( Optional const& _other ) { - if( &_other != this ) { + Optional( Optional const& _other ): + nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {} + Optional( Optional&& _other ): + nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) ) + : nullptr ) {} + + Optional& operator=( Optional const& _other ) { + if ( &_other != this ) { reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); + if ( _other ) { nullableValue = new ( storage ) T( *_other ); } } return *this; } - Optional& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); + Optional& operator=( Optional&& _other ) { + if ( &_other != this ) { + reset(); + if ( _other ) { + nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) ); + } + } return *this; } void reset() { - if( nullableValue ) - nullableValue->~T(); + if ( nullableValue ) { nullableValue->~T(); } nullableValue = nullptr; } @@ -4028,7 +3606,7 @@ namespace Catch { } private: - T *nullableValue; + T* nullableValue; alignas(alignof(T)) char storage[sizeof(T)]; }; @@ -4051,151 +3629,16 @@ namespace Catch { Default }; - std::uint32_t generateRandomSeed(GenerateFrom from); - -} // end namespace Catch - -#endif // CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED - - -#ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED -#define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED - - - -#ifndef CATCH_CONSOLE_COLOUR_HPP_INCLUDED -#define CATCH_CONSOLE_COLOUR_HPP_INCLUDED - - -#include -#include - -namespace Catch { - - enum class ColourMode : std::uint8_t; - class IStream; - - struct Colour { - enum Code { - None = 0, - - White, - Red, - Green, - Blue, - Cyan, - Yellow, - Grey, - - Bright = 0x10, - - BrightRed = Bright | Red, - BrightGreen = Bright | Green, - LightGrey = Bright | Grey, - BrightWhite = Bright | White, - BrightYellow = Bright | Yellow, - - // By intention - FileName = LightGrey, - Warning = BrightYellow, - ResultError = BrightRed, - ResultSuccess = BrightGreen, - ResultExpectedFailure = Warning, - - Error = BrightRed, - Success = Green, - Skip = LightGrey, - - OriginalExpression = Cyan, - ReconstructedExpression = BrightYellow, - - SecondaryText = LightGrey, - Headers = White - }; - }; - - class ColourImpl { - protected: - //! The associated stream of this ColourImpl instance - IStream* m_stream; - public: - ColourImpl( IStream* stream ): m_stream( stream ) {} - - //! RAII wrapper around writing specific colour of text using specific - //! colour impl into a stream. - class ColourGuard { - ColourImpl const* m_colourImpl; - Colour::Code m_code; - bool m_engaged = false; - - public: - //! Does **not** engage the guard/start the colour - ColourGuard( Colour::Code code, - ColourImpl const* colour ); - - ColourGuard( ColourGuard const& rhs ) = delete; - ColourGuard& operator=( ColourGuard const& rhs ) = delete; - - ColourGuard( ColourGuard&& rhs ) noexcept; - ColourGuard& operator=( ColourGuard&& rhs ) noexcept; - - //! Removes colour _if_ the guard was engaged - ~ColourGuard(); - - /** - * Explicitly engages colour for given stream. - * - * The API based on operator<< should be preferred. - */ - ColourGuard& engage( std::ostream& stream ) &; - /** - * Explicitly engages colour for given stream. - * - * The API based on operator<< should be preferred. - */ - ColourGuard&& engage( std::ostream& stream ) &&; - - private: - //! Engages the guard and starts using colour - friend std::ostream& operator<<( std::ostream& lhs, - ColourGuard& guard ) { - guard.engageImpl( lhs ); - return lhs; - } - //! Engages the guard and starts using colour - friend std::ostream& operator<<( std::ostream& lhs, - ColourGuard&& guard) { - guard.engageImpl( lhs ); - return lhs; - } - - void engageImpl( std::ostream& stream ); - - }; - - virtual ~ColourImpl(); // = default - /** - * Creates a guard object for given colour and this colour impl - * - * **Important:** - * the guard starts disengaged, and has to be engaged explicitly. - */ - ColourGuard guardColour( Colour::Code colourCode ); - - private: - virtual void use( Colour::Code colourCode ) const = 0; - }; - - //! Provides ColourImpl based on global config and target compilation platform - Detail::unique_ptr makeColourImpl( ColourMode colourSelection, - IStream* stream ); - - //! Checks if specific colour impl has been compiled into the binary - bool isColourImplAvailable( ColourMode colourSelection ); + std::uint32_t generateRandomSeed(GenerateFrom from); } // end namespace Catch -#endif // CATCH_CONSOLE_COLOUR_HPP_INCLUDED +#endif // CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED + + +#ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED +#define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED + #include #include @@ -4322,7 +3765,7 @@ namespace Catch { bool benchmarkNoAnalysis = false; unsigned int benchmarkSamples = 100; double benchmarkConfidenceInterval = 0.95; - unsigned int benchmarkResamples = 100000; + unsigned int benchmarkResamples = 100'000; std::chrono::milliseconds::rep benchmarkWarmupTime = 100; Verbosity verbosity = Verbosity::Normal; @@ -4424,6 +3867,29 @@ namespace Catch { + +/** \file + * Wrapper for the CATCH_CONFIG_PREFIX_MESSAGES configuration option + * + * CATCH_CONFIG_PREFIX_ALL can be used to avoid clashes with other macros + * by prepending CATCH_. This may not be desirable if the only clashes are with + * logger macros such as INFO and WARN. In this cases + * CATCH_CONFIG_PREFIX_MESSAGES can be used to only prefix a small subset + * of relevant macros. + * + */ + +#ifndef CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED +#define CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED + + +#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_PREFIX_MESSAGES) + #define CATCH_CONFIG_PREFIX_MESSAGES +#endif + +#endif // CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED + + #ifndef CATCH_STREAM_END_STOP_HPP_INCLUDED #define CATCH_STREAM_END_STOP_HPP_INCLUDED @@ -4435,10 +3901,10 @@ namespace Catch { // as well as // << stuff +StreamEndStop struct StreamEndStop { - StringRef operator+() const { return StringRef(); } + constexpr StringRef operator+() const { return StringRef(); } template - friend T const& operator+( T const& value, StreamEndStop ) { + constexpr friend T const& operator+( T const& value, StreamEndStop ) { return value; } }; @@ -4447,12 +3913,47 @@ namespace Catch { #endif // CATCH_STREAM_END_STOP_HPP_INCLUDED + +#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED +#define CATCH_MESSAGE_INFO_HPP_INCLUDED + + +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( StringRef _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; + + bool operator == (MessageInfo const& other) const { + return sequence == other.sequence; + } + bool operator < (MessageInfo const& other) const { + return sequence < other.sequence; + } + private: + static unsigned int globalCount; + }; + +} // end namespace Catch + +#endif // CATCH_MESSAGE_INFO_HPP_INCLUDED + #include #include namespace Catch { struct SourceLineInfo; + class IResultCapture; struct MessageStream { @@ -4493,7 +3994,7 @@ namespace Catch { class Capturer { std::vector m_messages; - IResultCapture& m_resultCapture = getResultCapture(); + IResultCapture& m_resultCapture; size_t m_captured = 0; public: Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); @@ -4524,7 +4025,7 @@ namespace Catch { do { \ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) /////////////////////////////////////////////////////////////////////////////// @@ -4544,28 +4045,28 @@ namespace Catch { Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) -#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) +#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) #define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg ) #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__ ) -#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) +#elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) #define CATCH_INFO( msg ) (void)(0) #define CATCH_UNSCOPED_INFO( msg ) (void)(0) #define CATCH_WARN( msg ) (void)(0) #define CATCH_CAPTURE( ... ) (void)(0) -#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) +#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) #define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__ ) -#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) +#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) #define INFO( msg ) (void)(0) #define UNSCOPED_INFO( msg ) (void)(0) @@ -4580,6 +4081,75 @@ namespace Catch { #endif // CATCH_MESSAGE_HPP_INCLUDED +#ifndef CATCH_SECTION_INFO_HPP_INCLUDED +#define CATCH_SECTION_INFO_HPP_INCLUDED + + + +#ifndef CATCH_TOTALS_HPP_INCLUDED +#define CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct Counts { + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); + + std::uint64_t total() const; + bool allPassed() const; + bool allOk() const; + + std::uint64_t passed = 0; + std::uint64_t failed = 0; + std::uint64_t failedButOk = 0; + std::uint64_t skipped = 0; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); + + Totals delta( Totals const& prevTotals ) const; + + Counts assertions; + Counts testCases; + }; +} + +#endif // CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct SectionInfo { + // The last argument is ignored, so that people can write + // SECTION("ShortName", "Proper description that is long") and + // still use the `-c` flag comfortably. + SectionInfo( SourceLineInfo const& _lineInfo, std::string _name, + const char* const = nullptr ): + name(CATCH_MOVE(_name)), + lineInfo(_lineInfo) + {} + + std::string name; + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +#endif // CATCH_SECTION_INFO_HPP_INCLUDED + + #ifndef CATCH_SESSION_HPP_INCLUDED #define CATCH_SESSION_HPP_INCLUDED @@ -4683,17 +4253,16 @@ namespace Catch { enum class TokenType { Option, Argument }; struct Token { TokenType type; - std::string token; + StringRef token; }; // Abstracts iterators into args as a stream of tokens, with option // arguments uniformly handled class TokenStream { - using Iterator = std::vector::const_iterator; + using Iterator = std::vector::const_iterator; Iterator it; Iterator itEnd; std::vector m_tokenBuffer; - void loadBuffer(); public: @@ -4745,12 +4314,17 @@ namespace Catch { ResultType m_type; }; - template class ResultValueBase : public ResultBase { + template + class ResultValueBase : public ResultBase { public: - auto value() const -> T const& { + T const& value() const& { enforceOk(); return m_value; } + T&& value() && { + enforceOk(); + return CATCH_MOVE( m_value ); + } protected: ResultValueBase( ResultType type ): ResultBase( type ) {} @@ -4760,13 +4334,23 @@ namespace Catch { if ( m_type == ResultType::Ok ) new ( &m_value ) T( other.m_value ); } + ResultValueBase( ResultValueBase&& other ): + ResultBase( other ) { + if ( m_type == ResultType::Ok ) + new ( &m_value ) T( CATCH_MOVE(other.m_value) ); + } - ResultValueBase( ResultType, T const& value ): ResultBase( ResultType::Ok ) { + + ResultValueBase( ResultType, T const& value ): + ResultBase( ResultType::Ok ) { new ( &m_value ) T( value ); } + ResultValueBase( ResultType, T&& value ): + ResultBase( ResultType::Ok ) { + new ( &m_value ) T( CATCH_MOVE(value) ); + } - auto operator=( ResultValueBase const& other ) - -> ResultValueBase& { + ResultValueBase& operator=( ResultValueBase const& other ) { if ( m_type == ResultType::Ok ) m_value.~T(); ResultBase::operator=( other ); @@ -4774,6 +4358,14 @@ namespace Catch { new ( &m_value ) T( other.m_value ); return *this; } + ResultValueBase& operator=( ResultValueBase&& other ) { + if ( m_type == ResultType::Ok ) m_value.~T(); + ResultBase::operator=( other ); + if ( m_type == ResultType::Ok ) + new ( &m_value ) T( CATCH_MOVE(other.m_value) ); + return *this; + } + ~ResultValueBase() override { if ( m_type == ResultType::Ok ) @@ -4801,8 +4393,8 @@ namespace Catch { } template - static auto ok( U const& value ) -> BasicResult { - return { ResultType::Ok, value }; + static auto ok( U&& value ) -> BasicResult { + return { ResultType::Ok, CATCH_FORWARD(value) }; } static auto ok() -> BasicResult { return { ResultType::Ok }; } static auto logicError( std::string&& message ) @@ -4849,12 +4441,15 @@ namespace Catch { class ParseState { public: ParseState( ParseResultType type, - TokenStream const& remainingTokens ); + TokenStream remainingTokens ); ParseResultType type() const { return m_type; } - TokenStream const& remainingTokens() const { + TokenStream const& remainingTokens() const& { return m_remainingTokens; } + TokenStream&& remainingTokens() && { + return CATCH_MOVE( m_remainingTokens ); + } private: ParseResultType m_type; @@ -4867,7 +4462,7 @@ namespace Catch { struct HelpColumns { std::string left; - std::string right; + StringRef descriptions; }; template @@ -5027,7 +4622,7 @@ namespace Catch { virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } virtual auto parse( std::string const& exeName, - TokenStream const& tokens ) const + TokenStream tokens ) const -> InternalParseResult = 0; virtual size_t cardinality() const; @@ -5047,8 +4642,8 @@ namespace Catch { protected: Optionality m_optionality = Optionality::Optional; std::shared_ptr m_ref; - std::string m_hint; - std::string m_description; + StringRef m_hint; + StringRef m_description; explicit ParserRefImpl( std::shared_ptr const& ref ): m_ref( ref ) {} @@ -5057,28 +4652,32 @@ namespace Catch { template ParserRefImpl( accept_many_t, LambdaT const& ref, - std::string const& hint ): + StringRef hint ): m_ref( std::make_shared>( ref ) ), m_hint( hint ) {} template ::value>> - ParserRefImpl( T& ref, std::string const& hint ): + ParserRefImpl( T& ref, StringRef hint ): m_ref( std::make_shared>( ref ) ), m_hint( hint ) {} template ::value>> - ParserRefImpl( LambdaT const& ref, std::string const& hint ): + ParserRefImpl( LambdaT const& ref, StringRef hint ): m_ref( std::make_shared>( ref ) ), m_hint( hint ) {} - auto operator()( std::string const& description ) -> DerivedT& { + DerivedT& operator()( StringRef description ) & { m_description = description; return static_cast( *this ); } + DerivedT&& operator()( StringRef description ) && { + m_description = description; + return static_cast( *this ); + } auto optional() -> DerivedT& { m_optionality = Optionality::Optional; @@ -5101,7 +4700,7 @@ namespace Catch { return 1; } - std::string const& hint() const { return m_hint; } + StringRef hint() const { return m_hint; } }; } // namespace detail @@ -5115,13 +4714,13 @@ namespace Catch { Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; }; // A parser for options class Opt : public Detail::ParserRefImpl { protected: - std::vector m_optNames; + std::vector m_optNames; public: template @@ -5134,33 +4733,37 @@ namespace Catch { template ::value>> - Opt( LambdaT const& ref, std::string const& hint ): + Opt( LambdaT const& ref, StringRef hint ): ParserRefImpl( ref, hint ) {} template - Opt( accept_many_t, LambdaT const& ref, std::string const& hint ): + Opt( accept_many_t, LambdaT const& ref, StringRef hint ): ParserRefImpl( accept_many, ref, hint ) {} template ::value>> - Opt( T& ref, std::string const& hint ): + Opt( T& ref, StringRef hint ): ParserRefImpl( ref, hint ) {} - auto operator[](std::string const& optName) -> Opt& { + Opt& operator[]( StringRef optName ) & { m_optNames.push_back(optName); return *this; } + Opt&& operator[]( StringRef optName ) && { + m_optNames.push_back( optName ); + return CATCH_MOVE(*this); + } - std::vector getHelpColumns() const; + Detail::HelpColumns getHelpColumns() const; - bool isMatch(std::string const& optToken) const; + bool isMatch(StringRef optToken) const; using ParserBase::parse; Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; Detail::Result validate() const override; }; @@ -5183,7 +4786,7 @@ namespace Catch { // handled specially Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; std::string const& name() const { return *m_name; } Detail::ParserResult set(std::string const& newName); @@ -5208,16 +4811,28 @@ namespace Catch { return *this; } - auto operator|=(Opt const& opt) -> Parser& { - m_options.push_back(opt); - return *this; + friend Parser& operator|=( Parser& p, Opt const& opt ) { + p.m_options.push_back( opt ); + return p; + } + friend Parser& operator|=( Parser& p, Opt&& opt ) { + p.m_options.push_back( CATCH_MOVE(opt) ); + return p; } Parser& operator|=(Parser const& other); template - auto operator|(T const& other) const -> Parser { - return Parser(*this) |= other; + friend Parser operator|( Parser const& p, T&& rhs ) { + Parser temp( p ); + temp |= rhs; + return temp; + } + + template + friend Parser operator|( Parser&& p, T&& rhs ) { + p |= CATCH_FORWARD(rhs); + return CATCH_MOVE(p); } std::vector getHelpColumns() const; @@ -5235,21 +4850,23 @@ namespace Catch { using ParserBase::parse; Detail::InternalParseResult parse(std::string const& exeName, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; }; - // Transport for raw args (copied from main args, or supplied via - // init list for testing) + /** + * Wrapper over argc + argv, assumes that the inputs outlive it + */ class Args { friend Detail::TokenStream; - std::string m_exeName; - std::vector m_args; + StringRef m_exeName; + std::vector m_args; public: Args(int argc, char const* const* argv); - Args(std::initializer_list args); + // Helper constructor for testing + Args(std::initializer_list args); - std::string const& exeName() const { return m_exeName; } + StringRef exeName() const { return m_exeName; } }; @@ -5529,6 +5146,86 @@ namespace Detail { #include #include +/** \file + * Why does decomposing look the way it does: + * + * Conceptually, decomposing is simple. We change `REQUIRE( a == b )` into + * `Decomposer{} <= a == b`, so that `Decomposer{} <= a` is evaluated first, + * and our custom operator is used for `a == b`, because `a` is transformed + * into `ExprLhs` and then into `BinaryExpr`. + * + * In practice, decomposing ends up a mess, because we have to support + * various fun things. + * + * 1) Types that are only comparable with literal 0, and they do this by + * comparing against a magic type with pointer constructor and deleted + * other constructors. Example: `REQUIRE((a <=> b) == 0)` in libstdc++ + * + * 2) Types that are only comparable with literal 0, and they do this by + * comparing against a magic type with consteval integer constructor. + * Example: `REQUIRE((a <=> b) == 0)` in current MSVC STL. + * + * 3) Types that have no linkage, and so we cannot form a reference to + * them. Example: some implementations of traits. + * + * 4) Starting with C++20, when the compiler sees `a == b`, it also uses + * `b == a` when constructing the overload set. For us this means that + * when the compiler handles `ExprLhs == b`, it also tries to resolve + * the overload set for `b == ExprLhs`. + * + * To accomodate these use cases, decomposer ended up rather complex. + * + * 1) These types are handled by adding SFINAE overloads to our comparison + * operators, checking whether `T == U` are comparable with the given + * operator, and if not, whether T (or U) are comparable with literal 0. + * If yes, the overload compares T (or U) with 0 literal inline in the + * definition. + * + * Note that for extra correctness, we check that the other type is + * either an `int` (literal 0 is captured as `int` by templates), or + * a `long` (some platforms use 0L for `NULL` and we want to support + * that for pointer comparisons). + * + * 2) For these types, `is_foo_comparable` is true, but letting + * them fall into the overload that actually does `T == int` causes + * compilation error. Handling them requires that the decomposition + * is `constexpr`, so that P2564R3 applies and the `consteval` from + * their accompanying magic type is propagated through the `constexpr` + * call stack. + * + * However this is not enough to handle these types automatically, + * because our default is to capture types by reference, to avoid + * runtime copies. While these references cannot become dangling, + * they outlive the constexpr context and thus the default capture + * path cannot be actually constexpr. + * + * The solution is to capture these types by value, by explicitly + * specializing `Catch::capture_by_value` for them. Catch2 provides + * specialization for `std::foo_ordering`s, but users can specialize + * the trait for their own types as well. + * + * 3) If a type has no linkage, we also cannot capture it by reference. + * The solution is once again to capture them by value. We handle + * the common cases by using `std::is_arithmetic` as the default + * for `Catch::capture_by_value`, but that is only a some-effort + * heuristic. But as with 2), users can specialize `capture_by_value` + * for their own types as needed. + * + * 4) To support C++20 and make the SFINAE on our decomposing operators + * work, the SFINAE has to happen in return type, rather than in + * a template type. This is due to our use of logical type traits + * (`conjunction`/`disjunction`/`negation`), that we use to workaround + * an issue in older (9-) versions of GCC. I still blame C++20 for + * this, because without the comparison order switching, the logical + * traits could still be used in template type. + * + * There are also other side concerns, e.g. supporting both `REQUIRE(a)` + * and `REQUIRE(a == b)`, or making `REQUIRE_THAT(a, IsEqual(b))` slot + * nicely into the same expression handling logic, but these are rather + * straightforward and add only a bit of complexity (e.g. common base + * class for decomposed expressions). + */ + #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4389) // '==' : signed/unsigned mismatch @@ -5541,13 +5238,46 @@ namespace Detail { #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" #elif defined __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + +#if defined(CATCH_CPP20_OR_GREATER) && __has_include() +# include +# if defined( __cpp_lib_three_way_comparison ) && \ + __cpp_lib_three_way_comparison >= 201907L +# define CATCH_CONFIG_CPP20_COMPARE_OVERLOADS +# endif #endif namespace Catch { + namespace Detail { + // This was added in C++20, but we require only C++14 for now. + template + using RemoveCVRef_t = std::remove_cv_t>; + } + + // Note: There is nothing that stops us from extending this, + // e.g. to `std::is_scalar`, but the more encompassing + // traits are usually also more expensive. For now we + // keep this as it used to be and it can be changed later. + template + struct capture_by_value + : std::integral_constant{}> {}; + +#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS ) + template <> + struct capture_by_value : std::true_type {}; + template <> + struct capture_by_value : std::true_type {}; + template <> + struct capture_by_value : std::true_type {}; +#endif + template struct always_false : std::false_type {}; @@ -5555,23 +5285,22 @@ namespace Catch { bool m_isBinaryExpression; bool m_result; + protected: + ~ITransientExpression() = default; + public: - auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } - auto getResult() const -> bool { return m_result; } - virtual void streamReconstructedExpression( std::ostream &os ) const = 0; + constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + constexpr auto getResult() const -> bool { return m_result; } + //! This function **has** to be overriden by the derived class. + virtual void streamReconstructedExpression( std::ostream& os ) const; - ITransientExpression( bool isBinaryExpression, bool result ) + constexpr ITransientExpression( bool isBinaryExpression, bool result ) : m_isBinaryExpression( isBinaryExpression ), m_result( result ) {} - ITransientExpression() = default; - ITransientExpression(ITransientExpression const&) = default; - ITransientExpression& operator=(ITransientExpression const&) = default; - - // We don't actually need a virtual destructor, but many static analysers - // complain if it's not here :-( - virtual ~ITransientExpression(); // = default; + constexpr ITransientExpression( ITransientExpression const& ) = default; + constexpr ITransientExpression& operator=( ITransientExpression const& ) = default; friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) { expr.streamReconstructedExpression(out); @@ -5593,7 +5322,7 @@ namespace Catch { } public: - BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + constexpr BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) : ITransientExpression{ true, comparisonResult }, m_lhs( lhs ), m_op( op ), @@ -5666,7 +5395,7 @@ namespace Catch { } public: - explicit UnaryExpr( LhsT lhs ) + explicit constexpr UnaryExpr( LhsT lhs ) : ITransientExpression{ false, static_cast(lhs) }, m_lhs( lhs ) {} @@ -5677,31 +5406,31 @@ namespace Catch { class ExprLhs { LhsT m_lhs; public: - explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + explicit constexpr ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} #define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ + -> std::enable_if_t< \ Detail::conjunction, \ - Detail::negation>>>::value, \ + Detail::negation>>>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction, \ - std::is_arithmetic>::value, \ + capture_by_value>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_eq_0_comparable, \ @@ -5714,8 +5443,8 @@ namespace Catch { static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_eq_0_comparable, \ @@ -5732,29 +5461,30 @@ namespace Catch { #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR + #define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ + -> std::enable_if_t< \ Detail::conjunction, \ - Detail::negation>>>::value, \ + Detail::negation>>>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction, \ - std::is_arithmetic>::value, \ + capture_by_value>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_##id##_0_comparable, \ @@ -5765,8 +5495,8 @@ namespace Catch { static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_##id##_0_comparable, \ @@ -5786,17 +5516,17 @@ namespace Catch { #define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - ->std::enable_if_t< \ - !std::is_arithmetic>::value, \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ + -> std::enable_if_t< \ + !capture_by_value>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t::value, \ - BinaryExpr> { \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t::value, \ + BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } @@ -5821,19 +5551,22 @@ namespace Catch { "wrap the expression inside parentheses, or decompose it"); } - auto makeUnaryExpr() const -> UnaryExpr { + constexpr auto makeUnaryExpr() const -> UnaryExpr { return UnaryExpr{ m_lhs }; } }; struct Decomposer { - template>::value, int> = 0> - friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs { + template >::value, + int> = 0> + constexpr friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs { return ExprLhs{ lhs }; } - template::value, int> = 0> - friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs { + template ::value, int> = 0> + constexpr friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs { return ExprLhs{ value }; } }; @@ -5855,8 +5588,6 @@ namespace Catch { namespace Catch { - class IResultCapture; - struct AssertionReaction { bool shouldDebugBreak = false; bool shouldThrow = false; @@ -5883,12 +5614,12 @@ namespace Catch { template - void handleExpr( ExprLhs const& expr ) { + constexpr void handleExpr( ExprLhs const& expr ) { handleExpr( expr.makeUnaryExpr() ); } void handleExpr( ITransientExpression const& expr ); - void handleMessage(ResultWas::OfType resultType, StringRef message); + void handleMessage(ResultWas::OfType resultType, std::string&& message); void handleExceptionThrownAsExpected(); void handleUnexpectedExceptionNotThrown(); @@ -5897,7 +5628,6 @@ namespace Catch { void handleUnexpectedInflightException(); void complete(); - void setCompleted(); // query auto allowThrows() const -> bool; @@ -5909,13 +5639,10 @@ namespace Catch { #endif // CATCH_ASSERTION_HANDLER_HPP_INCLUDED -// We need this suppression to leak, because it took until GCC 10 -// for the front end to handle local suppression via _Pragma properly -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9 - #pragma GCC diagnostic ignored "-Wparentheses" -#endif -#if !defined(CATCH_CONFIG_DISABLE) +#ifndef CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED +#define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED + #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr @@ -5923,6 +5650,16 @@ namespace Catch { #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr #endif +#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED + +// We need this suppression to leak, because it took until GCC 10 +// for the front end to handle local suppression via _Pragma properly +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9 + #pragma GCC diagnostic ignored "-Wparentheses" +#endif + +#if !defined(CATCH_CONFIG_DISABLE) + #if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) /////////////////////////////////////////////////////////////////////////////// @@ -5934,12 +5671,10 @@ namespace Catch { #else // CATCH_CONFIG_FAST_COMPILE #define INTERNAL_CATCH_TRY try -#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { (handler).handleUnexpectedInflightException(); } #endif -#define INTERNAL_CATCH_REACT( handler ) handler.complete(); - /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ do { /* NOLINT(bugprone-infinite-loop) */ \ @@ -5949,10 +5684,10 @@ namespace Catch { INTERNAL_CATCH_TRY { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); /* NOLINT(bugprone-chained-comparison) */ \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. @@ -5980,7 +5715,7 @@ namespace Catch { catch( ... ) { \ catchAssertionHandler.handleUnexpectedInflightException(); \ } \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) /////////////////////////////////////////////////////////////////////////////// @@ -5990,6 +5725,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -6000,7 +5736,7 @@ namespace Catch { } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) /////////////////////////////////////////////////////////////////////////////// @@ -6010,6 +5746,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(expr); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -6023,7 +5760,7 @@ namespace Catch { } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) @@ -6036,6 +5773,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -6046,7 +5784,7 @@ namespace Catch { } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) #endif // CATCH_CONFIG_DISABLE @@ -6059,6 +5797,34 @@ namespace Catch { + +/** \file + * Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option + * + * Some of Catch2's macros can be defined differently to work better with + * static analysis tools, like clang-tidy or coverity. + * Currently the main use case is to show that `SECTION`s are executed + * exclusively, and not all in one run of a `TEST_CASE`. + */ + +#ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED +#define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED + + +#if defined(__clang_analyzer__) || defined(__COVERITY__) + #define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT +#endif + +#if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \ + !defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \ + !defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) +# define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT +#endif + + +#endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED + + #ifndef CATCH_TIMER_HPP_INCLUDED #define CATCH_TIMER_HPP_INCLUDED @@ -6103,62 +5869,92 @@ namespace Catch { } // end namespace Catch -#define INTERNAL_CATCH_SECTION( ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) +# define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_Section ) = \ + Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_Section ) = \ + Catch::SectionInfo( \ + CATCH_INTERNAL_LINEINFO, \ + ( Catch::ReusableStringStream() << __VA_ARGS__ ) \ + .str() ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#endif // CATCH_SECTION_HPP_INCLUDED +#else +// These section definitions imply that at most one section at one level +// will be intered (because only one section's __LINE__ can be equal to +// the dummy `catchInternalSectionHint` variable from `TEST_CASE`). + +namespace Catch { + namespace Detail { + // Intentionally without linkage, as it should only be used as a dummy + // symbol for static analysis. + // The arguments are used as a dummy for checking warnings in the passed + // expressions. + int GetNewSectionHint( StringRef, const char* const = nullptr ); + } // namespace Detail +} // namespace Catch -#ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED -#define CATCH_TEST_REGISTRY_HPP_INCLUDED +# define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ + catchInternalSectionHint, \ + catchInternalSectionHint = \ + Catch::Detail::GetNewSectionHint(__VA_ARGS__); \ + catchInternalPreviousSectionHint == __LINE__ ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ + catchInternalSectionHint, \ + catchInternalSectionHint = Catch::Detail::GetNewSectionHint( \ + ( Catch::ReusableStringStream() << __VA_ARGS__ ).str()); \ + catchInternalPreviousSectionHint == __LINE__ ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED -#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED +#endif -#include -namespace Catch { +#endif // CATCH_SECTION_HPP_INCLUDED - class TestSpec; - struct TestCaseInfo; + +#ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED +#define CATCH_TEST_REGISTRY_HPP_INCLUDED + + + +#ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED +#define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED + +namespace Catch { class ITestInvoker { public: - virtual void invoke () const = 0; + virtual void prepareTestCase(); + virtual void tearDownTestCase(); + virtual void invoke() const = 0; virtual ~ITestInvoker(); // = default }; - class TestCaseHandle; - class IConfig; - - class ITestCaseRegistry { - public: - virtual ~ITestCaseRegistry(); // = default - // TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later - virtual std::vector const& getAllInfos() const = 0; - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ); - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} +} // namespace Catch -#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED +#endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED #ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED @@ -6189,7 +5985,8 @@ template class TestInvokerAsMethod : public ITestInvoker { void (C::*m_testAsMethod)(); public: - TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} + constexpr TestInvokerAsMethod( void ( C::*testAsMethod )() ) noexcept: + m_testAsMethod( testAsMethod ) {} void invoke() const override { C obj; @@ -6204,6 +6001,34 @@ Detail::unique_ptr makeTestInvoker( void (C::*testAsMethod)() ) { return Detail::make_unique>( testAsMethod ); } +template +class TestInvokerFixture : public ITestInvoker { + void ( C::*m_testAsMethod )() const; + Detail::unique_ptr m_fixture = nullptr; + +public: + constexpr TestInvokerFixture( void ( C::*testAsMethod )() const ) noexcept: + m_testAsMethod( testAsMethod ) {} + + void prepareTestCase() override { + m_fixture = Detail::make_unique(); + } + + void tearDownTestCase() override { + m_fixture.reset(); + } + + void invoke() const override { + auto* f = m_fixture.get(); + ( f->*m_testAsMethod )(); + } +}; + +template +Detail::unique_ptr makeTestInvokerFixture( void ( C::*testAsMethod )() const ) { + return Detail::make_unique>( testAsMethod ); +} + struct NameAndTags { constexpr NameAndTags( StringRef name_ = StringRef(), StringRef tags_ = StringRef() ) noexcept: @@ -6230,6 +6055,9 @@ struct AutoReg : Detail::NonCopyable { void TestName::test() #endif + +#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ @@ -6242,19 +6070,40 @@ struct AutoReg : Detail::NonCopyable { #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ ) - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - namespace { \ - const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ - Catch::makeTestInvoker( &QualifiedMethod ), \ - CATCH_INTERNAL_LINEINFO, \ - "&" #QualifiedMethod##_catch_sr, \ - Catch::NameAndTags{ __VA_ARGS__ } ); \ - } /* NOLINT */ \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT + + +// Dummy registrator for the dumy test case macros +namespace Catch { + namespace Detail { + struct DummyUse { + DummyUse( void ( * )( int ), Catch::NameAndTags const& ); + }; + } // namespace Detail +} // namespace Catch + +// Note that both the presence of the argument and its exact name are +// necessary for the section support. + +// We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE` +// tests can compile. The redefined `TEST_CASE` shadows this with param. +static int catchInternalSectionHint = 0; + +# define INTERNAL_CATCH_TESTCASE2( fname, ... ) \ + static void fname( int ); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \ + dummyUser )( &(fname), Catch::NameAndTags{ __VA_ARGS__ } ); \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + static void fname( [[maybe_unused]] int catchInternalSectionHint ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +# define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ), __VA_ARGS__ ) + + +#endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ @@ -6276,6 +6125,42 @@ struct AutoReg : Detail::NonCopyable { #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( TestName, ClassName, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace { \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS( ClassName ) { \ + void test() const; \ + }; \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ + Catch::makeTestInvokerFixture( &TestName::test ), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName##_catch_sr, \ + Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + void TestName::test() const + #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) + + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace { \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ + Catch::makeTestInvoker( &QualifiedMethod ), \ + CATCH_INTERNAL_LINEINFO, \ + "&" #QualifiedMethod##_catch_sr, \ + Catch::NameAndTags{ __VA_ARGS__ } ); \ + } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ do { \ @@ -6317,6 +6202,7 @@ struct AutoReg : Detail::NonCopyable { #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) @@ -6371,6 +6257,7 @@ struct AutoReg : Detail::NonCopyable { #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) + #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) #define CATCH_SECTION( ... ) #define CATCH_DYNAMIC_SECTION( ... ) @@ -6416,6 +6303,7 @@ struct AutoReg : Detail::NonCopyable { #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) @@ -6469,6 +6357,7 @@ struct AutoReg : Detail::NonCopyable { #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define METHOD_AS_TEST_CASE( method, ... ) + #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) #define REGISTER_TEST_CASE( Function, ... ) (void)(0) #define SECTION( ... ) #define DYNAMIC_SECTION( ... ) @@ -7194,6 +7083,7 @@ namespace Catch { }; class ITestInvoker; + struct NameAndTags; enum class TestCaseProperties : uint8_t { None = 0, @@ -7217,7 +7107,7 @@ namespace Catch { struct TestCaseInfo : Detail::NonCopyable { TestCaseInfo(StringRef _className, - NameAndTags const& _tags, + NameAndTags const& _nameAndTags, SourceLineInfo const& _lineInfo); bool isHidden() const; @@ -7258,14 +7148,24 @@ namespace Catch { TestCaseInfo* m_info; ITestInvoker* m_invoker; public: - TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) : + constexpr TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) : m_info(info), m_invoker(invoker) {} + void prepareTestCase() const { + m_invoker->prepareTestCase(); + } + + void tearDownTestCase() const { + m_invoker->tearDownTestCase(); + } + void invoke() const { m_invoker->invoke(); } - TestCaseInfo const& getTestCaseInfo() const; + constexpr TestCaseInfo const& getTestCaseInfo() const { + return *m_info; + } }; Detail::unique_ptr @@ -7318,13 +7218,17 @@ namespace Catch { #include namespace Catch { + namespace Detail { + void registerTranslatorImpl( + Detail::unique_ptr&& translator ); + } class ExceptionTranslatorRegistrar { template class ExceptionTranslator : public IExceptionTranslator { public: - ExceptionTranslator( std::string(*translateFunction)( T const& ) ) + constexpr ExceptionTranslator( std::string(*translateFunction)( T const& ) ) : m_translateFunction( translateFunction ) {} @@ -7351,9 +7255,9 @@ namespace Catch { public: template ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) { - getMutableRegistryHub().registerTranslator( - Detail::make_unique>(translateFunction) - ); + Detail::registerTranslatorImpl( + Detail::make_unique>( + translateFunction ) ); } }; @@ -7425,8 +7329,8 @@ namespace Catch { #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 3 -#define CATCH_VERSION_PATCH 2 +#define CATCH_VERSION_MINOR 7 +#define CATCH_VERSION_PATCH 1 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -7584,12 +7488,6 @@ namespace Detail { } public: - ~IGenerator() override = default; - IGenerator() = default; - IGenerator(IGenerator const&) = default; - IGenerator& operator=(IGenerator const&) = default; - - // Returns the current element of the generator // // \Precondition The generator is either freshly constructed, @@ -8063,32 +7961,579 @@ namespace Catch { explicit SimplePcg32(result_type seed_); - void seed(result_type seed_); - void discard(uint64_t skip); + void seed(result_type seed_); + void discard(uint64_t skip); + + result_type operator()(); + + private: + friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs); + friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs); + + // In theory we also need operator<< and operator>> + // In practice we do not use them, so we will skip them for now + + + std::uint64_t m_state; + // This part of the state determines which "stream" of the numbers + // is chosen -- we take it as a constant for Catch2, so we only + // need to deal with seeding the main state. + // Picked by reading 8 bytes from `/dev/random` :-) + static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL; + }; + +} // end namespace Catch + +#endif // CATCH_RANDOM_NUMBER_GENERATOR_HPP_INCLUDED + + + +#ifndef CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED +#define CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED + + + + +#ifndef CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED +#define CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED + +#include +#include +#include +#include + +// Note: We use the usual enable-disable-autodetect dance here even though +// we do not support these in CMake configuration options (yet?). +// It is highly unlikely that we will need to make these actually +// user-configurable, but this will make it simpler if weend up needing +// it, and it provides an escape hatch to the users who need it. +#if defined( __SIZEOF_INT128__ ) +# define CATCH_CONFIG_INTERNAL_UINT128 +// Unlike GCC, MSVC does not polyfill umul as mulh + mul pair on ARM machines. +// Currently we do not bother doing this ourselves, but we could if it became +// important for perf. +#elif defined( _MSC_VER ) && defined( _M_X64 ) +# define CATCH_CONFIG_INTERNAL_MSVC_UMUL128 +#endif + +#if defined( CATCH_CONFIG_INTERNAL_UINT128 ) && \ + !defined( CATCH_CONFIG_NO_UINT128 ) && \ + !defined( CATCH_CONFIG_UINT128 ) +#define CATCH_CONFIG_UINT128 +#endif + +#if defined( CATCH_CONFIG_INTERNAL_MSVC_UMUL128 ) && \ + !defined( CATCH_CONFIG_NO_MSVC_UMUL128 ) && \ + !defined( CATCH_CONFIG_MSVC_UMUL128 ) +# define CATCH_CONFIG_MSVC_UMUL128 +# include +#endif + + +namespace Catch { + namespace Detail { + + template + struct SizedUnsignedType; +#define SizedUnsignedTypeHelper( TYPE ) \ + template <> \ + struct SizedUnsignedType { \ + using type = TYPE; \ + } + + SizedUnsignedTypeHelper( std::uint8_t ); + SizedUnsignedTypeHelper( std::uint16_t ); + SizedUnsignedTypeHelper( std::uint32_t ); + SizedUnsignedTypeHelper( std::uint64_t ); +#undef SizedUnsignedTypeHelper + + template + using SizedUnsignedType_t = typename SizedUnsignedType::type; + + template + using DoubleWidthUnsignedType_t = SizedUnsignedType_t<2 * sizeof( T )>; + + template + struct ExtendedMultResult { + T upper; + T lower; + constexpr bool operator==( ExtendedMultResult const& rhs ) const { + return upper == rhs.upper && lower == rhs.lower; + } + }; + + /** + * Returns 128 bit result of lhs * rhs using portable C++ code + * + * This implementation is almost twice as fast as naive long multiplication, + * and unlike intrinsic-based approach, it supports constexpr evaluation. + */ + constexpr ExtendedMultResult + extendedMultPortable(std::uint64_t lhs, std::uint64_t rhs) { +#define CarryBits( x ) ( x >> 32 ) +#define Digits( x ) ( x & 0xFF'FF'FF'FF ) + std::uint64_t lhs_low = Digits( lhs ); + std::uint64_t rhs_low = Digits( rhs ); + std::uint64_t low_low = ( lhs_low * rhs_low ); + std::uint64_t high_high = CarryBits( lhs ) * CarryBits( rhs ); + + // We add in carry bits from low-low already + std::uint64_t high_low = + ( CarryBits( lhs ) * rhs_low ) + CarryBits( low_low ); + // Note that we can add only low bits from high_low, to avoid + // overflow with large inputs + std::uint64_t low_high = + ( lhs_low * CarryBits( rhs ) ) + Digits( high_low ); + + return { high_high + CarryBits( high_low ) + CarryBits( low_high ), + ( low_high << 32 ) | Digits( low_low ) }; +#undef CarryBits +#undef Digits + } + + //! Returns 128 bit result of lhs * rhs + inline ExtendedMultResult + extendedMult( std::uint64_t lhs, std::uint64_t rhs ) { +#if defined( CATCH_CONFIG_UINT128 ) + auto result = __uint128_t( lhs ) * __uint128_t( rhs ); + return { static_cast( result >> 64 ), + static_cast( result ) }; +#elif defined( CATCH_CONFIG_MSVC_UMUL128 ) + std::uint64_t high; + std::uint64_t low = _umul128( lhs, rhs, &high ); + return { high, low }; +#else + return extendedMultPortable( lhs, rhs ); +#endif + } + + + template + constexpr ExtendedMultResult extendedMult( UInt lhs, UInt rhs ) { + static_assert( std::is_unsigned::value, + "extendedMult can only handle unsigned integers" ); + static_assert( sizeof( UInt ) < sizeof( std::uint64_t ), + "Generic extendedMult can only handle types smaller " + "than uint64_t" ); + using WideType = DoubleWidthUnsignedType_t; + + auto result = WideType( lhs ) * WideType( rhs ); + return { + static_cast( result >> ( CHAR_BIT * sizeof( UInt ) ) ), + static_cast( result & UInt( -1 ) ) }; + } + + + template + std::enable_if_t= sizeof(TargetType), + TargetType> fillBitsFrom(Generator& gen) { + using gresult_type = typename Generator::result_type; + static_assert( std::is_unsigned::value, "Only unsigned integers are supported" ); + static_assert( Generator::min() == 0 && + Generator::max() == static_cast( -1 ), + "Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" ); + + // We want to return the top bits from a generator, as they are + // usually considered higher quality. + constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT; + constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT; + + return static_cast( gen() >> + ( generated_bits - return_bits) ); + } + + template + std::enable_if_t fillBitsFrom(Generator& gen) { + using gresult_type = typename Generator::result_type; + static_assert( std::is_unsigned::value, + "Only unsigned integers are supported" ); + static_assert( Generator::min() == 0 && + Generator::max() == static_cast( -1 ), + "Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" ); + + constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT; + constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT; + std::size_t filled_bits = 0; + TargetType ret = 0; + do { + ret <<= generated_bits; + ret |= gen(); + filled_bits += generated_bits; + } while ( filled_bits < return_bits ); + + return ret; + } + + /* + * Transposes numbers into unsigned type while keeping their ordering + * + * This means that signed types are changed so that the ordering is + * [INT_MIN, ..., -1, 0, ..., INT_MAX], rather than order we would + * get by simple casting ([0, ..., INT_MAX, INT_MIN, ..., -1]) + */ + template + constexpr + std::enable_if_t::value, UnsignedType> + transposeToNaturalOrder( UnsignedType in ) { + static_assert( + sizeof( OriginalType ) == sizeof( UnsignedType ), + "reordering requires the same sized types on both sides" ); + static_assert( std::is_unsigned::value, + "Input type must be unsigned" ); + // Assuming 2s complement (standardized in current C++), the + // positive and negative numbers are already internally ordered, + // and their difference is in the top bit. Swapping it orders + // them the desired way. + constexpr auto highest_bit = + UnsignedType( 1 ) << ( sizeof( UnsignedType ) * CHAR_BIT - 1 ); + return static_cast( in ^ highest_bit ); + } + + + + template + constexpr + std::enable_if_t::value, UnsignedType> + transposeToNaturalOrder(UnsignedType in) { + static_assert( + sizeof( OriginalType ) == sizeof( UnsignedType ), + "reordering requires the same sized types on both sides" ); + static_assert( std::is_unsigned::value, "Input type must be unsigned" ); + // No reordering is needed for unsigned -> unsigned + return in; + } + } // namespace Detail +} // namespace Catch + +#endif // CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED + +namespace Catch { + +/** + * Implementation of uniform distribution on integers. + * + * Unlike `std::uniform_int_distribution`, this implementation supports + * various 1 byte integral types, including bool (but you should not + * actually use it for bools). + * + * The underlying algorithm is based on the one described in "Fast Random + * Integer Generation in an Interval" by Daniel Lemire, but has been + * optimized under the assumption of reuse of the same distribution object. + */ +template +class uniform_integer_distribution { + static_assert(std::is_integral::value, "..."); + + using UnsignedIntegerType = Detail::SizedUnsignedType_t; + + // Only the left bound is stored, and we store it converted to its + // unsigned image. This avoids having to do the conversions inside + // the operator(), at the cost of having to do the conversion in + // the a() getter. The right bound is only needed in the b() getter, + // so we recompute it there from other stored data. + UnsignedIntegerType m_a; + + // How many different values are there in [a, b]. a == b => 1, can be 0 for distribution over all values in the type. + UnsignedIntegerType m_ab_distance; + + // We hoisted this out of the main generation function. Technically, + // this means that using this distribution will be slower than Lemire's + // algorithm if this distribution instance will be used only few times, + // but it will be faster if it is used many times. Since Catch2 uses + // distributions only to implement random generators, we assume that each + // distribution will be reused many times and this is an optimization. + UnsignedIntegerType m_rejection_threshold = 0; + + static constexpr UnsignedIntegerType computeDistance(IntegerType a, IntegerType b) { + // This overflows and returns 0 if a == 0 and b == TYPE_MAX. + // We handle that later when generating the number. + return transposeTo(b) - transposeTo(a) + 1; + } + + static constexpr UnsignedIntegerType computeRejectionThreshold(UnsignedIntegerType ab_distance) { + // distance == 0 means that we will return all possible values from + // the type's range, and that we shouldn't reject anything. + if ( ab_distance == 0 ) { return 0; } + return ( ~ab_distance + 1 ) % ab_distance; + } + + static constexpr UnsignedIntegerType transposeTo(IntegerType in) { + return Detail::transposeToNaturalOrder( + static_cast( in ) ); + } + static constexpr IntegerType transposeBack(UnsignedIntegerType in) { + return static_cast( + Detail::transposeToNaturalOrder(in) ); + } + +public: + using result_type = IntegerType; + + constexpr uniform_integer_distribution( IntegerType a, IntegerType b ): + m_a( transposeTo(a) ), + m_ab_distance( computeDistance(a, b) ), + m_rejection_threshold( computeRejectionThreshold(m_ab_distance) ) { + assert( a <= b ); + } + + template + constexpr result_type operator()( Generator& g ) { + // All possible values of result_type are valid. + if ( m_ab_distance == 0 ) { + return transposeBack( Detail::fillBitsFrom( g ) ); + } + + auto random_number = Detail::fillBitsFrom( g ); + auto emul = Detail::extendedMult( random_number, m_ab_distance ); + // Unlike Lemire's algorithm we skip the ab_distance check, since + // we precomputed the rejection threshold, which is always tighter. + while (emul.lower < m_rejection_threshold) { + random_number = Detail::fillBitsFrom( g ); + emul = Detail::extendedMult( random_number, m_ab_distance ); + } + + return transposeBack(m_a + emul.upper); + } + + constexpr result_type a() const { return transposeBack(m_a); } + constexpr result_type b() const { return transposeBack(m_ab_distance + m_a - 1); } +}; + +} // end namespace Catch + +#endif // CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED + + + +#ifndef CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED +#define CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED + + + + +#ifndef CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED +#define CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED + + + +#ifndef CATCH_POLYFILLS_HPP_INCLUDED +#define CATCH_POLYFILLS_HPP_INCLUDED + +namespace Catch { + + bool isnan(float f); + bool isnan(double d); + + float nextafter(float x, float y); + double nextafter(double x, double y); + +} + +#endif // CATCH_POLYFILLS_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace Catch { + + namespace Detail { + /** + * Returns the largest magnitude of 1-ULP distance inside the [a, b] range. + * + * Assumes `a < b`. + */ + template + FloatType gamma(FloatType a, FloatType b) { + static_assert( std::is_floating_point::value, + "gamma returns the largest ULP magnitude within " + "floating point range [a, b]. This only makes sense " + "for floating point types" ); + assert( a <= b ); + + const auto gamma_up = Catch::nextafter( a, std::numeric_limits::infinity() ) - a; + const auto gamma_down = b - Catch::nextafter( b, -std::numeric_limits::infinity() ); + + return gamma_up < gamma_down ? gamma_down : gamma_up; + } + + template + struct DistanceTypePicker; + template <> + struct DistanceTypePicker { + using type = std::uint32_t; + }; + template <> + struct DistanceTypePicker { + using type = std::uint64_t; + }; + + template + using DistanceType = typename DistanceTypePicker::type; + +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + /** + * Computes the number of equi-distant floats in [a, b] + * + * Since not every range can be split into equidistant floats + * exactly, we actually compute ceil(b/distance - a/distance), + * because in those cases we want to overcount. + * + * Uses modified Dekker's FastTwoSum algorithm to handle rounding. + */ + template + DistanceType + count_equidistant_floats( FloatType a, FloatType b, FloatType distance ) { + assert( a <= b ); + // We get distance as gamma for our uniform float distribution, + // so this will round perfectly. + const auto ag = a / distance; + const auto bg = b / distance; + + const auto s = bg - ag; + const auto err = ( std::fabs( a ) <= std::fabs( b ) ) + ? -ag - ( s - bg ) + : bg - ( s + ag ); + const auto ceil_s = static_cast>( std::ceil( s ) ); + + return ( ceil_s != s ) ? ceil_s : ceil_s + ( err > 0 ); + } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + + } + +} // end namespace Catch + +#endif // CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED + +#include +#include + +namespace Catch { - result_type operator()(); + namespace Detail { +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + // The issue with overflow only happens with maximal ULP and HUGE + // distance, e.g. when generating numbers in [-inf, inf] for given + // type. So we only check for the largest possible ULP in the + // type, and return something that does not overflow to inf in 1 mult. + constexpr std::uint64_t calculate_max_steps_in_one_go(double gamma) { + if ( gamma == 1.99584030953472e+292 ) { return 9007199254740991; } + return static_cast( -1 ); + } + constexpr std::uint32_t calculate_max_steps_in_one_go(float gamma) { + if ( gamma == 2.028241e+31f ) { return 16777215; } + return static_cast( -1 ); + } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + } - private: - friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs); - friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs); +/** + * Implementation of uniform distribution on floating point numbers. + * + * Note that we support only `float` and `double` types, because these + * usually mean the same thing across different platform. `long double` + * varies wildly by platform and thus we cannot provide reproducible + * implementation. Also note that we don't implement all parts of + * distribution per standard: this distribution is not serializable, nor + * can the range be arbitrarily reset. + * + * The implementation also uses different approach than the one taken by + * `std::uniform_real_distribution`, where instead of generating a number + * between [0, 1) and then multiplying the range bounds with it, we first + * split the [a, b] range into a set of equidistributed floating point + * numbers, and then use uniform int distribution to pick which one to + * return. + * + * This has the advantage of guaranteeing uniformity (the multiplication + * method loses uniformity due to rounding when multiplying floats), except + * for small non-uniformity at one side of the interval, where we have + * to deal with the fact that not every interval is splittable into + * equidistributed floats. + * + * Based on "Drawing random floating-point numbers from an interval" by + * Frederic Goualard. + */ +template +class uniform_floating_point_distribution { + static_assert(std::is_floating_point::value, "..."); + static_assert(!std::is_same::value, + "We do not support long double due to inconsistent behaviour between platforms"); + + using WidthType = Detail::DistanceType; + + FloatType m_a, m_b; + FloatType m_ulp_magnitude; + WidthType m_floats_in_range; + uniform_integer_distribution m_int_dist; + + // In specific cases, we can overflow into `inf` when computing the + // `steps * g` offset. To avoid this, we don't offset by more than this + // in one multiply + addition. + WidthType m_max_steps_in_one_go; + // We don't want to do the magnitude check every call to `operator()` + bool m_a_has_leq_magnitude; - // In theory we also need operator<< and operator>> - // In practice we do not use them, so we will skip them for now +public: + using result_type = FloatType; + + uniform_floating_point_distribution( FloatType a, FloatType b ): + m_a( a ), + m_b( b ), + m_ulp_magnitude( Detail::gamma( m_a, m_b ) ), + m_floats_in_range( Detail::count_equidistant_floats( m_a, m_b, m_ulp_magnitude ) ), + m_int_dist(0, m_floats_in_range), + m_max_steps_in_one_go( Detail::calculate_max_steps_in_one_go(m_ulp_magnitude)), + m_a_has_leq_magnitude(std::fabs(m_a) <= std::fabs(m_b)) + { + assert( a <= b ); + } + template + result_type operator()( Generator& g ) { + WidthType steps = m_int_dist( g ); + if ( m_a_has_leq_magnitude ) { + if ( steps == m_floats_in_range ) { return m_a; } + auto b = m_b; + while (steps > m_max_steps_in_one_go) { + b -= m_max_steps_in_one_go * m_ulp_magnitude; + steps -= m_max_steps_in_one_go; + } + return b - steps * m_ulp_magnitude; + } else { + if ( steps == m_floats_in_range ) { return m_b; } + auto a = m_a; + while (steps > m_max_steps_in_one_go) { + a += m_max_steps_in_one_go * m_ulp_magnitude; + steps -= m_max_steps_in_one_go; + } + return a + steps * m_ulp_magnitude; + } + } - std::uint64_t m_state; - // This part of the state determines which "stream" of the numbers - // is chosen -- we take it as a constant for Catch2, so we only - // need to deal with seeding the main state. - // Picked by reading 8 bytes from `/dev/random` :-) - static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL; - }; + result_type a() const { return m_a; } + result_type b() const { return m_b; } +}; } // end namespace Catch -#endif // CATCH_RANDOM_NUMBER_GENERATOR_HPP_INCLUDED - -#include +#endif // CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED namespace Catch { namespace Generators { @@ -8102,7 +8547,7 @@ namespace Detail { template class RandomFloatingGenerator final : public IGenerator { Catch::SimplePcg32 m_rng; - std::uniform_real_distribution m_dist; + Catch::uniform_floating_point_distribution m_dist; Float m_current_number; public: RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ): @@ -8120,10 +8565,27 @@ class RandomFloatingGenerator final : public IGenerator { } }; +template <> +class RandomFloatingGenerator final : public IGenerator { + // We still rely on for this specialization, but we don't + // want to drag it into the header. + struct PImpl; + Catch::Detail::unique_ptr m_pimpl; + long double m_current_number; + +public: + RandomFloatingGenerator( long double a, long double b, std::uint32_t seed ); + + long double const& get() const override { return m_current_number; } + bool next() override; + + ~RandomFloatingGenerator() override; // = default +}; + template class RandomIntegerGenerator final : public IGenerator { Catch::SimplePcg32 m_rng; - std::uniform_int_distribution m_dist; + Catch::uniform_integer_distribution m_dist; Integer m_current_number; public: RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ): @@ -8144,14 +8606,6 @@ class RandomIntegerGenerator final : public IGenerator { template std::enable_if_t::value, GeneratorWrapper> random(T a, T b) { - static_assert( - !std::is_same::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value, - "The requested type is not supported by the underlying random distributions from std" ); return GeneratorWrapper( Catch::Detail::make_unique>(a, b, Detail::getSeed()) ); @@ -8264,10 +8718,11 @@ GeneratorWrapper from_range(InputIterator from, InputSentinel to) { return GeneratorWrapper(Catch::Detail::make_unique>(from, to)); } -template -GeneratorWrapper from_range(Container const& cnt) { - return GeneratorWrapper(Catch::Detail::make_unique>(cnt.begin(), cnt.end())); +template +auto from_range(Container const& cnt) { + using std::begin; + using std::end; + return from_range( begin( cnt ), end( cnt ) ); } @@ -8294,9 +8749,235 @@ GeneratorWrapper from_range(Container const& cnt) { */ -#ifndef CATCH_INTERFACES_ALL_HPP_INCLUDED -#define CATCH_INTERFACES_ALL_HPP_INCLUDED +#ifndef CATCH_INTERFACES_ALL_HPP_INCLUDED +#define CATCH_INTERFACES_ALL_HPP_INCLUDED + + + +#ifndef CATCH_INTERFACES_REPORTER_HPP_INCLUDED +#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED + + + +#ifndef CATCH_TEST_RUN_INFO_HPP_INCLUDED +#define CATCH_TEST_RUN_INFO_HPP_INCLUDED + + +namespace Catch { + + struct TestRunInfo { + constexpr TestRunInfo(StringRef _name) : name(_name) {} + StringRef name; + }; + +} // end namespace Catch + +#endif // CATCH_TEST_RUN_INFO_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + struct ReporterDescription; + struct ListenerDescription; + struct TagInfo; + struct TestCaseInfo; + class TestCaseHandle; + class IConfig; + class IStream; + enum class ColourMode : std::uint8_t; + + struct ReporterConfig { + ReporterConfig( IConfig const* _fullConfig, + Detail::unique_ptr _stream, + ColourMode colourMode, + std::map customOptions ); + + ReporterConfig( ReporterConfig&& ) = default; + ReporterConfig& operator=( ReporterConfig&& ) = default; + ~ReporterConfig(); // = default + + Detail::unique_ptr takeStream() &&; + IConfig const* fullConfig() const; + ColourMode colourMode() const; + std::map const& customOptions() const; + + private: + Detail::unique_ptr m_stream; + IConfig const* m_fullConfig; + ColourMode m_colourMode; + std::map m_customOptions; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = delete; + AssertionStats& operator = ( AssertionStats && ) = delete; + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo&& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string&& _stdOut, + std::string&& _stdErr, + bool _aborting ); + + TestCaseInfo const * testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + //! By setting up its preferences, a reporter can modify Catch2's behaviour + //! in some regards, e.g. it can request Catch2 to capture writes to + //! stdout/stderr during test execution, and pass them to the reporter. + struct ReporterPreferences { + //! Catch2 should redirect writes to stdout and pass them to the + //! reporter + bool shouldRedirectStdOut = false; + //! Catch2 should call `Reporter::assertionEnded` even for passing + //! assertions + bool shouldReportAllAssertions = false; + }; + + /** + * The common base for all reporters and event listeners + * + * Implementing classes must also implement: + * + * //! User-friendly description of the reporter/listener type + * static std::string getDescription() + * + * Generally shouldn't be derived from by users of Catch2 directly, + * instead they should derive from one of the utility bases that + * derive from this class. + */ + class IEventListener { + protected: + //! Derived classes can set up their preferences here + ReporterPreferences m_preferences; + //! The test run's config as filled in from CLI and defaults + IConfig const* m_config; + + public: + IEventListener( IConfig const* config ): m_config( config ) {} + + virtual ~IEventListener(); // = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + + ReporterPreferences const& getPreferences() const { + return m_preferences; + } + + //! Called when no test cases match provided test spec + virtual void noMatchingTestCases( StringRef unmatchedSpec ) = 0; + //! Called for all invalid test specs from the cli + virtual void reportInvalidTestSpec( StringRef invalidArgument ) = 0; + + /** + * Called once in a testing run before tests are started + * + * Not called if tests won't be run (e.g. only listing will happen) + */ + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + + //! Called _once_ for each TEST_CASE, no matter how many times it is entered + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) + virtual void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) = 0; + //! Called when a `SECTION` is being entered. Not called for skipped sections + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + //! Called when user-code is being probed before the actual benchmark runs + virtual void benchmarkPreparing( StringRef benchmarkName ) = 0; + //! Called after probe but before the user-code is being benchmarked + virtual void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) = 0; + //! Called with the benchmark results if benchmark successfully finishes + virtual void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) = 0; + //! Called if running the benchmarks fails for any reason + virtual void benchmarkFailed( StringRef benchmarkName ) = 0; + + //! Called before assertion success/failure is evaluated + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + //! Called after assertion was fully evaluated + virtual void assertionEnded( AssertionStats const& assertionStats ) = 0; + + //! Called after a `SECTION` has finished running + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) + virtual void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber ) = 0; + //! Called _once_ for each TEST_CASE, no matter how many times it is entered + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + /** + * Called once after all tests in a testing run are finished + * + * Not called if tests weren't run (e.g. only listings happened) + */ + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + /** + * Called with test cases that are skipped due to the test run aborting. + * NOT called for test cases that are explicitly skipped using the `SKIP` macro. + * + * Deprecated - will be removed in the next major release. + */ + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + //! Called if a fatal error (signal/structured exception) occurred + virtual void fatalErrorEncountered( StringRef error ) = 0; + + //! Writes out information about provided reporters using reporter-specific format + virtual void listReporters(std::vector const& descriptions) = 0; + //! Writes out the provided listeners descriptions using reporter-specific format + virtual void listListeners(std::vector const& descriptions) = 0; + //! Writes out information about provided tests using reporter-specific format + virtual void listTests(std::vector const& tests) = 0; + //! Writes out information about the provided tags using reporter-specific format + virtual void listTags(std::vector const& tags) = 0; + }; + using IEventListenerPtr = Detail::unique_ptr; + +} // end namespace Catch +#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED #ifndef CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED @@ -8337,89 +9018,79 @@ namespace Catch { #endif // CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED -#ifndef CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED -#define CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED +#ifndef CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED +#define CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED + +#include +namespace Catch { + struct TagAlias; -#ifndef CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED -#define CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED + class ITagAliasRegistry { + public: + virtual ~ITagAliasRegistry(); // = default + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + static ITagAliasRegistry const& get(); + }; -namespace Catch { - namespace Detail { - //! Provides case-insensitive `op<` semantics when called - struct CaseInsensitiveLess { - bool operator()( StringRef lhs, - StringRef rhs ) const; - }; +} // end namespace Catch - //! Provides case-insensitive `op==` semantics when called - struct CaseInsensitiveEqualTo { - bool operator()( StringRef lhs, - StringRef rhs ) const; - }; +#endif // CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED - } // namespace Detail -} // namespace Catch -#endif // CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED +#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED +#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED -#include #include -#include namespace Catch { + struct TestCaseInfo; + class TestCaseHandle; class IConfig; - class IEventListener; - using IEventListenerPtr = Detail::unique_ptr; - class IReporterFactory; - using IReporterFactoryPtr = Detail::unique_ptr; - struct ReporterConfig; - class EventListenerFactory; - - class IReporterRegistry { + class ITestCaseRegistry { public: - using FactoryMap = std::map; - using Listeners = std::vector>; - - virtual ~IReporterRegistry(); // = default - virtual IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - virtual Listeners const& getListeners() const = 0; + virtual ~ITestCaseRegistry(); // = default + // TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later + virtual std::vector const& getAllInfos() const = 0; + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; -} // end namespace Catch - -#endif // CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED - +} -#ifndef CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED -#define CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED +#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED -#include +#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED -namespace Catch { - struct TagAlias; +#ifndef CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED +#define CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED - class ITagAliasRegistry { - public: - virtual ~ITagAliasRegistry(); // = default - // Nullptr if not present - virtual TagAlias const* find( std::string const& alias ) const = 0; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; - static ITagAliasRegistry const& get(); - }; +namespace Catch { + namespace Detail { + //! Provides case-insensitive `op<` semantics when called + struct CaseInsensitiveLess { + bool operator()( StringRef lhs, + StringRef rhs ) const; + }; -} // end namespace Catch + //! Provides case-insensitive `op==` semantics when called + struct CaseInsensitiveEqualTo { + bool operator()( StringRef lhs, + StringRef rhs ) const; + }; -#endif // CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED + } // namespace Detail +} // namespace Catch -#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED +#endif // CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED @@ -8461,6 +9132,7 @@ namespace Catch { #ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED #define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED + #if defined(_MSC_VER) # if _MSC_VER >= 1900 // Visual Studio 2015 or newer # define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS @@ -8488,6 +9160,141 @@ namespace Catch { #endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED +#ifndef CATCH_CONSOLE_COLOUR_HPP_INCLUDED +#define CATCH_CONSOLE_COLOUR_HPP_INCLUDED + + +#include +#include + +namespace Catch { + + enum class ColourMode : std::uint8_t; + class IStream; + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + BrightYellow = Bright | Yellow, + + // By intention + FileName = LightGrey, + Warning = BrightYellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + Skip = LightGrey, + + OriginalExpression = Cyan, + ReconstructedExpression = BrightYellow, + + SecondaryText = LightGrey, + Headers = White + }; + }; + + class ColourImpl { + protected: + //! The associated stream of this ColourImpl instance + IStream* m_stream; + public: + ColourImpl( IStream* stream ): m_stream( stream ) {} + + //! RAII wrapper around writing specific colour of text using specific + //! colour impl into a stream. + class ColourGuard { + ColourImpl const* m_colourImpl; + Colour::Code m_code; + bool m_engaged = false; + + public: + //! Does **not** engage the guard/start the colour + ColourGuard( Colour::Code code, + ColourImpl const* colour ); + + ColourGuard( ColourGuard const& rhs ) = delete; + ColourGuard& operator=( ColourGuard const& rhs ) = delete; + + ColourGuard( ColourGuard&& rhs ) noexcept; + ColourGuard& operator=( ColourGuard&& rhs ) noexcept; + + //! Removes colour _if_ the guard was engaged + ~ColourGuard(); + + /** + * Explicitly engages colour for given stream. + * + * The API based on operator<< should be preferred. + */ + ColourGuard& engage( std::ostream& stream ) &; + /** + * Explicitly engages colour for given stream. + * + * The API based on operator<< should be preferred. + */ + ColourGuard&& engage( std::ostream& stream ) &&; + + private: + //! Engages the guard and starts using colour + friend std::ostream& operator<<( std::ostream& lhs, + ColourGuard& guard ) { + guard.engageImpl( lhs ); + return lhs; + } + //! Engages the guard and starts using colour + friend std::ostream& operator<<( std::ostream& lhs, + ColourGuard&& guard) { + guard.engageImpl( lhs ); + return lhs; + } + + void engageImpl( std::ostream& stream ); + + }; + + virtual ~ColourImpl(); // = default + /** + * Creates a guard object for given colour and this colour impl + * + * **Important:** + * the guard starts disengaged, and has to be engaged explicitly. + */ + ColourGuard guardColour( Colour::Code colourCode ); + + private: + virtual void use( Colour::Code colourCode ) const = 0; + }; + + //! Provides ColourImpl based on global config and target compilation platform + Detail::unique_ptr makeColourImpl( ColourMode colourSelection, + IStream* stream ); + + //! Checks if specific colour impl has been compiled into the binary + bool isColourImplAvailable( ColourMode colourSelection ); + +} // end namespace Catch + +#endif // CATCH_CONSOLE_COLOUR_HPP_INCLUDED + + #ifndef CATCH_CONSOLE_WIDTH_HPP_INCLUDED #define CATCH_CONSOLE_WIDTH_HPP_INCLUDED @@ -8703,7 +9510,7 @@ namespace Catch { std::vector> m_enumInfos; - EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values) override; + EnumInfo const& registerEnum( StringRef enumName, StringRef allValueNames, std::vector const& values) override; }; std::vector parseEnums( StringRef enums ); @@ -8751,7 +9558,6 @@ namespace Catch { ~ExceptionTranslatorRegistry() override; void registerTranslator( Detail::unique_ptr&& translator ); std::string translateActiveException() const override; - std::string tryTranslators() const; private: ExceptionTranslators m_translators; @@ -8764,7 +9570,6 @@ namespace Catch { #ifndef CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED #define CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED - #include namespace Catch { @@ -8823,20 +9628,9 @@ namespace Catch { #endif // CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED -#ifndef CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED -#define CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED - - - -#ifndef CATCH_POLYFILLS_HPP_INCLUDED -#define CATCH_POLYFILLS_HPP_INCLUDED - -namespace Catch { - bool isnan(float f); - bool isnan(double d); -} +#ifndef CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED +#define CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED -#endif // CATCH_POLYFILLS_HPP_INCLUDED #include #include @@ -8850,6 +9644,11 @@ namespace Catch { uint32_t convertToBits(float f); uint64_t convertToBits(double d); + // Used when we know we want == comparison of two doubles + // to centralize warning suppression + bool directCompare( float lhs, float rhs ); + bool directCompare( double lhs, double rhs ); + } // end namespace Detail @@ -8959,6 +9758,7 @@ namespace Catch { typename Sentinel, typename T, typename Comparator> + constexpr ForwardIter find_sentinel( ForwardIter start, Sentinel sentinel, T const& value, @@ -8974,6 +9774,7 @@ namespace Catch { typename Sentinel, typename T, typename Comparator> + constexpr std::ptrdiff_t count_sentinel( ForwardIter start, Sentinel sentinel, T const& value, @@ -8987,6 +9788,7 @@ namespace Catch { } template + constexpr std::enable_if_t::value, std::ptrdiff_t> sentinel_distance( ForwardIter iter, const Sentinel sentinel ) { @@ -8999,8 +9801,8 @@ namespace Catch { } template - std::ptrdiff_t sentinel_distance( ForwardIter first, - ForwardIter last ) { + constexpr std::ptrdiff_t sentinel_distance( ForwardIter first, + ForwardIter last ) { return std::distance( first, last ); } @@ -9009,11 +9811,11 @@ namespace Catch { typename ForwardIter2, typename Sentinel2, typename Comparator> - bool check_element_counts( ForwardIter1 first_1, - const Sentinel1 end_1, - ForwardIter2 first_2, - const Sentinel2 end_2, - Comparator cmp ) { + constexpr bool check_element_counts( ForwardIter1 first_1, + const Sentinel1 end_1, + ForwardIter2 first_2, + const Sentinel2 end_2, + Comparator cmp ) { auto cursor = first_1; while ( cursor != end_1 ) { if ( find_sentinel( first_1, cursor, *cursor, cmp ) == @@ -9043,11 +9845,11 @@ namespace Catch { typename ForwardIter2, typename Sentinel2, typename Comparator> - bool is_permutation( ForwardIter1 first_1, - const Sentinel1 end_1, - ForwardIter2 first_2, - const Sentinel2 end_2, - Comparator cmp ) { + constexpr bool is_permutation( ForwardIter1 first_1, + const Sentinel1 end_1, + ForwardIter2 first_2, + const Sentinel2 end_2, + Comparator cmp ) { // TODO: no optimization for stronger iterators, because we would also have to constrain on sentinel vs not sentinel types // TODO: Comparator has to be "both sides", e.g. a == b => b == a // This skips shared prefix of the two ranges @@ -9126,6 +9928,119 @@ namespace Catch { #endif // CATCH_STREAM_HPP_INCLUDED +#ifndef CATCH_JSONWRITER_HPP_INCLUDED +#define CATCH_JSONWRITER_HPP_INCLUDED + + +#include +#include + +namespace Catch { + class JsonObjectWriter; + class JsonArrayWriter; + + struct JsonUtils { + static void indent( std::ostream& os, std::uint64_t level ); + static void appendCommaNewline( std::ostream& os, + bool& should_comma, + std::uint64_t level ); + }; + + class JsonValueWriter { + public: + JsonValueWriter( std::ostream& os ); + JsonValueWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonObjectWriter writeObject() &&; + JsonArrayWriter writeArray() &&; + + template + void write( T const& value ) && { + writeImpl( value, !std::is_arithmetic::value ); + } + void write( StringRef value ) &&; + void write( bool value ) &&; + + private: + void writeImpl( StringRef value, bool quote ); + + // Without this SFINAE, this overload is a better match + // for `std::string`, `char const*`, `char const[N]` args. + // While it would still work, it would cause code bloat + // and multiple iteration over the strings + template ::value>> + void writeImpl( T const& value, bool quote_value ) { + m_sstream << value; + writeImpl( m_sstream.str(), quote_value ); + } + + std::ostream& m_os; + std::stringstream m_sstream; + std::uint64_t m_indent_level; + }; + + class JsonObjectWriter { + public: + JsonObjectWriter( std::ostream& os ); + JsonObjectWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonObjectWriter( JsonObjectWriter&& source ) noexcept; + JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete; + + ~JsonObjectWriter(); + + JsonValueWriter write( StringRef key ); + + private: + std::ostream& m_os; + std::uint64_t m_indent_level; + bool m_should_comma = false; + bool m_active = true; + }; + + class JsonArrayWriter { + public: + JsonArrayWriter( std::ostream& os ); + JsonArrayWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonArrayWriter( JsonArrayWriter&& source ) noexcept; + JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete; + + ~JsonArrayWriter(); + + JsonObjectWriter writeObject(); + JsonArrayWriter writeArray(); + + template + JsonArrayWriter& write( T const& value ) { + return writeImpl( value ); + } + + JsonArrayWriter& write( bool value ); + + private: + template + JsonArrayWriter& writeImpl( T const& value ) { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + JsonValueWriter{ m_os }.write( value ); + + return *this; + } + + std::ostream& m_os; + std::uint64_t m_indent_level; + bool m_should_comma = false; + bool m_active = true; + }; + +} // namespace Catch + +#endif // CATCH_JSONWRITER_HPP_INCLUDED + + #ifndef CATCH_LEAK_DETECTOR_HPP_INCLUDED #define CATCH_LEAK_DETECTOR_HPP_INCLUDED @@ -9181,106 +10096,67 @@ namespace Catch { #define CATCH_OUTPUT_REDIRECT_HPP_INCLUDED -#include -#include +#include #include namespace Catch { - class RedirectedStream { - std::ostream& m_originalStream; - std::ostream& m_redirectionStream; - std::streambuf* m_prevBuf; - - public: - RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); - ~RedirectedStream(); - }; - - class RedirectedStdOut { - ReusableStringStream m_rss; - RedirectedStream m_cout; - public: - RedirectedStdOut(); - auto str() const -> std::string; - }; - - // StdErr has two constituent streams in C++, std::cerr and std::clog - // This means that we need to redirect 2 streams into 1 to keep proper - // order of writes - class RedirectedStdErr { - ReusableStringStream m_rss; - RedirectedStream m_cerr; - RedirectedStream m_clog; + class OutputRedirect { + bool m_redirectActive = false; + virtual void activateImpl() = 0; + virtual void deactivateImpl() = 0; public: - RedirectedStdErr(); - auto str() const -> std::string; - }; + enum Kind { + //! No redirect (noop implementation) + None, + //! Redirect std::cout/std::cerr/std::clog streams internally + Streams, + //! Redirect the stdout/stderr file descriptors into files + FileDescriptors, + }; - class RedirectedStreams { - public: - RedirectedStreams(RedirectedStreams const&) = delete; - RedirectedStreams& operator=(RedirectedStreams const&) = delete; - RedirectedStreams(RedirectedStreams&&) = delete; - RedirectedStreams& operator=(RedirectedStreams&&) = delete; + virtual ~OutputRedirect(); // = default; - RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr); - ~RedirectedStreams(); - private: - std::string& m_redirectedCout; - std::string& m_redirectedCerr; - RedirectedStdOut m_redirectedStdOut; - RedirectedStdErr m_redirectedStdErr; + // TODO: Do we want to check that redirect is not active before retrieving the output? + virtual std::string getStdout() = 0; + virtual std::string getStderr() = 0; + virtual void clearBuffers() = 0; + bool isActive() const { return m_redirectActive; } + void activate() { + assert( !m_redirectActive && "redirect is already active" ); + activateImpl(); + m_redirectActive = true; + } + void deactivate() { + assert( m_redirectActive && "redirect is not active" ); + deactivateImpl(); + m_redirectActive = false; + } }; -#if defined(CATCH_CONFIG_NEW_CAPTURE) - - // Windows's implementation of std::tmpfile is terrible (it tries - // to create a file inside system folder, thus requiring elevated - // privileges for the binary), so we have to use tmpnam(_s) and - // create the file ourselves there. - class TempFile { - public: - TempFile(TempFile const&) = delete; - TempFile& operator=(TempFile const&) = delete; - TempFile(TempFile&&) = delete; - TempFile& operator=(TempFile&&) = delete; - - TempFile(); - ~TempFile(); - - std::FILE* getFile(); - std::string getContents(); - - private: - std::FILE* m_file = nullptr; - #if defined(_MSC_VER) - char m_buffer[L_tmpnam] = { 0 }; - #endif - }; + bool isRedirectAvailable( OutputRedirect::Kind kind); + Detail::unique_ptr makeOutputRedirect( bool actual ); + class RedirectGuard { + OutputRedirect* m_redirect; + bool m_activate; + bool m_previouslyActive; + bool m_moved = false; - class OutputRedirect { public: - OutputRedirect(OutputRedirect const&) = delete; - OutputRedirect& operator=(OutputRedirect const&) = delete; - OutputRedirect(OutputRedirect&&) = delete; - OutputRedirect& operator=(OutputRedirect&&) = delete; + RedirectGuard( bool activate, OutputRedirect& redirectImpl ); + ~RedirectGuard() noexcept( false ); + RedirectGuard( RedirectGuard const& ) = delete; + RedirectGuard& operator=( RedirectGuard const& ) = delete; - OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); - ~OutputRedirect(); - - private: - int m_originalStdout = -1; - int m_originalStderr = -1; - TempFile m_stdoutFile; - TempFile m_stderrFile; - std::string& m_stdoutDest; - std::string& m_stderrDest; + // C++14 needs move-able guards to return them from functions + RedirectGuard( RedirectGuard&& rhs ) noexcept; + RedirectGuard& operator=( RedirectGuard&& rhs ) noexcept; }; -#endif + RedirectGuard scopedActivate( OutputRedirect& redirectImpl ); + RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl ); } // end namespace Catch @@ -9312,28 +10188,45 @@ namespace Catch { #include +#include +#include namespace Catch { - class ReporterRegistry : public IReporterRegistry { - public: + class IEventListener; + using IEventListenerPtr = Detail::unique_ptr; + class IReporterFactory; + using IReporterFactoryPtr = Detail::unique_ptr; + struct ReporterConfig; + class EventListenerFactory; + class ReporterRegistry { + struct ReporterRegistryImpl; + Detail::unique_ptr m_impl; + + public: ReporterRegistry(); - ~ReporterRegistry() override; // = default, out of line to allow fwd decl + ~ReporterRegistry(); // = default; - IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const override; + IEventListenerPtr create( std::string const& name, + ReporterConfig&& config ) const; - void registerReporter( std::string const& name, IReporterFactoryPtr factory ); - void registerListener( Detail::unique_ptr factory ); + void registerReporter( std::string const& name, + IReporterFactoryPtr factory ); - FactoryMap const& getFactories() const override; - Listeners const& getListeners() const override; + void + registerListener( Detail::unique_ptr factory ); - private: - FactoryMap m_factories; - Listeners m_listeners; + std::map const& + getFactories() const; + + std::vector> const& + getListeners() const; }; -} + +} // end namespace Catch #endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED @@ -9448,7 +10341,7 @@ namespace TestCaseTracking { //! Returns true if tracker run to completion (successfully or not) virtual bool isComplete() const = 0; - //! Returns true if tracker run to completion succesfully + //! Returns true if tracker run to completion successfully bool isSuccessfullyCompleted() const { return m_runState == CompletedSuccessfully; } @@ -9582,13 +10475,15 @@ using TestCaseTracking::SectionTracker; namespace Catch { - class IMutableContext; class IGeneratorTracker; class IConfig; + class IEventListener; + using IEventListenerPtr = Detail::unique_ptr; + class OutputRedirect; /////////////////////////////////////////////////////////////////////////// - class RunContext : public IResultCapture { + class RunContext final : public IResultCapture { public: RunContext( RunContext const& ) = delete; @@ -9610,14 +10505,14 @@ namespace Catch { void handleMessage ( AssertionInfo const& info, ResultWas::OfType resultType, - StringRef message, + std::string&& message, AssertionReaction& reaction ) override; void handleUnexpectedExceptionNotThrown ( AssertionInfo const& info, AssertionReaction& reaction ) override; void handleUnexpectedInflightException ( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) override; void handleIncomplete ( AssertionInfo const& info ) override; @@ -9626,6 +10521,7 @@ namespace Catch { ResultWas::OfType resultType, AssertionReaction &reaction ) override; + void notifyAssertionStarted( AssertionInfo const& info ) override; bool sectionStarted( StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts& assertions ) override; @@ -9670,13 +10566,13 @@ namespace Catch { private: - void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); + void runCurrentTest(); void invokeActiveTestCase(); void resetAssertionInfo(); bool testForMissingAssertions( Counts& assertions ); - void assertionEnded( AssertionResult const& result ); + void assertionEnded( AssertionResult&& result ); void reportExpr ( AssertionInfo const &info, ResultWas::OfType resultType, @@ -9690,7 +10586,6 @@ namespace Catch { void handleUnfinishedSections(); TestRunInfo m_runInfo; - IMutableContext& m_context; TestCaseHandle const* m_activeTestCase = nullptr; ITracker* m_testCaseTracker = nullptr; Optional m_lastResult; @@ -9704,6 +10599,7 @@ namespace Catch { std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; + Detail::unique_ptr m_outputRedirect; FatalConditionHandler m_fatalConditionhandler; bool m_lastAssertionPassed = false; bool m_shouldReportUnexpected = true; @@ -9720,7 +10616,7 @@ namespace Catch { #ifndef CATCH_SHARDING_HPP_INCLUDED #define CATCH_SHARDING_HPP_INCLUDED - +#include #include #include @@ -9947,30 +10843,28 @@ namespace Catch { namespace Catch { - class TestCaseHandle; class IConfig; + class ITestInvoker; + class TestCaseHandle; class TestSpec; std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ); - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ); - - void enforceNoDuplicateTestCases( std::vector const& functions ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); class TestRegistry : public ITestCaseRegistry { public: - ~TestRegistry() override = default; - void registerTest( Detail::unique_ptr testInfo, Detail::unique_ptr testInvoker ); std::vector const& getAllInfos() const override; std::vector const& getAllTests() const override; std::vector const& getAllTestsSorted( IConfig const& config ) const override; + ~TestRegistry() override; // = default + private: std::vector> m_owned_test_infos; // Keeps a materialized vector for `getAllInfos`. @@ -9985,18 +10879,6 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////// - class TestInvokerAsFunction final : public ITestInvoker { - using TestType = void(*)(); - TestType m_testAsFunction; - public: - TestInvokerAsFunction(TestType testAsFunction) noexcept: - m_testAsFunction(testAsFunction) {} - - void invoke() const override; - }; - - /////////////////////////////////////////////////////////////////////////// - } // end namespace Catch @@ -10082,6 +10964,7 @@ namespace Catch { #ifndef CATCH_TEXTFLOW_HPP_INCLUDED #define CATCH_TEXTFLOW_HPP_INCLUDED + #include #include #include @@ -10091,6 +10974,107 @@ namespace Catch { class Columns; + /** + * Abstraction for a string with ansi escape sequences that + * automatically skips over escapes when iterating. Only graphical + * escape sequences are considered. + * + * Internal representation: + * An escape sequence looks like \033[39;49m + * We need bidirectional iteration and the unbound length of escape + * sequences poses a problem for operator-- To make this work we'll + * replace the last `m` with a 0xff (this is a codepoint that won't have + * any utf-8 meaning). + */ + class AnsiSkippingString { + std::string m_string; + std::size_t m_size = 0; + + // perform 0xff replacement and calculate m_size + void preprocessString(); + + public: + class const_iterator; + using iterator = const_iterator; + // note: must be u-suffixed or this will cause a "truncation of + // constant value" warning on MSVC + static constexpr char sentinel = static_cast( 0xffu ); + + explicit AnsiSkippingString( std::string const& text ); + explicit AnsiSkippingString( std::string&& text ); + + const_iterator begin() const; + const_iterator end() const; + + size_t size() const { return m_size; } + + std::string substring( const_iterator begin, + const_iterator end ) const; + }; + + class AnsiSkippingString::const_iterator { + friend AnsiSkippingString; + struct EndTag {}; + + const std::string* m_string; + std::string::const_iterator m_it; + + explicit const_iterator( const std::string& string, EndTag ): + m_string( &string ), m_it( string.end() ) {} + + void tryParseAnsiEscapes(); + void advance(); + void unadvance(); + + public: + using difference_type = std::ptrdiff_t; + using value_type = char; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::bidirectional_iterator_tag; + + explicit const_iterator( const std::string& string ): + m_string( &string ), m_it( string.begin() ) { + tryParseAnsiEscapes(); + } + + char operator*() const { return *m_it; } + + const_iterator& operator++() { + advance(); + return *this; + } + const_iterator operator++( int ) { + iterator prev( *this ); + operator++(); + return prev; + } + const_iterator& operator--() { + unadvance(); + return *this; + } + const_iterator operator--( int ) { + iterator prev( *this ); + operator--(); + return prev; + } + + bool operator==( const_iterator const& other ) const { + return m_it == other.m_it; + } + bool operator!=( const_iterator const& other ) const { + return !operator==( other ); + } + bool operator<=( const_iterator const& other ) const { + return m_it <= other.m_it; + } + + const_iterator oneBefore() const { + auto it = *this; + return --it; + } + }; + /** * Represents a column of text with specific width and indentation * @@ -10100,17 +11084,18 @@ namespace Catch { */ class Column { // String to be written out - std::string m_string; + AnsiSkippingString m_string; // Width of the column for linebreaking size_t m_width = CATCH_CONFIG_CONSOLE_WIDTH - 1; - // Indentation of other lines (including first if initial indent is unset) + // Indentation of other lines (including first if initial indent is + // unset) size_t m_indent = 0; // Indentation of the first line size_t m_initialIndent = std::string::npos; public: /** - * Iterates "lines" in `Column` and return sthem + * Iterates "lines" in `Column` and returns them */ class const_iterator { friend Column; @@ -10118,27 +11103,31 @@ namespace Catch { Column const& m_column; // Where does the current line start? - size_t m_lineStart = 0; + AnsiSkippingString::const_iterator m_lineStart; // How long should the current line be? - size_t m_lineLength = 0; + AnsiSkippingString::const_iterator m_lineEnd; // How far have we checked the string to iterate? - size_t m_parsedTo = 0; + AnsiSkippingString::const_iterator m_parsedTo; // Should a '-' be appended to the line? bool m_addHyphen = false; const_iterator( Column const& column, EndTag ): - m_column( column ), m_lineStart( m_column.m_string.size() ) {} + m_column( column ), + m_lineStart( m_column.m_string.end() ), + m_lineEnd( column.m_string.end() ), + m_parsedTo( column.m_string.end() ) {} // Calculates the length of the current line void calcLength(); - // Returns current indention width + // Returns current indentation width size_t indentSize() const; // Creates an indented and (optionally) suffixed string from // current iterator position, indentation and length. - std::string addIndentAndSuffix( size_t position, - size_t length ) const; + std::string addIndentAndSuffix( + AnsiSkippingString::const_iterator start, + AnsiSkippingString::const_iterator end ) const; public: using difference_type = std::ptrdiff_t; @@ -10155,7 +11144,8 @@ namespace Catch { const_iterator operator++( int ); bool operator==( const_iterator const& other ) const { - return m_lineStart == other.m_lineStart && &m_column == &other.m_column; + return m_lineStart == other.m_lineStart && + &m_column == &other.m_column; } bool operator!=( const_iterator const& other ) const { return !operator==( other ); @@ -10164,29 +11154,47 @@ namespace Catch { using iterator = const_iterator; explicit Column( std::string const& text ): m_string( text ) {} + explicit Column( std::string&& text ): + m_string( CATCH_MOVE( text ) ) {} - Column& width( size_t newWidth ) { + Column& width( size_t newWidth ) & { assert( newWidth > 0 ); m_width = newWidth; return *this; } - Column& indent( size_t newIndent ) { + Column&& width( size_t newWidth ) && { + assert( newWidth > 0 ); + m_width = newWidth; + return CATCH_MOVE( *this ); + } + Column& indent( size_t newIndent ) & { m_indent = newIndent; return *this; } - Column& initialIndent( size_t newIndent ) { + Column&& indent( size_t newIndent ) && { + m_indent = newIndent; + return CATCH_MOVE( *this ); + } + Column& initialIndent( size_t newIndent ) & { m_initialIndent = newIndent; return *this; } + Column&& initialIndent( size_t newIndent ) && { + m_initialIndent = newIndent; + return CATCH_MOVE( *this ); + } size_t width() const { return m_width; } const_iterator begin() const { return const_iterator( *this ); } - const_iterator end() const { return { *this, const_iterator::EndTag{} }; } + const_iterator end() const { + return { *this, const_iterator::EndTag{} }; + } friend std::ostream& operator<<( std::ostream& os, Column const& col ); - Columns operator+( Column const& other ); + friend Columns operator+( Column const& lhs, Column const& rhs ); + friend Columns operator+( Column&& lhs, Column&& rhs ); }; //! Creates a column that serves as an empty space of specific width @@ -10230,8 +11238,10 @@ namespace Catch { iterator begin() const { return iterator( *this ); } iterator end() const { return { *this, iterator::EndTag() }; } - Columns& operator+=( Column const& col ); - Columns operator+( Column const& col ); + friend Columns& operator+=( Columns& lhs, Column const& rhs ); + friend Columns& operator+=( Columns& lhs, Column&& rhs ); + friend Columns operator+( Columns const& lhs, Column const& rhs ); + friend Columns operator+( Columns&& lhs, Column&& rhs ); friend std::ostream& operator<<( std::ostream& os, Columns const& cols ); @@ -10280,16 +11290,25 @@ namespace Catch { #include #include +#include namespace Catch { - enum class XmlFormatting { + enum class XmlFormatting : std::uint8_t { None = 0x00, Indent = 0x01, Newline = 0x02, }; - XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs); - XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs); + constexpr XmlFormatting operator|( XmlFormatting lhs, XmlFormatting rhs ) { + return static_cast( static_cast( lhs ) | + static_cast( rhs ) ); + } + + constexpr XmlFormatting operator&( XmlFormatting lhs, XmlFormatting rhs ) { + return static_cast( static_cast( lhs ) & + static_cast( rhs ) ); + } + /** * Helper for XML-encoding text (escaping angle brackets, quotes, etc) @@ -10301,7 +11320,9 @@ namespace Catch { public: enum ForWhat { ForTextNodes, ForAttributes }; - XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ); + constexpr XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ): + m_str( str ), m_forWhat( forWhat ) {} + void encodeTo( std::ostream& os ) const; @@ -10445,14 +11466,26 @@ namespace Catch { #define CATCH_MATCHERS_IMPL_HPP_INCLUDED +#include + namespace Catch { +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#elif defined __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + template class MatchExpr : public ITransientExpression { ArgT && m_arg; MatcherT const& m_matcher; public: - MatchExpr( ArgT && arg, MatcherT const& matcher ) + constexpr MatchExpr( ArgT && arg, MatcherT const& matcher ) : ITransientExpression{ true, matcher.match( arg ) }, // not forwarding arg here on purpose m_arg( CATCH_FORWARD(arg) ), m_matcher( matcher ) @@ -10465,6 +11498,13 @@ namespace Catch { } }; +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + + namespace Matchers { template class MatcherBase; @@ -10475,7 +11515,8 @@ namespace Catch { void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher ); template - auto makeMatchExpr( ArgT && arg, MatcherT const& matcher ) -> MatchExpr { + constexpr MatchExpr + makeMatchExpr( ArgT&& arg, MatcherT const& matcher ) { return MatchExpr( CATCH_FORWARD(arg), matcher ); } @@ -10489,7 +11530,7 @@ namespace Catch { INTERNAL_CATCH_TRY { \ catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher ) ); \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) @@ -10499,7 +11540,10 @@ namespace Catch { Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ if( catchAssertionHandler.allowThrows() ) \ try { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__ ); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( exceptionType const& ex ) { \ @@ -10510,7 +11554,7 @@ namespace Catch { } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) @@ -11579,12 +12623,14 @@ namespace Catch { public: template + constexpr RangeEqualsMatcher( TargetRangeLike2&& range, Equality2&& predicate ): m_desired( CATCH_FORWARD( range ) ), m_predicate( CATCH_FORWARD( predicate ) ) {} template + constexpr bool match( RangeLike&& rng ) const { auto rng_start = begin( rng ); const auto rng_end = end( rng ); @@ -11617,12 +12663,14 @@ namespace Catch { public: template + constexpr UnorderedRangeEqualsMatcher( TargetRangeLike2&& range, Equality2&& predicate ): m_desired( CATCH_FORWARD( range ) ), m_predicate( CATCH_FORWARD( predicate ) ) {} template + constexpr bool match( RangeLike&& rng ) const { using std::begin; using std::end; @@ -11646,6 +12694,7 @@ namespace Catch { * Uses `std::equal_to` to do the comparison */ template + constexpr std::enable_if_t::value, RangeEqualsMatcher>> RangeEquals( RangeLike&& range ) { @@ -11659,6 +12708,7 @@ namespace Catch { * Uses to provided predicate `predicate` to do the comparisons */ template + constexpr RangeEqualsMatcher RangeEquals( RangeLike&& range, Equality&& predicate ) { return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) }; @@ -11671,6 +12721,7 @@ namespace Catch { * Uses `std::equal_to` to do the comparison */ template + constexpr std::enable_if_t< !Detail::is_matcher::value, UnorderedRangeEqualsMatcher>> @@ -11680,11 +12731,12 @@ namespace Catch { /** * Creates a matcher that checks if all elements in a range are equal - * to all elements in another range, in some permuation. + * to all elements in another range, in some permutation. * * Uses to provided predicate `predicate` to do the comparisons */ template + constexpr UnorderedRangeEqualsMatcher UnorderedRangeEquals( RangeLike&& range, Equality&& predicate ) { return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) }; @@ -11850,11 +12902,10 @@ namespace Matchers { // - a more general approach would be via a compare template that defaults // to using !=. but could be specialised for, e.g. std::vector etc // - then just call that directly - if (m_comparator.size() != v.size()) - return false; - for (std::size_t i = 0; i < v.size(); ++i) - if (m_comparator[i] != v[i]) - return false; + if ( m_comparator.size() != v.size() ) { return false; } + for ( std::size_t i = 0; i < v.size(); ++i ) { + if ( !( m_comparator[i] == v[i] ) ) { return false; } + } return true; } std::string describe() const override { @@ -12358,7 +13409,7 @@ namespace Catch { void skipTest(TestCaseInfo const&) override {} protected: - //! Should the cumulative base store the assertion expansion for succesful assertions? + //! Should the cumulative base store the assertion expansion for successful assertions? bool m_shouldStoreSuccesfulAssertions = true; //! Should the cumulative base store the assertion expansion for failed assertions? bool m_shouldStoreFailedAssertions = true; @@ -12526,6 +13577,93 @@ namespace Catch { #endif // CATCH_REPORTER_HELPERS_HPP_INCLUDED + +#ifndef CATCH_REPORTER_JSON_HPP_INCLUDED +#define CATCH_REPORTER_JSON_HPP_INCLUDED + + +#include + +namespace Catch { + class JsonReporter : public StreamingReporterBase { + public: + JsonReporter( ReporterConfig&& config ); + + ~JsonReporter() override; + + static std::string getDescription(); + + public: // StreamingReporterBase + void testRunStarting( TestRunInfo const& runInfo ) override; + void testRunEnded( TestRunStats const& runStats ) override; + + void testCaseStarting( TestCaseInfo const& tcInfo ) override; + void testCaseEnded( TestCaseStats const& tcStats ) override; + + void testCasePartialStarting( TestCaseInfo const& tcInfo, + uint64_t index ) override; + void testCasePartialEnded( TestCaseStats const& tcStats, + uint64_t index ) override; + + void sectionStarting( SectionInfo const& sectionInfo ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + + void assertionStarting( AssertionInfo const& assertionInfo ) override; + void assertionEnded( AssertionStats const& assertionStats ) override; + + //void testRunEndedCumulative() override; + + void benchmarkPreparing( StringRef name ) override; + void benchmarkStarting( BenchmarkInfo const& ) override; + void benchmarkEnded( BenchmarkStats<> const& ) override; + void benchmarkFailed( StringRef error ) override; + + void listReporters( + std::vector const& descriptions ) override; + void listListeners( + std::vector const& descriptions ) override; + void listTests( std::vector const& tests ) override; + void listTags( std::vector const& tags ) override; + + private: + Timer m_testCaseTimer; + enum class Writer { + Object, + Array + }; + + JsonArrayWriter& startArray(); + JsonArrayWriter& startArray( StringRef key ); + + JsonObjectWriter& startObject(); + JsonObjectWriter& startObject( StringRef key ); + + void endObject(); + void endArray(); + + bool isInside( Writer writer ); + + void startListing(); + void endListing(); + + // Invariant: + // When m_writers is not empty and its top element is + // - Writer::Object, then m_objectWriters is not be empty + // - Writer::Array, then m_arrayWriters shall not be empty + std::stack m_objectWriters{}; + std::stack m_arrayWriters{}; + std::stack m_writers{}; + + bool m_startedListing = false; + + // std::size_t m_sectionDepth = 0; + // std::size_t m_sectionStarted = 0; + }; +} // namespace Catch + +#endif // CATCH_REPORTER_JSON_HPP_INCLUDED + + #ifndef CATCH_REPORTER_JUNIT_HPP_INCLUDED #define CATCH_REPORTER_JUNIT_HPP_INCLUDED @@ -12537,8 +13675,6 @@ namespace Catch { public: JunitReporter(ReporterConfig&& _config); - ~JunitReporter() override = default; - static std::string getDescription(); void testRunStarting(TestRunInfo const& runInfo) override; @@ -12623,7 +13759,7 @@ namespace Catch { void assertionEnded( AssertionStats const& assertionStats ) override; void sectionEnded( SectionStats const& sectionStats ) override; - void testCasePartialEnded(TestCaseStats const& testInfo, uint64_t partNumber) override; + void testCasePartialEnded(TestCaseStats const& testStats, uint64_t partNumber) override; void testCaseEnded( TestCaseStats const& testCaseStats ) override; void testRunEnded( TestRunStats const& testRunStats ) override; @@ -12665,7 +13801,8 @@ namespace Catch { //! independent on the reporter's concrete type void registerReporterImpl( std::string const& name, IReporterFactoryPtr reporterPtr ); - + //! Actually registers the factory, independent on listener's concrete type + void registerListenerImpl( Detail::unique_ptr listenerFactory ); } // namespace Detail class IEventListener; @@ -12726,7 +13863,7 @@ namespace Catch { public: ListenerRegistrar(StringRef listenerName) { - getMutableRegistryHub().registerListener( Detail::make_unique(listenerName) ); + registerListenerImpl( Detail::make_unique(listenerName) ); } }; } @@ -12774,12 +13911,10 @@ namespace Catch { : CumulativeReporterBase(CATCH_MOVE(config)) , xml(m_stream) { m_preferences.shouldRedirectStdOut = true; - m_preferences.shouldReportAllAssertions = true; + m_preferences.shouldReportAllAssertions = false; m_shouldStoreSuccesfulAssertions = false; } - ~SonarQubeReporter() override = default; - static std::string getDescription() { using namespace std::string_literals; return "Reports test results in the Generic Test Data SonarQube XML format"s; @@ -12792,7 +13927,7 @@ namespace Catch { xml.endElement(); } - void writeRun( TestRunNode const& groupNode ); + void writeRun( TestRunNode const& runNode ); void writeTestFile(StringRef filename, std::vector const& testCaseNodes); @@ -12826,7 +13961,6 @@ namespace Catch { StreamingReporterBase( CATCH_MOVE(config) ) { m_preferences.shouldReportAllAssertions = true; } - ~TAPReporter() override = default; static std::string getDescription() { using namespace std::string_literals; @@ -12878,8 +14012,8 @@ namespace Catch { return "Reports test results as TeamCity service messages"s; } - void testRunStarting( TestRunInfo const& groupInfo ) override; - void testRunEnded( TestRunStats const& testGroupStats ) override; + void testRunStarting( TestRunInfo const& runInfo ) override; + void testRunEnded( TestRunStats const& runStats ) override; void assertionEnded(AssertionStats const& assertionStats) override; diff --git a/src/external/catch2/fuzzing/NullOStream.cpp b/src/external/catch2/fuzzing/NullOStream.cpp index 53e0893d..e3a181e8 100644 --- a/src/external/catch2/fuzzing/NullOStream.cpp +++ b/src/external/catch2/fuzzing/NullOStream.cpp @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + #include "NullOStream.h" void NullOStream::avoidOutOfLineVirtualCompilerWarning() diff --git a/src/external/catch2/fuzzing/NullOStream.h b/src/external/catch2/fuzzing/NullOStream.h index e1fe15b0..abbec09c 100644 --- a/src/external/catch2/fuzzing/NullOStream.h +++ b/src/external/catch2/fuzzing/NullOStream.h @@ -1,3 +1,11 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + #pragma once #include diff --git a/src/external/catch2/fuzzing/fuzz_TestSpecParser.cpp b/src/external/catch2/fuzzing/fuzz_TestSpecParser.cpp index af4de406..3aba8c84 100644 --- a/src/external/catch2/fuzzing/fuzz_TestSpecParser.cpp +++ b/src/external/catch2/fuzzing/fuzz_TestSpecParser.cpp @@ -1,4 +1,10 @@ -//License: Boost 1.0 + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 //By Paul Dreik 2020 #include diff --git a/src/external/catch2/fuzzing/fuzz_XmlWriter.cpp b/src/external/catch2/fuzzing/fuzz_XmlWriter.cpp index f8e5a0d9..70c4ed80 100644 --- a/src/external/catch2/fuzzing/fuzz_XmlWriter.cpp +++ b/src/external/catch2/fuzzing/fuzz_XmlWriter.cpp @@ -1,4 +1,10 @@ -//License: Boost 1.0 + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 //By Paul Dreik 2020 #include diff --git a/src/external/catch2/fuzzing/fuzz_textflow.cpp b/src/external/catch2/fuzzing/fuzz_textflow.cpp index eafe79fe..7000f420 100644 --- a/src/external/catch2/fuzzing/fuzz_textflow.cpp +++ b/src/external/catch2/fuzzing/fuzz_textflow.cpp @@ -1,4 +1,10 @@ -//License: Boost 1.0 + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 //By Paul Dreik 2020 #include diff --git a/src/external/catch2/meson.build b/src/external/catch2/meson.build index 1faca35f..99bf3aff 100644 --- a/src/external/catch2/meson.build +++ b/src/external/catch2/meson.build @@ -8,10 +8,12 @@ project( 'catch2', 'cpp', - version: '3.3.2', # CML version placeholder, don't delete + version: '3.7.1', # CML version placeholder, don't delete license: 'BSL-1.0', - meson_version: '>=0.50.0', + meson_version: '>=0.54.1', ) subdir('src/catch2') -subdir('tests') +if get_option('tests') + subdir('tests') +endif diff --git a/src/external/catch2/meson_options.txt b/src/external/catch2/meson_options.txt new file mode 100644 index 00000000..76904873 --- /dev/null +++ b/src/external/catch2/meson_options.txt @@ -0,0 +1 @@ +option('tests', type: 'boolean', value: true, description: 'Build the unit tests') diff --git a/src/external/catch2/src/CMakeLists.txt b/src/external/catch2/src/CMakeLists.txt index fd05dbdd..c40de040 100644 --- a/src/external/catch2/src/CMakeLists.txt +++ b/src/external/catch2/src/CMakeLists.txt @@ -21,6 +21,8 @@ set(BENCHMARK_HEADERS ${SOURCES_DIR}/benchmark/catch_sample_analysis.hpp ${SOURCES_DIR}/benchmark/detail/catch_analyse.hpp ${SOURCES_DIR}/benchmark/detail/catch_benchmark_function.hpp + ${SOURCES_DIR}/benchmark/detail/catch_benchmark_stats.hpp + ${SOURCES_DIR}/benchmark/detail/catch_benchmark_stats_fwd.hpp ${SOURCES_DIR}/benchmark/detail/catch_complete_invoke.hpp ${SOURCES_DIR}/benchmark/detail/catch_estimate_clock.hpp ${SOURCES_DIR}/benchmark/detail/catch_measure.hpp @@ -31,6 +33,7 @@ set(BENCHMARK_HEADERS ) set(BENCHMARK_SOURCES ${SOURCES_DIR}/benchmark/catch_chronometer.cpp + ${SOURCES_DIR}/benchmark/detail/catch_analyse.cpp ${SOURCES_DIR}/benchmark/detail/catch_benchmark_function.cpp ${SOURCES_DIR}/benchmark/detail/catch_run_for_at_least.cpp ${SOURCES_DIR}/benchmark/detail/catch_stats.cpp @@ -45,6 +48,7 @@ set(IMPL_HEADERS ${SOURCES_DIR}/catch_approx.hpp ${SOURCES_DIR}/catch_assertion_info.hpp ${SOURCES_DIR}/catch_assertion_result.hpp + ${SOURCES_DIR}/catch_case_sensitive.hpp ${SOURCES_DIR}/catch_config.hpp ${SOURCES_DIR}/catch_get_random_seed.hpp ${SOURCES_DIR}/catch_message.hpp @@ -64,13 +68,14 @@ set(IMPL_HEADERS ${SOURCES_DIR}/catch_version_macros.hpp ${SOURCES_DIR}/internal/catch_assertion_handler.hpp ${SOURCES_DIR}/internal/catch_case_insensitive_comparisons.hpp - ${SOURCES_DIR}/internal/catch_case_sensitive.hpp ${SOURCES_DIR}/internal/catch_clara.hpp ${SOURCES_DIR}/internal/catch_commandline.hpp ${SOURCES_DIR}/internal/catch_compare_traits.hpp ${SOURCES_DIR}/internal/catch_compiler_capabilities.hpp ${SOURCES_DIR}/internal/catch_config_android_logwrite.hpp ${SOURCES_DIR}/internal/catch_config_counter.hpp + ${SOURCES_DIR}/internal/catch_config_prefix_messages.hpp + ${SOURCES_DIR}/internal/catch_config_static_analysis_support.hpp ${SOURCES_DIR}/internal/catch_config_uncaught_exceptions.hpp ${SOURCES_DIR}/internal/catch_config_wchar.hpp ${SOURCES_DIR}/internal/catch_console_colour.hpp @@ -89,6 +94,7 @@ set(IMPL_HEADERS ${SOURCES_DIR}/internal/catch_getenv.hpp ${SOURCES_DIR}/internal/catch_istream.hpp ${SOURCES_DIR}/internal/catch_is_permutation.hpp + ${SOURCES_DIR}/internal/catch_jsonwriter.hpp ${SOURCES_DIR}/internal/catch_lazy_expr.hpp ${SOURCES_DIR}/internal/catch_leak_detector.hpp ${SOURCES_DIR}/internal/catch_list.hpp @@ -104,6 +110,8 @@ set(IMPL_HEADERS ${SOURCES_DIR}/internal/catch_polyfills.hpp ${SOURCES_DIR}/internal/catch_preprocessor.hpp ${SOURCES_DIR}/internal/catch_preprocessor_remove_parens.hpp + ${SOURCES_DIR}/internal/catch_random_floating_point_helpers.hpp + ${SOURCES_DIR}/internal/catch_random_integer_helpers.hpp ${SOURCES_DIR}/internal/catch_random_number_generator.hpp ${SOURCES_DIR}/internal/catch_random_seed_generation.hpp ${SOURCES_DIR}/internal/catch_reporter_registry.hpp @@ -128,10 +136,13 @@ set(IMPL_HEADERS ${SOURCES_DIR}/internal/catch_test_failure_exception.hpp ${SOURCES_DIR}/internal/catch_test_macro_impl.hpp ${SOURCES_DIR}/internal/catch_test_registry.hpp + ${SOURCES_DIR}/internal/catch_test_run_info.hpp ${SOURCES_DIR}/internal/catch_test_spec_parser.hpp ${SOURCES_DIR}/internal/catch_textflow.hpp ${SOURCES_DIR}/internal/catch_to_string.hpp ${SOURCES_DIR}/internal/catch_uncaught_exceptions.hpp + ${SOURCES_DIR}/internal/catch_uniform_floating_point_distribution.hpp + ${SOURCES_DIR}/internal/catch_uniform_integer_distribution.hpp ${SOURCES_DIR}/internal/catch_unique_name.hpp ${SOURCES_DIR}/internal/catch_unique_ptr.hpp ${SOURCES_DIR}/internal/catch_void_type.hpp @@ -153,6 +164,7 @@ set(IMPL_SOURCES ${SOURCES_DIR}/catch_timer.cpp ${SOURCES_DIR}/catch_tostring.cpp ${SOURCES_DIR}/catch_totals.cpp + ${SOURCES_DIR}/catch_translate_exception.cpp ${SOURCES_DIR}/catch_version.cpp ${SOURCES_DIR}/internal/catch_assertion_handler.cpp ${SOURCES_DIR}/internal/catch_case_insensitive_comparisons.cpp @@ -171,6 +183,7 @@ set(IMPL_SOURCES ${SOURCES_DIR}/internal/catch_floating_point_helpers.cpp ${SOURCES_DIR}/internal/catch_getenv.cpp ${SOURCES_DIR}/internal/catch_istream.cpp + ${SOURCES_DIR}/internal/catch_jsonwriter.cpp ${SOURCES_DIR}/internal/catch_lazy_expr.cpp ${SOURCES_DIR}/internal/catch_leak_detector.cpp ${SOURCES_DIR}/internal/catch_list.cpp @@ -182,7 +195,6 @@ set(IMPL_SOURCES ${SOURCES_DIR}/internal/catch_random_seed_generation.cpp ${SOURCES_DIR}/internal/catch_reporter_registry.cpp ${SOURCES_DIR}/internal/catch_reporter_spec_parser.cpp - ${SOURCES_DIR}/internal/catch_result_type.cpp ${SOURCES_DIR}/internal/catch_reusable_string_stream.cpp ${SOURCES_DIR}/internal/catch_run_context.cpp ${SOURCES_DIR}/internal/catch_section.cpp @@ -216,8 +228,8 @@ set(INTERFACE_HEADERS ${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_reporter.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.hpp - ${SOURCES_DIR}/interfaces/catch_interfaces_reporter_registry.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_tag_alias_registry.hpp + ${SOURCES_DIR}/interfaces/catch_interfaces_test_invoker.hpp ${SOURCES_DIR}/interfaces/catch_interfaces_testcase.hpp ) set(INTERFACE_SOURCES @@ -228,7 +240,6 @@ set(INTERFACE_SOURCES ${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.cpp ${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp ${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.cpp - ${SOURCES_DIR}/interfaces/catch_interfaces_reporter_registry.cpp ${SOURCES_DIR}/interfaces/catch_interfaces_testcase.cpp ) set(INTERFACE_FILES ${INTERFACE_HEADERS} ${INTERFACE_SOURCES}) @@ -284,6 +295,7 @@ set(REPORTER_HEADERS ${SOURCES_DIR}/reporters/catch_reporter_cumulative_base.hpp ${SOURCES_DIR}/reporters/catch_reporter_event_listener.hpp ${SOURCES_DIR}/reporters/catch_reporter_helpers.hpp + ${SOURCES_DIR}/reporters/catch_reporter_json.hpp ${SOURCES_DIR}/reporters/catch_reporter_junit.hpp ${SOURCES_DIR}/reporters/catch_reporter_multi.hpp ${SOURCES_DIR}/reporters/catch_reporter_registrars.hpp @@ -302,6 +314,7 @@ set(REPORTER_SOURCES ${SOURCES_DIR}/reporters/catch_reporter_cumulative_base.cpp ${SOURCES_DIR}/reporters/catch_reporter_event_listener.cpp ${SOURCES_DIR}/reporters/catch_reporter_helpers.cpp + ${SOURCES_DIR}/reporters/catch_reporter_json.cpp ${SOURCES_DIR}/reporters/catch_reporter_junit.cpp ${SOURCES_DIR}/reporters/catch_reporter_multi.cpp ${SOURCES_DIR}/reporters/catch_reporter_registrars.cpp @@ -335,11 +348,13 @@ source_group("generated headers" ) add_library(Catch2 ${ALL_FILES}) -add_build_reproducibility_settings(Catch2) +if (CATCH_ENABLE_REPRODUCIBLE_BUILD) + add_build_reproducibility_settings(Catch2) +endif() add_library(Catch2::Catch2 ALIAS Catch2) if (ANDROID) - target_link_libraries(Catch2 INTERFACE log) + target_link_libraries(Catch2 PRIVATE log) endif() set_target_properties(Catch2 PROPERTIES @@ -347,29 +362,10 @@ set_target_properties(Catch2 PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION}) -# depend on bunch of C++11 and C++14 features to have C++14 enabled by default +# require C++14 target_compile_features(Catch2 PUBLIC - cxx_alignas - cxx_alignof - cxx_attributes - cxx_auto_type - cxx_constexpr - cxx_defaulted_functions - cxx_deleted_functions - cxx_final - cxx_lambdas - cxx_noexcept - cxx_override - cxx_range_for - cxx_rvalue_references - cxx_static_assert - cxx_strong_enums - cxx_trailing_return_types - cxx_unicode_literals - cxx_user_literals - cxx_variable_templates - cxx_variadic_macros + cxx_std_14 ) configure_file( @@ -388,7 +384,9 @@ target_include_directories(Catch2 add_library(Catch2WithMain ${SOURCES_DIR}/internal/catch_main.cpp ) -add_build_reproducibility_settings(Catch2WithMain) +if (CATCH_ENABLE_REPRODUCIBLE_BUILD) + add_build_reproducibility_settings(Catch2WithMain) +endif() add_library(Catch2::Catch2WithMain ALIAS Catch2WithMain) target_link_libraries(Catch2WithMain PUBLIC Catch2) set_target_properties(Catch2WithMain @@ -458,26 +456,7 @@ if (CATCH_BUILD_EXAMPLES OR CATCH_BUILD_EXTRA_TESTS) ) target_compile_features(Catch2_buildall_interface INTERFACE - cxx_alignas - cxx_alignof - cxx_attributes - cxx_auto_type - cxx_constexpr - cxx_defaulted_functions - cxx_deleted_functions - cxx_final - cxx_lambdas - cxx_noexcept - cxx_override - cxx_range_for - cxx_rvalue_references - cxx_static_assert - cxx_strong_enums - cxx_trailing_return_types - cxx_unicode_literals - cxx_user_literals - cxx_variable_templates - cxx_variadic_macros + cxx_std_14 ) endif() diff --git a/src/external/catch2/src/catch2/benchmark/catch_benchmark.hpp b/src/external/catch2/src/catch2/benchmark/catch_benchmark.hpp index 1cf10be6..d0f88cfc 100644 --- a/src/external/catch2/src/catch2/benchmark/catch_benchmark.hpp +++ b/src/external/catch2/src/catch2/benchmark/catch_benchmark.hpp @@ -10,26 +10,28 @@ #ifndef CATCH_BENCHMARK_HPP_INCLUDED #define CATCH_BENCHMARK_HPP_INCLUDED -#include +#include #include #include -#include -#include #include -#include +#include +#include +#include +#include +#include +#include #include #include #include #include -#include #include #include #include #include -#include +#include +#include #include -#include #include namespace Catch { @@ -43,16 +45,18 @@ namespace Catch { : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} template - ExecutionPlan> prepare(const IConfig &cfg, Environment> env) const { + ExecutionPlan prepare(const IConfig &cfg, Environment env) { auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; auto run_time = std::max(min_time, std::chrono::duration_cast(cfg.benchmarkWarmupTime())); - auto&& test = Detail::run_for_at_least(std::chrono::duration_cast>(run_time), 1, fun); + auto&& test = Detail::run_for_at_least(std::chrono::duration_cast(run_time), 1, fun); int new_iters = static_cast(std::ceil(min_time * test.iterations / test.elapsed)); - return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; + return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; } template void run() { + static_assert( Clock::is_steady, + "Benchmarking clock should be steady" ); auto const* cfg = getCurrentContext().getConfig(); auto env = Detail::measure_environment(); @@ -79,10 +83,10 @@ namespace Catch { return plan.template run(*cfg, env); }); - auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); - BenchmarkStats> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; + auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size()); + BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; getResultCapture().benchmarkEnded(stats); - } CATCH_CATCH_ANON (TestFailureException) { + } CATCH_CATCH_ANON (TestFailureException const&) { getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); } CATCH_CATCH_ALL{ getResultCapture().benchmarkFailed(translateActiveException()); diff --git a/src/external/catch2/src/catch2/benchmark/catch_benchmark_all.hpp b/src/external/catch2/src/catch2/benchmark/catch_benchmark_all.hpp index eb81f238..56fc7c74 100644 --- a/src/external/catch2/src/catch2/benchmark/catch_benchmark_all.hpp +++ b/src/external/catch2/src/catch2/benchmark/catch_benchmark_all.hpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/src/external/catch2/src/catch2/benchmark/catch_chronometer.hpp b/src/external/catch2/src/catch2/benchmark/catch_chronometer.hpp index bce2406b..95498e6b 100644 --- a/src/external/catch2/src/catch2/benchmark/catch_chronometer.hpp +++ b/src/external/catch2/src/catch2/benchmark/catch_chronometer.hpp @@ -12,7 +12,6 @@ #include #include -#include #include #include @@ -33,7 +32,10 @@ namespace Catch { void start() override { started = Clock::now(); } void finish() override { finished = Clock::now(); } - ClockDuration elapsed() const { return finished - started; } + IDuration elapsed() const { + return std::chrono::duration_cast( + finished - started ); + } TimePoint started; TimePoint finished; diff --git a/src/external/catch2/src/catch2/benchmark/catch_clock.hpp b/src/external/catch2/src/catch2/benchmark/catch_clock.hpp index cee46097..4068c4d2 100644 --- a/src/external/catch2/src/catch2/benchmark/catch_clock.hpp +++ b/src/external/catch2/src/catch2/benchmark/catch_clock.hpp @@ -11,28 +11,16 @@ #define CATCH_CLOCK_HPP_INCLUDED #include -#include namespace Catch { namespace Benchmark { - template - using ClockDuration = typename Clock::duration; - template - using FloatDuration = std::chrono::duration; + using IDuration = std::chrono::nanoseconds; + using FDuration = std::chrono::duration; template using TimePoint = typename Clock::time_point; using default_clock = std::chrono::steady_clock; - - template - struct now { - TimePoint operator()() const { - return Clock::now(); - } - }; - - using fp_seconds = std::chrono::duration>; } // namespace Benchmark } // namespace Catch diff --git a/src/external/catch2/src/catch2/benchmark/catch_environment.hpp b/src/external/catch2/src/catch2/benchmark/catch_environment.hpp index de4d77df..da3f2fa9 100644 --- a/src/external/catch2/src/catch2/benchmark/catch_environment.hpp +++ b/src/external/catch2/src/catch2/benchmark/catch_environment.hpp @@ -15,21 +15,13 @@ namespace Catch { namespace Benchmark { - template struct EnvironmentEstimate { - Duration mean; + FDuration mean; OutlierClassification outliers; - - template - operator EnvironmentEstimate() const { - return { mean, outliers }; - } }; - template struct Environment { - using clock_type = Clock; - EnvironmentEstimate> clock_resolution; - EnvironmentEstimate> clock_cost; + EnvironmentEstimate clock_resolution; + EnvironmentEstimate clock_cost; }; } // namespace Benchmark } // namespace Catch diff --git a/src/external/catch2/src/catch2/benchmark/catch_estimate.hpp b/src/external/catch2/src/catch2/benchmark/catch_estimate.hpp index be594a18..64383a2e 100644 --- a/src/external/catch2/src/catch2/benchmark/catch_estimate.hpp +++ b/src/external/catch2/src/catch2/benchmark/catch_estimate.hpp @@ -12,17 +12,12 @@ namespace Catch { namespace Benchmark { - template + template struct Estimate { - Duration point; - Duration lower_bound; - Duration upper_bound; + Type point; + Type lower_bound; + Type upper_bound; double confidence_interval; - - template - operator Estimate() const { - return { point, lower_bound, upper_bound, confidence_interval }; - } }; } // namespace Benchmark } // namespace Catch diff --git a/src/external/catch2/src/catch2/benchmark/catch_execution_plan.hpp b/src/external/catch2/src/catch2/benchmark/catch_execution_plan.hpp index 039de7ee..17ca589f 100644 --- a/src/external/catch2/src/catch2/benchmark/catch_execution_plan.hpp +++ b/src/external/catch2/src/catch2/benchmark/catch_execution_plan.hpp @@ -17,38 +17,38 @@ #include #include -#include -#include +#include namespace Catch { namespace Benchmark { - template struct ExecutionPlan { int iterations_per_sample; - Duration estimated_duration; + FDuration estimated_duration; Detail::BenchmarkFunction benchmark; - Duration warmup_time; + FDuration warmup_time; int warmup_iterations; - template - operator ExecutionPlan() const { - return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations }; - } - template - std::vector> run(const IConfig &cfg, Environment> env) const { + std::vector run(const IConfig &cfg, Environment env) const { // warmup a bit - Detail::run_for_at_least(std::chrono::duration_cast>(warmup_time), warmup_iterations, Detail::repeat(now{})); - - std::vector> times; - times.reserve(cfg.benchmarkSamples()); - std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] { + Detail::run_for_at_least( + std::chrono::duration_cast( warmup_time ), + warmup_iterations, + Detail::repeat( []() { return Clock::now(); } ) + ); + + std::vector times; + const auto num_samples = cfg.benchmarkSamples(); + times.reserve( num_samples ); + for ( size_t i = 0; i < num_samples; ++i ) { Detail::ChronometerModel model; - this->benchmark(Chronometer(model, iterations_per_sample)); + this->benchmark( Chronometer( model, iterations_per_sample ) ); auto sample_time = model.elapsed() - env.clock_cost.mean; - if (sample_time < FloatDuration::zero()) sample_time = FloatDuration::zero(); - return sample_time / iterations_per_sample; - }); + if ( sample_time < FDuration::zero() ) { + sample_time = FDuration::zero(); + } + times.push_back(sample_time / iterations_per_sample); + } return times; } }; diff --git a/src/external/catch2/src/catch2/benchmark/catch_optimizer.hpp b/src/external/catch2/src/catch2/benchmark/catch_optimizer.hpp index 0dbfc145..61e6571f 100644 --- a/src/external/catch2/src/catch2/benchmark/catch_optimizer.hpp +++ b/src/external/catch2/src/catch2/benchmark/catch_optimizer.hpp @@ -10,7 +10,7 @@ #ifndef CATCH_OPTIMIZER_HPP_INCLUDED #define CATCH_OPTIMIZER_HPP_INCLUDED -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) # include // atomic_thread_fence #endif @@ -32,16 +32,23 @@ namespace Catch { namespace Detail { inline void optimizer_barrier() { keep_memory(); } } // namespace Detail -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) +#if defined(_MSVC_VER) #pragma optimize("", off) +#elif defined(__IAR_SYSTEMS_ICC__) +// For IAR the pragma only affects the following function +#pragma optimize=disable +#endif template inline void keep_memory(T* p) { // thanks @milleniumbug *reinterpret_cast(p) = *reinterpret_cast(p); } // TODO equivalent keep_memory() +#if defined(_MSVC_VER) #pragma optimize("", on) +#endif namespace Detail { inline void optimizer_barrier() { @@ -63,7 +70,7 @@ namespace Catch { template inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { - CATCH_FORWARD(fn) (CATCH_FORWARD(args)...); + CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...); } } // namespace Benchmark } // namespace Catch diff --git a/src/external/catch2/src/catch2/benchmark/catch_outlier_classification.hpp b/src/external/catch2/src/catch2/benchmark/catch_outlier_classification.hpp index e31d65d4..b9a11782 100644 --- a/src/external/catch2/src/catch2/benchmark/catch_outlier_classification.hpp +++ b/src/external/catch2/src/catch2/benchmark/catch_outlier_classification.hpp @@ -19,7 +19,7 @@ namespace Catch { int high_mild = 0; // 1.5 to 3 times IQR above Q3 int high_severe = 0; // more than 3 times IQR above Q3 - int total() const { + constexpr int total() const { return low_severe + low_mild + high_mild + high_severe; } }; diff --git a/src/external/catch2/src/catch2/benchmark/catch_sample_analysis.hpp b/src/external/catch2/src/catch2/benchmark/catch_sample_analysis.hpp index d849d246..aeb87d05 100644 --- a/src/external/catch2/src/catch2/benchmark/catch_sample_analysis.hpp +++ b/src/external/catch2/src/catch2/benchmark/catch_sample_analysis.hpp @@ -10,38 +10,20 @@ #ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED #define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED -#include #include #include -#include +#include -#include #include -#include namespace Catch { namespace Benchmark { - template struct SampleAnalysis { - std::vector samples; - Estimate mean; - Estimate standard_deviation; + std::vector samples; + Estimate mean; + Estimate standard_deviation; OutlierClassification outliers; double outlier_variance; - - template - operator SampleAnalysis() const { - std::vector samples2; - samples2.reserve(samples.size()); - std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); - return { - CATCH_MOVE(samples2), - mean, - standard_deviation, - outliers, - outlier_variance, - }; - } }; } // namespace Benchmark } // namespace Catch diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_analyse.cpp b/src/external/catch2/src/catch2/benchmark/detail/catch_analyse.cpp new file mode 100644 index 00000000..14d7f450 --- /dev/null +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_analyse.cpp @@ -0,0 +1,85 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +// Adapted from donated nonius code. + +#include +#include +#include +#include +#include +#include + +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last) { + if (!cfg.benchmarkNoAnalysis()) { + std::vector samples; + samples.reserve(static_cast(last - first)); + for (auto current = first; current != last; ++current) { + samples.push_back( current->count() ); + } + + auto analysis = Catch::Benchmark::Detail::analyse_samples( + cfg.benchmarkConfidenceInterval(), + cfg.benchmarkResamples(), + samples.data(), + samples.data() + samples.size() ); + auto outliers = Catch::Benchmark::Detail::classify_outliers( + samples.data(), samples.data() + samples.size() ); + + auto wrap_estimate = [](Estimate e) { + return Estimate { + FDuration(e.point), + FDuration(e.lower_bound), + FDuration(e.upper_bound), + e.confidence_interval, + }; + }; + std::vector samples2; + samples2.reserve(samples.size()); + for (auto s : samples) { + samples2.push_back( FDuration( s ) ); + } + + return { + CATCH_MOVE(samples2), + wrap_estimate(analysis.mean), + wrap_estimate(analysis.standard_deviation), + outliers, + analysis.outlier_variance, + }; + } else { + std::vector samples; + samples.reserve(static_cast(last - first)); + + FDuration mean = FDuration(0); + int i = 0; + for (auto it = first; it < last; ++it, ++i) { + samples.push_back(*it); + mean += *it; + } + mean /= i; + + return SampleAnalysis{ + CATCH_MOVE(samples), + Estimate{ mean, mean, mean, 0.0 }, + Estimate{ FDuration( 0 ), + FDuration( 0 ), + FDuration( 0 ), + 0.0 }, + OutlierClassification{}, + 0.0 + }; + } + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_analyse.hpp b/src/external/catch2/src/catch2/benchmark/detail/catch_analyse.hpp index 77b0a9d3..5e3f7b0f 100644 --- a/src/external/catch2/src/catch2/benchmark/detail/catch_analyse.hpp +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_analyse.hpp @@ -11,68 +11,15 @@ #define CATCH_ANALYSE_HPP_INCLUDED #include -#include #include -#include -#include -#include -#include -#include -#include namespace Catch { + class IConfig; + namespace Benchmark { namespace Detail { - template - SampleAnalysis analyse(const IConfig &cfg, Environment, Iterator first, Iterator last) { - if (!cfg.benchmarkNoAnalysis()) { - std::vector samples; - samples.reserve(static_cast(last - first)); - std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); }); - - auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end()); - auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end()); - - auto wrap_estimate = [](Estimate e) { - return Estimate { - Duration(e.point), - Duration(e.lower_bound), - Duration(e.upper_bound), - e.confidence_interval, - }; - }; - std::vector samples2; - samples2.reserve(samples.size()); - std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); }); - return { - CATCH_MOVE(samples2), - wrap_estimate(analysis.mean), - wrap_estimate(analysis.standard_deviation), - outliers, - analysis.outlier_variance, - }; - } else { - std::vector samples; - samples.reserve(static_cast(last - first)); - - Duration mean = Duration(0); - int i = 0; - for (auto it = first; it < last; ++it, ++i) { - samples.push_back(Duration(*it)); - mean += Duration(*it); - } - mean /= i; - - return { - CATCH_MOVE(samples), - Estimate{mean, mean, mean, 0.0}, - Estimate{Duration(0), Duration(0), Duration(0), 0.0}, - OutlierClassification{}, - 0.0 - }; - } - } + SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last); } // namespace Detail } // namespace Benchmark } // namespace Catch diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_function.cpp b/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_function.cpp index b437d049..66d4e619 100644 --- a/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_function.cpp +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_function.cpp @@ -11,7 +11,13 @@ namespace Catch { namespace Benchmark { namespace Detail { + struct do_nothing { + void operator()() const {} + }; + BenchmarkFunction::callable::~callable() = default; + BenchmarkFunction::BenchmarkFunction(): + f( new model{ {} } ){} } // namespace Detail } // namespace Benchmark } // namespace Catch diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp b/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp index 15298258..a03cb112 100644 --- a/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp @@ -11,7 +11,6 @@ #define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED #include -#include #include #include #include @@ -36,22 +35,17 @@ namespace Catch { private: struct callable { virtual void call(Chronometer meter) const = 0; - virtual Catch::Detail::unique_ptr clone() const = 0; virtual ~callable(); // = default; callable() = default; - callable(callable const&) = default; - callable& operator=(callable const&) = default; + callable(callable&&) = default; + callable& operator=(callable&&) = default; }; template struct model : public callable { model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {} model(Fun const& fun_) : fun(fun_) {} - Catch::Detail::unique_ptr clone() const override { - return Catch::Detail::make_unique>( *this ); - } - void call(Chronometer meter) const override { call(meter, is_callable()); } @@ -65,14 +59,8 @@ namespace Catch { Fun fun; }; - struct do_nothing { void operator()() const {} }; - - template - BenchmarkFunction(model* c) : f(c) {} - public: - BenchmarkFunction() - : f(new model{ {} }) {} + BenchmarkFunction(); template ::value, int> = 0> @@ -82,20 +70,12 @@ namespace Catch { BenchmarkFunction( BenchmarkFunction&& that ) noexcept: f( CATCH_MOVE( that.f ) ) {} - BenchmarkFunction(BenchmarkFunction const& that) - : f(that.f->clone()) {} - BenchmarkFunction& operator=( BenchmarkFunction&& that ) noexcept { f = CATCH_MOVE( that.f ); return *this; } - BenchmarkFunction& operator=(BenchmarkFunction const& that) { - f = that.f->clone(); - return *this; - } - void operator()(Chronometer meter) const { f->call(meter); } private: diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp b/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp new file mode 100644 index 00000000..3633bc9f --- /dev/null +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp @@ -0,0 +1,48 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED +#define CATCH_BENCHMARK_STATS_HPP_INCLUDED + +#include +#include +// The fwd decl & default specialization needs to be seen by VS2017 before +// BenchmarkStats itself, or VS2017 will report compilation error. +#include + +#include +#include + +namespace Catch { + + struct BenchmarkInfo { + std::string name; + double estimatedDuration; + int iterations; + unsigned int samples; + unsigned int resamples; + double clockResolution; + double clockCost; + }; + + // We need to keep template parameter for backwards compatibility, + // but we also do not want to use the template paraneter. + template + struct BenchmarkStats { + BenchmarkInfo info; + + std::vector samples; + Benchmark::Estimate mean; + Benchmark::Estimate standardDeviation; + Benchmark::OutlierClassification outliers; + double outlierVariance; + }; + + +} // end namespace Catch + +#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp b/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp new file mode 100644 index 00000000..2ccc25d5 --- /dev/null +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp @@ -0,0 +1,23 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED +#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED + +#include + +namespace Catch { + + // We cannot forward declare the type with default template argument + // multiple times, so it is split out into a separate header so that + // we can prevent multiple declarations in dependees + template + struct BenchmarkStats; + +} // end namespace Catch + +#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_complete_invoke.hpp b/src/external/catch2/src/catch2/benchmark/detail/catch_complete_invoke.hpp index 49db413e..4dff4b7e 100644 --- a/src/external/catch2/src/catch2/benchmark/detail/catch_complete_invoke.hpp +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_complete_invoke.hpp @@ -10,14 +10,9 @@ #ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED #define CATCH_COMPLETE_INVOKE_HPP_INCLUDED -#include #include -#include -#include #include -#include - namespace Catch { namespace Benchmark { namespace Detail { diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp b/src/external/catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp index 907773f2..6da24ce5 100644 --- a/src/external/catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp @@ -19,7 +19,6 @@ #include #include -#include #include #include @@ -28,48 +27,53 @@ namespace Catch { namespace Detail { template std::vector resolution(int k) { - std::vector> times; - times.reserve(static_cast(k + 1)); - std::generate_n(std::back_inserter(times), k + 1, now{}); + const size_t points = static_cast( k + 1 ); + // To avoid overhead from the branch inside vector::push_back, + // we allocate them all and then overwrite. + std::vector> times(points); + for ( auto& time : times ) { + time = Clock::now(); + } std::vector deltas; deltas.reserve(static_cast(k)); - std::transform(std::next(times.begin()), times.end(), times.begin(), - std::back_inserter(deltas), - [](TimePoint a, TimePoint b) { return static_cast((a - b).count()); }); + for ( size_t idx = 1; idx < points; ++idx ) { + deltas.push_back( static_cast( + ( times[idx] - times[idx - 1] ).count() ) ); + } return deltas; } - const auto warmup_iterations = 10000; - const auto warmup_time = std::chrono::milliseconds(100); - const auto minimum_ticks = 1000; - const auto warmup_seed = 10000; - const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); - const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); - const auto clock_cost_estimation_tick_limit = 100000; - const auto clock_cost_estimation_time = std::chrono::milliseconds(10); - const auto clock_cost_estimation_iterations = 10000; + constexpr auto warmup_iterations = 10000; + constexpr auto warmup_time = std::chrono::milliseconds(100); + constexpr auto minimum_ticks = 1000; + constexpr auto warmup_seed = 10000; + constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500); + constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1); + constexpr auto clock_cost_estimation_tick_limit = 100000; + constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10); + constexpr auto clock_cost_estimation_iterations = 10000; template int warmup() { - return run_for_at_least(std::chrono::duration_cast>(warmup_time), warmup_seed, &resolution) + return run_for_at_least(warmup_time, warmup_seed, &resolution) .iterations; } template - EnvironmentEstimate> estimate_clock_resolution(int iterations) { - auto r = run_for_at_least(std::chrono::duration_cast>(clock_resolution_estimation_time), iterations, &resolution) + EnvironmentEstimate estimate_clock_resolution(int iterations) { + auto r = run_for_at_least(clock_resolution_estimation_time, iterations, &resolution) .result; return { - FloatDuration(mean(r.begin(), r.end())), - classify_outliers(r.begin(), r.end()), + FDuration(mean(r.data(), r.data() + r.size())), + classify_outliers(r.data(), r.data() + r.size()), }; } template - EnvironmentEstimate> estimate_clock_cost(FloatDuration resolution) { + EnvironmentEstimate estimate_clock_cost(FDuration resolution) { auto time_limit = (std::min)( resolution * clock_cost_estimation_tick_limit, - FloatDuration(clock_cost_estimation_time_limit)); + FDuration(clock_cost_estimation_time_limit)); auto time_clock = [](int k) { return Detail::measure([k] { for (int i = 0; i < k; ++i) { @@ -80,26 +84,28 @@ namespace Catch { }; time_clock(1); int iters = clock_cost_estimation_iterations; - auto&& r = run_for_at_least(std::chrono::duration_cast>(clock_cost_estimation_time), iters, time_clock); + auto&& r = run_for_at_least(clock_cost_estimation_time, iters, time_clock); std::vector times; int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); times.reserve(static_cast(nsamples)); - std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { - return static_cast((time_clock(r.iterations) / r.iterations).count()); - }); + for ( int s = 0; s < nsamples; ++s ) { + times.push_back( static_cast( + ( time_clock( r.iterations ) / r.iterations ) + .count() ) ); + } return { - FloatDuration(mean(times.begin(), times.end())), - classify_outliers(times.begin(), times.end()), + FDuration(mean(times.data(), times.data() + times.size())), + classify_outliers(times.data(), times.data() + times.size()), }; } template - Environment> measure_environment() { + Environment measure_environment() { #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wexit-time-destructors" #endif - static Catch::Detail::unique_ptr>> env; + static Catch::Detail::unique_ptr env; #if defined(__clang__) # pragma clang diagnostic pop #endif @@ -111,7 +117,7 @@ namespace Catch { auto resolution = Detail::estimate_clock_resolution(iters); auto cost = Detail::estimate_clock_cost(resolution.mean); - env = Catch::Detail::make_unique>>( Environment>{resolution, cost} ); + env = Catch::Detail::make_unique( Environment{resolution, cost} ); return *env; } } // namespace Detail diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_measure.hpp b/src/external/catch2/src/catch2/benchmark/detail/catch_measure.hpp index 388814c1..a8049072 100644 --- a/src/external/catch2/src/catch2/benchmark/detail/catch_measure.hpp +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_measure.hpp @@ -10,7 +10,6 @@ #ifndef CATCH_MEASURE_HPP_INCLUDED #define CATCH_MEASURE_HPP_INCLUDED -#include #include #include #include @@ -19,9 +18,9 @@ namespace Catch { namespace Benchmark { namespace Detail { template - TimingOf measure(Fun&& fun, Args&&... args) { + TimingOf measure(Fun&& fun, Args&&... args) { auto start = Clock::now(); - auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...); + auto&& r = Detail::complete_invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); auto end = Clock::now(); auto delta = end - start; return { delta, CATCH_FORWARD(r), 1 }; diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.cpp b/src/external/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.cpp index 35778b27..3ebdcc05 100644 --- a/src/external/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.cpp +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.cpp @@ -7,9 +7,10 @@ // SPDX-License-Identifier: BSL-1.0 #include -#include #include +#include + namespace Catch { namespace Benchmark { namespace Detail { diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp b/src/external/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp index 976a4b24..4dfa8bbb 100644 --- a/src/external/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp @@ -24,11 +24,11 @@ namespace Catch { namespace Benchmark { namespace Detail { template - TimingOf measure_one(Fun&& fun, int iters, std::false_type) { + TimingOf measure_one(Fun&& fun, int iters, std::false_type) { return Detail::measure(fun, iters); } template - TimingOf measure_one(Fun&& fun, int iters, std::true_type) { + TimingOf measure_one(Fun&& fun, int iters, std::true_type) { Detail::ChronometerModel meter; auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); @@ -43,8 +43,8 @@ namespace Catch { void throw_optimized_away_error(); template - TimingOf> - run_for_at_least(ClockDuration how_long, + TimingOf> + run_for_at_least(IDuration how_long, const int initial_iterations, Fun&& fun) { auto iters = initial_iterations; diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_stats.cpp b/src/external/catch2/src/catch2/benchmark/detail/catch_stats.cpp index 514ed1f7..e6de359c 100644 --- a/src/external/catch2/src/catch2/benchmark/detail/catch_stats.cpp +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_stats.cpp @@ -10,10 +10,15 @@ #include #include +#include +#include +#include +#include #include +#include #include -#include +#include #include @@ -21,139 +26,199 @@ #include #endif -namespace { - -using Catch::Benchmark::Detail::sample; - - template - sample resample(URng& rng, unsigned int resamples, std::vector::iterator first, std::vector::iterator last, Estimator& estimator) { - auto n = static_cast(last - first); - std::uniform_int_distribution dist(0, n - 1); - - sample out; - out.reserve(resamples); - std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] { - std::vector resampled; - resampled.reserve(n); - std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[static_cast(dist(rng))]; }); - return estimator(resampled.begin(), resampled.end()); - }); - std::sort(out.begin(), out.end()); - return out; - } - - - double erf_inv(double x) { - // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2 - double w, p; - - w = -log((1.0 - x) * (1.0 + x)); - - if (w < 6.250000) { - w = w - 3.125000; - p = -3.6444120640178196996e-21; - p = -1.685059138182016589e-19 + p * w; - p = 1.2858480715256400167e-18 + p * w; - p = 1.115787767802518096e-17 + p * w; - p = -1.333171662854620906e-16 + p * w; - p = 2.0972767875968561637e-17 + p * w; - p = 6.6376381343583238325e-15 + p * w; - p = -4.0545662729752068639e-14 + p * w; - p = -8.1519341976054721522e-14 + p * w; - p = 2.6335093153082322977e-12 + p * w; - p = -1.2975133253453532498e-11 + p * w; - p = -5.4154120542946279317e-11 + p * w; - p = 1.051212273321532285e-09 + p * w; - p = -4.1126339803469836976e-09 + p * w; - p = -2.9070369957882005086e-08 + p * w; - p = 4.2347877827932403518e-07 + p * w; - p = -1.3654692000834678645e-06 + p * w; - p = -1.3882523362786468719e-05 + p * w; - p = 0.0001867342080340571352 + p * w; - p = -0.00074070253416626697512 + p * w; - p = -0.0060336708714301490533 + p * w; - p = 0.24015818242558961693 + p * w; - p = 1.6536545626831027356 + p * w; - } else if (w < 16.000000) { - w = sqrt(w) - 3.250000; - p = 2.2137376921775787049e-09; - p = 9.0756561938885390979e-08 + p * w; - p = -2.7517406297064545428e-07 + p * w; - p = 1.8239629214389227755e-08 + p * w; - p = 1.5027403968909827627e-06 + p * w; - p = -4.013867526981545969e-06 + p * w; - p = 2.9234449089955446044e-06 + p * w; - p = 1.2475304481671778723e-05 + p * w; - p = -4.7318229009055733981e-05 + p * w; - p = 6.8284851459573175448e-05 + p * w; - p = 2.4031110387097893999e-05 + p * w; - p = -0.0003550375203628474796 + p * w; - p = 0.00095328937973738049703 + p * w; - p = -0.0016882755560235047313 + p * w; - p = 0.0024914420961078508066 + p * w; - p = -0.0037512085075692412107 + p * w; - p = 0.005370914553590063617 + p * w; - p = 1.0052589676941592334 + p * w; - p = 3.0838856104922207635 + p * w; - } else { - w = sqrt(w) - 5.000000; - p = -2.7109920616438573243e-11; - p = -2.5556418169965252055e-10 + p * w; - p = 1.5076572693500548083e-09 + p * w; - p = -3.7894654401267369937e-09 + p * w; - p = 7.6157012080783393804e-09 + p * w; - p = -1.4960026627149240478e-08 + p * w; - p = 2.9147953450901080826e-08 + p * w; - p = -6.7711997758452339498e-08 + p * w; - p = 2.2900482228026654717e-07 + p * w; - p = -9.9298272942317002539e-07 + p * w; - p = 4.5260625972231537039e-06 + p * w; - p = -1.9681778105531670567e-05 + p * w; - p = 7.5995277030017761139e-05 + p * w; - p = -0.00021503011930044477347 + p * w; - p = -0.00013871931833623122026 + p * w; - p = 1.0103004648645343977 + p * w; - p = 4.8499064014085844221 + p * w; - } - return p * x; - } - - double standard_deviation(std::vector::iterator first, std::vector::iterator last) { - auto m = Catch::Benchmark::Detail::mean(first, last); - double variance = std::accumulate( first, - last, - 0., - [m]( double a, double b ) { - double diff = b - m; - return a + diff * diff; - } ) / - ( last - first ); - return std::sqrt( variance ); - } - -} - namespace Catch { namespace Benchmark { namespace Detail { + namespace { + + template + static sample + resample( URng& rng, + unsigned int resamples, + double const* first, + double const* last, + Estimator& estimator ) { + auto n = static_cast( last - first ); + Catch::uniform_integer_distribution dist( 0, n - 1 ); + + sample out; + out.reserve( resamples ); + std::vector resampled; + resampled.reserve( n ); + for ( size_t i = 0; i < resamples; ++i ) { + resampled.clear(); + for ( size_t s = 0; s < n; ++s ) { + resampled.push_back( first[dist( rng )] ); + } + const auto estimate = + estimator( resampled.data(), resampled.data() + resampled.size() ); + out.push_back( estimate ); + } + std::sort( out.begin(), out.end() ); + return out; + } -#if defined( __GNUC__ ) || defined( __clang__ ) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - bool directCompare( double lhs, double rhs ) { return lhs == rhs; } -#if defined( __GNUC__ ) || defined( __clang__ ) -# pragma GCC diagnostic pop -#endif + static double outlier_variance( Estimate mean, + Estimate stddev, + int n ) { + double sb = stddev.point; + double mn = mean.point / n; + double mg_min = mn / 2.; + double sg = (std::min)( mg_min / 4., sb / std::sqrt( n ) ); + double sg2 = sg * sg; + double sb2 = sb * sb; + + auto c_max = [n, mn, sb2, sg2]( double x ) -> double { + double k = mn - x; + double d = k * k; + double nd = n * d; + double k0 = -n * nd; + double k1 = sb2 - n * sg2 + nd; + double det = k1 * k1 - 4 * sg2 * k0; + return static_cast( -2. * k0 / + ( k1 + std::sqrt( det ) ) ); + }; + + auto var_out = [n, sb2, sg2]( double c ) { + double nc = n - c; + return ( nc / n ) * ( sb2 - nc * sg2 ); + }; + + return (std::min)( var_out( 1 ), + var_out( + (std::min)( c_max( 0. ), + c_max( mg_min ) ) ) ) / + sb2; + } + + static double erf_inv( double x ) { + // Code accompanying the article "Approximating the erfinv + // function" in GPU Computing Gems, Volume 2 + double w, p; + + w = -log( ( 1.0 - x ) * ( 1.0 + x ) ); + + if ( w < 6.250000 ) { + w = w - 3.125000; + p = -3.6444120640178196996e-21; + p = -1.685059138182016589e-19 + p * w; + p = 1.2858480715256400167e-18 + p * w; + p = 1.115787767802518096e-17 + p * w; + p = -1.333171662854620906e-16 + p * w; + p = 2.0972767875968561637e-17 + p * w; + p = 6.6376381343583238325e-15 + p * w; + p = -4.0545662729752068639e-14 + p * w; + p = -8.1519341976054721522e-14 + p * w; + p = 2.6335093153082322977e-12 + p * w; + p = -1.2975133253453532498e-11 + p * w; + p = -5.4154120542946279317e-11 + p * w; + p = 1.051212273321532285e-09 + p * w; + p = -4.1126339803469836976e-09 + p * w; + p = -2.9070369957882005086e-08 + p * w; + p = 4.2347877827932403518e-07 + p * w; + p = -1.3654692000834678645e-06 + p * w; + p = -1.3882523362786468719e-05 + p * w; + p = 0.0001867342080340571352 + p * w; + p = -0.00074070253416626697512 + p * w; + p = -0.0060336708714301490533 + p * w; + p = 0.24015818242558961693 + p * w; + p = 1.6536545626831027356 + p * w; + } else if ( w < 16.000000 ) { + w = sqrt( w ) - 3.250000; + p = 2.2137376921775787049e-09; + p = 9.0756561938885390979e-08 + p * w; + p = -2.7517406297064545428e-07 + p * w; + p = 1.8239629214389227755e-08 + p * w; + p = 1.5027403968909827627e-06 + p * w; + p = -4.013867526981545969e-06 + p * w; + p = 2.9234449089955446044e-06 + p * w; + p = 1.2475304481671778723e-05 + p * w; + p = -4.7318229009055733981e-05 + p * w; + p = 6.8284851459573175448e-05 + p * w; + p = 2.4031110387097893999e-05 + p * w; + p = -0.0003550375203628474796 + p * w; + p = 0.00095328937973738049703 + p * w; + p = -0.0016882755560235047313 + p * w; + p = 0.0024914420961078508066 + p * w; + p = -0.0037512085075692412107 + p * w; + p = 0.005370914553590063617 + p * w; + p = 1.0052589676941592334 + p * w; + p = 3.0838856104922207635 + p * w; + } else { + w = sqrt( w ) - 5.000000; + p = -2.7109920616438573243e-11; + p = -2.5556418169965252055e-10 + p * w; + p = 1.5076572693500548083e-09 + p * w; + p = -3.7894654401267369937e-09 + p * w; + p = 7.6157012080783393804e-09 + p * w; + p = -1.4960026627149240478e-08 + p * w; + p = 2.9147953450901080826e-08 + p * w; + p = -6.7711997758452339498e-08 + p * w; + p = 2.2900482228026654717e-07 + p * w; + p = -9.9298272942317002539e-07 + p * w; + p = 4.5260625972231537039e-06 + p * w; + p = -1.9681778105531670567e-05 + p * w; + p = 7.5995277030017761139e-05 + p * w; + p = -0.00021503011930044477347 + p * w; + p = -0.00013871931833623122026 + p * w; + p = 1.0103004648645343977 + p * w; + p = 4.8499064014085844221 + p * w; + } + return p * x; + } + + static double + standard_deviation( double const* first, double const* last ) { + auto m = Catch::Benchmark::Detail::mean( first, last ); + double variance = + std::accumulate( first, + last, + 0., + [m]( double a, double b ) { + double diff = b - m; + return a + diff * diff; + } ) / + ( last - first ); + return std::sqrt( variance ); + } + + static sample jackknife( double ( *estimator )( double const*, + double const* ), + double* first, + double* last ) { + const auto second = first + 1; + sample results; + results.reserve( static_cast( last - first ) ); + + for ( auto it = first; it != last; ++it ) { + std::iter_swap( it, first ); + results.push_back( estimator( second, last ) ); + } + + return results; + } + + + } // namespace + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +namespace Catch { + namespace Benchmark { + namespace Detail { - double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last) { + double weighted_average_quantile( int k, + int q, + double* first, + double* last ) { auto count = last - first; double idx = (count - 1) * k / static_cast(q); int j = static_cast(idx); double g = idx - j; std::nth_element(first, first + j, last); auto xj = first[j]; - if ( directCompare( g, 0 ) ) { + if ( Catch::Detail::directCompare( g, 0 ) ) { return xj; } @@ -161,6 +226,48 @@ namespace Catch { return xj + g * (xj1 - xj); } + OutlierClassification + classify_outliers( double const* first, double const* last ) { + std::vector copy( first, last ); + + auto q1 = weighted_average_quantile( 1, 4, copy.data(), copy.data() + copy.size() ); + auto q3 = weighted_average_quantile( 3, 4, copy.data(), copy.data() + copy.size() ); + auto iqr = q3 - q1; + auto los = q1 - ( iqr * 3. ); + auto lom = q1 - ( iqr * 1.5 ); + auto him = q3 + ( iqr * 1.5 ); + auto his = q3 + ( iqr * 3. ); + + OutlierClassification o; + for ( ; first != last; ++first ) { + const double t = *first; + if ( t < los ) { + ++o.low_severe; + } else if ( t < lom ) { + ++o.low_mild; + } else if ( t > his ) { + ++o.high_severe; + } else if ( t > him ) { + ++o.high_mild; + } + ++o.samples_seen; + } + return o; + } + + double mean( double const* first, double const* last ) { + auto count = last - first; + double sum = 0.; + while (first != last) { + sum += *first; + ++first; + } + return sum / static_cast(count); + } + + double normal_cdf( double x ) { + return std::erfc( -x / std::sqrt( 2.0 ) ) / 2.0; + } double erfc_inv(double x) { return erf_inv(1.0 - x); @@ -182,50 +289,77 @@ namespace Catch { return result; } + Estimate + bootstrap( double confidence_level, + double* first, + double* last, + sample const& resample, + double ( *estimator )( double const*, double const* ) ) { + auto n_samples = last - first; + + double point = estimator( first, last ); + // Degenerate case with a single sample + if ( n_samples == 1 ) + return { point, point, point, confidence_level }; + + sample jack = jackknife( estimator, first, last ); + double jack_mean = + mean( jack.data(), jack.data() + jack.size() ); + double sum_squares = 0, sum_cubes = 0; + for ( double x : jack ) { + auto difference = jack_mean - x; + auto square = difference * difference; + auto cube = square * difference; + sum_squares += square; + sum_cubes += cube; + } - double outlier_variance(Estimate mean, Estimate stddev, int n) { - double sb = stddev.point; - double mn = mean.point / n; - double mg_min = mn / 2.; - double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); - double sg2 = sg * sg; - double sb2 = sb * sb; - - auto c_max = [n, mn, sb2, sg2](double x) -> double { - double k = mn - x; - double d = k * k; - double nd = n * d; - double k0 = -n * nd; - double k1 = sb2 - n * sg2 + nd; - double det = k1 * k1 - 4 * sg2 * k0; - return static_cast(-2. * k0 / (k1 + std::sqrt(det))); - }; + double accel = sum_cubes / ( 6 * std::pow( sum_squares, 1.5 ) ); + long n = static_cast( resample.size() ); + double prob_n = + std::count_if( resample.begin(), + resample.end(), + [point]( double x ) { return x < point; } ) / + static_cast( n ); + // degenerate case with uniform samples + if ( Catch::Detail::directCompare( prob_n, 0. ) ) { + return { point, point, point, confidence_level }; + } - auto var_out = [n, sb2, sg2](double c) { - double nc = n - c; - return (nc / n) * (sb2 - nc * sg2); - }; + double bias = normal_quantile( prob_n ); + double z1 = normal_quantile( ( 1. - confidence_level ) / 2. ); - return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; + auto cumn = [n]( double x ) -> long { + return std::lround( normal_cdf( x ) * + static_cast( n ) ); + }; + auto a = [bias, accel]( double b ) { + return bias + b / ( 1. - accel * b ); + }; + double b1 = bias + z1; + double b2 = bias - z1; + double a1 = a( b1 ); + double a2 = a( b2 ); + auto lo = static_cast( (std::max)( cumn( a1 ), 0l ) ); + auto hi = + static_cast( (std::min)( cumn( a2 ), n - 1 ) ); + + return { point, resample[lo], resample[hi], confidence_level }; } - - bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector::iterator first, std::vector::iterator last) { - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS - static std::random_device entropy; - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - - auto n = static_cast(last - first); // seriously, one can't use integral types without hell in C++ - - auto mean = &Detail::mean::iterator>; + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + double* first, + double* last) { + auto mean = &Detail::mean; auto stddev = &standard_deviation; #if defined(CATCH_CONFIG_USE_ASYNC) - auto Estimate = [=](double(*f)(std::vector::iterator, std::vector::iterator)) { - auto seed = entropy(); + auto Estimate = [=](double(*f)(double const*, double const*)) { + std::random_device rd; + auto seed = rd(); return std::async(std::launch::async, [=] { - std::mt19937 rng(seed); + SimplePcg32 rng( seed ); auto resampled = resample(rng, n_resamples, first, last, f); return bootstrap(confidence_level, first, last, resampled, f); }); @@ -237,9 +371,10 @@ namespace Catch { auto mean_estimate = mean_future.get(); auto stddev_estimate = stddev_future.get(); #else - auto Estimate = [=](double(*f)(std::vector::iterator, std::vector::iterator)) { - auto seed = entropy(); - std::mt19937 rng(seed); + auto Estimate = [=](double(*f)(double const* , double const*)) { + std::random_device rd; + auto seed = rd(); + SimplePcg32 rng( seed ); auto resampled = resample(rng, n_resamples, first, last, f); return bootstrap(confidence_level, first, last, resampled, f); }; @@ -248,6 +383,7 @@ namespace Catch { auto stddev_estimate = Estimate(stddev); #endif // CATCH_USE_ASYNC + auto n = static_cast(last - first); // seriously, one can't use integral types without hell in C++ double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); return { mean_estimate, stddev_estimate, outlier_variance }; diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_stats.hpp b/src/external/catch2/src/catch2/benchmark/detail/catch_stats.hpp index 4c54ec52..3bea612f 100644 --- a/src/external/catch2/src/catch2/benchmark/detail/catch_stats.hpp +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_stats.hpp @@ -13,122 +13,35 @@ #include #include -#include #include -#include -#include -#include namespace Catch { namespace Benchmark { namespace Detail { using sample = std::vector; - // Used when we know we want == comparison of two doubles - // to centralize warning suppression - bool directCompare( double lhs, double rhs ); - - double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last); - - template - OutlierClassification classify_outliers(Iterator first, Iterator last) { - std::vector copy(first, last); - - auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end()); - auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end()); - auto iqr = q3 - q1; - auto los = q1 - (iqr * 3.); - auto lom = q1 - (iqr * 1.5); - auto him = q3 + (iqr * 1.5); - auto his = q3 + (iqr * 3.); - - OutlierClassification o; - for (; first != last; ++first) { - auto&& t = *first; - if (t < los) ++o.low_severe; - else if (t < lom) ++o.low_mild; - else if (t > his) ++o.high_severe; - else if (t > him) ++o.high_mild; - ++o.samples_seen; - } - return o; - } - - template - double mean(Iterator first, Iterator last) { - auto count = last - first; - double sum = std::accumulate(first, last, 0.); - return sum / static_cast(count); - } - - template - sample jackknife(Estimator&& estimator, Iterator first, Iterator last) { - auto n = static_cast(last - first); - auto second = first; - ++second; - sample results; - results.reserve(n); - - for (auto it = first; it != last; ++it) { - std::iter_swap(it, first); - results.push_back(estimator(second, last)); - } - - return results; - } - - inline double normal_cdf(double x) { - return std::erfc(-x / std::sqrt(2.0)) / 2.0; - } + double weighted_average_quantile( int k, + int q, + double* first, + double* last ); + + OutlierClassification + classify_outliers( double const* first, double const* last ); + + double mean( double const* first, double const* last ); + + double normal_cdf( double x ); double erfc_inv(double x); double normal_quantile(double p); - template - Estimate bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) { - auto n_samples = last - first; - - double point = estimator(first, last); - // Degenerate case with a single sample - if (n_samples == 1) return { point, point, point, confidence_level }; - - sample jack = jackknife(estimator, first, last); - double jack_mean = mean(jack.begin(), jack.end()); - double sum_squares, sum_cubes; - std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair sqcb, double x) -> std::pair { - auto d = jack_mean - x; - auto d2 = d * d; - auto d3 = d2 * d; - return { sqcb.first + d2, sqcb.second + d3 }; - }); - - double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); - long n = static_cast(resample.size()); - double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / static_cast(n); - // degenerate case with uniform samples - if ( directCompare( prob_n, 0. ) ) { - return { point, point, point, confidence_level }; - } - - double bias = normal_quantile(prob_n); - double z1 = normal_quantile((1. - confidence_level) / 2.); - - auto cumn = [n]( double x ) -> long { - return std::lround( normal_cdf( x ) * static_cast(n) ); - }; - auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); }; - double b1 = bias + z1; - double b2 = bias - z1; - double a1 = a(b1); - double a2 = a(b2); - auto lo = static_cast((std::max)(cumn(a1), 0l)); - auto hi = static_cast((std::min)(cumn(a2), n - 1)); - - return { point, resample[lo], resample[hi], confidence_level }; - } - - double outlier_variance(Estimate mean, Estimate stddev, int n); + Estimate + bootstrap( double confidence_level, + double* first, + double* last, + sample const& resample, + double ( *estimator )( double const*, double const* ) ); struct bootstrap_analysis { Estimate mean; @@ -136,7 +49,10 @@ namespace Catch { double outlier_variance; }; - bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector::iterator first, std::vector::iterator last); + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + double* first, + double* last); } // namespace Detail } // namespace Benchmark } // namespace Catch diff --git a/src/external/catch2/src/catch2/benchmark/detail/catch_timing.hpp b/src/external/catch2/src/catch2/benchmark/detail/catch_timing.hpp index f5c25571..da567190 100644 --- a/src/external/catch2/src/catch2/benchmark/detail/catch_timing.hpp +++ b/src/external/catch2/src/catch2/benchmark/detail/catch_timing.hpp @@ -17,14 +17,14 @@ namespace Catch { namespace Benchmark { - template + template struct Timing { - Duration elapsed; + IDuration elapsed; Result result; int iterations; }; - template - using TimingOf = Timing, Detail::CompleteType_t>>; + template + using TimingOf = Timing>>; } // namespace Benchmark } // namespace Catch diff --git a/src/external/catch2/src/catch2/catch_all.hpp b/src/external/catch2/src/catch2/catch_all.hpp index be146421..57159955 100644 --- a/src/external/catch2/src/catch2/catch_all.hpp +++ b/src/external/catch2/src/catch2/catch_all.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -47,13 +48,14 @@ #include #include #include -#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -72,6 +74,7 @@ #include #include #include +#include #include #include #include @@ -86,7 +89,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -111,10 +117,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include #include diff --git a/src/external/catch2/src/catch2/catch_approx.cpp b/src/external/catch2/src/catch2/catch_approx.cpp index 407586d1..08c6799b 100644 --- a/src/external/catch2/src/catch2/catch_approx.cpp +++ b/src/external/catch2/src/catch2/catch_approx.cpp @@ -25,7 +25,7 @@ bool marginComparison(double lhs, double rhs, double margin) { namespace Catch { Approx::Approx ( double value ) - : m_epsilon( std::numeric_limits::epsilon()*100. ), + : m_epsilon( static_cast(std::numeric_limits::epsilon())*100. ), m_margin( 0.0 ), m_scale( 0.0 ), m_value( value ) @@ -70,10 +70,10 @@ namespace Catch { } namespace literals { - Approx operator "" _a(long double val) { + Approx operator ""_a(long double val) { return Approx(val); } - Approx operator "" _a(unsigned long long val) { + Approx operator ""_a(unsigned long long val) { return Approx(val); } } // end namespace literals diff --git a/src/external/catch2/src/catch2/catch_assertion_result.cpp b/src/external/catch2/src/catch2/catch_assertion_result.cpp index 61d4fd06..dba86229 100644 --- a/src/external/catch2/src/catch2/catch_assertion_result.cpp +++ b/src/external/catch2/src/catch2/catch_assertion_result.cpp @@ -11,7 +11,7 @@ namespace Catch { - AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const& _lazyExpression): lazyExpression(_lazyExpression), resultType(_resultType) {} diff --git a/src/external/catch2/src/catch2/internal/catch_case_sensitive.hpp b/src/external/catch2/src/catch2/catch_case_sensitive.hpp similarity index 100% rename from src/external/catch2/src/catch2/internal/catch_case_sensitive.hpp rename to src/external/catch2/src/catch2/catch_case_sensitive.hpp diff --git a/src/external/catch2/src/catch2/catch_config.cpp b/src/external/catch2/src/catch2/catch_config.cpp index eb4f5ad3..352c1f42 100644 --- a/src/external/catch2/src/catch2/catch_config.cpp +++ b/src/external/catch2/src/catch2/catch_config.cpp @@ -105,16 +105,18 @@ namespace Catch { elem = trim(elem); } - // Insert the default reporter if user hasn't asked for a specfic one + // Insert the default reporter if user hasn't asked for a specific one if ( m_data.reporterSpecifications.empty() ) { - m_data.reporterSpecifications.push_back( { #if defined( CATCH_CONFIG_DEFAULT_REPORTER ) - CATCH_CONFIG_DEFAULT_REPORTER, + const auto default_spec = CATCH_CONFIG_DEFAULT_REPORTER; #else - "console", + const auto default_spec = "console"; #endif - {}, {}, {} - } ); + auto parsed = parseReporterSpec(default_spec); + CATCH_ENFORCE( parsed, + "Cannot parse the provided default reporter spec: '" + << default_spec << '\'' ); + m_data.reporterSpecifications.push_back( std::move( *parsed ) ); } if ( enableBazelEnvSupport() ) { diff --git a/src/external/catch2/src/catch2/catch_config.hpp b/src/external/catch2/src/catch2/catch_config.hpp index 784de4aa..17e983e5 100644 --- a/src/external/catch2/src/catch2/catch_config.hpp +++ b/src/external/catch2/src/catch2/catch_config.hpp @@ -69,7 +69,7 @@ namespace Catch { bool benchmarkNoAnalysis = false; unsigned int benchmarkSamples = 100; double benchmarkConfidenceInterval = 0.95; - unsigned int benchmarkResamples = 100000; + unsigned int benchmarkResamples = 100'000; std::chrono::milliseconds::rep benchmarkWarmupTime = 100; Verbosity verbosity = Verbosity::Normal; diff --git a/src/external/catch2/src/catch2/catch_message.cpp b/src/external/catch2/src/catch2/catch_message.cpp index d4723e94..7b09ab87 100644 --- a/src/external/catch2/src/catch2/catch_message.cpp +++ b/src/external/catch2/src/catch2/catch_message.cpp @@ -37,7 +37,11 @@ namespace Catch { } - Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { + Capturer::Capturer( StringRef macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType resultType, + StringRef names ): + m_resultCapture( getResultCapture() ) { auto trimmed = [&] (size_t start, size_t end) { while (names[start] == ',' || isspace(static_cast(names[start]))) { ++start; @@ -87,6 +91,8 @@ namespace Catch { m_messages.back().message += " := "; start = pos; } + break; + default:; // noop } } assert(openings.empty() && "Mismatched openings"); diff --git a/src/external/catch2/src/catch2/catch_message.hpp b/src/external/catch2/src/catch2/catch_message.hpp index e6bc1b5d..0a738341 100644 --- a/src/external/catch2/src/catch2/catch_message.hpp +++ b/src/external/catch2/src/catch2/catch_message.hpp @@ -8,12 +8,13 @@ #ifndef CATCH_MESSAGE_HPP_INCLUDED #define CATCH_MESSAGE_HPP_INCLUDED +#include #include #include #include #include -#include #include +#include #include #include @@ -21,6 +22,7 @@ namespace Catch { struct SourceLineInfo; + class IResultCapture; struct MessageStream { @@ -61,7 +63,7 @@ namespace Catch { class Capturer { std::vector m_messages; - IResultCapture& m_resultCapture = getResultCapture(); + IResultCapture& m_resultCapture; size_t m_captured = 0; public: Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); @@ -92,7 +94,7 @@ namespace Catch { do { \ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) /////////////////////////////////////////////////////////////////////////////// @@ -112,28 +114,28 @@ namespace Catch { Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) -#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) +#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) #define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg ) #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__ ) -#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) +#elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) #define CATCH_INFO( msg ) (void)(0) #define CATCH_UNSCOPED_INFO( msg ) (void)(0) #define CATCH_WARN( msg ) (void)(0) #define CATCH_CAPTURE( ... ) (void)(0) -#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) +#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) #define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__ ) -#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) +#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) #define INFO( msg ) (void)(0) #define UNSCOPED_INFO( msg ) (void)(0) diff --git a/src/external/catch2/src/catch2/catch_registry_hub.cpp b/src/external/catch2/src/catch2/catch_registry_hub.cpp index 243dd2b0..3a594678 100644 --- a/src/external/catch2/src/catch2/catch_registry_hub.cpp +++ b/src/external/catch2/src/catch2/catch_registry_hub.cpp @@ -21,6 +21,8 @@ #include #include +#include + namespace Catch { namespace { @@ -31,7 +33,7 @@ namespace Catch { public: // IRegistryHub RegistryHub() = default; - IReporterRegistry const& getReporterRegistry() const override { + ReporterRegistry const& getReporterRegistry() const override { return m_reporterRegistry; } ITestCaseRegistry const& getTestCaseRegistry() const override { diff --git a/src/external/catch2/src/catch2/catch_session.cpp b/src/external/catch2/src/catch2/catch_session.cpp index 43465f0c..a4a410c9 100644 --- a/src/external/catch2/src/catch2/catch_session.cpp +++ b/src/external/catch2/src/catch2/catch_session.cpp @@ -13,13 +13,13 @@ #include #include #include -#include #include #include +#include #include #include #include -#include +#include #include #include #include @@ -27,13 +27,20 @@ #include #include +#include #include #include namespace Catch { namespace { - const int MaxExitCode = 255; + static constexpr int TestFailureExitCode = 42; + static constexpr int UnspecifiedErrorExitCode = 1; + static constexpr int AllTestsSkippedExitCode = 4; + static constexpr int NoTestsRunExitCode = 2; + static constexpr int UnmatchedTestSpecExitCode = 3; + static constexpr int InvalidTestSpecExitCode = 5; + IEventListenerPtr createReporter(std::string const& reporterName, ReporterConfig&& config) { auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, CATCH_MOVE(config)); @@ -197,8 +204,7 @@ namespace Catch { } int Session::applyCommandLine( int argc, char const * const * argv ) { - if( m_startupExceptions ) - return 1; + if ( m_startupExceptions ) { return UnspecifiedErrorExitCode; } auto result = m_cli.parse( Clara::Args( argc, argv ) ); @@ -214,7 +220,7 @@ namespace Catch { << TextFlow::Column( result.errorMessage() ).indent( 2 ) << "\n\n"; errStream->stream() << "Run with -? for usage\n\n" << std::flush; - return MaxExitCode; + return UnspecifiedErrorExitCode; } if( m_configData.showHelp ) @@ -284,8 +290,7 @@ namespace Catch { } int Session::runInternal() { - if( m_startupExceptions ) - return 1; + if ( m_startupExceptions ) { return UnspecifiedErrorExitCode; } if (m_configData.showHelp || m_configData.libIdentify) { return 0; @@ -296,7 +301,7 @@ namespace Catch { << ") must be greater than the shard index (" << m_configData.shardIndex << ")\n" << std::flush; - return 1; + return UnspecifiedErrorExitCode; } CATCH_TRY { @@ -319,7 +324,7 @@ namespace Catch { for ( auto const& spec : invalidSpecs ) { reporter->reportInvalidTestSpec( spec ); } - return 1; + return InvalidTestSpecExitCode; } @@ -333,29 +338,29 @@ namespace Catch { if ( tests.hadUnmatchedTestSpecs() && m_config->warnAboutUnmatchedTestSpecs() ) { - return 3; + // UnmatchedTestSpecExitCode + return UnmatchedTestSpecExitCode; } if ( totals.testCases.total() == 0 && !m_config->zeroTestsCountAsSuccess() ) { - return 2; + return NoTestsRunExitCode; } if ( totals.testCases.total() > 0 && totals.testCases.total() == totals.testCases.skipped && !m_config->zeroTestsCountAsSuccess() ) { - return 4; + return AllTestsSkippedExitCode; } - // Note that on unices only the lower 8 bits are usually used, clamping - // the return value to 255 prevents false negative when some multiple - // of 256 tests has failed - return (std::min) (MaxExitCode, static_cast(totals.assertions.failed)); + if ( totals.assertions.failed ) { return TestFailureExitCode; } + return 0; + } #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) catch( std::exception& ex ) { Catch::cerr() << ex.what() << '\n' << std::flush; - return MaxExitCode; + return UnspecifiedErrorExitCode; } #endif } diff --git a/src/external/catch2/src/catch2/catch_test_case_info.cpp b/src/external/catch2/src/catch2/catch_test_case_info.cpp index a6adce0a..9d64e532 100644 --- a/src/external/catch2/src/catch2/catch_test_case_info.cpp +++ b/src/external/catch2/src/catch2/catch_test_case_info.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -21,26 +22,26 @@ namespace Catch { static_assert(sizeof(TestCaseProperties) == sizeof(TCP_underlying_type), "The size of the TestCaseProperties is different from the assumed size"); - TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) { + constexpr TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) { return static_cast( static_cast(lhs) | static_cast(rhs) ); } - TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) { + constexpr TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) { lhs = static_cast( static_cast(lhs) | static_cast(rhs) ); return lhs; } - TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) { + constexpr TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) { return static_cast( static_cast(lhs) & static_cast(rhs) ); } - bool applies(TestCaseProperties tcp) { + constexpr bool applies(TestCaseProperties tcp) { static_assert(static_cast(TestCaseProperties::None) == 0, "TestCaseProperties::None must be equal to 0"); return tcp != TestCaseProperties::None; @@ -79,13 +80,15 @@ namespace Catch { return "Anonymous test case " + std::to_string(++counter); } - StringRef extractFilenamePart(StringRef filename) { + constexpr StringRef extractFilenamePart(StringRef filename) { size_t lastDot = filename.size(); while (lastDot > 0 && filename[lastDot - 1] != '.') { --lastDot; } - --lastDot; + // In theory we could have filename without any extension in it + if ( lastDot == 0 ) { return StringRef(); } + --lastDot; size_t nameStart = lastDot; while (nameStart > 0 && filename[nameStart - 1] != '/' && filename[nameStart - 1] != '\\') { --nameStart; @@ -95,7 +98,7 @@ namespace Catch { } // Returns the upper bound on size of extra tags ([#file]+[.]) - size_t sizeOfExtraTags(StringRef filepath) { + constexpr size_t sizeOfExtraTags(StringRef filepath) { // [.] is 3, [#] is another 3 const size_t extras = 3 + 3; return extractFilenamePart(filepath).size() + extras; @@ -139,12 +142,20 @@ namespace Catch { for (size_t idx = 0; idx < originalTags.size(); ++idx) { auto c = originalTags[idx]; if (c == '[') { - assert(!inTag); + CATCH_ENFORCE( + !inTag, + "Found '[' inside a tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + inTag = true; tagStart = idx; } if (c == ']') { - assert(inTag); + CATCH_ENFORCE( + inTag, + "Found unmatched ']' while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + inTag = false; tagEnd = idx; assert(tagStart < tagEnd); @@ -153,7 +164,11 @@ namespace Catch { // it over to backing storage and actually reference the // backing storage in the saved tags StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1); - CATCH_ENFORCE(!tagStr.empty(), "Empty tags are not allowed"); + CATCH_ENFORCE( !tagStr.empty(), + "Found an empty tag while registering test case '" + << _nameAndTags.name << "' at " + << _lineInfo ); + enforceNotReservedTag(tagStr, lineInfo); properties |= parseSpecialTag(tagStr); // When copying a tag to the backing storage, we need to @@ -167,8 +182,12 @@ namespace Catch { // the tags. internalAppendTag(tagStr); } - (void)inTag; // Silence "set-but-unused" warning in release mode. } + CATCH_ENFORCE( !inTag, + "Found an unclosed tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + + // Add [.] if relevant if (isHidden()) { internalAppendTag("."_sr); @@ -240,8 +259,4 @@ namespace Catch { return lhs.tags < rhs.tags; } - TestCaseInfo const& TestCaseHandle::getTestCaseInfo() const { - return *m_info; - } - } // end namespace Catch diff --git a/src/external/catch2/src/catch2/catch_test_case_info.hpp b/src/external/catch2/src/catch2/catch_test_case_info.hpp index 5ff3e3e7..3466660c 100644 --- a/src/external/catch2/src/catch2/catch_test_case_info.hpp +++ b/src/external/catch2/src/catch2/catch_test_case_info.hpp @@ -8,10 +8,10 @@ #ifndef CATCH_TEST_CASE_INFO_HPP_INCLUDED #define CATCH_TEST_CASE_INFO_HPP_INCLUDED +#include #include #include #include -#include #include @@ -44,6 +44,7 @@ namespace Catch { }; class ITestInvoker; + struct NameAndTags; enum class TestCaseProperties : uint8_t { None = 0, @@ -67,7 +68,7 @@ namespace Catch { struct TestCaseInfo : Detail::NonCopyable { TestCaseInfo(StringRef _className, - NameAndTags const& _tags, + NameAndTags const& _nameAndTags, SourceLineInfo const& _lineInfo); bool isHidden() const; @@ -108,14 +109,24 @@ namespace Catch { TestCaseInfo* m_info; ITestInvoker* m_invoker; public: - TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) : + constexpr TestCaseHandle(TestCaseInfo* info, ITestInvoker* invoker) : m_info(info), m_invoker(invoker) {} + void prepareTestCase() const { + m_invoker->prepareTestCase(); + } + + void tearDownTestCase() const { + m_invoker->tearDownTestCase(); + } + void invoke() const { m_invoker->invoke(); } - TestCaseInfo const& getTestCaseInfo() const; + constexpr TestCaseInfo const& getTestCaseInfo() const { + return *m_info; + } }; Detail::unique_ptr diff --git a/src/external/catch2/src/catch2/catch_test_macros.hpp b/src/external/catch2/src/catch2/catch_test_macros.hpp index 1088afbe..6ee2129f 100644 --- a/src/external/catch2/src/catch2/catch_test_macros.hpp +++ b/src/external/catch2/src/catch2/catch_test_macros.hpp @@ -43,6 +43,7 @@ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) @@ -97,6 +98,7 @@ #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) + #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) #define CATCH_SECTION( ... ) #define CATCH_DYNAMIC_SECTION( ... ) @@ -142,6 +144,7 @@ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) @@ -195,6 +198,7 @@ #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define METHOD_AS_TEST_CASE( method, ... ) + #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) #define REGISTER_TEST_CASE( Function, ... ) (void)(0) #define SECTION( ... ) #define DYNAMIC_SECTION( ... ) diff --git a/src/external/catch2/src/catch2/catch_test_spec.cpp b/src/external/catch2/src/catch2/catch_test_spec.cpp index f27ce99c..f32f9864 100644 --- a/src/external/catch2/src/catch2/catch_test_spec.cpp +++ b/src/external/catch2/src/catch2/catch_test_spec.cpp @@ -6,6 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 #include +#include +#include #include #include #include @@ -106,16 +108,18 @@ namespace Catch { return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } ); } - TestSpec::Matches TestSpec::matchesByFilter( std::vector const& testCases, IConfig const& config ) const - { - Matches matches( m_filters.size() ); - std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){ + TestSpec::Matches TestSpec::matchesByFilter( std::vector const& testCases, IConfig const& config ) const { + Matches matches; + matches.reserve( m_filters.size() ); + for ( auto const& filter : m_filters ) { std::vector currentMatches; - for( auto const& test : testCases ) - if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) ) + for ( auto const& test : testCases ) + if ( isThrowSafe( test, config ) && + filter.matches( test.getTestCaseInfo() ) ) currentMatches.emplace_back( &test ); - return FilterMatch{ extractFilterName(filter), currentMatches }; - } ); + matches.push_back( + FilterMatch{ extractFilterName( filter ), currentMatches } ); + } return matches; } diff --git a/src/external/catch2/src/catch2/catch_timer.cpp b/src/external/catch2/src/catch2/catch_timer.cpp index d75ea70c..51396654 100644 --- a/src/external/catch2/src/catch2/catch_timer.cpp +++ b/src/external/catch2/src/catch2/catch_timer.cpp @@ -13,7 +13,7 @@ namespace Catch { namespace { static auto getCurrentNanosecondsSinceEpoch() -> uint64_t { - return std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); } } // end unnamed namespace diff --git a/src/external/catch2/src/catch2/catch_tostring.cpp b/src/external/catch2/src/catch2/catch_tostring.cpp index b97cf560..3f034d1a 100644 --- a/src/external/catch2/src/catch2/catch_tostring.cpp +++ b/src/external/catch2/src/catch2/catch_tostring.cpp @@ -54,13 +54,13 @@ namespace Detail { } } // end unnamed namespace - std::string convertIntoString(StringRef string, bool escape_invisibles) { + std::string convertIntoString(StringRef string, bool escapeInvisibles) { std::string ret; // This is enough for the "don't escape invisibles" case, and a good // lower bound on the "escape invisibles" case. ret.reserve(string.size() + 2); - if (!escape_invisibles) { + if (!escapeInvisibles) { ret += '"'; ret += string; ret += '"'; @@ -138,7 +138,7 @@ std::string StringMaker::convert(char const* str) { return{ "{null string}" }; } } -std::string StringMaker::convert(char* str) { +std::string StringMaker::convert(char* str) { // NOLINT(readability-non-const-parameter) if (str) { return Detail::convertIntoString( str ); } else { @@ -235,17 +235,17 @@ std::string StringMaker::convert(signed char value) { std::string StringMaker::convert(char c) { return ::Catch::Detail::stringify(static_cast(c)); } -std::string StringMaker::convert(unsigned char c) { - return ::Catch::Detail::stringify(static_cast(c)); +std::string StringMaker::convert(unsigned char value) { + return ::Catch::Detail::stringify(static_cast(value)); } -int StringMaker::precision = 5; +int StringMaker::precision = std::numeric_limits::max_digits10; std::string StringMaker::convert(float value) { return Detail::fpToString(value, precision) + 'f'; } -int StringMaker::precision = 10; +int StringMaker::precision = std::numeric_limits::max_digits10; std::string StringMaker::convert(double value) { return Detail::fpToString(value, precision); diff --git a/src/external/catch2/src/catch2/catch_tostring.hpp b/src/external/catch2/src/catch2/catch_tostring.hpp index 904caa7e..67a7c1d2 100644 --- a/src/external/catch2/src/catch2/catch_tostring.hpp +++ b/src/external/catch2/src/catch2/catch_tostring.hpp @@ -116,7 +116,6 @@ namespace Catch { } // namespace Detail - // If we decide for C++14, change these to enable_if_ts template struct StringMaker { template @@ -280,11 +279,11 @@ namespace Catch { }; template<> struct StringMaker { - static std::string convert(signed char c); + static std::string convert(signed char value); }; template<> struct StringMaker { - static std::string convert(unsigned char c); + static std::string convert(unsigned char value); }; template<> @@ -399,6 +398,12 @@ namespace Catch { } } }; + template <> + struct StringMaker { + static std::string convert(const std::nullopt_t&) { + return "{ }"; + } + }; } #endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER diff --git a/src/external/catch2/src/catch2/catch_translate_exception.cpp b/src/external/catch2/src/catch2/catch_translate_exception.cpp new file mode 100644 index 00000000..c4b28944 --- /dev/null +++ b/src/external/catch2/src/catch2/catch_translate_exception.cpp @@ -0,0 +1,20 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#include +#include + +namespace Catch { + namespace Detail { + void registerTranslatorImpl( + Detail::unique_ptr&& translator ) { + getMutableRegistryHub().registerTranslator( + CATCH_MOVE( translator ) ); + } + } // namespace Detail +} // namespace Catch diff --git a/src/external/catch2/src/catch2/catch_translate_exception.hpp b/src/external/catch2/src/catch2/catch_translate_exception.hpp index 2dbeb17e..2bf8d360 100644 --- a/src/external/catch2/src/catch2/catch_translate_exception.hpp +++ b/src/external/catch2/src/catch2/catch_translate_exception.hpp @@ -15,13 +15,17 @@ #include namespace Catch { + namespace Detail { + void registerTranslatorImpl( + Detail::unique_ptr&& translator ); + } class ExceptionTranslatorRegistrar { template class ExceptionTranslator : public IExceptionTranslator { public: - ExceptionTranslator( std::string(*translateFunction)( T const& ) ) + constexpr ExceptionTranslator( std::string(*translateFunction)( T const& ) ) : m_translateFunction( translateFunction ) {} @@ -48,9 +52,9 @@ namespace Catch { public: template ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) { - getMutableRegistryHub().registerTranslator( - Detail::make_unique>(translateFunction) - ); + Detail::registerTranslatorImpl( + Detail::make_unique>( + translateFunction ) ); } }; diff --git a/src/external/catch2/src/catch2/catch_user_config.hpp.in b/src/external/catch2/src/catch2/catch_user_config.hpp.in index 3f6b10e8..10d61937 100644 --- a/src/external/catch2/src/catch2/catch_user_config.hpp.in +++ b/src/external/catch2/src/catch2/catch_user_config.hpp.in @@ -169,9 +169,18 @@ #endif +#cmakedefine CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT +#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT + +#if defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \ + defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) +# error Cannot force STATIC_ANALYSIS_SUPPORT to both ON and OFF +#endif + + // ------ // Simple toggle defines -// their value is never used and they cannot be overriden +// their value is never used and they cannot be overridden // ------ @@ -189,6 +198,7 @@ #cmakedefine CATCH_CONFIG_FAST_COMPILE #cmakedefine CATCH_CONFIG_NOSTDOUT #cmakedefine CATCH_CONFIG_PREFIX_ALL +#cmakedefine CATCH_CONFIG_PREFIX_MESSAGES #cmakedefine CATCH_CONFIG_WINDOWS_CRTDBG #cmakedefine CATCH_CONFIG_SHARED_LIBRARY diff --git a/src/external/catch2/src/catch2/catch_version.cpp b/src/external/catch2/src/catch2/catch_version.cpp index 19cab91b..1701f3ad 100644 --- a/src/external/catch2/src/catch2/catch_version.cpp +++ b/src/external/catch2/src/catch2/catch_version.cpp @@ -36,7 +36,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 3, 2, "", 0 ); + static Version version( 3, 7, 1, "", 0 ); return version; } diff --git a/src/external/catch2/src/catch2/catch_version_macros.hpp b/src/external/catch2/src/catch2/catch_version_macros.hpp index 9ece8505..88b5d0d0 100644 --- a/src/external/catch2/src/catch2/catch_version_macros.hpp +++ b/src/external/catch2/src/catch2/catch_version_macros.hpp @@ -9,7 +9,7 @@ #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 3 -#define CATCH_VERSION_PATCH 2 +#define CATCH_VERSION_MINOR 7 +#define CATCH_VERSION_PATCH 1 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/generators/catch_generators.hpp b/src/external/catch2/src/catch2/generators/catch_generators.hpp index 117f1901..0f35a996 100644 --- a/src/external/catch2/src/catch2/generators/catch_generators.hpp +++ b/src/external/catch2/src/catch2/generators/catch_generators.hpp @@ -37,12 +37,6 @@ namespace Detail { } public: - ~IGenerator() override = default; - IGenerator() = default; - IGenerator(IGenerator const&) = default; - IGenerator& operator=(IGenerator const&) = default; - - // Returns the current element of the generator // // \Precondition The generator is either freshly constructed, diff --git a/src/external/catch2/src/catch2/generators/catch_generators_random.cpp b/src/external/catch2/src/catch2/generators/catch_generators_random.cpp index 2e3390fd..00a8e634 100644 --- a/src/external/catch2/src/catch2/generators/catch_generators_random.cpp +++ b/src/external/catch2/src/catch2/generators/catch_generators_random.cpp @@ -7,7 +7,35 @@ // SPDX-License-Identifier: BSL-1.0 #include - #include -std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); } +#include + +namespace Catch { + namespace Generators { + namespace Detail { + std::uint32_t getSeed() { return sharedRng()(); } + } // namespace Detail + + struct RandomFloatingGenerator::PImpl { + PImpl( long double a, long double b, uint32_t seed ): + rng( seed ), dist( a, b ) {} + + Catch::SimplePcg32 rng; + std::uniform_real_distribution dist; + }; + + RandomFloatingGenerator::RandomFloatingGenerator( + long double a, long double b, std::uint32_t seed) : + m_pimpl(Catch::Detail::make_unique(a, b, seed)) { + static_cast( next() ); + } + + RandomFloatingGenerator::~RandomFloatingGenerator() = + default; + bool RandomFloatingGenerator::next() { + m_current_number = m_pimpl->dist( m_pimpl->rng ); + return true; + } + } // namespace Generators +} // namespace Catch diff --git a/src/external/catch2/src/catch2/generators/catch_generators_random.hpp b/src/external/catch2/src/catch2/generators/catch_generators_random.hpp index bcd4888d..71283561 100644 --- a/src/external/catch2/src/catch2/generators/catch_generators_random.hpp +++ b/src/external/catch2/src/catch2/generators/catch_generators_random.hpp @@ -8,11 +8,11 @@ #ifndef CATCH_GENERATORS_RANDOM_HPP_INCLUDED #define CATCH_GENERATORS_RANDOM_HPP_INCLUDED -#include #include #include - -#include +#include +#include +#include namespace Catch { namespace Generators { @@ -26,7 +26,7 @@ namespace Detail { template class RandomFloatingGenerator final : public IGenerator { Catch::SimplePcg32 m_rng; - std::uniform_real_distribution m_dist; + Catch::uniform_floating_point_distribution m_dist; Float m_current_number; public: RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ): @@ -44,10 +44,27 @@ class RandomFloatingGenerator final : public IGenerator { } }; +template <> +class RandomFloatingGenerator final : public IGenerator { + // We still rely on for this specialization, but we don't + // want to drag it into the header. + struct PImpl; + Catch::Detail::unique_ptr m_pimpl; + long double m_current_number; + +public: + RandomFloatingGenerator( long double a, long double b, std::uint32_t seed ); + + long double const& get() const override { return m_current_number; } + bool next() override; + + ~RandomFloatingGenerator() override; // = default +}; + template class RandomIntegerGenerator final : public IGenerator { Catch::SimplePcg32 m_rng; - std::uniform_int_distribution m_dist; + Catch::uniform_integer_distribution m_dist; Integer m_current_number; public: RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ): @@ -68,14 +85,6 @@ class RandomIntegerGenerator final : public IGenerator { template std::enable_if_t::value, GeneratorWrapper> random(T a, T b) { - static_assert( - !std::is_same::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value && - !std::is_same::value, - "The requested type is not supported by the underlying random distributions from std" ); return GeneratorWrapper( Catch::Detail::make_unique>(a, b, Detail::getSeed()) ); diff --git a/src/external/catch2/src/catch2/generators/catch_generators_range.hpp b/src/external/catch2/src/catch2/generators/catch_generators_range.hpp index 495acb95..b67c1590 100644 --- a/src/external/catch2/src/catch2/generators/catch_generators_range.hpp +++ b/src/external/catch2/src/catch2/generators/catch_generators_range.hpp @@ -96,10 +96,11 @@ GeneratorWrapper from_range(InputIterator from, InputSentinel to) { return GeneratorWrapper(Catch::Detail::make_unique>(from, to)); } -template -GeneratorWrapper from_range(Container const& cnt) { - return GeneratorWrapper(Catch::Detail::make_unique>(cnt.begin(), cnt.end())); +template +auto from_range(Container const& cnt) { + using std::begin; + using std::end; + return from_range( begin( cnt ), end( cnt ) ); } diff --git a/src/external/catch2/src/catch2/interfaces/catch_interfaces_all.hpp b/src/external/catch2/src/catch2/interfaces/catch_interfaces_all.hpp index 87b746d8..a99fdcdc 100644 --- a/src/external/catch2/src/catch2/interfaces/catch_interfaces_all.hpp +++ b/src/external/catch2/src/catch2/interfaces/catch_interfaces_all.hpp @@ -30,8 +30,8 @@ #include #include #include -#include #include +#include #include #endif // CATCH_INTERFACES_ALL_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/interfaces/catch_interfaces_capture.hpp b/src/external/catch2/src/catch2/interfaces/catch_interfaces_capture.hpp index 2a469c12..9e5431d2 100644 --- a/src/external/catch2/src/catch2/interfaces/catch_interfaces_capture.hpp +++ b/src/external/catch2/src/catch2/interfaces/catch_interfaces_capture.hpp @@ -9,11 +9,11 @@ #define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED #include -#include #include #include #include +#include namespace Catch { @@ -31,8 +31,6 @@ namespace Catch { class IGeneratorTracker; struct BenchmarkInfo; - template > - struct BenchmarkStats; namespace Generators { class GeneratorUntypedBase; @@ -44,6 +42,7 @@ namespace Catch { public: virtual ~IResultCapture(); + virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0; virtual bool sectionStarted( StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts& assertions ) = 0; @@ -77,14 +76,14 @@ namespace Catch { virtual void handleMessage ( AssertionInfo const& info, ResultWas::OfType resultType, - StringRef message, + std::string&& message, AssertionReaction& reaction ) = 0; virtual void handleUnexpectedExceptionNotThrown ( AssertionInfo const& info, AssertionReaction& reaction ) = 0; virtual void handleUnexpectedInflightException ( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) = 0; virtual void handleIncomplete ( AssertionInfo const& info ) = 0; diff --git a/src/external/catch2/src/catch2/interfaces/catch_interfaces_exception.hpp b/src/external/catch2/src/catch2/interfaces/catch_interfaces_exception.hpp index 9177666a..fcc2a8f9 100644 --- a/src/external/catch2/src/catch2/interfaces/catch_interfaces_exception.hpp +++ b/src/external/catch2/src/catch2/interfaces/catch_interfaces_exception.hpp @@ -8,7 +8,6 @@ #ifndef CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED #define CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED -#include #include #include diff --git a/src/external/catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp b/src/external/catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp index 8813b538..113f223e 100644 --- a/src/external/catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp +++ b/src/external/catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp @@ -19,7 +19,7 @@ namespace Catch { class ITestCaseRegistry; class IExceptionTranslatorRegistry; class IExceptionTranslator; - class IReporterRegistry; + class ReporterRegistry; class IReporterFactory; class ITagAliasRegistry; class ITestInvoker; @@ -35,7 +35,7 @@ namespace Catch { public: virtual ~IRegistryHub(); // = default - virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; diff --git a/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp b/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp index 67c5c80e..90536bb3 100644 --- a/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp +++ b/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp @@ -7,19 +7,11 @@ // SPDX-License-Identifier: BSL-1.0 #include #include -#include -#include #include -#include -#include -#include -#include #include #include -#include #include -#include namespace Catch { @@ -54,8 +46,6 @@ namespace Catch { infoMessages( _infoMessages ), totals( _totals ) { - assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; - if( assertionResult.hasMessage() ) { // Copy message into messages list. // !TBD This should have been done earlier, somewhere diff --git a/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp b/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp index cf414f10..a052c5db 100644 --- a/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp +++ b/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp @@ -13,11 +13,9 @@ #include #include #include +#include #include -#include -#include -#include - +#include #include #include @@ -57,11 +55,6 @@ namespace Catch { std::map m_customOptions; }; - struct TestRunInfo { - constexpr TestRunInfo(StringRef _name) : name(_name) {} - StringRef name; - }; - struct AssertionStats { AssertionStats( AssertionResult const& _assertionResult, std::vector const& _infoMessages, @@ -113,45 +106,6 @@ namespace Catch { bool aborting; }; - - struct BenchmarkInfo { - std::string name; - double estimatedDuration; - int iterations; - unsigned int samples; - unsigned int resamples; - double clockResolution; - double clockCost; - }; - - template - struct BenchmarkStats { - BenchmarkInfo info; - - std::vector samples; - Benchmark::Estimate mean; - Benchmark::Estimate standardDeviation; - Benchmark::OutlierClassification outliers; - double outlierVariance; - - template - operator BenchmarkStats() const { - std::vector samples2; - samples2.reserve(samples.size()); - for (auto const& sample : samples) { - samples2.push_back(Duration2(sample)); - } - return { - info, - CATCH_MOVE(samples2), - mean, - standardDeviation, - outliers, - outlierVariance, - }; - } - }; - //! By setting up its preferences, a reporter can modify Catch2's behaviour //! in some regards, e.g. it can request Catch2 to capture writes to //! stdout/stderr during test execution, and pass them to the reporter. @@ -250,7 +204,7 @@ namespace Catch { */ virtual void skipTest( TestCaseInfo const& testInfo ) = 0; - //! Called if a fatal error (signal/structured exception) occured + //! Called if a fatal error (signal/structured exception) occurred virtual void fatalErrorEncountered( StringRef error ) = 0; //! Writes out information about provided reporters using reporter-specific format diff --git a/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.cpp b/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.cpp deleted file mode 100644 index f620cbc8..00000000 --- a/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.cpp +++ /dev/null @@ -1,13 +0,0 @@ - -// Copyright Catch2 Authors -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.txt or copy at -// https://www.boost.org/LICENSE_1_0.txt) - -// SPDX-License-Identifier: BSL-1.0 - -#include - -namespace Catch { - IReporterRegistry::~IReporterRegistry() = default; -} diff --git a/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.hpp b/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.hpp deleted file mode 100644 index 277d1761..00000000 --- a/src/external/catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.hpp +++ /dev/null @@ -1,42 +0,0 @@ - -// Copyright Catch2 Authors -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.txt or copy at -// https://www.boost.org/LICENSE_1_0.txt) - -// SPDX-License-Identifier: BSL-1.0 -#ifndef CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED -#define CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED - -#include -#include - -#include -#include -#include - -namespace Catch { - - class IConfig; - - class IEventListener; - using IEventListenerPtr = Detail::unique_ptr; - class IReporterFactory; - using IReporterFactoryPtr = Detail::unique_ptr; - struct ReporterConfig; - class EventListenerFactory; - - class IReporterRegistry { - public: - using FactoryMap = std::map; - using Listeners = std::vector>; - - virtual ~IReporterRegistry(); // = default - virtual IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - virtual Listeners const& getListeners() const = 0; - }; - -} // end namespace Catch - -#endif // CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/interfaces/catch_interfaces_test_invoker.hpp b/src/external/catch2/src/catch2/interfaces/catch_interfaces_test_invoker.hpp new file mode 100644 index 00000000..124a7f7d --- /dev/null +++ b/src/external/catch2/src/catch2/interfaces/catch_interfaces_test_invoker.hpp @@ -0,0 +1,23 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +#ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED +#define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED + +namespace Catch { + + class ITestInvoker { + public: + virtual void prepareTestCase(); + virtual void tearDownTestCase(); + virtual void invoke() const = 0; + virtual ~ITestInvoker(); // = default + }; + +} // namespace Catch + +#endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/interfaces/catch_interfaces_testcase.cpp b/src/external/catch2/src/catch2/interfaces/catch_interfaces_testcase.cpp index 5e632ba8..a543116c 100644 --- a/src/external/catch2/src/catch2/interfaces/catch_interfaces_testcase.cpp +++ b/src/external/catch2/src/catch2/interfaces/catch_interfaces_testcase.cpp @@ -9,6 +9,5 @@ #include namespace Catch { - ITestInvoker::~ITestInvoker() = default; ITestCaseRegistry::~ITestCaseRegistry() = default; } diff --git a/src/external/catch2/src/catch2/interfaces/catch_interfaces_testcase.hpp b/src/external/catch2/src/catch2/interfaces/catch_interfaces_testcase.hpp index 78ee2021..daee8482 100644 --- a/src/external/catch2/src/catch2/interfaces/catch_interfaces_testcase.hpp +++ b/src/external/catch2/src/catch2/interfaces/catch_interfaces_testcase.hpp @@ -12,15 +12,7 @@ namespace Catch { - class TestSpec; struct TestCaseInfo; - - class ITestInvoker { - public: - virtual void invoke () const = 0; - virtual ~ITestInvoker(); // = default - }; - class TestCaseHandle; class IConfig; @@ -33,11 +25,6 @@ namespace Catch { virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; - bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ); - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - } #endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_assertion_handler.cpp b/src/external/catch2/src/catch2/internal/catch_assertion_handler.cpp index 0b14e0bb..9a28e79c 100644 --- a/src/external/catch2/src/catch2/internal/catch_assertion_handler.cpp +++ b/src/external/catch2/src/catch2/internal/catch_assertion_handler.cpp @@ -8,11 +8,8 @@ #include #include #include -#include #include #include -#include -#include #include namespace Catch { @@ -24,13 +21,15 @@ namespace Catch { ResultDisposition::Flags resultDisposition ) : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, m_resultCapture( getResultCapture() ) - {} + { + m_resultCapture.notifyAssertionStarted( m_assertionInfo ); + } void AssertionHandler::handleExpr( ITransientExpression const& expr ) { m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); } - void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef message) { - m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); + void AssertionHandler::handleMessage(ResultWas::OfType resultType, std::string&& message) { + m_resultCapture.handleMessage( m_assertionInfo, resultType, CATCH_MOVE(message), m_reaction ); } auto AssertionHandler::allowThrows() const -> bool { @@ -38,7 +37,7 @@ namespace Catch { } void AssertionHandler::complete() { - setCompleted(); + m_completed = true; if( m_reaction.shouldDebugBreak ) { // If you find your debugger stopping you here then go one level up on the @@ -51,16 +50,9 @@ namespace Catch { throw_test_failure_exception(); } if ( m_reaction.shouldSkip ) { -#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) - throw Catch::TestSkipException(); -#else - CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); -#endif + throw_test_skip_exception(); } } - void AssertionHandler::setCompleted() { - m_completed = true; - } void AssertionHandler::handleUnexpectedInflightException() { m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); diff --git a/src/external/catch2/src/catch2/internal/catch_assertion_handler.hpp b/src/external/catch2/src/catch2/internal/catch_assertion_handler.hpp index ae7776d8..c71c6898 100644 --- a/src/external/catch2/src/catch2/internal/catch_assertion_handler.hpp +++ b/src/external/catch2/src/catch2/internal/catch_assertion_handler.hpp @@ -11,14 +11,11 @@ #include #include #include -#include #include namespace Catch { - class IResultCapture; - struct AssertionReaction { bool shouldDebugBreak = false; bool shouldThrow = false; @@ -45,12 +42,12 @@ namespace Catch { template - void handleExpr( ExprLhs const& expr ) { + constexpr void handleExpr( ExprLhs const& expr ) { handleExpr( expr.makeUnaryExpr() ); } void handleExpr( ITransientExpression const& expr ); - void handleMessage(ResultWas::OfType resultType, StringRef message); + void handleMessage(ResultWas::OfType resultType, std::string&& message); void handleExceptionThrownAsExpected(); void handleUnexpectedExceptionNotThrown(); @@ -59,7 +56,6 @@ namespace Catch { void handleUnexpectedInflightException(); void complete(); - void setCompleted(); // query auto allowThrows() const -> bool; diff --git a/src/external/catch2/src/catch2/internal/catch_clara.cpp b/src/external/catch2/src/catch2/internal/catch_clara.cpp index c9bc7695..f9dd9138 100644 --- a/src/external/catch2/src/catch2/internal/catch_clara.cpp +++ b/src/external/catch2/src/catch2/internal/catch_clara.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -24,13 +25,29 @@ namespace { ; } - std::string normaliseOpt( std::string const& optName ) { -#ifdef CATCH_PLATFORM_WINDOWS - if ( optName[0] == '/' ) - return "-" + optName.substr( 1 ); - else + Catch::StringRef normaliseOpt( Catch::StringRef optName ) { + if ( optName[0] == '-' +#if defined(CATCH_PLATFORM_WINDOWS) + || optName[0] == '/' #endif - return optName; + ) { + return optName.substr( 1, optName.size() ); + } + + return optName; + } + + static size_t find_first_separator(Catch::StringRef sr) { + auto is_separator = []( char c ) { + return c == ' ' || c == ':' || c == '='; + }; + size_t pos = 0; + while (pos < sr.size()) { + if (is_separator(sr[pos])) { return pos; } + ++pos; + } + + return Catch::StringRef::npos; } } // namespace @@ -48,23 +65,23 @@ namespace Catch { } if ( it != itEnd ) { - auto const& next = *it; + StringRef next = *it; if ( isOptPrefix( next[0] ) ) { - auto delimiterPos = next.find_first_of( " :=" ); - if ( delimiterPos != std::string::npos ) { + auto delimiterPos = find_first_separator(next); + if ( delimiterPos != StringRef::npos ) { m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); m_tokenBuffer.push_back( { TokenType::Argument, - next.substr( delimiterPos + 1 ) } ); + next.substr( delimiterPos + 1, next.size() ) } ); } else { - if ( next[1] != '-' && next.size() > 2 ) { - std::string opt = "- "; + if ( next.size() > 1 && next[1] != '-' && next.size() > 2 ) { + // Combined short args, e.g. "-ab" for "-a -b" for ( size_t i = 1; i < next.size(); ++i ) { - opt[1] = next[i]; m_tokenBuffer.push_back( - { TokenType::Option, opt } ); + { TokenType::Option, + next.substr( i, 1 ) } ); } } else { m_tokenBuffer.push_back( @@ -124,12 +141,12 @@ namespace Catch { size_t ParserBase::cardinality() const { return 1; } InternalParseResult ParserBase::parse( Args const& args ) const { - return parse( args.exeName(), TokenStream( args ) ); + return parse( static_cast(args.exeName()), TokenStream( args ) ); } ParseState::ParseState( ParseResultType type, - TokenStream const& remainingTokens ): - m_type( type ), m_remainingTokens( remainingTokens ) {} + TokenStream remainingTokens ): + m_type( type ), m_remainingTokens( CATCH_MOVE(remainingTokens) ) {} ParserResult BoundFlagRef::setFlag( bool flag ) { m_ref = flag; @@ -147,34 +164,34 @@ namespace Catch { } // namespace Detail Detail::InternalParseResult Arg::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream tokens) const { auto validationResult = validate(); if (!validationResult) return Detail::InternalParseResult(validationResult); - auto remainingTokens = tokens; - auto const& token = *remainingTokens; + auto token = *tokens; if (token.type != Detail::TokenType::Argument) return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::NoMatch, remainingTokens)); + ParseResultType::NoMatch, CATCH_MOVE(tokens))); assert(!m_ref->isFlag()); auto valueRef = static_cast(m_ref.get()); - auto result = valueRef->setValue(remainingTokens->token); - if (!result) - return Detail::InternalParseResult(result); + auto result = valueRef->setValue(static_cast(token.token)); + if ( !result ) + return Detail::InternalParseResult( result ); else - return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::Matched, ++remainingTokens)); + return Detail::InternalParseResult::ok( + Detail::ParseState( ParseResultType::Matched, + CATCH_MOVE( ++tokens ) ) ); } Opt::Opt(bool& ref) : ParserRefImpl(std::make_shared(ref)) {} - std::vector Opt::getHelpColumns() const { - std::ostringstream oss; + Detail::HelpColumns Opt::getHelpColumns() const { + ReusableStringStream oss; bool first = true; for (auto const& opt : m_optNames) { if (first) @@ -185,10 +202,10 @@ namespace Catch { } if (!m_hint.empty()) oss << " <" << m_hint << '>'; - return { { oss.str(), m_description } }; + return { oss.str(), m_description }; } - bool Opt::isMatch(std::string const& optToken) const { + bool Opt::isMatch(StringRef optToken) const { auto normalisedToken = normaliseOpt(optToken); for (auto const& name : m_optNames) { if (normaliseOpt(name) == normalisedToken) @@ -198,15 +215,14 @@ namespace Catch { } Detail::InternalParseResult Opt::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream tokens) const { auto validationResult = validate(); if (!validationResult) return Detail::InternalParseResult(validationResult); - auto remainingTokens = tokens; - if (remainingTokens && - remainingTokens->type == Detail::TokenType::Option) { - auto const& token = *remainingTokens; + if (tokens && + tokens->type == Detail::TokenType::Option) { + auto const& token = *tokens; if (isMatch(token.token)) { if (m_ref->isFlag()) { auto flagRef = @@ -218,35 +234,35 @@ namespace Catch { if (result.value() == ParseResultType::ShortCircuitAll) return Detail::InternalParseResult::ok(Detail::ParseState( - result.value(), remainingTokens)); + result.value(), CATCH_MOVE(tokens))); } else { auto valueRef = static_cast( m_ref.get()); - ++remainingTokens; - if (!remainingTokens) + ++tokens; + if (!tokens) return Detail::InternalParseResult::runtimeError( "Expected argument following " + token.token); - auto const& argToken = *remainingTokens; + auto const& argToken = *tokens; if (argToken.type != Detail::TokenType::Argument) return Detail::InternalParseResult::runtimeError( "Expected argument following " + token.token); - const auto result = valueRef->setValue(argToken.token); + const auto result = valueRef->setValue(static_cast(argToken.token)); if (!result) return Detail::InternalParseResult(result); if (result.value() == ParseResultType::ShortCircuitAll) return Detail::InternalParseResult::ok(Detail::ParseState( - result.value(), remainingTokens)); + result.value(), CATCH_MOVE(tokens))); } return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::Matched, ++remainingTokens)); + ParseResultType::Matched, CATCH_MOVE(++tokens))); } } return Detail::InternalParseResult::ok( - Detail::ParseState(ParseResultType::NoMatch, remainingTokens)); + Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); } Detail::Result Opt::validate() const { @@ -278,9 +294,9 @@ namespace Catch { Detail::InternalParseResult ExeName::parse(std::string const&, - Detail::TokenStream const& tokens) const { + Detail::TokenStream tokens) const { return Detail::InternalParseResult::ok( - Detail::ParseState(ParseResultType::NoMatch, tokens)); + Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); } ParserResult ExeName::set(std::string const& newName) { @@ -310,9 +326,9 @@ namespace Catch { std::vector Parser::getHelpColumns() const { std::vector cols; + cols.reserve( m_options.size() ); for ( auto const& o : m_options ) { - auto childCols = o.getHelpColumns(); - cols.insert( cols.end(), childCols.begin(), childCols.end() ); + cols.push_back(o.getHelpColumns()); } return cols; } @@ -350,12 +366,12 @@ namespace Catch { optWidth = ( std::min )( optWidth, consoleWidth / 2 ); - for ( auto const& cols : rows ) { - auto row = TextFlow::Column( cols.left ) + for ( auto& cols : rows ) { + auto row = TextFlow::Column( CATCH_MOVE(cols.left) ) .width( optWidth ) .indent( 2 ) + TextFlow::Spacer( 4 ) + - TextFlow::Column( cols.right ) + TextFlow::Column( static_cast(cols.descriptions) ) .width( consoleWidth - 7 - optWidth ); os << row << '\n'; } @@ -377,7 +393,7 @@ namespace Catch { Detail::InternalParseResult Parser::parse( std::string const& exeName, - Detail::TokenStream const& tokens ) const { + Detail::TokenStream tokens ) const { struct ParserInfo { ParserBase const* parser = nullptr; @@ -395,7 +411,7 @@ namespace Catch { m_exeName.set( exeName ); auto result = Detail::InternalParseResult::ok( - Detail::ParseState( ParseResultType::NoMatch, tokens ) ); + Detail::ParseState( ParseResultType::NoMatch, CATCH_MOVE(tokens) ) ); while ( result.value().remainingTokens() ) { bool tokenParsed = false; @@ -403,7 +419,7 @@ namespace Catch { if ( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { result = parseInfo.parser->parse( - exeName, result.value().remainingTokens() ); + exeName, CATCH_MOVE(result).value().remainingTokens() ); if ( !result ) return result; if ( result.value().type() != @@ -429,7 +445,7 @@ namespace Catch { Args::Args(int argc, char const* const* argv) : m_exeName(argv[0]), m_args(argv + 1, argv + argc) {} - Args::Args(std::initializer_list args) : + Args::Args(std::initializer_list args) : m_exeName(*args.begin()), m_args(args.begin() + 1, args.end()) {} diff --git a/src/external/catch2/src/catch2/internal/catch_clara.hpp b/src/external/catch2/src/catch2/internal/catch_clara.hpp index 9117b65e..d869593b 100644 --- a/src/external/catch2/src/catch2/internal/catch_clara.hpp +++ b/src/external/catch2/src/catch2/internal/catch_clara.hpp @@ -29,6 +29,7 @@ # endif #endif +#include #include #include #include @@ -101,17 +102,16 @@ namespace Catch { enum class TokenType { Option, Argument }; struct Token { TokenType type; - std::string token; + StringRef token; }; // Abstracts iterators into args as a stream of tokens, with option // arguments uniformly handled class TokenStream { - using Iterator = std::vector::const_iterator; + using Iterator = std::vector::const_iterator; Iterator it; Iterator itEnd; std::vector m_tokenBuffer; - void loadBuffer(); public: @@ -163,12 +163,17 @@ namespace Catch { ResultType m_type; }; - template class ResultValueBase : public ResultBase { + template + class ResultValueBase : public ResultBase { public: - auto value() const -> T const& { + T const& value() const& { enforceOk(); return m_value; } + T&& value() && { + enforceOk(); + return CATCH_MOVE( m_value ); + } protected: ResultValueBase( ResultType type ): ResultBase( type ) {} @@ -178,13 +183,23 @@ namespace Catch { if ( m_type == ResultType::Ok ) new ( &m_value ) T( other.m_value ); } + ResultValueBase( ResultValueBase&& other ): + ResultBase( other ) { + if ( m_type == ResultType::Ok ) + new ( &m_value ) T( CATCH_MOVE(other.m_value) ); + } + - ResultValueBase( ResultType, T const& value ): ResultBase( ResultType::Ok ) { + ResultValueBase( ResultType, T const& value ): + ResultBase( ResultType::Ok ) { new ( &m_value ) T( value ); } + ResultValueBase( ResultType, T&& value ): + ResultBase( ResultType::Ok ) { + new ( &m_value ) T( CATCH_MOVE(value) ); + } - auto operator=( ResultValueBase const& other ) - -> ResultValueBase& { + ResultValueBase& operator=( ResultValueBase const& other ) { if ( m_type == ResultType::Ok ) m_value.~T(); ResultBase::operator=( other ); @@ -192,6 +207,14 @@ namespace Catch { new ( &m_value ) T( other.m_value ); return *this; } + ResultValueBase& operator=( ResultValueBase&& other ) { + if ( m_type == ResultType::Ok ) m_value.~T(); + ResultBase::operator=( other ); + if ( m_type == ResultType::Ok ) + new ( &m_value ) T( CATCH_MOVE(other.m_value) ); + return *this; + } + ~ResultValueBase() override { if ( m_type == ResultType::Ok ) @@ -219,8 +242,8 @@ namespace Catch { } template - static auto ok( U const& value ) -> BasicResult { - return { ResultType::Ok, value }; + static auto ok( U&& value ) -> BasicResult { + return { ResultType::Ok, CATCH_FORWARD(value) }; } static auto ok() -> BasicResult { return { ResultType::Ok }; } static auto logicError( std::string&& message ) @@ -267,12 +290,15 @@ namespace Catch { class ParseState { public: ParseState( ParseResultType type, - TokenStream const& remainingTokens ); + TokenStream remainingTokens ); ParseResultType type() const { return m_type; } - TokenStream const& remainingTokens() const { + TokenStream const& remainingTokens() const& { return m_remainingTokens; } + TokenStream&& remainingTokens() && { + return CATCH_MOVE( m_remainingTokens ); + } private: ParseResultType m_type; @@ -285,7 +311,7 @@ namespace Catch { struct HelpColumns { std::string left; - std::string right; + StringRef descriptions; }; template @@ -445,7 +471,7 @@ namespace Catch { virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } virtual auto parse( std::string const& exeName, - TokenStream const& tokens ) const + TokenStream tokens ) const -> InternalParseResult = 0; virtual size_t cardinality() const; @@ -465,8 +491,8 @@ namespace Catch { protected: Optionality m_optionality = Optionality::Optional; std::shared_ptr m_ref; - std::string m_hint; - std::string m_description; + StringRef m_hint; + StringRef m_description; explicit ParserRefImpl( std::shared_ptr const& ref ): m_ref( ref ) {} @@ -475,28 +501,32 @@ namespace Catch { template ParserRefImpl( accept_many_t, LambdaT const& ref, - std::string const& hint ): + StringRef hint ): m_ref( std::make_shared>( ref ) ), m_hint( hint ) {} template ::value>> - ParserRefImpl( T& ref, std::string const& hint ): + ParserRefImpl( T& ref, StringRef hint ): m_ref( std::make_shared>( ref ) ), m_hint( hint ) {} template ::value>> - ParserRefImpl( LambdaT const& ref, std::string const& hint ): + ParserRefImpl( LambdaT const& ref, StringRef hint ): m_ref( std::make_shared>( ref ) ), m_hint( hint ) {} - auto operator()( std::string const& description ) -> DerivedT& { + DerivedT& operator()( StringRef description ) & { m_description = description; return static_cast( *this ); } + DerivedT&& operator()( StringRef description ) && { + m_description = description; + return static_cast( *this ); + } auto optional() -> DerivedT& { m_optionality = Optionality::Optional; @@ -519,7 +549,7 @@ namespace Catch { return 1; } - std::string const& hint() const { return m_hint; } + StringRef hint() const { return m_hint; } }; } // namespace detail @@ -533,13 +563,13 @@ namespace Catch { Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; }; // A parser for options class Opt : public Detail::ParserRefImpl { protected: - std::vector m_optNames; + std::vector m_optNames; public: template @@ -552,33 +582,37 @@ namespace Catch { template ::value>> - Opt( LambdaT const& ref, std::string const& hint ): + Opt( LambdaT const& ref, StringRef hint ): ParserRefImpl( ref, hint ) {} template - Opt( accept_many_t, LambdaT const& ref, std::string const& hint ): + Opt( accept_many_t, LambdaT const& ref, StringRef hint ): ParserRefImpl( accept_many, ref, hint ) {} template ::value>> - Opt( T& ref, std::string const& hint ): + Opt( T& ref, StringRef hint ): ParserRefImpl( ref, hint ) {} - auto operator[](std::string const& optName) -> Opt& { + Opt& operator[]( StringRef optName ) & { m_optNames.push_back(optName); return *this; } + Opt&& operator[]( StringRef optName ) && { + m_optNames.push_back( optName ); + return CATCH_MOVE(*this); + } - std::vector getHelpColumns() const; + Detail::HelpColumns getHelpColumns() const; - bool isMatch(std::string const& optToken) const; + bool isMatch(StringRef optToken) const; using ParserBase::parse; Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; Detail::Result validate() const override; }; @@ -601,7 +635,7 @@ namespace Catch { // handled specially Detail::InternalParseResult parse(std::string const&, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; std::string const& name() const { return *m_name; } Detail::ParserResult set(std::string const& newName); @@ -626,16 +660,28 @@ namespace Catch { return *this; } - auto operator|=(Opt const& opt) -> Parser& { - m_options.push_back(opt); - return *this; + friend Parser& operator|=( Parser& p, Opt const& opt ) { + p.m_options.push_back( opt ); + return p; + } + friend Parser& operator|=( Parser& p, Opt&& opt ) { + p.m_options.push_back( CATCH_MOVE(opt) ); + return p; } Parser& operator|=(Parser const& other); template - auto operator|(T const& other) const -> Parser { - return Parser(*this) |= other; + friend Parser operator|( Parser const& p, T&& rhs ) { + Parser temp( p ); + temp |= rhs; + return temp; + } + + template + friend Parser operator|( Parser&& p, T&& rhs ) { + p |= CATCH_FORWARD(rhs); + return CATCH_MOVE(p); } std::vector getHelpColumns() const; @@ -653,21 +699,23 @@ namespace Catch { using ParserBase::parse; Detail::InternalParseResult parse(std::string const& exeName, - Detail::TokenStream const& tokens) const override; + Detail::TokenStream tokens) const override; }; - // Transport for raw args (copied from main args, or supplied via - // init list for testing) + /** + * Wrapper over argc + argv, assumes that the inputs outlive it + */ class Args { friend Detail::TokenStream; - std::string m_exeName; - std::vector m_args; + StringRef m_exeName; + std::vector m_args; public: Args(int argc, char const* const* argv); - Args(std::initializer_list args); + // Helper constructor for testing + Args(std::initializer_list args); - std::string const& exeName() const { return m_exeName; } + StringRef exeName() const { return m_exeName; } }; diff --git a/src/external/catch2/src/catch2/internal/catch_commandline.cpp b/src/external/catch2/src/catch2/internal/catch_commandline.cpp index 81aa073c..212f1774 100644 --- a/src/external/catch2/src/catch2/internal/catch_commandline.cpp +++ b/src/external/catch2/src/catch2/internal/catch_commandline.cpp @@ -9,8 +9,9 @@ #include #include +#include #include -#include +#include #include #include #include @@ -46,7 +47,7 @@ namespace Catch { line = trim(line); if( !line.empty() && !startsWith( line, '#' ) ) { if( !startsWith( line, '"' ) ) - line = '"' + line + '"'; + line = '"' + CATCH_MOVE(line) + '"'; config.testsOrTags.push_back( line ); config.testsOrTags.emplace_back( "," ); } @@ -144,7 +145,7 @@ namespace Catch { auto const& reporterSpec = *parsed; - IReporterRegistry::FactoryMap const& factories = + auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); auto result = factories.find( reporterSpec.name() ); @@ -300,8 +301,8 @@ namespace Catch { ( "split the tests to execute into this many groups" ) | Opt( setShardIndex, "shard index" ) ["--shard-index"] - ( "index of the group of tests to execute (see --shard-count)" ) | - Opt( config.allowZeroTests ) + ( "index of the group of tests to execute (see --shard-count)" ) + | Opt( config.allowZeroTests ) ["--allow-running-no-tests"] ( "Treat 'No tests run' as a success" ) | Arg( config.testsOrTags, "test name|pattern|tags" ) diff --git a/src/external/catch2/src/catch2/internal/catch_compiler_capabilities.hpp b/src/external/catch2/src/catch2/internal/catch_compiler_capabilities.hpp index 42631a5f..7f09da51 100644 --- a/src/external/catch2/src/catch2/internal/catch_compiler_capabilities.hpp +++ b/src/external/catch2/src/catch2/internal/catch_compiler_capabilities.hpp @@ -29,14 +29,14 @@ #ifdef __cplusplus -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif - # if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) # define CATCH_CPP17_OR_GREATER # endif +# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +# define CATCH_CPP20_OR_GREATER +# endif + #endif // Only GCC compiler should be used in this block, so other compilers trying to @@ -50,12 +50,18 @@ # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ + _Pragma( "GCC diagnostic ignored \"-Wunused-result\"" ) + # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" ) # define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) + # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) #endif @@ -128,6 +134,9 @@ # define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wcomma\"" ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) + #endif // __clang__ @@ -147,7 +156,9 @@ //////////////////////////////////////////////////////////////////////////////// // Assume that some platforms do not support getenv. -#if defined(CATCH_PLATFORM_WINDOWS_UWP) || defined(CATCH_PLATFORM_PLAYSTATION) +#if defined( CATCH_PLATFORM_WINDOWS_UWP ) || \ + defined( CATCH_PLATFORM_PLAYSTATION ) || \ + defined( _GAMING_XBOX ) # define CATCH_INTERNAL_CONFIG_NO_GETENV #else # define CATCH_INTERNAL_CONFIG_GETENV @@ -365,6 +376,9 @@ #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS #endif @@ -374,6 +388,16 @@ #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif +#if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif +#if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS +#endif +#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS +#endif + // The goal of this macro is to avoid evaluation of the arguments, but // still have the compiler warn on problems inside... @@ -387,13 +411,6 @@ # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS -#endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #define CATCH_TRY if ((true)) diff --git a/src/external/catch2/src/catch2/internal/catch_config_counter.hpp b/src/external/catch2/src/catch2/internal/catch_config_counter.hpp index 23b22324..a482ce34 100644 --- a/src/external/catch2/src/catch2/internal/catch_config_counter.hpp +++ b/src/external/catch2/src/catch2/internal/catch_config_counter.hpp @@ -18,6 +18,8 @@ #ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED #define CATCH_CONFIG_COUNTER_HPP_INCLUDED +#include + #if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif diff --git a/src/external/catch2/src/catch2/internal/catch_config_prefix_messages.hpp b/src/external/catch2/src/catch2/internal/catch_config_prefix_messages.hpp new file mode 100644 index 00000000..be1e9a96 --- /dev/null +++ b/src/external/catch2/src/catch2/internal/catch_config_prefix_messages.hpp @@ -0,0 +1,29 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +/** \file + * Wrapper for the CATCH_CONFIG_PREFIX_MESSAGES configuration option + * + * CATCH_CONFIG_PREFIX_ALL can be used to avoid clashes with other macros + * by prepending CATCH_. This may not be desirable if the only clashes are with + * logger macros such as INFO and WARN. In this cases + * CATCH_CONFIG_PREFIX_MESSAGES can be used to only prefix a small subset + * of relevant macros. + * + */ + +#ifndef CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED +#define CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED + +#include + +#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_PREFIX_MESSAGES) + #define CATCH_CONFIG_PREFIX_MESSAGES +#endif + +#endif // CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_config_static_analysis_support.hpp b/src/external/catch2/src/catch2/internal/catch_config_static_analysis_support.hpp new file mode 100644 index 00000000..81bdf39f --- /dev/null +++ b/src/external/catch2/src/catch2/internal/catch_config_static_analysis_support.hpp @@ -0,0 +1,34 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +/** \file + * Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option + * + * Some of Catch2's macros can be defined differently to work better with + * static analysis tools, like clang-tidy or coverity. + * Currently the main use case is to show that `SECTION`s are executed + * exclusively, and not all in one run of a `TEST_CASE`. + */ + +#ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED +#define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED + +#include + +#if defined(__clang_analyzer__) || defined(__COVERITY__) + #define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT +#endif + +#if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \ + !defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \ + !defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) +# define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT +#endif + + +#endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_config_uncaught_exceptions.hpp b/src/external/catch2/src/catch2/internal/catch_config_uncaught_exceptions.hpp index 5c4cb930..20b1dfca 100644 --- a/src/external/catch2/src/catch2/internal/catch_config_uncaught_exceptions.hpp +++ b/src/external/catch2/src/catch2/internal/catch_config_uncaught_exceptions.hpp @@ -17,6 +17,8 @@ #ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED #define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED +#include + #if defined(_MSC_VER) # if _MSC_VER >= 1900 // Visual Studio 2015 or newer # define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS diff --git a/src/external/catch2/src/catch2/internal/catch_config_wchar.hpp b/src/external/catch2/src/catch2/internal/catch_config_wchar.hpp index 8c758ec4..90d85d05 100644 --- a/src/external/catch2/src/catch2/internal/catch_config_wchar.hpp +++ b/src/external/catch2/src/catch2/internal/catch_config_wchar.hpp @@ -17,6 +17,8 @@ #ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED #define CATCH_CONFIG_WCHAR_HPP_INCLUDED +#include + // We assume that WCHAR should be enabled by default, and only disabled // for a shortlist (so far only DJGPP) of compilers. diff --git a/src/external/catch2/src/catch2/internal/catch_console_colour.cpp b/src/external/catch2/src/catch2/internal/catch_console_colour.cpp index 099a6c59..b19e01ec 100644 --- a/src/external/catch2/src/catch2/internal/catch_console_colour.cpp +++ b/src/external/catch2/src/catch2/internal/catch_console_colour.cpp @@ -85,7 +85,7 @@ namespace Catch { namespace { //! A do-nothing implementation of colour, used as fallback for unknown //! platforms, and when the user asks to deactivate all colours. - class NoColourImpl : public ColourImpl { + class NoColourImpl final : public ColourImpl { public: NoColourImpl( IStream* stream ): ColourImpl( stream ) {} @@ -103,7 +103,7 @@ namespace Catch { namespace Catch { namespace { - class Win32ColourImpl : public ColourImpl { + class Win32ColourImpl final : public ColourImpl { public: Win32ColourImpl(IStream* stream): ColourImpl(stream) { @@ -169,7 +169,7 @@ namespace { namespace Catch { namespace { - class ANSIColourImpl : public ColourImpl { + class ANSIColourImpl final : public ColourImpl { public: ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {} @@ -230,21 +230,21 @@ namespace { namespace Catch { - Detail::unique_ptr makeColourImpl( ColourMode implSelection, + Detail::unique_ptr makeColourImpl( ColourMode colourSelection, IStream* stream ) { #if defined( CATCH_CONFIG_COLOUR_WIN32 ) - if ( implSelection == ColourMode::Win32 ) { + if ( colourSelection == ColourMode::Win32 ) { return Detail::make_unique( stream ); } #endif - if ( implSelection == ColourMode::ANSI ) { + if ( colourSelection == ColourMode::ANSI ) { return Detail::make_unique( stream ); } - if ( implSelection == ColourMode::None ) { + if ( colourSelection == ColourMode::None ) { return Detail::make_unique( stream ); } - if ( implSelection == ColourMode::PlatformDefault) { + if ( colourSelection == ColourMode::PlatformDefault) { #if defined( CATCH_CONFIG_COLOUR_WIN32 ) if ( Win32ColourImpl::useImplementationForStream( *stream ) ) { return Detail::make_unique( stream ); @@ -256,7 +256,7 @@ namespace Catch { return Detail::make_unique( stream ); } - CATCH_ERROR( "Could not create colour impl for selection " << static_cast(implSelection) ); + CATCH_ERROR( "Could not create colour impl for selection " << static_cast(colourSelection) ); } bool isColourImplAvailable( ColourMode colourSelection ) { diff --git a/src/external/catch2/src/catch2/internal/catch_context.cpp b/src/external/catch2/src/catch2/internal/catch_context.cpp index 17f28509..8acf1eda 100644 --- a/src/external/catch2/src/catch2/internal/catch_context.cpp +++ b/src/external/catch2/src/catch2/internal/catch_context.cpp @@ -11,49 +11,21 @@ namespace Catch { - class Context : public IMutableContext, private Detail::NonCopyable { + Context* Context::currentContext = nullptr; - public: // IContext - IResultCapture* getResultCapture() override { - return m_resultCapture; - } - - IConfig const* getConfig() const override { - return m_config; - } - - ~Context() override; - - public: // IMutableContext - void setResultCapture( IResultCapture* resultCapture ) override { - m_resultCapture = resultCapture; - } - void setConfig( IConfig const* config ) override { - m_config = config; - } - - friend IMutableContext& getCurrentMutableContext(); - - private: - IConfig const* m_config = nullptr; - IResultCapture* m_resultCapture = nullptr; - }; - - IMutableContext *IMutableContext::currentContext = nullptr; - - void IMutableContext::createContext() - { + void cleanUpContext() { + delete Context::currentContext; + Context::currentContext = nullptr; + } + void Context::createContext() { currentContext = new Context(); } - void cleanUpContext() { - delete IMutableContext::currentContext; - IMutableContext::currentContext = nullptr; + Context& getCurrentMutableContext() { + if ( !Context::currentContext ) { Context::createContext(); } + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *Context::currentContext; } - IContext::~IContext() = default; - IMutableContext::~IMutableContext() = default; - Context::~Context() = default; - SimplePcg32& sharedRng() { static SimplePcg32 s_rng; diff --git a/src/external/catch2/src/catch2/internal/catch_context.hpp b/src/external/catch2/src/catch2/internal/catch_context.hpp index a9d1b394..4d8a5da1 100644 --- a/src/external/catch2/src/catch2/internal/catch_context.hpp +++ b/src/external/catch2/src/catch2/internal/catch_context.hpp @@ -15,38 +15,36 @@ namespace Catch { class IResultCapture; class IConfig; - class IContext { - public: - virtual ~IContext(); // = default + class Context { + IConfig const* m_config = nullptr; + IResultCapture* m_resultCapture = nullptr; - virtual IResultCapture* getResultCapture() = 0; - virtual IConfig const* getConfig() const = 0; - }; + CATCH_EXPORT static Context* currentContext; + friend Context& getCurrentMutableContext(); + friend Context const& getCurrentContext(); + static void createContext(); + friend void cleanUpContext(); - class IMutableContext : public IContext { public: - ~IMutableContext() override; // = default - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setConfig( IConfig const* config ) = 0; + constexpr IResultCapture* getResultCapture() const { + return m_resultCapture; + } + constexpr IConfig const* getConfig() const { return m_config; } + constexpr void setResultCapture( IResultCapture* resultCapture ) { + m_resultCapture = resultCapture; + } + constexpr void setConfig( IConfig const* config ) { m_config = config; } - private: - CATCH_EXPORT static IMutableContext* currentContext; - friend IMutableContext& getCurrentMutableContext(); - friend void cleanUpContext(); - static void createContext(); }; - inline IMutableContext& getCurrentMutableContext() - { - if( !IMutableContext::currentContext ) - IMutableContext::createContext(); - // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) - return *IMutableContext::currentContext; - } + Context& getCurrentMutableContext(); - inline IContext& getCurrentContext() - { - return getCurrentMutableContext(); + inline Context const& getCurrentContext() { + // We duplicate the logic from `getCurrentMutableContext` here, + // to avoid paying the call overhead in debug mode. + if ( !Context::currentContext ) { Context::createContext(); } + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *Context::currentContext; } void cleanUpContext(); diff --git a/src/external/catch2/src/catch2/internal/catch_decomposer.cpp b/src/external/catch2/src/catch2/internal/catch_decomposer.cpp index 3f398fcc..17a7bc95 100644 --- a/src/external/catch2/src/catch2/internal/catch_decomposer.cpp +++ b/src/external/catch2/src/catch2/internal/catch_decomposer.cpp @@ -10,7 +10,12 @@ namespace Catch { - ITransientExpression::~ITransientExpression() = default; + void ITransientExpression::streamReconstructedExpression( + std::ostream& os ) const { + // We can't make this function pure virtual to keep ITransientExpression + // constexpr, so we write error message instead + os << "Some class derived from ITransientExpression without overriding streamReconstructedExpression"; + } void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { if( lhs.size() + rhs.size() < 40 && diff --git a/src/external/catch2/src/catch2/internal/catch_decomposer.hpp b/src/external/catch2/src/catch2/internal/catch_decomposer.hpp index e0e46c1d..adce89f2 100644 --- a/src/external/catch2/src/catch2/internal/catch_decomposer.hpp +++ b/src/external/catch2/src/catch2/internal/catch_decomposer.hpp @@ -13,10 +13,91 @@ #include #include #include +#include #include #include +/** \file + * Why does decomposing look the way it does: + * + * Conceptually, decomposing is simple. We change `REQUIRE( a == b )` into + * `Decomposer{} <= a == b`, so that `Decomposer{} <= a` is evaluated first, + * and our custom operator is used for `a == b`, because `a` is transformed + * into `ExprLhs` and then into `BinaryExpr`. + * + * In practice, decomposing ends up a mess, because we have to support + * various fun things. + * + * 1) Types that are only comparable with literal 0, and they do this by + * comparing against a magic type with pointer constructor and deleted + * other constructors. Example: `REQUIRE((a <=> b) == 0)` in libstdc++ + * + * 2) Types that are only comparable with literal 0, and they do this by + * comparing against a magic type with consteval integer constructor. + * Example: `REQUIRE((a <=> b) == 0)` in current MSVC STL. + * + * 3) Types that have no linkage, and so we cannot form a reference to + * them. Example: some implementations of traits. + * + * 4) Starting with C++20, when the compiler sees `a == b`, it also uses + * `b == a` when constructing the overload set. For us this means that + * when the compiler handles `ExprLhs == b`, it also tries to resolve + * the overload set for `b == ExprLhs`. + * + * To accomodate these use cases, decomposer ended up rather complex. + * + * 1) These types are handled by adding SFINAE overloads to our comparison + * operators, checking whether `T == U` are comparable with the given + * operator, and if not, whether T (or U) are comparable with literal 0. + * If yes, the overload compares T (or U) with 0 literal inline in the + * definition. + * + * Note that for extra correctness, we check that the other type is + * either an `int` (literal 0 is captured as `int` by templates), or + * a `long` (some platforms use 0L for `NULL` and we want to support + * that for pointer comparisons). + * + * 2) For these types, `is_foo_comparable` is true, but letting + * them fall into the overload that actually does `T == int` causes + * compilation error. Handling them requires that the decomposition + * is `constexpr`, so that P2564R3 applies and the `consteval` from + * their accompanying magic type is propagated through the `constexpr` + * call stack. + * + * However this is not enough to handle these types automatically, + * because our default is to capture types by reference, to avoid + * runtime copies. While these references cannot become dangling, + * they outlive the constexpr context and thus the default capture + * path cannot be actually constexpr. + * + * The solution is to capture these types by value, by explicitly + * specializing `Catch::capture_by_value` for them. Catch2 provides + * specialization for `std::foo_ordering`s, but users can specialize + * the trait for their own types as well. + * + * 3) If a type has no linkage, we also cannot capture it by reference. + * The solution is once again to capture them by value. We handle + * the common cases by using `std::is_arithmetic` as the default + * for `Catch::capture_by_value`, but that is only a some-effort + * heuristic. But as with 2), users can specialize `capture_by_value` + * for their own types as needed. + * + * 4) To support C++20 and make the SFINAE on our decomposing operators + * work, the SFINAE has to happen in return type, rather than in + * a template type. This is due to our use of logical type traits + * (`conjunction`/`disjunction`/`negation`), that we use to workaround + * an issue in older (9-) versions of GCC. I still blame C++20 for + * this, because without the comparison order switching, the logical + * traits could still be used in template type. + * + * There are also other side concerns, e.g. supporting both `REQUIRE(a)` + * and `REQUIRE(a == b)`, or making `REQUIRE_THAT(a, IsEqual(b))` slot + * nicely into the same expression handling logic, but these are rather + * straightforward and add only a bit of complexity (e.g. common base + * class for decomposed expressions). + */ + #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4389) // '==' : signed/unsigned mismatch @@ -29,13 +110,46 @@ #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" #elif defined __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + +#if defined(CATCH_CPP20_OR_GREATER) && __has_include() +# include +# if defined( __cpp_lib_three_way_comparison ) && \ + __cpp_lib_three_way_comparison >= 201907L +# define CATCH_CONFIG_CPP20_COMPARE_OVERLOADS +# endif #endif namespace Catch { + namespace Detail { + // This was added in C++20, but we require only C++14 for now. + template + using RemoveCVRef_t = std::remove_cv_t>; + } + + // Note: There is nothing that stops us from extending this, + // e.g. to `std::is_scalar`, but the more encompassing + // traits are usually also more expensive. For now we + // keep this as it used to be and it can be changed later. + template + struct capture_by_value + : std::integral_constant{}> {}; + +#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS ) + template <> + struct capture_by_value : std::true_type {}; + template <> + struct capture_by_value : std::true_type {}; + template <> + struct capture_by_value : std::true_type {}; +#endif + template struct always_false : std::false_type {}; @@ -43,23 +157,22 @@ namespace Catch { bool m_isBinaryExpression; bool m_result; + protected: + ~ITransientExpression() = default; + public: - auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } - auto getResult() const -> bool { return m_result; } - virtual void streamReconstructedExpression( std::ostream &os ) const = 0; + constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + constexpr auto getResult() const -> bool { return m_result; } + //! This function **has** to be overriden by the derived class. + virtual void streamReconstructedExpression( std::ostream& os ) const; - ITransientExpression( bool isBinaryExpression, bool result ) + constexpr ITransientExpression( bool isBinaryExpression, bool result ) : m_isBinaryExpression( isBinaryExpression ), m_result( result ) {} - ITransientExpression() = default; - ITransientExpression(ITransientExpression const&) = default; - ITransientExpression& operator=(ITransientExpression const&) = default; - - // We don't actually need a virtual destructor, but many static analysers - // complain if it's not here :-( - virtual ~ITransientExpression(); // = default; + constexpr ITransientExpression( ITransientExpression const& ) = default; + constexpr ITransientExpression& operator=( ITransientExpression const& ) = default; friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) { expr.streamReconstructedExpression(out); @@ -81,7 +194,7 @@ namespace Catch { } public: - BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + constexpr BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) : ITransientExpression{ true, comparisonResult }, m_lhs( lhs ), m_op( op ), @@ -154,7 +267,7 @@ namespace Catch { } public: - explicit UnaryExpr( LhsT lhs ) + explicit constexpr UnaryExpr( LhsT lhs ) : ITransientExpression{ false, static_cast(lhs) }, m_lhs( lhs ) {} @@ -165,31 +278,31 @@ namespace Catch { class ExprLhs { LhsT m_lhs; public: - explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + explicit constexpr ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} #define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ + -> std::enable_if_t< \ Detail::conjunction, \ - Detail::negation>>>::value, \ + Detail::negation>>>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction, \ - std::is_arithmetic>::value, \ + capture_by_value>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_eq_0_comparable, \ @@ -202,8 +315,8 @@ namespace Catch { static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_eq_0_comparable, \ @@ -220,29 +333,30 @@ namespace Catch { #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR + #define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ + -> std::enable_if_t< \ Detail::conjunction, \ - Detail::negation>>>::value, \ + Detail::negation>>>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction, \ - std::is_arithmetic>::value, \ + capture_by_value>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_##id##_0_comparable, \ @@ -253,8 +367,8 @@ namespace Catch { static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t< \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_##id##_0_comparable, \ @@ -274,17 +388,17 @@ namespace Catch { #define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - ->std::enable_if_t< \ - !std::is_arithmetic>::value, \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ + -> std::enable_if_t< \ + !capture_by_value>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ - friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - ->std::enable_if_t::value, \ - BinaryExpr> { \ + constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + -> std::enable_if_t::value, \ + BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } @@ -309,19 +423,22 @@ namespace Catch { "wrap the expression inside parentheses, or decompose it"); } - auto makeUnaryExpr() const -> UnaryExpr { + constexpr auto makeUnaryExpr() const -> UnaryExpr { return UnaryExpr{ m_lhs }; } }; struct Decomposer { - template>::value, int> = 0> - friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs { + template >::value, + int> = 0> + constexpr friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs { return ExprLhs{ lhs }; } - template::value, int> = 0> - friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs { + template ::value, int> = 0> + constexpr friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs { return ExprLhs{ value }; } }; diff --git a/src/external/catch2/src/catch2/internal/catch_enum_values_registry.cpp b/src/external/catch2/src/catch2/internal/catch_enum_values_registry.cpp index 7e8bf5e5..a94b6088 100644 --- a/src/external/catch2/src/catch2/internal/catch_enum_values_registry.cpp +++ b/src/external/catch2/src/catch2/internal/catch_enum_values_registry.cpp @@ -39,7 +39,7 @@ namespace Catch { return parsed; } - EnumInfo::~EnumInfo() {} + EnumInfo::~EnumInfo() = default; StringRef EnumInfo::lookup( int value ) const { for( auto const& valueToName : m_values ) { diff --git a/src/external/catch2/src/catch2/internal/catch_enum_values_registry.hpp b/src/external/catch2/src/catch2/internal/catch_enum_values_registry.hpp index 999059ae..de994c35 100644 --- a/src/external/catch2/src/catch2/internal/catch_enum_values_registry.hpp +++ b/src/external/catch2/src/catch2/internal/catch_enum_values_registry.hpp @@ -24,7 +24,7 @@ namespace Catch { std::vector> m_enumInfos; - EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values) override; + EnumInfo const& registerEnum( StringRef enumName, StringRef allValueNames, std::vector const& values) override; }; std::vector parseEnums( StringRef enums ); diff --git a/src/external/catch2/src/catch2/internal/catch_exception_translator_registry.cpp b/src/external/catch2/src/catch2/internal/catch_exception_translator_registry.cpp index 0645c6ce..1eb61147 100644 --- a/src/external/catch2/src/catch2/internal/catch_exception_translator_registry.cpp +++ b/src/external/catch2/src/catch2/internal/catch_exception_translator_registry.cpp @@ -11,10 +11,27 @@ #include #include +#include + namespace Catch { - ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + namespace { + static std::string tryTranslators( + std::vector< + Detail::unique_ptr> const& translators ) { + if ( translators.empty() ) { + std::rethrow_exception( std::current_exception() ); + } else { + return translators[0]->translate( translators.begin() + 1, + translators.end() ); + } + } + } +#endif //!defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() = default; void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr&& translator ) { m_translators.push_back( CATCH_MOVE( translator ) ); @@ -37,7 +54,7 @@ namespace Catch { // First we try user-registered translators. If none of them can // handle the exception, it will be rethrown handled by our defaults. try { - return tryTranslators(); + return tryTranslators(m_translators); } // To avoid having to handle TFE explicitly everywhere, we just // rethrow it so that it goes back up the caller. @@ -61,23 +78,10 @@ namespace Catch { } } - std::string ExceptionTranslatorRegistry::tryTranslators() const { - if (m_translators.empty()) { - std::rethrow_exception(std::current_exception()); - } else { - return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); - } - } - #else // ^^ Exceptions are enabled // Exceptions are disabled vv std::string ExceptionTranslatorRegistry::translateActiveException() const { CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); } - - std::string ExceptionTranslatorRegistry::tryTranslators() const { - CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); - } #endif - } diff --git a/src/external/catch2/src/catch2/internal/catch_exception_translator_registry.hpp b/src/external/catch2/src/catch2/internal/catch_exception_translator_registry.hpp index 2aafa684..3123e93d 100644 --- a/src/external/catch2/src/catch2/internal/catch_exception_translator_registry.hpp +++ b/src/external/catch2/src/catch2/internal/catch_exception_translator_registry.hpp @@ -21,7 +21,6 @@ namespace Catch { ~ExceptionTranslatorRegistry() override; void registerTranslator( Detail::unique_ptr&& translator ); std::string translateActiveException() const override; - std::string tryTranslators() const; private: ExceptionTranslators m_translators; diff --git a/src/external/catch2/src/catch2/internal/catch_fatal_condition_handler.cpp b/src/external/catch2/src/catch2/internal/catch_fatal_condition_handler.cpp index f9702b18..9ef5b217 100644 --- a/src/external/catch2/src/catch2/internal/catch_fatal_condition_handler.cpp +++ b/src/external/catch2/src/catch2/internal/catch_fatal_condition_handler.cpp @@ -26,6 +26,7 @@ #include +#include #include #include #include diff --git a/src/external/catch2/src/catch2/internal/catch_fatal_condition_handler.hpp b/src/external/catch2/src/catch2/internal/catch_fatal_condition_handler.hpp index ce07f9b6..81728b56 100644 --- a/src/external/catch2/src/catch2/internal/catch_fatal_condition_handler.hpp +++ b/src/external/catch2/src/catch2/internal/catch_fatal_condition_handler.hpp @@ -8,9 +8,6 @@ #ifndef CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED #define CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED -#include -#include - #include namespace Catch { diff --git a/src/external/catch2/src/catch2/internal/catch_floating_point_helpers.cpp b/src/external/catch2/src/catch2/internal/catch_floating_point_helpers.cpp index e30ee434..9631ed6d 100644 --- a/src/external/catch2/src/catch2/internal/catch_floating_point_helpers.cpp +++ b/src/external/catch2/src/catch2/internal/catch_floating_point_helpers.cpp @@ -27,6 +27,17 @@ namespace Catch { return i; } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + bool directCompare( float lhs, float rhs ) { return lhs == rhs; } + bool directCompare( double lhs, double rhs ) { return lhs == rhs; } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + + } // end namespace Detail } // end namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_floating_point_helpers.hpp b/src/external/catch2/src/catch2/internal/catch_floating_point_helpers.hpp index ca883c61..b2143726 100644 --- a/src/external/catch2/src/catch2/internal/catch_floating_point_helpers.hpp +++ b/src/external/catch2/src/catch2/internal/catch_floating_point_helpers.hpp @@ -22,6 +22,11 @@ namespace Catch { uint32_t convertToBits(float f); uint64_t convertToBits(double d); + // Used when we know we want == comparison of two doubles + // to centralize warning suppression + bool directCompare( float lhs, float rhs ); + bool directCompare( double lhs, double rhs ); + } // end namespace Detail diff --git a/src/external/catch2/src/catch2/internal/catch_is_permutation.hpp b/src/external/catch2/src/catch2/internal/catch_is_permutation.hpp index 708053d3..c77a6d3b 100644 --- a/src/external/catch2/src/catch2/internal/catch_is_permutation.hpp +++ b/src/external/catch2/src/catch2/internal/catch_is_permutation.hpp @@ -18,6 +18,7 @@ namespace Catch { typename Sentinel, typename T, typename Comparator> + constexpr ForwardIter find_sentinel( ForwardIter start, Sentinel sentinel, T const& value, @@ -33,6 +34,7 @@ namespace Catch { typename Sentinel, typename T, typename Comparator> + constexpr std::ptrdiff_t count_sentinel( ForwardIter start, Sentinel sentinel, T const& value, @@ -46,6 +48,7 @@ namespace Catch { } template + constexpr std::enable_if_t::value, std::ptrdiff_t> sentinel_distance( ForwardIter iter, const Sentinel sentinel ) { @@ -58,8 +61,8 @@ namespace Catch { } template - std::ptrdiff_t sentinel_distance( ForwardIter first, - ForwardIter last ) { + constexpr std::ptrdiff_t sentinel_distance( ForwardIter first, + ForwardIter last ) { return std::distance( first, last ); } @@ -68,11 +71,11 @@ namespace Catch { typename ForwardIter2, typename Sentinel2, typename Comparator> - bool check_element_counts( ForwardIter1 first_1, - const Sentinel1 end_1, - ForwardIter2 first_2, - const Sentinel2 end_2, - Comparator cmp ) { + constexpr bool check_element_counts( ForwardIter1 first_1, + const Sentinel1 end_1, + ForwardIter2 first_2, + const Sentinel2 end_2, + Comparator cmp ) { auto cursor = first_1; while ( cursor != end_1 ) { if ( find_sentinel( first_1, cursor, *cursor, cmp ) == @@ -102,11 +105,11 @@ namespace Catch { typename ForwardIter2, typename Sentinel2, typename Comparator> - bool is_permutation( ForwardIter1 first_1, - const Sentinel1 end_1, - ForwardIter2 first_2, - const Sentinel2 end_2, - Comparator cmp ) { + constexpr bool is_permutation( ForwardIter1 first_1, + const Sentinel1 end_1, + ForwardIter2 first_2, + const Sentinel2 end_2, + Comparator cmp ) { // TODO: no optimization for stronger iterators, because we would also have to constrain on sentinel vs not sentinel types // TODO: Comparator has to be "both sides", e.g. a == b => b == a // This skips shared prefix of the two ranges diff --git a/src/external/catch2/src/catch2/internal/catch_istream.cpp b/src/external/catch2/src/catch2/internal/catch_istream.cpp index 489396ec..2867ce74 100644 --- a/src/external/catch2/src/catch2/internal/catch_istream.cpp +++ b/src/external/catch2/src/catch2/internal/catch_istream.cpp @@ -24,7 +24,7 @@ namespace Catch { namespace Detail { namespace { template - class StreamBufImpl : public std::streambuf { + class StreamBufImpl final : public std::streambuf { char data[bufferSize]; WriterF m_writer; @@ -72,7 +72,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class FileStream : public IStream { + class FileStream final : public IStream { std::ofstream m_ofs; public: FileStream( std::string const& filename ) { @@ -80,7 +80,6 @@ namespace Detail { CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << '\'' ); m_ofs << std::unitbuf; } - ~FileStream() override = default; public: // IStream std::ostream& stream() override { return m_ofs; @@ -89,13 +88,12 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class CoutStream : public IStream { + class CoutStream final : public IStream { std::ostream m_os; public: // Store the streambuf from cout up-front because // cout may get redirected when running tests CoutStream() : m_os( Catch::cout().rdbuf() ) {} - ~CoutStream() override = default; public: // IStream std::ostream& stream() override { return m_os; } @@ -109,7 +107,6 @@ namespace Detail { // Store the streambuf from cerr up-front because // cout may get redirected when running tests CerrStream(): m_os( Catch::cerr().rdbuf() ) {} - ~CerrStream() override = default; public: // IStream std::ostream& stream() override { return m_os; } @@ -118,7 +115,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class DebugOutStream : public IStream { + class DebugOutStream final : public IStream { Detail::unique_ptr> m_streamBuf; std::ostream m_os; public: @@ -127,8 +124,6 @@ namespace Detail { m_os( m_streamBuf.get() ) {} - ~DebugOutStream() override = default; - public: // IStream std::ostream& stream() override { return m_os; } }; diff --git a/src/external/catch2/src/catch2/internal/catch_jsonwriter.cpp b/src/external/catch2/src/catch2/internal/catch_jsonwriter.cpp new file mode 100644 index 00000000..1a96e348 --- /dev/null +++ b/src/external/catch2/src/catch2/internal/catch_jsonwriter.cpp @@ -0,0 +1,148 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +#include +#include + +namespace Catch { + void JsonUtils::indent( std::ostream& os, std::uint64_t level ) { + for ( std::uint64_t i = 0; i < level; ++i ) { + os << " "; + } + } + void JsonUtils::appendCommaNewline( std::ostream& os, + bool& should_comma, + std::uint64_t level ) { + if ( should_comma ) { os << ','; } + should_comma = true; + os << '\n'; + indent( os, level ); + } + + JsonObjectWriter::JsonObjectWriter( std::ostream& os ): + JsonObjectWriter{ os, 0 } {} + + JsonObjectWriter::JsonObjectWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } { + m_os << '{'; + } + JsonObjectWriter::JsonObjectWriter( JsonObjectWriter&& source ) noexcept: + m_os{ source.m_os }, + m_indent_level{ source.m_indent_level }, + m_should_comma{ source.m_should_comma }, + m_active{ source.m_active } { + source.m_active = false; + } + + JsonObjectWriter::~JsonObjectWriter() { + if ( !m_active ) { return; } + + m_os << '\n'; + JsonUtils::indent( m_os, m_indent_level ); + m_os << '}'; + } + + JsonValueWriter JsonObjectWriter::write( StringRef key ) { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + + m_os << '"' << key << "\": "; + return JsonValueWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter::JsonArrayWriter( std::ostream& os ): + JsonArrayWriter{ os, 0 } {} + JsonArrayWriter::JsonArrayWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } { + m_os << '['; + } + JsonArrayWriter::JsonArrayWriter( JsonArrayWriter&& source ) noexcept: + m_os{ source.m_os }, + m_indent_level{ source.m_indent_level }, + m_should_comma{ source.m_should_comma }, + m_active{ source.m_active } { + source.m_active = false; + } + JsonArrayWriter::~JsonArrayWriter() { + if ( !m_active ) { return; } + + m_os << '\n'; + JsonUtils::indent( m_os, m_indent_level ); + m_os << ']'; + } + + JsonObjectWriter JsonArrayWriter::writeObject() { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + return JsonObjectWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter JsonArrayWriter::writeArray() { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + return JsonArrayWriter{ m_os, m_indent_level + 1 }; + } + + JsonArrayWriter& JsonArrayWriter::write( bool value ) { + return writeImpl( value ); + } + + JsonValueWriter::JsonValueWriter( std::ostream& os ): + JsonValueWriter{ os, 0 } {} + + JsonValueWriter::JsonValueWriter( std::ostream& os, + std::uint64_t indent_level ): + m_os{ os }, m_indent_level{ indent_level } {} + + JsonObjectWriter JsonValueWriter::writeObject() && { + return JsonObjectWriter{ m_os, m_indent_level }; + } + + JsonArrayWriter JsonValueWriter::writeArray() && { + return JsonArrayWriter{ m_os, m_indent_level }; + } + + void JsonValueWriter::write( Catch::StringRef value ) && { + writeImpl( value, true ); + } + + void JsonValueWriter::write( bool value ) && { + writeImpl( value ? "true"_sr : "false"_sr, false ); + } + + void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) { + if ( quote ) { m_os << '"'; } + for (char c : value) { + // Escape list taken from https://www.json.org/json-en.html, + // string definition. + // Note that while forward slash _can_ be escaped, it does + // not have to be, if JSON is not further embedded somewhere + // where forward slash is meaningful. + if ( c == '"' ) { + m_os << "\\\""; + } else if ( c == '\\' ) { + m_os << "\\\\"; + } else if ( c == '\b' ) { + m_os << "\\b"; + } else if ( c == '\f' ) { + m_os << "\\f"; + } else if ( c == '\n' ) { + m_os << "\\n"; + } else if ( c == '\r' ) { + m_os << "\\r"; + } else if ( c == '\t' ) { + m_os << "\\t"; + } else { + m_os << c; + } + } + if ( quote ) { m_os << '"'; } + } + +} // namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_jsonwriter.hpp b/src/external/catch2/src/catch2/internal/catch_jsonwriter.hpp new file mode 100644 index 00000000..23b56d13 --- /dev/null +++ b/src/external/catch2/src/catch2/internal/catch_jsonwriter.hpp @@ -0,0 +1,120 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +#ifndef CATCH_JSONWRITER_HPP_INCLUDED +#define CATCH_JSONWRITER_HPP_INCLUDED + +#include +#include + +#include +#include + +namespace Catch { + class JsonObjectWriter; + class JsonArrayWriter; + + struct JsonUtils { + static void indent( std::ostream& os, std::uint64_t level ); + static void appendCommaNewline( std::ostream& os, + bool& should_comma, + std::uint64_t level ); + }; + + class JsonValueWriter { + public: + JsonValueWriter( std::ostream& os ); + JsonValueWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonObjectWriter writeObject() &&; + JsonArrayWriter writeArray() &&; + + template + void write( T const& value ) && { + writeImpl( value, !std::is_arithmetic::value ); + } + void write( StringRef value ) &&; + void write( bool value ) &&; + + private: + void writeImpl( StringRef value, bool quote ); + + // Without this SFINAE, this overload is a better match + // for `std::string`, `char const*`, `char const[N]` args. + // While it would still work, it would cause code bloat + // and multiple iteration over the strings + template ::value>> + void writeImpl( T const& value, bool quote_value ) { + m_sstream << value; + writeImpl( m_sstream.str(), quote_value ); + } + + std::ostream& m_os; + std::stringstream m_sstream; + std::uint64_t m_indent_level; + }; + + class JsonObjectWriter { + public: + JsonObjectWriter( std::ostream& os ); + JsonObjectWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonObjectWriter( JsonObjectWriter&& source ) noexcept; + JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete; + + ~JsonObjectWriter(); + + JsonValueWriter write( StringRef key ); + + private: + std::ostream& m_os; + std::uint64_t m_indent_level; + bool m_should_comma = false; + bool m_active = true; + }; + + class JsonArrayWriter { + public: + JsonArrayWriter( std::ostream& os ); + JsonArrayWriter( std::ostream& os, std::uint64_t indent_level ); + + JsonArrayWriter( JsonArrayWriter&& source ) noexcept; + JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete; + + ~JsonArrayWriter(); + + JsonObjectWriter writeObject(); + JsonArrayWriter writeArray(); + + template + JsonArrayWriter& write( T const& value ) { + return writeImpl( value ); + } + + JsonArrayWriter& write( bool value ); + + private: + template + JsonArrayWriter& writeImpl( T const& value ) { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1 ); + JsonValueWriter{ m_os }.write( value ); + + return *this; + } + + std::ostream& m_os; + std::uint64_t m_indent_level; + bool m_should_comma = false; + bool m_active = true; + }; + +} // namespace Catch + +#endif // CATCH_JSONWRITER_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_lazy_expr.hpp b/src/external/catch2/src/catch2/internal/catch_lazy_expr.hpp index 36e0ac50..c6ff2241 100644 --- a/src/external/catch2/src/catch2/internal/catch_lazy_expr.hpp +++ b/src/external/catch2/src/catch2/internal/catch_lazy_expr.hpp @@ -22,13 +22,13 @@ namespace Catch { ITransientExpression const* m_transientExpression = nullptr; bool m_isNegated; public: - LazyExpression( bool isNegated ): + constexpr LazyExpression( bool isNegated ): m_isNegated(isNegated) {} - LazyExpression(LazyExpression const& other) = default; + constexpr LazyExpression(LazyExpression const& other) = default; LazyExpression& operator = ( LazyExpression const& ) = delete; - explicit operator bool() const { + constexpr explicit operator bool() const { return m_transientExpression != nullptr; } diff --git a/src/external/catch2/src/catch2/internal/catch_leak_detector.cpp b/src/external/catch2/src/catch2/internal/catch_leak_detector.cpp index 7389eaf7..691bc772 100644 --- a/src/external/catch2/src/catch2/internal/catch_leak_detector.cpp +++ b/src/external/catch2/src/catch2/internal/catch_leak_detector.cpp @@ -29,7 +29,7 @@ namespace Catch { #else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv - Catch::LeakDetector::LeakDetector() {} + Catch::LeakDetector::LeakDetector() = default; #endif // CATCH_CONFIG_WINDOWS_CRTDBG diff --git a/src/external/catch2/src/catch2/internal/catch_list.cpp b/src/external/catch2/src/catch2/internal/catch_list.cpp index 263781d6..5bd06a2a 100644 --- a/src/external/catch2/src/catch2/internal/catch_list.cpp +++ b/src/external/catch2/src/catch2/internal/catch_list.cpp @@ -9,15 +9,12 @@ #include #include -#include -#include #include +#include +#include #include #include - -#include #include -#include #include namespace Catch { @@ -54,7 +51,7 @@ namespace Catch { void listReporters(IEventListener& reporter) { std::vector descriptions; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); descriptions.reserve(factories.size()); for (auto const& fac : factories) { descriptions.push_back({ fac.first, fac.second->getDescription() }); diff --git a/src/external/catch2/src/catch2/internal/catch_message_info.hpp b/src/external/catch2/src/catch2/internal/catch_message_info.hpp index d2658429..1ef43fda 100644 --- a/src/external/catch2/src/catch2/internal/catch_message_info.hpp +++ b/src/external/catch2/src/catch2/internal/catch_message_info.hpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include diff --git a/src/external/catch2/src/catch2/internal/catch_optional.hpp b/src/external/catch2/src/catch2/internal/catch_optional.hpp index ac3714ee..d1e953ad 100644 --- a/src/external/catch2/src/catch2/internal/catch_optional.hpp +++ b/src/external/catch2/src/catch2/internal/catch_optional.hpp @@ -8,6 +8,8 @@ #ifndef CATCH_OPTIONAL_HPP_INCLUDED #define CATCH_OPTIONAL_HPP_INCLUDED +#include + #include namespace Catch { @@ -16,35 +18,50 @@ namespace Catch { template class Optional { public: - Optional() : nullableValue( nullptr ) {} - Optional( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Optional( Optional const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) - {} - - ~Optional() { + Optional(): nullableValue( nullptr ) {} + ~Optional() { reset(); } + + Optional( T const& _value ): + nullableValue( new ( storage ) T( _value ) ) {} + Optional( T&& _value ): + nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {} + + Optional& operator=( T const& _value ) { reset(); + nullableValue = new ( storage ) T( _value ); + return *this; } + Optional& operator=( T&& _value ) { + reset(); + nullableValue = new ( storage ) T( CATCH_MOVE( _value ) ); + return *this; + } + + Optional( Optional const& _other ): + nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {} + Optional( Optional&& _other ): + nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) ) + : nullptr ) {} - Optional& operator= ( Optional const& _other ) { - if( &_other != this ) { + Optional& operator=( Optional const& _other ) { + if ( &_other != this ) { reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); + if ( _other ) { nullableValue = new ( storage ) T( *_other ); } } return *this; } - Optional& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); + Optional& operator=( Optional&& _other ) { + if ( &_other != this ) { + reset(); + if ( _other ) { + nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) ); + } + } return *this; } void reset() { - if( nullableValue ) - nullableValue->~T(); + if ( nullableValue ) { nullableValue->~T(); } nullableValue = nullptr; } @@ -91,7 +108,7 @@ namespace Catch { } private: - T *nullableValue; + T* nullableValue; alignas(alignof(T)) char storage[sizeof(T)]; }; diff --git a/src/external/catch2/src/catch2/internal/catch_output_redirect.cpp b/src/external/catch2/src/catch2/internal/catch_output_redirect.cpp index 02f7c982..245e1376 100644 --- a/src/external/catch2/src/catch2/internal/catch_output_redirect.cpp +++ b/src/external/catch2/src/catch2/internal/catch_output_redirect.cpp @@ -5,142 +5,335 @@ // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 -#include +#include #include +#include +#include +#include #include #include #include +#include #include -#if defined(CATCH_CONFIG_NEW_CAPTURE) - #if defined(_MSC_VER) - #include //_dup and _dup2 - #define dup _dup - #define dup2 _dup2 - #define fileno _fileno - #else - #include // dup and dup2 - #endif +#if defined( CATCH_CONFIG_NEW_CAPTURE ) +# if defined( _MSC_VER ) +# include //_dup and _dup2 +# define dup _dup +# define dup2 _dup2 +# define fileno _fileno +# else +# include // dup and dup2 +# endif #endif - namespace Catch { - RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) - : m_originalStream( originalStream ), - m_redirectionStream( redirectionStream ), - m_prevBuf( m_originalStream.rdbuf() ) - { - m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); - } + namespace { + //! A no-op implementation, used if no reporter wants output + //! redirection. + class NoopRedirect : public OutputRedirect { + void activateImpl() override {} + void deactivateImpl() override {} + std::string getStdout() override { return {}; } + std::string getStderr() override { return {}; } + void clearBuffers() override {} + }; - RedirectedStream::~RedirectedStream() { - m_originalStream.rdbuf( m_prevBuf ); - } + /** + * Redirects specific stream's rdbuf with another's. + * + * Redirection can be stopped and started on-demand, assumes + * that the underlying stream's rdbuf aren't changed by other + * users. + */ + class RedirectedStreamNew { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; - RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} - auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + public: + RedirectedStreamNew( std::ostream& originalStream, + std::ostream& redirectionStream ): + m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) {} - RedirectedStdErr::RedirectedStdErr() - : m_cerr( Catch::cerr(), m_rss.get() ), - m_clog( Catch::clog(), m_rss.get() ) - {} - auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + void startRedirect() { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + void stopRedirect() { m_originalStream.rdbuf( m_prevBuf ); } + }; - RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr) - : m_redirectedCout(redirectedCout), - m_redirectedCerr(redirectedCerr) - {} + /** + * Redirects the `std::cout`, `std::cerr`, `std::clog` streams, + * but does not touch the actual `stdout`/`stderr` file descriptors. + */ + class StreamRedirect : public OutputRedirect { + ReusableStringStream m_redirectedOut, m_redirectedErr; + RedirectedStreamNew m_cout, m_cerr, m_clog; - RedirectedStreams::~RedirectedStreams() { - m_redirectedCout += m_redirectedStdOut.str(); - m_redirectedCerr += m_redirectedStdErr.str(); - } + public: + StreamRedirect(): + m_cout( Catch::cout(), m_redirectedOut.get() ), + m_cerr( Catch::cerr(), m_redirectedErr.get() ), + m_clog( Catch::clog(), m_redirectedErr.get() ) {} -#if defined(CATCH_CONFIG_NEW_CAPTURE) + void activateImpl() override { + m_cout.startRedirect(); + m_cerr.startRedirect(); + m_clog.startRedirect(); + } + void deactivateImpl() override { + m_cout.stopRedirect(); + m_cerr.stopRedirect(); + m_clog.stopRedirect(); + } + std::string getStdout() override { return m_redirectedOut.str(); } + std::string getStderr() override { return m_redirectedErr.str(); } + void clearBuffers() override { + m_redirectedOut.str( "" ); + m_redirectedErr.str( "" ); + } + }; -#if defined(_MSC_VER) - TempFile::TempFile() { - if (tmpnam_s(m_buffer)) { - CATCH_RUNTIME_ERROR("Could not get a temp filename"); - } - if (fopen_s(&m_file, m_buffer, "w+")) { - char buffer[100]; - if (strerror_s(buffer, errno)) { - CATCH_RUNTIME_ERROR("Could not translate errno to a string"); +#if defined( CATCH_CONFIG_NEW_CAPTURE ) + + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile( TempFile const& ) = delete; + TempFile& operator=( TempFile const& ) = delete; + TempFile( TempFile&& ) = delete; + TempFile& operator=( TempFile&& ) = delete; + +# if defined( _MSC_VER ) + TempFile() { + if ( tmpnam_s( m_buffer ) ) { + CATCH_RUNTIME_ERROR( "Could not get a temp filename" ); + } + if ( fopen_s( &m_file, m_buffer, "wb+" ) ) { + char buffer[100]; + if ( strerror_s( buffer, errno ) ) { + CATCH_RUNTIME_ERROR( + "Could not translate errno to a string" ); + } + CATCH_RUNTIME_ERROR( "Could not open the temp file: '" + << m_buffer + << "' because: " << buffer ); + } + } +# else + TempFile() { + m_file = std::tmpfile(); + if ( !m_file ) { + CATCH_RUNTIME_ERROR( "Could not create a temp file." ); + } + } +# endif + + ~TempFile() { + // TBD: What to do about errors here? + std::fclose( m_file ); + // We manually create the file on Windows only, on Linux + // it will be autodeleted +# if defined( _MSC_VER ) + std::remove( m_buffer ); +# endif + } + + std::FILE* getFile() { return m_file; } + std::string getContents() { + ReusableStringStream sstr; + constexpr long buffer_size = 100; + char buffer[buffer_size + 1] = {}; + long current_pos = ftell( m_file ); + CATCH_ENFORCE( current_pos >= 0, + "ftell failed, errno: " << errno ); + std::rewind( m_file ); + while ( current_pos > 0 ) { + auto read_characters = + std::fread( buffer, + 1, + std::min( buffer_size, current_pos ), + m_file ); + buffer[read_characters] = '\0'; + sstr << buffer; + current_pos -= static_cast( read_characters ); + } + return sstr.str(); + } + + void clear() { std::rewind( m_file ); } + + private: + std::FILE* m_file = nullptr; + char m_buffer[L_tmpnam] = { 0 }; + }; + + /** + * Redirects the actual `stdout`/`stderr` file descriptors. + * + * Works by replacing the file descriptors numbered 1 and 2 + * with an open temporary file. + */ + class FileRedirect : public OutputRedirect { + TempFile m_outFile, m_errFile; + int m_originalOut = -1; + int m_originalErr = -1; + + // Flushes cout/cerr/clog streams and stdout/stderr FDs + void flushEverything() { + Catch::cout() << std::flush; + fflush( stdout ); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush( stderr ); + } + + public: + FileRedirect(): + m_originalOut( dup( fileno( stdout ) ) ), + m_originalErr( dup( fileno( stderr ) ) ) { + CATCH_ENFORCE( m_originalOut >= 0, "Could not dup stdout" ); + CATCH_ENFORCE( m_originalErr >= 0, "Could not dup stderr" ); } - CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); + + std::string getStdout() override { return m_outFile.getContents(); } + std::string getStderr() override { return m_errFile.getContents(); } + void clearBuffers() override { + m_outFile.clear(); + m_errFile.clear(); + } + + void activateImpl() override { + // We flush before starting redirect, to ensure that we do + // not capture the end of message sent before activation. + flushEverything(); + + int ret; + ret = dup2( fileno( m_outFile.getFile() ), fileno( stdout ) ); + CATCH_ENFORCE( ret >= 0, + "dup2 to stdout has failed, errno: " << errno ); + ret = dup2( fileno( m_errFile.getFile() ), fileno( stderr ) ); + CATCH_ENFORCE( ret >= 0, + "dup2 to stderr has failed, errno: " << errno ); + } + void deactivateImpl() override { + // We flush before ending redirect, to ensure that we + // capture all messages sent while the redirect was active. + flushEverything(); + + int ret; + ret = dup2( m_originalOut, fileno( stdout ) ); + CATCH_ENFORCE( + ret >= 0, + "dup2 of original stdout has failed, errno: " << errno ); + ret = dup2( m_originalErr, fileno( stderr ) ); + CATCH_ENFORCE( + ret >= 0, + "dup2 of original stderr has failed, errno: " << errno ); + } + }; + +#endif // CATCH_CONFIG_NEW_CAPTURE + + } // end namespace + + bool isRedirectAvailable( OutputRedirect::Kind kind ) { + switch ( kind ) { + // These two are always available + case OutputRedirect::None: + case OutputRedirect::Streams: + return true; +#if defined( CATCH_CONFIG_NEW_CAPTURE ) + case OutputRedirect::FileDescriptors: + return true; +#endif + default: + return false; } } + + Detail::unique_ptr makeOutputRedirect( bool actual ) { + if ( actual ) { + // TODO: Clean this up later +#if defined( CATCH_CONFIG_NEW_CAPTURE ) + return Detail::make_unique(); #else - TempFile::TempFile() { - m_file = std::tmpfile(); - if (!m_file) { - CATCH_RUNTIME_ERROR("Could not create a temp file."); + return Detail::make_unique(); +#endif + } else { + return Detail::make_unique(); } } -#endif + RedirectGuard scopedActivate( OutputRedirect& redirectImpl ) { + return RedirectGuard( true, redirectImpl ); + } - TempFile::~TempFile() { - // TBD: What to do about errors here? - std::fclose(m_file); - // We manually create the file on Windows only, on Linux - // it will be autodeleted -#if defined(_MSC_VER) - std::remove(m_buffer); -#endif + RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl ) { + return RedirectGuard( false, redirectImpl ); } + OutputRedirect::~OutputRedirect() = default; - FILE* TempFile::getFile() { - return m_file; - } + RedirectGuard::RedirectGuard( bool activate, OutputRedirect& redirectImpl ): + m_redirect( &redirectImpl ), + m_activate( activate ), + m_previouslyActive( redirectImpl.isActive() ) { - std::string TempFile::getContents() { - std::stringstream sstr; - char buffer[100] = {}; - std::rewind(m_file); - while (std::fgets(buffer, sizeof(buffer), m_file)) { - sstr << buffer; - } - return sstr.str(); - } + // Skip cases where there is no actual state change. + if ( m_activate == m_previouslyActive ) { return; } - OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : - m_originalStdout(dup(1)), - m_originalStderr(dup(2)), - m_stdoutDest(stdout_dest), - m_stderrDest(stderr_dest) { - dup2(fileno(m_stdoutFile.getFile()), 1); - dup2(fileno(m_stderrFile.getFile()), 2); + if ( m_activate ) { + m_redirect->activate(); + } else { + m_redirect->deactivate(); + } } - OutputRedirect::~OutputRedirect() { - Catch::cout() << std::flush; - fflush(stdout); - // Since we support overriding these streams, we flush cerr - // even though std::cerr is unbuffered - Catch::cerr() << std::flush; - Catch::clog() << std::flush; - fflush(stderr); + RedirectGuard::~RedirectGuard() noexcept( false ) { + if ( m_moved ) { return; } + // Skip cases where there is no actual state change. + if ( m_activate == m_previouslyActive ) { return; } - dup2(m_originalStdout, 1); - dup2(m_originalStderr, 2); + if ( m_activate ) { + m_redirect->deactivate(); + } else { + m_redirect->activate(); + } + } - m_stdoutDest += m_stdoutFile.getContents(); - m_stderrDest += m_stderrFile.getContents(); + RedirectGuard::RedirectGuard( RedirectGuard&& rhs ) noexcept: + m_redirect( rhs.m_redirect ), + m_activate( rhs.m_activate ), + m_previouslyActive( rhs.m_previouslyActive ), + m_moved( false ) { + rhs.m_moved = true; } -#endif // CATCH_CONFIG_NEW_CAPTURE + RedirectGuard& RedirectGuard::operator=( RedirectGuard&& rhs ) noexcept { + m_redirect = rhs.m_redirect; + m_activate = rhs.m_activate; + m_previouslyActive = rhs.m_previouslyActive; + m_moved = false; + rhs.m_moved = true; + return *this; + } } // namespace Catch -#if defined(CATCH_CONFIG_NEW_CAPTURE) - #if defined(_MSC_VER) - #undef dup - #undef dup2 - #undef fileno - #endif +#if defined( CATCH_CONFIG_NEW_CAPTURE ) +# if defined( _MSC_VER ) +# undef dup +# undef dup2 +# undef fileno +# endif #endif diff --git a/src/external/catch2/src/catch2/internal/catch_output_redirect.hpp b/src/external/catch2/src/catch2/internal/catch_output_redirect.hpp index dc89223b..51b796ba 100644 --- a/src/external/catch2/src/catch2/internal/catch_output_redirect.hpp +++ b/src/external/catch2/src/catch2/internal/catch_output_redirect.hpp @@ -8,110 +8,69 @@ #ifndef CATCH_OUTPUT_REDIRECT_HPP_INCLUDED #define CATCH_OUTPUT_REDIRECT_HPP_INCLUDED -#include -#include -#include +#include -#include -#include +#include #include namespace Catch { - class RedirectedStream { - std::ostream& m_originalStream; - std::ostream& m_redirectionStream; - std::streambuf* m_prevBuf; - - public: - RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ); - ~RedirectedStream(); - }; - - class RedirectedStdOut { - ReusableStringStream m_rss; - RedirectedStream m_cout; - public: - RedirectedStdOut(); - auto str() const -> std::string; - }; - - // StdErr has two constituent streams in C++, std::cerr and std::clog - // This means that we need to redirect 2 streams into 1 to keep proper - // order of writes - class RedirectedStdErr { - ReusableStringStream m_rss; - RedirectedStream m_cerr; - RedirectedStream m_clog; + class OutputRedirect { + bool m_redirectActive = false; + virtual void activateImpl() = 0; + virtual void deactivateImpl() = 0; public: - RedirectedStdErr(); - auto str() const -> std::string; + enum Kind { + //! No redirect (noop implementation) + None, + //! Redirect std::cout/std::cerr/std::clog streams internally + Streams, + //! Redirect the stdout/stderr file descriptors into files + FileDescriptors, + }; + + virtual ~OutputRedirect(); // = default; + + // TODO: Do we want to check that redirect is not active before retrieving the output? + virtual std::string getStdout() = 0; + virtual std::string getStderr() = 0; + virtual void clearBuffers() = 0; + bool isActive() const { return m_redirectActive; } + void activate() { + assert( !m_redirectActive && "redirect is already active" ); + activateImpl(); + m_redirectActive = true; + } + void deactivate() { + assert( m_redirectActive && "redirect is not active" ); + deactivateImpl(); + m_redirectActive = false; + } }; - class RedirectedStreams { - public: - RedirectedStreams(RedirectedStreams const&) = delete; - RedirectedStreams& operator=(RedirectedStreams const&) = delete; - RedirectedStreams(RedirectedStreams&&) = delete; - RedirectedStreams& operator=(RedirectedStreams&&) = delete; - - RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr); - ~RedirectedStreams(); - private: - std::string& m_redirectedCout; - std::string& m_redirectedCerr; - RedirectedStdOut m_redirectedStdOut; - RedirectedStdErr m_redirectedStdErr; - }; + bool isRedirectAvailable( OutputRedirect::Kind kind); + Detail::unique_ptr makeOutputRedirect( bool actual ); -#if defined(CATCH_CONFIG_NEW_CAPTURE) + class RedirectGuard { + OutputRedirect* m_redirect; + bool m_activate; + bool m_previouslyActive; + bool m_moved = false; - // Windows's implementation of std::tmpfile is terrible (it tries - // to create a file inside system folder, thus requiring elevated - // privileges for the binary), so we have to use tmpnam(_s) and - // create the file ourselves there. - class TempFile { public: - TempFile(TempFile const&) = delete; - TempFile& operator=(TempFile const&) = delete; - TempFile(TempFile&&) = delete; - TempFile& operator=(TempFile&&) = delete; - - TempFile(); - ~TempFile(); - - std::FILE* getFile(); - std::string getContents(); - - private: - std::FILE* m_file = nullptr; - #if defined(_MSC_VER) - char m_buffer[L_tmpnam] = { 0 }; - #endif - }; + RedirectGuard( bool activate, OutputRedirect& redirectImpl ); + ~RedirectGuard() noexcept( false ); + RedirectGuard( RedirectGuard const& ) = delete; + RedirectGuard& operator=( RedirectGuard const& ) = delete; - class OutputRedirect { - public: - OutputRedirect(OutputRedirect const&) = delete; - OutputRedirect& operator=(OutputRedirect const&) = delete; - OutputRedirect(OutputRedirect&&) = delete; - OutputRedirect& operator=(OutputRedirect&&) = delete; - - - OutputRedirect(std::string& stdout_dest, std::string& stderr_dest); - ~OutputRedirect(); - - private: - int m_originalStdout = -1; - int m_originalStderr = -1; - TempFile m_stdoutFile; - TempFile m_stderrFile; - std::string& m_stdoutDest; - std::string& m_stderrDest; + // C++14 needs move-able guards to return them from functions + RedirectGuard( RedirectGuard&& rhs ) noexcept; + RedirectGuard& operator=( RedirectGuard&& rhs ) noexcept; }; -#endif + RedirectGuard scopedActivate( OutputRedirect& redirectImpl ); + RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl ); } // end namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_platform.hpp b/src/external/catch2/src/catch2/internal/catch_platform.hpp index b11d9ccd..b653a58c 100644 --- a/src/external/catch2/src/catch2/internal/catch_platform.hpp +++ b/src/external/catch2/src/catch2/internal/catch_platform.hpp @@ -11,6 +11,9 @@ // See e.g.: // https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ +# ifndef __has_extension +# define __has_extension(x) 0 +# endif # include # if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) diff --git a/src/external/catch2/src/catch2/internal/catch_polyfills.cpp b/src/external/catch2/src/catch2/internal/catch_polyfills.cpp index 96efad5d..776c2243 100644 --- a/src/external/catch2/src/catch2/internal/catch_polyfills.cpp +++ b/src/external/catch2/src/catch2/internal/catch_polyfills.cpp @@ -31,4 +31,12 @@ namespace Catch { } #endif +#if !defined( CATCH_CONFIG_GLOBAL_NEXTAFTER ) + float nextafter( float x, float y ) { return std::nextafter( x, y ); } + double nextafter( double x, double y ) { return std::nextafter( x, y ); } +#else + float nextafter( float x, float y ) { return ::nextafterf( x, y ); } + double nextafter( double x, double y ) { return ::nextafter( x, y ); } +#endif + } // end namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_polyfills.hpp b/src/external/catch2/src/catch2/internal/catch_polyfills.hpp index 23a9332b..4503f8f2 100644 --- a/src/external/catch2/src/catch2/internal/catch_polyfills.hpp +++ b/src/external/catch2/src/catch2/internal/catch_polyfills.hpp @@ -9,8 +9,13 @@ #define CATCH_POLYFILLS_HPP_INCLUDED namespace Catch { + bool isnan(float f); bool isnan(double d); + + float nextafter(float x, float y); + double nextafter(double x, double y); + } #endif // CATCH_POLYFILLS_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_preprocessor_internal_stringify.hpp b/src/external/catch2/src/catch2/internal/catch_preprocessor_internal_stringify.hpp new file mode 100644 index 00000000..2fd64e1c --- /dev/null +++ b/src/external/catch2/src/catch2/internal/catch_preprocessor_internal_stringify.hpp @@ -0,0 +1,19 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +#ifndef CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED +#define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED + +#include + +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr +#else + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr +#endif + +#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_random_floating_point_helpers.hpp b/src/external/catch2/src/catch2/internal/catch_random_floating_point_helpers.hpp new file mode 100644 index 00000000..c59c0539 --- /dev/null +++ b/src/external/catch2/src/catch2/internal/catch_random_floating_point_helpers.hpp @@ -0,0 +1,94 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED +#define CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED + +#include + +#include +#include +#include +#include +#include + +namespace Catch { + + namespace Detail { + /** + * Returns the largest magnitude of 1-ULP distance inside the [a, b] range. + * + * Assumes `a < b`. + */ + template + FloatType gamma(FloatType a, FloatType b) { + static_assert( std::is_floating_point::value, + "gamma returns the largest ULP magnitude within " + "floating point range [a, b]. This only makes sense " + "for floating point types" ); + assert( a <= b ); + + const auto gamma_up = Catch::nextafter( a, std::numeric_limits::infinity() ) - a; + const auto gamma_down = b - Catch::nextafter( b, -std::numeric_limits::infinity() ); + + return gamma_up < gamma_down ? gamma_down : gamma_up; + } + + template + struct DistanceTypePicker; + template <> + struct DistanceTypePicker { + using type = std::uint32_t; + }; + template <> + struct DistanceTypePicker { + using type = std::uint64_t; + }; + + template + using DistanceType = typename DistanceTypePicker::type; + +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + /** + * Computes the number of equi-distant floats in [a, b] + * + * Since not every range can be split into equidistant floats + * exactly, we actually compute ceil(b/distance - a/distance), + * because in those cases we want to overcount. + * + * Uses modified Dekker's FastTwoSum algorithm to handle rounding. + */ + template + DistanceType + count_equidistant_floats( FloatType a, FloatType b, FloatType distance ) { + assert( a <= b ); + // We get distance as gamma for our uniform float distribution, + // so this will round perfectly. + const auto ag = a / distance; + const auto bg = b / distance; + + const auto s = bg - ag; + const auto err = ( std::fabs( a ) <= std::fabs( b ) ) + ? -ag - ( s - bg ) + : bg - ( s + ag ); + const auto ceil_s = static_cast>( std::ceil( s ) ); + + return ( ceil_s != s ) ? ceil_s : ceil_s + ( err > 0 ); + } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + + } + +} // end namespace Catch + +#endif // CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_random_integer_helpers.hpp b/src/external/catch2/src/catch2/internal/catch_random_integer_helpers.hpp new file mode 100644 index 00000000..be4bbe92 --- /dev/null +++ b/src/external/catch2/src/catch2/internal/catch_random_integer_helpers.hpp @@ -0,0 +1,224 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED +#define CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED + +#include +#include +#include +#include + +// Note: We use the usual enable-disable-autodetect dance here even though +// we do not support these in CMake configuration options (yet?). +// It is highly unlikely that we will need to make these actually +// user-configurable, but this will make it simpler if weend up needing +// it, and it provides an escape hatch to the users who need it. +#if defined( __SIZEOF_INT128__ ) +# define CATCH_CONFIG_INTERNAL_UINT128 +// Unlike GCC, MSVC does not polyfill umul as mulh + mul pair on ARM machines. +// Currently we do not bother doing this ourselves, but we could if it became +// important for perf. +#elif defined( _MSC_VER ) && defined( _M_X64 ) +# define CATCH_CONFIG_INTERNAL_MSVC_UMUL128 +#endif + +#if defined( CATCH_CONFIG_INTERNAL_UINT128 ) && \ + !defined( CATCH_CONFIG_NO_UINT128 ) && \ + !defined( CATCH_CONFIG_UINT128 ) +#define CATCH_CONFIG_UINT128 +#endif + +#if defined( CATCH_CONFIG_INTERNAL_MSVC_UMUL128 ) && \ + !defined( CATCH_CONFIG_NO_MSVC_UMUL128 ) && \ + !defined( CATCH_CONFIG_MSVC_UMUL128 ) +# define CATCH_CONFIG_MSVC_UMUL128 +# include +#endif + + +namespace Catch { + namespace Detail { + + template + struct SizedUnsignedType; +#define SizedUnsignedTypeHelper( TYPE ) \ + template <> \ + struct SizedUnsignedType { \ + using type = TYPE; \ + } + + SizedUnsignedTypeHelper( std::uint8_t ); + SizedUnsignedTypeHelper( std::uint16_t ); + SizedUnsignedTypeHelper( std::uint32_t ); + SizedUnsignedTypeHelper( std::uint64_t ); +#undef SizedUnsignedTypeHelper + + template + using SizedUnsignedType_t = typename SizedUnsignedType::type; + + template + using DoubleWidthUnsignedType_t = SizedUnsignedType_t<2 * sizeof( T )>; + + template + struct ExtendedMultResult { + T upper; + T lower; + constexpr bool operator==( ExtendedMultResult const& rhs ) const { + return upper == rhs.upper && lower == rhs.lower; + } + }; + + /** + * Returns 128 bit result of lhs * rhs using portable C++ code + * + * This implementation is almost twice as fast as naive long multiplication, + * and unlike intrinsic-based approach, it supports constexpr evaluation. + */ + constexpr ExtendedMultResult + extendedMultPortable(std::uint64_t lhs, std::uint64_t rhs) { +#define CarryBits( x ) ( x >> 32 ) +#define Digits( x ) ( x & 0xFF'FF'FF'FF ) + std::uint64_t lhs_low = Digits( lhs ); + std::uint64_t rhs_low = Digits( rhs ); + std::uint64_t low_low = ( lhs_low * rhs_low ); + std::uint64_t high_high = CarryBits( lhs ) * CarryBits( rhs ); + + // We add in carry bits from low-low already + std::uint64_t high_low = + ( CarryBits( lhs ) * rhs_low ) + CarryBits( low_low ); + // Note that we can add only low bits from high_low, to avoid + // overflow with large inputs + std::uint64_t low_high = + ( lhs_low * CarryBits( rhs ) ) + Digits( high_low ); + + return { high_high + CarryBits( high_low ) + CarryBits( low_high ), + ( low_high << 32 ) | Digits( low_low ) }; +#undef CarryBits +#undef Digits + } + + //! Returns 128 bit result of lhs * rhs + inline ExtendedMultResult + extendedMult( std::uint64_t lhs, std::uint64_t rhs ) { +#if defined( CATCH_CONFIG_UINT128 ) + auto result = __uint128_t( lhs ) * __uint128_t( rhs ); + return { static_cast( result >> 64 ), + static_cast( result ) }; +#elif defined( CATCH_CONFIG_MSVC_UMUL128 ) + std::uint64_t high; + std::uint64_t low = _umul128( lhs, rhs, &high ); + return { high, low }; +#else + return extendedMultPortable( lhs, rhs ); +#endif + } + + + template + constexpr ExtendedMultResult extendedMult( UInt lhs, UInt rhs ) { + static_assert( std::is_unsigned::value, + "extendedMult can only handle unsigned integers" ); + static_assert( sizeof( UInt ) < sizeof( std::uint64_t ), + "Generic extendedMult can only handle types smaller " + "than uint64_t" ); + using WideType = DoubleWidthUnsignedType_t; + + auto result = WideType( lhs ) * WideType( rhs ); + return { + static_cast( result >> ( CHAR_BIT * sizeof( UInt ) ) ), + static_cast( result & UInt( -1 ) ) }; + } + + + template + std::enable_if_t= sizeof(TargetType), + TargetType> fillBitsFrom(Generator& gen) { + using gresult_type = typename Generator::result_type; + static_assert( std::is_unsigned::value, "Only unsigned integers are supported" ); + static_assert( Generator::min() == 0 && + Generator::max() == static_cast( -1 ), + "Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" ); + + // We want to return the top bits from a generator, as they are + // usually considered higher quality. + constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT; + constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT; + + return static_cast( gen() >> + ( generated_bits - return_bits) ); + } + + template + std::enable_if_t fillBitsFrom(Generator& gen) { + using gresult_type = typename Generator::result_type; + static_assert( std::is_unsigned::value, + "Only unsigned integers are supported" ); + static_assert( Generator::min() == 0 && + Generator::max() == static_cast( -1 ), + "Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" ); + + constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT; + constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT; + std::size_t filled_bits = 0; + TargetType ret = 0; + do { + ret <<= generated_bits; + ret |= gen(); + filled_bits += generated_bits; + } while ( filled_bits < return_bits ); + + return ret; + } + + /* + * Transposes numbers into unsigned type while keeping their ordering + * + * This means that signed types are changed so that the ordering is + * [INT_MIN, ..., -1, 0, ..., INT_MAX], rather than order we would + * get by simple casting ([0, ..., INT_MAX, INT_MIN, ..., -1]) + */ + template + constexpr + std::enable_if_t::value, UnsignedType> + transposeToNaturalOrder( UnsignedType in ) { + static_assert( + sizeof( OriginalType ) == sizeof( UnsignedType ), + "reordering requires the same sized types on both sides" ); + static_assert( std::is_unsigned::value, + "Input type must be unsigned" ); + // Assuming 2s complement (standardized in current C++), the + // positive and negative numbers are already internally ordered, + // and their difference is in the top bit. Swapping it orders + // them the desired way. + constexpr auto highest_bit = + UnsignedType( 1 ) << ( sizeof( UnsignedType ) * CHAR_BIT - 1 ); + return static_cast( in ^ highest_bit ); + } + + + + template + constexpr + std::enable_if_t::value, UnsignedType> + transposeToNaturalOrder(UnsignedType in) { + static_assert( + sizeof( OriginalType ) == sizeof( UnsignedType ), + "reordering requires the same sized types on both sides" ); + static_assert( std::is_unsigned::value, "Input type must be unsigned" ); + // No reordering is needed for unsigned -> unsigned + return in; + } + } // namespace Detail +} // namespace Catch + +#endif // CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_random_seed_generation.cpp b/src/external/catch2/src/catch2/internal/catch_random_seed_generation.cpp index 40c468cb..fdc3fa19 100644 --- a/src/external/catch2/src/catch2/internal/catch_random_seed_generation.cpp +++ b/src/external/catch2/src/catch2/internal/catch_random_seed_generation.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -21,10 +22,10 @@ namespace Catch { return static_cast( std::time( nullptr ) ); case GenerateFrom::Default: - case GenerateFrom::RandomDevice: - // In theory, a platform could have random_device that returns just - // 16 bits. That is still some randomness, so we don't care too much - return static_cast( std::random_device{}() ); + case GenerateFrom::RandomDevice: { + std::random_device rd; + return Detail::fillBitsFrom( rd ); + } default: CATCH_ERROR("Unknown generation method"); diff --git a/src/external/catch2/src/catch2/internal/catch_reporter_registry.cpp b/src/external/catch2/src/catch2/internal/catch_reporter_registry.cpp index 4c0c44f4..cea8c4dc 100644 --- a/src/external/catch2/src/catch2/internal/catch_reporter_registry.cpp +++ b/src/external/catch2/src/catch2/internal/catch_reporter_registry.cpp @@ -5,61 +5,87 @@ // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 -#include -#include +#include +#include +#include +#include #include #include #include +#include #include +#include #include #include #include #include -#include -#include namespace Catch { + struct ReporterRegistry::ReporterRegistryImpl { + std::vector> listeners; + std::map + factories; + }; - ReporterRegistry::ReporterRegistry() { + ReporterRegistry::ReporterRegistry(): + m_impl( Detail::make_unique() ) { // Because it is impossible to move out of initializer list, // we have to add the elements manually - m_factories["Automake"] = Detail::make_unique>(); - m_factories["compact"] = Detail::make_unique>(); - m_factories["console"] = Detail::make_unique>(); - m_factories["JUnit"] = Detail::make_unique>(); - m_factories["SonarQube"] = Detail::make_unique>(); - m_factories["TAP"] = Detail::make_unique>(); - m_factories["TeamCity"] = Detail::make_unique>(); - m_factories["XML"] = Detail::make_unique>(); + m_impl->factories["Automake"] = + Detail::make_unique>(); + m_impl->factories["compact"] = + Detail::make_unique>(); + m_impl->factories["console"] = + Detail::make_unique>(); + m_impl->factories["JUnit"] = + Detail::make_unique>(); + m_impl->factories["SonarQube"] = + Detail::make_unique>(); + m_impl->factories["TAP"] = + Detail::make_unique>(); + m_impl->factories["TeamCity"] = + Detail::make_unique>(); + m_impl->factories["XML"] = + Detail::make_unique>(); + m_impl->factories["JSON"] = + Detail::make_unique>(); } ReporterRegistry::~ReporterRegistry() = default; - - IEventListenerPtr ReporterRegistry::create( std::string const& name, ReporterConfig&& config ) const { - auto it = m_factories.find( name ); - if( it == m_factories.end() ) - return nullptr; - return it->second->create( CATCH_MOVE(config) ); + IEventListenerPtr + ReporterRegistry::create( std::string const& name, + ReporterConfig&& config ) const { + auto it = m_impl->factories.find( name ); + if ( it == m_impl->factories.end() ) return nullptr; + return it->second->create( CATCH_MOVE( config ) ); } - void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) { + void ReporterRegistry::registerReporter( std::string const& name, + IReporterFactoryPtr factory ) { CATCH_ENFORCE( name.find( "::" ) == name.npos, - "'::' is not allowed in reporter name: '" + name + '\'' ); - auto ret = m_factories.emplace(name, CATCH_MOVE(factory)); - CATCH_ENFORCE( ret.second, "reporter using '" + name + "' as name was already registered" ); + "'::' is not allowed in reporter name: '" + name + + '\'' ); + auto ret = m_impl->factories.emplace( name, CATCH_MOVE( factory ) ); + CATCH_ENFORCE( ret.second, + "reporter using '" + name + + "' as name was already registered" ); } void ReporterRegistry::registerListener( Detail::unique_ptr factory ) { - m_listeners.push_back( CATCH_MOVE(factory) ); + m_impl->listeners.push_back( CATCH_MOVE( factory ) ); } - IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { - return m_factories; - } - IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { - return m_listeners; + std::map const& + ReporterRegistry::getFactories() const { + return m_impl->factories; } -} + std::vector> const& + ReporterRegistry::getListeners() const { + return m_impl->listeners; + } +} // namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_reporter_registry.hpp b/src/external/catch2/src/catch2/internal/catch_reporter_registry.hpp index 5577b9ef..92a88927 100644 --- a/src/external/catch2/src/catch2/internal/catch_reporter_registry.hpp +++ b/src/external/catch2/src/catch2/internal/catch_reporter_registry.hpp @@ -8,31 +8,48 @@ #ifndef CATCH_REPORTER_REGISTRY_HPP_INCLUDED #define CATCH_REPORTER_REGISTRY_HPP_INCLUDED -#include -#include +#include +#include #include +#include +#include namespace Catch { - class ReporterRegistry : public IReporterRegistry { - public: + class IEventListener; + using IEventListenerPtr = Detail::unique_ptr; + class IReporterFactory; + using IReporterFactoryPtr = Detail::unique_ptr; + struct ReporterConfig; + class EventListenerFactory; + + class ReporterRegistry { + struct ReporterRegistryImpl; + Detail::unique_ptr m_impl; + public: ReporterRegistry(); - ~ReporterRegistry() override; // = default, out of line to allow fwd decl + ~ReporterRegistry(); // = default; - IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const override; + IEventListenerPtr create( std::string const& name, + ReporterConfig&& config ) const; - void registerReporter( std::string const& name, IReporterFactoryPtr factory ); - void registerListener( Detail::unique_ptr factory ); + void registerReporter( std::string const& name, + IReporterFactoryPtr factory ); - FactoryMap const& getFactories() const override; - Listeners const& getListeners() const override; + void + registerListener( Detail::unique_ptr factory ); - private: - FactoryMap m_factories; - Listeners m_listeners; + std::map const& + getFactories() const; + + std::vector> const& + getListeners() const; }; -} + +} // end namespace Catch #endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_reporter_spec_parser.cpp b/src/external/catch2/src/catch2/internal/catch_reporter_spec_parser.cpp index f6591d9a..2b08758a 100644 --- a/src/external/catch2/src/catch2/internal/catch_reporter_spec_parser.cpp +++ b/src/external/catch2/src/catch2/internal/catch_reporter_spec_parser.cpp @@ -21,9 +21,9 @@ namespace Catch { }; kvPair splitKVPair(StringRef kvString) { - auto splitPos = static_cast( std::distance( - kvString.begin(), - std::find( kvString.begin(), kvString.end(), '=' ) ) ); + auto splitPos = static_cast( + std::find( kvString.begin(), kvString.end(), '=' ) - + kvString.begin() ); return { kvString.substr( 0, splitPos ), kvString.substr( splitPos + 1, kvString.size() ) }; @@ -117,7 +117,7 @@ namespace Catch { auto kv = splitKVPair( parts[i] ); auto key = kv.key, value = kv.value; - if ( key.empty() || value.empty() ) { + if ( key.empty() || value.empty() ) { // NOLINT(bugprone-branch-clone) return {}; } else if ( key[0] == 'X' ) { // This is a reporter-specific option, we don't check these diff --git a/src/external/catch2/src/catch2/internal/catch_reporter_spec_parser.hpp b/src/external/catch2/src/catch2/internal/catch_reporter_spec_parser.hpp index d446ce98..9f447ee2 100644 --- a/src/external/catch2/src/catch2/internal/catch_reporter_spec_parser.hpp +++ b/src/external/catch2/src/catch2/internal/catch_reporter_spec_parser.hpp @@ -8,7 +8,7 @@ #ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED #define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED -#include +#include #include #include diff --git a/src/external/catch2/src/catch2/internal/catch_result_type.cpp b/src/external/catch2/src/catch2/internal/catch_result_type.cpp deleted file mode 100644 index 6cedce71..00000000 --- a/src/external/catch2/src/catch2/internal/catch_result_type.cpp +++ /dev/null @@ -1,26 +0,0 @@ - -// Copyright Catch2 Authors -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.txt or copy at -// https://www.boost.org/LICENSE_1_0.txt) - -// SPDX-License-Identifier: BSL-1.0 -#include - -namespace Catch { - - bool isOk( ResultWas::OfType resultType ) { - return ( resultType & ResultWas::FailureBit ) == 0; - } - bool isJustInfo( int flags ) { - return flags == ResultWas::Info; - } - - ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { - return static_cast( static_cast( lhs ) | static_cast( rhs ) ); - } - - bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } - bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } - -} // end namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_result_type.hpp b/src/external/catch2/src/catch2/internal/catch_result_type.hpp index e66afaff..69a6ef14 100644 --- a/src/external/catch2/src/catch2/internal/catch_result_type.hpp +++ b/src/external/catch2/src/catch2/internal/catch_result_type.hpp @@ -33,8 +33,10 @@ namespace Catch { }; }; - bool isOk( ResultWas::OfType resultType ); - bool isJustInfo( int flags ); + constexpr bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + constexpr bool isJustInfo( int flags ) { return flags == ResultWas::Info; } // ResultDisposition::Flags enum @@ -46,11 +48,18 @@ namespace Catch { SuppressFail = 0x08 // Failures are reported but do not fail the test }; }; - ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); - - bool shouldContinueOnFailure( int flags ); - inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } - bool shouldSuppressFailure( int flags ); + constexpr ResultDisposition::Flags operator|( ResultDisposition::Flags lhs, + ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | + static_cast( rhs ) ); + } + + constexpr bool isFalseTest( int flags ) { + return ( flags & ResultDisposition::FalseTest ) != 0; + } + constexpr bool shouldSuppressFailure( int flags ) { + return ( flags & ResultDisposition::SuppressFail ) != 0; + } } // end namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_run_context.cpp b/src/external/catch2/src/catch2/internal/catch_run_context.cpp index 08086b28..2a102fbe 100644 --- a/src/external/catch2/src/catch2/internal/catch_run_context.cpp +++ b/src/external/catch2/src/catch2/internal/catch_run_context.cpp @@ -8,8 +8,9 @@ #include #include -#include #include +#include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -26,146 +28,152 @@ namespace Catch { namespace Generators { - struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { - GeneratorBasePtr m_generator; - - GeneratorTracker( TestCaseTracking::NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ) - {} - ~GeneratorTracker() override; - - static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocationRef const& nameAndLocation ) { - GeneratorTracker* tracker; - - ITracker& currentTracker = ctx.currentTracker(); - // Under specific circumstances, the generator we want - // to acquire is also the current tracker. If this is - // the case, we have to avoid looking through current - // tracker's children, and instead return the current - // tracker. - // A case where this check is important is e.g. - // for (int i = 0; i < 5; ++i) { - // int n = GENERATE(1, 2); - // } - // - // without it, the code above creates 5 nested generators. - if ( currentTracker.nameAndLocation() == nameAndLocation ) { - auto thisTracker = - currentTracker.parent()->findChild( nameAndLocation ); - assert( thisTracker ); - assert( thisTracker->isGeneratorTracker() ); - tracker = static_cast( thisTracker ); - } else if ( ITracker* childTracker = - currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isGeneratorTracker() ); - tracker = static_cast( childTracker ); - } else { - return nullptr; - } - - if( !tracker->isComplete() ) { - tracker->open(); - } + namespace { + struct GeneratorTracker final : TestCaseTracking::TrackerBase, + IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker( + TestCaseTracking::NameAndLocation&& nameAndLocation, + TrackerContext& ctx, + ITracker* parent ): + TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {} + + static GeneratorTracker* + acquire( TrackerContext& ctx, + TestCaseTracking::NameAndLocationRef const& + nameAndLocation ) { + GeneratorTracker* tracker; + + ITracker& currentTracker = ctx.currentTracker(); + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if ( currentTracker.nameAndLocation() == nameAndLocation ) { + auto thisTracker = currentTracker.parent()->findChild( + nameAndLocation ); + assert( thisTracker ); + assert( thisTracker->isGeneratorTracker() ); + tracker = static_cast( thisTracker ); + } else if ( ITracker* childTracker = + currentTracker.findChild( + nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isGeneratorTracker() ); + tracker = + static_cast( childTracker ); + } else { + return nullptr; + } - return tracker; - } + if ( !tracker->isComplete() ) { tracker->open(); } - // TrackerBase interface - bool isGeneratorTracker() const override { return true; } - auto hasGenerator() const -> bool override { - return !!m_generator; - } - void close() override { - TrackerBase::close(); - // If a generator has a child (it is followed by a section) - // and none of its children have started, then we must wait - // until later to start consuming its values. - // This catches cases where `GENERATE` is placed between two - // `SECTION`s. - // **The check for m_children.empty cannot be removed**. - // doing so would break `GENERATE` _not_ followed by `SECTION`s. - const bool should_wait_for_child = [&]() { - // No children -> nobody to wait for - if ( m_children.empty() ) { - return false; - } - // If at least one child started executing, don't wait - if ( std::find_if( - m_children.begin(), - m_children.end(), - []( TestCaseTracking::ITrackerPtr const& tracker ) { - return tracker->hasStarted(); - } ) != m_children.end() ) { - return false; - } + return tracker; + } - // No children have started. We need to check if they _can_ - // start, and thus we should wait for them, or they cannot - // start (due to filters), and we shouldn't wait for them - ITracker* parent = m_parent; - // This is safe: there is always at least one section - // tracker in a test case tracking tree - while ( !parent->isSectionTracker() ) { - parent = parent->parent(); - } - assert( parent && - "Missing root (test case) level section" ); - - auto const& parentSection = - static_cast( *parent ); - auto const& filters = parentSection.getFilters(); - // No filters -> no restrictions on running sections - if ( filters.empty() ) { - return true; - } + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by + // `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if ( m_children.empty() ) { return false; } + // If at least one child started executing, don't wait + if ( std::find_if( + m_children.begin(), + m_children.end(), + []( TestCaseTracking::ITrackerPtr const& + tracker ) { + return tracker->hasStarted(); + } ) != m_children.end() ) { + return false; + } - for ( auto const& child : m_children ) { - if ( child->isSectionTracker() && - std::find( - filters.begin(), - filters.end(), - static_cast( *child ) - .trimmedName() ) != filters.end() ) { - return true; + // No children have started. We need to check if they + // _can_ start, and thus we should wait for them, or + // they cannot start (due to filters), and we shouldn't + // wait for them + ITracker* parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while ( !parent->isSectionTracker() ) { + parent = parent->parent(); + } + assert( parent && + "Missing root (test case) level section" ); + + auto const& parentSection = + static_cast( *parent ); + auto const& filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if ( filters.empty() ) { return true; } + + for ( auto const& child : m_children ) { + if ( child->isSectionTracker() && + std::find( filters.begin(), + filters.end(), + static_cast( + *child ) + .trimmedName() ) != + filters.end() ) { + return true; + } } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + assert( m_generator && "Tracker without generator" ); + if ( should_wait_for_child || + ( m_runState == CompletedSuccessfully && + m_generator->countedNext() ) ) { + m_children.clear(); + m_runState = Executing; } - return false; - }(); - - // This check is a bit tricky, because m_generator->next() - // has a side-effect, where it consumes generator's current - // value, but we do not want to invoke the side-effect if - // this generator is still waiting for any child to start. - assert( m_generator && "Tracker without generator" ); - if ( should_wait_for_child || - ( m_runState == CompletedSuccessfully && - m_generator->countedNext() ) ) { - m_children.clear(); - m_runState = Executing; } - } - // IGeneratorTracker interface - auto getGenerator() const -> GeneratorBasePtr const& override { - return m_generator; - } - void setGenerator( GeneratorBasePtr&& generator ) override { - m_generator = CATCH_MOVE( generator ); - } - }; - GeneratorTracker::~GeneratorTracker() = default; + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = CATCH_MOVE( generator ); + } + }; + } // namespace } RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter) : m_runInfo(_config->name()), - m_context(getCurrentMutableContext()), m_config(_config), m_reporter(CATCH_MOVE(reporter)), m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, + m_outputRedirect( makeOutputRedirect( m_reporter->getPreferences().shouldRedirectStdOut ) ), m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) { - m_context.setResultCapture(this); + getCurrentMutableContext().setResultCapture( this ); m_reporter->testRunStarting(m_runInfo); } @@ -178,6 +186,7 @@ namespace Catch { auto const& testInfo = testCase.getTestCaseInfo(); m_reporter->testCaseStarting(testInfo); + testCase.prepareTestCase(); m_activeTestCase = &testCase; @@ -228,15 +237,17 @@ namespace Catch { m_reporter->testCasePartialStarting(testInfo, testRuns); const auto beforeRunTotals = m_totals; - std::string oneRunCout, oneRunCerr; - runCurrentTest(oneRunCout, oneRunCerr); + runCurrentTest(); + std::string oneRunCout = m_outputRedirect->getStdout(); + std::string oneRunCerr = m_outputRedirect->getStderr(); + m_outputRedirect->clearBuffers(); redirectedCout += oneRunCout; redirectedCerr += oneRunCerr; const auto singleRunTotals = m_totals.delta(beforeRunTotals); auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting()); - m_reporter->testCasePartialEnded(statsForOneRun, testRuns); + ++testRuns; } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); @@ -247,6 +258,7 @@ namespace Catch { deltaTotals.testCases.failed++; } m_totals.testCases += deltaTotals.testCases; + testCase.tearDownTestCase(); m_reporter->testCaseEnded(TestCaseStats(testInfo, deltaTotals, CATCH_MOVE(redirectedCout), @@ -260,7 +272,7 @@ namespace Catch { } - void RunContext::assertionEnded(AssertionResult const & result) { + void RunContext::assertionEnded(AssertionResult&& result) { if (result.getResultType() == ResultWas::Ok) { m_totals.assertions.passed++; m_lastAssertionPassed = true; @@ -280,21 +292,33 @@ namespace Catch { m_lastAssertionPassed = true; } - m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)); + { + auto _ = scopedDeactivate( *m_outputRedirect ); + m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ); + } - if (result.getResultType() != ResultWas::Warning) + if ( result.getResultType() != ResultWas::Warning ) { m_messageScopes.clear(); + } - // Reset working state - resetAssertionInfo(); - m_lastResult = result; + // Reset working state. assertion info will be reset after + // populateReaction is run if it is needed + m_lastResult = CATCH_MOVE( result ); } void RunContext::resetAssertionInfo() { m_lastAssertionInfo.macroName = StringRef(); m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + m_lastAssertionInfo.resultDisposition = ResultDisposition::Normal; + } + + void RunContext::notifyAssertionStarted( AssertionInfo const& info ) { + auto _ = scopedDeactivate( *m_outputRedirect ); + m_reporter->assertionStarting( info ); } - bool RunContext::sectionStarted(StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts & assertions) { + bool RunContext::sectionStarted( StringRef sectionName, + SourceLineInfo const& sectionLineInfo, + Counts& assertions ) { ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocationRef( @@ -307,7 +331,10 @@ namespace Catch { SectionInfo sectionInfo( sectionLineInfo, static_cast(sectionName) ); m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; - m_reporter->sectionStarting(sectionInfo); + { + auto _ = scopedDeactivate( *m_outputRedirect ); + m_reporter->sectionStarting( sectionInfo ); + } assertions = m_totals.assertions; @@ -367,7 +394,15 @@ namespace Catch { m_activeSections.pop_back(); } - m_reporter->sectionEnded(SectionStats(CATCH_MOVE(endInfo.sectionInfo), assertions, endInfo.durationInSeconds, missingAssertions)); + { + auto _ = scopedDeactivate( *m_outputRedirect ); + m_reporter->sectionEnded( + SectionStats( CATCH_MOVE( endInfo.sectionInfo ), + assertions, + endInfo.durationInSeconds, + missingAssertions ) ); + } + m_messages.clear(); m_messageScopes.clear(); } @@ -384,15 +419,19 @@ namespace Catch { } void RunContext::benchmarkPreparing( StringRef name ) { - m_reporter->benchmarkPreparing(name); + auto _ = scopedDeactivate( *m_outputRedirect ); + m_reporter->benchmarkPreparing( name ); } void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + auto _ = scopedDeactivate( *m_outputRedirect ); m_reporter->benchmarkStarting( info ); } void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { + auto _ = scopedDeactivate( *m_outputRedirect ); m_reporter->benchmarkEnded( stats ); } void RunContext::benchmarkFailed( StringRef error ) { + auto _ = scopedDeactivate( *m_outputRedirect ); m_reporter->benchmarkFailed( error ); } @@ -423,8 +462,13 @@ namespace Catch { } void RunContext::handleFatalErrorCondition( StringRef message ) { + // TODO: scoped deactivate here? Just give up and do best effort? + // the deactivation can break things further, OTOH so can the + // capture + auto _ = scopedDeactivate( *m_outputRedirect ); + // First notify reporter that bad things happened - m_reporter->fatalErrorEncountered(message); + m_reporter->fatalErrorEncountered( message ); // Don't rebuild the result -- the stringification itself can cause more fatal errors // Instead, fake a result data. @@ -432,8 +476,16 @@ namespace Catch { tempResult.message = static_cast(message); AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult)); - assertionEnded(result); + assertionEnded(CATCH_MOVE(result) ); + resetAssertionInfo(); + // Best effort cleanup for sections that have not been destructed yet + // Since this is a fatal error, we have not had and won't have the opportunity to destruct them properly + while (!m_activeSections.empty()) { + auto nl = m_activeSections.back()->nameAndLocation(); + SectionEndInfo endInfo{ SectionInfo(CATCH_MOVE(nl.location), CATCH_MOVE(nl.name)), {}, 0.0 }; + sectionEndedEarly(CATCH_MOVE(endInfo)); + } handleUnfinishedSections(); // Recreate section for test case (as we will lose the one that was in scope) @@ -443,7 +495,7 @@ namespace Catch { Counts assertions; assertions.failed = 1; SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false); - m_reporter->sectionEnded(testCaseSectionStats); + m_reporter->sectionEnded( testCaseSectionStats ); auto const& testInfo = m_activeTestCase->getTestCaseInfo(); @@ -474,7 +526,7 @@ namespace Catch { return m_totals.assertions.failed >= static_cast(m_config->abortAfter()); } - void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + void RunContext::runCurrentTest() { auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); m_reporter->sectionStarting(testCaseSection); @@ -485,18 +537,8 @@ namespace Catch { Timer timer; CATCH_TRY { - if (m_reporter->getPreferences().shouldRedirectStdOut) { -#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) - RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); - - timer.start(); - invokeActiveTestCase(); -#else - OutputRedirect r(redirectedCout, redirectedCerr); - timer.start(); - invokeActiveTestCase(); -#endif - } else { + { + auto _ = scopedActivate( *m_outputRedirect ); timer.start(); invokeActiveTestCase(); } @@ -541,11 +583,12 @@ namespace Catch { void RunContext::handleUnfinishedSections() { // If sections ended prematurely due to an exception we stored their // infos here so we can tear them down outside the unwind process. - for (auto it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it) - sectionEnded(CATCH_MOVE(*it)); + for ( auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it ) { + sectionEnded( CATCH_MOVE( *it ) ); + } m_unfinishedSections.clear(); } @@ -554,8 +597,6 @@ namespace Catch { ITransientExpression const& expr, AssertionReaction& reaction ) { - m_reporter->assertionStarting( info ); - bool negated = isFalseTest( info.resultDisposition ); bool result = expr.getResult() != negated; @@ -571,6 +612,7 @@ namespace Catch { reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); populateReaction( reaction ); } + resetAssertionInfo(); } void RunContext::reportExpr( AssertionInfo const &info, @@ -584,31 +626,32 @@ namespace Catch { AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); } void RunContext::handleMessage( AssertionInfo const& info, ResultWas::OfType resultType, - StringRef message, + std::string&& message, AssertionReaction& reaction ) { - m_reporter->assertionStarting( info ); - m_lastAssertionInfo = info; AssertionResultData data( resultType, LazyExpression( false ) ); - data.message = static_cast(message); + data.message = CATCH_MOVE( message ); AssertionResult assertionResult{ m_lastAssertionInfo, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); - if ( !assertionResult.isOk() ) { + + const auto isOk = assertionResult.isOk(); + assertionEnded( CATCH_MOVE(assertionResult) ); + if ( !isOk ) { populateReaction( reaction ); } else if ( resultType == ResultWas::ExplicitSkip ) { // TODO: Need to handle this explicitly, as ExplicitSkip is // considered "OK" reaction.shouldSkip = true; } + resetAssertionInfo(); } void RunContext::handleUnexpectedExceptionNotThrown( AssertionInfo const& info, @@ -619,16 +662,17 @@ namespace Catch { void RunContext::handleUnexpectedInflightException( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) { m_lastAssertionInfo = info; AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); - data.message = message; + data.message = CATCH_MOVE(message); AssertionResult assertionResult{ info, CATCH_MOVE(data) }; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); populateReaction( reaction ); + resetAssertionInfo(); } void RunContext::populateReaction( AssertionReaction& reaction ) { @@ -645,7 +689,8 @@ namespace Catch { AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s; AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); + resetAssertionInfo(); } void RunContext::handleNonExpr( AssertionInfo const &info, @@ -656,10 +701,11 @@ namespace Catch { AssertionResultData data( resultType, LazyExpression( false ) ); AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); - if( !assertionResult.isOk() ) - populateReaction( reaction ); + const auto isOk = assertionResult.isOk(); + assertionEnded( CATCH_MOVE(assertionResult) ); + if ( !isOk ) { populateReaction( reaction ); } + resetAssertionInfo(); } diff --git a/src/external/catch2/src/catch2/internal/catch_run_context.hpp b/src/external/catch2/src/catch2/internal/catch_run_context.hpp index 3928180e..c66fec0c 100644 --- a/src/external/catch2/src/catch2/internal/catch_run_context.hpp +++ b/src/external/catch2/src/catch2/internal/catch_run_context.hpp @@ -8,8 +8,9 @@ #ifndef CATCH_RUN_CONTEXT_HPP_INCLUDED #define CATCH_RUN_CONTEXT_HPP_INCLUDED -#include +#include #include +#include #include #include #include @@ -24,13 +25,15 @@ namespace Catch { - class IMutableContext; class IGeneratorTracker; class IConfig; + class IEventListener; + using IEventListenerPtr = Detail::unique_ptr; + class OutputRedirect; /////////////////////////////////////////////////////////////////////////// - class RunContext : public IResultCapture { + class RunContext final : public IResultCapture { public: RunContext( RunContext const& ) = delete; @@ -52,14 +55,14 @@ namespace Catch { void handleMessage ( AssertionInfo const& info, ResultWas::OfType resultType, - StringRef message, + std::string&& message, AssertionReaction& reaction ) override; void handleUnexpectedExceptionNotThrown ( AssertionInfo const& info, AssertionReaction& reaction ) override; void handleUnexpectedInflightException ( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) override; void handleIncomplete ( AssertionInfo const& info ) override; @@ -68,6 +71,7 @@ namespace Catch { ResultWas::OfType resultType, AssertionReaction &reaction ) override; + void notifyAssertionStarted( AssertionInfo const& info ) override; bool sectionStarted( StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts& assertions ) override; @@ -112,13 +116,13 @@ namespace Catch { private: - void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); + void runCurrentTest(); void invokeActiveTestCase(); void resetAssertionInfo(); bool testForMissingAssertions( Counts& assertions ); - void assertionEnded( AssertionResult const& result ); + void assertionEnded( AssertionResult&& result ); void reportExpr ( AssertionInfo const &info, ResultWas::OfType resultType, @@ -132,7 +136,6 @@ namespace Catch { void handleUnfinishedSections(); TestRunInfo m_runInfo; - IMutableContext& m_context; TestCaseHandle const* m_activeTestCase = nullptr; ITracker* m_testCaseTracker = nullptr; Optional m_lastResult; @@ -146,6 +149,7 @@ namespace Catch { std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; + Detail::unique_ptr m_outputRedirect; FatalConditionHandler m_fatalConditionhandler; bool m_lastAssertionPassed = false; bool m_shouldReportUnexpected = true; diff --git a/src/external/catch2/src/catch2/internal/catch_section.cpp b/src/external/catch2/src/catch2/internal/catch_section.cpp index 061732b1..677c2164 100644 --- a/src/external/catch2/src/catch2/internal/catch_section.cpp +++ b/src/external/catch2/src/catch2/internal/catch_section.cpp @@ -6,7 +6,7 @@ // SPDX-License-Identifier: BSL-1.0 #include -#include +#include #include #include diff --git a/src/external/catch2/src/catch2/internal/catch_section.hpp b/src/external/catch2/src/catch2/internal/catch_section.hpp index 8c1a882b..e56c79f3 100644 --- a/src/external/catch2/src/catch2/internal/catch_section.hpp +++ b/src/external/catch2/src/catch2/internal/catch_section.hpp @@ -9,6 +9,7 @@ #define CATCH_SECTION_HPP_INCLUDED #include +#include #include #include #include @@ -38,16 +39,66 @@ namespace Catch { } // end namespace Catch -#define INTERNAL_CATCH_SECTION( ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - -#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) +# define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_Section ) = \ + Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_Section ) = \ + Catch::SectionInfo( \ + CATCH_INTERNAL_LINEINFO, \ + ( Catch::ReusableStringStream() << __VA_ARGS__ ) \ + .str() ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#else + +// These section definitions imply that at most one section at one level +// will be intered (because only one section's __LINE__ can be equal to +// the dummy `catchInternalSectionHint` variable from `TEST_CASE`). + +namespace Catch { + namespace Detail { + // Intentionally without linkage, as it should only be used as a dummy + // symbol for static analysis. + // The arguments are used as a dummy for checking warnings in the passed + // expressions. + int GetNewSectionHint( StringRef, const char* const = nullptr ); + } // namespace Detail +} // namespace Catch + + +# define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ + catchInternalSectionHint, \ + catchInternalSectionHint = \ + Catch::Detail::GetNewSectionHint(__VA_ARGS__); \ + catchInternalPreviousSectionHint == __LINE__ ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ + catchInternalSectionHint, \ + catchInternalSectionHint = Catch::Detail::GetNewSectionHint( \ + ( Catch::ReusableStringStream() << __VA_ARGS__ ).str()); \ + catchInternalPreviousSectionHint == __LINE__ ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#endif + #endif // CATCH_SECTION_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_sharding.hpp b/src/external/catch2/src/catch2/internal/catch_sharding.hpp index d0e4cfa1..22561f4b 100644 --- a/src/external/catch2/src/catch2/internal/catch_sharding.hpp +++ b/src/external/catch2/src/catch2/internal/catch_sharding.hpp @@ -8,8 +8,7 @@ #ifndef CATCH_SHARDING_HPP_INCLUDED #define CATCH_SHARDING_HPP_INCLUDED -#include - +#include #include #include diff --git a/src/external/catch2/src/catch2/internal/catch_stream_end_stop.hpp b/src/external/catch2/src/catch2/internal/catch_stream_end_stop.hpp index 61379f20..66d678cf 100644 --- a/src/external/catch2/src/catch2/internal/catch_stream_end_stop.hpp +++ b/src/external/catch2/src/catch2/internal/catch_stream_end_stop.hpp @@ -17,10 +17,10 @@ namespace Catch { // as well as // << stuff +StreamEndStop struct StreamEndStop { - StringRef operator+() const { return StringRef(); } + constexpr StringRef operator+() const { return StringRef(); } template - friend T const& operator+( T const& value, StreamEndStop ) { + constexpr friend T const& operator+( T const& value, StreamEndStop ) { return value; } }; diff --git a/src/external/catch2/src/catch2/internal/catch_string_manip.cpp b/src/external/catch2/src/catch2/internal/catch_string_manip.cpp index cb96dd4f..ce1abaa0 100644 --- a/src/external/catch2/src/catch2/internal/catch_string_manip.cpp +++ b/src/external/catch2/src/catch2/internal/catch_string_manip.cpp @@ -5,10 +5,10 @@ // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 +#include #include #include -#include #include #include #include @@ -32,9 +32,9 @@ namespace Catch { return s.find( infix ) != std::string::npos; } void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), []( char c ) { - return toLower( c ); - } ); + for ( char& c : s ) { + c = toLower( c ); + } } std::string toLower( std::string const& s ) { std::string lc = s; @@ -66,17 +66,29 @@ namespace Catch { } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { - bool replaced = false; std::size_t i = str.find( replaceThis ); - while( i != std::string::npos ) { - replaced = true; - str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); - if( i < str.size()-withThis.size() ) - i = str.find( replaceThis, i+withThis.size() ); + if (i == std::string::npos) { + return false; + } + std::size_t copyBegin = 0; + std::string origStr = CATCH_MOVE(str); + str.clear(); + // There is at least one replacement, so reserve with the best guess + // we can make without actually counting the number of occurences. + str.reserve(origStr.size() - replaceThis.size() + withThis.size()); + do { + str.append(origStr, copyBegin, i-copyBegin ); + str += withThis; + copyBegin = i + replaceThis.size(); + if( copyBegin < origStr.size() ) + i = origStr.find( replaceThis, copyBegin ); else i = std::string::npos; + } while( i != std::string::npos ); + if ( copyBegin < origStr.size() ) { + str.append(origStr, copyBegin, origStr.size() ); } - return replaced; + return true; } std::vector splitStringRef( StringRef str, char delimiter ) { diff --git a/src/external/catch2/src/catch2/internal/catch_stringref.hpp b/src/external/catch2/src/catch2/internal/catch_stringref.hpp index 99bb9a98..421ce712 100644 --- a/src/external/catch2/src/catch2/internal/catch_stringref.hpp +++ b/src/external/catch2/src/catch2/internal/catch_stringref.hpp @@ -25,6 +25,8 @@ namespace Catch { using size_type = std::size_t; using const_iterator = const char*; + static constexpr size_type npos{ static_cast( -1 ) }; + private: static constexpr char const* const s_empty = ""; @@ -75,7 +77,7 @@ namespace Catch { } // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, start + size()). + // If start + length > size(), then the substring is [start, size()). // If start > size(), then the substring is empty. constexpr StringRef substr(size_type start, size_type length) const noexcept { if (start < m_size) { @@ -95,8 +97,8 @@ namespace Catch { constexpr const_iterator end() const { return m_start + m_size; } - friend std::string& operator += (std::string& lhs, StringRef sr); - friend std::ostream& operator << (std::ostream& os, StringRef sr); + friend std::string& operator += (std::string& lhs, StringRef rhs); + friend std::ostream& operator << (std::ostream& os, StringRef str); friend std::string operator+(StringRef lhs, StringRef rhs); /** diff --git a/src/external/catch2/src/catch2/internal/catch_tag_alias_registry.cpp b/src/external/catch2/src/catch2/internal/catch_tag_alias_registry.cpp index b7c6b9ec..510df167 100644 --- a/src/external/catch2/src/catch2/internal/catch_tag_alias_registry.cpp +++ b/src/external/catch2/src/catch2/internal/catch_tag_alias_registry.cpp @@ -6,14 +6,13 @@ // SPDX-License-Identifier: BSL-1.0 #include -#include #include #include #include namespace Catch { - TagAliasRegistry::~TagAliasRegistry() {} + TagAliasRegistry::~TagAliasRegistry() = default; TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { auto it = m_registry.find( alias ); diff --git a/src/external/catch2/src/catch2/internal/catch_test_case_registry_impl.cpp b/src/external/catch2/src/catch2/internal/catch_test_case_registry_impl.cpp index 4b3d2e47..e77e7bce 100644 --- a/src/external/catch2/src/catch2/internal/catch_test_case_registry_impl.cpp +++ b/src/external/catch2/src/catch2/internal/catch_test_case_registry_impl.cpp @@ -7,11 +7,9 @@ // SPDX-License-Identifier: BSL-1.0 #include -#include #include +#include #include -#include -#include #include #include #include @@ -23,6 +21,38 @@ namespace Catch { + namespace { + static void enforceNoDuplicateTestCases( + std::vector const& tests ) { + auto testInfoCmp = []( TestCaseInfo const* lhs, + TestCaseInfo const* rhs ) { + return *lhs < *rhs; + }; + std::set seenTests( + testInfoCmp ); + for ( auto const& test : tests ) { + const auto infoPtr = &test.getTestCaseInfo(); + const auto prev = seenTests.insert( infoPtr ); + CATCH_ENFORCE( prev.second, + "error: test case \"" + << infoPtr->name << "\", with tags \"" + << infoPtr->tagsAsString() + << "\" already defined.\n" + << "\tFirst seen at " + << ( *prev.first )->lineInfo << "\n" + << "\tRedefined at " << infoPtr->lineInfo ); + } + } + + static bool matchTest( TestCaseHandle const& testCase, + TestSpec const& testSpec, + IConfig const& config ) { + return testSpec.matches( testCase.getTestCaseInfo() ) && + isThrowSafe( testCase, config ); + } + + } // end unnamed namespace + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { switch (config.runOrder()) { case TestRunOrder::Declared: @@ -40,7 +70,6 @@ namespace Catch { return sorted; } case TestRunOrder::Randomized: { - seedRng(config); using TestWithHash = std::pair; TestCaseInfoHasher h{ config.rngSeed() }; @@ -79,29 +108,6 @@ namespace Catch { return !testCase.getTestCaseInfo().throws() || config.allowThrows(); } - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ) { - return testSpec.matches( testCase.getTestCaseInfo() ) && isThrowSafe( testCase, config ); - } - - void - enforceNoDuplicateTestCases( std::vector const& tests ) { - auto testInfoCmp = []( TestCaseInfo const* lhs, - TestCaseInfo const* rhs ) { - return *lhs < *rhs; - }; - std::set seenTests(testInfoCmp); - for ( auto const& test : tests ) { - const auto infoPtr = &test.getTestCaseInfo(); - const auto prev = seenTests.insert( infoPtr ); - CATCH_ENFORCE( - prev.second, - "error: test case \"" << infoPtr->name << "\", with tags \"" - << infoPtr->tagsAsString() << "\" already defined.\n" - << "\tFirst seen at " << ( *prev.first )->lineInfo << "\n" - << "\tRedefined at " << infoPtr->lineInfo ); - } - } - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { std::vector filtered; filtered.reserve( testCases.size() ); @@ -117,6 +123,8 @@ namespace Catch { return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); } + TestRegistry::~TestRegistry() = default; + void TestRegistry::registerTest(Detail::unique_ptr testInfo, Detail::unique_ptr testInvoker) { m_handles.emplace_back(testInfo.get(), testInvoker.get()); m_viewed_test_infos.push_back(testInfo.get()); @@ -142,11 +150,4 @@ namespace Catch { return m_sortedFunctions; } - - - /////////////////////////////////////////////////////////////////////////// - void TestInvokerAsFunction::invoke() const { - m_testAsFunction(); - } - } // end namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_test_case_registry_impl.hpp b/src/external/catch2/src/catch2/internal/catch_test_case_registry_impl.hpp index 228dbb79..fbca89f9 100644 --- a/src/external/catch2/src/catch2/internal/catch_test_case_registry_impl.hpp +++ b/src/external/catch2/src/catch2/internal/catch_test_case_registry_impl.hpp @@ -8,37 +8,36 @@ #ifndef CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED #define CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED -#include +#include #include +#include #include namespace Catch { - class TestCaseHandle; class IConfig; + class ITestInvoker; + class TestCaseHandle; class TestSpec; std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ); - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ); - - void enforceNoDuplicateTestCases( std::vector const& functions ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); class TestRegistry : public ITestCaseRegistry { public: - ~TestRegistry() override = default; - void registerTest( Detail::unique_ptr testInfo, Detail::unique_ptr testInvoker ); std::vector const& getAllInfos() const override; std::vector const& getAllTests() const override; std::vector const& getAllTestsSorted( IConfig const& config ) const override; + ~TestRegistry() override; // = default + private: std::vector> m_owned_test_infos; // Keeps a materialized vector for `getAllInfos`. @@ -53,18 +52,6 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////// - class TestInvokerAsFunction final : public ITestInvoker { - using TestType = void(*)(); - TestType m_testAsFunction; - public: - TestInvokerAsFunction(TestType testAsFunction) noexcept: - m_testAsFunction(testAsFunction) {} - - void invoke() const override; - }; - - /////////////////////////////////////////////////////////////////////////// - } // end namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_test_case_tracker.hpp b/src/external/catch2/src/catch2/internal/catch_test_case_tracker.hpp index beff8d6d..50278c91 100644 --- a/src/external/catch2/src/catch2/internal/catch_test_case_tracker.hpp +++ b/src/external/catch2/src/catch2/internal/catch_test_case_tracker.hpp @@ -113,7 +113,7 @@ namespace TestCaseTracking { //! Returns true if tracker run to completion (successfully or not) virtual bool isComplete() const = 0; - //! Returns true if tracker run to completion succesfully + //! Returns true if tracker run to completion successfully bool isSuccessfullyCompleted() const { return m_runState == CompletedSuccessfully; } diff --git a/src/external/catch2/src/catch2/internal/catch_test_failure_exception.cpp b/src/external/catch2/src/catch2/internal/catch_test_failure_exception.cpp index c1edff3c..8ea31313 100644 --- a/src/external/catch2/src/catch2/internal/catch_test_failure_exception.cpp +++ b/src/external/catch2/src/catch2/internal/catch_test_failure_exception.cpp @@ -20,4 +20,12 @@ namespace Catch { #endif } + void throw_test_skip_exception() { +#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) + throw Catch::TestSkipException(); +#else + CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); +#endif + } + } // namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_test_failure_exception.hpp b/src/external/catch2/src/catch2/internal/catch_test_failure_exception.hpp index 13c5fc08..1ef88364 100644 --- a/src/external/catch2/src/catch2/internal/catch_test_failure_exception.hpp +++ b/src/external/catch2/src/catch2/internal/catch_test_failure_exception.hpp @@ -12,6 +12,8 @@ namespace Catch { //! Used to signal that an assertion macro failed struct TestFailureException{}; + //! Used to signal that the remainder of a test should be skipped + struct TestSkipException {}; /** * Outlines throwing of `TestFailureException` into a single TU @@ -20,8 +22,12 @@ namespace Catch { */ [[noreturn]] void throw_test_failure_exception(); - //! Used to signal that the remainder of a test should be skipped - struct TestSkipException{}; + /** + * Outlines throwing of `TestSkipException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_skip_exception(); } // namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_test_macro_impl.hpp b/src/external/catch2/src/catch2/internal/catch_test_macro_impl.hpp index e569680b..ccd5bb35 100644 --- a/src/external/catch2/src/catch2/internal/catch_test_macro_impl.hpp +++ b/src/external/catch2/src/catch2/internal/catch_test_macro_impl.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -22,12 +23,6 @@ #if !defined(CATCH_CONFIG_DISABLE) -#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) - #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr -#else - #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr -#endif - #if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) /////////////////////////////////////////////////////////////////////////////// @@ -39,12 +34,10 @@ #else // CATCH_CONFIG_FAST_COMPILE #define INTERNAL_CATCH_TRY try -#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { (handler).handleUnexpectedInflightException(); } #endif -#define INTERNAL_CATCH_REACT( handler ) handler.complete(); - /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ do { /* NOLINT(bugprone-infinite-loop) */ \ @@ -54,10 +47,10 @@ INTERNAL_CATCH_TRY { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); /* NOLINT(bugprone-chained-comparison) */ \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. @@ -85,7 +78,7 @@ catch( ... ) { \ catchAssertionHandler.handleUnexpectedInflightException(); \ } \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) /////////////////////////////////////////////////////////////////////////////// @@ -95,6 +88,7 @@ if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -105,7 +99,7 @@ } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) /////////////////////////////////////////////////////////////////////////////// @@ -115,6 +109,7 @@ if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(expr); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -128,7 +123,7 @@ } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) @@ -141,6 +136,7 @@ if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -151,7 +147,7 @@ } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) #endif // CATCH_CONFIG_DISABLE diff --git a/src/external/catch2/src/catch2/internal/catch_test_registry.cpp b/src/external/catch2/src/catch2/internal/catch_test_registry.cpp index 9769ed03..d017c50e 100644 --- a/src/external/catch2/src/catch2/internal/catch_test_registry.cpp +++ b/src/external/catch2/src/catch2/internal/catch_test_registry.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -17,9 +16,12 @@ #include namespace Catch { + void ITestInvoker::prepareTestCase() {} + void ITestInvoker::tearDownTestCase() {} + ITestInvoker::~ITestInvoker() = default; namespace { - StringRef extractClassName( StringRef classOrMethodName ) { + static StringRef extractClassName( StringRef classOrMethodName ) { if ( !startsWith( classOrMethodName, '&' ) ) { return classOrMethodName; } @@ -46,6 +48,18 @@ namespace Catch { static_cast( startIdx ), static_cast( classNameSize ) ); } + + class TestInvokerAsFunction final : public ITestInvoker { + using TestType = void ( * )(); + TestType m_testAsFunction; + + public: + constexpr TestInvokerAsFunction( TestType testAsFunction ) noexcept: + m_testAsFunction( testAsFunction ) {} + + void invoke() const override { m_testAsFunction(); } + }; + } // namespace Detail::unique_ptr makeTestInvoker( void(*testAsFunction)() ) { diff --git a/src/external/catch2/src/catch2/internal/catch_test_registry.hpp b/src/external/catch2/src/catch2/internal/catch_test_registry.hpp index f53b93c8..5c3a226d 100644 --- a/src/external/catch2/src/catch2/internal/catch_test_registry.hpp +++ b/src/external/catch2/src/catch2/internal/catch_test_registry.hpp @@ -8,9 +8,10 @@ #ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED #define CATCH_TEST_REGISTRY_HPP_INCLUDED +#include #include #include -#include +#include #include #include #include @@ -31,7 +32,8 @@ template class TestInvokerAsMethod : public ITestInvoker { void (C::*m_testAsMethod)(); public: - TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} + constexpr TestInvokerAsMethod( void ( C::*testAsMethod )() ) noexcept: + m_testAsMethod( testAsMethod ) {} void invoke() const override { C obj; @@ -46,6 +48,34 @@ Detail::unique_ptr makeTestInvoker( void (C::*testAsMethod)() ) { return Detail::make_unique>( testAsMethod ); } +template +class TestInvokerFixture : public ITestInvoker { + void ( C::*m_testAsMethod )() const; + Detail::unique_ptr m_fixture = nullptr; + +public: + constexpr TestInvokerFixture( void ( C::*testAsMethod )() const ) noexcept: + m_testAsMethod( testAsMethod ) {} + + void prepareTestCase() override { + m_fixture = Detail::make_unique(); + } + + void tearDownTestCase() override { + m_fixture.reset(); + } + + void invoke() const override { + auto* f = m_fixture.get(); + ( f->*m_testAsMethod )(); + } +}; + +template +Detail::unique_ptr makeTestInvokerFixture( void ( C::*testAsMethod )() const ) { + return Detail::make_unique>( testAsMethod ); +} + struct NameAndTags { constexpr NameAndTags( StringRef name_ = StringRef(), StringRef tags_ = StringRef() ) noexcept: @@ -72,6 +102,9 @@ struct AutoReg : Detail::NonCopyable { void TestName::test() #endif + +#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ @@ -84,19 +117,40 @@ struct AutoReg : Detail::NonCopyable { #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ ) - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - namespace { \ - const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ - Catch::makeTestInvoker( &QualifiedMethod ), \ - CATCH_INTERNAL_LINEINFO, \ - "&" #QualifiedMethod##_catch_sr, \ - Catch::NameAndTags{ __VA_ARGS__ } ); \ - } /* NOLINT */ \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT + + +// Dummy registrator for the dumy test case macros +namespace Catch { + namespace Detail { + struct DummyUse { + DummyUse( void ( * )( int ), Catch::NameAndTags const& ); + }; + } // namespace Detail +} // namespace Catch + +// Note that both the presence of the argument and its exact name are +// necessary for the section support. + +// We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE` +// tests can compile. The redefined `TEST_CASE` shadows this with param. +static int catchInternalSectionHint = 0; + +# define INTERNAL_CATCH_TESTCASE2( fname, ... ) \ + static void fname( int ); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \ + dummyUser )( &(fname), Catch::NameAndTags{ __VA_ARGS__ } ); \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + static void fname( [[maybe_unused]] int catchInternalSectionHint ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +# define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ), __VA_ARGS__ ) + + +#endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ @@ -118,6 +172,42 @@ struct AutoReg : Detail::NonCopyable { #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( TestName, ClassName, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace { \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS( ClassName ) { \ + void test() const; \ + }; \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ + Catch::makeTestInvokerFixture( &TestName::test ), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName##_catch_sr, \ + Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + void TestName::test() const + #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) + + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace { \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ + Catch::makeTestInvoker( &QualifiedMethod ), \ + CATCH_INTERNAL_LINEINFO, \ + "&" #QualifiedMethod##_catch_sr, \ + Catch::NameAndTags{ __VA_ARGS__ } ); \ + } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ do { \ diff --git a/src/external/catch2/src/catch2/internal/catch_test_run_info.hpp b/src/external/catch2/src/catch2/internal/catch_test_run_info.hpp new file mode 100644 index 00000000..90357b0a --- /dev/null +++ b/src/external/catch2/src/catch2/internal/catch_test_run_info.hpp @@ -0,0 +1,22 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +#ifndef CATCH_TEST_RUN_INFO_HPP_INCLUDED +#define CATCH_TEST_RUN_INFO_HPP_INCLUDED + +#include + +namespace Catch { + + struct TestRunInfo { + constexpr TestRunInfo(StringRef _name) : name(_name) {} + StringRef name; + }; + +} // end namespace Catch + +#endif // CATCH_TEST_RUN_INFO_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_textflow.cpp b/src/external/catch2/src/catch2/internal/catch_textflow.cpp index 7eac9732..1c21d20e 100644 --- a/src/external/catch2/src/catch2/internal/catch_textflow.cpp +++ b/src/external/catch2/src/catch2/internal/catch_textflow.cpp @@ -26,117 +26,228 @@ namespace { return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr; } - bool isBoundary( std::string const& line, size_t at ) { - assert( at > 0 ); - assert( at <= line.size() ); - - return at == line.size() || - ( isWhitespace( line[at] ) && !isWhitespace( line[at - 1] ) ) || - isBreakableBefore( line[at] ) || - isBreakableAfter( line[at - 1] ); - } - } // namespace namespace Catch { namespace TextFlow { + void AnsiSkippingString::preprocessString() { + for ( auto it = m_string.begin(); it != m_string.end(); ) { + // try to read through an ansi sequence + while ( it != m_string.end() && *it == '\033' && + it + 1 != m_string.end() && *( it + 1 ) == '[' ) { + auto cursor = it + 2; + while ( cursor != m_string.end() && + ( isdigit( *cursor ) || *cursor == ';' ) ) { + ++cursor; + } + if ( cursor == m_string.end() || *cursor != 'm' ) { + break; + } + // 'm' -> 0xff + *cursor = AnsiSkippingString::sentinel; + // if we've read an ansi sequence, set the iterator and + // return to the top of the loop + it = cursor + 1; + } + if ( it != m_string.end() ) { + ++m_size; + ++it; + } + } + } + + AnsiSkippingString::AnsiSkippingString( std::string const& text ): + m_string( text ) { + preprocessString(); + } + + AnsiSkippingString::AnsiSkippingString( std::string&& text ): + m_string( CATCH_MOVE( text ) ) { + preprocessString(); + } + + AnsiSkippingString::const_iterator AnsiSkippingString::begin() const { + return const_iterator( m_string ); + } + + AnsiSkippingString::const_iterator AnsiSkippingString::end() const { + return const_iterator( m_string, const_iterator::EndTag{} ); + } + + std::string AnsiSkippingString::substring( const_iterator begin, + const_iterator end ) const { + // There's one caveat here to an otherwise simple substring: when + // making a begin iterator we might have skipped ansi sequences at + // the start. If `begin` here is a begin iterator, skipped over + // initial ansi sequences, we'll use the true beginning of the + // string. Lastly: We need to transform any chars we replaced with + // 0xff back to 'm' + auto str = std::string( begin == this->begin() ? m_string.begin() + : begin.m_it, + end.m_it ); + std::transform( str.begin(), str.end(), str.begin(), []( char c ) { + return c == AnsiSkippingString::sentinel ? 'm' : c; + } ); + return str; + } + + void AnsiSkippingString::const_iterator::tryParseAnsiEscapes() { + // check if we've landed on an ansi sequence, and if so read through + // it + while ( m_it != m_string->end() && *m_it == '\033' && + m_it + 1 != m_string->end() && *( m_it + 1 ) == '[' ) { + auto cursor = m_it + 2; + while ( cursor != m_string->end() && + ( isdigit( *cursor ) || *cursor == ';' ) ) { + ++cursor; + } + if ( cursor == m_string->end() || + *cursor != AnsiSkippingString::sentinel ) { + break; + } + // if we've read an ansi sequence, set the iterator and + // return to the top of the loop + m_it = cursor + 1; + } + } + + void AnsiSkippingString::const_iterator::advance() { + assert( m_it != m_string->end() ); + m_it++; + tryParseAnsiEscapes(); + } + + void AnsiSkippingString::const_iterator::unadvance() { + assert( m_it != m_string->begin() ); + m_it--; + // if *m_it is 0xff, scan back to the \033 and then m_it-- once more + // (and repeat check) + while ( *m_it == AnsiSkippingString::sentinel ) { + while ( *m_it != '\033' ) { + assert( m_it != m_string->begin() ); + m_it--; + } + // if this happens, we must have been a begin iterator that had + // skipped over ansi sequences at the start of a string + assert( m_it != m_string->begin() ); + assert( *m_it == '\033' ); + m_it--; + } + } + + static bool isBoundary( AnsiSkippingString const& line, + AnsiSkippingString::const_iterator it ) { + return it == line.end() || + ( isWhitespace( *it ) && + !isWhitespace( *it.oneBefore() ) ) || + isBreakableBefore( *it ) || + isBreakableAfter( *it.oneBefore() ); + } void Column::const_iterator::calcLength() { m_addHyphen = false; m_parsedTo = m_lineStart; + AnsiSkippingString const& current_line = m_column.m_string; - std::string const& current_line = m_column.m_string; - if ( current_line[m_lineStart] == '\n' ) { - ++m_parsedTo; + if ( m_parsedTo == current_line.end() ) { + m_lineEnd = m_parsedTo; + return; } + assert( m_lineStart != current_line.end() ); + if ( *m_lineStart == '\n' ) { ++m_parsedTo; } + const auto maxLineLength = m_column.m_width - indentSize(); - const auto maxParseTo = std::min(current_line.size(), m_lineStart + maxLineLength); - while ( m_parsedTo < maxParseTo && - current_line[m_parsedTo] != '\n' ) { + std::size_t lineLength = 0; + while ( m_parsedTo != current_line.end() && + lineLength < maxLineLength && *m_parsedTo != '\n' ) { ++m_parsedTo; + ++lineLength; } // If we encountered a newline before the column is filled, // then we linebreak at the newline and consider this line // finished. - if ( m_parsedTo < m_lineStart + maxLineLength ) { - m_lineLength = m_parsedTo - m_lineStart; + if ( lineLength < maxLineLength ) { + m_lineEnd = m_parsedTo; } else { // Look for a natural linebreak boundary in the column // (We look from the end, so that the first found boundary is // the right one) - size_t newLineLength = maxLineLength; - while ( newLineLength > 0 && !isBoundary( current_line, m_lineStart + newLineLength ) ) { - --newLineLength; + m_lineEnd = m_parsedTo; + while ( lineLength > 0 && + !isBoundary( current_line, m_lineEnd ) ) { + --lineLength; + --m_lineEnd; } - while ( newLineLength > 0 && - isWhitespace( current_line[m_lineStart + newLineLength - 1] ) ) { - --newLineLength; + while ( lineLength > 0 && + isWhitespace( *m_lineEnd.oneBefore() ) ) { + --lineLength; + --m_lineEnd; } - // If we found one, then that is where we linebreak - if ( newLineLength > 0 ) { - m_lineLength = newLineLength; - } else { - // Otherwise we have to split text with a hyphen + // If we found one, then that is where we linebreak, otherwise + // we have to split text with a hyphen + if ( lineLength == 0 ) { m_addHyphen = true; - m_lineLength = maxLineLength - 1; + m_lineEnd = m_parsedTo.oneBefore(); } } } size_t Column::const_iterator::indentSize() const { - auto initial = - m_lineStart == 0 ? m_column.m_initialIndent : std::string::npos; + auto initial = m_lineStart == m_column.m_string.begin() + ? m_column.m_initialIndent + : std::string::npos; return initial == std::string::npos ? m_column.m_indent : initial; } - std::string - Column::const_iterator::addIndentAndSuffix( size_t position, - size_t length ) const { + std::string Column::const_iterator::addIndentAndSuffix( + AnsiSkippingString::const_iterator start, + AnsiSkippingString::const_iterator end ) const { std::string ret; const auto desired_indent = indentSize(); - ret.reserve( desired_indent + length + m_addHyphen ); + // ret.reserve( desired_indent + (end - start) + m_addHyphen ); ret.append( desired_indent, ' ' ); - ret.append( m_column.m_string, position, length ); - if ( m_addHyphen ) { - ret.push_back( '-' ); - } + // ret.append( start, end ); + ret += m_column.m_string.substring( start, end ); + if ( m_addHyphen ) { ret.push_back( '-' ); } return ret; } - Column::const_iterator::const_iterator( Column const& column ): m_column( column ) { + Column::const_iterator::const_iterator( Column const& column ): + m_column( column ), + m_lineStart( column.m_string.begin() ), + m_lineEnd( column.m_string.begin() ), + m_parsedTo( column.m_string.begin() ) { assert( m_column.m_width > m_column.m_indent ); assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); calcLength(); - if ( m_lineLength == 0 ) { - m_lineStart = m_column.m_string.size(); + if ( m_lineStart == m_lineEnd ) { + m_lineStart = m_column.m_string.end(); } } std::string Column::const_iterator::operator*() const { assert( m_lineStart <= m_parsedTo ); - return addIndentAndSuffix( m_lineStart, m_lineLength ); + return addIndentAndSuffix( m_lineStart, m_lineEnd ); } Column::const_iterator& Column::const_iterator::operator++() { - m_lineStart += m_lineLength; - std::string const& current_line = m_column.m_string; - if ( m_lineStart < current_line.size() && current_line[m_lineStart] == '\n' ) { - m_lineStart += 1; + m_lineStart = m_lineEnd; + AnsiSkippingString const& current_line = m_column.m_string; + if ( m_lineStart != current_line.end() && *m_lineStart == '\n' ) { + m_lineStart++; } else { - while ( m_lineStart < current_line.size() && - isWhitespace( current_line[m_lineStart] ) ) { + while ( m_lineStart != current_line.end() && + isWhitespace( *m_lineStart ) ) { ++m_lineStart; } } - if ( m_lineStart != current_line.size() ) { - calcLength(); - } + if ( m_lineStart != current_line.end() ) { calcLength(); } return *this; } @@ -233,23 +344,36 @@ namespace Catch { return os; } - Columns Column::operator+( Column const& other ) { + Columns operator+( Column const& lhs, Column const& rhs ) { Columns cols; - cols += *this; - cols += other; + cols += lhs; + cols += rhs; return cols; } - - Columns& Columns::operator+=( Column const& col ) { - m_columns.push_back( col ); - return *this; + Columns operator+( Column&& lhs, Column&& rhs ) { + Columns cols; + cols += CATCH_MOVE( lhs ); + cols += CATCH_MOVE( rhs ); + return cols; } - Columns Columns::operator+( Column const& col ) { - Columns combined = *this; - combined += col; + Columns& operator+=( Columns& lhs, Column const& rhs ) { + lhs.m_columns.push_back( rhs ); + return lhs; + } + Columns& operator+=( Columns& lhs, Column&& rhs ) { + lhs.m_columns.push_back( CATCH_MOVE( rhs ) ); + return lhs; + } + Columns operator+( Columns const& lhs, Column const& rhs ) { + auto combined( lhs ); + combined += rhs; return combined; } + Columns operator+( Columns&& lhs, Column&& rhs ) { + lhs += CATCH_MOVE( rhs ); + return CATCH_MOVE( lhs ); + } } // namespace TextFlow } // namespace Catch diff --git a/src/external/catch2/src/catch2/internal/catch_textflow.hpp b/src/external/catch2/src/catch2/internal/catch_textflow.hpp index ceac675d..2d9d78a5 100644 --- a/src/external/catch2/src/catch2/internal/catch_textflow.hpp +++ b/src/external/catch2/src/catch2/internal/catch_textflow.hpp @@ -8,8 +8,10 @@ #ifndef CATCH_TEXTFLOW_HPP_INCLUDED #define CATCH_TEXTFLOW_HPP_INCLUDED -#include #include +#include + +#include #include #include @@ -18,6 +20,107 @@ namespace Catch { class Columns; + /** + * Abstraction for a string with ansi escape sequences that + * automatically skips over escapes when iterating. Only graphical + * escape sequences are considered. + * + * Internal representation: + * An escape sequence looks like \033[39;49m + * We need bidirectional iteration and the unbound length of escape + * sequences poses a problem for operator-- To make this work we'll + * replace the last `m` with a 0xff (this is a codepoint that won't have + * any utf-8 meaning). + */ + class AnsiSkippingString { + std::string m_string; + std::size_t m_size = 0; + + // perform 0xff replacement and calculate m_size + void preprocessString(); + + public: + class const_iterator; + using iterator = const_iterator; + // note: must be u-suffixed or this will cause a "truncation of + // constant value" warning on MSVC + static constexpr char sentinel = static_cast( 0xffu ); + + explicit AnsiSkippingString( std::string const& text ); + explicit AnsiSkippingString( std::string&& text ); + + const_iterator begin() const; + const_iterator end() const; + + size_t size() const { return m_size; } + + std::string substring( const_iterator begin, + const_iterator end ) const; + }; + + class AnsiSkippingString::const_iterator { + friend AnsiSkippingString; + struct EndTag {}; + + const std::string* m_string; + std::string::const_iterator m_it; + + explicit const_iterator( const std::string& string, EndTag ): + m_string( &string ), m_it( string.end() ) {} + + void tryParseAnsiEscapes(); + void advance(); + void unadvance(); + + public: + using difference_type = std::ptrdiff_t; + using value_type = char; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::bidirectional_iterator_tag; + + explicit const_iterator( const std::string& string ): + m_string( &string ), m_it( string.begin() ) { + tryParseAnsiEscapes(); + } + + char operator*() const { return *m_it; } + + const_iterator& operator++() { + advance(); + return *this; + } + const_iterator operator++( int ) { + iterator prev( *this ); + operator++(); + return prev; + } + const_iterator& operator--() { + unadvance(); + return *this; + } + const_iterator operator--( int ) { + iterator prev( *this ); + operator--(); + return prev; + } + + bool operator==( const_iterator const& other ) const { + return m_it == other.m_it; + } + bool operator!=( const_iterator const& other ) const { + return !operator==( other ); + } + bool operator<=( const_iterator const& other ) const { + return m_it <= other.m_it; + } + + const_iterator oneBefore() const { + auto it = *this; + return --it; + } + }; + /** * Represents a column of text with specific width and indentation * @@ -27,17 +130,18 @@ namespace Catch { */ class Column { // String to be written out - std::string m_string; + AnsiSkippingString m_string; // Width of the column for linebreaking size_t m_width = CATCH_CONFIG_CONSOLE_WIDTH - 1; - // Indentation of other lines (including first if initial indent is unset) + // Indentation of other lines (including first if initial indent is + // unset) size_t m_indent = 0; // Indentation of the first line size_t m_initialIndent = std::string::npos; public: /** - * Iterates "lines" in `Column` and return sthem + * Iterates "lines" in `Column` and returns them */ class const_iterator { friend Column; @@ -45,27 +149,31 @@ namespace Catch { Column const& m_column; // Where does the current line start? - size_t m_lineStart = 0; + AnsiSkippingString::const_iterator m_lineStart; // How long should the current line be? - size_t m_lineLength = 0; + AnsiSkippingString::const_iterator m_lineEnd; // How far have we checked the string to iterate? - size_t m_parsedTo = 0; + AnsiSkippingString::const_iterator m_parsedTo; // Should a '-' be appended to the line? bool m_addHyphen = false; const_iterator( Column const& column, EndTag ): - m_column( column ), m_lineStart( m_column.m_string.size() ) {} + m_column( column ), + m_lineStart( m_column.m_string.end() ), + m_lineEnd( column.m_string.end() ), + m_parsedTo( column.m_string.end() ) {} // Calculates the length of the current line void calcLength(); - // Returns current indention width + // Returns current indentation width size_t indentSize() const; // Creates an indented and (optionally) suffixed string from // current iterator position, indentation and length. - std::string addIndentAndSuffix( size_t position, - size_t length ) const; + std::string addIndentAndSuffix( + AnsiSkippingString::const_iterator start, + AnsiSkippingString::const_iterator end ) const; public: using difference_type = std::ptrdiff_t; @@ -82,7 +190,8 @@ namespace Catch { const_iterator operator++( int ); bool operator==( const_iterator const& other ) const { - return m_lineStart == other.m_lineStart && &m_column == &other.m_column; + return m_lineStart == other.m_lineStart && + &m_column == &other.m_column; } bool operator!=( const_iterator const& other ) const { return !operator==( other ); @@ -91,29 +200,47 @@ namespace Catch { using iterator = const_iterator; explicit Column( std::string const& text ): m_string( text ) {} + explicit Column( std::string&& text ): + m_string( CATCH_MOVE( text ) ) {} - Column& width( size_t newWidth ) { + Column& width( size_t newWidth ) & { assert( newWidth > 0 ); m_width = newWidth; return *this; } - Column& indent( size_t newIndent ) { + Column&& width( size_t newWidth ) && { + assert( newWidth > 0 ); + m_width = newWidth; + return CATCH_MOVE( *this ); + } + Column& indent( size_t newIndent ) & { m_indent = newIndent; return *this; } - Column& initialIndent( size_t newIndent ) { + Column&& indent( size_t newIndent ) && { + m_indent = newIndent; + return CATCH_MOVE( *this ); + } + Column& initialIndent( size_t newIndent ) & { m_initialIndent = newIndent; return *this; } + Column&& initialIndent( size_t newIndent ) && { + m_initialIndent = newIndent; + return CATCH_MOVE( *this ); + } size_t width() const { return m_width; } const_iterator begin() const { return const_iterator( *this ); } - const_iterator end() const { return { *this, const_iterator::EndTag{} }; } + const_iterator end() const { + return { *this, const_iterator::EndTag{} }; + } friend std::ostream& operator<<( std::ostream& os, Column const& col ); - Columns operator+( Column const& other ); + friend Columns operator+( Column const& lhs, Column const& rhs ); + friend Columns operator+( Column&& lhs, Column&& rhs ); }; //! Creates a column that serves as an empty space of specific width @@ -157,8 +284,10 @@ namespace Catch { iterator begin() const { return iterator( *this ); } iterator end() const { return { *this, iterator::EndTag() }; } - Columns& operator+=( Column const& col ); - Columns operator+( Column const& col ); + friend Columns& operator+=( Columns& lhs, Column const& rhs ); + friend Columns& operator+=( Columns& lhs, Column&& rhs ); + friend Columns operator+( Columns const& lhs, Column const& rhs ); + friend Columns operator+( Columns&& lhs, Column&& rhs ); friend std::ostream& operator<<( std::ostream& os, Columns const& cols ); diff --git a/src/external/catch2/src/catch2/internal/catch_uncaught_exceptions.cpp b/src/external/catch2/src/catch2/internal/catch_uncaught_exceptions.cpp index 704d6e1c..8cfabc0f 100644 --- a/src/external/catch2/src/catch2/internal/catch_uncaught_exceptions.cpp +++ b/src/external/catch2/src/catch2/internal/catch_uncaught_exceptions.cpp @@ -7,7 +7,6 @@ // SPDX-License-Identifier: BSL-1.0 #include -#include #include #include diff --git a/src/external/catch2/src/catch2/internal/catch_uniform_floating_point_distribution.hpp b/src/external/catch2/src/catch2/internal/catch_uniform_floating_point_distribution.hpp new file mode 100644 index 00000000..23d03b43 --- /dev/null +++ b/src/external/catch2/src/catch2/internal/catch_uniform_floating_point_distribution.hpp @@ -0,0 +1,131 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED +#define CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED + +#include +#include + +#include +#include + +namespace Catch { + + namespace Detail { +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + // The issue with overflow only happens with maximal ULP and HUGE + // distance, e.g. when generating numbers in [-inf, inf] for given + // type. So we only check for the largest possible ULP in the + // type, and return something that does not overflow to inf in 1 mult. + constexpr std::uint64_t calculate_max_steps_in_one_go(double gamma) { + if ( gamma == 1.99584030953472e+292 ) { return 9007199254740991; } + return static_cast( -1 ); + } + constexpr std::uint32_t calculate_max_steps_in_one_go(float gamma) { + if ( gamma == 2.028241e+31f ) { return 16777215; } + return static_cast( -1 ); + } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + } + +/** + * Implementation of uniform distribution on floating point numbers. + * + * Note that we support only `float` and `double` types, because these + * usually mean the same thing across different platform. `long double` + * varies wildly by platform and thus we cannot provide reproducible + * implementation. Also note that we don't implement all parts of + * distribution per standard: this distribution is not serializable, nor + * can the range be arbitrarily reset. + * + * The implementation also uses different approach than the one taken by + * `std::uniform_real_distribution`, where instead of generating a number + * between [0, 1) and then multiplying the range bounds with it, we first + * split the [a, b] range into a set of equidistributed floating point + * numbers, and then use uniform int distribution to pick which one to + * return. + * + * This has the advantage of guaranteeing uniformity (the multiplication + * method loses uniformity due to rounding when multiplying floats), except + * for small non-uniformity at one side of the interval, where we have + * to deal with the fact that not every interval is splittable into + * equidistributed floats. + * + * Based on "Drawing random floating-point numbers from an interval" by + * Frederic Goualard. + */ +template +class uniform_floating_point_distribution { + static_assert(std::is_floating_point::value, "..."); + static_assert(!std::is_same::value, + "We do not support long double due to inconsistent behaviour between platforms"); + + using WidthType = Detail::DistanceType; + + FloatType m_a, m_b; + FloatType m_ulp_magnitude; + WidthType m_floats_in_range; + uniform_integer_distribution m_int_dist; + + // In specific cases, we can overflow into `inf` when computing the + // `steps * g` offset. To avoid this, we don't offset by more than this + // in one multiply + addition. + WidthType m_max_steps_in_one_go; + // We don't want to do the magnitude check every call to `operator()` + bool m_a_has_leq_magnitude; + +public: + using result_type = FloatType; + + uniform_floating_point_distribution( FloatType a, FloatType b ): + m_a( a ), + m_b( b ), + m_ulp_magnitude( Detail::gamma( m_a, m_b ) ), + m_floats_in_range( Detail::count_equidistant_floats( m_a, m_b, m_ulp_magnitude ) ), + m_int_dist(0, m_floats_in_range), + m_max_steps_in_one_go( Detail::calculate_max_steps_in_one_go(m_ulp_magnitude)), + m_a_has_leq_magnitude(std::fabs(m_a) <= std::fabs(m_b)) + { + assert( a <= b ); + } + + template + result_type operator()( Generator& g ) { + WidthType steps = m_int_dist( g ); + if ( m_a_has_leq_magnitude ) { + if ( steps == m_floats_in_range ) { return m_a; } + auto b = m_b; + while (steps > m_max_steps_in_one_go) { + b -= m_max_steps_in_one_go * m_ulp_magnitude; + steps -= m_max_steps_in_one_go; + } + return b - steps * m_ulp_magnitude; + } else { + if ( steps == m_floats_in_range ) { return m_b; } + auto a = m_a; + while (steps > m_max_steps_in_one_go) { + a += m_max_steps_in_one_go * m_ulp_magnitude; + steps -= m_max_steps_in_one_go; + } + return a + steps * m_ulp_magnitude; + } + } + + result_type a() const { return m_a; } + result_type b() const { return m_b; } +}; + +} // end namespace Catch + +#endif // CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_uniform_integer_distribution.hpp b/src/external/catch2/src/catch2/internal/catch_uniform_integer_distribution.hpp new file mode 100644 index 00000000..799a93e2 --- /dev/null +++ b/src/external/catch2/src/catch2/internal/catch_uniform_integer_distribution.hpp @@ -0,0 +1,108 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED +#define CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED + +#include + +namespace Catch { + +/** + * Implementation of uniform distribution on integers. + * + * Unlike `std::uniform_int_distribution`, this implementation supports + * various 1 byte integral types, including bool (but you should not + * actually use it for bools). + * + * The underlying algorithm is based on the one described in "Fast Random + * Integer Generation in an Interval" by Daniel Lemire, but has been + * optimized under the assumption of reuse of the same distribution object. + */ +template +class uniform_integer_distribution { + static_assert(std::is_integral::value, "..."); + + using UnsignedIntegerType = Detail::SizedUnsignedType_t; + + // Only the left bound is stored, and we store it converted to its + // unsigned image. This avoids having to do the conversions inside + // the operator(), at the cost of having to do the conversion in + // the a() getter. The right bound is only needed in the b() getter, + // so we recompute it there from other stored data. + UnsignedIntegerType m_a; + + // How many different values are there in [a, b]. a == b => 1, can be 0 for distribution over all values in the type. + UnsignedIntegerType m_ab_distance; + + // We hoisted this out of the main generation function. Technically, + // this means that using this distribution will be slower than Lemire's + // algorithm if this distribution instance will be used only few times, + // but it will be faster if it is used many times. Since Catch2 uses + // distributions only to implement random generators, we assume that each + // distribution will be reused many times and this is an optimization. + UnsignedIntegerType m_rejection_threshold = 0; + + static constexpr UnsignedIntegerType computeDistance(IntegerType a, IntegerType b) { + // This overflows and returns 0 if a == 0 and b == TYPE_MAX. + // We handle that later when generating the number. + return transposeTo(b) - transposeTo(a) + 1; + } + + static constexpr UnsignedIntegerType computeRejectionThreshold(UnsignedIntegerType ab_distance) { + // distance == 0 means that we will return all possible values from + // the type's range, and that we shouldn't reject anything. + if ( ab_distance == 0 ) { return 0; } + return ( ~ab_distance + 1 ) % ab_distance; + } + + static constexpr UnsignedIntegerType transposeTo(IntegerType in) { + return Detail::transposeToNaturalOrder( + static_cast( in ) ); + } + static constexpr IntegerType transposeBack(UnsignedIntegerType in) { + return static_cast( + Detail::transposeToNaturalOrder(in) ); + } + +public: + using result_type = IntegerType; + + constexpr uniform_integer_distribution( IntegerType a, IntegerType b ): + m_a( transposeTo(a) ), + m_ab_distance( computeDistance(a, b) ), + m_rejection_threshold( computeRejectionThreshold(m_ab_distance) ) { + assert( a <= b ); + } + + template + constexpr result_type operator()( Generator& g ) { + // All possible values of result_type are valid. + if ( m_ab_distance == 0 ) { + return transposeBack( Detail::fillBitsFrom( g ) ); + } + + auto random_number = Detail::fillBitsFrom( g ); + auto emul = Detail::extendedMult( random_number, m_ab_distance ); + // Unlike Lemire's algorithm we skip the ab_distance check, since + // we precomputed the rejection threshold, which is always tighter. + while (emul.lower < m_rejection_threshold) { + random_number = Detail::fillBitsFrom( g ); + emul = Detail::extendedMult( random_number, m_ab_distance ); + } + + return transposeBack(m_a + emul.upper); + } + + constexpr result_type a() const { return transposeBack(m_a); } + constexpr result_type b() const { return transposeBack(m_ab_distance + m_a - 1); } +}; + +} // end namespace Catch + +#endif // CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/internal/catch_wildcard_pattern.hpp b/src/external/catch2/src/catch2/internal/catch_wildcard_pattern.hpp index 72479ba8..4f410859 100644 --- a/src/external/catch2/src/catch2/internal/catch_wildcard_pattern.hpp +++ b/src/external/catch2/src/catch2/internal/catch_wildcard_pattern.hpp @@ -8,7 +8,7 @@ #ifndef CATCH_WILDCARD_PATTERN_HPP_INCLUDED #define CATCH_WILDCARD_PATTERN_HPP_INCLUDED -#include +#include #include diff --git a/src/external/catch2/src/catch2/internal/catch_xmlwriter.cpp b/src/external/catch2/src/catch2/internal/catch_xmlwriter.cpp index 6c1d45df..ccf63a56 100644 --- a/src/external/catch2/src/catch2/internal/catch_xmlwriter.cpp +++ b/src/external/catch2/src/catch2/internal/catch_xmlwriter.cpp @@ -53,36 +53,16 @@ namespace { os.flags(f); } - bool shouldNewline(XmlFormatting fmt) { + constexpr bool shouldNewline(XmlFormatting fmt) { return !!(static_cast>(fmt & XmlFormatting::Newline)); } - bool shouldIndent(XmlFormatting fmt) { + constexpr bool shouldIndent(XmlFormatting fmt) { return !!(static_cast>(fmt & XmlFormatting::Indent)); } } // anonymous namespace - XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) { - return static_cast( - static_cast>(lhs) | - static_cast>(rhs) - ); - } - - XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) { - return static_cast( - static_cast>(lhs) & - static_cast>(rhs) - ); - } - - - XmlEncode::XmlEncode( StringRef str, ForWhat forWhat ) - : m_str( str ), - m_forWhat( forWhat ) - {} - void XmlEncode::encodeTo( std::ostream& os ) const { // Apostrophe escaping not necessary if we always use " to write attributes // (see: http://www.w3.org/TR/xml/#syntax) diff --git a/src/external/catch2/src/catch2/internal/catch_xmlwriter.hpp b/src/external/catch2/src/catch2/internal/catch_xmlwriter.hpp index ec55f3c4..22b42c5c 100644 --- a/src/external/catch2/src/catch2/internal/catch_xmlwriter.hpp +++ b/src/external/catch2/src/catch2/internal/catch_xmlwriter.hpp @@ -13,16 +13,25 @@ #include #include +#include namespace Catch { - enum class XmlFormatting { + enum class XmlFormatting : std::uint8_t { None = 0x00, Indent = 0x01, Newline = 0x02, }; - XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs); - XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs); + constexpr XmlFormatting operator|( XmlFormatting lhs, XmlFormatting rhs ) { + return static_cast( static_cast( lhs ) | + static_cast( rhs ) ); + } + + constexpr XmlFormatting operator&( XmlFormatting lhs, XmlFormatting rhs ) { + return static_cast( static_cast( lhs ) & + static_cast( rhs ) ); + } + /** * Helper for XML-encoding text (escaping angle brackets, quotes, etc) @@ -34,7 +43,9 @@ namespace Catch { public: enum ForWhat { ForTextNodes, ForAttributes }; - XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ); + constexpr XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ): + m_str( str ), m_forWhat( forWhat ) {} + void encodeTo( std::ostream& os ) const; diff --git a/src/external/catch2/src/catch2/matchers/catch_matchers_floating_point.cpp b/src/external/catch2/src/catch2/matchers/catch_matchers_floating_point.cpp index 6e596466..fc7b444e 100644 --- a/src/external/catch2/src/catch2/matchers/catch_matchers_floating_point.cpp +++ b/src/external/catch2/src/catch2/matchers/catch_matchers_floating_point.cpp @@ -38,26 +38,11 @@ namespace { return ulpDist <= maxUlpDiff; } -#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) - - float nextafter(float x, float y) { - return ::nextafterf(x, y); - } - - double nextafter(double x, double y) { - return ::nextafter(x, y); - } - -#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^ template FP step(FP start, FP direction, uint64_t steps) { for (uint64_t i = 0; i < steps; ++i) { -#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) start = Catch::nextafter(start, direction); -#else - start = std::nextafter(start, direction); -#endif } return start; } @@ -191,7 +176,7 @@ namespace Detail { std::string WithinRelMatcher::describe() const { Catch::ReusableStringStream sstr; - sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other"; + sstr << "and " << ::Catch::Detail::stringify(m_target) << " are within " << m_epsilon * 100. << "% of each other"; return sstr.str(); } diff --git a/src/external/catch2/src/catch2/matchers/catch_matchers_range_equals.hpp b/src/external/catch2/src/catch2/matchers/catch_matchers_range_equals.hpp index ce66bed9..c4feece4 100644 --- a/src/external/catch2/src/catch2/matchers/catch_matchers_range_equals.hpp +++ b/src/external/catch2/src/catch2/matchers/catch_matchers_range_equals.hpp @@ -28,12 +28,14 @@ namespace Catch { public: template + constexpr RangeEqualsMatcher( TargetRangeLike2&& range, Equality2&& predicate ): m_desired( CATCH_FORWARD( range ) ), m_predicate( CATCH_FORWARD( predicate ) ) {} template + constexpr bool match( RangeLike&& rng ) const { auto rng_start = begin( rng ); const auto rng_end = end( rng ); @@ -66,12 +68,14 @@ namespace Catch { public: template + constexpr UnorderedRangeEqualsMatcher( TargetRangeLike2&& range, Equality2&& predicate ): m_desired( CATCH_FORWARD( range ) ), m_predicate( CATCH_FORWARD( predicate ) ) {} template + constexpr bool match( RangeLike&& rng ) const { using std::begin; using std::end; @@ -95,6 +99,7 @@ namespace Catch { * Uses `std::equal_to` to do the comparison */ template + constexpr std::enable_if_t::value, RangeEqualsMatcher>> RangeEquals( RangeLike&& range ) { @@ -108,6 +113,7 @@ namespace Catch { * Uses to provided predicate `predicate` to do the comparisons */ template + constexpr RangeEqualsMatcher RangeEquals( RangeLike&& range, Equality&& predicate ) { return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) }; @@ -120,6 +126,7 @@ namespace Catch { * Uses `std::equal_to` to do the comparison */ template + constexpr std::enable_if_t< !Detail::is_matcher::value, UnorderedRangeEqualsMatcher>> @@ -129,11 +136,12 @@ namespace Catch { /** * Creates a matcher that checks if all elements in a range are equal - * to all elements in another range, in some permuation. + * to all elements in another range, in some permutation. * * Uses to provided predicate `predicate` to do the comparisons */ template + constexpr UnorderedRangeEqualsMatcher UnorderedRangeEquals( RangeLike&& range, Equality&& predicate ) { return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) }; diff --git a/src/external/catch2/src/catch2/matchers/catch_matchers_string.hpp b/src/external/catch2/src/catch2/matchers/catch_matchers_string.hpp index 718022e3..61a385d0 100644 --- a/src/external/catch2/src/catch2/matchers/catch_matchers_string.hpp +++ b/src/external/catch2/src/catch2/matchers/catch_matchers_string.hpp @@ -8,9 +8,9 @@ #ifndef CATCH_MATCHERS_STRING_HPP_INCLUDED #define CATCH_MATCHERS_STRING_HPP_INCLUDED -#include -#include #include +#include +#include #include diff --git a/src/external/catch2/src/catch2/matchers/catch_matchers_vector.hpp b/src/external/catch2/src/catch2/matchers/catch_matchers_vector.hpp index 9a4b024f..fffbfdf6 100644 --- a/src/external/catch2/src/catch2/matchers/catch_matchers_vector.hpp +++ b/src/external/catch2/src/catch2/matchers/catch_matchers_vector.hpp @@ -85,11 +85,10 @@ namespace Matchers { // - a more general approach would be via a compare template that defaults // to using !=. but could be specialised for, e.g. std::vector etc // - then just call that directly - if (m_comparator.size() != v.size()) - return false; - for (std::size_t i = 0; i < v.size(); ++i) - if (m_comparator[i] != v[i]) - return false; + if ( m_comparator.size() != v.size() ) { return false; } + for ( std::size_t i = 0; i < v.size(); ++i ) { + if ( !( m_comparator[i] == v[i] ) ) { return false; } + } return true; } std::string describe() const override { diff --git a/src/external/catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp b/src/external/catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp index 12455bfe..24a3f8b6 100644 --- a/src/external/catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp +++ b/src/external/catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp @@ -8,17 +8,32 @@ #ifndef CATCH_MATCHERS_IMPL_HPP_INCLUDED #define CATCH_MATCHERS_IMPL_HPP_INCLUDED -#include +#include +#include +#include +#include #include +#include + namespace Catch { +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#elif defined __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + template class MatchExpr : public ITransientExpression { ArgT && m_arg; MatcherT const& m_matcher; public: - MatchExpr( ArgT && arg, MatcherT const& matcher ) + constexpr MatchExpr( ArgT && arg, MatcherT const& matcher ) : ITransientExpression{ true, matcher.match( arg ) }, // not forwarding arg here on purpose m_arg( CATCH_FORWARD(arg) ), m_matcher( matcher ) @@ -31,6 +46,13 @@ namespace Catch { } }; +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + + namespace Matchers { template class MatcherBase; @@ -41,7 +63,8 @@ namespace Catch { void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher ); template - auto makeMatchExpr( ArgT && arg, MatcherT const& matcher ) -> MatchExpr { + constexpr MatchExpr + makeMatchExpr( ArgT&& arg, MatcherT const& matcher ) { return MatchExpr( CATCH_FORWARD(arg), matcher ); } @@ -55,7 +78,7 @@ namespace Catch { INTERNAL_CATCH_TRY { \ catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher ) ); \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) @@ -65,7 +88,10 @@ namespace Catch { Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ if( catchAssertionHandler.allowThrows() ) \ try { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__ ); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( exceptionType const& ex ) { \ @@ -76,7 +102,7 @@ namespace Catch { } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + catchAssertionHandler.complete(); \ } while( false ) diff --git a/src/external/catch2/src/catch2/meson.build b/src/external/catch2/src/catch2/meson.build index 0e114065..65be3437 100644 --- a/src/external/catch2/src/catch2/meson.build +++ b/src/external/catch2/src/catch2/meson.build @@ -18,6 +18,8 @@ configure_file( configuration: conf_data, ) +fs = import('fs') + benchmark_headers = [ 'benchmark/catch_benchmark.hpp', 'benchmark/catch_benchmark_all.hpp', @@ -32,6 +34,8 @@ benchmark_headers = [ 'benchmark/catch_sample_analysis.hpp', 'benchmark/detail/catch_analyse.hpp', 'benchmark/detail/catch_benchmark_function.hpp', + 'benchmark/detail/catch_benchmark_stats.hpp', + 'benchmark/detail/catch_benchmark_stats_fwd.hpp', 'benchmark/detail/catch_complete_invoke.hpp', 'benchmark/detail/catch_estimate_clock.hpp', 'benchmark/detail/catch_measure.hpp', @@ -43,6 +47,7 @@ benchmark_headers = [ benchmark_sources = files( 'benchmark/catch_chronometer.cpp', + 'benchmark/detail/catch_analyse.cpp', 'benchmark/detail/catch_benchmark_function.cpp', 'benchmark/detail/catch_run_for_at_least.cpp', 'benchmark/detail/catch_stats.cpp', @@ -64,18 +69,19 @@ internal_headers = [ 'interfaces/catch_interfaces_registry_hub.hpp', 'interfaces/catch_interfaces_reporter.hpp', 'interfaces/catch_interfaces_reporter_factory.hpp', - 'interfaces/catch_interfaces_reporter_registry.hpp', 'interfaces/catch_interfaces_tag_alias_registry.hpp', + 'interfaces/catch_interfaces_test_invoker.hpp', 'interfaces/catch_interfaces_testcase.hpp', 'internal/catch_assertion_handler.hpp', 'internal/catch_case_insensitive_comparisons.hpp', - 'internal/catch_case_sensitive.hpp', 'internal/catch_clara.hpp', 'internal/catch_commandline.hpp', 'internal/catch_compare_traits.hpp', 'internal/catch_compiler_capabilities.hpp', 'internal/catch_config_android_logwrite.hpp', 'internal/catch_config_counter.hpp', + 'internal/catch_config_prefix_messages.hpp', + 'internal/catch_config_static_analysis_support.hpp', 'internal/catch_config_uncaught_exceptions.hpp', 'internal/catch_config_wchar.hpp', 'internal/catch_console_colour.hpp', @@ -94,6 +100,7 @@ internal_headers = [ 'internal/catch_getenv.hpp', 'internal/catch_istream.hpp', 'internal/catch_is_permutation.hpp', + 'internal/catch_jsonwriter.hpp', 'internal/catch_lazy_expr.hpp', 'internal/catch_leak_detector.hpp', 'internal/catch_list.hpp', @@ -108,7 +115,10 @@ internal_headers = [ 'internal/catch_platform.hpp', 'internal/catch_polyfills.hpp', 'internal/catch_preprocessor.hpp', + 'internal/catch_preprocessor_internal_stringify.hpp', 'internal/catch_preprocessor_remove_parens.hpp', + 'internal/catch_random_floating_point_helpers.hpp', + 'internal/catch_random_integer_helpers.hpp', 'internal/catch_random_number_generator.hpp', 'internal/catch_random_seed_generation.hpp', 'internal/catch_reporter_registry.hpp', @@ -133,10 +143,13 @@ internal_headers = [ 'internal/catch_test_failure_exception.hpp', 'internal/catch_test_macro_impl.hpp', 'internal/catch_test_registry.hpp', + 'internal/catch_test_run_info.hpp', 'internal/catch_test_spec_parser.hpp', 'internal/catch_textflow.hpp', 'internal/catch_to_string.hpp', 'internal/catch_uncaught_exceptions.hpp', + 'internal/catch_uniform_floating_point_distribution.hpp', + 'internal/catch_uniform_integer_distribution.hpp', 'internal/catch_unique_name.hpp', 'internal/catch_unique_ptr.hpp', 'internal/catch_void_type.hpp', @@ -151,6 +164,7 @@ internal_headers = [ 'matchers/catch_matchers_floating_point.hpp', 'matchers/catch_matchers_predicate.hpp', 'matchers/catch_matchers_quantifiers.hpp', + 'matchers/catch_matchers_range_equals.hpp', 'matchers/catch_matchers_string.hpp', 'matchers/catch_matchers_templated.hpp', 'matchers/catch_matchers_vector.hpp', @@ -159,6 +173,7 @@ internal_headers = [ 'catch_approx.hpp', 'catch_assertion_info.hpp', 'catch_assertion_result.hpp', + 'catch_case_sensitive.hpp', 'catch_config.hpp', 'catch_get_random_seed.hpp', 'catch_message.hpp', @@ -189,7 +204,6 @@ internal_sources = files( 'interfaces/catch_interfaces_registry_hub.cpp', 'interfaces/catch_interfaces_reporter.cpp', 'interfaces/catch_interfaces_reporter_factory.cpp', - 'interfaces/catch_interfaces_reporter_registry.cpp', 'interfaces/catch_interfaces_testcase.cpp', 'internal/catch_assertion_handler.cpp', 'internal/catch_case_insensitive_comparisons.cpp', @@ -208,6 +222,7 @@ internal_sources = files( 'internal/catch_floating_point_helpers.cpp', 'internal/catch_getenv.cpp', 'internal/catch_istream.cpp', + 'internal/catch_jsonwriter.cpp', 'internal/catch_lazy_expr.cpp', 'internal/catch_leak_detector.cpp', 'internal/catch_list.cpp', @@ -219,7 +234,6 @@ internal_sources = files( 'internal/catch_random_seed_generation.cpp', 'internal/catch_reporter_registry.cpp', 'internal/catch_reporter_spec_parser.cpp', - 'internal/catch_result_type.cpp', 'internal/catch_reusable_string_stream.cpp', 'internal/catch_run_context.cpp', 'internal/catch_section.cpp', @@ -262,6 +276,7 @@ internal_sources = files( 'catch_timer.cpp', 'catch_tostring.cpp', 'catch_totals.cpp', + 'catch_translate_exception.cpp', 'catch_version.cpp', ) @@ -273,6 +288,7 @@ reporter_headers = [ 'reporters/catch_reporter_cumulative_base.hpp', 'reporters/catch_reporter_event_listener.hpp', 'reporters/catch_reporter_helpers.hpp', + 'reporters/catch_reporter_json.hpp', 'reporters/catch_reporter_junit.hpp', 'reporters/catch_reporter_multi.hpp', 'reporters/catch_reporter_registrars.hpp', @@ -292,6 +308,7 @@ reporter_sources = files( 'reporters/catch_reporter_cumulative_base.cpp', 'reporters/catch_reporter_event_listener.cpp', 'reporters/catch_reporter_helpers.cpp', + 'reporters/catch_reporter_json.cpp', 'reporters/catch_reporter_junit.cpp', 'reporters/catch_reporter_multi.cpp', 'reporters/catch_reporter_registrars.cpp', @@ -325,9 +342,19 @@ foreach file : headers install_headers(file, subdir: join_paths(include_subdir, folder)) endforeach +catch2_dependencies = [] +# Check if this is an Android NDK build. +if ((host_machine.system() == 'android') or + # Check if this is an Android Termux build. + (host_machine.system() == 'linux' and fs.is_dir('/data/data/com.termux'))) + log_dep = meson.get_compiler('cpp').find_library('log') + catch2_dependencies += log_dep +endif + catch2 = static_library( 'Catch2', sources, + dependencies: catch2_dependencies, include_directories: '..', install: true, ) diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_automake.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_automake.cpp index 993b594b..5e506a6b 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_automake.cpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_automake.cpp @@ -12,7 +12,7 @@ namespace Catch { - AutomakeReporter::~AutomakeReporter() {} + AutomakeReporter::~AutomakeReporter() = default; void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_automake.hpp b/src/external/catch2/src/catch2/reporters/catch_reporter_automake.hpp index 3475a1fd..a639428c 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_automake.hpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_automake.hpp @@ -8,7 +8,6 @@ #ifndef CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED #define CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED -#include #include #include diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_compact.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_compact.cpp index 3a9b870c..0f855944 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_compact.cpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_compact.cpp @@ -171,7 +171,7 @@ class AssertionPrinter { return; const auto itEnd = messages.cend(); - const auto N = static_cast(std::distance(itMessage, itEnd)); + const auto N = static_cast(itEnd - itMessage); stream << colourImpl->guardColour( colour ) << " with " << pluralise( N, "message"_sr ) << ':'; @@ -249,6 +249,6 @@ class AssertionPrinter { StreamingReporterBase::testRunEnded( _testRunStats ); } - CompactReporter::~CompactReporter() {} + CompactReporter::~CompactReporter() = default; } // end namespace Catch diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_console.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_console.cpp index a46b22cf..c5678548 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_console.cpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_console.cpp @@ -209,15 +209,9 @@ findMax( std::size_t& i, std::size_t& j, std::size_t& k, std::size_t& l ) { return l; } -enum class Justification { Left, Right }; - -struct ColumnInfo { - std::string name; - std::size_t width; - Justification justification; -}; struct ColumnBreak {}; struct RowBreak {}; +struct OutputFlush {}; class Duration { enum class Unit { @@ -292,6 +286,14 @@ class Duration { }; } // end anon namespace +enum class Justification { Left, Right }; + +struct ColumnInfo { + std::string name; + std::size_t width; + Justification justification; +}; + class TablePrinter { std::ostream& m_os; std::vector m_columnInfos; @@ -314,11 +316,10 @@ class TablePrinter { *this << RowBreak(); TextFlow::Columns headerCols; - auto spacer = TextFlow::Spacer(2); for (auto const& info : m_columnInfos) { assert(info.width > 2); headerCols += TextFlow::Column(info.name).width(info.width - 2); - headerCols += spacer; + headerCols += TextFlow::Spacer( 2 ); } m_os << headerCols << '\n'; @@ -334,12 +335,12 @@ class TablePrinter { } template - friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + friend TablePrinter& operator<< (TablePrinter& tp, T const& value) { tp.m_oss << value; return tp; } - friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + friend TablePrinter& operator<< (TablePrinter& tp, ColumnBreak) { auto colStr = tp.m_oss.str(); const auto strSize = colStr.size(); tp.m_oss.str(""); @@ -361,13 +362,18 @@ class TablePrinter { return tp; } - friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + friend TablePrinter& operator<< (TablePrinter& tp, RowBreak) { if (tp.m_currentColumn > 0) { tp.m_os << '\n'; tp.m_currentColumn = -1; } return tp; } + + friend TablePrinter& operator<<(TablePrinter& tp, OutputFlush) { + tp.m_os << std::flush; + return tp; + } }; ConsoleReporter::ConsoleReporter(ReporterConfig&& config): @@ -389,7 +395,7 @@ ConsoleReporter::ConsoleReporter(ReporterConfig&& config): { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left }, { "samples mean std dev", 14, Justification::Right }, { "iterations low mean low std dev", 14, Justification::Right }, - { "estimated high mean high std dev", 14, Justification::Right } + { "est run time high mean high std dev", 14, Justification::Right } }; } }())) {} @@ -473,8 +479,11 @@ void ConsoleReporter::benchmarkPreparing( StringRef name ) { void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { (*m_tablePrinter) << info.samples << ColumnBreak() << info.iterations << ColumnBreak(); - if (!m_config->benchmarkNoAnalysis()) - (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak(); + if ( !m_config->benchmarkNoAnalysis() ) { + ( *m_tablePrinter ) + << Duration( info.estimatedDuration ) << ColumnBreak(); + } + ( *m_tablePrinter ) << OutputFlush{}; } void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { if (m_config->benchmarkNoAnalysis()) @@ -511,8 +520,8 @@ void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { m_stream << '\n' << std::flush; StreamingReporterBase::testRunEnded(_testRunStats); } -void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) { - StreamingReporterBase::testRunStarting(_testInfo); +void ConsoleReporter::testRunStarting(TestRunInfo const& _testRunInfo) { + StreamingReporterBase::testRunStarting(_testRunInfo); if ( m_config->testSpec().hasFilters() ) { m_stream << m_colour->guardColour( Colour::BrightYellow ) << "Filters: " << m_config->testSpec() << '\n'; diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp index 5e106326..09169632 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp @@ -16,8 +16,7 @@ namespace Catch { namespace { struct BySectionInfo { BySectionInfo( SectionInfo const& other ): m_other( other ) {} - BySectionInfo( BySectionInfo const& other ): - m_other( other.m_other ) {} + BySectionInfo( BySectionInfo const& other ) = default; bool operator()( Detail::unique_ptr const& node ) const { diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_cumulative_base.hpp b/src/external/catch2/src/catch2/reporters/catch_reporter_cumulative_base.hpp index cdff9991..267b39fd 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_cumulative_base.hpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_cumulative_base.hpp @@ -8,7 +8,6 @@ #ifndef CATCH_REPORTER_CUMULATIVE_BASE_HPP_INCLUDED #define CATCH_REPORTER_CUMULATIVE_BASE_HPP_INCLUDED -#include #include #include #include @@ -125,7 +124,7 @@ namespace Catch { void skipTest(TestCaseInfo const&) override {} protected: - //! Should the cumulative base store the assertion expansion for succesful assertions? + //! Should the cumulative base store the assertion expansion for successful assertions? bool m_shouldStoreSuccesfulAssertions = true; //! Should the cumulative base store the assertion expansion for failed assertions? bool m_shouldStoreFailedAssertions = true; diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_json.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_json.cpp new file mode 100644 index 00000000..6a8e655f --- /dev/null +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_json.cpp @@ -0,0 +1,372 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 +// +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { + namespace { + void writeSourceInfo( JsonObjectWriter& writer, + SourceLineInfo const& sourceInfo ) { + auto source_location_writer = + writer.write( "source-location"_sr ).writeObject(); + source_location_writer.write( "filename"_sr ) + .write( sourceInfo.file ); + source_location_writer.write( "line"_sr ).write( sourceInfo.line ); + } + + void writeTags( JsonArrayWriter writer, std::vector const& tags ) { + for ( auto const& tag : tags ) { + writer.write( tag.original ); + } + } + + void writeProperties( JsonArrayWriter writer, + TestCaseInfo const& info ) { + if ( info.isHidden() ) { writer.write( "is-hidden"_sr ); } + if ( info.okToFail() ) { writer.write( "ok-to-fail"_sr ); } + if ( info.expectedToFail() ) { + writer.write( "expected-to-fail"_sr ); + } + if ( info.throws() ) { writer.write( "throws"_sr ); } + } + + } // namespace + + JsonReporter::JsonReporter( ReporterConfig&& config ): + StreamingReporterBase{ CATCH_MOVE( config ) } { + + m_preferences.shouldRedirectStdOut = true; + // TBD: Do we want to report all assertions? XML reporter does + // not, but for machine-parseable reporters I think the answer + // should be yes. + m_preferences.shouldReportAllAssertions = true; + + m_objectWriters.emplace( m_stream ); + m_writers.emplace( Writer::Object ); + auto& writer = m_objectWriters.top(); + + writer.write( "version"_sr ).write( 1 ); + + { + auto metadata_writer = writer.write( "metadata"_sr ).writeObject(); + metadata_writer.write( "name"_sr ).write( m_config->name() ); + metadata_writer.write( "rng-seed"_sr ).write( m_config->rngSeed() ); + metadata_writer.write( "catch2-version"_sr ) + .write( libraryVersion() ); + if ( m_config->testSpec().hasFilters() ) { + metadata_writer.write( "filters"_sr ) + .write( m_config->testSpec() ); + } + } + } + + JsonReporter::~JsonReporter() { + endListing(); + // TODO: Ensure this closes the top level object, add asserts + assert( m_writers.size() == 1 && "Only the top level object should be open" ); + assert( m_writers.top() == Writer::Object ); + endObject(); + m_stream << '\n' << std::flush; + assert( m_writers.empty() ); + } + + JsonArrayWriter& JsonReporter::startArray() { + m_arrayWriters.emplace( m_arrayWriters.top().writeArray() ); + m_writers.emplace( Writer::Array ); + return m_arrayWriters.top(); + } + JsonArrayWriter& JsonReporter::startArray( StringRef key ) { + m_arrayWriters.emplace( + m_objectWriters.top().write( key ).writeArray() ); + m_writers.emplace( Writer::Array ); + return m_arrayWriters.top(); + } + + JsonObjectWriter& JsonReporter::startObject() { + m_objectWriters.emplace( m_arrayWriters.top().writeObject() ); + m_writers.emplace( Writer::Object ); + return m_objectWriters.top(); + } + JsonObjectWriter& JsonReporter::startObject( StringRef key ) { + m_objectWriters.emplace( + m_objectWriters.top().write( key ).writeObject() ); + m_writers.emplace( Writer::Object ); + return m_objectWriters.top(); + } + + void JsonReporter::endObject() { + assert( isInside( Writer::Object ) ); + m_objectWriters.pop(); + m_writers.pop(); + } + void JsonReporter::endArray() { + assert( isInside( Writer::Array ) ); + m_arrayWriters.pop(); + m_writers.pop(); + } + + bool JsonReporter::isInside( Writer writer ) { + return !m_writers.empty() && m_writers.top() == writer; + } + + void JsonReporter::startListing() { + if ( !m_startedListing ) { startObject( "listings"_sr ); } + m_startedListing = true; + } + void JsonReporter::endListing() { + if ( m_startedListing ) { endObject(); } + m_startedListing = false; + } + + std::string JsonReporter::getDescription() { + return "Outputs listings as JSON. Test listing is Work-in-Progress!"; + } + + void JsonReporter::testRunStarting( TestRunInfo const& runInfo ) { + StreamingReporterBase::testRunStarting( runInfo ); + endListing(); + + assert( isInside( Writer::Object ) ); + startObject( "test-run"_sr ); + startArray( "test-cases"_sr ); + } + + static void writeCounts( JsonObjectWriter&& writer, Counts const& counts ) { + writer.write( "passed"_sr ).write( counts.passed ); + writer.write( "failed"_sr ).write( counts.failed ); + writer.write( "fail-but-ok"_sr ).write( counts.failedButOk ); + writer.write( "skipped"_sr ).write( counts.skipped ); + } + + void JsonReporter::testRunEnded(TestRunStats const& runStats) { + assert( isInside( Writer::Array ) ); + // End "test-cases" + endArray(); + + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + runStats.totals.assertions ); + writeCounts( totals.write( "test-cases"_sr ).writeObject(), + runStats.totals.testCases ); + } + + // End the "test-run" object + endObject(); + } + + void JsonReporter::testCaseStarting( TestCaseInfo const& tcInfo ) { + StreamingReporterBase::testCaseStarting( tcInfo ); + + assert( isInside( Writer::Array ) && + "We should be in the 'test-cases' array" ); + startObject(); + // "test-info" prelude + { + auto testInfo = + m_objectWriters.top().write( "test-info"_sr ).writeObject(); + // TODO: handle testName vs className!! + testInfo.write( "name"_sr ).write( tcInfo.name ); + writeSourceInfo(testInfo, tcInfo.lineInfo); + writeTags( testInfo.write( "tags"_sr ).writeArray(), tcInfo.tags ); + writeProperties( testInfo.write( "properties"_sr ).writeArray(), + tcInfo ); + } + + + // Start the array for individual test runs (testCasePartial pairs) + startArray( "runs"_sr ); + } + + void JsonReporter::testCaseEnded( TestCaseStats const& tcStats ) { + StreamingReporterBase::testCaseEnded( tcStats ); + + // We need to close the 'runs' array before finishing the test case + assert( isInside( Writer::Array ) ); + endArray(); + + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + tcStats.totals.assertions ); + // We do not write the test case totals, because there will always be just one test case here. + // TODO: overall "result" -> success, skip, fail here? Or in partial result? + } + // We do not write out stderr/stdout, because we instead wrote those out in partial runs + + // TODO: aborting? + + // And we also close this test case's object + assert( isInside( Writer::Object ) ); + endObject(); + } + + void JsonReporter::testCasePartialStarting( TestCaseInfo const& /*tcInfo*/, + uint64_t index ) { + startObject(); + m_objectWriters.top().write( "run-idx"_sr ).write( index ); + startArray( "path"_sr ); + // TODO: we want to delay most of the printing to the 'root' section + // TODO: childSection key name? + } + + void JsonReporter::testCasePartialEnded( TestCaseStats const& tcStats, + uint64_t /*index*/ ) { + // Fixme: the top level section handles this. + //// path object + endArray(); + if ( !tcStats.stdOut.empty() ) { + m_objectWriters.top() + .write( "captured-stdout"_sr ) + .write( tcStats.stdOut ); + } + if ( !tcStats.stdErr.empty() ) { + m_objectWriters.top() + .write( "captured-stderr"_sr ) + .write( tcStats.stdErr ); + } + { + auto totals = + m_objectWriters.top().write( "totals"_sr ).writeObject(); + writeCounts( totals.write( "assertions"_sr ).writeObject(), + tcStats.totals.assertions ); + // We do not write the test case totals, because there will + // always be just one test case here. + // TODO: overall "result" -> success, skip, fail here? Or in + // partial result? + } + // TODO: aborting? + // run object + endObject(); + } + + void JsonReporter::sectionStarting( SectionInfo const& sectionInfo ) { + assert( isInside( Writer::Array ) && + "Section should always start inside an object" ); + // We want to nest top level sections, even though it shares name + // and source loc with the TEST_CASE + auto& sectionObject = startObject(); + sectionObject.write( "kind"_sr ).write( "section"_sr ); + sectionObject.write( "name"_sr ).write( sectionInfo.name ); + writeSourceInfo( m_objectWriters.top(), sectionInfo.lineInfo ); + + + // TBD: Do we want to create this event lazily? It would become + // rather complex, but we could do it, and it would look + // better for empty sections. OTOH, empty sections should + // be rare. + startArray( "path"_sr ); + } + void JsonReporter::sectionEnded( SectionStats const& /*sectionStats */) { + // End the subpath array + endArray(); + // TODO: metadata + // TODO: what info do we have here? + + // End the section object + endObject(); + } + + void JsonReporter::assertionStarting( AssertionInfo const& /*assertionInfo*/ ) {} + void JsonReporter::assertionEnded( AssertionStats const& assertionStats ) { + // TODO: There is lot of different things to handle here, but + // we can fill it in later, after we show that the basic + // outline and streaming reporter impl works well enough. + //if ( !m_config->includeSuccessfulResults() + // && assertionStats.assertionResult.isOk() ) { + // return; + //} + assert( isInside( Writer::Array ) ); + auto assertionObject = m_arrayWriters.top().writeObject(); + + assertionObject.write( "kind"_sr ).write( "assertion"_sr ); + writeSourceInfo( assertionObject, + assertionStats.assertionResult.getSourceInfo() ); + assertionObject.write( "status"_sr ) + .write( assertionStats.assertionResult.isOk() ); + // TODO: handling of result. + // TODO: messages + // TODO: totals? + } + + + void JsonReporter::benchmarkPreparing( StringRef name ) { (void)name; } + void JsonReporter::benchmarkStarting( BenchmarkInfo const& ) {} + void JsonReporter::benchmarkEnded( BenchmarkStats<> const& ) {} + void JsonReporter::benchmarkFailed( StringRef error ) { (void)error; } + + void JsonReporter::listReporters( + std::vector const& descriptions ) { + startListing(); + + auto writer = + m_objectWriters.top().write( "reporters"_sr ).writeArray(); + for ( auto const& desc : descriptions ) { + auto desc_writer = writer.writeObject(); + desc_writer.write( "name"_sr ).write( desc.name ); + desc_writer.write( "description"_sr ).write( desc.description ); + } + } + void JsonReporter::listListeners( + std::vector const& descriptions ) { + startListing(); + + auto writer = + m_objectWriters.top().write( "listeners"_sr ).writeArray(); + + for ( auto const& desc : descriptions ) { + auto desc_writer = writer.writeObject(); + desc_writer.write( "name"_sr ).write( desc.name ); + desc_writer.write( "description"_sr ).write( desc.description ); + } + } + void JsonReporter::listTests( std::vector const& tests ) { + startListing(); + + auto writer = m_objectWriters.top().write( "tests"_sr ).writeArray(); + + for ( auto const& test : tests ) { + auto desc_writer = writer.writeObject(); + auto const& info = test.getTestCaseInfo(); + + desc_writer.write( "name"_sr ).write( info.name ); + desc_writer.write( "class-name"_sr ).write( info.className ); + { + auto tag_writer = desc_writer.write( "tags"_sr ).writeArray(); + for ( auto const& tag : info.tags ) { + tag_writer.write( tag.original ); + } + } + writeSourceInfo( desc_writer, info.lineInfo ); + } + } + void JsonReporter::listTags( std::vector const& tags ) { + startListing(); + + auto writer = m_objectWriters.top().write( "tags"_sr ).writeArray(); + for ( auto const& tag : tags ) { + auto tag_writer = writer.writeObject(); + { + auto aliases_writer = + tag_writer.write( "aliases"_sr ).writeArray(); + for ( auto alias : tag.spellings ) { + aliases_writer.write( alias ); + } + } + tag_writer.write( "count"_sr ).write( tag.count ); + } + } +} // namespace Catch diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_json.hpp b/src/external/catch2/src/catch2/reporters/catch_reporter_json.hpp new file mode 100644 index 00000000..c938ca39 --- /dev/null +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_json.hpp @@ -0,0 +1,95 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_REPORTER_JSON_HPP_INCLUDED +#define CATCH_REPORTER_JSON_HPP_INCLUDED + +#include +#include +#include + +#include + +namespace Catch { + class JsonReporter : public StreamingReporterBase { + public: + JsonReporter( ReporterConfig&& config ); + + ~JsonReporter() override; + + static std::string getDescription(); + + public: // StreamingReporterBase + void testRunStarting( TestRunInfo const& runInfo ) override; + void testRunEnded( TestRunStats const& runStats ) override; + + void testCaseStarting( TestCaseInfo const& tcInfo ) override; + void testCaseEnded( TestCaseStats const& tcStats ) override; + + void testCasePartialStarting( TestCaseInfo const& tcInfo, + uint64_t index ) override; + void testCasePartialEnded( TestCaseStats const& tcStats, + uint64_t index ) override; + + void sectionStarting( SectionInfo const& sectionInfo ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + + void assertionStarting( AssertionInfo const& assertionInfo ) override; + void assertionEnded( AssertionStats const& assertionStats ) override; + + //void testRunEndedCumulative() override; + + void benchmarkPreparing( StringRef name ) override; + void benchmarkStarting( BenchmarkInfo const& ) override; + void benchmarkEnded( BenchmarkStats<> const& ) override; + void benchmarkFailed( StringRef error ) override; + + void listReporters( + std::vector const& descriptions ) override; + void listListeners( + std::vector const& descriptions ) override; + void listTests( std::vector const& tests ) override; + void listTags( std::vector const& tags ) override; + + private: + Timer m_testCaseTimer; + enum class Writer { + Object, + Array + }; + + JsonArrayWriter& startArray(); + JsonArrayWriter& startArray( StringRef key ); + + JsonObjectWriter& startObject(); + JsonObjectWriter& startObject( StringRef key ); + + void endObject(); + void endArray(); + + bool isInside( Writer writer ); + + void startListing(); + void endListing(); + + // Invariant: + // When m_writers is not empty and its top element is + // - Writer::Object, then m_objectWriters is not be empty + // - Writer::Array, then m_arrayWriters shall not be empty + std::stack m_objectWriters{}; + std::stack m_arrayWriters{}; + std::stack m_writers{}; + + bool m_startedListing = false; + + // std::size_t m_sectionDepth = 0; + // std::size_t m_sectionStarted = 0; + }; +} // namespace Catch + +#endif // CATCH_REPORTER_JSON_HPP_INCLUDED diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_junit.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_junit.cpp index 22d6526f..27bdfe28 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_junit.cpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_junit.cpp @@ -33,6 +33,8 @@ namespace Catch { gmtime_s(&timeInfo, &rawtime); #elif defined (CATCH_PLATFORM_PLAYSTATION) gmtime_s(&rawtime, &timeInfo); +#elif defined (__IAR_SYSTEMS_ICC__) + timeInfo = *std::gmtime(&rawtime); #else gmtime_r(&rawtime, &timeInfo); #endif @@ -72,7 +74,7 @@ namespace Catch { static void normalizeNamespaceMarkers(std::string& str) { std::size_t pos = str.find( "::" ); - while ( pos != str.npos ) { + while ( pos != std::string::npos ) { str.replace( pos, 2, "." ); pos += 1; pos = str.find( "::", pos ); @@ -86,7 +88,7 @@ namespace Catch { xml( m_stream ) { m_preferences.shouldRedirectStdOut = true; - m_preferences.shouldReportAllAssertions = true; + m_preferences.shouldReportAllAssertions = false; m_shouldStoreSuccesfulAssertions = false; } @@ -196,7 +198,7 @@ namespace Catch { if( !rootName.empty() ) name = rootName + '/' + name; - if( sectionNode.hasAnyAssertions() + if ( sectionNode.stats.assertions.total() > 0 || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty() ) { XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); @@ -293,7 +295,7 @@ namespace Catch { } } - if( !result.getMessage().empty() ) + if( result.hasMessage() ) rss << result.getMessage() << '\n'; for( auto const& msg : stats.infoMessages ) if( msg.type == ResultWas::Info ) diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_junit.hpp b/src/external/catch2/src/catch2/reporters/catch_reporter_junit.hpp index 87c7c567..7cb53c25 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_junit.hpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_junit.hpp @@ -19,8 +19,6 @@ namespace Catch { public: JunitReporter(ReporterConfig&& _config); - ~JunitReporter() override = default; - static std::string getDescription(); void testRunStarting(TestRunInfo const& runInfo) override; diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_multi.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_multi.cpp index ebf28b64..531902be 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_multi.cpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_multi.cpp @@ -114,7 +114,6 @@ namespace Catch { } } - // The return value indicates if the messages buffer should be cleared: void MultiReporter::assertionEnded( AssertionStats const& assertionStats ) { const bool reportByDefault = assertionStats.assertionResult.getResultType() != ResultWas::Ok || diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_multi.hpp b/src/external/catch2/src/catch2/reporters/catch_reporter_multi.hpp index c43f511f..66113837 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_multi.hpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_multi.hpp @@ -53,7 +53,7 @@ namespace Catch { void assertionEnded( AssertionStats const& assertionStats ) override; void sectionEnded( SectionStats const& sectionStats ) override; - void testCasePartialEnded(TestCaseStats const& testInfo, uint64_t partNumber) override; + void testCasePartialEnded(TestCaseStats const& testStats, uint64_t partNumber) override; void testCaseEnded( TestCaseStats const& testCaseStats ) override; void testRunEnded( TestRunStats const& testRunStats ) override; diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_registrars.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_registrars.cpp index a9787ce5..2a3ac957 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_registrars.cpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_registrars.cpp @@ -8,6 +8,7 @@ #include +#include #include namespace Catch { @@ -26,5 +27,10 @@ namespace Catch { } } + void registerListenerImpl( Detail::unique_ptr listenerFactory ) { + getMutableRegistryHub().registerListener( CATCH_MOVE(listenerFactory) ); + } + + } // namespace Detail } // namespace Catch diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_registrars.hpp b/src/external/catch2/src/catch2/reporters/catch_reporter_registrars.hpp index db5688f2..a93963f0 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_registrars.hpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_registrars.hpp @@ -8,8 +8,6 @@ #ifndef CATCH_REPORTER_REGISTRARS_HPP_INCLUDED #define CATCH_REPORTER_REGISTRARS_HPP_INCLUDED -#include -#include #include #include #include @@ -36,7 +34,8 @@ namespace Catch { //! independent on the reporter's concrete type void registerReporterImpl( std::string const& name, IReporterFactoryPtr reporterPtr ); - + //! Actually registers the factory, independent on listener's concrete type + void registerListenerImpl( Detail::unique_ptr listenerFactory ); } // namespace Detail class IEventListener; @@ -97,7 +96,7 @@ namespace Catch { public: ListenerRegistrar(StringRef listenerName) { - getMutableRegistryHub().registerListener( Detail::make_unique(listenerName) ); + registerListenerImpl( Detail::make_unique(listenerName) ); } }; } diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_sonarqube.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_sonarqube.cpp index dd002b61..2c3eb1cd 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_sonarqube.cpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_sonarqube.cpp @@ -73,9 +73,9 @@ namespace Catch { if (!rootName.empty()) name = rootName + '/' + name; - if ( sectionNode.hasAnyAssertions() + if ( sectionNode.stats.assertions.total() > 0 || !sectionNode.stdOut.empty() - || !sectionNode.stdErr.empty() ) { + || !sectionNode.stdErr.empty() ) { XmlWriter::ScopedElement e = xml.scopedElement("testCase"); xml.writeAttribute("name"_sr, name); xml.writeAttribute("duration"_sr, static_cast(sectionNode.stats.durationInSeconds * 1000)); @@ -147,7 +147,7 @@ namespace Catch { } } - if (!result.getMessage().empty()) + if (result.hasMessage()) textRss << result.getMessage() << '\n'; for (auto const& msg : stats.infoMessages) diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp b/src/external/catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp index cad6deec..509f411e 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp @@ -21,12 +21,10 @@ namespace Catch { : CumulativeReporterBase(CATCH_MOVE(config)) , xml(m_stream) { m_preferences.shouldRedirectStdOut = true; - m_preferences.shouldReportAllAssertions = true; + m_preferences.shouldReportAllAssertions = false; m_shouldStoreSuccesfulAssertions = false; } - ~SonarQubeReporter() override = default; - static std::string getDescription() { using namespace std::string_literals; return "Reports test results in the Generic Test Data SonarQube XML format"s; @@ -39,7 +37,7 @@ namespace Catch { xml.endElement(); } - void writeRun( TestRunNode const& groupNode ); + void writeRun( TestRunNode const& runNode ); void writeTestFile(StringRef filename, std::vector const& testCaseNodes); diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_streaming_base.hpp b/src/external/catch2/src/catch2/reporters/catch_reporter_streaming_base.hpp index 13672a28..5448000c 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_streaming_base.hpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_streaming_base.hpp @@ -8,7 +8,6 @@ #ifndef CATCH_REPORTER_STREAMING_BASE_HPP_INCLUDED #define CATCH_REPORTER_STREAMING_BASE_HPP_INCLUDED -#include #include #include diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_tap.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_tap.cpp index 563d6fb1..67d406fb 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_tap.cpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_tap.cpp @@ -14,7 +14,6 @@ #include #include -#include #include namespace Catch { @@ -165,7 +164,7 @@ namespace Catch { // using messages.end() directly (or auto) yields compilation error: std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + const std::size_t N = static_cast(itEnd - itMessage); stream << colourImpl->guardColour( colour ) << " with " << pluralise( N, "message"_sr ) << ':'; diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_tap.hpp b/src/external/catch2/src/catch2/reporters/catch_reporter_tap.hpp index fe45df63..e6889bb1 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_tap.hpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_tap.hpp @@ -19,7 +19,6 @@ namespace Catch { StreamingReporterBase( CATCH_MOVE(config) ) { m_preferences.shouldReportAllAssertions = true; } - ~TAPReporter() override = default; static std::string getDescription() { using namespace std::string_literals; diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_teamcity.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_teamcity.cpp index 32072800..38aa55a6 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_teamcity.cpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_teamcity.cpp @@ -45,7 +45,7 @@ namespace Catch { } // end anonymous namespace - TeamCityReporter::~TeamCityReporter() {} + TeamCityReporter::~TeamCityReporter() = default; void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) { m_stream << "##teamcity[testSuiteStarted name='" << escape( runInfo.name ) diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_teamcity.hpp b/src/external/catch2/src/catch2/reporters/catch_reporter_teamcity.hpp index 04feb2e6..662e9892 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_teamcity.hpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_teamcity.hpp @@ -35,8 +35,8 @@ namespace Catch { return "Reports test results as TeamCity service messages"s; } - void testRunStarting( TestRunInfo const& groupInfo ) override; - void testRunEnded( TestRunStats const& testGroupStats ) override; + void testRunStarting( TestRunInfo const& runInfo ) override; + void testRunEnded( TestRunStats const& runStats ) override; void assertionEnded(AssertionStats const& assertionStats) override; diff --git a/src/external/catch2/src/catch2/reporters/catch_reporter_xml.cpp b/src/external/catch2/src/catch2/reporters/catch_reporter_xml.cpp index 13812b92..35a3028e 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporter_xml.cpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporter_xml.cpp @@ -56,7 +56,7 @@ namespace Catch { m_xml.startElement("Catch2TestRun") .writeAttribute("name"_sr, m_config->name()) .writeAttribute("rng-seed"_sr, m_config->rngSeed()) - .writeAttribute("xml-format-version"_sr, 2) + .writeAttribute("xml-format-version"_sr, 3) .writeAttribute("catch2-version"_sr, libraryVersion()); if ( m_config->testSpec().hasFilters() ) { m_xml.writeAttribute( "filters"_sr, m_config->testSpec() ); @@ -98,11 +98,13 @@ namespace Catch { // Print any info messages in tags. for( auto const& msg : assertionStats.infoMessages ) { if( msg.type == ResultWas::Info && includeResults ) { - m_xml.scopedElement( "Info" ) - .writeText( msg.message ); + auto t = m_xml.scopedElement( "Info" ); + writeSourceInfo( msg.lineInfo ); + t.writeText( msg.message ); } else if ( msg.type == ResultWas::Warning ) { - m_xml.scopedElement( "Warning" ) - .writeText( msg.message ); + auto t = m_xml.scopedElement( "Warning" ); + writeSourceInfo( msg.lineInfo ); + t.writeText( msg.message ); } } } @@ -232,26 +234,23 @@ namespace Catch { } void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { - m_xml.startElement("mean") + m_xml.scopedElement("mean") .writeAttribute("value"_sr, benchmarkStats.mean.point.count()) .writeAttribute("lowerBound"_sr, benchmarkStats.mean.lower_bound.count()) .writeAttribute("upperBound"_sr, benchmarkStats.mean.upper_bound.count()) .writeAttribute("ci"_sr, benchmarkStats.mean.confidence_interval); - m_xml.endElement(); - m_xml.startElement("standardDeviation") + m_xml.scopedElement("standardDeviation") .writeAttribute("value"_sr, benchmarkStats.standardDeviation.point.count()) .writeAttribute("lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count()) .writeAttribute("upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count()) .writeAttribute("ci"_sr, benchmarkStats.standardDeviation.confidence_interval); - m_xml.endElement(); - m_xml.startElement("outliers") + m_xml.scopedElement("outliers") .writeAttribute("variance"_sr, benchmarkStats.outlierVariance) .writeAttribute("lowMild"_sr, benchmarkStats.outliers.low_mild) .writeAttribute("lowSevere"_sr, benchmarkStats.outliers.low_severe) .writeAttribute("highMild"_sr, benchmarkStats.outliers.high_mild) .writeAttribute("highSevere"_sr, benchmarkStats.outliers.high_severe); m_xml.endElement(); - m_xml.endElement(); } void XmlReporter::benchmarkFailed(StringRef error) { diff --git a/src/external/catch2/src/catch2/reporters/catch_reporters_all.hpp b/src/external/catch2/src/catch2/reporters/catch_reporters_all.hpp index 16f7bd70..5c713fe1 100644 --- a/src/external/catch2/src/catch2/reporters/catch_reporters_all.hpp +++ b/src/external/catch2/src/catch2/reporters/catch_reporters_all.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/src/external/catch2/tests/BUILD.bazel b/src/external/catch2/tests/BUILD.bazel new file mode 100644 index 00000000..5f0362fb --- /dev/null +++ b/src/external/catch2/tests/BUILD.bazel @@ -0,0 +1,83 @@ +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "catch2_self_test_helper", + srcs = ["SelfTest/helpers/parse_test_spec.cpp"], + hdrs = [ + "SelfTest/helpers/parse_test_spec.hpp", + "SelfTest/helpers/range_test_helpers.hpp", + "SelfTest/helpers/type_with_lit_0_comparisons.hpp", + ], + includes = ["SelfTest"], + deps = [ + "//:catch2", + ], +) + +cc_test( + name = "catch2_self_test", + size = "small", + srcs = [ + "SelfTest/IntrospectiveTests/Algorithms.tests.cpp", + "SelfTest/IntrospectiveTests/Clara.tests.cpp", + "SelfTest/IntrospectiveTests/CmdLine.tests.cpp", + "SelfTest/IntrospectiveTests/CmdLineHelpers.tests.cpp", + "SelfTest/IntrospectiveTests/ColourImpl.tests.cpp", + "SelfTest/IntrospectiveTests/Details.tests.cpp", + "SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp", + "SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp", + "SelfTest/IntrospectiveTests/Integer.tests.cpp", + "SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp", + "SelfTest/IntrospectiveTests/Parse.tests.cpp", + "SelfTest/IntrospectiveTests/PartTracker.tests.cpp", + "SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp", + "SelfTest/IntrospectiveTests/Reporters.tests.cpp", + "SelfTest/IntrospectiveTests/Sharding.tests.cpp", + "SelfTest/IntrospectiveTests/Stream.tests.cpp", + "SelfTest/IntrospectiveTests/String.tests.cpp", + "SelfTest/IntrospectiveTests/StringManip.tests.cpp", + "SelfTest/IntrospectiveTests/Tag.tests.cpp", + "SelfTest/IntrospectiveTests/TestCaseInfoHasher.tests.cpp", + "SelfTest/IntrospectiveTests/TestSpec.tests.cpp", + "SelfTest/IntrospectiveTests/TestSpecParser.tests.cpp", + "SelfTest/IntrospectiveTests/TextFlow.tests.cpp", + "SelfTest/IntrospectiveTests/ToString.tests.cpp", + "SelfTest/IntrospectiveTests/Traits.tests.cpp", + "SelfTest/IntrospectiveTests/UniquePtr.tests.cpp", + "SelfTest/IntrospectiveTests/Xml.tests.cpp", + "SelfTest/TestRegistrations.cpp", + "SelfTest/TimingTests/Sleep.tests.cpp", + "SelfTest/UsageTests/Approx.tests.cpp", + "SelfTest/UsageTests/BDD.tests.cpp", + "SelfTest/UsageTests/Benchmark.tests.cpp", + "SelfTest/UsageTests/Class.tests.cpp", + "SelfTest/UsageTests/Compilation.tests.cpp", + "SelfTest/UsageTests/Condition.tests.cpp", + "SelfTest/UsageTests/Decomposition.tests.cpp", + "SelfTest/UsageTests/EnumToString.tests.cpp", + "SelfTest/UsageTests/Exception.tests.cpp", + "SelfTest/UsageTests/Generators.tests.cpp", + "SelfTest/UsageTests/Matchers.tests.cpp", + "SelfTest/UsageTests/MatchersRanges.tests.cpp", + "SelfTest/UsageTests/Message.tests.cpp", + "SelfTest/UsageTests/Misc.tests.cpp", + "SelfTest/UsageTests/ToStringByte.tests.cpp", + "SelfTest/UsageTests/ToStringChrono.tests.cpp", + "SelfTest/UsageTests/ToStringGeneral.tests.cpp", + "SelfTest/UsageTests/ToStringOptional.tests.cpp", + "SelfTest/UsageTests/ToStringPair.tests.cpp", + "SelfTest/UsageTests/ToStringTuple.tests.cpp", + "SelfTest/UsageTests/ToStringVariant.tests.cpp", + "SelfTest/UsageTests/ToStringVector.tests.cpp", + "SelfTest/UsageTests/ToStringWhich.tests.cpp", + "SelfTest/UsageTests/Tricky.tests.cpp", + "SelfTest/UsageTests/VariadicMacros.tests.cpp", + ], + deps = [ + ":catch2_self_test_helper", + "//:catch2", + "//:catch2_main", + ], +) diff --git a/src/external/catch2/tests/CMakeLists.txt b/src/external/catch2/tests/CMakeLists.txt index 7be57abe..37a5977e 100644 --- a/src/external/catch2/tests/CMakeLists.txt +++ b/src/external/catch2/tests/CMakeLists.txt @@ -59,7 +59,7 @@ if (CATCH_BUILD_SURROGATES) ) target_link_libraries(Catch2SurrogateTarget PRIVATE Catch2WithMain) -endif(CATCH_BUILD_SURROGATES) +endif() #### # Temporary workaround for VS toolset changes in 2017 @@ -70,7 +70,7 @@ if (MSVC) configure_file(${CATCH_DIR}/tools/misc/SelfTest.vcxproj.user ${CMAKE_BINARY_DIR}/tests COPYONLY) -endif(MSVC) #Temporary workaround +endif() #Temporary workaround # define the sources of the self test @@ -78,6 +78,7 @@ endif(MSVC) #Temporary workaround set(TEST_SOURCES ${SELF_TEST_DIR}/TestRegistrations.cpp ${SELF_TEST_DIR}/IntrospectiveTests/Algorithms.tests.cpp + ${SELF_TEST_DIR}/IntrospectiveTests/AssertionHandler.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/Clara.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/CmdLine.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/CmdLineHelpers.tests.cpp @@ -85,7 +86,9 @@ set(TEST_SOURCES ${SELF_TEST_DIR}/IntrospectiveTests/Details.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/FloatingPoint.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/GeneratorsImpl.tests.cpp + ${SELF_TEST_DIR}/IntrospectiveTests/Integer.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/InternalBenchmark.tests.cpp + ${SELF_TEST_DIR}/IntrospectiveTests/Json.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/Parse.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/PartTracker.tests.cpp ${SELF_TEST_DIR}/IntrospectiveTests/RandomNumberGeneration.tests.cpp @@ -622,6 +625,18 @@ if (CATCH_ENABLE_CONFIGURE_TESTS) endforeach() endif() +if (CATCH_ENABLE_CMAKE_HELPER_TESTS) + add_test(NAME "CMakeHelper::DiscoverTests" + COMMAND + "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/TestScripts/DiscoverTests/VerifyRegistration.py" "${CATCH_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" + ) + set_tests_properties("CMakeHelper::DiscoverTests" + PROPERTIES + COST 240 + LABELS "uses-python" + ) +endif() + foreach (reporterName # "Automake" - the simple .trs format does not support any kind of comments/metadata "compact" "console" @@ -629,7 +644,8 @@ foreach (reporterName # "Automake" - the simple .trs format does not support any "SonarQube" "TAP" # "TeamCity" - does not seem to support test suite-level metadata/comments - "XML") + "XML" + "JSON") add_test(NAME "Reporters:Filters:${reporterName}" COMMAND @@ -639,6 +655,8 @@ foreach (reporterName # "Automake" - the simple .trs format does not support any # Different regex for these two reporters, because the commas end up xml-escaped if (reporterName MATCHES "JUnit|XML") set(testCaseNameFormat ""CaseInsensitiveLess is case insensitive"") + elseif(reporterName MATCHES "JSON") + set(testCaseNameFormat "\\\\\"CaseInsensitiveLess is case insensitive\\\\\"") else() set(testCaseNameFormat "\"CaseInsensitiveLess is case insensitive\"") endif() diff --git a/src/external/catch2/tests/ExtraTests/CMakeLists.txt b/src/external/catch2/tests/ExtraTests/CMakeLists.txt index 4172d7a0..9f6d8173 100644 --- a/src/external/catch2/tests/ExtraTests/CMakeLists.txt +++ b/src/external/catch2/tests/ExtraTests/CMakeLists.txt @@ -183,7 +183,7 @@ if (NOT WIN32) PROPERTIES PASS_REGULAR_EXPRESSION "Catch will terminate" ) -endif(NOT WIN32) +endif() add_test(NAME CATCH_CONFIG_DISABLE_EXCEPTIONS-3 COMMAND DisabledExceptions-CustomHandler "Tests that run") @@ -467,6 +467,29 @@ set_tests_properties( PASS_REGULAR_EXPRESSION "Errors occurred during startup!" ) +add_executable(ReportingCrashWithJunitReporter ${TESTS_DIR}/X36-ReportingCrashWithJunitReporter.cpp) +target_link_libraries(ReportingCrashWithJunitReporter PRIVATE Catch2::Catch2WithMain) +add_test( + NAME Reporters::CrashInJunitReporter + COMMAND ${CMAKE_COMMAND} -E env $ --reporter JUnit +) +set_tests_properties( + Reporters::CrashInJunitReporter + PROPERTIES + PASS_REGULAR_EXPRESSION "" + LABELS "uses-signals" +) + +add_executable(AssertionStartingEventGoesBeforeAssertionIsEvaluated + X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp +) +target_link_libraries(AssertionStartingEventGoesBeforeAssertionIsEvaluated + PRIVATE Catch2::Catch2WithMain +) +add_test( + NAME ReporterEvents::AssertionStartingHappensBeforeAssertionIsEvaluated + COMMAND $ +) #add_executable(DebugBreakMacros ${TESTS_DIR}/X12-CustomDebugBreakMacro.cpp) #target_link_libraries(DebugBreakMacros Catch2) @@ -545,6 +568,7 @@ add_executable(AmalgamatedTestCompilation ${CATCH_DIR}/extras/catch_amalgamated.cpp ) target_include_directories(AmalgamatedTestCompilation PRIVATE ${CATCH_DIR}/extras) +target_compile_features(AmalgamatedTestCompilation PRIVATE cxx_std_14) add_test(NAME AmalgamatedFileTest COMMAND AmalgamatedTestCompilation) set_tests_properties( diff --git a/src/external/catch2/tests/ExtraTests/X02-DisabledMacros.cpp b/src/external/catch2/tests/ExtraTests/X02-DisabledMacros.cpp index 68bc2add..231adfb0 100644 --- a/src/external/catch2/tests/ExtraTests/X02-DisabledMacros.cpp +++ b/src/external/catch2/tests/ExtraTests/X02-DisabledMacros.cpp @@ -11,34 +11,28 @@ * and expressions in assertion macros are not run. */ - -#include #include +#include #include #include #include struct foo { - foo(){ - REQUIRE_NOTHROW( print() ); - } - void print() const { - std::cout << "This should not happen\n"; - } + foo() { REQUIRE_NOTHROW( print() ); } + void print() const { std::cout << "This should not happen\n"; } }; -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wglobal-constructors" +#if defined( __clang__ ) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wglobal-constructors" #endif // Construct foo, but `foo::print` should not be run static foo f; - -#if defined(__clang__) +#if defined( __clang__ ) // The test is unused since the registration is disabled -#pragma clang diagnostic ignored "-Wunused-function" +# pragma clang diagnostic ignored "-Wunused-function" #endif // This test should not be run, because it won't be registered @@ -60,6 +54,26 @@ TEST_CASE( "Disabled Macros" ) { BENCHMARK( "Disabled benchmark" ) { REQUIRE( 1 == 2 ); }; } -#if defined(__clang__) -#pragma clang diagnostic pop +struct DisabledFixture {}; + +TEST_CASE_PERSISTENT_FIXTURE( DisabledFixture, "Disabled Persistent Fixture" ) { + CHECK( 1 == 2 ); + REQUIRE( 1 == 2 ); + std::cout << "This should not happen\n"; + FAIL(); + + // Test that static assertions don't fire when macros are disabled + STATIC_CHECK( 0 == 1 ); + STATIC_REQUIRE( !true ); + + CAPTURE( 1 ); + CAPTURE( 1, "captured" ); + + REQUIRE_THAT( 1, + Catch::Matchers::Predicate( []( int ) { return false; } ) ); + BENCHMARK( "Disabled benchmark" ) { REQUIRE( 1 == 2 ); }; +} + +#if defined( __clang__ ) +# pragma clang diagnostic pop #endif diff --git a/src/external/catch2/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp b/src/external/catch2/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp new file mode 100644 index 00000000..6f44bf69 --- /dev/null +++ b/src/external/catch2/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp @@ -0,0 +1,77 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +/**\file + * Registers an event listener to increments counter of assertionStarting events. + * + * Different assertion macros then check that the counter is at expected + * value when they are evaluated. + */ + +#include +#include +#include +#include + +namespace { + + static size_t assertion_starting_events_seen = 0; + + class AssertionStartingListener : public Catch::EventListenerBase { + public: + AssertionStartingListener( Catch::IConfig const* config ): + EventListenerBase( config ) {} + + void assertionStarting( Catch::AssertionInfo const& ) override { + ++assertion_starting_events_seen; + } + }; + + static bool f1() { + return assertion_starting_events_seen == 1; + } + + static void f2() { + if ( assertion_starting_events_seen != 2 ) { throw 1; } + } + + static void f3() { + if ( assertion_starting_events_seen == 3 ) { throw 1; } + } + + static bool f4() { return assertion_starting_events_seen == 4; } + + static void f5() { throw assertion_starting_events_seen; } + +} // anonymous namespace + +CATCH_REGISTER_LISTENER( AssertionStartingListener ) + +TEST_CASE() { + // **IMPORTANT** + // The order of assertions below matters. + REQUIRE( f1() ); + REQUIRE_NOTHROW( f2() ); + REQUIRE_THROWS( f3() ); + REQUIRE_THAT( f4(), + Catch::Matchers::Predicate( []( bool b ) { return b; } ) ); + REQUIRE_THROWS_MATCHES( + f5(), size_t, Catch::Matchers::Predicate( []( size_t i ) { + return i == 5; + } ) ); + + CAPTURE( assertion_starting_events_seen ); // **not** an assertion + INFO( "some info msg" ); // **not** an assertion + WARN( "warning! warning!" ); // assertion-like message + SUCCEED(); // assertion-like message + + // We skip FAIL/SKIP and so on, which fail the test. + + // This require will also increment the count once + REQUIRE( assertion_starting_events_seen == 8 ); +} diff --git a/src/external/catch2/tests/ExtraTests/X22-BenchmarksInCumulativeReporter.cpp b/src/external/catch2/tests/ExtraTests/X22-BenchmarksInCumulativeReporter.cpp index af03ce30..33399a68 100644 --- a/src/external/catch2/tests/ExtraTests/X22-BenchmarksInCumulativeReporter.cpp +++ b/src/external/catch2/tests/ExtraTests/X22-BenchmarksInCumulativeReporter.cpp @@ -34,7 +34,7 @@ class CumulativeBenchmarkReporter final : public Catch::CumulativeReporterBase { return "Custom reporter for testing cumulative reporter base"; } - virtual void testRunEndedCumulative() override; + void testRunEndedCumulative() override; }; CATCH_REGISTER_REPORTER("testReporter", CumulativeBenchmarkReporter) diff --git a/src/external/catch2/tests/ExtraTests/X29-CustomArgumentsForReporters.cpp b/src/external/catch2/tests/ExtraTests/X29-CustomArgumentsForReporters.cpp index 9bd816ef..13d9fc18 100644 --- a/src/external/catch2/tests/ExtraTests/X29-CustomArgumentsForReporters.cpp +++ b/src/external/catch2/tests/ExtraTests/X29-CustomArgumentsForReporters.cpp @@ -36,6 +36,7 @@ class TestReporter : public Catch::StreamingReporterBase { void testRunStarting( Catch::TestRunInfo const& ) override { std::vector> options; + options.reserve( m_customOptions.size() ); for ( auto const& kv : m_customOptions ) { options.push_back( kv ); } diff --git a/src/external/catch2/tests/ExtraTests/X36-ReportingCrashWithJunitReporter.cpp b/src/external/catch2/tests/ExtraTests/X36-ReportingCrashWithJunitReporter.cpp new file mode 100644 index 00000000..34f4cd85 --- /dev/null +++ b/src/external/catch2/tests/ExtraTests/X36-ReportingCrashWithJunitReporter.cpp @@ -0,0 +1,32 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +/**\file + * Checks that signals/SEH within open section does not hard crash JUnit + * (or similar reporter) while we are trying to report fatal error. + */ + +#include + +#include + +// On Windows we need to send SEH and not signal to test the +// RunContext::handleFatalErrorCondition code path +#if defined( _MSC_VER ) +# include +#endif + +TEST_CASE( "raises signal" ) { + SECTION( "section" ) { +#if defined( _MSC_VER ) + RaiseException( 0xC0000005, 0, 0, NULL ); +#else + std::raise( SIGILL ); +#endif + } +} diff --git a/src/external/catch2/tests/ExtraTests/X91-AmalgamatedCatch.cpp b/src/external/catch2/tests/ExtraTests/X91-AmalgamatedCatch.cpp index c00462be..78d45a2c 100644 --- a/src/external/catch2/tests/ExtraTests/X91-AmalgamatedCatch.cpp +++ b/src/external/catch2/tests/ExtraTests/X91-AmalgamatedCatch.cpp @@ -16,10 +16,10 @@ TEST_CASE("Just a dummy test") { auto i = GENERATE(1, 2, 3); SECTION("a") { - REQUIRE(1 != 4); + REQUIRE(i != 4); } SECTION("b") { - CHECK(1 != 5); + CHECK(i != 5); } REQUIRE_THAT(1, Catch::Matchers::Predicate([](int i) { diff --git a/src/external/catch2/tests/SelfTest/Baselines/automake.sw.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/automake.sw.approved.txt index d33effdd..08fbf09c 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/automake.sw.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/automake.sw.approved.txt @@ -68,6 +68,8 @@ Nor would this :test-result: PASS A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds - 6 :test-result: FAIL A TEST_CASE_METHOD based test run that fails :test-result: PASS A TEST_CASE_METHOD based test run that succeeds +:test-result: FAIL A TEST_CASE_PERSISTENT_FIXTURE based test run that fails +:test-result: PASS A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds :test-result: PASS A Template product test case - Foo :test-result: PASS A Template product test case - Foo :test-result: PASS A Template product test case - std::vector @@ -103,6 +105,7 @@ Nor would this :test-result: PASS CaseInsensitiveEqualsTo is case insensitive :test-result: PASS CaseInsensitiveLess is case insensitive :test-result: PASS Character pretty printing +:test-result: PASS Clara::Arg does not crash on incomplete input :test-result: PASS Clara::Arg supports single-arg parse the way Opt does :test-result: PASS Clara::Opt supports accept-many lambdas :test-result: PASS ColourGuard behaviour @@ -130,8 +133,8 @@ Nor would this :test-result: FAIL Custom std-exceptions can be custom translated :test-result: PASS Default scale is invisible to comparison :test-result: PASS Directly creating an EnumInfo +:test-result: SKIP Empty generators can SKIP in constructor :test-result: PASS Empty stream name opens cout stream -:test-result: PASS Empty tag is not allowed :test-result: FAIL EndsWith string matcher :test-result: PASS Enums can quickly have stringification enabled using REGISTER_ENUM :test-result: PASS Enums in namespaces can quickly have stringification enabled using REGISTER_ENUM @@ -154,6 +157,7 @@ Nor would this :test-result: PASS Filter generator throws exception for empty generator :test-result: PASS Floating point matchers: double :test-result: PASS Floating point matchers: float +:test-result: PASS GENERATE can combine literals and generators :test-result: PASS Generators -- adapters :test-result: PASS Generators -- simple :test-result: PASS Generators internals @@ -162,12 +166,16 @@ Nor would this :test-result: PASS Hashers with same seed produce same hash :test-result: PASS Hashing different test cases produces different result :test-result: PASS Hashing test case produces same hash across multiple calls +:test-result: FAIL INFO and UNSCOPED_INFO can stream multiple arguments :test-result: FAIL INFO and WARN do not abort tests :test-result: FAIL INFO gets logged on failure :test-result: FAIL INFO gets logged on failure, even if captured before successful assertions :test-result: FAIL INFO is reset for each loop +:test-result: XFAIL Incomplete AssertionHandler :test-result: XFAIL Inequality checks that should fail :test-result: PASS Inequality checks that should succeed +:test-result: PASS JsonWriter +:test-result: PASS JsonWriter escapes charaters in strings properly :test-result: PASS Lambdas in assertions :test-result: PASS Less-than inequalities with different epsilons :test-result: PASS ManuallyRegistered @@ -265,6 +273,8 @@ Message from section two :test-result: PASS Testing checked-if :test-result: XFAIL Testing checked-if 2 :test-result: XFAIL Testing checked-if 3 +:test-result: XFAIL Testing checked-if 4 +:test-result: XFAIL Testing checked-if 5 :test-result: FAIL The NO_FAIL macro reports a failure but does not fail the test :test-result: PASS The default listing implementation write to provided stream :test-result: FAIL This test 'should' fail but doesn't @@ -347,7 +357,6 @@ b1! :test-result: FAIL nested sections can be skipped dynamically at runtime :test-result: PASS non streamable - with conv. op :test-result: PASS non-copyable objects -:test-result: PASS normal_cdf :test-result: PASS normal_quantile :test-result: PASS not allowed :test-result: FAIL not prints unscoped info from previous failures @@ -408,6 +417,7 @@ b1! :test-result: PASS tuple :test-result: PASS tuple,tuple<>,float> :test-result: PASS uniform samples +:test-result: PASS uniform_integer_distribution can return the bounds :test-result: PASS unique_ptr reimplementation: basic functionality :test-result: PASS vec> -> toString :test-result: PASS vector -> toString diff --git a/src/external/catch2/tests/SelfTest/Baselines/automake.sw.multi.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/automake.sw.multi.approved.txt index f698f0c5..c4b7b967 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/automake.sw.multi.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/automake.sw.multi.approved.txt @@ -66,6 +66,8 @@ :test-result: PASS A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds - 6 :test-result: FAIL A TEST_CASE_METHOD based test run that fails :test-result: PASS A TEST_CASE_METHOD based test run that succeeds +:test-result: FAIL A TEST_CASE_PERSISTENT_FIXTURE based test run that fails +:test-result: PASS A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds :test-result: PASS A Template product test case - Foo :test-result: PASS A Template product test case - Foo :test-result: PASS A Template product test case - std::vector @@ -101,6 +103,7 @@ :test-result: PASS CaseInsensitiveEqualsTo is case insensitive :test-result: PASS CaseInsensitiveLess is case insensitive :test-result: PASS Character pretty printing +:test-result: PASS Clara::Arg does not crash on incomplete input :test-result: PASS Clara::Arg supports single-arg parse the way Opt does :test-result: PASS Clara::Opt supports accept-many lambdas :test-result: PASS ColourGuard behaviour @@ -128,8 +131,8 @@ :test-result: FAIL Custom std-exceptions can be custom translated :test-result: PASS Default scale is invisible to comparison :test-result: PASS Directly creating an EnumInfo +:test-result: SKIP Empty generators can SKIP in constructor :test-result: PASS Empty stream name opens cout stream -:test-result: PASS Empty tag is not allowed :test-result: FAIL EndsWith string matcher :test-result: PASS Enums can quickly have stringification enabled using REGISTER_ENUM :test-result: PASS Enums in namespaces can quickly have stringification enabled using REGISTER_ENUM @@ -152,6 +155,7 @@ :test-result: PASS Filter generator throws exception for empty generator :test-result: PASS Floating point matchers: double :test-result: PASS Floating point matchers: float +:test-result: PASS GENERATE can combine literals and generators :test-result: PASS Generators -- adapters :test-result: PASS Generators -- simple :test-result: PASS Generators internals @@ -160,12 +164,16 @@ :test-result: PASS Hashers with same seed produce same hash :test-result: PASS Hashing different test cases produces different result :test-result: PASS Hashing test case produces same hash across multiple calls +:test-result: FAIL INFO and UNSCOPED_INFO can stream multiple arguments :test-result: FAIL INFO and WARN do not abort tests :test-result: FAIL INFO gets logged on failure :test-result: FAIL INFO gets logged on failure, even if captured before successful assertions :test-result: FAIL INFO is reset for each loop +:test-result: XFAIL Incomplete AssertionHandler :test-result: XFAIL Inequality checks that should fail :test-result: PASS Inequality checks that should succeed +:test-result: PASS JsonWriter +:test-result: PASS JsonWriter escapes charaters in strings properly :test-result: PASS Lambdas in assertions :test-result: PASS Less-than inequalities with different epsilons :test-result: PASS ManuallyRegistered @@ -258,6 +266,8 @@ :test-result: PASS Testing checked-if :test-result: XFAIL Testing checked-if 2 :test-result: XFAIL Testing checked-if 3 +:test-result: XFAIL Testing checked-if 4 +:test-result: XFAIL Testing checked-if 5 :test-result: FAIL The NO_FAIL macro reports a failure but does not fail the test :test-result: PASS The default listing implementation write to provided stream :test-result: FAIL This test 'should' fail but doesn't @@ -336,7 +346,6 @@ :test-result: FAIL nested sections can be skipped dynamically at runtime :test-result: PASS non streamable - with conv. op :test-result: PASS non-copyable objects -:test-result: PASS normal_cdf :test-result: PASS normal_quantile :test-result: PASS not allowed :test-result: FAIL not prints unscoped info from previous failures @@ -397,6 +406,7 @@ :test-result: PASS tuple :test-result: PASS tuple,tuple<>,float> :test-result: PASS uniform samples +:test-result: PASS uniform_integer_distribution can return the bounds :test-result: PASS unique_ptr reimplementation: basic functionality :test-result: PASS vec> -> toString :test-result: PASS vector -> toString diff --git a/src/external/catch2/tests/SelfTest/Baselines/compact.sw.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/compact.sw.approved.txt index 541770cc..1f0dd012 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/compact.sw.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/compact.sw.approved.txt @@ -241,6 +241,10 @@ Class.tests.cpp:: passed: Nttp_Fixture::value > 0 for: 3 > 0 Class.tests.cpp:: passed: Nttp_Fixture::value > 0 for: 6 > 0 Class.tests.cpp:: failed: m_a == 2 for: 1 == 2 Class.tests.cpp:: passed: m_a == 1 for: 1 == 1 +Class.tests.cpp:: passed: m_a++ == 0 for: 0 == 0 +Class.tests.cpp:: failed: m_a == 0 for: 1 == 0 +Class.tests.cpp:: passed: m_a++ == 0 for: 0 == 0 +Class.tests.cpp:: passed: m_a == 1 for: 1 == 1 Misc.tests.cpp:: passed: x.size() == 0 for: 0 == 0 Misc.tests.cpp:: passed: x.size() == 0 for: 0 == 0 Misc.tests.cpp:: passed: x.size() == 0 for: 0 == 0 @@ -249,12 +253,22 @@ Misc.tests.cpp:: passed: x.size() > 0 for: 42 > 0 Misc.tests.cpp:: passed: x.size() > 0 for: 9 > 0 Misc.tests.cpp:: passed: x.size() > 0 for: 42 > 0 Misc.tests.cpp:: passed: x.size() > 0 for: 9 > 0 -Approx.tests.cpp:: passed: d == 1.23_a for: 1.23 == Approx( 1.23 ) -Approx.tests.cpp:: passed: d != 1.22_a for: 1.23 != Approx( 1.22 ) -Approx.tests.cpp:: passed: -d == -1.23_a for: -1.23 == Approx( -1.23 ) -Approx.tests.cpp:: passed: d == 1.2_a .epsilon(.1) for: 1.23 == Approx( 1.2 ) -Approx.tests.cpp:: passed: d != 1.2_a .epsilon(.001) for: 1.23 != Approx( 1.2 ) -Approx.tests.cpp:: passed: d == 1_a .epsilon(.3) for: 1.23 == Approx( 1.0 ) +Approx.tests.cpp:: passed: d == 1.23_a for: 1.22999999999999998 +== +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: d != 1.22_a for: 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) +Approx.tests.cpp:: passed: -d == -1.23_a for: -1.22999999999999998 +== +Approx( -1.22999999999999998 ) +Approx.tests.cpp:: passed: d == 1.2_a .epsilon(.1) for: 1.22999999999999998 +== +Approx( 1.19999999999999996 ) +Approx.tests.cpp:: passed: d != 1.2_a .epsilon(.001) for: 1.22999999999999998 +!= +Approx( 1.19999999999999996 ) +Approx.tests.cpp:: passed: d == 1_a .epsilon(.3) for: 1.22999999999999998 == Approx( 1.0 ) Misc.tests.cpp:: passed: with 1 message: 'that's not flying - that's failing in style' Misc.tests.cpp:: failed: explicitly with 1 message: 'to infinity and beyond' Tricky.tests.cpp:: failed: &o1 == &o2 for: 0x == 0x @@ -263,8 +277,8 @@ Approx.tests.cpp:: passed: 104.0 != Approx(100.0) for: 104.0 != App Approx.tests.cpp:: passed: 104.0 == Approx(100.0).margin(5) for: 104.0 == Approx( 100.0 ) Approx.tests.cpp:: passed: 104.0 == Approx(100.0).margin(4) for: 104.0 == Approx( 100.0 ) Approx.tests.cpp:: passed: 104.0 != Approx(100.0).margin(3) for: 104.0 != Approx( 100.0 ) -Approx.tests.cpp:: passed: 100.3 != Approx(100.0) for: 100.3 != Approx( 100.0 ) -Approx.tests.cpp:: passed: 100.3 == Approx(100.0).margin(0.5) for: 100.3 == Approx( 100.0 ) +Approx.tests.cpp:: passed: 100.3 != Approx(100.0) for: 100.29999999999999716 != Approx( 100.0 ) +Approx.tests.cpp:: passed: 100.3 == Approx(100.0).margin(0.5) for: 100.29999999999999716 == Approx( 100.0 ) Tricky.tests.cpp:: passed: i++ == 7 for: 7 == 7 Tricky.tests.cpp:: passed: i++ == 8 for: 8 == 8 Exception.tests.cpp:: passed: 1 == 1 @@ -282,19 +296,33 @@ Approx.tests.cpp:: passed: 0.0f == Approx(0.25f).margin(0.25f) for: Approx.tests.cpp:: passed: 0.5f == Approx(0.25f).margin(0.25f) for: 0.5f == Approx( 0.25 ) Approx.tests.cpp:: passed: 245.0f == Approx(245.25f).margin(0.25f) for: 245.0f == Approx( 245.25 ) Approx.tests.cpp:: passed: 245.5f == Approx(245.25f).margin(0.25f) for: 245.5f == Approx( 245.25 ) -Approx.tests.cpp:: passed: divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) for: 3.1428571429 == Approx( 3.141 ) -Approx.tests.cpp:: passed: divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) for: 3.1428571429 != Approx( 3.141 ) -Approx.tests.cpp:: passed: d != Approx( 1.231 ) for: 1.23 != Approx( 1.231 ) -Approx.tests.cpp:: passed: d == Approx( 1.231 ).epsilon( 0.1 ) for: 1.23 == Approx( 1.231 ) -Approx.tests.cpp:: passed: 1.23f == Approx( 1.23f ) for: 1.23f == Approx( 1.2300000191 ) +Approx.tests.cpp:: passed: divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) for: 3.14285714285714279 +== +Approx( 3.14100000000000001 ) +Approx.tests.cpp:: passed: divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) for: 3.14285714285714279 +!= +Approx( 3.14100000000000001 ) +Approx.tests.cpp:: passed: d != Approx( 1.231 ) for: 1.22999999999999998 +!= +Approx( 1.23100000000000009 ) +Approx.tests.cpp:: passed: d == Approx( 1.231 ).epsilon( 0.1 ) for: 1.22999999999999998 +== +Approx( 1.23100000000000009 ) +Approx.tests.cpp:: passed: 1.23f == Approx( 1.23f ) for: 1.230000019f +== +Approx( 1.23000001907348633 ) Approx.tests.cpp:: passed: 0.0f == Approx( 0.0f ) for: 0.0f == Approx( 0.0 ) Approx.tests.cpp:: passed: 1 == Approx( 1 ) for: 1 == Approx( 1.0 ) Approx.tests.cpp:: passed: 0 == Approx( 0 ) for: 0 == Approx( 0.0 ) Approx.tests.cpp:: passed: 1.0f == Approx( 1 ) for: 1.0f == Approx( 1.0 ) Approx.tests.cpp:: passed: 0 == Approx( dZero) for: 0 == Approx( 0.0 ) Approx.tests.cpp:: passed: 0 == Approx( dSmall ).margin( 0.001 ) for: 0 == Approx( 0.00001 ) -Approx.tests.cpp:: passed: 1.234f == Approx( dMedium ) for: 1.234f == Approx( 1.234 ) -Approx.tests.cpp:: passed: dMedium == Approx( 1.234f ) for: 1.234 == Approx( 1.2339999676 ) +Approx.tests.cpp:: passed: 1.234f == Approx( dMedium ) for: 1.233999968f +== +Approx( 1.23399999999999999 ) +Approx.tests.cpp:: passed: dMedium == Approx( 1.234f ) for: 1.23399999999999999 +== +Approx( 1.23399996757507324 ) Matchers.tests.cpp:: passed: 1, Predicate( alwaysTrue, "always true" ) for: 1 matches predicate: "always true" Matchers.tests.cpp:: passed: 1, !Predicate( alwaysFalse, "always false" ) for: 1 not matches predicate: "always false" Matchers.tests.cpp:: passed: "Hello olleH", Predicate( []( std::string const& str ) -> bool { return str.front() == str.back(); }, "First and last character should be equal" ) for: "Hello olleH" matches predicate: "First and last character should be equal" @@ -331,7 +359,7 @@ MatchersRanges.tests.cpp:: passed: inner_lists_are_empty.front(), I MatchersRanges.tests.cpp:: passed: has_empty{}, !IsEmpty() for: {?} not is empty MatchersRanges.tests.cpp:: passed: unrelated::ADL_empty{}, IsEmpty() for: {?} is empty Message.tests.cpp:: passed: with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true' -Message.tests.cpp:: passed: with 7 messages: 'std::vector{1, 2, 3}[0, 1, 2] := 3' and 'std::vector{1, 2, 3}[(0, 1)] := 2' and 'std::vector{1, 2, 3}[0] := 1' and '(helper_1436{12, -12}) := { 12, -12 }' and '(helper_1436(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' +Message.tests.cpp:: passed: with 7 messages: 'custom_index_op{1, 2, 3}[0, 1, 2] := 0' and 'custom_index_op{1, 2, 3}[(0, 1)] := 0' and 'custom_index_op{1, 2, 3}[0] := 0' and '(helper_1436{12, -12}) := { 12, -12 }' and '(helper_1436(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' Message.tests.cpp:: passed: with 11 messages: '("comma, in string", "escaped, \", ") := "escaped, ", "' and '"single quote in string,'," := "single quote in string,',"' and '"some escapes, \\,\\\\" := "some escapes, \,\\"' and '"some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<"' and ''"' := '"'' and ''\'' := '''' and '',' := ','' and ''}' := '}'' and '')' := ')'' and ''(' := '('' and ''{' := '{'' ToStringGeneral.tests.cpp:: passed: true with 1 message: 'i := 2' ToStringGeneral.tests.cpp:: passed: true with 1 message: '3' @@ -350,20 +378,22 @@ Details.tests.cpp:: passed: lt( "a", "b" ) for: true Details.tests.cpp:: passed: lt( "a", "B" ) for: true Details.tests.cpp:: passed: lt( "A", "b" ) for: true Details.tests.cpp:: passed: lt( "A", "B" ) for: true -ToStringGeneral.tests.cpp:: passed: tab == '\t' for: '\t' == '\t' -ToStringGeneral.tests.cpp:: passed: newline == '\n' for: '\n' == '\n' -ToStringGeneral.tests.cpp:: passed: carr_return == '\r' for: '\r' == '\r' -ToStringGeneral.tests.cpp:: passed: form_feed == '\f' for: '\f' == '\f' -ToStringGeneral.tests.cpp:: passed: space == ' ' for: ' ' == ' ' -ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'a' == 'a' -ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'z' == 'z' -ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'A' == 'A' -ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'Z' == 'Z' -ToStringGeneral.tests.cpp:: passed: null_terminator == '\0' for: 0 == 0 -ToStringGeneral.tests.cpp:: passed: c == i for: 2 == 2 -ToStringGeneral.tests.cpp:: passed: c == i for: 3 == 3 -ToStringGeneral.tests.cpp:: passed: c == i for: 4 == 4 -ToStringGeneral.tests.cpp:: passed: c == i for: 5 == 5 +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify('\t') == "'\\t'" for: "'\t'" == "'\t'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify('\n') == "'\\n'" for: "'\n'" == "'\n'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify('\r') == "'\\r'" for: "'\r'" == "'\r'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify('\f') == "'\\f'" for: "'\f'" == "'\f'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( ' ' ) == "' '" for: "' '" == "' '" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( 'A' ) == "'A'" for: "'A'" == "'A'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( 'z' ) == "'z'" for: "'z'" == "'z'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( '\0' ) == "0" for: "0" == "0" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( static_cast(2) ) == "2" for: "2" == "2" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( static_cast(5) ) == "5" for: "5" == "5" +Clara.tests.cpp:: passed: name.empty() for: true +Clara.tests.cpp:: passed: result for: {?} +Clara.tests.cpp:: passed: result.type() == Catch::Clara::Detail::ResultType::Ok for: 0 == 0 +Clara.tests.cpp:: passed: parsed.type() == Catch::Clara::ParseResultType::NoMatch for: 1 == 1 +Clara.tests.cpp:: passed: parsed.remainingTokens().count() == 2 for: 2 == 2 +Clara.tests.cpp:: passed: name.empty() for: true Clara.tests.cpp:: passed: name.empty() for: true Clara.tests.cpp:: passed: name == "foo" for: "foo" == "foo" Clara.tests.cpp:: passed: !(parse_result) for: !{?} @@ -513,15 +543,15 @@ Stream.tests.cpp:: passed: Catch::makeStream( "-" )->isConsole() fo Exception.tests.cpp:: failed: unexpected exception with message: 'custom exception - not std'; expression was: throwCustom() Exception.tests.cpp:: failed: unexpected exception with message: 'custom exception - not std'; expression was: throwCustom(), std::exception Exception.tests.cpp:: failed: unexpected exception with message: 'custom std exception' -Approx.tests.cpp:: passed: 101.000001 != Approx(100).epsilon(0.01) for: 101.000001 != Approx( 100.0 ) +Approx.tests.cpp:: passed: 101.000001 != Approx(100).epsilon(0.01) for: 101.00000099999999748 != Approx( 100.0 ) Approx.tests.cpp:: passed: std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 ) ToString.tests.cpp:: passed: enumInfo->lookup(0) == "Value1" for: Value1 == "Value1" ToString.tests.cpp:: passed: enumInfo->lookup(1) == "Value2" for: Value2 == "Value2" ToString.tests.cpp:: passed: enumInfo->lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **} == "{** unexpected enum value **}" +Skip.tests.cpp:: skipped: 'This generator is empty' Stream.tests.cpp:: passed: Catch::makeStream( "" )->isConsole() for: true -Tag.tests.cpp:: passed: Catch::TestCaseInfo( "", { "fake test name", "[]" }, dummySourceLineInfo ) Matchers.tests.cpp:: failed: testStringForMatching(), EndsWith( "Substring" ) for: "this string contains 'abc' as a substring" ends with: "Substring" Matchers.tests.cpp:: failed: testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: "this" (case insensitive) EnumToString.tests.cpp:: passed: stringify( EnumClass3::Value1 ) == "Value1" for: "Value1" == "Value1" @@ -533,27 +563,39 @@ EnumToString.tests.cpp:: passed: stringify( EnumClass3::Value4 ) == EnumToString.tests.cpp:: passed: stringify( ec3 ) == "Value2" for: "Value2" == "Value2" EnumToString.tests.cpp:: passed: stringify( Bikeshed::Colours::Red ) == "Red" for: "Red" == "Red" EnumToString.tests.cpp:: passed: stringify( Bikeshed::Colours::Blue ) == "Blue" for: "Blue" == "Blue" -Approx.tests.cpp:: passed: 101.01 != Approx(100).epsilon(0.01) for: 101.01 != Approx( 100.0 ) +Approx.tests.cpp:: passed: 101.01 != Approx(100).epsilon(0.01) for: 101.01000000000000512 != Approx( 100.0 ) Condition.tests.cpp:: failed: data.int_seven == 6 for: 7 == 6 Condition.tests.cpp:: failed: data.int_seven == 8 for: 7 == 8 Condition.tests.cpp:: failed: data.int_seven == 0 for: 7 == 0 -Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 9.11f ) for: 9.1f == Approx( 9.1099996567 ) -Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 9.0f ) for: 9.1f == Approx( 9.0 ) -Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 1 ) for: 9.1f == Approx( 1.0 ) -Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 0 ) for: 9.1f == Approx( 0.0 ) -Condition.tests.cpp:: failed: data.double_pi == Approx( 3.1415 ) for: 3.1415926535 == Approx( 3.1415 ) +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 9.11f ) for: 9.100000381f +== +Approx( 9.10999965667724609 ) +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 9.0f ) for: 9.100000381f == Approx( 9.0 ) +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 1 ) for: 9.100000381f == Approx( 1.0 ) +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 0 ) for: 9.100000381f == Approx( 0.0 ) +Condition.tests.cpp:: failed: data.double_pi == Approx( 3.1415 ) for: 3.14159265350000005 +== +Approx( 3.14150000000000018 ) Condition.tests.cpp:: failed: data.str_hello == "goodbye" for: "hello" == "goodbye" Condition.tests.cpp:: failed: data.str_hello == "hell" for: "hello" == "hell" Condition.tests.cpp:: failed: data.str_hello == "hello1" for: "hello" == "hello1" Condition.tests.cpp:: failed: data.str_hello.size() == 6 for: 5 == 6 -Condition.tests.cpp:: failed: x == Approx( 1.301 ) for: 1.3 == Approx( 1.301 ) +Condition.tests.cpp:: failed: x == Approx( 1.301 ) for: 1.30000000000000027 +== +Approx( 1.30099999999999993 ) Condition.tests.cpp:: passed: data.int_seven == 7 for: 7 == 7 -Condition.tests.cpp:: passed: data.float_nine_point_one == Approx( 9.1f ) for: 9.1f == Approx( 9.1000003815 ) -Condition.tests.cpp:: passed: data.double_pi == Approx( 3.1415926535 ) for: 3.1415926535 == Approx( 3.1415926535 ) +Condition.tests.cpp:: passed: data.float_nine_point_one == Approx( 9.1f ) for: 9.100000381f +== +Approx( 9.10000038146972656 ) +Condition.tests.cpp:: passed: data.double_pi == Approx( 3.1415926535 ) for: 3.14159265350000005 +== +Approx( 3.14159265350000005 ) Condition.tests.cpp:: passed: data.str_hello == "hello" for: "hello" == "hello" Condition.tests.cpp:: passed: "hello" == data.str_hello for: "hello" == "hello" Condition.tests.cpp:: passed: data.str_hello.size() == 5 for: 5 == 5 -Condition.tests.cpp:: passed: x == Approx( 1.3 ) for: 1.3 == Approx( 1.3 ) +Condition.tests.cpp:: passed: x == Approx( 1.3 ) for: 1.30000000000000027 +== +Approx( 1.30000000000000004 ) Matchers.tests.cpp:: passed: testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" Matchers.tests.cpp:: passed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive) Matchers.tests.cpp:: failed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring" @@ -600,21 +642,21 @@ Misc.tests.cpp:: passed: Factorial(2) == 2 for: 2 == 2 Misc.tests.cpp:: passed: Factorial(3) == 6 for: 6 == 6 Misc.tests.cpp:: passed: Factorial(10) == 3628800 for: 3628800 (0x) == 3628800 (0x) GeneratorsImpl.tests.cpp:: passed: filter( []( int ) { return false; }, value( 3 ) ), Catch::GeneratorException -Matchers.tests.cpp:: passed: 10., WithinRel( 11.1, 0.1 ) for: 10.0 and 11.1 are within 10% of each other -Matchers.tests.cpp:: passed: 10., !WithinRel( 11.2, 0.1 ) for: 10.0 not and 11.2 are within 10% of each other -Matchers.tests.cpp:: passed: 1., !WithinRel( 0., 0.99 ) for: 1.0 not and 0 are within 99% of each other -Matchers.tests.cpp:: passed: -0., WithinRel( 0. ) for: -0.0 and 0 are within 2.22045e-12% of each other -Matchers.tests.cpp:: passed: v1, WithinRel( v2 ) for: 0.0 and 2.22507e-308 are within 2.22045e-12% of each other +Matchers.tests.cpp:: passed: 10., WithinRel( 11.1, 0.1 ) for: 10.0 and 11.09999999999999964 are within 10% of each other +Matchers.tests.cpp:: passed: 10., !WithinRel( 11.2, 0.1 ) for: 10.0 not and 11.19999999999999929 are within 10% of each other +Matchers.tests.cpp:: passed: 1., !WithinRel( 0., 0.99 ) for: 1.0 not and 0.0 are within 99% of each other +Matchers.tests.cpp:: passed: -0., WithinRel( 0. ) for: -0.0 and 0.0 are within 2.22045e-12% of each other +Matchers.tests.cpp:: passed: v1, WithinRel( v2 ) for: 0.0 and 0.0 are within 2.22045e-12% of each other Matchers.tests.cpp:: passed: 1., WithinAbs( 1., 0 ) for: 1.0 is within 0.0 of 1.0 Matchers.tests.cpp:: passed: 0., WithinAbs( 1., 1 ) for: 0.0 is within 1.0 of 1.0 -Matchers.tests.cpp:: passed: 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0 -Matchers.tests.cpp:: passed: 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0 +Matchers.tests.cpp:: passed: 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.98999999999999999 of 1.0 +Matchers.tests.cpp:: passed: 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.98999999999999999 of 1.0 Matchers.tests.cpp:: passed: 11., !WithinAbs( 10., 0.5 ) for: 11.0 not is within 0.5 of 10.0 Matchers.tests.cpp:: passed: 10., !WithinAbs( 11., 0.5 ) for: 10.0 not is within 0.5 of 11.0 Matchers.tests.cpp:: passed: -10., WithinAbs( -10., 0.5 ) for: -10.0 is within 0.5 of -10.0 -Matchers.tests.cpp:: passed: -10., WithinAbs( -9.6, 0.5 ) for: -10.0 is within 0.5 of -9.6 +Matchers.tests.cpp:: passed: -10., WithinAbs( -9.6, 0.5 ) for: -10.0 is within 0.5 of -9.59999999999999964 Matchers.tests.cpp:: passed: 1., WithinULP( 1., 0 ) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) -Matchers.tests.cpp:: passed: nextafter( 1., 2. ), WithinULP( 1., 1 ) for: 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) +Matchers.tests.cpp:: passed: nextafter( 1., 2. ), WithinULP( 1., 1 ) for: 1.00000000000000022 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) Matchers.tests.cpp:: passed: 0., WithinULP( nextafter( 0., 1. ), 1 ) for: 0.0 is within 1 ULPs of 4.9406564584124654e-324 ([0.0000000000000000e+00, 9.8813129168249309e-324]) Matchers.tests.cpp:: passed: 1., WithinULP( nextafter( 1., 0. ), 1 ) for: 1.0 is within 1 ULPs of 9.9999999999999989e-01 ([9.9999999999999978e-01, 1.0000000000000000e+00]) Matchers.tests.cpp:: passed: 1., !WithinULP( nextafter( 1., 2. ), 0 ) for: 1.0 not is within 0 ULPs of 1.0000000000000002e+00 ([1.0000000000000002e+00, 1.0000000000000002e+00]) @@ -622,7 +664,7 @@ Matchers.tests.cpp:: passed: 1., WithinULP( 1., 0 ) for: 1.0 is wit Matchers.tests.cpp:: passed: -0., WithinULP( 0., 0 ) for: -0.0 is within 0 ULPs of 0.0000000000000000e+00 ([0.0000000000000000e+00, 0.0000000000000000e+00]) Matchers.tests.cpp:: passed: 1., WithinAbs( 1., 0.5 ) || WithinULP( 2., 1 ) for: 1.0 ( is within 0.5 of 1.0 or is within 1 ULPs of 2.0000000000000000e+00 ([1.9999999999999998e+00, 2.0000000000000004e+00]) ) Matchers.tests.cpp:: passed: 1., WithinAbs( 2., 0.5 ) || WithinULP( 1., 0 ) for: 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) ) -Matchers.tests.cpp:: passed: 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) for: 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) +Matchers.tests.cpp:: passed: 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) for: 0.0001 ( is within 0.001 of 0.0 or and 0.0 are within 10% of each other ) Matchers.tests.cpp:: passed: WithinAbs( 1., 0. ) Matchers.tests.cpp:: passed: WithinAbs( 1., -1. ), std::domain_error Matchers.tests.cpp:: passed: WithinULP( 1., 0 ) @@ -630,23 +672,23 @@ Matchers.tests.cpp:: passed: WithinRel( 1., 0. ) Matchers.tests.cpp:: passed: WithinRel( 1., -0.2 ), std::domain_error Matchers.tests.cpp:: passed: WithinRel( 1., 1. ), std::domain_error Matchers.tests.cpp:: passed: 1., !IsNaN() for: 1.0 not is NaN -Matchers.tests.cpp:: passed: 10.f, WithinRel( 11.1f, 0.1f ) for: 10.0f and 11.1 are within 10% of each other -Matchers.tests.cpp:: passed: 10.f, !WithinRel( 11.2f, 0.1f ) for: 10.0f not and 11.2 are within 10% of each other -Matchers.tests.cpp:: passed: 1.f, !WithinRel( 0.f, 0.99f ) for: 1.0f not and 0 are within 99% of each other -Matchers.tests.cpp:: passed: -0.f, WithinRel( 0.f ) for: -0.0f and 0 are within 0.00119209% of each other -Matchers.tests.cpp:: passed: v1, WithinRel( v2 ) for: 0.0f and 1.17549e-38 are within 0.00119209% of each other +Matchers.tests.cpp:: passed: 10.f, WithinRel( 11.1f, 0.1f ) for: 10.0f and 11.10000038146972656 are within 10% of each other +Matchers.tests.cpp:: passed: 10.f, !WithinRel( 11.2f, 0.1f ) for: 10.0f not and 11.19999980926513672 are within 10% of each other +Matchers.tests.cpp:: passed: 1.f, !WithinRel( 0.f, 0.99f ) for: 1.0f not and 0.0 are within 99% of each other +Matchers.tests.cpp:: passed: -0.f, WithinRel( 0.f ) for: -0.0f and 0.0 are within 0.00119209% of each other +Matchers.tests.cpp:: passed: v1, WithinRel( v2 ) for: 0.0f and 0.0 are within 0.00119209% of each other Matchers.tests.cpp:: passed: 1.f, WithinAbs( 1.f, 0 ) for: 1.0f is within 0.0 of 1.0 Matchers.tests.cpp:: passed: 0.f, WithinAbs( 1.f, 1 ) for: 0.0f is within 1.0 of 1.0 -Matchers.tests.cpp:: passed: 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0 -Matchers.tests.cpp:: passed: 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0 +Matchers.tests.cpp:: passed: 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.99000000953674316 of 1.0 +Matchers.tests.cpp:: passed: 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.99000000953674316 of 1.0 Matchers.tests.cpp:: passed: 0.f, WithinAbs( -0.f, 0 ) for: 0.0f is within 0.0 of -0.0 Matchers.tests.cpp:: passed: 11.f, !WithinAbs( 10.f, 0.5f ) for: 11.0f not is within 0.5 of 10.0 Matchers.tests.cpp:: passed: 10.f, !WithinAbs( 11.f, 0.5f ) for: 10.0f not is within 0.5 of 11.0 Matchers.tests.cpp:: passed: -10.f, WithinAbs( -10.f, 0.5f ) for: -10.0f is within 0.5 of -10.0 -Matchers.tests.cpp:: passed: -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of -9.6000003815 +Matchers.tests.cpp:: passed: -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of -9.60000038146972656 Matchers.tests.cpp:: passed: 1.f, WithinULP( 1.f, 0 ) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) Matchers.tests.cpp:: passed: -1.f, WithinULP( -1.f, 0 ) for: -1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+00]) -Matchers.tests.cpp:: passed: nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) +Matchers.tests.cpp:: passed: nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.000000119f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) Matchers.tests.cpp:: passed: 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) for: 0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45]) Matchers.tests.cpp:: passed: 1.f, WithinULP( nextafter( 1.f, 0.f ), 1 ) for: 1.0f is within 1 ULPs of 9.99999940e-01f ([9.99999881e-01, 1.00000000e+00]) Matchers.tests.cpp:: passed: 1.f, !WithinULP( nextafter( 1.f, 2.f ), 0 ) for: 1.0f not is within 0 ULPs of 1.00000012e+00f ([1.00000012e+00, 1.00000012e+00]) @@ -654,7 +696,7 @@ Matchers.tests.cpp:: passed: 1.f, WithinULP( 1.f, 0 ) for: 1.0f is Matchers.tests.cpp:: passed: -0.f, WithinULP( 0.f, 0 ) for: -0.0f is within 0 ULPs of 0.00000000e+00f ([0.00000000e+00, 0.00000000e+00]) Matchers.tests.cpp:: passed: 1.f, WithinAbs( 1.f, 0.5 ) || WithinULP( 1.f, 1 ) for: 1.0f ( is within 0.5 of 1.0 or is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) ) Matchers.tests.cpp:: passed: 1.f, WithinAbs( 2.f, 0.5 ) || WithinULP( 1.f, 0 ) for: 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) ) -Matchers.tests.cpp:: passed: 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) for: 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) +Matchers.tests.cpp:: passed: 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) for: 0.0001f ( is within 0.00100000004749745 of 0.0 or and 0.0 are within 10% of each other ) Matchers.tests.cpp:: passed: WithinAbs( 1.f, 0.f ) Matchers.tests.cpp:: passed: WithinAbs( 1.f, -1.f ), std::domain_error Matchers.tests.cpp:: passed: WithinULP( 1.f, 0 ) @@ -666,6 +708,10 @@ Matchers.tests.cpp:: passed: 1., !IsNaN() for: 1.0 not is NaN Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:: passed: filter([] (int) {return false; }, value(1)), Catch::GeneratorException Generators.tests.cpp:: passed: i < 4 for: 1 < 4 Generators.tests.cpp:: passed: i < 4 for: 2 < 4 @@ -830,68 +876,122 @@ GeneratorsImpl.tests.cpp:: passed: gen.get() == 5 for: 5 == 5 GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) with 1 message: 'Current expected value is -1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.9 == Approx( -0.9 ) with 1 message: 'Current expected value is -0.9' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.90000000000000002 +== +Approx( -0.90000000000000002 ) with 1 message: 'Current expected value is -0.9' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.9' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.8 == Approx( -0.8 ) with 1 message: 'Current expected value is -0.8' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.80000000000000004 +== +Approx( -0.80000000000000004 ) with 1 message: 'Current expected value is -0.8' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.8' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.70000000000000007 +== +Approx( -0.70000000000000007 ) with 1 message: 'Current expected value is -0.7' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.7' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.6 == Approx( -0.6 ) with 1 message: 'Current expected value is -0.6' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.60000000000000009 +== +Approx( -0.60000000000000009 ) with 1 message: 'Current expected value is -0.6' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.6' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.5 == Approx( -0.5 ) with 1 message: 'Current expected value is -0.5' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.50000000000000011 +== +Approx( -0.50000000000000011 ) with 1 message: 'Current expected value is -0.5' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.5' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.40000000000000013 +== +Approx( -0.40000000000000013 ) with 1 message: 'Current expected value is -0.4' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.4' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.3 == Approx( -0.3 ) with 1 message: 'Current expected value is -0.3' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.30000000000000016 +== +Approx( -0.30000000000000016 ) with 1 message: 'Current expected value is -0.3' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.3' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.2 == Approx( -0.2 ) with 1 message: 'Current expected value is -0.2' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.20000000000000015 +== +Approx( -0.20000000000000015 ) with 1 message: 'Current expected value is -0.2' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.2' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.10000000000000014 +== +Approx( -0.10000000000000014 ) with 1 message: 'Current expected value is -0.1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.0 == Approx( -0.0 ) with 1 message: 'Current expected value is -1.38778e-16' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.00000000000000014 +== +Approx( -0.00000000000000014 ) with 1 message: 'Current expected value is -1.38778e-16' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -1.38778e-16' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.1 == Approx( 0.1 ) with 1 message: 'Current expected value is 0.1' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.09999999999999987 +== +Approx( 0.09999999999999987 ) with 1 message: 'Current expected value is 0.1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.19999999999999987 +== +Approx( 0.19999999999999987 ) with 1 message: 'Current expected value is 0.2' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.2' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.3 == Approx( 0.3 ) with 1 message: 'Current expected value is 0.3' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.29999999999999988 +== +Approx( 0.29999999999999988 ) with 1 message: 'Current expected value is 0.3' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.3' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.4 == Approx( 0.4 ) with 1 message: 'Current expected value is 0.4' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.39999999999999991 +== +Approx( 0.39999999999999991 ) with 1 message: 'Current expected value is 0.4' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.4' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.5 == Approx( 0.5 ) with 1 message: 'Current expected value is 0.5' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.49999999999999989 +== +Approx( 0.49999999999999989 ) with 1 message: 'Current expected value is 0.5' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.5' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.6 == Approx( 0.6 ) with 1 message: 'Current expected value is 0.6' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.59999999999999987 +== +Approx( 0.59999999999999987 ) with 1 message: 'Current expected value is 0.6' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.6' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.7 == Approx( 0.7 ) with 1 message: 'Current expected value is 0.7' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.69999999999999984 +== +Approx( 0.69999999999999984 ) with 1 message: 'Current expected value is 0.7' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.7' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.8 == Approx( 0.8 ) with 1 message: 'Current expected value is 0.8' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.79999999999999982 +== +Approx( 0.79999999999999982 ) with 1 message: 'Current expected value is 0.8' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.8' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.9 == Approx( 0.9 ) with 1 message: 'Current expected value is 0.9' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.8999999999999998 +== +Approx( 0.8999999999999998 ) with 1 message: 'Current expected value is 0.9' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.9' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx( rangeEnd ) for: 1.0 == Approx( 1.0 ) +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx( rangeEnd ) for: 0.99999999999999978 == Approx( 1.0 ) GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) with 1 message: 'Current expected value is -1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.69999999999999996 +== +Approx( -0.69999999999999996 ) with 1 message: 'Current expected value is -0.7' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.7' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.39999999999999997 +== +Approx( -0.39999999999999997 ) with 1 message: 'Current expected value is -0.4' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.4' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.09999999999999998 +== +Approx( -0.09999999999999998 ) with 1 message: 'Current expected value is -0.1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.20000000000000001 +== +Approx( 0.20000000000000001 ) with 1 message: 'Current expected value is 0.2' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.2' GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.5 == Approx( 0.5 ) with 1 message: 'Current expected value is 0.5' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.5' GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) with 1 message: 'Current expected value is -1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.69999999999999996 +== +Approx( -0.69999999999999996 ) with 1 message: 'Current expected value is -0.7' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.7' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.39999999999999997 +== +Approx( -0.39999999999999997 ) with 1 message: 'Current expected value is -0.4' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.4' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.09999999999999998 +== +Approx( -0.09999999999999998 ) with 1 message: 'Current expected value is -0.1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.20000000000000001 +== +Approx( 0.20000000000000001 ) with 1 message: 'Current expected value is 0.2' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.2' GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.5 == Approx( 0.5 ) with 1 message: 'Current expected value is 0.5' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.5' @@ -922,10 +1022,18 @@ GeneratorsImpl.tests.cpp:: passed: gen.get() == -4 for: -4 == -4 GeneratorsImpl.tests.cpp:: passed: gen.next() for: true GeneratorsImpl.tests.cpp:: passed: gen.get() == -7 for: -7 == -7 GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false -Approx.tests.cpp:: passed: d >= Approx( 1.22 ) for: 1.23 >= Approx( 1.22 ) -Approx.tests.cpp:: passed: d >= Approx( 1.23 ) for: 1.23 >= Approx( 1.23 ) -Approx.tests.cpp:: passed: !(d >= Approx( 1.24 )) for: !(1.23 >= Approx( 1.24 )) -Approx.tests.cpp:: passed: d >= Approx( 1.24 ).epsilon(0.1) for: 1.23 >= Approx( 1.24 ) +Approx.tests.cpp:: passed: d >= Approx( 1.22 ) for: 1.22999999999999998 +>= +Approx( 1.21999999999999997 ) +Approx.tests.cpp:: passed: d >= Approx( 1.23 ) for: 1.22999999999999998 +>= +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: !(d >= Approx( 1.24 )) for: !(1.22999999999999998 +>= +Approx( 1.23999999999999999 )) +Approx.tests.cpp:: passed: d >= Approx( 1.24 ).epsilon(0.1) for: 1.22999999999999998 +>= +Approx( 1.23999999999999999 ) TestCaseInfoHasher.tests.cpp:: passed: h1( dummy ) != h2( dummy ) for: 3422778688 (0x) != 130711275 (0x) @@ -944,6 +1052,7 @@ TestCaseInfoHasher.tests.cpp:: passed: h( dummy1 ) != h( dummy2 ) f TestCaseInfoHasher.tests.cpp:: passed: h( dummy ) == h( dummy ) for: 3422778688 (0x) == 3422778688 (0x) +Message.tests.cpp:: failed: explicitly with 3 messages: 'This info has multiple parts.' and 'This unscoped info has multiple parts.' and 'Show infos!' Message.tests.cpp:: warning: 'this is a message' with 1 message: 'this is a warning' Message.tests.cpp:: failed: a == 1 for: 2 == 1 with 2 messages: 'this message should be logged' and 'so should this' Message.tests.cpp:: passed: a == 2 for: 2 == 2 with 1 message: 'this message may be logged later' @@ -961,27 +1070,129 @@ Message.tests.cpp:: passed: i < 10 for: 7 < 10 with 2 messages: 'cu Message.tests.cpp:: passed: i < 10 for: 8 < 10 with 2 messages: 'current counter 8' and 'i := 8' Message.tests.cpp:: passed: i < 10 for: 9 < 10 with 2 messages: 'current counter 9' and 'i := 9' Message.tests.cpp:: failed: i < 10 for: 10 < 10 with 2 messages: 'current counter 10' and 'i := 10' +AssertionHandler.tests.cpp:: failed: unexpected exception with message: 'Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE'; expression was: Dummy Condition.tests.cpp:: failed: data.int_seven != 7 for: 7 != 7 -Condition.tests.cpp:: failed: data.float_nine_point_one != Approx( 9.1f ) for: 9.1f != Approx( 9.1000003815 ) -Condition.tests.cpp:: failed: data.double_pi != Approx( 3.1415926535 ) for: 3.1415926535 != Approx( 3.1415926535 ) +Condition.tests.cpp:: failed: data.float_nine_point_one != Approx( 9.1f ) for: 9.100000381f +!= +Approx( 9.10000038146972656 ) +Condition.tests.cpp:: failed: data.double_pi != Approx( 3.1415926535 ) for: 3.14159265350000005 +!= +Approx( 3.14159265350000005 ) Condition.tests.cpp:: failed: data.str_hello != "hello" for: "hello" != "hello" Condition.tests.cpp:: failed: data.str_hello.size() != 5 for: 5 != 5 Condition.tests.cpp:: passed: data.int_seven != 6 for: 7 != 6 Condition.tests.cpp:: passed: data.int_seven != 8 for: 7 != 8 -Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 9.11f ) for: 9.1f != Approx( 9.1099996567 ) -Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 9.0f ) for: 9.1f != Approx( 9.0 ) -Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 1 ) for: 9.1f != Approx( 1.0 ) -Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 0 ) for: 9.1f != Approx( 0.0 ) -Condition.tests.cpp:: passed: data.double_pi != Approx( 3.1415 ) for: 3.1415926535 != Approx( 3.1415 ) +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 9.11f ) for: 9.100000381f +!= +Approx( 9.10999965667724609 ) +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 9.0f ) for: 9.100000381f != Approx( 9.0 ) +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 1 ) for: 9.100000381f != Approx( 1.0 ) +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 0 ) for: 9.100000381f != Approx( 0.0 ) +Condition.tests.cpp:: passed: data.double_pi != Approx( 3.1415 ) for: 3.14159265350000005 +!= +Approx( 3.14150000000000018 ) Condition.tests.cpp:: passed: data.str_hello != "goodbye" for: "hello" != "goodbye" Condition.tests.cpp:: passed: data.str_hello != "hell" for: "hello" != "hell" Condition.tests.cpp:: passed: data.str_hello != "hello1" for: "hello" != "hello1" Condition.tests.cpp:: passed: data.str_hello.size() != 6 for: 5 != 6 +Json.tests.cpp:: passed: stream.str() == "" for: "" == "" +Json.tests.cpp:: passed: stream.str() == "{\n}" for: "{ +}" +== +"{ +}" +Json.tests.cpp:: passed: stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) for: "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] +}" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ + 1, + 2 + ] +}" ) +Json.tests.cpp:: passed: stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) for: "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } +}" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) +Json.tests.cpp:: passed: stream.str() == "[\n]" for: "[ +]" +== +"[ +]" +Json.tests.cpp:: passed: stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" for: "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +== +"[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +Json.tests.cpp:: passed: stream.str() == "{\n}" for: "{ +}" +== +"{ +}" +Json.tests.cpp:: passed: stream.str() == "[\n]" for: "[ +]" +== +"[ +]" +Json.tests.cpp:: passed: stream.str() == "\"custom\"" for: ""custom"" == ""custom"" +Json.tests.cpp:: passed: sstream.str() == "\"\\\"\"" for: ""\""" == ""\""" +Json.tests.cpp:: passed: sstream.str() == "\"\\\\\"" for: ""\\"" == ""\\"" +Json.tests.cpp:: passed: sstream.str() == "\"/\"" for: ""/"" == ""/"" +Json.tests.cpp:: passed: sstream.str() == "\"\\b\"" for: ""\b"" == ""\b"" +Json.tests.cpp:: passed: sstream.str() == "\"\\f\"" for: ""\f"" == ""\f"" +Json.tests.cpp:: passed: sstream.str() == "\"\\n\"" for: ""\n"" == ""\n"" +Json.tests.cpp:: passed: sstream.str() == "\"\\r\"" for: ""\r"" == ""\r"" +Json.tests.cpp:: passed: sstream.str() == "\"\\t\"" for: ""\t"" == ""\t"" +Json.tests.cpp:: passed: sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n"" Compilation.tests.cpp:: passed: []() { return true; }() for: true -Approx.tests.cpp:: passed: d <= Approx( 1.24 ) for: 1.23 <= Approx( 1.24 ) -Approx.tests.cpp:: passed: d <= Approx( 1.23 ) for: 1.23 <= Approx( 1.23 ) -Approx.tests.cpp:: passed: !(d <= Approx( 1.22 )) for: !(1.23 <= Approx( 1.22 )) -Approx.tests.cpp:: passed: d <= Approx( 1.22 ).epsilon(0.1) for: 1.23 <= Approx( 1.22 ) +Approx.tests.cpp:: passed: d <= Approx( 1.24 ) for: 1.22999999999999998 +<= +Approx( 1.23999999999999999 ) +Approx.tests.cpp:: passed: d <= Approx( 1.23 ) for: 1.22999999999999998 +<= +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: !(d <= Approx( 1.22 )) for: !(1.22999999999999998 +<= +Approx( 1.21999999999999997 )) +Approx.tests.cpp:: passed: d <= Approx( 1.22 ).epsilon(0.1) for: 1.22999999999999998 +<= +Approx( 1.21999999999999997 ) Misc.tests.cpp:: passed: with 1 message: 'was called' Matchers.tests.cpp:: passed: testStringForMatching(), ContainsSubstring( "string" ) && ContainsSubstring( "abc" ) && ContainsSubstring( "substring" ) && ContainsSubstring( "contains" ) for: "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" and contains: "substring" and contains: "contains" ) Matchers.tests.cpp:: passed: testStringForMatching(), ContainsSubstring( "string" ) || ContainsSubstring( "different" ) || ContainsSubstring( "random" ) for: "this string contains 'abc' as a substring" ( contains: "string" or contains: "different" or contains: "random" ) @@ -1048,9 +1259,9 @@ Condition.tests.cpp:: failed: data.int_seven < 0 for: 7 < 0 Condition.tests.cpp:: failed: data.int_seven < -1 for: 7 < -1 Condition.tests.cpp:: failed: data.int_seven >= 8 for: 7 >= 8 Condition.tests.cpp:: failed: data.int_seven <= 6 for: 7 <= 6 -Condition.tests.cpp:: failed: data.float_nine_point_one < 9 for: 9.1f < 9 -Condition.tests.cpp:: failed: data.float_nine_point_one > 10 for: 9.1f > 10 -Condition.tests.cpp:: failed: data.float_nine_point_one > 9.2 for: 9.1f > 9.2 +Condition.tests.cpp:: failed: data.float_nine_point_one < 9 for: 9.100000381f < 9 +Condition.tests.cpp:: failed: data.float_nine_point_one > 10 for: 9.100000381f > 10 +Condition.tests.cpp:: failed: data.float_nine_point_one > 9.2 for: 9.100000381f > 9.19999999999999929 Condition.tests.cpp:: failed: data.str_hello > "hello" for: "hello" > "hello" Condition.tests.cpp:: failed: data.str_hello < "hello" for: "hello" < "hello" Condition.tests.cpp:: failed: data.str_hello > "hellp" for: "hello" > "hellp" @@ -1067,9 +1278,9 @@ Condition.tests.cpp:: passed: data.int_seven >= 7 for: 7 >= 7 Condition.tests.cpp:: passed: data.int_seven >= 6 for: 7 >= 6 Condition.tests.cpp:: passed: data.int_seven <= 7 for: 7 <= 7 Condition.tests.cpp:: passed: data.int_seven <= 8 for: 7 <= 8 -Condition.tests.cpp:: passed: data.float_nine_point_one > 9 for: 9.1f > 9 -Condition.tests.cpp:: passed: data.float_nine_point_one < 10 for: 9.1f < 10 -Condition.tests.cpp:: passed: data.float_nine_point_one < 9.2 for: 9.1f < 9.2 +Condition.tests.cpp:: passed: data.float_nine_point_one > 9 for: 9.100000381f > 9 +Condition.tests.cpp:: passed: data.float_nine_point_one < 10 for: 9.100000381f < 10 +Condition.tests.cpp:: passed: data.float_nine_point_one < 9.2 for: 9.100000381f < 9.19999999999999929 Condition.tests.cpp:: passed: data.str_hello <= "hello" for: "hello" <= "hello" Condition.tests.cpp:: passed: data.str_hello >= "hello" for: "hello" >= "hello" Condition.tests.cpp:: passed: data.str_hello < "hellp" for: "hello" < "hellp" @@ -1267,7 +1478,9 @@ CmdLine.tests.cpp:: passed: config.benchmarkSamples == 200 for: 200 CmdLine.tests.cpp:: passed: cli.parse({ "test", "--benchmark-resamples=20000" }) for: {?} CmdLine.tests.cpp:: passed: config.benchmarkResamples == 20000 for: 20000 (0x) == 20000 (0x) CmdLine.tests.cpp:: passed: cli.parse({ "test", "--benchmark-confidence-interval=0.99" }) for: {?} -CmdLine.tests.cpp:: passed: config.benchmarkConfidenceInterval == Catch::Approx(0.99) for: 0.99 == Approx( 0.99 ) +CmdLine.tests.cpp:: passed: config.benchmarkConfidenceInterval == Catch::Approx(0.99) for: 0.98999999999999999 +== +Approx( 0.98999999999999999 ) CmdLine.tests.cpp:: passed: cli.parse({ "test", "--benchmark-no-analysis" }) for: {?} CmdLine.tests.cpp:: passed: config.benchmarkNoAnalysis for: true CmdLine.tests.cpp:: passed: cli.parse({ "test", "--benchmark-warmup-time=10" }) for: {?} @@ -1341,6 +1554,60 @@ Reporters.tests.cpp:: passed: listingString, ContainsSubstring( "fa " ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: console' Reporters.tests.cpp:: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:: passed: listingString, ContainsSubstring("fakeTag"s) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:: passed: listingString, ContainsSubstring("fake reporter"s) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:: passed: listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:: passed: !(factories.empty()) for: !false Reporters.tests.cpp:: passed: listingString, ContainsSubstring("fakeTag"s) for: " All available tags: 1 [fakeTag] @@ -1468,14 +1735,30 @@ BDD.tests.cpp:: passed: v.size() == 0 for: 0 == 0 A string sent directly to stdout A string sent directly to stderr A string sent to stderr via clog -Approx.tests.cpp:: passed: d == Approx( 1.23 ) for: 1.23 == Approx( 1.23 ) -Approx.tests.cpp:: passed: d != Approx( 1.22 ) for: 1.23 != Approx( 1.22 ) -Approx.tests.cpp:: passed: d != Approx( 1.24 ) for: 1.23 != Approx( 1.24 ) -Approx.tests.cpp:: passed: d == 1.23_a for: 1.23 == Approx( 1.23 ) -Approx.tests.cpp:: passed: d != 1.22_a for: 1.23 != Approx( 1.22 ) -Approx.tests.cpp:: passed: Approx( d ) == 1.23 for: Approx( 1.23 ) == 1.23 -Approx.tests.cpp:: passed: Approx( d ) != 1.22 for: Approx( 1.23 ) != 1.22 -Approx.tests.cpp:: passed: Approx( d ) != 1.24 for: Approx( 1.23 ) != 1.24 +Approx.tests.cpp:: passed: d == Approx( 1.23 ) for: 1.22999999999999998 +== +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: d != Approx( 1.22 ) for: 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) +Approx.tests.cpp:: passed: d != Approx( 1.24 ) for: 1.22999999999999998 +!= +Approx( 1.23999999999999999 ) +Approx.tests.cpp:: passed: d == 1.23_a for: 1.22999999999999998 +== +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: d != 1.22_a for: 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) +Approx.tests.cpp:: passed: Approx( d ) == 1.23 for: Approx( 1.22999999999999998 ) +== +1.22999999999999998 +Approx.tests.cpp:: passed: Approx( d ) != 1.22 for: Approx( 1.22999999999999998 ) +!= +1.21999999999999997 +Approx.tests.cpp:: passed: Approx( d ) != 1.24 for: Approx( 1.22999999999999998 ) +!= +1.23999999999999999 Message from section one Message from section two Matchers.tests.cpp:: failed: testStringForMatching(), StartsWith( "This String" ) for: "this string contains 'abc' as a substring" starts with: "This String" @@ -1592,13 +1875,13 @@ Tag.tests.cpp:: passed: testCase.tags, VectorContains( Tag( "tag wi Class.tests.cpp:: passed: Template_Fixture::m_a == 1 for: 1 == 1 Class.tests.cpp:: passed: Template_Fixture::m_a == 1 for: 1 == 1 Class.tests.cpp:: passed: Template_Fixture::m_a == 1 for: 1.0 == 1 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 1 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 4 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 1 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 4 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 4 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 1 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 4 > 0 +Misc.tests.cpp:: passed: std::is_default_constructible::value for: true +Misc.tests.cpp:: passed: std::is_default_constructible::value for: true +Misc.tests.cpp:: passed: std::is_trivially_copyable::value for: true +Misc.tests.cpp:: passed: std::is_trivially_copyable::value for: true +Misc.tests.cpp:: passed: std::is_arithmetic::value for: true +Misc.tests.cpp:: passed: std::is_arithmetic::value for: true +Misc.tests.cpp:: passed: std::is_arithmetic::value for: true Misc.tests.cpp:: passed: v.size() == 5 for: 5 == 5 Misc.tests.cpp:: passed: v.capacity() >= 5 for: 5 >= 5 Misc.tests.cpp:: passed: v.size() == 10 for: 10 == 10 @@ -1750,6 +2033,10 @@ Misc.tests.cpp:: passed: true Misc.tests.cpp:: failed: explicitly Misc.tests.cpp:: failed - but was ok: false Misc.tests.cpp:: failed: explicitly +Misc.tests.cpp:: passed: true +Misc.tests.cpp:: failed: unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} +Misc.tests.cpp:: failed - but was ok: false +Misc.tests.cpp:: failed: unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} Message.tests.cpp:: failed - but was ok: 1 == 2 Reporters.tests.cpp:: passed: listingString, ContainsSubstring("[fakeTag]"s) for: "All available tags: 1 [fakeTag] @@ -1880,7 +2167,7 @@ MatchersRanges.tests.cpp:: passed: a, !RangeEquals( b ) for: { 1, 2 MatchersRanges.tests.cpp:: passed: a, UnorderedRangeEquals( b ) for: { 1, 2, 3 } unordered elements are { 3, 2, 1 } MatchersRanges.tests.cpp:: passed: vector_a, RangeEquals( array_a_plus_1, close_enough ) for: { 1, 2, 3 } elements are { 2, 3, 4 } MatchersRanges.tests.cpp:: passed: vector_a, UnorderedRangeEquals( array_a_plus_1, close_enough ) for: { 1, 2, 3 } unordered elements are { 2, 3, 4 } -Exception.tests.cpp:: failed: unexpected exception with message: '3.14' +Exception.tests.cpp:: failed: unexpected exception with message: '3.14000000000000012' UniquePtr.tests.cpp:: passed: bptr->i == 3 for: 3 == 3 UniquePtr.tests.cpp:: passed: bptr->i == 3 for: 3 == 3 MatchersRanges.tests.cpp:: passed: data, AllMatch(SizeIs(5)) for: { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } all match has size == 5 @@ -2026,14 +2313,26 @@ MatchersRanges.tests.cpp:: passed: arr, !SizeIs(!Lt(3)) for: { 0, 0 MatchersRanges.tests.cpp:: passed: map, SizeIs(3) for: { {?}, {?}, {?} } has size == 3 MatchersRanges.tests.cpp:: passed: unrelated::ADL_size{}, SizeIs(12) for: {?} has size == 12 MatchersRanges.tests.cpp:: passed: has_size{}, SizeIs(13) for: {?} has size == 13 -Approx.tests.cpp:: passed: d == approx( 1.23 ) for: 1.23 == Approx( 1.23 ) -Approx.tests.cpp:: passed: d == approx( 1.22 ) for: 1.23 == Approx( 1.22 ) -Approx.tests.cpp:: passed: d == approx( 1.24 ) for: 1.23 == Approx( 1.24 ) -Approx.tests.cpp:: passed: d != approx( 1.25 ) for: 1.23 != Approx( 1.25 ) -Approx.tests.cpp:: passed: approx( d ) == 1.23 for: Approx( 1.23 ) == 1.23 -Approx.tests.cpp:: passed: approx( d ) == 1.22 for: Approx( 1.23 ) == 1.22 -Approx.tests.cpp:: passed: approx( d ) == 1.24 for: Approx( 1.23 ) == 1.24 -Approx.tests.cpp:: passed: approx( d ) != 1.25 for: Approx( 1.23 ) != 1.25 +Approx.tests.cpp:: passed: d == approx( 1.23 ) for: 1.22999999999999998 +== +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: d == approx( 1.22 ) for: 1.22999999999999998 +== +Approx( 1.21999999999999997 ) +Approx.tests.cpp:: passed: d == approx( 1.24 ) for: 1.22999999999999998 +== +Approx( 1.23999999999999999 ) +Approx.tests.cpp:: passed: d != approx( 1.25 ) for: 1.22999999999999998 != Approx( 1.25 ) +Approx.tests.cpp:: passed: approx( d ) == 1.23 for: Approx( 1.22999999999999998 ) +== +1.22999999999999998 +Approx.tests.cpp:: passed: approx( d ) == 1.22 for: Approx( 1.22999999999999998 ) +== +1.21999999999999997 +Approx.tests.cpp:: passed: approx( d ) == 1.24 for: Approx( 1.22999999999999998 ) +== +1.23999999999999999 +Approx.tests.cpp:: passed: approx( d ) != 1.25 for: Approx( 1.22999999999999998 ) != 1.25 VariadicMacros.tests.cpp:: passed: with 1 message: 'no assertions' Matchers.tests.cpp:: passed: empty, Approx( empty ) for: { } is approx: { } Matchers.tests.cpp:: passed: v1, Approx( v1 ) for: { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 } @@ -2208,9 +2507,15 @@ Skip.tests.cpp:: skipped: 'skipping because answer = 41' Skip.tests.cpp:: passed: Skip.tests.cpp:: skipped: 'skipping because answer = 43' Tag.tests.cpp:: passed: Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo) -InternalBenchmark.tests.cpp:: passed: erfc_inv(1.103560) == Approx(-0.09203687623843015) for: -0.0920368762 == Approx( -0.0920368762 ) -InternalBenchmark.tests.cpp:: passed: erfc_inv(1.067400) == Approx(-0.05980291115763361) for: -0.0598029112 == Approx( -0.0598029112 ) -InternalBenchmark.tests.cpp:: passed: erfc_inv(0.050000) == Approx(1.38590382434967796) for: 1.3859038243 == Approx( 1.3859038243 ) +InternalBenchmark.tests.cpp:: passed: erfc_inv(1.103560) == Approx(-0.09203687623843015) for: -0.09203687623843014 +== +Approx( -0.09203687623843015 ) +InternalBenchmark.tests.cpp:: passed: erfc_inv(1.067400) == Approx(-0.05980291115763361) for: -0.05980291115763361 +== +Approx( -0.05980291115763361 ) +InternalBenchmark.tests.cpp:: passed: erfc_inv(0.050000) == Approx(1.38590382434967796) for: 1.38590382434967774 +== +Approx( 1.38590382434967796 ) InternalBenchmark.tests.cpp:: passed: res.mean.count() == rate for: 2000.0 == 2000 (0x) InternalBenchmark.tests.cpp:: passed: res.outliers.total() == 0 for: 0 == 0 Misc.tests.cpp:: passed: @@ -2287,14 +2592,15 @@ Skip.tests.cpp:: skipped: ! Tricky.tests.cpp:: passed: s == "7" for: "7" == "7" Tricky.tests.cpp:: passed: ti == typeid(int) for: {?} == {?} -InternalBenchmark.tests.cpp:: passed: normal_cdf(0.000000) == Approx(0.50000000000000000) for: 0.5 == Approx( 0.5 ) -InternalBenchmark.tests.cpp:: passed: normal_cdf(1.000000) == Approx(0.84134474606854293) for: 0.8413447461 == Approx( 0.8413447461 ) -InternalBenchmark.tests.cpp:: passed: normal_cdf(-1.000000) == Approx(0.15865525393145705) for: 0.1586552539 == Approx( 0.1586552539 ) -InternalBenchmark.tests.cpp:: passed: normal_cdf(2.809729) == Approx(0.99752083845315409) for: 0.9975208385 == Approx( 0.9975208385 ) -InternalBenchmark.tests.cpp:: passed: normal_cdf(-1.352570) == Approx(0.08809652095066035) for: 0.088096521 == Approx( 0.088096521 ) -InternalBenchmark.tests.cpp:: passed: normal_quantile(0.551780) == Approx(0.13015979861484198) for: 0.1301597986 == Approx( 0.1301597986 ) -InternalBenchmark.tests.cpp:: passed: normal_quantile(0.533700) == Approx(0.08457408802851875) for: 0.084574088 == Approx( 0.084574088 ) -InternalBenchmark.tests.cpp:: passed: normal_quantile(0.025000) == Approx(-1.95996398454005449) for: -1.9599639845 == Approx( -1.9599639845 ) +InternalBenchmark.tests.cpp:: passed: normal_quantile(0.551780) == Approx(0.13015979861484198) for: 0.13015979861484195 +== +Approx( 0.13015979861484198 ) +InternalBenchmark.tests.cpp:: passed: normal_quantile(0.533700) == Approx(0.08457408802851875) for: 0.08457408802851875 +== +Approx( 0.08457408802851875 ) +InternalBenchmark.tests.cpp:: passed: normal_quantile(0.025000) == Approx(-1.95996398454005449) for: -1.95996398454005405 +== +Approx( -1.95996398454005449 ) Misc.tests.cpp:: passed: Message.tests.cpp:: passed: true with 1 message: 'this MAY be seen only for the FIRST assertion IF info is printed for passing assertions' Message.tests.cpp:: passed: true with 1 message: 'this MAY be seen only for the SECOND assertion IF info is printed for passing assertions' @@ -2334,6 +2640,10 @@ StringManip.tests.cpp:: passed: Catch::replaceInPlace(letters, lett StringManip.tests.cpp:: passed: letters == "replaced" for: "replaced" == "replaced" StringManip.tests.cpp:: passed: !(Catch::replaceInPlace(letters, "x", "z")) for: !false StringManip.tests.cpp:: passed: letters == letters for: "abcdefcg" == "abcdefcg" +StringManip.tests.cpp:: passed: Catch::replaceInPlace(letters, "c", "cc") for: true +StringManip.tests.cpp:: passed: letters == "abccdefccg" for: "abccdefccg" == "abccdefccg" +StringManip.tests.cpp:: passed: Catch::replaceInPlace(s, "--", "-") for: true +StringManip.tests.cpp:: passed: s == "--" for: "--" == "--" StringManip.tests.cpp:: passed: Catch::replaceInPlace(s, "'", "|'") for: true StringManip.tests.cpp:: passed: s == "didn|'t" for: "didn|'t" == "didn|'t" Stream.tests.cpp:: passed: Catch::makeStream( "%somestream" ) @@ -2460,19 +2770,21 @@ EnumToString.tests.cpp:: passed: ::Catch::Detail::stringify(e0) == EnumToString.tests.cpp:: passed: ::Catch::Detail::stringify(e1) == "1" for: "1" == "1" ToStringTuple.tests.cpp:: passed: "{ }" == ::Catch::Detail::stringify(type{}) for: "{ }" == "{ }" ToStringTuple.tests.cpp:: passed: "{ }" == ::Catch::Detail::stringify(value) for: "{ }" == "{ }" -ToStringTuple.tests.cpp:: passed: "1.2f" == ::Catch::Detail::stringify(float(1.2)) for: "1.2f" == "1.2f" -ToStringTuple.tests.cpp:: passed: "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) for: "{ 1.2f, 0 }" == "{ 1.2f, 0 }" +ToStringTuple.tests.cpp:: passed: "1.5f" == ::Catch::Detail::stringify(float(1.5)) for: "1.5f" == "1.5f" +ToStringTuple.tests.cpp:: passed: "{ 1.5f, 0 }" == ::Catch::Detail::stringify(type{1.5f,0}) for: "{ 1.5f, 0 }" == "{ 1.5f, 0 }" ToStringTuple.tests.cpp:: passed: "{ 0 }" == ::Catch::Detail::stringify(type{0}) for: "{ 0 }" == "{ 0 }" ToStringTuple.tests.cpp:: passed: "{ \"hello\", \"world\" }" == ::Catch::Detail::stringify(type{"hello","world"}) for: "{ "hello", "world" }" == "{ "hello", "world" }" -ToStringTuple.tests.cpp:: passed: "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) for: "{ { 42 }, { }, 1.2f }" +ToStringTuple.tests.cpp:: passed: "{ { 42 }, { }, 1.5f }" == ::Catch::Detail::stringify(value) for: "{ { 42 }, { }, 1.5f }" == -"{ { 42 }, { }, 1.2f }" +"{ { 42 }, { }, 1.5f }" InternalBenchmark.tests.cpp:: passed: e.point == 23 for: 23.0 == 23 InternalBenchmark.tests.cpp:: passed: e.upper_bound == 23 for: 23.0 == 23 InternalBenchmark.tests.cpp:: passed: e.lower_bound == 23 for: 23.0 == 23 -InternalBenchmark.tests.cpp:: passed: e.confidence_interval == 0.95 for: 0.95 == 0.95 +InternalBenchmark.tests.cpp:: passed: e.confidence_interval == 0.95 for: 0.94999999999999996 == 0.94999999999999996 +RandomNumberGeneration.tests.cpp:: passed: dist.a() == -10 for: -10 == -10 +RandomNumberGeneration.tests.cpp:: passed: dist.b() == 10 for: 10 == 10 UniquePtr.tests.cpp:: passed: !(ptr) for: !{?} UniquePtr.tests.cpp:: passed: ptr.get() == 0 for: 0 == 0 UniquePtr.tests.cpp:: passed: ptr for: {?} @@ -2538,7 +2850,7 @@ InternalBenchmark.tests.cpp:: passed: med == 18. for: 18.0 == 18.0 InternalBenchmark.tests.cpp:: passed: q3 == 23. for: 23.0 == 23.0 Misc.tests.cpp:: passed: Misc.tests.cpp:: passed: -test cases: 409 | 309 passed | 84 failed | 5 skipped | 11 failed as expected -assertions: 2226 | 2049 passed | 145 failed | 32 failed as expected +test cases: 419 | 313 passed | 86 failed | 6 skipped | 14 failed as expected +assertions: 2265 | 2083 passed | 147 failed | 35 failed as expected diff --git a/src/external/catch2/tests/SelfTest/Baselines/compact.sw.multi.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/compact.sw.multi.approved.txt index 5b292da1..77812a62 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/compact.sw.multi.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/compact.sw.multi.approved.txt @@ -239,6 +239,10 @@ Class.tests.cpp:: passed: Nttp_Fixture::value > 0 for: 3 > 0 Class.tests.cpp:: passed: Nttp_Fixture::value > 0 for: 6 > 0 Class.tests.cpp:: failed: m_a == 2 for: 1 == 2 Class.tests.cpp:: passed: m_a == 1 for: 1 == 1 +Class.tests.cpp:: passed: m_a++ == 0 for: 0 == 0 +Class.tests.cpp:: failed: m_a == 0 for: 1 == 0 +Class.tests.cpp:: passed: m_a++ == 0 for: 0 == 0 +Class.tests.cpp:: passed: m_a == 1 for: 1 == 1 Misc.tests.cpp:: passed: x.size() == 0 for: 0 == 0 Misc.tests.cpp:: passed: x.size() == 0 for: 0 == 0 Misc.tests.cpp:: passed: x.size() == 0 for: 0 == 0 @@ -247,12 +251,22 @@ Misc.tests.cpp:: passed: x.size() > 0 for: 42 > 0 Misc.tests.cpp:: passed: x.size() > 0 for: 9 > 0 Misc.tests.cpp:: passed: x.size() > 0 for: 42 > 0 Misc.tests.cpp:: passed: x.size() > 0 for: 9 > 0 -Approx.tests.cpp:: passed: d == 1.23_a for: 1.23 == Approx( 1.23 ) -Approx.tests.cpp:: passed: d != 1.22_a for: 1.23 != Approx( 1.22 ) -Approx.tests.cpp:: passed: -d == -1.23_a for: -1.23 == Approx( -1.23 ) -Approx.tests.cpp:: passed: d == 1.2_a .epsilon(.1) for: 1.23 == Approx( 1.2 ) -Approx.tests.cpp:: passed: d != 1.2_a .epsilon(.001) for: 1.23 != Approx( 1.2 ) -Approx.tests.cpp:: passed: d == 1_a .epsilon(.3) for: 1.23 == Approx( 1.0 ) +Approx.tests.cpp:: passed: d == 1.23_a for: 1.22999999999999998 +== +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: d != 1.22_a for: 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) +Approx.tests.cpp:: passed: -d == -1.23_a for: -1.22999999999999998 +== +Approx( -1.22999999999999998 ) +Approx.tests.cpp:: passed: d == 1.2_a .epsilon(.1) for: 1.22999999999999998 +== +Approx( 1.19999999999999996 ) +Approx.tests.cpp:: passed: d != 1.2_a .epsilon(.001) for: 1.22999999999999998 +!= +Approx( 1.19999999999999996 ) +Approx.tests.cpp:: passed: d == 1_a .epsilon(.3) for: 1.22999999999999998 == Approx( 1.0 ) Misc.tests.cpp:: passed: with 1 message: 'that's not flying - that's failing in style' Misc.tests.cpp:: failed: explicitly with 1 message: 'to infinity and beyond' Tricky.tests.cpp:: failed: &o1 == &o2 for: 0x == 0x @@ -261,8 +275,8 @@ Approx.tests.cpp:: passed: 104.0 != Approx(100.0) for: 104.0 != App Approx.tests.cpp:: passed: 104.0 == Approx(100.0).margin(5) for: 104.0 == Approx( 100.0 ) Approx.tests.cpp:: passed: 104.0 == Approx(100.0).margin(4) for: 104.0 == Approx( 100.0 ) Approx.tests.cpp:: passed: 104.0 != Approx(100.0).margin(3) for: 104.0 != Approx( 100.0 ) -Approx.tests.cpp:: passed: 100.3 != Approx(100.0) for: 100.3 != Approx( 100.0 ) -Approx.tests.cpp:: passed: 100.3 == Approx(100.0).margin(0.5) for: 100.3 == Approx( 100.0 ) +Approx.tests.cpp:: passed: 100.3 != Approx(100.0) for: 100.29999999999999716 != Approx( 100.0 ) +Approx.tests.cpp:: passed: 100.3 == Approx(100.0).margin(0.5) for: 100.29999999999999716 == Approx( 100.0 ) Tricky.tests.cpp:: passed: i++ == 7 for: 7 == 7 Tricky.tests.cpp:: passed: i++ == 8 for: 8 == 8 Exception.tests.cpp:: passed: 1 == 1 @@ -280,19 +294,33 @@ Approx.tests.cpp:: passed: 0.0f == Approx(0.25f).margin(0.25f) for: Approx.tests.cpp:: passed: 0.5f == Approx(0.25f).margin(0.25f) for: 0.5f == Approx( 0.25 ) Approx.tests.cpp:: passed: 245.0f == Approx(245.25f).margin(0.25f) for: 245.0f == Approx( 245.25 ) Approx.tests.cpp:: passed: 245.5f == Approx(245.25f).margin(0.25f) for: 245.5f == Approx( 245.25 ) -Approx.tests.cpp:: passed: divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) for: 3.1428571429 == Approx( 3.141 ) -Approx.tests.cpp:: passed: divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) for: 3.1428571429 != Approx( 3.141 ) -Approx.tests.cpp:: passed: d != Approx( 1.231 ) for: 1.23 != Approx( 1.231 ) -Approx.tests.cpp:: passed: d == Approx( 1.231 ).epsilon( 0.1 ) for: 1.23 == Approx( 1.231 ) -Approx.tests.cpp:: passed: 1.23f == Approx( 1.23f ) for: 1.23f == Approx( 1.2300000191 ) +Approx.tests.cpp:: passed: divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) for: 3.14285714285714279 +== +Approx( 3.14100000000000001 ) +Approx.tests.cpp:: passed: divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) for: 3.14285714285714279 +!= +Approx( 3.14100000000000001 ) +Approx.tests.cpp:: passed: d != Approx( 1.231 ) for: 1.22999999999999998 +!= +Approx( 1.23100000000000009 ) +Approx.tests.cpp:: passed: d == Approx( 1.231 ).epsilon( 0.1 ) for: 1.22999999999999998 +== +Approx( 1.23100000000000009 ) +Approx.tests.cpp:: passed: 1.23f == Approx( 1.23f ) for: 1.230000019f +== +Approx( 1.23000001907348633 ) Approx.tests.cpp:: passed: 0.0f == Approx( 0.0f ) for: 0.0f == Approx( 0.0 ) Approx.tests.cpp:: passed: 1 == Approx( 1 ) for: 1 == Approx( 1.0 ) Approx.tests.cpp:: passed: 0 == Approx( 0 ) for: 0 == Approx( 0.0 ) Approx.tests.cpp:: passed: 1.0f == Approx( 1 ) for: 1.0f == Approx( 1.0 ) Approx.tests.cpp:: passed: 0 == Approx( dZero) for: 0 == Approx( 0.0 ) Approx.tests.cpp:: passed: 0 == Approx( dSmall ).margin( 0.001 ) for: 0 == Approx( 0.00001 ) -Approx.tests.cpp:: passed: 1.234f == Approx( dMedium ) for: 1.234f == Approx( 1.234 ) -Approx.tests.cpp:: passed: dMedium == Approx( 1.234f ) for: 1.234 == Approx( 1.2339999676 ) +Approx.tests.cpp:: passed: 1.234f == Approx( dMedium ) for: 1.233999968f +== +Approx( 1.23399999999999999 ) +Approx.tests.cpp:: passed: dMedium == Approx( 1.234f ) for: 1.23399999999999999 +== +Approx( 1.23399996757507324 ) Matchers.tests.cpp:: passed: 1, Predicate( alwaysTrue, "always true" ) for: 1 matches predicate: "always true" Matchers.tests.cpp:: passed: 1, !Predicate( alwaysFalse, "always false" ) for: 1 not matches predicate: "always false" Matchers.tests.cpp:: passed: "Hello olleH", Predicate( []( std::string const& str ) -> bool { return str.front() == str.back(); }, "First and last character should be equal" ) for: "Hello olleH" matches predicate: "First and last character should be equal" @@ -329,7 +357,7 @@ MatchersRanges.tests.cpp:: passed: inner_lists_are_empty.front(), I MatchersRanges.tests.cpp:: passed: has_empty{}, !IsEmpty() for: {?} not is empty MatchersRanges.tests.cpp:: passed: unrelated::ADL_empty{}, IsEmpty() for: {?} is empty Message.tests.cpp:: passed: with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true' -Message.tests.cpp:: passed: with 7 messages: 'std::vector{1, 2, 3}[0, 1, 2] := 3' and 'std::vector{1, 2, 3}[(0, 1)] := 2' and 'std::vector{1, 2, 3}[0] := 1' and '(helper_1436{12, -12}) := { 12, -12 }' and '(helper_1436(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' +Message.tests.cpp:: passed: with 7 messages: 'custom_index_op{1, 2, 3}[0, 1, 2] := 0' and 'custom_index_op{1, 2, 3}[(0, 1)] := 0' and 'custom_index_op{1, 2, 3}[0] := 0' and '(helper_1436{12, -12}) := { 12, -12 }' and '(helper_1436(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' Message.tests.cpp:: passed: with 11 messages: '("comma, in string", "escaped, \", ") := "escaped, ", "' and '"single quote in string,'," := "single quote in string,',"' and '"some escapes, \\,\\\\" := "some escapes, \,\\"' and '"some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<"' and ''"' := '"'' and ''\'' := '''' and '',' := ','' and ''}' := '}'' and '')' := ')'' and ''(' := '('' and ''{' := '{'' ToStringGeneral.tests.cpp:: passed: true with 1 message: 'i := 2' ToStringGeneral.tests.cpp:: passed: true with 1 message: '3' @@ -348,20 +376,22 @@ Details.tests.cpp:: passed: lt( "a", "b" ) for: true Details.tests.cpp:: passed: lt( "a", "B" ) for: true Details.tests.cpp:: passed: lt( "A", "b" ) for: true Details.tests.cpp:: passed: lt( "A", "B" ) for: true -ToStringGeneral.tests.cpp:: passed: tab == '\t' for: '\t' == '\t' -ToStringGeneral.tests.cpp:: passed: newline == '\n' for: '\n' == '\n' -ToStringGeneral.tests.cpp:: passed: carr_return == '\r' for: '\r' == '\r' -ToStringGeneral.tests.cpp:: passed: form_feed == '\f' for: '\f' == '\f' -ToStringGeneral.tests.cpp:: passed: space == ' ' for: ' ' == ' ' -ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'a' == 'a' -ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'z' == 'z' -ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'A' == 'A' -ToStringGeneral.tests.cpp:: passed: c == chars[i] for: 'Z' == 'Z' -ToStringGeneral.tests.cpp:: passed: null_terminator == '\0' for: 0 == 0 -ToStringGeneral.tests.cpp:: passed: c == i for: 2 == 2 -ToStringGeneral.tests.cpp:: passed: c == i for: 3 == 3 -ToStringGeneral.tests.cpp:: passed: c == i for: 4 == 4 -ToStringGeneral.tests.cpp:: passed: c == i for: 5 == 5 +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify('\t') == "'\\t'" for: "'\t'" == "'\t'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify('\n') == "'\\n'" for: "'\n'" == "'\n'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify('\r') == "'\\r'" for: "'\r'" == "'\r'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify('\f') == "'\\f'" for: "'\f'" == "'\f'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( ' ' ) == "' '" for: "' '" == "' '" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( 'A' ) == "'A'" for: "'A'" == "'A'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( 'z' ) == "'z'" for: "'z'" == "'z'" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( '\0' ) == "0" for: "0" == "0" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( static_cast(2) ) == "2" for: "2" == "2" +ToStringGeneral.tests.cpp:: passed: ::Catch::Detail::stringify( static_cast(5) ) == "5" for: "5" == "5" +Clara.tests.cpp:: passed: name.empty() for: true +Clara.tests.cpp:: passed: result for: {?} +Clara.tests.cpp:: passed: result.type() == Catch::Clara::Detail::ResultType::Ok for: 0 == 0 +Clara.tests.cpp:: passed: parsed.type() == Catch::Clara::ParseResultType::NoMatch for: 1 == 1 +Clara.tests.cpp:: passed: parsed.remainingTokens().count() == 2 for: 2 == 2 +Clara.tests.cpp:: passed: name.empty() for: true Clara.tests.cpp:: passed: name.empty() for: true Clara.tests.cpp:: passed: name == "foo" for: "foo" == "foo" Clara.tests.cpp:: passed: !(parse_result) for: !{?} @@ -511,15 +541,15 @@ Stream.tests.cpp:: passed: Catch::makeStream( "-" )->isConsole() fo Exception.tests.cpp:: failed: unexpected exception with message: 'custom exception - not std'; expression was: throwCustom() Exception.tests.cpp:: failed: unexpected exception with message: 'custom exception - not std'; expression was: throwCustom(), std::exception Exception.tests.cpp:: failed: unexpected exception with message: 'custom std exception' -Approx.tests.cpp:: passed: 101.000001 != Approx(100).epsilon(0.01) for: 101.000001 != Approx( 100.0 ) +Approx.tests.cpp:: passed: 101.000001 != Approx(100).epsilon(0.01) for: 101.00000099999999748 != Approx( 100.0 ) Approx.tests.cpp:: passed: std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 ) ToString.tests.cpp:: passed: enumInfo->lookup(0) == "Value1" for: Value1 == "Value1" ToString.tests.cpp:: passed: enumInfo->lookup(1) == "Value2" for: Value2 == "Value2" ToString.tests.cpp:: passed: enumInfo->lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **} == "{** unexpected enum value **}" +Skip.tests.cpp:: skipped: 'This generator is empty' Stream.tests.cpp:: passed: Catch::makeStream( "" )->isConsole() for: true -Tag.tests.cpp:: passed: Catch::TestCaseInfo( "", { "fake test name", "[]" }, dummySourceLineInfo ) Matchers.tests.cpp:: failed: testStringForMatching(), EndsWith( "Substring" ) for: "this string contains 'abc' as a substring" ends with: "Substring" Matchers.tests.cpp:: failed: testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: "this" (case insensitive) EnumToString.tests.cpp:: passed: stringify( EnumClass3::Value1 ) == "Value1" for: "Value1" == "Value1" @@ -531,27 +561,39 @@ EnumToString.tests.cpp:: passed: stringify( EnumClass3::Value4 ) == EnumToString.tests.cpp:: passed: stringify( ec3 ) == "Value2" for: "Value2" == "Value2" EnumToString.tests.cpp:: passed: stringify( Bikeshed::Colours::Red ) == "Red" for: "Red" == "Red" EnumToString.tests.cpp:: passed: stringify( Bikeshed::Colours::Blue ) == "Blue" for: "Blue" == "Blue" -Approx.tests.cpp:: passed: 101.01 != Approx(100).epsilon(0.01) for: 101.01 != Approx( 100.0 ) +Approx.tests.cpp:: passed: 101.01 != Approx(100).epsilon(0.01) for: 101.01000000000000512 != Approx( 100.0 ) Condition.tests.cpp:: failed: data.int_seven == 6 for: 7 == 6 Condition.tests.cpp:: failed: data.int_seven == 8 for: 7 == 8 Condition.tests.cpp:: failed: data.int_seven == 0 for: 7 == 0 -Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 9.11f ) for: 9.1f == Approx( 9.1099996567 ) -Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 9.0f ) for: 9.1f == Approx( 9.0 ) -Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 1 ) for: 9.1f == Approx( 1.0 ) -Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 0 ) for: 9.1f == Approx( 0.0 ) -Condition.tests.cpp:: failed: data.double_pi == Approx( 3.1415 ) for: 3.1415926535 == Approx( 3.1415 ) +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 9.11f ) for: 9.100000381f +== +Approx( 9.10999965667724609 ) +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 9.0f ) for: 9.100000381f == Approx( 9.0 ) +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 1 ) for: 9.100000381f == Approx( 1.0 ) +Condition.tests.cpp:: failed: data.float_nine_point_one == Approx( 0 ) for: 9.100000381f == Approx( 0.0 ) +Condition.tests.cpp:: failed: data.double_pi == Approx( 3.1415 ) for: 3.14159265350000005 +== +Approx( 3.14150000000000018 ) Condition.tests.cpp:: failed: data.str_hello == "goodbye" for: "hello" == "goodbye" Condition.tests.cpp:: failed: data.str_hello == "hell" for: "hello" == "hell" Condition.tests.cpp:: failed: data.str_hello == "hello1" for: "hello" == "hello1" Condition.tests.cpp:: failed: data.str_hello.size() == 6 for: 5 == 6 -Condition.tests.cpp:: failed: x == Approx( 1.301 ) for: 1.3 == Approx( 1.301 ) +Condition.tests.cpp:: failed: x == Approx( 1.301 ) for: 1.30000000000000027 +== +Approx( 1.30099999999999993 ) Condition.tests.cpp:: passed: data.int_seven == 7 for: 7 == 7 -Condition.tests.cpp:: passed: data.float_nine_point_one == Approx( 9.1f ) for: 9.1f == Approx( 9.1000003815 ) -Condition.tests.cpp:: passed: data.double_pi == Approx( 3.1415926535 ) for: 3.1415926535 == Approx( 3.1415926535 ) +Condition.tests.cpp:: passed: data.float_nine_point_one == Approx( 9.1f ) for: 9.100000381f +== +Approx( 9.10000038146972656 ) +Condition.tests.cpp:: passed: data.double_pi == Approx( 3.1415926535 ) for: 3.14159265350000005 +== +Approx( 3.14159265350000005 ) Condition.tests.cpp:: passed: data.str_hello == "hello" for: "hello" == "hello" Condition.tests.cpp:: passed: "hello" == data.str_hello for: "hello" == "hello" Condition.tests.cpp:: passed: data.str_hello.size() == 5 for: 5 == 5 -Condition.tests.cpp:: passed: x == Approx( 1.3 ) for: 1.3 == Approx( 1.3 ) +Condition.tests.cpp:: passed: x == Approx( 1.3 ) for: 1.30000000000000027 +== +Approx( 1.30000000000000004 ) Matchers.tests.cpp:: passed: testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" Matchers.tests.cpp:: passed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive) Matchers.tests.cpp:: failed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring" @@ -598,21 +640,21 @@ Misc.tests.cpp:: passed: Factorial(2) == 2 for: 2 == 2 Misc.tests.cpp:: passed: Factorial(3) == 6 for: 6 == 6 Misc.tests.cpp:: passed: Factorial(10) == 3628800 for: 3628800 (0x) == 3628800 (0x) GeneratorsImpl.tests.cpp:: passed: filter( []( int ) { return false; }, value( 3 ) ), Catch::GeneratorException -Matchers.tests.cpp:: passed: 10., WithinRel( 11.1, 0.1 ) for: 10.0 and 11.1 are within 10% of each other -Matchers.tests.cpp:: passed: 10., !WithinRel( 11.2, 0.1 ) for: 10.0 not and 11.2 are within 10% of each other -Matchers.tests.cpp:: passed: 1., !WithinRel( 0., 0.99 ) for: 1.0 not and 0 are within 99% of each other -Matchers.tests.cpp:: passed: -0., WithinRel( 0. ) for: -0.0 and 0 are within 2.22045e-12% of each other -Matchers.tests.cpp:: passed: v1, WithinRel( v2 ) for: 0.0 and 2.22507e-308 are within 2.22045e-12% of each other +Matchers.tests.cpp:: passed: 10., WithinRel( 11.1, 0.1 ) for: 10.0 and 11.09999999999999964 are within 10% of each other +Matchers.tests.cpp:: passed: 10., !WithinRel( 11.2, 0.1 ) for: 10.0 not and 11.19999999999999929 are within 10% of each other +Matchers.tests.cpp:: passed: 1., !WithinRel( 0., 0.99 ) for: 1.0 not and 0.0 are within 99% of each other +Matchers.tests.cpp:: passed: -0., WithinRel( 0. ) for: -0.0 and 0.0 are within 2.22045e-12% of each other +Matchers.tests.cpp:: passed: v1, WithinRel( v2 ) for: 0.0 and 0.0 are within 2.22045e-12% of each other Matchers.tests.cpp:: passed: 1., WithinAbs( 1., 0 ) for: 1.0 is within 0.0 of 1.0 Matchers.tests.cpp:: passed: 0., WithinAbs( 1., 1 ) for: 0.0 is within 1.0 of 1.0 -Matchers.tests.cpp:: passed: 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0 -Matchers.tests.cpp:: passed: 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0 +Matchers.tests.cpp:: passed: 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.98999999999999999 of 1.0 +Matchers.tests.cpp:: passed: 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.98999999999999999 of 1.0 Matchers.tests.cpp:: passed: 11., !WithinAbs( 10., 0.5 ) for: 11.0 not is within 0.5 of 10.0 Matchers.tests.cpp:: passed: 10., !WithinAbs( 11., 0.5 ) for: 10.0 not is within 0.5 of 11.0 Matchers.tests.cpp:: passed: -10., WithinAbs( -10., 0.5 ) for: -10.0 is within 0.5 of -10.0 -Matchers.tests.cpp:: passed: -10., WithinAbs( -9.6, 0.5 ) for: -10.0 is within 0.5 of -9.6 +Matchers.tests.cpp:: passed: -10., WithinAbs( -9.6, 0.5 ) for: -10.0 is within 0.5 of -9.59999999999999964 Matchers.tests.cpp:: passed: 1., WithinULP( 1., 0 ) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) -Matchers.tests.cpp:: passed: nextafter( 1., 2. ), WithinULP( 1., 1 ) for: 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) +Matchers.tests.cpp:: passed: nextafter( 1., 2. ), WithinULP( 1., 1 ) for: 1.00000000000000022 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) Matchers.tests.cpp:: passed: 0., WithinULP( nextafter( 0., 1. ), 1 ) for: 0.0 is within 1 ULPs of 4.9406564584124654e-324 ([0.0000000000000000e+00, 9.8813129168249309e-324]) Matchers.tests.cpp:: passed: 1., WithinULP( nextafter( 1., 0. ), 1 ) for: 1.0 is within 1 ULPs of 9.9999999999999989e-01 ([9.9999999999999978e-01, 1.0000000000000000e+00]) Matchers.tests.cpp:: passed: 1., !WithinULP( nextafter( 1., 2. ), 0 ) for: 1.0 not is within 0 ULPs of 1.0000000000000002e+00 ([1.0000000000000002e+00, 1.0000000000000002e+00]) @@ -620,7 +662,7 @@ Matchers.tests.cpp:: passed: 1., WithinULP( 1., 0 ) for: 1.0 is wit Matchers.tests.cpp:: passed: -0., WithinULP( 0., 0 ) for: -0.0 is within 0 ULPs of 0.0000000000000000e+00 ([0.0000000000000000e+00, 0.0000000000000000e+00]) Matchers.tests.cpp:: passed: 1., WithinAbs( 1., 0.5 ) || WithinULP( 2., 1 ) for: 1.0 ( is within 0.5 of 1.0 or is within 1 ULPs of 2.0000000000000000e+00 ([1.9999999999999998e+00, 2.0000000000000004e+00]) ) Matchers.tests.cpp:: passed: 1., WithinAbs( 2., 0.5 ) || WithinULP( 1., 0 ) for: 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) ) -Matchers.tests.cpp:: passed: 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) for: 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) +Matchers.tests.cpp:: passed: 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) for: 0.0001 ( is within 0.001 of 0.0 or and 0.0 are within 10% of each other ) Matchers.tests.cpp:: passed: WithinAbs( 1., 0. ) Matchers.tests.cpp:: passed: WithinAbs( 1., -1. ), std::domain_error Matchers.tests.cpp:: passed: WithinULP( 1., 0 ) @@ -628,23 +670,23 @@ Matchers.tests.cpp:: passed: WithinRel( 1., 0. ) Matchers.tests.cpp:: passed: WithinRel( 1., -0.2 ), std::domain_error Matchers.tests.cpp:: passed: WithinRel( 1., 1. ), std::domain_error Matchers.tests.cpp:: passed: 1., !IsNaN() for: 1.0 not is NaN -Matchers.tests.cpp:: passed: 10.f, WithinRel( 11.1f, 0.1f ) for: 10.0f and 11.1 are within 10% of each other -Matchers.tests.cpp:: passed: 10.f, !WithinRel( 11.2f, 0.1f ) for: 10.0f not and 11.2 are within 10% of each other -Matchers.tests.cpp:: passed: 1.f, !WithinRel( 0.f, 0.99f ) for: 1.0f not and 0 are within 99% of each other -Matchers.tests.cpp:: passed: -0.f, WithinRel( 0.f ) for: -0.0f and 0 are within 0.00119209% of each other -Matchers.tests.cpp:: passed: v1, WithinRel( v2 ) for: 0.0f and 1.17549e-38 are within 0.00119209% of each other +Matchers.tests.cpp:: passed: 10.f, WithinRel( 11.1f, 0.1f ) for: 10.0f and 11.10000038146972656 are within 10% of each other +Matchers.tests.cpp:: passed: 10.f, !WithinRel( 11.2f, 0.1f ) for: 10.0f not and 11.19999980926513672 are within 10% of each other +Matchers.tests.cpp:: passed: 1.f, !WithinRel( 0.f, 0.99f ) for: 1.0f not and 0.0 are within 99% of each other +Matchers.tests.cpp:: passed: -0.f, WithinRel( 0.f ) for: -0.0f and 0.0 are within 0.00119209% of each other +Matchers.tests.cpp:: passed: v1, WithinRel( v2 ) for: 0.0f and 0.0 are within 0.00119209% of each other Matchers.tests.cpp:: passed: 1.f, WithinAbs( 1.f, 0 ) for: 1.0f is within 0.0 of 1.0 Matchers.tests.cpp:: passed: 0.f, WithinAbs( 1.f, 1 ) for: 0.0f is within 1.0 of 1.0 -Matchers.tests.cpp:: passed: 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0 -Matchers.tests.cpp:: passed: 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0 +Matchers.tests.cpp:: passed: 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.99000000953674316 of 1.0 +Matchers.tests.cpp:: passed: 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.99000000953674316 of 1.0 Matchers.tests.cpp:: passed: 0.f, WithinAbs( -0.f, 0 ) for: 0.0f is within 0.0 of -0.0 Matchers.tests.cpp:: passed: 11.f, !WithinAbs( 10.f, 0.5f ) for: 11.0f not is within 0.5 of 10.0 Matchers.tests.cpp:: passed: 10.f, !WithinAbs( 11.f, 0.5f ) for: 10.0f not is within 0.5 of 11.0 Matchers.tests.cpp:: passed: -10.f, WithinAbs( -10.f, 0.5f ) for: -10.0f is within 0.5 of -10.0 -Matchers.tests.cpp:: passed: -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of -9.6000003815 +Matchers.tests.cpp:: passed: -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of -9.60000038146972656 Matchers.tests.cpp:: passed: 1.f, WithinULP( 1.f, 0 ) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) Matchers.tests.cpp:: passed: -1.f, WithinULP( -1.f, 0 ) for: -1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+00]) -Matchers.tests.cpp:: passed: nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) +Matchers.tests.cpp:: passed: nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.000000119f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) Matchers.tests.cpp:: passed: 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) for: 0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45]) Matchers.tests.cpp:: passed: 1.f, WithinULP( nextafter( 1.f, 0.f ), 1 ) for: 1.0f is within 1 ULPs of 9.99999940e-01f ([9.99999881e-01, 1.00000000e+00]) Matchers.tests.cpp:: passed: 1.f, !WithinULP( nextafter( 1.f, 2.f ), 0 ) for: 1.0f not is within 0 ULPs of 1.00000012e+00f ([1.00000012e+00, 1.00000012e+00]) @@ -652,7 +694,7 @@ Matchers.tests.cpp:: passed: 1.f, WithinULP( 1.f, 0 ) for: 1.0f is Matchers.tests.cpp:: passed: -0.f, WithinULP( 0.f, 0 ) for: -0.0f is within 0 ULPs of 0.00000000e+00f ([0.00000000e+00, 0.00000000e+00]) Matchers.tests.cpp:: passed: 1.f, WithinAbs( 1.f, 0.5 ) || WithinULP( 1.f, 1 ) for: 1.0f ( is within 0.5 of 1.0 or is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) ) Matchers.tests.cpp:: passed: 1.f, WithinAbs( 2.f, 0.5 ) || WithinULP( 1.f, 0 ) for: 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) ) -Matchers.tests.cpp:: passed: 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) for: 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) +Matchers.tests.cpp:: passed: 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) for: 0.0001f ( is within 0.00100000004749745 of 0.0 or and 0.0 are within 10% of each other ) Matchers.tests.cpp:: passed: WithinAbs( 1.f, 0.f ) Matchers.tests.cpp:: passed: WithinAbs( 1.f, -1.f ), std::domain_error Matchers.tests.cpp:: passed: WithinULP( 1.f, 0 ) @@ -664,6 +706,10 @@ Matchers.tests.cpp:: passed: 1., !IsNaN() for: 1.0 not is NaN Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 Generators.tests.cpp:: passed: filter([] (int) {return false; }, value(1)), Catch::GeneratorException Generators.tests.cpp:: passed: i < 4 for: 1 < 4 Generators.tests.cpp:: passed: i < 4 for: 2 < 4 @@ -828,68 +874,122 @@ GeneratorsImpl.tests.cpp:: passed: gen.get() == 5 for: 5 == 5 GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) with 1 message: 'Current expected value is -1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.9 == Approx( -0.9 ) with 1 message: 'Current expected value is -0.9' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.90000000000000002 +== +Approx( -0.90000000000000002 ) with 1 message: 'Current expected value is -0.9' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.9' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.8 == Approx( -0.8 ) with 1 message: 'Current expected value is -0.8' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.80000000000000004 +== +Approx( -0.80000000000000004 ) with 1 message: 'Current expected value is -0.8' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.8' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.70000000000000007 +== +Approx( -0.70000000000000007 ) with 1 message: 'Current expected value is -0.7' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.7' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.6 == Approx( -0.6 ) with 1 message: 'Current expected value is -0.6' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.60000000000000009 +== +Approx( -0.60000000000000009 ) with 1 message: 'Current expected value is -0.6' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.6' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.5 == Approx( -0.5 ) with 1 message: 'Current expected value is -0.5' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.50000000000000011 +== +Approx( -0.50000000000000011 ) with 1 message: 'Current expected value is -0.5' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.5' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.40000000000000013 +== +Approx( -0.40000000000000013 ) with 1 message: 'Current expected value is -0.4' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.4' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.3 == Approx( -0.3 ) with 1 message: 'Current expected value is -0.3' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.30000000000000016 +== +Approx( -0.30000000000000016 ) with 1 message: 'Current expected value is -0.3' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.3' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.2 == Approx( -0.2 ) with 1 message: 'Current expected value is -0.2' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.20000000000000015 +== +Approx( -0.20000000000000015 ) with 1 message: 'Current expected value is -0.2' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.2' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.10000000000000014 +== +Approx( -0.10000000000000014 ) with 1 message: 'Current expected value is -0.1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.0 == Approx( -0.0 ) with 1 message: 'Current expected value is -1.38778e-16' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.00000000000000014 +== +Approx( -0.00000000000000014 ) with 1 message: 'Current expected value is -1.38778e-16' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -1.38778e-16' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.1 == Approx( 0.1 ) with 1 message: 'Current expected value is 0.1' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.09999999999999987 +== +Approx( 0.09999999999999987 ) with 1 message: 'Current expected value is 0.1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.19999999999999987 +== +Approx( 0.19999999999999987 ) with 1 message: 'Current expected value is 0.2' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.2' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.3 == Approx( 0.3 ) with 1 message: 'Current expected value is 0.3' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.29999999999999988 +== +Approx( 0.29999999999999988 ) with 1 message: 'Current expected value is 0.3' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.3' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.4 == Approx( 0.4 ) with 1 message: 'Current expected value is 0.4' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.39999999999999991 +== +Approx( 0.39999999999999991 ) with 1 message: 'Current expected value is 0.4' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.4' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.5 == Approx( 0.5 ) with 1 message: 'Current expected value is 0.5' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.49999999999999989 +== +Approx( 0.49999999999999989 ) with 1 message: 'Current expected value is 0.5' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.5' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.6 == Approx( 0.6 ) with 1 message: 'Current expected value is 0.6' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.59999999999999987 +== +Approx( 0.59999999999999987 ) with 1 message: 'Current expected value is 0.6' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.6' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.7 == Approx( 0.7 ) with 1 message: 'Current expected value is 0.7' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.69999999999999984 +== +Approx( 0.69999999999999984 ) with 1 message: 'Current expected value is 0.7' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.7' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.8 == Approx( 0.8 ) with 1 message: 'Current expected value is 0.8' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.79999999999999982 +== +Approx( 0.79999999999999982 ) with 1 message: 'Current expected value is 0.8' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.8' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.9 == Approx( 0.9 ) with 1 message: 'Current expected value is 0.9' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.8999999999999998 +== +Approx( 0.8999999999999998 ) with 1 message: 'Current expected value is 0.9' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.9' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx( rangeEnd ) for: 1.0 == Approx( 1.0 ) +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx( rangeEnd ) for: 0.99999999999999978 == Approx( 1.0 ) GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) with 1 message: 'Current expected value is -1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.69999999999999996 +== +Approx( -0.69999999999999996 ) with 1 message: 'Current expected value is -0.7' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.7' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.39999999999999997 +== +Approx( -0.39999999999999997 ) with 1 message: 'Current expected value is -0.4' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.4' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.09999999999999998 +== +Approx( -0.09999999999999998 ) with 1 message: 'Current expected value is -0.1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.20000000000000001 +== +Approx( 0.20000000000000001 ) with 1 message: 'Current expected value is 0.2' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.2' GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.5 == Approx( 0.5 ) with 1 message: 'Current expected value is 0.5' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.5' GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) with 1 message: 'Current expected value is -1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.69999999999999996 +== +Approx( -0.69999999999999996 ) with 1 message: 'Current expected value is -0.7' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.7' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.39999999999999997 +== +Approx( -0.39999999999999997 ) with 1 message: 'Current expected value is -0.4' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.4' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: -0.09999999999999998 +== +Approx( -0.09999999999999998 ) with 1 message: 'Current expected value is -0.1' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is -0.1' -GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.20000000000000001 +== +Approx( 0.20000000000000001 ) with 1 message: 'Current expected value is 0.2' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.2' GeneratorsImpl.tests.cpp:: passed: gen.get() == Approx(expected) for: 0.5 == Approx( 0.5 ) with 1 message: 'Current expected value is 0.5' GeneratorsImpl.tests.cpp:: passed: gen.next() for: true with 1 message: 'Current expected value is 0.5' @@ -920,10 +1020,18 @@ GeneratorsImpl.tests.cpp:: passed: gen.get() == -4 for: -4 == -4 GeneratorsImpl.tests.cpp:: passed: gen.next() for: true GeneratorsImpl.tests.cpp:: passed: gen.get() == -7 for: -7 == -7 GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false -Approx.tests.cpp:: passed: d >= Approx( 1.22 ) for: 1.23 >= Approx( 1.22 ) -Approx.tests.cpp:: passed: d >= Approx( 1.23 ) for: 1.23 >= Approx( 1.23 ) -Approx.tests.cpp:: passed: !(d >= Approx( 1.24 )) for: !(1.23 >= Approx( 1.24 )) -Approx.tests.cpp:: passed: d >= Approx( 1.24 ).epsilon(0.1) for: 1.23 >= Approx( 1.24 ) +Approx.tests.cpp:: passed: d >= Approx( 1.22 ) for: 1.22999999999999998 +>= +Approx( 1.21999999999999997 ) +Approx.tests.cpp:: passed: d >= Approx( 1.23 ) for: 1.22999999999999998 +>= +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: !(d >= Approx( 1.24 )) for: !(1.22999999999999998 +>= +Approx( 1.23999999999999999 )) +Approx.tests.cpp:: passed: d >= Approx( 1.24 ).epsilon(0.1) for: 1.22999999999999998 +>= +Approx( 1.23999999999999999 ) TestCaseInfoHasher.tests.cpp:: passed: h1( dummy ) != h2( dummy ) for: 3422778688 (0x) != 130711275 (0x) @@ -942,6 +1050,7 @@ TestCaseInfoHasher.tests.cpp:: passed: h( dummy1 ) != h( dummy2 ) f TestCaseInfoHasher.tests.cpp:: passed: h( dummy ) == h( dummy ) for: 3422778688 (0x) == 3422778688 (0x) +Message.tests.cpp:: failed: explicitly with 3 messages: 'This info has multiple parts.' and 'This unscoped info has multiple parts.' and 'Show infos!' Message.tests.cpp:: warning: 'this is a message' with 1 message: 'this is a warning' Message.tests.cpp:: failed: a == 1 for: 2 == 1 with 2 messages: 'this message should be logged' and 'so should this' Message.tests.cpp:: passed: a == 2 for: 2 == 2 with 1 message: 'this message may be logged later' @@ -959,27 +1068,129 @@ Message.tests.cpp:: passed: i < 10 for: 7 < 10 with 2 messages: 'cu Message.tests.cpp:: passed: i < 10 for: 8 < 10 with 2 messages: 'current counter 8' and 'i := 8' Message.tests.cpp:: passed: i < 10 for: 9 < 10 with 2 messages: 'current counter 9' and 'i := 9' Message.tests.cpp:: failed: i < 10 for: 10 < 10 with 2 messages: 'current counter 10' and 'i := 10' +AssertionHandler.tests.cpp:: failed: unexpected exception with message: 'Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE'; expression was: Dummy Condition.tests.cpp:: failed: data.int_seven != 7 for: 7 != 7 -Condition.tests.cpp:: failed: data.float_nine_point_one != Approx( 9.1f ) for: 9.1f != Approx( 9.1000003815 ) -Condition.tests.cpp:: failed: data.double_pi != Approx( 3.1415926535 ) for: 3.1415926535 != Approx( 3.1415926535 ) +Condition.tests.cpp:: failed: data.float_nine_point_one != Approx( 9.1f ) for: 9.100000381f +!= +Approx( 9.10000038146972656 ) +Condition.tests.cpp:: failed: data.double_pi != Approx( 3.1415926535 ) for: 3.14159265350000005 +!= +Approx( 3.14159265350000005 ) Condition.tests.cpp:: failed: data.str_hello != "hello" for: "hello" != "hello" Condition.tests.cpp:: failed: data.str_hello.size() != 5 for: 5 != 5 Condition.tests.cpp:: passed: data.int_seven != 6 for: 7 != 6 Condition.tests.cpp:: passed: data.int_seven != 8 for: 7 != 8 -Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 9.11f ) for: 9.1f != Approx( 9.1099996567 ) -Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 9.0f ) for: 9.1f != Approx( 9.0 ) -Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 1 ) for: 9.1f != Approx( 1.0 ) -Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 0 ) for: 9.1f != Approx( 0.0 ) -Condition.tests.cpp:: passed: data.double_pi != Approx( 3.1415 ) for: 3.1415926535 != Approx( 3.1415 ) +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 9.11f ) for: 9.100000381f +!= +Approx( 9.10999965667724609 ) +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 9.0f ) for: 9.100000381f != Approx( 9.0 ) +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 1 ) for: 9.100000381f != Approx( 1.0 ) +Condition.tests.cpp:: passed: data.float_nine_point_one != Approx( 0 ) for: 9.100000381f != Approx( 0.0 ) +Condition.tests.cpp:: passed: data.double_pi != Approx( 3.1415 ) for: 3.14159265350000005 +!= +Approx( 3.14150000000000018 ) Condition.tests.cpp:: passed: data.str_hello != "goodbye" for: "hello" != "goodbye" Condition.tests.cpp:: passed: data.str_hello != "hell" for: "hello" != "hell" Condition.tests.cpp:: passed: data.str_hello != "hello1" for: "hello" != "hello1" Condition.tests.cpp:: passed: data.str_hello.size() != 6 for: 5 != 6 +Json.tests.cpp:: passed: stream.str() == "" for: "" == "" +Json.tests.cpp:: passed: stream.str() == "{\n}" for: "{ +}" +== +"{ +}" +Json.tests.cpp:: passed: stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) for: "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] +}" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ + 1, + 2 + ] +}" ) +Json.tests.cpp:: passed: stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) for: "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } +}" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) +Json.tests.cpp:: passed: stream.str() == "[\n]" for: "[ +]" +== +"[ +]" +Json.tests.cpp:: passed: stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" for: "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +== +"[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +Json.tests.cpp:: passed: stream.str() == "{\n}" for: "{ +}" +== +"{ +}" +Json.tests.cpp:: passed: stream.str() == "[\n]" for: "[ +]" +== +"[ +]" +Json.tests.cpp:: passed: stream.str() == "\"custom\"" for: ""custom"" == ""custom"" +Json.tests.cpp:: passed: sstream.str() == "\"\\\"\"" for: ""\""" == ""\""" +Json.tests.cpp:: passed: sstream.str() == "\"\\\\\"" for: ""\\"" == ""\\"" +Json.tests.cpp:: passed: sstream.str() == "\"/\"" for: ""/"" == ""/"" +Json.tests.cpp:: passed: sstream.str() == "\"\\b\"" for: ""\b"" == ""\b"" +Json.tests.cpp:: passed: sstream.str() == "\"\\f\"" for: ""\f"" == ""\f"" +Json.tests.cpp:: passed: sstream.str() == "\"\\n\"" for: ""\n"" == ""\n"" +Json.tests.cpp:: passed: sstream.str() == "\"\\r\"" for: ""\r"" == ""\r"" +Json.tests.cpp:: passed: sstream.str() == "\"\\t\"" for: ""\t"" == ""\t"" +Json.tests.cpp:: passed: sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n"" Compilation.tests.cpp:: passed: []() { return true; }() for: true -Approx.tests.cpp:: passed: d <= Approx( 1.24 ) for: 1.23 <= Approx( 1.24 ) -Approx.tests.cpp:: passed: d <= Approx( 1.23 ) for: 1.23 <= Approx( 1.23 ) -Approx.tests.cpp:: passed: !(d <= Approx( 1.22 )) for: !(1.23 <= Approx( 1.22 )) -Approx.tests.cpp:: passed: d <= Approx( 1.22 ).epsilon(0.1) for: 1.23 <= Approx( 1.22 ) +Approx.tests.cpp:: passed: d <= Approx( 1.24 ) for: 1.22999999999999998 +<= +Approx( 1.23999999999999999 ) +Approx.tests.cpp:: passed: d <= Approx( 1.23 ) for: 1.22999999999999998 +<= +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: !(d <= Approx( 1.22 )) for: !(1.22999999999999998 +<= +Approx( 1.21999999999999997 )) +Approx.tests.cpp:: passed: d <= Approx( 1.22 ).epsilon(0.1) for: 1.22999999999999998 +<= +Approx( 1.21999999999999997 ) Misc.tests.cpp:: passed: with 1 message: 'was called' Matchers.tests.cpp:: passed: testStringForMatching(), ContainsSubstring( "string" ) && ContainsSubstring( "abc" ) && ContainsSubstring( "substring" ) && ContainsSubstring( "contains" ) for: "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" and contains: "substring" and contains: "contains" ) Matchers.tests.cpp:: passed: testStringForMatching(), ContainsSubstring( "string" ) || ContainsSubstring( "different" ) || ContainsSubstring( "random" ) for: "this string contains 'abc' as a substring" ( contains: "string" or contains: "different" or contains: "random" ) @@ -1046,9 +1257,9 @@ Condition.tests.cpp:: failed: data.int_seven < 0 for: 7 < 0 Condition.tests.cpp:: failed: data.int_seven < -1 for: 7 < -1 Condition.tests.cpp:: failed: data.int_seven >= 8 for: 7 >= 8 Condition.tests.cpp:: failed: data.int_seven <= 6 for: 7 <= 6 -Condition.tests.cpp:: failed: data.float_nine_point_one < 9 for: 9.1f < 9 -Condition.tests.cpp:: failed: data.float_nine_point_one > 10 for: 9.1f > 10 -Condition.tests.cpp:: failed: data.float_nine_point_one > 9.2 for: 9.1f > 9.2 +Condition.tests.cpp:: failed: data.float_nine_point_one < 9 for: 9.100000381f < 9 +Condition.tests.cpp:: failed: data.float_nine_point_one > 10 for: 9.100000381f > 10 +Condition.tests.cpp:: failed: data.float_nine_point_one > 9.2 for: 9.100000381f > 9.19999999999999929 Condition.tests.cpp:: failed: data.str_hello > "hello" for: "hello" > "hello" Condition.tests.cpp:: failed: data.str_hello < "hello" for: "hello" < "hello" Condition.tests.cpp:: failed: data.str_hello > "hellp" for: "hello" > "hellp" @@ -1065,9 +1276,9 @@ Condition.tests.cpp:: passed: data.int_seven >= 7 for: 7 >= 7 Condition.tests.cpp:: passed: data.int_seven >= 6 for: 7 >= 6 Condition.tests.cpp:: passed: data.int_seven <= 7 for: 7 <= 7 Condition.tests.cpp:: passed: data.int_seven <= 8 for: 7 <= 8 -Condition.tests.cpp:: passed: data.float_nine_point_one > 9 for: 9.1f > 9 -Condition.tests.cpp:: passed: data.float_nine_point_one < 10 for: 9.1f < 10 -Condition.tests.cpp:: passed: data.float_nine_point_one < 9.2 for: 9.1f < 9.2 +Condition.tests.cpp:: passed: data.float_nine_point_one > 9 for: 9.100000381f > 9 +Condition.tests.cpp:: passed: data.float_nine_point_one < 10 for: 9.100000381f < 10 +Condition.tests.cpp:: passed: data.float_nine_point_one < 9.2 for: 9.100000381f < 9.19999999999999929 Condition.tests.cpp:: passed: data.str_hello <= "hello" for: "hello" <= "hello" Condition.tests.cpp:: passed: data.str_hello >= "hello" for: "hello" >= "hello" Condition.tests.cpp:: passed: data.str_hello < "hellp" for: "hello" < "hellp" @@ -1265,7 +1476,9 @@ CmdLine.tests.cpp:: passed: config.benchmarkSamples == 200 for: 200 CmdLine.tests.cpp:: passed: cli.parse({ "test", "--benchmark-resamples=20000" }) for: {?} CmdLine.tests.cpp:: passed: config.benchmarkResamples == 20000 for: 20000 (0x) == 20000 (0x) CmdLine.tests.cpp:: passed: cli.parse({ "test", "--benchmark-confidence-interval=0.99" }) for: {?} -CmdLine.tests.cpp:: passed: config.benchmarkConfidenceInterval == Catch::Approx(0.99) for: 0.99 == Approx( 0.99 ) +CmdLine.tests.cpp:: passed: config.benchmarkConfidenceInterval == Catch::Approx(0.99) for: 0.98999999999999999 +== +Approx( 0.98999999999999999 ) CmdLine.tests.cpp:: passed: cli.parse({ "test", "--benchmark-no-analysis" }) for: {?} CmdLine.tests.cpp:: passed: config.benchmarkNoAnalysis for: true CmdLine.tests.cpp:: passed: cli.parse({ "test", "--benchmark-warmup-time=10" }) for: {?} @@ -1339,6 +1552,60 @@ Reporters.tests.cpp:: passed: listingString, ContainsSubstring( "fa " ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: console' Reporters.tests.cpp:: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:: passed: listingString, ContainsSubstring("fakeTag"s) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:: passed: listingString, ContainsSubstring("fake reporter"s) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:: passed: !(factories.empty()) for: !false +Reporters.tests.cpp:: passed: listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON' +Reporters.tests.cpp:: passed: !(factories.empty()) for: !false Reporters.tests.cpp:: passed: listingString, ContainsSubstring("fakeTag"s) for: " All available tags: 1 [fakeTag] @@ -1463,14 +1730,30 @@ BDD.tests.cpp:: passed: v.capacity() >= 10 for: 10 >= 10 BDD.tests.cpp:: passed: v.size() == 0 for: 0 == 0 BDD.tests.cpp:: passed: v.capacity() >= 10 for: 10 >= 10 BDD.tests.cpp:: passed: v.size() == 0 for: 0 == 0 -Approx.tests.cpp:: passed: d == Approx( 1.23 ) for: 1.23 == Approx( 1.23 ) -Approx.tests.cpp:: passed: d != Approx( 1.22 ) for: 1.23 != Approx( 1.22 ) -Approx.tests.cpp:: passed: d != Approx( 1.24 ) for: 1.23 != Approx( 1.24 ) -Approx.tests.cpp:: passed: d == 1.23_a for: 1.23 == Approx( 1.23 ) -Approx.tests.cpp:: passed: d != 1.22_a for: 1.23 != Approx( 1.22 ) -Approx.tests.cpp:: passed: Approx( d ) == 1.23 for: Approx( 1.23 ) == 1.23 -Approx.tests.cpp:: passed: Approx( d ) != 1.22 for: Approx( 1.23 ) != 1.22 -Approx.tests.cpp:: passed: Approx( d ) != 1.24 for: Approx( 1.23 ) != 1.24 +Approx.tests.cpp:: passed: d == Approx( 1.23 ) for: 1.22999999999999998 +== +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: d != Approx( 1.22 ) for: 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) +Approx.tests.cpp:: passed: d != Approx( 1.24 ) for: 1.22999999999999998 +!= +Approx( 1.23999999999999999 ) +Approx.tests.cpp:: passed: d == 1.23_a for: 1.22999999999999998 +== +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: d != 1.22_a for: 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) +Approx.tests.cpp:: passed: Approx( d ) == 1.23 for: Approx( 1.22999999999999998 ) +== +1.22999999999999998 +Approx.tests.cpp:: passed: Approx( d ) != 1.22 for: Approx( 1.22999999999999998 ) +!= +1.21999999999999997 +Approx.tests.cpp:: passed: Approx( d ) != 1.24 for: Approx( 1.22999999999999998 ) +!= +1.23999999999999999 Matchers.tests.cpp:: failed: testStringForMatching(), StartsWith( "This String" ) for: "this string contains 'abc' as a substring" starts with: "This String" Matchers.tests.cpp:: failed: testStringForMatching(), StartsWith( "string", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "string" (case insensitive) ToStringGeneral.tests.cpp:: passed: Catch::Detail::stringify(singular) == "{ 1 }" for: "{ 1 }" == "{ 1 }" @@ -1585,13 +1868,13 @@ Tag.tests.cpp:: passed: testCase.tags, VectorContains( Tag( "tag wi Class.tests.cpp:: passed: Template_Fixture::m_a == 1 for: 1 == 1 Class.tests.cpp:: passed: Template_Fixture::m_a == 1 for: 1 == 1 Class.tests.cpp:: passed: Template_Fixture::m_a == 1 for: 1.0 == 1 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 1 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 4 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 1 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 4 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 4 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 1 > 0 -Misc.tests.cpp:: passed: sizeof(TestType) > 0 for: 4 > 0 +Misc.tests.cpp:: passed: std::is_default_constructible::value for: true +Misc.tests.cpp:: passed: std::is_default_constructible::value for: true +Misc.tests.cpp:: passed: std::is_trivially_copyable::value for: true +Misc.tests.cpp:: passed: std::is_trivially_copyable::value for: true +Misc.tests.cpp:: passed: std::is_arithmetic::value for: true +Misc.tests.cpp:: passed: std::is_arithmetic::value for: true +Misc.tests.cpp:: passed: std::is_arithmetic::value for: true Misc.tests.cpp:: passed: v.size() == 5 for: 5 == 5 Misc.tests.cpp:: passed: v.capacity() >= 5 for: 5 >= 5 Misc.tests.cpp:: passed: v.size() == 10 for: 10 == 10 @@ -1743,6 +2026,10 @@ Misc.tests.cpp:: passed: true Misc.tests.cpp:: failed: explicitly Misc.tests.cpp:: failed - but was ok: false Misc.tests.cpp:: failed: explicitly +Misc.tests.cpp:: passed: true +Misc.tests.cpp:: failed: unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} +Misc.tests.cpp:: failed - but was ok: false +Misc.tests.cpp:: failed: unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} Message.tests.cpp:: failed - but was ok: 1 == 2 Reporters.tests.cpp:: passed: listingString, ContainsSubstring("[fakeTag]"s) for: "All available tags: 1 [fakeTag] @@ -1873,7 +2160,7 @@ MatchersRanges.tests.cpp:: passed: a, !RangeEquals( b ) for: { 1, 2 MatchersRanges.tests.cpp:: passed: a, UnorderedRangeEquals( b ) for: { 1, 2, 3 } unordered elements are { 3, 2, 1 } MatchersRanges.tests.cpp:: passed: vector_a, RangeEquals( array_a_plus_1, close_enough ) for: { 1, 2, 3 } elements are { 2, 3, 4 } MatchersRanges.tests.cpp:: passed: vector_a, UnorderedRangeEquals( array_a_plus_1, close_enough ) for: { 1, 2, 3 } unordered elements are { 2, 3, 4 } -Exception.tests.cpp:: failed: unexpected exception with message: '3.14' +Exception.tests.cpp:: failed: unexpected exception with message: '3.14000000000000012' UniquePtr.tests.cpp:: passed: bptr->i == 3 for: 3 == 3 UniquePtr.tests.cpp:: passed: bptr->i == 3 for: 3 == 3 MatchersRanges.tests.cpp:: passed: data, AllMatch(SizeIs(5)) for: { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } all match has size == 5 @@ -2019,14 +2306,26 @@ MatchersRanges.tests.cpp:: passed: arr, !SizeIs(!Lt(3)) for: { 0, 0 MatchersRanges.tests.cpp:: passed: map, SizeIs(3) for: { {?}, {?}, {?} } has size == 3 MatchersRanges.tests.cpp:: passed: unrelated::ADL_size{}, SizeIs(12) for: {?} has size == 12 MatchersRanges.tests.cpp:: passed: has_size{}, SizeIs(13) for: {?} has size == 13 -Approx.tests.cpp:: passed: d == approx( 1.23 ) for: 1.23 == Approx( 1.23 ) -Approx.tests.cpp:: passed: d == approx( 1.22 ) for: 1.23 == Approx( 1.22 ) -Approx.tests.cpp:: passed: d == approx( 1.24 ) for: 1.23 == Approx( 1.24 ) -Approx.tests.cpp:: passed: d != approx( 1.25 ) for: 1.23 != Approx( 1.25 ) -Approx.tests.cpp:: passed: approx( d ) == 1.23 for: Approx( 1.23 ) == 1.23 -Approx.tests.cpp:: passed: approx( d ) == 1.22 for: Approx( 1.23 ) == 1.22 -Approx.tests.cpp:: passed: approx( d ) == 1.24 for: Approx( 1.23 ) == 1.24 -Approx.tests.cpp:: passed: approx( d ) != 1.25 for: Approx( 1.23 ) != 1.25 +Approx.tests.cpp:: passed: d == approx( 1.23 ) for: 1.22999999999999998 +== +Approx( 1.22999999999999998 ) +Approx.tests.cpp:: passed: d == approx( 1.22 ) for: 1.22999999999999998 +== +Approx( 1.21999999999999997 ) +Approx.tests.cpp:: passed: d == approx( 1.24 ) for: 1.22999999999999998 +== +Approx( 1.23999999999999999 ) +Approx.tests.cpp:: passed: d != approx( 1.25 ) for: 1.22999999999999998 != Approx( 1.25 ) +Approx.tests.cpp:: passed: approx( d ) == 1.23 for: Approx( 1.22999999999999998 ) +== +1.22999999999999998 +Approx.tests.cpp:: passed: approx( d ) == 1.22 for: Approx( 1.22999999999999998 ) +== +1.21999999999999997 +Approx.tests.cpp:: passed: approx( d ) == 1.24 for: Approx( 1.22999999999999998 ) +== +1.23999999999999999 +Approx.tests.cpp:: passed: approx( d ) != 1.25 for: Approx( 1.22999999999999998 ) != 1.25 VariadicMacros.tests.cpp:: passed: with 1 message: 'no assertions' Matchers.tests.cpp:: passed: empty, Approx( empty ) for: { } is approx: { } Matchers.tests.cpp:: passed: v1, Approx( v1 ) for: { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 } @@ -2201,9 +2500,15 @@ Skip.tests.cpp:: skipped: 'skipping because answer = 41' Skip.tests.cpp:: passed: Skip.tests.cpp:: skipped: 'skipping because answer = 43' Tag.tests.cpp:: passed: Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo) -InternalBenchmark.tests.cpp:: passed: erfc_inv(1.103560) == Approx(-0.09203687623843015) for: -0.0920368762 == Approx( -0.0920368762 ) -InternalBenchmark.tests.cpp:: passed: erfc_inv(1.067400) == Approx(-0.05980291115763361) for: -0.0598029112 == Approx( -0.0598029112 ) -InternalBenchmark.tests.cpp:: passed: erfc_inv(0.050000) == Approx(1.38590382434967796) for: 1.3859038243 == Approx( 1.3859038243 ) +InternalBenchmark.tests.cpp:: passed: erfc_inv(1.103560) == Approx(-0.09203687623843015) for: -0.09203687623843014 +== +Approx( -0.09203687623843015 ) +InternalBenchmark.tests.cpp:: passed: erfc_inv(1.067400) == Approx(-0.05980291115763361) for: -0.05980291115763361 +== +Approx( -0.05980291115763361 ) +InternalBenchmark.tests.cpp:: passed: erfc_inv(0.050000) == Approx(1.38590382434967796) for: 1.38590382434967774 +== +Approx( 1.38590382434967796 ) InternalBenchmark.tests.cpp:: passed: res.mean.count() == rate for: 2000.0 == 2000 (0x) InternalBenchmark.tests.cpp:: passed: res.outliers.total() == 0 for: 0 == 0 Misc.tests.cpp:: passed: @@ -2276,14 +2581,15 @@ Misc.tests.cpp:: passed: a != b for: 1 != 2 Skip.tests.cpp:: skipped: Tricky.tests.cpp:: passed: s == "7" for: "7" == "7" Tricky.tests.cpp:: passed: ti == typeid(int) for: {?} == {?} -InternalBenchmark.tests.cpp:: passed: normal_cdf(0.000000) == Approx(0.50000000000000000) for: 0.5 == Approx( 0.5 ) -InternalBenchmark.tests.cpp:: passed: normal_cdf(1.000000) == Approx(0.84134474606854293) for: 0.8413447461 == Approx( 0.8413447461 ) -InternalBenchmark.tests.cpp:: passed: normal_cdf(-1.000000) == Approx(0.15865525393145705) for: 0.1586552539 == Approx( 0.1586552539 ) -InternalBenchmark.tests.cpp:: passed: normal_cdf(2.809729) == Approx(0.99752083845315409) for: 0.9975208385 == Approx( 0.9975208385 ) -InternalBenchmark.tests.cpp:: passed: normal_cdf(-1.352570) == Approx(0.08809652095066035) for: 0.088096521 == Approx( 0.088096521 ) -InternalBenchmark.tests.cpp:: passed: normal_quantile(0.551780) == Approx(0.13015979861484198) for: 0.1301597986 == Approx( 0.1301597986 ) -InternalBenchmark.tests.cpp:: passed: normal_quantile(0.533700) == Approx(0.08457408802851875) for: 0.084574088 == Approx( 0.084574088 ) -InternalBenchmark.tests.cpp:: passed: normal_quantile(0.025000) == Approx(-1.95996398454005449) for: -1.9599639845 == Approx( -1.9599639845 ) +InternalBenchmark.tests.cpp:: passed: normal_quantile(0.551780) == Approx(0.13015979861484198) for: 0.13015979861484195 +== +Approx( 0.13015979861484198 ) +InternalBenchmark.tests.cpp:: passed: normal_quantile(0.533700) == Approx(0.08457408802851875) for: 0.08457408802851875 +== +Approx( 0.08457408802851875 ) +InternalBenchmark.tests.cpp:: passed: normal_quantile(0.025000) == Approx(-1.95996398454005449) for: -1.95996398454005405 +== +Approx( -1.95996398454005449 ) Misc.tests.cpp:: passed: Message.tests.cpp:: passed: true with 1 message: 'this MAY be seen only for the FIRST assertion IF info is printed for passing assertions' Message.tests.cpp:: passed: true with 1 message: 'this MAY be seen only for the SECOND assertion IF info is printed for passing assertions' @@ -2323,6 +2629,10 @@ StringManip.tests.cpp:: passed: Catch::replaceInPlace(letters, lett StringManip.tests.cpp:: passed: letters == "replaced" for: "replaced" == "replaced" StringManip.tests.cpp:: passed: !(Catch::replaceInPlace(letters, "x", "z")) for: !false StringManip.tests.cpp:: passed: letters == letters for: "abcdefcg" == "abcdefcg" +StringManip.tests.cpp:: passed: Catch::replaceInPlace(letters, "c", "cc") for: true +StringManip.tests.cpp:: passed: letters == "abccdefccg" for: "abccdefccg" == "abccdefccg" +StringManip.tests.cpp:: passed: Catch::replaceInPlace(s, "--", "-") for: true +StringManip.tests.cpp:: passed: s == "--" for: "--" == "--" StringManip.tests.cpp:: passed: Catch::replaceInPlace(s, "'", "|'") for: true StringManip.tests.cpp:: passed: s == "didn|'t" for: "didn|'t" == "didn|'t" Stream.tests.cpp:: passed: Catch::makeStream( "%somestream" ) @@ -2449,19 +2759,21 @@ EnumToString.tests.cpp:: passed: ::Catch::Detail::stringify(e0) == EnumToString.tests.cpp:: passed: ::Catch::Detail::stringify(e1) == "1" for: "1" == "1" ToStringTuple.tests.cpp:: passed: "{ }" == ::Catch::Detail::stringify(type{}) for: "{ }" == "{ }" ToStringTuple.tests.cpp:: passed: "{ }" == ::Catch::Detail::stringify(value) for: "{ }" == "{ }" -ToStringTuple.tests.cpp:: passed: "1.2f" == ::Catch::Detail::stringify(float(1.2)) for: "1.2f" == "1.2f" -ToStringTuple.tests.cpp:: passed: "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) for: "{ 1.2f, 0 }" == "{ 1.2f, 0 }" +ToStringTuple.tests.cpp:: passed: "1.5f" == ::Catch::Detail::stringify(float(1.5)) for: "1.5f" == "1.5f" +ToStringTuple.tests.cpp:: passed: "{ 1.5f, 0 }" == ::Catch::Detail::stringify(type{1.5f,0}) for: "{ 1.5f, 0 }" == "{ 1.5f, 0 }" ToStringTuple.tests.cpp:: passed: "{ 0 }" == ::Catch::Detail::stringify(type{0}) for: "{ 0 }" == "{ 0 }" ToStringTuple.tests.cpp:: passed: "{ \"hello\", \"world\" }" == ::Catch::Detail::stringify(type{"hello","world"}) for: "{ "hello", "world" }" == "{ "hello", "world" }" -ToStringTuple.tests.cpp:: passed: "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) for: "{ { 42 }, { }, 1.2f }" +ToStringTuple.tests.cpp:: passed: "{ { 42 }, { }, 1.5f }" == ::Catch::Detail::stringify(value) for: "{ { 42 }, { }, 1.5f }" == -"{ { 42 }, { }, 1.2f }" +"{ { 42 }, { }, 1.5f }" InternalBenchmark.tests.cpp:: passed: e.point == 23 for: 23.0 == 23 InternalBenchmark.tests.cpp:: passed: e.upper_bound == 23 for: 23.0 == 23 InternalBenchmark.tests.cpp:: passed: e.lower_bound == 23 for: 23.0 == 23 -InternalBenchmark.tests.cpp:: passed: e.confidence_interval == 0.95 for: 0.95 == 0.95 +InternalBenchmark.tests.cpp:: passed: e.confidence_interval == 0.95 for: 0.94999999999999996 == 0.94999999999999996 +RandomNumberGeneration.tests.cpp:: passed: dist.a() == -10 for: -10 == -10 +RandomNumberGeneration.tests.cpp:: passed: dist.b() == 10 for: 10 == 10 UniquePtr.tests.cpp:: passed: !(ptr) for: !{?} UniquePtr.tests.cpp:: passed: ptr.get() == 0 for: 0 == 0 UniquePtr.tests.cpp:: passed: ptr for: {?} @@ -2527,7 +2839,7 @@ InternalBenchmark.tests.cpp:: passed: med == 18. for: 18.0 == 18.0 InternalBenchmark.tests.cpp:: passed: q3 == 23. for: 23.0 == 23.0 Misc.tests.cpp:: passed: Misc.tests.cpp:: passed: -test cases: 409 | 309 passed | 84 failed | 5 skipped | 11 failed as expected -assertions: 2226 | 2049 passed | 145 failed | 32 failed as expected +test cases: 419 | 313 passed | 86 failed | 6 skipped | 14 failed as expected +assertions: 2265 | 2083 passed | 147 failed | 35 failed as expected diff --git a/src/external/catch2/tests/SelfTest/Baselines/console.std.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/console.std.approved.txt index 15d8b024..1003e549 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/console.std.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/console.std.approved.txt @@ -297,6 +297,18 @@ Class.tests.cpp:: FAILED: with expansion: 1 == 2 +------------------------------------------------------------------------------- +A TEST_CASE_PERSISTENT_FIXTURE based test run that fails + Second partial run +------------------------------------------------------------------------------- +Class.tests.cpp: +............................................................................... + +Class.tests.cpp:: FAILED: + REQUIRE( m_a == 0 ) +with expansion: + 1 == 0 + ------------------------------------------------------------------------------- A couple of nested sections followed by a failure ------------------------------------------------------------------------------- @@ -383,6 +395,16 @@ Exception.tests.cpp:: FAILED: due to unexpected exception with message: custom std exception +------------------------------------------------------------------------------- +Empty generators can SKIP in constructor +------------------------------------------------------------------------------- +Skip.tests.cpp: +............................................................................... + +Skip.tests.cpp:: SKIPPED: +explicitly with message: + This generator is empty + ------------------------------------------------------------------------------- EndsWith string matcher ------------------------------------------------------------------------------- @@ -424,27 +446,31 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 9.11f ) ) with expansion: - 9.1f == Approx( 9.1099996567 ) + 9.100000381f + == + Approx( 9.10999965667724609 ) Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 9.0f ) ) with expansion: - 9.1f == Approx( 9.0 ) + 9.100000381f == Approx( 9.0 ) Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 1 ) ) with expansion: - 9.1f == Approx( 1.0 ) + 9.100000381f == Approx( 1.0 ) Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 0 ) ) with expansion: - 9.1f == Approx( 0.0 ) + 9.100000381f == Approx( 0.0 ) Condition.tests.cpp:: FAILED: CHECK( data.double_pi == Approx( 3.1415 ) ) with expansion: - 3.1415926535 == Approx( 3.1415 ) + 3.14159265350000005 + == + Approx( 3.14150000000000018 ) Condition.tests.cpp:: FAILED: CHECK( data.str_hello == "goodbye" ) @@ -469,7 +495,9 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( x == Approx( 1.301 ) ) with expansion: - 1.3 == Approx( 1.301 ) + 1.30000000000000027 + == + Approx( 1.30099999999999993 ) ------------------------------------------------------------------------------- Equals string matcher @@ -589,6 +617,18 @@ explicitly with message: Message.tests.cpp:: warning: This message appears in the output +------------------------------------------------------------------------------- +INFO and UNSCOPED_INFO can stream multiple arguments +------------------------------------------------------------------------------- +Message.tests.cpp: +............................................................................... + +Message.tests.cpp:: FAILED: +explicitly with messages: + This info has multiple parts. + This unscoped info has multiple parts. + Show infos! + ------------------------------------------------------------------------------- INFO and WARN do not abort tests ------------------------------------------------------------------------------- @@ -649,6 +689,17 @@ with messages: current counter 10 i := 10 +------------------------------------------------------------------------------- +Incomplete AssertionHandler +------------------------------------------------------------------------------- +AssertionHandler.tests.cpp: +............................................................................... + +AssertionHandler.tests.cpp:: FAILED: + REQUIRE( Dummy ) +due to unexpected exception with message: + Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE + ------------------------------------------------------------------------------- Inequality checks that should fail ------------------------------------------------------------------------------- @@ -663,12 +714,16 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one != Approx( 9.1f ) ) with expansion: - 9.1f != Approx( 9.1000003815 ) + 9.100000381f + != + Approx( 9.10000038146972656 ) Condition.tests.cpp:: FAILED: CHECK( data.double_pi != Approx( 3.1415926535 ) ) with expansion: - 3.1415926535 != Approx( 3.1415926535 ) + 3.14159265350000005 + != + Approx( 3.14159265350000005 ) Condition.tests.cpp:: FAILED: CHECK( data.str_hello != "hello" ) @@ -822,17 +877,17 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one < 9 ) with expansion: - 9.1f < 9 + 9.100000381f < 9 Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one > 10 ) with expansion: - 9.1f > 10 + 9.100000381f > 10 Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one > 9.2 ) with expansion: - 9.1f > 9.2 + 9.100000381f > 9.19999999999999929 Condition.tests.cpp:: FAILED: CHECK( data.str_hello > "hello" ) @@ -987,6 +1042,28 @@ Misc.tests.cpp: Misc.tests.cpp:: FAILED: +------------------------------------------------------------------------------- +Testing checked-if 4 +------------------------------------------------------------------------------- +Misc.tests.cpp: +............................................................................... + +Misc.tests.cpp:: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + +------------------------------------------------------------------------------- +Testing checked-if 5 +------------------------------------------------------------------------------- +Misc.tests.cpp: +............................................................................... + +Misc.tests.cpp:: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + ------------------------------------------------------------------------------- Thrown string literals are translated ------------------------------------------------------------------------------- @@ -1005,7 +1082,7 @@ Exception.tests.cpp: Exception.tests.cpp:: FAILED: due to unexpected exception with message: - 3.14 + 3.14000000000000012 ------------------------------------------------------------------------------- Vector Approx matcher -- failing @@ -1533,6 +1610,6 @@ due to unexpected exception with message: Why would you throw a std::string? =============================================================================== -test cases: 409 | 323 passed | 69 failed | 6 skipped | 11 failed as expected -assertions: 2209 | 2049 passed | 128 failed | 32 failed as expected +test cases: 419 | 327 passed | 71 failed | 7 skipped | 14 failed as expected +assertions: 2248 | 2083 passed | 130 failed | 35 failed as expected diff --git a/src/external/catch2/tests/SelfTest/Baselines/console.sw.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/console.sw.approved.txt index 26d5e8f3..3519b771 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/console.sw.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/console.sw.approved.txt @@ -2023,6 +2023,54 @@ A TEST_CASE_METHOD based test run that succeeds Class.tests.cpp: ............................................................................... +Class.tests.cpp:: PASSED: + REQUIRE( m_a == 1 ) +with expansion: + 1 == 1 + +------------------------------------------------------------------------------- +A TEST_CASE_PERSISTENT_FIXTURE based test run that fails + First partial run +------------------------------------------------------------------------------- +Class.tests.cpp: +............................................................................... + +Class.tests.cpp:: PASSED: + REQUIRE( m_a++ == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +A TEST_CASE_PERSISTENT_FIXTURE based test run that fails + Second partial run +------------------------------------------------------------------------------- +Class.tests.cpp: +............................................................................... + +Class.tests.cpp:: FAILED: + REQUIRE( m_a == 0 ) +with expansion: + 1 == 0 + +------------------------------------------------------------------------------- +A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds + First partial run +------------------------------------------------------------------------------- +Class.tests.cpp: +............................................................................... + +Class.tests.cpp:: PASSED: + REQUIRE( m_a++ == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds + Second partial run +------------------------------------------------------------------------------- +Class.tests.cpp: +............................................................................... + Class.tests.cpp:: PASSED: REQUIRE( m_a == 1 ) with expansion: @@ -2125,32 +2173,42 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d == 1.23_a ) with expansion: - 1.23 == Approx( 1.23 ) + 1.22999999999999998 + == + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE( d != 1.22_a ) with expansion: - 1.23 != Approx( 1.22 ) + 1.22999999999999998 + != + Approx( 1.21999999999999997 ) Approx.tests.cpp:: PASSED: REQUIRE( -d == -1.23_a ) with expansion: - -1.23 == Approx( -1.23 ) + -1.22999999999999998 + == + Approx( -1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE( d == 1.2_a .epsilon(.1) ) with expansion: - 1.23 == Approx( 1.2 ) + 1.22999999999999998 + == + Approx( 1.19999999999999996 ) Approx.tests.cpp:: PASSED: REQUIRE( d != 1.2_a .epsilon(.001) ) with expansion: - 1.23 != Approx( 1.2 ) + 1.22999999999999998 + != + Approx( 1.19999999999999996 ) Approx.tests.cpp:: PASSED: REQUIRE( d == 1_a .epsilon(.3) ) with expansion: - 1.23 == Approx( 1.0 ) + 1.22999999999999998 == Approx( 1.0 ) ------------------------------------------------------------------------------- A couple of nested sections followed by a failure @@ -2219,12 +2277,12 @@ with expansion: Approx.tests.cpp:: PASSED: REQUIRE( 100.3 != Approx(100.0) ) with expansion: - 100.3 != Approx( 100.0 ) + 100.29999999999999716 != Approx( 100.0 ) Approx.tests.cpp:: PASSED: REQUIRE( 100.3 == Approx(100.0).margin(0.5) ) with expansion: - 100.3 == Approx( 100.0 ) + 100.29999999999999716 == Approx( 100.0 ) ------------------------------------------------------------------------------- An empty test with no assertions @@ -2342,12 +2400,16 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) ) with expansion: - 3.1428571429 == Approx( 3.141 ) + 3.14285714285714279 + == + Approx( 3.14100000000000001 ) Approx.tests.cpp:: PASSED: REQUIRE( divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) ) with expansion: - 3.1428571429 != Approx( 3.141 ) + 3.14285714285714279 + != + Approx( 3.14100000000000001 ) ------------------------------------------------------------------------------- Approximate comparisons with different epsilons @@ -2358,12 +2420,16 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d != Approx( 1.231 ) ) with expansion: - 1.23 != Approx( 1.231 ) + 1.22999999999999998 + != + Approx( 1.23100000000000009 ) Approx.tests.cpp:: PASSED: REQUIRE( d == Approx( 1.231 ).epsilon( 0.1 ) ) with expansion: - 1.23 == Approx( 1.231 ) + 1.22999999999999998 + == + Approx( 1.23100000000000009 ) ------------------------------------------------------------------------------- Approximate comparisons with floats @@ -2374,7 +2440,9 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( 1.23f == Approx( 1.23f ) ) with expansion: - 1.23f == Approx( 1.2300000191 ) + 1.230000019f + == + Approx( 1.23000001907348633 ) Approx.tests.cpp:: PASSED: REQUIRE( 0.0f == Approx( 0.0f ) ) @@ -2421,12 +2489,16 @@ with expansion: Approx.tests.cpp:: PASSED: REQUIRE( 1.234f == Approx( dMedium ) ) with expansion: - 1.234f == Approx( 1.234 ) + 1.233999968f + == + Approx( 1.23399999999999999 ) Approx.tests.cpp:: PASSED: REQUIRE( dMedium == Approx( 1.234f ) ) with expansion: - 1.234 == Approx( 1.2339999676 ) + 1.23399999999999999 + == + Approx( 1.23399996757507324 ) ------------------------------------------------------------------------------- Arbitrary predicate matcher @@ -2740,9 +2812,9 @@ Message.tests.cpp: Message.tests.cpp:: PASSED: with messages: - std::vector{1, 2, 3}[0, 1, 2] := 3 - std::vector{1, 2, 3}[(0, 1)] := 2 - std::vector{1, 2, 3}[0] := 1 + custom_index_op{1, 2, 3}[0, 1, 2] := 0 + custom_index_op{1, 2, 3}[(0, 1)] := 0 + custom_index_op{1, 2, 3}[0] := 0 (helper_1436{12, -12}) := { 12, -12 } (helper_1436(-12, 12)) := { -12, 12 } (1, 2) := 2 @@ -2904,24 +2976,24 @@ ToStringGeneral.tests.cpp: ............................................................................... ToStringGeneral.tests.cpp:: PASSED: - CHECK( tab == '\t' ) + CHECK( ::Catch::Detail::stringify('\t') == "'\\t'" ) with expansion: - '\t' == '\t' + "'\t'" == "'\t'" ToStringGeneral.tests.cpp:: PASSED: - CHECK( newline == '\n' ) + CHECK( ::Catch::Detail::stringify('\n') == "'\\n'" ) with expansion: - '\n' == '\n' + "'\n'" == "'\n'" ToStringGeneral.tests.cpp:: PASSED: - CHECK( carr_return == '\r' ) + CHECK( ::Catch::Detail::stringify('\r') == "'\\r'" ) with expansion: - '\r' == '\r' + "'\r'" == "'\r'" ToStringGeneral.tests.cpp:: PASSED: - CHECK( form_feed == '\f' ) + CHECK( ::Catch::Detail::stringify('\f') == "'\\f'" ) with expansion: - '\f' == '\f' + "'\f'" == "'\f'" ------------------------------------------------------------------------------- Character pretty printing @@ -2931,29 +3003,19 @@ ToStringGeneral.tests.cpp: ............................................................................... ToStringGeneral.tests.cpp:: PASSED: - CHECK( space == ' ' ) -with expansion: - ' ' == ' ' - -ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == chars[i] ) -with expansion: - 'a' == 'a' - -ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == chars[i] ) + CHECK( ::Catch::Detail::stringify( ' ' ) == "' '" ) with expansion: - 'z' == 'z' + "' '" == "' '" ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == chars[i] ) + CHECK( ::Catch::Detail::stringify( 'A' ) == "'A'" ) with expansion: - 'A' == 'A' + "'A'" == "'A'" ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == chars[i] ) + CHECK( ::Catch::Detail::stringify( 'z' ) == "'z'" ) with expansion: - 'Z' == 'Z' + "'z'" == "'z'" ------------------------------------------------------------------------------- Character pretty printing @@ -2963,29 +3025,55 @@ ToStringGeneral.tests.cpp: ............................................................................... ToStringGeneral.tests.cpp:: PASSED: - CHECK( null_terminator == '\0' ) + CHECK( ::Catch::Detail::stringify( '\0' ) == "0" ) with expansion: - 0 == 0 + "0" == "0" ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == i ) + CHECK( ::Catch::Detail::stringify( static_cast(2) ) == "2" ) with expansion: - 2 == 2 + "2" == "2" ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == i ) + CHECK( ::Catch::Detail::stringify( static_cast(5) ) == "5" ) with expansion: - 3 == 3 + "5" == "5" -ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == i ) +------------------------------------------------------------------------------- +Clara::Arg does not crash on incomplete input +------------------------------------------------------------------------------- +Clara.tests.cpp: +............................................................................... + +Clara.tests.cpp:: PASSED: + CHECK( name.empty() ) with expansion: - 4 == 4 + true -ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == i ) +Clara.tests.cpp:: PASSED: + CHECK( result ) with expansion: - 5 == 5 + {?} + +Clara.tests.cpp:: PASSED: + CHECK( result.type() == Catch::Clara::Detail::ResultType::Ok ) +with expansion: + 0 == 0 + +Clara.tests.cpp:: PASSED: + CHECK( parsed.type() == Catch::Clara::ParseResultType::NoMatch ) +with expansion: + 1 == 1 + +Clara.tests.cpp:: PASSED: + CHECK( parsed.remainingTokens().count() == 2 ) +with expansion: + 2 == 2 + +Clara.tests.cpp:: PASSED: + CHECK( name.empty() ) +with expansion: + true ------------------------------------------------------------------------------- Clara::Arg supports single-arg parse the way Opt does @@ -3926,7 +4014,7 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( 101.000001 != Approx(100).epsilon(0.01) ) with expansion: - 101.000001 != Approx( 100.0 ) + 101.00000099999999748 != Approx( 100.0 ) Approx.tests.cpp:: PASSED: REQUIRE( std::pow(10, -5) != Approx(std::pow(10, -7)) ) @@ -3956,6 +4044,16 @@ with expansion: == "{** unexpected enum value **}" +------------------------------------------------------------------------------- +Empty generators can SKIP in constructor +------------------------------------------------------------------------------- +Skip.tests.cpp: +............................................................................... + +Skip.tests.cpp:: SKIPPED: +explicitly with message: + This generator is empty + ------------------------------------------------------------------------------- Empty stream name opens cout stream ------------------------------------------------------------------------------- @@ -3967,15 +4065,6 @@ Stream.tests.cpp:: PASSED: with expansion: true -------------------------------------------------------------------------------- -Empty tag is not allowed -------------------------------------------------------------------------------- -Tag.tests.cpp: -............................................................................... - -Tag.tests.cpp:: PASSED: - REQUIRE_THROWS( Catch::TestCaseInfo( "", { "fake test name", "[]" }, dummySourceLineInfo ) ) - ------------------------------------------------------------------------------- EndsWith string matcher ------------------------------------------------------------------------------- @@ -4052,7 +4141,7 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( 101.01 != Approx(100).epsilon(0.01) ) with expansion: - 101.01 != Approx( 100.0 ) + 101.01000000000000512 != Approx( 100.0 ) ------------------------------------------------------------------------------- Equality checks that should fail @@ -4078,27 +4167,31 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 9.11f ) ) with expansion: - 9.1f == Approx( 9.1099996567 ) + 9.100000381f + == + Approx( 9.10999965667724609 ) Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 9.0f ) ) with expansion: - 9.1f == Approx( 9.0 ) + 9.100000381f == Approx( 9.0 ) Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 1 ) ) with expansion: - 9.1f == Approx( 1.0 ) + 9.100000381f == Approx( 1.0 ) Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 0 ) ) with expansion: - 9.1f == Approx( 0.0 ) + 9.100000381f == Approx( 0.0 ) Condition.tests.cpp:: FAILED: CHECK( data.double_pi == Approx( 3.1415 ) ) with expansion: - 3.1415926535 == Approx( 3.1415 ) + 3.14159265350000005 + == + Approx( 3.14150000000000018 ) Condition.tests.cpp:: FAILED: CHECK( data.str_hello == "goodbye" ) @@ -4123,7 +4216,9 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( x == Approx( 1.301 ) ) with expansion: - 1.3 == Approx( 1.301 ) + 1.30000000000000027 + == + Approx( 1.30099999999999993 ) ------------------------------------------------------------------------------- Equality checks that should succeed @@ -4139,12 +4234,16 @@ with expansion: Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one == Approx( 9.1f ) ) with expansion: - 9.1f == Approx( 9.1000003815 ) + 9.100000381f + == + Approx( 9.10000038146972656 ) Condition.tests.cpp:: PASSED: REQUIRE( data.double_pi == Approx( 3.1415926535 ) ) with expansion: - 3.1415926535 == Approx( 3.1415926535 ) + 3.14159265350000005 + == + Approx( 3.14159265350000005 ) Condition.tests.cpp:: PASSED: REQUIRE( data.str_hello == "hello" ) @@ -4164,7 +4263,9 @@ with expansion: Condition.tests.cpp:: PASSED: REQUIRE( x == Approx( 1.3 ) ) with expansion: - 1.3 == Approx( 1.3 ) + 1.30000000000000027 + == + Approx( 1.30000000000000004 ) ------------------------------------------------------------------------------- Equals @@ -4496,22 +4597,22 @@ Matchers.tests.cpp: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 10., WithinRel( 11.1, 0.1 ) ) with expansion: - 10.0 and 11.1 are within 10% of each other + 10.0 and 11.09999999999999964 are within 10% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 10., !WithinRel( 11.2, 0.1 ) ) with expansion: - 10.0 not and 11.2 are within 10% of each other + 10.0 not and 11.19999999999999929 are within 10% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 1., !WithinRel( 0., 0.99 ) ) with expansion: - 1.0 not and 0 are within 99% of each other + 1.0 not and 0.0 are within 99% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( -0., WithinRel( 0. ) ) with expansion: - -0.0 and 0 are within 2.22045e-12% of each other + -0.0 and 0.0 are within 2.22045e-12% of each other ------------------------------------------------------------------------------- Floating point matchers: double @@ -4524,7 +4625,7 @@ Matchers.tests.cpp: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( v1, WithinRel( v2 ) ) with expansion: - 0.0 and 2.22507e-308 are within 2.22045e-12% of each other + 0.0 and 0.0 are within 2.22045e-12% of each other ------------------------------------------------------------------------------- Floating point matchers: double @@ -4546,12 +4647,12 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) ) with expansion: - 0.0 not is within 0.99 of 1.0 + 0.0 not is within 0.98999999999999999 of 1.0 Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) ) with expansion: - 0.0 not is within 0.99 of 1.0 + 0.0 not is within 0.98999999999999999 of 1.0 Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 11., !WithinAbs( 10., 0.5 ) ) @@ -4571,7 +4672,7 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( -10., WithinAbs( -9.6, 0.5 ) ) with expansion: - -10.0 is within 0.5 of -9.6 + -10.0 is within 0.5 of -9.59999999999999964 ------------------------------------------------------------------------------- Floating point matchers: double @@ -4589,8 +4690,8 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( nextafter( 1., 2. ), WithinULP( 1., 1 ) ) with expansion: - 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1. - 0000000000000002e+00]) + 1.00000000000000022 is within 1 ULPs of 1.0000000000000000e+00 ([9. + 9999999999999989e-01, 1.0000000000000002e+00]) Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0., WithinULP( nextafter( 0., 1. ), 1 ) ) @@ -4644,7 +4745,7 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) ) with expansion: - 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + 0.0001 ( is within 0.001 of 0.0 or and 0.0 are within 10% of each other ) ------------------------------------------------------------------------------- Floating point matchers: double @@ -4693,22 +4794,22 @@ Matchers.tests.cpp: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 10.f, WithinRel( 11.1f, 0.1f ) ) with expansion: - 10.0f and 11.1 are within 10% of each other + 10.0f and 11.10000038146972656 are within 10% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 10.f, !WithinRel( 11.2f, 0.1f ) ) with expansion: - 10.0f not and 11.2 are within 10% of each other + 10.0f not and 11.19999980926513672 are within 10% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 1.f, !WithinRel( 0.f, 0.99f ) ) with expansion: - 1.0f not and 0 are within 99% of each other + 1.0f not and 0.0 are within 99% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( -0.f, WithinRel( 0.f ) ) with expansion: - -0.0f and 0 are within 0.00119209% of each other + -0.0f and 0.0 are within 0.00119209% of each other ------------------------------------------------------------------------------- Floating point matchers: float @@ -4721,7 +4822,7 @@ Matchers.tests.cpp: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( v1, WithinRel( v2 ) ) with expansion: - 0.0f and 1.17549e-38 are within 0.00119209% of each other + 0.0f and 0.0 are within 0.00119209% of each other ------------------------------------------------------------------------------- Floating point matchers: float @@ -4743,12 +4844,12 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) ) with expansion: - 0.0f not is within 0.9900000095 of 1.0 + 0.0f not is within 0.99000000953674316 of 1.0 Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) ) with expansion: - 0.0f not is within 0.9900000095 of 1.0 + 0.0f not is within 0.99000000953674316 of 1.0 Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.f, WithinAbs( -0.f, 0 ) ) @@ -4773,7 +4874,7 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( -10.f, WithinAbs( -9.6f, 0.5f ) ) with expansion: - -10.0f is within 0.5 of -9.6000003815 + -10.0f is within 0.5 of -9.60000038146972656 ------------------------------------------------------------------------------- Floating point matchers: float @@ -4796,7 +4897,8 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) ) with expansion: - 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) + 1.000000119f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1. + 00000012e+00]) Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) ) @@ -4846,7 +4948,8 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) ) with expansion: - 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + 0.0001f ( is within 0.00100000004749745 of 0.0 or and 0.0 are within 10% of + each other ) ------------------------------------------------------------------------------- Floating point matchers: float @@ -4888,6 +4991,50 @@ Matchers.tests.cpp:: PASSED: with expansion: 1.0 not is NaN +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + ------------------------------------------------------------------------------- Generators -- adapters Filtering by predicate @@ -6243,7 +6390,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.9 == Approx( -0.9 ) + -0.90000000000000002 + == + Approx( -0.90000000000000002 ) with message: Current expected value is -0.9 @@ -6257,7 +6406,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.8 == Approx( -0.8 ) + -0.80000000000000004 + == + Approx( -0.80000000000000004 ) with message: Current expected value is -0.8 @@ -6271,7 +6422,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.7 == Approx( -0.7 ) + -0.70000000000000007 + == + Approx( -0.70000000000000007 ) with message: Current expected value is -0.7 @@ -6285,7 +6438,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.6 == Approx( -0.6 ) + -0.60000000000000009 + == + Approx( -0.60000000000000009 ) with message: Current expected value is -0.6 @@ -6299,7 +6454,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.5 == Approx( -0.5 ) + -0.50000000000000011 + == + Approx( -0.50000000000000011 ) with message: Current expected value is -0.5 @@ -6313,7 +6470,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.4 == Approx( -0.4 ) + -0.40000000000000013 + == + Approx( -0.40000000000000013 ) with message: Current expected value is -0.4 @@ -6327,7 +6486,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.3 == Approx( -0.3 ) + -0.30000000000000016 + == + Approx( -0.30000000000000016 ) with message: Current expected value is -0.3 @@ -6341,7 +6502,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.2 == Approx( -0.2 ) + -0.20000000000000015 + == + Approx( -0.20000000000000015 ) with message: Current expected value is -0.2 @@ -6355,7 +6518,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.1 == Approx( -0.1 ) + -0.10000000000000014 + == + Approx( -0.10000000000000014 ) with message: Current expected value is -0.1 @@ -6369,7 +6534,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.0 == Approx( -0.0 ) + -0.00000000000000014 + == + Approx( -0.00000000000000014 ) with message: Current expected value is -1.38778e-16 @@ -6383,7 +6550,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.1 == Approx( 0.1 ) + 0.09999999999999987 + == + Approx( 0.09999999999999987 ) with message: Current expected value is 0.1 @@ -6397,7 +6566,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.2 == Approx( 0.2 ) + 0.19999999999999987 + == + Approx( 0.19999999999999987 ) with message: Current expected value is 0.2 @@ -6411,7 +6582,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.3 == Approx( 0.3 ) + 0.29999999999999988 + == + Approx( 0.29999999999999988 ) with message: Current expected value is 0.3 @@ -6425,7 +6598,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.4 == Approx( 0.4 ) + 0.39999999999999991 + == + Approx( 0.39999999999999991 ) with message: Current expected value is 0.4 @@ -6439,7 +6614,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.5 == Approx( 0.5 ) + 0.49999999999999989 + == + Approx( 0.49999999999999989 ) with message: Current expected value is 0.5 @@ -6453,7 +6630,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.6 == Approx( 0.6 ) + 0.59999999999999987 + == + Approx( 0.59999999999999987 ) with message: Current expected value is 0.6 @@ -6467,7 +6646,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.7 == Approx( 0.7 ) + 0.69999999999999984 + == + Approx( 0.69999999999999984 ) with message: Current expected value is 0.7 @@ -6481,7 +6662,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.8 == Approx( 0.8 ) + 0.79999999999999982 + == + Approx( 0.79999999999999982 ) with message: Current expected value is 0.8 @@ -6495,7 +6678,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.9 == Approx( 0.9 ) + 0.8999999999999998 + == + Approx( 0.8999999999999998 ) with message: Current expected value is 0.9 @@ -6509,7 +6694,7 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx( rangeEnd ) ) with expansion: - 1.0 == Approx( 1.0 ) + 0.99999999999999978 == Approx( 1.0 ) GeneratorsImpl.tests.cpp:: PASSED: REQUIRE_FALSE( gen.next() ) @@ -6543,7 +6728,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.7 == Approx( -0.7 ) + -0.69999999999999996 + == + Approx( -0.69999999999999996 ) with message: Current expected value is -0.7 @@ -6557,7 +6744,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.4 == Approx( -0.4 ) + -0.39999999999999997 + == + Approx( -0.39999999999999997 ) with message: Current expected value is -0.4 @@ -6571,7 +6760,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.1 == Approx( -0.1 ) + -0.09999999999999998 + == + Approx( -0.09999999999999998 ) with message: Current expected value is -0.1 @@ -6585,7 +6776,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.2 == Approx( 0.2 ) + 0.20000000000000001 + == + Approx( 0.20000000000000001 ) with message: Current expected value is 0.2 @@ -6642,7 +6835,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.7 == Approx( -0.7 ) + -0.69999999999999996 + == + Approx( -0.69999999999999996 ) with message: Current expected value is -0.7 @@ -6656,7 +6851,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.4 == Approx( -0.4 ) + -0.39999999999999997 + == + Approx( -0.39999999999999997 ) with message: Current expected value is -0.4 @@ -6670,7 +6867,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.1 == Approx( -0.1 ) + -0.09999999999999998 + == + Approx( -0.09999999999999998 ) with message: Current expected value is -0.1 @@ -6684,7 +6883,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.2 == Approx( 0.2 ) + 0.20000000000000001 + == + Approx( 0.20000000000000001 ) with message: Current expected value is 0.2 @@ -6883,22 +7084,30 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d >= Approx( 1.22 ) ) with expansion: - 1.23 >= Approx( 1.22 ) + 1.22999999999999998 + >= + Approx( 1.21999999999999997 ) Approx.tests.cpp:: PASSED: REQUIRE( d >= Approx( 1.23 ) ) with expansion: - 1.23 >= Approx( 1.23 ) + 1.22999999999999998 + >= + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE_FALSE( d >= Approx( 1.24 ) ) with expansion: - !(1.23 >= Approx( 1.24 )) + !(1.22999999999999998 + >= + Approx( 1.23999999999999999 )) Approx.tests.cpp:: PASSED: REQUIRE( d >= Approx( 1.24 ).epsilon(0.1) ) with expansion: - 1.23 >= Approx( 1.24 ) + 1.22999999999999998 + >= + Approx( 1.23999999999999999 ) ------------------------------------------------------------------------------- Hashers with different seed produce different hash with same test case @@ -6981,6 +7190,18 @@ with expansion: == 3422778688 (0x) +------------------------------------------------------------------------------- +INFO and UNSCOPED_INFO can stream multiple arguments +------------------------------------------------------------------------------- +Message.tests.cpp: +............................................................................... + +Message.tests.cpp:: FAILED: +explicitly with messages: + This info has multiple parts. + This unscoped info has multiple parts. + Show infos! + ------------------------------------------------------------------------------- INFO and WARN do not abort tests ------------------------------------------------------------------------------- @@ -7142,6 +7363,17 @@ with messages: current counter 10 i := 10 +------------------------------------------------------------------------------- +Incomplete AssertionHandler +------------------------------------------------------------------------------- +AssertionHandler.tests.cpp: +............................................................................... + +AssertionHandler.tests.cpp:: FAILED: + REQUIRE( Dummy ) +due to unexpected exception with message: + Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE + ------------------------------------------------------------------------------- Inequality checks that should fail ------------------------------------------------------------------------------- @@ -7156,12 +7388,16 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one != Approx( 9.1f ) ) with expansion: - 9.1f != Approx( 9.1000003815 ) + 9.100000381f + != + Approx( 9.10000038146972656 ) Condition.tests.cpp:: FAILED: CHECK( data.double_pi != Approx( 3.1415926535 ) ) with expansion: - 3.1415926535 != Approx( 3.1415926535 ) + 3.14159265350000005 + != + Approx( 3.14159265350000005 ) Condition.tests.cpp:: FAILED: CHECK( data.str_hello != "hello" ) @@ -7192,27 +7428,31 @@ with expansion: Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one != Approx( 9.11f ) ) with expansion: - 9.1f != Approx( 9.1099996567 ) + 9.100000381f + != + Approx( 9.10999965667724609 ) Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one != Approx( 9.0f ) ) with expansion: - 9.1f != Approx( 9.0 ) + 9.100000381f != Approx( 9.0 ) Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one != Approx( 1 ) ) with expansion: - 9.1f != Approx( 1.0 ) + 9.100000381f != Approx( 1.0 ) Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one != Approx( 0 ) ) with expansion: - 9.1f != Approx( 0.0 ) + 9.100000381f != Approx( 0.0 ) Condition.tests.cpp:: PASSED: REQUIRE( data.double_pi != Approx( 3.1415 ) ) with expansion: - 3.1415926535 != Approx( 3.1415 ) + 3.14159265350000005 + != + Approx( 3.14150000000000018 ) Condition.tests.cpp:: PASSED: REQUIRE( data.str_hello != "goodbye" ) @@ -7234,6 +7474,291 @@ Condition.tests.cpp:: PASSED: with expansion: 5 != 6 +------------------------------------------------------------------------------- +JsonWriter + Newly constructed JsonWriter does nothing +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "" ) +with expansion: + "" == "" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeObject will create an empty pair of braces +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "{\n}" ) +with expansion: + "{ + }" + == + "{ + }" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeObject with key will create an object to write the value +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE_THAT( stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) ) +with expansion: + "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] + }" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: + ""true": true," and contains: ""false": false," and contains: ""string": + "this is a string"," and contains: ""array": [ + 1, + 2 + ] + }" ) + +------------------------------------------------------------------------------- +JsonWriter + nesting objects +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE_THAT( stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) ) +with expansion: + "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } + }" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) + +------------------------------------------------------------------------------- +JsonWriter + Calling writeArray will create an empty pair of braces +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "[\n]" ) +with expansion: + "[ + ]" + == + "[ + ]" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeArray creates array to write the values to +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" ) +with expansion: + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] + ]" + == + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] + ]" + +------------------------------------------------------------------------------- +JsonWriter + Moved from JsonObjectWriter shall not insert superfluous brace +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "{\n}" ) +with expansion: + "{ + }" + == + "{ + }" + +------------------------------------------------------------------------------- +JsonWriter + Moved from JsonArrayWriter shall not insert superfluous bracket +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "[\n]" ) +with expansion: + "[ + ]" + == + "[ + ]" + +------------------------------------------------------------------------------- +JsonWriter + Custom class shall be quoted +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "\"custom\"" ) +with expansion: + ""custom"" == ""custom"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Quote in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\\"\"" ) +with expansion: + ""\""" == ""\""" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Backslash in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\\\\"" ) +with expansion: + ""\\"" == ""\\"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Forward slash in a string is **not** escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"/\"" ) +with expansion: + ""/"" == ""/"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Backspace in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\b\"" ) +with expansion: + ""\b"" == ""\b"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Formfeed in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\f\"" ) +with expansion: + ""\f"" == ""\f"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + linefeed in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\n\"" ) +with expansion: + ""\n"" == ""\n"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + carriage return in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\r\"" ) +with expansion: + ""\r"" == ""\r"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + tab in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\t\"" ) +with expansion: + ""\t"" == ""\t"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + combination of characters is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\\\/\\t\\r\\n\"" ) +with expansion: + ""\\/\t\r\n"" == ""\\/\t\r\n"" + ------------------------------------------------------------------------------- Lambdas in assertions ------------------------------------------------------------------------------- @@ -7254,22 +7779,30 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d <= Approx( 1.24 ) ) with expansion: - 1.23 <= Approx( 1.24 ) + 1.22999999999999998 + <= + Approx( 1.23999999999999999 ) Approx.tests.cpp:: PASSED: REQUIRE( d <= Approx( 1.23 ) ) with expansion: - 1.23 <= Approx( 1.23 ) + 1.22999999999999998 + <= + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE_FALSE( d <= Approx( 1.22 ) ) with expansion: - !(1.23 <= Approx( 1.22 )) + !(1.22999999999999998 + <= + Approx( 1.21999999999999997 )) Approx.tests.cpp:: PASSED: REQUIRE( d <= Approx( 1.22 ).epsilon(0.1) ) with expansion: - 1.23 <= Approx( 1.22 ) + 1.22999999999999998 + <= + Approx( 1.21999999999999997 ) ------------------------------------------------------------------------------- ManuallyRegistered @@ -7822,17 +8355,17 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one < 9 ) with expansion: - 9.1f < 9 + 9.100000381f < 9 Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one > 10 ) with expansion: - 9.1f > 10 + 9.100000381f > 10 Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one > 9.2 ) with expansion: - 9.1f > 9.2 + 9.100000381f > 9.19999999999999929 Condition.tests.cpp:: FAILED: CHECK( data.str_hello > "hello" ) @@ -7923,17 +8456,17 @@ with expansion: Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one > 9 ) with expansion: - 9.1f > 9 + 9.100000381f > 9 Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one < 10 ) with expansion: - 9.1f < 10 + 9.100000381f < 10 Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one < 9.2 ) with expansion: - 9.1f < 9.2 + 9.100000381f < 9.19999999999999929 Condition.tests.cpp:: PASSED: REQUIRE( data.str_hello <= "hello" ) @@ -9289,7 +9822,9 @@ with expansion: CmdLine.tests.cpp:: PASSED: REQUIRE( config.benchmarkConfidenceInterval == Catch::Approx(0.99) ) with expansion: - 0.99 == Approx( 0.99 ) + 0.98999999999999999 + == + Approx( 0.98999999999999999 ) ------------------------------------------------------------------------------- Process can be configured on command line @@ -9732,6 +10267,129 @@ Reporter's write listings to provided stream Reporters.tests.cpp: ............................................................................... +Reporters.tests.cpp:: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists tags +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + +Reporters.tests.cpp:: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring("fakeTag"s) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + +Reporters.tests.cpp:: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists reporters +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + +Reporters.tests.cpp:: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring("fake reporter"s) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + +Reporters.tests.cpp:: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists tests +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + +Reporters.tests.cpp:: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + Reporters.tests.cpp:: PASSED: REQUIRE_FALSE( factories.empty() ) with expansion: @@ -10387,42 +11045,58 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d == Approx( 1.23 ) ) with expansion: - 1.23 == Approx( 1.23 ) + 1.22999999999999998 + == + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE( d != Approx( 1.22 ) ) with expansion: - 1.23 != Approx( 1.22 ) + 1.22999999999999998 + != + Approx( 1.21999999999999997 ) Approx.tests.cpp:: PASSED: REQUIRE( d != Approx( 1.24 ) ) with expansion: - 1.23 != Approx( 1.24 ) + 1.22999999999999998 + != + Approx( 1.23999999999999999 ) Approx.tests.cpp:: PASSED: REQUIRE( d == 1.23_a ) with expansion: - 1.23 == Approx( 1.23 ) + 1.22999999999999998 + == + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE( d != 1.22_a ) with expansion: - 1.23 != Approx( 1.22 ) + 1.22999999999999998 + != + Approx( 1.21999999999999997 ) Approx.tests.cpp:: PASSED: REQUIRE( Approx( d ) == 1.23 ) with expansion: - Approx( 1.23 ) == 1.23 + Approx( 1.22999999999999998 ) + == + 1.22999999999999998 Approx.tests.cpp:: PASSED: REQUIRE( Approx( d ) != 1.22 ) with expansion: - Approx( 1.23 ) != 1.22 + Approx( 1.22999999999999998 ) + != + 1.21999999999999997 Approx.tests.cpp:: PASSED: REQUIRE( Approx( d ) != 1.24 ) with expansion: - Approx( 1.23 ) != 1.24 + Approx( 1.22999999999999998 ) + != + 1.23999999999999999 Message from section one ------------------------------------------------------------------------------- @@ -11199,9 +11873,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_default_constructible::value ) with expansion: - 1 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside non-copyable and non- @@ -11211,9 +11885,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_default_constructible::value ) with expansion: - 4 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside non-default-constructible @@ -11223,9 +11897,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_trivially_copyable::value ) with expansion: - 1 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside non-default-constructible @@ -11235,9 +11909,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_trivially_copyable::value ) with expansion: - 4 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside std::tuple - MyTypes - 0 @@ -11246,9 +11920,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_arithmetic::value ) with expansion: - 4 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside std::tuple - MyTypes - 1 @@ -11257,9 +11931,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_arithmetic::value ) with expansion: - 1 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside std::tuple - MyTypes - 2 @@ -11268,9 +11942,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_arithmetic::value ) with expansion: - 4 > 0 + true ------------------------------------------------------------------------------- TemplateTest: vectors can be sized and resized - float @@ -12521,6 +13195,34 @@ Misc.tests.cpp:: FAILED - but was ok: Misc.tests.cpp:: FAILED: +------------------------------------------------------------------------------- +Testing checked-if 4 +------------------------------------------------------------------------------- +Misc.tests.cpp: +............................................................................... + +Misc.tests.cpp:: PASSED: + CHECKED_ELSE( true ) + +Misc.tests.cpp:: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + +------------------------------------------------------------------------------- +Testing checked-if 5 +------------------------------------------------------------------------------- +Misc.tests.cpp: +............................................................................... + +Misc.tests.cpp:: FAILED - but was ok: + CHECKED_ELSE( false ) + +Misc.tests.cpp:: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + ------------------------------------------------------------------------------- The NO_FAIL macro reports a failure but does not fail the test ------------------------------------------------------------------------------- @@ -13297,7 +13999,7 @@ Exception.tests.cpp: Exception.tests.cpp:: FAILED: due to unexpected exception with message: - 3.14 + 3.14000000000000012 ------------------------------------------------------------------------------- Upcasting special member functions @@ -13460,7 +14162,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of AllTrue range matcher Basic usage - One false evalutes to false + One false evaluates to false ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -13499,7 +14201,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of AllTrue range matcher Contained type is convertible to bool - One false evalutes to false + One false evaluates to false ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -13735,7 +14437,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of AnyTrue range matcher Basic usage - One true evalutes to true + One true evaluates to true ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -13774,7 +14476,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of AnyTrue range matcher Contained type is convertible to bool - One true evalutes to true + One true evaluates to true ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -14010,7 +14712,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of NoneTrue range matcher Basic usage - One true evalutes to false + One true evaluates to false ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -14049,7 +14751,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of NoneTrue range matcher Contained type is convertible to bool - One true evalutes to false + One true evaluates to false ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -14541,42 +15243,54 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d == approx( 1.23 ) ) with expansion: - 1.23 == Approx( 1.23 ) + 1.22999999999999998 + == + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE( d == approx( 1.22 ) ) with expansion: - 1.23 == Approx( 1.22 ) + 1.22999999999999998 + == + Approx( 1.21999999999999997 ) Approx.tests.cpp:: PASSED: REQUIRE( d == approx( 1.24 ) ) with expansion: - 1.23 == Approx( 1.24 ) + 1.22999999999999998 + == + Approx( 1.23999999999999999 ) Approx.tests.cpp:: PASSED: REQUIRE( d != approx( 1.25 ) ) with expansion: - 1.23 != Approx( 1.25 ) + 1.22999999999999998 != Approx( 1.25 ) Approx.tests.cpp:: PASSED: REQUIRE( approx( d ) == 1.23 ) with expansion: - Approx( 1.23 ) == 1.23 + Approx( 1.22999999999999998 ) + == + 1.22999999999999998 Approx.tests.cpp:: PASSED: REQUIRE( approx( d ) == 1.22 ) with expansion: - Approx( 1.23 ) == 1.22 + Approx( 1.22999999999999998 ) + == + 1.21999999999999997 Approx.tests.cpp:: PASSED: REQUIRE( approx( d ) == 1.24 ) with expansion: - Approx( 1.23 ) == 1.24 + Approx( 1.22999999999999998 ) + == + 1.23999999999999999 Approx.tests.cpp:: PASSED: REQUIRE( approx( d ) != 1.25 ) with expansion: - Approx( 1.23 ) != 1.25 + Approx( 1.22999999999999998 ) != 1.25 ------------------------------------------------------------------------------- Variadic macros @@ -15769,17 +16483,23 @@ InternalBenchmark.tests.cpp: InternalBenchmark.tests.cpp:: PASSED: CHECK( erfc_inv(1.103560) == Approx(-0.09203687623843015) ) with expansion: - -0.0920368762 == Approx( -0.0920368762 ) + -0.09203687623843014 + == + Approx( -0.09203687623843015 ) InternalBenchmark.tests.cpp:: PASSED: CHECK( erfc_inv(1.067400) == Approx(-0.05980291115763361) ) with expansion: - -0.0598029112 == Approx( -0.0598029112 ) + -0.05980291115763361 + == + Approx( -0.05980291115763361 ) InternalBenchmark.tests.cpp:: PASSED: CHECK( erfc_inv(0.050000) == Approx(1.38590382434967796) ) with expansion: - 1.3859038243 == Approx( 1.3859038243 ) + 1.38590382434967774 + == + Approx( 1.38590382434967796 ) ------------------------------------------------------------------------------- estimate_clock_resolution @@ -16439,37 +17159,6 @@ Tricky.tests.cpp:: PASSED: with expansion: {?} == {?} -------------------------------------------------------------------------------- -normal_cdf -------------------------------------------------------------------------------- -InternalBenchmark.tests.cpp: -............................................................................... - -InternalBenchmark.tests.cpp:: PASSED: - CHECK( normal_cdf(0.000000) == Approx(0.50000000000000000) ) -with expansion: - 0.5 == Approx( 0.5 ) - -InternalBenchmark.tests.cpp:: PASSED: - CHECK( normal_cdf(1.000000) == Approx(0.84134474606854293) ) -with expansion: - 0.8413447461 == Approx( 0.8413447461 ) - -InternalBenchmark.tests.cpp:: PASSED: - CHECK( normal_cdf(-1.000000) == Approx(0.15865525393145705) ) -with expansion: - 0.1586552539 == Approx( 0.1586552539 ) - -InternalBenchmark.tests.cpp:: PASSED: - CHECK( normal_cdf(2.809729) == Approx(0.99752083845315409) ) -with expansion: - 0.9975208385 == Approx( 0.9975208385 ) - -InternalBenchmark.tests.cpp:: PASSED: - CHECK( normal_cdf(-1.352570) == Approx(0.08809652095066035) ) -with expansion: - 0.088096521 == Approx( 0.088096521 ) - ------------------------------------------------------------------------------- normal_quantile ------------------------------------------------------------------------------- @@ -16479,17 +17168,23 @@ InternalBenchmark.tests.cpp: InternalBenchmark.tests.cpp:: PASSED: CHECK( normal_quantile(0.551780) == Approx(0.13015979861484198) ) with expansion: - 0.1301597986 == Approx( 0.1301597986 ) + 0.13015979861484195 + == + Approx( 0.13015979861484198 ) InternalBenchmark.tests.cpp:: PASSED: CHECK( normal_quantile(0.533700) == Approx(0.08457408802851875) ) with expansion: - 0.084574088 == Approx( 0.084574088 ) + 0.08457408802851875 + == + Approx( 0.08457408802851875 ) InternalBenchmark.tests.cpp:: PASSED: CHECK( normal_quantile(0.025000) == Approx(-1.95996398454005449) ) with expansion: - -1.9599639845 == Approx( -1.9599639845 ) + -1.95996398454005405 + == + Approx( -1.95996398454005449 ) ------------------------------------------------------------------------------- not allowed @@ -16805,6 +17500,42 @@ StringManip.tests.cpp:: PASSED: with expansion: "abcdefcg" == "abcdefcg" +------------------------------------------------------------------------------- +replaceInPlace + no replace in already-replaced string + lengthening +------------------------------------------------------------------------------- +StringManip.tests.cpp: +............................................................................... + +StringManip.tests.cpp:: PASSED: + CHECK( Catch::replaceInPlace(letters, "c", "cc") ) +with expansion: + true + +StringManip.tests.cpp:: PASSED: + CHECK( letters == "abccdefccg" ) +with expansion: + "abccdefccg" == "abccdefccg" + +------------------------------------------------------------------------------- +replaceInPlace + no replace in already-replaced string + shortening +------------------------------------------------------------------------------- +StringManip.tests.cpp: +............................................................................... + +StringManip.tests.cpp:: PASSED: + CHECK( Catch::replaceInPlace(s, "--", "-") ) +with expansion: + true + +StringManip.tests.cpp:: PASSED: + CHECK( s == "--" ) +with expansion: + "--" == "--" + ------------------------------------------------------------------------------- replaceInPlace escape ' @@ -17672,14 +18403,14 @@ ToStringTuple.tests.cpp: ............................................................................... ToStringTuple.tests.cpp:: PASSED: - CHECK( "1.2f" == ::Catch::Detail::stringify(float(1.2)) ) + CHECK( "1.5f" == ::Catch::Detail::stringify(float(1.5)) ) with expansion: - "1.2f" == "1.2f" + "1.5f" == "1.5f" ToStringTuple.tests.cpp:: PASSED: - CHECK( "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) ) + CHECK( "{ 1.5f, 0 }" == ::Catch::Detail::stringify(type{1.5f,0}) ) with expansion: - "{ 1.2f, 0 }" == "{ 1.2f, 0 }" + "{ 1.5f, 0 }" == "{ 1.5f, 0 }" ------------------------------------------------------------------------------- tuple @@ -17712,11 +18443,11 @@ ToStringTuple.tests.cpp: ............................................................................... ToStringTuple.tests.cpp:: PASSED: - CHECK( "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) ) + CHECK( "{ { 42 }, { }, 1.5f }" == ::Catch::Detail::stringify(value) ) with expansion: - "{ { 42 }, { }, 1.2f }" + "{ { 42 }, { }, 1.5f }" == - "{ { 42 }, { }, 1.2f }" + "{ { 42 }, { }, 1.5f }" ------------------------------------------------------------------------------- uniform samples @@ -17742,7 +18473,23 @@ with expansion: InternalBenchmark.tests.cpp:: PASSED: CHECK( e.confidence_interval == 0.95 ) with expansion: - 0.95 == 0.95 + 0.94999999999999996 == 0.94999999999999996 + +------------------------------------------------------------------------------- +uniform_integer_distribution can return the bounds +------------------------------------------------------------------------------- +RandomNumberGeneration.tests.cpp: +............................................................................... + +RandomNumberGeneration.tests.cpp:: PASSED: + REQUIRE( dist.a() == -10 ) +with expansion: + -10 == -10 + +RandomNumberGeneration.tests.cpp:: PASSED: + REQUIRE( dist.b() == 10 ) +with expansion: + 10 == 10 ------------------------------------------------------------------------------- unique_ptr reimplementation: basic functionality @@ -18231,6 +18978,6 @@ Misc.tests.cpp: Misc.tests.cpp:: PASSED: =============================================================================== -test cases: 409 | 309 passed | 84 failed | 5 skipped | 11 failed as expected -assertions: 2226 | 2049 passed | 145 failed | 32 failed as expected +test cases: 419 | 313 passed | 86 failed | 6 skipped | 14 failed as expected +assertions: 2265 | 2083 passed | 147 failed | 35 failed as expected diff --git a/src/external/catch2/tests/SelfTest/Baselines/console.sw.multi.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/console.sw.multi.approved.txt index 80b63ab8..1fe0b73e 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/console.sw.multi.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/console.sw.multi.approved.txt @@ -2021,6 +2021,54 @@ A TEST_CASE_METHOD based test run that succeeds Class.tests.cpp: ............................................................................... +Class.tests.cpp:: PASSED: + REQUIRE( m_a == 1 ) +with expansion: + 1 == 1 + +------------------------------------------------------------------------------- +A TEST_CASE_PERSISTENT_FIXTURE based test run that fails + First partial run +------------------------------------------------------------------------------- +Class.tests.cpp: +............................................................................... + +Class.tests.cpp:: PASSED: + REQUIRE( m_a++ == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +A TEST_CASE_PERSISTENT_FIXTURE based test run that fails + Second partial run +------------------------------------------------------------------------------- +Class.tests.cpp: +............................................................................... + +Class.tests.cpp:: FAILED: + REQUIRE( m_a == 0 ) +with expansion: + 1 == 0 + +------------------------------------------------------------------------------- +A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds + First partial run +------------------------------------------------------------------------------- +Class.tests.cpp: +............................................................................... + +Class.tests.cpp:: PASSED: + REQUIRE( m_a++ == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds + Second partial run +------------------------------------------------------------------------------- +Class.tests.cpp: +............................................................................... + Class.tests.cpp:: PASSED: REQUIRE( m_a == 1 ) with expansion: @@ -2123,32 +2171,42 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d == 1.23_a ) with expansion: - 1.23 == Approx( 1.23 ) + 1.22999999999999998 + == + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE( d != 1.22_a ) with expansion: - 1.23 != Approx( 1.22 ) + 1.22999999999999998 + != + Approx( 1.21999999999999997 ) Approx.tests.cpp:: PASSED: REQUIRE( -d == -1.23_a ) with expansion: - -1.23 == Approx( -1.23 ) + -1.22999999999999998 + == + Approx( -1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE( d == 1.2_a .epsilon(.1) ) with expansion: - 1.23 == Approx( 1.2 ) + 1.22999999999999998 + == + Approx( 1.19999999999999996 ) Approx.tests.cpp:: PASSED: REQUIRE( d != 1.2_a .epsilon(.001) ) with expansion: - 1.23 != Approx( 1.2 ) + 1.22999999999999998 + != + Approx( 1.19999999999999996 ) Approx.tests.cpp:: PASSED: REQUIRE( d == 1_a .epsilon(.3) ) with expansion: - 1.23 == Approx( 1.0 ) + 1.22999999999999998 == Approx( 1.0 ) ------------------------------------------------------------------------------- A couple of nested sections followed by a failure @@ -2217,12 +2275,12 @@ with expansion: Approx.tests.cpp:: PASSED: REQUIRE( 100.3 != Approx(100.0) ) with expansion: - 100.3 != Approx( 100.0 ) + 100.29999999999999716 != Approx( 100.0 ) Approx.tests.cpp:: PASSED: REQUIRE( 100.3 == Approx(100.0).margin(0.5) ) with expansion: - 100.3 == Approx( 100.0 ) + 100.29999999999999716 == Approx( 100.0 ) ------------------------------------------------------------------------------- An empty test with no assertions @@ -2340,12 +2398,16 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) ) with expansion: - 3.1428571429 == Approx( 3.141 ) + 3.14285714285714279 + == + Approx( 3.14100000000000001 ) Approx.tests.cpp:: PASSED: REQUIRE( divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) ) with expansion: - 3.1428571429 != Approx( 3.141 ) + 3.14285714285714279 + != + Approx( 3.14100000000000001 ) ------------------------------------------------------------------------------- Approximate comparisons with different epsilons @@ -2356,12 +2418,16 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d != Approx( 1.231 ) ) with expansion: - 1.23 != Approx( 1.231 ) + 1.22999999999999998 + != + Approx( 1.23100000000000009 ) Approx.tests.cpp:: PASSED: REQUIRE( d == Approx( 1.231 ).epsilon( 0.1 ) ) with expansion: - 1.23 == Approx( 1.231 ) + 1.22999999999999998 + == + Approx( 1.23100000000000009 ) ------------------------------------------------------------------------------- Approximate comparisons with floats @@ -2372,7 +2438,9 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( 1.23f == Approx( 1.23f ) ) with expansion: - 1.23f == Approx( 1.2300000191 ) + 1.230000019f + == + Approx( 1.23000001907348633 ) Approx.tests.cpp:: PASSED: REQUIRE( 0.0f == Approx( 0.0f ) ) @@ -2419,12 +2487,16 @@ with expansion: Approx.tests.cpp:: PASSED: REQUIRE( 1.234f == Approx( dMedium ) ) with expansion: - 1.234f == Approx( 1.234 ) + 1.233999968f + == + Approx( 1.23399999999999999 ) Approx.tests.cpp:: PASSED: REQUIRE( dMedium == Approx( 1.234f ) ) with expansion: - 1.234 == Approx( 1.2339999676 ) + 1.23399999999999999 + == + Approx( 1.23399996757507324 ) ------------------------------------------------------------------------------- Arbitrary predicate matcher @@ -2738,9 +2810,9 @@ Message.tests.cpp: Message.tests.cpp:: PASSED: with messages: - std::vector{1, 2, 3}[0, 1, 2] := 3 - std::vector{1, 2, 3}[(0, 1)] := 2 - std::vector{1, 2, 3}[0] := 1 + custom_index_op{1, 2, 3}[0, 1, 2] := 0 + custom_index_op{1, 2, 3}[(0, 1)] := 0 + custom_index_op{1, 2, 3}[0] := 0 (helper_1436{12, -12}) := { 12, -12 } (helper_1436(-12, 12)) := { -12, 12 } (1, 2) := 2 @@ -2902,24 +2974,24 @@ ToStringGeneral.tests.cpp: ............................................................................... ToStringGeneral.tests.cpp:: PASSED: - CHECK( tab == '\t' ) + CHECK( ::Catch::Detail::stringify('\t') == "'\\t'" ) with expansion: - '\t' == '\t' + "'\t'" == "'\t'" ToStringGeneral.tests.cpp:: PASSED: - CHECK( newline == '\n' ) + CHECK( ::Catch::Detail::stringify('\n') == "'\\n'" ) with expansion: - '\n' == '\n' + "'\n'" == "'\n'" ToStringGeneral.tests.cpp:: PASSED: - CHECK( carr_return == '\r' ) + CHECK( ::Catch::Detail::stringify('\r') == "'\\r'" ) with expansion: - '\r' == '\r' + "'\r'" == "'\r'" ToStringGeneral.tests.cpp:: PASSED: - CHECK( form_feed == '\f' ) + CHECK( ::Catch::Detail::stringify('\f') == "'\\f'" ) with expansion: - '\f' == '\f' + "'\f'" == "'\f'" ------------------------------------------------------------------------------- Character pretty printing @@ -2929,29 +3001,19 @@ ToStringGeneral.tests.cpp: ............................................................................... ToStringGeneral.tests.cpp:: PASSED: - CHECK( space == ' ' ) -with expansion: - ' ' == ' ' - -ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == chars[i] ) -with expansion: - 'a' == 'a' - -ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == chars[i] ) + CHECK( ::Catch::Detail::stringify( ' ' ) == "' '" ) with expansion: - 'z' == 'z' + "' '" == "' '" ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == chars[i] ) + CHECK( ::Catch::Detail::stringify( 'A' ) == "'A'" ) with expansion: - 'A' == 'A' + "'A'" == "'A'" ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == chars[i] ) + CHECK( ::Catch::Detail::stringify( 'z' ) == "'z'" ) with expansion: - 'Z' == 'Z' + "'z'" == "'z'" ------------------------------------------------------------------------------- Character pretty printing @@ -2961,29 +3023,55 @@ ToStringGeneral.tests.cpp: ............................................................................... ToStringGeneral.tests.cpp:: PASSED: - CHECK( null_terminator == '\0' ) + CHECK( ::Catch::Detail::stringify( '\0' ) == "0" ) with expansion: - 0 == 0 + "0" == "0" ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == i ) + CHECK( ::Catch::Detail::stringify( static_cast(2) ) == "2" ) with expansion: - 2 == 2 + "2" == "2" ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == i ) + CHECK( ::Catch::Detail::stringify( static_cast(5) ) == "5" ) with expansion: - 3 == 3 + "5" == "5" -ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == i ) +------------------------------------------------------------------------------- +Clara::Arg does not crash on incomplete input +------------------------------------------------------------------------------- +Clara.tests.cpp: +............................................................................... + +Clara.tests.cpp:: PASSED: + CHECK( name.empty() ) with expansion: - 4 == 4 + true -ToStringGeneral.tests.cpp:: PASSED: - REQUIRE( c == i ) +Clara.tests.cpp:: PASSED: + CHECK( result ) with expansion: - 5 == 5 + {?} + +Clara.tests.cpp:: PASSED: + CHECK( result.type() == Catch::Clara::Detail::ResultType::Ok ) +with expansion: + 0 == 0 + +Clara.tests.cpp:: PASSED: + CHECK( parsed.type() == Catch::Clara::ParseResultType::NoMatch ) +with expansion: + 1 == 1 + +Clara.tests.cpp:: PASSED: + CHECK( parsed.remainingTokens().count() == 2 ) +with expansion: + 2 == 2 + +Clara.tests.cpp:: PASSED: + CHECK( name.empty() ) +with expansion: + true ------------------------------------------------------------------------------- Clara::Arg supports single-arg parse the way Opt does @@ -3924,7 +4012,7 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( 101.000001 != Approx(100).epsilon(0.01) ) with expansion: - 101.000001 != Approx( 100.0 ) + 101.00000099999999748 != Approx( 100.0 ) Approx.tests.cpp:: PASSED: REQUIRE( std::pow(10, -5) != Approx(std::pow(10, -7)) ) @@ -3954,6 +4042,16 @@ with expansion: == "{** unexpected enum value **}" +------------------------------------------------------------------------------- +Empty generators can SKIP in constructor +------------------------------------------------------------------------------- +Skip.tests.cpp: +............................................................................... + +Skip.tests.cpp:: SKIPPED: +explicitly with message: + This generator is empty + ------------------------------------------------------------------------------- Empty stream name opens cout stream ------------------------------------------------------------------------------- @@ -3965,15 +4063,6 @@ Stream.tests.cpp:: PASSED: with expansion: true -------------------------------------------------------------------------------- -Empty tag is not allowed -------------------------------------------------------------------------------- -Tag.tests.cpp: -............................................................................... - -Tag.tests.cpp:: PASSED: - REQUIRE_THROWS( Catch::TestCaseInfo( "", { "fake test name", "[]" }, dummySourceLineInfo ) ) - ------------------------------------------------------------------------------- EndsWith string matcher ------------------------------------------------------------------------------- @@ -4050,7 +4139,7 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( 101.01 != Approx(100).epsilon(0.01) ) with expansion: - 101.01 != Approx( 100.0 ) + 101.01000000000000512 != Approx( 100.0 ) ------------------------------------------------------------------------------- Equality checks that should fail @@ -4076,27 +4165,31 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 9.11f ) ) with expansion: - 9.1f == Approx( 9.1099996567 ) + 9.100000381f + == + Approx( 9.10999965667724609 ) Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 9.0f ) ) with expansion: - 9.1f == Approx( 9.0 ) + 9.100000381f == Approx( 9.0 ) Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 1 ) ) with expansion: - 9.1f == Approx( 1.0 ) + 9.100000381f == Approx( 1.0 ) Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one == Approx( 0 ) ) with expansion: - 9.1f == Approx( 0.0 ) + 9.100000381f == Approx( 0.0 ) Condition.tests.cpp:: FAILED: CHECK( data.double_pi == Approx( 3.1415 ) ) with expansion: - 3.1415926535 == Approx( 3.1415 ) + 3.14159265350000005 + == + Approx( 3.14150000000000018 ) Condition.tests.cpp:: FAILED: CHECK( data.str_hello == "goodbye" ) @@ -4121,7 +4214,9 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( x == Approx( 1.301 ) ) with expansion: - 1.3 == Approx( 1.301 ) + 1.30000000000000027 + == + Approx( 1.30099999999999993 ) ------------------------------------------------------------------------------- Equality checks that should succeed @@ -4137,12 +4232,16 @@ with expansion: Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one == Approx( 9.1f ) ) with expansion: - 9.1f == Approx( 9.1000003815 ) + 9.100000381f + == + Approx( 9.10000038146972656 ) Condition.tests.cpp:: PASSED: REQUIRE( data.double_pi == Approx( 3.1415926535 ) ) with expansion: - 3.1415926535 == Approx( 3.1415926535 ) + 3.14159265350000005 + == + Approx( 3.14159265350000005 ) Condition.tests.cpp:: PASSED: REQUIRE( data.str_hello == "hello" ) @@ -4162,7 +4261,9 @@ with expansion: Condition.tests.cpp:: PASSED: REQUIRE( x == Approx( 1.3 ) ) with expansion: - 1.3 == Approx( 1.3 ) + 1.30000000000000027 + == + Approx( 1.30000000000000004 ) ------------------------------------------------------------------------------- Equals @@ -4494,22 +4595,22 @@ Matchers.tests.cpp: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 10., WithinRel( 11.1, 0.1 ) ) with expansion: - 10.0 and 11.1 are within 10% of each other + 10.0 and 11.09999999999999964 are within 10% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 10., !WithinRel( 11.2, 0.1 ) ) with expansion: - 10.0 not and 11.2 are within 10% of each other + 10.0 not and 11.19999999999999929 are within 10% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 1., !WithinRel( 0., 0.99 ) ) with expansion: - 1.0 not and 0 are within 99% of each other + 1.0 not and 0.0 are within 99% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( -0., WithinRel( 0. ) ) with expansion: - -0.0 and 0 are within 2.22045e-12% of each other + -0.0 and 0.0 are within 2.22045e-12% of each other ------------------------------------------------------------------------------- Floating point matchers: double @@ -4522,7 +4623,7 @@ Matchers.tests.cpp: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( v1, WithinRel( v2 ) ) with expansion: - 0.0 and 2.22507e-308 are within 2.22045e-12% of each other + 0.0 and 0.0 are within 2.22045e-12% of each other ------------------------------------------------------------------------------- Floating point matchers: double @@ -4544,12 +4645,12 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) ) with expansion: - 0.0 not is within 0.99 of 1.0 + 0.0 not is within 0.98999999999999999 of 1.0 Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) ) with expansion: - 0.0 not is within 0.99 of 1.0 + 0.0 not is within 0.98999999999999999 of 1.0 Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 11., !WithinAbs( 10., 0.5 ) ) @@ -4569,7 +4670,7 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( -10., WithinAbs( -9.6, 0.5 ) ) with expansion: - -10.0 is within 0.5 of -9.6 + -10.0 is within 0.5 of -9.59999999999999964 ------------------------------------------------------------------------------- Floating point matchers: double @@ -4587,8 +4688,8 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( nextafter( 1., 2. ), WithinULP( 1., 1 ) ) with expansion: - 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1. - 0000000000000002e+00]) + 1.00000000000000022 is within 1 ULPs of 1.0000000000000000e+00 ([9. + 9999999999999989e-01, 1.0000000000000002e+00]) Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0., WithinULP( nextafter( 0., 1. ), 1 ) ) @@ -4642,7 +4743,7 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) ) with expansion: - 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + 0.0001 ( is within 0.001 of 0.0 or and 0.0 are within 10% of each other ) ------------------------------------------------------------------------------- Floating point matchers: double @@ -4691,22 +4792,22 @@ Matchers.tests.cpp: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 10.f, WithinRel( 11.1f, 0.1f ) ) with expansion: - 10.0f and 11.1 are within 10% of each other + 10.0f and 11.10000038146972656 are within 10% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 10.f, !WithinRel( 11.2f, 0.1f ) ) with expansion: - 10.0f not and 11.2 are within 10% of each other + 10.0f not and 11.19999980926513672 are within 10% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 1.f, !WithinRel( 0.f, 0.99f ) ) with expansion: - 1.0f not and 0 are within 99% of each other + 1.0f not and 0.0 are within 99% of each other Matchers.tests.cpp:: PASSED: REQUIRE_THAT( -0.f, WithinRel( 0.f ) ) with expansion: - -0.0f and 0 are within 0.00119209% of each other + -0.0f and 0.0 are within 0.00119209% of each other ------------------------------------------------------------------------------- Floating point matchers: float @@ -4719,7 +4820,7 @@ Matchers.tests.cpp: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( v1, WithinRel( v2 ) ) with expansion: - 0.0f and 1.17549e-38 are within 0.00119209% of each other + 0.0f and 0.0 are within 0.00119209% of each other ------------------------------------------------------------------------------- Floating point matchers: float @@ -4741,12 +4842,12 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) ) with expansion: - 0.0f not is within 0.9900000095 of 1.0 + 0.0f not is within 0.99000000953674316 of 1.0 Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) ) with expansion: - 0.0f not is within 0.9900000095 of 1.0 + 0.0f not is within 0.99000000953674316 of 1.0 Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.f, WithinAbs( -0.f, 0 ) ) @@ -4771,7 +4872,7 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( -10.f, WithinAbs( -9.6f, 0.5f ) ) with expansion: - -10.0f is within 0.5 of -9.6000003815 + -10.0f is within 0.5 of -9.60000038146972656 ------------------------------------------------------------------------------- Floating point matchers: float @@ -4794,7 +4895,8 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) ) with expansion: - 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) + 1.000000119f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1. + 00000012e+00]) Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) ) @@ -4844,7 +4946,8 @@ with expansion: Matchers.tests.cpp:: PASSED: REQUIRE_THAT( 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) ) with expansion: - 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + 0.0001f ( is within 0.00100000004749745 of 0.0 or and 0.0 are within 10% of + each other ) ------------------------------------------------------------------------------- Floating point matchers: float @@ -4886,6 +4989,50 @@ Matchers.tests.cpp:: PASSED: with expansion: 1.0 not is NaN +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +GENERATE can combine literals and generators +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + ------------------------------------------------------------------------------- Generators -- adapters Filtering by predicate @@ -6241,7 +6388,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.9 == Approx( -0.9 ) + -0.90000000000000002 + == + Approx( -0.90000000000000002 ) with message: Current expected value is -0.9 @@ -6255,7 +6404,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.8 == Approx( -0.8 ) + -0.80000000000000004 + == + Approx( -0.80000000000000004 ) with message: Current expected value is -0.8 @@ -6269,7 +6420,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.7 == Approx( -0.7 ) + -0.70000000000000007 + == + Approx( -0.70000000000000007 ) with message: Current expected value is -0.7 @@ -6283,7 +6436,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.6 == Approx( -0.6 ) + -0.60000000000000009 + == + Approx( -0.60000000000000009 ) with message: Current expected value is -0.6 @@ -6297,7 +6452,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.5 == Approx( -0.5 ) + -0.50000000000000011 + == + Approx( -0.50000000000000011 ) with message: Current expected value is -0.5 @@ -6311,7 +6468,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.4 == Approx( -0.4 ) + -0.40000000000000013 + == + Approx( -0.40000000000000013 ) with message: Current expected value is -0.4 @@ -6325,7 +6484,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.3 == Approx( -0.3 ) + -0.30000000000000016 + == + Approx( -0.30000000000000016 ) with message: Current expected value is -0.3 @@ -6339,7 +6500,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.2 == Approx( -0.2 ) + -0.20000000000000015 + == + Approx( -0.20000000000000015 ) with message: Current expected value is -0.2 @@ -6353,7 +6516,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.1 == Approx( -0.1 ) + -0.10000000000000014 + == + Approx( -0.10000000000000014 ) with message: Current expected value is -0.1 @@ -6367,7 +6532,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.0 == Approx( -0.0 ) + -0.00000000000000014 + == + Approx( -0.00000000000000014 ) with message: Current expected value is -1.38778e-16 @@ -6381,7 +6548,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.1 == Approx( 0.1 ) + 0.09999999999999987 + == + Approx( 0.09999999999999987 ) with message: Current expected value is 0.1 @@ -6395,7 +6564,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.2 == Approx( 0.2 ) + 0.19999999999999987 + == + Approx( 0.19999999999999987 ) with message: Current expected value is 0.2 @@ -6409,7 +6580,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.3 == Approx( 0.3 ) + 0.29999999999999988 + == + Approx( 0.29999999999999988 ) with message: Current expected value is 0.3 @@ -6423,7 +6596,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.4 == Approx( 0.4 ) + 0.39999999999999991 + == + Approx( 0.39999999999999991 ) with message: Current expected value is 0.4 @@ -6437,7 +6612,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.5 == Approx( 0.5 ) + 0.49999999999999989 + == + Approx( 0.49999999999999989 ) with message: Current expected value is 0.5 @@ -6451,7 +6628,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.6 == Approx( 0.6 ) + 0.59999999999999987 + == + Approx( 0.59999999999999987 ) with message: Current expected value is 0.6 @@ -6465,7 +6644,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.7 == Approx( 0.7 ) + 0.69999999999999984 + == + Approx( 0.69999999999999984 ) with message: Current expected value is 0.7 @@ -6479,7 +6660,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.8 == Approx( 0.8 ) + 0.79999999999999982 + == + Approx( 0.79999999999999982 ) with message: Current expected value is 0.8 @@ -6493,7 +6676,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.9 == Approx( 0.9 ) + 0.8999999999999998 + == + Approx( 0.8999999999999998 ) with message: Current expected value is 0.9 @@ -6507,7 +6692,7 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx( rangeEnd ) ) with expansion: - 1.0 == Approx( 1.0 ) + 0.99999999999999978 == Approx( 1.0 ) GeneratorsImpl.tests.cpp:: PASSED: REQUIRE_FALSE( gen.next() ) @@ -6541,7 +6726,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.7 == Approx( -0.7 ) + -0.69999999999999996 + == + Approx( -0.69999999999999996 ) with message: Current expected value is -0.7 @@ -6555,7 +6742,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.4 == Approx( -0.4 ) + -0.39999999999999997 + == + Approx( -0.39999999999999997 ) with message: Current expected value is -0.4 @@ -6569,7 +6758,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.1 == Approx( -0.1 ) + -0.09999999999999998 + == + Approx( -0.09999999999999998 ) with message: Current expected value is -0.1 @@ -6583,7 +6774,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.2 == Approx( 0.2 ) + 0.20000000000000001 + == + Approx( 0.20000000000000001 ) with message: Current expected value is 0.2 @@ -6640,7 +6833,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.7 == Approx( -0.7 ) + -0.69999999999999996 + == + Approx( -0.69999999999999996 ) with message: Current expected value is -0.7 @@ -6654,7 +6849,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.4 == Approx( -0.4 ) + -0.39999999999999997 + == + Approx( -0.39999999999999997 ) with message: Current expected value is -0.4 @@ -6668,7 +6865,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - -0.1 == Approx( -0.1 ) + -0.09999999999999998 + == + Approx( -0.09999999999999998 ) with message: Current expected value is -0.1 @@ -6682,7 +6881,9 @@ with message: GeneratorsImpl.tests.cpp:: PASSED: REQUIRE( gen.get() == Approx(expected) ) with expansion: - 0.2 == Approx( 0.2 ) + 0.20000000000000001 + == + Approx( 0.20000000000000001 ) with message: Current expected value is 0.2 @@ -6881,22 +7082,30 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d >= Approx( 1.22 ) ) with expansion: - 1.23 >= Approx( 1.22 ) + 1.22999999999999998 + >= + Approx( 1.21999999999999997 ) Approx.tests.cpp:: PASSED: REQUIRE( d >= Approx( 1.23 ) ) with expansion: - 1.23 >= Approx( 1.23 ) + 1.22999999999999998 + >= + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE_FALSE( d >= Approx( 1.24 ) ) with expansion: - !(1.23 >= Approx( 1.24 )) + !(1.22999999999999998 + >= + Approx( 1.23999999999999999 )) Approx.tests.cpp:: PASSED: REQUIRE( d >= Approx( 1.24 ).epsilon(0.1) ) with expansion: - 1.23 >= Approx( 1.24 ) + 1.22999999999999998 + >= + Approx( 1.23999999999999999 ) ------------------------------------------------------------------------------- Hashers with different seed produce different hash with same test case @@ -6979,6 +7188,18 @@ with expansion: == 3422778688 (0x) +------------------------------------------------------------------------------- +INFO and UNSCOPED_INFO can stream multiple arguments +------------------------------------------------------------------------------- +Message.tests.cpp: +............................................................................... + +Message.tests.cpp:: FAILED: +explicitly with messages: + This info has multiple parts. + This unscoped info has multiple parts. + Show infos! + ------------------------------------------------------------------------------- INFO and WARN do not abort tests ------------------------------------------------------------------------------- @@ -7140,6 +7361,17 @@ with messages: current counter 10 i := 10 +------------------------------------------------------------------------------- +Incomplete AssertionHandler +------------------------------------------------------------------------------- +AssertionHandler.tests.cpp: +............................................................................... + +AssertionHandler.tests.cpp:: FAILED: + REQUIRE( Dummy ) +due to unexpected exception with message: + Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE + ------------------------------------------------------------------------------- Inequality checks that should fail ------------------------------------------------------------------------------- @@ -7154,12 +7386,16 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one != Approx( 9.1f ) ) with expansion: - 9.1f != Approx( 9.1000003815 ) + 9.100000381f + != + Approx( 9.10000038146972656 ) Condition.tests.cpp:: FAILED: CHECK( data.double_pi != Approx( 3.1415926535 ) ) with expansion: - 3.1415926535 != Approx( 3.1415926535 ) + 3.14159265350000005 + != + Approx( 3.14159265350000005 ) Condition.tests.cpp:: FAILED: CHECK( data.str_hello != "hello" ) @@ -7190,27 +7426,31 @@ with expansion: Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one != Approx( 9.11f ) ) with expansion: - 9.1f != Approx( 9.1099996567 ) + 9.100000381f + != + Approx( 9.10999965667724609 ) Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one != Approx( 9.0f ) ) with expansion: - 9.1f != Approx( 9.0 ) + 9.100000381f != Approx( 9.0 ) Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one != Approx( 1 ) ) with expansion: - 9.1f != Approx( 1.0 ) + 9.100000381f != Approx( 1.0 ) Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one != Approx( 0 ) ) with expansion: - 9.1f != Approx( 0.0 ) + 9.100000381f != Approx( 0.0 ) Condition.tests.cpp:: PASSED: REQUIRE( data.double_pi != Approx( 3.1415 ) ) with expansion: - 3.1415926535 != Approx( 3.1415 ) + 3.14159265350000005 + != + Approx( 3.14150000000000018 ) Condition.tests.cpp:: PASSED: REQUIRE( data.str_hello != "goodbye" ) @@ -7232,6 +7472,291 @@ Condition.tests.cpp:: PASSED: with expansion: 5 != 6 +------------------------------------------------------------------------------- +JsonWriter + Newly constructed JsonWriter does nothing +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "" ) +with expansion: + "" == "" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeObject will create an empty pair of braces +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "{\n}" ) +with expansion: + "{ + }" + == + "{ + }" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeObject with key will create an object to write the value +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE_THAT( stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) ) +with expansion: + "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] + }" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: + ""true": true," and contains: ""false": false," and contains: ""string": + "this is a string"," and contains: ""array": [ + 1, + 2 + ] + }" ) + +------------------------------------------------------------------------------- +JsonWriter + nesting objects +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE_THAT( stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) ) +with expansion: + "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } + }" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) + +------------------------------------------------------------------------------- +JsonWriter + Calling writeArray will create an empty pair of braces +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "[\n]" ) +with expansion: + "[ + ]" + == + "[ + ]" + +------------------------------------------------------------------------------- +JsonWriter + Calling writeArray creates array to write the values to +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" ) +with expansion: + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] + ]" + == + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] + ]" + +------------------------------------------------------------------------------- +JsonWriter + Moved from JsonObjectWriter shall not insert superfluous brace +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "{\n}" ) +with expansion: + "{ + }" + == + "{ + }" + +------------------------------------------------------------------------------- +JsonWriter + Moved from JsonArrayWriter shall not insert superfluous bracket +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "[\n]" ) +with expansion: + "[ + ]" + == + "[ + ]" + +------------------------------------------------------------------------------- +JsonWriter + Custom class shall be quoted +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( stream.str() == "\"custom\"" ) +with expansion: + ""custom"" == ""custom"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Quote in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\\"\"" ) +with expansion: + ""\""" == ""\""" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Backslash in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\\\\"" ) +with expansion: + ""\\"" == ""\\"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Forward slash in a string is **not** escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"/\"" ) +with expansion: + ""/"" == ""/"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Backspace in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\b\"" ) +with expansion: + ""\b"" == ""\b"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + Formfeed in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\f\"" ) +with expansion: + ""\f"" == ""\f"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + linefeed in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\n\"" ) +with expansion: + ""\n"" == ""\n"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + carriage return in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\r\"" ) +with expansion: + ""\r"" == ""\r"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + tab in a string is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\t\"" ) +with expansion: + ""\t"" == ""\t"" + +------------------------------------------------------------------------------- +JsonWriter escapes charaters in strings properly + combination of characters is escaped +------------------------------------------------------------------------------- +Json.tests.cpp: +............................................................................... + +Json.tests.cpp:: PASSED: + REQUIRE( sstream.str() == "\"\\\\/\\t\\r\\n\"" ) +with expansion: + ""\\/\t\r\n"" == ""\\/\t\r\n"" + ------------------------------------------------------------------------------- Lambdas in assertions ------------------------------------------------------------------------------- @@ -7252,22 +7777,30 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d <= Approx( 1.24 ) ) with expansion: - 1.23 <= Approx( 1.24 ) + 1.22999999999999998 + <= + Approx( 1.23999999999999999 ) Approx.tests.cpp:: PASSED: REQUIRE( d <= Approx( 1.23 ) ) with expansion: - 1.23 <= Approx( 1.23 ) + 1.22999999999999998 + <= + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE_FALSE( d <= Approx( 1.22 ) ) with expansion: - !(1.23 <= Approx( 1.22 )) + !(1.22999999999999998 + <= + Approx( 1.21999999999999997 )) Approx.tests.cpp:: PASSED: REQUIRE( d <= Approx( 1.22 ).epsilon(0.1) ) with expansion: - 1.23 <= Approx( 1.22 ) + 1.22999999999999998 + <= + Approx( 1.21999999999999997 ) ------------------------------------------------------------------------------- ManuallyRegistered @@ -7820,17 +8353,17 @@ with expansion: Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one < 9 ) with expansion: - 9.1f < 9 + 9.100000381f < 9 Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one > 10 ) with expansion: - 9.1f > 10 + 9.100000381f > 10 Condition.tests.cpp:: FAILED: CHECK( data.float_nine_point_one > 9.2 ) with expansion: - 9.1f > 9.2 + 9.100000381f > 9.19999999999999929 Condition.tests.cpp:: FAILED: CHECK( data.str_hello > "hello" ) @@ -7921,17 +8454,17 @@ with expansion: Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one > 9 ) with expansion: - 9.1f > 9 + 9.100000381f > 9 Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one < 10 ) with expansion: - 9.1f < 10 + 9.100000381f < 10 Condition.tests.cpp:: PASSED: REQUIRE( data.float_nine_point_one < 9.2 ) with expansion: - 9.1f < 9.2 + 9.100000381f < 9.19999999999999929 Condition.tests.cpp:: PASSED: REQUIRE( data.str_hello <= "hello" ) @@ -9287,7 +9820,9 @@ with expansion: CmdLine.tests.cpp:: PASSED: REQUIRE( config.benchmarkConfidenceInterval == Catch::Approx(0.99) ) with expansion: - 0.99 == Approx( 0.99 ) + 0.98999999999999999 + == + Approx( 0.98999999999999999 ) ------------------------------------------------------------------------------- Process can be configured on command line @@ -9730,6 +10265,129 @@ Reporter's write listings to provided stream Reporters.tests.cpp: ............................................................................... +Reporters.tests.cpp:: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists tags +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + +Reporters.tests.cpp:: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring("fakeTag"s) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + +Reporters.tests.cpp:: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists reporters +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + +Reporters.tests.cpp:: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring("fake reporter"s) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + +Reporters.tests.cpp:: PASSED: + REQUIRE_FALSE( factories.empty() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream + JSON reporter lists tests +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + +Reporters.tests.cpp:: PASSED: + REQUIRE_THAT( listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) ) +with expansion: + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) +with message: + Tested reporter: JSON + +------------------------------------------------------------------------------- +Reporter's write listings to provided stream +------------------------------------------------------------------------------- +Reporters.tests.cpp: +............................................................................... + Reporters.tests.cpp:: PASSED: REQUIRE_FALSE( factories.empty() ) with expansion: @@ -10382,42 +11040,58 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d == Approx( 1.23 ) ) with expansion: - 1.23 == Approx( 1.23 ) + 1.22999999999999998 + == + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE( d != Approx( 1.22 ) ) with expansion: - 1.23 != Approx( 1.22 ) + 1.22999999999999998 + != + Approx( 1.21999999999999997 ) Approx.tests.cpp:: PASSED: REQUIRE( d != Approx( 1.24 ) ) with expansion: - 1.23 != Approx( 1.24 ) + 1.22999999999999998 + != + Approx( 1.23999999999999999 ) Approx.tests.cpp:: PASSED: REQUIRE( d == 1.23_a ) with expansion: - 1.23 == Approx( 1.23 ) + 1.22999999999999998 + == + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE( d != 1.22_a ) with expansion: - 1.23 != Approx( 1.22 ) + 1.22999999999999998 + != + Approx( 1.21999999999999997 ) Approx.tests.cpp:: PASSED: REQUIRE( Approx( d ) == 1.23 ) with expansion: - Approx( 1.23 ) == 1.23 + Approx( 1.22999999999999998 ) + == + 1.22999999999999998 Approx.tests.cpp:: PASSED: REQUIRE( Approx( d ) != 1.22 ) with expansion: - Approx( 1.23 ) != 1.22 + Approx( 1.22999999999999998 ) + != + 1.21999999999999997 Approx.tests.cpp:: PASSED: REQUIRE( Approx( d ) != 1.24 ) with expansion: - Approx( 1.23 ) != 1.24 + Approx( 1.22999999999999998 ) + != + 1.23999999999999999 ------------------------------------------------------------------------------- Standard output from all sections is reported @@ -11192,9 +11866,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_default_constructible::value ) with expansion: - 1 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside non-copyable and non- @@ -11204,9 +11878,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_default_constructible::value ) with expansion: - 4 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside non-default-constructible @@ -11216,9 +11890,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_trivially_copyable::value ) with expansion: - 1 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside non-default-constructible @@ -11228,9 +11902,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_trivially_copyable::value ) with expansion: - 4 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside std::tuple - MyTypes - 0 @@ -11239,9 +11913,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_arithmetic::value ) with expansion: - 4 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside std::tuple - MyTypes - 1 @@ -11250,9 +11924,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_arithmetic::value ) with expansion: - 1 > 0 + true ------------------------------------------------------------------------------- Template test case with test types specified inside std::tuple - MyTypes - 2 @@ -11261,9 +11935,9 @@ Misc.tests.cpp: ............................................................................... Misc.tests.cpp:: PASSED: - REQUIRE( sizeof(TestType) > 0 ) + REQUIRE( std::is_arithmetic::value ) with expansion: - 4 > 0 + true ------------------------------------------------------------------------------- TemplateTest: vectors can be sized and resized - float @@ -12514,6 +13188,34 @@ Misc.tests.cpp:: FAILED - but was ok: Misc.tests.cpp:: FAILED: +------------------------------------------------------------------------------- +Testing checked-if 4 +------------------------------------------------------------------------------- +Misc.tests.cpp: +............................................................................... + +Misc.tests.cpp:: PASSED: + CHECKED_ELSE( true ) + +Misc.tests.cpp:: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + +------------------------------------------------------------------------------- +Testing checked-if 5 +------------------------------------------------------------------------------- +Misc.tests.cpp: +............................................................................... + +Misc.tests.cpp:: FAILED - but was ok: + CHECKED_ELSE( false ) + +Misc.tests.cpp:: FAILED: + {Unknown expression after the reported line} +due to unexpected exception with message: + Uncaught exception should fail! + ------------------------------------------------------------------------------- The NO_FAIL macro reports a failure but does not fail the test ------------------------------------------------------------------------------- @@ -13290,7 +13992,7 @@ Exception.tests.cpp: Exception.tests.cpp:: FAILED: due to unexpected exception with message: - 3.14 + 3.14000000000000012 ------------------------------------------------------------------------------- Upcasting special member functions @@ -13453,7 +14155,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of AllTrue range matcher Basic usage - One false evalutes to false + One false evaluates to false ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -13492,7 +14194,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of AllTrue range matcher Contained type is convertible to bool - One false evalutes to false + One false evaluates to false ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -13728,7 +14430,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of AnyTrue range matcher Basic usage - One true evalutes to true + One true evaluates to true ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -13767,7 +14469,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of AnyTrue range matcher Contained type is convertible to bool - One true evalutes to true + One true evaluates to true ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -14003,7 +14705,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of NoneTrue range matcher Basic usage - One true evalutes to false + One true evaluates to false ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -14042,7 +14744,7 @@ with expansion: ------------------------------------------------------------------------------- Usage of NoneTrue range matcher Contained type is convertible to bool - One true evalutes to false + One true evaluates to false ------------------------------------------------------------------------------- MatchersRanges.tests.cpp: ............................................................................... @@ -14534,42 +15236,54 @@ Approx.tests.cpp: Approx.tests.cpp:: PASSED: REQUIRE( d == approx( 1.23 ) ) with expansion: - 1.23 == Approx( 1.23 ) + 1.22999999999999998 + == + Approx( 1.22999999999999998 ) Approx.tests.cpp:: PASSED: REQUIRE( d == approx( 1.22 ) ) with expansion: - 1.23 == Approx( 1.22 ) + 1.22999999999999998 + == + Approx( 1.21999999999999997 ) Approx.tests.cpp:: PASSED: REQUIRE( d == approx( 1.24 ) ) with expansion: - 1.23 == Approx( 1.24 ) + 1.22999999999999998 + == + Approx( 1.23999999999999999 ) Approx.tests.cpp:: PASSED: REQUIRE( d != approx( 1.25 ) ) with expansion: - 1.23 != Approx( 1.25 ) + 1.22999999999999998 != Approx( 1.25 ) Approx.tests.cpp:: PASSED: REQUIRE( approx( d ) == 1.23 ) with expansion: - Approx( 1.23 ) == 1.23 + Approx( 1.22999999999999998 ) + == + 1.22999999999999998 Approx.tests.cpp:: PASSED: REQUIRE( approx( d ) == 1.22 ) with expansion: - Approx( 1.23 ) == 1.22 + Approx( 1.22999999999999998 ) + == + 1.21999999999999997 Approx.tests.cpp:: PASSED: REQUIRE( approx( d ) == 1.24 ) with expansion: - Approx( 1.23 ) == 1.24 + Approx( 1.22999999999999998 ) + == + 1.23999999999999999 Approx.tests.cpp:: PASSED: REQUIRE( approx( d ) != 1.25 ) with expansion: - Approx( 1.23 ) != 1.25 + Approx( 1.22999999999999998 ) != 1.25 ------------------------------------------------------------------------------- Variadic macros @@ -15762,17 +16476,23 @@ InternalBenchmark.tests.cpp: InternalBenchmark.tests.cpp:: PASSED: CHECK( erfc_inv(1.103560) == Approx(-0.09203687623843015) ) with expansion: - -0.0920368762 == Approx( -0.0920368762 ) + -0.09203687623843014 + == + Approx( -0.09203687623843015 ) InternalBenchmark.tests.cpp:: PASSED: CHECK( erfc_inv(1.067400) == Approx(-0.05980291115763361) ) with expansion: - -0.0598029112 == Approx( -0.0598029112 ) + -0.05980291115763361 + == + Approx( -0.05980291115763361 ) InternalBenchmark.tests.cpp:: PASSED: CHECK( erfc_inv(0.050000) == Approx(1.38590382434967796) ) with expansion: - 1.3859038243 == Approx( 1.3859038243 ) + 1.38590382434967774 + == + Approx( 1.38590382434967796 ) ------------------------------------------------------------------------------- estimate_clock_resolution @@ -16428,37 +17148,6 @@ Tricky.tests.cpp:: PASSED: with expansion: {?} == {?} -------------------------------------------------------------------------------- -normal_cdf -------------------------------------------------------------------------------- -InternalBenchmark.tests.cpp: -............................................................................... - -InternalBenchmark.tests.cpp:: PASSED: - CHECK( normal_cdf(0.000000) == Approx(0.50000000000000000) ) -with expansion: - 0.5 == Approx( 0.5 ) - -InternalBenchmark.tests.cpp:: PASSED: - CHECK( normal_cdf(1.000000) == Approx(0.84134474606854293) ) -with expansion: - 0.8413447461 == Approx( 0.8413447461 ) - -InternalBenchmark.tests.cpp:: PASSED: - CHECK( normal_cdf(-1.000000) == Approx(0.15865525393145705) ) -with expansion: - 0.1586552539 == Approx( 0.1586552539 ) - -InternalBenchmark.tests.cpp:: PASSED: - CHECK( normal_cdf(2.809729) == Approx(0.99752083845315409) ) -with expansion: - 0.9975208385 == Approx( 0.9975208385 ) - -InternalBenchmark.tests.cpp:: PASSED: - CHECK( normal_cdf(-1.352570) == Approx(0.08809652095066035) ) -with expansion: - 0.088096521 == Approx( 0.088096521 ) - ------------------------------------------------------------------------------- normal_quantile ------------------------------------------------------------------------------- @@ -16468,17 +17157,23 @@ InternalBenchmark.tests.cpp: InternalBenchmark.tests.cpp:: PASSED: CHECK( normal_quantile(0.551780) == Approx(0.13015979861484198) ) with expansion: - 0.1301597986 == Approx( 0.1301597986 ) + 0.13015979861484195 + == + Approx( 0.13015979861484198 ) InternalBenchmark.tests.cpp:: PASSED: CHECK( normal_quantile(0.533700) == Approx(0.08457408802851875) ) with expansion: - 0.084574088 == Approx( 0.084574088 ) + 0.08457408802851875 + == + Approx( 0.08457408802851875 ) InternalBenchmark.tests.cpp:: PASSED: CHECK( normal_quantile(0.025000) == Approx(-1.95996398454005449) ) with expansion: - -1.9599639845 == Approx( -1.9599639845 ) + -1.95996398454005405 + == + Approx( -1.95996398454005449 ) ------------------------------------------------------------------------------- not allowed @@ -16794,6 +17489,42 @@ StringManip.tests.cpp:: PASSED: with expansion: "abcdefcg" == "abcdefcg" +------------------------------------------------------------------------------- +replaceInPlace + no replace in already-replaced string + lengthening +------------------------------------------------------------------------------- +StringManip.tests.cpp: +............................................................................... + +StringManip.tests.cpp:: PASSED: + CHECK( Catch::replaceInPlace(letters, "c", "cc") ) +with expansion: + true + +StringManip.tests.cpp:: PASSED: + CHECK( letters == "abccdefccg" ) +with expansion: + "abccdefccg" == "abccdefccg" + +------------------------------------------------------------------------------- +replaceInPlace + no replace in already-replaced string + shortening +------------------------------------------------------------------------------- +StringManip.tests.cpp: +............................................................................... + +StringManip.tests.cpp:: PASSED: + CHECK( Catch::replaceInPlace(s, "--", "-") ) +with expansion: + true + +StringManip.tests.cpp:: PASSED: + CHECK( s == "--" ) +with expansion: + "--" == "--" + ------------------------------------------------------------------------------- replaceInPlace escape ' @@ -17661,14 +18392,14 @@ ToStringTuple.tests.cpp: ............................................................................... ToStringTuple.tests.cpp:: PASSED: - CHECK( "1.2f" == ::Catch::Detail::stringify(float(1.2)) ) + CHECK( "1.5f" == ::Catch::Detail::stringify(float(1.5)) ) with expansion: - "1.2f" == "1.2f" + "1.5f" == "1.5f" ToStringTuple.tests.cpp:: PASSED: - CHECK( "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) ) + CHECK( "{ 1.5f, 0 }" == ::Catch::Detail::stringify(type{1.5f,0}) ) with expansion: - "{ 1.2f, 0 }" == "{ 1.2f, 0 }" + "{ 1.5f, 0 }" == "{ 1.5f, 0 }" ------------------------------------------------------------------------------- tuple @@ -17701,11 +18432,11 @@ ToStringTuple.tests.cpp: ............................................................................... ToStringTuple.tests.cpp:: PASSED: - CHECK( "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) ) + CHECK( "{ { 42 }, { }, 1.5f }" == ::Catch::Detail::stringify(value) ) with expansion: - "{ { 42 }, { }, 1.2f }" + "{ { 42 }, { }, 1.5f }" == - "{ { 42 }, { }, 1.2f }" + "{ { 42 }, { }, 1.5f }" ------------------------------------------------------------------------------- uniform samples @@ -17731,7 +18462,23 @@ with expansion: InternalBenchmark.tests.cpp:: PASSED: CHECK( e.confidence_interval == 0.95 ) with expansion: - 0.95 == 0.95 + 0.94999999999999996 == 0.94999999999999996 + +------------------------------------------------------------------------------- +uniform_integer_distribution can return the bounds +------------------------------------------------------------------------------- +RandomNumberGeneration.tests.cpp: +............................................................................... + +RandomNumberGeneration.tests.cpp:: PASSED: + REQUIRE( dist.a() == -10 ) +with expansion: + -10 == -10 + +RandomNumberGeneration.tests.cpp:: PASSED: + REQUIRE( dist.b() == 10 ) +with expansion: + 10 == 10 ------------------------------------------------------------------------------- unique_ptr reimplementation: basic functionality @@ -18220,6 +18967,6 @@ Misc.tests.cpp: Misc.tests.cpp:: PASSED: =============================================================================== -test cases: 409 | 309 passed | 84 failed | 5 skipped | 11 failed as expected -assertions: 2226 | 2049 passed | 145 failed | 32 failed as expected +test cases: 419 | 313 passed | 86 failed | 6 skipped | 14 failed as expected +assertions: 2265 | 2083 passed | 147 failed | 35 failed as expected diff --git a/src/external/catch2/tests/SelfTest/Baselines/junit.sw.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/junit.sw.approved.txt index 25129349..33f207c5 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/junit.sw.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ - + @@ -12,6 +12,7 @@ + @@ -30,10 +31,12 @@ Nor would this + + @@ -56,6 +59,7 @@ failure to init at Generators.tests.cpp:
+ @@ -89,6 +93,7 @@ at Misc.tests.cpp:
+ @@ -147,6 +152,7 @@ at Condition.tests.cpp:
+ @@ -313,6 +319,19 @@ at Class.tests.cpp: + + + +FAILED: + REQUIRE( m_a == 0 ) +with expansion: + 1 == 0 +at Class.tests.cpp: + + + + + @@ -329,6 +348,7 @@ to infinity and beyond at Misc.tests.cpp: + @@ -347,6 +367,7 @@ at Tricky.tests.cpp: + @@ -364,6 +385,7 @@ at Exception.tests.cpp: + @@ -371,30 +393,39 @@ at Exception.tests.cpp: + + + + + + + + + @@ -412,8 +443,10 @@ at Exception.tests.cpp: + + @@ -433,6 +466,7 @@ with expansion: at Matchers.tests.cpp: + @@ -462,8 +496,14 @@ at Exception.tests.cpp: + + +SKIPPED +This generator is empty +at Skip.tests.cpp: + + - FAILED: @@ -511,35 +551,39 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 9.11f ) ) with expansion: - 9.1f == Approx( 9.1099996567 ) + 9.100000381f + == + Approx( 9.10999965667724609 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 9.0f ) ) with expansion: - 9.1f == Approx( 9.0 ) + 9.100000381f == Approx( 9.0 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 1 ) ) with expansion: - 9.1f == Approx( 1.0 ) + 9.100000381f == Approx( 1.0 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 0 ) ) with expansion: - 9.1f == Approx( 0.0 ) + 9.100000381f == Approx( 0.0 ) at Condition.tests.cpp: FAILED: CHECK( data.double_pi == Approx( 3.1415 ) ) with expansion: - 3.1415926535 == Approx( 3.1415 ) + 3.14159265350000005 + == + Approx( 3.14150000000000018 ) at Condition.tests.cpp: @@ -574,7 +618,9 @@ at Condition.tests.cpp: FAILED: CHECK( x == Approx( 1.301 ) ) with expansion: - 1.3 == Approx( 1.301 ) + 1.30000000000000027 + == + Approx( 1.30099999999999993 ) at Condition.tests.cpp: @@ -643,6 +689,7 @@ at Matchers.tests.cpp: + @@ -688,6 +735,7 @@ at Message.tests.cpp: + @@ -695,6 +743,7 @@ at Message.tests.cpp: + @@ -702,50 +751,78 @@ at Message.tests.cpp: + + + + + + + + + + + + + + + + + + + + + +FAILED: +Show infos! +This info has multiple parts. +This unscoped info has multiple parts. +at Message.tests.cpp: + + @@ -790,6 +867,15 @@ i := 10 at Message.tests.cpp: + + + +FAILED: + REQUIRE( Dummy ) +Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE +at AssertionHandler.tests.cpp: + + @@ -803,14 +889,18 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one != Approx( 9.1f ) ) with expansion: - 9.1f != Approx( 9.1000003815 ) + 9.100000381f + != + Approx( 9.10000038146972656 ) at Condition.tests.cpp: FAILED: CHECK( data.double_pi != Approx( 3.1415926535 ) ) with expansion: - 3.1415926535 != Approx( 3.1415926535 ) + 3.14159265350000005 + != + Approx( 3.14159265350000005 ) at Condition.tests.cpp: @@ -829,6 +919,26 @@ at Condition.tests.cpp: + + + + + + + + + + + + + + + + + + + + @@ -855,6 +965,9 @@ with expansion: at Matchers.tests.cpp: + + + @@ -862,6 +975,9 @@ FAILED: at Condition.tests.cpp: + + + @@ -869,6 +985,9 @@ FAILED: at Condition.tests.cpp: + + + @@ -876,6 +995,9 @@ FAILED: at Condition.tests.cpp: + + + @@ -968,21 +1090,21 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one < 9 ) with expansion: - 9.1f < 9 + 9.100000381f < 9 at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one > 10 ) with expansion: - 9.1f > 10 + 9.100000381f > 10 at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one > 9.2 ) with expansion: - 9.1f > 9.2 + 9.100000381f > 9.19999999999999929 at Condition.tests.cpp: @@ -1043,6 +1165,7 @@ at Condition.tests.cpp: + @@ -1060,9 +1183,11 @@ at Message.tests.cpp: + + @@ -1070,44 +1195,58 @@ at Message.tests.cpp: + + + + + + + + + + + + + + @@ -1166,6 +1305,9 @@ at Matchers.tests.cpp: + + + @@ -1184,14 +1326,27 @@ at Matchers.tests.cpp: + + + + + + + + + + + + + @@ -1203,6 +1358,8 @@ A string sent to stderr via clog + + Message from section one @@ -1226,15 +1383,18 @@ with expansion: at Matchers.tests.cpp: + + + @@ -1243,13 +1403,16 @@ at Matchers.tests.cpp: + + + @@ -1274,6 +1437,7 @@ with expansion: at Misc.tests.cpp: + @@ -1354,7 +1518,26 @@ FAILED: at Misc.tests.cpp: + + + +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp: + + + + + +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp: + + + @@ -1378,91 +1561,126 @@ at Exception.tests.cpp: + + + + FAILED: -3.14 +3.14000000000000012 at Exception.tests.cpp: + + + + + - + + - + + + + + + - + + - + + + + + + - + + - + + + + + + + + + + + + + + FAILED: @@ -1481,11 +1699,13 @@ with expansion: at Matchers.tests.cpp: + + FAILED: @@ -1617,10 +1837,12 @@ unexpected exception at Exception.tests.cpp: + + @@ -1638,6 +1860,7 @@ at Skip.tests.cpp: + @@ -1661,6 +1884,7 @@ with expansion: at Misc.tests.cpp: + @@ -1685,6 +1909,8 @@ at Skip.tests.cpp: + + @@ -1731,6 +1957,8 @@ FAILED: at Skip.tests.cpp: + + @@ -1746,7 +1974,10 @@ previous unscoped info SHOULD not be seen at Message.tests.cpp: + + + FAILED: @@ -1824,12 +2055,15 @@ at Misc.tests.cpp: + + + FAILED: @@ -1839,10 +2073,15 @@ with expansion: at Misc.tests.cpp: + + + + + SKIPPED @@ -1858,7 +2097,6 @@ b1! - @@ -1872,6 +2110,7 @@ at Message.tests.cpp: + @@ -1894,19 +2133,26 @@ this SHOULD be seen only ONCE at Message.tests.cpp: + + + + + + + @@ -1962,11 +2208,13 @@ at Message.tests.cpp: + + @@ -2011,6 +2259,8 @@ at Exception.tests.cpp: + + @@ -2032,6 +2282,7 @@ at Exception.tests.cpp: + diff --git a/src/external/catch2/tests/SelfTest/Baselines/junit.sw.multi.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/junit.sw.multi.approved.txt index 6220d8e2..876a4db4 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/junit.sw.multi.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/junit.sw.multi.approved.txt @@ -1,6 +1,6 @@ - + @@ -11,6 +11,7 @@ + @@ -29,10 +30,12 @@ Nor would this + + @@ -55,6 +58,7 @@ failure to init at Generators.tests.cpp: + @@ -88,6 +92,7 @@ at Misc.tests.cpp: + @@ -146,6 +151,7 @@ at Condition.tests.cpp: + @@ -312,6 +318,19 @@ at Class.tests.cpp: + + + +FAILED: + REQUIRE( m_a == 0 ) +with expansion: + 1 == 0 +at Class.tests.cpp: + + + + + @@ -328,6 +347,7 @@ to infinity and beyond at Misc.tests.cpp: + @@ -346,6 +366,7 @@ at Tricky.tests.cpp: + @@ -363,6 +384,7 @@ at Exception.tests.cpp: + @@ -370,30 +392,39 @@ at Exception.tests.cpp: + + + + + + + + + @@ -411,8 +442,10 @@ at Exception.tests.cpp: + + @@ -432,6 +465,7 @@ with expansion: at Matchers.tests.cpp: + @@ -461,8 +495,14 @@ at Exception.tests.cpp: + + +SKIPPED +This generator is empty +at Skip.tests.cpp: + + - FAILED: @@ -510,35 +550,39 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 9.11f ) ) with expansion: - 9.1f == Approx( 9.1099996567 ) + 9.100000381f + == + Approx( 9.10999965667724609 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 9.0f ) ) with expansion: - 9.1f == Approx( 9.0 ) + 9.100000381f == Approx( 9.0 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 1 ) ) with expansion: - 9.1f == Approx( 1.0 ) + 9.100000381f == Approx( 1.0 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 0 ) ) with expansion: - 9.1f == Approx( 0.0 ) + 9.100000381f == Approx( 0.0 ) at Condition.tests.cpp: FAILED: CHECK( data.double_pi == Approx( 3.1415 ) ) with expansion: - 3.1415926535 == Approx( 3.1415 ) + 3.14159265350000005 + == + Approx( 3.14150000000000018 ) at Condition.tests.cpp: @@ -573,7 +617,9 @@ at Condition.tests.cpp: FAILED: CHECK( x == Approx( 1.301 ) ) with expansion: - 1.3 == Approx( 1.301 ) + 1.30000000000000027 + == + Approx( 1.30099999999999993 ) at Condition.tests.cpp: @@ -642,6 +688,7 @@ at Matchers.tests.cpp: + @@ -687,6 +734,7 @@ at Message.tests.cpp: + @@ -694,6 +742,7 @@ at Message.tests.cpp: + @@ -701,50 +750,78 @@ at Message.tests.cpp: + + + + + + + + + + + + + + + + + + + + + +FAILED: +Show infos! +This info has multiple parts. +This unscoped info has multiple parts. +at Message.tests.cpp: + + @@ -789,6 +866,15 @@ i := 10 at Message.tests.cpp: + + + +FAILED: + REQUIRE( Dummy ) +Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE +at AssertionHandler.tests.cpp: + + @@ -802,14 +888,18 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one != Approx( 9.1f ) ) with expansion: - 9.1f != Approx( 9.1000003815 ) + 9.100000381f + != + Approx( 9.10000038146972656 ) at Condition.tests.cpp: FAILED: CHECK( data.double_pi != Approx( 3.1415926535 ) ) with expansion: - 3.1415926535 != Approx( 3.1415926535 ) + 3.14159265350000005 + != + Approx( 3.14159265350000005 ) at Condition.tests.cpp: @@ -828,6 +918,26 @@ at Condition.tests.cpp: + + + + + + + + + + + + + + + + + + + + @@ -854,6 +964,9 @@ with expansion: at Matchers.tests.cpp: + + + @@ -861,6 +974,9 @@ FAILED: at Condition.tests.cpp: + + + @@ -868,6 +984,9 @@ FAILED: at Condition.tests.cpp: + + + @@ -875,6 +994,9 @@ FAILED: at Condition.tests.cpp: + + + @@ -967,21 +1089,21 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one < 9 ) with expansion: - 9.1f < 9 + 9.100000381f < 9 at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one > 10 ) with expansion: - 9.1f > 10 + 9.100000381f > 10 at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one > 9.2 ) with expansion: - 9.1f > 9.2 + 9.100000381f > 9.19999999999999929 at Condition.tests.cpp: @@ -1042,6 +1164,7 @@ at Condition.tests.cpp: + @@ -1059,9 +1182,11 @@ at Message.tests.cpp: + + @@ -1069,44 +1194,58 @@ at Message.tests.cpp: + + + + + + + + + + + + + + @@ -1165,6 +1304,9 @@ at Matchers.tests.cpp: + + + @@ -1183,14 +1325,27 @@ at Matchers.tests.cpp: + + + + + + + + + + + + + @@ -1202,6 +1357,8 @@ A string sent to stderr via clog + + Message from section one @@ -1225,15 +1382,18 @@ with expansion: at Matchers.tests.cpp: + + + @@ -1242,13 +1402,16 @@ at Matchers.tests.cpp: + + + @@ -1273,6 +1436,7 @@ with expansion: at Misc.tests.cpp: + @@ -1353,7 +1517,26 @@ FAILED: at Misc.tests.cpp: + + + +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp: + + + + + +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp: + + + @@ -1377,91 +1560,126 @@ at Exception.tests.cpp: + + + + FAILED: -3.14 +3.14000000000000012 at Exception.tests.cpp: + + + + + - + + - + + + + + + - + + - + + + + + + - + + - + + + + + + + + + + + + + + FAILED: @@ -1480,11 +1698,13 @@ with expansion: at Matchers.tests.cpp: + + FAILED: @@ -1616,10 +1836,12 @@ unexpected exception at Exception.tests.cpp: + + @@ -1637,6 +1859,7 @@ at Skip.tests.cpp: + @@ -1660,6 +1883,7 @@ with expansion: at Misc.tests.cpp: + @@ -1684,6 +1908,8 @@ at Skip.tests.cpp: + + @@ -1730,6 +1956,8 @@ FAILED: at Skip.tests.cpp: + + @@ -1745,7 +1973,10 @@ previous unscoped info SHOULD not be seen at Message.tests.cpp: + + + FAILED: @@ -1823,12 +2054,15 @@ at Misc.tests.cpp: + + + FAILED: @@ -1838,10 +2072,15 @@ with expansion: at Misc.tests.cpp: + + + + + SKIPPED @@ -1857,7 +2096,6 @@ b1! - @@ -1871,6 +2109,7 @@ at Message.tests.cpp: + @@ -1893,19 +2132,26 @@ this SHOULD be seen only ONCE at Message.tests.cpp: + + + + + + + @@ -1961,11 +2207,13 @@ at Message.tests.cpp: + + @@ -2010,6 +2258,8 @@ at Exception.tests.cpp: + + @@ -2031,6 +2281,7 @@ at Exception.tests.cpp: + diff --git a/src/external/catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt index a4e08bd2..18397240 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt @@ -2,53 +2,79 @@ + + + +FAILED: + REQUIRE( Dummy ) +Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE +at AssertionHandler.tests.cpp: + + + + + + + + + + + + + + + + + + + @@ -57,13 +83,16 @@ + + + @@ -72,35 +101,49 @@ + + + + + + + + + + + + + + @@ -111,7 +154,6 @@ - @@ -120,11 +162,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -147,10 +213,12 @@ + + @@ -168,6 +236,9 @@ + + + @@ -184,6 +255,7 @@ + @@ -197,11 +269,13 @@ + + @@ -210,30 +284,37 @@ + + + + + + + - + @@ -245,6 +326,7 @@ + @@ -252,6 +334,7 @@ + @@ -265,16 +348,20 @@ + + + + @@ -285,6 +372,7 @@ + @@ -314,14 +402,27 @@ + + + + + + + + + + + + + @@ -485,6 +586,19 @@ at Class.tests.cpp: + + + +FAILED: + REQUIRE( m_a == 0 ) +with expansion: + 1 == 0 +at Class.tests.cpp: + + + + + @@ -494,6 +608,7 @@ at Class.tests.cpp: + @@ -587,35 +702,39 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 9.11f ) ) with expansion: - 9.1f == Approx( 9.1099996567 ) + 9.100000381f +== +Approx( 9.10999965667724609 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 9.0f ) ) with expansion: - 9.1f == Approx( 9.0 ) + 9.100000381f == Approx( 9.0 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 1 ) ) with expansion: - 9.1f == Approx( 1.0 ) + 9.100000381f == Approx( 1.0 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 0 ) ) with expansion: - 9.1f == Approx( 0.0 ) + 9.100000381f == Approx( 0.0 ) at Condition.tests.cpp: FAILED: CHECK( data.double_pi == Approx( 3.1415 ) ) with expansion: - 3.1415926535 == Approx( 3.1415 ) + 3.14159265350000005 +== +Approx( 3.14150000000000018 ) at Condition.tests.cpp: @@ -650,7 +769,9 @@ at Condition.tests.cpp: FAILED: CHECK( x == Approx( 1.301 ) ) with expansion: - 1.3 == Approx( 1.301 ) + 1.30000000000000027 +== +Approx( 1.30099999999999993 ) at Condition.tests.cpp: @@ -667,14 +788,18 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one != Approx( 9.1f ) ) with expansion: - 9.1f != Approx( 9.1000003815 ) + 9.100000381f +!= +Approx( 9.10000038146972656 ) at Condition.tests.cpp: FAILED: CHECK( data.double_pi != Approx( 3.1415926535 ) ) with expansion: - 3.1415926535 != Approx( 3.1415926535 ) + 3.14159265350000005 +!= +Approx( 3.14159265350000005 ) at Condition.tests.cpp: @@ -693,24 +818,28 @@ at Condition.tests.cpp: + FAILED: at Condition.tests.cpp: + FAILED: at Condition.tests.cpp: + FAILED: at Condition.tests.cpp: + FAILED: @@ -778,21 +907,21 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one < 9 ) with expansion: - 9.1f < 9 + 9.100000381f < 9 at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one > 10 ) with expansion: - 9.1f > 10 + 9.100000381f > 10 at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one > 9.2 ) with expansion: - 9.1f > 9.2 + 9.100000381f > 9.19999999999999929 at Condition.tests.cpp: @@ -877,6 +1006,7 @@ at Decomposition.tests.cpp: + FAILED: @@ -926,6 +1056,7 @@ custom std exception at Exception.tests.cpp: + @@ -974,7 +1105,7 @@ at Exception.tests.cpp: FAILED: -3.14 +3.14000000000000012 at Exception.tests.cpp: @@ -1017,6 +1148,7 @@ unexpected exception at Exception.tests.cpp: + FAILED: @@ -1036,20 +1168,27 @@ at Generators.tests.cpp: + + + + + + + @@ -1059,6 +1198,7 @@ at Generators.tests.cpp: + @@ -1068,8 +1208,10 @@ at Generators.tests.cpp: + + @@ -1166,6 +1308,7 @@ at Matchers.tests.cpp: + @@ -1173,6 +1316,7 @@ at Matchers.tests.cpp: + @@ -1245,10 +1389,13 @@ at Matchers.tests.cpp: + + + FAILED: @@ -1267,11 +1414,13 @@ with expansion: at Matchers.tests.cpp: + + FAILED: @@ -1366,82 +1515,114 @@ at Matchers.tests.cpp: + + + + + + + + + + - + + - + + + + + + - + + - + + + + + + - + + - + + + + + + + + + + @@ -1468,6 +1649,15 @@ at Message.tests.cpp: FAILED: This is a failure +at Message.tests.cpp: + + + + +FAILED: +Show infos! +This info has multiple parts. +This unscoped info has multiple parts. at Message.tests.cpp: @@ -1531,6 +1721,8 @@ at Message.tests.cpp: + + @@ -1547,6 +1739,8 @@ previous unscoped info SHOULD not be seen at Message.tests.cpp: + + @@ -1619,6 +1813,7 @@ with expansion: at Misc.tests.cpp: + @@ -1639,7 +1834,9 @@ to infinity and beyond at Misc.tests.cpp: + + @@ -1728,6 +1925,22 @@ at Misc.tests.cpp: FAILED: +at Misc.tests.cpp: + + + + +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp: + + + + +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! at Misc.tests.cpp: @@ -1752,10 +1965,14 @@ with expansion: at Misc.tests.cpp: + + + + FAILED: @@ -1832,6 +2049,8 @@ Testing if fib[7] (21) is even at Misc.tests.cpp: + + FAILED: @@ -1841,14 +2060,18 @@ with expansion: at Misc.tests.cpp: + + + + FAILED: @@ -1867,10 +2090,18 @@ at Misc.tests.cpp: + + + +SKIPPED +This generator is empty +at Skip.tests.cpp: + + SKIPPED @@ -1930,6 +2161,9 @@ FAILED: at Skip.tests.cpp: + + + SKIPPED @@ -1937,6 +2171,7 @@ at Skip.tests.cpp: + @@ -1965,20 +2200,26 @@ at Skip.tests.cpp: + + + + + + @@ -2023,6 +2264,7 @@ FAILED: at Tricky.tests.cpp: + @@ -2058,6 +2300,7 @@ at Tricky.tests.cpp: + @@ -2066,6 +2309,7 @@ at Tricky.tests.cpp: + diff --git a/src/external/catch2/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt index c00defae..77348788 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt @@ -1,53 +1,79 @@ + + + +FAILED: + REQUIRE( Dummy ) +Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE +at AssertionHandler.tests.cpp: + + + + + + + + + + + + + + + + + + + @@ -56,13 +82,16 @@ + + + @@ -71,35 +100,49 @@ + + + + + + + + + + + + + + @@ -110,7 +153,6 @@ - @@ -119,11 +161,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -146,10 +212,12 @@ + + @@ -167,6 +235,9 @@ + + + @@ -183,6 +254,7 @@ + @@ -196,11 +268,13 @@ + + @@ -209,30 +283,37 @@ + + + + + + + - + @@ -244,6 +325,7 @@ + @@ -251,6 +333,7 @@ + @@ -264,16 +347,20 @@ + + + + @@ -284,6 +371,7 @@ + @@ -313,14 +401,27 @@ + + + + + + + + + + + + + @@ -484,6 +585,19 @@ at Class.tests.cpp: + + + +FAILED: + REQUIRE( m_a == 0 ) +with expansion: + 1 == 0 +at Class.tests.cpp: + + + + + @@ -493,6 +607,7 @@ at Class.tests.cpp: + @@ -586,35 +701,39 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 9.11f ) ) with expansion: - 9.1f == Approx( 9.1099996567 ) + 9.100000381f +== +Approx( 9.10999965667724609 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 9.0f ) ) with expansion: - 9.1f == Approx( 9.0 ) + 9.100000381f == Approx( 9.0 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 1 ) ) with expansion: - 9.1f == Approx( 1.0 ) + 9.100000381f == Approx( 1.0 ) at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one == Approx( 0 ) ) with expansion: - 9.1f == Approx( 0.0 ) + 9.100000381f == Approx( 0.0 ) at Condition.tests.cpp: FAILED: CHECK( data.double_pi == Approx( 3.1415 ) ) with expansion: - 3.1415926535 == Approx( 3.1415 ) + 3.14159265350000005 +== +Approx( 3.14150000000000018 ) at Condition.tests.cpp: @@ -649,7 +768,9 @@ at Condition.tests.cpp: FAILED: CHECK( x == Approx( 1.301 ) ) with expansion: - 1.3 == Approx( 1.301 ) + 1.30000000000000027 +== +Approx( 1.30099999999999993 ) at Condition.tests.cpp: @@ -666,14 +787,18 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one != Approx( 9.1f ) ) with expansion: - 9.1f != Approx( 9.1000003815 ) + 9.100000381f +!= +Approx( 9.10000038146972656 ) at Condition.tests.cpp: FAILED: CHECK( data.double_pi != Approx( 3.1415926535 ) ) with expansion: - 3.1415926535 != Approx( 3.1415926535 ) + 3.14159265350000005 +!= +Approx( 3.14159265350000005 ) at Condition.tests.cpp: @@ -692,24 +817,28 @@ at Condition.tests.cpp: + FAILED: at Condition.tests.cpp: + FAILED: at Condition.tests.cpp: + FAILED: at Condition.tests.cpp: + FAILED: @@ -777,21 +906,21 @@ at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one < 9 ) with expansion: - 9.1f < 9 + 9.100000381f < 9 at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one > 10 ) with expansion: - 9.1f > 10 + 9.100000381f > 10 at Condition.tests.cpp: FAILED: CHECK( data.float_nine_point_one > 9.2 ) with expansion: - 9.1f > 9.2 + 9.100000381f > 9.19999999999999929 at Condition.tests.cpp: @@ -876,6 +1005,7 @@ at Decomposition.tests.cpp: + FAILED: @@ -925,6 +1055,7 @@ custom std exception at Exception.tests.cpp: + @@ -973,7 +1104,7 @@ at Exception.tests.cpp: FAILED: -3.14 +3.14000000000000012 at Exception.tests.cpp: @@ -1016,6 +1147,7 @@ unexpected exception at Exception.tests.cpp: + FAILED: @@ -1035,20 +1167,27 @@ at Generators.tests.cpp: + + + + + + + @@ -1058,6 +1197,7 @@ at Generators.tests.cpp: + @@ -1067,8 +1207,10 @@ at Generators.tests.cpp: + + @@ -1165,6 +1307,7 @@ at Matchers.tests.cpp: + @@ -1172,6 +1315,7 @@ at Matchers.tests.cpp: + @@ -1244,10 +1388,13 @@ at Matchers.tests.cpp: + + + FAILED: @@ -1266,11 +1413,13 @@ with expansion: at Matchers.tests.cpp: + + FAILED: @@ -1365,82 +1514,114 @@ at Matchers.tests.cpp: + + + + + + + + + + - + + - + + + + + + - + + - + + + + + + - + + - + + + + + + + + + + @@ -1467,6 +1648,15 @@ at Message.tests.cpp: FAILED: This is a failure +at Message.tests.cpp: + + + + +FAILED: +Show infos! +This info has multiple parts. +This unscoped info has multiple parts. at Message.tests.cpp: @@ -1530,6 +1720,8 @@ at Message.tests.cpp: + + @@ -1546,6 +1738,8 @@ previous unscoped info SHOULD not be seen at Message.tests.cpp: + + @@ -1618,6 +1812,7 @@ with expansion: at Misc.tests.cpp: + @@ -1638,7 +1833,9 @@ to infinity and beyond at Misc.tests.cpp: + + @@ -1727,6 +1924,22 @@ at Misc.tests.cpp: FAILED: +at Misc.tests.cpp: + + + + +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! +at Misc.tests.cpp: + + + + +FAILED: + {Unknown expression after the reported line} +Uncaught exception should fail! at Misc.tests.cpp: @@ -1751,10 +1964,14 @@ with expansion: at Misc.tests.cpp: + + + + FAILED: @@ -1831,6 +2048,8 @@ Testing if fib[7] (21) is even at Misc.tests.cpp: + + FAILED: @@ -1840,14 +2059,18 @@ with expansion: at Misc.tests.cpp: + + + + FAILED: @@ -1866,10 +2089,18 @@ at Misc.tests.cpp: + + + +SKIPPED +This generator is empty +at Skip.tests.cpp: + + SKIPPED @@ -1929,6 +2160,9 @@ FAILED: at Skip.tests.cpp: + + + SKIPPED @@ -1936,6 +2170,7 @@ at Skip.tests.cpp: + @@ -1964,20 +2199,26 @@ at Skip.tests.cpp: + + + + + + @@ -2022,6 +2263,7 @@ FAILED: at Tricky.tests.cpp: + @@ -2057,6 +2299,7 @@ at Tricky.tests.cpp: + @@ -2065,6 +2308,7 @@ at Tricky.tests.cpp: + diff --git a/src/external/catch2/tests/SelfTest/Baselines/tap.sw.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/tap.sw.approved.txt index 920c95fd..ea53d421 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/tap.sw.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/tap.sw.approved.txt @@ -478,6 +478,14 @@ ok {test-number} - Nttp_Fixture::value > 0 for: 6 > 0 not ok {test-number} - m_a == 2 for: 1 == 2 # A TEST_CASE_METHOD based test run that succeeds ok {test-number} - m_a == 1 for: 1 == 1 +# A TEST_CASE_PERSISTENT_FIXTURE based test run that fails +ok {test-number} - m_a++ == 0 for: 0 == 0 +# A TEST_CASE_PERSISTENT_FIXTURE based test run that fails +not ok {test-number} - m_a == 0 for: 1 == 0 +# A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds +ok {test-number} - m_a++ == 0 for: 0 == 0 +# A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds +ok {test-number} - m_a == 1 for: 1 == 1 # A Template product test case - Foo ok {test-number} - x.size() == 0 for: 0 == 0 # A Template product test case - Foo @@ -495,17 +503,17 @@ ok {test-number} - x.size() > 0 for: 42 > 0 # A Template product test case with array signature - std::array ok {test-number} - x.size() > 0 for: 9 > 0 # A comparison that uses literals instead of the normal constructor -ok {test-number} - d == 1.23_a for: 1.23 == Approx( 1.23 ) +ok {test-number} - d == 1.23_a for: 1.22999999999999998 == Approx( 1.22999999999999998 ) # A comparison that uses literals instead of the normal constructor -ok {test-number} - d != 1.22_a for: 1.23 != Approx( 1.22 ) +ok {test-number} - d != 1.22_a for: 1.22999999999999998 != Approx( 1.21999999999999997 ) # A comparison that uses literals instead of the normal constructor -ok {test-number} - -d == -1.23_a for: -1.23 == Approx( -1.23 ) +ok {test-number} - -d == -1.23_a for: -1.22999999999999998 == Approx( -1.22999999999999998 ) # A comparison that uses literals instead of the normal constructor -ok {test-number} - d == 1.2_a .epsilon(.1) for: 1.23 == Approx( 1.2 ) +ok {test-number} - d == 1.2_a .epsilon(.1) for: 1.22999999999999998 == Approx( 1.19999999999999996 ) # A comparison that uses literals instead of the normal constructor -ok {test-number} - d != 1.2_a .epsilon(.001) for: 1.23 != Approx( 1.2 ) +ok {test-number} - d != 1.2_a .epsilon(.001) for: 1.22999999999999998 != Approx( 1.19999999999999996 ) # A comparison that uses literals instead of the normal constructor -ok {test-number} - d == 1_a .epsilon(.3) for: 1.23 == Approx( 1.0 ) +ok {test-number} - d == 1_a .epsilon(.3) for: 1.22999999999999998 == Approx( 1.0 ) # A couple of nested sections followed by a failure ok {test-number} - with 1 message: 'that's not flying - that's failing in style' # A couple of nested sections followed by a failure @@ -523,9 +531,9 @@ ok {test-number} - 104.0 == Approx(100.0).margin(4) for: 104.0 == Approx( 100.0 # Absolute margin ok {test-number} - 104.0 != Approx(100.0).margin(3) for: 104.0 != Approx( 100.0 ) # Absolute margin -ok {test-number} - 100.3 != Approx(100.0) for: 100.3 != Approx( 100.0 ) +ok {test-number} - 100.3 != Approx(100.0) for: 100.29999999999999716 != Approx( 100.0 ) # Absolute margin -ok {test-number} - 100.3 == Approx(100.0).margin(0.5) for: 100.3 == Approx( 100.0 ) +ok {test-number} - 100.3 == Approx(100.0).margin(0.5) for: 100.29999999999999716 == Approx( 100.0 ) # An expression with side-effects should only be evaluated once ok {test-number} - i++ == 7 for: 7 == 7 # An expression with side-effects should only be evaluated once @@ -561,15 +569,15 @@ ok {test-number} - 245.0f == Approx(245.25f).margin(0.25f) for: 245.0f == Approx # Approx with exactly-representable margin ok {test-number} - 245.5f == Approx(245.25f).margin(0.25f) for: 245.5f == Approx( 245.25 ) # Approximate PI -ok {test-number} - divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) for: 3.1428571429 == Approx( 3.141 ) +ok {test-number} - divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) for: 3.14285714285714279 == Approx( 3.14100000000000001 ) # Approximate PI -ok {test-number} - divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) for: 3.1428571429 != Approx( 3.141 ) +ok {test-number} - divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) for: 3.14285714285714279 != Approx( 3.14100000000000001 ) # Approximate comparisons with different epsilons -ok {test-number} - d != Approx( 1.231 ) for: 1.23 != Approx( 1.231 ) +ok {test-number} - d != Approx( 1.231 ) for: 1.22999999999999998 != Approx( 1.23100000000000009 ) # Approximate comparisons with different epsilons -ok {test-number} - d == Approx( 1.231 ).epsilon( 0.1 ) for: 1.23 == Approx( 1.231 ) +ok {test-number} - d == Approx( 1.231 ).epsilon( 0.1 ) for: 1.22999999999999998 == Approx( 1.23100000000000009 ) # Approximate comparisons with floats -ok {test-number} - 1.23f == Approx( 1.23f ) for: 1.23f == Approx( 1.2300000191 ) +ok {test-number} - 1.23f == Approx( 1.23f ) for: 1.230000019f == Approx( 1.23000001907348633 ) # Approximate comparisons with floats ok {test-number} - 0.0f == Approx( 0.0f ) for: 0.0f == Approx( 0.0 ) # Approximate comparisons with ints @@ -583,9 +591,9 @@ ok {test-number} - 0 == Approx( dZero) for: 0 == Approx( 0.0 ) # Approximate comparisons with mixed numeric types ok {test-number} - 0 == Approx( dSmall ).margin( 0.001 ) for: 0 == Approx( 0.00001 ) # Approximate comparisons with mixed numeric types -ok {test-number} - 1.234f == Approx( dMedium ) for: 1.234f == Approx( 1.234 ) +ok {test-number} - 1.234f == Approx( dMedium ) for: 1.233999968f == Approx( 1.23399999999999999 ) # Approximate comparisons with mixed numeric types -ok {test-number} - dMedium == Approx( 1.234f ) for: 1.234 == Approx( 1.2339999676 ) +ok {test-number} - dMedium == Approx( 1.234f ) for: 1.23399999999999999 == Approx( 1.23399996757507324 ) # Arbitrary predicate matcher ok {test-number} - 1, Predicate( alwaysTrue, "always true" ) for: 1 matches predicate: "always true" # Arbitrary predicate matcher @@ -659,7 +667,7 @@ ok {test-number} - unrelated::ADL_empty{}, IsEmpty() for: {?} is empty # CAPTURE can deal with complex expressions ok {test-number} - with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true' # CAPTURE can deal with complex expressions involving commas -ok {test-number} - with 7 messages: 'std::vector{1, 2, 3}[0, 1, 2] := 3' and 'std::vector{1, 2, 3}[(0, 1)] := 2' and 'std::vector{1, 2, 3}[0] := 1' and '(helper_1436{12, -12}) := { 12, -12 }' and '(helper_1436(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' +ok {test-number} - with 7 messages: 'custom_index_op{1, 2, 3}[0, 1, 2] := 0' and 'custom_index_op{1, 2, 3}[(0, 1)] := 0' and 'custom_index_op{1, 2, 3}[0] := 0' and '(helper_1436{12, -12}) := { 12, -12 }' and '(helper_1436(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' # CAPTURE parses string and character constants ok {test-number} - with 11 messages: '("comma, in string", "escaped, \", ") := "escaped, ", "' and '"single quote in string,'," := "single quote in string,',"' and '"some escapes, \\,\\\\" := "some escapes, \,\\"' and '"some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<"' and ''"' := '"'' and ''\'' := '''' and '',' := ','' and ''}' := '}'' and '')' := ')'' and ''(' := '('' and ''{' := '{'' # Capture and info messages @@ -697,33 +705,37 @@ ok {test-number} - lt( "A", "b" ) for: true # CaseInsensitiveLess is case insensitive ok {test-number} - lt( "A", "B" ) for: true # Character pretty printing -ok {test-number} - tab == '\t' for: '\t' == '\t' -# Character pretty printing -ok {test-number} - newline == '\n' for: '\n' == '\n' -# Character pretty printing -ok {test-number} - carr_return == '\r' for: '\r' == '\r' -# Character pretty printing -ok {test-number} - form_feed == '\f' for: '\f' == '\f' +ok {test-number} - ::Catch::Detail::stringify('\t') == "'\\t'" for: "'\t'" == "'\t'" # Character pretty printing -ok {test-number} - space == ' ' for: ' ' == ' ' +ok {test-number} - ::Catch::Detail::stringify('\n') == "'\\n'" for: "'\n'" == "'\n'" # Character pretty printing -ok {test-number} - c == chars[i] for: 'a' == 'a' +ok {test-number} - ::Catch::Detail::stringify('\r') == "'\\r'" for: "'\r'" == "'\r'" # Character pretty printing -ok {test-number} - c == chars[i] for: 'z' == 'z' +ok {test-number} - ::Catch::Detail::stringify('\f') == "'\\f'" for: "'\f'" == "'\f'" # Character pretty printing -ok {test-number} - c == chars[i] for: 'A' == 'A' +ok {test-number} - ::Catch::Detail::stringify( ' ' ) == "' '" for: "' '" == "' '" # Character pretty printing -ok {test-number} - c == chars[i] for: 'Z' == 'Z' +ok {test-number} - ::Catch::Detail::stringify( 'A' ) == "'A'" for: "'A'" == "'A'" # Character pretty printing -ok {test-number} - null_terminator == '\0' for: 0 == 0 +ok {test-number} - ::Catch::Detail::stringify( 'z' ) == "'z'" for: "'z'" == "'z'" # Character pretty printing -ok {test-number} - c == i for: 2 == 2 +ok {test-number} - ::Catch::Detail::stringify( '\0' ) == "0" for: "0" == "0" # Character pretty printing -ok {test-number} - c == i for: 3 == 3 +ok {test-number} - ::Catch::Detail::stringify( static_cast(2) ) == "2" for: "2" == "2" # Character pretty printing -ok {test-number} - c == i for: 4 == 4 -# Character pretty printing -ok {test-number} - c == i for: 5 == 5 +ok {test-number} - ::Catch::Detail::stringify( static_cast(5) ) == "5" for: "5" == "5" +# Clara::Arg does not crash on incomplete input +ok {test-number} - name.empty() for: true +# Clara::Arg does not crash on incomplete input +ok {test-number} - result for: {?} +# Clara::Arg does not crash on incomplete input +ok {test-number} - result.type() == Catch::Clara::Detail::ResultType::Ok for: 0 == 0 +# Clara::Arg does not crash on incomplete input +ok {test-number} - parsed.type() == Catch::Clara::ParseResultType::NoMatch for: 1 == 1 +# Clara::Arg does not crash on incomplete input +ok {test-number} - parsed.remainingTokens().count() == 2 for: 2 == 2 +# Clara::Arg does not crash on incomplete input +ok {test-number} - name.empty() for: true # Clara::Arg supports single-arg parse the way Opt does ok {test-number} - name.empty() for: true # Clara::Arg supports single-arg parse the way Opt does @@ -975,7 +987,7 @@ not ok {test-number} - unexpected exception with message: 'custom exception - no # Custom std-exceptions can be custom translated not ok {test-number} - unexpected exception with message: 'custom std exception' # Default scale is invisible to comparison -ok {test-number} - 101.000001 != Approx(100).epsilon(0.01) for: 101.000001 != Approx( 100.0 ) +ok {test-number} - 101.000001 != Approx(100).epsilon(0.01) for: 101.00000099999999748 != Approx( 100.0 ) # Default scale is invisible to comparison ok {test-number} - std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 ) # Directly creating an EnumInfo @@ -984,10 +996,10 @@ ok {test-number} - enumInfo->lookup(0) == "Value1" for: Value1 == "Value1" ok {test-number} - enumInfo->lookup(1) == "Value2" for: Value2 == "Value2" # Directly creating an EnumInfo ok {test-number} - enumInfo->lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **} == "{** unexpected enum value **}" +# Empty generators can SKIP in constructor +ok {test-number} - # SKIP 'This generator is empty' # Empty stream name opens cout stream ok {test-number} - Catch::makeStream( "" )->isConsole() for: true -# Empty tag is not allowed -ok {test-number} - Catch::TestCaseInfo( "", { "fake test name", "[]" }, dummySourceLineInfo ) # EndsWith string matcher not ok {test-number} - testStringForMatching(), EndsWith( "Substring" ) for: "this string contains 'abc' as a substring" ends with: "Substring" # EndsWith string matcher @@ -1007,7 +1019,7 @@ ok {test-number} - stringify( Bikeshed::Colours::Red ) == "Red" for: "Red" == "R # Enums in namespaces can quickly have stringification enabled using REGISTER_ENUM ok {test-number} - stringify( Bikeshed::Colours::Blue ) == "Blue" for: "Blue" == "Blue" # Epsilon only applies to Approx's value -ok {test-number} - 101.01 != Approx(100).epsilon(0.01) for: 101.01 != Approx( 100.0 ) +ok {test-number} - 101.01 != Approx(100).epsilon(0.01) for: 101.01000000000000512 != Approx( 100.0 ) # Equality checks that should fail not ok {test-number} - data.int_seven == 6 for: 7 == 6 # Equality checks that should fail @@ -1015,15 +1027,15 @@ not ok {test-number} - data.int_seven == 8 for: 7 == 8 # Equality checks that should fail not ok {test-number} - data.int_seven == 0 for: 7 == 0 # Equality checks that should fail -not ok {test-number} - data.float_nine_point_one == Approx( 9.11f ) for: 9.1f == Approx( 9.1099996567 ) +not ok {test-number} - data.float_nine_point_one == Approx( 9.11f ) for: 9.100000381f == Approx( 9.10999965667724609 ) # Equality checks that should fail -not ok {test-number} - data.float_nine_point_one == Approx( 9.0f ) for: 9.1f == Approx( 9.0 ) +not ok {test-number} - data.float_nine_point_one == Approx( 9.0f ) for: 9.100000381f == Approx( 9.0 ) # Equality checks that should fail -not ok {test-number} - data.float_nine_point_one == Approx( 1 ) for: 9.1f == Approx( 1.0 ) +not ok {test-number} - data.float_nine_point_one == Approx( 1 ) for: 9.100000381f == Approx( 1.0 ) # Equality checks that should fail -not ok {test-number} - data.float_nine_point_one == Approx( 0 ) for: 9.1f == Approx( 0.0 ) +not ok {test-number} - data.float_nine_point_one == Approx( 0 ) for: 9.100000381f == Approx( 0.0 ) # Equality checks that should fail -not ok {test-number} - data.double_pi == Approx( 3.1415 ) for: 3.1415926535 == Approx( 3.1415 ) +not ok {test-number} - data.double_pi == Approx( 3.1415 ) for: 3.14159265350000005 == Approx( 3.14150000000000018 ) # Equality checks that should fail not ok {test-number} - data.str_hello == "goodbye" for: "hello" == "goodbye" # Equality checks that should fail @@ -1033,13 +1045,13 @@ not ok {test-number} - data.str_hello == "hello1" for: "hello" == "hello1" # Equality checks that should fail not ok {test-number} - data.str_hello.size() == 6 for: 5 == 6 # Equality checks that should fail -not ok {test-number} - x == Approx( 1.301 ) for: 1.3 == Approx( 1.301 ) +not ok {test-number} - x == Approx( 1.301 ) for: 1.30000000000000027 == Approx( 1.30099999999999993 ) # Equality checks that should succeed ok {test-number} - data.int_seven == 7 for: 7 == 7 # Equality checks that should succeed -ok {test-number} - data.float_nine_point_one == Approx( 9.1f ) for: 9.1f == Approx( 9.1000003815 ) +ok {test-number} - data.float_nine_point_one == Approx( 9.1f ) for: 9.100000381f == Approx( 9.10000038146972656 ) # Equality checks that should succeed -ok {test-number} - data.double_pi == Approx( 3.1415926535 ) for: 3.1415926535 == Approx( 3.1415926535 ) +ok {test-number} - data.double_pi == Approx( 3.1415926535 ) for: 3.14159265350000005 == Approx( 3.14159265350000005 ) # Equality checks that should succeed ok {test-number} - data.str_hello == "hello" for: "hello" == "hello" # Equality checks that should succeed @@ -1047,7 +1059,7 @@ ok {test-number} - "hello" == data.str_hello for: "hello" == "hello" # Equality checks that should succeed ok {test-number} - data.str_hello.size() == 5 for: 5 == 5 # Equality checks that should succeed -ok {test-number} - x == Approx( 1.3 ) for: 1.3 == Approx( 1.3 ) +ok {test-number} - x == Approx( 1.3 ) for: 1.30000000000000027 == Approx( 1.30000000000000004 ) # Equals ok {test-number} - testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" # Equals @@ -1133,23 +1145,23 @@ ok {test-number} - Factorial(10) == 3628800 for: 3628800 (0x) == 362 # Filter generator throws exception for empty generator ok {test-number} - filter( []( int ) { return false; }, value( 3 ) ), Catch::GeneratorException # Floating point matchers: double -ok {test-number} - 10., WithinRel( 11.1, 0.1 ) for: 10.0 and 11.1 are within 10% of each other +ok {test-number} - 10., WithinRel( 11.1, 0.1 ) for: 10.0 and 11.09999999999999964 are within 10% of each other # Floating point matchers: double -ok {test-number} - 10., !WithinRel( 11.2, 0.1 ) for: 10.0 not and 11.2 are within 10% of each other +ok {test-number} - 10., !WithinRel( 11.2, 0.1 ) for: 10.0 not and 11.19999999999999929 are within 10% of each other # Floating point matchers: double -ok {test-number} - 1., !WithinRel( 0., 0.99 ) for: 1.0 not and 0 are within 99% of each other +ok {test-number} - 1., !WithinRel( 0., 0.99 ) for: 1.0 not and 0.0 are within 99% of each other # Floating point matchers: double -ok {test-number} - -0., WithinRel( 0. ) for: -0.0 and 0 are within 2.22045e-12% of each other +ok {test-number} - -0., WithinRel( 0. ) for: -0.0 and 0.0 are within 2.22045e-12% of each other # Floating point matchers: double -ok {test-number} - v1, WithinRel( v2 ) for: 0.0 and 2.22507e-308 are within 2.22045e-12% of each other +ok {test-number} - v1, WithinRel( v2 ) for: 0.0 and 0.0 are within 2.22045e-12% of each other # Floating point matchers: double ok {test-number} - 1., WithinAbs( 1., 0 ) for: 1.0 is within 0.0 of 1.0 # Floating point matchers: double ok {test-number} - 0., WithinAbs( 1., 1 ) for: 0.0 is within 1.0 of 1.0 # Floating point matchers: double -ok {test-number} - 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0 +ok {test-number} - 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.98999999999999999 of 1.0 # Floating point matchers: double -ok {test-number} - 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0 +ok {test-number} - 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.98999999999999999 of 1.0 # Floating point matchers: double ok {test-number} - 11., !WithinAbs( 10., 0.5 ) for: 11.0 not is within 0.5 of 10.0 # Floating point matchers: double @@ -1157,11 +1169,11 @@ ok {test-number} - 10., !WithinAbs( 11., 0.5 ) for: 10.0 not is within 0.5 of 11 # Floating point matchers: double ok {test-number} - -10., WithinAbs( -10., 0.5 ) for: -10.0 is within 0.5 of -10.0 # Floating point matchers: double -ok {test-number} - -10., WithinAbs( -9.6, 0.5 ) for: -10.0 is within 0.5 of -9.6 +ok {test-number} - -10., WithinAbs( -9.6, 0.5 ) for: -10.0 is within 0.5 of -9.59999999999999964 # Floating point matchers: double ok {test-number} - 1., WithinULP( 1., 0 ) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) # Floating point matchers: double -ok {test-number} - nextafter( 1., 2. ), WithinULP( 1., 1 ) for: 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) +ok {test-number} - nextafter( 1., 2. ), WithinULP( 1., 1 ) for: 1.00000000000000022 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) # Floating point matchers: double ok {test-number} - 0., WithinULP( nextafter( 0., 1. ), 1 ) for: 0.0 is within 1 ULPs of 4.9406564584124654e-324 ([0.0000000000000000e+00, 9.8813129168249309e-324]) # Floating point matchers: double @@ -1177,7 +1189,7 @@ ok {test-number} - 1., WithinAbs( 1., 0.5 ) || WithinULP( 2., 1 ) for: 1.0 ( is # Floating point matchers: double ok {test-number} - 1., WithinAbs( 2., 0.5 ) || WithinULP( 1., 0 ) for: 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) ) # Floating point matchers: double -ok {test-number} - 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) for: 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) +ok {test-number} - 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) for: 0.0001 ( is within 0.001 of 0.0 or and 0.0 are within 10% of each other ) # Floating point matchers: double ok {test-number} - WithinAbs( 1., 0. ) # Floating point matchers: double @@ -1193,23 +1205,23 @@ ok {test-number} - WithinRel( 1., 1. ), std::domain_error # Floating point matchers: double ok {test-number} - 1., !IsNaN() for: 1.0 not is NaN # Floating point matchers: float -ok {test-number} - 10.f, WithinRel( 11.1f, 0.1f ) for: 10.0f and 11.1 are within 10% of each other +ok {test-number} - 10.f, WithinRel( 11.1f, 0.1f ) for: 10.0f and 11.10000038146972656 are within 10% of each other # Floating point matchers: float -ok {test-number} - 10.f, !WithinRel( 11.2f, 0.1f ) for: 10.0f not and 11.2 are within 10% of each other +ok {test-number} - 10.f, !WithinRel( 11.2f, 0.1f ) for: 10.0f not and 11.19999980926513672 are within 10% of each other # Floating point matchers: float -ok {test-number} - 1.f, !WithinRel( 0.f, 0.99f ) for: 1.0f not and 0 are within 99% of each other +ok {test-number} - 1.f, !WithinRel( 0.f, 0.99f ) for: 1.0f not and 0.0 are within 99% of each other # Floating point matchers: float -ok {test-number} - -0.f, WithinRel( 0.f ) for: -0.0f and 0 are within 0.00119209% of each other +ok {test-number} - -0.f, WithinRel( 0.f ) for: -0.0f and 0.0 are within 0.00119209% of each other # Floating point matchers: float -ok {test-number} - v1, WithinRel( v2 ) for: 0.0f and 1.17549e-38 are within 0.00119209% of each other +ok {test-number} - v1, WithinRel( v2 ) for: 0.0f and 0.0 are within 0.00119209% of each other # Floating point matchers: float ok {test-number} - 1.f, WithinAbs( 1.f, 0 ) for: 1.0f is within 0.0 of 1.0 # Floating point matchers: float ok {test-number} - 0.f, WithinAbs( 1.f, 1 ) for: 0.0f is within 1.0 of 1.0 # Floating point matchers: float -ok {test-number} - 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0 +ok {test-number} - 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.99000000953674316 of 1.0 # Floating point matchers: float -ok {test-number} - 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0 +ok {test-number} - 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.99000000953674316 of 1.0 # Floating point matchers: float ok {test-number} - 0.f, WithinAbs( -0.f, 0 ) for: 0.0f is within 0.0 of -0.0 # Floating point matchers: float @@ -1219,13 +1231,13 @@ ok {test-number} - 10.f, !WithinAbs( 11.f, 0.5f ) for: 10.0f not is within 0.5 o # Floating point matchers: float ok {test-number} - -10.f, WithinAbs( -10.f, 0.5f ) for: -10.0f is within 0.5 of -10.0 # Floating point matchers: float -ok {test-number} - -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of -9.6000003815 +ok {test-number} - -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of -9.60000038146972656 # Floating point matchers: float ok {test-number} - 1.f, WithinULP( 1.f, 0 ) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) # Floating point matchers: float ok {test-number} - -1.f, WithinULP( -1.f, 0 ) for: -1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+00]) # Floating point matchers: float -ok {test-number} - nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) +ok {test-number} - nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.000000119f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) # Floating point matchers: float ok {test-number} - 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) for: 0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45]) # Floating point matchers: float @@ -1241,7 +1253,7 @@ ok {test-number} - 1.f, WithinAbs( 1.f, 0.5 ) || WithinULP( 1.f, 1 ) for: 1.0f ( # Floating point matchers: float ok {test-number} - 1.f, WithinAbs( 2.f, 0.5 ) || WithinULP( 1.f, 0 ) for: 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) ) # Floating point matchers: float -ok {test-number} - 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) for: 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) +ok {test-number} - 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) for: 0.0001f ( is within 0.00100000004749745 of 0.0 or and 0.0 are within 10% of each other ) # Floating point matchers: float ok {test-number} - WithinAbs( 1.f, 0.f ) # Floating point matchers: float @@ -1258,6 +1270,14 @@ ok {test-number} - WithinRel( 1.f, -0.2f ), std::domain_error ok {test-number} - WithinRel( 1.f, 1.f ), std::domain_error # Floating point matchers: float ok {test-number} - 1., !IsNaN() for: 1.0 not is NaN +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 # Generators -- adapters ok {test-number} - i % 2 == 0 for: 0 == 0 # Generators -- adapters @@ -1593,83 +1613,83 @@ ok {test-number} - gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) wit # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.9 == Approx( -0.9 ) with 1 message: 'Current expected value is -0.9' +ok {test-number} - gen.get() == Approx(expected) for: -0.90000000000000002 == Approx( -0.90000000000000002 ) with 1 message: 'Current expected value is -0.9' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.9' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.8 == Approx( -0.8 ) with 1 message: 'Current expected value is -0.8' +ok {test-number} - gen.get() == Approx(expected) for: -0.80000000000000004 == Approx( -0.80000000000000004 ) with 1 message: 'Current expected value is -0.8' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.8' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +ok {test-number} - gen.get() == Approx(expected) for: -0.70000000000000007 == Approx( -0.70000000000000007 ) with 1 message: 'Current expected value is -0.7' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.7' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.6 == Approx( -0.6 ) with 1 message: 'Current expected value is -0.6' +ok {test-number} - gen.get() == Approx(expected) for: -0.60000000000000009 == Approx( -0.60000000000000009 ) with 1 message: 'Current expected value is -0.6' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.6' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.5 == Approx( -0.5 ) with 1 message: 'Current expected value is -0.5' +ok {test-number} - gen.get() == Approx(expected) for: -0.50000000000000011 == Approx( -0.50000000000000011 ) with 1 message: 'Current expected value is -0.5' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.5' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +ok {test-number} - gen.get() == Approx(expected) for: -0.40000000000000013 == Approx( -0.40000000000000013 ) with 1 message: 'Current expected value is -0.4' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.4' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.3 == Approx( -0.3 ) with 1 message: 'Current expected value is -0.3' +ok {test-number} - gen.get() == Approx(expected) for: -0.30000000000000016 == Approx( -0.30000000000000016 ) with 1 message: 'Current expected value is -0.3' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.3' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.2 == Approx( -0.2 ) with 1 message: 'Current expected value is -0.2' +ok {test-number} - gen.get() == Approx(expected) for: -0.20000000000000015 == Approx( -0.20000000000000015 ) with 1 message: 'Current expected value is -0.2' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.2' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +ok {test-number} - gen.get() == Approx(expected) for: -0.10000000000000014 == Approx( -0.10000000000000014 ) with 1 message: 'Current expected value is -0.1' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.0 == Approx( -0.0 ) with 1 message: 'Current expected value is -1.38778e-16' +ok {test-number} - gen.get() == Approx(expected) for: -0.00000000000000014 == Approx( -0.00000000000000014 ) with 1 message: 'Current expected value is -1.38778e-16' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -1.38778e-16' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.1 == Approx( 0.1 ) with 1 message: 'Current expected value is 0.1' +ok {test-number} - gen.get() == Approx(expected) for: 0.09999999999999987 == Approx( 0.09999999999999987 ) with 1 message: 'Current expected value is 0.1' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +ok {test-number} - gen.get() == Approx(expected) for: 0.19999999999999987 == Approx( 0.19999999999999987 ) with 1 message: 'Current expected value is 0.2' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.2' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.3 == Approx( 0.3 ) with 1 message: 'Current expected value is 0.3' +ok {test-number} - gen.get() == Approx(expected) for: 0.29999999999999988 == Approx( 0.29999999999999988 ) with 1 message: 'Current expected value is 0.3' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.3' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.4 == Approx( 0.4 ) with 1 message: 'Current expected value is 0.4' +ok {test-number} - gen.get() == Approx(expected) for: 0.39999999999999991 == Approx( 0.39999999999999991 ) with 1 message: 'Current expected value is 0.4' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.4' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.5 == Approx( 0.5 ) with 1 message: 'Current expected value is 0.5' +ok {test-number} - gen.get() == Approx(expected) for: 0.49999999999999989 == Approx( 0.49999999999999989 ) with 1 message: 'Current expected value is 0.5' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.5' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.6 == Approx( 0.6 ) with 1 message: 'Current expected value is 0.6' +ok {test-number} - gen.get() == Approx(expected) for: 0.59999999999999987 == Approx( 0.59999999999999987 ) with 1 message: 'Current expected value is 0.6' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.6' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.7 == Approx( 0.7 ) with 1 message: 'Current expected value is 0.7' +ok {test-number} - gen.get() == Approx(expected) for: 0.69999999999999984 == Approx( 0.69999999999999984 ) with 1 message: 'Current expected value is 0.7' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.7' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.8 == Approx( 0.8 ) with 1 message: 'Current expected value is 0.8' +ok {test-number} - gen.get() == Approx(expected) for: 0.79999999999999982 == Approx( 0.79999999999999982 ) with 1 message: 'Current expected value is 0.8' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.8' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.9 == Approx( 0.9 ) with 1 message: 'Current expected value is 0.9' +ok {test-number} - gen.get() == Approx(expected) for: 0.8999999999999998 == Approx( 0.8999999999999998 ) with 1 message: 'Current expected value is 0.9' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.9' # Generators internals -ok {test-number} - gen.get() == Approx( rangeEnd ) for: 1.0 == Approx( 1.0 ) +ok {test-number} - gen.get() == Approx( rangeEnd ) for: 0.99999999999999978 == Approx( 1.0 ) # Generators internals ok {test-number} - !(gen.next()) for: !false # Generators internals @@ -1677,19 +1697,19 @@ ok {test-number} - gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) wit # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +ok {test-number} - gen.get() == Approx(expected) for: -0.69999999999999996 == Approx( -0.69999999999999996 ) with 1 message: 'Current expected value is -0.7' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.7' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +ok {test-number} - gen.get() == Approx(expected) for: -0.39999999999999997 == Approx( -0.39999999999999997 ) with 1 message: 'Current expected value is -0.4' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.4' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +ok {test-number} - gen.get() == Approx(expected) for: -0.09999999999999998 == Approx( -0.09999999999999998 ) with 1 message: 'Current expected value is -0.1' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +ok {test-number} - gen.get() == Approx(expected) for: 0.20000000000000001 == Approx( 0.20000000000000001 ) with 1 message: 'Current expected value is 0.2' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.2' # Generators internals @@ -1703,19 +1723,19 @@ ok {test-number} - gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) wit # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +ok {test-number} - gen.get() == Approx(expected) for: -0.69999999999999996 == Approx( -0.69999999999999996 ) with 1 message: 'Current expected value is -0.7' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.7' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +ok {test-number} - gen.get() == Approx(expected) for: -0.39999999999999997 == Approx( -0.39999999999999997 ) with 1 message: 'Current expected value is -0.4' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.4' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +ok {test-number} - gen.get() == Approx(expected) for: -0.09999999999999998 == Approx( -0.09999999999999998 ) with 1 message: 'Current expected value is -0.1' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +ok {test-number} - gen.get() == Approx(expected) for: 0.20000000000000001 == Approx( 0.20000000000000001 ) with 1 message: 'Current expected value is 0.2' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.2' # Generators internals @@ -1777,13 +1797,13 @@ ok {test-number} - gen.get() == -7 for: -7 == -7 # Generators internals ok {test-number} - !(gen.next()) for: !false # Greater-than inequalities with different epsilons -ok {test-number} - d >= Approx( 1.22 ) for: 1.23 >= Approx( 1.22 ) +ok {test-number} - d >= Approx( 1.22 ) for: 1.22999999999999998 >= Approx( 1.21999999999999997 ) # Greater-than inequalities with different epsilons -ok {test-number} - d >= Approx( 1.23 ) for: 1.23 >= Approx( 1.23 ) +ok {test-number} - d >= Approx( 1.23 ) for: 1.22999999999999998 >= Approx( 1.22999999999999998 ) # Greater-than inequalities with different epsilons -ok {test-number} - !(d >= Approx( 1.24 )) for: !(1.23 >= Approx( 1.24 )) +ok {test-number} - !(d >= Approx( 1.24 )) for: !(1.22999999999999998 >= Approx( 1.23999999999999999 )) # Greater-than inequalities with different epsilons -ok {test-number} - d >= Approx( 1.24 ).epsilon(0.1) for: 1.23 >= Approx( 1.24 ) +ok {test-number} - d >= Approx( 1.24 ).epsilon(0.1) for: 1.22999999999999998 >= Approx( 1.23999999999999999 ) # Hashers with different seed produce different hash with same test case ok {test-number} - h1( dummy ) != h2( dummy ) for: 3422778688 (0x) != 130711275 (0x) # Hashers with same seed produce same hash @@ -1796,6 +1816,8 @@ ok {test-number} - h( dummy1 ) != h( dummy2 ) for: 2673152918 (0x) ! ok {test-number} - h( dummy1 ) != h( dummy2 ) for: 2074929312 (0x) != 3429949824 (0x) # Hashing test case produces same hash across multiple calls ok {test-number} - h( dummy ) == h( dummy ) for: 3422778688 (0x) == 3422778688 (0x) +# INFO and UNSCOPED_INFO can stream multiple arguments +not ok {test-number} - explicitly with 3 messages: 'This info has multiple parts.' and 'This unscoped info has multiple parts.' and 'Show infos!' # INFO and WARN do not abort tests warning {test-number} - 'this is a message' with 1 message: 'this is a warning' # INFO gets logged on failure @@ -1830,12 +1852,14 @@ ok {test-number} - i < 10 for: 8 < 10 with 2 messages: 'current counter 8' and ' ok {test-number} - i < 10 for: 9 < 10 with 2 messages: 'current counter 9' and 'i := 9' # INFO is reset for each loop not ok {test-number} - i < 10 for: 10 < 10 with 2 messages: 'current counter 10' and 'i := 10' +# Incomplete AssertionHandler +not ok {test-number} - unexpected exception with message: 'Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE'; expression was: Dummy # Inequality checks that should fail not ok {test-number} - data.int_seven != 7 for: 7 != 7 # Inequality checks that should fail -not ok {test-number} - data.float_nine_point_one != Approx( 9.1f ) for: 9.1f != Approx( 9.1000003815 ) +not ok {test-number} - data.float_nine_point_one != Approx( 9.1f ) for: 9.100000381f != Approx( 9.10000038146972656 ) # Inequality checks that should fail -not ok {test-number} - data.double_pi != Approx( 3.1415926535 ) for: 3.1415926535 != Approx( 3.1415926535 ) +not ok {test-number} - data.double_pi != Approx( 3.1415926535 ) for: 3.14159265350000005 != Approx( 3.14159265350000005 ) # Inequality checks that should fail not ok {test-number} - data.str_hello != "hello" for: "hello" != "hello" # Inequality checks that should fail @@ -1845,15 +1869,15 @@ ok {test-number} - data.int_seven != 6 for: 7 != 6 # Inequality checks that should succeed ok {test-number} - data.int_seven != 8 for: 7 != 8 # Inequality checks that should succeed -ok {test-number} - data.float_nine_point_one != Approx( 9.11f ) for: 9.1f != Approx( 9.1099996567 ) +ok {test-number} - data.float_nine_point_one != Approx( 9.11f ) for: 9.100000381f != Approx( 9.10999965667724609 ) # Inequality checks that should succeed -ok {test-number} - data.float_nine_point_one != Approx( 9.0f ) for: 9.1f != Approx( 9.0 ) +ok {test-number} - data.float_nine_point_one != Approx( 9.0f ) for: 9.100000381f != Approx( 9.0 ) # Inequality checks that should succeed -ok {test-number} - data.float_nine_point_one != Approx( 1 ) for: 9.1f != Approx( 1.0 ) +ok {test-number} - data.float_nine_point_one != Approx( 1 ) for: 9.100000381f != Approx( 1.0 ) # Inequality checks that should succeed -ok {test-number} - data.float_nine_point_one != Approx( 0 ) for: 9.1f != Approx( 0.0 ) +ok {test-number} - data.float_nine_point_one != Approx( 0 ) for: 9.100000381f != Approx( 0.0 ) # Inequality checks that should succeed -ok {test-number} - data.double_pi != Approx( 3.1415 ) for: 3.1415926535 != Approx( 3.1415 ) +ok {test-number} - data.double_pi != Approx( 3.1415 ) for: 3.14159265350000005 != Approx( 3.14150000000000018 ) # Inequality checks that should succeed ok {test-number} - data.str_hello != "goodbye" for: "hello" != "goodbye" # Inequality checks that should succeed @@ -1862,16 +1886,52 @@ ok {test-number} - data.str_hello != "hell" for: "hello" != "hell" ok {test-number} - data.str_hello != "hello1" for: "hello" != "hello1" # Inequality checks that should succeed ok {test-number} - data.str_hello.size() != 6 for: 5 != 6 +# JsonWriter +ok {test-number} - stream.str() == "" for: "" == "" +# JsonWriter +ok {test-number} - stream.str() == "{\n}" for: "{ }" == "{ }" +# JsonWriter +ok {test-number} - stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) for: "{ "int": 1, "double": 1.5, "true": true, "false": false, "string": "this is a string", "array": [ 1, 2 ] }" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ 1, 2 ] }" ) +# JsonWriter +ok {test-number} - stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) for: "{ "empty_object": { }, "fully_object": { "key": 1 } }" ( contains: ""empty_object": { }," and contains: ""fully_object": { "key": 1 }" ) +# JsonWriter +ok {test-number} - stream.str() == "[\n]" for: "[ ]" == "[ ]" +# JsonWriter +ok {test-number} - stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" for: "[ 1, 1.5, true, false, "this is a string", { "object": 42 }, [ "array", 42.5 ] ]" == "[ 1, 1.5, true, false, "this is a string", { "object": 42 }, [ "array", 42.5 ] ]" +# JsonWriter +ok {test-number} - stream.str() == "{\n}" for: "{ }" == "{ }" +# JsonWriter +ok {test-number} - stream.str() == "[\n]" for: "[ ]" == "[ ]" +# JsonWriter +ok {test-number} - stream.str() == "\"custom\"" for: ""custom"" == ""custom"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\"\"" for: ""\""" == ""\""" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\\\"" for: ""\\"" == ""\\"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"/\"" for: ""/"" == ""/"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\b\"" for: ""\b"" == ""\b"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\f\"" for: ""\f"" == ""\f"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\n\"" for: ""\n"" == ""\n"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\r\"" for: ""\r"" == ""\r"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\t\"" for: ""\t"" == ""\t"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n"" # Lambdas in assertions ok {test-number} - []() { return true; }() for: true # Less-than inequalities with different epsilons -ok {test-number} - d <= Approx( 1.24 ) for: 1.23 <= Approx( 1.24 ) +ok {test-number} - d <= Approx( 1.24 ) for: 1.22999999999999998 <= Approx( 1.23999999999999999 ) # Less-than inequalities with different epsilons -ok {test-number} - d <= Approx( 1.23 ) for: 1.23 <= Approx( 1.23 ) +ok {test-number} - d <= Approx( 1.23 ) for: 1.22999999999999998 <= Approx( 1.22999999999999998 ) # Less-than inequalities with different epsilons -ok {test-number} - !(d <= Approx( 1.22 )) for: !(1.23 <= Approx( 1.22 )) +ok {test-number} - !(d <= Approx( 1.22 )) for: !(1.22999999999999998 <= Approx( 1.21999999999999997 )) # Less-than inequalities with different epsilons -ok {test-number} - d <= Approx( 1.22 ).epsilon(0.1) for: 1.23 <= Approx( 1.22 ) +ok {test-number} - d <= Approx( 1.22 ).epsilon(0.1) for: 1.22999999999999998 <= Approx( 1.21999999999999997 ) # ManuallyRegistered ok {test-number} - with 1 message: 'was called' # Matchers can be (AllOf) composed with the && operator @@ -2001,11 +2061,11 @@ not ok {test-number} - data.int_seven >= 8 for: 7 >= 8 # Ordering comparison checks that should fail not ok {test-number} - data.int_seven <= 6 for: 7 <= 6 # Ordering comparison checks that should fail -not ok {test-number} - data.float_nine_point_one < 9 for: 9.1f < 9 +not ok {test-number} - data.float_nine_point_one < 9 for: 9.100000381f < 9 # Ordering comparison checks that should fail -not ok {test-number} - data.float_nine_point_one > 10 for: 9.1f > 10 +not ok {test-number} - data.float_nine_point_one > 10 for: 9.100000381f > 10 # Ordering comparison checks that should fail -not ok {test-number} - data.float_nine_point_one > 9.2 for: 9.1f > 9.2 +not ok {test-number} - data.float_nine_point_one > 9.2 for: 9.100000381f > 9.19999999999999929 # Ordering comparison checks that should fail not ok {test-number} - data.str_hello > "hello" for: "hello" > "hello" # Ordering comparison checks that should fail @@ -2039,11 +2099,11 @@ ok {test-number} - data.int_seven <= 7 for: 7 <= 7 # Ordering comparison checks that should succeed ok {test-number} - data.int_seven <= 8 for: 7 <= 8 # Ordering comparison checks that should succeed -ok {test-number} - data.float_nine_point_one > 9 for: 9.1f > 9 +ok {test-number} - data.float_nine_point_one > 9 for: 9.100000381f > 9 # Ordering comparison checks that should succeed -ok {test-number} - data.float_nine_point_one < 10 for: 9.1f < 10 +ok {test-number} - data.float_nine_point_one < 10 for: 9.100000381f < 10 # Ordering comparison checks that should succeed -ok {test-number} - data.float_nine_point_one < 9.2 for: 9.1f < 9.2 +ok {test-number} - data.float_nine_point_one < 9.2 for: 9.100000381f < 9.19999999999999929 # Ordering comparison checks that should succeed ok {test-number} - data.str_hello <= "hello" for: "hello" <= "hello" # Ordering comparison checks that should succeed @@ -2379,7 +2439,7 @@ ok {test-number} - config.benchmarkResamples == 20000 for: 20000 (0x # Process can be configured on command line ok {test-number} - cli.parse({ "test", "--benchmark-confidence-interval=0.99" }) for: {?} # Process can be configured on command line -ok {test-number} - config.benchmarkConfidenceInterval == Catch::Approx(0.99) for: 0.99 == Approx( 0.99 ) +ok {test-number} - config.benchmarkConfidenceInterval == Catch::Approx(0.99) for: 0.98999999999999999 == Approx( 0.98999999999999999 ) # Process can be configured on command line ok {test-number} - cli.parse({ "test", "--benchmark-no-analysis" }) for: {?} # Process can be configured on command line @@ -2455,6 +2515,18 @@ ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && Cont # Reporter's write listings to provided stream ok {test-number} - !(factories.empty()) for: !false # Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "" }, "listings": { "tags": [ { "aliases": [ "fakeTag" ], "count": 1 } ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring("fake reporter"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "" }, "listings": { "reporters": [ { "name": "fake reporter", "description": "fake description" } ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "" }, "listings": { "tests": [ { "name": "fake test name", "class-name": "", "tags": [ "fakeTestTag" ], "source-location": { "filename": "fake-file.cpp", "line": 123456789 } } ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: " All available tags: 1 [fakeTag] 1 tag " contains: "fakeTag" with 1 message: 'Tested reporter: JUnit' # Reporter's write listings to provided stream ok {test-number} - !(factories.empty()) for: !false @@ -2548,21 +2620,21 @@ A string sent directly to stdout A string sent directly to stderr A string sent to stderr via clog # Some simple comparisons between doubles -ok {test-number} - d == Approx( 1.23 ) for: 1.23 == Approx( 1.23 ) +ok {test-number} - d == Approx( 1.23 ) for: 1.22999999999999998 == Approx( 1.22999999999999998 ) # Some simple comparisons between doubles -ok {test-number} - d != Approx( 1.22 ) for: 1.23 != Approx( 1.22 ) +ok {test-number} - d != Approx( 1.22 ) for: 1.22999999999999998 != Approx( 1.21999999999999997 ) # Some simple comparisons between doubles -ok {test-number} - d != Approx( 1.24 ) for: 1.23 != Approx( 1.24 ) +ok {test-number} - d != Approx( 1.24 ) for: 1.22999999999999998 != Approx( 1.23999999999999999 ) # Some simple comparisons between doubles -ok {test-number} - d == 1.23_a for: 1.23 == Approx( 1.23 ) +ok {test-number} - d == 1.23_a for: 1.22999999999999998 == Approx( 1.22999999999999998 ) # Some simple comparisons between doubles -ok {test-number} - d != 1.22_a for: 1.23 != Approx( 1.22 ) +ok {test-number} - d != 1.22_a for: 1.22999999999999998 != Approx( 1.21999999999999997 ) # Some simple comparisons between doubles -ok {test-number} - Approx( d ) == 1.23 for: Approx( 1.23 ) == 1.23 +ok {test-number} - Approx( d ) == 1.23 for: Approx( 1.22999999999999998 ) == 1.22999999999999998 # Some simple comparisons between doubles -ok {test-number} - Approx( d ) != 1.22 for: Approx( 1.23 ) != 1.22 +ok {test-number} - Approx( d ) != 1.22 for: Approx( 1.22999999999999998 ) != 1.21999999999999997 # Some simple comparisons between doubles -ok {test-number} - Approx( d ) != 1.24 for: Approx( 1.23 ) != 1.24 +ok {test-number} - Approx( d ) != 1.24 for: Approx( 1.22999999999999998 ) != 1.23999999999999999 Message from section one Message from section two # StartsWith string matcher @@ -2752,19 +2824,19 @@ ok {test-number} - Template_Fixture::m_a == 1 for: 1 == 1 # Template test case method with test types specified inside std::tuple - MyTypes - 2 ok {test-number} - Template_Fixture::m_a == 1 for: 1.0 == 1 # Template test case with test types specified inside non-copyable and non-movable std::tuple - NonCopyableAndNonMovableTypes - 0 -ok {test-number} - sizeof(TestType) > 0 for: 1 > 0 +ok {test-number} - std::is_default_constructible::value for: true # Template test case with test types specified inside non-copyable and non-movable std::tuple - NonCopyableAndNonMovableTypes - 1 -ok {test-number} - sizeof(TestType) > 0 for: 4 > 0 +ok {test-number} - std::is_default_constructible::value for: true # Template test case with test types specified inside non-default-constructible std::tuple - MyNonDefaultConstructibleTypes - 0 -ok {test-number} - sizeof(TestType) > 0 for: 1 > 0 +ok {test-number} - std::is_trivially_copyable::value for: true # Template test case with test types specified inside non-default-constructible std::tuple - MyNonDefaultConstructibleTypes - 1 -ok {test-number} - sizeof(TestType) > 0 for: 4 > 0 +ok {test-number} - std::is_trivially_copyable::value for: true # Template test case with test types specified inside std::tuple - MyTypes - 0 -ok {test-number} - sizeof(TestType) > 0 for: 4 > 0 +ok {test-number} - std::is_arithmetic::value for: true # Template test case with test types specified inside std::tuple - MyTypes - 1 -ok {test-number} - sizeof(TestType) > 0 for: 1 > 0 +ok {test-number} - std::is_arithmetic::value for: true # Template test case with test types specified inside std::tuple - MyTypes - 2 -ok {test-number} - sizeof(TestType) > 0 for: 4 > 0 +ok {test-number} - std::is_arithmetic::value for: true # TemplateTest: vectors can be sized and resized - float ok {test-number} - v.size() == 5 for: 5 == 5 # TemplateTest: vectors can be sized and resized - float @@ -3067,6 +3139,14 @@ not ok {test-number} - explicitly ok {test-number} - false # TODO # Testing checked-if 3 not ok {test-number} - explicitly +# Testing checked-if 4 +ok {test-number} - true +# Testing checked-if 4 +not ok {test-number} - unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} +# Testing checked-if 5 +ok {test-number} - false # TODO +# Testing checked-if 5 +not ok {test-number} - unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} # The NO_FAIL macro reports a failure but does not fail the test ok {test-number} - 1 == 2 # TODO # The default listing implementation write to provided stream @@ -3266,7 +3346,7 @@ ok {test-number} - vector_a, RangeEquals( array_a_plus_1, close_enough ) for: { # Type conversions of RangeEquals and similar ok {test-number} - vector_a, UnorderedRangeEquals( array_a_plus_1, close_enough ) for: { 1, 2, 3 } unordered elements are { 2, 3, 4 } # Unexpected exceptions can be translated -not ok {test-number} - unexpected exception with message: '3.14' +not ok {test-number} - unexpected exception with message: '3.14000000000000012' # Upcasting special member functions ok {test-number} - bptr->i == 3 for: 3 == 3 # Upcasting special member functions @@ -3558,21 +3638,21 @@ ok {test-number} - unrelated::ADL_size{}, SizeIs(12) for: {?} has size == 12 # Usage of the SizeIs range matcher ok {test-number} - has_size{}, SizeIs(13) for: {?} has size == 13 # Use a custom approx -ok {test-number} - d == approx( 1.23 ) for: 1.23 == Approx( 1.23 ) +ok {test-number} - d == approx( 1.23 ) for: 1.22999999999999998 == Approx( 1.22999999999999998 ) # Use a custom approx -ok {test-number} - d == approx( 1.22 ) for: 1.23 == Approx( 1.22 ) +ok {test-number} - d == approx( 1.22 ) for: 1.22999999999999998 == Approx( 1.21999999999999997 ) # Use a custom approx -ok {test-number} - d == approx( 1.24 ) for: 1.23 == Approx( 1.24 ) +ok {test-number} - d == approx( 1.24 ) for: 1.22999999999999998 == Approx( 1.23999999999999999 ) # Use a custom approx -ok {test-number} - d != approx( 1.25 ) for: 1.23 != Approx( 1.25 ) +ok {test-number} - d != approx( 1.25 ) for: 1.22999999999999998 != Approx( 1.25 ) # Use a custom approx -ok {test-number} - approx( d ) == 1.23 for: Approx( 1.23 ) == 1.23 +ok {test-number} - approx( d ) == 1.23 for: Approx( 1.22999999999999998 ) == 1.22999999999999998 # Use a custom approx -ok {test-number} - approx( d ) == 1.22 for: Approx( 1.23 ) == 1.22 +ok {test-number} - approx( d ) == 1.22 for: Approx( 1.22999999999999998 ) == 1.21999999999999997 # Use a custom approx -ok {test-number} - approx( d ) == 1.24 for: Approx( 1.23 ) == 1.24 +ok {test-number} - approx( d ) == 1.24 for: Approx( 1.22999999999999998 ) == 1.23999999999999999 # Use a custom approx -ok {test-number} - approx( d ) != 1.25 for: Approx( 1.23 ) != 1.25 +ok {test-number} - approx( d ) != 1.25 for: Approx( 1.22999999999999998 ) != 1.25 # Variadic macros ok {test-number} - with 1 message: 'no assertions' # Vector Approx matcher @@ -3898,11 +3978,11 @@ ok {test-number} - # SKIP 'skipping because answer = 43' # empty tags are not allowed ok {test-number} - Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo) # erfc_inv -ok {test-number} - erfc_inv(1.103560) == Approx(-0.09203687623843015) for: -0.0920368762 == Approx( -0.0920368762 ) +ok {test-number} - erfc_inv(1.103560) == Approx(-0.09203687623843015) for: -0.09203687623843014 == Approx( -0.09203687623843015 ) # erfc_inv -ok {test-number} - erfc_inv(1.067400) == Approx(-0.05980291115763361) for: -0.0598029112 == Approx( -0.0598029112 ) +ok {test-number} - erfc_inv(1.067400) == Approx(-0.05980291115763361) for: -0.05980291115763361 == Approx( -0.05980291115763361 ) # erfc_inv -ok {test-number} - erfc_inv(0.050000) == Approx(1.38590382434967796) for: 1.3859038243 == Approx( 1.3859038243 ) +ok {test-number} - erfc_inv(0.050000) == Approx(1.38590382434967796) for: 1.38590382434967774 == Approx( 1.38590382434967796 ) # estimate_clock_resolution ok {test-number} - res.mean.count() == rate for: 2000.0 == 2000 (0x) # estimate_clock_resolution @@ -4047,22 +4127,12 @@ ok {test-number} - # SKIP ok {test-number} - s == "7" for: "7" == "7" # non-copyable objects ok {test-number} - ti == typeid(int) for: {?} == {?} -# normal_cdf -ok {test-number} - normal_cdf(0.000000) == Approx(0.50000000000000000) for: 0.5 == Approx( 0.5 ) -# normal_cdf -ok {test-number} - normal_cdf(1.000000) == Approx(0.84134474606854293) for: 0.8413447461 == Approx( 0.8413447461 ) -# normal_cdf -ok {test-number} - normal_cdf(-1.000000) == Approx(0.15865525393145705) for: 0.1586552539 == Approx( 0.1586552539 ) -# normal_cdf -ok {test-number} - normal_cdf(2.809729) == Approx(0.99752083845315409) for: 0.9975208385 == Approx( 0.9975208385 ) -# normal_cdf -ok {test-number} - normal_cdf(-1.352570) == Approx(0.08809652095066035) for: 0.088096521 == Approx( 0.088096521 ) # normal_quantile -ok {test-number} - normal_quantile(0.551780) == Approx(0.13015979861484198) for: 0.1301597986 == Approx( 0.1301597986 ) +ok {test-number} - normal_quantile(0.551780) == Approx(0.13015979861484198) for: 0.13015979861484195 == Approx( 0.13015979861484198 ) # normal_quantile -ok {test-number} - normal_quantile(0.533700) == Approx(0.08457408802851875) for: 0.084574088 == Approx( 0.084574088 ) +ok {test-number} - normal_quantile(0.533700) == Approx(0.08457408802851875) for: 0.08457408802851875 == Approx( 0.08457408802851875 ) # normal_quantile -ok {test-number} - normal_quantile(0.025000) == Approx(-1.95996398454005449) for: -1.9599639845 == Approx( -1.9599639845 ) +ok {test-number} - normal_quantile(0.025000) == Approx(-1.95996398454005449) for: -1.95996398454005405 == Approx( -1.95996398454005449 ) # not allowed ok {test-number} - # not prints unscoped info from previous failures @@ -4138,6 +4208,14 @@ ok {test-number} - !(Catch::replaceInPlace(letters, "x", "z")) for: !false # replaceInPlace ok {test-number} - letters == letters for: "abcdefcg" == "abcdefcg" # replaceInPlace +ok {test-number} - Catch::replaceInPlace(letters, "c", "cc") for: true +# replaceInPlace +ok {test-number} - letters == "abccdefccg" for: "abccdefccg" == "abccdefccg" +# replaceInPlace +ok {test-number} - Catch::replaceInPlace(s, "--", "-") for: true +# replaceInPlace +ok {test-number} - s == "--" for: "--" == "--" +# replaceInPlace ok {test-number} - Catch::replaceInPlace(s, "'", "|'") for: true # replaceInPlace ok {test-number} - s == "didn|'t" for: "didn|'t" == "didn|'t" @@ -4338,15 +4416,15 @@ ok {test-number} - "{ }" == ::Catch::Detail::stringify(type{}) for: "{ }" == "{ # tuple<> ok {test-number} - "{ }" == ::Catch::Detail::stringify(value) for: "{ }" == "{ }" # tuple -ok {test-number} - "1.2f" == ::Catch::Detail::stringify(float(1.2)) for: "1.2f" == "1.2f" +ok {test-number} - "1.5f" == ::Catch::Detail::stringify(float(1.5)) for: "1.5f" == "1.5f" # tuple -ok {test-number} - "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) for: "{ 1.2f, 0 }" == "{ 1.2f, 0 }" +ok {test-number} - "{ 1.5f, 0 }" == ::Catch::Detail::stringify(type{1.5f,0}) for: "{ 1.5f, 0 }" == "{ 1.5f, 0 }" # tuple ok {test-number} - "{ 0 }" == ::Catch::Detail::stringify(type{0}) for: "{ 0 }" == "{ 0 }" # tuple ok {test-number} - "{ \"hello\", \"world\" }" == ::Catch::Detail::stringify(type{"hello","world"}) for: "{ "hello", "world" }" == "{ "hello", "world" }" # tuple,tuple<>,float> -ok {test-number} - "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) for: "{ { 42 }, { }, 1.2f }" == "{ { 42 }, { }, 1.2f }" +ok {test-number} - "{ { 42 }, { }, 1.5f }" == ::Catch::Detail::stringify(value) for: "{ { 42 }, { }, 1.5f }" == "{ { 42 }, { }, 1.5f }" # uniform samples ok {test-number} - e.point == 23 for: 23.0 == 23 # uniform samples @@ -4354,7 +4432,11 @@ ok {test-number} - e.upper_bound == 23 for: 23.0 == 23 # uniform samples ok {test-number} - e.lower_bound == 23 for: 23.0 == 23 # uniform samples -ok {test-number} - e.confidence_interval == 0.95 for: 0.95 == 0.95 +ok {test-number} - e.confidence_interval == 0.95 for: 0.94999999999999996 == 0.94999999999999996 +# uniform_integer_distribution can return the bounds +ok {test-number} - dist.a() == -10 for: -10 == -10 +# uniform_integer_distribution can return the bounds +ok {test-number} - dist.b() == 10 for: 10 == 10 # unique_ptr reimplementation: basic functionality ok {test-number} - !(ptr) for: !{?} # unique_ptr reimplementation: basic functionality @@ -4477,5 +4559,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0 ok {test-number} - # xmlentitycheck ok {test-number} - -1..2237 +1..2277 diff --git a/src/external/catch2/tests/SelfTest/Baselines/tap.sw.multi.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/tap.sw.multi.approved.txt index c0e0c4db..3f8f72eb 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/tap.sw.multi.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/tap.sw.multi.approved.txt @@ -476,6 +476,14 @@ ok {test-number} - Nttp_Fixture::value > 0 for: 6 > 0 not ok {test-number} - m_a == 2 for: 1 == 2 # A TEST_CASE_METHOD based test run that succeeds ok {test-number} - m_a == 1 for: 1 == 1 +# A TEST_CASE_PERSISTENT_FIXTURE based test run that fails +ok {test-number} - m_a++ == 0 for: 0 == 0 +# A TEST_CASE_PERSISTENT_FIXTURE based test run that fails +not ok {test-number} - m_a == 0 for: 1 == 0 +# A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds +ok {test-number} - m_a++ == 0 for: 0 == 0 +# A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds +ok {test-number} - m_a == 1 for: 1 == 1 # A Template product test case - Foo ok {test-number} - x.size() == 0 for: 0 == 0 # A Template product test case - Foo @@ -493,17 +501,17 @@ ok {test-number} - x.size() > 0 for: 42 > 0 # A Template product test case with array signature - std::array ok {test-number} - x.size() > 0 for: 9 > 0 # A comparison that uses literals instead of the normal constructor -ok {test-number} - d == 1.23_a for: 1.23 == Approx( 1.23 ) +ok {test-number} - d == 1.23_a for: 1.22999999999999998 == Approx( 1.22999999999999998 ) # A comparison that uses literals instead of the normal constructor -ok {test-number} - d != 1.22_a for: 1.23 != Approx( 1.22 ) +ok {test-number} - d != 1.22_a for: 1.22999999999999998 != Approx( 1.21999999999999997 ) # A comparison that uses literals instead of the normal constructor -ok {test-number} - -d == -1.23_a for: -1.23 == Approx( -1.23 ) +ok {test-number} - -d == -1.23_a for: -1.22999999999999998 == Approx( -1.22999999999999998 ) # A comparison that uses literals instead of the normal constructor -ok {test-number} - d == 1.2_a .epsilon(.1) for: 1.23 == Approx( 1.2 ) +ok {test-number} - d == 1.2_a .epsilon(.1) for: 1.22999999999999998 == Approx( 1.19999999999999996 ) # A comparison that uses literals instead of the normal constructor -ok {test-number} - d != 1.2_a .epsilon(.001) for: 1.23 != Approx( 1.2 ) +ok {test-number} - d != 1.2_a .epsilon(.001) for: 1.22999999999999998 != Approx( 1.19999999999999996 ) # A comparison that uses literals instead of the normal constructor -ok {test-number} - d == 1_a .epsilon(.3) for: 1.23 == Approx( 1.0 ) +ok {test-number} - d == 1_a .epsilon(.3) for: 1.22999999999999998 == Approx( 1.0 ) # A couple of nested sections followed by a failure ok {test-number} - with 1 message: 'that's not flying - that's failing in style' # A couple of nested sections followed by a failure @@ -521,9 +529,9 @@ ok {test-number} - 104.0 == Approx(100.0).margin(4) for: 104.0 == Approx( 100.0 # Absolute margin ok {test-number} - 104.0 != Approx(100.0).margin(3) for: 104.0 != Approx( 100.0 ) # Absolute margin -ok {test-number} - 100.3 != Approx(100.0) for: 100.3 != Approx( 100.0 ) +ok {test-number} - 100.3 != Approx(100.0) for: 100.29999999999999716 != Approx( 100.0 ) # Absolute margin -ok {test-number} - 100.3 == Approx(100.0).margin(0.5) for: 100.3 == Approx( 100.0 ) +ok {test-number} - 100.3 == Approx(100.0).margin(0.5) for: 100.29999999999999716 == Approx( 100.0 ) # An expression with side-effects should only be evaluated once ok {test-number} - i++ == 7 for: 7 == 7 # An expression with side-effects should only be evaluated once @@ -559,15 +567,15 @@ ok {test-number} - 245.0f == Approx(245.25f).margin(0.25f) for: 245.0f == Approx # Approx with exactly-representable margin ok {test-number} - 245.5f == Approx(245.25f).margin(0.25f) for: 245.5f == Approx( 245.25 ) # Approximate PI -ok {test-number} - divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) for: 3.1428571429 == Approx( 3.141 ) +ok {test-number} - divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) for: 3.14285714285714279 == Approx( 3.14100000000000001 ) # Approximate PI -ok {test-number} - divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) for: 3.1428571429 != Approx( 3.141 ) +ok {test-number} - divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) for: 3.14285714285714279 != Approx( 3.14100000000000001 ) # Approximate comparisons with different epsilons -ok {test-number} - d != Approx( 1.231 ) for: 1.23 != Approx( 1.231 ) +ok {test-number} - d != Approx( 1.231 ) for: 1.22999999999999998 != Approx( 1.23100000000000009 ) # Approximate comparisons with different epsilons -ok {test-number} - d == Approx( 1.231 ).epsilon( 0.1 ) for: 1.23 == Approx( 1.231 ) +ok {test-number} - d == Approx( 1.231 ).epsilon( 0.1 ) for: 1.22999999999999998 == Approx( 1.23100000000000009 ) # Approximate comparisons with floats -ok {test-number} - 1.23f == Approx( 1.23f ) for: 1.23f == Approx( 1.2300000191 ) +ok {test-number} - 1.23f == Approx( 1.23f ) for: 1.230000019f == Approx( 1.23000001907348633 ) # Approximate comparisons with floats ok {test-number} - 0.0f == Approx( 0.0f ) for: 0.0f == Approx( 0.0 ) # Approximate comparisons with ints @@ -581,9 +589,9 @@ ok {test-number} - 0 == Approx( dZero) for: 0 == Approx( 0.0 ) # Approximate comparisons with mixed numeric types ok {test-number} - 0 == Approx( dSmall ).margin( 0.001 ) for: 0 == Approx( 0.00001 ) # Approximate comparisons with mixed numeric types -ok {test-number} - 1.234f == Approx( dMedium ) for: 1.234f == Approx( 1.234 ) +ok {test-number} - 1.234f == Approx( dMedium ) for: 1.233999968f == Approx( 1.23399999999999999 ) # Approximate comparisons with mixed numeric types -ok {test-number} - dMedium == Approx( 1.234f ) for: 1.234 == Approx( 1.2339999676 ) +ok {test-number} - dMedium == Approx( 1.234f ) for: 1.23399999999999999 == Approx( 1.23399996757507324 ) # Arbitrary predicate matcher ok {test-number} - 1, Predicate( alwaysTrue, "always true" ) for: 1 matches predicate: "always true" # Arbitrary predicate matcher @@ -657,7 +665,7 @@ ok {test-number} - unrelated::ADL_empty{}, IsEmpty() for: {?} is empty # CAPTURE can deal with complex expressions ok {test-number} - with 7 messages: 'a := 1' and 'b := 2' and 'c := 3' and 'a + b := 3' and 'a+b := 3' and 'c > b := true' and 'a == 1 := true' # CAPTURE can deal with complex expressions involving commas -ok {test-number} - with 7 messages: 'std::vector{1, 2, 3}[0, 1, 2] := 3' and 'std::vector{1, 2, 3}[(0, 1)] := 2' and 'std::vector{1, 2, 3}[0] := 1' and '(helper_1436{12, -12}) := { 12, -12 }' and '(helper_1436(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' +ok {test-number} - with 7 messages: 'custom_index_op{1, 2, 3}[0, 1, 2] := 0' and 'custom_index_op{1, 2, 3}[(0, 1)] := 0' and 'custom_index_op{1, 2, 3}[0] := 0' and '(helper_1436{12, -12}) := { 12, -12 }' and '(helper_1436(-12, 12)) := { -12, 12 }' and '(1, 2) := 2' and '(2, 3) := 3' # CAPTURE parses string and character constants ok {test-number} - with 11 messages: '("comma, in string", "escaped, \", ") := "escaped, ", "' and '"single quote in string,'," := "single quote in string,',"' and '"some escapes, \\,\\\\" := "some escapes, \,\\"' and '"some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<"' and ''"' := '"'' and ''\'' := '''' and '',' := ','' and ''}' := '}'' and '')' := ')'' and ''(' := '('' and ''{' := '{'' # Capture and info messages @@ -695,33 +703,37 @@ ok {test-number} - lt( "A", "b" ) for: true # CaseInsensitiveLess is case insensitive ok {test-number} - lt( "A", "B" ) for: true # Character pretty printing -ok {test-number} - tab == '\t' for: '\t' == '\t' -# Character pretty printing -ok {test-number} - newline == '\n' for: '\n' == '\n' -# Character pretty printing -ok {test-number} - carr_return == '\r' for: '\r' == '\r' -# Character pretty printing -ok {test-number} - form_feed == '\f' for: '\f' == '\f' +ok {test-number} - ::Catch::Detail::stringify('\t') == "'\\t'" for: "'\t'" == "'\t'" # Character pretty printing -ok {test-number} - space == ' ' for: ' ' == ' ' +ok {test-number} - ::Catch::Detail::stringify('\n') == "'\\n'" for: "'\n'" == "'\n'" # Character pretty printing -ok {test-number} - c == chars[i] for: 'a' == 'a' +ok {test-number} - ::Catch::Detail::stringify('\r') == "'\\r'" for: "'\r'" == "'\r'" # Character pretty printing -ok {test-number} - c == chars[i] for: 'z' == 'z' +ok {test-number} - ::Catch::Detail::stringify('\f') == "'\\f'" for: "'\f'" == "'\f'" # Character pretty printing -ok {test-number} - c == chars[i] for: 'A' == 'A' +ok {test-number} - ::Catch::Detail::stringify( ' ' ) == "' '" for: "' '" == "' '" # Character pretty printing -ok {test-number} - c == chars[i] for: 'Z' == 'Z' +ok {test-number} - ::Catch::Detail::stringify( 'A' ) == "'A'" for: "'A'" == "'A'" # Character pretty printing -ok {test-number} - null_terminator == '\0' for: 0 == 0 +ok {test-number} - ::Catch::Detail::stringify( 'z' ) == "'z'" for: "'z'" == "'z'" # Character pretty printing -ok {test-number} - c == i for: 2 == 2 +ok {test-number} - ::Catch::Detail::stringify( '\0' ) == "0" for: "0" == "0" # Character pretty printing -ok {test-number} - c == i for: 3 == 3 +ok {test-number} - ::Catch::Detail::stringify( static_cast(2) ) == "2" for: "2" == "2" # Character pretty printing -ok {test-number} - c == i for: 4 == 4 -# Character pretty printing -ok {test-number} - c == i for: 5 == 5 +ok {test-number} - ::Catch::Detail::stringify( static_cast(5) ) == "5" for: "5" == "5" +# Clara::Arg does not crash on incomplete input +ok {test-number} - name.empty() for: true +# Clara::Arg does not crash on incomplete input +ok {test-number} - result for: {?} +# Clara::Arg does not crash on incomplete input +ok {test-number} - result.type() == Catch::Clara::Detail::ResultType::Ok for: 0 == 0 +# Clara::Arg does not crash on incomplete input +ok {test-number} - parsed.type() == Catch::Clara::ParseResultType::NoMatch for: 1 == 1 +# Clara::Arg does not crash on incomplete input +ok {test-number} - parsed.remainingTokens().count() == 2 for: 2 == 2 +# Clara::Arg does not crash on incomplete input +ok {test-number} - name.empty() for: true # Clara::Arg supports single-arg parse the way Opt does ok {test-number} - name.empty() for: true # Clara::Arg supports single-arg parse the way Opt does @@ -973,7 +985,7 @@ not ok {test-number} - unexpected exception with message: 'custom exception - no # Custom std-exceptions can be custom translated not ok {test-number} - unexpected exception with message: 'custom std exception' # Default scale is invisible to comparison -ok {test-number} - 101.000001 != Approx(100).epsilon(0.01) for: 101.000001 != Approx( 100.0 ) +ok {test-number} - 101.000001 != Approx(100).epsilon(0.01) for: 101.00000099999999748 != Approx( 100.0 ) # Default scale is invisible to comparison ok {test-number} - std::pow(10, -5) != Approx(std::pow(10, -7)) for: 0.00001 != Approx( 0.0000001 ) # Directly creating an EnumInfo @@ -982,10 +994,10 @@ ok {test-number} - enumInfo->lookup(0) == "Value1" for: Value1 == "Value1" ok {test-number} - enumInfo->lookup(1) == "Value2" for: Value2 == "Value2" # Directly creating an EnumInfo ok {test-number} - enumInfo->lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **} == "{** unexpected enum value **}" +# Empty generators can SKIP in constructor +ok {test-number} - # SKIP 'This generator is empty' # Empty stream name opens cout stream ok {test-number} - Catch::makeStream( "" )->isConsole() for: true -# Empty tag is not allowed -ok {test-number} - Catch::TestCaseInfo( "", { "fake test name", "[]" }, dummySourceLineInfo ) # EndsWith string matcher not ok {test-number} - testStringForMatching(), EndsWith( "Substring" ) for: "this string contains 'abc' as a substring" ends with: "Substring" # EndsWith string matcher @@ -1005,7 +1017,7 @@ ok {test-number} - stringify( Bikeshed::Colours::Red ) == "Red" for: "Red" == "R # Enums in namespaces can quickly have stringification enabled using REGISTER_ENUM ok {test-number} - stringify( Bikeshed::Colours::Blue ) == "Blue" for: "Blue" == "Blue" # Epsilon only applies to Approx's value -ok {test-number} - 101.01 != Approx(100).epsilon(0.01) for: 101.01 != Approx( 100.0 ) +ok {test-number} - 101.01 != Approx(100).epsilon(0.01) for: 101.01000000000000512 != Approx( 100.0 ) # Equality checks that should fail not ok {test-number} - data.int_seven == 6 for: 7 == 6 # Equality checks that should fail @@ -1013,15 +1025,15 @@ not ok {test-number} - data.int_seven == 8 for: 7 == 8 # Equality checks that should fail not ok {test-number} - data.int_seven == 0 for: 7 == 0 # Equality checks that should fail -not ok {test-number} - data.float_nine_point_one == Approx( 9.11f ) for: 9.1f == Approx( 9.1099996567 ) +not ok {test-number} - data.float_nine_point_one == Approx( 9.11f ) for: 9.100000381f == Approx( 9.10999965667724609 ) # Equality checks that should fail -not ok {test-number} - data.float_nine_point_one == Approx( 9.0f ) for: 9.1f == Approx( 9.0 ) +not ok {test-number} - data.float_nine_point_one == Approx( 9.0f ) for: 9.100000381f == Approx( 9.0 ) # Equality checks that should fail -not ok {test-number} - data.float_nine_point_one == Approx( 1 ) for: 9.1f == Approx( 1.0 ) +not ok {test-number} - data.float_nine_point_one == Approx( 1 ) for: 9.100000381f == Approx( 1.0 ) # Equality checks that should fail -not ok {test-number} - data.float_nine_point_one == Approx( 0 ) for: 9.1f == Approx( 0.0 ) +not ok {test-number} - data.float_nine_point_one == Approx( 0 ) for: 9.100000381f == Approx( 0.0 ) # Equality checks that should fail -not ok {test-number} - data.double_pi == Approx( 3.1415 ) for: 3.1415926535 == Approx( 3.1415 ) +not ok {test-number} - data.double_pi == Approx( 3.1415 ) for: 3.14159265350000005 == Approx( 3.14150000000000018 ) # Equality checks that should fail not ok {test-number} - data.str_hello == "goodbye" for: "hello" == "goodbye" # Equality checks that should fail @@ -1031,13 +1043,13 @@ not ok {test-number} - data.str_hello == "hello1" for: "hello" == "hello1" # Equality checks that should fail not ok {test-number} - data.str_hello.size() == 6 for: 5 == 6 # Equality checks that should fail -not ok {test-number} - x == Approx( 1.301 ) for: 1.3 == Approx( 1.301 ) +not ok {test-number} - x == Approx( 1.301 ) for: 1.30000000000000027 == Approx( 1.30099999999999993 ) # Equality checks that should succeed ok {test-number} - data.int_seven == 7 for: 7 == 7 # Equality checks that should succeed -ok {test-number} - data.float_nine_point_one == Approx( 9.1f ) for: 9.1f == Approx( 9.1000003815 ) +ok {test-number} - data.float_nine_point_one == Approx( 9.1f ) for: 9.100000381f == Approx( 9.10000038146972656 ) # Equality checks that should succeed -ok {test-number} - data.double_pi == Approx( 3.1415926535 ) for: 3.1415926535 == Approx( 3.1415926535 ) +ok {test-number} - data.double_pi == Approx( 3.1415926535 ) for: 3.14159265350000005 == Approx( 3.14159265350000005 ) # Equality checks that should succeed ok {test-number} - data.str_hello == "hello" for: "hello" == "hello" # Equality checks that should succeed @@ -1045,7 +1057,7 @@ ok {test-number} - "hello" == data.str_hello for: "hello" == "hello" # Equality checks that should succeed ok {test-number} - data.str_hello.size() == 5 for: 5 == 5 # Equality checks that should succeed -ok {test-number} - x == Approx( 1.3 ) for: 1.3 == Approx( 1.3 ) +ok {test-number} - x == Approx( 1.3 ) for: 1.30000000000000027 == Approx( 1.30000000000000004 ) # Equals ok {test-number} - testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" # Equals @@ -1131,23 +1143,23 @@ ok {test-number} - Factorial(10) == 3628800 for: 3628800 (0x) == 362 # Filter generator throws exception for empty generator ok {test-number} - filter( []( int ) { return false; }, value( 3 ) ), Catch::GeneratorException # Floating point matchers: double -ok {test-number} - 10., WithinRel( 11.1, 0.1 ) for: 10.0 and 11.1 are within 10% of each other +ok {test-number} - 10., WithinRel( 11.1, 0.1 ) for: 10.0 and 11.09999999999999964 are within 10% of each other # Floating point matchers: double -ok {test-number} - 10., !WithinRel( 11.2, 0.1 ) for: 10.0 not and 11.2 are within 10% of each other +ok {test-number} - 10., !WithinRel( 11.2, 0.1 ) for: 10.0 not and 11.19999999999999929 are within 10% of each other # Floating point matchers: double -ok {test-number} - 1., !WithinRel( 0., 0.99 ) for: 1.0 not and 0 are within 99% of each other +ok {test-number} - 1., !WithinRel( 0., 0.99 ) for: 1.0 not and 0.0 are within 99% of each other # Floating point matchers: double -ok {test-number} - -0., WithinRel( 0. ) for: -0.0 and 0 are within 2.22045e-12% of each other +ok {test-number} - -0., WithinRel( 0. ) for: -0.0 and 0.0 are within 2.22045e-12% of each other # Floating point matchers: double -ok {test-number} - v1, WithinRel( v2 ) for: 0.0 and 2.22507e-308 are within 2.22045e-12% of each other +ok {test-number} - v1, WithinRel( v2 ) for: 0.0 and 0.0 are within 2.22045e-12% of each other # Floating point matchers: double ok {test-number} - 1., WithinAbs( 1., 0 ) for: 1.0 is within 0.0 of 1.0 # Floating point matchers: double ok {test-number} - 0., WithinAbs( 1., 1 ) for: 0.0 is within 1.0 of 1.0 # Floating point matchers: double -ok {test-number} - 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0 +ok {test-number} - 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.98999999999999999 of 1.0 # Floating point matchers: double -ok {test-number} - 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0 +ok {test-number} - 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.98999999999999999 of 1.0 # Floating point matchers: double ok {test-number} - 11., !WithinAbs( 10., 0.5 ) for: 11.0 not is within 0.5 of 10.0 # Floating point matchers: double @@ -1155,11 +1167,11 @@ ok {test-number} - 10., !WithinAbs( 11., 0.5 ) for: 10.0 not is within 0.5 of 11 # Floating point matchers: double ok {test-number} - -10., WithinAbs( -10., 0.5 ) for: -10.0 is within 0.5 of -10.0 # Floating point matchers: double -ok {test-number} - -10., WithinAbs( -9.6, 0.5 ) for: -10.0 is within 0.5 of -9.6 +ok {test-number} - -10., WithinAbs( -9.6, 0.5 ) for: -10.0 is within 0.5 of -9.59999999999999964 # Floating point matchers: double ok {test-number} - 1., WithinULP( 1., 0 ) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) # Floating point matchers: double -ok {test-number} - nextafter( 1., 2. ), WithinULP( 1., 1 ) for: 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) +ok {test-number} - nextafter( 1., 2. ), WithinULP( 1., 1 ) for: 1.00000000000000022 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) # Floating point matchers: double ok {test-number} - 0., WithinULP( nextafter( 0., 1. ), 1 ) for: 0.0 is within 1 ULPs of 4.9406564584124654e-324 ([0.0000000000000000e+00, 9.8813129168249309e-324]) # Floating point matchers: double @@ -1175,7 +1187,7 @@ ok {test-number} - 1., WithinAbs( 1., 0.5 ) || WithinULP( 2., 1 ) for: 1.0 ( is # Floating point matchers: double ok {test-number} - 1., WithinAbs( 2., 0.5 ) || WithinULP( 1., 0 ) for: 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) ) # Floating point matchers: double -ok {test-number} - 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) for: 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) +ok {test-number} - 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) for: 0.0001 ( is within 0.001 of 0.0 or and 0.0 are within 10% of each other ) # Floating point matchers: double ok {test-number} - WithinAbs( 1., 0. ) # Floating point matchers: double @@ -1191,23 +1203,23 @@ ok {test-number} - WithinRel( 1., 1. ), std::domain_error # Floating point matchers: double ok {test-number} - 1., !IsNaN() for: 1.0 not is NaN # Floating point matchers: float -ok {test-number} - 10.f, WithinRel( 11.1f, 0.1f ) for: 10.0f and 11.1 are within 10% of each other +ok {test-number} - 10.f, WithinRel( 11.1f, 0.1f ) for: 10.0f and 11.10000038146972656 are within 10% of each other # Floating point matchers: float -ok {test-number} - 10.f, !WithinRel( 11.2f, 0.1f ) for: 10.0f not and 11.2 are within 10% of each other +ok {test-number} - 10.f, !WithinRel( 11.2f, 0.1f ) for: 10.0f not and 11.19999980926513672 are within 10% of each other # Floating point matchers: float -ok {test-number} - 1.f, !WithinRel( 0.f, 0.99f ) for: 1.0f not and 0 are within 99% of each other +ok {test-number} - 1.f, !WithinRel( 0.f, 0.99f ) for: 1.0f not and 0.0 are within 99% of each other # Floating point matchers: float -ok {test-number} - -0.f, WithinRel( 0.f ) for: -0.0f and 0 are within 0.00119209% of each other +ok {test-number} - -0.f, WithinRel( 0.f ) for: -0.0f and 0.0 are within 0.00119209% of each other # Floating point matchers: float -ok {test-number} - v1, WithinRel( v2 ) for: 0.0f and 1.17549e-38 are within 0.00119209% of each other +ok {test-number} - v1, WithinRel( v2 ) for: 0.0f and 0.0 are within 0.00119209% of each other # Floating point matchers: float ok {test-number} - 1.f, WithinAbs( 1.f, 0 ) for: 1.0f is within 0.0 of 1.0 # Floating point matchers: float ok {test-number} - 0.f, WithinAbs( 1.f, 1 ) for: 0.0f is within 1.0 of 1.0 # Floating point matchers: float -ok {test-number} - 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0 +ok {test-number} - 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.99000000953674316 of 1.0 # Floating point matchers: float -ok {test-number} - 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0 +ok {test-number} - 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.99000000953674316 of 1.0 # Floating point matchers: float ok {test-number} - 0.f, WithinAbs( -0.f, 0 ) for: 0.0f is within 0.0 of -0.0 # Floating point matchers: float @@ -1217,13 +1229,13 @@ ok {test-number} - 10.f, !WithinAbs( 11.f, 0.5f ) for: 10.0f not is within 0.5 o # Floating point matchers: float ok {test-number} - -10.f, WithinAbs( -10.f, 0.5f ) for: -10.0f is within 0.5 of -10.0 # Floating point matchers: float -ok {test-number} - -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of -9.6000003815 +ok {test-number} - -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of -9.60000038146972656 # Floating point matchers: float ok {test-number} - 1.f, WithinULP( 1.f, 0 ) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) # Floating point matchers: float ok {test-number} - -1.f, WithinULP( -1.f, 0 ) for: -1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+00]) # Floating point matchers: float -ok {test-number} - nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) +ok {test-number} - nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.000000119f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) # Floating point matchers: float ok {test-number} - 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) for: 0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45]) # Floating point matchers: float @@ -1239,7 +1251,7 @@ ok {test-number} - 1.f, WithinAbs( 1.f, 0.5 ) || WithinULP( 1.f, 1 ) for: 1.0f ( # Floating point matchers: float ok {test-number} - 1.f, WithinAbs( 2.f, 0.5 ) || WithinULP( 1.f, 0 ) for: 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) ) # Floating point matchers: float -ok {test-number} - 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) for: 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) +ok {test-number} - 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) for: 0.0001f ( is within 0.00100000004749745 of 0.0 or and 0.0 are within 10% of each other ) # Floating point matchers: float ok {test-number} - WithinAbs( 1.f, 0.f ) # Floating point matchers: float @@ -1256,6 +1268,14 @@ ok {test-number} - WithinRel( 1.f, -0.2f ), std::domain_error ok {test-number} - WithinRel( 1.f, 1.f ), std::domain_error # Floating point matchers: float ok {test-number} - 1., !IsNaN() for: 1.0 not is NaN +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 +# GENERATE can combine literals and generators +ok {test-number} - i % 2 == 0 for: 0 == 0 # Generators -- adapters ok {test-number} - i % 2 == 0 for: 0 == 0 # Generators -- adapters @@ -1591,83 +1611,83 @@ ok {test-number} - gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) wit # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.9 == Approx( -0.9 ) with 1 message: 'Current expected value is -0.9' +ok {test-number} - gen.get() == Approx(expected) for: -0.90000000000000002 == Approx( -0.90000000000000002 ) with 1 message: 'Current expected value is -0.9' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.9' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.8 == Approx( -0.8 ) with 1 message: 'Current expected value is -0.8' +ok {test-number} - gen.get() == Approx(expected) for: -0.80000000000000004 == Approx( -0.80000000000000004 ) with 1 message: 'Current expected value is -0.8' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.8' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +ok {test-number} - gen.get() == Approx(expected) for: -0.70000000000000007 == Approx( -0.70000000000000007 ) with 1 message: 'Current expected value is -0.7' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.7' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.6 == Approx( -0.6 ) with 1 message: 'Current expected value is -0.6' +ok {test-number} - gen.get() == Approx(expected) for: -0.60000000000000009 == Approx( -0.60000000000000009 ) with 1 message: 'Current expected value is -0.6' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.6' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.5 == Approx( -0.5 ) with 1 message: 'Current expected value is -0.5' +ok {test-number} - gen.get() == Approx(expected) for: -0.50000000000000011 == Approx( -0.50000000000000011 ) with 1 message: 'Current expected value is -0.5' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.5' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +ok {test-number} - gen.get() == Approx(expected) for: -0.40000000000000013 == Approx( -0.40000000000000013 ) with 1 message: 'Current expected value is -0.4' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.4' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.3 == Approx( -0.3 ) with 1 message: 'Current expected value is -0.3' +ok {test-number} - gen.get() == Approx(expected) for: -0.30000000000000016 == Approx( -0.30000000000000016 ) with 1 message: 'Current expected value is -0.3' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.3' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.2 == Approx( -0.2 ) with 1 message: 'Current expected value is -0.2' +ok {test-number} - gen.get() == Approx(expected) for: -0.20000000000000015 == Approx( -0.20000000000000015 ) with 1 message: 'Current expected value is -0.2' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.2' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +ok {test-number} - gen.get() == Approx(expected) for: -0.10000000000000014 == Approx( -0.10000000000000014 ) with 1 message: 'Current expected value is -0.1' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.0 == Approx( -0.0 ) with 1 message: 'Current expected value is -1.38778e-16' +ok {test-number} - gen.get() == Approx(expected) for: -0.00000000000000014 == Approx( -0.00000000000000014 ) with 1 message: 'Current expected value is -1.38778e-16' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -1.38778e-16' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.1 == Approx( 0.1 ) with 1 message: 'Current expected value is 0.1' +ok {test-number} - gen.get() == Approx(expected) for: 0.09999999999999987 == Approx( 0.09999999999999987 ) with 1 message: 'Current expected value is 0.1' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +ok {test-number} - gen.get() == Approx(expected) for: 0.19999999999999987 == Approx( 0.19999999999999987 ) with 1 message: 'Current expected value is 0.2' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.2' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.3 == Approx( 0.3 ) with 1 message: 'Current expected value is 0.3' +ok {test-number} - gen.get() == Approx(expected) for: 0.29999999999999988 == Approx( 0.29999999999999988 ) with 1 message: 'Current expected value is 0.3' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.3' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.4 == Approx( 0.4 ) with 1 message: 'Current expected value is 0.4' +ok {test-number} - gen.get() == Approx(expected) for: 0.39999999999999991 == Approx( 0.39999999999999991 ) with 1 message: 'Current expected value is 0.4' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.4' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.5 == Approx( 0.5 ) with 1 message: 'Current expected value is 0.5' +ok {test-number} - gen.get() == Approx(expected) for: 0.49999999999999989 == Approx( 0.49999999999999989 ) with 1 message: 'Current expected value is 0.5' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.5' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.6 == Approx( 0.6 ) with 1 message: 'Current expected value is 0.6' +ok {test-number} - gen.get() == Approx(expected) for: 0.59999999999999987 == Approx( 0.59999999999999987 ) with 1 message: 'Current expected value is 0.6' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.6' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.7 == Approx( 0.7 ) with 1 message: 'Current expected value is 0.7' +ok {test-number} - gen.get() == Approx(expected) for: 0.69999999999999984 == Approx( 0.69999999999999984 ) with 1 message: 'Current expected value is 0.7' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.7' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.8 == Approx( 0.8 ) with 1 message: 'Current expected value is 0.8' +ok {test-number} - gen.get() == Approx(expected) for: 0.79999999999999982 == Approx( 0.79999999999999982 ) with 1 message: 'Current expected value is 0.8' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.8' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.9 == Approx( 0.9 ) with 1 message: 'Current expected value is 0.9' +ok {test-number} - gen.get() == Approx(expected) for: 0.8999999999999998 == Approx( 0.8999999999999998 ) with 1 message: 'Current expected value is 0.9' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.9' # Generators internals -ok {test-number} - gen.get() == Approx( rangeEnd ) for: 1.0 == Approx( 1.0 ) +ok {test-number} - gen.get() == Approx( rangeEnd ) for: 0.99999999999999978 == Approx( 1.0 ) # Generators internals ok {test-number} - !(gen.next()) for: !false # Generators internals @@ -1675,19 +1695,19 @@ ok {test-number} - gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) wit # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +ok {test-number} - gen.get() == Approx(expected) for: -0.69999999999999996 == Approx( -0.69999999999999996 ) with 1 message: 'Current expected value is -0.7' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.7' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +ok {test-number} - gen.get() == Approx(expected) for: -0.39999999999999997 == Approx( -0.39999999999999997 ) with 1 message: 'Current expected value is -0.4' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.4' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +ok {test-number} - gen.get() == Approx(expected) for: -0.09999999999999998 == Approx( -0.09999999999999998 ) with 1 message: 'Current expected value is -0.1' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +ok {test-number} - gen.get() == Approx(expected) for: 0.20000000000000001 == Approx( 0.20000000000000001 ) with 1 message: 'Current expected value is 0.2' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.2' # Generators internals @@ -1701,19 +1721,19 @@ ok {test-number} - gen.get() == Approx(expected) for: -1.0 == Approx( -1.0 ) wit # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.7 == Approx( -0.7 ) with 1 message: 'Current expected value is -0.7' +ok {test-number} - gen.get() == Approx(expected) for: -0.69999999999999996 == Approx( -0.69999999999999996 ) with 1 message: 'Current expected value is -0.7' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.7' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.4 == Approx( -0.4 ) with 1 message: 'Current expected value is -0.4' +ok {test-number} - gen.get() == Approx(expected) for: -0.39999999999999997 == Approx( -0.39999999999999997 ) with 1 message: 'Current expected value is -0.4' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.4' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: -0.1 == Approx( -0.1 ) with 1 message: 'Current expected value is -0.1' +ok {test-number} - gen.get() == Approx(expected) for: -0.09999999999999998 == Approx( -0.09999999999999998 ) with 1 message: 'Current expected value is -0.1' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is -0.1' # Generators internals -ok {test-number} - gen.get() == Approx(expected) for: 0.2 == Approx( 0.2 ) with 1 message: 'Current expected value is 0.2' +ok {test-number} - gen.get() == Approx(expected) for: 0.20000000000000001 == Approx( 0.20000000000000001 ) with 1 message: 'Current expected value is 0.2' # Generators internals ok {test-number} - gen.next() for: true with 1 message: 'Current expected value is 0.2' # Generators internals @@ -1775,13 +1795,13 @@ ok {test-number} - gen.get() == -7 for: -7 == -7 # Generators internals ok {test-number} - !(gen.next()) for: !false # Greater-than inequalities with different epsilons -ok {test-number} - d >= Approx( 1.22 ) for: 1.23 >= Approx( 1.22 ) +ok {test-number} - d >= Approx( 1.22 ) for: 1.22999999999999998 >= Approx( 1.21999999999999997 ) # Greater-than inequalities with different epsilons -ok {test-number} - d >= Approx( 1.23 ) for: 1.23 >= Approx( 1.23 ) +ok {test-number} - d >= Approx( 1.23 ) for: 1.22999999999999998 >= Approx( 1.22999999999999998 ) # Greater-than inequalities with different epsilons -ok {test-number} - !(d >= Approx( 1.24 )) for: !(1.23 >= Approx( 1.24 )) +ok {test-number} - !(d >= Approx( 1.24 )) for: !(1.22999999999999998 >= Approx( 1.23999999999999999 )) # Greater-than inequalities with different epsilons -ok {test-number} - d >= Approx( 1.24 ).epsilon(0.1) for: 1.23 >= Approx( 1.24 ) +ok {test-number} - d >= Approx( 1.24 ).epsilon(0.1) for: 1.22999999999999998 >= Approx( 1.23999999999999999 ) # Hashers with different seed produce different hash with same test case ok {test-number} - h1( dummy ) != h2( dummy ) for: 3422778688 (0x) != 130711275 (0x) # Hashers with same seed produce same hash @@ -1794,6 +1814,8 @@ ok {test-number} - h( dummy1 ) != h( dummy2 ) for: 2673152918 (0x) ! ok {test-number} - h( dummy1 ) != h( dummy2 ) for: 2074929312 (0x) != 3429949824 (0x) # Hashing test case produces same hash across multiple calls ok {test-number} - h( dummy ) == h( dummy ) for: 3422778688 (0x) == 3422778688 (0x) +# INFO and UNSCOPED_INFO can stream multiple arguments +not ok {test-number} - explicitly with 3 messages: 'This info has multiple parts.' and 'This unscoped info has multiple parts.' and 'Show infos!' # INFO and WARN do not abort tests warning {test-number} - 'this is a message' with 1 message: 'this is a warning' # INFO gets logged on failure @@ -1828,12 +1850,14 @@ ok {test-number} - i < 10 for: 8 < 10 with 2 messages: 'current counter 8' and ' ok {test-number} - i < 10 for: 9 < 10 with 2 messages: 'current counter 9' and 'i := 9' # INFO is reset for each loop not ok {test-number} - i < 10 for: 10 < 10 with 2 messages: 'current counter 10' and 'i := 10' +# Incomplete AssertionHandler +not ok {test-number} - unexpected exception with message: 'Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE'; expression was: Dummy # Inequality checks that should fail not ok {test-number} - data.int_seven != 7 for: 7 != 7 # Inequality checks that should fail -not ok {test-number} - data.float_nine_point_one != Approx( 9.1f ) for: 9.1f != Approx( 9.1000003815 ) +not ok {test-number} - data.float_nine_point_one != Approx( 9.1f ) for: 9.100000381f != Approx( 9.10000038146972656 ) # Inequality checks that should fail -not ok {test-number} - data.double_pi != Approx( 3.1415926535 ) for: 3.1415926535 != Approx( 3.1415926535 ) +not ok {test-number} - data.double_pi != Approx( 3.1415926535 ) for: 3.14159265350000005 != Approx( 3.14159265350000005 ) # Inequality checks that should fail not ok {test-number} - data.str_hello != "hello" for: "hello" != "hello" # Inequality checks that should fail @@ -1843,15 +1867,15 @@ ok {test-number} - data.int_seven != 6 for: 7 != 6 # Inequality checks that should succeed ok {test-number} - data.int_seven != 8 for: 7 != 8 # Inequality checks that should succeed -ok {test-number} - data.float_nine_point_one != Approx( 9.11f ) for: 9.1f != Approx( 9.1099996567 ) +ok {test-number} - data.float_nine_point_one != Approx( 9.11f ) for: 9.100000381f != Approx( 9.10999965667724609 ) # Inequality checks that should succeed -ok {test-number} - data.float_nine_point_one != Approx( 9.0f ) for: 9.1f != Approx( 9.0 ) +ok {test-number} - data.float_nine_point_one != Approx( 9.0f ) for: 9.100000381f != Approx( 9.0 ) # Inequality checks that should succeed -ok {test-number} - data.float_nine_point_one != Approx( 1 ) for: 9.1f != Approx( 1.0 ) +ok {test-number} - data.float_nine_point_one != Approx( 1 ) for: 9.100000381f != Approx( 1.0 ) # Inequality checks that should succeed -ok {test-number} - data.float_nine_point_one != Approx( 0 ) for: 9.1f != Approx( 0.0 ) +ok {test-number} - data.float_nine_point_one != Approx( 0 ) for: 9.100000381f != Approx( 0.0 ) # Inequality checks that should succeed -ok {test-number} - data.double_pi != Approx( 3.1415 ) for: 3.1415926535 != Approx( 3.1415 ) +ok {test-number} - data.double_pi != Approx( 3.1415 ) for: 3.14159265350000005 != Approx( 3.14150000000000018 ) # Inequality checks that should succeed ok {test-number} - data.str_hello != "goodbye" for: "hello" != "goodbye" # Inequality checks that should succeed @@ -1860,16 +1884,52 @@ ok {test-number} - data.str_hello != "hell" for: "hello" != "hell" ok {test-number} - data.str_hello != "hello1" for: "hello" != "hello1" # Inequality checks that should succeed ok {test-number} - data.str_hello.size() != 6 for: 5 != 6 +# JsonWriter +ok {test-number} - stream.str() == "" for: "" == "" +# JsonWriter +ok {test-number} - stream.str() == "{\n}" for: "{ }" == "{ }" +# JsonWriter +ok {test-number} - stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) for: "{ "int": 1, "double": 1.5, "true": true, "false": false, "string": "this is a string", "array": [ 1, 2 ] }" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ 1, 2 ] }" ) +# JsonWriter +ok {test-number} - stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) for: "{ "empty_object": { }, "fully_object": { "key": 1 } }" ( contains: ""empty_object": { }," and contains: ""fully_object": { "key": 1 }" ) +# JsonWriter +ok {test-number} - stream.str() == "[\n]" for: "[ ]" == "[ ]" +# JsonWriter +ok {test-number} - stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" for: "[ 1, 1.5, true, false, "this is a string", { "object": 42 }, [ "array", 42.5 ] ]" == "[ 1, 1.5, true, false, "this is a string", { "object": 42 }, [ "array", 42.5 ] ]" +# JsonWriter +ok {test-number} - stream.str() == "{\n}" for: "{ }" == "{ }" +# JsonWriter +ok {test-number} - stream.str() == "[\n]" for: "[ ]" == "[ ]" +# JsonWriter +ok {test-number} - stream.str() == "\"custom\"" for: ""custom"" == ""custom"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\"\"" for: ""\""" == ""\""" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\\\"" for: ""\\"" == ""\\"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"/\"" for: ""/"" == ""/"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\b\"" for: ""\b"" == ""\b"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\f\"" for: ""\f"" == ""\f"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\n\"" for: ""\n"" == ""\n"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\r\"" for: ""\r"" == ""\r"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\t\"" for: ""\t"" == ""\t"" +# JsonWriter escapes charaters in strings properly +ok {test-number} - sstream.str() == "\"\\\\/\\t\\r\\n\"" for: ""\\/\t\r\n"" == ""\\/\t\r\n"" # Lambdas in assertions ok {test-number} - []() { return true; }() for: true # Less-than inequalities with different epsilons -ok {test-number} - d <= Approx( 1.24 ) for: 1.23 <= Approx( 1.24 ) +ok {test-number} - d <= Approx( 1.24 ) for: 1.22999999999999998 <= Approx( 1.23999999999999999 ) # Less-than inequalities with different epsilons -ok {test-number} - d <= Approx( 1.23 ) for: 1.23 <= Approx( 1.23 ) +ok {test-number} - d <= Approx( 1.23 ) for: 1.22999999999999998 <= Approx( 1.22999999999999998 ) # Less-than inequalities with different epsilons -ok {test-number} - !(d <= Approx( 1.22 )) for: !(1.23 <= Approx( 1.22 )) +ok {test-number} - !(d <= Approx( 1.22 )) for: !(1.22999999999999998 <= Approx( 1.21999999999999997 )) # Less-than inequalities with different epsilons -ok {test-number} - d <= Approx( 1.22 ).epsilon(0.1) for: 1.23 <= Approx( 1.22 ) +ok {test-number} - d <= Approx( 1.22 ).epsilon(0.1) for: 1.22999999999999998 <= Approx( 1.21999999999999997 ) # ManuallyRegistered ok {test-number} - with 1 message: 'was called' # Matchers can be (AllOf) composed with the && operator @@ -1999,11 +2059,11 @@ not ok {test-number} - data.int_seven >= 8 for: 7 >= 8 # Ordering comparison checks that should fail not ok {test-number} - data.int_seven <= 6 for: 7 <= 6 # Ordering comparison checks that should fail -not ok {test-number} - data.float_nine_point_one < 9 for: 9.1f < 9 +not ok {test-number} - data.float_nine_point_one < 9 for: 9.100000381f < 9 # Ordering comparison checks that should fail -not ok {test-number} - data.float_nine_point_one > 10 for: 9.1f > 10 +not ok {test-number} - data.float_nine_point_one > 10 for: 9.100000381f > 10 # Ordering comparison checks that should fail -not ok {test-number} - data.float_nine_point_one > 9.2 for: 9.1f > 9.2 +not ok {test-number} - data.float_nine_point_one > 9.2 for: 9.100000381f > 9.19999999999999929 # Ordering comparison checks that should fail not ok {test-number} - data.str_hello > "hello" for: "hello" > "hello" # Ordering comparison checks that should fail @@ -2037,11 +2097,11 @@ ok {test-number} - data.int_seven <= 7 for: 7 <= 7 # Ordering comparison checks that should succeed ok {test-number} - data.int_seven <= 8 for: 7 <= 8 # Ordering comparison checks that should succeed -ok {test-number} - data.float_nine_point_one > 9 for: 9.1f > 9 +ok {test-number} - data.float_nine_point_one > 9 for: 9.100000381f > 9 # Ordering comparison checks that should succeed -ok {test-number} - data.float_nine_point_one < 10 for: 9.1f < 10 +ok {test-number} - data.float_nine_point_one < 10 for: 9.100000381f < 10 # Ordering comparison checks that should succeed -ok {test-number} - data.float_nine_point_one < 9.2 for: 9.1f < 9.2 +ok {test-number} - data.float_nine_point_one < 9.2 for: 9.100000381f < 9.19999999999999929 # Ordering comparison checks that should succeed ok {test-number} - data.str_hello <= "hello" for: "hello" <= "hello" # Ordering comparison checks that should succeed @@ -2377,7 +2437,7 @@ ok {test-number} - config.benchmarkResamples == 20000 for: 20000 (0x # Process can be configured on command line ok {test-number} - cli.parse({ "test", "--benchmark-confidence-interval=0.99" }) for: {?} # Process can be configured on command line -ok {test-number} - config.benchmarkConfidenceInterval == Catch::Approx(0.99) for: 0.99 == Approx( 0.99 ) +ok {test-number} - config.benchmarkConfidenceInterval == Catch::Approx(0.99) for: 0.98999999999999999 == Approx( 0.98999999999999999 ) # Process can be configured on command line ok {test-number} - cli.parse({ "test", "--benchmark-no-analysis" }) for: {?} # Process can be configured on command line @@ -2453,6 +2513,18 @@ ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && Cont # Reporter's write listings to provided stream ok {test-number} - !(factories.empty()) for: !false # Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "" }, "listings": { "tags": [ { "aliases": [ "fakeTag" ], "count": 1 } ]" contains: "fakeTag" with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring("fake reporter"s) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "" }, "listings": { "reporters": [ { "name": "fake reporter", "description": "fake description" } ]" contains: "fake reporter" with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream +ok {test-number} - listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) for: "{ "version": 1, "metadata": { "name": "", "rng-seed": 1234, "catch2-version": "" }, "listings": { "tests": [ { "name": "fake test name", "class-name": "", "tags": [ "fakeTestTag" ], "source-location": { "filename": "fake-file.cpp", "line": 123456789 } } ]" ( contains: "fake test name" and contains: "fakeTestTag" ) with 1 message: 'Tested reporter: JSON' +# Reporter's write listings to provided stream +ok {test-number} - !(factories.empty()) for: !false +# Reporter's write listings to provided stream ok {test-number} - listingString, ContainsSubstring("fakeTag"s) for: " All available tags: 1 [fakeTag] 1 tag " contains: "fakeTag" with 1 message: 'Tested reporter: JUnit' # Reporter's write listings to provided stream ok {test-number} - !(factories.empty()) for: !false @@ -2543,21 +2615,21 @@ ok {test-number} - v.capacity() >= 10 for: 10 >= 10 # Scenario: Vector resizing affects size and capacity ok {test-number} - v.size() == 0 for: 0 == 0 # Some simple comparisons between doubles -ok {test-number} - d == Approx( 1.23 ) for: 1.23 == Approx( 1.23 ) +ok {test-number} - d == Approx( 1.23 ) for: 1.22999999999999998 == Approx( 1.22999999999999998 ) # Some simple comparisons between doubles -ok {test-number} - d != Approx( 1.22 ) for: 1.23 != Approx( 1.22 ) +ok {test-number} - d != Approx( 1.22 ) for: 1.22999999999999998 != Approx( 1.21999999999999997 ) # Some simple comparisons between doubles -ok {test-number} - d != Approx( 1.24 ) for: 1.23 != Approx( 1.24 ) +ok {test-number} - d != Approx( 1.24 ) for: 1.22999999999999998 != Approx( 1.23999999999999999 ) # Some simple comparisons between doubles -ok {test-number} - d == 1.23_a for: 1.23 == Approx( 1.23 ) +ok {test-number} - d == 1.23_a for: 1.22999999999999998 == Approx( 1.22999999999999998 ) # Some simple comparisons between doubles -ok {test-number} - d != 1.22_a for: 1.23 != Approx( 1.22 ) +ok {test-number} - d != 1.22_a for: 1.22999999999999998 != Approx( 1.21999999999999997 ) # Some simple comparisons between doubles -ok {test-number} - Approx( d ) == 1.23 for: Approx( 1.23 ) == 1.23 +ok {test-number} - Approx( d ) == 1.23 for: Approx( 1.22999999999999998 ) == 1.22999999999999998 # Some simple comparisons between doubles -ok {test-number} - Approx( d ) != 1.22 for: Approx( 1.23 ) != 1.22 +ok {test-number} - Approx( d ) != 1.22 for: Approx( 1.22999999999999998 ) != 1.21999999999999997 # Some simple comparisons between doubles -ok {test-number} - Approx( d ) != 1.24 for: Approx( 1.23 ) != 1.24 +ok {test-number} - Approx( d ) != 1.24 for: Approx( 1.22999999999999998 ) != 1.23999999999999999 # StartsWith string matcher not ok {test-number} - testStringForMatching(), StartsWith( "This String" ) for: "this string contains 'abc' as a substring" starts with: "This String" # StartsWith string matcher @@ -2745,19 +2817,19 @@ ok {test-number} - Template_Fixture::m_a == 1 for: 1 == 1 # Template test case method with test types specified inside std::tuple - MyTypes - 2 ok {test-number} - Template_Fixture::m_a == 1 for: 1.0 == 1 # Template test case with test types specified inside non-copyable and non-movable std::tuple - NonCopyableAndNonMovableTypes - 0 -ok {test-number} - sizeof(TestType) > 0 for: 1 > 0 +ok {test-number} - std::is_default_constructible::value for: true # Template test case with test types specified inside non-copyable and non-movable std::tuple - NonCopyableAndNonMovableTypes - 1 -ok {test-number} - sizeof(TestType) > 0 for: 4 > 0 +ok {test-number} - std::is_default_constructible::value for: true # Template test case with test types specified inside non-default-constructible std::tuple - MyNonDefaultConstructibleTypes - 0 -ok {test-number} - sizeof(TestType) > 0 for: 1 > 0 +ok {test-number} - std::is_trivially_copyable::value for: true # Template test case with test types specified inside non-default-constructible std::tuple - MyNonDefaultConstructibleTypes - 1 -ok {test-number} - sizeof(TestType) > 0 for: 4 > 0 +ok {test-number} - std::is_trivially_copyable::value for: true # Template test case with test types specified inside std::tuple - MyTypes - 0 -ok {test-number} - sizeof(TestType) > 0 for: 4 > 0 +ok {test-number} - std::is_arithmetic::value for: true # Template test case with test types specified inside std::tuple - MyTypes - 1 -ok {test-number} - sizeof(TestType) > 0 for: 1 > 0 +ok {test-number} - std::is_arithmetic::value for: true # Template test case with test types specified inside std::tuple - MyTypes - 2 -ok {test-number} - sizeof(TestType) > 0 for: 4 > 0 +ok {test-number} - std::is_arithmetic::value for: true # TemplateTest: vectors can be sized and resized - float ok {test-number} - v.size() == 5 for: 5 == 5 # TemplateTest: vectors can be sized and resized - float @@ -3060,6 +3132,14 @@ not ok {test-number} - explicitly ok {test-number} - false # TODO # Testing checked-if 3 not ok {test-number} - explicitly +# Testing checked-if 4 +ok {test-number} - true +# Testing checked-if 4 +not ok {test-number} - unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} +# Testing checked-if 5 +ok {test-number} - false # TODO +# Testing checked-if 5 +not ok {test-number} - unexpected exception with message: 'Uncaught exception should fail!'; expression was: {Unknown expression after the reported line} # The NO_FAIL macro reports a failure but does not fail the test ok {test-number} - 1 == 2 # TODO # The default listing implementation write to provided stream @@ -3259,7 +3339,7 @@ ok {test-number} - vector_a, RangeEquals( array_a_plus_1, close_enough ) for: { # Type conversions of RangeEquals and similar ok {test-number} - vector_a, UnorderedRangeEquals( array_a_plus_1, close_enough ) for: { 1, 2, 3 } unordered elements are { 2, 3, 4 } # Unexpected exceptions can be translated -not ok {test-number} - unexpected exception with message: '3.14' +not ok {test-number} - unexpected exception with message: '3.14000000000000012' # Upcasting special member functions ok {test-number} - bptr->i == 3 for: 3 == 3 # Upcasting special member functions @@ -3551,21 +3631,21 @@ ok {test-number} - unrelated::ADL_size{}, SizeIs(12) for: {?} has size == 12 # Usage of the SizeIs range matcher ok {test-number} - has_size{}, SizeIs(13) for: {?} has size == 13 # Use a custom approx -ok {test-number} - d == approx( 1.23 ) for: 1.23 == Approx( 1.23 ) +ok {test-number} - d == approx( 1.23 ) for: 1.22999999999999998 == Approx( 1.22999999999999998 ) # Use a custom approx -ok {test-number} - d == approx( 1.22 ) for: 1.23 == Approx( 1.22 ) +ok {test-number} - d == approx( 1.22 ) for: 1.22999999999999998 == Approx( 1.21999999999999997 ) # Use a custom approx -ok {test-number} - d == approx( 1.24 ) for: 1.23 == Approx( 1.24 ) +ok {test-number} - d == approx( 1.24 ) for: 1.22999999999999998 == Approx( 1.23999999999999999 ) # Use a custom approx -ok {test-number} - d != approx( 1.25 ) for: 1.23 != Approx( 1.25 ) +ok {test-number} - d != approx( 1.25 ) for: 1.22999999999999998 != Approx( 1.25 ) # Use a custom approx -ok {test-number} - approx( d ) == 1.23 for: Approx( 1.23 ) == 1.23 +ok {test-number} - approx( d ) == 1.23 for: Approx( 1.22999999999999998 ) == 1.22999999999999998 # Use a custom approx -ok {test-number} - approx( d ) == 1.22 for: Approx( 1.23 ) == 1.22 +ok {test-number} - approx( d ) == 1.22 for: Approx( 1.22999999999999998 ) == 1.21999999999999997 # Use a custom approx -ok {test-number} - approx( d ) == 1.24 for: Approx( 1.23 ) == 1.24 +ok {test-number} - approx( d ) == 1.24 for: Approx( 1.22999999999999998 ) == 1.23999999999999999 # Use a custom approx -ok {test-number} - approx( d ) != 1.25 for: Approx( 1.23 ) != 1.25 +ok {test-number} - approx( d ) != 1.25 for: Approx( 1.22999999999999998 ) != 1.25 # Variadic macros ok {test-number} - with 1 message: 'no assertions' # Vector Approx matcher @@ -3891,11 +3971,11 @@ ok {test-number} - # SKIP 'skipping because answer = 43' # empty tags are not allowed ok {test-number} - Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo) # erfc_inv -ok {test-number} - erfc_inv(1.103560) == Approx(-0.09203687623843015) for: -0.0920368762 == Approx( -0.0920368762 ) +ok {test-number} - erfc_inv(1.103560) == Approx(-0.09203687623843015) for: -0.09203687623843014 == Approx( -0.09203687623843015 ) # erfc_inv -ok {test-number} - erfc_inv(1.067400) == Approx(-0.05980291115763361) for: -0.0598029112 == Approx( -0.0598029112 ) +ok {test-number} - erfc_inv(1.067400) == Approx(-0.05980291115763361) for: -0.05980291115763361 == Approx( -0.05980291115763361 ) # erfc_inv -ok {test-number} - erfc_inv(0.050000) == Approx(1.38590382434967796) for: 1.3859038243 == Approx( 1.3859038243 ) +ok {test-number} - erfc_inv(0.050000) == Approx(1.38590382434967796) for: 1.38590382434967774 == Approx( 1.38590382434967796 ) # estimate_clock_resolution ok {test-number} - res.mean.count() == rate for: 2000.0 == 2000 (0x) # estimate_clock_resolution @@ -4036,22 +4116,12 @@ ok {test-number} - # SKIP ok {test-number} - s == "7" for: "7" == "7" # non-copyable objects ok {test-number} - ti == typeid(int) for: {?} == {?} -# normal_cdf -ok {test-number} - normal_cdf(0.000000) == Approx(0.50000000000000000) for: 0.5 == Approx( 0.5 ) -# normal_cdf -ok {test-number} - normal_cdf(1.000000) == Approx(0.84134474606854293) for: 0.8413447461 == Approx( 0.8413447461 ) -# normal_cdf -ok {test-number} - normal_cdf(-1.000000) == Approx(0.15865525393145705) for: 0.1586552539 == Approx( 0.1586552539 ) -# normal_cdf -ok {test-number} - normal_cdf(2.809729) == Approx(0.99752083845315409) for: 0.9975208385 == Approx( 0.9975208385 ) -# normal_cdf -ok {test-number} - normal_cdf(-1.352570) == Approx(0.08809652095066035) for: 0.088096521 == Approx( 0.088096521 ) # normal_quantile -ok {test-number} - normal_quantile(0.551780) == Approx(0.13015979861484198) for: 0.1301597986 == Approx( 0.1301597986 ) +ok {test-number} - normal_quantile(0.551780) == Approx(0.13015979861484198) for: 0.13015979861484195 == Approx( 0.13015979861484198 ) # normal_quantile -ok {test-number} - normal_quantile(0.533700) == Approx(0.08457408802851875) for: 0.084574088 == Approx( 0.084574088 ) +ok {test-number} - normal_quantile(0.533700) == Approx(0.08457408802851875) for: 0.08457408802851875 == Approx( 0.08457408802851875 ) # normal_quantile -ok {test-number} - normal_quantile(0.025000) == Approx(-1.95996398454005449) for: -1.9599639845 == Approx( -1.9599639845 ) +ok {test-number} - normal_quantile(0.025000) == Approx(-1.95996398454005449) for: -1.95996398454005405 == Approx( -1.95996398454005449 ) # not allowed ok {test-number} - # not prints unscoped info from previous failures @@ -4127,6 +4197,14 @@ ok {test-number} - !(Catch::replaceInPlace(letters, "x", "z")) for: !false # replaceInPlace ok {test-number} - letters == letters for: "abcdefcg" == "abcdefcg" # replaceInPlace +ok {test-number} - Catch::replaceInPlace(letters, "c", "cc") for: true +# replaceInPlace +ok {test-number} - letters == "abccdefccg" for: "abccdefccg" == "abccdefccg" +# replaceInPlace +ok {test-number} - Catch::replaceInPlace(s, "--", "-") for: true +# replaceInPlace +ok {test-number} - s == "--" for: "--" == "--" +# replaceInPlace ok {test-number} - Catch::replaceInPlace(s, "'", "|'") for: true # replaceInPlace ok {test-number} - s == "didn|'t" for: "didn|'t" == "didn|'t" @@ -4327,15 +4405,15 @@ ok {test-number} - "{ }" == ::Catch::Detail::stringify(type{}) for: "{ }" == "{ # tuple<> ok {test-number} - "{ }" == ::Catch::Detail::stringify(value) for: "{ }" == "{ }" # tuple -ok {test-number} - "1.2f" == ::Catch::Detail::stringify(float(1.2)) for: "1.2f" == "1.2f" +ok {test-number} - "1.5f" == ::Catch::Detail::stringify(float(1.5)) for: "1.5f" == "1.5f" # tuple -ok {test-number} - "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) for: "{ 1.2f, 0 }" == "{ 1.2f, 0 }" +ok {test-number} - "{ 1.5f, 0 }" == ::Catch::Detail::stringify(type{1.5f,0}) for: "{ 1.5f, 0 }" == "{ 1.5f, 0 }" # tuple ok {test-number} - "{ 0 }" == ::Catch::Detail::stringify(type{0}) for: "{ 0 }" == "{ 0 }" # tuple ok {test-number} - "{ \"hello\", \"world\" }" == ::Catch::Detail::stringify(type{"hello","world"}) for: "{ "hello", "world" }" == "{ "hello", "world" }" # tuple,tuple<>,float> -ok {test-number} - "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) for: "{ { 42 }, { }, 1.2f }" == "{ { 42 }, { }, 1.2f }" +ok {test-number} - "{ { 42 }, { }, 1.5f }" == ::Catch::Detail::stringify(value) for: "{ { 42 }, { }, 1.5f }" == "{ { 42 }, { }, 1.5f }" # uniform samples ok {test-number} - e.point == 23 for: 23.0 == 23 # uniform samples @@ -4343,7 +4421,11 @@ ok {test-number} - e.upper_bound == 23 for: 23.0 == 23 # uniform samples ok {test-number} - e.lower_bound == 23 for: 23.0 == 23 # uniform samples -ok {test-number} - e.confidence_interval == 0.95 for: 0.95 == 0.95 +ok {test-number} - e.confidence_interval == 0.95 for: 0.94999999999999996 == 0.94999999999999996 +# uniform_integer_distribution can return the bounds +ok {test-number} - dist.a() == -10 for: -10 == -10 +# uniform_integer_distribution can return the bounds +ok {test-number} - dist.b() == 10 for: 10 == 10 # unique_ptr reimplementation: basic functionality ok {test-number} - !(ptr) for: !{?} # unique_ptr reimplementation: basic functionality @@ -4466,5 +4548,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0 ok {test-number} - # xmlentitycheck ok {test-number} - -1..2237 +1..2277 diff --git a/src/external/catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt index 8a8b55e2..9a59fa6d 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt @@ -166,6 +166,11 @@ ##teamcity[testFinished name='A TEST_CASE_METHOD based test run that fails' duration="{duration}"] ##teamcity[testStarted name='A TEST_CASE_METHOD based test run that succeeds'] ##teamcity[testFinished name='A TEST_CASE_METHOD based test run that succeeds' duration="{duration}"] +##teamcity[testStarted name='A TEST_CASE_PERSISTENT_FIXTURE based test run that fails'] +##teamcity[testFailed name='A TEST_CASE_PERSISTENT_FIXTURE based test run that fails' message='-------------------------------------------------------------------------------|nSecond partial run|n-------------------------------------------------------------------------------|nClass.tests.cpp:|n...............................................................................|n|nClass.tests.cpp:|nexpression failed|n REQUIRE( m_a == 0 )|nwith expansion:|n 1 == 0|n'] +##teamcity[testFinished name='A TEST_CASE_PERSISTENT_FIXTURE based test run that fails' duration="{duration}"] +##teamcity[testStarted name='A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds'] +##teamcity[testFinished name='A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds' duration="{duration}"] ##teamcity[testStarted name='A Template product test case - Foo'] ##teamcity[testFinished name='A Template product test case - Foo' duration="{duration}"] ##teamcity[testStarted name='A Template product test case - Foo'] @@ -240,6 +245,8 @@ ##teamcity[testFinished name='CaseInsensitiveLess is case insensitive' duration="{duration}"] ##teamcity[testStarted name='Character pretty printing'] ##teamcity[testFinished name='Character pretty printing' duration="{duration}"] +##teamcity[testStarted name='Clara::Arg does not crash on incomplete input'] +##teamcity[testFinished name='Clara::Arg does not crash on incomplete input' duration="{duration}"] ##teamcity[testStarted name='Clara::Arg supports single-arg parse the way Opt does'] ##teamcity[testFinished name='Clara::Arg supports single-arg parse the way Opt does' duration="{duration}"] ##teamcity[testStarted name='Clara::Opt supports accept-many lambdas'] @@ -299,10 +306,11 @@ ##teamcity[testFinished name='Default scale is invisible to comparison' duration="{duration}"] ##teamcity[testStarted name='Directly creating an EnumInfo'] ##teamcity[testFinished name='Directly creating an EnumInfo' duration="{duration}"] +##teamcity[testStarted name='Empty generators can SKIP in constructor'] +##teamcity[testIgnored name='Empty generators can SKIP in constructor' message='Skip.tests.cpp:|n...............................................................................|n|nSkip.tests.cpp:|nexplicit skip with message:|n "This generator is empty"'] +##teamcity[testFinished name='Empty generators can SKIP in constructor' duration="{duration}"] ##teamcity[testStarted name='Empty stream name opens cout stream'] ##teamcity[testFinished name='Empty stream name opens cout stream' duration="{duration}"] -##teamcity[testStarted name='Empty tag is not allowed'] -##teamcity[testFinished name='Empty tag is not allowed' duration="{duration}"] ##teamcity[testStarted name='EndsWith string matcher'] ##teamcity[testFailed name='EndsWith string matcher' message='Matchers.tests.cpp:|n...............................................................................|n|nMatchers.tests.cpp:|nexpression failed|n CHECK_THAT( testStringForMatching(), EndsWith( "Substring" ) )|nwith expansion:|n "this string contains |'abc|' as a substring" ends with: "Substring"|n'] ##teamcity[testFailed name='EndsWith string matcher' message='Matchers.tests.cpp:|nexpression failed|n CHECK_THAT( testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) )|nwith expansion:|n "this string contains |'abc|' as a substring" ends with: "this" (case insensitive)|n'] @@ -317,16 +325,16 @@ ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|n...............................................................................|n|nCondition.tests.cpp:|nexpression failed|n CHECK( data.int_seven == 6 )|nwith expansion:|n 7 == 6|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.int_seven == 8 )|nwith expansion:|n 7 == 8|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.int_seven == 0 )|nwith expansion:|n 7 == 0|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 9.11f ) )|nwith expansion:|n 9.1f == Approx( 9.1099996567 )|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 9.0f ) )|nwith expansion:|n 9.1f == Approx( 9.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 1 ) )|nwith expansion:|n 9.1f == Approx( 1.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 0 ) )|nwith expansion:|n 9.1f == Approx( 0.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.double_pi == Approx( 3.1415 ) )|nwith expansion:|n 3.1415926535 == Approx( 3.1415 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 9.11f ) )|nwith expansion:|n 9.100000381f|n==|nApprox( 9.10999965667724609 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 9.0f ) )|nwith expansion:|n 9.100000381f == Approx( 9.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 1 ) )|nwith expansion:|n 9.100000381f == Approx( 1.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 0 ) )|nwith expansion:|n 9.100000381f == Approx( 0.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.double_pi == Approx( 3.1415 ) )|nwith expansion:|n 3.14159265350000005|n==|nApprox( 3.14150000000000018 )|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello == "goodbye" )|nwith expansion:|n "hello" == "goodbye"|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello == "hell" )|nwith expansion:|n "hello" == "hell"|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello == "hello1" )|nwith expansion:|n "hello" == "hello1"|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello.size() == 6 )|nwith expansion:|n 5 == 6|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( x == Approx( 1.301 ) )|nwith expansion:|n 1.3 == Approx( 1.301 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( x == Approx( 1.301 ) )|nwith expansion:|n 1.30000000000000027|n==|nApprox( 1.30099999999999993 )|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testFinished name='Equality checks that should fail' duration="{duration}"] ##teamcity[testStarted name='Equality checks that should succeed'] ##teamcity[testFinished name='Equality checks that should succeed' duration="{duration}"] @@ -376,6 +384,8 @@ ##teamcity[testFinished name='Floating point matchers: double' duration="{duration}"] ##teamcity[testStarted name='Floating point matchers: float'] ##teamcity[testFinished name='Floating point matchers: float' duration="{duration}"] +##teamcity[testStarted name='GENERATE can combine literals and generators'] +##teamcity[testFinished name='GENERATE can combine literals and generators' duration="{duration}"] ##teamcity[testStarted name='Generators -- adapters'] ##teamcity[testFinished name='Generators -- adapters' duration="{duration}"] ##teamcity[testStarted name='Generators -- simple'] @@ -392,6 +402,9 @@ ##teamcity[testFinished name='Hashing different test cases produces different result' duration="{duration}"] ##teamcity[testStarted name='Hashing test case produces same hash across multiple calls'] ##teamcity[testFinished name='Hashing test case produces same hash across multiple calls' duration="{duration}"] +##teamcity[testStarted name='INFO and UNSCOPED_INFO can stream multiple arguments'] +##teamcity[testFailed name='INFO and UNSCOPED_INFO can stream multiple arguments' message='Message.tests.cpp:|n...............................................................................|n|nMessage.tests.cpp:|nexplicit failure with messages:|n "This info has multiple parts."|n "This unscoped info has multiple parts."|n "Show infos!"'] +##teamcity[testFinished name='INFO and UNSCOPED_INFO can stream multiple arguments' duration="{duration}"] ##teamcity[testStarted name='INFO and WARN do not abort tests'] ##teamcity[testFinished name='INFO and WARN do not abort tests' duration="{duration}"] ##teamcity[testStarted name='INFO gets logged on failure'] @@ -404,15 +417,22 @@ ##teamcity[testStarted name='INFO is reset for each loop'] ##teamcity[testFailed name='INFO is reset for each loop' message='Message.tests.cpp:|n...............................................................................|n|nMessage.tests.cpp:|nexpression failed with messages:|n "current counter 10"|n "i := 10"|n REQUIRE( i < 10 )|nwith expansion:|n 10 < 10|n'] ##teamcity[testFinished name='INFO is reset for each loop' duration="{duration}"] +##teamcity[testStarted name='Incomplete AssertionHandler'] +##teamcity[testIgnored name='Incomplete AssertionHandler' message='AssertionHandler.tests.cpp:|n...............................................................................|n|nAssertionHandler.tests.cpp:|nunexpected exception with message:|n "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"|n REQUIRE( Dummy )|nwith expansion:|n Dummy|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Incomplete AssertionHandler' duration="{duration}"] ##teamcity[testStarted name='Inequality checks that should fail'] ##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|n...............................................................................|n|nCondition.tests.cpp:|nexpression failed|n CHECK( data.int_seven != 7 )|nwith expansion:|n 7 != 7|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one != Approx( 9.1f ) )|nwith expansion:|n 9.1f != Approx( 9.1000003815 )|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.double_pi != Approx( 3.1415926535 ) )|nwith expansion:|n 3.1415926535 != Approx( 3.1415926535 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one != Approx( 9.1f ) )|nwith expansion:|n 9.100000381f|n!=|nApprox( 9.10000038146972656 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.double_pi != Approx( 3.1415926535 ) )|nwith expansion:|n 3.14159265350000005|n!=|nApprox( 3.14159265350000005 )|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello != "hello" )|nwith expansion:|n "hello" != "hello"|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello.size() != 5 )|nwith expansion:|n 5 != 5|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testFinished name='Inequality checks that should fail' duration="{duration}"] ##teamcity[testStarted name='Inequality checks that should succeed'] ##teamcity[testFinished name='Inequality checks that should succeed' duration="{duration}"] +##teamcity[testStarted name='JsonWriter'] +##teamcity[testFinished name='JsonWriter' duration="{duration}"] +##teamcity[testStarted name='JsonWriter escapes charaters in strings properly'] +##teamcity[testFinished name='JsonWriter escapes charaters in strings properly' duration="{duration}"] ##teamcity[testStarted name='Lambdas in assertions'] ##teamcity[testFinished name='Lambdas in assertions' duration="{duration}"] ##teamcity[testStarted name='Less-than inequalities with different epsilons'] @@ -466,9 +486,9 @@ ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.int_seven < -1 )|nwith expansion:|n 7 < -1|n'] ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.int_seven >= 8 )|nwith expansion:|n 7 >= 8|n'] ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.int_seven <= 6 )|nwith expansion:|n 7 <= 6|n'] -##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one < 9 )|nwith expansion:|n 9.1f < 9|n'] -##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one > 10 )|nwith expansion:|n 9.1f > 10|n'] -##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one > 9.2 )|nwith expansion:|n 9.1f > 9.2|n'] +##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one < 9 )|nwith expansion:|n 9.100000381f < 9|n'] +##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one > 10 )|nwith expansion:|n 9.100000381f > 10|n'] +##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one > 9.2 )|nwith expansion:|n 9.100000381f > 9.19999999999999929|n'] ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello > "hello" )|nwith expansion:|n "hello" > "hello"|n'] ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello < "hello" )|nwith expansion:|n "hello" < "hello"|n'] ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello > "hellp" )|nwith expansion:|n "hello" > "hellp"|n'] @@ -638,6 +658,12 @@ ##teamcity[testStarted name='Testing checked-if 3'] ##teamcity[testIgnored name='Testing checked-if 3' message='Misc.tests.cpp:|n...............................................................................|n|nMisc.tests.cpp:|nexplicit failure- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testFinished name='Testing checked-if 3' duration="{duration}"] +##teamcity[testStarted name='Testing checked-if 4'] +##teamcity[testIgnored name='Testing checked-if 4' message='Misc.tests.cpp:|n...............................................................................|n|nMisc.tests.cpp:|nunexpected exception with message:|n "Uncaught exception should fail!"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Testing checked-if 4' duration="{duration}"] +##teamcity[testStarted name='Testing checked-if 5'] +##teamcity[testIgnored name='Testing checked-if 5' message='Misc.tests.cpp:|n...............................................................................|n|nMisc.tests.cpp:|nunexpected exception with message:|n "Uncaught exception should fail!"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Testing checked-if 5' duration="{duration}"] ##teamcity[testStarted name='The NO_FAIL macro reports a failure but does not fail the test'] ##teamcity[testFinished name='The NO_FAIL macro reports a failure but does not fail the test' duration="{duration}"] ##teamcity[testStarted name='The default listing implementation write to provided stream'] @@ -654,7 +680,7 @@ ##teamcity[testStarted name='Type conversions of RangeEquals and similar'] ##teamcity[testFinished name='Type conversions of RangeEquals and similar' duration="{duration}"] ##teamcity[testStarted name='Unexpected exceptions can be translated'] -##teamcity[testFailed name='Unexpected exceptions can be translated' message='Exception.tests.cpp:|n...............................................................................|n|nException.tests.cpp:|nunexpected exception with message:|n "3.14"'] +##teamcity[testFailed name='Unexpected exceptions can be translated' message='Exception.tests.cpp:|n...............................................................................|n|nException.tests.cpp:|nunexpected exception with message:|n "3.14000000000000012"'] ##teamcity[testFinished name='Unexpected exceptions can be translated' duration="{duration}"] ##teamcity[testStarted name='Upcasting special member functions'] ##teamcity[testFinished name='Upcasting special member functions' duration="{duration}"] @@ -842,8 +868,6 @@ loose text artifact ##teamcity[testFinished name='non streamable - with conv. op' duration="{duration}"] ##teamcity[testStarted name='non-copyable objects'] ##teamcity[testFinished name='non-copyable objects' duration="{duration}"] -##teamcity[testStarted name='normal_cdf'] -##teamcity[testFinished name='normal_cdf' duration="{duration}"] ##teamcity[testStarted name='normal_quantile'] ##teamcity[testFinished name='normal_quantile' duration="{duration}"] ##teamcity[testStarted name='not allowed'] @@ -975,6 +999,8 @@ loose text artifact ##teamcity[testFinished name='tuple,tuple<>,float>' duration="{duration}"] ##teamcity[testStarted name='uniform samples'] ##teamcity[testFinished name='uniform samples' duration="{duration}"] +##teamcity[testStarted name='uniform_integer_distribution can return the bounds'] +##teamcity[testFinished name='uniform_integer_distribution can return the bounds' duration="{duration}"] ##teamcity[testStarted name='unique_ptr reimplementation: basic functionality'] ##teamcity[testFinished name='unique_ptr reimplementation: basic functionality' duration="{duration}"] ##teamcity[testStarted name='vec> -> toString'] diff --git a/src/external/catch2/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt index 77f70a63..989a37fe 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt @@ -166,6 +166,11 @@ ##teamcity[testFinished name='A TEST_CASE_METHOD based test run that fails' duration="{duration}"] ##teamcity[testStarted name='A TEST_CASE_METHOD based test run that succeeds'] ##teamcity[testFinished name='A TEST_CASE_METHOD based test run that succeeds' duration="{duration}"] +##teamcity[testStarted name='A TEST_CASE_PERSISTENT_FIXTURE based test run that fails'] +##teamcity[testFailed name='A TEST_CASE_PERSISTENT_FIXTURE based test run that fails' message='-------------------------------------------------------------------------------|nSecond partial run|n-------------------------------------------------------------------------------|nClass.tests.cpp:|n...............................................................................|n|nClass.tests.cpp:|nexpression failed|n REQUIRE( m_a == 0 )|nwith expansion:|n 1 == 0|n'] +##teamcity[testFinished name='A TEST_CASE_PERSISTENT_FIXTURE based test run that fails' duration="{duration}"] +##teamcity[testStarted name='A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds'] +##teamcity[testFinished name='A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds' duration="{duration}"] ##teamcity[testStarted name='A Template product test case - Foo'] ##teamcity[testFinished name='A Template product test case - Foo' duration="{duration}"] ##teamcity[testStarted name='A Template product test case - Foo'] @@ -240,6 +245,8 @@ ##teamcity[testFinished name='CaseInsensitiveLess is case insensitive' duration="{duration}"] ##teamcity[testStarted name='Character pretty printing'] ##teamcity[testFinished name='Character pretty printing' duration="{duration}"] +##teamcity[testStarted name='Clara::Arg does not crash on incomplete input'] +##teamcity[testFinished name='Clara::Arg does not crash on incomplete input' duration="{duration}"] ##teamcity[testStarted name='Clara::Arg supports single-arg parse the way Opt does'] ##teamcity[testFinished name='Clara::Arg supports single-arg parse the way Opt does' duration="{duration}"] ##teamcity[testStarted name='Clara::Opt supports accept-many lambdas'] @@ -299,10 +306,11 @@ ##teamcity[testFinished name='Default scale is invisible to comparison' duration="{duration}"] ##teamcity[testStarted name='Directly creating an EnumInfo'] ##teamcity[testFinished name='Directly creating an EnumInfo' duration="{duration}"] +##teamcity[testStarted name='Empty generators can SKIP in constructor'] +##teamcity[testIgnored name='Empty generators can SKIP in constructor' message='Skip.tests.cpp:|n...............................................................................|n|nSkip.tests.cpp:|nexplicit skip with message:|n "This generator is empty"'] +##teamcity[testFinished name='Empty generators can SKIP in constructor' duration="{duration}"] ##teamcity[testStarted name='Empty stream name opens cout stream'] ##teamcity[testFinished name='Empty stream name opens cout stream' duration="{duration}"] -##teamcity[testStarted name='Empty tag is not allowed'] -##teamcity[testFinished name='Empty tag is not allowed' duration="{duration}"] ##teamcity[testStarted name='EndsWith string matcher'] ##teamcity[testFailed name='EndsWith string matcher' message='Matchers.tests.cpp:|n...............................................................................|n|nMatchers.tests.cpp:|nexpression failed|n CHECK_THAT( testStringForMatching(), EndsWith( "Substring" ) )|nwith expansion:|n "this string contains |'abc|' as a substring" ends with: "Substring"|n'] ##teamcity[testFailed name='EndsWith string matcher' message='Matchers.tests.cpp:|nexpression failed|n CHECK_THAT( testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) )|nwith expansion:|n "this string contains |'abc|' as a substring" ends with: "this" (case insensitive)|n'] @@ -317,16 +325,16 @@ ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|n...............................................................................|n|nCondition.tests.cpp:|nexpression failed|n CHECK( data.int_seven == 6 )|nwith expansion:|n 7 == 6|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.int_seven == 8 )|nwith expansion:|n 7 == 8|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.int_seven == 0 )|nwith expansion:|n 7 == 0|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 9.11f ) )|nwith expansion:|n 9.1f == Approx( 9.1099996567 )|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 9.0f ) )|nwith expansion:|n 9.1f == Approx( 9.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 1 ) )|nwith expansion:|n 9.1f == Approx( 1.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 0 ) )|nwith expansion:|n 9.1f == Approx( 0.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.double_pi == Approx( 3.1415 ) )|nwith expansion:|n 3.1415926535 == Approx( 3.1415 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 9.11f ) )|nwith expansion:|n 9.100000381f|n==|nApprox( 9.10999965667724609 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 9.0f ) )|nwith expansion:|n 9.100000381f == Approx( 9.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 1 ) )|nwith expansion:|n 9.100000381f == Approx( 1.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one == Approx( 0 ) )|nwith expansion:|n 9.100000381f == Approx( 0.0 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.double_pi == Approx( 3.1415 ) )|nwith expansion:|n 3.14159265350000005|n==|nApprox( 3.14150000000000018 )|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello == "goodbye" )|nwith expansion:|n "hello" == "goodbye"|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello == "hell" )|nwith expansion:|n "hello" == "hell"|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello == "hello1" )|nwith expansion:|n "hello" == "hello1"|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello.size() == 6 )|nwith expansion:|n 5 == 6|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( x == Approx( 1.301 ) )|nwith expansion:|n 1.3 == Approx( 1.301 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Equality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( x == Approx( 1.301 ) )|nwith expansion:|n 1.30000000000000027|n==|nApprox( 1.30099999999999993 )|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testFinished name='Equality checks that should fail' duration="{duration}"] ##teamcity[testStarted name='Equality checks that should succeed'] ##teamcity[testFinished name='Equality checks that should succeed' duration="{duration}"] @@ -376,6 +384,8 @@ ##teamcity[testFinished name='Floating point matchers: double' duration="{duration}"] ##teamcity[testStarted name='Floating point matchers: float'] ##teamcity[testFinished name='Floating point matchers: float' duration="{duration}"] +##teamcity[testStarted name='GENERATE can combine literals and generators'] +##teamcity[testFinished name='GENERATE can combine literals and generators' duration="{duration}"] ##teamcity[testStarted name='Generators -- adapters'] ##teamcity[testFinished name='Generators -- adapters' duration="{duration}"] ##teamcity[testStarted name='Generators -- simple'] @@ -392,6 +402,9 @@ ##teamcity[testFinished name='Hashing different test cases produces different result' duration="{duration}"] ##teamcity[testStarted name='Hashing test case produces same hash across multiple calls'] ##teamcity[testFinished name='Hashing test case produces same hash across multiple calls' duration="{duration}"] +##teamcity[testStarted name='INFO and UNSCOPED_INFO can stream multiple arguments'] +##teamcity[testFailed name='INFO and UNSCOPED_INFO can stream multiple arguments' message='Message.tests.cpp:|n...............................................................................|n|nMessage.tests.cpp:|nexplicit failure with messages:|n "This info has multiple parts."|n "This unscoped info has multiple parts."|n "Show infos!"'] +##teamcity[testFinished name='INFO and UNSCOPED_INFO can stream multiple arguments' duration="{duration}"] ##teamcity[testStarted name='INFO and WARN do not abort tests'] ##teamcity[testFinished name='INFO and WARN do not abort tests' duration="{duration}"] ##teamcity[testStarted name='INFO gets logged on failure'] @@ -404,15 +417,22 @@ ##teamcity[testStarted name='INFO is reset for each loop'] ##teamcity[testFailed name='INFO is reset for each loop' message='Message.tests.cpp:|n...............................................................................|n|nMessage.tests.cpp:|nexpression failed with messages:|n "current counter 10"|n "i := 10"|n REQUIRE( i < 10 )|nwith expansion:|n 10 < 10|n'] ##teamcity[testFinished name='INFO is reset for each loop' duration="{duration}"] +##teamcity[testStarted name='Incomplete AssertionHandler'] +##teamcity[testIgnored name='Incomplete AssertionHandler' message='AssertionHandler.tests.cpp:|n...............................................................................|n|nAssertionHandler.tests.cpp:|nunexpected exception with message:|n "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"|n REQUIRE( Dummy )|nwith expansion:|n Dummy|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Incomplete AssertionHandler' duration="{duration}"] ##teamcity[testStarted name='Inequality checks that should fail'] ##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|n...............................................................................|n|nCondition.tests.cpp:|nexpression failed|n CHECK( data.int_seven != 7 )|nwith expansion:|n 7 != 7|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one != Approx( 9.1f ) )|nwith expansion:|n 9.1f != Approx( 9.1000003815 )|n- failure ignore as test marked as |'ok to fail|'|n'] -##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.double_pi != Approx( 3.1415926535 ) )|nwith expansion:|n 3.1415926535 != Approx( 3.1415926535 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one != Approx( 9.1f ) )|nwith expansion:|n 9.100000381f|n!=|nApprox( 9.10000038146972656 )|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.double_pi != Approx( 3.1415926535 ) )|nwith expansion:|n 3.14159265350000005|n!=|nApprox( 3.14159265350000005 )|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello != "hello" )|nwith expansion:|n "hello" != "hello"|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testIgnored name='Inequality checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello.size() != 5 )|nwith expansion:|n 5 != 5|n- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testFinished name='Inequality checks that should fail' duration="{duration}"] ##teamcity[testStarted name='Inequality checks that should succeed'] ##teamcity[testFinished name='Inequality checks that should succeed' duration="{duration}"] +##teamcity[testStarted name='JsonWriter'] +##teamcity[testFinished name='JsonWriter' duration="{duration}"] +##teamcity[testStarted name='JsonWriter escapes charaters in strings properly'] +##teamcity[testFinished name='JsonWriter escapes charaters in strings properly' duration="{duration}"] ##teamcity[testStarted name='Lambdas in assertions'] ##teamcity[testFinished name='Lambdas in assertions' duration="{duration}"] ##teamcity[testStarted name='Less-than inequalities with different epsilons'] @@ -466,9 +486,9 @@ ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.int_seven < -1 )|nwith expansion:|n 7 < -1|n'] ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.int_seven >= 8 )|nwith expansion:|n 7 >= 8|n'] ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.int_seven <= 6 )|nwith expansion:|n 7 <= 6|n'] -##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one < 9 )|nwith expansion:|n 9.1f < 9|n'] -##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one > 10 )|nwith expansion:|n 9.1f > 10|n'] -##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one > 9.2 )|nwith expansion:|n 9.1f > 9.2|n'] +##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one < 9 )|nwith expansion:|n 9.100000381f < 9|n'] +##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one > 10 )|nwith expansion:|n 9.100000381f > 10|n'] +##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.float_nine_point_one > 9.2 )|nwith expansion:|n 9.100000381f > 9.19999999999999929|n'] ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello > "hello" )|nwith expansion:|n "hello" > "hello"|n'] ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello < "hello" )|nwith expansion:|n "hello" < "hello"|n'] ##teamcity[testFailed name='Ordering comparison checks that should fail' message='Condition.tests.cpp:|nexpression failed|n CHECK( data.str_hello > "hellp" )|nwith expansion:|n "hello" > "hellp"|n'] @@ -638,6 +658,12 @@ ##teamcity[testStarted name='Testing checked-if 3'] ##teamcity[testIgnored name='Testing checked-if 3' message='Misc.tests.cpp:|n...............................................................................|n|nMisc.tests.cpp:|nexplicit failure- failure ignore as test marked as |'ok to fail|'|n'] ##teamcity[testFinished name='Testing checked-if 3' duration="{duration}"] +##teamcity[testStarted name='Testing checked-if 4'] +##teamcity[testIgnored name='Testing checked-if 4' message='Misc.tests.cpp:|n...............................................................................|n|nMisc.tests.cpp:|nunexpected exception with message:|n "Uncaught exception should fail!"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Testing checked-if 4' duration="{duration}"] +##teamcity[testStarted name='Testing checked-if 5'] +##teamcity[testIgnored name='Testing checked-if 5' message='Misc.tests.cpp:|n...............................................................................|n|nMisc.tests.cpp:|nunexpected exception with message:|n "Uncaught exception should fail!"|n {Unknown expression after the reported line}|nwith expansion:|n {Unknown expression after the reported line}|n- failure ignore as test marked as |'ok to fail|'|n'] +##teamcity[testFinished name='Testing checked-if 5' duration="{duration}"] ##teamcity[testStarted name='The NO_FAIL macro reports a failure but does not fail the test'] ##teamcity[testFinished name='The NO_FAIL macro reports a failure but does not fail the test' duration="{duration}"] ##teamcity[testStarted name='The default listing implementation write to provided stream'] @@ -654,7 +680,7 @@ ##teamcity[testStarted name='Type conversions of RangeEquals and similar'] ##teamcity[testFinished name='Type conversions of RangeEquals and similar' duration="{duration}"] ##teamcity[testStarted name='Unexpected exceptions can be translated'] -##teamcity[testFailed name='Unexpected exceptions can be translated' message='Exception.tests.cpp:|n...............................................................................|n|nException.tests.cpp:|nunexpected exception with message:|n "3.14"'] +##teamcity[testFailed name='Unexpected exceptions can be translated' message='Exception.tests.cpp:|n...............................................................................|n|nException.tests.cpp:|nunexpected exception with message:|n "3.14000000000000012"'] ##teamcity[testFinished name='Unexpected exceptions can be translated' duration="{duration}"] ##teamcity[testStarted name='Upcasting special member functions'] ##teamcity[testFinished name='Upcasting special member functions' duration="{duration}"] @@ -841,8 +867,6 @@ ##teamcity[testFinished name='non streamable - with conv. op' duration="{duration}"] ##teamcity[testStarted name='non-copyable objects'] ##teamcity[testFinished name='non-copyable objects' duration="{duration}"] -##teamcity[testStarted name='normal_cdf'] -##teamcity[testFinished name='normal_cdf' duration="{duration}"] ##teamcity[testStarted name='normal_quantile'] ##teamcity[testFinished name='normal_quantile' duration="{duration}"] ##teamcity[testStarted name='not allowed'] @@ -974,6 +998,8 @@ ##teamcity[testFinished name='tuple,tuple<>,float>' duration="{duration}"] ##teamcity[testStarted name='uniform samples'] ##teamcity[testFinished name='uniform samples' duration="{duration}"] +##teamcity[testStarted name='uniform_integer_distribution can return the bounds'] +##teamcity[testFinished name='uniform_integer_distribution can return the bounds' duration="{duration}"] ##teamcity[testStarted name='unique_ptr reimplementation: basic functionality'] ##teamcity[testFinished name='unique_ptr reimplementation: basic functionality' duration="{duration}"] ##teamcity[testStarted name='vec> -> toString'] diff --git a/src/external/catch2/tests/SelfTest/Baselines/xml.sw.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/xml.sw.approved.txt index 6a0d1587..c64c5a7b 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/xml.sw.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/xml.sw.approved.txt @@ -1,5 +1,5 @@ - + @@ -77,10 +77,10 @@ - + uarr := "123" - + sarr := "456" @@ -91,10 +91,10 @@ 0 == 0 - + uarr := "123" - + sarr := "456" @@ -128,11 +128,11 @@ - + This info message starts with a linebreak - + This warning message starts with a linebreak @@ -384,91 +384,91 @@ Nor would this
- + i := 1 - + j := 3 - + k := 5
- + i := 1 - + j := 3 - + k := 6
- + i := 1 - + j := 4 - + k := 5 - + i := 1 - + j := 4 - + k := 6
- + i := 2 - + j := 3 - + k := 5
- + i := 2 - + j := 3 - + k := 6
- + i := 2 - + j := 4 - + k := 5 - + i := 2 - + j := 4 - + k := 6 @@ -667,7 +667,7 @@ Nor would this
- + failure to init @@ -675,7 +675,7 @@ Nor would this
- + answer := 42 @@ -684,7 +684,7 @@ Nor would this
- + answer := 42 @@ -701,7 +701,7 @@ Nor would this
- + answer := 42 @@ -806,7 +806,7 @@ Nor would this - + dummy := 0 @@ -2056,6 +2056,56 @@ Nor would this + +
+ + + m_a++ == 0 + + + 0 == 0 + + + +
+
+ + + m_a == 0 + + + 1 == 0 + + + +
+ +
+ +
+ + + m_a++ == 0 + + + 0 == 0 + + + +
+
+ + + m_a == 1 + + + 1 == 1 + + + +
+ +
@@ -2150,7 +2200,9 @@ Nor would this d == 1.23_a - 1.23 == Approx( 1.23 ) + 1.22999999999999998 +== +Approx( 1.22999999999999998 ) @@ -2158,7 +2210,9 @@ Nor would this d != 1.22_a - 1.23 != Approx( 1.22 ) + 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) @@ -2166,7 +2220,9 @@ Nor would this -d == -1.23_a - -1.23 == Approx( -1.23 ) + -1.22999999999999998 +== +Approx( -1.22999999999999998 ) @@ -2174,7 +2230,9 @@ Nor would this d == 1.2_a .epsilon(.1) - 1.23 == Approx( 1.2 ) + 1.22999999999999998 +== +Approx( 1.19999999999999996 ) @@ -2182,7 +2240,9 @@ Nor would this d != 1.2_a .epsilon(.001) - 1.23 != Approx( 1.2 ) + 1.22999999999999998 +!= +Approx( 1.19999999999999996 ) @@ -2190,7 +2250,7 @@ Nor would this d == 1_a .epsilon(.3) - 1.23 == Approx( 1.0 ) + 1.22999999999999998 == Approx( 1.0 ) @@ -2264,7 +2324,7 @@ Nor would this 100.3 != Approx(100.0) - 100.3 != Approx( 100.0 ) + 100.29999999999999716 != Approx( 100.0 )
@@ -2272,7 +2332,7 @@ Nor would this 100.3 == Approx(100.0).margin(0.5) - 100.3 == Approx( 100.0 ) + 100.29999999999999716 == Approx( 100.0 ) @@ -2432,7 +2492,9 @@ Nor would this divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) - 3.1428571429 == Approx( 3.141 ) + 3.14285714285714279 +== +Approx( 3.14100000000000001 ) @@ -2440,7 +2502,9 @@ Nor would this divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) - 3.1428571429 != Approx( 3.141 ) + 3.14285714285714279 +!= +Approx( 3.14100000000000001 ) @@ -2451,7 +2515,9 @@ Nor would this d != Approx( 1.231 ) - 1.23 != Approx( 1.231 ) + 1.22999999999999998 +!= +Approx( 1.23100000000000009 ) @@ -2459,7 +2525,9 @@ Nor would this d == Approx( 1.231 ).epsilon( 0.1 ) - 1.23 == Approx( 1.231 ) + 1.22999999999999998 +== +Approx( 1.23100000000000009 ) @@ -2470,7 +2538,9 @@ Nor would this 1.23f == Approx( 1.23f ) - 1.23f == Approx( 1.2300000191 ) + 1.230000019f +== +Approx( 1.23000001907348633 ) @@ -2532,7 +2602,9 @@ Nor would this 1.234f == Approx( dMedium ) - 1.234f == Approx( 1.234 ) + 1.233999968f +== +Approx( 1.23399999999999999 ) @@ -2540,7 +2612,9 @@ Nor would this dMedium == Approx( 1.234f ) - 1.234 == Approx( 1.2339999676 ) + 1.23399999999999999 +== +Approx( 1.23399996757507324 ) @@ -2886,92 +2960,92 @@ Nor would this - + a := 1 - + b := 2 - + c := 3 - + a + b := 3 - + a+b := 3 - + c > b := true - + a == 1 := true - - std::vector<int>{1, 2, 3}[0, 1, 2] := 3 + + custom_index_op<int>{1, 2, 3}[0, 1, 2] := 0 - - std::vector<int>{1, 2, 3}[(0, 1)] := 2 + + custom_index_op<int>{1, 2, 3}[(0, 1)] := 0 - - std::vector<int>{1, 2, 3}[0] := 1 + + custom_index_op<int>{1, 2, 3}[0] := 0 - + (helper_1436<int, int>{12, -12}) := { 12, -12 } - + (helper_1436<int, int>(-12, 12)) := { -12, 12 } - + (1, 2) := 2 - + (2, 3) := 3 - + ("comma, in string", "escaped, \", ") := "escaped, ", " - + "single quote in string,'," := "single quote in string,'," - + "some escapes, \\,\\\\" := "some escapes, \,\\" - + "some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<" - + '"' := '"' - + '\'' := ''' - + ',' := ',' - + '}' := '}' - + ')' := ')' - + '(' := '(' - + '{' := '{'
- + i := 2 @@ -2985,7 +3059,7 @@ Nor would this
- + 3 @@ -3142,34 +3216,34 @@ Nor would this
- tab == '\t' + ::Catch::Detail::stringify('\t') == "'\\t'" - '\t' == '\t' + "'\t'" == "'\t'" - newline == '\n' + ::Catch::Detail::stringify('\n') == "'\\n'" - '\n' == '\n' + "'\n'" == "'\n'" - carr_return == '\r' + ::Catch::Detail::stringify('\r') == "'\\r'" - '\r' == '\r' + "'\r'" == "'\r'" - form_feed == '\f' + ::Catch::Detail::stringify('\f') == "'\\f'" - '\f' == '\f' + "'\f'" == "'\f'" @@ -3177,91 +3251,110 @@ Nor would this
- space == ' ' - - - ' ' == ' ' - - - - - c == chars[i] - - - 'a' == 'a' - - - - - c == chars[i] + ::Catch::Detail::stringify( ' ' ) == "' '" - 'z' == 'z' + "' '" == "' '" - + - c == chars[i] + ::Catch::Detail::stringify( 'A' ) == "'A'" - 'A' == 'A' + "'A'" == "'A'" - + - c == chars[i] + ::Catch::Detail::stringify( 'z' ) == "'z'" - 'Z' == 'Z' + "'z'" == "'z'" - +
- null_terminator == '\0' - - - 0 == 0 - - - - - c == i - - - 2 == 2 - - - - - c == i + ::Catch::Detail::stringify( '\0' ) == "0" - 3 == 3 + "0" == "0" - + - c == i + ::Catch::Detail::stringify( static_cast<char>(2) ) == "2" - 4 == 4 + "2" == "2" - + - c == i + ::Catch::Detail::stringify( static_cast<char>(5) ) == "5" - 5 == 5 + "5" == "5" - +
+ + + + name.empty() + + + true + + + + + result + + + {?} + + + + + result.type() == Catch::Clara::Detail::ResultType::Ok + + + 0 == 0 + + + + + parsed.type() == Catch::Clara::ParseResultType::NoMatch + + + 1 == 1 + + + + + parsed.remainingTokens().count() == 2 + + + 2 == 2 + + + + + name.empty() + + + true + + + + @@ -4322,7 +4415,7 @@ C 101.000001 != Approx(100).epsilon(0.01) - 101.000001 != Approx( 100.0 ) + 101.00000099999999748 != Approx( 100.0 ) @@ -4364,6 +4457,12 @@ C + + + This generator is empty + + + @@ -4375,17 +4474,6 @@ C - - - - Catch::TestCaseInfo( "", { "fake test name", "[]" }, dummySourceLineInfo ) - - - Catch::TestCaseInfo( "", { "fake test name", "[]" }, dummySourceLineInfo ) - - - - @@ -4475,7 +4563,7 @@ C 101.01 != Approx(100).epsilon(0.01) - 101.01 != Approx( 100.0 ) + 101.01000000000000512 != Approx( 100.0 ) @@ -4510,7 +4598,9 @@ C data.float_nine_point_one == Approx( 9.11f ) - 9.1f == Approx( 9.1099996567 ) + 9.100000381f +== +Approx( 9.10999965667724609 ) @@ -4518,7 +4608,7 @@ C data.float_nine_point_one == Approx( 9.0f ) - 9.1f == Approx( 9.0 ) + 9.100000381f == Approx( 9.0 ) @@ -4526,7 +4616,7 @@ C data.float_nine_point_one == Approx( 1 ) - 9.1f == Approx( 1.0 ) + 9.100000381f == Approx( 1.0 ) @@ -4534,7 +4624,7 @@ C data.float_nine_point_one == Approx( 0 ) - 9.1f == Approx( 0.0 ) + 9.100000381f == Approx( 0.0 ) @@ -4542,7 +4632,9 @@ C data.double_pi == Approx( 3.1415 ) - 3.1415926535 == Approx( 3.1415 ) + 3.14159265350000005 +== +Approx( 3.14150000000000018 ) @@ -4582,7 +4674,9 @@ C x == Approx( 1.301 ) - 1.3 == Approx( 1.301 ) + 1.30000000000000027 +== +Approx( 1.30099999999999993 ) @@ -4601,7 +4695,9 @@ C data.float_nine_point_one == Approx( 9.1f ) - 9.1f == Approx( 9.1000003815 ) + 9.100000381f +== +Approx( 9.10000038146972656 ) @@ -4609,7 +4705,9 @@ C data.double_pi == Approx( 3.1415926535 ) - 3.1415926535 == Approx( 3.1415926535 ) + 3.14159265350000005 +== +Approx( 3.14159265350000005 ) @@ -4641,7 +4739,9 @@ C x == Approx( 1.3 ) - 1.3 == Approx( 1.3 ) + 1.30000000000000027 +== +Approx( 1.30000000000000004 ) @@ -4977,7 +5077,7 @@ C This is a failure - + This message appears in the output @@ -5043,7 +5143,7 @@ C 10., WithinRel( 11.1, 0.1 ) - 10.0 and 11.1 are within 10% of each other + 10.0 and 11.09999999999999964 are within 10% of each other @@ -5051,7 +5151,7 @@ C 10., !WithinRel( 11.2, 0.1 ) - 10.0 not and 11.2 are within 10% of each other + 10.0 not and 11.19999999999999929 are within 10% of each other @@ -5059,7 +5159,7 @@ C 1., !WithinRel( 0., 0.99 ) - 1.0 not and 0 are within 99% of each other + 1.0 not and 0.0 are within 99% of each other @@ -5067,7 +5167,7 @@ C -0., WithinRel( 0. ) - -0.0 and 0 are within 2.22045e-12% of each other + -0.0 and 0.0 are within 2.22045e-12% of each other
@@ -5076,7 +5176,7 @@ C v1, WithinRel( v2 ) - 0.0 and 2.22507e-308 are within 2.22045e-12% of each other + 0.0 and 0.0 are within 2.22045e-12% of each other @@ -5105,7 +5205,7 @@ C 0., !WithinAbs( 1., 0.99 ) - 0.0 not is within 0.99 of 1.0 + 0.0 not is within 0.98999999999999999 of 1.0 @@ -5113,7 +5213,7 @@ C 0., !WithinAbs( 1., 0.99 ) - 0.0 not is within 0.99 of 1.0 + 0.0 not is within 0.98999999999999999 of 1.0 @@ -5145,7 +5245,7 @@ C -10., WithinAbs( -9.6, 0.5 ) - -10.0 is within 0.5 of -9.6 + -10.0 is within 0.5 of -9.59999999999999964 @@ -5164,7 +5264,7 @@ C nextafter( 1., 2. ), WithinULP( 1., 1 ) - 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) + 1.00000000000000022 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) @@ -5231,7 +5331,7 @@ C 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) - 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + 0.0001 ( is within 0.001 of 0.0 or and 0.0 are within 10% of each other ) @@ -5307,7 +5407,7 @@ C 10.f, WithinRel( 11.1f, 0.1f ) - 10.0f and 11.1 are within 10% of each other + 10.0f and 11.10000038146972656 are within 10% of each other @@ -5315,7 +5415,7 @@ C 10.f, !WithinRel( 11.2f, 0.1f ) - 10.0f not and 11.2 are within 10% of each other + 10.0f not and 11.19999980926513672 are within 10% of each other @@ -5323,7 +5423,7 @@ C 1.f, !WithinRel( 0.f, 0.99f ) - 1.0f not and 0 are within 99% of each other + 1.0f not and 0.0 are within 99% of each other @@ -5331,7 +5431,7 @@ C -0.f, WithinRel( 0.f ) - -0.0f and 0 are within 0.00119209% of each other + -0.0f and 0.0 are within 0.00119209% of each other
@@ -5340,7 +5440,7 @@ C v1, WithinRel( v2 ) - 0.0f and 1.17549e-38 are within 0.00119209% of each other + 0.0f and 0.0 are within 0.00119209% of each other @@ -5369,7 +5469,7 @@ C 0.f, !WithinAbs( 1.f, 0.99f ) - 0.0f not is within 0.9900000095 of 1.0 + 0.0f not is within 0.99000000953674316 of 1.0 @@ -5377,7 +5477,7 @@ C 0.f, !WithinAbs( 1.f, 0.99f ) - 0.0f not is within 0.9900000095 of 1.0 + 0.0f not is within 0.99000000953674316 of 1.0 @@ -5417,7 +5517,7 @@ C -10.f, WithinAbs( -9.6f, 0.5f ) - -10.0f is within 0.5 of -9.6000003815 + -10.0f is within 0.5 of -9.60000038146972656 @@ -5444,7 +5544,7 @@ C nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) - 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) + 1.000000119f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) @@ -5511,7 +5611,7 @@ C 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) - 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + 0.0001f ( is within 0.00100000004749745 of 0.0 or and 0.0 are within 10% of each other ) @@ -5588,6 +5688,41 @@ C
+ + + + i % 2 == 0 + + + 0 == 0 + + + + + i % 2 == 0 + + + 0 == 0 + + + + + i % 2 == 0 + + + 0 == 0 + + + + + i % 2 == 0 + + + 0 == 0 + + + +
@@ -7246,7 +7381,7 @@ C
- + Current expected value is -1 @@ -7257,7 +7392,7 @@ C -1.0 == Approx( -1.0 ) - + Current expected value is -1 @@ -7268,7 +7403,7 @@ C true - + Current expected value is -0.9 @@ -7276,10 +7411,12 @@ C gen.get() == Approx(expected) - -0.9 == Approx( -0.9 ) + -0.90000000000000002 +== +Approx( -0.90000000000000002 ) - + Current expected value is -0.9 @@ -7290,7 +7427,7 @@ C true - + Current expected value is -0.8 @@ -7298,10 +7435,12 @@ C gen.get() == Approx(expected) - -0.8 == Approx( -0.8 ) + -0.80000000000000004 +== +Approx( -0.80000000000000004 ) - + Current expected value is -0.8 @@ -7312,7 +7451,7 @@ C true - + Current expected value is -0.7 @@ -7320,10 +7459,12 @@ C gen.get() == Approx(expected) - -0.7 == Approx( -0.7 ) + -0.70000000000000007 +== +Approx( -0.70000000000000007 ) - + Current expected value is -0.7 @@ -7334,7 +7475,7 @@ C true - + Current expected value is -0.6 @@ -7342,10 +7483,12 @@ C gen.get() == Approx(expected) - -0.6 == Approx( -0.6 ) + -0.60000000000000009 +== +Approx( -0.60000000000000009 ) - + Current expected value is -0.6 @@ -7356,7 +7499,7 @@ C true - + Current expected value is -0.5 @@ -7364,10 +7507,12 @@ C gen.get() == Approx(expected) - -0.5 == Approx( -0.5 ) + -0.50000000000000011 +== +Approx( -0.50000000000000011 ) - + Current expected value is -0.5 @@ -7378,7 +7523,7 @@ C true - + Current expected value is -0.4 @@ -7386,10 +7531,12 @@ C gen.get() == Approx(expected) - -0.4 == Approx( -0.4 ) + -0.40000000000000013 +== +Approx( -0.40000000000000013 ) - + Current expected value is -0.4 @@ -7400,7 +7547,7 @@ C true - + Current expected value is -0.3 @@ -7408,10 +7555,12 @@ C gen.get() == Approx(expected) - -0.3 == Approx( -0.3 ) + -0.30000000000000016 +== +Approx( -0.30000000000000016 ) - + Current expected value is -0.3 @@ -7422,7 +7571,7 @@ C true - + Current expected value is -0.2 @@ -7430,10 +7579,12 @@ C gen.get() == Approx(expected) - -0.2 == Approx( -0.2 ) + -0.20000000000000015 +== +Approx( -0.20000000000000015 ) - + Current expected value is -0.2 @@ -7444,7 +7595,7 @@ C true - + Current expected value is -0.1 @@ -7452,10 +7603,12 @@ C gen.get() == Approx(expected) - -0.1 == Approx( -0.1 ) + -0.10000000000000014 +== +Approx( -0.10000000000000014 ) - + Current expected value is -0.1 @@ -7466,7 +7619,7 @@ C true - + Current expected value is -1.38778e-16 @@ -7474,10 +7627,12 @@ C gen.get() == Approx(expected) - -0.0 == Approx( -0.0 ) + -0.00000000000000014 +== +Approx( -0.00000000000000014 ) - + Current expected value is -1.38778e-16 @@ -7488,7 +7643,7 @@ C true - + Current expected value is 0.1 @@ -7496,10 +7651,12 @@ C gen.get() == Approx(expected) - 0.1 == Approx( 0.1 ) + 0.09999999999999987 +== +Approx( 0.09999999999999987 ) - + Current expected value is 0.1 @@ -7510,7 +7667,7 @@ C true - + Current expected value is 0.2 @@ -7518,10 +7675,12 @@ C gen.get() == Approx(expected) - 0.2 == Approx( 0.2 ) + 0.19999999999999987 +== +Approx( 0.19999999999999987 ) - + Current expected value is 0.2 @@ -7532,7 +7691,7 @@ C true - + Current expected value is 0.3 @@ -7540,10 +7699,12 @@ C gen.get() == Approx(expected) - 0.3 == Approx( 0.3 ) + 0.29999999999999988 +== +Approx( 0.29999999999999988 ) - + Current expected value is 0.3 @@ -7554,7 +7715,7 @@ C true - + Current expected value is 0.4 @@ -7562,10 +7723,12 @@ C gen.get() == Approx(expected) - 0.4 == Approx( 0.4 ) + 0.39999999999999991 +== +Approx( 0.39999999999999991 ) - + Current expected value is 0.4 @@ -7576,7 +7739,7 @@ C true - + Current expected value is 0.5 @@ -7584,10 +7747,12 @@ C gen.get() == Approx(expected) - 0.5 == Approx( 0.5 ) + 0.49999999999999989 +== +Approx( 0.49999999999999989 ) - + Current expected value is 0.5 @@ -7598,7 +7763,7 @@ C true - + Current expected value is 0.6 @@ -7606,10 +7771,12 @@ C gen.get() == Approx(expected) - 0.6 == Approx( 0.6 ) + 0.59999999999999987 +== +Approx( 0.59999999999999987 ) - + Current expected value is 0.6 @@ -7620,7 +7787,7 @@ C true - + Current expected value is 0.7 @@ -7628,10 +7795,12 @@ C gen.get() == Approx(expected) - 0.7 == Approx( 0.7 ) + 0.69999999999999984 +== +Approx( 0.69999999999999984 ) - + Current expected value is 0.7 @@ -7642,7 +7811,7 @@ C true - + Current expected value is 0.8 @@ -7650,10 +7819,12 @@ C gen.get() == Approx(expected) - 0.8 == Approx( 0.8 ) + 0.79999999999999982 +== +Approx( 0.79999999999999982 ) - + Current expected value is 0.8 @@ -7664,7 +7835,7 @@ C true - + Current expected value is 0.9 @@ -7672,10 +7843,12 @@ C gen.get() == Approx(expected) - 0.9 == Approx( 0.9 ) + 0.8999999999999998 +== +Approx( 0.8999999999999998 ) - + Current expected value is 0.9 @@ -7691,7 +7864,7 @@ C gen.get() == Approx( rangeEnd ) - 1.0 == Approx( 1.0 ) + 0.99999999999999978 == Approx( 1.0 ) @@ -7714,7 +7887,7 @@ C
- + Current expected value is -1 @@ -7725,7 +7898,7 @@ C -1.0 == Approx( -1.0 ) - + Current expected value is -1 @@ -7736,7 +7909,7 @@ C true - + Current expected value is -0.7 @@ -7744,10 +7917,12 @@ C gen.get() == Approx(expected) - -0.7 == Approx( -0.7 ) + -0.69999999999999996 +== +Approx( -0.69999999999999996 ) - + Current expected value is -0.7 @@ -7758,7 +7933,7 @@ C true - + Current expected value is -0.4 @@ -7766,10 +7941,12 @@ C gen.get() == Approx(expected) - -0.4 == Approx( -0.4 ) + -0.39999999999999997 +== +Approx( -0.39999999999999997 ) - + Current expected value is -0.4 @@ -7780,7 +7957,7 @@ C true - + Current expected value is -0.1 @@ -7788,10 +7965,12 @@ C gen.get() == Approx(expected) - -0.1 == Approx( -0.1 ) + -0.09999999999999998 +== +Approx( -0.09999999999999998 ) - + Current expected value is -0.1 @@ -7802,7 +7981,7 @@ C true - + Current expected value is 0.2 @@ -7810,10 +7989,12 @@ C gen.get() == Approx(expected) - 0.2 == Approx( 0.2 ) + 0.20000000000000001 +== +Approx( 0.20000000000000001 ) - + Current expected value is 0.2 @@ -7824,7 +8005,7 @@ C true - + Current expected value is 0.5 @@ -7835,7 +8016,7 @@ C 0.5 == Approx( 0.5 ) - + Current expected value is 0.5 @@ -7866,7 +8047,7 @@ C
- + Current expected value is -1 @@ -7877,7 +8058,7 @@ C -1.0 == Approx( -1.0 ) - + Current expected value is -1 @@ -7888,7 +8069,7 @@ C true - + Current expected value is -0.7 @@ -7896,10 +8077,12 @@ C gen.get() == Approx(expected) - -0.7 == Approx( -0.7 ) + -0.69999999999999996 +== +Approx( -0.69999999999999996 ) - + Current expected value is -0.7 @@ -7910,7 +8093,7 @@ C true - + Current expected value is -0.4 @@ -7918,10 +8101,12 @@ C gen.get() == Approx(expected) - -0.4 == Approx( -0.4 ) + -0.39999999999999997 +== +Approx( -0.39999999999999997 ) - + Current expected value is -0.4 @@ -7932,7 +8117,7 @@ C true - + Current expected value is -0.1 @@ -7940,10 +8125,12 @@ C gen.get() == Approx(expected) - -0.1 == Approx( -0.1 ) + -0.09999999999999998 +== +Approx( -0.09999999999999998 ) - + Current expected value is -0.1 @@ -7954,7 +8141,7 @@ C true - + Current expected value is 0.2 @@ -7962,10 +8149,12 @@ C gen.get() == Approx(expected) - 0.2 == Approx( 0.2 ) + 0.20000000000000001 +== +Approx( 0.20000000000000001 ) - + Current expected value is 0.2 @@ -7976,7 +8165,7 @@ C true - + Current expected value is 0.5 @@ -7987,7 +8176,7 @@ C 0.5 == Approx( 0.5 ) - + Current expected value is 0.5 @@ -8266,7 +8455,9 @@ C d >= Approx( 1.22 ) - 1.23 >= Approx( 1.22 ) + 1.22999999999999998 +>= +Approx( 1.21999999999999997 ) @@ -8274,7 +8465,9 @@ C d >= Approx( 1.23 ) - 1.23 >= Approx( 1.23 ) + 1.22999999999999998 +>= +Approx( 1.22999999999999998 ) @@ -8282,7 +8475,9 @@ C !(d >= Approx( 1.24 )) - !(1.23 >= Approx( 1.24 )) + !(1.22999999999999998 +>= +Approx( 1.23999999999999999 )) @@ -8290,7 +8485,9 @@ C d >= Approx( 1.24 ).epsilon(0.1) - 1.23 >= Approx( 1.24 ) + 1.22999999999999998 +>= +Approx( 1.23999999999999999 ) @@ -8376,20 +8573,32 @@ C + + + This info has multiple parts. + + + This unscoped info has multiple parts. + + + Show infos! + + + - + this is a message - + this is a warning - + this message should be logged - + so should this @@ -8403,7 +8612,7 @@ C - + this message may be logged later @@ -8414,10 +8623,10 @@ C 2 == 2 - + this message may be logged later - + this message should be logged @@ -8428,13 +8637,13 @@ C 2 == 1 - + this message may be logged later - + this message should be logged - + and this, but later @@ -8445,16 +8654,16 @@ C 2 == 0 - + this message may be logged later - + this message should be logged - + and this, but later - + but not this @@ -8468,10 +8677,10 @@ C - + current counter 0 - + i := 0 @@ -8482,10 +8691,10 @@ C 0 < 10 - + current counter 1 - + i := 1 @@ -8496,10 +8705,10 @@ C 1 < 10 - + current counter 2 - + i := 2 @@ -8510,10 +8719,10 @@ C 2 < 10 - + current counter 3 - + i := 3 @@ -8524,10 +8733,10 @@ C 3 < 10 - + current counter 4 - + i := 4 @@ -8538,10 +8747,10 @@ C 4 < 10 - + current counter 5 - + i := 5 @@ -8552,10 +8761,10 @@ C 5 < 10 - + current counter 6 - + i := 6 @@ -8566,10 +8775,10 @@ C 6 < 10 - + current counter 7 - + i := 7 @@ -8580,10 +8789,10 @@ C 7 < 10 - + current counter 8 - + i := 8 @@ -8594,10 +8803,10 @@ C 8 < 10 - + current counter 9 - + i := 9 @@ -8608,10 +8817,10 @@ C 9 < 10 - + current counter 10 - + i := 10 @@ -8624,6 +8833,20 @@ C + + + + Dummy + + + Dummy + + + Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE + + + + @@ -8638,7 +8861,9 @@ C data.float_nine_point_one != Approx( 9.1f ) - 9.1f != Approx( 9.1000003815 ) + 9.100000381f +!= +Approx( 9.10000038146972656 ) @@ -8646,7 +8871,9 @@ C data.double_pi != Approx( 3.1415926535 ) - 3.1415926535 != Approx( 3.1415926535 ) + 3.14159265350000005 +!= +Approx( 3.14159265350000005 ) @@ -8689,7 +8916,9 @@ C data.float_nine_point_one != Approx( 9.11f ) - 9.1f != Approx( 9.1099996567 ) + 9.100000381f +!= +Approx( 9.10999965667724609 ) @@ -8697,7 +8926,7 @@ C data.float_nine_point_one != Approx( 9.0f ) - 9.1f != Approx( 9.0 ) + 9.100000381f != Approx( 9.0 ) @@ -8705,7 +8934,7 @@ C data.float_nine_point_one != Approx( 1 ) - 9.1f != Approx( 1.0 ) + 9.100000381f != Approx( 1.0 ) @@ -8713,7 +8942,7 @@ C data.float_nine_point_one != Approx( 0 ) - 9.1f != Approx( 0.0 ) + 9.100000381f != Approx( 0.0 ) @@ -8721,7 +8950,9 @@ C data.double_pi != Approx( 3.1415 ) - 3.1415926535 != Approx( 3.1415 ) + 3.14159265350000005 +!= +Approx( 3.14150000000000018 ) @@ -8758,6 +8989,277 @@ C + +
+ + + stream.str() == "" + + + "" == "" + + + +
+
+ + + stream.str() == "{\n}" + + + "{ +}" +== +"{ +}" + + + +
+
+ + + stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) + + + "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] +}" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ + 1, + 2 + ] +}" ) + + + +
+
+ + + stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) + + + "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } +}" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) + + + +
+
+ + + stream.str() == "[\n]" + + + "[ +]" +== +"[ +]" + + + +
+
+ + + stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" + + + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +== +"[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" + + + +
+
+ + + stream.str() == "{\n}" + + + "{ +}" +== +"{ +}" + + + +
+
+ + + stream.str() == "[\n]" + + + "[ +]" +== +"[ +]" + + + +
+
+ + + stream.str() == "\"custom\"" + + + ""custom"" == ""custom"" + + + +
+ +
+ +
+ + + sstream.str() == "\"\\\"\"" + + + ""\""" == ""\""" + + + +
+
+ + + sstream.str() == "\"\\\\\"" + + + ""\\"" == ""\\"" + + + +
+
+ + + sstream.str() == "\"/\"" + + + ""/"" == ""/"" + + + +
+
+ + + sstream.str() == "\"\\b\"" + + + ""\b"" == ""\b"" + + + +
+
+ + + sstream.str() == "\"\\f\"" + + + ""\f"" == ""\f"" + + + +
+
+ + + sstream.str() == "\"\\n\"" + + + ""\n"" == ""\n"" + + + +
+
+ + + sstream.str() == "\"\\r\"" + + + ""\r"" == ""\r"" + + + +
+
+ + + sstream.str() == "\"\\t\"" + + + ""\t"" == ""\t"" + + + +
+
+ + + sstream.str() == "\"\\\\/\\t\\r\\n\"" + + + ""\\/\t\r\n"" == ""\\/\t\r\n"" + + + +
+ +
@@ -8775,7 +9277,9 @@ C d <= Approx( 1.24 ) - 1.23 <= Approx( 1.24 ) + 1.22999999999999998 +<= +Approx( 1.23999999999999999 ) @@ -8783,7 +9287,9 @@ C d <= Approx( 1.23 ) - 1.23 <= Approx( 1.23 ) + 1.22999999999999998 +<= +Approx( 1.22999999999999998 ) @@ -8791,7 +9297,9 @@ C !(d <= Approx( 1.22 )) - !(1.23 <= Approx( 1.22 )) + !(1.22999999999999998 +<= +Approx( 1.21999999999999997 )) @@ -8799,7 +9307,9 @@ C d <= Approx( 1.22 ).epsilon(0.1) - 1.23 <= Approx( 1.22 ) + 1.22999999999999998 +<= +Approx( 1.21999999999999997 ) @@ -9219,7 +9729,7 @@ C - + This one ran @@ -9330,7 +9840,7 @@ C data.float_nine_point_one < 9 - 9.1f < 9 + 9.100000381f < 9 @@ -9338,7 +9848,7 @@ C data.float_nine_point_one > 10 - 9.1f > 10 + 9.100000381f > 10 @@ -9346,7 +9856,7 @@ C data.float_nine_point_one > 9.2 - 9.1f > 9.2 + 9.100000381f > 9.19999999999999929 @@ -9485,7 +9995,7 @@ C data.float_nine_point_one > 9 - 9.1f > 9 + 9.100000381f > 9 @@ -9493,7 +10003,7 @@ C data.float_nine_point_one < 10 - 9.1f < 10 + 9.100000381f < 10 @@ -9501,7 +10011,7 @@ C data.float_nine_point_one < 9.2 - 9.1f < 9.2 + 9.100000381f < 9.19999999999999929 @@ -10005,7 +10515,7 @@ C - + tagString := "[tag with spaces]" @@ -10016,7 +10526,7 @@ C true - + tagString := "[tag with spaces]" @@ -10027,7 +10537,7 @@ C true - + tagString := "[tag with spaces]" @@ -10038,7 +10548,7 @@ C true - + tagString := "[I said "good day" sir!]" @@ -10049,7 +10559,7 @@ C true - + tagString := "[I said "good day" sir!]" @@ -10060,7 +10570,7 @@ C true - + tagString := "[I said "good day" sir!]" @@ -10471,7 +10981,7 @@ C
- + result.errorMessage() := "" @@ -10482,7 +10992,7 @@ C {?} - + result.errorMessage() := "" @@ -10499,7 +11009,7 @@ C
- + result.errorMessage() := "" @@ -10510,7 +11020,7 @@ C {?} - + result.errorMessage() := "" @@ -10527,7 +11037,7 @@ C
- + result.errorMessage() := "" @@ -10538,7 +11048,7 @@ C {?} - + result.errorMessage() := "" @@ -10577,7 +11087,7 @@ C
- + result.errorMessage() := "" @@ -10588,7 +11098,7 @@ C {?} - + result.errorMessage() := "" @@ -10605,7 +11115,7 @@ C
- + result.errorMessage() := "" @@ -10616,7 +11126,7 @@ C {?} - + result.errorMessage() := "" @@ -11236,7 +11746,9 @@ C config.benchmarkConfidenceInterval == Catch::Approx(0.99) - 0.99 == Approx( 0.99 ) + 0.98999999999999999 +== +Approx( 0.98999999999999999 ) @@ -11441,7 +11953,7 @@ C
- + Tested reporter: Automake @@ -11467,7 +11979,7 @@ C
- + Tested reporter: Automake @@ -11492,7 +12004,7 @@ C
- + Tested reporter: Automake @@ -11519,7 +12031,7 @@ C
- + Tested reporter: compact @@ -11545,7 +12057,7 @@ C
- + Tested reporter: compact @@ -11570,7 +12082,7 @@ C
- + Tested reporter: compact @@ -11597,7 +12109,7 @@ C
- + Tested reporter: console @@ -11623,7 +12135,7 @@ C
- + Tested reporter: console @@ -11648,7 +12160,7 @@ C
- + Tested reporter: console @@ -11674,8 +12186,122 @@ C !false +
+ + Tested reporter: JSON + + + + listingString, ContainsSubstring("fakeTag"s) + + + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" + + + +
+ + + !(factories.empty()) + + + !false + + +
+ + Tested reporter: JSON + + + + listingString, ContainsSubstring("fake reporter"s) + + + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" + + + +
+ + + !(factories.empty()) + + + !false + + +
+ + Tested reporter: JSON + + + + listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) + + + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) + + + +
+ + + !(factories.empty()) + + + !false + +
- + Tested reporter: JUnit @@ -11702,7 +12328,7 @@ All available tags:
- + Tested reporter: JUnit @@ -11728,7 +12354,7 @@ Available reporters:
- + Tested reporter: JUnit @@ -11756,7 +12382,7 @@ All available test cases:
- + Tested reporter: SonarQube @@ -11783,7 +12409,7 @@ All available tags:
- + Tested reporter: SonarQube @@ -11809,7 +12435,7 @@ Available reporters:
- + Tested reporter: SonarQube @@ -11837,7 +12463,7 @@ All available test cases:
- + Tested reporter: TAP @@ -11863,7 +12489,7 @@ All available test cases:
- + Tested reporter: TAP @@ -11888,7 +12514,7 @@ All available test cases:
- + Tested reporter: TAP @@ -11915,7 +12541,7 @@ All available test cases:
- + Tested reporter: TeamCity @@ -11941,7 +12567,7 @@ All available test cases:
- + Tested reporter: TeamCity @@ -11966,7 +12592,7 @@ All available test cases:
- + Tested reporter: TeamCity @@ -11993,7 +12619,7 @@ All available test cases:
- + Tested reporter: XML @@ -12023,7 +12649,7 @@ All available test cases:
- + Tested reporter: XML @@ -12051,7 +12677,7 @@ All available test cases:
- + Tested reporter: XML @@ -12268,7 +12894,9 @@ A string sent to stderr via clog d == Approx( 1.23 ) - 1.23 == Approx( 1.23 ) + 1.22999999999999998 +== +Approx( 1.22999999999999998 ) @@ -12276,7 +12904,9 @@ A string sent to stderr via clog d != Approx( 1.22 ) - 1.23 != Approx( 1.22 ) + 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) @@ -12284,7 +12914,9 @@ A string sent to stderr via clog d != Approx( 1.24 ) - 1.23 != Approx( 1.24 ) + 1.22999999999999998 +!= +Approx( 1.23999999999999999 ) @@ -12292,7 +12924,9 @@ A string sent to stderr via clog d == 1.23_a - 1.23 == Approx( 1.23 ) + 1.22999999999999998 +== +Approx( 1.22999999999999998 ) @@ -12300,7 +12934,9 @@ A string sent to stderr via clog d != 1.22_a - 1.23 != Approx( 1.22 ) + 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) @@ -12308,7 +12944,9 @@ A string sent to stderr via clog Approx( d ) == 1.23 - Approx( 1.23 ) == 1.23 + Approx( 1.22999999999999998 ) +== +1.22999999999999998 @@ -12316,7 +12954,9 @@ A string sent to stderr via clog Approx( d ) != 1.22 - Approx( 1.23 ) != 1.22 + Approx( 1.22999999999999998 ) +!= +1.21999999999999997 @@ -12324,7 +12964,9 @@ A string sent to stderr via clog Approx( d ) != 1.24 - Approx( 1.23 ) != 1.24 + Approx( 1.22999999999999998 ) +!= +1.23999999999999999 @@ -13151,10 +13793,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_default_constructible<TestType>::value - 1 > 0 + true @@ -13162,10 +13804,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_default_constructible<TestType>::value - 4 > 0 + true @@ -13173,10 +13815,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_trivially_copyable<TestType>::value - 1 > 0 + true @@ -13184,10 +13826,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_trivially_copyable<TestType>::value - 4 > 0 + true @@ -13195,10 +13837,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_arithmetic<TestType>::value - 4 > 0 + true @@ -13206,10 +13848,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_arithmetic<TestType>::value - 1 > 0 + true @@ -13217,10 +13859,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_arithmetic<TestType>::value - 4 > 0 + true @@ -14552,6 +15194,50 @@ Message from section two + + + + true + + + true + + + + + {Unknown expression after the reported line} + + + {Unknown expression after the reported line} + + + Uncaught exception should fail! + + + + + + + + false + + + false + + + + + {Unknown expression after the reported line} + + + {Unknown expression after the reported line} + + + Uncaught exception should fail! + + + + @@ -15470,7 +16156,7 @@ There is no extra whitespace here - 3.14 + 3.14000000000000012 @@ -15670,7 +16356,7 @@ There is no extra whitespace here
-
+
data, !AllTrue() @@ -15712,7 +16398,7 @@ There is no extra whitespace here
-
+
data, !AllTrue() @@ -16020,7 +16706,7 @@ There is no extra whitespace here
-
+
data, AnyTrue() @@ -16062,7 +16748,7 @@ There is no extra whitespace here
-
+
data, AnyTrue() @@ -16370,7 +17056,7 @@ There is no extra whitespace here
-
+
data, !NoneTrue() @@ -16412,7 +17098,7 @@ There is no extra whitespace here
-
+
data, !NoneTrue() @@ -17015,7 +17701,9 @@ There is no extra whitespace here d == approx( 1.23 ) - 1.23 == Approx( 1.23 ) + 1.22999999999999998 +== +Approx( 1.22999999999999998 ) @@ -17023,7 +17711,9 @@ There is no extra whitespace here d == approx( 1.22 ) - 1.23 == Approx( 1.22 ) + 1.22999999999999998 +== +Approx( 1.21999999999999997 ) @@ -17031,7 +17721,9 @@ There is no extra whitespace here d == approx( 1.24 ) - 1.23 == Approx( 1.24 ) + 1.22999999999999998 +== +Approx( 1.23999999999999999 ) @@ -17039,7 +17731,7 @@ There is no extra whitespace here d != approx( 1.25 ) - 1.23 != Approx( 1.25 ) + 1.22999999999999998 != Approx( 1.25 ) @@ -17047,7 +17739,9 @@ There is no extra whitespace here approx( d ) == 1.23 - Approx( 1.23 ) == 1.23 + Approx( 1.22999999999999998 ) +== +1.22999999999999998 @@ -17055,7 +17749,9 @@ There is no extra whitespace here approx( d ) == 1.22 - Approx( 1.23 ) == 1.22 + Approx( 1.22999999999999998 ) +== +1.21999999999999997 @@ -17063,7 +17759,9 @@ There is no extra whitespace here approx( d ) == 1.24 - Approx( 1.23 ) == 1.24 + Approx( 1.22999999999999998 ) +== +1.23999999999999999 @@ -17071,7 +17769,7 @@ There is no extra whitespace here approx( d ) != 1.25 - Approx( 1.23 ) != 1.25 + Approx( 1.22999999999999998 ) != 1.25 @@ -18523,7 +19221,9 @@ There is no extra whitespace here erfc_inv(1.103560) == Approx(-0.09203687623843015) - -0.0920368762 == Approx( -0.0920368762 ) + -0.09203687623843014 +== +Approx( -0.09203687623843015 ) @@ -18531,7 +19231,9 @@ There is no extra whitespace here erfc_inv(1.067400) == Approx(-0.05980291115763361) - -0.0598029112 == Approx( -0.0598029112 ) + -0.05980291115763361 +== +Approx( -0.05980291115763361 ) @@ -18539,7 +19241,9 @@ There is no extra whitespace here erfc_inv(0.050000) == Approx(1.38590382434967796) - 1.3859038243 == Approx( 1.3859038243 ) + 1.38590382434967774 +== +Approx( 1.38590382434967796 ) @@ -18766,7 +19470,7 @@ loose text artifact - + Testing if fib[0] (1) is even @@ -18777,7 +19481,7 @@ loose text artifact 1 == 0 - + Testing if fib[1] (1) is even @@ -18788,7 +19492,7 @@ loose text artifact 1 == 0 - + Testing if fib[2] (2) is even @@ -18799,7 +19503,7 @@ loose text artifact 0 == 0 - + Testing if fib[3] (3) is even @@ -18810,7 +19514,7 @@ loose text artifact 1 == 0 - + Testing if fib[4] (5) is even @@ -18821,7 +19525,7 @@ loose text artifact 1 == 0 - + Testing if fib[5] (8) is even @@ -18832,7 +19536,7 @@ loose text artifact 0 == 0 - + Testing if fib[6] (13) is even @@ -18843,7 +19547,7 @@ loose text artifact 1 == 0 - + Testing if fib[7] (21) is even @@ -18982,22 +19686,22 @@ loose text artifact - + info - + unscoped info - + and warn may mix - + info - + unscoped info - + they are not cleared after warnings @@ -19130,56 +19834,15 @@ b1! - - - - normal_cdf(0.000000) == Approx(0.50000000000000000) - - - 0.5 == Approx( 0.5 ) - - - - - normal_cdf(1.000000) == Approx(0.84134474606854293) - - - 0.8413447461 == Approx( 0.8413447461 ) - - - - - normal_cdf(-1.000000) == Approx(0.15865525393145705) - - - 0.1586552539 == Approx( 0.1586552539 ) - - - - - normal_cdf(2.809729) == Approx(0.99752083845315409) - - - 0.9975208385 == Approx( 0.9975208385 ) - - - - - normal_cdf(-1.352570) == Approx(0.08809652095066035) - - - 0.088096521 == Approx( 0.088096521 ) - - - - normal_quantile(0.551780) == Approx(0.13015979861484198) - 0.1301597986 == Approx( 0.1301597986 ) + 0.13015979861484195 +== +Approx( 0.13015979861484198 ) @@ -19187,7 +19850,9 @@ b1! normal_quantile(0.533700) == Approx(0.08457408802851875) - 0.084574088 == Approx( 0.084574088 ) + 0.08457408802851875 +== +Approx( 0.08457408802851875 ) @@ -19195,7 +19860,9 @@ b1! normal_quantile(0.025000) == Approx(-1.95996398454005449) - -1.9599639845 == Approx( -1.9599639845 ) + -1.95996398454005405 +== +Approx( -1.95996398454005449 ) @@ -19204,7 +19871,7 @@ b1! - + this MAY be seen only for the FIRST assertion IF info is printed for passing assertions @@ -19215,7 +19882,7 @@ b1! true - + this MAY be seen only for the SECOND assertion IF info is printed for passing assertions @@ -19226,7 +19893,7 @@ b1! true - + this SHOULD be seen @@ -19362,7 +20029,7 @@ b1! - + this MAY be seen IF info is printed for passing assertions @@ -19376,10 +20043,10 @@ b1! - + this SHOULD be seen - + this SHOULD also be seen @@ -19393,7 +20060,7 @@ b1! - + this SHOULD be seen only ONCE @@ -19412,7 +20079,7 @@ b1! true - + this MAY also be seen only ONCE IF info is printed for passing assertions @@ -19581,6 +20248,50 @@ b1!
+
+
+ + + Catch::replaceInPlace(letters, "c", "cc") + + + true + + + + + letters == "abccdefccg" + + + "abccdefccg" == "abccdefccg" + + + +
+ +
+
+
+ + + Catch::replaceInPlace(s, "--", "-") + + + true + + + + + s == "--" + + + "--" == "--" + + + +
+ +
@@ -19895,7 +20606,7 @@ b1! - + 3 @@ -19909,10 +20620,10 @@ b1! - + hi - + i := 7 @@ -19970,16 +20681,16 @@ b1! - + Count 1 to 3... - + 1 - + 2 - + 3 @@ -19990,16 +20701,16 @@ b1! false - + Count 4 to 6... - + 4 - + 5 - + 6 @@ -20561,18 +21272,18 @@ b1! - "1.2f" == ::Catch::Detail::stringify(float(1.2)) + "1.5f" == ::Catch::Detail::stringify(float(1.5)) - "1.2f" == "1.2f" + "1.5f" == "1.5f" - "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) + "{ 1.5f, 0 }" == ::Catch::Detail::stringify(type{1.5f,0}) - "{ 1.2f, 0 }" == "{ 1.2f, 0 }" + "{ 1.5f, 0 }" == "{ 1.5f, 0 }" @@ -20604,12 +21315,12 @@ b1! - "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) + "{ { 42 }, { }, 1.5f }" == ::Catch::Detail::stringify(value) - "{ { 42 }, { }, 1.2f }" + "{ { 42 }, { }, 1.5f }" == -"{ { 42 }, { }, 1.2f }" +"{ { 42 }, { }, 1.5f }" @@ -20644,7 +21355,26 @@ b1! e.confidence_interval == 0.95 - 0.95 == 0.95 + 0.94999999999999996 == 0.94999999999999996 + + + + + + + + dist.a() == -10 + + + -10 == -10 + + + + + dist.b() == 10 + + + 10 == 10 @@ -21203,6 +21933,6 @@ b1!
- - + + diff --git a/src/external/catch2/tests/SelfTest/Baselines/xml.sw.multi.approved.txt b/src/external/catch2/tests/SelfTest/Baselines/xml.sw.multi.approved.txt index c6ddfc80..d35ba1af 100644 --- a/src/external/catch2/tests/SelfTest/Baselines/xml.sw.multi.approved.txt +++ b/src/external/catch2/tests/SelfTest/Baselines/xml.sw.multi.approved.txt @@ -1,5 +1,5 @@ - + @@ -77,10 +77,10 @@ - + uarr := "123" - + sarr := "456" @@ -91,10 +91,10 @@ 0 == 0 - + uarr := "123" - + sarr := "456" @@ -128,11 +128,11 @@ - + This info message starts with a linebreak - + This warning message starts with a linebreak @@ -384,91 +384,91 @@ Nor would this
- + i := 1 - + j := 3 - + k := 5
- + i := 1 - + j := 3 - + k := 6
- + i := 1 - + j := 4 - + k := 5 - + i := 1 - + j := 4 - + k := 6
- + i := 2 - + j := 3 - + k := 5
- + i := 2 - + j := 3 - + k := 6
- + i := 2 - + j := 4 - + k := 5 - + i := 2 - + j := 4 - + k := 6 @@ -667,7 +667,7 @@ Nor would this
- + failure to init @@ -675,7 +675,7 @@ Nor would this
- + answer := 42 @@ -684,7 +684,7 @@ Nor would this
- + answer := 42 @@ -701,7 +701,7 @@ Nor would this
- + answer := 42 @@ -806,7 +806,7 @@ Nor would this - + dummy := 0 @@ -2056,6 +2056,56 @@ Nor would this + +
+ + + m_a++ == 0 + + + 0 == 0 + + + +
+
+ + + m_a == 0 + + + 1 == 0 + + + +
+ +
+ +
+ + + m_a++ == 0 + + + 0 == 0 + + + +
+
+ + + m_a == 1 + + + 1 == 1 + + + +
+ +
@@ -2150,7 +2200,9 @@ Nor would this d == 1.23_a - 1.23 == Approx( 1.23 ) + 1.22999999999999998 +== +Approx( 1.22999999999999998 ) @@ -2158,7 +2210,9 @@ Nor would this d != 1.22_a - 1.23 != Approx( 1.22 ) + 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) @@ -2166,7 +2220,9 @@ Nor would this -d == -1.23_a - -1.23 == Approx( -1.23 ) + -1.22999999999999998 +== +Approx( -1.22999999999999998 ) @@ -2174,7 +2230,9 @@ Nor would this d == 1.2_a .epsilon(.1) - 1.23 == Approx( 1.2 ) + 1.22999999999999998 +== +Approx( 1.19999999999999996 ) @@ -2182,7 +2240,9 @@ Nor would this d != 1.2_a .epsilon(.001) - 1.23 != Approx( 1.2 ) + 1.22999999999999998 +!= +Approx( 1.19999999999999996 ) @@ -2190,7 +2250,7 @@ Nor would this d == 1_a .epsilon(.3) - 1.23 == Approx( 1.0 ) + 1.22999999999999998 == Approx( 1.0 ) @@ -2264,7 +2324,7 @@ Nor would this 100.3 != Approx(100.0) - 100.3 != Approx( 100.0 ) + 100.29999999999999716 != Approx( 100.0 )
@@ -2272,7 +2332,7 @@ Nor would this 100.3 == Approx(100.0).margin(0.5) - 100.3 == Approx( 100.0 ) + 100.29999999999999716 == Approx( 100.0 ) @@ -2432,7 +2492,9 @@ Nor would this divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 ) - 3.1428571429 == Approx( 3.141 ) + 3.14285714285714279 +== +Approx( 3.14100000000000001 ) @@ -2440,7 +2502,9 @@ Nor would this divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 ) - 3.1428571429 != Approx( 3.141 ) + 3.14285714285714279 +!= +Approx( 3.14100000000000001 ) @@ -2451,7 +2515,9 @@ Nor would this d != Approx( 1.231 ) - 1.23 != Approx( 1.231 ) + 1.22999999999999998 +!= +Approx( 1.23100000000000009 ) @@ -2459,7 +2525,9 @@ Nor would this d == Approx( 1.231 ).epsilon( 0.1 ) - 1.23 == Approx( 1.231 ) + 1.22999999999999998 +== +Approx( 1.23100000000000009 ) @@ -2470,7 +2538,9 @@ Nor would this 1.23f == Approx( 1.23f ) - 1.23f == Approx( 1.2300000191 ) + 1.230000019f +== +Approx( 1.23000001907348633 ) @@ -2532,7 +2602,9 @@ Nor would this 1.234f == Approx( dMedium ) - 1.234f == Approx( 1.234 ) + 1.233999968f +== +Approx( 1.23399999999999999 ) @@ -2540,7 +2612,9 @@ Nor would this dMedium == Approx( 1.234f ) - 1.234 == Approx( 1.2339999676 ) + 1.23399999999999999 +== +Approx( 1.23399996757507324 ) @@ -2886,92 +2960,92 @@ Nor would this - + a := 1 - + b := 2 - + c := 3 - + a + b := 3 - + a+b := 3 - + c > b := true - + a == 1 := true - - std::vector<int>{1, 2, 3}[0, 1, 2] := 3 + + custom_index_op<int>{1, 2, 3}[0, 1, 2] := 0 - - std::vector<int>{1, 2, 3}[(0, 1)] := 2 + + custom_index_op<int>{1, 2, 3}[(0, 1)] := 0 - - std::vector<int>{1, 2, 3}[0] := 1 + + custom_index_op<int>{1, 2, 3}[0] := 0 - + (helper_1436<int, int>{12, -12}) := { 12, -12 } - + (helper_1436<int, int>(-12, 12)) := { -12, 12 } - + (1, 2) := 2 - + (2, 3) := 3 - + ("comma, in string", "escaped, \", ") := "escaped, ", " - + "single quote in string,'," := "single quote in string,'," - + "some escapes, \\,\\\\" := "some escapes, \,\\" - + "some, ), unmatched, } prenheses {[<" := "some, ), unmatched, } prenheses {[<" - + '"' := '"' - + '\'' := ''' - + ',' := ',' - + '}' := '}' - + ')' := ')' - + '(' := '(' - + '{' := '{'
- + i := 2 @@ -2985,7 +3059,7 @@ Nor would this
- + 3 @@ -3142,34 +3216,34 @@ Nor would this
- tab == '\t' + ::Catch::Detail::stringify('\t') == "'\\t'" - '\t' == '\t' + "'\t'" == "'\t'" - newline == '\n' + ::Catch::Detail::stringify('\n') == "'\\n'" - '\n' == '\n' + "'\n'" == "'\n'" - carr_return == '\r' + ::Catch::Detail::stringify('\r') == "'\\r'" - '\r' == '\r' + "'\r'" == "'\r'" - form_feed == '\f' + ::Catch::Detail::stringify('\f') == "'\\f'" - '\f' == '\f' + "'\f'" == "'\f'" @@ -3177,91 +3251,110 @@ Nor would this
- space == ' ' - - - ' ' == ' ' - - - - - c == chars[i] - - - 'a' == 'a' - - - - - c == chars[i] + ::Catch::Detail::stringify( ' ' ) == "' '" - 'z' == 'z' + "' '" == "' '" - + - c == chars[i] + ::Catch::Detail::stringify( 'A' ) == "'A'" - 'A' == 'A' + "'A'" == "'A'" - + - c == chars[i] + ::Catch::Detail::stringify( 'z' ) == "'z'" - 'Z' == 'Z' + "'z'" == "'z'" - +
- null_terminator == '\0' - - - 0 == 0 - - - - - c == i - - - 2 == 2 - - - - - c == i + ::Catch::Detail::stringify( '\0' ) == "0" - 3 == 3 + "0" == "0" - + - c == i + ::Catch::Detail::stringify( static_cast<char>(2) ) == "2" - 4 == 4 + "2" == "2" - + - c == i + ::Catch::Detail::stringify( static_cast<char>(5) ) == "5" - 5 == 5 + "5" == "5" - +
+ + + + name.empty() + + + true + + + + + result + + + {?} + + + + + result.type() == Catch::Clara::Detail::ResultType::Ok + + + 0 == 0 + + + + + parsed.type() == Catch::Clara::ParseResultType::NoMatch + + + 1 == 1 + + + + + parsed.remainingTokens().count() == 2 + + + 2 == 2 + + + + + name.empty() + + + true + + + + @@ -4322,7 +4415,7 @@ C 101.000001 != Approx(100).epsilon(0.01) - 101.000001 != Approx( 100.0 ) + 101.00000099999999748 != Approx( 100.0 ) @@ -4364,6 +4457,12 @@ C + + + This generator is empty + + + @@ -4375,17 +4474,6 @@ C - - - - Catch::TestCaseInfo( "", { "fake test name", "[]" }, dummySourceLineInfo ) - - - Catch::TestCaseInfo( "", { "fake test name", "[]" }, dummySourceLineInfo ) - - - - @@ -4475,7 +4563,7 @@ C 101.01 != Approx(100).epsilon(0.01) - 101.01 != Approx( 100.0 ) + 101.01000000000000512 != Approx( 100.0 ) @@ -4510,7 +4598,9 @@ C data.float_nine_point_one == Approx( 9.11f ) - 9.1f == Approx( 9.1099996567 ) + 9.100000381f +== +Approx( 9.10999965667724609 ) @@ -4518,7 +4608,7 @@ C data.float_nine_point_one == Approx( 9.0f ) - 9.1f == Approx( 9.0 ) + 9.100000381f == Approx( 9.0 ) @@ -4526,7 +4616,7 @@ C data.float_nine_point_one == Approx( 1 ) - 9.1f == Approx( 1.0 ) + 9.100000381f == Approx( 1.0 ) @@ -4534,7 +4624,7 @@ C data.float_nine_point_one == Approx( 0 ) - 9.1f == Approx( 0.0 ) + 9.100000381f == Approx( 0.0 ) @@ -4542,7 +4632,9 @@ C data.double_pi == Approx( 3.1415 ) - 3.1415926535 == Approx( 3.1415 ) + 3.14159265350000005 +== +Approx( 3.14150000000000018 ) @@ -4582,7 +4674,9 @@ C x == Approx( 1.301 ) - 1.3 == Approx( 1.301 ) + 1.30000000000000027 +== +Approx( 1.30099999999999993 ) @@ -4601,7 +4695,9 @@ C data.float_nine_point_one == Approx( 9.1f ) - 9.1f == Approx( 9.1000003815 ) + 9.100000381f +== +Approx( 9.10000038146972656 ) @@ -4609,7 +4705,9 @@ C data.double_pi == Approx( 3.1415926535 ) - 3.1415926535 == Approx( 3.1415926535 ) + 3.14159265350000005 +== +Approx( 3.14159265350000005 ) @@ -4641,7 +4739,9 @@ C x == Approx( 1.3 ) - 1.3 == Approx( 1.3 ) + 1.30000000000000027 +== +Approx( 1.30000000000000004 ) @@ -4977,7 +5077,7 @@ C This is a failure - + This message appears in the output @@ -5043,7 +5143,7 @@ C 10., WithinRel( 11.1, 0.1 ) - 10.0 and 11.1 are within 10% of each other + 10.0 and 11.09999999999999964 are within 10% of each other @@ -5051,7 +5151,7 @@ C 10., !WithinRel( 11.2, 0.1 ) - 10.0 not and 11.2 are within 10% of each other + 10.0 not and 11.19999999999999929 are within 10% of each other @@ -5059,7 +5159,7 @@ C 1., !WithinRel( 0., 0.99 ) - 1.0 not and 0 are within 99% of each other + 1.0 not and 0.0 are within 99% of each other @@ -5067,7 +5167,7 @@ C -0., WithinRel( 0. ) - -0.0 and 0 are within 2.22045e-12% of each other + -0.0 and 0.0 are within 2.22045e-12% of each other
@@ -5076,7 +5176,7 @@ C v1, WithinRel( v2 ) - 0.0 and 2.22507e-308 are within 2.22045e-12% of each other + 0.0 and 0.0 are within 2.22045e-12% of each other @@ -5105,7 +5205,7 @@ C 0., !WithinAbs( 1., 0.99 ) - 0.0 not is within 0.99 of 1.0 + 0.0 not is within 0.98999999999999999 of 1.0 @@ -5113,7 +5213,7 @@ C 0., !WithinAbs( 1., 0.99 ) - 0.0 not is within 0.99 of 1.0 + 0.0 not is within 0.98999999999999999 of 1.0 @@ -5145,7 +5245,7 @@ C -10., WithinAbs( -9.6, 0.5 ) - -10.0 is within 0.5 of -9.6 + -10.0 is within 0.5 of -9.59999999999999964 @@ -5164,7 +5264,7 @@ C nextafter( 1., 2. ), WithinULP( 1., 1 ) - 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) + 1.00000000000000022 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00]) @@ -5231,7 +5331,7 @@ C 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) - 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + 0.0001 ( is within 0.001 of 0.0 or and 0.0 are within 10% of each other ) @@ -5307,7 +5407,7 @@ C 10.f, WithinRel( 11.1f, 0.1f ) - 10.0f and 11.1 are within 10% of each other + 10.0f and 11.10000038146972656 are within 10% of each other @@ -5315,7 +5415,7 @@ C 10.f, !WithinRel( 11.2f, 0.1f ) - 10.0f not and 11.2 are within 10% of each other + 10.0f not and 11.19999980926513672 are within 10% of each other @@ -5323,7 +5423,7 @@ C 1.f, !WithinRel( 0.f, 0.99f ) - 1.0f not and 0 are within 99% of each other + 1.0f not and 0.0 are within 99% of each other @@ -5331,7 +5431,7 @@ C -0.f, WithinRel( 0.f ) - -0.0f and 0 are within 0.00119209% of each other + -0.0f and 0.0 are within 0.00119209% of each other
@@ -5340,7 +5440,7 @@ C v1, WithinRel( v2 ) - 0.0f and 1.17549e-38 are within 0.00119209% of each other + 0.0f and 0.0 are within 0.00119209% of each other @@ -5369,7 +5469,7 @@ C 0.f, !WithinAbs( 1.f, 0.99f ) - 0.0f not is within 0.9900000095 of 1.0 + 0.0f not is within 0.99000000953674316 of 1.0 @@ -5377,7 +5477,7 @@ C 0.f, !WithinAbs( 1.f, 0.99f ) - 0.0f not is within 0.9900000095 of 1.0 + 0.0f not is within 0.99000000953674316 of 1.0 @@ -5417,7 +5517,7 @@ C -10.f, WithinAbs( -9.6f, 0.5f ) - -10.0f is within 0.5 of -9.6000003815 + -10.0f is within 0.5 of -9.60000038146972656 @@ -5444,7 +5544,7 @@ C nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) - 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) + 1.000000119f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) @@ -5511,7 +5611,7 @@ C 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) - 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other ) + 0.0001f ( is within 0.00100000004749745 of 0.0 or and 0.0 are within 10% of each other ) @@ -5588,6 +5688,41 @@ C
+ + + + i % 2 == 0 + + + 0 == 0 + + + + + i % 2 == 0 + + + 0 == 0 + + + + + i % 2 == 0 + + + 0 == 0 + + + + + i % 2 == 0 + + + 0 == 0 + + + +
@@ -7246,7 +7381,7 @@ C
- + Current expected value is -1 @@ -7257,7 +7392,7 @@ C -1.0 == Approx( -1.0 ) - + Current expected value is -1 @@ -7268,7 +7403,7 @@ C true - + Current expected value is -0.9 @@ -7276,10 +7411,12 @@ C gen.get() == Approx(expected) - -0.9 == Approx( -0.9 ) + -0.90000000000000002 +== +Approx( -0.90000000000000002 ) - + Current expected value is -0.9 @@ -7290,7 +7427,7 @@ C true - + Current expected value is -0.8 @@ -7298,10 +7435,12 @@ C gen.get() == Approx(expected) - -0.8 == Approx( -0.8 ) + -0.80000000000000004 +== +Approx( -0.80000000000000004 ) - + Current expected value is -0.8 @@ -7312,7 +7451,7 @@ C true - + Current expected value is -0.7 @@ -7320,10 +7459,12 @@ C gen.get() == Approx(expected) - -0.7 == Approx( -0.7 ) + -0.70000000000000007 +== +Approx( -0.70000000000000007 ) - + Current expected value is -0.7 @@ -7334,7 +7475,7 @@ C true - + Current expected value is -0.6 @@ -7342,10 +7483,12 @@ C gen.get() == Approx(expected) - -0.6 == Approx( -0.6 ) + -0.60000000000000009 +== +Approx( -0.60000000000000009 ) - + Current expected value is -0.6 @@ -7356,7 +7499,7 @@ C true - + Current expected value is -0.5 @@ -7364,10 +7507,12 @@ C gen.get() == Approx(expected) - -0.5 == Approx( -0.5 ) + -0.50000000000000011 +== +Approx( -0.50000000000000011 ) - + Current expected value is -0.5 @@ -7378,7 +7523,7 @@ C true - + Current expected value is -0.4 @@ -7386,10 +7531,12 @@ C gen.get() == Approx(expected) - -0.4 == Approx( -0.4 ) + -0.40000000000000013 +== +Approx( -0.40000000000000013 ) - + Current expected value is -0.4 @@ -7400,7 +7547,7 @@ C true - + Current expected value is -0.3 @@ -7408,10 +7555,12 @@ C gen.get() == Approx(expected) - -0.3 == Approx( -0.3 ) + -0.30000000000000016 +== +Approx( -0.30000000000000016 ) - + Current expected value is -0.3 @@ -7422,7 +7571,7 @@ C true - + Current expected value is -0.2 @@ -7430,10 +7579,12 @@ C gen.get() == Approx(expected) - -0.2 == Approx( -0.2 ) + -0.20000000000000015 +== +Approx( -0.20000000000000015 ) - + Current expected value is -0.2 @@ -7444,7 +7595,7 @@ C true - + Current expected value is -0.1 @@ -7452,10 +7603,12 @@ C gen.get() == Approx(expected) - -0.1 == Approx( -0.1 ) + -0.10000000000000014 +== +Approx( -0.10000000000000014 ) - + Current expected value is -0.1 @@ -7466,7 +7619,7 @@ C true - + Current expected value is -1.38778e-16 @@ -7474,10 +7627,12 @@ C gen.get() == Approx(expected) - -0.0 == Approx( -0.0 ) + -0.00000000000000014 +== +Approx( -0.00000000000000014 ) - + Current expected value is -1.38778e-16 @@ -7488,7 +7643,7 @@ C true - + Current expected value is 0.1 @@ -7496,10 +7651,12 @@ C gen.get() == Approx(expected) - 0.1 == Approx( 0.1 ) + 0.09999999999999987 +== +Approx( 0.09999999999999987 ) - + Current expected value is 0.1 @@ -7510,7 +7667,7 @@ C true - + Current expected value is 0.2 @@ -7518,10 +7675,12 @@ C gen.get() == Approx(expected) - 0.2 == Approx( 0.2 ) + 0.19999999999999987 +== +Approx( 0.19999999999999987 ) - + Current expected value is 0.2 @@ -7532,7 +7691,7 @@ C true - + Current expected value is 0.3 @@ -7540,10 +7699,12 @@ C gen.get() == Approx(expected) - 0.3 == Approx( 0.3 ) + 0.29999999999999988 +== +Approx( 0.29999999999999988 ) - + Current expected value is 0.3 @@ -7554,7 +7715,7 @@ C true - + Current expected value is 0.4 @@ -7562,10 +7723,12 @@ C gen.get() == Approx(expected) - 0.4 == Approx( 0.4 ) + 0.39999999999999991 +== +Approx( 0.39999999999999991 ) - + Current expected value is 0.4 @@ -7576,7 +7739,7 @@ C true - + Current expected value is 0.5 @@ -7584,10 +7747,12 @@ C gen.get() == Approx(expected) - 0.5 == Approx( 0.5 ) + 0.49999999999999989 +== +Approx( 0.49999999999999989 ) - + Current expected value is 0.5 @@ -7598,7 +7763,7 @@ C true - + Current expected value is 0.6 @@ -7606,10 +7771,12 @@ C gen.get() == Approx(expected) - 0.6 == Approx( 0.6 ) + 0.59999999999999987 +== +Approx( 0.59999999999999987 ) - + Current expected value is 0.6 @@ -7620,7 +7787,7 @@ C true - + Current expected value is 0.7 @@ -7628,10 +7795,12 @@ C gen.get() == Approx(expected) - 0.7 == Approx( 0.7 ) + 0.69999999999999984 +== +Approx( 0.69999999999999984 ) - + Current expected value is 0.7 @@ -7642,7 +7811,7 @@ C true - + Current expected value is 0.8 @@ -7650,10 +7819,12 @@ C gen.get() == Approx(expected) - 0.8 == Approx( 0.8 ) + 0.79999999999999982 +== +Approx( 0.79999999999999982 ) - + Current expected value is 0.8 @@ -7664,7 +7835,7 @@ C true - + Current expected value is 0.9 @@ -7672,10 +7843,12 @@ C gen.get() == Approx(expected) - 0.9 == Approx( 0.9 ) + 0.8999999999999998 +== +Approx( 0.8999999999999998 ) - + Current expected value is 0.9 @@ -7691,7 +7864,7 @@ C gen.get() == Approx( rangeEnd ) - 1.0 == Approx( 1.0 ) + 0.99999999999999978 == Approx( 1.0 ) @@ -7714,7 +7887,7 @@ C
- + Current expected value is -1 @@ -7725,7 +7898,7 @@ C -1.0 == Approx( -1.0 ) - + Current expected value is -1 @@ -7736,7 +7909,7 @@ C true - + Current expected value is -0.7 @@ -7744,10 +7917,12 @@ C gen.get() == Approx(expected) - -0.7 == Approx( -0.7 ) + -0.69999999999999996 +== +Approx( -0.69999999999999996 ) - + Current expected value is -0.7 @@ -7758,7 +7933,7 @@ C true - + Current expected value is -0.4 @@ -7766,10 +7941,12 @@ C gen.get() == Approx(expected) - -0.4 == Approx( -0.4 ) + -0.39999999999999997 +== +Approx( -0.39999999999999997 ) - + Current expected value is -0.4 @@ -7780,7 +7957,7 @@ C true - + Current expected value is -0.1 @@ -7788,10 +7965,12 @@ C gen.get() == Approx(expected) - -0.1 == Approx( -0.1 ) + -0.09999999999999998 +== +Approx( -0.09999999999999998 ) - + Current expected value is -0.1 @@ -7802,7 +7981,7 @@ C true - + Current expected value is 0.2 @@ -7810,10 +7989,12 @@ C gen.get() == Approx(expected) - 0.2 == Approx( 0.2 ) + 0.20000000000000001 +== +Approx( 0.20000000000000001 ) - + Current expected value is 0.2 @@ -7824,7 +8005,7 @@ C true - + Current expected value is 0.5 @@ -7835,7 +8016,7 @@ C 0.5 == Approx( 0.5 ) - + Current expected value is 0.5 @@ -7866,7 +8047,7 @@ C
- + Current expected value is -1 @@ -7877,7 +8058,7 @@ C -1.0 == Approx( -1.0 ) - + Current expected value is -1 @@ -7888,7 +8069,7 @@ C true - + Current expected value is -0.7 @@ -7896,10 +8077,12 @@ C gen.get() == Approx(expected) - -0.7 == Approx( -0.7 ) + -0.69999999999999996 +== +Approx( -0.69999999999999996 ) - + Current expected value is -0.7 @@ -7910,7 +8093,7 @@ C true - + Current expected value is -0.4 @@ -7918,10 +8101,12 @@ C gen.get() == Approx(expected) - -0.4 == Approx( -0.4 ) + -0.39999999999999997 +== +Approx( -0.39999999999999997 ) - + Current expected value is -0.4 @@ -7932,7 +8117,7 @@ C true - + Current expected value is -0.1 @@ -7940,10 +8125,12 @@ C gen.get() == Approx(expected) - -0.1 == Approx( -0.1 ) + -0.09999999999999998 +== +Approx( -0.09999999999999998 ) - + Current expected value is -0.1 @@ -7954,7 +8141,7 @@ C true - + Current expected value is 0.2 @@ -7962,10 +8149,12 @@ C gen.get() == Approx(expected) - 0.2 == Approx( 0.2 ) + 0.20000000000000001 +== +Approx( 0.20000000000000001 ) - + Current expected value is 0.2 @@ -7976,7 +8165,7 @@ C true - + Current expected value is 0.5 @@ -7987,7 +8176,7 @@ C 0.5 == Approx( 0.5 ) - + Current expected value is 0.5 @@ -8266,7 +8455,9 @@ C d >= Approx( 1.22 ) - 1.23 >= Approx( 1.22 ) + 1.22999999999999998 +>= +Approx( 1.21999999999999997 ) @@ -8274,7 +8465,9 @@ C d >= Approx( 1.23 ) - 1.23 >= Approx( 1.23 ) + 1.22999999999999998 +>= +Approx( 1.22999999999999998 ) @@ -8282,7 +8475,9 @@ C !(d >= Approx( 1.24 )) - !(1.23 >= Approx( 1.24 )) + !(1.22999999999999998 +>= +Approx( 1.23999999999999999 )) @@ -8290,7 +8485,9 @@ C d >= Approx( 1.24 ).epsilon(0.1) - 1.23 >= Approx( 1.24 ) + 1.22999999999999998 +>= +Approx( 1.23999999999999999 ) @@ -8376,20 +8573,32 @@ C + + + This info has multiple parts. + + + This unscoped info has multiple parts. + + + Show infos! + + + - + this is a message - + this is a warning - + this message should be logged - + so should this @@ -8403,7 +8612,7 @@ C - + this message may be logged later @@ -8414,10 +8623,10 @@ C 2 == 2 - + this message may be logged later - + this message should be logged @@ -8428,13 +8637,13 @@ C 2 == 1 - + this message may be logged later - + this message should be logged - + and this, but later @@ -8445,16 +8654,16 @@ C 2 == 0 - + this message may be logged later - + this message should be logged - + and this, but later - + but not this @@ -8468,10 +8677,10 @@ C - + current counter 0 - + i := 0 @@ -8482,10 +8691,10 @@ C 0 < 10 - + current counter 1 - + i := 1 @@ -8496,10 +8705,10 @@ C 1 < 10 - + current counter 2 - + i := 2 @@ -8510,10 +8719,10 @@ C 2 < 10 - + current counter 3 - + i := 3 @@ -8524,10 +8733,10 @@ C 3 < 10 - + current counter 4 - + i := 4 @@ -8538,10 +8747,10 @@ C 4 < 10 - + current counter 5 - + i := 5 @@ -8552,10 +8761,10 @@ C 5 < 10 - + current counter 6 - + i := 6 @@ -8566,10 +8775,10 @@ C 6 < 10 - + current counter 7 - + i := 7 @@ -8580,10 +8789,10 @@ C 7 < 10 - + current counter 8 - + i := 8 @@ -8594,10 +8803,10 @@ C 8 < 10 - + current counter 9 - + i := 9 @@ -8608,10 +8817,10 @@ C 9 < 10 - + current counter 10 - + i := 10 @@ -8624,6 +8833,20 @@ C + + + + Dummy + + + Dummy + + + Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE + + + + @@ -8638,7 +8861,9 @@ C data.float_nine_point_one != Approx( 9.1f ) - 9.1f != Approx( 9.1000003815 ) + 9.100000381f +!= +Approx( 9.10000038146972656 ) @@ -8646,7 +8871,9 @@ C data.double_pi != Approx( 3.1415926535 ) - 3.1415926535 != Approx( 3.1415926535 ) + 3.14159265350000005 +!= +Approx( 3.14159265350000005 ) @@ -8689,7 +8916,9 @@ C data.float_nine_point_one != Approx( 9.11f ) - 9.1f != Approx( 9.1099996567 ) + 9.100000381f +!= +Approx( 9.10999965667724609 ) @@ -8697,7 +8926,7 @@ C data.float_nine_point_one != Approx( 9.0f ) - 9.1f != Approx( 9.0 ) + 9.100000381f != Approx( 9.0 ) @@ -8705,7 +8934,7 @@ C data.float_nine_point_one != Approx( 1 ) - 9.1f != Approx( 1.0 ) + 9.100000381f != Approx( 1.0 ) @@ -8713,7 +8942,7 @@ C data.float_nine_point_one != Approx( 0 ) - 9.1f != Approx( 0.0 ) + 9.100000381f != Approx( 0.0 ) @@ -8721,7 +8950,9 @@ C data.double_pi != Approx( 3.1415 ) - 3.1415926535 != Approx( 3.1415 ) + 3.14159265350000005 +!= +Approx( 3.14150000000000018 ) @@ -8758,6 +8989,277 @@ C + +
+ + + stream.str() == "" + + + "" == "" + + + +
+
+ + + stream.str() == "{\n}" + + + "{ +}" +== +"{ +}" + + + +
+
+ + + stream.str(), ContainsSubstring( "\"int\": 1," ) && ContainsSubstring( "\"double\": 1.5," ) && ContainsSubstring( "\"true\": true," ) && ContainsSubstring( "\"false\": false," ) && ContainsSubstring( "\"string\": \"this is a string\"," ) && ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) + + + "{ + "int": 1, + "double": 1.5, + "true": true, + "false": false, + "string": "this is a string", + "array": [ + 1, + 2 + ] +}" ( contains: ""int": 1," and contains: ""double": 1.5," and contains: ""true": true," and contains: ""false": false," and contains: ""string": "this is a string"," and contains: ""array": [ + 1, + 2 + ] +}" ) + + + +
+
+ + + stream.str(), ContainsSubstring( "\"empty_object\": {\n }," ) && ContainsSubstring( "\"fully_object\": {\n \"key\": 1\n }" ) + + + "{ + "empty_object": { + }, + "fully_object": { + "key": 1 + } +}" ( contains: ""empty_object": { + }," and contains: ""fully_object": { + "key": 1 + }" ) + + + +
+
+ + + stream.str() == "[\n]" + + + "[ +]" +== +"[ +]" + + + +
+
+ + + stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" + + + "[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" +== +"[ + 1, + 1.5, + true, + false, + "this is a string", + { + "object": 42 + }, + [ + "array", + 42.5 + ] +]" + + + +
+
+ + + stream.str() == "{\n}" + + + "{ +}" +== +"{ +}" + + + +
+
+ + + stream.str() == "[\n]" + + + "[ +]" +== +"[ +]" + + + +
+
+ + + stream.str() == "\"custom\"" + + + ""custom"" == ""custom"" + + + +
+ +
+ +
+ + + sstream.str() == "\"\\\"\"" + + + ""\""" == ""\""" + + + +
+
+ + + sstream.str() == "\"\\\\\"" + + + ""\\"" == ""\\"" + + + +
+
+ + + sstream.str() == "\"/\"" + + + ""/"" == ""/"" + + + +
+
+ + + sstream.str() == "\"\\b\"" + + + ""\b"" == ""\b"" + + + +
+
+ + + sstream.str() == "\"\\f\"" + + + ""\f"" == ""\f"" + + + +
+
+ + + sstream.str() == "\"\\n\"" + + + ""\n"" == ""\n"" + + + +
+
+ + + sstream.str() == "\"\\r\"" + + + ""\r"" == ""\r"" + + + +
+
+ + + sstream.str() == "\"\\t\"" + + + ""\t"" == ""\t"" + + + +
+
+ + + sstream.str() == "\"\\\\/\\t\\r\\n\"" + + + ""\\/\t\r\n"" == ""\\/\t\r\n"" + + + +
+ +
@@ -8775,7 +9277,9 @@ C d <= Approx( 1.24 ) - 1.23 <= Approx( 1.24 ) + 1.22999999999999998 +<= +Approx( 1.23999999999999999 ) @@ -8783,7 +9287,9 @@ C d <= Approx( 1.23 ) - 1.23 <= Approx( 1.23 ) + 1.22999999999999998 +<= +Approx( 1.22999999999999998 ) @@ -8791,7 +9297,9 @@ C !(d <= Approx( 1.22 )) - !(1.23 <= Approx( 1.22 )) + !(1.22999999999999998 +<= +Approx( 1.21999999999999997 )) @@ -8799,7 +9307,9 @@ C d <= Approx( 1.22 ).epsilon(0.1) - 1.23 <= Approx( 1.22 ) + 1.22999999999999998 +<= +Approx( 1.21999999999999997 ) @@ -9219,7 +9729,7 @@ C - + This one ran @@ -9330,7 +9840,7 @@ C data.float_nine_point_one < 9 - 9.1f < 9 + 9.100000381f < 9 @@ -9338,7 +9848,7 @@ C data.float_nine_point_one > 10 - 9.1f > 10 + 9.100000381f > 10 @@ -9346,7 +9856,7 @@ C data.float_nine_point_one > 9.2 - 9.1f > 9.2 + 9.100000381f > 9.19999999999999929 @@ -9485,7 +9995,7 @@ C data.float_nine_point_one > 9 - 9.1f > 9 + 9.100000381f > 9 @@ -9493,7 +10003,7 @@ C data.float_nine_point_one < 10 - 9.1f < 10 + 9.100000381f < 10 @@ -9501,7 +10011,7 @@ C data.float_nine_point_one < 9.2 - 9.1f < 9.2 + 9.100000381f < 9.19999999999999929 @@ -10005,7 +10515,7 @@ C - + tagString := "[tag with spaces]" @@ -10016,7 +10526,7 @@ C true - + tagString := "[tag with spaces]" @@ -10027,7 +10537,7 @@ C true - + tagString := "[tag with spaces]" @@ -10038,7 +10548,7 @@ C true - + tagString := "[I said "good day" sir!]" @@ -10049,7 +10559,7 @@ C true - + tagString := "[I said "good day" sir!]" @@ -10060,7 +10570,7 @@ C true - + tagString := "[I said "good day" sir!]" @@ -10471,7 +10981,7 @@ C
- + result.errorMessage() := "" @@ -10482,7 +10992,7 @@ C {?} - + result.errorMessage() := "" @@ -10499,7 +11009,7 @@ C
- + result.errorMessage() := "" @@ -10510,7 +11020,7 @@ C {?} - + result.errorMessage() := "" @@ -10527,7 +11037,7 @@ C
- + result.errorMessage() := "" @@ -10538,7 +11048,7 @@ C {?} - + result.errorMessage() := "" @@ -10577,7 +11087,7 @@ C
- + result.errorMessage() := "" @@ -10588,7 +11098,7 @@ C {?} - + result.errorMessage() := "" @@ -10605,7 +11115,7 @@ C
- + result.errorMessage() := "" @@ -10616,7 +11126,7 @@ C {?} - + result.errorMessage() := "" @@ -11236,7 +11746,9 @@ C config.benchmarkConfidenceInterval == Catch::Approx(0.99) - 0.99 == Approx( 0.99 ) + 0.98999999999999999 +== +Approx( 0.98999999999999999 ) @@ -11441,7 +11953,7 @@ C
- + Tested reporter: Automake @@ -11467,7 +11979,7 @@ C
- + Tested reporter: Automake @@ -11492,7 +12004,7 @@ C
- + Tested reporter: Automake @@ -11519,7 +12031,7 @@ C
- + Tested reporter: compact @@ -11545,7 +12057,7 @@ C
- + Tested reporter: compact @@ -11570,7 +12082,7 @@ C
- + Tested reporter: compact @@ -11597,7 +12109,7 @@ C
- + Tested reporter: console @@ -11623,7 +12135,7 @@ C
- + Tested reporter: console @@ -11648,7 +12160,7 @@ C
- + Tested reporter: console @@ -11674,8 +12186,122 @@ C !false +
+ + Tested reporter: JSON + + + + listingString, ContainsSubstring("fakeTag"s) + + + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tags": [ + { + "aliases": [ + "fakeTag" + ], + "count": 1 + } + ]" contains: "fakeTag" + + + +
+ + + !(factories.empty()) + + + !false + + +
+ + Tested reporter: JSON + + + + listingString, ContainsSubstring("fake reporter"s) + + + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "reporters": [ + { + "name": "fake reporter", + "description": "fake description" + } + ]" contains: "fake reporter" + + + +
+ + + !(factories.empty()) + + + !false + + +
+ + Tested reporter: JSON + + + + listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) + + + "{ + "version": 1, + "metadata": { + "name": "", + "rng-seed": 1234, + "catch2-version": "" + }, + "listings": { + "tests": [ + { + "name": "fake test name", + "class-name": "", + "tags": [ + "fakeTestTag" + ], + "source-location": { + "filename": "fake-file.cpp", + "line": 123456789 + } + } + ]" ( contains: "fake test name" and contains: "fakeTestTag" ) + + + +
+ + + !(factories.empty()) + + + !false + +
- + Tested reporter: JUnit @@ -11702,7 +12328,7 @@ All available tags:
- + Tested reporter: JUnit @@ -11728,7 +12354,7 @@ Available reporters:
- + Tested reporter: JUnit @@ -11756,7 +12382,7 @@ All available test cases:
- + Tested reporter: SonarQube @@ -11783,7 +12409,7 @@ All available tags:
- + Tested reporter: SonarQube @@ -11809,7 +12435,7 @@ Available reporters:
- + Tested reporter: SonarQube @@ -11837,7 +12463,7 @@ All available test cases:
- + Tested reporter: TAP @@ -11863,7 +12489,7 @@ All available test cases:
- + Tested reporter: TAP @@ -11888,7 +12514,7 @@ All available test cases:
- + Tested reporter: TAP @@ -11915,7 +12541,7 @@ All available test cases:
- + Tested reporter: TeamCity @@ -11941,7 +12567,7 @@ All available test cases:
- + Tested reporter: TeamCity @@ -11966,7 +12592,7 @@ All available test cases:
- + Tested reporter: TeamCity @@ -11993,7 +12619,7 @@ All available test cases:
- + Tested reporter: XML @@ -12023,7 +12649,7 @@ All available test cases:
- + Tested reporter: XML @@ -12051,7 +12677,7 @@ All available test cases:
- + Tested reporter: XML @@ -12268,7 +12894,9 @@ A string sent to stderr via clog d == Approx( 1.23 ) - 1.23 == Approx( 1.23 ) + 1.22999999999999998 +== +Approx( 1.22999999999999998 ) @@ -12276,7 +12904,9 @@ A string sent to stderr via clog d != Approx( 1.22 ) - 1.23 != Approx( 1.22 ) + 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) @@ -12284,7 +12914,9 @@ A string sent to stderr via clog d != Approx( 1.24 ) - 1.23 != Approx( 1.24 ) + 1.22999999999999998 +!= +Approx( 1.23999999999999999 ) @@ -12292,7 +12924,9 @@ A string sent to stderr via clog d == 1.23_a - 1.23 == Approx( 1.23 ) + 1.22999999999999998 +== +Approx( 1.22999999999999998 ) @@ -12300,7 +12934,9 @@ A string sent to stderr via clog d != 1.22_a - 1.23 != Approx( 1.22 ) + 1.22999999999999998 +!= +Approx( 1.21999999999999997 ) @@ -12308,7 +12944,9 @@ A string sent to stderr via clog Approx( d ) == 1.23 - Approx( 1.23 ) == 1.23 + Approx( 1.22999999999999998 ) +== +1.22999999999999998 @@ -12316,7 +12954,9 @@ A string sent to stderr via clog Approx( d ) != 1.22 - Approx( 1.23 ) != 1.22 + Approx( 1.22999999999999998 ) +!= +1.21999999999999997 @@ -12324,7 +12964,9 @@ A string sent to stderr via clog Approx( d ) != 1.24 - Approx( 1.23 ) != 1.24 + Approx( 1.22999999999999998 ) +!= +1.23999999999999999 @@ -13151,10 +13793,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_default_constructible<TestType>::value - 1 > 0 + true @@ -13162,10 +13804,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_default_constructible<TestType>::value - 4 > 0 + true @@ -13173,10 +13815,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_trivially_copyable<TestType>::value - 1 > 0 + true @@ -13184,10 +13826,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_trivially_copyable<TestType>::value - 4 > 0 + true @@ -13195,10 +13837,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_arithmetic<TestType>::value - 4 > 0 + true @@ -13206,10 +13848,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_arithmetic<TestType>::value - 1 > 0 + true @@ -13217,10 +13859,10 @@ Message from section two - sizeof(TestType) > 0 + std::is_arithmetic<TestType>::value - 4 > 0 + true @@ -14552,6 +15194,50 @@ Message from section two + + + + true + + + true + + + + + {Unknown expression after the reported line} + + + {Unknown expression after the reported line} + + + Uncaught exception should fail! + + + + + + + + false + + + false + + + + + {Unknown expression after the reported line} + + + {Unknown expression after the reported line} + + + Uncaught exception should fail! + + + + @@ -15470,7 +16156,7 @@ There is no extra whitespace here - 3.14 + 3.14000000000000012 @@ -15670,7 +16356,7 @@ There is no extra whitespace here
-
+
data, !AllTrue() @@ -15712,7 +16398,7 @@ There is no extra whitespace here
-
+
data, !AllTrue() @@ -16020,7 +16706,7 @@ There is no extra whitespace here
-
+
data, AnyTrue() @@ -16062,7 +16748,7 @@ There is no extra whitespace here
-
+
data, AnyTrue() @@ -16370,7 +17056,7 @@ There is no extra whitespace here
-
+
data, !NoneTrue() @@ -16412,7 +17098,7 @@ There is no extra whitespace here
-
+
data, !NoneTrue() @@ -17015,7 +17701,9 @@ There is no extra whitespace here d == approx( 1.23 ) - 1.23 == Approx( 1.23 ) + 1.22999999999999998 +== +Approx( 1.22999999999999998 ) @@ -17023,7 +17711,9 @@ There is no extra whitespace here d == approx( 1.22 ) - 1.23 == Approx( 1.22 ) + 1.22999999999999998 +== +Approx( 1.21999999999999997 ) @@ -17031,7 +17721,9 @@ There is no extra whitespace here d == approx( 1.24 ) - 1.23 == Approx( 1.24 ) + 1.22999999999999998 +== +Approx( 1.23999999999999999 ) @@ -17039,7 +17731,7 @@ There is no extra whitespace here d != approx( 1.25 ) - 1.23 != Approx( 1.25 ) + 1.22999999999999998 != Approx( 1.25 ) @@ -17047,7 +17739,9 @@ There is no extra whitespace here approx( d ) == 1.23 - Approx( 1.23 ) == 1.23 + Approx( 1.22999999999999998 ) +== +1.22999999999999998 @@ -17055,7 +17749,9 @@ There is no extra whitespace here approx( d ) == 1.22 - Approx( 1.23 ) == 1.22 + Approx( 1.22999999999999998 ) +== +1.21999999999999997 @@ -17063,7 +17759,9 @@ There is no extra whitespace here approx( d ) == 1.24 - Approx( 1.23 ) == 1.24 + Approx( 1.22999999999999998 ) +== +1.23999999999999999 @@ -17071,7 +17769,7 @@ There is no extra whitespace here approx( d ) != 1.25 - Approx( 1.23 ) != 1.25 + Approx( 1.22999999999999998 ) != 1.25 @@ -18523,7 +19221,9 @@ There is no extra whitespace here erfc_inv(1.103560) == Approx(-0.09203687623843015) - -0.0920368762 == Approx( -0.0920368762 ) + -0.09203687623843014 +== +Approx( -0.09203687623843015 ) @@ -18531,7 +19231,9 @@ There is no extra whitespace here erfc_inv(1.067400) == Approx(-0.05980291115763361) - -0.0598029112 == Approx( -0.0598029112 ) + -0.05980291115763361 +== +Approx( -0.05980291115763361 ) @@ -18539,7 +19241,9 @@ There is no extra whitespace here erfc_inv(0.050000) == Approx(1.38590382434967796) - 1.3859038243 == Approx( 1.3859038243 ) + 1.38590382434967774 +== +Approx( 1.38590382434967796 ) @@ -18765,7 +19469,7 @@ There is no extra whitespace here - + Testing if fib[0] (1) is even @@ -18776,7 +19480,7 @@ There is no extra whitespace here 1 == 0 - + Testing if fib[1] (1) is even @@ -18787,7 +19491,7 @@ There is no extra whitespace here 1 == 0 - + Testing if fib[2] (2) is even @@ -18798,7 +19502,7 @@ There is no extra whitespace here 0 == 0 - + Testing if fib[3] (3) is even @@ -18809,7 +19513,7 @@ There is no extra whitespace here 1 == 0 - + Testing if fib[4] (5) is even @@ -18820,7 +19524,7 @@ There is no extra whitespace here 1 == 0 - + Testing if fib[5] (8) is even @@ -18831,7 +19535,7 @@ There is no extra whitespace here 0 == 0 - + Testing if fib[6] (13) is even @@ -18842,7 +19546,7 @@ There is no extra whitespace here 1 == 0 - + Testing if fib[7] (21) is even @@ -18981,22 +19685,22 @@ There is no extra whitespace here - + info - + unscoped info - + and warn may mix - + info - + unscoped info - + they are not cleared after warnings @@ -19129,56 +19833,15 @@ b1! - - - - normal_cdf(0.000000) == Approx(0.50000000000000000) - - - 0.5 == Approx( 0.5 ) - - - - - normal_cdf(1.000000) == Approx(0.84134474606854293) - - - 0.8413447461 == Approx( 0.8413447461 ) - - - - - normal_cdf(-1.000000) == Approx(0.15865525393145705) - - - 0.1586552539 == Approx( 0.1586552539 ) - - - - - normal_cdf(2.809729) == Approx(0.99752083845315409) - - - 0.9975208385 == Approx( 0.9975208385 ) - - - - - normal_cdf(-1.352570) == Approx(0.08809652095066035) - - - 0.088096521 == Approx( 0.088096521 ) - - - - normal_quantile(0.551780) == Approx(0.13015979861484198) - 0.1301597986 == Approx( 0.1301597986 ) + 0.13015979861484195 +== +Approx( 0.13015979861484198 ) @@ -19186,7 +19849,9 @@ b1! normal_quantile(0.533700) == Approx(0.08457408802851875) - 0.084574088 == Approx( 0.084574088 ) + 0.08457408802851875 +== +Approx( 0.08457408802851875 ) @@ -19194,7 +19859,9 @@ b1! normal_quantile(0.025000) == Approx(-1.95996398454005449) - -1.9599639845 == Approx( -1.9599639845 ) + -1.95996398454005405 +== +Approx( -1.95996398454005449 ) @@ -19203,7 +19870,7 @@ b1! - + this MAY be seen only for the FIRST assertion IF info is printed for passing assertions @@ -19214,7 +19881,7 @@ b1! true - + this MAY be seen only for the SECOND assertion IF info is printed for passing assertions @@ -19225,7 +19892,7 @@ b1! true - + this SHOULD be seen @@ -19361,7 +20028,7 @@ b1! - + this MAY be seen IF info is printed for passing assertions @@ -19375,10 +20042,10 @@ b1! - + this SHOULD be seen - + this SHOULD also be seen @@ -19392,7 +20059,7 @@ b1! - + this SHOULD be seen only ONCE @@ -19411,7 +20078,7 @@ b1! true - + this MAY also be seen only ONCE IF info is printed for passing assertions @@ -19580,6 +20247,50 @@ b1!
+
+
+ + + Catch::replaceInPlace(letters, "c", "cc") + + + true + + + + + letters == "abccdefccg" + + + "abccdefccg" == "abccdefccg" + + + +
+ +
+
+
+ + + Catch::replaceInPlace(s, "--", "-") + + + true + + + + + s == "--" + + + "--" == "--" + + + +
+ +
@@ -19894,7 +20605,7 @@ b1! - + 3 @@ -19908,10 +20619,10 @@ b1! - + hi - + i := 7 @@ -19969,16 +20680,16 @@ b1! - + Count 1 to 3... - + 1 - + 2 - + 3 @@ -19989,16 +20700,16 @@ b1! false - + Count 4 to 6... - + 4 - + 5 - + 6 @@ -20560,18 +21271,18 @@ b1! - "1.2f" == ::Catch::Detail::stringify(float(1.2)) + "1.5f" == ::Catch::Detail::stringify(float(1.5)) - "1.2f" == "1.2f" + "1.5f" == "1.5f" - "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) + "{ 1.5f, 0 }" == ::Catch::Detail::stringify(type{1.5f,0}) - "{ 1.2f, 0 }" == "{ 1.2f, 0 }" + "{ 1.5f, 0 }" == "{ 1.5f, 0 }" @@ -20603,12 +21314,12 @@ b1! - "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) + "{ { 42 }, { }, 1.5f }" == ::Catch::Detail::stringify(value) - "{ { 42 }, { }, 1.2f }" + "{ { 42 }, { }, 1.5f }" == -"{ { 42 }, { }, 1.2f }" +"{ { 42 }, { }, 1.5f }" @@ -20643,7 +21354,26 @@ b1! e.confidence_interval == 0.95 - 0.95 == 0.95 + 0.94999999999999996 == 0.94999999999999996 + + + + + + + + dist.a() == -10 + + + -10 == -10 + + + + + dist.b() == 10 + + + 10 == 10 @@ -21202,6 +21932,6 @@ b1!
- - + + diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp new file mode 100644 index 00000000..ab096074 --- /dev/null +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp @@ -0,0 +1,17 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#include + +TEST_CASE( "Incomplete AssertionHandler", "[assertion-handler][!shouldfail]" ) { + Catch::AssertionHandler catchAssertionHandler( + "REQUIRE"_catch_sr, + CATCH_INTERNAL_LINEINFO, + "Dummy", + Catch::ResultDisposition::Normal ); +} diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/Clara.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Clara.tests.cpp index 14ba433d..53023b50 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/Clara.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Clara.tests.cpp @@ -51,6 +51,21 @@ TEST_CASE("Clara::Arg supports single-arg parse the way Opt does", "[clara][arg] REQUIRE(name == "foo"); } +TEST_CASE("Clara::Arg does not crash on incomplete input", "[clara][arg][compilation]") { + std::string name; + auto p = Catch::Clara::Arg(name, "-"); + + CHECK(name.empty()); + + auto result = p.parse( Catch::Clara::Args{ "UnitTest", "-" } ); + CHECK( result ); + CHECK( result.type() == Catch::Clara::Detail::ResultType::Ok ); + const auto& parsed = result.value(); + CHECK( parsed.type() == Catch::Clara::ParseResultType::NoMatch ); + CHECK( parsed.remainingTokens().count() == 2 ); + CHECK( name.empty() ); +} + TEST_CASE("Clara::Opt supports accept-many lambdas", "[clara][opt]") { using namespace Catch::Clara; std::vector res; diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/Details.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Details.tests.cpp index a5a43926..5566bb59 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/Details.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Details.tests.cpp @@ -89,6 +89,47 @@ TEST_CASE("Optional comparison ops", "[optional][approvals]") { } } +namespace { + struct MoveChecker { + bool has_moved = false; + MoveChecker() = default; + MoveChecker( MoveChecker const& rhs ) = default; + MoveChecker& operator=( MoveChecker const& rhs ) = default; + MoveChecker( MoveChecker&& rhs ) noexcept { rhs.has_moved = true; } + MoveChecker& operator=( MoveChecker&& rhs ) noexcept { + rhs.has_moved = true; + return *this; + } + }; +} + +TEST_CASE( "Optional supports move ops", "[optional][approvals]" ) { + using Catch::Optional; + MoveChecker a; + Optional opt_A( a ); + REQUIRE_FALSE( a.has_moved ); + REQUIRE_FALSE( opt_A->has_moved ); + + SECTION( "Move construction from element" ) { + Optional opt_B( CATCH_MOVE( a ) ); + REQUIRE( a.has_moved ); + } + SECTION( "Move assignment from element" ) { + opt_A = CATCH_MOVE( a ); + REQUIRE( a.has_moved ); + } + SECTION( "Move construction from optional" ) { + Optional opt_B( CATCH_MOVE( opt_A ) ); + REQUIRE( opt_A->has_moved ); // NOLINT(clang-analyzer-cplusplus.Move) + } + SECTION( "Move assignment from optional" ) { + Optional opt_B( opt_A ); + REQUIRE_FALSE( opt_A->has_moved ); + opt_B = CATCH_MOVE( opt_A ); + REQUIRE( opt_A->has_moved ); // NOLINT(clang-analyzer-cplusplus.Move) + } +} + TEST_CASE( "Decomposer checks that the argument is 0 when handling " "only-0-comparable types", "[decomposition][approvals]" ) { diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp index 08a579c9..d2181702 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp @@ -9,7 +9,9 @@ #include #include #include +#include +#include TEST_CASE("convertToBits", "[floating-point][conversion]") { using Catch::Detail::convertToBits; @@ -72,3 +74,66 @@ TEST_CASE("UlpDistance", "[floating-point][ulp][approvals]") { CHECK( ulpDistance( 1.f, 2.f ) == 0x80'00'00 ); CHECK( ulpDistance( -2.f, 2.f ) == 0x80'00'00'00 ); } + + + +TEMPLATE_TEST_CASE("gamma", "[approvals][floating-point][ulp][gamma]", float, double) { + using Catch::Detail::gamma; + using Catch::Detail::directCompare; + + // We need to butcher the equal tests with the directCompare helper, + // because the Wfloat-equal triggers in decomposer rather than here, + // so we cannot locally disable it. Goddamn GCC. + CHECK( directCompare( gamma( TestType( -1. ), TestType( 1. ) ), + gamma( TestType( 0.2332 ), TestType( 1.0 ) ) ) ); + CHECK( directCompare( gamma( TestType( -2. ), TestType( 0 ) ), + gamma( TestType( 1. ), TestType( 1.5 ) ) ) ); + CHECK( gamma( TestType( 0. ), TestType( 1.0 ) ) < + gamma( TestType( 1.0 ), TestType( 1.5 ) ) ); + CHECK( gamma( TestType( 0 ), TestType( 1. ) ) < + std::numeric_limits::epsilon() ); + CHECK( gamma( TestType( -1. ), TestType( -0. ) ) < + std::numeric_limits::epsilon() ); + CHECK( directCompare( gamma( TestType( 1. ), TestType( 2. ) ), + std::numeric_limits::epsilon() ) ); + CHECK( directCompare( gamma( TestType( -2. ), TestType( -1. ) ), + std::numeric_limits::epsilon() ) ); +} + +TEMPLATE_TEST_CASE("count_equidistant_floats", + "[approvals][floating-point][distance]", + float, + double) { + using Catch::Detail::count_equidistant_floats; + auto count_steps = []( TestType a, TestType b ) { + return count_equidistant_floats( a, b, Catch::Detail::gamma( a, b ) ); + }; + + CHECK( count_steps( TestType( -1. ), TestType( 1. ) ) == + 2 * count_steps( TestType( 0. ), TestType( 1. ) ) ); +} + +TEST_CASE( "count_equidistant_floats", + "[approvals][floating-point][distance]" ) { + using Catch::Detail::count_equidistant_floats; + auto count_floats_with_scaled_ulp = []( auto a, auto b ) { + return count_equidistant_floats( a, b, Catch::Detail::gamma( a, b ) ); + }; + + CHECK( count_floats_with_scaled_ulp( 1., 1.5 ) == 1ull << 51 ); + CHECK( count_floats_with_scaled_ulp( 1.25, 1.5 ) == 1ull << 50 ); + CHECK( count_floats_with_scaled_ulp( 1.f, 1.5f ) == 1 << 22 ); + CHECK( count_floats_with_scaled_ulp( -std::numeric_limits::max(), + std::numeric_limits::max() ) == + 33554430 ); // (1 << 25) - 2 due to not including infinities + CHECK( count_floats_with_scaled_ulp( -std::numeric_limits::max(), + std::numeric_limits::max() ) == + 18014398509481982 ); // (1 << 54) - 2 due to not including infinities + + STATIC_REQUIRE( std::is_same::value ); + STATIC_REQUIRE( std::is_same::value ); +} diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp index 64e943f8..14c90114 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp @@ -10,13 +10,14 @@ # pragma GCC diagnostic ignored "-Wfloat-equal" #endif +#include + #include #include #include #include #include #include -#include // Tests of generator implementation details TEST_CASE("Generators internals", "[generators][internals]") { @@ -412,6 +413,7 @@ TEST_CASE("GENERATE handles function (pointers)", "[generators][compilation][app TEST_CASE("GENERATE decays arrays", "[generators][compilation][approvals]") { auto str = GENERATE("abc", "def", "gh"); + (void)str; STATIC_REQUIRE(std::is_same::value); } @@ -544,3 +546,30 @@ TEST_CASE("Filter generator throws exception for empty generator", filter( []( int ) { return false; }, value( 3 ) ), Catch::GeneratorException ); } + +TEST_CASE("from_range(container) supports ADL begin/end and arrays", "[generators][from-range][approvals]") { + using namespace Catch::Generators; + + SECTION("C array") { + int arr[3]{ 5, 6, 7 }; + auto gen = from_range( arr ); + REQUIRE( gen.get() == 5 ); + REQUIRE( gen.next() ); + REQUIRE( gen.get() == 6 ); + REQUIRE( gen.next() ); + REQUIRE( gen.get() == 7 ); + REQUIRE_FALSE( gen.next() ); + } + + SECTION( "ADL range" ) { + unrelated::needs_ADL_begin range{ 1, 2, 3 }; + auto gen = from_range( range ); + REQUIRE( gen.get() == 1 ); + REQUIRE( gen.next() ); + REQUIRE( gen.get() == 2 ); + REQUIRE( gen.next() ); + REQUIRE( gen.get() == 3 ); + REQUIRE_FALSE( gen.next() ); + } + +} diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp new file mode 100644 index 00000000..8955f400 --- /dev/null +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp @@ -0,0 +1,224 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#include +#include +#include + +namespace { + template + static void + CommutativeMultCheck( Int a, Int b, Int upper_result, Int lower_result ) { + using Catch::Detail::extendedMult; + using Catch::Detail::ExtendedMultResult; + CHECK( extendedMult( a, b ) == + ExtendedMultResult{ upper_result, lower_result } ); + CHECK( extendedMult( b, a ) == + ExtendedMultResult{ upper_result, lower_result } ); + } + + // Simple (and slow) implmentation of extended multiplication for tests + constexpr Catch::Detail::ExtendedMultResult + extendedMultNaive( std::uint64_t lhs, std::uint64_t rhs ) { + // This is a simple long multiplication, where we split lhs and rhs + // into two 32-bit "digits", so that we can do ops with carry in 64-bits. + // + // 32b 32b 32b 32b + // lhs L1 L2 + // * rhs R1 R2 + // ------------------------ + // | R2 * L2 | + // | R2 * L1 | + // | R1 * L2 | + // | R1 * L1 | + // ------------------------- + // | a | b | c | d | + +#define CarryBits( x ) ( x >> 32 ) +#define Digits( x ) ( x & 0xFF'FF'FF'FF ) + + auto r2l2 = Digits( rhs ) * Digits( lhs ); + auto r2l1 = Digits( rhs ) * CarryBits( lhs ); + auto r1l2 = CarryBits( rhs ) * Digits( lhs ); + auto r1l1 = CarryBits( rhs ) * CarryBits( lhs ); + + // Sum to columns first + auto d = Digits( r2l2 ); + auto c = CarryBits( r2l2 ) + Digits( r2l1 ) + Digits( r1l2 ); + auto b = CarryBits( r2l1 ) + CarryBits( r1l2 ) + Digits( r1l1 ); + auto a = CarryBits( r1l1 ); + + // Propagate carries between columns + c += CarryBits( d ); + b += CarryBits( c ); + a += CarryBits( b ); + + // Remove the used carries + c = Digits( c ); + b = Digits( b ); + a = Digits( a ); + +#undef CarryBits +#undef Digits + + return { + a << 32 | b, // upper 64 bits + c << 32 | d // lower 64 bits + }; + } + + +} // namespace + +TEST_CASE( "extendedMult 64x64", "[Integer][approvals]" ) { + // a x 0 == 0 + CommutativeMultCheck( 0x1234'5678'9ABC'DEFF, 0, 0, 0 ); + + // bit carried from low half to upper half + CommutativeMultCheck( uint64_t( 1 ) << 63, 2, 1, 0 ); + + // bits in upper half on one side, bits in lower half on other side + CommutativeMultCheck( 0xcdcd'dcdc'0000'0000, + 0x0000'0000'aeae'aeae, + 0x0000'0000'8c6e'5a77, + 0x7391'a588'0000'0000 ); + + // Some input numbers without interesting patterns + CommutativeMultCheck( 0xaaaa'aaaa'aaaa'aaaa, + 0xbbbb'bbbb'bbbb'bbbb, + 0x7d27'd27d'27d2'7d26, + 0xd82d'82d8'2d82'd82e ); + + CommutativeMultCheck( 0x7d27'd27d'27d2'7d26, + 0xd82d'82d8'2d82'd82e, + 0x69af'd991'8256'b953, + 0x8724'8909'fcb6'8cd4 ); + + CommutativeMultCheck( 0xdead'beef'dead'beef, + 0xfeed'feed'feed'feef, + 0xddbf'680b'2b0c'b558, + 0x7a36'b06f'2ce9'6321 ); + + CommutativeMultCheck( 0xddbf'680b'2b0c'b558, + 0x7a36'b06f'2ce9'6321, + 0x69dc'96c9'294b'fc7f, + 0xd038'39fa'a3dc'6858 ); + + CommutativeMultCheck( 0x61c8'8646'80b5'83eb, + 0x61c8'8646'80b5'83eb, + 0x2559'92d3'8220'8bbe, + 0xdf44'2d22'ce48'59b9 ); +} + +TEST_CASE("extendedMult 64x64 - all implementations", "[integer][approvals]") { + using Catch::Detail::extendedMult; + using Catch::Detail::extendedMultPortable; + using Catch::Detail::fillBitsFrom; + + std::random_device rng; + for (size_t i = 0; i < 100; ++i) { + auto a = fillBitsFrom( rng ); + auto b = fillBitsFrom( rng ); + CAPTURE( a, b ); + + auto naive_ab = extendedMultNaive( a, b ); + + REQUIRE( naive_ab == extendedMultNaive( b, a ) ); + REQUIRE( naive_ab == extendedMultPortable( a, b ) ); + REQUIRE( naive_ab == extendedMultPortable( b, a ) ); + REQUIRE( naive_ab == extendedMult( a, b ) ); + REQUIRE( naive_ab == extendedMult( b, a ) ); + } +} + +TEST_CASE( "SizedUnsignedType helpers", "[integer][approvals]" ) { + using Catch::Detail::SizedUnsignedType_t; + using Catch::Detail::DoubleWidthUnsignedType_t; + + STATIC_REQUIRE( sizeof( SizedUnsignedType_t<1> ) == 1 ); + STATIC_REQUIRE( sizeof( SizedUnsignedType_t<2> ) == 2 ); + STATIC_REQUIRE( sizeof( SizedUnsignedType_t<4> ) == 4 ); + STATIC_REQUIRE( sizeof( SizedUnsignedType_t<8> ) == 8 ); + + STATIC_REQUIRE( sizeof( DoubleWidthUnsignedType_t ) == 2 ); + STATIC_REQUIRE( std::is_unsigned>::value ); + STATIC_REQUIRE( sizeof( DoubleWidthUnsignedType_t ) == 4 ); + STATIC_REQUIRE( std::is_unsigned>::value ); + STATIC_REQUIRE( sizeof( DoubleWidthUnsignedType_t ) == 8 ); + STATIC_REQUIRE( std::is_unsigned>::value ); +} + +TEST_CASE( "extendedMult 32x32", "[integer][approvals]" ) { + // a x 0 == 0 + CommutativeMultCheck( 0x1234'5678, 0, 0, 0 ); + + // bit carried from low half to upper half + CommutativeMultCheck( uint32_t(1) << 31, 2, 1, 0 ); + + // bits in upper half on one side, bits in lower half on other side + CommutativeMultCheck( 0xdcdc'0000, 0x0000'aabb, 0x0000'934b, 0x6cb4'0000 ); + + // Some input numbers without interesting patterns + CommutativeMultCheck( + 0xaaaa'aaaa, 0xbbbb'bbbb, 0x7d27'd27c, 0x2d82'd82e ); + + CommutativeMultCheck( + 0x7d27'd27c, 0x2d82'd82e, 0x163f'f7e8, 0xc5b8'7248 ); + + CommutativeMultCheck( + 0xdead'beef, 0xfeed'feed, 0xddbf'6809, 0x6f8d'e543 ); + + CommutativeMultCheck( + 0xddbf'6809, 0x6f8d'e543, 0x60a0'e71e, 0x751d'475b ); +} + +TEST_CASE( "extendedMult 8x8", "[integer][approvals]" ) { + // a x 0 == 0 + CommutativeMultCheck( 0xcd, 0, 0, 0 ); + + // bit carried from low half to upper half + CommutativeMultCheck( uint8_t( 1 ) << 7, 2, 1, 0 ); + + // bits in upper half on one side, bits in lower half on other side + CommutativeMultCheck( 0x80, 0x03, 0x01, 0x80 ); + + // Some input numbers without interesting patterns + CommutativeMultCheck( 0xaa, 0xbb, 0x7c, 0x2e ); + CommutativeMultCheck( 0x7c, 0x2e, 0x16, 0x48 ); + CommutativeMultCheck( 0xdc, 0xcd, 0xb0, 0x2c ); + CommutativeMultCheck( 0xb0, 0x2c, 0x1e, 0x40 ); +} + + +TEST_CASE( "negative and positive signed integers keep their order after transposeToNaturalOrder", + "[integer][approvals]") { + using Catch::Detail::transposeToNaturalOrder; + int32_t negative( -1 ); + int32_t positive( 1 ); + uint32_t adjusted_negative = + transposeToNaturalOrder( static_cast( negative ) ); + uint32_t adjusted_positive = + transposeToNaturalOrder( static_cast( positive ) ); + REQUIRE( adjusted_negative < adjusted_positive ); + REQUIRE( adjusted_positive - adjusted_negative == 2 ); + + // Conversion has to be reversible + REQUIRE( negative == static_cast( transposeToNaturalOrder( + adjusted_negative ) ) ); + REQUIRE( positive == static_cast( transposeToNaturalOrder( + adjusted_positive ) ) ); +} + +TEST_CASE( "unsigned integers are unchanged by transposeToNaturalOrder", + "[integer][approvals]") { + using Catch::Detail::transposeToNaturalOrder; + uint32_t max = std::numeric_limits::max(); + uint32_t zero = 0; + REQUIRE( max == transposeToNaturalOrder( max ) ); + REQUIRE( zero == transposeToNaturalOrder( zero ) ); +} diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp index 96c0977b..69251d97 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp @@ -22,6 +22,8 @@ #include #include +#include + namespace { struct manual_clock { public: @@ -154,8 +156,12 @@ TEST_CASE("uniform samples", "[benchmark]") { std::vector samples(100); std::fill(samples.begin(), samples.end(), 23); - using it = std::vector::iterator; - auto e = Catch::Benchmark::Detail::bootstrap(0.95, samples.begin(), samples.end(), samples, [](it a, it b) { + auto e = Catch::Benchmark::Detail::bootstrap( + 0.95, + samples.data(), + samples.data() + samples.size(), + samples, + []( double const* a, double const* b ) { auto sum = std::accumulate(a, b, 0.); return sum / (b - a); }); @@ -166,7 +172,7 @@ TEST_CASE("uniform samples", "[benchmark]") { } -TEST_CASE("normal_cdf", "[benchmark]") { +TEST_CASE("normal_cdf", "[benchmark][approvals]") { using Catch::Benchmark::Detail::normal_cdf; using Catch::Approx; CHECK(normal_cdf(0.000000) == Approx(0.50000000000000000)); @@ -196,7 +202,7 @@ TEST_CASE("normal_quantile", "[benchmark]") { TEST_CASE("mean", "[benchmark]") { std::vector x{ 10., 20., 14., 16., 30., 24. }; - auto m = Catch::Benchmark::Detail::mean(x.begin(), x.end()); + auto m = Catch::Benchmark::Detail::mean(x.data(), x.data() + x.size()); REQUIRE(m == 19.); } @@ -204,9 +210,9 @@ TEST_CASE("mean", "[benchmark]") { TEST_CASE("weighted_average_quantile", "[benchmark]") { std::vector x{ 10., 20., 14., 16., 30., 24. }; - auto q1 = Catch::Benchmark::Detail::weighted_average_quantile(1, 4, x.begin(), x.end()); - auto med = Catch::Benchmark::Detail::weighted_average_quantile(1, 2, x.begin(), x.end()); - auto q3 = Catch::Benchmark::Detail::weighted_average_quantile(3, 4, x.begin(), x.end()); + auto q1 = Catch::Benchmark::Detail::weighted_average_quantile(1, 4, x.data(), x.data() + x.size()); + auto med = Catch::Benchmark::Detail::weighted_average_quantile(1, 2, x.data(), x.data() + x.size()); + auto q3 = Catch::Benchmark::Detail::weighted_average_quantile(3, 4, x.data(), x.data() + x.size()); REQUIRE(q1 == 14.5); REQUIRE(med == 18.); @@ -225,7 +231,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("none") { std::vector x{ 10., 20., 14., 16., 30., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast(x.size())); require_outliers(o, 0, 0, 0, 0); @@ -233,7 +240,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("low severe") { std::vector x{ -12., 20., 14., 16., 30., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast(x.size())); require_outliers(o, 1, 0, 0, 0); @@ -241,7 +249,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("low mild") { std::vector x{ 1., 20., 14., 16., 30., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast(x.size())); require_outliers(o, 0, 1, 0, 0); @@ -249,7 +258,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("high mild") { std::vector x{ 10., 20., 14., 16., 36., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast(x.size())); require_outliers(o, 0, 0, 1, 0); @@ -257,7 +267,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("high severe") { std::vector x{ 10., 20., 14., 16., 49., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast(x.size())); require_outliers(o, 0, 0, 0, 1); @@ -265,7 +276,8 @@ TEST_CASE("classify_outliers", "[benchmark]") { SECTION("mixed") { std::vector x{ -20., 20., 14., 16., 39., 24. }; - auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); + auto o = Catch::Benchmark::Detail::classify_outliers( + x.data(), x.data() + x.size() ); REQUIRE(o.samples_seen == static_cast(x.size())); require_outliers(o, 1, 0, 1, 0); @@ -280,15 +292,13 @@ TEST_CASE("analyse", "[approvals][benchmark]") { data.benchmarkSamples = 99; Catch::Config config{data}; - using Duration = Catch::Benchmark::FloatDuration; - - Catch::Benchmark::Environment env; - std::vector samples(99); + using FDuration = Catch::Benchmark::FDuration; + std::vector samples(99); for (size_t i = 0; i < samples.size(); ++i) { - samples[i] = Duration(23 + (i % 3 - 1)); + samples[i] = FDuration(23 + (i % 3 - 1)); } - auto analysis = Catch::Benchmark::Detail::analyse(config, env, samples.begin(), samples.end()); + auto analysis = Catch::Benchmark::Detail::analyse(config, samples.data(), samples.data() + samples.size()); CHECK( analysis.mean.point.count() == 23 ); CHECK( analysis.mean.lower_bound.count() < 23 ); CHECK(analysis.mean.lower_bound.count() > 22); @@ -321,15 +331,13 @@ TEST_CASE("analyse no analysis", "[benchmark]") { data.benchmarkSamples = 99; Catch::Config config{ data }; - using Duration = Catch::Benchmark::FloatDuration; - - Catch::Benchmark::Environment env; - std::vector samples(99); + using FDuration = Catch::Benchmark::FDuration; + std::vector samples(99); for (size_t i = 0; i < samples.size(); ++i) { - samples[i] = Duration(23 + (i % 3 - 1)); + samples[i] = FDuration(23 + (i % 3 - 1)); } - auto analysis = Catch::Benchmark::Detail::analyse(config, env, samples.begin(), samples.end()); + auto analysis = Catch::Benchmark::Detail::analyse(config, samples.data(), samples.data() + samples.size()); CHECK(analysis.mean.point.count() == 23); CHECK(analysis.mean.lower_bound.count() == 23); CHECK(analysis.mean.upper_bound.count() == 23); @@ -442,6 +450,6 @@ TEST_CASE("Failing benchmarks", "[!benchmark][.approvals]") { } TEST_CASE( "Failing benchmark respects should-fail", - "[!shouldfail][!benchmark][.approvals]" ) { + "[!shouldfail][!benchmark][approvals]" ) { BENCHMARK( "Asserting benchmark" ) { REQUIRE( 1 == 2 ); }; } diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/Json.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Json.tests.cpp new file mode 100644 index 00000000..8204e3c4 --- /dev/null +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Json.tests.cpp @@ -0,0 +1,152 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#include +#include +#include + +#include + +namespace { + struct Custom {}; + static std::ostream& operator<<( std::ostream& os, Custom const& ) { + return os << "custom"; + } +} // namespace + +TEST_CASE( "JsonWriter", "[JSON][JsonWriter]" ) { + + std::stringstream stream; + SECTION( "Newly constructed JsonWriter does nothing" ) { + Catch::JsonValueWriter writer{ stream }; + REQUIRE( stream.str() == "" ); + } + + SECTION( "Calling writeObject will create an empty pair of braces" ) { + { auto writer = Catch::JsonValueWriter{ stream }.writeObject(); } + REQUIRE( stream.str() == "{\n}" ); + } + + SECTION( "Calling writeObject with key will create an object to write the " + "value" ) { + using Catch::Matchers::ContainsSubstring; + { + auto writer = Catch::JsonValueWriter{ stream }.writeObject(); + writer.write( "int" ).write( 1 ); + writer.write( "double" ).write( 1.5 ); + writer.write( "true" ).write( true ); + writer.write( "false" ).write( false ); + writer.write( "string" ).write( "this is a string" ); + writer.write( "array" ).writeArray().write( 1 ).write( 2 ); + } + REQUIRE_THAT( + stream.str(), + ContainsSubstring( "\"int\": 1," ) && + ContainsSubstring( "\"double\": 1.5," ) && + ContainsSubstring( "\"true\": true," ) && + ContainsSubstring( "\"false\": false," ) && + ContainsSubstring( "\"string\": \"this is a string\"," ) && + ContainsSubstring( "\"array\": [\n 1,\n 2\n ]\n}" ) ); + } + + SECTION( "nesting objects" ) { + using Catch::Matchers::ContainsSubstring; + { + auto writer = Catch::JsonValueWriter{ stream }.writeObject(); + writer.write( "empty_object" ).writeObject(); + writer.write( "fully_object" ) + .writeObject() + .write( "key" ) + .write( 1 ); + } + REQUIRE_THAT( stream.str(), + ContainsSubstring( "\"empty_object\": {\n }," ) && + ContainsSubstring( + "\"fully_object\": {\n \"key\": 1\n }" ) ); + } + + SECTION( "Calling writeArray will create an empty pair of braces" ) { + { auto writer = Catch::JsonValueWriter{ stream }.writeArray(); } + REQUIRE( stream.str() == "[\n]" ); + } + + SECTION( "Calling writeArray creates array to write the values to" ) { + { + auto writer = Catch::JsonValueWriter{ stream }.writeArray(); + writer.write( 1 ); + writer.write( 1.5 ); + writer.write( true ); + writer.write( false ); + writer.write( "this is a string" ); + writer.writeObject().write( "object" ).write( 42 ); + writer.writeArray().write( "array" ).write( 42.5 ); + } + REQUIRE( stream.str() == "[\n 1,\n 1.5,\n true,\n false,\n \"this is a string\",\n {\n \"object\": 42\n },\n [\n \"array\",\n 42.5\n ]\n]" ); + } + + SECTION( + "Moved from JsonObjectWriter shall not insert superfluous brace" ) { + { + auto writer = Catch::JsonObjectWriter{ stream }; + auto another_writer = std::move( writer ); + } + REQUIRE( stream.str() == "{\n}" ); + } + SECTION( + "Moved from JsonArrayWriter shall not insert superfluous bracket" ) { + { + auto writer = Catch::JsonArrayWriter{ stream }; + auto another_writer = std::move( writer ); + } + REQUIRE( stream.str() == "[\n]" ); + } + SECTION( "Custom class shall be quoted" ) { + Catch::JsonValueWriter{ stream }.write( Custom{} ); + REQUIRE( stream.str() == "\"custom\"" ); + } +} + +TEST_CASE( "JsonWriter escapes charaters in strings properly", "[JsonWriter]" ) { + std::stringstream sstream; + SECTION( "Quote in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\"" ); + REQUIRE( sstream.str() == "\"\\\"\"" ); + } + SECTION("Backslash in a string is escaped") { + Catch::JsonValueWriter{ sstream }.write( "\\" ); + REQUIRE( sstream.str() == "\"\\\\\"" ); + } + SECTION( "Forward slash in a string is **not** escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "/" ); + REQUIRE( sstream.str() == "\"/\"" ); + } + SECTION( "Backspace in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\b" ); + REQUIRE( sstream.str() == "\"\\b\"" ); + } + SECTION( "Formfeed in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\f" ); + REQUIRE( sstream.str() == "\"\\f\"" ); + } + SECTION( "linefeed in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\n" ); + REQUIRE( sstream.str() == "\"\\n\"" ); + } + SECTION( "carriage return in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\r" ); + REQUIRE( sstream.str() == "\"\\r\"" ); + } + SECTION( "tab in a string is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\t" ); + REQUIRE( sstream.str() == "\"\\t\"" ); + } + SECTION( "combination of characters is escaped" ) { + Catch::JsonValueWriter{ sstream }.write( "\\/\t\r\n" ); + REQUIRE( sstream.str() == "\"\\\\/\\t\\r\\n\"" ); + } +} diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp index 8018b7eb..89323215 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp @@ -7,9 +7,17 @@ // SPDX-License-Identifier: BSL-1.0 #include +#include +#include +#include #include #include +#include +#include #include +#include + +#include TEST_CASE("Our PCG implementation provides expected results for known seeds", "[rng]") { Catch::SimplePcg32 rng; @@ -60,3 +68,542 @@ TEST_CASE("Random seed generation accepts known methods", "[rng][seed]") { REQUIRE_NOTHROW(Catch::generateRandomSeed(method)); } + +TEMPLATE_TEST_CASE("uniform_floating_point_distribution never returns infs from finite range", + "[rng][distribution][floating-point][approvals]", float, double) { + std::random_device rd{}; + Catch::SimplePcg32 pcg( rd() ); + Catch::uniform_floating_point_distribution dist( + -std::numeric_limits::max(), + std::numeric_limits::max() ); + + for (size_t i = 0; i < 10'000; ++i) { + auto ret = dist( pcg ); + REQUIRE_FALSE( std::isinf( ret ) ); + REQUIRE_FALSE( std::isnan( ret ) ); + } +} + +TEST_CASE( "fillBitsFrom - shortening and stretching", "[rng][approvals]" ) { + using Catch::Detail::fillBitsFrom; + + // The seed is not important, but the numbers below have to be repeatable. + // They should also exhibit the same general pattern of being prefixes + Catch::SimplePcg32 pcg( 0xaabb'ccdd ); + + SECTION( "Shorten to 8 bits" ) { + // We cast the result to avoid dealing with char-like type in uint8_t + auto shortened = static_cast( fillBitsFrom( pcg ) ); + REQUIRE( shortened == 0xcc ); + } + SECTION( "Shorten to 16 bits" ) { + auto shortened = fillBitsFrom( pcg ); + REQUIRE( shortened == 0xccbe ); + } + SECTION( "Keep at 32 bits" ) { + auto n = fillBitsFrom( pcg ); + REQUIRE( n == 0xccbe'5f04 ); + } + SECTION( "Stretch to 64 bits" ) { + auto stretched = fillBitsFrom( pcg ); + REQUIRE( stretched == 0xccbe'5f04'a424'a486 ); + } +} + +TEST_CASE("uniform_integer_distribution can return the bounds", "[rng][distribution]") { + Catch::uniform_integer_distribution dist( -10, 10 ); + REQUIRE( dist.a() == -10 ); + REQUIRE( dist.b() == 10 ); +} + +namespace { + template + static void CheckReturnValue(Catch::uniform_integer_distribution& dist, + Catch::SimplePcg32& rng, + T target) { + REQUIRE( dist.a() == dist.b() ); + for (int i = 0; i < 1'000; ++i) { + REQUIRE( dist( rng ) == target ); + } + } +} + +TEMPLATE_TEST_CASE( "uniform_integer_distribution can handle unit ranges", + "[rng][distribution][approvals]", + unsigned char, + signed char, + char, + uint8_t, + int8_t, + uint16_t, + int16_t, + uint32_t, + int32_t, + uint64_t, + int64_t, + size_t, + ptrdiff_t) { + // We want random seed to sample different parts of the rng state, + // the output is predetermined anyway + std::random_device rd; + auto seed = rd(); + CAPTURE( seed ); + Catch::SimplePcg32 pcg( seed ); + + // We check unitary ranges of 3 different values, min for type, max for type, + // some value inbetween just to make sure + SECTION("lowest value") { + constexpr auto lowest = std::numeric_limits::min(); + Catch::uniform_integer_distribution dist( lowest, lowest ); + CheckReturnValue( dist, pcg, lowest ); + } + SECTION( "highest value" ) { + constexpr auto highest = std::numeric_limits::max(); + Catch::uniform_integer_distribution dist( highest, highest ); + CheckReturnValue( dist, pcg, highest ); + } + SECTION( "some value" ) { + constexpr auto some = TestType( 42 ); + Catch::uniform_integer_distribution dist( some, some ); + CheckReturnValue( dist, pcg, some ); + } +} + +// Bool needs its own test because it doesn't have a valid "third" value +TEST_CASE( "uniform_integer_distribution can handle boolean unit ranges", + "[rng][distribution][approvals]" ) { + // We want random seed to sample different parts of the rng state, + // the output is predetermined anyway + std::random_device rd; + auto seed = rd(); + CAPTURE( seed ); + Catch::SimplePcg32 pcg( seed ); + + // We check unitary ranges of 3 different values, min for type, max for + // type, some value inbetween just to make sure + SECTION( "lowest value" ) { + Catch::uniform_integer_distribution dist( false, false ); + CheckReturnValue( dist, pcg, false ); + } + SECTION( "highest value" ) { + Catch::uniform_integer_distribution dist( true, true ); + CheckReturnValue( dist, pcg, true ); + } +} + +TEMPLATE_TEST_CASE( "uniform_integer_distribution can handle full width ranges", + "[rng][distribution][approvals]", + unsigned char, + signed char, + char, + uint8_t, + int8_t, + uint16_t, + int16_t, + uint32_t, + int32_t, + uint64_t, + int64_t ) { + // We want random seed to sample different parts of the rng state, + // the output is predetermined anyway + std::random_device rd; + auto seed = rd(); + CAPTURE( seed ); + Catch::SimplePcg32 pcg( seed ); + + constexpr auto lowest = std::numeric_limits::min(); + constexpr auto highest = std::numeric_limits::max(); + Catch::uniform_integer_distribution dist( lowest, highest ); + STATIC_REQUIRE( std::is_same::value ); + + // We need to do bit operations on the results, so we will have to + // cast them to unsigned type. + using BitType = std::make_unsigned_t; + BitType ORs = 0; + BitType ANDs = BitType(-1); + for (int i = 0; i < 100; ++i) { + auto bits = static_cast( dist( pcg ) ); + ORs |= bits; + ANDs &= bits; + } + // Assuming both our RNG and distribution are unbiased, asking for + // the full range should essentially give us random bit generator. + // Over long run, OR of all the generated values should have all + // bits set to 1, while AND should have all bits set to 0. + // The chance of this test failing for unbiased pipeline is + // 1 / 2**iters, which for 100 iterations is astronomical. + REQUIRE( ORs == BitType( -1 ) ); + REQUIRE( ANDs == 0 ); +} + +namespace { + template + struct uniform_integer_test_params; + + template <> + struct uniform_integer_test_params { + static constexpr bool lowest = false; + static constexpr bool highest = true; + // This seems weird, but it is an artifact of the specific seed + static constexpr bool expected[] = { true, + true, + true, + true, + true, + true, + false, + true, + true, + true, + true, + true, + false, + true, + true }; + }; + + template <> + struct uniform_integer_test_params { + static constexpr char lowest = 32; + static constexpr char highest = 126; + static constexpr char expected[] = { 'k', + '\\', + 'Z', + 'X', + '`', + 'Q', + ';', + 'o', + ']', + 'T', + 'v', + 'p', + ':', + 'S', + 't' }; + }; + + template <> + struct uniform_integer_test_params { + static constexpr uint8_t lowest = 3; + static constexpr uint8_t highest = 123; + static constexpr uint8_t expected[] = { 'c', + 'P', + 'M', + 'J', + 'U', + 'A', + '%', + 'h', + 'Q', + 'F', + 'q', + 'i', + '$', + 'E', + 'o' }; + }; + + template <> + struct uniform_integer_test_params { + static constexpr int8_t lowest = -27; + static constexpr int8_t highest = 73; + static constexpr int8_t expected[] = { '5', + '%', + '#', + ' ', + '*', + 25, + 2, + '9', + '&', + 29, + 'A', + ':', + 1, + 28, + '?' }; + }; + + template <> + struct uniform_integer_test_params { + static constexpr uint16_t lowest = 123; + static constexpr uint16_t highest = 33333; + static constexpr uint16_t expected[] = { 26684, + 21417, + 20658, + 19791, + 22896, + 17433, + 9806, + 27948, + 21767, + 18588, + 30556, + 28244, + 9439, + 18293, + 29949 }; + }; + + template <> + struct uniform_integer_test_params { + static constexpr int16_t lowest = -17222; + static constexpr int16_t highest = 17222; + static constexpr int16_t expected[] = { 10326, + 4863, + 4076, + 3177, + 6397, + 731, + -7179, + 11637, + 5226, + 1929, + 14342, + 11944, + -7560, + 1623, + 13712 }; + }; + + template <> + struct uniform_integer_test_params { + static constexpr uint32_t lowest = 17222; + static constexpr uint32_t highest = 234234; + static constexpr uint32_t expected[] = { 190784, + 156367, + 151409, + 145743, + 166032, + 130337, + 80501, + 199046, + 158654, + 137883, + 216091, + 200981, + 78099, + 135954, + 212120 }; + }; + + template <> + struct uniform_integer_test_params { + static constexpr int32_t lowest = -237272; + static constexpr int32_t highest = 234234; + static constexpr int32_t expected[] = { 139829, + 65050, + 54278, + 41969, + 86051, + 8494, + -99785, + 157781, + 70021, + 24890, + 194815, + 161985, + -105004, + 20699, + 186186 }; + }; + + template <> + struct uniform_integer_test_params { + static constexpr uint64_t lowest = 1234; + static constexpr uint64_t highest = 1234567890; + static constexpr uint64_t expected[] = { 987382749, + 763380386, + 846572137, + 359990258, + 804599765, + 1131353566, + 346324913, + 1108760730, + 1141693933, + 856999148, + 879390623, + 1149485521, + 900556586, + 952385958, + 807916408 }; + }; + + template <> + struct uniform_integer_test_params { + static constexpr int64_t lowest = -1234567890; + static constexpr int64_t highest = 1234567890; + static constexpr int64_t expected[] = { 740197113, + 292191940, + 458575608, + -514589122, + 374630781, + 1028139036, + -541919840, + 982953318, + 1048819790, + 479429651, + 524212647, + 1064402981, + 566544615, + 670203462, + 381264073 }; + }; + + // We need these definitions for C++14 and earlier, but + // GCC will complain about them in newer C++ standards +#if __cplusplus <= 201402L + constexpr bool uniform_integer_test_params::expected[]; + constexpr char uniform_integer_test_params::expected[]; + constexpr uint8_t uniform_integer_test_params::expected[]; + constexpr int8_t uniform_integer_test_params::expected[]; + constexpr uint16_t uniform_integer_test_params::expected[]; + constexpr int16_t uniform_integer_test_params::expected[]; + constexpr uint32_t uniform_integer_test_params::expected[]; + constexpr int32_t uniform_integer_test_params::expected[]; + constexpr uint64_t uniform_integer_test_params::expected[]; + constexpr int64_t uniform_integer_test_params::expected[]; +#endif + +} + +TEMPLATE_TEST_CASE( "uniform_integer_distribution is reproducible", + "[rng][distribution][approvals]", + bool, + char, + uint8_t, + int8_t, + uint16_t, + int16_t, + uint32_t, + int32_t, + uint64_t, + int64_t) { + Catch::SimplePcg32 pcg( 0xaabb'ccdd ); + + constexpr auto lowest = uniform_integer_test_params::lowest; + constexpr auto highest = uniform_integer_test_params::highest; + Catch::uniform_integer_distribution dist(lowest, highest); + + constexpr auto iters = 15; + std::array generated; + for (int i = 0; i < iters; ++i) { + generated[i] = dist( pcg ); + } + + REQUIRE_THAT(generated, Catch::Matchers::RangeEquals(uniform_integer_test_params::expected)); +} + +// The reproducibility tests assume that operations on `float`/`double` +// happen in the same precision as the operated-upon type. This is +// generally true, unless the code is compiled for 32 bit targets without +// SSE2 enabled, in which case the operations are done in the x87 FPU, +// which usually implies doing math in 80 bit floats, and then rounding +// into smaller type when the type is saved into memory. This obviously +// leads to a different answer, than doing the math in the correct precision. +#if ( defined( _MSC_VER ) && _M_IX86_FP < 2 ) || \ + ( defined( __GNUC__ ) && \ + ( ( defined( __i386__ ) || defined( __x86_64__ ) ) ) && \ + !defined( __SSE2_MATH__ ) ) +# define CATCH_TEST_CONFIG_DISABLE_FLOAT_REPRODUCIBILITY_TESTS +#endif + +#if !defined( CATCH_TEST_CONFIG_DISABLE_FLOAT_REPRODUCIBILITY_TESTS ) + +namespace { + template + struct uniform_fp_test_params; + + template<> + struct uniform_fp_test_params { + // These are exactly representable + static constexpr float lowest = -256.125f; + static constexpr float highest = 385.125f; + // These are just round-trip formatted + static constexpr float expected[] = { 92.56961f, + -23.170044f, + 310.81833f, + -53.023132f, + 105.03287f, + 198.77591f, + -172.72931f, + 51.805176f, + -241.10156f, + 64.66101f, + 212.12509f, + -49.24292f, + -177.1399f, + 245.23679f, + 173.22421f }; + }; + template <> + struct uniform_fp_test_params { + // These are exactly representable + static constexpr double lowest = -234582.9921875; + static constexpr double highest = 261238.015625; + // These are just round-trip formatted + static constexpr double expected[] = { 35031.207052832615, + 203783.3401838024, + 44667.940405848756, + -170100.5877224467, + -222966.7418051684, + 127472.72630072923, + -173510.88209096913, + 97394.16172239158, + 119123.6921592663, + 22595.741022785165, + 8988.68409120926, + 136906.86520606978, + 33369.19104222473, + 60912.7615841752, + -149060.05936760217 }; + }; + +// We need these definitions for C++14 and earlier, but +// GCC will complain about them in newer C++ standards +#if __cplusplus <= 201402L + constexpr float uniform_fp_test_params::expected[]; + constexpr double uniform_fp_test_params::expected[]; +#endif +} // namespace + +TEMPLATE_TEST_CASE( "uniform_floating_point_distribution is reproducible", + "[rng][distribution][floating-point][approvals]", + float, + double ) { + Catch::SimplePcg32 pcg( 0xaabb'aabb ); + + const auto lowest = uniform_fp_test_params::lowest; + const auto highest = uniform_fp_test_params::highest; + Catch::uniform_floating_point_distribution dist( lowest, highest ); + + constexpr auto iters = 15; + std::array generated; + for ( int i = 0; i < iters; ++i ) { + generated[i] = dist( pcg ); + } + + REQUIRE_THAT( generated, Catch::Matchers::RangeEquals( uniform_fp_test_params::expected ) ); +} + +#endif // ^^ float reproducibility tests are enabled + +TEMPLATE_TEST_CASE( "uniform_floating_point_distribution can handle unitary ranges", + "[rng][distribution][floating-point][approvals]", + float, + double ) { + std::random_device rd; + auto seed = rd(); + CAPTURE( seed ); + Catch::SimplePcg32 pcg( seed ); + + const auto highest = TestType(385.125); + Catch::uniform_floating_point_distribution dist( highest, + highest ); + + constexpr auto iters = 20; + for (int i = 0; i < iters; ++i) { + REQUIRE( Catch::Detail::directCompare( dist( pcg ), highest ) ); + } +} diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp index 54c26a7a..86ba1175 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -108,9 +107,11 @@ TEST_CASE( "Reporter's write listings to provided stream", "[reporters]" ) { for (auto const& factory : factories) { INFO("Tested reporter: " << factory.first); auto sstream = Catch::Detail::make_unique(); - auto& sstreamRef = *sstream.get(); + auto& sstreamRef = *sstream; - Catch::Config config( Catch::ConfigData{} ); + Catch::ConfigData cfg_data; + cfg_data.rngSeed = 1234; + Catch::Config config( cfg_data ); auto reporter = factory.second->create( Catch::ReporterConfig{ &config, CATCH_MOVE( sstream ), Catch::ColourMode::None, {} } ); @@ -164,7 +165,7 @@ namespace { std::vector& recorder, Catch::IConfig const* config ): EventListenerBase( config ), - m_witness( witness ), + m_witness( CATCH_MOVE(witness) ), m_recorder( recorder ) {} @@ -182,7 +183,7 @@ namespace { std::vector& recorder, Catch::ReporterConfig&& config ): StreamingReporterBase( CATCH_MOVE(config) ), - m_witness( witness ), + m_witness( CATCH_MOVE(witness) ), m_recorder( recorder ) {} diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp index 7a0b3b4a..43c58b49 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp @@ -177,7 +177,7 @@ TEST_CASE("StringRef at compilation time", "[Strings][StringRef][constexpr]") { STATIC_REQUIRE_FALSE(sr1.empty()); STATIC_REQUIRE(sr1.size() == 3); - using Catch::operator"" _sr; + using Catch::operator""_sr; constexpr auto sr2 = ""_sr; STATIC_REQUIRE(sr2.empty()); STATIC_REQUIRE(sr2.size() == 0); diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp index 36554ddc..f30573cc 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp @@ -57,6 +57,17 @@ TEST_CASE("replaceInPlace", "[string-manip]") { CHECK_FALSE(Catch::replaceInPlace(letters, "x", "z")); CHECK(letters == letters); } + SECTION("no replace in already-replaced string") { + SECTION("lengthening") { + CHECK(Catch::replaceInPlace(letters, "c", "cc")); + CHECK(letters == "abccdefccg"); + } + SECTION("shortening") { + std::string s = "----"; + CHECK(Catch::replaceInPlace(s, "--", "-")); + CHECK(s == "--"); + } + } SECTION("escape '") { std::string s = "didn't"; CHECK(Catch::replaceInPlace(s, "'", "|'")); diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp index ef321b27..43723758 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp @@ -98,7 +98,20 @@ TEST_CASE( "Test case with identical tags keeps just one", "[tags]" ) { REQUIRE( testCase.tags[0] == Tag( "tag1" ) ); } -TEST_CASE( "Empty tag is not allowed" ) { - REQUIRE_THROWS( Catch::TestCaseInfo( - "", { "fake test name", "[]" }, dummySourceLineInfo ) ); +TEST_CASE("Mismatched square brackets in tags are caught and reported", + "[tags][approvals]") { + using Catch::TestCaseInfo; + using Catch::Matchers::ContainsSubstring; + REQUIRE_THROWS_WITH( TestCaseInfo( "", + { "test with unclosed tag", "[abc" }, + dummySourceLineInfo ), + ContainsSubstring("registering test case 'test with unclosed tag'") ); + REQUIRE_THROWS_WITH( TestCaseInfo( "", + { "test with nested tags", "[abc[def]]" }, + dummySourceLineInfo ), + ContainsSubstring("registering test case 'test with nested tags'") ); + REQUIRE_THROWS_WITH( TestCaseInfo( "", + { "test with superfluous close tags", "[abc][def]]" }, + dummySourceLineInfo ), + ContainsSubstring("registering test case 'test with superfluous close tags'") ); } diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/TestSpec.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/TestSpec.tests.cpp index 9c4eb03b..11a7a7a8 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/TestSpec.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/TestSpec.tests.cpp @@ -236,7 +236,7 @@ TEST_CASE( "Parse test names and tags", "[command-line][test-spec][approvals]" ) CHECK( spec.matches( *tcD ) == false ); } SECTION( "two wildcarded names" ) { - TestSpec spec = parseTestSpec( "\"longer*\"\"*spaces\"" ); + TestSpec spec = parseTestSpec( R"("longer*""*spaces")" ); CHECK( spec.hasFilters() == true ); CHECK( spec.matches( *tcA ) == false ); CHECK( spec.matches( *tcB ) == false ); diff --git a/src/external/catch2/tests/SelfTest/IntrospectiveTests/TextFlow.tests.cpp b/src/external/catch2/tests/SelfTest/IntrospectiveTests/TextFlow.tests.cpp index 82de5a27..de03ed09 100644 --- a/src/external/catch2/tests/SelfTest/IntrospectiveTests/TextFlow.tests.cpp +++ b/src/external/catch2/tests/SelfTest/IntrospectiveTests/TextFlow.tests.cpp @@ -12,6 +12,7 @@ #include using Catch::TextFlow::Column; +using Catch::TextFlow::AnsiSkippingString; namespace { static std::string as_written(Column const& c) { @@ -152,7 +153,7 @@ TEST_CASE( "TextFlow::Column respects indentation for empty lines", std::string written = as_written(col); - REQUIRE(as_written(col) == " \n \n third line"); + REQUIRE(written == " \n \n third line"); } TEST_CASE( "TextFlow::Column leading/trailing whitespace", @@ -198,3 +199,202 @@ TEST_CASE( "#1400 - TextFlow::Column wrapping would sometimes duplicate words", " in \n" " convallis posuere, libero nisi ultricies orci, nec lobortis."); } + +TEST_CASE( "TextFlow::AnsiSkippingString skips ansi sequences", + "[TextFlow][ansiskippingstring][approvals]" ) { + + SECTION("basic string") { + std::string text = "a\033[38;2;98;174;239mb\033[38mc\033[0md\033[me"; + AnsiSkippingString str(text); + + SECTION( "iterates forward" ) { + auto it = str.begin(); + CHECK(*it == 'a'); + ++it; + CHECK(*it == 'b'); + ++it; + CHECK(*it == 'c'); + ++it; + CHECK(*it == 'd'); + ++it; + CHECK(*it == 'e'); + ++it; + CHECK(it == str.end()); + } + SECTION( "iterates backwards" ) { + auto it = str.end(); + --it; + CHECK(*it == 'e'); + --it; + CHECK(*it == 'd'); + --it; + CHECK(*it == 'c'); + --it; + CHECK(*it == 'b'); + --it; + CHECK(*it == 'a'); + CHECK(it == str.begin()); + } + } + + SECTION( "ansi escape sequences at the start" ) { + std::string text = "\033[38;2;98;174;239ma\033[38;2;98;174;239mb\033[38mc\033[0md\033[me"; + AnsiSkippingString str(text); + auto it = str.begin(); + CHECK(*it == 'a'); + ++it; + CHECK(*it == 'b'); + ++it; + CHECK(*it == 'c'); + ++it; + CHECK(*it == 'd'); + ++it; + CHECK(*it == 'e'); + ++it; + CHECK(it == str.end()); + --it; + CHECK(*it == 'e'); + --it; + CHECK(*it == 'd'); + --it; + CHECK(*it == 'c'); + --it; + CHECK(*it == 'b'); + --it; + CHECK(*it == 'a'); + CHECK(it == str.begin()); + } + + SECTION( "ansi escape sequences at the end" ) { + std::string text = "a\033[38;2;98;174;239mb\033[38mc\033[0md\033[me\033[38;2;98;174;239m"; + AnsiSkippingString str(text); + auto it = str.begin(); + CHECK(*it == 'a'); + ++it; + CHECK(*it == 'b'); + ++it; + CHECK(*it == 'c'); + ++it; + CHECK(*it == 'd'); + ++it; + CHECK(*it == 'e'); + ++it; + CHECK(it == str.end()); + --it; + CHECK(*it == 'e'); + --it; + CHECK(*it == 'd'); + --it; + CHECK(*it == 'c'); + --it; + CHECK(*it == 'b'); + --it; + CHECK(*it == 'a'); + CHECK(it == str.begin()); + } + + SECTION( "skips consecutive escapes" ) { + std::string text = "\033[38;2;98;174;239m\033[38;2;98;174;239ma\033[38;2;98;174;239mb\033[38m\033[38m\033[38mc\033[0md\033[me"; + AnsiSkippingString str(text); + auto it = str.begin(); + CHECK(*it == 'a'); + ++it; + CHECK(*it == 'b'); + ++it; + CHECK(*it == 'c'); + ++it; + CHECK(*it == 'd'); + ++it; + CHECK(*it == 'e'); + ++it; + CHECK(it == str.end()); + --it; + CHECK(*it == 'e'); + --it; + CHECK(*it == 'd'); + --it; + CHECK(*it == 'c'); + --it; + CHECK(*it == 'b'); + --it; + CHECK(*it == 'a'); + CHECK(it == str.begin()); + } + + SECTION( "handles incomplete ansi sequences" ) { + std::string text = "a\033[b\033[30c\033[30;d\033[30;2e"; + AnsiSkippingString str(text); + CHECK(std::string(str.begin(), str.end()) == text); + } +} + +TEST_CASE( "TextFlow::AnsiSkippingString computes the size properly", + "[TextFlow][ansiskippingstring][approvals]" ) { + std::string text = "\033[38;2;98;174;239m\033[38;2;98;174;239ma\033[38;2;98;174;239mb\033[38m\033[38m\033[38mc\033[0md\033[me"; + AnsiSkippingString str(text); + CHECK(str.size() == 5); +} + +TEST_CASE( "TextFlow::AnsiSkippingString substrings properly", + "[TextFlow][ansiskippingstring][approvals]" ) { + SECTION("basic test") { + std::string text = "a\033[38;2;98;174;239mb\033[38mc\033[0md\033[me"; + AnsiSkippingString str(text); + auto a = str.begin(); + auto b = str.begin(); + ++b; + ++b; + CHECK(str.substring(a, b) == "a\033[38;2;98;174;239mb\033[38m"); + ++a; + ++b; + CHECK(str.substring(a, b) == "b\033[38mc\033[0m"); + CHECK(str.substring(a, str.end()) == "b\033[38mc\033[0md\033[me"); + CHECK(str.substring(str.begin(), str.end()) == text); + } + SECTION("escapes at the start") { + std::string text = "\033[38;2;98;174;239m\033[38;2;98;174;239ma\033[38;2;98;174;239mb\033[38m\033[38m\033[38mc\033[0md\033[me"; + AnsiSkippingString str(text); + auto a = str.begin(); + auto b = str.begin(); + ++b; + ++b; + CHECK(str.substring(a, b) == "\033[38;2;98;174;239m\033[38;2;98;174;239ma\033[38;2;98;174;239mb\033[38m\033[38m\033[38m"); + ++a; + ++b; + CHECK(str.substring(a, b) == "b\033[38m\033[38m\033[38mc\033[0m"); + CHECK(str.substring(a, str.end()) == "b\033[38m\033[38m\033[38mc\033[0md\033[me"); + CHECK(str.substring(str.begin(), str.end()) == text); + } + SECTION("escapes at the end") { + std::string text = "a\033[38;2;98;174;239mb\033[38mc\033[0md\033[me\033[38m"; + AnsiSkippingString str(text); + auto a = str.begin(); + auto b = str.begin(); + ++b; + ++b; + CHECK(str.substring(a, b) == "a\033[38;2;98;174;239mb\033[38m"); + ++a; + ++b; + CHECK(str.substring(a, b) == "b\033[38mc\033[0m"); + CHECK(str.substring(a, str.end()) == "b\033[38mc\033[0md\033[me\033[38m"); + CHECK(str.substring(str.begin(), str.end()) == text); + } +} + +TEST_CASE( "TextFlow::Column skips ansi escape sequences", + "[TextFlow][column][approvals]" ) { + std::string text = "\033[38;2;98;174;239m\033[38;2;198;120;221mThe quick brown \033[38;2;198;120;221mfox jumped over the lazy dog\033[0m"; + Column col(text); + + SECTION( "width=20" ) { + col.width( 20 ); + REQUIRE( as_written( col ) == "\033[38;2;98;174;239m\033[38;2;198;120;221mThe quick brown \033[38;2;198;120;221mfox\n" + "jumped over the lazy\n" + "dog\033[0m" ); + } + + SECTION( "width=80" ) { + col.width( 80 ); + REQUIRE( as_written( col ) == text ); + } +} diff --git a/src/external/catch2/tests/SelfTest/TestRegistrations.cpp b/src/external/catch2/tests/SelfTest/TestRegistrations.cpp index 64767735..d7a6966f 100644 --- a/src/external/catch2/tests/SelfTest/TestRegistrations.cpp +++ b/src/external/catch2/tests/SelfTest/TestRegistrations.cpp @@ -20,7 +20,6 @@ CATCH_REGISTER_TAG_ALIAS("[@tricky]", "[tricky]~[.]") #ifdef __clang__ # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wweak-vtables" -# pragma clang diagnostic ignored "-Wc++98-compat" #endif /** diff --git a/src/external/catch2/tests/SelfTest/UsageTests/Benchmark.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/Benchmark.tests.cpp index 557b2131..c489eaa8 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/Benchmark.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/Benchmark.tests.cpp @@ -90,14 +90,14 @@ TEST_CASE("Benchmark containers", "[!benchmark]") { }; REQUIRE(v.size() == size); - int array[size]; + int array[size] {}; BENCHMARK("A fixed size array that should require no allocations") { for (int i = 0; i < size; ++i) array[i] = i; }; int sum = 0; - for (int i = 0; i < size; ++i) - sum += array[i]; + for (int val : array) + sum += val; REQUIRE(sum > size); SECTION("XYZ") { @@ -121,8 +121,8 @@ TEST_CASE("Benchmark containers", "[!benchmark]") { runs = benchmarkIndex; }; - for (size_t i = 0; i < v.size(); ++i) { - REQUIRE(v[i] == runs); + for (int val : v) { + REQUIRE(val == runs); } } } @@ -135,8 +135,8 @@ TEST_CASE("Benchmark containers", "[!benchmark]") { for (int i = 0; i < size; ++i) v[i] = generated; }; - for (size_t i = 0; i < v.size(); ++i) { - REQUIRE(v[i] == generated); + for (int val : v) { + REQUIRE(val == generated); } } diff --git a/src/external/catch2/tests/SelfTest/UsageTests/Class.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/Class.tests.cpp index bab7a684..75510f10 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/Class.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/Class.tests.cpp @@ -32,6 +32,10 @@ namespace { int m_a; }; + struct Persistent_Fixture { + mutable int m_a = 0; + }; + template struct Template_Fixture { Template_Fixture(): m_a( 1 ) {} @@ -39,7 +43,7 @@ namespace { }; template struct Template_Fixture_2 { - Template_Fixture_2() {} + Template_Fixture_2() = default; T m_a; }; @@ -64,6 +68,17 @@ TEST_CASE_METHOD( Fixture, "A TEST_CASE_METHOD based test run that succeeds", "[ REQUIRE( m_a == 1 ); } +TEST_CASE_PERSISTENT_FIXTURE( Persistent_Fixture, "A TEST_CASE_PERSISTENT_FIXTURE based test run that succeeds", "[class]" ) +{ + SECTION( "First partial run" ) { + REQUIRE( m_a++ == 0 ); + } + + SECTION( "Second partial run" ) { + REQUIRE( m_a == 1 ); + } +} + TEMPLATE_TEST_CASE_METHOD(Template_Fixture, "A TEMPLATE_TEST_CASE_METHOD based test run that succeeds", "[class][template]", int, float, double) { REQUIRE( Template_Fixture::m_a == 1 ); } @@ -96,6 +111,17 @@ namespace Inner REQUIRE( m_a == 2 ); } + TEST_CASE_PERSISTENT_FIXTURE( Persistent_Fixture, "A TEST_CASE_PERSISTENT_FIXTURE based test run that fails", "[.][class][failing]" ) + { + SECTION( "First partial run" ) { + REQUIRE( m_a++ == 0 ); + } + + SECTION( "Second partial run" ) { + REQUIRE( m_a == 0 ); + } + } + TEMPLATE_TEST_CASE_METHOD(Template_Fixture,"A TEMPLATE_TEST_CASE_METHOD based test run that fails", "[.][class][template][failing]", int, float, double) { REQUIRE( Template_Fixture::m_a == 2 ); diff --git a/src/external/catch2/tests/SelfTest/UsageTests/Compilation.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/Compilation.tests.cpp index 1cdcfb78..a7fbf08f 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/Compilation.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/Compilation.tests.cpp @@ -313,11 +313,12 @@ TEST_CASE("ADL universal operators don't hijack expression deconstruction", "[co REQUIRE(0 ^ adl::always_true{}); } -TEST_CASE( "#2555 - types that can only be compared with 0 literal (not int/long) are supported", "[compilation][approvals]" ) { +TEST_CASE( "#2555 - types that can only be compared with 0 literal implemented as pointer conversion are supported", + "[compilation][approvals]" ) { REQUIRE( TypeWithLit0Comparisons{} < 0 ); REQUIRE_FALSE( 0 < TypeWithLit0Comparisons{} ); REQUIRE( TypeWithLit0Comparisons{} <= 0 ); - REQUIRE_FALSE( 0 > TypeWithLit0Comparisons{} ); + REQUIRE_FALSE( 0 <= TypeWithLit0Comparisons{} ); REQUIRE( TypeWithLit0Comparisons{} > 0 ); REQUIRE_FALSE( 0 > TypeWithLit0Comparisons{} ); @@ -330,6 +331,105 @@ TEST_CASE( "#2555 - types that can only be compared with 0 literal (not int/long REQUIRE_FALSE( 0 != TypeWithLit0Comparisons{} ); } +// These tests require `consteval` to propagate through `constexpr` calls +// which is a late DR against C++20. +#if defined( CATCH_CPP20_OR_GREATER ) && defined( __cpp_consteval ) && \ + __cpp_consteval >= 202211L +// Can't have internal linkage to avoid warnings +void ZeroLiteralErrorFunc(); +namespace { + struct ZeroLiteralConsteval { + template , int> = 0> + consteval ZeroLiteralConsteval( T zero ) noexcept { + if ( zero != 0 ) { ZeroLiteralErrorFunc(); } + } + }; + + // Should only be constructible from literal 0. Uses the propagating + // consteval constructor trick (currently used by MSVC, might be used + // by libc++ in the future as well). + struct TypeWithConstevalLit0Comparison { +# define DEFINE_COMP_OP( op ) \ + constexpr friend bool operator op( TypeWithConstevalLit0Comparison, \ + ZeroLiteralConsteval ) { \ + return true; \ + } \ + constexpr friend bool operator op( ZeroLiteralConsteval, \ + TypeWithConstevalLit0Comparison ) { \ + return false; \ + } \ + /* std::orderings only have these for ==, but we add them for all \ + operators so we can test all overloads for decomposer */ \ + constexpr friend bool operator op( TypeWithConstevalLit0Comparison, \ + TypeWithConstevalLit0Comparison ) { \ + return true; \ + } + + DEFINE_COMP_OP( < ) + DEFINE_COMP_OP( <= ) + DEFINE_COMP_OP( > ) + DEFINE_COMP_OP( >= ) + DEFINE_COMP_OP( == ) + DEFINE_COMP_OP( != ) + +#undef DEFINE_COMP_OP + }; + +} // namespace + +namespace Catch { + template <> + struct capture_by_value : std::true_type {}; +} + +TEST_CASE( "#2555 - types that can only be compared with 0 literal implemented as consteval check are supported", + "[compilation][approvals]" ) { + REQUIRE( TypeWithConstevalLit0Comparison{} < 0 ); + REQUIRE_FALSE( 0 < TypeWithConstevalLit0Comparison{} ); + REQUIRE( TypeWithConstevalLit0Comparison{} <= 0 ); + REQUIRE_FALSE( 0 <= TypeWithConstevalLit0Comparison{} ); + + REQUIRE( TypeWithConstevalLit0Comparison{} > 0 ); + REQUIRE_FALSE( 0 > TypeWithConstevalLit0Comparison{} ); + REQUIRE( TypeWithConstevalLit0Comparison{} >= 0 ); + REQUIRE_FALSE( 0 >= TypeWithConstevalLit0Comparison{} ); + + REQUIRE( TypeWithConstevalLit0Comparison{} == 0 ); + REQUIRE_FALSE( 0 == TypeWithConstevalLit0Comparison{} ); + REQUIRE( TypeWithConstevalLit0Comparison{} != 0 ); + REQUIRE_FALSE( 0 != TypeWithConstevalLit0Comparison{} ); +} + +// We check all comparison ops to test, even though orderings, the primary +// motivation for this functionality, only have self-comparison (and thus +// have the ambiguity issue) for `==` and `!=`. +TEST_CASE( "Comparing const instances of type registered with capture_by_value", + "[regression][approvals][compilation]" ) { + SECTION("Type with consteval-int constructor") { + auto const const_Lit0Type_1 = TypeWithConstevalLit0Comparison{}; + auto const const_Lit0Type_2 = TypeWithConstevalLit0Comparison{}; + REQUIRE( const_Lit0Type_1 == const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 <= const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 < const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 >= const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 > const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 != const_Lit0Type_2 ); + } + SECTION("Type with constexpr-int constructor") { + auto const const_Lit0Type_1 = TypeWithLit0Comparisons{}; + auto const const_Lit0Type_2 = TypeWithLit0Comparisons{}; + REQUIRE( const_Lit0Type_1 == const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 <= const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 < const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 >= const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 > const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 != const_Lit0Type_2 ); + } +} + +#endif // C++20 consteval + + namespace { struct MultipleImplicitConstructors { MultipleImplicitConstructors( double ) {} @@ -353,3 +453,17 @@ TEST_CASE("#2571 - tests compile types that have multiple implicit constructors REQUIRE( mic1 > mic2 ); REQUIRE( mic1 >= mic2 ); } + +#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS ) +// This test does not test all the related codepaths, but it is the original +// reproducer +TEST_CASE( "Comparing const std::weak_ordering instances must compile", + "[compilation][approvals][regression]" ) { + auto const const_ordering_1 = std::weak_ordering::less; + auto const const_ordering_2 = std::weak_ordering::less; + auto plain_ordering_1 = std::weak_ordering::less; + REQUIRE( const_ordering_1 == plain_ordering_1 ); + REQUIRE( const_ordering_1 == const_ordering_2 ); + REQUIRE( plain_ordering_1 == const_ordering_1 ); +} +#endif diff --git a/src/external/catch2/tests/SelfTest/UsageTests/Exception.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/Exception.tests.cpp index f917932f..7c6b0c86 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/Exception.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/Exception.tests.cpp @@ -20,7 +20,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wweak-vtables" #pragma clang diagnostic ignored "-Wmissing-noreturn" -#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wunreachable-code-return" #endif namespace { @@ -119,7 +119,7 @@ TEST_CASE( "When unchecked exceptions are thrown, but caught, they do not affect try { throw std::domain_error( "unexpected exception" ); } - catch(...) {} + catch(...) {} // NOLINT(bugprone-empty-catch) } @@ -152,7 +152,7 @@ TEST_CASE( "Custom exceptions can be translated when testing for throwing as som } TEST_CASE( "Unexpected exceptions can be translated", "[.][failing][!throws]" ) { - throw double( 3.14 ); + throw double( 3.14 ); // NOLINT(readability-redundant-casting): the type is important here, so we want to be explicit } TEST_CASE("Thrown string literals are translated", "[.][failing][!throws]") { diff --git a/src/external/catch2/tests/SelfTest/UsageTests/Generators.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/Generators.tests.cpp index 274c63b8..f04cf4f0 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/Generators.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/Generators.tests.cpp @@ -261,6 +261,10 @@ TEST_CASE("Copy and then generate a range", "[generators]") { } } +#if defined( __clang__ ) +# pragma clang diagnostic pop +#endif + TEST_CASE("#1913 - GENERATE inside a for loop should not keep recreating the generator", "[regression][generators]") { static int counter = 0; for (int i = 0; i < 3; ++i) { @@ -301,13 +305,19 @@ namespace { } // namespace -TEST_CASE( "#2615 - Throwing in constructor generator fails test case but does not abort", "[!shouldfail]" ) { +TEST_CASE( "#2615 - Throwing in constructor generator fails test case but does not abort", + "[!shouldfail][regression][generators]" ) { // this should fail the test case, but not abort the application auto sample = GENERATE( make_test_generator() ); // this assertion shouldn't trigger - REQUIRE( sample == 0U ); + REQUIRE( sample == 0 ); } -#if defined( __clang__ ) -# pragma clang diagnostic pop -#endif +TEST_CASE( "GENERATE can combine literals and generators", "[generators]" ) { + auto i = GENERATE( 2, + 4, + take( 2, + filter( []( int val ) { return val % 2 == 0; }, + random( -100, 100 ) ) ) ); + REQUIRE( i % 2 == 0 ); +} diff --git a/src/external/catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp index 49e25232..7c4501c6 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp @@ -406,6 +406,25 @@ TEST_CASE( "Vector matchers that fail", "[matchers][vector][.][failing]" ) { } } +namespace { + struct SomeType { + int i; + friend bool operator==( SomeType lhs, SomeType rhs ) { + return lhs.i == rhs.i; + } + }; +} // end anonymous namespace + +TEST_CASE( "Vector matcher with elements without !=", "[matchers][vector][approvals]" ) { + std::vector lhs, rhs; + lhs.push_back( { 1 } ); + lhs.push_back( { 2 } ); + rhs.push_back( { 1 } ); + rhs.push_back( { 1 } ); + + REQUIRE_THAT( lhs, !Equals(rhs) ); +} + TEST_CASE( "Exception matchers that succeed", "[matchers][exceptions][!throws]" ) { CHECK_THROWS_MATCHES( @@ -871,7 +890,7 @@ struct MatcherA : Catch::Matchers::MatcherGenericBase { return "equals: (int) 1 or (string) \"1\""; } bool match( int i ) const { return i == 1; } - bool match( std::string s ) const { return s == "1"; } + bool match( std::string const& s ) const { return s == "1"; } }; struct MatcherB : Catch::Matchers::MatcherGenericBase { @@ -1008,7 +1027,6 @@ TEST_CASE( "Combining MatchNotOfGeneric does not nest", } struct EvilAddressOfOperatorUsed : std::exception { - EvilAddressOfOperatorUsed() {} const char* what() const noexcept override { return "overloaded address-of operator of matcher was used instead of " "std::addressof"; @@ -1016,7 +1034,6 @@ struct EvilAddressOfOperatorUsed : std::exception { }; struct EvilCommaOperatorUsed : std::exception { - EvilCommaOperatorUsed() {} const char* what() const noexcept override { return "overloaded comma operator of matcher was used"; } @@ -1054,7 +1071,6 @@ struct ImmovableMatcher : Catch::Matchers::MatcherGenericBase { }; struct MatcherWasMovedOrCopied : std::exception { - MatcherWasMovedOrCopied() {} const char* what() const noexcept override { return "attempted to copy or move a matcher"; } @@ -1062,17 +1078,20 @@ struct MatcherWasMovedOrCopied : std::exception { struct ThrowOnCopyOrMoveMatcher : Catch::Matchers::MatcherGenericBase { ThrowOnCopyOrMoveMatcher() = default; - [[noreturn]] ThrowOnCopyOrMoveMatcher( ThrowOnCopyOrMoveMatcher const& ): - Catch::Matchers::MatcherGenericBase() { + + [[noreturn]] ThrowOnCopyOrMoveMatcher( ThrowOnCopyOrMoveMatcher const& other ): + Catch::Matchers::MatcherGenericBase( other ) { throw MatcherWasMovedOrCopied(); } - [[noreturn]] ThrowOnCopyOrMoveMatcher( ThrowOnCopyOrMoveMatcher&& ): - Catch::Matchers::MatcherGenericBase() { + // NOLINTNEXTLINE(performance-noexcept-move-constructor) + [[noreturn]] ThrowOnCopyOrMoveMatcher( ThrowOnCopyOrMoveMatcher&& other ): + Catch::Matchers::MatcherGenericBase( CATCH_MOVE(other) ) { throw MatcherWasMovedOrCopied(); } ThrowOnCopyOrMoveMatcher& operator=( ThrowOnCopyOrMoveMatcher const& ) { throw MatcherWasMovedOrCopied(); } + // NOLINTNEXTLINE(performance-noexcept-move-constructor) ThrowOnCopyOrMoveMatcher& operator=( ThrowOnCopyOrMoveMatcher&& ) { throw MatcherWasMovedOrCopied(); } diff --git a/src/external/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp index 05e11b0c..cc8c54f8 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp @@ -381,7 +381,7 @@ TEST_CASE("Usage of AllTrue range matcher", "[matchers][templated][quantifiers]" std::array const data{}; REQUIRE_THAT( data, AllTrue() ); } - SECTION( "One false evalutes to false" ) { + SECTION( "One false evaluates to false" ) { std::array const data{ { true, true, false, true, true } }; REQUIRE_THAT( data, !AllTrue() ); } @@ -398,7 +398,7 @@ TEST_CASE("Usage of AllTrue range matcher", "[matchers][templated][quantifiers]" { { true }, { true }, { true }, { true }, { true } } }; REQUIRE_THAT( data, AllTrue() ); } - SECTION( "One false evalutes to false" ) { + SECTION( "One false evaluates to false" ) { std::array const data{ { { true }, { true }, { false }, { true }, { true } } }; REQUIRE_THAT( data, !AllTrue() ); @@ -446,7 +446,7 @@ TEST_CASE( "Usage of NoneTrue range matcher", "[matchers][templated][quantifiers std::array const data{}; REQUIRE_THAT( data, NoneTrue() ); } - SECTION( "One true evalutes to false" ) { + SECTION( "One true evaluates to false" ) { std::array const data{ { false, false, true, false, false } }; REQUIRE_THAT( data, !NoneTrue() ); @@ -464,7 +464,7 @@ TEST_CASE( "Usage of NoneTrue range matcher", "[matchers][templated][quantifiers { { true }, { true }, { true }, { true }, { true } } }; REQUIRE_THAT( data, !NoneTrue() ); } - SECTION( "One true evalutes to false" ) { + SECTION( "One true evaluates to false" ) { std::array const data{ { { false }, { false }, { true }, { false }, { false } } }; REQUIRE_THAT( data, !NoneTrue() ); @@ -512,7 +512,7 @@ TEST_CASE( "Usage of AnyTrue range matcher", "[matchers][templated][quantifiers] std::array const data{}; REQUIRE_THAT( data, !AnyTrue() ); } - SECTION( "One true evalutes to true" ) { + SECTION( "One true evaluates to true" ) { std::array const data{ { false, false, true, false, false } }; REQUIRE_THAT( data, AnyTrue() ); @@ -530,7 +530,7 @@ TEST_CASE( "Usage of AnyTrue range matcher", "[matchers][templated][quantifiers] { { true }, { true }, { true }, { true }, { true } } }; REQUIRE_THAT( data, AnyTrue() ); } - SECTION( "One true evalutes to true" ) { + SECTION( "One true evaluates to true" ) { std::array const data{ { { false }, { false }, { true }, { false }, { false } } }; REQUIRE_THAT( data, AnyTrue() ); diff --git a/src/external/catch2/tests/SelfTest/UsageTests/Message.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/Message.tests.cpp index a5e69582..7626e005 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/Message.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/Message.tests.cpp @@ -80,20 +80,20 @@ TEST_CASE( "Output from all sections is reported", "[failing][messages][.]" ) { TEST_CASE( "Standard output from all sections is reported", "[messages][.]" ) { SECTION( "one" ) { - std::cout << "Message from section one" << std::endl; + std::cout << "Message from section one\n"; } SECTION( "two" ) { - std::cout << "Message from section two" << std::endl; + std::cout << "Message from section two\n"; } } TEST_CASE( "Standard error is reported and redirected", "[messages][.][approvals]" ) { SECTION( "std::cerr" ) { - std::cerr << "Write to std::cerr" << std::endl; + std::cerr << "Write to std::cerr\n"; } SECTION( "std::clog" ) { - std::clog << "Write to std::clog" << std::endl; + std::clog << "Write to std::clog\n"; } SECTION( "Interleaved writes to cerr and clog" ) { std::cerr << "Inter"; @@ -101,7 +101,7 @@ TEST_CASE( "Standard error is reported and redirected", "[messages][.][approvals std::cerr << ' '; std::clog << "writes"; std::cerr << " to error"; - std::clog << " streams" << std::endl; + std::clog << " streams\n" << std::flush; } } @@ -255,10 +255,24 @@ std::ostream& operator<<(std::ostream& out, helper_1436 const& helper) { #pragma clang diagnostic ignored "-Wunused-value" #endif +namespace { + template + struct custom_index_op { + constexpr custom_index_op( std::initializer_list ) {} + constexpr T operator[]( size_t ) { return T{}; } +#if defined( __cpp_multidimensional_subscript ) && \ + __cpp_multidimensional_subscript >= 202110L + constexpr T operator[]( size_t, size_t, size_t ) const noexcept { + return T{}; + } +#endif + }; +} + TEST_CASE("CAPTURE can deal with complex expressions involving commas", "[messages][capture]") { - CAPTURE(std::vector{1, 2, 3}[0, 1, 2], - std::vector{1, 2, 3}[(0, 1)], - std::vector{1, 2, 3}[0]); + CAPTURE(custom_index_op{1, 2, 3}[0, 1, 2], + custom_index_op{1, 2, 3}[(0, 1)], + custom_index_op{1, 2, 3}[0]); CAPTURE((helper_1436{12, -12}), (helper_1436(-12, 12))); CAPTURE( (1, 2), (2, 3) ); @@ -285,3 +299,14 @@ TEST_CASE("CAPTURE parses string and character constants", "[messages][capture]" #ifdef _MSC_VER #pragma warning(pop) #endif + +TEST_CASE( "INFO and UNSCOPED_INFO can stream multiple arguments", + "[messages][info][.failing]" ) { + INFO( "This info" + << " has multiple" + << " parts." ); + UNSCOPED_INFO( "This unscoped info" + << " has multiple" + << " parts." ); + FAIL( "Show infos!" ); +} diff --git a/src/external/catch2/tests/SelfTest/UsageTests/Misc.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/Misc.tests.cpp index 6c1fd68f..3697f069 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/Misc.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/Misc.tests.cpp @@ -11,11 +11,6 @@ #include #include -#ifdef __clang__ -# pragma clang diagnostic ignored "-Wc++98-compat" -# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#endif - #include #include @@ -158,9 +153,9 @@ TEST_CASE( "looped tests", "[.][failing]" ) { } TEST_CASE( "Sends stuff to stdout and stderr", "[.]" ) { - std::cout << "A string sent directly to stdout" << std::endl; - std::cerr << "A string sent directly to stderr" << std::endl; - std::clog << "A string sent to stderr via clog" << std::endl; + std::cout << "A string sent directly to stdout\n" << std::flush; + std::cerr << "A string sent directly to stderr\n" << std::flush; + std::clog << "A string sent to stderr via clog\n" << std::flush; } TEST_CASE( "null strings" ) { @@ -217,6 +212,18 @@ TEST_CASE("Testing checked-if 3", "[checked-if][!shouldfail]") { SUCCEED(); } +[[noreturn]] +TEST_CASE("Testing checked-if 4", "[checked-if][!shouldfail]") { + CHECKED_ELSE(true) {} + throw std::runtime_error("Uncaught exception should fail!"); +} + +[[noreturn]] +TEST_CASE("Testing checked-if 5", "[checked-if][!shouldfail]") { + CHECKED_ELSE(false) {} + throw std::runtime_error("Uncaught exception should fail!"); +} + TEST_CASE( "xmlentitycheck" ) { SECTION( "embedded xml: it should be possible to embed xml characters, such as <, \" or &, or even whole documents within an attribute" ) { SUCCEED(); // We need this here to stop it failing due to no tests @@ -384,7 +391,7 @@ TEMPLATE_PRODUCT_TEST_CASE("Product with differing arities", "[template][product using MyTypes = std::tuple; TEMPLATE_LIST_TEST_CASE("Template test case with test types specified inside std::tuple", "[template][list]", MyTypes) { - REQUIRE(sizeof(TestType) > 0); + REQUIRE(std::is_arithmetic::value); } struct NonDefaultConstructibleType { @@ -394,7 +401,7 @@ struct NonDefaultConstructibleType { using MyNonDefaultConstructibleTypes = std::tuple; TEMPLATE_LIST_TEST_CASE("Template test case with test types specified inside non-default-constructible std::tuple", "[template][list]", MyNonDefaultConstructibleTypes) { - REQUIRE(sizeof(TestType) > 0); + REQUIRE(std::is_trivially_copyable::value); } struct NonCopyableAndNonMovableType { @@ -409,7 +416,7 @@ struct NonCopyableAndNonMovableType { using NonCopyableAndNonMovableTypes = std::tuple; TEMPLATE_LIST_TEST_CASE("Template test case with test types specified inside non-copyable and non-movable std::tuple", "[template][list]", NonCopyableAndNonMovableTypes) { - REQUIRE(sizeof(TestType) > 0); + REQUIRE(std::is_default_constructible::value); } // https://github.com/philsquared/Catch/issues/166 diff --git a/src/external/catch2/tests/SelfTest/UsageTests/Skip.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/Skip.tests.cpp index 6bd4189b..661795e1 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/Skip.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/Skip.tests.cpp @@ -71,3 +71,30 @@ TEST_CASE( "failing for some generator values causes entire test case to fail", FAIL(); } } + +namespace { + class test_skip_generator : public Catch::Generators::IGenerator { + public: + explicit test_skip_generator() { SKIP( "This generator is empty" ); } + + auto get() const -> int const& override { + static constexpr int value = 1; + return value; + } + + auto next() -> bool override { return false; } + }; + + static auto make_test_skip_generator() + -> Catch::Generators::GeneratorWrapper { + return { new test_skip_generator() }; + } + +} // namespace + +TEST_CASE( "Empty generators can SKIP in constructor", "[skipping]" ) { + // The generator signals emptiness with `SKIP` + auto sample = GENERATE( make_test_skip_generator() ); + // This assertion would fail, but shouldn't trigger + REQUIRE( sample == 0 ); +} diff --git a/src/external/catch2/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp index 28d60196..78c0c80a 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp @@ -14,31 +14,20 @@ TEST_CASE( "Character pretty printing" ){ SECTION("Specifically escaped"){ - char tab = '\t'; - char newline = '\n'; - char carr_return = '\r'; - char form_feed = '\f'; - CHECK(tab == '\t'); - CHECK(newline == '\n'); - CHECK(carr_return == '\r'); - CHECK(form_feed == '\f'); + CHECK(::Catch::Detail::stringify('\t') == "'\\t'"); + CHECK(::Catch::Detail::stringify('\n') == "'\\n'"); + CHECK(::Catch::Detail::stringify('\r') == "'\\r'"); + CHECK(::Catch::Detail::stringify('\f') == "'\\f'"); } SECTION("General chars"){ - char space = ' '; - CHECK(space == ' '); - char chars[] = {'a', 'z', 'A', 'Z'}; - for (int i = 0; i < 4; ++i){ - char c = chars[i]; - REQUIRE(c == chars[i]); - } + CHECK(::Catch::Detail::stringify( ' ' ) == "' '" ); + CHECK(::Catch::Detail::stringify( 'A' ) == "'A'" ); + CHECK(::Catch::Detail::stringify( 'z' ) == "'z'" ); } SECTION("Low ASCII"){ - char null_terminator = '\0'; - CHECK(null_terminator == '\0'); - for (int i = 2; i < 6; ++i){ - char c = static_cast(i); - REQUIRE(c == i); - } + CHECK(::Catch::Detail::stringify( '\0' ) == "0" ); + CHECK(::Catch::Detail::stringify( static_cast(2) ) == "2" ); + CHECK(::Catch::Detail::stringify( static_cast(5) ) == "5" ); } } @@ -135,8 +124,8 @@ TEST_CASE("Precision of floating point stringification can be set", "[toString][ const auto oldPrecision = sm::precision; const float testFloat = 1.12345678901234567899f; - auto str1 = sm::convert(testFloat); sm::precision = 5; + auto str1 = sm::convert( testFloat ); // "1." prefix = 2 chars, f suffix is another char CHECK(str1.size() == 3 + 5); diff --git a/src/external/catch2/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp index 9fd9d6b4..3671771a 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp @@ -28,4 +28,8 @@ TEST_CASE( "std::vector > -> toString", "[toString][optional] REQUIRE( "{ 0, { }, 2 }" == ::Catch::Detail::stringify( type{ 0, {}, 2 } ) ); } +TEST_CASE( "std::nullopt -> toString", "[toString][optional][approvals]" ) { + REQUIRE( "{ }" == ::Catch::Detail::stringify( std::nullopt ) ); +} + #endif // CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL diff --git a/src/external/catch2/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp index b2813a81..9d1d2c46 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp @@ -29,8 +29,8 @@ TEST_CASE( "tuple", "[toString][tuple]" ) TEST_CASE( "tuple", "[toString][tuple]" ) { typedef std::tuple type; - CHECK( "1.2f" == ::Catch::Detail::stringify(float(1.2)) ); - CHECK( "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0}) ); + CHECK( "1.5f" == ::Catch::Detail::stringify(float(1.5)) ); + CHECK( "{ 1.5f, 0 }" == ::Catch::Detail::stringify(type{1.5f,0}) ); } TEST_CASE( "tuple", "[toString][tuple]" ) @@ -42,8 +42,8 @@ TEST_CASE( "tuple", "[toString][tuple]" ) TEST_CASE( "tuple,tuple<>,float>", "[toString][tuple]" ) { typedef std::tuple,std::tuple<>,float> type; - type value { std::tuple{42}, {}, 1.2f }; - CHECK( "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value) ); + type value { std::tuple{42}, {}, 1.5f }; + CHECK( "{ { 42 }, { }, 1.5f }" == ::Catch::Detail::stringify(value) ); } TEST_CASE( "tuple", "[approvals][toString][tuple]" ) { diff --git a/src/external/catch2/tests/SelfTest/UsageTests/Tricky.tests.cpp b/src/external/catch2/tests/SelfTest/UsageTests/Tricky.tests.cpp index 25770340..041d7867 100644 --- a/src/external/catch2/tests/SelfTest/UsageTests/Tricky.tests.cpp +++ b/src/external/catch2/tests/SelfTest/UsageTests/Tricky.tests.cpp @@ -261,7 +261,7 @@ TEST_CASE( "non streamable - with conv. op", "[Tricky]" ) inline void foo() {} -typedef void (*fooptr_t)(); +using fooptr_t = void (*)(); TEST_CASE( "Comparing function pointers", "[Tricky][function pointer]" ) { @@ -281,7 +281,7 @@ struct S TEST_CASE( "Comparing member function pointers", "[Tricky][member function pointer][approvals]" ) { - typedef void (S::*MF)(); + using MF = void (S::*)(); MF m = &S::f; CHECK( m == &S::f ); diff --git a/src/external/catch2/tests/SelfTest/helpers/type_with_lit_0_comparisons.hpp b/src/external/catch2/tests/SelfTest/helpers/type_with_lit_0_comparisons.hpp index 202c3af4..a8e517c0 100644 --- a/src/external/catch2/tests/SelfTest/helpers/type_with_lit_0_comparisons.hpp +++ b/src/external/catch2/tests/SelfTest/helpers/type_with_lit_0_comparisons.hpp @@ -12,23 +12,34 @@ #include // Should only be constructible from literal 0. +// Based on the constructor from pointer trick, used by libstdc++ and libc++ +// (formerly also MSVC, but they've moved to consteval int constructor). // Used by `TypeWithLit0Comparisons` for testing comparison // ops that only work with literal zero, the way std::*orderings do -struct ZeroLiteralDetector { - constexpr ZeroLiteralDetector( ZeroLiteralDetector* ) noexcept {} +struct ZeroLiteralAsPointer { + constexpr ZeroLiteralAsPointer( ZeroLiteralAsPointer* ) noexcept {} template ::value>> - constexpr ZeroLiteralDetector( T ) = delete; + constexpr ZeroLiteralAsPointer( T ) = delete; }; + struct TypeWithLit0Comparisons { -#define DEFINE_COMP_OP( op ) \ - friend bool operator op( TypeWithLit0Comparisons, ZeroLiteralDetector ) { \ - return true; \ - } \ - friend bool operator op( ZeroLiteralDetector, TypeWithLit0Comparisons ) { \ - return false; \ +#define DEFINE_COMP_OP( op ) \ + constexpr friend bool operator op( TypeWithLit0Comparisons, \ + ZeroLiteralAsPointer ) { \ + return true; \ + } \ + constexpr friend bool operator op( ZeroLiteralAsPointer, \ + TypeWithLit0Comparisons ) { \ + return false; \ + } \ + /* std::orderings only have these for ==, but we add them for all \ + operators so we can test all overloads for decomposer */ \ + constexpr friend bool operator op( TypeWithLit0Comparisons, \ + TypeWithLit0Comparisons ) { \ + return true; \ } DEFINE_COMP_OP( < ) diff --git a/src/external/catch2/tests/TestScripts/DiscoverTests/CMakeLists.txt b/src/external/catch2/tests/TestScripts/DiscoverTests/CMakeLists.txt new file mode 100644 index 00000000..5105cddb --- /dev/null +++ b/src/external/catch2/tests/TestScripts/DiscoverTests/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.10) + +project(discover-tests-test + LANGUAGES CXX +) + +add_executable(tests + register-tests.cpp +) + +add_subdirectory(${CATCH2_PATH} catch2-build) +target_link_libraries(tests PRIVATE Catch2::Catch2WithMain) + +enable_testing() +include(Catch) +set(extra_args) +if (CMAKE_VERSION GREATER_EQUAL 3.27) + list(APPEND extra_args + DL_PATHS "${CMAKE_CURRENT_LIST_DIR};${CMAKE_CURRENT_LIST_DIR}/.." + ) +endif () +catch_discover_tests(tests ${extra_args}) diff --git a/src/external/catch2/tests/TestScripts/DiscoverTests/VerifyRegistration.py b/src/external/catch2/tests/TestScripts/DiscoverTests/VerifyRegistration.py new file mode 100644 index 00000000..9800674f --- /dev/null +++ b/src/external/catch2/tests/TestScripts/DiscoverTests/VerifyRegistration.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 + +# Copyright Catch2 Authors +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE.txt or copy at +# https://www.boost.org/LICENSE_1_0.txt) + +# SPDX-License-Identifier: BSL-1.0 + +import os +import subprocess +import sys +import re +import json + +cmake_version_regex = re.compile('cmake version (\d+)\.(\d+)\.(\d+)') + +def get_cmake_version(): + result = subprocess.run(['cmake', '--version'], + capture_output = True, + check = True, + text = True) + version_match = cmake_version_regex.match(result.stdout) + if not version_match: + print('Could not find cmake version in output') + print(f"output: '{result.stdout}'") + exit(4) + return (int(version_match.group(1)), + int(version_match.group(2)), + int(version_match.group(3))) + +def build_project(sources_dir, output_base_path, catch2_path): + build_dir = os.path.join(output_base_path, 'ctest-registration-test') + config_cmd = ['cmake', + '-B', build_dir, + '-S', sources_dir, + f'-DCATCH2_PATH={catch2_path}', + '-DCMAKE_BUILD_TYPE=Debug'] + + build_cmd = ['cmake', + '--build', build_dir, + '--config', 'Debug'] + + try: + subprocess.run(config_cmd, + capture_output = True, + check = True, + text = True) + subprocess.run(build_cmd, + capture_output = True, + check = True, + text = True) + except subprocess.CalledProcessError as err: + print('Error when building the test project') + print(f'cmd: {err.cmd}') + print(f'stderr: {err.stderr}') + print(f'stdout: {err.stdout}') + exit(3) + + return build_dir + + + +def get_test_names(build_path): + # For now we assume that Windows builds are done using MSBuild under + # Debug configuration. This means that we need to add "Debug" folder + # to the path when constructing it. On Linux, we don't add anything. + config_path = "Debug" if os.name == 'nt' else "" + full_path = os.path.join(build_path, config_path, 'tests') + + + cmd = [full_path, '--reporter', 'xml', '--list-tests'] + result = subprocess.run(cmd, + capture_output = True, + check = True, + text = True) + + import xml.etree.ElementTree as ET + root = ET.fromstring(result.stdout) + return [tc.text for tc in root.findall('TestCase/Name')] + +def get_ctest_listing(build_path): + old_path = os.getcwd() + os.chdir(build_path) + + cmd = ['ctest', '-C', 'debug', '--show-only=json-v1'] + result = subprocess.run(cmd, + capture_output = True, + check = True, + text = True) + os.chdir(old_path) + return result.stdout + +def extract_tests_from_ctest(ctest_output): + ctest_response = json.loads(ctest_output) + tests = ctest_response['tests'] + test_names = [] + for test in tests: + test_command = test['command'] + # First part of the command is the binary, second is the filter. + # If there are less, registration has failed. If there are more, + # registration has changed and the script needs updating. + assert len(test_command) == 2 + test_names.append(test_command[1]) + test_name = test_command[1] + + return test_names + +def check_DL_PATHS(ctest_output): + ctest_response = json.loads(ctest_output) + tests = ctest_response['tests'] + for test in tests: + properties = test['properties'] + for property in properties: + if property['name'] == 'ENVIRONMENT_MODIFICATION': + assert len(property['value']) == 2, f"The test provides 2 arguments to DL_PATHS, but instead found {len(property['value'])}" + +def escape_catch2_test_name(name): + for char in ('\\', ',', '[', ']'): + name = name.replace(char, f"\\{char}") + return name + +if __name__ == '__main__': + if len(sys.argv) != 3: + print(f'Usage: {sys.argv[0]} path-to-catch2-cml output-path') + exit(2) + catch2_path = sys.argv[1] + output_base_path = sys.argv[2] + sources_dir = os.path.dirname(os.path.abspath(sys.argv[0])) + + build_path = build_project(sources_dir, output_base_path, catch2_path) + + catch_test_names = [escape_catch2_test_name(name) for name in get_test_names(build_path)] + ctest_output = get_ctest_listing(build_path) + ctest_test_names = extract_tests_from_ctest(ctest_output) + + mismatched = 0 + for catch_test in catch_test_names: + if catch_test not in ctest_test_names: + print(f"Catch2 test '{catch_test}' not found in CTest") + mismatched += 1 + for ctest_test in ctest_test_names: + if ctest_test not in catch_test_names: + print(f"CTest test '{ctest_test}' not found in Catch2") + mismatched += 1 + + if mismatched: + print(f"Found {mismatched} mismatched tests catch test names and ctest test commands!") + exit(1) + + cmake_version = get_cmake_version() + if cmake_version >= (3, 27): + check_DL_PATHS(ctest_output) diff --git a/src/external/catch2/tests/TestScripts/DiscoverTests/register-tests.cpp b/src/external/catch2/tests/TestScripts/DiscoverTests/register-tests.cpp new file mode 100644 index 00000000..aa603df1 --- /dev/null +++ b/src/external/catch2/tests/TestScripts/DiscoverTests/register-tests.cpp @@ -0,0 +1,16 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#include + +TEST_CASE("@Script[C:\\EPM1A]=x;\"SCALA_ZERO:\"", "[script regressions]"){} +TEST_CASE("Some test") {} +TEST_CASE( "Let's have a test case with a long name. Longer. No, even longer. " + "Really looooooooooooong. Even longer than that. Multiple lines " + "worth of test name. Yep, like this." ) {} +TEST_CASE( "And now a test case with weird tags.", "[tl;dr][tl;dw][foo,bar]" ) {} diff --git a/src/external/catch2/tests/TestScripts/testBazelReporter.py b/src/external/catch2/tests/TestScripts/testBazelReporter.py index d0893f81..573eafd2 100644 --- a/src/external/catch2/tests/TestScripts/testBazelReporter.py +++ b/src/external/catch2/tests/TestScripts/testBazelReporter.py @@ -52,7 +52,7 @@ ) stdout = ret.stdout except subprocess.SubprocessError as ex: - if ex.returncode == 1: + if ex.returncode == 42: # The test cases are allowed to fail. test_passing = False stdout = ex.stdout diff --git a/src/external/catch2/tests/TestScripts/testConfigureDefaultReporter.py b/src/external/catch2/tests/TestScripts/testConfigureDefaultReporter.py index 5bf97871..119e1ca2 100644 --- a/src/external/catch2/tests/TestScripts/testConfigureDefaultReporter.py +++ b/src/external/catch2/tests/TestScripts/testConfigureDefaultReporter.py @@ -28,14 +28,23 @@ catch2_source_path = os.path.abspath(sys.argv[1]) build_dir_path = os.path.join(os.path.abspath(sys.argv[2]), 'CMakeConfigTests', 'DefaultReporter') +output_file = f"{build_dir_path}/foo.xml" +# We need to escape backslashes in Windows paths, because otherwise they +# are interpreted as escape characters in strings, and cause compilation +# error. +escaped_output_file = output_file.replace('\\', '\\\\') configure_and_build(catch2_source_path, build_dir_path, - [("CATCH_CONFIG_DEFAULT_REPORTER", "xml")]) + [("CATCH_CONFIG_DEFAULT_REPORTER", f"xml::out={escaped_output_file}")]) stdout, _ = run_and_return_output(os.path.join(build_dir_path, 'tests'), 'SelfTest', ['[approx][custom]']) -xml_tag = '' -if xml_tag not in stdout: - print("Could not find '{}' in the stdout".format(xml_tag)) - print('stdout: "{}"'.format(stdout)) +if not os.path.exists(output_file): + print(f'Did not find the {output_file} file') exit(2) + +xml_tag = '' +with open(output_file, 'r', encoding='utf-8') as file: + if xml_tag not in file.read(): + print(f"Could not find '{xml_tag}' in the file") + exit(3) diff --git a/src/external/catch2/tests/meson.build b/src/external/catch2/tests/meson.build index f525f041..58302b7a 100644 --- a/src/external/catch2/tests/meson.build +++ b/src/external/catch2/tests/meson.build @@ -17,6 +17,7 @@ self_test_sources = files( 'SelfTest/IntrospectiveTests/Details.tests.cpp', 'SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp', 'SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp', + 'SelfTest/IntrospectiveTests/Integer.tests.cpp', 'SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp', 'SelfTest/IntrospectiveTests/Parse.tests.cpp', 'SelfTest/IntrospectiveTests/PartTracker.tests.cpp', diff --git a/src/external/catch2/tools/misc/appveyorTestRunScript.bat b/src/external/catch2/tools/misc/appveyorTestRunScript.bat index 5982fc92..661bae24 100644 --- a/src/external/catch2/tools/misc/appveyorTestRunScript.bat +++ b/src/external/catch2/tools/misc/appveyorTestRunScript.bat @@ -5,7 +5,7 @@ reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\AutoExclusion cd Build if "%CONFIGURATION%"=="Debug" ( if "%coverage%"=="1" ( - ctest -j 2 -C %CONFIGURATION% -D ExperimentalMemCheck || exit /b !ERRORLEVEL! + ctest -j 2 -C %CONFIGURATION% -D ExperimentalMemCheck -LE uses-signals || exit /b !ERRORLEVEL! python ..\tools\misc\appveyorMergeCoverageScript.py || exit /b !ERRORLEVEL! codecov --root .. --no-color --disable gcov -f cobertura.xml -t %CODECOV_TOKEN% || exit /b !ERRORLEVEL! ) else ( diff --git a/src/external/catch2/tools/scripts/buildAndTest.cmd b/src/external/catch2/tools/scripts/buildAndTest.cmd index 7c10e564..fa359124 100644 --- a/src/external/catch2/tools/scripts/buildAndTest.cmd +++ b/src/external/catch2/tools/scripts/buildAndTest.cmd @@ -12,6 +12,5 @@ rem 3. Run the actual build cmake --build debug-build rem 4. Run the tests using CTest -cd debug-build -ctest -j 4 --output-on-failure -C Debug +ctest -j 4 --output-on-failure -C Debug --test-dir debug-build rem end-snippet diff --git a/src/external/catch2/tools/scripts/buildAndTest.sh b/src/external/catch2/tools/scripts/buildAndTest.sh index 01a82837..0383c97e 100755 --- a/src/external/catch2/tools/scripts/buildAndTest.sh +++ b/src/external/catch2/tools/scripts/buildAndTest.sh @@ -14,6 +14,5 @@ cmake -B debug-build -S . -DCMAKE_BUILD_TYPE=Debug --preset all-tests cmake --build debug-build # 4. Run the tests using CTest -cd debug-build -ctest -j 4 --output-on-failure -C Debug +ctest -j 4 --output-on-failure -C Debug --test-dir debug-build # end-snippet diff --git a/src/external/catch2/tools/scripts/checkLicense.py b/src/external/catch2/tools/scripts/checkLicense.py index 9a949769..7078d3ec 100755 --- a/src/external/catch2/tools/scripts/checkLicense.py +++ b/src/external/catch2/tools/scripts/checkLicense.py @@ -33,7 +33,8 @@ def check_licences_in_path(path: str) -> int: def check_licences(): failed = 0 - roots = ['src/catch2', 'tests'] + # Add 'extras' after the amalgamted files are regenerated with the new script (past 3.4.0) + roots = ['src/catch2', 'tests', 'examples', 'fuzzing'] for root in roots: failed += check_licences_in_path(root) diff --git a/src/external/catch2/tools/scripts/generateAmalgamatedFiles.py b/src/external/catch2/tools/scripts/generateAmalgamatedFiles.py index 99fc446b..e3e86aab 100755 --- a/src/external/catch2/tools/scripts/generateAmalgamatedFiles.py +++ b/src/external/catch2/tools/scripts/generateAmalgamatedFiles.py @@ -1,4 +1,9 @@ #!/usr/bin/env python3 +# Copyright Catch2 Authors +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE.txt or copy at +# https://www.boost.org/LICENSE_1_0.txt) +# SPDX-License-Identifier: BSL-1.0 import os import re @@ -12,6 +17,8 @@ output_header = os.path.join(catchPath, 'extras', 'catch_amalgamated.hpp') output_cpp = os.path.join(catchPath, 'extras', 'catch_amalgamated.cpp') +# REUSE-IgnoreStart + # These are the copyright comments in each file, we want to ignore them copyright_lines = [ '// Copyright Catch2 Authors\n', @@ -24,6 +31,7 @@ # The header of the amalgamated file: copyright information + explanation # what this file is. file_header = '''\ + // Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at @@ -39,6 +47,8 @@ // ---------------------------------------------------------- ''' +# REUSE-IgnoreEnd + # Returns file header with proper version string and generation time def formatted_file_header(version): return file_header.format(version_string=version.getVersionString(), diff --git a/src/external/catch2/tools/scripts/releaseCommon.py b/src/external/catch2/tools/scripts/releaseCommon.py index 0d995eaf..81efa762 100644 --- a/src/external/catch2/tools/scripts/releaseCommon.py +++ b/src/external/catch2/tools/scripts/releaseCommon.py @@ -89,7 +89,7 @@ def updateCmakeFile(version): def updateMesonFile(version): with open(mesonPath, 'rb') as file: lines = file.readlines() - replacementRegex = re.compile(b'''version\s*:\s*'(\\d+.\\d+.\\d+)', # CML version placeholder, don't delete''') + replacementRegex = re.compile(b'''version\\s*:\\s*'(\\d+.\\d+.\\d+)', # CML version placeholder, don't delete''') replacement = '''version: '{0}', # CML version placeholder, don't delete'''.format(version.getVersionString()).encode('ascii') with open(mesonPath, 'wb') as file: for line in lines: @@ -114,8 +114,8 @@ def updateVersionDefine(version): def updateVersionPlaceholder(filename, version): with open(filename, 'rb') as file: lines = file.readlines() - placeholderRegex = re.compile(b'in Catch[0-9]? X.Y.Z') - replacement = 'in Catch2 {}.{}.{}'.format(version.majorVersion, version.minorVersion, version.patchNumber).encode('ascii') + placeholderRegex = re.compile(b'Catch[0-9]? X.Y.Z') + replacement = 'Catch2 {}.{}.{}'.format(version.majorVersion, version.minorVersion, version.patchNumber).encode('ascii') with open(filename, 'wb') as file: for line in lines: file.write(placeholderRegex.sub(replacement, line)) diff --git a/src/external/catch2/tools/scripts/updateDocumentToC.py b/src/external/catch2/tools/scripts/updateDocumentToC.py index 7b56cfc7..1840cecc 100755 --- a/src/external/catch2/tools/scripts/updateDocumentToC.py +++ b/src/external/catch2/tools/scripts/updateDocumentToC.py @@ -287,7 +287,7 @@ def markdownToclify( Path to the markdown output file. min_toc_len: int (default: 2) - Miniumum number of entries to create a table of contents for. + Minimum number of entries to create a table of contents for. github: bool (default: False) Uses GitHub TOC syntax if True.