ProcessLaunchParseTests.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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/UnitTest/TestTypes.h>
  9. #include <AzFramework/Process/ProcessWatcher.h>
  10. #include <AzFramework/Process/ProcessCommunicator.h>
  11. #include <AzCore/std/containers/vector.h>
  12. #include <AzCore/std/string/string.h>
  13. #include <AzCore/std/containers/unordered_map.h>
  14. #include <AzCore/StringFunc/StringFunc.h>
  15. #include <AzCore/IO/Path/Path.h>
  16. // this is an intentional relative local include so that it can be shared between two unrelated projects
  17. // namely this one test file as well as the one shot executable that these tests invoke.
  18. #include "ProcessLaunchTestTokens.h"
  19. namespace UnitTest
  20. {
  21. class ProcessLaunchParseTests
  22. : public LeakDetectionFixture
  23. {
  24. public:
  25. using ParsedArgMap = AZStd::unordered_map<AZStd::string, AZStd::vector<AZStd::string>>;
  26. static ParsedArgMap ParseParameters(const AZStd::string& processOutput);
  27. };
  28. ProcessLaunchParseTests::ParsedArgMap ProcessLaunchParseTests::ParseParameters(const AZStd::string& processOutput)
  29. {
  30. ParsedArgMap parsedArgs;
  31. AZStd::string currentSwitch;
  32. bool inSwitches{ false };
  33. AZStd::vector<AZStd::string> parsedLines;
  34. AZ::StringFunc::Tokenize(processOutput.c_str(), parsedLines, "\r\n");
  35. for (const AZStd::string& thisLine : parsedLines)
  36. {
  37. if (thisLine == "Switch List:")
  38. {
  39. inSwitches = true;
  40. continue;
  41. }
  42. else if (thisLine == "End Switch List:")
  43. {
  44. inSwitches = false;
  45. continue;
  46. }
  47. if (thisLine.empty())
  48. {
  49. continue;
  50. }
  51. if(inSwitches)
  52. {
  53. if (thisLine[0] != ' ')
  54. {
  55. currentSwitch = thisLine;
  56. }
  57. else
  58. {
  59. parsedArgs[currentSwitch].push_back(thisLine.substr(1));
  60. }
  61. }
  62. }
  63. return parsedArgs;
  64. }
  65. TEST_F(ProcessLaunchParseTests, ProcessLauncher_LaunchBasicProcess_Success)
  66. {
  67. AzFramework::ProcessOutput processOutput;
  68. AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
  69. processLaunchInfo.m_commandlineParameters.emplace<AZStd::string>((AZ::IO::Path(AZ::Test::GetCurrentExecutablePath()) / "ProcessLaunchTest").Native());
  70. processLaunchInfo.m_workingDirectory = AZ::Test::GetCurrentExecutablePath();
  71. processLaunchInfo.m_showWindow = false;
  72. bool launchReturn = AzFramework::ProcessWatcher::LaunchProcessAndRetrieveOutput(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT, processOutput);
  73. EXPECT_EQ(launchReturn, true);
  74. EXPECT_EQ(processOutput.outputResult.empty(), false);
  75. }
  76. TEST_F(ProcessLaunchParseTests, LaunchProcessAndRetrieveOutput_LargeDataNoError_ContainsEntireOutput)
  77. {
  78. using namespace ProcessLaunchTestInternal;
  79. AzFramework::ProcessOutput processOutput;
  80. AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
  81. processLaunchInfo.m_commandlineParameters.emplace<AZStd::vector<AZStd::string>>(
  82. AZStd::vector<AZStd::string>{(AZ::IO::Path(AZ::Test::GetCurrentExecutablePath()) / "ProcessLaunchTest").Native(), "-plentyOfOutput"});
  83. processLaunchInfo.m_workingDirectory = AZ::Test::GetCurrentExecutablePath();
  84. processLaunchInfo.m_showWindow = false;
  85. bool launchReturn = AzFramework::ProcessWatcher::LaunchProcessAndRetrieveOutput(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT, processOutput);
  86. EXPECT_TRUE(launchReturn);
  87. EXPECT_FALSE(processOutput.outputResult.empty());
  88. EXPECT_FALSE(processOutput.HasError());
  89. const auto& output = processOutput.outputResult;
  90. EXPECT_EQ(output.length(), s_numOutputBytesInPlentyMode);
  91. EXPECT_TRUE(output.starts_with(s_beginToken));
  92. EXPECT_TRUE(output.ends_with(s_endToken));
  93. }
  94. // this also tests that it can separately read error and stdout, and that the stream way of doing it works.
  95. TEST_F(ProcessLaunchParseTests, LaunchProcessAndRetrieveOutput_LargeDataWithError_ContainsEntireOutput)
  96. {
  97. using namespace ProcessLaunchTestInternal;
  98. AzFramework::ProcessOutput processOutput;
  99. AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
  100. processLaunchInfo.m_commandlineParameters.emplace<AZStd::vector<AZStd::string>>(
  101. AZStd::vector<AZStd::string>{(AZ::IO::Path(AZ::Test::GetCurrentExecutablePath()) / "ProcessLaunchTest").Native(), "-plentyOfOutput", "-exitCode", "1"});
  102. processLaunchInfo.m_workingDirectory = AZ::Test::GetCurrentExecutablePath();
  103. processLaunchInfo.m_showWindow = false;
  104. bool launchReturn = AzFramework::ProcessWatcher::LaunchProcessAndRetrieveOutput(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT, processOutput);
  105. EXPECT_TRUE(launchReturn);
  106. EXPECT_TRUE(processOutput.HasOutput());
  107. EXPECT_TRUE(processOutput.HasError());
  108. EXPECT_EQ(processOutput.outputResult.length(), s_numOutputBytesInPlentyMode);
  109. EXPECT_TRUE(processOutput.outputResult.starts_with(s_beginToken));
  110. EXPECT_TRUE(processOutput.outputResult.ends_with(s_endToken));
  111. EXPECT_EQ(processOutput.errorResult.length(), s_numOutputBytesInPlentyMode);
  112. EXPECT_TRUE(processOutput.errorResult.starts_with(s_beginToken));
  113. EXPECT_TRUE(processOutput.errorResult.ends_with(s_endToken));
  114. }
  115. TEST_F(ProcessLaunchParseTests, ProcessLauncher_BasicParameter_Success)
  116. {
  117. ProcessLaunchParseTests::ParsedArgMap argMap;
  118. AzFramework::ProcessOutput processOutput;
  119. AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
  120. processLaunchInfo.m_commandlineParameters.emplace<AZStd::vector<AZStd::string>>(
  121. AZStd::vector<AZStd::string>{(AZ::IO::Path(AZ::Test::GetCurrentExecutablePath()) / "ProcessLaunchTest").Native(), "-param1", "param1val", "-param2", "param2val"});
  122. processLaunchInfo.m_workingDirectory = AZ::Test::GetCurrentExecutablePath();
  123. processLaunchInfo.m_showWindow = false;
  124. bool launchReturn = AzFramework::ProcessWatcher::LaunchProcessAndRetrieveOutput(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT, processOutput);
  125. EXPECT_EQ(launchReturn, true);
  126. argMap = ProcessLaunchParseTests::ParseParameters(processOutput.outputResult);
  127. auto param1itr = argMap.find("param1");
  128. EXPECT_NE(param1itr, argMap.end());
  129. AZStd::vector<AZStd::string> param1{ param1itr->second };
  130. EXPECT_EQ(param1.size(), 1);
  131. EXPECT_EQ(param1[0], "param1val");
  132. auto param2itr = argMap.find("param2");
  133. EXPECT_NE(param2itr, argMap.end());
  134. AZStd::vector<AZStd::string> param2{ param2itr->second };
  135. EXPECT_EQ(param2.size(), 1);
  136. EXPECT_EQ(param2[0], "param2val");
  137. }
  138. TEST_F(ProcessLaunchParseTests, ProcessLauncher_WithCommas_Success)
  139. {
  140. ProcessLaunchParseTests::ParsedArgMap argMap;
  141. AzFramework::ProcessOutput processOutput;
  142. AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
  143. processLaunchInfo.m_commandlineParameters.emplace<AZStd::vector<AZStd::string>>(
  144. AZStd::vector<AZStd::string>{(AZ::IO::Path(AZ::Test::GetCurrentExecutablePath()) / "ProcessLaunchTest").Native(), "-param1", "param,1val", "-param2", "param2v,al"});
  145. processLaunchInfo.m_workingDirectory = AZ::Test::GetCurrentExecutablePath();
  146. processLaunchInfo.m_showWindow = false;
  147. bool launchReturn = AzFramework::ProcessWatcher::LaunchProcessAndRetrieveOutput(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT, processOutput);
  148. EXPECT_EQ(launchReturn, true);
  149. argMap = ProcessLaunchParseTests::ParseParameters(processOutput.outputResult);
  150. auto param1itr = argMap.find("param1");
  151. EXPECT_NE(param1itr, argMap.end());
  152. AZStd::vector<AZStd::string> param1{ param1itr->second };
  153. EXPECT_EQ(param1.size(), 2);
  154. EXPECT_EQ(param1[0], "param");
  155. EXPECT_EQ(param1[1], "1val");
  156. auto param2itr = argMap.find("param2");
  157. EXPECT_NE(param2itr, argMap.end());
  158. AZStd::vector<AZStd::string> param2{ param2itr->second };
  159. EXPECT_EQ(param2.size(), 2);
  160. EXPECT_EQ(param2[0], "param2v");
  161. EXPECT_EQ(param2[1], "al");
  162. }
  163. TEST_F(ProcessLaunchParseTests, ProcessLauncher_WithSpaces_Success)
  164. {
  165. ProcessLaunchParseTests::ParsedArgMap argMap;
  166. AzFramework::ProcessOutput processOutput;
  167. AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
  168. processLaunchInfo.m_commandlineParameters.emplace<AZStd::vector<AZStd::string>>(AZStd::vector<AZStd::string>{
  169. (AZ::IO::Path(AZ::Test::GetCurrentExecutablePath()) / "ProcessLaunchTest").Native(), "-param1", R"("param 1val")", R"(-param2="param2v al")" });
  170. processLaunchInfo.m_workingDirectory = AZ::Test::GetCurrentExecutablePath();
  171. processLaunchInfo.m_showWindow = false;
  172. bool launchReturn = AzFramework::ProcessWatcher::LaunchProcessAndRetrieveOutput(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT, processOutput);
  173. EXPECT_EQ(launchReturn, true);
  174. argMap = ProcessLaunchParseTests::ParseParameters(processOutput.outputResult);
  175. auto param1itr = argMap.find("param1");
  176. EXPECT_NE(param1itr, argMap.end());
  177. AZStd::vector<AZStd::string> param1{ param1itr->second };
  178. EXPECT_EQ(param1.size(), 1);
  179. EXPECT_EQ(param1[0], "param 1val");
  180. auto param2itr = argMap.find("param2");
  181. EXPECT_NE(param2itr, argMap.end());
  182. AZStd::vector<AZStd::string> param2{ param2itr->second };
  183. EXPECT_EQ(param2.size(), 1);
  184. EXPECT_EQ(param2[0], "param2v al");
  185. }
  186. TEST_F(ProcessLaunchParseTests, ProcessLauncher_WithSpacesAndComma_Success)
  187. {
  188. ProcessLaunchParseTests::ParsedArgMap argMap;
  189. AzFramework::ProcessOutput processOutput;
  190. AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
  191. processLaunchInfo.m_commandlineParameters.emplace<AZStd::vector<AZStd::string>>(AZStd::vector<AZStd::string>{
  192. (AZ::IO::Path(AZ::Test::GetCurrentExecutablePath()) / "ProcessLaunchTest").Native(), "-param1", R"("param, 1val")", R"(-param2="param,2v al")" });
  193. processLaunchInfo.m_workingDirectory = AZ::Test::GetCurrentExecutablePath();
  194. processLaunchInfo.m_showWindow = false;
  195. bool launchReturn = AzFramework::ProcessWatcher::LaunchProcessAndRetrieveOutput(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT, processOutput);
  196. EXPECT_EQ(launchReturn, true);
  197. argMap = ProcessLaunchParseTests::ParseParameters(processOutput.outputResult);
  198. auto param1itr = argMap.find("param1");
  199. EXPECT_NE(param1itr, argMap.end());
  200. AZStd::vector<AZStd::string> param1{ param1itr->second };
  201. EXPECT_EQ(param1.size(), 1);
  202. EXPECT_EQ(param1[0], "param, 1val");
  203. auto param2itr = argMap.find("param2");
  204. EXPECT_NE(param2itr, argMap.end());
  205. AZStd::vector<AZStd::string> param2{ param2itr->second };
  206. EXPECT_EQ(param2.size(), 1);
  207. EXPECT_EQ(param2[0], "param,2v al");
  208. }
  209. } // namespace UnitTest