| // Copyright 2017 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/lib/containers/cpp/circular_deque.h" | |
| #include <gtest/gtest.h> | |
| #include "src/lib/containers/cpp/test_objects.h" | |
| using containers::internal::VectorBuffer; | |
| namespace containers { | |
| namespace { | |
| circular_deque<int> MakeSequence(size_t max) { | |
| circular_deque<int> ret; | |
| for (size_t i = 0; i < max; i++) | |
| ret.push_back(i); | |
| return ret; | |
| } | |
| // Cycles through the queue, popping items from the back and pushing items | |
| // at the front to validate behavior across different configurations of the | |
| // queue in relation to the underlying buffer. The tester closure is run for | |
| // each cycle. | |
| template <class QueueT, class Tester> | |
| void CycleTest(circular_deque<QueueT>& queue, const Tester& tester) { | |
| size_t steps = queue.size() * 2; | |
| for (size_t i = 0; i < steps; i++) { | |
| tester(queue, i); | |
| queue.pop_back(); | |
| queue.push_front(QueueT()); | |
| } | |
| } | |
| class DestructorCounter { | |
| public: | |
| DestructorCounter(int* counter) : counter_(counter) {} | |
| ~DestructorCounter() { ++(*counter_); } | |
| private: | |
| int* counter_; | |
| }; | |
| } // namespace | |
| TEST(CircularDeque, FillConstructor) { | |
| constexpr size_t num_elts = 9; | |
| std::vector<int> foo(15); | |
| EXPECT_EQ(15u, foo.size()); | |
| // Fill with default constructor. | |
| { | |
| circular_deque<int> buf(num_elts); | |
| EXPECT_EQ(num_elts, buf.size()); | |
| EXPECT_EQ(num_elts, static_cast<size_t>(buf.end() - buf.begin())); | |
| for (size_t i = 0; i < num_elts; i++) | |
| EXPECT_EQ(0, buf[i]); | |
| } | |
| // Fill with explicit value. | |
| { | |
| int value = 199; | |
| circular_deque<int> buf(num_elts, value); | |
| EXPECT_EQ(num_elts, buf.size()); | |
| EXPECT_EQ(num_elts, static_cast<size_t>(buf.end() - buf.begin())); | |
| for (size_t i = 0; i < num_elts; i++) | |
| EXPECT_EQ(value, buf[i]); | |
| } | |
| } | |
| TEST(CircularDeque, CopyAndRangeConstructor) { | |
| int values[] = {1, 2, 3, 4, 5, 6}; | |
| circular_deque<CopyOnlyInt> first(std::begin(values), std::end(values)); | |
| circular_deque<CopyOnlyInt> second(first); | |
| EXPECT_EQ(6u, second.size()); | |
| for (int i = 0; i < 6; i++) | |
| EXPECT_EQ(i + 1, second[i].data()); | |
| } | |
| TEST(CircularDeque, MoveConstructor) { | |
| int values[] = {1, 2, 3, 4, 5, 6}; | |
| circular_deque<MoveOnlyInt> first(std::begin(values), std::end(values)); | |
| circular_deque<MoveOnlyInt> second(std::move(first)); | |
| EXPECT_TRUE(first.empty()); | |
| EXPECT_EQ(6u, second.size()); | |
| for (int i = 0; i < 6; i++) | |
| EXPECT_EQ(i + 1, second[i].data()); | |
| } | |
| TEST(CircularDeque, InitializerListConstructor) { | |
| circular_deque<int> empty({}); | |
| ASSERT_TRUE(empty.empty()); | |
| circular_deque<int> first({1, 2, 3, 4, 5, 6}); | |
| EXPECT_EQ(6u, first.size()); | |
| for (int i = 0; i < 6; i++) | |
| EXPECT_EQ(i + 1, first[i]); | |
| } | |
| TEST(CircularDeque, Destructor) { | |
| int destruct_count = 0; | |
| // Contiguous buffer. | |
| { | |
| circular_deque<DestructorCounter> q; | |
| q.resize(5, DestructorCounter(&destruct_count)); | |
| EXPECT_EQ(1, destruct_count); // The temporary in the call to resize(). | |
| destruct_count = 0; | |
| } | |
| EXPECT_EQ(5, destruct_count); // One call for each. | |
| // Force a wraparound buffer. | |
| { | |
| circular_deque<DestructorCounter> q; | |
| q.reserve(7); | |
| q.resize(5, DestructorCounter(&destruct_count)); | |
| // Cycle throught some elements in our buffer to force a wraparound. | |
| destruct_count = 0; | |
| for (int i = 0; i < 4; i++) { | |
| q.emplace_back(&destruct_count); | |
| q.pop_front(); | |
| } | |
| EXPECT_EQ(4, destruct_count); // One for each cycle. | |
| destruct_count = 0; | |
| } | |
| EXPECT_EQ(5, destruct_count); // One call for each. | |
| } | |
| TEST(CircularDeque, EqualsCopy) { | |
| circular_deque<int> first = {1, 2, 3, 4, 5, 6}; | |
| circular_deque<int> copy; | |
| EXPECT_TRUE(copy.empty()); | |
| copy = first; | |
| EXPECT_EQ(6u, copy.size()); | |
| for (int i = 0; i < 6; i++) { | |
| EXPECT_EQ(i + 1, first[i]); | |
| EXPECT_EQ(i + 1, copy[i]); | |
| EXPECT_NE(&first[i], ©[i]); | |
| } | |
| } | |
| TEST(CircularDeque, EqualsMove) { | |
| circular_deque<int> first = {1, 2, 3, 4, 5, 6}; | |
| circular_deque<int> move; | |
| EXPECT_TRUE(move.empty()); | |
| move = std::move(first); | |
| EXPECT_TRUE(first.empty()); | |
| EXPECT_EQ(6u, move.size()); | |
| for (int i = 0; i < 6; i++) | |
| EXPECT_EQ(i + 1, move[i]); | |
| } | |
| // Tests that self-assignment is a no-op. | |
| TEST(CircularDeque, EqualsSelf) { | |
| circular_deque<int> q = {1, 2, 3, 4, 5, 6}; | |
| q = *&q; // The *& defeats Clang's -Wself-assign warning. | |
| EXPECT_EQ(6u, q.size()); | |
| for (int i = 0; i < 6; i++) | |
| EXPECT_EQ(i + 1, q[i]); | |
| } | |
| TEST(CircularDeque, EqualsInitializerList) { | |
| circular_deque<int> q; | |
| EXPECT_TRUE(q.empty()); | |
| q = {1, 2, 3, 4, 5, 6}; | |
| EXPECT_EQ(6u, q.size()); | |
| for (int i = 0; i < 6; i++) | |
| EXPECT_EQ(i + 1, q[i]); | |
| } | |
| TEST(CircularDeque, AssignCountValue) { | |
| circular_deque<int> empty; | |
| empty.assign(0, 52); | |
| EXPECT_EQ(0u, empty.size()); | |
| circular_deque<int> full; | |
| size_t count = 13; | |
| int value = 12345; | |
| full.assign(count, value); | |
| EXPECT_EQ(count, full.size()); | |
| for (size_t i = 0; i < count; i++) | |
| EXPECT_EQ(value, full[i]); | |
| } | |
| TEST(CircularDeque, AssignIterator) { | |
| int range[8] = {11, 12, 13, 14, 15, 16, 17, 18}; | |
| circular_deque<int> empty; | |
| empty.assign(std::begin(range), std::begin(range)); | |
| EXPECT_TRUE(empty.empty()); | |
| circular_deque<int> full; | |
| full.assign(std::begin(range), std::end(range)); | |
| EXPECT_EQ(8u, full.size()); | |
| for (size_t i = 0; i < 8; i++) | |
| EXPECT_EQ(range[i], full[i]); | |
| } | |
| TEST(CircularDeque, AssignInitializerList) { | |
| circular_deque<int> empty; | |
| empty.assign({}); | |
| EXPECT_TRUE(empty.empty()); | |
| circular_deque<int> full; | |
| full.assign({11, 12, 13, 14, 15, 16, 17, 18}); | |
| EXPECT_EQ(8u, full.size()); | |
| for (int i = 0; i < 8; i++) | |
| EXPECT_EQ(11 + i, full[i]); | |
| } | |
| // Tests [] and .at(). | |
| TEST(CircularDeque, At) { | |
| circular_deque<int> q = MakeSequence(10); | |
| CycleTest(q, [](const circular_deque<int>& q, size_t cycle) { | |
| size_t expected_size = 10; | |
| EXPECT_EQ(expected_size, q.size()); | |
| // A sequence of 0's. | |
| size_t index = 0; | |
| size_t num_zeros = std::min(expected_size, cycle); | |
| for (size_t i = 0; i < num_zeros; i++, index++) { | |
| EXPECT_EQ(0, q[index]); | |
| EXPECT_EQ(0, q.at(index)); | |
| } | |
| // Followed by a sequence of increasing ints. | |
| size_t num_ints = expected_size - num_zeros; | |
| for (int i = 0; i < static_cast<int>(num_ints); i++, index++) { | |
| EXPECT_EQ(i, q[index]); | |
| EXPECT_EQ(i, q.at(index)); | |
| } | |
| }); | |
| } | |
| // This also tests the copy constructor with lots of different types of | |
| // input configurations. | |
| TEST(CircularDeque, FrontBackPushPop) { | |
| circular_deque<int> q = MakeSequence(10); | |
| int expected_front = 0; | |
| int expected_back = 9; | |
| // Go in one direction. | |
| for (int i = 0; i < 100; i++) { | |
| const circular_deque<int> const_q(q); | |
| EXPECT_EQ(expected_front, q.front()); | |
| EXPECT_EQ(expected_back, q.back()); | |
| EXPECT_EQ(expected_front, const_q.front()); | |
| EXPECT_EQ(expected_back, const_q.back()); | |
| expected_front++; | |
| expected_back++; | |
| q.pop_front(); | |
| q.push_back(expected_back); | |
| } | |
| // Go back in reverse. | |
| for (int i = 0; i < 100; i++) { | |
| const circular_deque<int> const_q(q); | |
| EXPECT_EQ(expected_front, q.front()); | |
| EXPECT_EQ(expected_back, q.back()); | |
| EXPECT_EQ(expected_front, const_q.front()); | |
| EXPECT_EQ(expected_back, const_q.back()); | |
| expected_front--; | |
| expected_back--; | |
| q.pop_back(); | |
| q.push_front(expected_front); | |
| } | |
| } | |
| TEST(CircularDeque, ReallocateWithSplitBuffer) { | |
| // Tests reallocating a deque with an internal buffer that looks like this: | |
| // 4 5 x x 0 1 2 3 | |
| // end-^ ^-begin | |
| circular_deque<int> q; | |
| q.reserve(7); // Internal buffer is always 1 larger than requested. | |
| q.push_back(-1); | |
| q.push_back(-1); | |
| q.push_back(-1); | |
| q.push_back(-1); | |
| q.push_back(0); | |
| q.pop_front(); | |
| q.pop_front(); | |
| q.pop_front(); | |
| q.pop_front(); | |
| q.push_back(1); | |
| q.push_back(2); | |
| q.push_back(3); | |
| q.push_back(4); | |
| q.push_back(5); | |
| q.shrink_to_fit(); | |
| EXPECT_EQ(6u, q.size()); | |
| EXPECT_EQ(0, q[0]); | |
| EXPECT_EQ(1, q[1]); | |
| EXPECT_EQ(2, q[2]); | |
| EXPECT_EQ(3, q[3]); | |
| EXPECT_EQ(4, q[4]); | |
| EXPECT_EQ(5, q[5]); | |
| } | |
| TEST(CircularDeque, Swap) { | |
| circular_deque<int> a = MakeSequence(10); | |
| circular_deque<int> b = MakeSequence(100); | |
| a.swap(b); | |
| EXPECT_EQ(100u, a.size()); | |
| for (int i = 0; i < 100; i++) | |
| EXPECT_EQ(i, a[i]); | |
| EXPECT_EQ(10u, b.size()); | |
| for (int i = 0; i < 10; i++) | |
| EXPECT_EQ(i, b[i]); | |
| } | |
| TEST(CircularDeque, Iteration) { | |
| circular_deque<int> q = MakeSequence(10); | |
| int expected_front = 0; | |
| int expected_back = 9; | |
| // This loop causes various combinations of begin and end to be tested. | |
| for (int i = 0; i < 30; i++) { | |
| // Range-containersd for loop going forward. | |
| int current_expected = expected_front; | |
| for (int cur : q) { | |
| EXPECT_EQ(current_expected, cur); | |
| current_expected++; | |
| } | |
| // Manually test reverse iterators. | |
| current_expected = expected_back; | |
| for (auto cur = q.crbegin(); cur < q.crend(); cur++) { | |
| EXPECT_EQ(current_expected, *cur); | |
| current_expected--; | |
| } | |
| expected_front++; | |
| expected_back++; | |
| q.pop_front(); | |
| q.push_back(expected_back); | |
| } | |
| // Go back in reverse. | |
| for (int i = 0; i < 100; i++) { | |
| const circular_deque<int> const_q(q); | |
| EXPECT_EQ(expected_front, q.front()); | |
| EXPECT_EQ(expected_back, q.back()); | |
| EXPECT_EQ(expected_front, const_q.front()); | |
| EXPECT_EQ(expected_back, const_q.back()); | |
| expected_front--; | |
| expected_back--; | |
| q.pop_back(); | |
| q.push_front(expected_front); | |
| } | |
| } | |
| TEST(CircularDeque, IteratorComparisons) { | |
| circular_deque<int> q = MakeSequence(10); | |
| // This loop causes various combinations of begin and end to be tested. | |
| for (int i = 0; i < 30; i++) { | |
| EXPECT_LT(q.begin(), q.end()); | |
| EXPECT_LE(q.begin(), q.end()); | |
| EXPECT_LE(q.begin(), q.begin()); | |
| EXPECT_GT(q.end(), q.begin()); | |
| EXPECT_GE(q.end(), q.begin()); | |
| EXPECT_GE(q.end(), q.end()); | |
| EXPECT_EQ(q.begin(), q.begin()); | |
| EXPECT_NE(q.begin(), q.end()); | |
| q.push_front(10); | |
| q.pop_back(); | |
| } | |
| } | |
| TEST(CircularDeque, IteratorIncDec) { | |
| circular_deque<int> q; | |
| // No-op offset computations with no capacity. | |
| EXPECT_EQ(q.end(), q.end() + 0); | |
| EXPECT_EQ(q.end(), q.end() - 0); | |
| q = MakeSequence(10); | |
| // Mutable preincrement, predecrement. | |
| { | |
| circular_deque<int>::iterator it = q.begin(); | |
| circular_deque<int>::iterator op_result = ++it; | |
| EXPECT_EQ(1, *op_result); | |
| EXPECT_EQ(1, *it); | |
| op_result = --it; | |
| EXPECT_EQ(0, *op_result); | |
| EXPECT_EQ(0, *it); | |
| } | |
| // Const preincrement, predecrement. | |
| { | |
| circular_deque<int>::const_iterator it = q.begin(); | |
| circular_deque<int>::const_iterator op_result = ++it; | |
| EXPECT_EQ(1, *op_result); | |
| EXPECT_EQ(1, *it); | |
| op_result = --it; | |
| EXPECT_EQ(0, *op_result); | |
| EXPECT_EQ(0, *it); | |
| } | |
| // Mutable postincrement, postdecrement. | |
| { | |
| circular_deque<int>::iterator it = q.begin(); | |
| circular_deque<int>::iterator op_result = it++; | |
| EXPECT_EQ(0, *op_result); | |
| EXPECT_EQ(1, *it); | |
| op_result = it--; | |
| EXPECT_EQ(1, *op_result); | |
| EXPECT_EQ(0, *it); | |
| } | |
| // Const postincrement, postdecrement. | |
| { | |
| circular_deque<int>::const_iterator it = q.begin(); | |
| circular_deque<int>::const_iterator op_result = it++; | |
| EXPECT_EQ(0, *op_result); | |
| EXPECT_EQ(1, *it); | |
| op_result = it--; | |
| EXPECT_EQ(1, *op_result); | |
| EXPECT_EQ(0, *it); | |
| } | |
| } | |
| TEST(CircularDeque, IteratorIntegerOps) { | |
| circular_deque<int> q = MakeSequence(10); | |
| int expected_front = 0; | |
| int expected_back = 9; | |
| for (int i = 0; i < 30; i++) { | |
| EXPECT_EQ(0, q.begin() - q.begin()); | |
| EXPECT_EQ(0, q.end() - q.end()); | |
| EXPECT_EQ(q.size(), static_cast<size_t>(q.end() - q.begin())); | |
| // += | |
| circular_deque<int>::iterator eight = q.begin(); | |
| eight += 8; | |
| EXPECT_EQ(8, eight - q.begin()); | |
| EXPECT_EQ(expected_front + 8, *eight); | |
| // -= | |
| eight -= 8; | |
| EXPECT_EQ(q.begin(), eight); | |
| // + | |
| eight = eight + 8; | |
| EXPECT_EQ(8, eight - q.begin()); | |
| // - | |
| eight = eight - 8; | |
| EXPECT_EQ(q.begin(), eight); | |
| expected_front++; | |
| expected_back++; | |
| q.pop_front(); | |
| q.push_back(expected_back); | |
| } | |
| } | |
| TEST(CircularDeque, IteratorArrayAccess) { | |
| circular_deque<int> q = MakeSequence(10); | |
| circular_deque<int>::iterator begin = q.begin(); | |
| EXPECT_EQ(0, begin[0]); | |
| EXPECT_EQ(9, begin[9]); | |
| circular_deque<int>::iterator end = q.end(); | |
| EXPECT_EQ(0, end[-10]); | |
| EXPECT_EQ(9, end[-1]); | |
| begin[0] = 100; | |
| EXPECT_EQ(100, end[-10]); | |
| } | |
| TEST(CircularDeque, ReverseIterator) { | |
| circular_deque<int> q; | |
| q.push_back(4); | |
| q.push_back(3); | |
| q.push_back(2); | |
| q.push_back(1); | |
| circular_deque<int>::reverse_iterator iter = q.rbegin(); | |
| EXPECT_EQ(1, *iter); | |
| iter++; | |
| EXPECT_EQ(2, *iter); | |
| ++iter; | |
| EXPECT_EQ(3, *iter); | |
| iter++; | |
| EXPECT_EQ(4, *iter); | |
| ++iter; | |
| EXPECT_EQ(q.rend(), iter); | |
| } | |
| TEST(CircularDeque, CapacityReserveShrink) { | |
| circular_deque<int> q; | |
| // A default constructed queue should have no capacity since it should waste | |
| // no space. | |
| EXPECT_TRUE(q.empty()); | |
| EXPECT_EQ(0u, q.size()); | |
| EXPECT_EQ(0u, q.capacity()); | |
| size_t new_capacity = 100; | |
| q.reserve(new_capacity); | |
| EXPECT_EQ(new_capacity, q.capacity()); | |
| // Adding that many items should not cause a resize. | |
| for (size_t i = 0; i < new_capacity; i++) | |
| q.push_back(i); | |
| EXPECT_EQ(new_capacity, q.size()); | |
| EXPECT_EQ(new_capacity, q.capacity()); | |
| // Shrink to fit to a smaller size. | |
| size_t capacity_2 = new_capacity / 2; | |
| q.resize(capacity_2); | |
| q.shrink_to_fit(); | |
| EXPECT_EQ(capacity_2, q.size()); | |
| EXPECT_EQ(capacity_2, q.capacity()); | |
| } | |
| TEST(CircularDeque, CapacityAutoShrink) { | |
| size_t big_size = 1000u; | |
| circular_deque<int> q; | |
| q.resize(big_size); | |
| size_t big_capacity = q.capacity(); | |
| // Delete 3/4 of the items. | |
| for (size_t i = 0; i < big_size / 4 * 3; i++) | |
| q.pop_back(); | |
| // The capacity should have shrunk by deleting that many items. | |
| size_t medium_capacity = q.capacity(); | |
| EXPECT_GT(big_capacity, medium_capacity); | |
| // Using resize to shrink should keep some extra capacity. | |
| q.resize(1); | |
| EXPECT_LT(1u, q.capacity()); | |
| q.resize(0); | |
| EXPECT_LT(0u, q.capacity()); | |
| // Using clear() should delete everything. | |
| q.clear(); | |
| EXPECT_EQ(0u, q.capacity()); | |
| } | |
| TEST(CircularDeque, ClearAndEmpty) { | |
| circular_deque<int> q; | |
| EXPECT_TRUE(q.empty()); | |
| q.resize(10); | |
| EXPECT_EQ(10u, q.size()); | |
| EXPECT_FALSE(q.empty()); | |
| q.clear(); | |
| EXPECT_EQ(0u, q.size()); | |
| EXPECT_TRUE(q.empty()); | |
| // clear() also should reset the capacity. | |
| EXPECT_EQ(0u, q.capacity()); | |
| } | |
| TEST(CircularDeque, Resize) { | |
| circular_deque<int> q; | |
| // Resize with default constructor. | |
| size_t first_size = 10; | |
| q.resize(first_size); | |
| EXPECT_EQ(first_size, q.size()); | |
| for (size_t i = 0; i < first_size; i++) | |
| EXPECT_EQ(0, q[i]); | |
| // Resize with different value. | |
| size_t second_expand = 10; | |
| q.resize(first_size + second_expand, 3); | |
| EXPECT_EQ(first_size + second_expand, q.size()); | |
| for (size_t i = 0; i < first_size; i++) | |
| EXPECT_EQ(0, q[i]); | |
| for (size_t i = 0; i < second_expand; i++) | |
| EXPECT_EQ(3, q[i + first_size]); | |
| // Erase from the end and add to the beginning so resize is forced to cross | |
| // a circular buffer wrap boundary. | |
| q.shrink_to_fit(); | |
| for (int i = 0; i < 5; i++) { | |
| q.pop_back(); | |
| q.push_front(6); | |
| } | |
| q.resize(10); | |
| EXPECT_EQ(6, q[0]); | |
| EXPECT_EQ(6, q[1]); | |
| EXPECT_EQ(6, q[2]); | |
| EXPECT_EQ(6, q[3]); | |
| EXPECT_EQ(6, q[4]); | |
| EXPECT_EQ(0, q[5]); | |
| EXPECT_EQ(0, q[6]); | |
| EXPECT_EQ(0, q[7]); | |
| EXPECT_EQ(0, q[8]); | |
| EXPECT_EQ(0, q[9]); | |
| } | |
| // Tests destructor behavior of resize. | |
| TEST(CircularDeque, ResizeDelete) { | |
| int counter = 0; | |
| circular_deque<DestructorCounter> q; | |
| q.resize(10, DestructorCounter(&counter)); | |
| // The one temporary when calling resize() should be deleted, that's it. | |
| EXPECT_EQ(1, counter); | |
| // The loops below assume the capacity will be set by resize(). | |
| EXPECT_EQ(10u, q.capacity()); | |
| // Delete some via resize(). This is done so that the wasted items are | |
| // still greater than the size() so that auto-shrinking is not triggered | |
| // (which will mess up our destructor counting). | |
| counter = 0; | |
| q.resize(8, DestructorCounter(&counter)); | |
| // 2 deleted ones + the one temporary in the resize() call. | |
| EXPECT_EQ(3, counter); | |
| // Cycle through some items so two items will cross the boundary in the | |
| // 11-item buffer (one more than the capacity). | |
| // Before: x x x x x x x x . . . | |
| // After: x . . . x x x x x x x | |
| counter = 0; | |
| for (int i = 0; i < 4; i++) { | |
| q.emplace_back(&counter); | |
| q.pop_front(); | |
| } | |
| EXPECT_EQ(4, counter); // Loop should have deleted 7 items. | |
| // Delete two items with resize, these should be on either side of the | |
| // buffer wrap point. | |
| counter = 0; | |
| q.resize(6, DestructorCounter(&counter)); | |
| // 2 deleted ones + the one temporary in the resize() call. | |
| EXPECT_EQ(3, counter); | |
| } | |
| TEST(CircularDeque, InsertEraseSingle) { | |
| circular_deque<int> q; | |
| q.push_back(1); | |
| q.push_back(2); | |
| // Insert at the beginning. | |
| auto result = q.insert(q.begin(), 0); | |
| EXPECT_EQ(q.begin(), result); | |
| EXPECT_EQ(3u, q.size()); | |
| EXPECT_EQ(0, q[0]); | |
| EXPECT_EQ(1, q[1]); | |
| EXPECT_EQ(2, q[2]); | |
| // Erase at the beginning. | |
| result = q.erase(q.begin()); | |
| EXPECT_EQ(q.begin(), result); | |
| EXPECT_EQ(2u, q.size()); | |
| EXPECT_EQ(1, q[0]); | |
| EXPECT_EQ(2, q[1]); | |
| // Insert at the end. | |
| result = q.insert(q.end(), 3); | |
| EXPECT_EQ(q.end() - 1, result); | |
| EXPECT_EQ(1, q[0]); | |
| EXPECT_EQ(2, q[1]); | |
| EXPECT_EQ(3, q[2]); | |
| // Erase at the end. | |
| result = q.erase(q.end() - 1); | |
| EXPECT_EQ(q.end(), result); | |
| EXPECT_EQ(1, q[0]); | |
| EXPECT_EQ(2, q[1]); | |
| // Insert in the middle. | |
| result = q.insert(q.begin() + 1, 10); | |
| EXPECT_EQ(q.begin() + 1, result); | |
| EXPECT_EQ(1, q[0]); | |
| EXPECT_EQ(10, q[1]); | |
| EXPECT_EQ(2, q[2]); | |
| // Erase in the middle. | |
| result = q.erase(q.begin() + 1); | |
| EXPECT_EQ(q.begin() + 1, result); | |
| EXPECT_EQ(1, q[0]); | |
| EXPECT_EQ(2, q[1]); | |
| } | |
| TEST(CircularDeque, InsertFill) { | |
| circular_deque<int> q; | |
| // Fill when empty. | |
| q.insert(q.begin(), 2, 1); | |
| // 0's at the beginning. | |
| q.insert(q.begin(), 2, 0); | |
| // 50's in the middle (now at offset 3). | |
| q.insert(q.begin() + 3, 2, 50); | |
| // 200's at the end. | |
| q.insert(q.end(), 2, 200); | |
| ASSERT_EQ(8u, q.size()); | |
| EXPECT_EQ(0, q[0]); | |
| EXPECT_EQ(0, q[1]); | |
| EXPECT_EQ(1, q[2]); | |
| EXPECT_EQ(50, q[3]); | |
| EXPECT_EQ(50, q[4]); | |
| EXPECT_EQ(1, q[5]); | |
| EXPECT_EQ(200, q[6]); | |
| EXPECT_EQ(200, q[7]); | |
| } | |
| TEST(CircularDeque, InsertEraseRange) { | |
| circular_deque<int> q; | |
| // Erase nothing from an empty deque should work. | |
| q.erase(q.begin(), q.end()); | |
| // Loop index used below to shift the used items in the buffer. | |
| for (int i = 0; i < 10; i++) { | |
| circular_deque<int> source; | |
| // Fill empty range. | |
| q.insert(q.begin(), source.begin(), source.end()); | |
| // Have some stuff to insert. | |
| source.push_back(1); | |
| source.push_back(2); | |
| q.insert(q.begin(), source.begin(), source.end()); | |
| // Shift the used items in the buffer by i which will place the two used | |
| // elements in different places in the buffer each time through this loop. | |
| for (int shift_i = 0; shift_i < i; shift_i++) { | |
| q.push_back(0); | |
| q.pop_front(); | |
| } | |
| // Set the two items to notable values so we can check for them later. | |
| ASSERT_EQ(2u, q.size()); | |
| q[0] = 100; | |
| q[1] = 101; | |
| // Insert at the beginning, middle (now at offset 3), and end. | |
| q.insert(q.begin(), source.begin(), source.end()); | |
| q.insert(q.begin() + 3, source.begin(), source.end()); | |
| q.insert(q.end(), source.begin(), source.end()); | |
| ASSERT_EQ(8u, q.size()); | |
| EXPECT_EQ(1, q[0]); | |
| EXPECT_EQ(2, q[1]); | |
| EXPECT_EQ(100, q[2]); // First inserted one. | |
| EXPECT_EQ(1, q[3]); | |
| EXPECT_EQ(2, q[4]); | |
| EXPECT_EQ(101, q[5]); // First inserted second one. | |
| EXPECT_EQ(1, q[6]); | |
| EXPECT_EQ(2, q[7]); | |
| // Now erase the inserted ranges. Try each subsection also with no items | |
| // being erased, which should be a no-op. | |
| auto result = q.erase(q.begin(), q.begin()); // No-op. | |
| EXPECT_EQ(q.begin(), result); | |
| result = q.erase(q.begin(), q.begin() + 2); | |
| EXPECT_EQ(q.begin(), result); | |
| result = q.erase(q.begin() + 1, q.begin() + 1); // No-op. | |
| EXPECT_EQ(q.begin() + 1, result); | |
| result = q.erase(q.begin() + 1, q.begin() + 3); | |
| EXPECT_EQ(q.begin() + 1, result); | |
| result = q.erase(q.end() - 2, q.end() - 2); // No-op. | |
| EXPECT_EQ(q.end() - 2, result); | |
| result = q.erase(q.end() - 2, q.end()); | |
| EXPECT_EQ(q.end(), result); | |
| ASSERT_EQ(2u, q.size()); | |
| EXPECT_EQ(100, q[0]); | |
| EXPECT_EQ(101, q[1]); | |
| // Erase everything. | |
| result = q.erase(q.begin(), q.end()); | |
| EXPECT_EQ(q.end(), result); | |
| EXPECT_TRUE(q.empty()); | |
| } | |
| } | |
| TEST(CircularDeque, EmplaceMoveOnly) { | |
| int values[] = {1, 3}; | |
| circular_deque<MoveOnlyInt> q(std::begin(values), std::end(values)); | |
| q.emplace(q.begin(), MoveOnlyInt(0)); | |
| q.emplace(q.begin() + 2, MoveOnlyInt(2)); | |
| q.emplace(q.end(), MoveOnlyInt(4)); | |
| ASSERT_EQ(5u, q.size()); | |
| EXPECT_EQ(0, q[0].data()); | |
| EXPECT_EQ(1, q[1].data()); | |
| EXPECT_EQ(2, q[2].data()); | |
| EXPECT_EQ(3, q[3].data()); | |
| EXPECT_EQ(4, q[4].data()); | |
| } | |
| TEST(CircularDeque, EmplaceFrontBackReturnsReference) { | |
| circular_deque<int> q; | |
| q.reserve(2); | |
| int& front = q.emplace_front(1); | |
| int& back = q.emplace_back(2); | |
| ASSERT_EQ(2u, q.size()); | |
| EXPECT_EQ(1, q[0]); | |
| EXPECT_EQ(2, q[1]); | |
| EXPECT_EQ(&front, &q.front()); | |
| EXPECT_EQ(&back, &q.back()); | |
| front = 3; | |
| back = 4; | |
| ASSERT_EQ(2u, q.size()); | |
| EXPECT_EQ(3, q[0]); | |
| EXPECT_EQ(4, q[1]); | |
| EXPECT_EQ(&front, &q.front()); | |
| EXPECT_EQ(&back, &q.back()); | |
| } | |
| /* | |
| This test should assert in a debug build. It tries to dereference an iterator | |
| after mutating the container. Uncomment to double-check that this works. | |
| TEST(CircularDeque, UseIteratorAfterMutate) { | |
| circular_deque<int> q; | |
| q.push_back(0); | |
| auto old_begin = q.begin(); | |
| EXPECT_EQ(0, *old_begin); | |
| q.push_back(1); | |
| EXPECT_EQ(0, *old_begin); // Should DCHECK. | |
| } | |
| */ | |
| } // namespace containers |