MONC
test_collections.F90
Go to the documentation of this file.
1 ! Tests the collections_mod utility functions
3  use fruit, only : assert_equals, add_fail, assert_false, assert_true
5  c_is_empty, c_pop, c_value_at, c_add, c_remove, c_insert, c_push, c_remove
6  implicit none
7 
8 contains
9  ! Tests the map_type put - ensures that key-value pairs are being added correctly and are updated
10  ! when additional put requests with existing keys are issued
11  subroutine test_map_type_put
12  type(map_type) :: my_map
13  integer :: i, j, v
14  class(*), pointer :: iptr
15 
16  call assert_equals(.true., c_is_empty(my_map), "map_type is empty")
17 
18  do i=1,10
19  allocate(iptr, source=i)
20  call c_put(my_map, str(i), iptr)
21  call assert_equals(i, c_size(my_map), "map_type size incremented with put")
22  end do
23 
24  do j=1,10
25  v = j * 10
26  allocate(iptr, source=v)
27  call c_put(my_map, str(j), iptr)
28  call assert_equals(10, c_size(my_map), "map_type size remains unchanged after duplicate key")
29  end do
30 
31  call c_free(my_map)
32  end subroutine test_map_type_put
33 
34  ! Tests that if we put many identical keys into a map_type then it will just update the value rather than
35  ! add new key-value pairs
37  type(map_type) :: my_map
38  integer :: i
39  class(*), pointer :: iptr, data
40 
41  call assert_equals(.true., c_is_empty(my_map), "map_type is empty")
42 
43  do i=1,10
44  allocate(iptr, source=i)
45  call c_put(my_map, "A", iptr)
46  data=>c_get(my_map, "A")
47  select type(data)
48  type is (integer)
49  call assert_equals(i, data, "Value of key-value pair is correct")
50  class default
51  call add_fail("Unknown type")
52  end select
53  call assert_equals(1, c_size(my_map), "Size of may is one due to unique key")
54  end do
55 
56  call c_free(my_map)
57  end subroutine test_map_type_put_unique
58 
60  type(map_type) :: my_map
61  integer, target :: i
62  class(*), pointer :: iptr, data
63  i=20
64  iptr=>i
65  call c_put(my_map, "A", iptr)
66  i=50
67  data=>c_get(my_map, "A")
68  select type(data)
69  type is (integer)
70  call assert_equals(50, data, "Value of entry has been changed through modifying original variable")
71  class default
72  call add_fail("Unknown type")
73  end select
74  call c_free(my_map)
75  end subroutine test_map_type_pointers
76 
77  ! Tests the map_type get functionality, will put in a number of key-value pairs, check that the map_type contains
78  ! the key added, gets the key and checks the value along with ensuring the key is held where we expect it.
79  ! Then it will modify the values associated with keys and check that these are represented correctly
80  subroutine test_map_type_get
81  type(map_type) :: my_map
82  integer :: i, j, v, x
83  class(*), pointer :: iptr, data
84 
85  call assert_equals(.true., c_is_empty(my_map), "map_type is empty")
86 
87  do i=1,10
88  v = i * 10
89  call assert_false(c_contains(my_map, str(i)), "map_type does not contain the key before put")
90  allocate(iptr, source=v)
91  call c_put(my_map, str(i), iptr)
92  call assert_true(c_contains(my_map, str(i)), "map_type contains the key after put")
93  data => c_get(my_map, str(i))
94  select type (data)
95  type is (integer)
96  call assert_equals(i*10, data, "Value of entry is consistent")
97  end select
98  call assert_equals(str(i), c_key_at(my_map, i), "Key at location i is consistent")
99  end do
100 
101  do j=1,10
102  x = j * 100
103  allocate(iptr, source=x)
104  call c_put(my_map, str(j), iptr)
105  data => c_get(my_map, str(j))
106  select type (data)
107  type is (integer)
108  call assert_equals(j*100, data, "Value modified due to duplicate key")
109  class default
110  call add_fail("Unknown type")
111  end select
112  data => c_value_at(my_map, j)
113  select type (data)
114  type is (integer)
115  call assert_equals(j*100, data, "Value at returned correct value at i")
116  class default
117  call add_fail("Unknown type")
118  end select
119  end do
120 
121  call c_free(my_map)
122  end subroutine test_map_type_get
123 
124  ! Will add in a load of key-value pairs into the map_type and then test removing them and ensure that the map_type
125  ! does not contain the key-value pair once the removal has completed.
127  type(map_type) :: my_map
128  integer :: i, j, v
129  class(*), pointer :: iptr, data
130 
131  call assert_equals(.true., c_is_empty(my_map), "map_type is empty")
132 
133  do i=1,10
134  v=i*10
135  allocate(iptr, source=v)
136  call c_put(my_map, str(i), iptr)
137  data=>c_get(my_map, str(i))
138  select type(data)
139  type is (integer)
140  call assert_equals(i*10, data, "Value of key is consistent")
141  class default
142  call add_fail("Unknown type")
143  end select
144  end do
145 
146  do j=1,10
147  call assert_true(c_contains(my_map, str(11-j)), "map_type contains the key pre-removal")
148  call c_remove(my_map, str(11-j))
149  call assert_false(c_contains(my_map, str(11-j)), "map_type does not contain the key post-removal")
150  call assert_equals(10-j, c_size(my_map), "Size of map_type is consistent post-removal")
151  end do
152  call assert_equals(.true., c_is_empty(my_map), "map_type is empty at the end")
153 
154  call c_free(my_map)
155  end subroutine test_map_type_remove
156 
157  ! Helper function to convert an integer into a string (for map_type keying in a loop)
158  character(len=15) function str(k)
159  integer, intent(in) :: k
160  write (str, *) k
161  str = adjustl(str)
162  end function str
163 
165  type(stack_type) :: my_stack
166  integer, target :: i
167  class(*), pointer :: iptr, data
168  i=20
169  iptr=>i
170  call c_push(my_stack, iptr)
171  i=50
172  data=>c_pop(my_stack)
173  select type(data)
174  type is (integer)
175  call assert_equals(50, data, "Stack data modified through changing original variable")
176  class default
177  call add_fail("Unknown type")
178  end select
179 
180  call c_free(my_stack)
181  end subroutine test_stack_type_pointers
182 
183  ! Tests a stack_type push and pop - ensures that it is working in LIFO order
185  type(stack_type) :: my_stack
186  integer :: i,j
187  class(*), pointer :: iptr, data
188 
189  call assert_equals(.true., c_is_empty(my_stack), "Stack is empty")
190 
191  do i=1,10
192  allocate(iptr, source=i)
193  call c_push(my_stack, iptr)
194  call assert_equals(i, c_size(my_stack), "Size of stack_type increasing as data pushed")
195  end do
196 
197  call assert_equals(.false., c_is_empty(my_stack), "Stack is not empty after values pushed")
198 
199  do j=1,10
200  data => c_pop(my_stack)
201  select type(data)
202  type is (integer)
203  call assert_equals(data, 11-j, "Stack pop gives LIFO value")
204  call assert_equals(10-j, c_size(my_stack), "Stack pop removes the LIFO value")
205  class default
206  call add_fail("Type unknown")
207  end select
208  end do
209 
210  call c_free(my_stack)
211  end subroutine test_stack_type_push_pop
212 
214  type(queue_type) :: my_queue
215  integer, target :: i
216  class(*), pointer :: iptr, data
217  i=20
218  iptr=>i
219  call c_push(my_queue, iptr)
220  i=50
221  data=>c_pop(my_queue)
222  select type(data)
223  type is (integer)
224  call assert_equals(50, data, "Queue data modified by changing the original variable")
225  class default
226  call add_fail("Unknown type")
227  end select
228 
229  call c_free(my_queue)
230  end subroutine test_queue_type_pointers
231 
232  ! Tests a queue_type push and pop - ensures that it is working in FIFO order
234  type(queue_type) :: my_queue
235  integer :: i,j
236  class(*), pointer :: iptr, data
237 
238  call assert_equals(.true., c_is_empty(my_queue), "Queue is empty")
239 
240  do i=1,10
241  allocate(iptr, source=i)
242  call c_push(my_queue, iptr)
243  call assert_equals(i, c_size(my_queue), "Queue size increases as elements are pushed")
244  end do
245 
246  call assert_equals(.false., c_is_empty(my_queue), "Queue is not empty after elements pushed")
247 
248  do j=1,10
249  data => c_pop(my_queue)
250  select type(data)
251  type is (integer)
252  call assert_equals(data, j, "Queue popped element is FIFO")
253  call assert_equals(10-j, c_size(my_queue), "Queue pop removes element")
254  class default
255  call add_fail("Type unknown")
256  end select
257  end do
258 
259  call c_free(my_queue)
260  end subroutine test_queue_type_push_pop
261 
262  ! Tests adding an element to the list_type and ensures that the list_type sizes up correctly
264  type(list_type) :: my_list
265  integer :: i
266  class(*), pointer :: iptr
267 
268  call assert_equals(.true., c_is_empty(my_list), "List is empty")
269 
270  do i=1,10
271  allocate(iptr, source=i)
272  call c_add(my_list, iptr)
273  call assert_equals(i, c_size(my_list), "List add increases list_type size")
274  end do
275 
276  call assert_equals(.false., c_is_empty(my_list), "List is not empty after element adds")
277 
278  call c_free(my_list)
279  end subroutine test_list_type_add
280 
282  type(list_type) :: my_list
283  integer, target :: i
284  class(*), pointer :: iptr, data
285  i=20
286  iptr=>i
287  call c_add(my_list, iptr)
288  i=50
289  data=>c_get(my_list, 0)
290  select type(data)
291  type is (integer)
292  call assert_equals(50, data, "List element modified by changing original value")
293  class default
294  call add_fail("Unknown type")
295  end select
296 
297  call c_free(my_list)
298  end subroutine test_list_type_pointers
299 
300  ! Adds a number of elements to the list_type and then checks each one to ensure that the value
301  ! and order has not changed
303  type(list_type) :: my_list
304  integer :: i,j
305  class(*), pointer :: iptr, data
306 
307  do i=1,10
308  allocate(iptr, source=i)
309  call c_add(my_list, iptr)
310  end do
311 
312  call assert_equals(10, c_size(my_list), "List size increased after adding elements")
313 
314  do j=1,10
315  data => c_get(my_list, j)
316  select type(data)
317  type is(integer)
318  call assert_equals(j, data, "Element at location j is consistent with expectations")
319  class default
320  call add_fail("Type unknown")
321  end select
322  end do
323 
324  call c_free(my_list)
325  end subroutine test_list_type_get
326 
327  ! Creates a list_type with a number of elements and then inserts new elements into it. Checks all
328  ! values at the end to ensure consistency
330  type(list_type) :: my_list
331  integer :: i, element_to_remove, j, k
332  class(*), pointer :: iptr=>null(), data=>null()
333 
334  do i=1,10
335  allocate(iptr, source=i)
336  call c_add(my_list, iptr)
337  end do
338 
339  do i=1,10
340  j=i*100
341  allocate(iptr, source=j)
342  call c_insert(my_list, iptr, i)
343  end do
344 
345  call assert_equals(20, c_size(my_list), "Post addition and insertion list_type size is correct")
346 
347  do k=1,20
348  data => c_get(my_list, k)
349  select type(data)
350  type is (integer)
351  call assert_equals(data, merge(k-10, k*100, k .gt. 10), "Element at k is consistent with addition and insertion")
352  class default
353  call add_fail("Unknown type")
354  end select
355  end do
356 
357  call c_free(my_list)
358  end subroutine test_list_type_insert
359 
360  ! Will create a list_type and randomly determine an index to remove, will remove this and then check
361  ! that it has gone and not affected any other elements in the list_type
363  type(list_type) :: my_list
364  integer :: i, j, element_to_remove
365  class(*), pointer :: iptr=>null(), data=>null()
366  real :: r
367 
368  call init_random_seed
369  call random_number(r)
370  element_to_remove = int(r*9)+1
371 
372  do i=1,10
373  allocate(iptr, source=i)
374  call c_add(my_list, iptr)
375  end do
376 
377  data => c_get(my_list, element_to_remove)
378  select type(data)
379  type is (integer)
380  call assert_equals(element_to_remove, data, "Element to remove is consistent")
381  class default
382  call add_fail("Unknown type")
383  end select
384  call c_remove(my_list, element_to_remove)
385 
386  do j=1,9
387  if (j .ne. element_to_remove) then
388  data => c_get(my_list, j)
389  select type(data)
390  type is (integer)
391  call assert_equals(merge(j, j+1, j .lt. element_to_remove), data, "After element removal element at j is consistent")
392  class default
393  call add_fail("Unknown type")
394  end select
395  end if
396  end do
397 
398  call c_free(my_list)
399  end subroutine test_list_type_remove
400 
401  ! Helper subroutine to initialise the random seed (based on the clock)
402  subroutine init_random_seed()
403  integer :: i, n, clock
404  integer, dimension(:), allocatable :: seed
405 
406  call random_seed(size = n)
407  allocate(seed(n))
408 
409  call system_clock(count=clock)
410 
411  seed = clock + 37 * (/ (i - 1, i = 1, n) /)
412  call random_seed(put=seed)
413 
414  deallocate(seed)
415  end subroutine init_random_seed
416 
417 end module test_collections_mod
418 
419 ! Driver for the collections_mod unit tests
421  use fruit, only : init_fruit, run_test_case, fruit_summary
426 
427  implicit none
428 
429  call init_fruit
430  call run_test_case(test_list_type_add, "List addition")
431  call run_test_case(test_list_type_get, "List retrieval")
432  call run_test_case(test_list_type_remove, "List removal")
433  call run_test_case(test_list_type_insert, "List insertion")
434  call run_test_case(test_list_type_pointers, "List pointer consistency")
435  call run_test_case(test_stack_type_push_pop, "Stack push and pop")
436  call run_test_case(test_stack_type_pointers, "Stack pointer consistency")
437  call run_test_case(test_queue_type_push_pop, "Queue push and pop")
438  call run_test_case(test_queue_type_pointers, "Queue pointer consistency")
439  call run_test_case(test_map_type_put, "map_type put")
440  call run_test_case(test_map_type_put_unique, "map_type unique key property")
441  call run_test_case(test_map_type_get, "map_type retrieval")
442  call run_test_case(test_map_type_remove, "map_type removal")
443  call run_test_case(test_map_type_pointers, "map_type pointer consistency")
444  call fruit_summary
445 end program test_collections_driver
collections_mod::map_type
Map data structure that holds string (length 20 maximum) key value pairs.
Definition: collections.F90:86
collections_mod::c_is_empty
Returns whether a collection is empty.
Definition: collections.F90:437
test_collections_mod::test_map_type_pointers
subroutine test_map_type_pointers
Definition: test_collections.F90:60
test_collections_mod::test_list_type_insert
subroutine test_list_type_insert
Definition: test_collections.F90:330
test_collections_mod::test_list_type_pointers
subroutine test_list_type_pointers
Definition: test_collections.F90:282
collections_mod::c_key_at
Retrieves the key currently being held at a specific index in the map or "" if the index > map elemen...
Definition: collections.F90:457
test_collections_mod::test_map_type_remove
subroutine test_map_type_remove
Definition: test_collections.F90:127
collections_mod
Collection data structures.
Definition: collections.F90:7
test_collections_mod::test_map_type_get
subroutine test_map_type_get
Definition: test_collections.F90:81
test_collections_mod::test_map_type_put_unique
subroutine test_map_type_put_unique
Definition: test_collections.F90:37
test_collections_mod
Definition: test_collections.F90:2
collections_mod::c_size
Returns the number of elements in the collection.
Definition: collections.F90:428
collections_mod::stack_type
Stack (FILO) data structure.
Definition: collections.F90:78
test_collections_mod::test_list_type_add
subroutine test_list_type_add
Definition: test_collections.F90:264
test_collections_mod::test_list_type_remove
subroutine test_list_type_remove
Definition: test_collections.F90:363
test_collections_mod::test_list_type_get
subroutine test_list_type_get
Definition: test_collections.F90:303
test_collections_mod::test_queue_type_push_pop
subroutine test_queue_type_push_pop
Definition: test_collections.F90:234
collections_mod::c_contains
Determines whether or not a map contains a specific key.
Definition: collections.F90:447
test_collections_mod::init_random_seed
subroutine init_random_seed()
Definition: test_collections.F90:403
test_collections_mod::test_stack_type_pointers
subroutine test_stack_type_pointers
Definition: test_collections.F90:165
test_collections_driver
program test_collections_driver
Definition: test_collections.F90:420
collections_mod::queue_type
Queue (FIFO) data structure.
Definition: collections.F90:70
collections_mod::c_free
Frees up all the allocatable, heap, memory associated with a list, stack, queue or map.
Definition: collections.F90:577
test_collections_mod::test_queue_type_pointers
subroutine test_queue_type_pointers
Definition: test_collections.F90:214
collections_mod::list_type
List data structure which implements a doubly linked list. This list will preserve its order.
Definition: collections.F90:60
collections_mod::c_remove
Removes a specific element from the list or map.
Definition: collections.F90:419
test_collections_mod::test_stack_type_push_pop
subroutine test_stack_type_push_pop
Definition: test_collections.F90:185
test_collections_mod::str
character(len=15) function str(k)
Definition: test_collections.F90:159
test_collections_mod::test_map_type_put
subroutine test_map_type_put
Definition: test_collections.F90:12