OptionsTest.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // OptionsTest.cpp //
  4. // Copyright (C) Microsoft Corporation. All rights reserved. //
  5. // This file is distributed under the University of Illinois Open Source //
  6. // License. See LICENSE.TXT for details. //
  7. // //
  8. // Provides tests for the command-line options APIs. //
  9. // //
  10. ///////////////////////////////////////////////////////////////////////////////
  11. #ifndef UNICODE
  12. #define UNICODE
  13. #endif
  14. #include <memory>
  15. #include <vector>
  16. #include <string>
  17. #include <cassert>
  18. #include <sstream>
  19. #include <algorithm>
  20. #include "dxc/Support/WinIncludes.h"
  21. #include "dxc/dxcapi.h"
  22. #include "dxc/Test/HLSLTestData.h"
  23. #ifdef _WIN32
  24. #include "WexTestClass.h"
  25. #endif
  26. #include "dxc/Test/HlslTestUtils.h"
  27. #include "llvm/Support/raw_os_ostream.h"
  28. #include "llvm/ADT/STLExtras.h"
  29. #include "dxc/Support/Global.h"
  30. #include "dxc/Support/dxcapi.use.h"
  31. #include "dxc/Support/HLSLOptions.h"
  32. #include "dxc/Support/Unicode.h"
  33. #include <fstream>
  34. using namespace std;
  35. using namespace hlsl_test;
  36. using namespace hlsl::options;
  37. /// Use this class to construct MainArgs from constants. Handy to use because
  38. /// DxcOpts will StringRef into it.
  39. class MainArgsArr : public MainArgs {
  40. public:
  41. template <size_t n>
  42. MainArgsArr(const wchar_t *(&arr)[n]) : MainArgs(n, arr) {}
  43. };
  44. #ifdef _WIN32
  45. class OptionsTest {
  46. #else
  47. class OptionsTest : public ::testing::Test {
  48. #endif
  49. public:
  50. BEGIN_TEST_CLASS(OptionsTest)
  51. TEST_CLASS_PROPERTY(L"Parallel", L"true")
  52. TEST_METHOD_PROPERTY(L"Priority", L"0")
  53. END_TEST_CLASS()
  54. TEST_METHOD(ReadOptionsWhenDefinesThenInit)
  55. TEST_METHOD(ReadOptionsWhenExtensionsThenOK)
  56. TEST_METHOD(ReadOptionsWhenHelpThenShortcut)
  57. TEST_METHOD(ReadOptionsWhenInvalidThenFail)
  58. TEST_METHOD(ReadOptionsConflict)
  59. TEST_METHOD(ReadOptionsWhenValidThenOK)
  60. TEST_METHOD(ReadOptionsWhenJoinedThenOK)
  61. TEST_METHOD(ReadOptionsWhenNoEntryThenOK)
  62. TEST_METHOD(ReadOptionsForOutputObject)
  63. TEST_METHOD(ReadOptionsForDxcWhenApiArgMissingThenFail)
  64. TEST_METHOD(ReadOptionsForApiWhenApiArgMissingThenOK)
  65. TEST_METHOD(ConvertWhenFailThenThrow)
  66. TEST_METHOD(CopyOptionsWhenSingleThenOK)
  67. //TEST_METHOD(CopyOptionsWhenMultipleThenOK)
  68. TEST_METHOD(ReadOptionsJoinedWithSpacesThenOK)
  69. std::unique_ptr<DxcOpts> ReadOptsTest(const MainArgs &mainArgs,
  70. unsigned flagsToInclude,
  71. bool shouldFail = false,
  72. bool shouldMessage = false) {
  73. std::string errorString;
  74. llvm::raw_string_ostream errorStream(errorString);
  75. std::unique_ptr<DxcOpts> opts = llvm::make_unique<DxcOpts>();
  76. int result = ReadDxcOpts(getHlslOptTable(), flagsToInclude, mainArgs,
  77. *(opts.get()), errorStream);
  78. EXPECT_EQ(shouldFail, result != 0);
  79. EXPECT_EQ(shouldMessage, !errorStream.str().empty());
  80. return opts;
  81. }
  82. void ReadOptsTest(const MainArgs &mainArgs,
  83. unsigned flagsToInclude,
  84. const char *expectErrorMsg) {
  85. std::string errorString;
  86. llvm::raw_string_ostream errorStream(errorString);
  87. std::unique_ptr<DxcOpts> opts = llvm::make_unique<DxcOpts>();
  88. int result = ReadDxcOpts(getHlslOptTable(), flagsToInclude, mainArgs,
  89. *(opts.get()), errorStream);
  90. EXPECT_EQ(result, 1);
  91. VERIFY_ARE_EQUAL_STR(expectErrorMsg, errorStream.str().c_str());
  92. }
  93. };
  94. TEST_F(OptionsTest, ReadOptionsWhenExtensionsThenOK) {
  95. const wchar_t *Args[] = {
  96. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  97. L"hlsl.hlsl", L"-external", L"foo.dll", L"-external-fn", L"CreateObj"};
  98. const wchar_t *ArgsNoLib[] = {
  99. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  100. L"hlsl.hlsl", L"-external-fn", L"CreateObj" };
  101. const wchar_t *ArgsNoFn[] = {
  102. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  103. L"hlsl.hlsl", L"-external", L"foo.dll" };
  104. MainArgsArr ArgsArr(Args);
  105. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  106. VERIFY_ARE_EQUAL_STR("CreateObj", o->ExternalFn.data());
  107. VERIFY_ARE_EQUAL_STR("foo.dll", o->ExternalLib.data());
  108. MainArgsArr ArgsNoLibArr(ArgsNoLib);
  109. ReadOptsTest(ArgsNoLibArr, DxcFlags, true, true);
  110. MainArgsArr ArgsNoFnArr(ArgsNoFn);
  111. ReadOptsTest(ArgsNoFnArr, DxcFlags, true, true);
  112. }
  113. TEST_F(OptionsTest, ReadOptionsForOutputObject) {
  114. const wchar_t *Args[] = {
  115. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  116. L"hlsl.hlsl", L"-Fo", L"hlsl.dxbc"};
  117. MainArgsArr ArgsArr(Args);
  118. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  119. VERIFY_ARE_EQUAL_STR("hlsl.dxbc", o->OutputObject.data());
  120. }
  121. TEST_F(OptionsTest, ReadOptionsConflict) {
  122. const wchar_t *matrixArgs[] = {
  123. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  124. L"-Zpr", L"-Zpc",
  125. L"hlsl.hlsl"};
  126. MainArgsArr ArgsArr(matrixArgs);
  127. ReadOptsTest(ArgsArr, DxcFlags, "Cannot specify /Zpr and /Zpc together, use /? to get usage information");
  128. const wchar_t *controlFlowArgs[] = {
  129. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  130. L"-Gfa", L"-Gfp",
  131. L"hlsl.hlsl"};
  132. MainArgsArr controlFlowArr(controlFlowArgs);
  133. ReadOptsTest(controlFlowArr, DxcFlags, "Cannot specify /Gfa and /Gfp together, use /? to get usage information");
  134. const wchar_t *libArgs[] = {
  135. L"exe.exe", L"/E", L"main", L"/T", L"lib_6_1",
  136. L"hlsl.hlsl"};
  137. MainArgsArr libArr(libArgs);
  138. ReadOptsTest(libArr, DxcFlags, "Must disable validation for unsupported lib_6_1 or lib_6_2 targets.");
  139. }
  140. TEST_F(OptionsTest, ReadOptionsWhenHelpThenShortcut) {
  141. const wchar_t *Args[] = { L"exe.exe", L"--help", L"--unknown-flag" };
  142. MainArgsArr ArgsArr(Args);
  143. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  144. EXPECT_EQ(true, o->ShowHelp);
  145. }
  146. TEST_F(OptionsTest, ReadOptionsWhenValidThenOK) {
  147. const wchar_t *Args[] = { L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0", L"hlsl.hlsl" };
  148. MainArgsArr ArgsArr(Args);
  149. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  150. VERIFY_ARE_EQUAL_STR("main", o->EntryPoint.data());
  151. VERIFY_ARE_EQUAL_STR("ps_6_0", o->TargetProfile.data());
  152. VERIFY_ARE_EQUAL_STR("hlsl.hlsl", o->InputFile.data());
  153. }
  154. TEST_F(OptionsTest, ReadOptionsWhenJoinedThenOK) {
  155. const wchar_t *Args[] = { L"exe.exe", L"/Emain", L"/Tps_6_0", L"hlsl.hlsl" };
  156. MainArgsArr ArgsArr(Args);
  157. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  158. VERIFY_ARE_EQUAL_STR("main", o->EntryPoint.data());
  159. VERIFY_ARE_EQUAL_STR("ps_6_0", o->TargetProfile.data());
  160. VERIFY_ARE_EQUAL_STR("hlsl.hlsl", o->InputFile.data());
  161. }
  162. TEST_F(OptionsTest, ReadOptionsWhenNoEntryThenOK) {
  163. // It's not an error to omit the entry function name, but it's not
  164. // set to 'main' on behalf of callers either.
  165. const wchar_t *Args[] = { L"exe.exe", L"/T", L"ps_6_0", L"hlsl.hlsl" };
  166. MainArgsArr ArgsArr(Args);
  167. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  168. VERIFY_IS_TRUE(o->EntryPoint.empty());
  169. }
  170. TEST_F(OptionsTest, ReadOptionsWhenInvalidThenFail) {
  171. const wchar_t *ArgsNoTarget[] = {L"exe.exe", L"/E", L"main", L"hlsl.hlsl"};
  172. const wchar_t *ArgsNoInput[] = {L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0"};
  173. const wchar_t *ArgsNoArg[] = {L"exe.exe", L"hlsl.hlsl", L"/E", L"main",
  174. L"/T"};
  175. const wchar_t *ArgsUnknown[] = { L"exe.exe", L"hlsl.hlsl", L"/E", L"main",
  176. L"/T" L"ps_6_0", L"--unknown"};
  177. const wchar_t *ArgsUnknownButIgnore[] = { L"exe.exe", L"hlsl.hlsl", L"/E", L"main",
  178. L"/T", L"ps_6_0", L"--unknown", L"-Qunused-arguments" };
  179. MainArgsArr ArgsNoTargetArr(ArgsNoTarget),
  180. ArgsNoInputArr(ArgsNoInput), ArgsNoArgArr(ArgsNoArg),
  181. ArgsUnknownArr(ArgsUnknown), ArgsUnknownButIgnoreArr(ArgsUnknownButIgnore);
  182. ReadOptsTest(ArgsNoTargetArr, DxcFlags, true, true);
  183. ReadOptsTest(ArgsNoInputArr, DxcFlags, true, true);
  184. ReadOptsTest(ArgsNoArgArr, DxcFlags, true, true);
  185. ReadOptsTest(ArgsUnknownArr, DxcFlags, true, true);
  186. ReadOptsTest(ArgsUnknownButIgnoreArr, DxcFlags);
  187. }
  188. TEST_F(OptionsTest, ReadOptionsWhenDefinesThenInit) {
  189. const wchar_t *ArgsNoDefines[] = { L"exe.exe", L"/T", L"ps_6_0", L"/E", L"main", L"hlsl.hlsl" };
  190. const wchar_t *ArgsOneDefine[] = { L"exe.exe", L"/DNAME1=1", L"/T", L"ps_6_0", L"/E", L"main", L"hlsl.hlsl" };
  191. const wchar_t *ArgsTwoDefines[] = { L"exe.exe", L"/DNAME1=1", L"/T", L"ps_6_0", L"/D", L"NAME2=2", L"/E", L"main", L"/T", L"ps_6_0", L"hlsl.hlsl"};
  192. const wchar_t *ArgsEmptyDefine[] = { L"exe.exe", L"/DNAME1", L"hlsl.hlsl", L"/E", L"main", L"/T", L"ps_6_0", };
  193. MainArgsArr ArgsNoDefinesArr(ArgsNoDefines), ArgsOneDefineArr(ArgsOneDefine),
  194. ArgsTwoDefinesArr(ArgsTwoDefines), ArgsEmptyDefineArr(ArgsEmptyDefine);
  195. std::unique_ptr<DxcOpts> o;
  196. o = ReadOptsTest(ArgsNoDefinesArr, DxcFlags);
  197. EXPECT_EQ(0U, o->Defines.size());
  198. o = ReadOptsTest(ArgsOneDefineArr, DxcFlags);
  199. EXPECT_EQ(1U, o->Defines.size());
  200. EXPECT_STREQW(L"NAME1", o->Defines.data()[0].Name);
  201. EXPECT_STREQW(L"1", o->Defines.data()[0].Value);
  202. o = ReadOptsTest(ArgsTwoDefinesArr, DxcFlags);
  203. EXPECT_EQ(2U, o->Defines.size());
  204. EXPECT_STREQW(L"NAME1", o->Defines.data()[0].Name);
  205. EXPECT_STREQW(L"1", o->Defines.data()[0].Value);
  206. EXPECT_STREQW(L"NAME2", o->Defines.data()[1].Name);
  207. EXPECT_STREQW(L"2", o->Defines.data()[1].Value);
  208. o = ReadOptsTest(ArgsEmptyDefineArr, DxcFlags);
  209. EXPECT_EQ(1U, o->Defines.size());
  210. EXPECT_STREQW(L"NAME1", o->Defines.data()[0].Name);
  211. EXPECT_EQ(nullptr, o->Defines.data()[0].Value);
  212. }
  213. TEST_F(OptionsTest, ReadOptionsForDxcWhenApiArgMissingThenFail) {
  214. // When an argument specified through an API argument is not specified (eg the
  215. // target model), for the command-line dxc.exe tool, then the validation should
  216. // fail.
  217. const wchar_t *Args[] = {L"exe.exe", L"/E", L"main", L"hlsl.hlsl"};
  218. MainArgsArr mainArgsArr(Args);
  219. std::unique_ptr<DxcOpts> o;
  220. o = ReadOptsTest(mainArgsArr, DxcFlags, true, true);
  221. }
  222. TEST_F(OptionsTest, ReadOptionsForApiWhenApiArgMissingThenOK) {
  223. // When an argument specified through an API argument is not specified (eg the
  224. // target model), for an API, then the validation should not fail.
  225. const wchar_t *Args[] = { L"exe.exe", L"/E", L"main", L"hlsl.hlsl" };
  226. MainArgsArr mainArgsArr(Args);
  227. std::unique_ptr<DxcOpts> o;
  228. o = ReadOptsTest(mainArgsArr, CompilerFlags, false, false);
  229. }
  230. TEST_F(OptionsTest, ConvertWhenFailThenThrow) {
  231. std::wstring utf16;
  232. // Simple test to verify conversion works.
  233. EXPECT_EQ(true, Unicode::UTF8ToUTF16String("test", &utf16));
  234. EXPECT_STREQW(L"test", utf16.data());
  235. // Simple test to verify conversion works with actual UTF-8 and not just ASCII.
  236. // n with tilde is Unicode 0x00F1, encoded in UTF-8 as 0xC3 0xB1
  237. EXPECT_EQ(true, Unicode::UTF8ToUTF16String("\xC3\xB1", &utf16));
  238. EXPECT_STREQW(L"\x00F1", utf16.data());
  239. // Fail when the sequence is incomplete.
  240. EXPECT_EQ(false, Unicode::UTF8ToUTF16String("\xC3", &utf16));
  241. // Throw on failure.
  242. bool thrown = false;
  243. try {
  244. Unicode::UTF8ToUTF16StringOrThrow("\xC3");
  245. }
  246. catch (...) {
  247. thrown = true;
  248. }
  249. EXPECT_EQ(true, thrown);
  250. }
  251. TEST_F(OptionsTest, CopyOptionsWhenSingleThenOK) {
  252. const char *ArgsNoDefines[] = {"/T", "ps_6_0", "/E",
  253. "main", "hlsl.hlsl", "-unknown"};
  254. const llvm::opt::OptTable *table = getHlslOptTable();
  255. unsigned missingIndex = 0, missingArgCount = 0;
  256. llvm::opt::InputArgList args =
  257. table->ParseArgs(ArgsNoDefines, missingIndex, missingArgCount, DxcFlags);
  258. std::vector<std::wstring> outArgs;
  259. CopyArgsToWStrings(args, DxcFlags, outArgs);
  260. EXPECT_EQ(4U, outArgs.size()); // -unknown and hlsl.hlsl are missing
  261. VERIFY_ARE_NOT_EQUAL(outArgs.end(), std::find(outArgs.begin(), outArgs.end(), std::wstring(L"/T")));
  262. VERIFY_ARE_NOT_EQUAL(outArgs.end(), std::find(outArgs.begin(), outArgs.end(), std::wstring(L"ps_6_0")));
  263. VERIFY_ARE_NOT_EQUAL(outArgs.end(), std::find(outArgs.begin(), outArgs.end(), std::wstring(L"/E")));
  264. VERIFY_ARE_NOT_EQUAL(outArgs.end(), std::find(outArgs.begin(), outArgs.end(), std::wstring(L"main")));
  265. VERIFY_ARE_EQUAL (outArgs.end(), std::find(outArgs.begin(), outArgs.end(), std::wstring(L"hlsl.hlsl")));
  266. }
  267. TEST_F(OptionsTest, ReadOptionsJoinedWithSpacesThenOK) {
  268. {
  269. // Ensure parsing arguments in joined form with embedded spaces
  270. // between the option and the argument works, for these argument types:
  271. // - JoinedOrSeparateClass (-E, -T)
  272. // - SeparateClass (-external, -external-fn)
  273. const wchar_t *Args[] = {
  274. L"exe.exe", L"-E main", L"/T ps_6_0",
  275. L"hlsl.hlsl", L"-external foo.dll", L"-external-fn CreateObj"};
  276. MainArgsArr ArgsArr(Args);
  277. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  278. VERIFY_ARE_EQUAL_STR("main", o->EntryPoint.data());
  279. VERIFY_ARE_EQUAL_STR("ps_6_0", o->TargetProfile.data());
  280. VERIFY_ARE_EQUAL_STR("CreateObj", o->ExternalFn.data());
  281. VERIFY_ARE_EQUAL_STR("foo.dll", o->ExternalLib.data());
  282. }
  283. {
  284. // Ignore trailing spaces in option name for JoinedOrSeparateClass
  285. // Otherwise error messages are not easy for user to interpret
  286. const wchar_t *Args[] = {
  287. L"exe.exe", L"-E ", L"main", L"/T ", L"ps_6_0",
  288. L"hlsl.hlsl"};
  289. MainArgsArr ArgsArr(Args);
  290. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  291. VERIFY_ARE_EQUAL_STR("main", o->EntryPoint.data());
  292. VERIFY_ARE_EQUAL_STR("ps_6_0", o->TargetProfile.data());
  293. }
  294. {
  295. // Ignore trailing spaces in option name for SeparateClass
  296. // Otherwise error messages are not easy for user to interpret
  297. const wchar_t *Args[] = {
  298. L"exe.exe", L"-E", L"main", L"/T", L"ps_6_0",
  299. L"hlsl.hlsl", L"-external ", L"foo.dll", L"-external-fn ", L"CreateObj"};
  300. MainArgsArr ArgsArr(Args);
  301. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  302. VERIFY_ARE_EQUAL_STR("CreateObj", o->ExternalFn.data());
  303. VERIFY_ARE_EQUAL_STR("foo.dll", o->ExternalLib.data());
  304. }
  305. }