Skip to content

Commit a5c7be2

Browse files
committed
Add exception tests for vector capacity operations
1 parent ae9b91a commit a5c7be2

File tree

5 files changed

+343
-115
lines changed

5 files changed

+343
-115
lines changed

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,52 @@
1616

1717
#include "count_new.h"
1818

19+
template <typename T>
20+
struct throwing_data {
21+
T data_;
22+
int* throw_after_n_ = nullptr;
23+
throwing_data() { throw 0; }
24+
25+
throwing_data(const T& data, int& throw_after_n) : data_(data), throw_after_n_(&throw_after_n) {
26+
if (throw_after_n == 0)
27+
throw 0;
28+
--throw_after_n;
29+
}
30+
31+
throwing_data(const throwing_data& rhs) : data_(rhs.data_), throw_after_n_(rhs.throw_after_n_) {
32+
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
33+
throw 1;
34+
--*throw_after_n_;
35+
}
36+
37+
throwing_data& operator=(const throwing_data& rhs) {
38+
data_ = rhs.data_;
39+
throw_after_n_ = rhs.throw_after_n_;
40+
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
41+
throw 1;
42+
--*throw_after_n_;
43+
return *this;
44+
}
45+
46+
throwing_data(throwing_data&& rhs) : data_(std::move(rhs.data_)), throw_after_n_(rhs.throw_after_n_) {
47+
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
48+
throw 1;
49+
--*throw_after_n_;
50+
}
51+
52+
throwing_data& operator=(throwing_data&& rhs) {
53+
if (this == &rhs)
54+
return *this;
55+
data_ = std::move(rhs.data_);
56+
throw_after_n_ = rhs.throw_after_n_;
57+
rhs.throw_after_n_ = nullptr;
58+
if (throw_after_n_ == nullptr || *throw_after_n_ == 0)
59+
throw 1;
60+
--*throw_after_n_;
61+
return *this;
62+
}
63+
};
64+
1965
template <class T>
2066
struct throwing_allocator {
2167
using value_type = T;

libcxx/test/std/containers/sequences/vector/vector.capacity/reserve.pass.cpp

Lines changed: 65 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -19,126 +19,76 @@
1919
#include "asan_testing.h"
2020

2121
TEST_CONSTEXPR_CXX20 bool tests() {
22-
{
23-
std::vector<int> v;
24-
v.reserve(10);
25-
assert(v.capacity() >= 10);
26-
assert(is_contiguous_container_asan_correct(v));
27-
}
28-
{
29-
std::vector<int> v(100);
30-
assert(v.capacity() == 100);
31-
v.reserve(50);
32-
assert(v.size() == 100);
33-
assert(v.capacity() == 100);
34-
v.reserve(150);
35-
assert(v.size() == 100);
36-
assert(v.capacity() == 150);
37-
assert(is_contiguous_container_asan_correct(v));
38-
}
39-
{
40-
// Add 1 for implementations that dynamically allocate a container proxy.
41-
std::vector<int, limited_allocator<int, 250 + 1> > v(100);
42-
assert(v.capacity() == 100);
43-
v.reserve(50);
44-
assert(v.size() == 100);
45-
assert(v.capacity() == 100);
46-
v.reserve(150);
47-
assert(v.size() == 100);
48-
assert(v.capacity() == 150);
49-
assert(is_contiguous_container_asan_correct(v));
50-
}
51-
#ifndef TEST_HAS_NO_EXCEPTIONS
52-
if (!TEST_IS_CONSTANT_EVALUATED) {
53-
std::vector<int> v;
54-
std::size_t sz = v.max_size() + 1;
55-
56-
try {
57-
v.reserve(sz);
58-
assert(false);
59-
} catch (const std::length_error&) {
60-
assert(v.size() == 0);
61-
assert(v.capacity() == 0);
62-
}
63-
}
64-
if (!TEST_IS_CONSTANT_EVALUATED) {
65-
std::vector<int> v(10, 42);
66-
int* previous_data = v.data();
67-
std::size_t previous_capacity = v.capacity();
68-
std::size_t sz = v.max_size() + 1;
69-
70-
try {
71-
v.reserve(sz);
72-
assert(false);
73-
} catch (std::length_error&) {
74-
assert(v.size() == 10);
75-
assert(v.capacity() == previous_capacity);
76-
assert(v.data() == previous_data);
77-
78-
for (int i = 0; i < 10; ++i) {
79-
assert(v[i] == 42);
80-
}
81-
}
82-
}
83-
#endif
22+
{
23+
std::vector<int> v;
24+
v.reserve(10);
25+
assert(v.capacity() >= 10);
26+
assert(is_contiguous_container_asan_correct(v));
27+
}
28+
{
29+
std::vector<int> v(100);
30+
assert(v.capacity() == 100);
31+
v.reserve(50);
32+
assert(v.size() == 100);
33+
assert(v.capacity() == 100);
34+
v.reserve(150);
35+
assert(v.size() == 100);
36+
assert(v.capacity() == 150);
37+
assert(is_contiguous_container_asan_correct(v));
38+
}
39+
{
40+
// Add 1 for implementations that dynamically allocate a container proxy.
41+
std::vector<int, limited_allocator<int, 250 + 1> > v(100);
42+
assert(v.capacity() == 100);
43+
v.reserve(50);
44+
assert(v.size() == 100);
45+
assert(v.capacity() == 100);
46+
v.reserve(150);
47+
assert(v.size() == 100);
48+
assert(v.capacity() == 150);
49+
assert(is_contiguous_container_asan_correct(v));
50+
}
8451
#if TEST_STD_VER >= 11
85-
{
86-
std::vector<int, min_allocator<int>> v;
87-
v.reserve(10);
88-
assert(v.capacity() >= 10);
89-
assert(is_contiguous_container_asan_correct(v));
90-
}
91-
{
92-
std::vector<int, min_allocator<int>> v(100);
93-
assert(v.capacity() == 100);
94-
v.reserve(50);
95-
assert(v.size() == 100);
96-
assert(v.capacity() == 100);
97-
v.reserve(150);
98-
assert(v.size() == 100);
99-
assert(v.capacity() == 150);
100-
assert(is_contiguous_container_asan_correct(v));
101-
}
102-
{
103-
std::vector<int, safe_allocator<int>> v;
104-
v.reserve(10);
105-
assert(v.capacity() >= 10);
106-
assert(is_contiguous_container_asan_correct(v));
107-
}
108-
{
109-
std::vector<int, safe_allocator<int>> v(100);
110-
assert(v.capacity() == 100);
111-
v.reserve(50);
112-
assert(v.size() == 100);
113-
assert(v.capacity() == 100);
114-
v.reserve(150);
115-
assert(v.size() == 100);
116-
assert(v.capacity() == 150);
117-
assert(is_contiguous_container_asan_correct(v));
118-
}
119-
#endif
120-
#ifndef TEST_HAS_NO_EXCEPTIONS
121-
if (!TEST_IS_CONSTANT_EVALUATED) {
122-
std::vector<int, limited_allocator<int, 100> > v;
123-
v.reserve(50);
124-
assert(v.capacity() == 50);
125-
assert(is_contiguous_container_asan_correct(v));
126-
try {
127-
v.reserve(101);
128-
assert(false);
129-
} catch (const std::length_error&) {
130-
// no-op
131-
}
132-
assert(v.capacity() == 50);
133-
assert(is_contiguous_container_asan_correct(v));
134-
}
52+
{
53+
std::vector<int, min_allocator<int>> v;
54+
v.reserve(10);
55+
assert(v.capacity() >= 10);
56+
assert(is_contiguous_container_asan_correct(v));
57+
}
58+
{
59+
std::vector<int, min_allocator<int>> v(100);
60+
assert(v.capacity() == 100);
61+
v.reserve(50);
62+
assert(v.size() == 100);
63+
assert(v.capacity() == 100);
64+
v.reserve(150);
65+
assert(v.size() == 100);
66+
assert(v.capacity() == 150);
67+
assert(is_contiguous_container_asan_correct(v));
68+
}
69+
{
70+
std::vector<int, safe_allocator<int>> v;
71+
v.reserve(10);
72+
assert(v.capacity() >= 10);
73+
assert(is_contiguous_container_asan_correct(v));
74+
}
75+
{
76+
std::vector<int, safe_allocator<int>> v(100);
77+
assert(v.capacity() == 100);
78+
v.reserve(50);
79+
assert(v.size() == 100);
80+
assert(v.capacity() == 100);
81+
v.reserve(150);
82+
assert(v.size() == 100);
83+
assert(v.capacity() == 150);
84+
assert(is_contiguous_container_asan_correct(v));
85+
}
13586
#endif
13687

137-
return true;
88+
return true;
13889
}
13990

140-
int main(int, char**)
141-
{
91+
int main(int, char**) {
14292
tests();
14393

14494
#if TEST_STD_VER > 17
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: no-exceptions
10+
11+
// Check that std::vector::reserve provides strong exception guarantees
12+
13+
#include <cstddef>
14+
#include <memory>
15+
#include <stdexcept>
16+
#include <type_traits>
17+
#include <vector>
18+
19+
#include "../common.h"
20+
#include "count_new.h"
21+
#include "min_allocator.h"
22+
#include "test_allocator.h"
23+
#include "test_iterators.h"
24+
25+
template <typename T = int, typename Alloc = std::allocator<T> >
26+
void test_allocation_exception(std::vector<T, Alloc>& v) {
27+
std::vector<T, Alloc> old_vector = v;
28+
T* old_data = v.data();
29+
std::size_t old_size = v.size();
30+
std::size_t old_cap = v.capacity();
31+
std::size_t new_cap = v.max_size() + 1;
32+
try {
33+
v.reserve(new_cap);
34+
} catch (std::length_error&) {
35+
assert(v.size() == old_size);
36+
assert(v.capacity() == old_cap);
37+
assert(v.data() == old_data);
38+
assert(v == old_vector);
39+
}
40+
}
41+
42+
template <typename T = int, typename Alloc = std::allocator<throwing_data<T> > >
43+
void test_construction_exception(std::vector<throwing_data<T>, Alloc>& v, const std::vector<T>& in) {
44+
assert(v.empty() && !in.empty());
45+
int throw_after = 2 * in.size() - 1;
46+
v.reserve(in.size());
47+
for (std::size_t i = 0; i < in.size(); ++i)
48+
v.emplace_back(in[i], throw_after);
49+
50+
throwing_data<T>* old_data = v.data();
51+
std::size_t old_size = v.size();
52+
std::size_t old_cap = v.capacity();
53+
std::size_t new_cap = 2 * old_cap;
54+
55+
try {
56+
v.reserve(new_cap);
57+
} catch (int) {
58+
assert(v.size() == old_size);
59+
assert(v.capacity() == old_cap);
60+
assert(v.data() == old_data);
61+
for (std::size_t i = 0; i < in.size(); ++i)
62+
assert(v[i].data_ == in[i]);
63+
}
64+
}
65+
66+
void test_allocation_exceptions() {
67+
{
68+
std::vector<int> v;
69+
test_allocation_exception(v);
70+
}
71+
check_new_delete_called();
72+
73+
{
74+
std::vector<int> v(10, 42);
75+
test_allocation_exception(v);
76+
}
77+
check_new_delete_called();
78+
79+
{
80+
std::vector<int, min_allocator<int> > v(10, 42);
81+
test_allocation_exception(v);
82+
}
83+
check_new_delete_called();
84+
85+
{
86+
std::vector<int, safe_allocator<int> > v(10, 42);
87+
test_allocation_exception(v);
88+
}
89+
check_new_delete_called();
90+
91+
{
92+
std::vector<int, test_allocator<int> > v(10, 42);
93+
test_allocation_exception(v);
94+
}
95+
check_new_delete_called();
96+
97+
{
98+
std::vector<int, limited_allocator<int, 100> > v(10, 42);
99+
test_allocation_exception(v);
100+
}
101+
check_new_delete_called();
102+
}
103+
104+
void test_construction_exceptions() {
105+
{
106+
std::vector<throwing_data<int> > v;
107+
int a[] = {1, 2, 3, 4, 5};
108+
std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
109+
test_construction_exception(v, in);
110+
}
111+
check_new_delete_called();
112+
113+
{
114+
std::vector<throwing_data<int>, min_allocator<throwing_data<int> > > v;
115+
int a[] = {1, 2, 3, 4, 5};
116+
std::vector<int> in(a, a + sizeof(a) / sizeof(a[0]));
117+
test_construction_exception(v, in);
118+
}
119+
check_new_delete_called();
120+
121+
{
122+
std::vector<throwing_data<int>, safe_allocator<throwing_data<int> > > v;
123+
std::vector<int> in(10, 42);
124+
test_construction_exception(v, in);
125+
}
126+
check_new_delete_called();
127+
128+
{
129+
std::vector<throwing_data<int>, test_allocator<throwing_data<int> > > v;
130+
std::vector<int> in(10, 42);
131+
test_construction_exception(v, in);
132+
}
133+
check_new_delete_called();
134+
135+
{
136+
std::vector<throwing_data<int>, limited_allocator<throwing_data<int>, 100> > v;
137+
std::vector<int> in(10, 42);
138+
test_construction_exception(v, in);
139+
}
140+
check_new_delete_called();
141+
}
142+
143+
int main(int, char**) {
144+
test_allocation_exceptions();
145+
test_construction_exceptions();
146+
}

0 commit comments

Comments
 (0)