RemoteShaderCompilerUnitTests.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /*
  2. * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  3. * its licensors.
  4. *
  5. * For complete copyright and license terms please see the LICENSE at the root of this
  6. * distribution (the "License"). All use of this software is governed by the License,
  7. * or, if provided, by the license below or the license accompanying this file. Do not
  8. * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  9. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. *
  11. */
  12. #include "RenderDll_precompiled.h"
  13. #include <AzTest/AzTest.h>
  14. #include <AzCore/UnitTest/TestTypes.h>
  15. #include <AzCore/Memory/AllocatorScope.h>
  16. #include <AzCore/Settings/SettingsRegistry.h>
  17. #include <AzCore/UnitTest/UnitTest.h>
  18. #include "Mocks/IConsoleMock.h"
  19. #include "Mocks/ICVarMock.h"
  20. #include "Mocks/ISystemMock.h"
  21. #include "RemoteCompiler.h"
  22. namespace AZ
  23. {
  24. class SettingsRegistrySimpleMock;
  25. using NiceSettingsRegistrySimpleMock = ::testing::NiceMock<SettingsRegistrySimpleMock>;
  26. class SettingsRegistrySimpleMock : public AZ::SettingsRegistryInterface
  27. {
  28. public:
  29. MOCK_CONST_METHOD1(GetType, Type(AZStd::string_view));
  30. MOCK_CONST_METHOD2(Visit, bool(Visitor&, AZStd::string_view));
  31. MOCK_CONST_METHOD2(Visit, bool(const VisitorCallback&, AZStd::string_view));
  32. MOCK_METHOD1(RegisterNotifier, NotifyEventHandler(const NotifyCallback&));
  33. MOCK_METHOD1(RegisterNotifier, NotifyEventHandler(NotifyCallback&&));
  34. MOCK_CONST_METHOD2(Get, bool(bool&, AZStd::string_view));
  35. MOCK_CONST_METHOD2(Get, bool(s64&, AZStd::string_view));
  36. MOCK_CONST_METHOD2(Get, bool(u64&, AZStd::string_view));
  37. MOCK_CONST_METHOD2(Get, bool(double&, AZStd::string_view));
  38. MOCK_CONST_METHOD2(Get, bool(AZStd::string&, AZStd::string_view));
  39. MOCK_CONST_METHOD2(Get, bool(FixedValueString&, AZStd::string_view));
  40. MOCK_CONST_METHOD3(GetObject, bool(void*, Uuid, AZStd::string_view));
  41. MOCK_METHOD2(Set, bool(AZStd::string_view, bool));
  42. MOCK_METHOD2(Set, bool(AZStd::string_view, s64));
  43. MOCK_METHOD2(Set, bool(AZStd::string_view, u64));
  44. MOCK_METHOD2(Set, bool(AZStd::string_view, double));
  45. MOCK_METHOD2(Set, bool(AZStd::string_view, AZStd::string_view));
  46. MOCK_METHOD2(Set, bool(AZStd::string_view, const char*));
  47. MOCK_METHOD3(SetObject, bool(AZStd::string_view, const void*, Uuid));
  48. MOCK_METHOD1(Remove, bool(AZStd::string_view));
  49. MOCK_METHOD3(MergeCommandLineArgument, bool(AZStd::string_view, AZStd::string_view, const CommandLineArgumentSettings&));
  50. MOCK_METHOD2(MergeSettings, bool(AZStd::string_view, Format));
  51. MOCK_METHOD4(MergeSettingsFile, bool(AZStd::string_view, Format, AZStd::string_view, AZStd::vector<char>*));
  52. MOCK_METHOD5(
  53. MergeSettingsFolder,
  54. bool(AZStd::string_view, const Specializations&, AZStd::string_view, AZStd::string_view, AZStd::vector<char>*));
  55. };
  56. } // namespace AZ
  57. namespace NRemoteCompiler
  58. {
  59. using ::testing::NiceMock;
  60. using ::testing::Return;
  61. using ::testing::DoAll;
  62. using SystemAllocatorScope = AZ::AllocatorScope<AZ::LegacyAllocator, CryStringAllocator>;
  63. // define a dummy compressor decompressor that simply does nothing.
  64. // the actual ISystem::Decompress / Compress should be tested in isolation in the unit it lives in.
  65. // this is expected to fit both of these functions:
  66. //virtual bool CompressDataBlock(const void* input, size_t inputSize, void* output, size_t& outputSize, int level = 3) = 0;
  67. //virtual bool DecompressDataBlock(const void* input, size_t inputSize, void* output, size_t& outputSize) = 0;
  68. // it operates by copying the input into the output and the input size into the output size and always returning true.
  69. AZ_PUSH_DISABLE_WARNING(4100, "-Wunknown-warning-option")
  70. ACTION(MockCompressDecompress)
  71. {
  72. AZ_Assert(arg3 >= arg1, "MockCompressDecompress would overrun buffer (%i must be >= %i)", (int)arg3, (int)arg1);
  73. if (arg3 < arg1)
  74. {
  75. return false;
  76. }
  77. memcpy(arg2, arg0, arg1);
  78. arg3 = arg1;
  79. return true;
  80. };
  81. AZ_POP_DISABLE_WARNING
  82. using ::UnitTest::AllocatorsTestFixture;
  83. class RemoteCompilerTest
  84. : public AllocatorsTestFixture
  85. , public SystemAllocatorScope
  86. {
  87. public:
  88. const int m_fakePortNumber = 12345;
  89. void SetUp() override
  90. {
  91. using namespace ::testing;
  92. AllocatorsTestFixture::SetUp();
  93. SystemAllocatorScope::ActivateAllocators();
  94. m_priorEnv = gEnv;
  95. m_priorSettingsRegistry = AZ::SettingsRegistry::Get();
  96. m_data.reset(new DataMembers);
  97. AZ::SettingsRegistry::Register(&m_data->m_settings);
  98. ON_CALL(m_data->m_console, GetCVar(_))
  99. .WillByDefault(Return(&m_data->m_cvarMock));
  100. ON_CALL(m_data->m_system, CompressDataBlock(_, _, _, _, _))
  101. .WillByDefault(MockCompressDecompress());
  102. ON_CALL(m_data->m_system, DecompressDataBlock(_, _, _, _))
  103. .WillByDefault(MockCompressDecompress());
  104. ON_CALL(m_data->m_cvarMock, GetIVal())
  105. .WillByDefault(Return(m_fakePortNumber));
  106. memset(&m_data->m_stubEnv, 0, sizeof(SSystemGlobalEnvironment));
  107. m_data->m_stubEnv.pConsole = &m_data->m_console;
  108. m_data->m_stubEnv.pSystem = &m_data->m_system;
  109. gEnv = &m_data->m_stubEnv;
  110. }
  111. void TearDown() override
  112. {
  113. gEnv = m_priorEnv;
  114. AZ::SettingsRegistry::Unregister(&m_data->m_settings);
  115. if (m_priorSettingsRegistry)
  116. {
  117. AZ::SettingsRegistry::Register(m_priorSettingsRegistry);
  118. }
  119. m_data.reset();
  120. SystemAllocatorScope::DeactivateAllocators();
  121. AllocatorsTestFixture::TearDown();
  122. }
  123. struct DataMembers
  124. {
  125. NiceMock<SystemMock> m_system;
  126. NiceMock<ConsoleMock> m_console;
  127. NiceMock<CVarMock> m_cvarMock;
  128. AZ::NiceSettingsRegistrySimpleMock m_settings;
  129. SSystemGlobalEnvironment m_stubEnv;
  130. };
  131. AZStd::unique_ptr<DataMembers> m_data;
  132. SSystemGlobalEnvironment* m_priorEnv = nullptr;
  133. AZ::SettingsRegistryInterface* m_priorSettingsRegistry = nullptr;
  134. };
  135. // allow punch through to PRIVATE functions so that they do not need to be made PUBLIC.
  136. class ShaderSrvUnitTestAccessor
  137. : public CShaderSrv
  138. {
  139. public:
  140. ShaderSrvUnitTestAccessor()
  141. : CShaderSrv()
  142. {
  143. CShaderSrv::EnableUnitTestingMode(true);
  144. }
  145. EServerError SendRequestViaEngineConnection(std::vector<uint8>& rCompileData) const
  146. {
  147. return CShaderSrv::SendRequestViaEngineConnection(rCompileData);
  148. }
  149. bool EncapsulateRequestInEngineConnectionProtocol(std::vector<uint8>& rCompileData) const
  150. {
  151. return CShaderSrv::EncapsulateRequestInEngineConnectionProtocol(rCompileData);
  152. }
  153. };
  154. TEST_F(RemoteCompilerTest, CShaderSrv_Constructor_WithNoGameName_Fails)
  155. {
  156. using namespace ::testing;
  157. AZ::SettingsRegistryInterface::FixedValueString regResult;
  158. EXPECT_CALL(m_data->m_settings, Get(regResult, _));
  159. AZ_TEST_START_TRACE_SUPPRESSION;
  160. ShaderSrvUnitTestAccessor srv;
  161. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  162. }
  163. TEST_F(RemoteCompilerTest, CShaderSrv_Constructor_WithValidGameName_Succeeds)
  164. {
  165. // when we construct the server it calls get on the game name
  166. using namespace ::testing;
  167. AZ::SettingsRegistryInterface::FixedValueString projectName;
  168. EXPECT_CALL(m_data->m_settings, Get(projectName, _))
  169. .WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
  170. ShaderSrvUnitTestAccessor srv;
  171. }
  172. TEST_F(RemoteCompilerTest, CShaderSrv_EncapsulateRequestInEngineConnectionProtocol_EmptyData_Fails)
  173. {
  174. // when we construct the server it calls get on the game name
  175. using namespace ::testing;
  176. AZ::SettingsRegistryInterface::FixedValueString projectName;
  177. EXPECT_CALL(m_data->m_settings, Get(projectName, _))
  178. .WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
  179. ShaderSrvUnitTestAccessor srv;
  180. std::vector<uint8> testVector;
  181. AZ_TEST_START_TRACE_SUPPRESSION;
  182. EXPECT_FALSE(srv.EncapsulateRequestInEngineConnectionProtocol(testVector)); // empty vector error condition
  183. AZ_TEST_STOP_TRACE_SUPPRESSION(1); // expect the above to have thrown an AZ_ERROR
  184. }
  185. TEST_F(RemoteCompilerTest, CShaderSrv_EncapsulateRequestInEngineConnectionProtocol_ValidData_EmptyServerList_Fails)
  186. {
  187. // when we construct the server it calls get on the game name
  188. using namespace ::testing;
  189. AZ::SettingsRegistryInterface::FixedValueString projectName;
  190. EXPECT_CALL(m_data->m_settings, Get(projectName, _))
  191. .WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
  192. ShaderSrvUnitTestAccessor srv;
  193. EXPECT_CALL(m_data->m_cvarMock, GetString())
  194. .WillRepeatedly(Return("")); // empty server list
  195. std::vector<uint8> testVector;
  196. std::string testString("_-=!-");
  197. testVector.insert(testVector.begin(), testString.begin(), testString.end());
  198. AZ_TEST_START_TRACE_SUPPRESSION;
  199. EXPECT_FALSE(srv.EncapsulateRequestInEngineConnectionProtocol(testVector)); // empty vector error condition
  200. AZ_TEST_STOP_TRACE_SUPPRESSION(1); // expect the above to have thrown an AZ_ERROR
  201. }
  202. TEST_F(RemoteCompilerTest, CShaderSrv_EncapsulateRequestInEngineConnectionProtocol_ValidInputs_Succeeds)
  203. {
  204. // when we construct the server it calls get on the game name
  205. using namespace ::testing;
  206. AZ::SettingsRegistryInterface::FixedValueString projectName;
  207. EXPECT_CALL(m_data->m_settings, Get(projectName, _))
  208. .WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
  209. ShaderSrvUnitTestAccessor srv;
  210. // After this, it will repeatedly call get cvar to get the server address:
  211. const char* testList = "10.20.30.40";
  212. EXPECT_CALL(m_data->m_cvarMock, GetString())
  213. .WillRepeatedly(Return(testList));
  214. std::vector<uint8> testVector;
  215. std::string testString("_-=!-");
  216. testVector.insert(testVector.begin(), testString.begin(), testString.end());
  217. EXPECT_TRUE(srv.EncapsulateRequestInEngineConnectionProtocol(testVector));
  218. }
  219. TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_EmptyData_Fails)
  220. {
  221. // when we construct the server it calls get on the game name
  222. using namespace ::testing;
  223. AZ::SettingsRegistryInterface::FixedValueString projectName;
  224. EXPECT_CALL(m_data->m_settings, Get(projectName, _))
  225. .WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
  226. ShaderSrvUnitTestAccessor srv;
  227. // After this, it will repeatedly call get cvar to get the server address:
  228. const char* testList = "10.20.30.40";
  229. EXPECT_CALL(m_data->m_cvarMock, GetString())
  230. .WillRepeatedly(Return(testList));
  231. std::vector<uint8> testVector;
  232. std::string testString("empty");
  233. // test for empty data - RecvFailed expected (error emitted)
  234. AZ_TEST_START_TRACE_SUPPRESSION;
  235. testString = "empty";
  236. testVector.assign(testString.begin(), testString.end());
  237. EXPECT_EQ(srv.SendRequestViaEngineConnection(testVector), EServerError::ESRecvFailed);
  238. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  239. }
  240. TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_IncompleteData_Fails)
  241. {
  242. // when we construct the server it calls get on the game name
  243. using namespace ::testing;
  244. AZ::SettingsRegistryInterface::FixedValueString projectName;
  245. EXPECT_CALL(m_data->m_settings, Get(projectName, _))
  246. .WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
  247. ShaderSrvUnitTestAccessor srv;
  248. // After this, it will repeatedly call get cvar to get the server address:
  249. const char* testList = "10.20.30.40";
  250. EXPECT_CALL(m_data->m_cvarMock, GetString())
  251. .WillRepeatedly(Return(testList));
  252. std::vector<uint8> testVector;
  253. std::string testString("incomplete");
  254. testVector.assign(testString.begin(), testString.end());
  255. // test for incomplete data - RecvFailed expected
  256. AZ_TEST_START_TRACE_SUPPRESSION;
  257. EXPECT_EQ(srv.SendRequestViaEngineConnection(testVector), EServerError::ESRecvFailed);
  258. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  259. }
  260. TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_CorruptData_Fails)
  261. {
  262. // when we construct the server it calls get on the game name
  263. using namespace ::testing;
  264. AZ::SettingsRegistryInterface::FixedValueString projectName;
  265. EXPECT_CALL(m_data->m_settings, Get(projectName, _))
  266. .WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
  267. ShaderSrvUnitTestAccessor srv;
  268. // After this, it will repeatedly call get cvar to get the server address:
  269. const char* testList = "10.20.30.40";
  270. EXPECT_CALL(m_data->m_cvarMock, GetString())
  271. .WillRepeatedly(Return(testList));
  272. std::vector<uint8> testVector;
  273. std::string testString("corrupt");
  274. testVector.assign(testString.begin(), testString.end());
  275. // test for incomplete data - RecvFailed expected
  276. AZ_TEST_START_TRACE_SUPPRESSION;
  277. EXPECT_EQ(srv.SendRequestViaEngineConnection(testVector), EServerError::ESRecvFailed);
  278. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  279. }
  280. TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_CompileError_Fails_ReturnsText)
  281. {
  282. // when we construct the server it calls get on the game name
  283. using namespace ::testing;
  284. AZ::SettingsRegistryInterface::FixedValueString projectName;
  285. EXPECT_CALL(m_data->m_settings, Get(projectName, _))
  286. .WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
  287. ShaderSrvUnitTestAccessor srv;
  288. // After this, it will repeatedly call get cvar to get the server address:
  289. const char* testList = "10.20.30.40";
  290. EXPECT_CALL(m_data->m_cvarMock, GetString())
  291. .WillRepeatedly(Return(testList));
  292. std::vector<uint8> testVector;
  293. std::string testString("corrupt");
  294. testVector.assign(testString.begin(), testString.end());
  295. // test for an actual compile error - decompressed compile error expected to be attached.
  296. testString = "compile_failure";
  297. testVector.assign(testString.begin(), testString.end());
  298. EXPECT_EQ(srv.SendRequestViaEngineConnection(testVector), EServerError::ESCompileError);
  299. // validate the compile error decompressed successfully
  300. const char* expected_decode = "decompressed_plaintext";
  301. EXPECT_EQ(testVector.size(), strlen(expected_decode));
  302. EXPECT_EQ(memcmp(testVector.data(), expected_decode, strlen(expected_decode)), 0);
  303. }
  304. TEST_F(RemoteCompilerTest, CShaderSrv_SendRequestViaEngineConnection_ValidInput_Succeeds_ReturnsText)
  305. {
  306. // when we construct the server it calls get on the game name
  307. using namespace ::testing;
  308. AZ::SettingsRegistryInterface::FixedValueString projectName;
  309. EXPECT_CALL(m_data->m_settings, Get(projectName, _))
  310. .WillOnce(DoAll(testing::SetArgReferee<0>("StarterGame"), Return(true)));
  311. ShaderSrvUnitTestAccessor srv;
  312. // After this, it will repeatedly call get cvar to get the server address:
  313. const char* testList = "10.20.30.40";
  314. EXPECT_CALL(m_data->m_cvarMock, GetString())
  315. .WillRepeatedly(Return(testList));
  316. AZStd::string testString = "success";
  317. std::vector<uint8> testVector;
  318. testVector.assign(testString.begin(), testString.end());
  319. EXPECT_EQ(srv.SendRequestViaEngineConnection(testVector), EServerError::ESOK);
  320. // validate that the result decompressed successfully - its expected to contain "decompressed_plaintext"
  321. const char* expected_decode = "decompressed_plaintext";
  322. EXPECT_EQ(testVector.size(), strlen(expected_decode));
  323. EXPECT_EQ(memcmp(testVector.data(), expected_decode, strlen(expected_decode)), 0);
  324. }
  325. }