InterfacesTest.cpp 28 KB

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