// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "PrivatePCH.h" #include "RequiredProgramMainCPPInclude.h" #include "TestDirectoryWatcher.h" DEFINE_LOG_CATEGORY(LogTestPAL); IMPLEMENT_APPLICATION(TestPAL, "TestPAL"); namespace TestPAL { FString CommandLine; }; /** * FProcHandle test (child instance) */ int32 ProcRunAsChild(const TCHAR* CommandLine) { FPlatformMisc::SetCrashHandler(NULL); FPlatformMisc::SetGracefulTerminationHandler(); GEngineLoop.PreInit(CommandLine); // set a random delay pretending to do some useful work up to a minute. srand(FPlatformProcess::GetCurrentProcessId()); double RandomWorkTime = FMath::FRandRange(0.0f, 6.0f); UE_LOG(LogTestPAL, Display, TEXT("Running proc test as child (pid %d), will be doing work for %f seconds."), FPlatformProcess::GetCurrentProcessId(), RandomWorkTime); double StartTime = FPlatformTime::Seconds(); // Use all the CPU! for (;;) { double CurrentTime = FPlatformTime::Seconds(); if (CurrentTime - StartTime >= RandomWorkTime) { break; } } UE_LOG(LogTestPAL, Display, TEXT("Child (pid %d) finished work."), FPlatformProcess::GetCurrentProcessId()); FEngineLoop::AppPreExit(); FEngineLoop::AppExit(); return 0; } /** * FProcHandle test (parent instance) */ int32 ProcRunAsParent(const TCHAR* CommandLine) { FPlatformMisc::SetCrashHandler(NULL); FPlatformMisc::SetGracefulTerminationHandler(); GEngineLoop.PreInit(CommandLine); UE_LOG(LogTestPAL, Display, TEXT("Running proc test as parent.")); // Run slave instance continuously int NumChildrenToSpawn = 255, MaxAtOnce = 5; FParent Parent(NumChildrenToSpawn, MaxAtOnce); Parent.Run(); UE_LOG(LogTestPAL, Display, TEXT("Parent quit.")); FEngineLoop::AppPreExit(); FEngineLoop::AppExit(); return 0; } /** * Tests a single file. */ void TestCaseInsensitiveFile(const FString & Filename, const FString & WrongFilename) { IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); IFileHandle * CreationHandle = PlatformFile.OpenWrite(*Filename); checkf(CreationHandle, TEXT("Could not create a test file for '%s'"), *Filename); delete CreationHandle; IFileHandle* CheckGoodHandle = PlatformFile.OpenRead(*Filename); checkf(CheckGoodHandle, TEXT("Could not open a test file for '%s' (zero probe)"), *Filename); delete CheckGoodHandle; IFileHandle* CheckWrongCaseRelHandle = PlatformFile.OpenRead(*WrongFilename); checkf(CheckWrongCaseRelHandle, TEXT("Could not open a test file for '%s'"), *WrongFilename); delete CheckWrongCaseRelHandle; PlatformFile.DeleteFile(*Filename); } /** * Case-(in)sensitivity test/ */ int32 CaseTest(const TCHAR* CommandLine) { FPlatformMisc::SetCrashHandler(NULL); FPlatformMisc::SetGracefulTerminationHandler(); GEngineLoop.PreInit(CommandLine); UE_LOG(LogTestPAL, Display, TEXT("Running case sensitivity test.")); TestCaseInsensitiveFile(TEXT("Test.Test"), TEXT("teSt.teSt")); FString File(TEXT("Test^%!CaseInsens")); FString AbsFile = FPaths::ConvertRelativePathToFull(File); FString AbsFileUpper = AbsFile.ToUpper(); TestCaseInsensitiveFile(AbsFile, AbsFileUpper); FEngineLoop::AppPreExit(); FEngineLoop::AppExit(); return 0; } /** * Message box test/ */ int32 MessageBoxTest(const TCHAR* CommandLine) { FPlatformMisc::SetCrashHandler(NULL); FPlatformMisc::SetGracefulTerminationHandler(); GEngineLoop.PreInit(CommandLine); UE_LOG(LogTestPAL, Display, TEXT("Running message box test.")); FString Display(TEXT("I am a big big string in a big big game, it's not a big big thing if you print me. But I do do feel that I do do will be displayed wrong, displayed wrong... or not.")); FString Caption(TEXT("I am a big big caption in a big big game, it's not a big big thing if you print me. But I do do feel that I do do will be displayed wrong, displayed wrong... or not.")); EAppReturnType::Type Result = FPlatformMisc::MessageBoxExt(EAppMsgType::YesNo, *Display, *Caption); UE_LOG(LogTestPAL, Display, TEXT("MessageBoxExt result: %d."), static_cast(Result)); FEngineLoop::AppPreExit(); FEngineLoop::AppExit(); return 0; } /** * ************ Thread singleton test ***************** */ /** * Per-thread singleton */ struct FPerThreadTestSingleton : public TThreadSingleton { FPerThreadTestSingleton() { UE_LOG(LogTestPAL, Log, TEXT("FPerThreadTestSingleton (this=%p) created for thread %d"), this, FPlatformTLS::GetCurrentThreadId()); } void DoSomething() { UE_LOG(LogTestPAL, Log, TEXT("Thread %d is about to quit"), FPlatformTLS::GetCurrentThreadId()); } virtual ~FPerThreadTestSingleton() { UE_LOG(LogTestPAL, Log, TEXT("FPerThreadTestSingleton (%p) destroyed for thread %d"), this, FPlatformTLS::GetCurrentThreadId()); } }; //DECLARE_THREAD_SINGLETON( FPerThreadTestSingleton ); /** * Thread runnable */ struct FSingletonTestingThread : public FRunnable { virtual uint32 Run() { FPerThreadTestSingleton& Dummy = FPerThreadTestSingleton::Get(); FPlatformProcess::Sleep(3.0f); Dummy.DoSomething(); return 0; } }; /** * Thread singleton test */ int32 ThreadSingletonTest(const TCHAR* CommandLine) { FPlatformMisc::SetCrashHandler(NULL); FPlatformMisc::SetGracefulTerminationHandler(); GEngineLoop.PreInit(CommandLine); UE_LOG(LogTestPAL, Display, TEXT("Running thread singleton test.")); const int kNumTestThreads = 10; FSingletonTestingThread * RunnableArray[kNumTestThreads] = { nullptr }; FRunnableThread * ThreadArray[kNumTestThreads] = { nullptr }; // start all threads for (int Idx = 0; Idx < kNumTestThreads; ++Idx) { RunnableArray[Idx] = new FSingletonTestingThread(); ThreadArray[Idx] = FRunnableThread::Create(RunnableArray[Idx], *FString::Printf(TEXT("TestThread%d"), Idx)); } GLog->FlushThreadedLogs(); GLog->Flush(); // join all threads for (int Idx = 0; Idx < kNumTestThreads; ++Idx) { ThreadArray[Idx]->WaitForCompletion(); delete ThreadArray[Idx]; ThreadArray[Idx] = nullptr; delete RunnableArray[Idx]; RunnableArray[Idx] = nullptr; } FEngineLoop::AppPreExit(); FEngineLoop::AppExit(); return 0; } /** * Sysinfo test */ int32 SysInfoTest(const TCHAR* CommandLine) { FPlatformMisc::SetCrashHandler(NULL); FPlatformMisc::SetGracefulTerminationHandler(); GEngineLoop.PreInit(CommandLine); UE_LOG(LogTestPAL, Display, TEXT("Running system info test.")); bool bIsRunningOnBattery = FPlatformMisc::IsRunningOnBattery(); UE_LOG(LogTestPAL, Display, TEXT(" FPlatformMisc::IsRunningOnBattery() = %s"), bIsRunningOnBattery ? TEXT("true") : TEXT("false")); FString OSInstanceGuid = FPlatformMisc::GetOperatingSystemId(); UE_LOG(LogTestPAL, Display, TEXT(" FPlatformMisc::GetOperatingSystemId() = %s"), *OSInstanceGuid); FEngineLoop::AppPreExit(); FEngineLoop::AppExit(); return 0; } /** * Selects and runs one of test cases. * * @param ArgC Number of commandline arguments. * @param ArgV Commandline arguments. * @return Test case's return code. */ int32 MultiplexedMain(int32 ArgC, char* ArgV[]) { for (int32 IdxArg = 0; IdxArg < ArgC; ++IdxArg) { if (!FCStringAnsi::Strcmp(ArgV[IdxArg], ARG_PROC_TEST_CHILD)) { return ProcRunAsChild(*TestPAL::CommandLine); } else if (!FCStringAnsi::Strcmp(ArgV[IdxArg], ARG_PROC_TEST)) { return ProcRunAsParent(*TestPAL::CommandLine); } else if (!FCStringAnsi::Strcmp(ArgV[IdxArg], ARG_CASE_SENSITIVITY_TEST)) { return CaseTest(*TestPAL::CommandLine); } else if (!FCStringAnsi::Strcmp(ArgV[IdxArg], ARG_MESSAGEBOX_TEST)) { return MessageBoxTest(*TestPAL::CommandLine); } else if (!FCStringAnsi::Strcmp(ArgV[IdxArg], ARG_DIRECTORY_WATCHER_TEST)) { return DirectoryWatcherTest(*TestPAL::CommandLine); } else if (!FCStringAnsi::Strcmp(ArgV[IdxArg], ARG_THREAD_SINGLETON_TEST)) { return ThreadSingletonTest(*TestPAL::CommandLine); } else if (!FCStringAnsi::Strcmp(ArgV[IdxArg], ARG_SYSINFO_TEST)) { return SysInfoTest(*TestPAL::CommandLine); } } FPlatformMisc::SetCrashHandler(NULL); FPlatformMisc::SetGracefulTerminationHandler(); GEngineLoop.PreInit(*TestPAL::CommandLine); UE_LOG(LogTestPAL, Warning, TEXT("Unable to find any known test name, no test started.")); UE_LOG(LogTestPAL, Warning, TEXT("")); UE_LOG(LogTestPAL, Warning, TEXT("Available test cases:")); UE_LOG(LogTestPAL, Warning, TEXT(" %s: test process handling API"), ANSI_TO_TCHAR( ARG_PROC_TEST )); UE_LOG(LogTestPAL, Warning, TEXT(" %s: test case-insensitive file operations"), ANSI_TO_TCHAR( ARG_CASE_SENSITIVITY_TEST )); UE_LOG(LogTestPAL, Warning, TEXT(" %s: test message box bug (too long strings)"), ANSI_TO_TCHAR( ARG_MESSAGEBOX_TEST )); UE_LOG(LogTestPAL, Warning, TEXT(" %s: test directory watcher"), ANSI_TO_TCHAR( ARG_DIRECTORY_WATCHER_TEST )); UE_LOG(LogTestPAL, Warning, TEXT(" %s: test per-thread singletons"), ANSI_TO_TCHAR( ARG_THREAD_SINGLETON_TEST )); UE_LOG(LogTestPAL, Warning, TEXT(" %s: test (some) system information"), ANSI_TO_TCHAR( ARG_SYSINFO_TEST )) UE_LOG(LogTestPAL, Warning, TEXT("")); UE_LOG(LogTestPAL, Warning, TEXT("Pass one of those to run an appropriate test.")); FEngineLoop::AppPreExit(); FEngineLoop::AppExit(); return 1; } int32 main(int32 ArgC, char* ArgV[]) { TestPAL::CommandLine = TEXT(""); for (int32 Option = 1; Option < ArgC; Option++) { TestPAL::CommandLine += TEXT(" "); FString Argument(ANSI_TO_TCHAR(ArgV[Option])); if (Argument.Contains(TEXT(" "))) { if (Argument.Contains(TEXT("="))) { FString ArgName; FString ArgValue; Argument.Split( TEXT("="), &ArgName, &ArgValue ); Argument = FString::Printf( TEXT("%s=\"%s\""), *ArgName, *ArgValue ); } else { Argument = FString::Printf(TEXT("\"%s\""), *Argument); } } TestPAL::CommandLine += Argument; } return MultiplexedMain(ArgC, ArgV); }