3030import os
3131sys .path .append (os .path .dirname (__file__ ))
3232from sim_state import IgnitionSimStateUtil , EntityNotFoundError
33+ from sim_state .metric_value import Pose
3334
3435
3536
@@ -60,7 +61,7 @@ def merge_ros_params_files(source, override, destination):
6061 with open (destination , "w" ) as f :
6162 yaml .dump (merged_params , f )
6263
63- @pytest .fixture ()
64+ @pytest .fixture (scope = "module" )
6465def reach_goal_proc ():
6566 reach_goal = Node (
6667 package = "sam_bot_nav2_gz" ,
@@ -71,8 +72,8 @@ def reach_goal_proc():
7172 return reach_goal
7273
7374
74- @launch_pytest .fixture
75- def launch_description (reach_goal_proc ):
75+ @launch_pytest .fixture ( scope = "module" )
76+ def launch_description (reach_goal_proc , sim ):
7677 """Launch description fixture for pytest-based testing"""
7778 try :
7879 world = get_artefacts_param ("launch" , "world" , default = "empty.sdf" )
@@ -100,11 +101,10 @@ def launch_description(reach_goal_proc):
100101 launch_arguments = [
101102 ("run_headless" , run_headless ),
102103 ("world_file" , world ),
103- ("params_file" , new_params_file ),
104+ # ("params_file", new_params_file),
104105 ],
105106 )
106107
107-
108108
109109 # Gazebo ros bridge
110110 gz_bridge = Node (
@@ -120,115 +120,30 @@ def launch_description(reach_goal_proc):
120120 output = "screen" ,
121121 )
122122
123- gz_env = {'GZ_SIM_SYSTEM_PLUGIN_PATH' :
124- ':' .join ([os .environ .get ('GZ_SIM_SYSTEM_PLUGIN_PATH' , default = '' ),
125- os .environ .get ('LD_LIBRARY_PATH' , default = '' )]),
126- 'IGN_GAZEBO_SYSTEM_PLUGIN_PATH' : # TODO(CH3): To support pre-garden. Deprecated.
127- ':' .join ([os .environ .get ('IGN_GAZEBO_SYSTEM_PLUGIN_PATH' , default = '' ),
128- os .environ .get ('LD_LIBRARY_PATH' , default = '' )])}
129- log_level = LaunchConfiguration ("log_level" )
130- use_sim_time = LaunchConfiguration ("use_sim_time" )
131- gz_verbosity = LaunchConfiguration ("gz_verbosity" )
132123 pkg_share = launch_ros .substitutions .FindPackageShare (
133124 package = "sam_bot_nav2_gz"
134125 ).find ("sam_bot_nav2_gz" )
135- default_model_path = os .path .join (
136- pkg_share , "src/description/sam_bot_description.urdf"
137- )
138126 run_headless = LaunchConfiguration ("run_headless" )
139- world_file_name = LaunchConfiguration ("world_file" )
140127 gz_models_path = ":" .join ([pkg_share , os .path .join (pkg_share , "models" )])
141- #gz_models_path = os.path.join(pkg_share, "models")
142- world_path = PathJoinSubstitution ([pkg_share , "world" , world_file_name ])
143- gazebo = [
144- ExecuteProcess (
145- condition = launch .conditions .IfCondition (run_headless ),
146- cmd = ['ruby' , FindExecutable (name = "ign" ), 'gazebo' , '-r' , '-v' , gz_verbosity , '-s' , '--headless-rendering' , world_path ],
147- output = 'screen' ,
148- additional_env = gz_env , # type: ignore
149- shell = False ,
150- ),
151- ExecuteProcess (
152- condition = launch .conditions .UnlessCondition (run_headless ),
153- cmd = ['ruby' , FindExecutable (name = "ign" ), 'gazebo' , '-r' , '-v' , gz_verbosity , world_path ],
154- output = 'screen' ,
155- additional_env = gz_env , # type: ignore
156- shell = False ,
157- )
158- ]
159128
160- spawn_entity = Node (
161- package = "ros_gz_sim" ,
162- executable = "create" ,
163- output = "screen" ,
164- arguments = [
165- "-name" ,
166- "sam_bot" ,
167- "-topic" ,
168- "robot_description" ,
169- "-z" ,
170- "1.0" ,
171- "-x" ,
172- "-2.0" ,
173- "--ros-args" ,
174- "--log-level" ,
175- log_level ,
176- ],
177- parameters = [{"use_sim_time" : use_sim_time }],
178- )
179129
180130
181131 return LaunchDescription (
182132 [
183- SetEnvironmentVariable (
184- name = "IGN_GAZEBO_RESOURCE_PATH" ,
185- value = gz_models_path ,
186- ),
187- DeclareLaunchArgument (
188- "gz_verbosity" ,
189- default_value = "3" ,
190- description = "Verbosity level for Ignition Gazebo (0~4)." ,
191- ),
192- DeclareLaunchArgument (
193- "gz_args" ,
194- default_value = "" ,
195- description = "Extra args for Gazebo (ie. '-s' for running headless)" ,
196- ),
197- DeclareLaunchArgument (
198- name = "world_file" ,
199- default_value = "empty.sdf" ,
200- ),
201133 DeclareLaunchArgument (
202134 name = "run_headless" ,
203135 default_value = "False" ,
204136 description = "Start GZ in hedless mode and don't start RViz (overrides use_rviz)" ,
205137 ),
206- DeclareLaunchArgument (
207- name = "use_sim_time" ,
208- default_value = "True" ,
209- description = "Flag to enable use_sim_time" ,
210- ),
211- DeclareLaunchArgument (
212- name = "log_level" ,
213- default_value = "warn" ,
214- description = "The level of logging that is applied to all ROS 2 nodes launched by this script." ,
215- ),
216- #DeclareLaunchArgument(
217- # name="run_headless",
218- # default_value="False",
219- # description="Start GZ in headless mode and don't start RViz (overrides use_rviz)",
220- #),
221- #launch_navigation_stack",
222- * gazebo ,
223- spawn_entity ,
224- #reach_goal_proc,
138+ launch_navigation_stack ,
139+ reach_goal_proc ,
225140 gz_bridge ,
226141 launch_pytest .actions .ReadyToTest ()
227142 ]
228143 )
229144
230145
231- @pytest .fixture (scope = "function " )
146+ @pytest .fixture (scope = "module " )
232147def sim ():
233148 """Fixture that provides access to simulation state during tests"""
234149 if not rclpy .ok ():
@@ -241,32 +156,41 @@ def sim():
241156 util .stop_recording ()
242157
243158
244- #@pytest.mark.launch(fixture=launch_description)
245- #def test_nav2_started(reach_goal_proc, launch_context):
246- # """Test that Nav2 starts successfully"""
247- # def validate_nav2_output(output):
248- # return output and 'Nav2 active!' in output
249- #
250- # # Get the reach_goal process from the launch context
251- # process_tools.wait_for_output_sync(
252- # launch_context, reach_goal_proc, validate_nav2_output, timeout=30)
253- #
159+ @pytest .mark .launch (fixture = launch_description )
160+ def test_nav2_started (reach_goal_proc , launch_context ):
161+ """Test that Nav2 starts successfully"""
162+ def validate_nav2_output (output ):
163+ print (output )
164+ assert 'Nav2 active' in output , "process never printed Nav2 is ready"
165+
166+ # Get the reach_goal process from the launch context
167+ process_tools .assert_output_sync (
168+ launch_context , reach_goal_proc , validate_nav2_output , timeout = 40 )
169+
254170
255171@pytest .mark .launch (fixture = launch_description )
256- def test_reached_goal (reach_goal_proc ,launch_context , sim ):
172+ def test_reached_goal (reach_goal_proc , launch_context , sim ):
257173 """Check that the navigation goal is reached"""
258174 def validate_goal_output (output ):
259- return output and 'Goal succeeded!' in output
175+ print (output )
176+ assert 'Goal succeeded!' in output , "process never printed Goal succeeded!"
260177
261178 # Get the reach_goal process from the launch context
262- process_tools .wait_for_output_sync (launch_context , reach_goal_proc , validate_goal_output , timeout = 5 )
179+ #process_tools.wait_for_output_sync(launch_context, reach_goal_proc, validate_goal_output, timeout=15)
180+ process_tools .assert_output_sync (launch_context , reach_goal_proc , validate_goal_output , timeout = 45 )
263181
264182 # Additional assertion using simulation state
265- goal_coordinates = (2.0 , 3.0 ) # Example goal coordinates - adjust as needed
266- entity = sim .get_entity ('ros_symbol' )
267- cam = sim .get_entity ('sky_cam/camera_link' )
268- dist = entity .distance_to (cam ).now ()
269- assert dist > 1.0 , f"Robot is not close enough to camera at { cam .pose ().now ().x } . Distance: { dist } "
270- assert cam .pose ().now ().z > 1.0
271- entity .distance_to (cam ).to_csv ("output/test.csv" )
183+ robot = sim .get_entity ('sam_bot' )
184+
185+ # Create odom frame using add_transform (uses robot's pose at time 0)
186+ initial_robot_pose = robot .pose ().at (0 ) #shall we use .first()
187+ sim .add_transform ("world" , "odom" , initial_robot_pose )
188+
189+ # Create goal waypoint in odom frame
190+ goal = Pose (x = 0.8 , y = - 0.5 , z = 0.0 , frame = "odom" )
191+
192+ dist = robot .distance_to (goal ).now ()
193+ assert dist < 1.0 , f"Robot is not close enough to goal. Distance: { dist } "
194+ robot .distance_to (goal ).to_csv ("output/test.csv" )
195+ robot .pose ().to_csv ("output/pose.csv" )
272196
0 commit comments