소스 검색

[linux-port] adapt HLSL test support to Unix (#1431)

HlslTestUtils.h and a few other headers facilitate test operations.
The header WEXAdapter.h as well as some inline modifications enable
these utilities to be used on Unix-based platforms.

HLSL tests make prodigious use of variadic macros. Unlike previous
cases, these are macros defined by TAEF, so we can't very well just
alter their names and signatures.

Instead, this change employs a combinaton of a clever argument
counting macro that I didn't invent and a standard macro name
concatination to allow a single macro with completely variadic
arguments to redirect to the appropriate macro, silencing any
warnings and allowing for custom definitions for each macro of
different argument numbers.
Greg Roth 7 년 전
부모
커밋
9981dc6915

+ 2 - 1
tools/clang/unittests/HLSL/CompilerTest.cpp

@@ -2547,7 +2547,8 @@ TEST_F(CompilerTest, CompileWhenODumpThenOptimizerMatch) {
                                               nullptr));
 
     string text = DisassembleProgram(m_dllSupport, pOptimizedModule);
-    LogCommentFmt(L"Final program:\r\n%S", text.c_str());
+    WEX::Logging::Log::Comment(L"Final program:");
+    WEX::Logging::Log::Comment(CA2W(text.c_str()));
 
     // At the very least, the module should be valid.
     pResult.Release();

+ 2 - 1
tools/clang/unittests/HLSL/HLSLTestOptions.cpp

@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "HLSLTestOptions.h"
+#include "WEXAdapter.h"
 #include "dxc/Support/WinAdapter.h"
 
 namespace clang {
@@ -29,7 +30,7 @@ ARG_LIST(ARG_DEFINE)
 namespace WEX {
 namespace TestExecution {
 namespace RuntimeParameters {
-HRESULT TryGetValue(const wchar_t *param, std::wstring &retStr) {
+HRESULT TryGetValue(const wchar_t *param, WEX::Common::String &retStr) {
 #define RETURN_ARG(argname)                                                    \
   if (wcscmp(param, L## #argname) == 0) {                                      \
     if (!clang::hlsl::testOptions::argname.empty()) {                          \

+ 48 - 18
tools/clang/unittests/HLSL/HlslTestUtils.h

@@ -11,10 +11,14 @@
 #include <string>
 #include <sstream>
 #include <fstream>
+#ifdef _WIN32
+#include <dxgiformat.h>
+#else
+#include "WEXAdapter.h"
+#endif
 #include "dxc/Support/Unicode.h"
 #include "dxc/HLSL/DxilConstants.h" // DenormMode
 #include "llvm/Support/Atomic.h"
-#include <dxgiformat.h>
 
 // If TAEF verify macros are available, use them to alias other legacy
 // comparison macros that don't have a direct translation.
@@ -28,10 +32,12 @@
 // preprocessor settings.
 
 #ifdef VERIFY_ARE_EQUAL
+#ifndef EXPECT_STREQ
 #define EXPECT_STREQ(a, b) VERIFY_ARE_EQUAL(0, strcmp(a, b))
+#endif
 #define EXPECT_STREQW(a, b) VERIFY_ARE_EQUAL(0, wcscmp(a, b))
 #define VERIFY_ARE_EQUAL_CMP(a, b, ...) VERIFY_IS_TRUE(a == b, __VA_ARGS__)
-#define VERIFY_ARE_EQUAL_STR(a, b, ...) { \
+#define VERIFY_ARE_EQUAL_STR(a, b) { \
   const char *pTmpA = (a);\
   const char *pTmpB = (b);\
   if (0 != strcmp(pTmpA, pTmpB)) {\
@@ -41,27 +47,42 @@
     wchar_t diffMsg[32]; swprintf_s(diffMsg, _countof(diffMsg), L"diff at %u", (unsigned)(pA-pTmpA)); \
     WEX::Logging::Log::Comment(diffMsg); \
   } \
-  VERIFY_ARE_EQUAL(0, strcmp(pTmpA, pTmpB), __VA_ARGS__); \
+  VERIFY_ARE_EQUAL(0, strcmp(pTmpA, pTmpB)); \
 }
-#define VERIFY_ARE_EQUAL_WSTR(a, b, ...) { \
+#define VERIFY_ARE_EQUAL_WSTR(a, b) { \
   if (0 != wcscmp(a, b)) { WEX::Logging::Log::Comment(b);} \
-  VERIFY_ARE_EQUAL(0, wcscmp(a, b), __VA_ARGS__); \
+  VERIFY_ARE_EQUAL(0, wcscmp(a, b)); \
 }
+#ifndef ASSERT_EQ
 #define ASSERT_EQ(expected, actual) VERIFY_ARE_EQUAL(expected, actual)
+#endif
+#ifndef ASSERT_NE
 #define ASSERT_NE(expected, actual) VERIFY_ARE_NOT_EQUAL(expected, actual)
+#endif
+#ifndef TEST_F
 #define TEST_F(typeName, functionName) void typeName::functionName()
+#endif
 #define ASSERT_HRESULT_SUCCEEDED VERIFY_SUCCEEDED
+#ifndef EXPECT_EQ
 #define EXPECT_EQ(expected, actual) VERIFY_ARE_EQUAL(expected, actual)
 #endif
+#endif
 
 namespace hlsl_test {
 
 inline std::wstring
 vFormatToWString(_In_z_ _Printf_format_string_ const wchar_t *fmt, va_list argptr) {
   std::wstring result;
+#ifdef _WIN32
   int len = _vscwprintf(fmt, argptr);
   result.resize(len + 1);
   vswprintf_s((wchar_t *)result.data(), len + 1, fmt, argptr);
+#else
+  wchar_t fmtOut[1000];
+  int len = vswprintf(fmtOut, 1000, fmt, argptr);
+  assert(len >= 0 && "Too long formatted string in vFormatToWstring");
+  result = fmtOut;
+#endif
   return result;
 }
 
@@ -97,14 +118,18 @@ inline std::wstring GetPathToHlslDataFile(const wchar_t* relative) {
 
   wchar_t envPath[MAX_PATH];
   wchar_t expanded[MAX_PATH];
-  swprintf_s(envPath, _countof(envPath), L"%s\\%s", reinterpret_cast<wchar_t*>(HlslDataDirValue.GetBuffer()), relative);
+  swprintf_s(envPath, _countof(envPath), L"%ls\\%ls", reinterpret_cast<const wchar_t*>(HlslDataDirValue.GetBuffer()), relative);
   VERIFY_WIN32_BOOL_SUCCEEDED(ExpandEnvironmentStringsW(envPath, expanded, _countof(expanded)));
   return std::wstring(expanded);
 }
 
 inline bool PathLooksAbsolute(LPCWSTR name) {
   // Very simplified, only for the cases we care about in the test suite.
+#ifdef _WIN32
   return name && *name && ((*name == L'\\') || (name[1] == L':'));
+#else
+  return name && *name && (*name == L'/');
+#endif
 }
 
 inline std::string GetFirstLine(LPCWSTR name) {
@@ -114,7 +139,11 @@ inline std::string GetFirstLine(LPCWSTR name) {
   const std::wstring path = PathLooksAbsolute(name)
                                 ? std::wstring(name)
                                 : hlsl_test::GetPathToHlslDataFile(name);
+#ifdef _WIN32
   std::ifstream infile(path);
+#else
+  std::ifstream infile((CW2A(path.c_str())));
+#endif
   if (infile.bad()) {
     std::wstring errMsg(L"Unable to read file ");
     errMsg += path;
@@ -174,7 +203,7 @@ inline bool GetTestParamUseWARP(bool defaultVal) {
           L"Adapter", AdapterValue))) {
     return defaultVal;
   }
-  if (defaultVal && AdapterValue.IsEmpty() ||
+  if ((defaultVal && AdapterValue.IsEmpty()) ||
       AdapterValue.CompareNoCase(L"WARP") == 0) {
     return true;
   }
@@ -184,11 +213,11 @@ inline bool GetTestParamUseWARP(bool defaultVal) {
 }
 
 inline bool isdenorm(float f) {
-  return FP_SUBNORMAL == fpclassify(f);
+  return FP_SUBNORMAL == std::fpclassify(f);
 }
 
 inline bool isdenorm(double d) {
-  return FP_SUBNORMAL == fpclassify(d);
+  return FP_SUBNORMAL == std::fpclassify(d);
 }
 
 inline float ifdenorm_flushf(float a) {
@@ -200,7 +229,7 @@ inline bool ifdenorm_flushf_eq(float a, float b) {
 }
 
 inline bool ifdenorm_flushf_eq_or_nans(float a, float b) {
-  if (isnan(a) && isnan(b)) return true;
+  if (std::isnan(a) && std::isnan(b)) return true;
   return ifdenorm_flushf(a) == ifdenorm_flushf(b);
 }
 
@@ -331,13 +360,13 @@ inline bool CompareFloatULP(const float &fsrc, const float &fref,
   if (fsrc == fref) {
     return true;
   }
-  if (isnan(fsrc)) {
-    return isnan(fref);
+  if (std::isnan(fsrc)) {
+    return std::isnan(fref);
   }
   if (mode == hlsl::DXIL::Float32DenormMode::Any) {
     // If denorm expected, output can be sign preserved zero. Otherwise output
     // should pass the regular ulp testing.
-    if (isdenorm(fref) && fsrc == 0 && signbit(fsrc) == signbit(fref))
+    if (isdenorm(fref) && fsrc == 0 && std::signbit(fsrc) == std::signbit(fref))
       return true;
   }
   // For FTZ or Preserve mode, we should get the expected number within
@@ -353,13 +382,13 @@ CompareFloatEpsilon(const float &fsrc, const float &fref, float epsilon,
   if (fsrc == fref) {
     return true;
   }
-  if (isnan(fsrc)) {
-    return isnan(fref);
+  if (std::isnan(fsrc)) {
+    return std::isnan(fref);
   }
   if (mode == hlsl::DXIL::Float32DenormMode::Any) {
     // If denorm expected, output can be sign preserved zero. Otherwise output
     // should pass the regular epsilon testing.
-    if (isdenorm(fref) && fsrc == 0 && signbit(fsrc) == signbit(fref))
+    if (isdenorm(fref) && fsrc == 0 && std::signbit(fsrc) == std::signbit(fref))
       return true;
   }
   // For FTZ or Preserve mode, we should get the expected number within
@@ -393,7 +422,7 @@ inline bool CompareHalfEpsilon(const uint16_t &fsrc, const uint16_t &fref, float
     return isnanFloat16(fref);
   float src_f32 = ConvertFloat16ToFloat32(fsrc);
   float ref_f32 = ConvertFloat16ToFloat32(fref);
-  return abs(src_f32-ref_f32) < epsilon;
+  return std::abs(src_f32-ref_f32) < epsilon;
 }
 
 inline bool
@@ -402,6 +431,7 @@ CompareHalfRelativeEpsilon(const uint16_t &fsrc, const uint16_t &fref,
   return CompareHalfULP(fsrc, fref, 10 - nRelativeExp);
 }
 
+#ifdef _WIN32
 // returns the number of bytes per pixel for a given dxgi format
 // add more cases if different format needed to copy back resources
 inline UINT GetByteSizeForFormat(DXGI_FORMAT value) {
@@ -477,7 +507,7 @@ inline UINT GetByteSizeForFormat(DXGI_FORMAT value) {
         return 0;
     }
 }
-
+#endif
 
 #define SIMPLE_IUNKNOWN_IMPL1(_IFACE_) \
   private: volatile llvm::sys::cas_flag m_dwRef; \

+ 3 - 0
tools/clang/unittests/HLSL/TestMain.cpp

@@ -21,6 +21,9 @@
 #endif
 #endif
 
+bool moduleSetup();
+bool moduleTeardown();
+
 namespace {
 using namespace ::testing;
 

+ 155 - 0
tools/clang/unittests/HLSL/WEXAdapter.h

@@ -0,0 +1,155 @@
+#ifndef LLVM_CLANG_UNITTESTS_WEX_ADAPTER_H
+#define LLVM_CLANG_UNITTESTS_WEX_ADAPTER_H
+
+#ifndef _WIN32
+
+#include <unistd.h>
+#include <wchar.h>
+
+#include "dxc/Support/WinAdapter.h"
+#include "dxc/Support/WinFunctions.h"
+#include "gtest/gtest.h"
+
+#define MAX_PATH 260
+
+// Concatinate two macro fragments
+#define CONCAT2(a, b) a##b
+#define CONCAT1(a, b) CONCAT2(a, b)
+#define CONCAT(a, b) CONCAT1(a, b)
+
+// Determine how many arguments are passed to NARG() up to 3
+#define ARG_CT(_1, _2, _3, N, ...) N
+#define NARG(...) ARG_CT(__VA_ARGS__, 3, 2, 1, 0)
+
+// Call the appropriate arity macro based on number of arguments
+#define MACRO_N_(PREFIX, N, ...) CONCAT(PREFIX, N)(__VA_ARGS__)
+#define MACRO_N(PREFIX, ...) MACRO_N_(PREFIX, NARG(__VA_ARGS__), __VA_ARGS__)
+
+// single and double argument versions
+#define VERIFY_SUCCEEDED_1(expr) EXPECT_TRUE(SUCCEEDED(expr))
+#define VERIFY_SUCCEEDED_2(expr, msg) EXPECT_TRUE(SUCCEEDED(expr)) << msg
+#define VERIFY_SUCCEEDED(...) MACRO_N(VERIFY_SUCCEEDED_, __VA_ARGS__)
+
+#define VERIFY_ARE_EQUAL_2(A, B) EXPECT_EQ(A, B)
+#define VERIFY_ARE_EQUAL_3(A, B, msg) EXPECT_EQ(A, B) << msg
+#define VERIFY_ARE_EQUAL(...) MACRO_N(VERIFY_ARE_EQUAL_, __VA_ARGS__)
+
+// Macros to convert TAEF macros to gtest equivalents
+#define VERIFY_IS_TRUE EXPECT_TRUE
+#define VERIFY_FAILED(expr) EXPECT_FALSE(SUCCEEDED(expr))
+#define VERIFY_IS_FALSE EXPECT_FALSE
+#define VERIFY_IS_NULL(exp) EXPECT_EQ(nullptr, (exp))
+#define VERIFY_IS_NOT_NULL(exp) EXPECT_NE(nullptr, (exp))
+#define VERIFY_FAIL ADD_FAILURE
+#define VERIFY_ARE_NOT_EQUAL EXPECT_NE
+#define VERIFY_WIN32_BOOL_SUCCEEDED EXPECT_TRUE
+
+#define TEST_CLASS_SETUP(method)                                               \
+  bool method();                                                               \
+  virtual void SetUp() { EXPECT_TRUE(method()); }
+#define TEST_CLASS_CLEANUP(method)                                             \
+  bool method();                                                               \
+  virtual void TearDown() { EXPECT_TRUE(method()); }
+#define BEGIN_TEST_CLASS(test)
+#define TEST_CLASS_PROPERTY(str1, str2)
+#define TEST_METHOD_PROPERTY(str1, str2)
+#define END_TEST_CLASS()
+#define TEST_METHOD(method)
+#define BEGIN_TEST_METHOD(method)
+#define END_TEST_METHOD()
+
+// gtest lacks any module setup/cleanup. These functions are called by the
+// main() function before and after tests are run. This approximates the
+// behavior.
+bool moduleSetup();
+bool moduleTeardown();
+#define MODULE_SETUP(method)                                                   \
+  bool method();                                                               \
+  bool moduleSetup() { return method(); }
+#define MODULE_CLEANUP(method)                                                 \
+  bool method();                                                               \
+  bool moduleTeardown() { return method(); }
+
+// No need to expand env vars on Unix platforms, so convert the slashes instead.
+inline DWORD ExpandEnvironmentStringsW(_In_ LPCWSTR lpSrc,
+                                       _Out_opt_ LPWSTR lpDst,
+                                       _In_ DWORD nSize) {
+  unsigned i;
+  bool wasSlash = false;
+  for (i = 0; i < nSize && *lpSrc; i++, lpSrc++) {
+    if (*lpSrc == L'\\' || *lpSrc == L'/') {
+      if (!wasSlash)
+        *lpDst++ = L'/';
+      wasSlash = true;
+    } else {
+      *lpDst++ = *lpSrc;
+      wasSlash = false;
+    }
+  }
+  *lpDst = L'\0';
+  return i;
+}
+
+typedef struct _LIST_ENTRY {
+  struct _LIST_ENTRY *Flink;
+  struct _LIST_ENTRY *Blink;
+} LIST_ENTRY, *PLIST_ENTRY, PRLIST_ENTRY;
+
+// Minimal implementation of the WEX namespace functions and classes
+// To either stub out or approximate behavior
+namespace WEX {
+namespace Common {
+class String : public std::wstring {
+public:
+  size_t GetLength() { return length(); }
+  bool IsEmpty() { return empty(); }
+  int CompareNoCase(std::wstring str) const {
+    return -1;
+    assert(!"unimplemented");
+  }
+  operator const wchar_t *() { return c_str(); }
+  const wchar_t *GetBuffer() { return *this; }
+  wchar_t *Format(const wchar_t *fmt, ...) {
+    static wchar_t msg[512];
+    va_list args;
+    va_start(args, fmt);
+    vswprintf(msg, 512, fmt, args);
+    va_end(args);
+    return msg;
+  }
+};
+} // namespace Common
+namespace TestExecution {
+enum class VerifyOutputSettings { LogOnlyFailures };
+class SetVerifyOutput {
+public:
+  SetVerifyOutput(VerifyOutputSettings) {}
+};
+class DisableVerifyExceptions {
+public:
+  DisableVerifyExceptions() {}
+};
+namespace RuntimeParameters {
+HRESULT TryGetValue(const wchar_t *param, Common::String &retStr);
+} // namespace RuntimeParameters
+} // namespace TestExecution
+namespace Logging {
+namespace Log {
+inline void StartGroup(const wchar_t *name) { wprintf(L"BEGIN TEST(S): <%ls>\n", name); };
+inline void EndGroup(const wchar_t *name) { wprintf(L"END TEST(S): <%ls>\n", name); };
+inline void Comment(const wchar_t *msg) {
+  fputws(msg, stdout);
+  fputwc(L'\n', stdout);
+}
+inline void Error(const wchar_t *msg) {
+  fputws(msg, stderr);
+  fputwc(L'\n', stderr);
+  ADD_FAILURE();
+}
+} // namespace Log
+} // namespace Logging
+} // namespace WEX
+
+#endif // _WIN32
+
+#endif // LLVM_CLANG_UNITTESTS_WEX_ADAPTER_H