InterfacesTest.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Component/ComponentApplication.h>
  9. #include <AzCore/Component/ComponentApplicationBus.h>
  10. #include <AzCore/Component/Entity.h>
  11. #include <AzCore/Component/EntityId.h>
  12. #include <AzCore/RTTI/RTTIMacros.h>
  13. #include <AzCore/Serialization/SerializeContext.h>
  14. #include <AzCore/Slice/SliceAssetHandler.h>
  15. #include <AzCore/UserSettings/UserSettingsComponent.h>
  16. #include <AzCore/std/containers/array.h>
  17. #include <AzCore/std/string/string_view.h>
  18. #include <AzQtComponents/Utilities/QtPluginPaths.h>
  19. #include <AzTest/GemTestEnvironment.h>
  20. #include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
  21. #include <AzToolsFramework/ToolsComponents/TransformComponent.h>
  22. #include <AzToolsFramework/UnitTest/ToolsTestApplication.h>
  23. #include <Clients/ROS2SimulationInterfacesSystemComponent.h>
  24. #include <ROS2/ROS2Bus.h>
  25. #include <Services/DeleteEntityServiceHandler.h>
  26. #include <Services/GetEntitiesServiceHandler.h>
  27. #include <Services/GetEntitiesStatesServiceHandler.h>
  28. #include <Services/GetEntityStateServiceHandler.h>
  29. #include <Services/GetSimulatorFeaturesServiceHandler.h>
  30. #include <Services/GetSimulationStateServiceHandler.h>
  31. #include <Services/GetSpawnablesServiceHandler.h>
  32. #include <Services/ResetSimulationServiceHandler.h>
  33. #include <Services/SetEntityStateServiceHandler.h>
  34. #include <Services/SetSimulationStateServiceHandler.h>
  35. #include <Services/SpawnEntityServiceHandler.h>
  36. #include <Services/StepSimulationServiceHandler.h>
  37. #include "Mocks/SimulationEntityManagerMock.h"
  38. #include "Mocks/SimulationFeaturesAggregatorRequestsHandlerMock.h"
  39. #include "Mocks/SimulationManagerMock.h"
  40. #include <QApplication>
  41. #include <rclcpp/publisher.hpp>
  42. #include <rclcpp/rclcpp.hpp>
  43. #include <rclcpp_action/create_client.hpp>
  44. #include <simulation_interfaces/action/simulate_steps.hpp>
  45. #include <simulation_interfaces/msg/simulator_features.hpp>
  46. #include <std_msgs/msg/string.hpp>
  47. #include <gtest/gtest.h>
  48. #include <memory>
  49. #include <string>
  50. #include <string_view>
  51. #include <vector>
  52. namespace UnitTest
  53. {
  54. class SimulationInterfaceROS2TestEnvironment : public AZ::Test::GemTestEnvironment
  55. {
  56. // AZ::Test::GemTestEnvironment overrides ...
  57. void AddGemsAndComponents() override;
  58. AZ::ComponentApplication* CreateApplicationInstance() override;
  59. void PostSystemEntityActivate() override;
  60. public:
  61. SimulationInterfaceROS2TestEnvironment() = default;
  62. ~SimulationInterfaceROS2TestEnvironment() override = default;
  63. };
  64. void SimulationInterfaceROS2TestEnvironment::AddGemsAndComponents()
  65. {
  66. using namespace ROS2SimulationInterfaces;
  67. AddActiveGems(AZStd::to_array<AZStd::string_view>({ "ROS2" }));
  68. AddDynamicModulePaths({ "ROS2" });
  69. AddComponentDescriptors(AZStd::initializer_list<AZ::ComponentDescriptor*>{
  70. ROS2SimulationInterfacesSystemComponent::CreateDescriptor(),
  71. });
  72. AddRequiredComponents(AZStd::to_array<AZ::TypeId const>({ ROS2SimulationInterfacesSystemComponent::TYPEINFO_Uuid() }));
  73. }
  74. AZ::ComponentApplication* SimulationInterfaceROS2TestEnvironment::CreateApplicationInstance()
  75. {
  76. // Using ToolsTestApplication to have AzFramework and AzToolsFramework components.
  77. return aznew UnitTest::ToolsTestApplication("SimulationInterfaceROS2TestEnvironment");
  78. }
  79. void SimulationInterfaceROS2TestEnvironment::PostSystemEntityActivate()
  80. {
  81. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
  82. }
  83. class SimulationInterfaceROS2TestFixture : public ::testing::Test
  84. {
  85. protected:
  86. std::shared_ptr<rclcpp::Node> GetRos2Node()
  87. {
  88. auto interface = ROS2::ROS2Interface::Get();
  89. AZ_Assert(interface, "ROS2 interface is not available.");
  90. auto node = interface->GetNode();
  91. AZ_Assert(node, "Node is not available.");
  92. return node;
  93. }
  94. void SpinAppSome(int numberOfTicks = 50)
  95. {
  96. AZ::ComponentApplication* app = nullptr;
  97. AZ::ComponentApplicationBus::BroadcastResult(app, &AZ::ComponentApplicationBus::Events::GetApplication);
  98. AZ_Assert(app, "Application pointer is not available.");
  99. for (int i = 0; i < numberOfTicks; ++i)
  100. {
  101. app->Tick();
  102. }
  103. }
  104. template<typename Future>
  105. void SpinAppUntilFuture(Future& future)
  106. {
  107. AZ::ComponentApplication* app = nullptr;
  108. AZ::ComponentApplicationBus::BroadcastResult(app, &AZ::ComponentApplicationBus::Events::GetApplication);
  109. AZ_Assert(app, "Application pointer is not available.");
  110. constexpr int MaximumTicks = 1000;
  111. for (int i = 0; i < MaximumTicks; ++i)
  112. {
  113. app->Tick();
  114. if (future.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
  115. {
  116. return;
  117. }
  118. }
  119. }
  120. };
  121. //! Perform a smoke test to check if the ROS2 node is available from ROS2 gem
  122. TEST_F(SimulationInterfaceROS2TestFixture, TestIfROS2NodeIsAvailable)
  123. {
  124. ASSERT_NE(GetRos2Node(), nullptr) << "ROS2 node is not available.";
  125. }
  126. //! Perform a smoke test to check if the ROS2 domain is working
  127. TEST_F(SimulationInterfaceROS2TestFixture, SmokeTestROS2Domain)
  128. {
  129. auto node = GetRos2Node();
  130. auto pub = node->create_publisher<std_msgs::msg::String>("hello", 20);
  131. int receivedMsgs = 0;
  132. auto sub = node->create_subscription<std_msgs::msg::String>(
  133. "hello",
  134. 10,
  135. [&receivedMsgs](const std_msgs::msg::String& msg)
  136. {
  137. receivedMsgs++;
  138. });
  139. std_msgs::msg::String msg;
  140. msg.data = "Hello World!";
  141. for (int i = 0; i < 10; ++i)
  142. {
  143. pub->publish(msg);
  144. }
  145. SpinAppSome();
  146. EXPECT_EQ(receivedMsgs, 10) << "Did not receive all messages.";
  147. }
  148. //! Check if expected services are available and has the default name
  149. TEST_F(SimulationInterfaceROS2TestFixture, TestIfServicesAvailableInROS2Domain)
  150. {
  151. auto node = GetRos2Node();
  152. auto services = node->get_service_names_and_types();
  153. ASSERT_FALSE(services.empty()) << "No services available.";
  154. EXPECT_NE(services.find("/delete_entity"), services.end());
  155. EXPECT_NE(services.find("/get_entities"), services.end());
  156. EXPECT_NE(services.find("/get_entities_states"), services.end());
  157. EXPECT_NE(services.find("/get_entity_state"), services.end());
  158. EXPECT_NE(services.find("/get_spawnables"), services.end());
  159. EXPECT_NE(services.find("/set_entity_state"), services.end());
  160. EXPECT_NE(services.find("/spawn_entity"), services.end());
  161. EXPECT_NE(services.find("/get_simulator_features"), services.end());
  162. EXPECT_NE(services.find("/step_simulation"), services.end());
  163. }
  164. //! Test if the service call succeeds when the entity is found
  165. TEST_F(SimulationInterfaceROS2TestFixture, TestDeleteEntity_01)
  166. {
  167. using ::testing::_;
  168. auto node = GetRos2Node();
  169. auto mock = std::make_shared<SimulationEntityManagerMockedHandler>();
  170. auto client = node->create_client<simulation_interfaces::srv::DeleteEntity>("/delete_entity");
  171. auto request = std::make_shared<simulation_interfaces::srv::DeleteEntity::Request>();
  172. const char TestEntityName[] = "test_entity";
  173. request->entity = std::string(TestEntityName);
  174. const AZStd::string entityName = AZStd::string(TestEntityName);
  175. EXPECT_CALL(*mock, DeleteEntity(entityName, _))
  176. .WillOnce(::testing::Invoke(
  177. [](const AZStd::string& name, SimulationInterfaces::DeletionCompletedCb completedCb)
  178. {
  179. EXPECT_EQ(name, "test_entity");
  180. completedCb(AZ::Success());
  181. }));
  182. auto future = client->async_send_request(request);
  183. SpinAppUntilFuture(future);
  184. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Service call timed out.";
  185. auto response = future.get();
  186. EXPECT_EQ(response->result.result, simulation_interfaces::msg::Result::RESULT_OK);
  187. }
  188. //! Test if the service call fails when the entity is not found
  189. TEST_F(SimulationInterfaceROS2TestFixture, TestDeleteEntity_02)
  190. {
  191. using ::testing::_;
  192. auto node = GetRos2Node();
  193. SimulationEntityManagerMockedHandler mock;
  194. auto client = node->create_client<simulation_interfaces::srv::DeleteEntity>("/delete_entity");
  195. auto request = std::make_shared<simulation_interfaces::srv::DeleteEntity::Request>();
  196. const char TestEntityName[] = "test_entity";
  197. request->entity = std::string(TestEntityName);
  198. const AZStd::string entityName = AZStd::string(TestEntityName);
  199. EXPECT_CALL(mock, DeleteEntity(entityName, _))
  200. .WillOnce(::testing::Invoke(
  201. [](const AZStd::string& name, SimulationInterfaces::DeletionCompletedCb completedCb)
  202. {
  203. EXPECT_EQ(name, "test_entity");
  204. FailedResult failedResult;
  205. failedResult.m_errorCode = simulation_interfaces::msg::Result::RESULT_NOT_FOUND,
  206. failedResult.m_errorString = "FooBar not found.";
  207. completedCb(AZ::Failure(failedResult));
  208. }));
  209. auto future = client->async_send_request(request);
  210. SpinAppUntilFuture(future);
  211. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Service call timed out.";
  212. auto response = future.get();
  213. EXPECT_EQ(response->result.result, simulation_interfaces::msg::Result::RESULT_NOT_FOUND);
  214. }
  215. //! Happy path test for GetEntities with a sphere shape
  216. //! Test if the service is called with a sphere shape and Posix filter is passed through
  217. TEST_F(SimulationInterfaceROS2TestFixture, GetEntitiesWithShapeSphereAndFilterValid)
  218. {
  219. using ::testing::_;
  220. auto node = GetRos2Node();
  221. SimulationEntityManagerMockedHandler mock;
  222. auto client = node->create_client<simulation_interfaces::srv::GetEntities>("/get_entities");
  223. auto request = std::make_shared<simulation_interfaces::srv::GetEntities::Request>();
  224. request->filters.bounds.points.resize(2);
  225. const AZ::Vector3 point1(1.0f, 2.0f, 3.0f);
  226. request->filters.bounds.points[0].x = point1.GetX();
  227. request->filters.bounds.points[0].y = point1.GetY();
  228. request->filters.bounds.points[0].z = point1.GetZ();
  229. request->filters.bounds.points[1].x = 99.0f;
  230. request->filters.bounds.type = simulation_interfaces::msg::Bounds::TYPE_SPHERE;
  231. request->filters.filter = "FooBarFilter";
  232. EXPECT_CALL(mock, GetEntities(_))
  233. .WillOnce(::testing::Invoke(
  234. [=](const EntityFilters& filter)
  235. {
  236. EXPECT_NE(filter.m_boundsShape, nullptr);
  237. if (filter.m_boundsShape)
  238. {
  239. EXPECT_EQ(filter.m_boundsShape->GetShapeType(), Physics::ShapeType::Sphere);
  240. Physics::SphereShapeConfiguration* sphereShape =
  241. azdynamic_cast<Physics::SphereShapeConfiguration*>(filter.m_boundsShape.get());
  242. EXPECT_EQ(sphereShape->m_radius, 99.0f);
  243. EXPECT_EQ(sphereShape->m_scale, AZ::Vector3(1.0f));
  244. }
  245. auto loc = filter.m_boundsPose.GetTranslation();
  246. EXPECT_EQ(loc.GetX(), point1.GetX());
  247. EXPECT_EQ(loc.GetY(), point1.GetY());
  248. EXPECT_EQ(loc.GetZ(), point1.GetZ());
  249. EXPECT_EQ(filter.m_nameFilter, "FooBarFilter");
  250. return AZ::Success(EntityNameList{ "FooBar" });
  251. }));
  252. auto future = client->async_send_request(request);
  253. SpinAppUntilFuture(future);
  254. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Service call timed out.";
  255. auto response = future.get();
  256. EXPECT_EQ(response->result.result, simulation_interfaces::msg::Result::RESULT_OK);
  257. }
  258. //! Try to call the service with invalid data (too few points) for the sphere shape
  259. TEST_F(SimulationInterfaceROS2TestFixture, GetEntitiesWithShapeSphereWithInvalidData)
  260. {
  261. using ::testing::_;
  262. auto node = GetRos2Node();
  263. [[maybe_unused]] testing::StrictMock<SimulationEntityManagerMockedHandler> mock;
  264. auto client = node->create_client<simulation_interfaces::srv::GetEntities>("/get_entities");
  265. auto request = std::make_shared<simulation_interfaces::srv::GetEntities::Request>();
  266. request->filters.bounds.points.resize(1); // too few points
  267. request->filters.bounds.type = simulation_interfaces::msg::Bounds::TYPE_SPHERE;
  268. auto future = client->async_send_request(request);
  269. SpinAppUntilFuture(future);
  270. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Service call timed out.";
  271. auto response = future.get();
  272. EXPECT_NE(response->result.result, simulation_interfaces::msg::Result::RESULT_OK);
  273. }
  274. //! Happy path test for GetEntities with a box shape
  275. TEST_F(SimulationInterfaceROS2TestFixture, GetEntitiesWithShapeBox)
  276. {
  277. using ::testing::_;
  278. auto node = GetRos2Node();
  279. testing::NiceMock<SimulationEntityManagerMockedHandler> mock;
  280. auto client = node->create_client<simulation_interfaces::srv::GetEntities>("/get_entities");
  281. auto request = std::make_shared<simulation_interfaces::srv::GetEntities::Request>();
  282. request->filters.bounds.points.resize(2);
  283. const AZ::Vector3 point1(1.0f, 2.0f, 3.0f);
  284. const AZ::Vector3 dims(4.0f, 5.0f, 6.0f);
  285. const AZ::Vector3 point2 = point1 + dims;
  286. // order of defined box is upper right, bottom left
  287. // https://github.com/ros-simulation/simulation_interfaces/blob/main/msg/Bounds.msg
  288. request->filters.bounds.points[0].x = point2.GetX();
  289. request->filters.bounds.points[0].y = point2.GetY();
  290. request->filters.bounds.points[0].z = point2.GetZ();
  291. request->filters.bounds.points[1].x = point1.GetX();
  292. request->filters.bounds.points[1].y = point1.GetY();
  293. request->filters.bounds.points[1].z = point1.GetZ();
  294. request->filters.bounds.type = simulation_interfaces::msg::Bounds::TYPE_BOX;
  295. EXPECT_CALL(mock, GetEntities(_))
  296. .WillOnce(::testing::Invoke(
  297. [=](const EntityFilters& filter)
  298. {
  299. EXPECT_NE(filter.m_boundsShape, nullptr);
  300. if (filter.m_boundsShape)
  301. {
  302. EXPECT_EQ(filter.m_boundsShape->GetShapeType(), Physics::ShapeType::Box);
  303. Physics::BoxShapeConfiguration* boxShape =
  304. azdynamic_cast<Physics::BoxShapeConfiguration*>(filter.m_boundsShape.get());
  305. EXPECT_EQ(boxShape->m_dimensions.GetX(), dims.GetX());
  306. EXPECT_EQ(boxShape->m_dimensions.GetY(), dims.GetY());
  307. EXPECT_EQ(boxShape->m_dimensions.GetZ(), dims.GetZ());
  308. }
  309. auto loc = filter.m_boundsPose.GetTranslation();
  310. EXPECT_EQ(loc, AZ::Vector3::CreateZero());
  311. return AZ::Success(EntityNameList{ "FooBar" });
  312. }));
  313. auto future = client->async_send_request(request);
  314. SpinAppUntilFuture(future);
  315. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Service call timed out.";
  316. auto response = future.get();
  317. EXPECT_EQ(response->result.result, simulation_interfaces::msg::Result::RESULT_OK);
  318. }
  319. //! Try to call the service with invalid data (first point is greater than second point in box)
  320. TEST_F(SimulationInterfaceROS2TestFixture, GetEntitiesWithShapeBoxInvalid)
  321. {
  322. using ::testing::_;
  323. auto node = GetRos2Node();
  324. // strict mock, since we don't want to call the real implementation in this test
  325. [[maybe_unused]] testing::StrictMock<SimulationEntityManagerMockedHandler> mock;
  326. auto client = node->create_client<simulation_interfaces::srv::GetEntities>("/get_entities");
  327. auto request = std::make_shared<simulation_interfaces::srv::GetEntities::Request>();
  328. request->filters.bounds.points.resize(2);
  329. request->filters.bounds.points[0].x = 2.0f;
  330. request->filters.bounds.points[0].y = 3.0f;
  331. request->filters.bounds.points[0].z = 4.0f;
  332. request->filters.bounds.points[1].x = -2.0f;
  333. request->filters.bounds.points[1].y = -3.0f;
  334. request->filters.bounds.points[1].z = 5.0f;
  335. request->filters.bounds.type = simulation_interfaces::msg::Bounds::TYPE_BOX;
  336. auto future = client->async_send_request(request);
  337. SpinAppUntilFuture(future);
  338. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Service call timed out.";
  339. auto response = future.get();
  340. EXPECT_NE(response->result.result, simulation_interfaces::msg::Result::RESULT_OK);
  341. }
  342. //! check if capabilities vector is empty when no features are provided by SimulationInterfaces gem
  343. TEST_F(SimulationInterfaceROS2TestFixture, GetSimulationFeaturesNoFeaturesProvidedByGem)
  344. {
  345. using ::testing::_;
  346. auto node = GetRos2Node();
  347. auto client = node->create_client<simulation_interfaces::srv::GetSimulatorFeatures>("/get_simulator_features");
  348. auto request = std::make_shared<simulation_interfaces::srv::GetSimulatorFeatures::Request>();
  349. auto future = client->async_send_request(request);
  350. SpinAppUntilFuture(future);
  351. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Service call timed out.";
  352. auto response = future.get();
  353. EXPECT_TRUE(response->features.features.empty()) << "Features vector is not empty.";
  354. }
  355. //! check if features are returned when the feature is provided by SimulationFeaturesAggregator
  356. //! and the feature is not in the list of features provided by the gem
  357. TEST_F(SimulationInterfaceROS2TestFixture, GetSimulationFeaturesSomeFeaturesProvided)
  358. {
  359. SimulationFeaturesAggregatorRequestsMockedHandler mockAggregator;
  360. using ::testing::_;
  361. auto node = GetRos2Node();
  362. AZStd::unordered_set<SimulationFeatureType> features{
  363. simulation_interfaces::msg::SimulatorFeatures::SPAWNING,
  364. static_cast<SimulationFeatureType>(0xFE), // invalid feature, should be ignored
  365. static_cast<SimulationFeatureType>(0xFF), // invalid feature, should be ignored
  366. };
  367. EXPECT_CALL(mockAggregator, GetSimulationFeatures())
  368. .WillOnce(::testing::Invoke(
  369. [&](void)
  370. {
  371. return features;
  372. }));
  373. auto client = node->create_client<simulation_interfaces::srv::GetSimulatorFeatures>("/get_simulator_features");
  374. auto request = std::make_shared<simulation_interfaces::srv::GetSimulatorFeatures::Request>();
  375. auto future = client->async_send_request(request);
  376. SpinAppUntilFuture(future);
  377. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Service call timed out.";
  378. auto response = future.get();
  379. ASSERT_FALSE(response->features.features.empty()) << "Features vector is empty.";
  380. EXPECT_EQ(response->features.features.size(), 1) << "Features vector should contain one feature only.";
  381. EXPECT_EQ(response->features.features[0], simulation_interfaces::msg::SimulatorFeatures::SPAWNING) << "Feature is not SPAWNING.";
  382. }
  383. //! Check if service succeeds when the name is valid
  384. TEST_F(SimulationInterfaceROS2TestFixture, TryToSpawnWithValidName)
  385. {
  386. using ::testing::_;
  387. SimulationEntityManagerMockedHandler mock;
  388. auto node = GetRos2Node();
  389. auto client = node->create_client<simulation_interfaces::srv::SpawnEntity>("/spawn_entity");
  390. auto request = std::make_shared<simulation_interfaces::srv::SpawnEntity::Request>();
  391. request->name = "valid_name";
  392. request->uri = "test_uri";
  393. request->entity_namespace = "test_namespace";
  394. request->allow_renaming = true;
  395. auto future = client->async_send_request(request);
  396. EXPECT_CALL(mock, SpawnEntity(_, _, _, _, _, _, _))
  397. .WillOnce(::testing::Invoke(
  398. [](const AZStd::string& name,
  399. const AZStd::string& uri,
  400. const AZStd::string& entityNamespace,
  401. const AZ::Transform& initialPose,
  402. const bool allowRename,
  403. PreInsertionCb preinsertionCb,
  404. SpawnCompletedCb completedCb)
  405. {
  406. EXPECT_EQ(name, "valid_name");
  407. EXPECT_EQ(uri, "test_uri");
  408. EXPECT_EQ(entityNamespace, "test_namespace");
  409. EXPECT_TRUE(allowRename);
  410. completedCb(AZ::Success("valid_name"));
  411. }));
  412. SpinAppUntilFuture(future);
  413. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Service call timed out.";
  414. auto response = future.get();
  415. EXPECT_EQ(response->result.result, simulation_interfaces::msg::Result::RESULT_OK);
  416. }
  417. //! Check if service fails when the name is invalid
  418. TEST_F(SimulationInterfaceROS2TestFixture, TryToSpawnWithInvalidName)
  419. {
  420. // strict mock, since we don't want to call the real implementation in this test
  421. [[maybe_unused]] testing::StrictMock<SimulationEntityManagerMockedHandler> mock;
  422. auto node = GetRos2Node();
  423. auto client = node->create_client<simulation_interfaces::srv::SpawnEntity>("/spawn_entity");
  424. auto request = std::make_shared<simulation_interfaces::srv::SpawnEntity::Request>();
  425. request->name = "invalid name"; // invalid name
  426. request->uri = "test_uri";
  427. request->entity_namespace = "test_namespace";
  428. auto future = client->async_send_request(request);
  429. SpinAppUntilFuture(future);
  430. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Service call timed out.";
  431. auto response = future.get();
  432. EXPECT_EQ(response->result.result, simulation_interfaces::srv::SpawnEntity::Response::NAME_INVALID)
  433. << "Service call should fail with invalid name.";
  434. }
  435. //! Check if service fails when the namespace is invalid
  436. TEST_F(SimulationInterfaceROS2TestFixture, TryToSpawnWithInvalidNamespace)
  437. {
  438. // strict mock, since we don't want to call the real implementation in this test
  439. [[maybe_unused]] testing::StrictMock<SimulationEntityManagerMockedHandler> mock;
  440. auto node = GetRos2Node();
  441. auto client = node->create_client<simulation_interfaces::srv::SpawnEntity>("/spawn_entity");
  442. auto request = std::make_shared<simulation_interfaces::srv::SpawnEntity::Request>();
  443. request->name = "valid_name";
  444. request->uri = "test_uri";
  445. request->entity_namespace = "invalid namespace";
  446. auto future = client->async_send_request(request);
  447. SpinAppUntilFuture(future);
  448. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Service call timed out.";
  449. auto response = future.get();
  450. EXPECT_EQ(response->result.result, simulation_interfaces::srv::SpawnEntity::Response::NAMESPACE_INVALID)
  451. << "Service call should fail with invalid name.";
  452. }
  453. TEST_F(SimulationInterfaceROS2TestFixture, SimulationStepsSuccess)
  454. {
  455. using ::testing::_;
  456. auto node = GetRos2Node();
  457. SimulationManagerMockedHandler mock;
  458. auto client = rclcpp_action::create_client<simulation_interfaces::action::SimulateSteps>(node, "/simulate_steps");
  459. auto goal = std::make_shared<simulation_interfaces::action::SimulateSteps::Goal>();
  460. constexpr AZ::u64 steps = 10;
  461. goal->steps = steps;
  462. ASSERT_TRUE(client->wait_for_action_server(std::chrono::seconds(0)) == true) << "Action server is unavailable";
  463. EXPECT_CALL(mock, IsSimulationPaused())
  464. .WillOnce(::testing::Invoke(
  465. []()
  466. {
  467. return true;
  468. }));
  469. EXPECT_CALL(mock, StepSimulation(steps));
  470. EXPECT_CALL(mock, IsSimulationStepsActive())
  471. .WillOnce(::testing::Invoke(
  472. []()
  473. {
  474. return false;
  475. }));
  476. auto future = client->async_send_goal(*goal);
  477. SpinAppUntilFuture(future);
  478. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Action call timed out.";
  479. auto goalHandle = future.get();
  480. ASSERT_NE(goalHandle, nullptr);
  481. auto result = client->async_get_result(goalHandle);
  482. SpinAppUntilFuture(result);
  483. ASSERT_TRUE(result.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Action call timed out.";
  484. EXPECT_EQ(result.get().result->result.result, simulation_interfaces::msg::Result::RESULT_OK);
  485. }
  486. TEST_F(SimulationInterfaceROS2TestFixture, SimulationStepsFailureSimulationIsNotPaused)
  487. {
  488. using ::testing::_;
  489. auto node = GetRos2Node();
  490. SimulationManagerMockedHandler mock;
  491. auto client = rclcpp_action::create_client<simulation_interfaces::action::SimulateSteps>(node, "/simulate_steps");
  492. auto goal = std::make_shared<simulation_interfaces::action::SimulateSteps::Goal>();
  493. goal->steps = 10;
  494. ASSERT_TRUE(client->wait_for_action_server(std::chrono::seconds(0)) == true) << "Action server is unavailable";
  495. EXPECT_CALL(mock, IsSimulationPaused())
  496. .WillOnce(::testing::Invoke(
  497. []()
  498. {
  499. return false;
  500. }));
  501. auto future = client->async_send_goal(*goal);
  502. SpinAppUntilFuture(future);
  503. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Action call timed out.";
  504. auto goalHandle = future.get();
  505. ASSERT_NE(goalHandle, nullptr);
  506. auto result = client->async_get_result(goalHandle);
  507. SpinAppUntilFuture(result);
  508. ASSERT_TRUE(result.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Action call timed out.";
  509. EXPECT_EQ(result.get().result->result.result, simulation_interfaces::msg::Result::RESULT_OPERATION_FAILED);
  510. }
  511. TEST_F(SimulationInterfaceROS2TestFixture, SimulationStepsCancelled)
  512. {
  513. using ::testing::_;
  514. auto node = GetRos2Node();
  515. SimulationManagerMockedHandler mock;
  516. auto client = rclcpp_action::create_client<simulation_interfaces::action::SimulateSteps>(node, "/simulate_steps");
  517. auto goal = std::make_shared<simulation_interfaces::action::SimulateSteps::Goal>();
  518. constexpr AZ::u64 steps = 10;
  519. goal->steps = steps;
  520. ASSERT_TRUE(client->wait_for_action_server(std::chrono::seconds(0)) == true) << "Action server is unavailable";
  521. EXPECT_CALL(mock, IsSimulationPaused())
  522. .WillOnce(::testing::Invoke(
  523. []()
  524. {
  525. return true;
  526. }));
  527. EXPECT_CALL(mock, StepSimulation(steps));
  528. EXPECT_CALL(mock, IsSimulationStepsActive())
  529. .WillRepeatedly(::testing::Invoke(
  530. []()
  531. {
  532. return true;
  533. }));
  534. EXPECT_CALL(mock, CancelStepSimulation());
  535. auto future = client->async_send_goal(*goal);
  536. SpinAppUntilFuture(future);
  537. ASSERT_TRUE(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Action call timed out.";
  538. auto goalHandle = future.get();
  539. ASSERT_NE(goalHandle, nullptr);
  540. auto cancelFuture = client->async_cancel_goal(goalHandle);
  541. SpinAppUntilFuture(cancelFuture);
  542. ASSERT_TRUE(cancelFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) << "Action call timed out.";
  543. }
  544. } // namespace UnitTest
  545. // required to support running integration tests with Qt and PhysX
  546. AZTEST_EXPORT int AZ_UNIT_TEST_HOOK_NAME(int argc, char** argv)
  547. {
  548. ::testing::InitGoogleMock(&argc, argv);
  549. AzQtComponents::PrepareQtPaths();
  550. QApplication app(argc, argv);
  551. AZ::Test::printUnusedParametersWarning(argc, argv);
  552. AZ::Test::addTestEnvironments({ new UnitTest::SimulationInterfaceROS2TestEnvironment() });
  553. int result = RUN_ALL_TESTS();
  554. return result;
  555. }
  556. IMPLEMENT_TEST_EXECUTABLE_MAIN();