Add helper shortcut to 'renderdoccmd test' to run functional tests

* This invokes run_tests.py with any arguments but specifies the renderdoc
  module and python module paths automatically. Only works if built within the
  project repo itself as otherwise it won't locate the test script
This commit is contained in:
baldurk
2019-02-14 15:45:22 +00:00
parent 634040d4b5
commit 34a97482dd
12 changed files with 267 additions and 45 deletions
+13 -4
View File
@@ -269,17 +269,26 @@ endif()
add_subdirectory(renderdoc)
if(ENABLE_RENDERDOCCMD)
add_subdirectory(renderdoccmd)
endif()
# these variables are handled within the CMakeLists.txt in qrenderdoc,
# but we need to add it if either is enabled since the swig bindings
# are handled in common
if(ENABLE_QRENDERDOC OR ENABLE_PYRENDERDOC)
# Make sure Python 3 is found
set(Python_ADDITIONAL_VERSIONS 3.4 3.5 3.6 3.7)
find_package(PythonInterp 3 REQUIRED)
find_package(PythonLibs 3 REQUIRED)
# we also need python3-config for swig
if(NOT EXISTS "${PYTHON_EXECUTABLE}-config" AND NOT EXISTS "${PYTHON_EXECUTABLE}.${PYTHON_VERSION_MINOR}-config")
message(FATAL_ERROR "We require ${PYTHON_EXECUTABLE}-config or ${PYTHON_EXECUTABLE}.${PYTHON_VERSION_MINOR}-config to build swig, please install the python dev package for your system.")
endif()
add_subdirectory(qrenderdoc)
endif()
if(ENABLE_RENDERDOCCMD)
add_subdirectory(renderdoccmd)
endif()
# install documentation files
install (FILES util/LINUX_DIST_README DESTINATION share/doc/renderdoc RENAME README)
install (FILES LICENSE.md DESTINATION share/doc/renderdoc)
-9
View File
@@ -60,15 +60,6 @@ else()
add_custom_command(OUTPUT RenderDoc.icns COMMAND touch RenderDoc.icns)
endif()
# Make sure Python 3 is found
set(Python_ADDITIONAL_VERSIONS 3.4 3.5 3.6 3.7)
find_package(PythonInterp 3 REQUIRED)
find_package(PythonLibs 3 REQUIRED)
# we also need python3-config for swig
if(NOT EXISTS "${PYTHON_EXECUTABLE}-config" AND NOT EXISTS "${PYTHON_EXECUTABLE}.${PYTHON_VERSION_MINOR}-config")
message(FATAL_ERROR "We require ${PYTHON_EXECUTABLE}-config or ${PYTHON_EXECUTABLE}.${PYTHON_VERSION_MINOR}-config to build swig, please install the python dev package for your system.")
endif()
include(ExternalProject)
# Need bison for swig
+4
View File
@@ -2291,3 +2291,7 @@ extern "C" RENDERDOC_API AndroidFlags RENDERDOC_CC RENDERDOC_MakeDebuggablePacka
DOCUMENT("Internal function that runs unit tests.");
extern "C" RENDERDOC_API int RENDERDOC_CC RENDERDOC_RunUnitTests(const rdcstr &command,
const rdcarray<rdcstr> &args);
DOCUMENT("Internal function that runs functional tests.");
extern "C" RENDERDOC_API int RENDERDOC_CC RENDERDOC_RunFunctionalTests(int pythonMinorVersion,
const rdcarray<rdcstr> &args);
+2 -1
View File
@@ -370,7 +370,8 @@ namespace StringFormat
{
void sntimef(time_t utcTime, char *str, size_t bufSize, const char *format);
string Wide2UTF8(const std::wstring &s);
std::string Wide2UTF8(const std::wstring &s);
std::wstring UTF82Wide(const std::string &s);
void Shutdown();
};
@@ -102,12 +102,18 @@ void GetLibraryFilename(string &selfName)
namespace StringFormat
{
string Wide2UTF8(const std::wstring &s)
std::string Wide2UTF8(const std::wstring &s)
{
RDCFATAL("Converting wide strings to UTF-8 is not supported on Android!");
return "";
}
std::wstring UTF82Wide(const std::string &s)
{
RDCFATAL("Converting UTF-8 to wide strings is not supported on Android!");
return L"";
}
void Shutdown()
{
}
+54 -9
View File
@@ -134,35 +134,38 @@ namespace StringFormat
{
// cache iconv_t descriptor to save on iconv_open/iconv_close each time
iconv_t iconvWide2UTF8 = (iconv_t)-1;
iconv_t iconvUTF82Wide = (iconv_t)-1;
// iconv is not thread safe when sharing an iconv_t descriptor
// I don't expect much contention but if it happens we could TryLock
// before creating a temporary iconv_t, or hold two iconv_ts, or something.
Threading::CriticalSection lockWide2UTF8;
Threading::CriticalSection iconvLock;
void Shutdown()
{
SCOPED_LOCK(lockWide2UTF8);
SCOPED_LOCK(iconvLock);
if(iconvWide2UTF8 != (iconv_t)-1)
iconv_close(iconvWide2UTF8);
iconvWide2UTF8 = (iconv_t)-1;
if(iconvUTF82Wide != (iconv_t)-1)
iconv_close(iconvUTF82Wide);
iconvUTF82Wide = (iconv_t)-1;
}
string Wide2UTF8(const std::wstring &s)
std::string Wide2UTF8(const std::wstring &s)
{
// include room for null terminator, assuming unicode input (not ucs)
// utf-8 characters can be max 4 bytes.
size_t len = (s.length() + 1) * 4;
vector<char> charBuffer;
if(charBuffer.size() < len)
charBuffer.resize(len);
std::vector<char> charBuffer(len);
size_t ret;
{
SCOPED_LOCK(lockWide2UTF8);
SCOPED_LOCK(iconvLock);
if(iconvWide2UTF8 == (iconv_t)-1)
iconvWide2UTF8 = iconv_open("UTF-8", "WCHAR_T");
@@ -192,7 +195,49 @@ string Wide2UTF8(const std::wstring &s)
// convert to string from null-terminated string - utf-8 never contains
// 0 bytes before the null terminator, and this way we don't care if
// charBuffer is larger than the string
return string(&charBuffer[0]);
return std::string(&charBuffer[0]);
}
std::wstring UTF82Wide(const std::string &s)
{
// include room for null terminator, for ascii input we need at least as many output chars as
// input.
size_t len = s.length() + 1;
std::vector<wchar_t> wcharBuffer(len);
size_t ret;
{
SCOPED_LOCK(iconvLock);
if(iconvUTF82Wide == (iconv_t)-1)
iconvUTF82Wide = iconv_open("WCHAR_T", "UTF-8");
if(iconvUTF82Wide == (iconv_t)-1)
{
RDCERR("Couldn't open iconv for UTF-8 to WCHAR_T: %d", errno);
return L"";
}
char *inbuf = (char *)s.c_str();
size_t insize = s.length() + 1; // include null terminator
char *outbuf = (char *)&wcharBuffer[0];
size_t outsize = len * sizeof(wchar_t);
ret = iconv(iconvUTF82Wide, &inbuf, &insize, &outbuf, &outsize);
}
if(ret == (size_t)-1)
{
#if ENABLED(RDOC_DEVEL)
RDCWARN("Failed to convert wstring");
#endif
return L"";
}
// convert to string from null-terminated string
return std::wstring(&wcharBuffer[0]);
}
};
+56 -10
View File
@@ -389,34 +389,38 @@ namespace StringFormat
{
// cache iconv_t descriptor to save on iconv_open/iconv_close each time
iconv_t iconvWide2UTF8 = (iconv_t)-1;
iconv_t iconvUTF82Wide = (iconv_t)-1;
// iconv is not thread safe when sharing an iconv_t descriptor
// I don't expect much contention but if it happens we could TryLock
// before creating a temporary iconv_t, or hold two iconv_ts, or something.
Threading::CriticalSection lockWide2UTF8;
Threading::CriticalSection iconvLock;
void Shutdown()
{
SCOPED_LOCK(lockWide2UTF8);
iconv_close(iconvWide2UTF8);
SCOPED_LOCK(iconvLock);
if(iconvWide2UTF8 != (iconv_t)-1)
iconv_close(iconvWide2UTF8);
iconvWide2UTF8 = (iconv_t)-1;
if(iconvUTF82Wide != (iconv_t)-1)
iconv_close(iconvUTF82Wide);
iconvUTF82Wide = (iconv_t)-1;
}
string Wide2UTF8(const std::wstring &s)
std::string Wide2UTF8(const std::wstring &s)
{
// include room for null terminator, assuming unicode input (not ucs)
// utf-8 characters can be max 4 bytes.
size_t len = (s.length() + 1) * 4;
vector<char> charBuffer;
if(charBuffer.size() < len)
charBuffer.resize(len);
std::vector<char> charBuffer(len);
size_t ret;
{
SCOPED_LOCK(lockWide2UTF8);
SCOPED_LOCK(iconvLock);
if(iconvWide2UTF8 == (iconv_t)-1)
iconvWide2UTF8 = iconv_open("UTF-8", "WCHAR_T");
@@ -446,7 +450,49 @@ string Wide2UTF8(const std::wstring &s)
// convert to string from null-terminated string - utf-8 never contains
// 0 bytes before the null terminator, and this way we don't care if
// charBuffer is larger than the string
return string(&charBuffer[0]);
return std::string(&charBuffer[0]);
}
std::wstring UTF82Wide(const std::string &s)
{
// include room for null terminator, for ascii input we need at least as many output chars as
// input.
size_t len = s.length() + 1;
std::vector<wchar_t> wcharBuffer(len);
size_t ret;
{
SCOPED_LOCK(iconvLock);
if(iconvUTF82Wide == (iconv_t)-1)
iconvUTF82Wide = iconv_open("WCHAR_T", "UTF-8");
if(iconvUTF82Wide == (iconv_t)-1)
{
RDCERR("Couldn't open iconv for UTF-8 to WCHAR_T: %d", errno);
return L"";
}
char *inbuf = (char *)s.c_str();
size_t insize = s.length() + 1; // include null terminator
char *outbuf = (char *)&wcharBuffer[0];
size_t outsize = len * sizeof(wchar_t);
ret = iconv(iconvUTF82Wide, &inbuf, &insize, &outbuf, &outsize);
}
if(ret == (size_t)-1)
{
#if ENABLED(RDOC_DEVEL)
RDCWARN("Failed to convert wstring");
#endif
return L"";
}
// convert to string from null-terminated string
return std::wstring(&wcharBuffer[0]);
}
};
-6
View File
@@ -47,12 +47,6 @@
#define GetEmbeddedResource(filename) GetDynamicEmbeddedResource(EmbeddedResource(filename))
std::string GetDynamicEmbeddedResource(int resource);
namespace StringFormat
{
// useful for converting to wide before passing to OS functions
std::wstring UTF82Wide(const string &s);
};
namespace OSUtility
{
inline void ForceCrash()
+110
View File
@@ -804,3 +804,113 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_ResourceFormatName(const Re
{
name = ResourceFormatName(fmt);
}
static void TestPrintMsg(const std::string &msg)
{
OSUtility::WriteOutput(OSUtility::Output_DebugMon, msg.c_str());
OSUtility::WriteOutput(OSUtility::Output_StdErr, msg.c_str());
}
extern "C" RENDERDOC_API int RENDERDOC_CC RENDERDOC_RunFunctionalTests(int pythonMinorVersion,
const rdcarray<rdcstr> &args)
{
#if ENABLED(RDOC_WIN32)
const char *moduledir = "/pymodules";
const char *modulename = "renderdoc.pyd";
std::string pythonlibs[] = {"python3?.dll"};
#elif ENABLED(RDOC_LINUX)
const char *moduledir = "";
const char *modulename = "renderdoc.so";
// we don't care about pymalloc or not
std::string pythonlibs[] = {"libpython3.?m.so.1.0", "libpython3.?.so.1.0", "libpython3.?m.so",
"libpython3.?.so"};
#else
const char *moduledir = "";
const char *modulename = "";
std::string pythonlibs[] = {};
TestPrintMsg(
"Running functional tests not directly supported on this platform.\n"
"Try running util/test/run_tests.py manually.\n");
return 1;
#endif
std::string libPath;
FileIO::GetLibraryFilename(libPath);
libPath = dirname(libPath);
std::string modulePath = libPath + moduledir;
std::string moduleFilename = modulePath + "/" + modulename;
if(!FileIO::exists(moduleFilename.c_str()))
{
TestPrintMsg(StringFormat::Fmt("Couldn't locate python module at %s\n", moduleFilename.c_str()));
return 1;
}
// if we've been built either on windows or on linux from within the project root, going up two
// directories from the library will put us at the project root. This is the most common scenario
// and we don't add handling for locating the script elsewhere as in that case the user can run it
// directly. This is just intended as a useful shortcut for common cases.
std::string scriptPath = libPath + "/../../util/test/run_tests.py";
if(!FileIO::exists(scriptPath.c_str()))
{
TestPrintMsg(StringFormat::Fmt("Couldn't locate run_tests.py script at %s\n", scriptPath.c_str()));
return 1;
}
void *handle = NULL;
for(std::string py : pythonlibs)
{
// patch up the python minor version
char *ver = strchr(&py[0], '?');
*ver = char('0' + pythonMinorVersion);
handle = Process::LoadModule(py.c_str());
RDCLOG("Loaded python from %s", py.c_str());
}
if(!handle)
{
TestPrintMsg("Couldn't locate python 3.6 library\n");
return 1;
}
typedef int(RENDERDOC_CC * PFN_Py_Main)(int, wchar_t **);
PFN_Py_Main mainFunc = (PFN_Py_Main)Process::GetFunctionAddress(handle, "Py_Main");
if(!mainFunc)
{
TestPrintMsg("Couldn't get Py_Main in python library\n");
return 1;
}
std::vector<std::wstring> wideArgs(args.size());
for(size_t i = 0; i < args.size(); i++)
wideArgs[i] = StringFormat::UTF82Wide(args[i]);
// insert fake arguments to point at the script and our modules
wideArgs.insert(wideArgs.begin(),
{
L"python",
// specify script path
StringFormat::UTF82Wide(scriptPath),
// specify native library path
L"--renderdoc", StringFormat::UTF82Wide(libPath),
// specify python module path
L"--pyrenderdoc", StringFormat::UTF82Wide(modulePath),
// force in-process as we can't fork out to python to pass args
L"--in-process",
});
std::vector<wchar_t *> wideArgStrings(wideArgs.size());
for(size_t i = 0; i < wideArgs.size(); i++)
wideArgStrings[i] = &wideArgs[i][0];
return mainFunc((int)wideArgStrings.size(), wideArgStrings.data());
}
+6
View File
@@ -2,6 +2,12 @@ set(sources renderdoccmd.cpp ${CMAKE_SOURCE_DIR}/renderdoc/api/replay/version.cp
set(includes PRIVATE ${CMAKE_SOURCE_DIR}/renderdoc/api)
set(libraries PRIVATE renderdoc)
if(PythonLibs_FOUND)
add_definitions(-DPYTHON_VERSION_MINOR=${PYTHON_VERSION_MINOR})
else()
add_definitions(-DPYTHON_VERSION_MINOR=0)
endif()
if(APPLE)
list(APPEND sources renderdoccmd_apple.cpp)
elseif(ANDROID)
+11 -1
View File
@@ -799,7 +799,13 @@ struct TestCommand : public Command
TestCommand(const GlobalEnvironment &env) : Command(env) {}
virtual void AddOptions(cmdline::parser &parser)
{
parser.set_footer("<unit> [... parameters to test framework ...]");
parser.set_footer(
#if PYTHON_MINOR_VERSION > 0
"<unit|functional>"
#else
"<unit>"
#endif
" [... parameters to test framework ...]");
parser.add("help", '\0', "print this message");
parser.stop_at_rest(true);
}
@@ -826,6 +832,10 @@ struct TestCommand : public Command
if(mode == "unit")
return RENDERDOC_RunUnitTests("renderdoccmd test unit", convertArgs(rest));
#if PYTHON_MINOR_VERSION > 0
else if(mode == "functional")
return RENDERDOC_RunFunctionalTests(PYTHON_MINOR_VERSION, convertArgs(rest));
#endif
std::cerr << "Unsupported test frame work '" << mode << "'" << std::endl << std::endl;
std::cerr << parser.usage() << std::endl;
+4 -4
View File
@@ -100,7 +100,7 @@
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;RENDERDOC_PLATFORM_WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;RENDERDOC_PLATFORM_WIN32;NDEBUG;_CONSOLE;PYTHON_VERSION_MINOR=6;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)renderdocshim\;$(SolutionDir)renderdoc\api\;$(SolutionDir)renderdoc\api\replay;$(SolutionDir)renderdoc\3rdparty\</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
@@ -121,7 +121,7 @@
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;WIN64;RENDERDOC_PLATFORM_WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;WIN64;RENDERDOC_PLATFORM_WIN32;NDEBUG;_CONSOLE;PYTHON_VERSION_MINOR=6;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)renderdocshim\;$(SolutionDir)renderdoc\api\;$(SolutionDir)renderdoc\api\replay;$(SolutionDir)renderdoc\3rdparty\</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
@@ -144,7 +144,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;RENDERDOC_PLATFORM_WIN32;NDEBUG;RELEASE;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;RENDERDOC_PLATFORM_WIN32;NDEBUG;RELEASE;_CONSOLE;PYTHON_VERSION_MINOR=6;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)renderdocshim\;$(SolutionDir)renderdoc\api\;$(SolutionDir)renderdoc\api\replay;$(SolutionDir)renderdoc\3rdparty\</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
@@ -167,7 +167,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;WIN64;RENDERDOC_PLATFORM_WIN32;NDEBUG;RELEASE;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;WIN64;RENDERDOC_PLATFORM_WIN32;NDEBUG;RELEASE;_CONSOLE;PYTHON_VERSION_MINOR=6;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)renderdocshim\;$(SolutionDir)renderdoc\api\;$(SolutionDir)renderdoc\api\replay;$(SolutionDir)renderdoc\3rdparty\</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>