v8/test/unittests/base/threaded-list-unittest.cc
Florian Sattler ea60dfcb05 [base] Fix wrong ThreadedList append for empty lists allocated on the stack.
Change-Id: Ieae88990f3d960c13a2bafc223e12061e994fce0
Reviewed-on: https://chromium-review.googlesource.com/c/1270580
Reviewed-by: Marja Hölttä <marja@chromium.org>
Commit-Queue: Florian Sattler <sattlerf@google.com>
Cr-Commit-Position: refs/heads/master@{#56474}
2018-10-09 10:49:15 +00:00

327 lines
9.3 KiB
C++

// Copyright 2018 the V8 project 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 <iterator>
#include "src/v8.h"
#include "src/base/threaded-list.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace base {
struct ThreadedListTestNode {
ThreadedListTestNode() : next_(nullptr), other_next_(nullptr) {}
ThreadedListTestNode** next() { return &next_; }
ThreadedListTestNode* next_;
struct OtherTraits {
static ThreadedListTestNode** next(ThreadedListTestNode* t) {
return t->other_next();
}
};
ThreadedListTestNode** other_next() { return &other_next_; }
ThreadedListTestNode* other_next_;
};
struct ThreadedListTest : public ::testing::Test {
static const size_t INIT_NODES = 5;
ThreadedListTest() {}
void SetUp() override {
for (size_t i = 0; i < INIT_NODES; i++) {
nodes[i] = ThreadedListTestNode();
}
for (size_t i = 0; i < INIT_NODES; i++) {
list.Add(&nodes[i]);
normal_next_list.Add(&nodes[i]);
}
// Verify if setup worked
CHECK(list.Verify());
CHECK_EQ(list.LengthForTest(), INIT_NODES);
CHECK(normal_next_list.Verify());
CHECK_EQ(normal_next_list.LengthForTest(), INIT_NODES);
extra_test_node_0 = ThreadedListTestNode();
extra_test_node_1 = ThreadedListTestNode();
extra_test_node_2 = ThreadedListTestNode();
extra_test_list.Add(&extra_test_node_0);
extra_test_list.Add(&extra_test_node_1);
extra_test_list.Add(&extra_test_node_2);
CHECK_EQ(extra_test_list.LengthForTest(), 3);
CHECK(extra_test_list.Verify());
normal_extra_test_list.Add(&extra_test_node_0);
normal_extra_test_list.Add(&extra_test_node_1);
normal_extra_test_list.Add(&extra_test_node_2);
CHECK_EQ(normal_extra_test_list.LengthForTest(), 3);
CHECK(normal_extra_test_list.Verify());
}
void TearDown() override {
// Check if the normal list threaded through next is still untouched.
CHECK(normal_next_list.Verify());
CHECK_EQ(normal_next_list.LengthForTest(), INIT_NODES);
CHECK_EQ(normal_next_list.AtForTest(0), &nodes[0]);
CHECK_EQ(normal_next_list.AtForTest(4), &nodes[4]);
CHECK(normal_extra_test_list.Verify());
CHECK_EQ(normal_extra_test_list.LengthForTest(), 3);
CHECK_EQ(normal_extra_test_list.AtForTest(0), &extra_test_node_0);
CHECK_EQ(normal_extra_test_list.AtForTest(2), &extra_test_node_2);
list.Clear();
extra_test_list.Clear();
}
ThreadedListTestNode nodes[INIT_NODES];
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> list;
ThreadedList<ThreadedListTestNode> normal_next_list;
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits>
extra_test_list;
ThreadedList<ThreadedListTestNode> normal_extra_test_list;
ThreadedListTestNode extra_test_node_0;
ThreadedListTestNode extra_test_node_1;
ThreadedListTestNode extra_test_node_2;
};
TEST_F(ThreadedListTest, Add) {
CHECK_EQ(list.LengthForTest(), 5);
ThreadedListTestNode new_node;
// Add to existing list
list.Add(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 6);
CHECK_EQ(list.AtForTest(5), &new_node);
list.Clear();
CHECK_EQ(list.LengthForTest(), 0);
new_node = ThreadedListTestNode();
// Add to empty list
list.Add(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 1);
CHECK_EQ(list.AtForTest(0), &new_node);
}
TEST_F(ThreadedListTest, AddFront) {
CHECK_EQ(list.LengthForTest(), 5);
ThreadedListTestNode new_node;
// AddFront to existing list
list.AddFront(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 6);
CHECK_EQ(list.first(), &new_node);
list.Clear();
CHECK_EQ(list.LengthForTest(), 0);
new_node = ThreadedListTestNode();
// AddFront to empty list
list.AddFront(&new_node);
list.Verify();
CHECK_EQ(list.LengthForTest(), 1);
CHECK_EQ(list.first(), &new_node);
}
TEST_F(ThreadedListTest, ReinitializeHead) {
CHECK_EQ(list.LengthForTest(), 5);
CHECK_NE(extra_test_list.first(), list.first());
list.ReinitializeHead(&extra_test_node_0);
list.Verify();
CHECK_EQ(extra_test_list.first(), list.first());
CHECK_EQ(extra_test_list.end(), list.end());
CHECK_EQ(extra_test_list.LengthForTest(), 3);
}
TEST_F(ThreadedListTest, DropHead) {
CHECK_EQ(extra_test_list.LengthForTest(), 3);
CHECK_EQ(extra_test_list.first(), &extra_test_node_0);
extra_test_list.DropHead();
extra_test_list.Verify();
CHECK_EQ(extra_test_list.first(), &extra_test_node_1);
CHECK_EQ(extra_test_list.LengthForTest(), 2);
}
TEST_F(ThreadedListTest, Append) {
auto initial_extra_list_end = extra_test_list.end();
CHECK_EQ(list.LengthForTest(), 5);
list.Append(std::move(extra_test_list));
list.Verify();
extra_test_list.Verify();
CHECK(extra_test_list.is_empty());
CHECK_EQ(list.LengthForTest(), 8);
CHECK_EQ(list.AtForTest(4), &nodes[4]);
CHECK_EQ(list.AtForTest(5), &extra_test_node_0);
CHECK_EQ(list.end(), initial_extra_list_end);
}
TEST_F(ThreadedListTest, AppendOutOfScope) {
ThreadedListTestNode local_extra_test_node_0;
CHECK_EQ(list.LengthForTest(), 5);
{
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits>
scoped_extra_test_list;
list.Append(std::move(scoped_extra_test_list));
}
list.Add(&local_extra_test_node_0);
list.Verify();
CHECK_EQ(list.LengthForTest(), 6);
CHECK_EQ(list.AtForTest(4), &nodes[4]);
CHECK_EQ(list.AtForTest(5), &local_extra_test_node_0);
}
TEST_F(ThreadedListTest, Prepend) {
CHECK_EQ(list.LengthForTest(), 5);
list.Prepend(std::move(extra_test_list));
list.Verify();
extra_test_list.Verify();
CHECK(extra_test_list.is_empty());
CHECK_EQ(list.LengthForTest(), 8);
CHECK_EQ(list.first(), &extra_test_node_0);
CHECK_EQ(list.AtForTest(2), &extra_test_node_2);
CHECK_EQ(list.AtForTest(3), &nodes[0]);
}
TEST_F(ThreadedListTest, Clear) {
CHECK_NE(list.LengthForTest(), 0);
list.Clear();
CHECK_EQ(list.LengthForTest(), 0);
CHECK_NULL(list.first());
}
TEST_F(ThreadedListTest, MoveAssign) {
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list;
CHECK_EQ(extra_test_list.LengthForTest(), 3);
m_list = std::move(extra_test_list);
m_list.Verify();
CHECK_EQ(m_list.first(), &extra_test_node_0);
CHECK_EQ(m_list.LengthForTest(), 3);
// move assign from empty list
extra_test_list.Clear();
CHECK_EQ(extra_test_list.LengthForTest(), 0);
m_list = std::move(extra_test_list);
CHECK_EQ(m_list.LengthForTest(), 0);
m_list.Verify();
CHECK_NULL(m_list.first());
}
TEST_F(ThreadedListTest, MoveCtor) {
CHECK_EQ(extra_test_list.LengthForTest(), 3);
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list(
std::move(extra_test_list));
m_list.Verify();
CHECK_EQ(m_list.LengthForTest(), 3);
CHECK_EQ(m_list.first(), &extra_test_node_0);
// move construct from empty list
extra_test_list.Clear();
CHECK_EQ(extra_test_list.LengthForTest(), 0);
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> m_list2(
std::move(extra_test_list));
CHECK_EQ(m_list2.LengthForTest(), 0);
m_list2.Verify();
CHECK_NULL(m_list2.first());
}
TEST_F(ThreadedListTest, Remove) {
CHECK_EQ(list.LengthForTest(), 5);
// Remove first
CHECK_EQ(list.first(), &nodes[0]);
list.Remove(&nodes[0]);
list.Verify();
CHECK_EQ(list.first(), &nodes[1]);
CHECK_EQ(list.LengthForTest(), 4);
// Remove middle
list.Remove(&nodes[2]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 3);
CHECK_EQ(list.first(), &nodes[1]);
CHECK_EQ(list.AtForTest(1), &nodes[3]);
// Remove last
list.Remove(&nodes[4]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 2);
CHECK_EQ(list.first(), &nodes[1]);
CHECK_EQ(list.AtForTest(1), &nodes[3]);
// Remove rest
list.Remove(&nodes[1]);
list.Remove(&nodes[3]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 0);
// Remove not found
list.Remove(&nodes[4]);
list.Verify();
CHECK_EQ(list.LengthForTest(), 0);
}
TEST_F(ThreadedListTest, Rewind) {
CHECK_EQ(extra_test_list.LengthForTest(), 3);
for (auto iter = extra_test_list.begin(); iter != extra_test_list.end();
++iter) {
if (*iter == &extra_test_node_2) {
extra_test_list.Rewind(iter);
break;
}
}
CHECK_EQ(extra_test_list.LengthForTest(), 2);
auto iter = extra_test_list.begin();
CHECK_EQ(*iter, &extra_test_node_0);
std::advance(iter, 1);
CHECK_EQ(*iter, &extra_test_node_1);
extra_test_list.Rewind(extra_test_list.begin());
CHECK_EQ(extra_test_list.LengthForTest(), 0);
}
TEST_F(ThreadedListTest, IterComp) {
ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits> c_list =
std::move(extra_test_list);
bool found_first;
for (auto iter = c_list.begin(); iter != c_list.end(); ++iter) {
// This triggers the operator== on the iterator
if (iter == c_list.begin()) {
found_first = true;
}
}
CHECK(found_first);
}
TEST_F(ThreadedListTest, ConstIterComp) {
const ThreadedList<ThreadedListTestNode, ThreadedListTestNode::OtherTraits>
c_list = std::move(extra_test_list);
bool found_first;
for (auto iter = c_list.begin(); iter != c_list.end(); ++iter) {
// This triggers the operator== on the iterator
if (iter == c_list.begin()) {
found_first = true;
}
}
CHECK(found_first);
}
} // namespace base
} // namespace v8