├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── appveyor.yml ├── bam.lua ├── list.h ├── slist.h └── test ├── greatest.h ├── test_list.cpp ├── test_list_main.cpp └── test_slist.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .bam 2 | .cproject 3 | .project 4 | local 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | - clang 5 | 6 | install: 7 | - git clone https://github.com/matricks/bam.git 8 | - cd bam 9 | - ./make_unix.sh 10 | - cd .. 11 | 12 | script: 13 | - bam/bam compiler=$CC config=debug -r sc test 14 | - bam/bam compiler=$CC config=release -r sc test 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Intrusive single/double linked list for C++. 2 | 3 | version 0.1, augusti, 2013 4 | 5 | Copyright (C) 2013- Fredrik Kihlander 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | 23 | Fredrik Kihlander 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intrusive linked list 2 | 3 | [![Build Status](https://travis-ci.org/wc-duck/intrusive_list.svg?branch=master)](https://travis-ci.org/wc-duck/intrusive_list) 4 | [![Build Status](https://ci.appveyor.com/api/projects/status/s5faxb2u8571s5xf)](https://ci.appveyor.com/project/wc-duck/intrusive-list) 5 | 6 | ## About: 7 | Implementation of single- and double- intrusive linked lists. 8 | 9 | After looking at the interwebs for some intrusive linked lists I found that there is no good alternatives 10 | available, if you like me do not want to use boost. 11 | 12 | So here is an implementation that has served me well for my own private projects. 13 | 14 | ## Example: 15 | 16 | ```c++ 17 | #include "list.h" 18 | 19 | // ... lets define a struct that should exist in a list ... 20 | struct my_struct 21 | { 22 | LIST_NODE( my_struct ) list; 23 | 24 | int some_data; 25 | }; 26 | 27 | // ... and a list to store "my_struct":s in ... 28 | LIST( my_struct, list ) the_list; // arg1 the type to store in list, arg2 name of member in my_struct to use for list. 29 | 30 | int main( int argc, char** argv ) 31 | { 32 | // ... create some structs ... 33 | my_struct s1; 34 | my_struct s2; 35 | my_struct s3; 36 | my_struct s4; 37 | 38 | // ... insert them at list head ... 39 | the_list.insert_head( &s1 ); 40 | the_list.insert_tail( &s2 ); 41 | the_list.insert_before( &s3, &s1 ); 42 | the_list.insert_after( &s4, &s2 ); 43 | 44 | // ... walk through items in list forward ... 45 | for( my_struct* iter = the_list.head(); iter != 0x0; iter = the_list.next( iter ) ) 46 | { 47 | // ... do stuff with iter ... 48 | } 49 | 50 | // ... walk through items in list backward ... 51 | for( my_struct* iter = the_list.tail(); iter != 0x0; iter = the_list.prev( iter ) ) 52 | { 53 | // ... do stuff with iter ... 54 | } 55 | 56 | // ... remove some elements from list ... 57 | the_list.remove( &s2 ); 58 | the_list.remove_head(); 59 | the_list.remove_tail(); 60 | 61 | return 0; 62 | }; 63 | 64 | ``` 65 | 66 | ## Licence: 67 | 68 | ``` 69 | Intrusive single/double linked list for C++. 70 | 71 | version 0.1, augusti, 2013 72 | 73 | Copyright (C) 2013- Fredrik Kihlander 74 | 75 | This software is provided 'as-is', without any express or implied 76 | warranty. In no event will the authors be held liable for any damages 77 | arising from the use of this software. 78 | 79 | Permission is granted to anyone to use this software for any purpose, 80 | including commercial applications, and to alter it and redistribute it 81 | freely, subject to the following restrictions: 82 | 83 | 1. The origin of this software must not be misrepresented; you must not 84 | claim that you wrote the original software. If you use this software 85 | in a product, an acknowledgment in the product documentation would be 86 | appreciated but is not required. 87 | 2. Altered source versions must be plainly marked as such, and must not be 88 | misrepresented as being the original software. 89 | 3. This notice may not be removed or altered from any source distribution. 90 | 91 | Fredrik Kihlander 92 | ``` 93 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | platform: 2 | - x86 3 | 4 | install: 5 | - c:\"Program Files (x86)"\"Microsoft Visual Studio 12.0"\VC\vcvarsall.bat 6 | - git clone https://github.com/matricks/bam.git 7 | - cd bam 8 | - make_win64_msvc.bat 9 | - cd .. 10 | - bam\bam.exe config=debug test 11 | - bam\bam.exe config=release test 12 | 13 | build: OFF 14 | 15 | -------------------------------------------------------------------------------- /bam.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Intrusive single/double linked list for C++. 3 | 4 | version 0.1, augusti, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | --]] 26 | 27 | BUILD_PATH = "local" 28 | 29 | function get_config() 30 | local config = ScriptArgs["config"] 31 | if config == nil then 32 | return "debug" 33 | end 34 | return config 35 | end 36 | 37 | function get_platform() 38 | local platform = ScriptArgs["platform"] 39 | if platform == nil then 40 | if family == "windows" then 41 | platform = "winx64" 42 | else 43 | platform = "linux_x86_64" 44 | end 45 | end 46 | return platform 47 | end 48 | 49 | function get_base_settings() 50 | local settings = {} 51 | 52 | settings._is_settingsobject = true 53 | settings.invoke_count = 0 54 | settings.debug = 0 55 | settings.optimize = 0 56 | SetCommonSettings(settings) 57 | 58 | -- add all tools 59 | for _, tool in pairs(_bam_tools) do 60 | tool(settings) 61 | end 62 | 63 | return settings 64 | end 65 | 66 | function set_compiler( settings, config ) 67 | if family == "windows" then 68 | compiler = "msvc" 69 | else 70 | compiler = ScriptArgs["compiler"] 71 | if compiler == nil then 72 | compiler = "gcc" 73 | end 74 | end 75 | 76 | InitCommonCCompiler(settings) 77 | if compiler == "msvc" then 78 | SetDriversCL( settings ) 79 | if config == "release" then 80 | settings.cc.flags:Add( "/Ox" ) 81 | settings.cc.flags:Add( "/TP" ) -- forcing c++ compile on windows =/ 82 | end 83 | elseif compiler == "gcc" then 84 | SetDriversGCC( settings ) 85 | settings.cc.flags:Add( "-Wconversion", "-Wextra", "-Wall", "-Werror", "-Wstrict-aliasing=2" ) 86 | if config == "release" then 87 | settings.cc.flags:Add( "-O2" ) 88 | end 89 | elseif compiler == "clang" then 90 | SetDriversClang( settings ) 91 | settings.cc.flags:Add( "-Wconversion", "-Wextra", "-Wall", "-Werror", "-Wstrict-aliasing=2" ) 92 | if config == "release" then 93 | settings.cc.flags:Add( "-O2" ) 94 | end 95 | end 96 | end 97 | 98 | config = get_config() 99 | platform = get_platform() 100 | settings = get_base_settings() 101 | set_compiler( settings, config ) 102 | TableLock( settings ) 103 | 104 | local output_path = PathJoin( BUILD_PATH, PathJoin( config, platform ) ) 105 | local output_func = function(settings, path) return PathJoin(output_path, PathFilename(PathBase(path)) .. settings.config_ext) end 106 | settings.cc.Output = output_func 107 | settings.lib.Output = output_func 108 | settings.link.Output = output_func 109 | 110 | settings.cc.defines:Add("_INTRUSIVE_LIST_ASSERT_ENABLED") 111 | 112 | if family ~= "windows" then 113 | settings.cc.flags:Add( "-Wconversion", "-Wextra", "-Wall", "-Werror", "-Wstrict-aliasing=2" ) 114 | else 115 | settings.link.flags:Add( "/NODEFAULTLIB:LIBCMT.LIB" ); 116 | settings.cc.defines:Add("_ITERATOR_DEBUG_LEVEL=0") 117 | end 118 | 119 | settings.link.libpath:Add( 'local/' .. config .. '/' .. platform ) 120 | 121 | local objs = Compile( settings, Collect( 'test/*.cpp' ) ) 122 | local tests = Link( settings, 'test_intrusive_lists', objs ) 123 | 124 | test_args = " -v" 125 | test_valgrind = "" 126 | if ScriptArgs["test"] then test_args = test_args .. " -t " .. ScriptArgs["test"] end 127 | if ScriptArgs["suite"] then test_args = test_args .. " -s " .. ScriptArgs["suite"] end 128 | if ScriptArgs["valgrind"] then test_valgrind = "valgrind -v --leak-check=full --track-origins=yes " end 129 | 130 | if family == "windows" then 131 | AddJob( "test", "unittest", string.gsub( tests, "/", "\\" ) .. test_args, tests, tests ) 132 | else 133 | AddJob( "test", "unittest", test_valgrind .. tests .. test_args, tests, tests ) 134 | end 135 | 136 | DefaultTarget( tests ) 137 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | /* 2 | Intrusive single/double linked list for C++. 3 | 4 | version 0.1, augusti, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #ifndef INTRUSIVE_LIST_H_INCLUDED 28 | #define INTRUSIVE_LIST_H_INCLUDED 29 | 30 | #if defined( _INTRUSIVE_LIST_ASSERT_ENABLED ) 31 | # if !defined( _INTRUSIVE_LIST_ASSERT ) 32 | # include 33 | # define _INTRUSIVE_LIST_ASSERT( cond, ... ) assert( cond ) 34 | # endif 35 | #else 36 | # if !defined( _INTRUSIVE_LIST_ASSERT ) 37 | # define _INTRUSIVE_LIST_ASSERT( ... ) 38 | # endif 39 | #endif 40 | 41 | template 42 | struct list_node 43 | { 44 | list_node() 45 | : next(0x0) 46 | , prev(0x0) 47 | {} 48 | 49 | T* next; 50 | T* prev; 51 | }; 52 | 53 | /** 54 | * intrusive double linked list. 55 | */ 56 | template T::*NODE> 57 | class list 58 | { 59 | T* head_ptr; 60 | T* tail_ptr; 61 | 62 | public: 63 | list() 64 | : head_ptr( 0x0 ) 65 | , tail_ptr( 0x0 ) 66 | {} 67 | ~list() { clear(); } 68 | 69 | /** 70 | * insert item at the head of list. 71 | * @param item item to insert in list. 72 | */ 73 | void insert_head( T* elem ) 74 | { 75 | list_node* node = &(elem->*NODE); 76 | 77 | _INTRUSIVE_LIST_ASSERT( node->next == 0x0 ); 78 | _INTRUSIVE_LIST_ASSERT( node->prev == 0x0 ); 79 | 80 | node->prev = 0; 81 | node->next = head_ptr; 82 | 83 | if( head_ptr != 0x0 ) 84 | { 85 | list_node* last_head = &(head_ptr->*NODE); 86 | last_head->prev = elem; 87 | } 88 | 89 | head_ptr = elem; 90 | 91 | if( tail_ptr == 0x0 ) 92 | tail_ptr = head_ptr; 93 | } 94 | 95 | /** 96 | * insert item at the tail of list. 97 | * @param item item to insert in list. 98 | */ 99 | void insert_tail( T* item ) 100 | { 101 | if( tail_ptr == 0x0 ) 102 | insert_head( item ); 103 | else 104 | { 105 | list_node* tail_node = &(tail_ptr->*NODE); 106 | list_node* item_node = &(item->*NODE); 107 | _INTRUSIVE_LIST_ASSERT( item_node->next == 0x0 ); 108 | _INTRUSIVE_LIST_ASSERT( item_node->prev == 0x0 ); 109 | tail_node->next = item; 110 | item_node->prev = tail_ptr; 111 | item_node->next = 0x0; 112 | tail_ptr = item; 113 | } 114 | } 115 | 116 | /** 117 | * insert item in list after other list item. 118 | * @param item item to insert in list. 119 | * @param in_list item that already is inserted in list. 120 | */ 121 | void insert_after( T* item, T* in_list ) 122 | { 123 | list_node* node = &(item->*NODE); 124 | list_node* in_node = &(in_list->*NODE); 125 | 126 | if( in_node->next ) 127 | { 128 | list_node* in_next = &(in_node->next->*NODE); 129 | in_next->prev = item; 130 | } 131 | 132 | node->next = in_node->next; 133 | node->prev = in_list; 134 | in_node->next = item; 135 | 136 | if( in_list == tail_ptr ) 137 | tail_ptr = item; 138 | } 139 | 140 | /** 141 | * insert item in list before other list item. 142 | * @param item item to insert in list. 143 | * @param in_list item that already is inserted in list. 144 | */ 145 | void insert_before( T* item, T* in_list ) 146 | { 147 | list_node* node = &(item->*NODE); 148 | list_node* in_node = &(in_list->*NODE); 149 | 150 | if( in_node->prev ) 151 | { 152 | list_node* in_prev = &(in_node->prev->*NODE); 153 | in_prev->next = item; 154 | } 155 | 156 | node->next = in_list; 157 | node->prev = in_node->prev; 158 | in_node->prev = item; 159 | 160 | if( in_list == head_ptr ) 161 | head_ptr = item; 162 | } 163 | 164 | /** 165 | * remove the first item in the list. 166 | * @return the removed item. 167 | */ 168 | T* remove_head() 169 | { 170 | T* ret = head(); 171 | remove( head() ); 172 | return ret; 173 | } 174 | 175 | /** 176 | * remove the last item in the list. 177 | * @return the removed item. 178 | */ 179 | T* remove_tail() 180 | { 181 | T* ret = tail(); 182 | remove( tail() ); 183 | return ret; 184 | } 185 | 186 | /** 187 | * remove item from list. 188 | * @param item the element to remove 189 | * @note item must exist in list! 190 | */ 191 | void remove( T* item ) 192 | { 193 | list_node* node = &(item->*NODE); 194 | 195 | T* next = node->next; 196 | T* prev = node->prev; 197 | 198 | list_node* next_node = &(next->*NODE); 199 | list_node* prev_node = &(prev->*NODE); 200 | 201 | if( item == head_ptr ) head_ptr = next; 202 | if( item == tail_ptr ) tail_ptr = prev; 203 | if( prev != 0x0 ) prev_node->next = next; 204 | if( next != 0x0 ) next_node->prev = prev; 205 | 206 | node->next = 0x0; 207 | node->prev = 0x0; 208 | } 209 | 210 | /** 211 | * return first item in list. 212 | * @return first item in list or 0x0 if list is empty. 213 | */ 214 | T* head() { return head_ptr; } 215 | 216 | /** 217 | * return last item in list. 218 | * @return last item in list or 0x0 if list is empty. 219 | */ 220 | T* tail() const { return tail_ptr; } 221 | 222 | /** 223 | * return next element in list after argument element or 0x0 on end of list. 224 | * @param item item to get next element in list for. 225 | * @note item must exist in list! 226 | * @return element after item in list. 227 | */ 228 | T* next( T* item ) 229 | { 230 | list_node* node = &(item->*NODE); 231 | return node->next; 232 | } 233 | 234 | /** 235 | * return next element in list after argument element or 0x0 on end of list. 236 | * @param item item to get next element in list for. 237 | * @note item must exist in list! 238 | * @return element after item in list. 239 | */ 240 | const T* next( const T* item ) 241 | { 242 | const list_node* node = &(item->*NODE); 243 | return node->next; 244 | } 245 | 246 | /** 247 | * return previous element in list after argument element or 0x0 on start of list. 248 | * @param item item to get previous element in list for. 249 | * @note item must exist in list! 250 | * @return element before item in list. 251 | */ 252 | T* prev( T* item ) 253 | { 254 | list_node* node = &(item->*NODE); 255 | return node->prev; 256 | } 257 | 258 | /** 259 | * return previous element in list after argument element or 0x0 on start of list. 260 | * @param item item to get previous element in list for. 261 | * @note item must exist in list! 262 | * @return element before item in list. 263 | */ 264 | const T* prev( const T* item ) 265 | { 266 | const list_node* node = &(item->*NODE); 267 | return node->prev; 268 | } 269 | 270 | /** 271 | * clear queue. 272 | */ 273 | void clear() 274 | { 275 | while( !empty() ) 276 | remove( head() ); 277 | } 278 | 279 | /** 280 | * check if the list is empty. 281 | * @return true if list is empty. 282 | */ 283 | bool empty() { return head_ptr == 0x0; } 284 | }; 285 | 286 | /** 287 | * macro to define node in struct/class to use in conjunction with list. 288 | * 289 | * @example 290 | * struct my_struct 291 | * { 292 | * LIST_NODE( my_struct ) list1; 293 | * LIST_NODE( my_struct ) list2; 294 | * 295 | * int some_data; 296 | * }; 297 | */ 298 | #define LIST_NODE( owner ) list_node 299 | 300 | /** 301 | * macro to define list that act upon specific member in struct-type. 302 | * 303 | * @example 304 | * LIST( my_struct, list1 ) first_list; 305 | * LIST( my_struct, list2 ) second_list; 306 | */ 307 | #define LIST( owner, member ) list 308 | 309 | #endif // INTRUSIVE_LIST_H_INCLUDED 310 | -------------------------------------------------------------------------------- /slist.h: -------------------------------------------------------------------------------- 1 | /* 2 | Intrusive single/double linked list for C++. 3 | 4 | version 0.1, augusti, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #ifndef INTRUSIVE_SLIST_H_INCLUDED 28 | #define INTRUSIVE_SLIST_H_INCLUDED 29 | 30 | #if defined( _INTRUSIVE_LIST_ASSERT_ENABLED ) 31 | # if !defined( _INTRUSIVE_LIST_ASSERT ) 32 | # include 33 | # define _INTRUSIVE_LIST_ASSERT( cond, ... ) assert( cond ) 34 | # endif 35 | #else 36 | # if !defined( _INTRUSIVE_LIST_ASSERT ) 37 | # define _INTRUSIVE_LIST_ASSERT( ... ) 38 | # endif 39 | #endif 40 | 41 | template 42 | struct slist_node 43 | { 44 | slist_node() : next( 0x0 ) {} 45 | T* next; 46 | }; 47 | 48 | /** 49 | * intrusive single linked list. 50 | */ 51 | template T::*NODE> 52 | class slist 53 | { 54 | T* head_ptr; 55 | T* tail_ptr; 56 | 57 | public: 58 | slist() 59 | : head_ptr( 0x0 ) 60 | , tail_ptr( 0x0 ) 61 | {} 62 | ~slist() { clear(); } 63 | 64 | /** 65 | * insert item at the head of list. 66 | * @param item item to insert in list. 67 | */ 68 | void insert_head( T* elem ) 69 | { 70 | slist_node* node = &(elem->*NODE); 71 | _INTRUSIVE_LIST_ASSERT( node->next == 0x0 ); 72 | node->next = head_ptr; 73 | head_ptr = elem; 74 | if( tail_ptr == 0x0 ) 75 | tail_ptr = elem; 76 | } 77 | 78 | /** 79 | * insert item at the tail of list. 80 | * @param item item to insert in list. 81 | */ 82 | void insert_tail( T* elem ) 83 | { 84 | if( tail_ptr == 0x0 ) 85 | insert_head( elem ); 86 | else 87 | { 88 | slist_node* tail_node = &(tail_ptr->*NODE); 89 | _INTRUSIVE_LIST_ASSERT( tail_node->next == 0x0 ); 90 | tail_node->next = elem; 91 | tail_ptr = elem; 92 | } 93 | } 94 | 95 | /** 96 | * insert item in list after other list item. 97 | * @param item item to insert in list. 98 | * @param in_list item that already is inserted in list. 99 | */ 100 | void insert_after( T* elem, T* in_list ) 101 | { 102 | slist_node* insert_node = &(elem->*NODE); 103 | slist_node* in_list_node = &(in_list->*NODE); 104 | 105 | insert_node->next = in_list_node->next; 106 | in_list_node->next = elem; 107 | } 108 | 109 | /** 110 | * remove the first item in the list. 111 | * @return the removed item. 112 | */ 113 | T* remove_head() 114 | { 115 | _INTRUSIVE_LIST_ASSERT( !empty() ); 116 | slist_node* node = &(head_ptr->*NODE); 117 | T* ret = head_ptr; 118 | if( head_ptr == tail_ptr ) 119 | tail_ptr = node->next; 120 | head_ptr = node->next; 121 | (&(ret->*NODE))->next = 0x0; 122 | return ret; 123 | } 124 | 125 | /** 126 | * remove item from list. 127 | * @param item the element to remove 128 | * @note item must exist in list! 129 | */ 130 | void remove( T* elem ) 131 | { 132 | if( elem == head_ptr ) 133 | remove_head(); 134 | else 135 | { 136 | T* iter = head_ptr; 137 | while( iter != 0x0 ) 138 | { 139 | slist_node* iter_node = &(iter->*NODE); 140 | if( iter_node->next == elem ) 141 | { 142 | if( iter_node->next == tail_ptr ) 143 | tail_ptr = iter; 144 | 145 | slist_node* elem_node = &(elem->*NODE); 146 | iter_node->next = elem_node->next; 147 | elem_node->next = 0; 148 | return; 149 | } 150 | 151 | iter = iter_node->next; 152 | } 153 | } 154 | } 155 | 156 | /** 157 | * return first item in list. 158 | * @return first item in list or 0x0 if list is empty. 159 | */ 160 | T* head() const { return head_ptr; } 161 | 162 | /** 163 | * return last item in list. 164 | * @return last item in list or 0x0 if list is empty. 165 | */ 166 | T* tail() const { return tail_ptr; } 167 | 168 | /** 169 | * return next element in list after argument element or 0x0 on end of list. 170 | * @param item item to get next element in list for. 171 | * @note item must exist in list! 172 | * @return element after item in list. 173 | */ 174 | T* next( T* prev ) 175 | { 176 | slist_node* node = &(prev->*NODE); 177 | return node->next; 178 | } 179 | 180 | /** 181 | * return next element in list after argument element or 0x0 on end of list. 182 | * @param item item to get next element in list for. 183 | * @note item must exist in list! 184 | * @return element after item in list. 185 | */ 186 | const T* next( const T* prev ) const 187 | { 188 | const slist_node* node = &(prev->*NODE); 189 | return node->next; 190 | } 191 | 192 | /** 193 | * clear queue. 194 | */ 195 | void clear() 196 | { 197 | while( !empty() ) 198 | remove_head(); 199 | } 200 | 201 | /** 202 | * check if the list is empty. 203 | * @return true if list is empty. 204 | */ 205 | bool empty() { return head_ptr == 0x0; } 206 | }; 207 | 208 | /** 209 | * macro to define node in struct/class to use in conjunction with slist. 210 | * 211 | * @example 212 | * struct my_struct 213 | * { 214 | * SLIST_NODE( my_struct ) list1; 215 | * SLIST_NODE( my_struct ) list2; 216 | * 217 | * int some_data; 218 | * }; 219 | */ 220 | #define SLIST_NODE( owner ) slist_node 221 | 222 | /** 223 | * macro to define slist that act upon specific member in struct-type. 224 | * 225 | * @example 226 | * SLIST( my_struct, list1 ) first_list; 227 | * SLIST( my_struct, list2 ) second_list; 228 | */ 229 | #define SLIST( owner, member ) slist 230 | 231 | #endif // INTRUSIVE_SLIST_H_INCLUDED 232 | -------------------------------------------------------------------------------- /test/greatest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Scott Vokes 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef GREATEST_H 18 | #define GREATEST_H 19 | 20 | #define GREATEST_VERSION_MAJOR 0 21 | #define GREATEST_VERSION_MINOR 9 22 | #define GREATEST_VERSION_PATCH 3 23 | 24 | /* A unit testing system for C, contained in 1 file. 25 | * It doesn't use dynamic allocation or depend on anything 26 | * beyond ANSI C89. */ 27 | 28 | 29 | /********************************************************************* 30 | * Minimal test runner template 31 | *********************************************************************/ 32 | #if 0 33 | 34 | #include "greatest.h" 35 | 36 | TEST foo_should_foo() { 37 | PASS(); 38 | } 39 | 40 | static void setup_cb(void *data) { 41 | printf("setup callback for each test case\n"); 42 | } 43 | 44 | static void teardown_cb(void *data) { 45 | printf("teardown callback for each test case\n"); 46 | } 47 | 48 | SUITE(suite) { 49 | /* Optional setup/teardown callbacks which will be run before/after 50 | * every test case in the suite. 51 | * Cleared when the suite finishes. */ 52 | SET_SETUP(setup_cb, voidp_to_callback_data); 53 | SET_TEARDOWN(teardown_cb, voidp_to_callback_data); 54 | 55 | RUN_TEST(foo_should_foo); 56 | } 57 | 58 | /* Add all the definitions that need to be in the test runner's main file. */ 59 | GREATEST_MAIN_DEFS(); 60 | 61 | int main(int argc, char **argv) { 62 | GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ 63 | RUN_SUITE(suite); 64 | GREATEST_MAIN_END(); /* display results */ 65 | } 66 | 67 | #endif 68 | /*********************************************************************/ 69 | 70 | 71 | #include 72 | #include 73 | #include 74 | #include 75 | 76 | 77 | /*********** 78 | * Options * 79 | ***********/ 80 | 81 | /* Default column width for non-verbose output. */ 82 | #ifndef GREATEST_DEFAULT_WIDTH 83 | #define GREATEST_DEFAULT_WIDTH 72 84 | #endif 85 | 86 | /* FILE *, for test logging. */ 87 | #ifndef GREATEST_STDOUT 88 | #define GREATEST_STDOUT stdout 89 | #endif 90 | 91 | /* Remove GREATEST_ prefix from most commonly used symbols? */ 92 | #ifndef GREATEST_USE_ABBREVS 93 | #define GREATEST_USE_ABBREVS 1 94 | #endif 95 | 96 | 97 | /********* 98 | * Types * 99 | *********/ 100 | 101 | /* Info for the current running suite. */ 102 | typedef struct greatest_suite_info { 103 | unsigned int tests_run; 104 | unsigned int passed; 105 | unsigned int failed; 106 | unsigned int skipped; 107 | 108 | /* timers, pre/post running suite and individual tests */ 109 | clock_t pre_suite; 110 | clock_t post_suite; 111 | clock_t pre_test; 112 | clock_t post_test; 113 | } greatest_suite_info; 114 | 115 | /* Type for a suite function. */ 116 | typedef void (greatest_suite_cb)(void); 117 | 118 | /* Types for setup/teardown callbacks. If non-NULL, these will be run 119 | * and passed the pointer to their additional data. */ 120 | typedef void (greatest_setup_cb)(void *udata); 121 | typedef void (greatest_teardown_cb)(void *udata); 122 | 123 | typedef enum { 124 | GREATEST_FLAG_VERBOSE = 0x01, 125 | GREATEST_FLAG_FIRST_FAIL = 0x02, 126 | GREATEST_FLAG_LIST_ONLY = 0x04 127 | } GREATEST_FLAG; 128 | 129 | typedef struct greatest_run_info { 130 | unsigned int flags; 131 | unsigned int tests_run; /* total test count */ 132 | 133 | /* Overall pass/fail/skip counts. */ 134 | unsigned int passed; 135 | unsigned int failed; 136 | unsigned int skipped; 137 | 138 | /* currently running test suite */ 139 | greatest_suite_info suite; 140 | 141 | /* info to print about the most recent failure */ 142 | const char *fail_file; 143 | unsigned int fail_line; 144 | const char *msg; 145 | 146 | /* current setup/teardown hooks and userdata */ 147 | greatest_setup_cb *setup; 148 | void *setup_udata; 149 | greatest_teardown_cb *teardown; 150 | void *teardown_udata; 151 | 152 | /* formatting info for ".....s...F"-style output */ 153 | unsigned int col; 154 | unsigned int width; 155 | 156 | /* only run a specific suite or test */ 157 | char *suite_filter; 158 | char *test_filter; 159 | 160 | /* overall timers */ 161 | clock_t begin; 162 | clock_t end; 163 | } greatest_run_info; 164 | 165 | /* Global var for the current testing context. 166 | * Initialized by GREATEST_MAIN_DEFS(). */ 167 | extern greatest_run_info greatest_info; 168 | 169 | 170 | /********************** 171 | * Exported functions * 172 | **********************/ 173 | 174 | void greatest_do_pass(const char *name); 175 | void greatest_do_fail(const char *name); 176 | void greatest_do_skip(const char *name); 177 | int greatest_pre_test(const char *name); 178 | void greatest_post_test(const char *name, int res); 179 | void greatest_usage(const char *name); 180 | void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata); 181 | void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, void *udata); 182 | 183 | 184 | /********** 185 | * Macros * 186 | **********/ 187 | 188 | /* Define a suite. */ 189 | #define GREATEST_SUITE(NAME) void NAME(void) 190 | 191 | /* Start defining a test function. 192 | * The arguments are not included, to allow parametric testing. */ 193 | #define GREATEST_TEST static int 194 | 195 | /* Run a suite. */ 196 | #define GREATEST_RUN_SUITE(S_NAME) greatest_run_suite(S_NAME, #S_NAME) 197 | 198 | /* Run a test in the current suite. */ 199 | #define GREATEST_RUN_TEST(TEST) \ 200 | do { \ 201 | if (greatest_pre_test(#TEST) == 1) { \ 202 | int res = TEST(); \ 203 | greatest_post_test(#TEST, res); \ 204 | } else if (GREATEST_LIST_ONLY()) { \ 205 | fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ 206 | } \ 207 | } while (0) 208 | 209 | /* Run a test in the current suite with one void* argument, 210 | * which can be a pointer to a struct with multiple arguments. */ 211 | #define GREATEST_RUN_TEST1(TEST, ENV) \ 212 | do { \ 213 | if (greatest_pre_test(#TEST) == 1) { \ 214 | int res = TEST(ENV); \ 215 | greatest_post_test(#TEST, res); \ 216 | } else if (GREATEST_LIST_ONLY()) { \ 217 | fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ 218 | } \ 219 | } while (0) 220 | 221 | /* If __VA_ARGS__ (C99) is supported, allow parametric testing 222 | * without needing to manually manage the argument struct. */ 223 | #if __STDC_VERSION__ >= 19901L 224 | #define GREATEST_RUN_TESTp(TEST, ...) \ 225 | do { \ 226 | if (greatest_pre_test(#TEST) == 1) { \ 227 | int res = TEST(__VA_ARGS__); \ 228 | greatest_post_test(#TEST, res); \ 229 | } else if (GREATEST_LIST_ONLY()) { \ 230 | fprintf(GREATEST_STDOUT, " %s\n", #TEST); \ 231 | } \ 232 | } while (0) 233 | #endif 234 | 235 | 236 | /* Check if the test runner is in verbose mode. */ 237 | #define GREATEST_IS_VERBOSE() (greatest_info.flags & GREATEST_FLAG_VERBOSE) 238 | #define GREATEST_LIST_ONLY() (greatest_info.flags & GREATEST_FLAG_LIST_ONLY) 239 | #define GREATEST_FIRST_FAIL() (greatest_info.flags & GREATEST_FLAG_FIRST_FAIL) 240 | #define GREATEST_FAILURE_ABORT() (greatest_info.suite.failed > 0 && GREATEST_FIRST_FAIL()) 241 | 242 | /* Message-less forms. */ 243 | #define GREATEST_PASS() GREATEST_PASSm(NULL) 244 | #define GREATEST_FAIL() GREATEST_FAILm(NULL) 245 | #define GREATEST_SKIP() GREATEST_SKIPm(NULL) 246 | #define GREATEST_ASSERT(COND) GREATEST_ASSERTm(#COND, COND) 247 | #define GREATEST_ASSERT_FALSE(COND) GREATEST_ASSERT_FALSEm(#COND, COND) 248 | #define GREATEST_ASSERT_EQ(EXP, GOT) GREATEST_ASSERT_EQm(#EXP " != " #GOT, EXP, GOT) 249 | #define GREATEST_ASSERT_STR_EQ(EXP, GOT) GREATEST_ASSERT_STR_EQm(#EXP " != " #GOT, EXP, GOT) 250 | 251 | /* The following forms take an additional message argument first, 252 | * to be displayed by the test runner. */ 253 | 254 | /* Fail if a condition is not true, with message. */ 255 | #define GREATEST_ASSERTm(MSG, COND) \ 256 | do { \ 257 | greatest_info.msg = MSG; \ 258 | greatest_info.fail_file = __FILE__; \ 259 | greatest_info.fail_line = __LINE__; \ 260 | if (!(COND)) { return -1; } \ 261 | greatest_info.msg = NULL; \ 262 | } while (0) 263 | 264 | #define GREATEST_ASSERT_FALSEm(MSG, COND) \ 265 | do { \ 266 | greatest_info.msg = MSG; \ 267 | greatest_info.fail_file = __FILE__; \ 268 | greatest_info.fail_line = __LINE__; \ 269 | if ((COND)) { return -1; } \ 270 | greatest_info.msg = NULL; \ 271 | } while (0) 272 | 273 | #define GREATEST_ASSERT_EQm(MSG, EXP, GOT) \ 274 | do { \ 275 | greatest_info.msg = MSG; \ 276 | greatest_info.fail_file = __FILE__; \ 277 | greatest_info.fail_line = __LINE__; \ 278 | if ((EXP) != (GOT)) { return -1; } \ 279 | greatest_info.msg = NULL; \ 280 | } while (0) 281 | 282 | #define GREATEST_ASSERT_STR_EQm(MSG, EXP, GOT) \ 283 | do { \ 284 | const char *exp_s = (EXP); \ 285 | const char *got_s = (GOT); \ 286 | greatest_info.msg = MSG; \ 287 | greatest_info.fail_file = __FILE__; \ 288 | greatest_info.fail_line = __LINE__; \ 289 | if (0 != strcmp(exp_s, got_s)) { \ 290 | fprintf(GREATEST_STDOUT, \ 291 | "Expected:\n####\n%s\n####\n", exp_s); \ 292 | fprintf(GREATEST_STDOUT, \ 293 | "Got:\n####\n%s\n####\n", got_s); \ 294 | return -1; \ 295 | } \ 296 | greatest_info.msg = NULL; \ 297 | } while (0) 298 | 299 | #define GREATEST_PASSm(MSG) \ 300 | do { \ 301 | greatest_info.msg = MSG; \ 302 | return 0; \ 303 | } while (0) 304 | 305 | #define GREATEST_FAILm(MSG) \ 306 | do { \ 307 | greatest_info.fail_file = __FILE__; \ 308 | greatest_info.fail_line = __LINE__; \ 309 | greatest_info.msg = MSG; \ 310 | return -1; \ 311 | } while (0) 312 | 313 | #define GREATEST_SKIPm(MSG) \ 314 | do { \ 315 | greatest_info.msg = MSG; \ 316 | return 1; \ 317 | } while (0) 318 | 319 | #define GREATEST_SET_TIME(NAME) \ 320 | NAME = clock(); \ 321 | if (NAME == (clock_t) -1) { \ 322 | fprintf(GREATEST_STDOUT, \ 323 | "clock error: %s\n", #NAME); \ 324 | exit(EXIT_FAILURE); \ 325 | } 326 | 327 | #define GREATEST_CLOCK_DIFF(C1, C2) \ 328 | fprintf(GREATEST_STDOUT, " (%lu ticks, %.3f sec)", \ 329 | (long unsigned int) (C2) - (long unsigned int)(C1), \ 330 | (double)((C2) - (C1)) / (1.0 * (double)CLOCKS_PER_SEC)) \ 331 | 332 | /* Include several function definitions in the main test file. */ 333 | #define GREATEST_MAIN_DEFS() \ 334 | \ 335 | /* Is FILTER a subset of NAME? */ \ 336 | static int greatest_name_match(const char *name, \ 337 | const char *filter) { \ 338 | size_t offset = 0; \ 339 | size_t filter_len = strlen(filter); \ 340 | while (name[offset] != '\0') { \ 341 | if (name[offset] == filter[0]) { \ 342 | if (0 == strncmp(&name[offset], filter, filter_len)) { \ 343 | return 1; \ 344 | } \ 345 | } \ 346 | offset++; \ 347 | } \ 348 | \ 349 | return 0; \ 350 | } \ 351 | \ 352 | int greatest_pre_test(const char *name) { \ 353 | if (!GREATEST_LIST_ONLY() \ 354 | && (!GREATEST_FIRST_FAIL() || greatest_info.suite.failed == 0) \ 355 | && (greatest_info.test_filter == NULL || \ 356 | greatest_name_match(name, greatest_info.test_filter))) { \ 357 | GREATEST_SET_TIME(greatest_info.suite.pre_test); \ 358 | if (greatest_info.setup) { \ 359 | greatest_info.setup(greatest_info.setup_udata); \ 360 | } \ 361 | return 1; /* test should be run */ \ 362 | } else { \ 363 | return 0; /* skipped */ \ 364 | } \ 365 | } \ 366 | \ 367 | void greatest_post_test(const char *name, int res) { \ 368 | GREATEST_SET_TIME(greatest_info.suite.post_test); \ 369 | if (greatest_info.teardown) { \ 370 | void *udata = greatest_info.teardown_udata; \ 371 | greatest_info.teardown(udata); \ 372 | } \ 373 | \ 374 | if (res < 0) { \ 375 | greatest_do_fail(name); \ 376 | } else if (res > 0) { \ 377 | greatest_do_skip(name); \ 378 | } else if (res == 0) { \ 379 | greatest_do_pass(name); \ 380 | } \ 381 | greatest_info.suite.tests_run++; \ 382 | greatest_info.col++; \ 383 | if (GREATEST_IS_VERBOSE()) { \ 384 | GREATEST_CLOCK_DIFF(greatest_info.suite.pre_test, \ 385 | greatest_info.suite.post_test); \ 386 | fprintf(GREATEST_STDOUT, "\n"); \ 387 | } else if (greatest_info.col % greatest_info.width == 0) { \ 388 | fprintf(GREATEST_STDOUT, "\n"); \ 389 | greatest_info.col = 0; \ 390 | } \ 391 | if (GREATEST_STDOUT == stdout) fflush(stdout); \ 392 | } \ 393 | \ 394 | static void greatest_run_suite(greatest_suite_cb *suite_cb, \ 395 | const char *suite_name) { \ 396 | if (greatest_info.suite_filter && \ 397 | !greatest_name_match(suite_name, greatest_info.suite_filter)) { \ 398 | return; \ 399 | } \ 400 | if (GREATEST_FIRST_FAIL() && greatest_info.failed > 0) { return; } \ 401 | greatest_info.suite.tests_run = 0; \ 402 | greatest_info.suite.failed = 0; \ 403 | greatest_info.suite.passed = 0; \ 404 | greatest_info.suite.skipped = 0; \ 405 | greatest_info.suite.pre_suite = 0; \ 406 | greatest_info.suite.post_suite = 0; \ 407 | greatest_info.suite.pre_test = 0; \ 408 | greatest_info.suite.post_test = 0; \ 409 | greatest_info.col = 0; \ 410 | fprintf(GREATEST_STDOUT, "\n* Suite %s:\n", suite_name); \ 411 | GREATEST_SET_TIME(greatest_info.suite.pre_suite); \ 412 | suite_cb(); \ 413 | GREATEST_SET_TIME(greatest_info.suite.post_suite); \ 414 | if (greatest_info.suite.tests_run > 0) { \ 415 | fprintf(GREATEST_STDOUT, \ 416 | "\n%u tests - %u pass, %u fail, %u skipped", \ 417 | greatest_info.suite.tests_run, \ 418 | greatest_info.suite.passed, \ 419 | greatest_info.suite.failed, \ 420 | greatest_info.suite.skipped); \ 421 | GREATEST_CLOCK_DIFF(greatest_info.suite.pre_suite, \ 422 | greatest_info.suite.post_suite); \ 423 | fprintf(GREATEST_STDOUT, "\n"); \ 424 | } \ 425 | greatest_info.setup = NULL; \ 426 | greatest_info.setup_udata = NULL; \ 427 | greatest_info.teardown = NULL; \ 428 | greatest_info.teardown_udata = NULL; \ 429 | greatest_info.passed += greatest_info.suite.passed; \ 430 | greatest_info.failed += greatest_info.suite.failed; \ 431 | greatest_info.skipped += greatest_info.suite.skipped; \ 432 | greatest_info.tests_run += greatest_info.suite.tests_run; \ 433 | } \ 434 | \ 435 | void greatest_do_pass(const char *name) { \ 436 | if (GREATEST_IS_VERBOSE()) { \ 437 | fprintf(GREATEST_STDOUT, "PASS %s: %s", \ 438 | name, greatest_info.msg ? greatest_info.msg : ""); \ 439 | } else { \ 440 | fprintf(GREATEST_STDOUT, "."); \ 441 | } \ 442 | greatest_info.suite.passed++; \ 443 | } \ 444 | \ 445 | void greatest_do_fail(const char *name) { \ 446 | if (GREATEST_IS_VERBOSE()) { \ 447 | fprintf(GREATEST_STDOUT, \ 448 | "FAIL %s: %s (%s:%u)", \ 449 | name, greatest_info.msg ? greatest_info.msg : "", \ 450 | greatest_info.fail_file, greatest_info.fail_line); \ 451 | } else { \ 452 | fprintf(GREATEST_STDOUT, "F"); \ 453 | greatest_info.col++; \ 454 | /* add linebreak if in line of '.'s */ \ 455 | if (greatest_info.col != 0) { \ 456 | fprintf(GREATEST_STDOUT, "\n"); \ 457 | greatest_info.col = 0; \ 458 | } \ 459 | fprintf(GREATEST_STDOUT, "FAIL %s: %s (%s:%u)\n", \ 460 | name, \ 461 | greatest_info.msg ? greatest_info.msg : "", \ 462 | greatest_info.fail_file, greatest_info.fail_line); \ 463 | } \ 464 | greatest_info.suite.failed++; \ 465 | } \ 466 | \ 467 | void greatest_do_skip(const char *name) { \ 468 | if (GREATEST_IS_VERBOSE()) { \ 469 | fprintf(GREATEST_STDOUT, "SKIP %s: %s", \ 470 | name, \ 471 | greatest_info.msg ? \ 472 | greatest_info.msg : "" ); \ 473 | } else { \ 474 | fprintf(GREATEST_STDOUT, "s"); \ 475 | } \ 476 | greatest_info.suite.skipped++; \ 477 | } \ 478 | \ 479 | void greatest_usage(const char *name) { \ 480 | fprintf(GREATEST_STDOUT, \ 481 | "Usage: %s [-hlfv] [-s SUITE] [-t TEST]\n" \ 482 | " -h print this Help\n" \ 483 | " -l List suites and their tests, then exit\n" \ 484 | " -f Stop runner after first failure\n" \ 485 | " -v Verbose output\n" \ 486 | " -s SUITE only run suite named SUITE\n" \ 487 | " -t TEST only run test named TEST\n", \ 488 | name); \ 489 | } \ 490 | \ 491 | void GREATEST_SET_SETUP_CB(greatest_setup_cb *cb, void *udata) { \ 492 | greatest_info.setup = cb; \ 493 | greatest_info.setup_udata = udata; \ 494 | } \ 495 | \ 496 | void GREATEST_SET_TEARDOWN_CB(greatest_teardown_cb *cb, \ 497 | void *udata) { \ 498 | greatest_info.teardown = cb; \ 499 | greatest_info.teardown_udata = udata; \ 500 | } \ 501 | \ 502 | greatest_run_info greatest_info 503 | 504 | /* Handle command-line arguments, etc. */ 505 | #define GREATEST_MAIN_BEGIN() \ 506 | do { \ 507 | int i = 0; \ 508 | memset(&greatest_info, 0, sizeof(greatest_info)); \ 509 | if (greatest_info.width == 0) { \ 510 | greatest_info.width = GREATEST_DEFAULT_WIDTH; \ 511 | } \ 512 | for (i = 1; i < argc; i++) { \ 513 | if (0 == strcmp("-t", argv[i])) { \ 514 | if (argc <= i + 1) { \ 515 | greatest_usage(argv[0]); \ 516 | exit(EXIT_FAILURE); \ 517 | } \ 518 | greatest_info.test_filter = argv[i+1]; \ 519 | i++; \ 520 | } else if (0 == strcmp("-s", argv[i])) { \ 521 | if (argc <= i + 1) { \ 522 | greatest_usage(argv[0]); \ 523 | exit(EXIT_FAILURE); \ 524 | } \ 525 | greatest_info.suite_filter = argv[i+1]; \ 526 | i++; \ 527 | } else if (0 == strcmp("-f", argv[i])) { \ 528 | greatest_info.flags |= GREATEST_FLAG_FIRST_FAIL; \ 529 | } else if (0 == strcmp("-v", argv[i])) { \ 530 | greatest_info.flags |= GREATEST_FLAG_VERBOSE; \ 531 | } else if (0 == strcmp("-l", argv[i])) { \ 532 | greatest_info.flags |= GREATEST_FLAG_LIST_ONLY; \ 533 | } else if (0 == strcmp("-h", argv[i])) { \ 534 | greatest_usage(argv[0]); \ 535 | exit(EXIT_SUCCESS); \ 536 | } else { \ 537 | fprintf(GREATEST_STDOUT, \ 538 | "Unknown argument '%s'\n", argv[i]); \ 539 | greatest_usage(argv[0]); \ 540 | exit(EXIT_FAILURE); \ 541 | } \ 542 | } \ 543 | } while (0); \ 544 | GREATEST_SET_TIME(greatest_info.begin) 545 | 546 | #define GREATEST_MAIN_END() \ 547 | do { \ 548 | if (!GREATEST_LIST_ONLY()) { \ 549 | GREATEST_SET_TIME(greatest_info.end); \ 550 | fprintf(GREATEST_STDOUT, \ 551 | "\nTotal: %u tests", greatest_info.tests_run); \ 552 | GREATEST_CLOCK_DIFF(greatest_info.begin, \ 553 | greatest_info.end); \ 554 | fprintf(GREATEST_STDOUT, "\n"); \ 555 | fprintf(GREATEST_STDOUT, \ 556 | "Pass: %u, fail: %u, skip: %u.\n", \ 557 | greatest_info.passed, \ 558 | greatest_info.failed, greatest_info.skipped); \ 559 | } \ 560 | return (greatest_info.failed > 0 \ 561 | ? EXIT_FAILURE : EXIT_SUCCESS); \ 562 | } while (0) 563 | 564 | /* Make abbreviations without the GREATEST_ prefix for the 565 | * most commonly used symbols. */ 566 | #if GREATEST_USE_ABBREVS 567 | #define TEST GREATEST_TEST 568 | #define SUITE GREATEST_SUITE 569 | #define RUN_TEST GREATEST_RUN_TEST 570 | #define RUN_TEST1 GREATEST_RUN_TEST1 571 | #define RUN_SUITE GREATEST_RUN_SUITE 572 | #define ASSERT GREATEST_ASSERT 573 | #define ASSERTm GREATEST_ASSERTm 574 | #define ASSERT_FALSE GREATEST_ASSERT_FALSE 575 | #define ASSERT_EQ GREATEST_ASSERT_EQ 576 | #define ASSERT_STR_EQ GREATEST_ASSERT_STR_EQ 577 | #define ASSERT_FALSEm GREATEST_ASSERT_FALSEm 578 | #define ASSERT_EQm GREATEST_ASSERT_EQm 579 | #define ASSERT_STR_EQm GREATEST_ASSERT_STR_EQm 580 | #define PASS GREATEST_PASS 581 | #define FAIL GREATEST_FAIL 582 | #define SKIP GREATEST_SKIP 583 | #define PASSm GREATEST_PASSm 584 | #define FAILm GREATEST_FAILm 585 | #define SKIPm GREATEST_SKIPm 586 | #define SET_SETUP GREATEST_SET_SETUP_CB 587 | #define SET_TEARDOWN GREATEST_SET_TEARDOWN_CB 588 | 589 | #if __STDC_VERSION__ >= 19901L 590 | #endif /* C99 */ 591 | #define RUN_TESTp GREATEST_RUN_TESTp 592 | #endif /* USE_ABBREVS */ 593 | 594 | #endif 595 | -------------------------------------------------------------------------------- /test/test_list.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Intrusive single/double linked list for C++. 3 | 4 | version 0.1, augusti, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #include "greatest.h" 28 | #include "../list.h" 29 | 30 | struct list_test_struct 31 | { 32 | int value; 33 | 34 | LIST_NODE(list_test_struct) list1; 35 | LIST_NODE(list_test_struct) list2; 36 | }; 37 | 38 | TEST insert_remove() 39 | { 40 | // create list 41 | LIST(list_test_struct, list1) list1; 42 | 43 | // insert some elements 44 | list_test_struct s1, s2, s3, s4; 45 | 46 | list1.insert_head( &s1 ); 47 | list1.insert_head( &s2 ); 48 | list1.insert_head( &s3 ); 49 | list1.insert_head( &s4 ); 50 | 51 | list_test_struct* elem; 52 | 53 | elem = list1.head(); 54 | ASSERT_EQ( &s4, elem ); elem = list1.next( elem ); 55 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 56 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 57 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 58 | ASSERT_EQ( 0x0, elem ); 59 | 60 | ASSERT_EQ( &s4, list1.remove_head() ); 61 | ASSERT_EQ( &s3, list1.remove_head() ); 62 | 63 | elem = list1.head(); 64 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 65 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 66 | ASSERT_EQ( 0x0, elem ); 67 | 68 | // remove elements 69 | list1.clear(); 70 | 71 | return 0; 72 | } 73 | 74 | TEST insert_tail() 75 | { 76 | // create list 77 | LIST(list_test_struct, list1) list1; 78 | 79 | ASSERT_EQ( 0x0, list1.head() ); 80 | ASSERT_EQ( 0x0, list1.tail() ); 81 | 82 | // insert some elements 83 | list_test_struct s1, s2, s3, s4; 84 | 85 | list1.insert_tail( &s1 ); 86 | ASSERT_EQ( &s1, list1.tail() ); 87 | ASSERT_EQ( &s1, list1.head() ); 88 | list1.insert_tail( &s2 ); 89 | ASSERT_EQ( &s2, list1.tail() ); 90 | ASSERT_EQ( &s1, list1.head() ); 91 | list1.insert_tail( &s3 ); 92 | ASSERT_EQ( &s3, list1.tail() ); 93 | ASSERT_EQ( &s1, list1.head() ); 94 | list1.insert_tail( &s4 ); 95 | ASSERT_EQ( &s4, list1.tail() ); 96 | ASSERT_EQ( &s1, list1.head() ); 97 | 98 | // iterate 99 | list_test_struct* elem; 100 | 101 | elem = list1.head(); 102 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 103 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 104 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 105 | ASSERT_EQ( &s4, elem ); elem = list1.next( elem ); 106 | ASSERT_EQ( 0x0, elem ); 107 | 108 | list1.remove( &s4 ); 109 | elem = list1.head(); 110 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 111 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 112 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 113 | ASSERT_EQ( 0x0, elem ); 114 | ASSERT_EQ( &s3, list1.tail() ); 115 | 116 | list1.remove( &s3 ); 117 | ASSERT_EQ( &s2, list1.tail() ); 118 | 119 | elem = list1.head(); 120 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 121 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 122 | ASSERT_EQ( 0x0, elem ); 123 | 124 | // remove tail 125 | ASSERT_EQ( &s2, list1.remove_tail() ); 126 | elem = list1.head(); 127 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 128 | ASSERT_EQ( 0x0, elem ); 129 | 130 | list1.clear(); 131 | ASSERT_EQ( 0x0, list1.tail() ); 132 | 133 | return 0; 134 | } 135 | 136 | 137 | TEST insert_after() 138 | { 139 | // create list 140 | LIST(list_test_struct, list1) list1; 141 | 142 | // insert some elements 143 | list_test_struct s1, s2, s5; 144 | 145 | list1.insert_head( &s1 ); 146 | list1.insert_head( &s2 ); 147 | 148 | // insert s5 after s2 after 149 | list1.insert_after( &s5, &s2 ); 150 | 151 | ASSERT_EQ( &s1, list1.remove_tail() ); 152 | ASSERT_EQ( &s2, list1.head() ); 153 | ASSERT_EQ( &s5, list1.tail() ); 154 | 155 | return 0; 156 | } 157 | 158 | 159 | TEST insert_before() 160 | { 161 | // create list 162 | LIST(list_test_struct, list1) list1; 163 | 164 | // insert some elements 165 | list_test_struct s1, s2, s5; 166 | 167 | list1.insert_head( &s1 ); 168 | list1.insert_head( &s2 ); 169 | 170 | // insert s5 after s2 after 171 | list1.insert_before( &s5, &s1 ); 172 | 173 | ASSERT_EQ( &s2, list1.remove_head() ); 174 | ASSERT_EQ( &s5, list1.head() ); 175 | ASSERT_EQ( &s1, list1.tail() ); 176 | 177 | return 0; 178 | } 179 | 180 | TEST head_tail_update_at_head() 181 | { 182 | LIST(list_test_struct, list1) list1; 183 | ASSERT_EQ( 0x0, list1.head() ); 184 | ASSERT_EQ( 0x0, list1.tail() ); 185 | 186 | list_test_struct s1, s2, s3; 187 | list1.insert_head( &s1 ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s1, list1.tail() ); 188 | list1.insert_head( &s2 ); ASSERT_EQ( &s2, list1.head() ); ASSERT_EQ( &s1, list1.tail() ); 189 | list1.insert_head( &s3 ); ASSERT_EQ( &s3, list1.head() ); ASSERT_EQ( &s1, list1.tail() ); 190 | 191 | ASSERT_EQ( &s3, list1.remove_head() ); ASSERT_EQ( &s2, list1.head() ); ASSERT_EQ( &s1, list1.tail() ); 192 | ASSERT_EQ( &s2, list1.remove_head() ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s1, list1.tail() ); 193 | ASSERT_EQ( &s1, list1.remove_head() ); ASSERT_EQ( 0x0, list1.head() ); ASSERT_EQ( 0x0, list1.tail() ); 194 | 195 | return 0; 196 | } 197 | 198 | TEST head_tail_update_at_tail() 199 | { 200 | LIST(list_test_struct, list1) list1; 201 | ASSERT_EQ( 0x0, list1.head() ); 202 | ASSERT_EQ( 0x0, list1.tail() ); 203 | 204 | list_test_struct s1, s2, s3; 205 | list1.insert_tail( &s1 ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s1, list1.tail() ); 206 | list1.insert_tail( &s2 ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s2, list1.tail() ); 207 | list1.insert_tail( &s3 ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s3, list1.tail() ); 208 | 209 | ASSERT_EQ( &s3, list1.remove_tail() ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s2, list1.tail() ); 210 | ASSERT_EQ( &s2, list1.remove_tail() ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s1, list1.tail() ); 211 | ASSERT_EQ( &s1, list1.remove_tail() ); ASSERT_EQ( 0x0, list1.head() ); ASSERT_EQ( 0x0, list1.tail() ); 212 | 213 | return 0; 214 | } 215 | 216 | TEST head_tail_update_at_middle() 217 | { 218 | LIST(list_test_struct, list1) list1; 219 | ASSERT_EQ( 0x0, list1.head() ); 220 | ASSERT_EQ( 0x0, list1.tail() ); 221 | 222 | list_test_struct s1, s2, s3, s4; 223 | list1.insert_head( &s4 ); 224 | list1.insert_head( &s3 ); 225 | list1.insert_head( &s2 ); 226 | list1.insert_head( &s1 ); 227 | 228 | list1.remove( &s3 ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s4, list1.tail() ); 229 | list1.remove( &s2 ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s4, list1.tail() ); 230 | list1.remove( &s1 ); ASSERT_EQ( &s4, list1.head() ); ASSERT_EQ( &s4, list1.tail() ); 231 | list1.remove( &s4 ); ASSERT_EQ( 0x0, list1.head() ); ASSERT_EQ( 0x0, list1.tail() ); 232 | 233 | list1.insert_head( &s4 ); 234 | list1.insert_head( &s3 ); 235 | list1.insert_head( &s2 ); 236 | list1.insert_head( &s1 ); 237 | 238 | list1.remove( &s3 ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s4, list1.tail() ); 239 | list1.remove( &s2 ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s4, list1.tail() ); 240 | list1.remove( &s4 ); ASSERT_EQ( &s1, list1.head() ); ASSERT_EQ( &s1, list1.tail() ); 241 | list1.remove( &s1 ); ASSERT_EQ( 0x0, list1.head() ); ASSERT_EQ( 0x0, list1.tail() ); 242 | 243 | return 0; 244 | } 245 | 246 | TEST insert_remove_middle() 247 | { 248 | // create list 249 | LIST(list_test_struct, list1) list1; 250 | 251 | // insert some elements 252 | list_test_struct s1, s2, s3, s4, s5; 253 | 254 | list1.insert_head( &s1 ); 255 | list1.insert_head( &s2 ); 256 | list1.insert_head( &s3 ); 257 | list1.insert_head( &s4 ); 258 | 259 | // insert s5 after s2 after 260 | list1.insert_after( &s5, &s2 ); 261 | 262 | // iterate 263 | list_test_struct* elem; 264 | 265 | elem = list1.head(); 266 | ASSERT_EQ( &s4, elem ); elem = list1.next( elem ); 267 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 268 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 269 | ASSERT_EQ( &s5, elem ); elem = list1.next( elem ); 270 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 271 | ASSERT_EQ( 0x0, elem ); 272 | 273 | list1.remove( &s2 ); 274 | ASSERT_EQ( &s1, list1.tail() ); 275 | 276 | elem = list1.head(); 277 | ASSERT_EQ( &s4, elem ); elem = list1.next( elem ); 278 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 279 | ASSERT_EQ( &s5, elem ); elem = list1.next( elem ); 280 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 281 | ASSERT_EQ( 0x0, elem ); 282 | 283 | // remove tail 284 | ASSERT_EQ( &s1, list1.tail() ); 285 | ASSERT_EQ( &s1, list1.remove_tail() ); 286 | ASSERT_EQ( &s5, list1.tail() ); 287 | 288 | elem = list1.head(); 289 | ASSERT_EQ( &s4, elem ); elem = list1.next( elem ); 290 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 291 | ASSERT_EQ( &s5, elem ); elem = list1.next( elem ); 292 | ASSERT_EQ( 0x0, elem ); 293 | 294 | // remove elements 295 | list1.clear(); 296 | 297 | return 0; 298 | } 299 | 300 | GREATEST_SUITE(list_tests) 301 | { 302 | RUN_TEST( insert_remove ); 303 | RUN_TEST( insert_tail ); 304 | RUN_TEST( insert_after ); 305 | RUN_TEST( insert_before ); 306 | RUN_TEST( head_tail_update_at_head ); 307 | RUN_TEST( head_tail_update_at_tail ); 308 | RUN_TEST( head_tail_update_at_middle ); 309 | RUN_TEST( insert_remove_middle ); 310 | } 311 | -------------------------------------------------------------------------------- /test/test_list_main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Intrusive single/double linked list for C++. 3 | 4 | version 0.1, august, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #include "greatest.h" 28 | 29 | extern SUITE(list_tests); 30 | extern SUITE(slist_tests); 31 | 32 | GREATEST_MAIN_DEFS(); 33 | 34 | int main( int argc, char **argv ) 35 | { 36 | GREATEST_MAIN_BEGIN(); 37 | RUN_SUITE( list_tests ); 38 | RUN_SUITE( slist_tests ); 39 | GREATEST_MAIN_END(); 40 | } 41 | -------------------------------------------------------------------------------- /test/test_slist.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Intrusive single/double linked list for C++. 3 | 4 | version 0.1, augusti, 2013 5 | 6 | Copyright (C) 2013- Fredrik Kihlander 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any damages 10 | arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, 13 | including commercial applications, and to alter it and redistribute it 14 | freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you must not 17 | claim that you wrote the original software. If you use this software 18 | in a product, an acknowledgment in the product documentation would be 19 | appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Fredrik Kihlander 25 | */ 26 | 27 | #include "greatest.h" 28 | #include "../slist.h" 29 | 30 | struct slist_test_struct 31 | { 32 | int value; 33 | 34 | SLIST_NODE(slist_test_struct) list1; 35 | SLIST_NODE(slist_test_struct) list2; 36 | }; 37 | 38 | TEST insert_remove() 39 | { 40 | // create list 41 | SLIST(slist_test_struct, list1) list1; 42 | 43 | // insert some elements 44 | slist_test_struct s1, s2, s3, s4; 45 | 46 | list1.insert_head( &s1 ); 47 | list1.insert_head( &s2 ); 48 | list1.insert_head( &s3 ); 49 | list1.insert_head( &s4 ); 50 | 51 | // itterate 52 | slist_test_struct* elem; 53 | 54 | elem = list1.head(); 55 | ASSERT_EQ( &s4, elem ); elem = list1.next( elem ); 56 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 57 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 58 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 59 | ASSERT_EQ( 0x0, elem ); 60 | 61 | ASSERT_EQ( &s4, list1.remove_head() ); 62 | ASSERT_EQ( &s3, list1.remove_head() ); 63 | 64 | elem = list1.head(); 65 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 66 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 67 | ASSERT_EQ( 0x0, elem ); 68 | 69 | // remove elements 70 | list1.clear(); 71 | 72 | return 0; 73 | } 74 | 75 | TEST insert_tail() 76 | { 77 | // create list 78 | SLIST(slist_test_struct, list1) list1; 79 | 80 | ASSERT_EQ( 0x0, list1.head() ); 81 | ASSERT_EQ( 0x0, list1.tail() ); 82 | 83 | // insert some elements 84 | slist_test_struct s1, s2, s3, s4; 85 | 86 | list1.insert_tail( &s1 ); 87 | ASSERT_EQ( &s1, list1.tail() ); 88 | ASSERT_EQ( &s1, list1.head() ); 89 | list1.insert_tail( &s2 ); 90 | ASSERT_EQ( &s2, list1.tail() ); 91 | ASSERT_EQ( &s1, list1.head() ); 92 | list1.insert_tail( &s3 ); 93 | ASSERT_EQ( &s3, list1.tail() ); 94 | ASSERT_EQ( &s1, list1.head() ); 95 | list1.insert_tail( &s4 ); 96 | ASSERT_EQ( &s4, list1.tail() ); 97 | ASSERT_EQ( &s1, list1.head() ); 98 | 99 | // itterate 100 | slist_test_struct* elem; 101 | 102 | elem = list1.head(); 103 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 104 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 105 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 106 | ASSERT_EQ( &s4, elem ); elem = list1.next( elem ); 107 | ASSERT_EQ( 0x0, elem ); 108 | 109 | list1.remove( &s4 ); 110 | elem = list1.head(); 111 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 112 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 113 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 114 | ASSERT_EQ( 0x0, elem ); 115 | ASSERT_EQ( &s3, list1.tail() ); 116 | 117 | list1.remove( &s3 ); 118 | ASSERT_EQ( &s2, list1.tail() ); 119 | 120 | elem = list1.head(); 121 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 122 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 123 | ASSERT_EQ( 0x0, elem ); 124 | 125 | list1.clear(); 126 | ASSERT_EQ( 0x0, list1.tail() ); 127 | 128 | return 0; 129 | } 130 | 131 | TEST insert_remove_middle() 132 | { 133 | // create list 134 | SLIST(slist_test_struct, list1) list1; 135 | 136 | // insert some elements 137 | slist_test_struct s1, s2, s3, s4, s5; 138 | 139 | list1.insert_head( &s1 ); 140 | list1.insert_head( &s2 ); 141 | list1.insert_head( &s3 ); 142 | list1.insert_head( &s4 ); 143 | 144 | // insert s5 after s2 after 145 | list1.insert_after( &s5, &s2 ); 146 | 147 | // itterate 148 | slist_test_struct* elem; 149 | 150 | elem = list1.head(); 151 | ASSERT_EQ( &s4, elem ); elem = list1.next( elem ); 152 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 153 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 154 | ASSERT_EQ( &s5, elem ); elem = list1.next( elem ); 155 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 156 | ASSERT_EQ( 0x0, elem ); 157 | 158 | list1.remove( &s2 ); 159 | ASSERT_EQ( &s4, list1.head() ); 160 | ASSERT_EQ( &s1, list1.tail() ); 161 | 162 | elem = list1.head(); 163 | ASSERT_EQ( &s4, elem ); elem = list1.next( elem ); 164 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 165 | ASSERT_EQ( &s5, elem ); elem = list1.next( elem ); 166 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 167 | ASSERT_EQ( 0x0, elem ); 168 | 169 | // remove elements 170 | list1.clear(); 171 | 172 | return 0; 173 | } 174 | 175 | TEST in_2_lists() 176 | { 177 | // create lists 178 | SLIST(slist_test_struct, list1) list1; 179 | SLIST(slist_test_struct, list2) list2; 180 | 181 | // insert some elements 182 | slist_test_struct s1, s2, s3, s4; 183 | 184 | list1.insert_head( &s1 ); 185 | list1.insert_head( &s2 ); 186 | list1.insert_head( &s3 ); 187 | list1.insert_head( &s4 ); 188 | 189 | list2.insert_head( &s4 ); 190 | list2.insert_head( &s3 ); 191 | list2.insert_head( &s2 ); 192 | list2.insert_head( &s1 ); 193 | 194 | // itterate 195 | slist_test_struct* elem = list1.head(); 196 | ASSERT_EQ( &s4, elem ); elem = list1.next( elem ); 197 | ASSERT_EQ( &s3, elem ); elem = list1.next( elem ); 198 | ASSERT_EQ( &s2, elem ); elem = list1.next( elem ); 199 | ASSERT_EQ( &s1, elem ); elem = list1.next( elem ); 200 | ASSERT_EQ( 0x0, elem ); 201 | 202 | elem = list2.head(); 203 | ASSERT_EQ( &s1, elem ); elem = list2.next( elem ); 204 | ASSERT_EQ( &s2, elem ); elem = list2.next( elem ); 205 | ASSERT_EQ( &s3, elem ); elem = list2.next( elem ); 206 | ASSERT_EQ( &s4, elem ); elem = list2.next( elem ); 207 | ASSERT_EQ( 0x0, elem ); 208 | 209 | // remove elements 210 | list1.clear(); 211 | 212 | // list 2 still ok? 213 | elem = list2.head(); 214 | ASSERT_EQ( &s1, elem ); elem = list2.next( elem ); 215 | ASSERT_EQ( &s2, elem ); elem = list2.next( elem ); 216 | ASSERT_EQ( &s3, elem ); elem = list2.next( elem ); 217 | ASSERT_EQ( &s4, elem ); elem = list2.next( elem ); 218 | ASSERT_EQ( 0x0, elem ); 219 | 220 | // remove elements 221 | list2.clear(); 222 | 223 | return 0; 224 | } 225 | 226 | TEST empty_list_head_zero() 227 | { 228 | // create lists 229 | SLIST(slist_test_struct, list1) list1; 230 | 231 | // insert some elements 232 | slist_test_struct s1; 233 | 234 | ASSERT( list1.empty() ); 235 | 236 | list1.insert_head( &s1 ); 237 | 238 | ASSERT( !list1.empty() ); 239 | 240 | list1.clear(); 241 | 242 | ASSERT( list1.empty() ); 243 | ASSERT_EQ( 0x0, list1.head() ); 244 | 245 | return 0; 246 | } 247 | 248 | GREATEST_SUITE(slist_tests) 249 | { 250 | RUN_TEST( insert_remove ); 251 | RUN_TEST( insert_tail ); 252 | RUN_TEST( insert_remove_middle ); 253 | RUN_TEST( in_2_lists ); 254 | RUN_TEST( empty_list_head_zero ); 255 | } 256 | --------------------------------------------------------------------------------