// 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 "base/containers/circular_deque.h" #include "base/test/copy_only_int.h" #include "base/test/move_only_int.h" #include "testing/gtest/include/gtest/gtest.h" using base::internal::VectorBuffer; namespace base { namespace { circular_deque MakeSequence(size_t max) { circular_deque 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 void CycleTest(circular_deque& 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 foo(15); EXPECT_EQ(15u, foo.size()); // Fill with default constructor. { circular_deque buf(num_elts); EXPECT_EQ(num_elts, buf.size()); EXPECT_EQ(num_elts, static_cast(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 buf(num_elts, value); EXPECT_EQ(num_elts, buf.size()); EXPECT_EQ(num_elts, static_cast(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 first(std::begin(values), std::end(values)); circular_deque 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 first(std::begin(values), std::end(values)); circular_deque 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 empty({}); ASSERT_TRUE(empty.empty()); circular_deque 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 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 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 first = {1, 2, 3, 4, 5, 6}; circular_deque 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 first = {1, 2, 3, 4, 5, 6}; circular_deque 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 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 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 empty; empty.assign(0, 52); EXPECT_EQ(0u, empty.size()); circular_deque 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 empty; empty.assign(std::begin(range), std::begin(range)); EXPECT_TRUE(empty.empty()); circular_deque 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 empty; empty.assign({}); EXPECT_TRUE(empty.empty()); circular_deque 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 q = MakeSequence(10); CycleTest(q, [](const circular_deque& 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(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 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 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 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 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 a = MakeSequence(10); circular_deque 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 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-based 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 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 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 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::iterator it = q.begin(); circular_deque::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::const_iterator it = q.begin(); circular_deque::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::iterator it = q.begin(); circular_deque::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::const_iterator it = q.begin(); circular_deque::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 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(q.end() - q.begin())); // += circular_deque::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 q = MakeSequence(10); circular_deque::iterator begin = q.begin(); EXPECT_EQ(0, begin[0]); EXPECT_EQ(9, begin[9]); circular_deque::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 q; q.push_back(4); q.push_back(3); q.push_back(2); q.push_back(1); circular_deque::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 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 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 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 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 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 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 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 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 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 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 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 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 base