[libcxx-commits] [libcxx] [libc++][test] Fix and refactor exception tests for std::vector (PR #117662)

Peng Liu via libcxx-commits libcxx-commits at lists.llvm.org
Tue Nov 26 04:05:32 PST 2024


https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/117662

>From ff594e2c9fef34c6fb31a418d6378374cb9f98f0 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Mon, 25 Nov 2024 22:51:57 -0500
Subject: [PATCH] Fix and refactor exception tests for std::vector

---
 .../vector/vector.cons/exceptions.pass.cpp    | 146 +++++-------------
 libcxx/test/support/exception_test_helpers.h  | 119 ++++++++++++++
 2 files changed, 154 insertions(+), 111 deletions(-)
 create mode 100644 libcxx/test/support/exception_test_helpers.h

diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/exceptions.pass.cpp
index e2b0d691889c6c..82768947986a27 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/exceptions.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/exceptions.pass.cpp
@@ -17,94 +17,12 @@
 #include <vector>
 
 #include "count_new.h"
+#include "exception_test_helpers.h"
+#include "test_allocator.h"
 #include "test_iterators.h"
 
-template <class T>
-struct Allocator {
-  using value_type      = T;
-  using is_always_equal = std::false_type;
-
-  template <class U>
-  Allocator(const Allocator<U>&) {}
-
-  Allocator(bool should_throw = true) {
-    if (should_throw)
-      throw 0;
-  }
-
-  T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
-  void deallocate(T* ptr, std::size_t n) { std::allocator<T>().deallocate(ptr, n); }
-
-  template <class U>
-  friend bool operator==(const Allocator&, const Allocator<U>&) { return true; }
-};
-
-struct ThrowingT {
-  int* throw_after_n_ = nullptr;
-  ThrowingT() { throw 0; }
-
-  ThrowingT(int& throw_after_n) : throw_after_n_(&throw_after_n) {
-    if (throw_after_n == 0)
-      throw 0;
-    --throw_after_n;
-  }
-
-  ThrowingT(const ThrowingT& rhs) : throw_after_n_(rhs.throw_after_n_) {
-    if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
-      throw 1;
-    --*throw_after_n_;
-  }
-
-  ThrowingT& operator=(const ThrowingT& rhs) {
-    throw_after_n_ = rhs.throw_after_n_;
-    if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
-      throw 1;
-    --*throw_after_n_;
-    return *this;
-  }
-};
-
-template <class IterCat>
-struct Iterator {
-  using iterator_category = IterCat;
-  using difference_type   = std::ptrdiff_t;
-  using value_type        = int;
-  using reference         = int&;
-  using pointer           = int*;
-
-  int i_;
-  Iterator(int i = 0) : i_(i) {}
-  int& operator*() {
-    if (i_ == 1)
-      throw 1;
-    return i_;
-  }
-
-  friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ == rhs.i_; }
-
-  friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ != rhs.i_; }
-
-  Iterator& operator++() {
-    ++i_;
-    return *this;
-  }
-
-  Iterator operator++(int) {
-    auto tmp = *this;
-    ++i_;
-    return tmp;
-  }
-};
-
-void check_new_delete_called() {
-  assert(globalMemCounter.new_called == globalMemCounter.delete_called);
-  assert(globalMemCounter.new_array_called == globalMemCounter.delete_array_called);
-  assert(globalMemCounter.aligned_new_called == globalMemCounter.aligned_delete_called);
-  assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called);
-}
-
 int main(int, char**) {
-  using AllocVec = std::vector<int, Allocator<int> >;
+  using AllocVec = std::vector<int, throwing_allocator<int> >;
   try { // vector()
     AllocVec vec;
   } catch (int) {
@@ -112,7 +30,7 @@ int main(int, char**) {
   check_new_delete_called();
 
   try { // Throw in vector(size_type) from type
-    std::vector<ThrowingT> get_alloc(1);
+    std::vector<throwing_t> get_alloc(1);
   } catch (int) {
   }
   check_new_delete_called();
@@ -120,42 +38,44 @@ int main(int, char**) {
 #if TEST_STD_VER >= 14
   try { // Throw in vector(size_type, value_type) from type
     int throw_after = 1;
-    ThrowingT v(throw_after);
-    std::vector<ThrowingT> get_alloc(1, v);
+    throwing_t v(throw_after);
+    std::vector<throwing_t> get_alloc(1, v);
   } catch (int) {
   }
   check_new_delete_called();
 
-  try { // Throw in vector(size_type, const allocator_type&) from allocator
-    Allocator<int> alloc(false);
+  try {                                         // Throw in vector(size_type, const allocator_type&) from allocator
+    throwing_allocator<int> alloc(false, true); // throw on copy only
     AllocVec get_alloc(0, alloc);
   } catch (int) {
   }
   check_new_delete_called();
 
   try { // Throw in vector(size_type, const allocator_type&) from the type
-    std::vector<ThrowingT> vec(1, std::allocator<ThrowingT>());
+    std::vector<throwing_t> vec(1, std::allocator<throwing_t>());
   } catch (int) {
   }
   check_new_delete_called();
-#endif  // TEST_STD_VER >= 14
+#endif // TEST_STD_VER >= 14
 
   try { // Throw in vector(size_type, value_type, const allocator_type&) from the type
     int throw_after = 1;
-    ThrowingT v(throw_after);
-    std::vector<ThrowingT> vec(1, v, std::allocator<ThrowingT>());
+    throwing_t v(throw_after);
+    std::vector<throwing_t> vec(1, v, std::allocator<throwing_t>());
   } catch (int) {
   }
   check_new_delete_called();
 
   try { // Throw in vector(InputIterator, InputIterator) from input iterator
-    std::vector<int> vec((Iterator<std::input_iterator_tag>()), Iterator<std::input_iterator_tag>(2));
+    std::vector<int> vec(
+        (throwing_iterator<int, std::input_iterator_tag>()), throwing_iterator<int, std::input_iterator_tag>(2));
   } catch (int) {
   }
   check_new_delete_called();
 
   try { // Throw in vector(InputIterator, InputIterator) from forward iterator
-    std::vector<int> vec((Iterator<std::forward_iterator_tag>()), Iterator<std::forward_iterator_tag>(2));
+    std::vector<int> vec(
+        (throwing_iterator<int, std::forward_iterator_tag>()), throwing_iterator<int, std::forward_iterator_tag>(2));
   } catch (int) {
   }
   check_new_delete_called();
@@ -169,21 +89,24 @@ int main(int, char**) {
 
   try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from input iterator
     std::allocator<int> alloc;
-    std::vector<int> vec(Iterator<std::input_iterator_tag>(), Iterator<std::input_iterator_tag>(2), alloc);
+    std::vector<int> vec(
+        throwing_iterator<int, std::input_iterator_tag>(), throwing_iterator<int, std::input_iterator_tag>(2), alloc);
   } catch (int) {
   }
   check_new_delete_called();
 
   try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from forward iterator
     std::allocator<int> alloc;
-    std::vector<int> vec(Iterator<std::forward_iterator_tag>(), Iterator<std::forward_iterator_tag>(2), alloc);
+    std::vector<int> vec(throwing_iterator<int, std::forward_iterator_tag>(),
+                         throwing_iterator<int, std::forward_iterator_tag>(2),
+                         alloc);
   } catch (int) {
   }
   check_new_delete_called();
 
   try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
     int a[] = {1, 2};
-    Allocator<int> alloc(false);
+    throwing_allocator<int> alloc(false, true); // throw on copy only
     AllocVec vec(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 2), alloc);
   } catch (int) {
     // FIXME: never called.
@@ -192,7 +115,7 @@ int main(int, char**) {
 
   try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
     int a[] = {1, 2};
-    Allocator<int> alloc(false);
+    throwing_allocator<int> alloc(false, true); // throw on copy only
     AllocVec vec(forward_iterator<int*>(a), forward_iterator<int*>(a + 2), alloc);
   } catch (int) {
     // FIXME: never called.
@@ -200,8 +123,8 @@ int main(int, char**) {
   check_new_delete_called();
 
   try { // Throw in vector(const vector&) from type
-    std::vector<ThrowingT> vec;
-    int throw_after = 0;
+    std::vector<throwing_t> vec;
+    int throw_after = 1;
     vec.emplace_back(throw_after);
     auto vec2 = vec;
   } catch (int) {
@@ -209,19 +132,20 @@ int main(int, char**) {
   check_new_delete_called();
 
   try { // Throw in vector(const vector&, const allocator_type&) from type
-    std::vector<ThrowingT> vec;
+    std::vector<throwing_t> vec;
     int throw_after = 1;
     vec.emplace_back(throw_after);
-    std::vector<ThrowingT> vec2(vec, std::allocator<int>());
+    std::vector<throwing_t> vec2(vec, std::allocator<int>());
   } catch (int) {
   }
   check_new_delete_called();
 
-  try { // Throw in vector(vector&&, const allocator_type&) from type
-    std::vector<ThrowingT, Allocator<ThrowingT> > vec(Allocator<ThrowingT>(false));
-    int throw_after = 1;
-    vec.emplace_back(throw_after);
-    std::vector<ThrowingT, Allocator<ThrowingT> > vec2(std::move(vec), Allocator<ThrowingT>(false));
+  try { // Throw in vector(vector&&, const allocator_type&) from type during element-wise move
+    std::vector<throwing_t, test_allocator<throwing_t> > vec(test_allocator<throwing_t>(1));
+    int throw_after = 10;
+    throwing_t v(throw_after);
+    vec.insert(vec.end(), 6, v);
+    std::vector<throwing_t, test_allocator<throwing_t> > vec2(std::move(vec), test_allocator<throwing_t>(2));
   } catch (int) {
   }
   check_new_delete_called();
@@ -229,14 +153,14 @@ int main(int, char**) {
 #if TEST_STD_VER >= 11
   try { // Throw in vector(initializer_list<value_type>) from type
     int throw_after = 1;
-    std::vector<ThrowingT> vec({ThrowingT(throw_after)});
+    std::vector<throwing_t> vec({throwing_t(throw_after)});
   } catch (int) {
   }
   check_new_delete_called();
 
   try { // Throw in vector(initializer_list<value_type>, const allocator_type&) constructor from type
     int throw_after = 1;
-    std::vector<ThrowingT> vec({ThrowingT(throw_after)}, std::allocator<ThrowingT>());
+    std::vector<throwing_t> vec({throwing_t(throw_after)}, std::allocator<throwing_t>());
   } catch (int) {
   }
   check_new_delete_called();
diff --git a/libcxx/test/support/exception_test_helpers.h b/libcxx/test/support/exception_test_helpers.h
new file mode 100644
index 00000000000000..b7c2e49a5f7dba
--- /dev/null
+++ b/libcxx/test/support/exception_test_helpers.h
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 EXCEPTION_TEST_HELPERS_H
+#define EXCEPTION_TEST_HELPERS_H
+
+#include "count_new.h"
+
+struct throwing_t {
+  int* throw_after_n_ = nullptr;
+  throwing_t() { throw 0; }
+
+  throwing_t(int& throw_after_n) : throw_after_n_(&throw_after_n) {
+    if (throw_after_n == 0)
+      throw 0;
+    --throw_after_n;
+  }
+
+  throwing_t(const throwing_t& rhs) : throw_after_n_(rhs.throw_after_n_) {
+    if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
+      throw 1;
+    --*throw_after_n_;
+  }
+
+  throwing_t& operator=(const throwing_t& rhs) {
+    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_t& lhs, const throwing_t& rhs) {
+    return lhs.throw_after_n_ == rhs.throw_after_n_;
+  }
+  friend bool operator!=(const throwing_t& lhs, const throwing_t& rhs) {
+    return lhs.throw_after_n_ != rhs.throw_after_n_;
+  }
+};
+
+template <class T>
+struct throwing_allocator {
+  using value_type      = T;
+  using is_always_equal = std::false_type;
+
+  bool throw_on_copy_ = false;
+
+  throwing_allocator(bool throw_on_ctor = true, bool throw_on_copy = false) : throw_on_copy_(throw_on_copy) {
+    if (throw_on_ctor)
+      throw 0;
+  }
+
+  throwing_allocator(const throwing_allocator& rhs) : throw_on_copy_(rhs.throw_on_copy_) {
+    if (throw_on_copy_)
+      throw 0;
+  }
+
+  template <class U>
+  throwing_allocator(const throwing_allocator<U>& rhs) : throw_on_copy_(rhs.throw_on_copy_) {
+    if (throw_on_copy_)
+      throw 0;
+  }
+
+  T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
+  void deallocate(T* ptr, std::size_t n) { std::allocator<T>().deallocate(ptr, n); }
+
+  template <class U>
+  friend bool operator==(const throwing_allocator&, const throwing_allocator<U>&) {
+    return true;
+  }
+};
+
+template <class T, class IterCat>
+struct throwing_iterator {
+  using iterator_category = IterCat;
+  using difference_type   = std::ptrdiff_t;
+  using value_type        = T;
+  using reference         = T&;
+  using pointer           = T*;
+
+  int i_;
+  T v_;
+
+  throwing_iterator(int i = 0, const T& v = T()) : i_(i), v_(v) {}
+
+  reference operator*() {
+    if (i_ == 1)
+      throw 1;
+    return v_;
+  }
+
+  friend bool operator==(const throwing_iterator& lhs, const throwing_iterator& rhs) { return lhs.i_ == rhs.i_; }
+  friend bool operator!=(const throwing_iterator& lhs, const throwing_iterator& rhs) { return lhs.i_ != rhs.i_; }
+
+  throwing_iterator& operator++() {
+    ++i_;
+    return *this;
+  }
+
+  throwing_iterator operator++(int) {
+    auto tmp = *this;
+    ++i_;
+    return tmp;
+  }
+};
+
+inline void check_new_delete_called() {
+  assert(globalMemCounter.new_called == globalMemCounter.delete_called);
+  assert(globalMemCounter.new_array_called == globalMemCounter.delete_array_called);
+  assert(globalMemCounter.aligned_new_called == globalMemCounter.aligned_delete_called);
+  assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called);
+}
+
+#endif // EXCEPTION_TEST_HELPERS_H
\ No newline at end of file



More information about the libcxx-commits mailing list