Skip to content

[libc++][test] Add exception tests for vector capacity operations #118141

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 13, 2025

Conversation

winner245
Copy link
Contributor

@winner245 winner245 commented Nov 29, 2024

As a follow-up to #117662, this PR provides a comprehensive set of exception tests for the following capacity-related functions in std::vector. Specifically, it includes tests for the following functions:

  • reserve(size_type)
  • resize(size_type) and resize(size_type, const_reference)
  • shrink_to_fit()

Previously, the exception safety tests for these functions were either missing or inadequate. We need a thorough coverage of exception tests to validate that these operations provide strong exception guarantees under various exceptional scenarios.

@winner245 winner245 force-pushed the capacity-exception-tests branch 5 times, most recently from a5c7be2 to 8119fc8 Compare December 2, 2024 04:53
Copy link

github-actions bot commented Dec 2, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@winner245 winner245 force-pushed the capacity-exception-tests branch 24 times, most recently from d673cc8 to 2595c3c Compare December 5, 2024 20:12
@winner245 winner245 force-pushed the capacity-exception-tests branch 13 times, most recently from 44f8a0b to 1950489 Compare December 7, 2024 19:51
@winner245 winner245 marked this pull request as ready for review December 7, 2024 21:58
@winner245 winner245 requested a review from a team as a code owner December 7, 2024 21:58
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Dec 7, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 7, 2024

@llvm/pr-subscribers-libcxx

Author: Peng Liu (winner245)

Changes

As a follow-up to #117662, this PR provides a comprehensive set of exception tests for the following capacity-related functions in std::vector. Specifically, it includes tests for the following functions:

  • reserve(size_type)
  • resize(size_type) and resize(size_type, const_reference)
  • shrink_to_fit()

Previously, the exception safety tests for these functions were either missing or inadequate. We need a thorough coverage of exception tests to validate that these operations provide strong exception guarantees under various exceptional scenarios.


Patch is 59.13 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/118141.diff

7 Files Affected:

  • (modified) libcxx/test/std/containers/sequences/vector/common.h (+133)
  • (modified) libcxx/test/std/containers/sequences/vector/vector.capacity/reserve.pass.cpp (+65-115)
  • (added) libcxx/test/std/containers/sequences/vector/vector.capacity/reserve_exceptions.pass.cpp (+314)
  • (added) libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp (+402)
  • (added) libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp (+232)
  • (added) libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit_exceptions.pass.cpp (+265)
  • (modified) libcxx/test/support/test_allocator.h (+47-37)
diff --git a/libcxx/test/std/containers/sequences/vector/common.h b/libcxx/test/std/containers/sequences/vector/common.h
index ff8147ef6b838b..fb8db972f1d23e 100644
--- a/libcxx/test/std/containers/sequences/vector/common.h
+++ b/libcxx/test/std/containers/sequences/vector/common.h
@@ -11,10 +11,15 @@
 
 #include <cassert>
 #include <cstddef>
+#include <cstdlib>
 #include <memory>
+#include <string>
 #include <type_traits>
+#include <utility>
+#include <vector>
 
 #include "count_new.h"
+#include "test_macros.h"
 
 struct throwing_t {
   int* throw_after_n_ = nullptr;
@@ -48,6 +53,95 @@ struct throwing_t {
   }
 };
 
+#if TEST_STD_VER >= 11
+
+template <typename T>
+struct move_only_throwing_t {
+  T data_;
+  int* throw_after_n_ = nullptr;
+  bool moved_from_    = false;
+
+  move_only_throwing_t() = default;
+
+  explicit move_only_throwing_t(const T& data, int& throw_after_n) : data_(data), throw_after_n_(&throw_after_n) {
+    if (throw_after_n == 0)
+      throw 1;
+    --throw_after_n;
+  }
+
+  explicit move_only_throwing_t(T&& data, int& throw_after_n) : data_(std::move(data)), throw_after_n_(&throw_after_n) {
+    if (throw_after_n == 0)
+      throw 1;
+    --throw_after_n;
+  }
+
+  move_only_throwing_t(const move_only_throwing_t&)            = delete;
+  move_only_throwing_t& operator=(const move_only_throwing_t&) = delete;
+
+  move_only_throwing_t(move_only_throwing_t&& rhs) : data_(std::move(rhs.data_)), throw_after_n_(rhs.throw_after_n_) {
+    rhs.throw_after_n_ = nullptr;
+    rhs.moved_from_    = true;
+    if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
+      throw 1;
+    --*throw_after_n_;
+  }
+
+  move_only_throwing_t& operator=(move_only_throwing_t&& rhs) {
+    if (this == &rhs)
+      return *this;
+    data_              = std::move(rhs.data_);
+    throw_after_n_     = rhs.throw_after_n_;
+    rhs.moved_from_    = true;
+    rhs.throw_after_n_ = nullptr;
+    if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
+      throw 1;
+    --*throw_after_n_;
+    return *this;
+  }
+
+  friend bool operator==(const move_only_throwing_t& lhs, const move_only_throwing_t& rhs) {
+    return lhs.data_ == rhs.data_;
+  }
+  friend bool operator!=(const move_only_throwing_t& lhs, const move_only_throwing_t& rhs) {
+    return lhs.data_ != rhs.data_;
+  }
+};
+
+#endif
+
+template <typename T>
+struct throwing_data {
+  T data_;
+  int* throw_after_n_ = nullptr;
+  throwing_data() { throw 0; }
+
+  throwing_data(const T& data, int& throw_after_n) : data_(data), throw_after_n_(&throw_after_n) {
+    if (throw_after_n == 0)
+      throw 0;
+    --throw_after_n;
+  }
+
+  throwing_data(const throwing_data& rhs) : data_(rhs.data_), throw_after_n_(rhs.throw_after_n_) {
+    if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
+      throw 1;
+    --*throw_after_n_;
+  }
+
+  throwing_data& operator=(const throwing_data& rhs) {
+    data_          = rhs.data_;
+    throw_after_n_ = rhs.throw_after_n_;
+    if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
+      throw 1;
+    --*throw_after_n_;
+    return *this;
+  }
+
+  friend bool operator==(const throwing_data& lhs, const throwing_data& rhs) {
+    return lhs.data_ == rhs.data_ && lhs.throw_after_n_ == rhs.throw_after_n_;
+  }
+  friend bool operator!=(const throwing_data& lhs, const throwing_data& rhs) { return !(lhs == rhs); }
+};
+
 template <class T>
 struct throwing_allocator {
   using value_type = T;
@@ -125,4 +219,43 @@ inline void check_new_delete_called() {
   assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called);
 }
 
+class Rnd {
+public:
+  static void initializeSeed(unsigned int seed = 12345) { std::srand(seed); }
+
+  static std::vector<int> getRandomIntegerInputs(std::size_t N) {
+    std::vector<int> v;
+    v.reserve(N);
+    for (std::size_t i = 0; i < N; ++i)
+      v.push_back(std::rand());
+    return v;
+  }
+
+  static std::vector<std::string> getRandomStringInputsWithLength(std::size_t N, std::size_t len) {
+    std::vector<std::string> v;
+    v.reserve(N);
+    for (std::size_t i = 0; i < N; ++i)
+      v.push_back(getRandomString(len));
+    return v;
+  }
+
+private:
+  static const char Letters[];
+  static const std::size_t LettersSize;
+
+  static std::string getRandomString(std::size_t len) {
+    std::string s;
+    s.reserve(len);
+    for (std::size_t i = 0; i < len; ++i)
+      s += Letters[std::rand() % LettersSize];
+    return s;
+  }
+};
+
+const char Rnd::Letters[] = {
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
+    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
+const std::size_t Rnd::LettersSize = sizeof(Rnd::Letters) / sizeof(Rnd::Letters[0]);
+
 #endif // TEST_STD_CONTAINERS_SEQUENCES_VECTOR_COMMON_H
diff --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve.pass.cpp
index b8548ad72d4376..38e969335e0adb 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve.pass.cpp
@@ -19,126 +19,76 @@
 #include "asan_testing.h"
 
 TEST_CONSTEXPR_CXX20 bool tests() {
-    {
-        std::vector<int> v;
-        v.reserve(10);
-        assert(v.capacity() >= 10);
-        assert(is_contiguous_container_asan_correct(v));
-    }
-    {
-        std::vector<int> v(100);
-        assert(v.capacity() == 100);
-        v.reserve(50);
-        assert(v.size() == 100);
-        assert(v.capacity() == 100);
-        v.reserve(150);
-        assert(v.size() == 100);
-        assert(v.capacity() == 150);
-        assert(is_contiguous_container_asan_correct(v));
-    }
-    {
-        // Add 1 for implementations that dynamically allocate a container proxy.
-        std::vector<int, limited_allocator<int, 250 + 1> > v(100);
-        assert(v.capacity() == 100);
-        v.reserve(50);
-        assert(v.size() == 100);
-        assert(v.capacity() == 100);
-        v.reserve(150);
-        assert(v.size() == 100);
-        assert(v.capacity() == 150);
-        assert(is_contiguous_container_asan_correct(v));
-    }
-#ifndef TEST_HAS_NO_EXCEPTIONS
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-        std::vector<int> v;
-        std::size_t sz = v.max_size() + 1;
-
-        try {
-            v.reserve(sz);
-            assert(false);
-        } catch (const std::length_error&) {
-            assert(v.size() == 0);
-            assert(v.capacity() == 0);
-        }
-    }
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-        std::vector<int> v(10, 42);
-        int* previous_data = v.data();
-        std::size_t previous_capacity = v.capacity();
-        std::size_t sz = v.max_size() + 1;
-
-        try {
-            v.reserve(sz);
-            assert(false);
-        } catch (std::length_error&) {
-            assert(v.size() == 10);
-            assert(v.capacity() == previous_capacity);
-            assert(v.data() == previous_data);
-
-            for (int i = 0; i < 10; ++i) {
-                assert(v[i] == 42);
-            }
-        }
-    }
-#endif
+  {
+    std::vector<int> v;
+    v.reserve(10);
+    assert(v.capacity() >= 10);
+    assert(is_contiguous_container_asan_correct(v));
+  }
+  {
+    std::vector<int> v(100);
+    assert(v.capacity() == 100);
+    v.reserve(50);
+    assert(v.size() == 100);
+    assert(v.capacity() == 100);
+    v.reserve(150);
+    assert(v.size() == 100);
+    assert(v.capacity() == 150);
+    assert(is_contiguous_container_asan_correct(v));
+  }
+  {
+    // Add 1 for implementations that dynamically allocate a container proxy.
+    std::vector<int, limited_allocator<int, 250 + 1> > v(100);
+    assert(v.capacity() == 100);
+    v.reserve(50);
+    assert(v.size() == 100);
+    assert(v.capacity() == 100);
+    v.reserve(150);
+    assert(v.size() == 100);
+    assert(v.capacity() == 150);
+    assert(is_contiguous_container_asan_correct(v));
+  }
 #if TEST_STD_VER >= 11
-    {
-        std::vector<int, min_allocator<int>> v;
-        v.reserve(10);
-        assert(v.capacity() >= 10);
-        assert(is_contiguous_container_asan_correct(v));
-    }
-    {
-        std::vector<int, min_allocator<int>> v(100);
-        assert(v.capacity() == 100);
-        v.reserve(50);
-        assert(v.size() == 100);
-        assert(v.capacity() == 100);
-        v.reserve(150);
-        assert(v.size() == 100);
-        assert(v.capacity() == 150);
-        assert(is_contiguous_container_asan_correct(v));
-    }
-    {
-      std::vector<int, safe_allocator<int>> v;
-      v.reserve(10);
-      assert(v.capacity() >= 10);
-      assert(is_contiguous_container_asan_correct(v));
-    }
-    {
-      std::vector<int, safe_allocator<int>> v(100);
-      assert(v.capacity() == 100);
-      v.reserve(50);
-      assert(v.size() == 100);
-      assert(v.capacity() == 100);
-      v.reserve(150);
-      assert(v.size() == 100);
-      assert(v.capacity() == 150);
-      assert(is_contiguous_container_asan_correct(v));
-    }
-#endif
-#ifndef TEST_HAS_NO_EXCEPTIONS
-    if (!TEST_IS_CONSTANT_EVALUATED) {
-        std::vector<int, limited_allocator<int, 100> > v;
-        v.reserve(50);
-        assert(v.capacity() == 50);
-        assert(is_contiguous_container_asan_correct(v));
-        try {
-            v.reserve(101);
-            assert(false);
-        } catch (const std::length_error&) {
-            // no-op
-        }
-        assert(v.capacity() == 50);
-        assert(is_contiguous_container_asan_correct(v));
-    }
+  {
+    std::vector<int, min_allocator<int>> v;
+    v.reserve(10);
+    assert(v.capacity() >= 10);
+    assert(is_contiguous_container_asan_correct(v));
+  }
+  {
+    std::vector<int, min_allocator<int>> v(100);
+    assert(v.capacity() == 100);
+    v.reserve(50);
+    assert(v.size() == 100);
+    assert(v.capacity() == 100);
+    v.reserve(150);
+    assert(v.size() == 100);
+    assert(v.capacity() == 150);
+    assert(is_contiguous_container_asan_correct(v));
+  }
+  {
+    std::vector<int, safe_allocator<int>> v;
+    v.reserve(10);
+    assert(v.capacity() >= 10);
+    assert(is_contiguous_container_asan_correct(v));
+  }
+  {
+    std::vector<int, safe_allocator<int>> v(100);
+    assert(v.capacity() == 100);
+    v.reserve(50);
+    assert(v.size() == 100);
+    assert(v.capacity() == 100);
+    v.reserve(150);
+    assert(v.size() == 100);
+    assert(v.capacity() == 150);
+    assert(is_contiguous_container_asan_correct(v));
+  }
 #endif
 
-    return true;
+  return true;
 }
 
-int main(int, char**)
-{
+int main(int, char**) {
   tests();
 
 #if TEST_STD_VER > 17
diff --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve_exceptions.pass.cpp
new file mode 100644
index 00000000000000..0f21746e80f626
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve_exceptions.pass.cpp
@@ -0,0 +1,314 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-exceptions
+
+// This test file validates that std::vector<T>::reserve provides a strong exception guarantee if T is
+// Cpp17MoveInsertible and no exception is thrown by the move constructor of T during the reserve call.
+// It also checks that if T's move constructor is not noexcept, reserve provides only a basic exception
+// guarantee.
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+#include "../common.h"
+#include "MoveOnly.h"
+#include "count_new.h"
+#include "increasing_allocator.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template <typename T, typename Alloc>
+void test_allocation_exception_for_strong_guarantee(
+    std::vector<T, Alloc>& v, const std::vector<T>& values, std::size_t new_cap) {
+  assert(v.size() == values.size());
+  T* old_data          = v.data();
+  std::size_t old_size = v.size();
+  std::size_t old_cap  = v.capacity();
+
+  try {
+    v.reserve(new_cap);
+  } catch (...) { // std::length_error, std::bad_alloc
+    assert(v.data() == old_data);
+    assert(v.size() == old_size);
+    assert(v.capacity() == old_cap);
+    for (std::size_t i = 0; i < v.size(); ++i)
+      assert(v[i] == values[i]);
+  }
+}
+
+template <typename T, typename Alloc>
+void test_copy_ctor_exception_for_strong_guarantee(std::vector<throwing_data<T>, Alloc>& v,
+                                                   const std::vector<T>& values) {
+  assert(v.empty() && !values.empty());
+  int throw_after = values.size() + values.size() / 2; // Trigger an exception halfway through reallocation
+  v.reserve(values.size());
+  for (std::size_t i = 0; i < values.size(); ++i)
+    v.emplace_back(values[i], throw_after);
+
+  throwing_data<T>* old_data = v.data();
+  std::size_t old_size       = v.size();
+  std::size_t old_cap        = v.capacity();
+  std::size_t new_cap        = 2 * old_cap;
+
+  try {
+    v.reserve(new_cap);
+  } catch (...) {
+    assert(v.data() == old_data);
+    assert(v.size() == old_size);
+    assert(v.capacity() == old_cap);
+    for (std::size_t i = 0; i < v.size(); ++i)
+      assert(v[i].data_ == values[i]);
+  }
+}
+
+#if TEST_STD_VER >= 11
+
+template <typename T, typename Alloc>
+void test_move_ctor_exception_for_basic_guarantee(std::vector<move_only_throwing_t<T>, Alloc>& v,
+                                                  const std::vector<T>& values) {
+  assert(v.empty() && !values.empty());
+  int throw_after = values.size() + values.size() / 2; // Trigger an exception halfway through reallocation
+  v.reserve(values.size());
+  for (std::size_t i = 0; i < values.size(); ++i)
+    v.emplace_back(values[i], throw_after);
+
+  try {
+    v.reserve(2 * v.capacity());
+  } catch (...) {
+    // After a failure during element-wise move, the vector elements are left in a valid but unspecified state.
+    for (std::size_t i = 0; i < v.size(); ++i) {
+      assert(((void)v[i],
+              (void)v[i].data_,
+              (void)v[i].throw_after_n_,
+              (void)(v[i] == v[i]),
+              v[i].moved_from_ || v[i].data_ == values[i]));
+    }
+  }
+}
+
+#endif
+
+// Check the strong exception guarantee during reallocation failures
+void test_allocation_exceptions() {
+  //
+  // Tests for std::length_error during reallocation failures
+  //
+  {
+    std::vector<int> v;
+    test_allocation_exception_for_strong_guarantee(v, std::vector<int>(), v.max_size() + 1);
+  }
+  check_new_delete_called();
+
+  {
+    int a[] = {1, 2, 3, 4, 5};
+    std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
+    std::vector<int> v(in.begin(), in.end());
+    test_allocation_exception_for_strong_guarantee(v, in, v.max_size() + 1);
+  }
+  check_new_delete_called();
+
+  {
+    int a[] = {1, 2, 3, 4, 5};
+    std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
+    std::vector<int, min_allocator<int> > v(in.begin(), in.end());
+    test_allocation_exception_for_strong_guarantee(v, in, v.max_size() + 1);
+  }
+  check_new_delete_called();
+
+  {
+    int a[] = {1, 2, 3, 4, 5};
+    std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
+    std::vector<int, safe_allocator<int> > v(in.begin(), in.end());
+    test_allocation_exception_for_strong_guarantee(v, in, v.max_size() + 1);
+  }
+  check_new_delete_called();
+
+  {
+    int a[] = {1, 2, 3, 4, 5};
+    std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
+    std::vector<int, test_allocator<int> > v(in.begin(), in.end());
+    test_allocation_exception_for_strong_guarantee(v, in, v.max_size() + 1);
+  }
+  check_new_delete_called();
+
+  {
+    std::vector<int> in(10, 42);
+    std::vector<int, limited_allocator<int, 100> > v(in.begin(), in.end());
+    test_allocation_exception_for_strong_guarantee(v, in, v.max_size() + 1);
+  }
+  check_new_delete_called();
+
+#if TEST_STD_VER >= 23
+  {
+    std::vector<int> in{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    std::vector<int, increasing_allocator<int>> v(in.begin(), in.end());
+    test_allocation_exception_for_strong_guarantee(v, in, v.max_size() + 1);
+  }
+  check_new_delete_called();
+#endif
+
+  //
+  // Tests for std::bad_alloc during reallocation failures
+  //
+  {
+    std::vector<int> in(10, 42);
+    std::vector<int, limited_allocator<int, 100> > v(in.begin(), in.end());
+    test_allocation_exception_for_strong_guarantee(v, in, 91);
+  }
+  check_new_delete_called();
+
+  {
+    std::vector<int> in(10, 42);
+    std::vector<int, limited_allocator<int, 100> > v(in.begin(), in.end());
+    v.reserve(30);
+    test_allocation_exception_for_strong_guarantee(v, in, 61);
+  }
+  check_new_delete_called();
+
+#if TEST_STD_VER >= 11
+  {
+    std::vector<MoveOnly> in(10);
+    std::vector<MoveOnly, limited_allocator<MoveOnly, 100> > v(10);
+    test_allocation_exception_for_strong_guarantee(v, in, 91);
+  }
+  check_new_delete_called();
+
+  {
+    std::vector<MoveOnly> in(10);
+    in.insert(in.cbegin() + 5, MoveOnly(42));
+    std::vector<MoveOnly, limited_allocator<MoveOnly, 100> > v(10);
+    v.reserve(30);
+    v.insert(v.cbegin() + 5, MoveOnly(42));
+    test_allocation_exception_for_strong_guarantee(v, in, 61);
+  }
+  check_new_delete_called();
+#endif
+
+  { // Practical example: Testing with 100 randomly generated integers.
+    auto in = Rnd::getRandomIntegerInputs(100);
+    std::vector<int, limited_allocator<int, 299> > v(in.begin(), in.end());
+    test_allocation_exception_for_strong_guarantee(v, in, 200);
+  }
+  check_new_delete_called();
+
+  { // Practical example: Testing with 100 randomly generated strings, each 256 characters long.
+    std::vector<std::string> in = Rnd::getRandomStringInputsWithLength(100, 256);
+    std::vector<std::string, limited_allocator<std::string, 299> > v(in.begin(), in.end());
+    test_allocation_exception_for_strong_guarantee(v, in, 200);
+  }
+  check_new_delete_called();
+}
+
+// Check the strong exception guarantee during copy-constructor failures
+void test_copy_ctor_exceptions() {
+  {
+    int a[] = {1, 2, 3, 4, 5};
+    std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
+    std::vector<throwing_data<int> > v;
+    test_copy_ctor_exception_for_strong_guarantee(v, in);
+  }
+  check_new_delete_called();
+
+  {
+    int a[] = {1, 2, 3, 4, 5};
+    std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
+    std::vector<throwing_data<int>, min_allocator<throwing_data<int> > > v;
+    test_copy_ctor_exception_for_strong_guarantee(v, in);
+  }
+  check_new_delete_called();
+
+  {
+    std::vector<int> in(10, 42);
+    std::vector<throwing_data<int>, safe_allocator<throwing_data<int> > > v;
+    test_copy_ctor_exception_for_strong_guarantee(v, in);
+  }
+  check_new_delete_called();
+
+  {
+    std::vector<int> in(10, 42);
+    std::vector<throwing_data<int>, test_allocator<throwing_data<int> > > v;
+    test_copy_ctor_exception_for_strong_guarantee(v, in);
+  }
+  check_new_delete_called();
+
+  {
+    std::vector<int> in(10, 42);
+    std::vector<throwing_data<int>, limited_allocator<throwing_data<int>, 100> > v;
+    test_copy_ctor_exception_for_strong_guarantee(v, in);
+  }
+  check_new_delete_called();
+
+#if TEST_STD_VER >= 23
+  {
+    std::vector<int> in{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    std::vector<throwing_data<int>, increasing_allocator<throwing_data<int>>> v;
+    test_copy_ctor_exception_for_strong_guarantee(v, in);
+  }
+  check_new_delete_called();
+#endif
+
+  { // Practical example: Testing with 100 randomly generated integers.
+    auto in = Rnd::getRandomIntegerInputs(100);
+    std::vector<throwing_data<int> > v;
+    test_copy_ctor_exception_for_strong_guarantee(v, in);
+  }
+  check_new_delete_called();
+
+  { // Practical example: Testing with 100 randomly generated strings, each 256 characters long.
+    std::vector<std::string> i...
[truncated]

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I had reviewed this earlier but forgot to press "send" on my comments.

@winner245 winner245 force-pushed the capacity-exception-tests branch 5 times, most recently from a94d839 to 7159e0d Compare December 18, 2024 02:50
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please note that I will be OOO from Dec 21st to Jan 6th, so you should expect no reviews from me until I'm back!

@winner245 winner245 force-pushed the capacity-exception-tests branch from 7159e0d to 2f6ad14 Compare December 19, 2024 20:38
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, this LGTM!

@ldionne ldionne merged commit 550d32f into llvm:main Jan 13, 2025
61 checks passed
@winner245 winner245 deleted the capacity-exception-tests branch January 13, 2025 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants