InterfacesTest.cpp 27 KB

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