Skip to content

Commit 37797d3

Browse files
authored
[libc++][test] Fix and refactor exception tests for std::vector constructors (#117662)
The existing exceptions tests for `vector<T>` have several issues: some tests did not throw exceptions at all, making them not useful for exception-safety testing, and some tests did not throw exceptions at the intended points, failing to serve their expected purpose. This PR fixes those tests for vector's constructors. Morever, this PR extracted common classes and utilities into a separate header file, and renamed those classes using more descriptive names.
1 parent 952c515 commit 37797d3

File tree

2 files changed

+62
-113
lines changed

2 files changed

+62
-113
lines changed

libcxx/test/std/containers/sequences/vector/common.h

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,41 @@
1616

1717
#include "count_new.h"
1818

19+
struct throwing_t {
20+
int* throw_after_n_ = nullptr;
21+
throwing_t() { throw 0; }
22+
23+
throwing_t(int& throw_after_n) : throw_after_n_(&throw_after_n) {
24+
if (throw_after_n == 0)
25+
throw 0;
26+
--throw_after_n;
27+
}
28+
29+
throwing_t(const throwing_t& rhs) : throw_after_n_(rhs.throw_after_n_) {
30+
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
31+
throw 1;
32+
--*throw_after_n_;
33+
}
34+
35+
throwing_t& operator=(const throwing_t& rhs) {
36+
throw_after_n_ = rhs.throw_after_n_;
37+
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
38+
throw 1;
39+
--*throw_after_n_;
40+
return *this;
41+
}
42+
43+
friend bool operator==(const throwing_t& lhs, const throwing_t& rhs) {
44+
return lhs.throw_after_n_ == rhs.throw_after_n_;
45+
}
46+
friend bool operator!=(const throwing_t& lhs, const throwing_t& rhs) {
47+
return lhs.throw_after_n_ != rhs.throw_after_n_;
48+
}
49+
};
50+
1951
template <class T>
2052
struct throwing_allocator {
21-
using value_type = T;
22-
using is_always_equal = std::false_type;
53+
using value_type = T;
2354

2455
bool throw_on_copy_ = false;
2556

libcxx/test/std/containers/sequences/vector/vector.cons/exceptions.pass.cpp

Lines changed: 29 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -16,149 +16,66 @@
1616
#include <type_traits>
1717
#include <vector>
1818

19+
#include "../common.h"
1920
#include "count_new.h"
2021
#include "test_allocator.h"
2122
#include "test_iterators.h"
2223

23-
template <class T>
24-
struct Allocator {
25-
using value_type = T;
26-
using is_always_equal = std::false_type;
27-
28-
template <class U>
29-
Allocator(const Allocator<U>&) {}
30-
31-
Allocator(bool should_throw = true) {
32-
if (should_throw)
33-
throw 0;
34-
}
35-
36-
T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
37-
void deallocate(T* ptr, std::size_t n) { std::allocator<T>().deallocate(ptr, n); }
38-
39-
template <class U>
40-
friend bool operator==(const Allocator&, const Allocator<U>&) {
41-
return true;
42-
}
43-
};
44-
45-
struct ThrowingT {
46-
int* throw_after_n_ = nullptr;
47-
ThrowingT() { throw 0; }
48-
49-
ThrowingT(int& throw_after_n) : throw_after_n_(&throw_after_n) {
50-
if (throw_after_n == 0)
51-
throw 0;
52-
--throw_after_n;
53-
}
54-
55-
ThrowingT(const ThrowingT& rhs) : throw_after_n_(rhs.throw_after_n_) {
56-
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
57-
throw 1;
58-
--*throw_after_n_;
59-
}
60-
61-
ThrowingT& operator=(const ThrowingT& rhs) {
62-
throw_after_n_ = rhs.throw_after_n_;
63-
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
64-
throw 1;
65-
--*throw_after_n_;
66-
return *this;
67-
}
68-
};
69-
70-
template <class IterCat>
71-
struct Iterator {
72-
using iterator_category = IterCat;
73-
using difference_type = std::ptrdiff_t;
74-
using value_type = int;
75-
using reference = int&;
76-
using pointer = int*;
77-
78-
int i_;
79-
Iterator(int i = 0) : i_(i) {}
80-
int& operator*() {
81-
if (i_ == 1)
82-
throw 1;
83-
return i_;
84-
}
85-
86-
friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ == rhs.i_; }
87-
88-
friend bool operator!=(const Iterator& lhs, const Iterator& rhs) { return lhs.i_ != rhs.i_; }
89-
90-
Iterator& operator++() {
91-
++i_;
92-
return *this;
93-
}
94-
95-
Iterator operator++(int) {
96-
auto tmp = *this;
97-
++i_;
98-
return tmp;
99-
}
100-
};
101-
102-
void check_new_delete_called() {
103-
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
104-
assert(globalMemCounter.new_array_called == globalMemCounter.delete_array_called);
105-
assert(globalMemCounter.aligned_new_called == globalMemCounter.aligned_delete_called);
106-
assert(globalMemCounter.aligned_new_array_called == globalMemCounter.aligned_delete_array_called);
107-
}
108-
10924
int main(int, char**) {
110-
using AllocVec = std::vector<int, Allocator<int> >;
25+
using AllocVec = std::vector<int, throwing_allocator<int> >;
11126
try { // vector()
11227
AllocVec vec;
11328
} catch (int) {
11429
}
11530
check_new_delete_called();
11631

11732
try { // Throw in vector(size_type) from type
118-
std::vector<ThrowingT> get_alloc(1);
33+
std::vector<throwing_t> get_alloc(1);
11934
} catch (int) {
12035
}
12136
check_new_delete_called();
12237

12338
#if TEST_STD_VER >= 14
12439
try { // Throw in vector(size_type, value_type) from type
12540
int throw_after = 1;
126-
ThrowingT v(throw_after);
127-
std::vector<ThrowingT> get_alloc(1, v);
41+
throwing_t v(throw_after);
42+
std::vector<throwing_t> get_alloc(1, v);
12843
} catch (int) {
12944
}
13045
check_new_delete_called();
13146

13247
try { // Throw in vector(size_type, const allocator_type&) from allocator
133-
Allocator<int> alloc(false);
48+
throwing_allocator<int> alloc(/*throw_on_ctor = */ false, /*throw_on_copy = */ true);
13449
AllocVec get_alloc(0, alloc);
13550
} catch (int) {
13651
}
13752
check_new_delete_called();
13853

13954
try { // Throw in vector(size_type, const allocator_type&) from the type
140-
std::vector<ThrowingT> vec(1, std::allocator<ThrowingT>());
55+
std::vector<throwing_t> vec(1, std::allocator<throwing_t>());
14156
} catch (int) {
14257
}
14358
check_new_delete_called();
14459
#endif // TEST_STD_VER >= 14
14560

14661
try { // Throw in vector(size_type, value_type, const allocator_type&) from the type
14762
int throw_after = 1;
148-
ThrowingT v(throw_after);
149-
std::vector<ThrowingT> vec(1, v, std::allocator<ThrowingT>());
63+
throwing_t v(throw_after);
64+
std::vector<throwing_t> vec(1, v, std::allocator<throwing_t>());
15065
} catch (int) {
15166
}
15267
check_new_delete_called();
15368

15469
try { // Throw in vector(InputIterator, InputIterator) from input iterator
155-
std::vector<int> vec((Iterator<std::input_iterator_tag>()), Iterator<std::input_iterator_tag>(2));
70+
std::vector<int> vec(
71+
(throwing_iterator<int, std::input_iterator_tag>()), throwing_iterator<int, std::input_iterator_tag>(2));
15672
} catch (int) {
15773
}
15874
check_new_delete_called();
15975

16076
try { // Throw in vector(InputIterator, InputIterator) from forward iterator
161-
std::vector<int> vec((Iterator<std::forward_iterator_tag>()), Iterator<std::forward_iterator_tag>(2));
77+
std::vector<int> vec(
78+
(throwing_iterator<int, std::forward_iterator_tag>()), throwing_iterator<int, std::forward_iterator_tag>(2));
16279
} catch (int) {
16380
}
16481
check_new_delete_called();
@@ -172,75 +89,76 @@ int main(int, char**) {
17289

17390
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from input iterator
17491
std::allocator<int> alloc;
175-
std::vector<int> vec(Iterator<std::input_iterator_tag>(), Iterator<std::input_iterator_tag>(2), alloc);
92+
std::vector<int> vec(
93+
throwing_iterator<int, std::input_iterator_tag>(), throwing_iterator<int, std::input_iterator_tag>(2), alloc);
17694
} catch (int) {
17795
}
17896
check_new_delete_called();
17997

18098
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from forward iterator
18199
std::allocator<int> alloc;
182-
std::vector<int> vec(Iterator<std::forward_iterator_tag>(), Iterator<std::forward_iterator_tag>(2), alloc);
100+
std::vector<int> vec(throwing_iterator<int, std::forward_iterator_tag>(),
101+
throwing_iterator<int, std::forward_iterator_tag>(2),
102+
alloc);
183103
} catch (int) {
184104
}
185105
check_new_delete_called();
186106

187107
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
188108
int a[] = {1, 2};
189-
Allocator<int> alloc(false);
109+
throwing_allocator<int> alloc(/*throw_on_ctor = */ false, /*throw_on_copy = */ true);
190110
AllocVec vec(cpp17_input_iterator<int*>(a), cpp17_input_iterator<int*>(a + 2), alloc);
191111
} catch (int) {
192-
// FIXME: never called.
193112
}
194113
check_new_delete_called();
195114

196115
try { // Throw in vector(InputIterator, InputIterator, const allocator_type&) from allocator
197116
int a[] = {1, 2};
198-
Allocator<int> alloc(false);
117+
throwing_allocator<int> alloc(/*throw_on_ctor = */ false, /*throw_on_copy = */ true);
199118
AllocVec vec(forward_iterator<int*>(a), forward_iterator<int*>(a + 2), alloc);
200119
} catch (int) {
201-
// FIXME: never called.
202120
}
203121
check_new_delete_called();
204122

205123
try { // Throw in vector(const vector&) from type
206-
std::vector<ThrowingT> vec;
207-
int throw_after = 0;
124+
std::vector<throwing_t> vec;
125+
int throw_after = 1;
208126
vec.emplace_back(throw_after);
209127
auto vec2 = vec;
210128
} catch (int) {
211129
}
212130
check_new_delete_called();
213131

214132
try { // Throw in vector(const vector&, const allocator_type&) from type
215-
std::vector<ThrowingT> vec;
133+
std::vector<throwing_t> vec;
216134
int throw_after = 1;
217135
vec.emplace_back(throw_after);
218-
std::vector<ThrowingT> vec2(vec, std::allocator<int>());
136+
std::vector<throwing_t> vec2(vec, std::allocator<int>());
219137
} catch (int) {
220138
}
221139
check_new_delete_called();
222140

223141
try { // Throw in vector(vector&&, const allocator_type&) from type during element-wise move
224-
std::vector<ThrowingT, test_allocator<ThrowingT> > vec(test_allocator<ThrowingT>(1));
142+
std::vector<throwing_t, test_allocator<throwing_t> > vec(test_allocator<throwing_t>(1));
225143
int throw_after = 10;
226-
ThrowingT v(throw_after);
144+
throwing_t v(throw_after);
227145
vec.insert(vec.end(), 6, v);
228-
std::vector<ThrowingT, test_allocator<ThrowingT> > vec2(std::move(vec), test_allocator<ThrowingT>(2));
146+
std::vector<throwing_t, test_allocator<throwing_t> > vec2(std::move(vec), test_allocator<throwing_t>(2));
229147
} catch (int) {
230148
}
231149
check_new_delete_called();
232150

233151
#if TEST_STD_VER >= 11
234152
try { // Throw in vector(initializer_list<value_type>) from type
235153
int throw_after = 1;
236-
std::vector<ThrowingT> vec({ThrowingT(throw_after)});
154+
std::vector<throwing_t> vec({throwing_t(throw_after)});
237155
} catch (int) {
238156
}
239157
check_new_delete_called();
240158

241159
try { // Throw in vector(initializer_list<value_type>, const allocator_type&) constructor from type
242160
int throw_after = 1;
243-
std::vector<ThrowingT> vec({ThrowingT(throw_after)}, std::allocator<ThrowingT>());
161+
std::vector<throwing_t> vec({throwing_t(throw_after)}, std::allocator<throwing_t>());
244162
} catch (int) {
245163
}
246164
check_new_delete_called();

0 commit comments

Comments
 (0)