diff --git a/libcxx/test/libcxx/algorithms/alg.sorting/assert.min.max.pass.cpp b/libcxx/test/libcxx/algorithms/alg.sorting/assert.min.max.pass.cpp --- a/libcxx/test/libcxx/algorithms/alg.sorting/assert.min.max.pass.cpp +++ b/libcxx/test/libcxx/algorithms/alg.sorting/assert.min.max.pass.cpp @@ -16,15 +16,17 @@ #include #include -#include "check_assertion.h" +#include "terminating_test_helper.h" -int main(int, char**) { - std::initializer_list init_list{}; - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::minmax(init_list), - "initializer_list has to contain at least one element"); +TEST_TERMINATIONS([] { + EXPECT_LIBCPP_ASSERT_FAILURE("initializer_list has to contain at least one element", [] { + std::initializer_list init_list{}; + (void)std::ranges::minmax(init_list); + }); - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::minmax(std::array{}), - "range has to contain at least one element"); + EXPECT_LIBCPP_ASSERT_FAILURE("range has to contain at least one element", [] { + (void)std::ranges::minmax(std::array{}); + }); return 0; -} +}); diff --git a/libcxx/test/libcxx/algorithms/alg.sorting/assert.sort.invalid_comparator.pass.cpp b/libcxx/test/libcxx/algorithms/alg.sorting/assert.sort.invalid_comparator.pass.cpp --- a/libcxx/test/libcxx/algorithms/alg.sorting/assert.sort.invalid_comparator.pass.cpp +++ b/libcxx/test/libcxx/algorithms/alg.sorting/assert.sort.invalid_comparator.pass.cpp @@ -43,175 +43,221 @@ #include #include "bad_comparator_values.h" -#include "check_assertion.h" +#include "terminating_test_helper.h" void check_oob_sort_read() { - std::map> comparison_results; // terrible for performance, but really convenient - for (auto line : std::views::split(DATA, '\n') | std::views::filter([](auto const& line) { return !line.empty(); })) { - auto values = std::views::split(line, ' '); - auto it = values.begin(); - std::size_t left = std::stol(std::string((*it).data(), (*it).size())); - it = std::next(it); - std::size_t right = std::stol(std::string((*it).data(), (*it).size())); - it = std::next(it); - bool result = static_cast(std::stol(std::string((*it).data(), (*it).size()))); - comparison_results[left][right] = result; - } - auto predicate = [&](std::size_t* left, std::size_t* right) { - assert(left != nullptr && right != nullptr && "something is wrong with the test"); - assert(comparison_results.contains(*left) && comparison_results[*left].contains(*right) && "malformed input data?"); - return comparison_results[*left][*right]; - }; - - std::vector> elements; - std::set valid_ptrs; - for (std::size_t i = 0; i != comparison_results.size(); ++i) { - elements.push_back(std::make_unique(i)); - valid_ptrs.insert(elements.back().get()); - } - - auto checked_predicate = [&](size_t* left, size_t* right) { - // If the pointers passed to the comparator are not in the set of pointers we - // set up above, then we're being passed garbage values from the algorithm - // because we're reading OOB. - assert(valid_ptrs.contains(left)); - assert(valid_ptrs.contains(right)); - return predicate(left, right); - }; - - // Check the classic sorting algorithms - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - TEST_LIBCPP_ASSERT_FAILURE(std::sort(copy.begin(), copy.end(), checked_predicate), "Would read out of bounds"); - } - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - TEST_LIBCPP_ASSERT_FAILURE(std::stable_sort(copy.begin(), copy.end(), checked_predicate), "not a valid strict-weak ordering"); - } - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - std::make_heap(copy.begin(), copy.end(), checked_predicate); // doesn't go OOB even with invalid comparator - TEST_LIBCPP_ASSERT_FAILURE(std::sort_heap(copy.begin(), copy.end(), checked_predicate), "not a valid strict-weak ordering"); - } - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - TEST_LIBCPP_ASSERT_FAILURE(std::partial_sort(copy.begin(), copy.end(), copy.end(), checked_predicate), "not a valid strict-weak ordering"); - } - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - std::vector results(copy.size(), nullptr); - TEST_LIBCPP_ASSERT_FAILURE(std::partial_sort_copy(copy.begin(), copy.end(), results.begin(), results.end(), checked_predicate), "not a valid strict-weak ordering"); - } - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - std::nth_element(copy.begin(), copy.end(), copy.end(), checked_predicate); // doesn't go OOB even with invalid comparator - } - - // Check the Ranges sorting algorithms - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::sort(copy, checked_predicate), "Would read out of bounds"); - } - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::stable_sort(copy, checked_predicate), "not a valid strict-weak ordering"); - } - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - std::ranges::make_heap(copy, checked_predicate); // doesn't go OOB even with invalid comparator - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::sort_heap(copy, checked_predicate), "not a valid strict-weak ordering"); - } - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::partial_sort(copy, copy.end(), checked_predicate), "not a valid strict-weak ordering"); - } - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - std::vector results(copy.size(), nullptr); - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::partial_sort_copy(copy, results, checked_predicate), "not a valid strict-weak ordering"); - } - { - std::vector copy; - for (auto const& e : elements) - copy.push_back(e.get()); - std::ranges::nth_element(copy, copy.end(), checked_predicate); // doesn't go OOB even with invalid comparator - } + std::map> + comparison_results; // terrible for performance, but really convenient + for (auto line : std::views::split(DATA, '\n') | std::views::filter([](auto const& line) { return !line.empty(); })) { + auto values = std::views::split(line, ' '); + auto it = values.begin(); + std::size_t left = std::stol(std::string((*it).data(), (*it).size())); + it = std::next(it); + std::size_t right = std::stol(std::string((*it).data(), (*it).size())); + it = std::next(it); + bool result = static_cast(std::stol(std::string((*it).data(), (*it).size()))); + comparison_results[left][right] = result; + } + auto predicate = [&](std::size_t* left, std::size_t* right) { + assert(left != nullptr && right != nullptr && "something is wrong with the test"); + assert(comparison_results.contains(*left) && comparison_results[*left].contains(*right) && "malformed input data?"); + return comparison_results[*left][*right]; + }; + + std::vector> elements; + std::set valid_ptrs; + for (std::size_t i = 0; i != comparison_results.size(); ++i) { + elements.push_back(std::make_unique(i)); + valid_ptrs.insert(elements.back().get()); + } + + auto checked_predicate = [&](size_t* left, size_t* right) { + // If the pointers passed to the comparator are not in the set of pointers we + // set up above, then we're being passed garbage values from the algorithm + // because we're reading OOB. + assert(valid_ptrs.contains(left)); + assert(valid_ptrs.contains(right)); + return predicate(left, right); + }; + + // Check the classic sorting algorithms + EXPECT_LIBCPP_ASSERT_FAILURE("Would read out of bounds", [&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + std::sort(copy.begin(), copy.end(), checked_predicate); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + std::stable_sort(copy.begin(), copy.end(), checked_predicate); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + std::make_heap(copy.begin(), copy.end(), checked_predicate); // doesn't go OOB even with invalid comparator + std::sort_heap(copy.begin(), copy.end(), checked_predicate); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + + std::partial_sort(copy.begin(), copy.end(), copy.end(), checked_predicate); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + std::vector results(copy.size(), nullptr); + + std::partial_sort_copy(copy.begin(), copy.end(), results.begin(), results.end(), checked_predicate); + }); + + EXPECT_SUCCESS([&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + std::nth_element( + copy.begin(), copy.end(), copy.end(), checked_predicate); // doesn't go OOB even with invalid comparator + }); + + // Check the Ranges sorting algorithms + EXPECT_LIBCPP_ASSERT_FAILURE("Would read out of bounds", [&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + std::ranges::sort(copy, checked_predicate); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + std::ranges::stable_sort(copy, checked_predicate); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + std::ranges::make_heap(copy, checked_predicate); // doesn't go OOB even with invalid comparator + std::ranges::sort_heap(copy, checked_predicate); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + + std::ranges::partial_sort(copy, copy.end(), checked_predicate); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + std::vector results(copy.size(), nullptr); + + std::ranges::partial_sort_copy(copy, results, checked_predicate); + }); + + EXPECT_SUCCESS([&] { + std::vector copy; + for (auto const& e : elements) + copy.push_back(e.get()); + std::ranges::nth_element(copy, copy.end(), checked_predicate); // doesn't go OOB even with invalid comparator + }); } struct FloatContainer { float value; - bool operator<(const FloatContainer& other) const { - return value < other.value; - } + bool operator<(const FloatContainer& other) const { return value < other.value; } }; // Nans in floats do not satisfy strict weak ordering by breaking transitivity of equivalence. std::vector generate_float_data() { - std::vector floats(50); - for (int i = 0; i < 50; ++i) { - floats[i].value = static_cast(i); - } - floats.push_back(FloatContainer{std::numeric_limits::quiet_NaN()}); - std::shuffle(floats.begin(), floats.end(), std::default_random_engine()); - return floats; + std::vector floats(50); + for (int i = 0; i < 50; ++i) { + floats[i].value = static_cast(i); + } + floats.push_back(FloatContainer{std::numeric_limits::quiet_NaN()}); + std::shuffle(floats.begin(), floats.end(), std::default_random_engine()); + return floats; } void check_nan_floats() { - auto floats = generate_float_data(); - TEST_LIBCPP_ASSERT_FAILURE(std::sort(floats.begin(), floats.end()), "not a valid strict-weak ordering"); - floats = generate_float_data(); - TEST_LIBCPP_ASSERT_FAILURE(std::stable_sort(floats.begin(), floats.end()), "not a valid strict-weak ordering"); - floats = generate_float_data(); - std::make_heap(floats.begin(), floats.end()); - TEST_LIBCPP_ASSERT_FAILURE(std::sort_heap(floats.begin(), floats.end()), "not a valid strict-weak ordering"); - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::sort(generate_float_data(), std::less()), "not a valid strict-weak ordering"); - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::stable_sort(generate_float_data(), std::less()), "not a valid strict-weak ordering"); - floats = generate_float_data(); - std::ranges::make_heap(floats, std::less()); - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::sort_heap(floats, std::less()), "not a valid strict-weak ordering"); + auto floats = generate_float_data(); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { std::sort(floats.begin(), floats.end()); }); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::stable_sort(floats.begin(), floats.end()); + }); + + std::make_heap(floats.begin(), floats.end()); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::sort_heap(floats.begin(), floats.end()); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::ranges::sort(generate_float_data(), std::less()); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [] { + std::ranges::stable_sort(generate_float_data(), std::less()); + }); + + floats = generate_float_data(); + + std::ranges::make_heap(floats, std::less()); + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::ranges::sort_heap(floats, std::less()); + }); } void check_irreflexive() { - std::vector v(1); - TEST_LIBCPP_ASSERT_FAILURE(std::sort(v.begin(), v.end(), std::greater_equal()), "not a valid strict-weak ordering"); - TEST_LIBCPP_ASSERT_FAILURE(std::stable_sort(v.begin(), v.end(), std::greater_equal()), "not a valid strict-weak ordering"); - std::make_heap(v.begin(), v.end(), std::greater_equal()); - TEST_LIBCPP_ASSERT_FAILURE(std::sort_heap(v.begin(), v.end(), std::greater_equal()), "not a valid strict-weak ordering"); - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::sort(v, std::greater_equal()), "not a valid strict-weak ordering"); - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::stable_sort(v, std::greater_equal()), "not a valid strict-weak ordering"); - std::ranges::make_heap(v, std::greater_equal()); - TEST_LIBCPP_ASSERT_FAILURE(std::ranges::sort_heap(v, std::greater_equal()), "not a valid strict-weak ordering"); -} + std::vector v(1); -int main(int, char**) { + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::sort(v.begin(), v.end(), std::greater_equal()); + }); - check_oob_sort_read(); + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::stable_sort(v.begin(), v.end(), std::greater_equal()); + }); - check_nan_floats(); + std::make_heap(v.begin(), v.end(), std::greater_equal()); + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::sort_heap(v.begin(), v.end(), std::greater_equal()); + }); - check_irreflexive(); + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::ranges::sort(v, std::greater_equal()); + }); - return 0; + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::ranges::stable_sort(v, std::greater_equal()); + }); + std::ranges::make_heap(v, std::greater_equal()); + EXPECT_LIBCPP_ASSERT_FAILURE("not a valid strict-weak ordering", [&] { + std::ranges::sort_heap(v, std::greater_equal()); + }); } + +TEST_TERMINATIONS([] { + check_oob_sort_read(); + + check_nan_floats(); + + check_irreflexive(); + + return 0; +}); diff --git a/libcxx/test/libcxx/algorithms/debug_less.inconsistent.pass.cpp b/libcxx/test/libcxx/algorithms/debug_less.inconsistent.pass.cpp --- a/libcxx/test/libcxx/algorithms/debug_less.inconsistent.pass.cpp +++ b/libcxx/test/libcxx/algorithms/debug_less.inconsistent.pass.cpp @@ -13,12 +13,13 @@ // Make sure __debug_less asserts when the comparator is not consistent. // REQUIRES: has-unix-headers -// UNSUPPORTED: !libcpp-has-debug-mode, c++03 +// REQUIRES: libcpp-has-debug-mode +// UNSUPPORTED: c++03 #include #include -#include "check_assertion.h" +#include "terminating_test_helper.h" template struct MyType { @@ -38,7 +39,7 @@ } }; -int main(int, char**) { +TEST_TERMINATIONS([] { typedef MyType<0> MT0; MT0 one(1); MT0 two(2); @@ -46,7 +47,7 @@ BadComparator c; std::__debug_less> d(c); - TEST_LIBCPP_ASSERT_FAILURE(d(one, two), "Comparator does not induce a strict weak ordering"); + EXPECT_LIBCPP_ASSERT_FAILURE("Comparator does not induce a strict weak ordering", [&] { d(one, two); }); return 0; -} +}); diff --git a/libcxx/test/libcxx/algorithms/debug_less.pass.cpp b/libcxx/test/libcxx/algorithms/debug_less.pass.cpp --- a/libcxx/test/libcxx/algorithms/debug_less.pass.cpp +++ b/libcxx/test/libcxx/algorithms/debug_less.pass.cpp @@ -21,7 +21,6 @@ #include #include "test_macros.h" -#include "check_assertion.h" template struct MyType { diff --git a/libcxx/test/libcxx/algorithms/debug_three_way_comp.inconsistent.pass.cpp b/libcxx/test/libcxx/algorithms/debug_three_way_comp.inconsistent.pass.cpp --- a/libcxx/test/libcxx/algorithms/debug_three_way_comp.inconsistent.pass.cpp +++ b/libcxx/test/libcxx/algorithms/debug_three_way_comp.inconsistent.pass.cpp @@ -12,12 +12,13 @@ // Make sure __debug_three_way_comp asserts when the comparator is not consistent. -// UNSUPPORTED: !libcpp-has-debug-mode, c++03, c++11, c++14, c++17 +// REQUIRES: libcpp-has-debug-mode +// UNSUPPORTED: c++03, c++11, c++14, c++17 #include #include -#include "check_assertion.h" +#include "terminating_test_helper.h" struct AlwaysLess { std::strong_ordering operator()(int, int) const { return std::strong_ordering::less; } @@ -35,21 +36,27 @@ } }; -int main(int, char**) { +TEST_TERMINATIONS([] { int zero = 0; int one = 1; - AlwaysLess alwaysLess; - std::__debug_three_way_comp debugAlwaysLess(alwaysLess); - TEST_LIBCPP_ASSERT_FAILURE(debugAlwaysLess(zero, one), "Comparator does not induce a strict weak ordering"); + EXPECT_LIBCPP_ASSERT_FAILURE("Comparator does not induce a strict weak ordering", [&] { + AlwaysLess alwaysLess; + std::__debug_three_way_comp debugAlwaysLess(alwaysLess); + debugAlwaysLess(zero, one); + }); - AlwaysGreater alwaysGreater; - std::__debug_three_way_comp debugAlwaysGreater(alwaysGreater); - TEST_LIBCPP_ASSERT_FAILURE(debugAlwaysGreater(zero, one), "Comparator does not induce a strict weak ordering"); + EXPECT_LIBCPP_ASSERT_FAILURE("Comparator does not induce a strict weak ordering", [&] { + AlwaysGreater alwaysGreater; + std::__debug_three_way_comp debugAlwaysGreater(alwaysGreater); + debugAlwaysGreater(zero, one); + }); - InconsistentEquals inconsistentEquals; - std::__debug_three_way_comp debugInconsistentEquals(inconsistentEquals); - TEST_LIBCPP_ASSERT_FAILURE(debugInconsistentEquals(zero, one), "Comparator does not induce a strict weak ordering"); + EXPECT_LIBCPP_ASSERT_FAILURE("Comparator does not induce a strict weak ordering", [&] { + InconsistentEquals inconsistentEquals; + std::__debug_three_way_comp debugInconsistentEquals(inconsistentEquals); + debugInconsistentEquals(zero, one); + }); return 0; -} +}); diff --git a/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.back.pass.cpp b/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.back.pass.cpp --- a/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.back.pass.cpp +++ b/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.back.pass.cpp @@ -15,23 +15,28 @@ #include -#include "check_assertion.h" - -int main(int, char**) { - { - typedef std::array C; - C c = {}; - C const& cc = c; - TEST_LIBCPP_ASSERT_FAILURE(c.back(), "cannot call array::back() on a zero-sized array"); - TEST_LIBCPP_ASSERT_FAILURE(cc.back(), "cannot call array::back() on a zero-sized array"); - } - { - typedef std::array C; - C c = {{}}; - C const& cc = c; - TEST_LIBCPP_ASSERT_FAILURE(c.back(), "cannot call array::back() on a zero-sized array"); - TEST_LIBCPP_ASSERT_FAILURE(cc.back(), "cannot call array::back() on a zero-sized array"); - } +#include "terminating_test_helper.h" + +TEST_TERMINATIONS([] { + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::back() on a zero-sized array", [] { + std::array c = {}; + c.back(); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::back() on a zero-sized array", [] { + const std::array cc = {}; + cc.back(); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::back() on a zero-sized array", [] { + std::array c = {}; + c.back(); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::back() on a zero-sized array", [] { + const std::array cc = {}; + cc.back(); + }); return 0; -} +}); diff --git a/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.front.pass.cpp b/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.front.pass.cpp --- a/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.front.pass.cpp +++ b/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.front.pass.cpp @@ -15,23 +15,28 @@ #include -#include "check_assertion.h" - -int main(int, char**) { - { - typedef std::array C; - C c = {}; - C const& cc = c; - TEST_LIBCPP_ASSERT_FAILURE(c.front(), "cannot call array::front() on a zero-sized array"); - TEST_LIBCPP_ASSERT_FAILURE(cc.front(), "cannot call array::front() on a zero-sized array"); - } - { - typedef std::array C; - C c = {{}}; - C const& cc = c; - TEST_LIBCPP_ASSERT_FAILURE(c.front(), "cannot call array::front() on a zero-sized array"); - TEST_LIBCPP_ASSERT_FAILURE(cc.front(), "cannot call array::front() on a zero-sized array"); - } +#include "terminating_test_helper.h" + +TEST_TERMINATIONS([] { + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::front() on a zero-sized array", [] { + std::array c = {}; + c.front(); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::front() on a zero-sized array", [] { + const std::array cc = {}; + cc.front(); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::front() on a zero-sized array", [] { + std::array c = {}; + c.front(); + }); + + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::front() on a zero-sized array", [] { + const std::array cc = {}; + cc.front(); + }); return 0; -} +}); diff --git a/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.subscript.pass.cpp b/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.subscript.pass.cpp --- a/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.subscript.pass.cpp +++ b/libcxx/test/libcxx/containers/sequences/array/array.zero/assert.subscript.pass.cpp @@ -15,26 +15,26 @@ #include -#include "check_assertion.h" +#include "terminating_test_helper.h" int main(int, char**) { { typedef std::array C; - C c = {}; + C c = {}; C const& cc = c; - TEST_LIBCPP_ASSERT_FAILURE(c[0], "cannot call array::operator[] on a zero-sized array"); - TEST_LIBCPP_ASSERT_FAILURE(c[1], "cannot call array::operator[] on a zero-sized array"); - TEST_LIBCPP_ASSERT_FAILURE(cc[0], "cannot call array::operator[] on a zero-sized array"); - TEST_LIBCPP_ASSERT_FAILURE(cc[1], "cannot call array::operator[] on a zero-sized array"); + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::operator[] on a zero-sized array", [&] { c[0]; }); + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::operator[] on a zero-sized array", [&] { c[1]; }); + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::operator[] on a zero-sized array", [&] { cc[0]; }); + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::operator[] on a zero-sized array", [&] { cc[1]; }); } { typedef std::array C; - C c = {{}}; + C c = {{}}; C const& cc = c; - TEST_LIBCPP_ASSERT_FAILURE(c[0], "cannot call array::operator[] on a zero-sized array"); - TEST_LIBCPP_ASSERT_FAILURE(c[1], "cannot call array::operator[] on a zero-sized array"); - TEST_LIBCPP_ASSERT_FAILURE(cc[0], "cannot call array::operator[] on a zero-sized array"); - TEST_LIBCPP_ASSERT_FAILURE(cc[1], "cannot call array::operator[] on a zero-sized array"); + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::operator[] on a zero-sized array", [&] { c[0]; }); + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::operator[] on a zero-sized array", [&] { c[1]; }); + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::operator[] on a zero-sized array", [&] { cc[0]; }); + EXPECT_LIBCPP_ASSERT_FAILURE("cannot call array::operator[] on a zero-sized array", [&] { cc[1]; }); } return 0; diff --git a/libcxx/test/support/terminating_test_helper.h b/libcxx/test/support/terminating_test_helper.h new file mode 100644 --- /dev/null +++ b/libcxx/test/support/terminating_test_helper.h @@ -0,0 +1,237 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_TERMINATING_TEST_HELPER_H +#define TEST_TERMINATING_TEST_HELPER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +#if __has_include() && __has_include() && __has_include() +# include +# include +# include +# define HAS_POSIX_SPAWN +extern char** environ; +#endif + +namespace terminate { + +enum class result : int { + success = 0, + std_terminate, + nondetermenistic_test_count, +#ifdef _LIBCPP_VERSION + libcpp_assert, +#endif +}; + +inline std::string to_string(result res) { + switch (res) { + case result::success: + return "unexpected success"; + case result::std_terminate: + return "std::terminate"; + case result::nondetermenistic_test_count: + return "nondeterministic test count"; +#ifdef _LIBCPP_VERSION + case result::libcpp_assert: + return "libc++ assertion"; +#endif + } + return "unknown"; +} + +struct process_output { + std::string std_out; + std::string std_err; +}; + +struct process_result { + result return_value; + process_output output; +}; + +#ifdef HAS_POSIX_SPAWN +inline process_output get_child_output(int stdout_descriptor, int stderr_descriptor) { + process_output data; + char buffer[256]; + + std::array polls = {{{stdout_descriptor, POLLIN, 0}, {stderr_descriptor, POLLIN, 0}}}; + std::array read_done = {false, false}; + + int rval; + while ((rval = poll(polls.data(), polls.size(), /*timeout=*/-1)) > 0) { + if (!read_done[0] && polls[0].revents & POLLIN) { + ssize_t bytes_read = read(stdout_descriptor, buffer, sizeof(buffer)); + if (bytes_read == 0) + read_done[0] = true; + data.std_out.append(buffer, bytes_read); + } else if (!read_done[1] && polls[1].revents & POLLIN) { + ssize_t bytes_read = read(stderr_descriptor, buffer, sizeof(buffer)); + if (bytes_read == 0) + read_done[1] = true; + data.std_err.append(buffer, bytes_read); + } else { + break; + } + } + + return data; +} + +inline process_result run_test_process(char* bin_path, size_t test_number) { + process_result res; + int stdout_pipe[2]; + int stderr_pipe[2]; + assert(pipe(stdout_pipe) == 0); + assert(pipe(stderr_pipe) == 0); + + posix_spawn_file_actions_t actions; + + // redirect child output to our pipes + assert(posix_spawn_file_actions_init(&actions) == 0); + assert(posix_spawn_file_actions_addclose(&actions, stdout_pipe[0]) == 0); + assert(posix_spawn_file_actions_addclose(&actions, stderr_pipe[0]) == 0); + assert(posix_spawn_file_actions_adddup2(&actions, stdout_pipe[1], 1) == 0); + assert(posix_spawn_file_actions_adddup2(&actions, stderr_pipe[1], 2) == 0); + assert(posix_spawn_file_actions_addclose(&actions, stdout_pipe[1]) == 0); + assert(posix_spawn_file_actions_addclose(&actions, stderr_pipe[1]) == 0); + + pid_t pid; + std::string test_number_str = std::to_string(test_number); + char* argv[] = {bin_path, test_number_str.data(), nullptr}; + assert(posix_spawn(&pid, bin_path, &actions, nullptr, argv, environ) == 0); + + assert(close(stdout_pipe[1]) == 0); + assert(close(stderr_pipe[1]) == 0); + + res.output = get_child_output(stdout_pipe[0], stderr_pipe[0]); + + assert(close(stdout_pipe[0]) == 0); + assert(close(stderr_pipe[0]) == 0); + + int status; + assert(waitpid(pid, &status, 0) != -1); + + assert(posix_spawn_file_actions_destroy(&actions) == 0); + + res.return_value = static_cast(WEXITSTATUS(status)); + + return res; +} +#else +# error "No known way to spawn a process on this platform" +#endif + +struct test_info { + std::string_view test_string; + std::function check_result; +}; + +static size_t current_test = 0; +static size_t test_to_run = 0; +static std::vector test_infos; + +[[noreturn]] inline void exit(result res) { std::exit(static_cast(res)); } + +[[noreturn]] inline void terminate_handler() { terminate::exit(result::std_terminate); } + +inline void run_tests(char* bin_path) { + for (size_t i = 0; i != current_test; ++i) { + auto result = terminate::run_test_process(bin_path, i); + if (!test_infos[i].check_result(result)) { + std::cerr << "Test #" << i << " ('" << test_infos[i].test_string << "') failed through " + << to_string(result.return_value) << ".\nstdout:\n" + << result.output.std_out << "\nstderr:\n" + << result.output.std_err << "\n"; + std::exit(1); + } + } +} + +inline bool result_is_expected_failure(process_result result) { + return result.return_value != result::success && result.return_value != result::nondetermenistic_test_count; +} + +inline bool result_is_success(process_result result) { return result.return_value == result::success; } + +inline bool result_is_std_terminate(process_result result) { return result.return_value == result::std_terminate; } + +#ifdef _LIBCPP_VERSION +inline bool result_is_libcpp_assert(const char* expected_string, process_result result) { + return result.return_value == result::libcpp_assert && result.output.std_err == expected_string; +} +#endif +} // namespace terminate + +#ifdef _LIBCPP_VERSION +void std::__libcpp_verbose_abort(char const* format, ...) { + // Extract information from the error message. This has to stay synchronized with + // how we format assertions in the library. + va_list list; + va_start(list, format); + [[maybe_unused]] const char* file = va_arg(list, const char*); + [[maybe_unused]] int line = va_arg(list, int); + [[maybe_unused]] const char* expression = va_arg(list, const char*); + [[maybe_unused]] const char* message = va_arg(list, const char*); + std::cerr << message; + va_end(list); + terminate::exit(terminate::result::libcpp_assert); +} +#endif + +#define TEST_TERMINATIONS(...) \ + int main(int argc, char** argv) { \ + if (argc == 1) { \ + ++::terminate::current_test; \ + __VA_ARGS__(); \ + --::terminate::current_test; \ + ::terminate::run_tests(argv[0]); \ + } else { \ + assert(argc == 2); \ + ::std::set_terminate(::terminate::terminate_handler); \ + ::terminate::test_to_run = ::std::stoull(argv[1]); \ + __VA_ARGS__(); \ + ::terminate::exit(::terminate::result::nondetermenistic_test_count); \ + } \ + \ + return 0; \ + } + +#define EXPECT_RESULT(result_check_function, ...) \ + do { \ + ::terminate::test_infos.push_back({TEST_STRINGIZE(__VA_ARGS__), result_check_function}); \ + if (::terminate::current_test++ == ::terminate::test_to_run) { \ + __VA_ARGS__(); \ + ::terminate::exit(::terminate::result::success); \ + } \ + } while (false) + +#define EXPECT_SUCCESS(...) EXPECT_RESULT(::terminate::result_is_success, __VA_ARGS__) +#define EXPECT_TERMINATE(...) EXPECT_RESULT(::terminate::result_is_expected_failure, __VA_ARGS__) +#define EXPECT_STD_TERMINATE(...) EXPECT_RESULT(::terminate::result_is_std_terminate, __VA_ARGS__) + +#ifdef _LIBCPP_VERSION +# define EXPECT_LIBCPP_ASSERT_FAILURE(expected_string, ...) \ + EXPECT_RESULT( \ + [](::terminate::process_result res) { return ::terminate::result_is_libcpp_assert(expected_string, res); }, \ + __VA_ARGS__) +#else +# define EXPECT_LIBCPP_ASSERT_FAILURE(...) +#endif + +#endif // TEST_TERMINATING_TEST_HELPER_H diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h --- a/libcxx/test/support/test_macros.h +++ b/libcxx/test/support/test_macros.h @@ -23,8 +23,8 @@ #include #endif -#define TEST_STRINGIZE_IMPL(x) #x -#define TEST_STRINGIZE(x) TEST_STRINGIZE_IMPL(x) +#define TEST_STRINGIZE_IMPL(...) #__VA_ARGS__ +#define TEST_STRINGIZE(...) TEST_STRINGIZE_IMPL(__VA_ARGS__) #define TEST_CONCAT1(X, Y) X##Y #define TEST_CONCAT(X, Y) TEST_CONCAT1(X, Y)