blob: 72dd411d3ebdf341b9758af2e2f10d499b46a7e6 [file] [log] [blame] [edit]
// Copyright (c) 2009 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.
// This file is a copy of Chromium's /src/base/containers/linked_list_unittest.cc
#include <gtest/gtest.h>
#include "common/LinkedList.h"
#include <list>
class Node : public LinkNode<Node> {
public:
explicit Node(int id) : id_(id) {
}
int id() const {
return id_;
}
void set_id(int id) {
id_ = id;
}
private:
int id_;
};
class MultipleInheritanceNodeBase {
public:
MultipleInheritanceNodeBase() : field_taking_up_space_(0) {
}
int field_taking_up_space_;
};
class MultipleInheritanceNode : public MultipleInheritanceNodeBase,
public LinkNode<MultipleInheritanceNode> {
public:
MultipleInheritanceNode() = default;
};
class MovableNode : public LinkNode<MovableNode> {
public:
explicit MovableNode(int id) : id_(id) {
}
MovableNode(MovableNode&&) = default;
int id() const {
return id_;
}
private:
int id_;
};
// Checks that when iterating |list| (either from head to tail, or from
// tail to head, as determined by |forward|), we get back |node_ids|,
// which is an array of size |num_nodes|.
void ExpectListContentsForDirection(const LinkedList<Node>& list,
int num_nodes,
const int* node_ids,
bool forward) {
int i = 0;
for (const LinkNode<Node>* node = (forward ? list.head() : list.tail()); node != list.end();
node = (forward ? node->next() : node->previous())) {
ASSERT_LT(i, num_nodes);
int index_of_id = forward ? i : num_nodes - i - 1;
EXPECT_EQ(node_ids[index_of_id], node->value()->id());
++i;
}
EXPECT_EQ(num_nodes, i);
}
void ExpectListContents(const LinkedList<Node>& list, int num_nodes, const int* node_ids) {
{
SCOPED_TRACE("Iterating forward (from head to tail)");
ExpectListContentsForDirection(list, num_nodes, node_ids, true);
}
{
SCOPED_TRACE("Iterating backward (from tail to head)");
ExpectListContentsForDirection(list, num_nodes, node_ids, false);
}
}
TEST(LinkedList, Empty) {
LinkedList<Node> list;
EXPECT_EQ(list.end(), list.head());
EXPECT_EQ(list.end(), list.tail());
ExpectListContents(list, 0, nullptr);
}
TEST(LinkedList, Append) {
LinkedList<Node> list;
ExpectListContents(list, 0, nullptr);
Node n1(1);
list.Append(&n1);
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n1, list.tail());
{
const int expected[] = {1};
ExpectListContents(list, 1, expected);
}
Node n2(2);
list.Append(&n2);
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n2, list.tail());
{
const int expected[] = {1, 2};
ExpectListContents(list, 2, expected);
}
Node n3(3);
list.Append(&n3);
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n3, list.tail());
{
const int expected[] = {1, 2, 3};
ExpectListContents(list, 3, expected);
}
}
TEST(LinkedList, RemoveFromList) {
LinkedList<Node> list;
Node n1(1);
Node n2(2);
Node n3(3);
Node n4(4);
Node n5(5);
list.Append(&n1);
list.Append(&n2);
list.Append(&n3);
list.Append(&n4);
list.Append(&n5);
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n5, list.tail());
{
const int expected[] = {1, 2, 3, 4, 5};
ExpectListContents(list, 5, expected);
}
// Remove from the middle.
n3.RemoveFromList();
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n5, list.tail());
{
const int expected[] = {1, 2, 4, 5};
ExpectListContents(list, 4, expected);
}
// Remove from the tail.
n5.RemoveFromList();
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n4, list.tail());
{
const int expected[] = {1, 2, 4};
ExpectListContents(list, 3, expected);
}
// Remove from the head.
n1.RemoveFromList();
EXPECT_EQ(&n2, list.head());
EXPECT_EQ(&n4, list.tail());
{
const int expected[] = {2, 4};
ExpectListContents(list, 2, expected);
}
// Empty the list.
n2.RemoveFromList();
n4.RemoveFromList();
ExpectListContents(list, 0, nullptr);
EXPECT_EQ(list.end(), list.head());
EXPECT_EQ(list.end(), list.tail());
// Fill the list once again.
list.Append(&n1);
list.Append(&n2);
list.Append(&n3);
list.Append(&n4);
list.Append(&n5);
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n5, list.tail());
{
const int expected[] = {1, 2, 3, 4, 5};
ExpectListContents(list, 5, expected);
}
}
TEST(LinkedList, InsertBefore) {
LinkedList<Node> list;
Node n1(1);
Node n2(2);
Node n3(3);
Node n4(4);
list.Append(&n1);
list.Append(&n2);
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n2, list.tail());
{
const int expected[] = {1, 2};
ExpectListContents(list, 2, expected);
}
n3.InsertBefore(&n2);
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n2, list.tail());
{
const int expected[] = {1, 3, 2};
ExpectListContents(list, 3, expected);
}
n4.InsertBefore(&n1);
EXPECT_EQ(&n4, list.head());
EXPECT_EQ(&n2, list.tail());
{
const int expected[] = {4, 1, 3, 2};
ExpectListContents(list, 4, expected);
}
}
TEST(LinkedList, InsertAfter) {
LinkedList<Node> list;
Node n1(1);
Node n2(2);
Node n3(3);
Node n4(4);
list.Append(&n1);
list.Append(&n2);
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n2, list.tail());
{
const int expected[] = {1, 2};
ExpectListContents(list, 2, expected);
}
n3.InsertAfter(&n2);
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n3, list.tail());
{
const int expected[] = {1, 2, 3};
ExpectListContents(list, 3, expected);
}
n4.InsertAfter(&n1);
EXPECT_EQ(&n1, list.head());
EXPECT_EQ(&n3, list.tail());
{
const int expected[] = {1, 4, 2, 3};
ExpectListContents(list, 4, expected);
}
}
TEST(LinkedList, MultipleInheritanceNode) {
MultipleInheritanceNode node;
EXPECT_EQ(&node, node.value());
}
TEST(LinkedList, EmptyListIsEmpty) {
LinkedList<Node> list;
EXPECT_TRUE(list.empty());
}
TEST(LinkedList, NonEmptyListIsNotEmpty) {
LinkedList<Node> list;
Node n(1);
list.Append(&n);
EXPECT_FALSE(list.empty());
}
TEST(LinkedList, EmptiedListIsEmptyAgain) {
LinkedList<Node> list;
Node n(1);
list.Append(&n);
n.RemoveFromList();
EXPECT_TRUE(list.empty());
}
TEST(LinkedList, NodesCanBeReused) {
LinkedList<Node> list1;
LinkedList<Node> list2;
Node n(1);
list1.Append(&n);
n.RemoveFromList();
list2.Append(&n);
EXPECT_EQ(list2.head()->value(), &n);
}
TEST(LinkedList, RemovedNodeHasNullNextPrevious) {
LinkedList<Node> list;
Node n(1);
list.Append(&n);
n.RemoveFromList();
EXPECT_EQ(nullptr, n.next());
EXPECT_EQ(nullptr, n.previous());
}
TEST(LinkedList, NodeMoveConstructor) {
LinkedList<MovableNode> list;
MovableNode n1(1);
MovableNode n2(2);
MovableNode n3(3);
list.Append(&n1);
list.Append(&n2);
list.Append(&n3);
EXPECT_EQ(&n1, n2.previous());
EXPECT_EQ(&n2, n1.next());
EXPECT_EQ(&n3, n2.next());
EXPECT_EQ(&n2, n3.previous());
EXPECT_EQ(2, n2.id());
MovableNode n2_new(std::move(n2));
EXPECT_EQ(nullptr, n2.next());
EXPECT_EQ(nullptr, n2.previous());
EXPECT_EQ(&n1, n2_new.previous());
EXPECT_EQ(&n2_new, n1.next());
EXPECT_EQ(&n3, n2_new.next());
EXPECT_EQ(&n2_new, n3.previous());
EXPECT_EQ(2, n2_new.id());
}
TEST(LinkedList, IsInList) {
LinkedList<Node> list;
Node n(1);
EXPECT_FALSE(n.IsInList());
list.Append(&n);
EXPECT_TRUE(n.IsInList());
EXPECT_TRUE(n.RemoveFromList());
EXPECT_FALSE(n.IsInList());
EXPECT_FALSE(n.RemoveFromList());
}
TEST(LinkedList, MoveInto) {
LinkedList<Node> l1;
LinkedList<Node> l2;
Node n1(1);
Node n2(2);
l1.Append(&n1);
l2.Append(&n2);
l2.MoveInto(&l1);
const int expected[] = {1, 2};
ExpectListContents(l1, 2, expected);
EXPECT_TRUE(l2.empty());
}
TEST(LinkedList, MoveEmptyListInto) {
LinkedList<Node> l1;
LinkedList<Node> l2;
Node n1(1);
Node n2(2);
l1.Append(&n1);
l1.Append(&n2);
l2.MoveInto(&l1);
const int expected[] = {1, 2};
ExpectListContents(l1, 2, expected);
EXPECT_TRUE(l2.empty());
}
TEST(LinkedList, MoveIntoEmpty) {
LinkedList<Node> l1;
LinkedList<Node> l2;
Node n1(1);
Node n2(2);
l2.Append(&n1);
l2.Append(&n2);
l2.MoveInto(&l1);
const int expected[] = {1, 2};
ExpectListContents(l1, 2, expected);
EXPECT_TRUE(l2.empty());
}
TEST(LinkedList, RangeBasedModify) {
LinkedList<Node> list;
Node n1(1);
Node n2(2);
list.Append(&n1);
list.Append(&n2);
for (LinkNode<Node>* node : list) {
node->value()->set_id(node->value()->id() + 1);
}
const int expected[] = {2, 3};
ExpectListContents(list, 2, expected);
}
TEST(LinkedList, RangeBasedEndIsEnd) {
LinkedList<Node> list;
EXPECT_EQ(list.end(), *end(list));
}