MONC
test_registry.F90
Go to the documentation of this file.
1 ! Tests the component registry functionality
3  use fruit, only : assert_equals, add_fail, assert_true, assert_false, set_unit_name
5  execute_initialisation_callbacks, execute_timestep_callbacks, execute_consolidation_callbacks, &
7  use state_mod, only : model_state_type
9  use collections_mod, only : map_type, c_size, c_value_at, c_key_at
10  implicit none
11 
12  ! Counters to determine the number of callback calls for testing
13  integer :: init_calls = 0
14  integer :: timestep_calls = 0
15  integer :: consolidation_calls = 0
16  integer :: modeldump_calls = 0
17  integer :: finalisation_calls = 0
18 
19 contains
20  ! Tests registering components of different names, checks that they all register and then check the info of each one to
21  ! ensure that the name and version numbers correspond to the correct values
22  subroutine test_register()
23  type(component_descriptor_type), pointer :: descriptor
24  type(map_type) :: component_info
25  class(*), pointer :: data
26  integer :: i
27 
28  call set_unit_name('test_register')
29  do i=1,10
30  allocate(descriptor)
31  descriptor%version=100-i
32  descriptor%name="Test "//str(i)
33  call register_component(descriptor)
34  end do
35 
36  component_info = get_all_registered_components()
37  call assert_equals(10, c_size(component_info), "Number of registered components after registrations")
38 
39  do i=1,10
40  call assert_equals("Test "//str(i), c_key_at(component_info, i))
41  data => c_value_at(component_info, i)
42  select type(data)
43  type is (real)
44  call assert_equals(real(100-i), data, "Version number of component at i correct")
45  class default
46  call add_fail("Unknown type")
47  end select
48  end do
49  end subroutine test_register
50 
51  ! Tests the deregistration of components. Will register a load of components, check these are registered and then
52  ! deregister each of them and ensure that they have been removed
53  subroutine test_deregister()
54  type(component_descriptor_type), pointer :: descriptor
55  type(map_type) :: component_info
56  integer :: i
57 
58  do i=1,10
59  allocate(descriptor)
60  descriptor%version=100-i
61  descriptor%name="Test "//str(i)
62  call register_component(descriptor)
63  end do
64 
65  component_info = get_all_registered_components()
66  call assert_equals(10, c_size(component_info), "Number of registered components after registrations")
67 
68  do i=1,10
69  call deregister_component("Test "//str(i))
70  component_info = get_all_registered_components()
71  call assert_equals(10-i, c_size(component_info), "Component at i has de-registered")
72  end do
73  end subroutine test_deregister
74 
75  ! Tests the component detailed information is correct for each registered component. Also provides incorrect (non registered)
76  ! names to ensure that NULL is returned and the registry handles this correctly. The name, version and call back pointers are
77  ! all checked for consistency
79  type(model_state_type) :: testing_state
80  type(component_descriptor_type), pointer :: data
81  integer :: init_callbacks, timestep_callbacks, consolidation_callbacks, modeldump_callbacks,&
82  finalisation_callbacks, i
83 
84  call free_registry() ! Clear the registry to eliminate residue of previous unit tests
85  call insert_component_callbacks(init_callbacks, timestep_callbacks, consolidation_callbacks, &
86  modeldump_callbacks, finalisation_callbacks)
87 
88  do i=1,120
89  data => get_component_info("Test "//str(i))
90  if (i .le. 100) then
91  call assert_true(associated(data), "Testing there is some component information if less than 100")
92  call assert_equals("Test "//str(i), data%name, "Compare registered and expected name")
93  call assert_equals(real(100-i), data%version, "Compare registered and expected version")
94  ! Check each callback is associated if i is within range or not if i is not
95  call assert_equals(merge(.true., .false., i .le. init_callbacks), associated(data%initialisation), &
96  "Consistency of initialisation call-back")
97  call assert_equals(merge(.true., .false., i .le. timestep_callbacks), associated(data%timestep), &
98  "Consistency of timestep call-back")
99  call assert_equals(merge(.true., .false., i .le. consolidation_callbacks), associated(data%consolidation), &
100  "Consistency of consolidation call-back")
101  call assert_equals(merge(.true., .false., i .le. modeldump_callbacks), associated(data%modeldump), &
102  "Consistency of model dump call-back")
103  call assert_equals(merge(.true., .false., i .le. finalisation_callbacks), associated(data%finalisation), &
104  "Consistency of finalisation all-back")
105  else
106  call assert_false(associated(data), "No component if greater than 100") ! Not registered i>100 so should be NULL
107  end if
108  end do
109 
110  end subroutine test_component_information
111 
112  ! Will register a hundred components and then de-register components from 25 to 50 (inclusive, so 26
113  ! deregistrations.) Will then execute the callback stages and ensure that the number of calls is
114  ! consistent with the expected number taking into account the deregistrations
116  type(model_state_type) :: testing_state
117  integer :: init_callbacks, timestep_callbacks, consolidation_callbacks, modeldump_callbacks,&
118  finalisation_callbacks, i
119 
120  call clear_counters()
121  call free_registry() ! Clear the registry to eliminate residue of previous unit tests
122  call insert_component_callbacks(init_callbacks, timestep_callbacks, consolidation_callbacks, &
123  modeldump_callbacks, finalisation_callbacks)
124 
125  do i=25,50
126  ! Now deregister 25-50 callbacks
127  call deregister_component("Test "//str(i))
128  end do
129 
130  ! In reference to the 25-50 inclusive removals, recalculate the expected number of each stage's callback
131  init_callbacks = calculate_remaining_calls(init_callbacks, 25, 50)
132  timestep_callbacks = calculate_remaining_calls(timestep_callbacks, 25, 50)
133  consolidation_callbacks = calculate_remaining_calls(consolidation_callbacks, 25, 50)
134  modeldump_callbacks = calculate_remaining_calls(modeldump_callbacks, 25, 50)
135  finalisation_callbacks = calculate_remaining_calls(finalisation_callbacks, 25, 50)
136 
137  ! Call the stages
138  call execute_initialisation_callbacks(testing_state)
139  call execute_timestep_callbacks(testing_state)
140  call execute_consolidation_callbacks(testing_state)
141  call execute_modeldump_callbacks(testing_state)
142  call execute_finalisation_callbacks(testing_state)
143 
144  ! Check number of calls in each stages's callbacks are appropriate
145  call assert_equals(init_callbacks, init_calls, "Number of initialisation call-backs post removal")
146  call assert_equals(timestep_callbacks, timestep_calls, "Number of timestep call-backs post removal")
147  call assert_equals(consolidation_callbacks, consolidation_calls, "Number of consolidation call-backs post removal")
148  call assert_equals(modeldump_callbacks, modeldump_calls, "Number of model dump call-backs post removal")
149  call assert_equals(finalisation_callbacks, finalisation_calls, "Number of finalisation call-backs post removal")
150 
151  end subroutine test_component_removal_callbacks
152 
153  ! Will register a number of components, execute the state callbacks and ensure that the number of callbacks in
154  ! each stage are consistent which the number registered
156  type(model_state_type) :: testing_state
157  integer :: init_callbacks, timestep_callbacks, consolidation_callbacks, modeldump_callbacks, finalisation_callbacks
158 
159  call clear_counters()
160  call free_registry() ! Clear the registry to remove any residue from previous unit tests
161  call insert_component_callbacks(init_callbacks, timestep_callbacks, consolidation_callbacks, &
162  modeldump_callbacks, finalisation_callbacks)
163 
164  ! Call the callbacks for each stage
165  call execute_initialisation_callbacks(testing_state)
166  call execute_timestep_callbacks(testing_state)
167  call execute_consolidation_callbacks(testing_state)
168  call execute_modeldump_callbacks(testing_state)
169  call execute_finalisation_callbacks(testing_state)
170 
171  ! Check that the number of callback calls is consistent with what we expected for each stage
172  call assert_equals(init_callbacks, init_calls, "Number of initialisation call-backs")
173  call assert_equals(timestep_callbacks, timestep_calls, "Number of timestep call-backs")
174  call assert_equals(consolidation_callbacks, consolidation_calls, "Number of consolidation call-backs")
175  call assert_equals(modeldump_callbacks, modeldump_calls, "Number of model dump call-backs")
176  call assert_equals(finalisation_callbacks, finalisation_calls, "Number of finalisation call-backs")
177  end subroutine test_component_callbacks
178 
179  ! Will register a number of call backs and then reregister identical named callbacks. This tests that
180  ! if someone reregisters a callback then the new registration takes the place of the previous callback
182  type(model_state_type), target :: testing_state
183  integer :: init_callbacks, timestep_callbacks, consolidation_callbacks, modeldump_callbacks, finalisation_callbacks
184 
185  call clear_counters()
186  call free_registry() ! Clear the registry to remove any residue of previous unit tests
187  call insert_component_callbacks(init_callbacks, timestep_callbacks, consolidation_callbacks, &
188  modeldump_callbacks, finalisation_callbacks)
189  ! Recreate our callbacks
190  call generate_dummy_callbacks(init_callbacks, timestep_callbacks, consolidation_callbacks, &
191  modeldump_callbacks, finalisation_callbacks)
192 
193  ! Execute callbacks for each stage
194  call execute_initialisation_callbacks(testing_state)
195  call execute_timestep_callbacks(testing_state)
196  call execute_consolidation_callbacks(testing_state)
197  call execute_modeldump_callbacks(testing_state)
198  call execute_finalisation_callbacks(testing_state)
199 
200  ! Check the number of calls to each callback is consistent with what we initially expected
201  call assert_equals(init_callbacks, init_calls, "Number of initialisation call-backs post replacement")
202  call assert_equals(timestep_callbacks, timestep_calls, "Number of timestep call-backs post replacement")
203  call assert_equals(consolidation_callbacks, consolidation_calls, "Number of consolidation call-backs post replacement")
204  call assert_equals(modeldump_callbacks, modeldump_calls, "Number of model dump call-backs post replacement")
205  call assert_equals(finalisation_callbacks, finalisation_calls, "Number of finalisation call-backs post replacement")
207 
208  ! A helper function which calculates the remaining calls from an initial number and the start and
209  ! end numbers (inclusive) of components that have been deregistered
210  integer function calculate_remaining_calls(orig_value, a, b)
211  integer, intent(in) :: orig_value, a, b
212 
213  if (orig_value .ge. a) then
214  calculate_remaining_calls = orig_value - merge(orig_value - a+1, a+1, orig_value .le. b)
215  else
216  calculate_remaining_calls = orig_value
217  end if
218  end function calculate_remaining_calls
219 
220  ! A helper subroutine to insert the component callbacks. It generates a random number of callbacks
221  ! for each stage and then inserts components based upon these.
222  subroutine insert_component_callbacks(init_callbacks, timestep_callbacks, consolidation_callbacks,&
223  modelDump_callbacks, finalisation_callbacks)
224 
225  integer, intent(out) :: init_callbacks, timestep_callbacks, consolidation_callbacks, &
226  modelDump_callbacks, finalisation_callbacks
227  real :: r
228 
229  call init_random_seed()
230 
231  call random_number(r)
232  init_callbacks = int(r*99)+1
233  call random_number(r)
234  timestep_callbacks = int(r*99)+1
235  call random_number(r)
236  consolidation_callbacks = int(r*99)+1
237  call random_number(r)
238  modeldump_callbacks = int(r*99)+1
239  call random_number(r)
240  finalisation_callbacks = int(r*99)+1
241 
242  call generate_dummy_callbacks(init_callbacks, timestep_callbacks, consolidation_callbacks, &
243  modeldump_callbacks, finalisation_callbacks)
244  end subroutine insert_component_callbacks
245 
246  ! Registers a hundred dummy components and specific callbacks depending upon the numbers provided
247  subroutine generate_dummy_callbacks(init_callbacks, timestep_callbacks, consolidation_callbacks, &
248  modelDump_callbacks, finalisation_callbacks)
249 
250  integer, intent(in) :: init_callbacks, timestep_callbacks, consolidation_callbacks, &
251  modelDump_callbacks, finalisation_callbacks
252  type(component_descriptor_type), pointer :: descriptor
253  integer :: i
254 
255  do i=1,100
256  allocate(descriptor)
257  descriptor%name="Test "//str(i)
258  descriptor%version=100-i
259  if (i .le. init_callbacks) descriptor%initialisation=>internal_test_init
260  if (i .le. timestep_callbacks) descriptor%timestep=>internal_test_timestep
261  if (i .le. consolidation_callbacks) descriptor%consolidation=>internal_test_consolidation
262  if (i .le. modeldump_callbacks) descriptor%modeldump=>internal_test_modeldump
263  if (i .le. finalisation_callbacks) descriptor%finalisation=>internal_test_finalisation
264  call register_component(descriptor)
265  end do
266  end subroutine generate_dummy_callbacks
267 
268  ! Test callback function, will increment the init integer value
269  subroutine internal_test_init(current_state)
270  type(model_state_type), target, intent(inout) :: current_state
271 
272  init_calls = init_calls + 1
273  end subroutine internal_test_init
274 
275  ! Test callback function, will increment the timestep integer value
276  subroutine internal_test_timestep(current_state)
277  type(model_state_type), target, intent(inout) :: current_state
278 
280  end subroutine internal_test_timestep
281 
282  ! Test callback function, will increment the consolidation integer value
283  subroutine internal_test_consolidation(current_state)
284  type(model_state_type), target, intent(inout) :: current_state
285 
287  end subroutine internal_test_consolidation
288 
289  ! Test callback function, will increment the modeldump integer value
290  subroutine internal_test_modeldump(current_state)
291  type(model_state_type), target, intent(inout) :: current_state
292 
294  end subroutine internal_test_modeldump
295 
296  ! Test callback function, will increment the finalisation integer value
297  subroutine internal_test_finalisation(current_state)
298  type(model_state_type), target, intent(inout) :: current_state
299 
301  end subroutine internal_test_finalisation
302 
303  ! Helper function to convert an integer into a string
304  character(len=15) function str(k)
305  integer, intent(in) :: k
306  write (str, *) k
307  str = adjustl(str)
308  end function str
309 
310  ! Helper function to seed the random number generator
311  subroutine init_random_seed()
312  integer :: i, n, clock
313  integer, dimension(:), allocatable :: seed
314 
315  call random_seed(size = n)
316  allocate(seed(n))
317 
318  call system_clock(count=clock)
319 
320  seed = clock + 37 * (/ (i - 1, i = 1, n) /)
321  call random_seed(put = seed)
322 
323  deallocate(seed)
324  end subroutine init_random_seed
325 
326  ! Clears the call back counters
327  subroutine clear_counters()
328  init_calls = 0
329  timestep_calls = 0
331  modeldump_calls = 0
333  end subroutine clear_counters
334 
335 end module test_registry_mod
336 
337 ! The driver for testing the registry
339  use fruit, only : init_fruit, run_test_case, fruit_summary
342 
343  implicit none
344 
345  call init_fruit
346  call run_test_case(test_register, "Component registration")
347  call run_test_case(test_deregister, "Component de-registration")
348  call run_test_case(test_component_information, "Retrieval of component information")
349  call run_test_case(test_component_callbacks, "Component call-backs")
350  call run_test_case(test_component_replacement_callbacks, "Component replacement with call-backs")
351  call run_test_case(test_component_removal_callbacks, "Component removal with call-backs")
352  call fruit_summary
353 end program test_registry_driver
registry_mod::register_component
subroutine, public register_component(options_database, descriptor)
Will register a component and install the nescesary callback hooks.
Definition: registry.F90:100
test_registry_mod::finalisation_calls
integer finalisation_calls
Definition: test_registry.F90:17
collections_mod::map_type
Map data structure that holds string (length 20 maximum) key value pairs.
Definition: collections.F90:86
registry_mod::free_registry
subroutine, public free_registry()
Will deregister all components and free up the registry data structures. This can either be called at...
Definition: registry.F90:77
test_registry_mod::calculate_remaining_calls
integer function calculate_remaining_calls(orig_value, a, b)
Definition: test_registry.F90:211
test_registry_mod::test_register
subroutine test_register()
Definition: test_registry.F90:23
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_registry_mod::test_component_replacement_callbacks
subroutine test_component_replacement_callbacks()
Definition: test_registry.F90:182
test_registry_mod::test_deregister
subroutine test_deregister()
Definition: test_registry.F90:54
test_registry_mod::test_component_information
subroutine test_component_information()
Definition: test_registry.F90:79
test_registry_mod
Definition: test_registry.F90:2
test_registry_mod::internal_test_init
subroutine internal_test_init(current_state)
Definition: test_registry.F90:270
collections_mod
Collection data structures.
Definition: collections.F90:7
collections_mod::c_size
Returns the number of elements in the collection.
Definition: collections.F90:428
test_registry_mod::internal_test_modeldump
subroutine internal_test_modeldump(current_state)
Definition: test_registry.F90:291
test_registry_mod::init_calls
integer init_calls
Definition: test_registry.F90:13
test_registry_mod::str
character(len=15) function str(k)
Definition: test_registry.F90:305
test_registry_mod::modeldump_calls
integer modeldump_calls
Definition: test_registry.F90:16
registry_mod::get_all_registered_components
type(map_type) function, public get_all_registered_components()
Returns a brief summary of all registered components.
Definition: registry.F90:258
monc_component_mod
Interfaces and types that MONC components must specify.
Definition: monc_component.F90:6
test_registry_mod::generate_dummy_callbacks
subroutine generate_dummy_callbacks(init_callbacks, timestep_callbacks, consolidation_callbacks, modelDump_callbacks, finalisation_callbacks)
Definition: test_registry.F90:249
registry_mod::execute_initialisation_callbacks
subroutine, public execute_initialisation_callbacks(current_state)
Calls all initialisation callbacks with the specified state.
Definition: registry.F90:275
test_registry_mod::test_component_removal_callbacks
subroutine test_component_removal_callbacks()
Definition: test_registry.F90:116
registry_mod::deregister_component
subroutine, public deregister_component(name)
Will deregister a component, remove all callback hooks and free registry specific memory allocated to...
Definition: registry.F90:226
test_registry_mod::internal_test_consolidation
subroutine internal_test_consolidation(current_state)
Definition: test_registry.F90:284
test_registry_mod::clear_counters
subroutine clear_counters()
Definition: test_registry.F90:328
test_registry_mod::internal_test_timestep
subroutine internal_test_timestep(current_state)
Definition: test_registry.F90:277
test_registry_mod::internal_test_finalisation
subroutine internal_test_finalisation(current_state)
Definition: test_registry.F90:298
state_mod::model_state_type
The ModelState which represents the current state of a run.
Definition: state.F90:39
test_registry_mod::timestep_calls
integer timestep_calls
Definition: test_registry.F90:14
test_registry_driver
program test_registry_driver
Definition: test_registry.F90:338
test_registry_mod::init_random_seed
subroutine init_random_seed()
Definition: test_registry.F90:312
registry_mod
MONC component registry.
Definition: registry.F90:5
registry_mod::execute_finalisation_callbacks
subroutine, public execute_finalisation_callbacks(current_state)
Calls all finalisation callbacks with the specified state.
Definition: registry.F90:294
registry_mod::get_component_info
type(component_descriptor_type) function, pointer, public get_component_info(name)
Retrieves detailed information about a specific component.
Definition: registry.F90:241
test_registry_mod::consolidation_calls
integer consolidation_calls
Definition: test_registry.F90:15
registry_mod::execute_timestep_callbacks
subroutine, public execute_timestep_callbacks(current_state, group_id)
Calls all timestep callbacks with the specified state.
Definition: registry.F90:283
test_registry_mod::insert_component_callbacks
subroutine insert_component_callbacks(init_callbacks, timestep_callbacks, consolidation_callbacks, modelDump_callbacks, finalisation_callbacks)
Definition: test_registry.F90:224
monc_component_mod::component_descriptor_type
Description of a component.
Definition: monc_component.F90:42
state_mod
The model state which represents the current state of a run.
Definition: state.F90:2
test_registry_mod::test_component_callbacks
subroutine test_component_callbacks()
Definition: test_registry.F90:156