Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ Match the assertion to the tested property:
* **Conditional Specifications**: Pair negative assertions with their positive converses (e.g., pair `static_assert(not noexcept(expr))` with a witness for which it is `noexcept`). This proves that the specification is conditional rather than unconditionally false. Apply the same positive-control discipline to negative constraint or viability probes.
* **Substitution Contexts**: Ensure expressions in negative viability probes are dependent on a template parameter. A `requires`-expression over concrete types is not a substitution context, meaning an invalid requirement triggers a hard error rather than a SFINAE failure. Enclose the dependent probe in a generic lambda or a type-keyed concept to yield `false` on invalid substitution.
* **Exception Fixtures**: Select exception-proving fixtures carefully. The library's `helper_t` implements a separate constructor for each value category, and its non-const-lvalue copy constructor is always `noexcept`. A `helper_t` configured to throw is therefore not throwing for every value category, which can hide incorrect `noexcept` specifications. Use a simple, local type if the test requires every relevant constructor to be potentially throwing.
* **Expected Failures**: Compile-must-fail tests build excluded object targets through CTest with `WILL_FAIL`. Pair each rejection with a normal-build positive control using the same fixture and compiler configuration. Check rejection-fixture assumptions in a build that must succeed, with the rejected call omitted. The suite cannot test runtime crashes; record these untested cases beside their positive controls.
* **Expected Failures**: Put a statement that must not compile in a `tests/pfn/reject/` source, alone in an `#ifdef LIBFN_REJECT_<scenario>` block, after its positive control: the same expression with a fixture differing only in what makes it fail. The build compiles each source as is, which must succeed; CTest compiles it once per guard with that guard defined, which must fail. The suite cannot test runtime crashes; record these untested cases beside their positive controls.

## Client code

Expand Down
125 changes: 58 additions & 67 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,63 @@ set(TESTS_PFN_SOURCES
pfn/utility.cpp
)

set(TESTS_PFN_REJECT_SOURCES
pfn/reject/expected_monadic.cpp
pfn/reject/optional_monadic.cpp
)

include(TargetGenerator)
include(CompilationOptions)

# Generate an object target for SOURCE, which must compile, and for each
# `#ifdef LIBFN_REJECT_<scenario>` guard in it a test building SOURCE with only that guard defined,
# which must fail
function(add_reject_tests)
set(oneValueArgs SOURCE MODE)
set(multiValueArgs DEPENDENCIES LABELS)
cmake_parse_arguments(Reject "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

set(path "${CMAKE_CURRENT_SOURCE_DIR}/${Reject_SOURCE}")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${path}")
file(STRINGS "${path}" guards REGEX "^#ifdef LIBFN_REJECT_[A-Za-z0-9_]+$")
file(STRINGS "${path}" directives REGEX "^[ \t]*#.*LIBFN_REJECT_")
if(NOT guards OR NOT guards STREQUAL directives)
message(FATAL_ERROR "${Reject_SOURCE}: needs at least one guard, each spelled `#ifdef LIBFN_REJECT_<scenario>`")
endif()
list(TRANSFORM guards REPLACE "^#ifdef LIBFN_REJECT_" "" OUTPUT_VARIABLE scenarios)

string(REGEX REPLACE "\\.cpp$" "" root_name "${Reject_SOURCE}")
string(REPLACE "/" "_" root_name "${root_name}")
set(control "tests_${root_name}_cxx${Reject_MODE}")
add_library("${control}" OBJECT "${Reject_SOURCE}")
add_dependencies("cxx${Reject_MODE}" "${control}")
add_dependencies("tests" "${control}")
set(targets "${control}")

foreach(scenario IN LISTS scenarios)
set(target "tests_${root_name}_${scenario}_cxx${Reject_MODE}")
add_library("${target}" OBJECT EXCLUDE_FROM_ALL "${Reject_SOURCE}")
target_compile_definitions("${target}" PRIVATE "LIBFN_REJECT_${scenario}")

add_test(
NAME "${target}"
COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config "$<CONFIG>" --target "${target}"
)
# Concurrent builds in one tree would race on its build-system state
set_tests_properties("${target}" PROPERTIES
WILL_FAIL TRUE
RESOURCE_LOCK build_tree
LABELS "${Reject_LABELS};cxx${Reject_MODE};${root_name}")
list(APPEND targets "${target}")
endforeach()

foreach(target IN LISTS targets)
target_link_libraries("${target}" PRIVATE ${Reject_DEPENDENCIES})
append_compilation_options("${target}" WARNINGS)
set_property(TARGET "${target}" PROPERTY CXX_STANDARD "${Reject_MODE}")
endforeach()
endfunction()

# Generate separate target for each individual test source
foreach(mode 20 23 26)
if (NOT VALIDATE_CXX23 AND mode EQUAL 23)
Expand All @@ -75,73 +129,6 @@ foreach(mode 20 23 26)
continue()
endif()

foreach(scenario RANGE 1 9)
set(rejection_target "tests_optional_monadic_reject_${scenario}_cxx${mode}")
set(control_target "tests_optional_monadic_control_${scenario}_cxx${mode}")
set(fixture_target "tests_optional_monadic_fixture_${scenario}_cxx${mode}")
add_library("${rejection_target}" OBJECT EXCLUDE_FROM_ALL compile_fail/optional_monadic.cpp)
add_library("${control_target}" OBJECT compile_fail/optional_monadic.cpp)
add_library("${fixture_target}" OBJECT compile_fail/optional_monadic.cpp)
foreach(target IN ITEMS "${rejection_target}" "${control_target}" "${fixture_target}")
target_link_libraries("${target}" PRIVATE include_pfn)
append_compilation_options("${target}" WARNINGS)
set_property(TARGET "${target}" PROPERTY CXX_STANDARD "${mode}")
target_compile_definitions("${target}" PRIVATE OPTIONAL_MONADIC_CASE=${scenario})
endforeach()
target_compile_definitions("${rejection_target}" PRIVATE OPTIONAL_MONADIC_POSITIVE=0)
target_compile_definitions("${control_target}" PRIVATE OPTIONAL_MONADIC_POSITIVE=1)
target_compile_definitions("${fixture_target}" PRIVATE
OPTIONAL_MONADIC_POSITIVE=0 OPTIONAL_MONADIC_FIXTURE_ONLY=1)
add_dependencies("tests" "${control_target}" "${fixture_target}")
add_dependencies("cxx${mode}" "${control_target}" "${fixture_target}")
add_test(NAME "${rejection_target}"
COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}"
--config "$<CONFIG>" --target "${rejection_target}")
set_tests_properties("${rejection_target}" PROPERTIES
WILL_FAIL TRUE RUN_SERIAL TRUE
LABELS "tests_pfn;cxx${mode};pfn_optional;compile_fail")
unset(rejection_target)
unset(control_target)
unset(fixture_target)
endforeach()

foreach(kind IN ITEMS object void)
foreach(operation RANGE 0 3)
foreach(direction RANGE 0 4)
if(kind STREQUAL "void" AND operation LESS 2 AND direction GREATER 0)
continue()
endif()
set(test_stem "tests_expected_monadic_${kind}_${operation}_${direction}_cxx${mode}")
foreach(variant IN ITEMS reject control fixture)
set(target "${test_stem}_${variant}")
if(variant STREQUAL "reject")
add_library("${target}" OBJECT EXCLUDE_FROM_ALL compile_fail/expected_monadic.cpp)
else()
add_library("${target}" OBJECT compile_fail/expected_monadic.cpp)
add_dependencies("tests" "${target}")
add_dependencies("cxx${mode}" "${target}")
endif()
target_link_libraries("${target}" PRIVATE include_pfn)
append_compilation_options("${target}" WARNINGS)
set_property(TARGET "${target}" PROPERTY CXX_STANDARD "${mode}")
target_compile_definitions("${target}" PRIVATE
EXPECTED_MONADIC_VOID=$<STREQUAL:${kind},void> EXPECTED_MONADIC_OP=${operation}
EXPECTED_MONADIC_DIRECTION=${direction}
EXPECTED_MONADIC_POSITIVE=$<STREQUAL:${variant},control>
EXPECTED_MONADIC_FIXTURE_ONLY=$<STREQUAL:${variant},fixture>)
endforeach()
add_test(NAME "${test_stem}_reject"
COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}"
--config "$<CONFIG>" --target "${test_stem}_reject")
set_tests_properties("${test_stem}_reject" PROPERTIES
WILL_FAIL TRUE RUN_SERIAL TRUE
LABELS "tests_pfn;cxx${mode};pfn_expected;compile_fail")
unset(target)
unset(test_stem)
endforeach()
endforeach()
endforeach()

foreach(source IN ITEMS ${TESTS_PFN_SOURCES})
string(REGEX REPLACE "^(pfn)/(detail|)/?([^\.]+)\.cpp$" "\\1_\\2\\3" root_name ${source})
set(target "tests_${root_name}_cxx${mode}")
Expand All @@ -167,6 +154,10 @@ foreach(mode 20 23 26)
unset(target)
unset(root_name)
endforeach()

foreach(source IN ITEMS ${TESTS_PFN_REJECT_SOURCES})
add_reject_tests(SOURCE "${source}" MODE "${mode}" DEPENDENCIES include_pfn LABELS tests_pfn)
endforeach()
endforeach()

### tests/fn
Expand Down
119 changes: 0 additions & 119 deletions tests/compile_fail/expected_monadic.cpp

This file was deleted.

101 changes: 0 additions & 101 deletions tests/compile_fail/optional_monadic.cpp

This file was deleted.

4 changes: 2 additions & 2 deletions tests/pfn/optional.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1655,7 +1655,7 @@ TEST_CASE("optional", "[optional][polyfill]")
static_assert(std::is_invocable_v<F, int const &>);
static_assert(not std::is_invocable_v<F, int &&>);
static_assert(not std::is_invocable_v<F, int const &&>);
// pfn/std owning rvalue calls with F are hard errors, covered in compile_fail/optional_monadic.cpp.
// pfn/std owning rvalue calls with F are hard errors, covered in reject/optional_monadic.cpp.
constexpr auto check = [] {
optional<int> a(7);
return a.and_then(F{}) == a && std::as_const(a).and_then(F{}) == a;
Expand Down Expand Up @@ -1925,7 +1925,7 @@ TEST_CASE("optional", "[optional][polyfill]")
static_assert(std::is_invocable_v<F, int const &>);
static_assert(not std::is_invocable_v<F, int &&>);
static_assert(not std::is_invocable_v<F, int const &&>);
// pfn/std owning rvalue calls with F are hard errors, covered in compile_fail/optional_monadic.cpp.
// pfn/std owning rvalue calls with F are hard errors, covered in reject/optional_monadic.cpp.
constexpr auto check = [] {
optional<int> a(7);
return a.transform(F{}) == a && std::as_const(a).transform(F{}) == a;
Expand Down
Loading
Loading