| /* | 
 |  * Copyright (C) 2013 The Android Open Source Project | 
 |  * All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  *  * Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  *  * Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in | 
 |  *    the documentation and/or other materials provided with the | 
 |  *    distribution. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
 |  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
 |  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 
 |  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | 
 |  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | 
 |  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | 
 |  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | 
 |  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | 
 |  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
 |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | 
 |  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
 |  * SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include <stdlib.h> | 
 | #include <string> | 
 | #include <sstream> | 
 |  | 
 | #include <gtest/gtest.h> | 
 |  | 
 | #include "linked_list.h" | 
 |  | 
 | namespace { | 
 |  | 
 | bool alloc_called = false; | 
 | bool free_called = false; | 
 |  | 
 | class LinkedListTestAllocator { | 
 |  public: | 
 |   typedef LinkedListEntry<const char> entry_t; | 
 |  | 
 |   static entry_t* alloc() { | 
 |     alloc_called = true; | 
 |     return reinterpret_cast<entry_t*>(::malloc(sizeof(entry_t))); | 
 |   } | 
 |  | 
 |   static void free(entry_t* p) { | 
 |     free_called = true; | 
 |     ::free(p); | 
 |   } | 
 |  private: | 
 |   DISALLOW_IMPLICIT_CONSTRUCTORS(LinkedListTestAllocator); | 
 | }; | 
 |  | 
 | typedef LinkedList<const char, LinkedListTestAllocator> test_list_t; | 
 |  | 
 | std::string test_list_to_string(test_list_t& list) { | 
 |   std::stringstream ss; | 
 |   list.for_each([&] (const char* c) { | 
 |     ss << c; | 
 |   }); | 
 |  | 
 |   return ss.str(); | 
 | } | 
 |  | 
 | }; | 
 |  | 
 | TEST(linked_list, simple) { | 
 |   alloc_called = free_called = false; | 
 |   test_list_t list; | 
 |   ASSERT_EQ("", test_list_to_string(list)); | 
 |   ASSERT_TRUE(!alloc_called); | 
 |   ASSERT_TRUE(!free_called); | 
 |   list.push_front("a"); | 
 |   ASSERT_TRUE(alloc_called); | 
 |   ASSERT_TRUE(!free_called); | 
 |   ASSERT_EQ("a", test_list_to_string(list)); | 
 |   list.push_front("b"); | 
 |   ASSERT_EQ("ba", test_list_to_string(list)); | 
 |   list.push_front("c"); | 
 |   list.push_front("d"); | 
 |   ASSERT_EQ("dcba", test_list_to_string(list)); | 
 |   ASSERT_TRUE(alloc_called); | 
 |   ASSERT_TRUE(!free_called); | 
 |   alloc_called = free_called = false; | 
 |   list.remove_if([] (const char* c) { | 
 |     return *c == 'c'; | 
 |   }); | 
 |  | 
 |   ASSERT_TRUE(!alloc_called); | 
 |   ASSERT_TRUE(free_called); | 
 |  | 
 |   ASSERT_EQ("dba", test_list_to_string(list)); | 
 |   alloc_called = free_called = false; | 
 |   list.remove_if([] (const char* c) { | 
 |     return *c == '2'; | 
 |   }); | 
 |   ASSERT_TRUE(!alloc_called); | 
 |   ASSERT_TRUE(!free_called); | 
 |   ASSERT_EQ("dba", test_list_to_string(list)); | 
 |   list.clear(); | 
 |   ASSERT_TRUE(!alloc_called); | 
 |   ASSERT_TRUE(free_called); | 
 |   ASSERT_EQ("", test_list_to_string(list)); | 
 | } | 
 |  | 
 | TEST(linked_list, push_pop) { | 
 |   test_list_t list; | 
 |   list.push_front("b"); | 
 |   list.push_front("a"); | 
 |   ASSERT_EQ("ab", test_list_to_string(list)); | 
 |   list.push_back("c"); | 
 |   ASSERT_EQ("abc", test_list_to_string(list)); | 
 |   ASSERT_STREQ("a", list.pop_front()); | 
 |   ASSERT_EQ("bc", test_list_to_string(list)); | 
 |   ASSERT_STREQ("b", list.pop_front()); | 
 |   ASSERT_EQ("c", test_list_to_string(list)); | 
 |   ASSERT_STREQ("c", list.pop_front()); | 
 |   ASSERT_EQ("", test_list_to_string(list)); | 
 |   ASSERT_TRUE(list.pop_front() == nullptr); | 
 |   list.push_back("r"); | 
 |   ASSERT_EQ("r", test_list_to_string(list)); | 
 |   ASSERT_STREQ("r", list.pop_front()); | 
 |   ASSERT_TRUE(list.pop_front() == nullptr); | 
 | } | 
 |  | 
 | TEST(linked_list, remove_if_then_pop) { | 
 |   test_list_t list; | 
 |   list.push_back("a"); | 
 |   list.push_back("b"); | 
 |   list.push_back("c"); | 
 |   list.push_back("d"); | 
 |   list.remove_if([](const char* c) { | 
 |     return *c == 'b' || *c == 'c'; | 
 |   }); | 
 |  | 
 |   ASSERT_EQ("ad", test_list_to_string(list)); | 
 |   ASSERT_STREQ("a", list.pop_front()); | 
 |   ASSERT_EQ("d", test_list_to_string(list)); | 
 |   ASSERT_STREQ("d", list.pop_front()); | 
 |   ASSERT_TRUE(list.pop_front() == nullptr); | 
 | } | 
 |  | 
 | TEST(linked_list, remove_if_last_then_push_back) { | 
 |   test_list_t list; | 
 |  | 
 |   list.push_back("a"); | 
 |   list.push_back("b"); | 
 |   list.push_back("c"); | 
 |   list.push_back("d"); | 
 |  | 
 |   list.remove_if([](const char* c) { | 
 |     return *c == 'c' || *c == 'd'; | 
 |   }); | 
 |  | 
 |   ASSERT_EQ("ab", test_list_to_string(list)); | 
 |   list.push_back("d"); | 
 |   ASSERT_EQ("abd", test_list_to_string(list)); | 
 | } | 
 |  | 
 | TEST(linked_list, copy_to_array) { | 
 |   test_list_t list; | 
 |   const size_t max_size = 128; | 
 |   const char* buf[max_size]; | 
 |   memset(buf, 0, sizeof(buf)); | 
 |  | 
 |   ASSERT_EQ(0U, list.copy_to_array(buf, max_size)); | 
 |   ASSERT_EQ(nullptr, buf[0]); | 
 |  | 
 |   list.push_back("a"); | 
 |   list.push_back("b"); | 
 |   list.push_back("c"); | 
 |   list.push_back("d"); | 
 |  | 
 |   memset(buf, 0, sizeof(buf)); | 
 |   ASSERT_EQ(2U, list.copy_to_array(buf, 2)); | 
 |   ASSERT_STREQ("a", buf[0]); | 
 |   ASSERT_STREQ("b", buf[1]); | 
 |   ASSERT_EQ(nullptr, buf[2]); | 
 |  | 
 |   ASSERT_EQ(4U, list.copy_to_array(buf, max_size)); | 
 |   ASSERT_STREQ("a", buf[0]); | 
 |   ASSERT_STREQ("b", buf[1]); | 
 |   ASSERT_STREQ("c", buf[2]); | 
 |   ASSERT_STREQ("d", buf[3]); | 
 |   ASSERT_EQ(nullptr, buf[4]); | 
 |  | 
 |   memset(buf, 0, sizeof(buf)); | 
 |   list.remove_if([](const char* c) { | 
 |     return *c != 'c'; | 
 |   }); | 
 |   ASSERT_EQ(1U, list.copy_to_array(buf, max_size)); | 
 |   ASSERT_STREQ("c", buf[0]); | 
 |   ASSERT_EQ(nullptr, buf[1]); | 
 |  | 
 |   memset(buf, 0, sizeof(buf)); | 
 |  | 
 |   list.remove_if([](const char* c) { | 
 |     return *c == 'c'; | 
 |   }); | 
 |  | 
 |   ASSERT_EQ(0U, list.copy_to_array(buf, max_size)); | 
 |   ASSERT_EQ(nullptr, buf[0]); | 
 | } | 
 |  | 
 | TEST(linked_list, test_visit) { | 
 |   test_list_t list; | 
 |   list.push_back("a"); | 
 |   list.push_back("b"); | 
 |   list.push_back("c"); | 
 |   list.push_back("d"); | 
 |  | 
 |   int visits = 0; | 
 |   std::stringstream ss; | 
 |   bool result = list.visit([&](const char* c) { | 
 |     ++visits; | 
 |     ss << c; | 
 |     return true; | 
 |   }); | 
 |  | 
 |   ASSERT_TRUE(result); | 
 |   ASSERT_EQ(4, visits); | 
 |   ASSERT_EQ("abcd", ss.str()); | 
 |  | 
 |   visits = 0; | 
 |   ss.str(std::string()); | 
 |  | 
 |   result = list.visit([&](const char* c) { | 
 |     if (++visits == 3) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     ss << c; | 
 |     return true; | 
 |   }); | 
 |  | 
 |   ASSERT_TRUE(!result); | 
 |   ASSERT_EQ(3, visits); | 
 |   ASSERT_EQ("ab", ss.str()); | 
 | } | 
 |  |