/* * Copyright (c) 2011 The WebRTC 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 in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "gtest/gtest.h" #include "system_wrappers/interface/list_wrapper.h" #include "system_wrappers/interface/scoped_ptr.h" using ::webrtc::ListWrapper; using ::webrtc::ListItem; using ::webrtc::scoped_ptr; // Note: kNumberOfElements needs to be even. const unsigned int kNumberOfElements = 10; // An opaque implementation of dynamic or statically allocated unsigned ints. // This class makes it possible to use the exact same code for testing of both // the dynamic and static implementation of ListWrapper. // Clarification: ListWrapper has two versions of PushBack(..). It takes an // unsigned integer or a void pointer. The integer implementation takes care // of memory management. The void pointer version expect the caller to manage // the memory associated with the void pointer. // This class works like the integer version but can be implemented on top of // either the integer version or void pointer version of ListWrapper. // Note: the non-virtual fuctions behave the same for both versions. class ListWrapperSimple { public: static ListWrapperSimple* Create(bool static_allocation); virtual ~ListWrapperSimple() {} // These three functions should be used for manipulating ListItems so that // they are the type corresponding to the underlying implementation. virtual unsigned int GetUnsignedItem( const ListItem* item) const = 0; virtual ListItem* CreateListItem(unsigned int item_id) = 0; unsigned int GetSize() const { return list_.GetSize(); } virtual int PushBack(const unsigned int item_id) = 0; virtual int PushFront(const unsigned int item_id) = 0; virtual int PopFront() = 0; virtual int PopBack() = 0; bool Empty() const { return list_.Empty(); } ListItem* First() const { return list_.First(); } ListItem* Last() const { return list_.Last(); } ListItem* Next(ListItem* item) const { return list_.Next(item); } ListItem* Previous(ListItem* item) const { return list_.Previous(item); } virtual int Erase(ListItem* item) = 0; int Insert(ListItem* existing_previous_item, ListItem* new_item) { const int retval = list_.Insert(existing_previous_item, new_item); if (retval != 0) { EXPECT_TRUE(DestroyListItem(new_item)); } return retval; } int InsertBefore(ListItem* existing_next_item, ListItem* new_item) { const int retval = list_.InsertBefore(existing_next_item, new_item); if (retval != 0) { EXPECT_TRUE(DestroyListItem(new_item)); } return retval; } protected: ListWrapperSimple() {} virtual bool DestroyListItemContent(ListItem* item) = 0; bool DestroyListItem(ListItem* item) { const bool retval = DestroyListItemContent(item); delete item; return retval; } ListWrapper list_; }; void ClearList(ListWrapperSimple* list_wrapper) { if (list_wrapper == NULL) { return; } ListItem* list_item = list_wrapper->First(); while (list_item != NULL) { EXPECT_EQ(list_wrapper->Erase(list_item), 0); list_item = list_wrapper->First(); } } class ListWrapperStatic : public ListWrapperSimple { public: ListWrapperStatic() {} virtual ~ListWrapperStatic() { ClearList(this); } virtual unsigned int GetUnsignedItem(const ListItem* item) const { return item->GetUnsignedItem(); } virtual ListItem* CreateListItem(unsigned int item_id) { return new ListItem(item_id); } virtual bool DestroyListItemContent(ListItem* item) { return true; } virtual int PushBack(const unsigned int item_id) { return list_.PushBack(item_id); } virtual int PushFront(const unsigned int item_id) { return list_.PushFront(item_id); } virtual int PopFront() { return list_.PopFront(); } virtual int PopBack() { return list_.PopBack(); } virtual int Erase(ListItem* item) { return list_.Erase(item); } }; class ListWrapperDynamic : public ListWrapperSimple { public: ListWrapperDynamic() {} virtual ~ListWrapperDynamic() { ClearList(this); } virtual unsigned int GetUnsignedItem(const ListItem* item) const { const unsigned int* return_value_pointer = reinterpret_cast<unsigned int*> (item->GetItem()); if (return_value_pointer == NULL) { return -1; } return *return_value_pointer; } virtual ListItem* CreateListItem(unsigned int item_id) { unsigned int* item_id_pointer = new unsigned int; if (item_id_pointer == NULL) { return NULL; } *item_id_pointer = item_id; ListItem* return_value = new ListItem( reinterpret_cast<void*>(item_id_pointer)); if (return_value == NULL) { delete item_id_pointer; return NULL; } return return_value; } virtual bool DestroyListItemContent(ListItem* item) { if (item == NULL) { return false; } bool return_value = false; unsigned int* item_id_ptr = reinterpret_cast<unsigned int*>( item->GetItem()); if (item_id_ptr != NULL) { return_value = true; delete item_id_ptr; } return return_value; } virtual int PushBack(const unsigned int item_id) { unsigned int* item_id_ptr = new unsigned int; if (item_id_ptr == NULL) { return -1; } *item_id_ptr = item_id; const int return_value = list_.PushBack( reinterpret_cast<void*>(item_id_ptr)); if (return_value != 0) { delete item_id_ptr; } return return_value; } virtual int PushFront(const unsigned int item_id) { unsigned int* item_id_ptr = new unsigned int; if (item_id_ptr == NULL) { return -1; } *item_id_ptr = item_id; const int return_value = list_.PushFront( reinterpret_cast<void*>(item_id_ptr)); if (return_value != 0) { delete item_id_ptr; } return return_value; } virtual int PopFront() { return Erase(list_.First()); } virtual int PopBack() { return Erase(list_.Last()); } virtual int Erase(ListItem* item) { if (item == NULL) { return -1; } int retval = 0; if (!DestroyListItemContent(item)) { retval = -1; ADD_FAILURE(); } if (list_.Erase(item) != 0) { retval = -1; } return retval; } }; ListWrapperSimple* ListWrapperSimple::Create(bool static_allocation) { if(static_allocation) { return new ListWrapperStatic(); } return new ListWrapperDynamic(); } ListWrapperSimple* CreateAscendingList(bool static_allocation) { ListWrapperSimple* return_value = ListWrapperSimple::Create( static_allocation); if (return_value == NULL) { return NULL; } for (unsigned int i = 0; i < kNumberOfElements; ++i) { if (return_value->PushBack(i) == -1) { ClearList(return_value); delete return_value; return NULL; } } return return_value; } ListWrapperSimple* CreateDescendingList(bool static_allocation) { ListWrapperSimple* return_value = ListWrapperSimple::Create( static_allocation); if (return_value == NULL) { return NULL; } for (unsigned int i = 0; i < kNumberOfElements; ++i) { if (return_value->PushBack(kNumberOfElements - i - 1) == -1) { ClearList(return_value); delete return_value; return NULL; } } return return_value; } // [0,kNumberOfElements - 1,1,kNumberOfElements - 2,...] (this is why // kNumberOfElements need to be even) ListWrapperSimple* CreateInterleavedList(bool static_allocation) { ListWrapperSimple* return_value = ListWrapperSimple::Create( static_allocation); if (return_value == NULL) { return NULL; } unsigned int uneven_count = 0; unsigned int even_count = 0; for (unsigned int i = 0; i < kNumberOfElements; i++) { unsigned int push_value = 0; if ((i % 2) == 0) { push_value = even_count; even_count++; } else { push_value = kNumberOfElements - uneven_count - 1; uneven_count++; } if (return_value->PushBack(push_value) == -1) { ClearList(return_value); delete return_value; return NULL; } } return return_value; } void PrintList(const ListWrapperSimple* list) { ListItem* list_item = list->First(); printf("["); while (list_item != NULL) { printf("%3u", list->GetUnsignedItem(list_item)); list_item = list->Next(list_item); } printf("]\n"); } bool CompareLists(const ListWrapperSimple* lhs, const ListWrapperSimple* rhs) { const unsigned int list_size = lhs->GetSize(); if (lhs->GetSize() != rhs->GetSize()) { return false; } if (lhs->Empty()) { return rhs->Empty(); } unsigned int i = 0; ListItem* lhs_item = lhs->First(); ListItem* rhs_item = rhs->First(); while (i < list_size) { if (lhs_item == NULL) { return false; } if (rhs_item == NULL) { return false; } if (lhs->GetUnsignedItem(lhs_item) != rhs->GetUnsignedItem(rhs_item)) { return false; } i++; lhs_item = lhs->Next(lhs_item); rhs_item = rhs->Next(rhs_item); } return true; } TEST(ListWrapperTest,ReverseNewIntList) { // Create a new temporary list with elements reversed those of // new_int_list_ const scoped_ptr<ListWrapperSimple> descending_list( CreateDescendingList(rand()%2)); ASSERT_FALSE(descending_list.get() == NULL); ASSERT_FALSE(descending_list->Empty()); ASSERT_EQ(kNumberOfElements,descending_list->GetSize()); const scoped_ptr<ListWrapperSimple> ascending_list( CreateAscendingList(rand()%2)); ASSERT_FALSE(ascending_list.get() == NULL); ASSERT_FALSE(ascending_list->Empty()); ASSERT_EQ(kNumberOfElements,ascending_list->GetSize()); scoped_ptr<ListWrapperSimple> list_to_reverse( ListWrapperSimple::Create(rand()%2)); // Reverse the list using PushBack and Previous. for (ListItem* item = ascending_list->Last(); item != NULL; item = ascending_list->Previous(item)) { list_to_reverse->PushBack(ascending_list->GetUnsignedItem(item)); } ASSERT_TRUE(CompareLists(descending_list.get(), list_to_reverse.get())); scoped_ptr<ListWrapperSimple> list_to_un_reverse( ListWrapperSimple::Create(rand()%2)); ASSERT_FALSE(list_to_un_reverse.get() == NULL); // Reverse the reversed list using PushFront and Next. for (ListItem* item = list_to_reverse->First(); item != NULL; item = list_to_reverse->Next(item)) { list_to_un_reverse->PushFront(list_to_reverse->GetUnsignedItem(item)); } ASSERT_TRUE(CompareLists(ascending_list.get(), list_to_un_reverse.get())); } TEST(ListWrapperTest,PopTest) { scoped_ptr<ListWrapperSimple> ascending_list(CreateAscendingList(rand()%2)); ASSERT_FALSE(ascending_list.get() == NULL); ASSERT_FALSE(ascending_list->Empty()); EXPECT_EQ(0, ascending_list->PopFront()); EXPECT_EQ(1U, ascending_list->GetUnsignedItem(ascending_list->First())); EXPECT_EQ(0, ascending_list->PopBack()); EXPECT_EQ(kNumberOfElements - 2, ascending_list->GetUnsignedItem( ascending_list->Last())); EXPECT_EQ(kNumberOfElements - 2, ascending_list->GetSize()); } // Use Insert to interleave two lists. TEST(ListWrapperTest,InterLeaveTest) { scoped_ptr<ListWrapperSimple> interleave_list( CreateAscendingList(rand()%2)); ASSERT_FALSE(interleave_list.get() == NULL); ASSERT_FALSE(interleave_list->Empty()); scoped_ptr<ListWrapperSimple> descending_list( CreateDescendingList(rand()%2)); ASSERT_FALSE(descending_list.get() == NULL); for (unsigned int i = 0; i < kNumberOfElements/2; ++i) { ASSERT_EQ(0, interleave_list->PopBack()); ASSERT_EQ(0, descending_list->PopBack()); } ASSERT_EQ(kNumberOfElements/2, interleave_list->GetSize()); ASSERT_EQ(kNumberOfElements/2, descending_list->GetSize()); unsigned int insert_position = kNumberOfElements/2; ASSERT_EQ(insert_position * 2, kNumberOfElements); while (!descending_list->Empty()) { ListItem* item = descending_list->Last(); ASSERT_FALSE(item == NULL); const unsigned int item_id = descending_list->GetUnsignedItem(item); ASSERT_EQ(0, descending_list->Erase(item)); ListItem* insert_item = interleave_list->CreateListItem(item_id); ASSERT_FALSE(insert_item == NULL); item = interleave_list->First(); ASSERT_FALSE(item == NULL); for (unsigned int j = 0; j < insert_position - 1; ++j) { item = interleave_list->Next(item); ASSERT_FALSE(item == NULL); } EXPECT_EQ(0, interleave_list->Insert(item, insert_item)); --insert_position; } scoped_ptr<ListWrapperSimple> interleaved_list( CreateInterleavedList(rand()%2)); ASSERT_FALSE(interleaved_list.get() == NULL); ASSERT_FALSE(interleaved_list->Empty()); ASSERT_TRUE(CompareLists(interleaved_list.get(), interleave_list.get())); } // Use InsertBefore to interleave two lists. TEST(ListWrapperTest,InterLeaveTestII) { scoped_ptr<ListWrapperSimple> interleave_list( CreateDescendingList(rand()%2)); ASSERT_FALSE(interleave_list.get() == NULL); ASSERT_FALSE(interleave_list->Empty()); scoped_ptr<ListWrapperSimple> ascending_list(CreateAscendingList(rand()%2)); ASSERT_FALSE(ascending_list.get() == NULL); for (unsigned int i = 0; i < kNumberOfElements/2; ++i) { ASSERT_EQ(0, interleave_list->PopBack()); ASSERT_EQ(0, ascending_list->PopBack()); } ASSERT_EQ(kNumberOfElements/2, interleave_list->GetSize()); ASSERT_EQ(kNumberOfElements/2, ascending_list->GetSize()); unsigned int insert_position = kNumberOfElements/2; ASSERT_EQ(insert_position * 2, kNumberOfElements); while (!ascending_list->Empty()) { ListItem* item = ascending_list->Last(); ASSERT_FALSE(item == NULL); const unsigned int item_id = ascending_list->GetUnsignedItem(item); ASSERT_EQ(0,ascending_list->Erase(item)); ListItem* insert_item = interleave_list->CreateListItem(item_id); ASSERT_FALSE(insert_item == NULL); item = interleave_list->First(); ASSERT_FALSE(item == NULL); for (unsigned int j = 0; j < insert_position - 1; ++j) { item = interleave_list->Next(item); ASSERT_FALSE(item == NULL); } EXPECT_EQ(interleave_list->InsertBefore(item, insert_item), 0); --insert_position; } scoped_ptr<ListWrapperSimple> interleaved_list( CreateInterleavedList(rand()%2)); ASSERT_FALSE(interleaved_list.get() == NULL); ASSERT_FALSE(interleaved_list->Empty()); ASSERT_TRUE(CompareLists(interleaved_list.get(), interleave_list.get())); }