2020#
2121# Integrated tests
2222#
23- # Purpose: This script runs a series of test to ensure that the messaging
23+ # Purpose: This script runs a series of test to ensure that the messaging
2424# interface is properly set up for C and Cpp messages
2525# Author: Benjamin Bercovici
2626# Creation Date: June 4th, 2021
2929
3030from Basilisk .architecture import bskLogging
3131from Basilisk .architecture import messaging
32+ from Basilisk .moduleTemplates import cppModuleTemplate
33+ from Basilisk .utilities import SimulationBaseClass
34+ from Basilisk .utilities import macros
35+ import numpy as np
3236
3337
3438# uncomment this line is this test is to be skipped in the global unit test run, adjust message as needed
@@ -47,6 +51,7 @@ def messaging_unit_tests():
4751 test_c_msg_subscription_check ()
4852 test_cpp_2_c_msg_subscription_check ()
4953 test_c_2_cpp_msg_subscription_check ()
54+ test_standalone_message_scope ()
5055
5156
5257
@@ -56,13 +61,13 @@ def test_cpp_msg_subscription_check():
5661 bskLogging .setDefaultLogLevel (bskLogging .BSK_WARNING )
5762 testFailCount = 0 # zero unit test result counter
5863 testMessages = [] # create empty array to store test log messages
59-
64+
6065
6166 # Try out all the existing cpp messages
62- cppMessages = [ el for el in dir (messaging ) if el .endswith ("Msg" )]
63-
67+ cppMessages = [ el for el in dir (messaging ) if el .endswith ("Msg" )]
68+
6469 for el in cppMessages :
65-
70+
6671 # Create three messages
6772 msgA = eval ("messaging." + el + "()" )
6873 msgB = eval ("messaging." + el + "()" )
@@ -86,8 +91,8 @@ def test_cpp_msg_subscription_check():
8691 if msgC_subscriber .isSubscribedTo (msgA ) != 0 :
8792 testFailCount += 1
8893 testMessages .append (el + ": msgC_subscriber.isSubscribedTo(msgA) should be False" )
89-
90-
94+
95+
9196 # Change subscription pattern
9297 msgB_subscriber .subscribeTo (msgC ) # Subscribe B to C
9398 msgC_subscriber .subscribeTo (msgA ) # Subscribe C to A
@@ -103,8 +108,8 @@ def test_cpp_msg_subscription_check():
103108 testFailCount += 1
104109 testMessages .append (el + ": msgC_subscriber.isSubscribedTo(msgB) should be False" )
105110
106-
107-
111+
112+
108113 if testFailCount == 0 :
109114 print ("PASSED" )
110115 else :
@@ -122,14 +127,14 @@ def test_c_msg_subscription_check():
122127 bskLogging .setDefaultLogLevel (bskLogging .BSK_WARNING )
123128 testFailCount = 0 # zero unit test result counter
124129 testMessages = [] # create empty array to store test log messages
125-
130+
126131
127132 # Try out all the existing c messages
128- cMessages = [ el for el in dir (messaging ) if el .endswith ("Msg_C" )]
133+ cMessages = [ el for el in dir (messaging ) if el .endswith ("Msg_C" )]
134+
129135
130-
131136 for el in cMessages :
132-
137+
133138 # Create three messages
134139 msgA = eval ("messaging." + el + "()" )
135140 msgB = eval ("messaging." + el + "()" )
@@ -149,8 +154,8 @@ def test_c_msg_subscription_check():
149154 if msgC .isSubscribedTo (msgA ) != 0 :
150155 testFailCount += 1
151156 testMessages .append (el + ": msgC.isSubscribedTo(msgA) should be False" )
152-
153-
157+
158+
154159 # Change subscription pattern
155160 msgB .subscribeTo (msgC ) # Subscribe B to C
156161 msgC .subscribeTo (msgA ) # Subscribe C to A
@@ -166,8 +171,8 @@ def test_c_msg_subscription_check():
166171 testFailCount += 1
167172 testMessages .append (el + ": msgC.isSubscribedTo(msgB) should be False" )
168173
169-
170-
174+
175+
171176 if testFailCount == 0 :
172177 print ("PASSED" )
173178 else :
@@ -176,34 +181,34 @@ def test_c_msg_subscription_check():
176181
177182
178183def test_c_2_cpp_msg_subscription_check ():
179-
184+
180185 bskLogging .setDefaultLogLevel (bskLogging .BSK_WARNING )
181186 testFailCount = 0 # zero unit test result counter
182187 testMessages = [] # create empty array to store test log messages
183-
188+
184189
185190 # Try out all the existing messages
186- cppMessages = [ el for el in dir (messaging ) if el .endswith ("Msg" )]
187- cMessages = [ el for el in dir (messaging ) if el .endswith ("Msg_C" )]
188-
191+ cppMessages = [ el for el in dir (messaging ) if el .endswith ("Msg" )]
192+ cMessages = [ el for el in dir (messaging ) if el .endswith ("Msg_C" )]
193+
189194 # Find common messages
190195 common_messages = [el for el in cppMessages if el + "_C" in cMessages ]
191196
192197
193198 for el in common_messages :
194-
199+
195200 # Create c and cpp messages
196201 msgA = eval ("messaging." + el + "()" )
197202 msgB = eval ("messaging." + el + "()" )
198203
199204 msgC = eval ("messaging." + el + "_C()" )
200205 msgD = eval ("messaging." + el + "_C()" )
201206
202-
207+
203208 # Subscribe
204209 msgC .subscribeTo (msgA ) # Subscribe C to A
205210 msgD .subscribeTo (msgB ) # Subscribe D to B
206-
211+
207212 # Check
208213 if msgC .isSubscribedTo (msgA ) != 1 :
209214 testFailCount += 1
@@ -217,7 +222,7 @@ def test_c_2_cpp_msg_subscription_check():
217222 if msgC .isSubscribedTo (msgB ) != 0 :
218223 testFailCount += 1
219224 testMessages .append (el + ": msgC.isSubscribedTo(msgB) should be False" )
220-
225+
221226 # Change subscription pattern
222227 msgC .subscribeTo (msgB ) # Subscribe C to B
223228 msgD .subscribeTo (msgA ) # Subscribe D to A
@@ -236,8 +241,8 @@ def test_c_2_cpp_msg_subscription_check():
236241 testFailCount += 1
237242 testMessages .append (el + ": msgD.isSubscribedTo(msgB) should be False" )
238243
239-
240-
244+
245+
241246 if testFailCount == 0 :
242247 print ("PASSED" )
243248 else :
@@ -249,18 +254,18 @@ def test_cpp_2_c_msg_subscription_check():
249254 bskLogging .setDefaultLogLevel (bskLogging .BSK_WARNING )
250255 testFailCount = 0 # zero unit test result counter
251256 testMessages = [] # create empty array to store test log messages
252-
257+
253258
254259 # Try out all the existing messages
255- cppMessages = [ el for el in dir (messaging ) if el .endswith ("Msg" )]
256- cMessages = [ el for el in dir (messaging ) if el .endswith ("Msg_C" )]
257-
260+ cppMessages = [ el for el in dir (messaging ) if el .endswith ("Msg" )]
261+ cMessages = [ el for el in dir (messaging ) if el .endswith ("Msg_C" )]
262+
258263 # Find common messages
259264 common_messages = [el for el in cppMessages if el + "_C" in cMessages ]
260265
261266
262267 for el in common_messages :
263-
268+
264269 # Create c and cpp messages
265270 msgA = eval ("messaging." + el + "_C()" )
266271 msgB = eval ("messaging." + el + "_C()" )
@@ -271,7 +276,7 @@ def test_cpp_2_c_msg_subscription_check():
271276 # Create subscribers to pair messages
272277 msgC_subscriber = msgC .addSubscriber ()
273278 msgD_subscriber = msgD .addSubscriber ()
274-
279+
275280 # Subscribe
276281 msgC_subscriber .subscribeTo (msgA ) # Subscribe C to A
277282 msgD_subscriber .subscribeTo (msgB ) # Subscribe D to B
@@ -289,7 +294,7 @@ def test_cpp_2_c_msg_subscription_check():
289294 if msgC_subscriber .isSubscribedTo (msgB ) != 0 :
290295 testFailCount += 1
291296 testMessages .append (el + ": msgC_subscriber.isSubscribedTo(msgB) should be False" )
292-
297+
293298 # Change subscription pattern
294299 msgC_subscriber .subscribeTo (msgB ) # Subscribe C to B
295300 msgD_subscriber .subscribeTo (msgA ) # Subscribe D to A
@@ -308,15 +313,78 @@ def test_cpp_2_c_msg_subscription_check():
308313 testFailCount += 1
309314 testMessages .append (el + ": msgD_subscriber.isSubscribedTo(msgB) should be False" )
310315
311-
312-
316+
317+
313318 if testFailCount == 0 :
314319 print ("PASSED" )
315320 else :
316321 [print (msg ) for msg in testMessages ]
317322 assert (testFailCount == 0 )
318323
319324
325+ def test_standalone_message_scope ():
326+ """
327+ Test that subscribed messages don't get garbage collected when going out of scope
328+ """
329+ bskLogging .setDefaultLogLevel (bskLogging .BSK_WARNING )
330+ testFailCount = 0 # zero unit test result counter
331+ testMessages = [] # create empty array to store test log messages
332+
333+ # Create a sim module as an empty container
334+ scSim = SimulationBaseClass .SimBaseClass ()
335+
336+ # create the simulation process
337+ dynProcess = scSim .CreateNewProcess ("dynamicsProcess" )
338+
339+ # create the dynamics task and specify the integration update time
340+ dynProcess .addTask (scSim .CreateNewTask ("dynamicsTask" , macros .sec2nano (1. )))
341+
342+ # create modules
343+ mod1 = cppModuleTemplate .CppModuleTemplate ()
344+ mod1 .ModelTag = "cppModule1"
345+ scSim .AddModelToTask ("dynamicsTask" , mod1 )
346+
347+ # setup message recording
348+ msgRec = mod1 .dataOutMsg .recorder ()
349+ scSim .AddModelToTask ("dynamicsTask" , msgRec )
350+
351+ # Create a local scope to test message lifetime
352+ def addLocalStandaloneMessage (module ):
353+ """Create a stand-alone message in local scope and subscribe to it"""
354+ msgData = messaging .CModuleTemplateMsgPayload ()
355+ msgData .dataVector = [10. , 20. , 30. ]
356+ msg = messaging .CModuleTemplateMsg ().write (msgData )
357+ module .dataInMsg .subscribeTo (msg )
358+
359+ # Subscribe to the input message in a local scope
360+ addLocalStandaloneMessage (mod1 )
361+
362+ # initialize Simulation:
363+ scSim .InitializeSimulation ()
364+
365+ # configure a simulation stop time and execute the simulation run
366+ scSim .ConfigureStopTime (macros .sec2nano (3.0 ))
367+ scSim .ExecuteSimulation ()
368+
369+ # Verify output matches expected values
370+ expected = np .array ([
371+ [11. , 20. , 30. ],
372+ [12. , 20. , 30. ],
373+ [13. , 20. , 30. ],
374+ [14. , 20. , 30. ]
375+ ])
376+
377+ if not (msgRec .dataVector == expected ).all ():
378+ testFailCount += 1
379+ testMessages .append ("Output data does not match expected values" )
380+
381+ if testFailCount == 0 :
382+ print ("PASSED" )
383+ else :
384+ [print (msg ) for msg in testMessages ]
385+ assert testFailCount < 1 , testMessages
386+
387+
320388
321389if __name__ == "__main__" :
322390 messaging_unit_tests ()
0 commit comments