OptionsTest.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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. std::unique_ptr<DxcOpts> ReadOptsTest(const MainArgs &mainArgs,
  69. unsigned flagsToInclude,
  70. bool shouldFail = false,
  71. bool shouldMessage = false) {
  72. std::string errorString;
  73. llvm::raw_string_ostream errorStream(errorString);
  74. std::unique_ptr<DxcOpts> opts = llvm::make_unique<DxcOpts>();
  75. int result = ReadDxcOpts(getHlslOptTable(), flagsToInclude, mainArgs,
  76. *(opts.get()), errorStream);
  77. EXPECT_EQ(shouldFail, result != 0);
  78. EXPECT_EQ(shouldMessage, !errorStream.str().empty());
  79. return opts;
  80. }
  81. void ReadOptsTest(const MainArgs &mainArgs,
  82. unsigned flagsToInclude,
  83. const char *expectErrorMsg) {
  84. std::string errorString;
  85. llvm::raw_string_ostream errorStream(errorString);
  86. std::unique_ptr<DxcOpts> opts = llvm::make_unique<DxcOpts>();
  87. int result = ReadDxcOpts(getHlslOptTable(), flagsToInclude, mainArgs,
  88. *(opts.get()), errorStream);
  89. EXPECT_EQ(result, 1);
  90. VERIFY_ARE_EQUAL_STR(expectErrorMsg, errorStream.str().c_str());
  91. }
  92. };
  93. TEST_F(OptionsTest, ReadOptionsWhenExtensionsThenOK) {
  94. const wchar_t *Args[] = {
  95. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  96. L"hlsl.hlsl", L"-external", L"foo.dll", L"-external-fn", L"CreateObj"};
  97. const wchar_t *ArgsNoLib[] = {
  98. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  99. L"hlsl.hlsl", L"-external-fn", L"CreateObj" };
  100. const wchar_t *ArgsNoFn[] = {
  101. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  102. L"hlsl.hlsl", L"-external", L"foo.dll" };
  103. MainArgsArr ArgsArr(Args);
  104. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  105. VERIFY_ARE_EQUAL_STR("CreateObj", o->ExternalFn.data());
  106. VERIFY_ARE_EQUAL_STR("foo.dll", o->ExternalLib.data());
  107. MainArgsArr ArgsNoLibArr(ArgsNoLib);
  108. ReadOptsTest(ArgsNoLibArr, DxcFlags, true, true);
  109. MainArgsArr ArgsNoFnArr(ArgsNoFn);
  110. ReadOptsTest(ArgsNoFnArr, DxcFlags, true, true);
  111. }
  112. TEST_F(OptionsTest, ReadOptionsForOutputObject) {
  113. const wchar_t *Args[] = {
  114. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  115. L"hlsl.hlsl", L"-Fo", L"hlsl.dxbc"};
  116. MainArgsArr ArgsArr(Args);
  117. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  118. VERIFY_ARE_EQUAL_STR("hlsl.dxbc", o->OutputObject.data());
  119. }
  120. TEST_F(OptionsTest, ReadOptionsConflict) {
  121. const wchar_t *matrixArgs[] = {
  122. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  123. L"-Zpr", L"-Zpc",
  124. L"hlsl.hlsl"};
  125. MainArgsArr ArgsArr(matrixArgs);
  126. ReadOptsTest(ArgsArr, DxcFlags, "Cannot specify /Zpr and /Zpc together, use /? to get usage information");
  127. const wchar_t *controlFlowArgs[] = {
  128. L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0",
  129. L"-Gfa", L"-Gfp",
  130. L"hlsl.hlsl"};
  131. MainArgsArr controlFlowArr(controlFlowArgs);
  132. ReadOptsTest(controlFlowArr, DxcFlags, "Cannot specify /Gfa and /Gfp together, use /? to get usage information");
  133. const wchar_t *libArgs[] = {
  134. L"exe.exe", L"/E", L"main", L"/T", L"lib_6_1",
  135. L"hlsl.hlsl"};
  136. MainArgsArr libArr(libArgs);
  137. ReadOptsTest(libArr, DxcFlags, "Must disable validation for unsupported lib_6_1 or lib_6_2 targets.");
  138. }
  139. TEST_F(OptionsTest, ReadOptionsWhenHelpThenShortcut) {
  140. const wchar_t *Args[] = { L"exe.exe", L"--help", L"--unknown-flag" };
  141. MainArgsArr ArgsArr(Args);
  142. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  143. EXPECT_EQ(true, o->ShowHelp);
  144. }
  145. TEST_F(OptionsTest, ReadOptionsWhenValidThenOK) {
  146. const wchar_t *Args[] = { L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0", L"hlsl.hlsl" };
  147. MainArgsArr ArgsArr(Args);
  148. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  149. VERIFY_ARE_EQUAL_STR("main", o->EntryPoint.data());
  150. VERIFY_ARE_EQUAL_STR("ps_6_0", o->TargetProfile.data());
  151. VERIFY_ARE_EQUAL_STR("hlsl.hlsl", o->InputFile.data());
  152. }
  153. TEST_F(OptionsTest, ReadOptionsWhenJoinedThenOK) {
  154. const wchar_t *Args[] = { L"exe.exe", L"/Emain", L"/Tps_6_0", L"hlsl.hlsl" };
  155. MainArgsArr ArgsArr(Args);
  156. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  157. VERIFY_ARE_EQUAL_STR("main", o->EntryPoint.data());
  158. VERIFY_ARE_EQUAL_STR("ps_6_0", o->TargetProfile.data());
  159. VERIFY_ARE_EQUAL_STR("hlsl.hlsl", o->InputFile.data());
  160. }
  161. TEST_F(OptionsTest, ReadOptionsWhenNoEntryThenOK) {
  162. // It's not an error to omit the entry function name, but it's not
  163. // set to 'main' on behalf of callers either.
  164. const wchar_t *Args[] = { L"exe.exe", L"/T", L"ps_6_0", L"hlsl.hlsl" };
  165. MainArgsArr ArgsArr(Args);
  166. std::unique_ptr<DxcOpts> o = ReadOptsTest(ArgsArr, DxcFlags);
  167. VERIFY_IS_TRUE(o->EntryPoint.empty());
  168. }
  169. TEST_F(OptionsTest, ReadOptionsWhenInvalidThenFail) {
  170. const wchar_t *ArgsNoTarget[] = {L"exe.exe", L"/E", L"main", L"hlsl.hlsl"};
  171. const wchar_t *ArgsNoInput[] = {L"exe.exe", L"/E", L"main", L"/T", L"ps_6_0"};
  172. const wchar_t *ArgsNoArg[] = {L"exe.exe", L"hlsl.hlsl", L"/E", L"main",
  173. L"/T"};
  174. const wchar_t *ArgsUnknown[] = { L"exe.exe", L"hlsl.hlsl", L"/E", L"main",
  175. L"/T" L"ps_6_0", L"--unknown"};
  176. const wchar_t *ArgsUnknownButIgnore[] = { L"exe.exe", L"hlsl.hlsl", L"/E", L"main",
  177. L"/T", L"ps_6_0", L"--unknown", L"-Qunused-arguments" };
  178. MainArgsArr ArgsNoTargetArr(ArgsNoTarget),
  179. ArgsNoInputArr(ArgsNoInput), ArgsNoArgArr(ArgsNoArg),
  180. ArgsUnknownArr(ArgsUnknown), ArgsUnknownButIgnoreArr(ArgsUnknownButIgnore);
  181. ReadOptsTest(ArgsNoTargetArr, DxcFlags, true, true);
  182. ReadOptsTest(ArgsNoInputArr, DxcFlags, true, true);
  183. ReadOptsTest(ArgsNoArgArr, DxcFlags, true, true);
  184. ReadOptsTest(ArgsUnknownArr, DxcFlags, true, true);
  185. ReadOptsTest(ArgsUnknownButIgnoreArr, DxcFlags);
  186. }
  187. TEST_F(OptionsTest, ReadOptionsWhenDefinesThenInit) {
  188. const wchar_t *ArgsNoDefines[] = { L"exe.exe", L"/T", L"ps_6_0", L"/E", L"main", L"hlsl.hlsl" };
  189. const wchar_t *ArgsOneDefine[] = { L"exe.exe", L"/DNAME1=1", L"/T", L"ps_6_0", L"/E", L"main", L"hlsl.hlsl" };
  190. 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"};
  191. const wchar_t *ArgsEmptyDefine[] = { L"exe.exe", L"/DNAME1", L"hlsl.hlsl", L"/E", L"main", L"/T", L"ps_6_0", };
  192. MainArgsArr ArgsNoDefinesArr(ArgsNoDefines), ArgsOneDefineArr(ArgsOneDefine),
  193. ArgsTwoDefinesArr(ArgsTwoDefines), ArgsEmptyDefineArr(ArgsEmptyDefine);
  194. std::unique_ptr<DxcOpts> o;
  195. o = ReadOptsTest(ArgsNoDefinesArr, DxcFlags);
  196. EXPECT_EQ(0U, o->Defines.size());
  197. o = ReadOptsTest(ArgsOneDefineArr, DxcFlags);
  198. EXPECT_EQ(1U, o->Defines.size());
  199. EXPECT_STREQW(L"NAME1", o->Defines.data()[0].Name);
  200. EXPECT_STREQW(L"1", o->Defines.data()[0].Value);
  201. o = ReadOptsTest(ArgsTwoDefinesArr, DxcFlags);
  202. EXPECT_EQ(2U, o->Defines.size());
  203. EXPECT_STREQW(L"NAME1", o->Defines.data()[0].Name);
  204. EXPECT_STREQW(L"1", o->Defines.data()[0].Value);
  205. EXPECT_STREQW(L"NAME2", o->Defines.data()[1].Name);
  206. EXPECT_STREQW(L"2", o->Defines.data()[1].Value);
  207. o = ReadOptsTest(ArgsEmptyDefineArr, DxcFlags);
  208. EXPECT_EQ(1U, o->Defines.size());
  209. EXPECT_STREQW(L"NAME1", o->Defines.data()[0].Name);
  210. EXPECT_EQ(nullptr, o->Defines.data()[0].Value);
  211. }
  212. TEST_F(OptionsTest, ReadOptionsForDxcWhenApiArgMissingThenFail) {
  213. // When an argument specified through an API argument is not specified (eg the
  214. // target model), for the command-line dxc.exe tool, then the validation should
  215. // fail.
  216. const wchar_t *Args[] = {L"exe.exe", L"/E", L"main", L"hlsl.hlsl"};
  217. MainArgsArr mainArgsArr(Args);
  218. std::unique_ptr<DxcOpts> o;
  219. o = ReadOptsTest(mainArgsArr, DxcFlags, true, true);
  220. }
  221. TEST_F(OptionsTest, ReadOptionsForApiWhenApiArgMissingThenOK) {
  222. // When an argument specified through an API argument is not specified (eg the
  223. // target model), for an API, then the validation should not fail.
  224. const wchar_t *Args[] = { L"exe.exe", L"/E", L"main", L"hlsl.hlsl" };
  225. MainArgsArr mainArgsArr(Args);
  226. std::unique_ptr<DxcOpts> o;
  227. o = ReadOptsTest(mainArgsArr, CompilerFlags, false, false);
  228. }
  229. TEST_F(OptionsTest, ConvertWhenFailThenThrow) {
  230. std::wstring utf16;
  231. // Simple test to verify conversion works.
  232. EXPECT_EQ(true, Unicode::UTF8ToUTF16String("test", &utf16));
  233. EXPECT_STREQW(L"test", utf16.data());
  234. // Simple test to verify conversion works with actual UTF-8 and not just ASCII.
  235. // n with tilde is Unicode 0x00F1, encoded in UTF-8 as 0xC3 0xB1
  236. EXPECT_EQ(true, Unicode::UTF8ToUTF16String("\xC3\xB1", &utf16));
  237. EXPECT_STREQW(L"\x00F1", utf16.data());
  238. // Fail when the sequence is incomplete.
  239. EXPECT_EQ(false, Unicode::UTF8ToUTF16String("\xC3", &utf16));
  240. // Throw on failure.
  241. bool thrown = false;
  242. try {
  243. Unicode::UTF8ToUTF16StringOrThrow("\xC3");
  244. }
  245. catch (...) {
  246. thrown = true;
  247. }
  248. EXPECT_EQ(true, thrown);
  249. }
  250. TEST_F(OptionsTest, CopyOptionsWhenSingleThenOK) {
  251. const char *ArgsNoDefines[] = {"/T", "ps_6_0", "/E",
  252. "main", "hlsl.hlsl", "-unknown"};
  253. const llvm::opt::OptTable *table = getHlslOptTable();
  254. unsigned missingIndex = 0, missingArgCount = 0;
  255. llvm::opt::InputArgList args =
  256. table->ParseArgs(ArgsNoDefines, missingIndex, missingArgCount, DxcFlags);
  257. std::vector<std::wstring> outArgs;
  258. CopyArgsToWStrings(args, DxcFlags, outArgs);
  259. EXPECT_EQ(4U, outArgs.size()); // -unknown and hlsl.hlsl are missing
  260. VERIFY_ARE_NOT_EQUAL(outArgs.end(), std::find(outArgs.begin(), outArgs.end(), std::wstring(L"/T")));
  261. VERIFY_ARE_NOT_EQUAL(outArgs.end(), std::find(outArgs.begin(), outArgs.end(), std::wstring(L"ps_6_0")));
  262. VERIFY_ARE_NOT_EQUAL(outArgs.end(), std::find(outArgs.begin(), outArgs.end(), std::wstring(L"/E")));
  263. VERIFY_ARE_NOT_EQUAL(outArgs.end(), std::find(outArgs.begin(), outArgs.end(), std::wstring(L"main")));
  264. VERIFY_ARE_EQUAL (outArgs.end(), std::find(outArgs.begin(), outArgs.end(), std::wstring(L"hlsl.hlsl")));
  265. }