diff --git a/Apps/ValidationTests/Android/app/CMakeLists.txt b/Apps/ValidationTests/Android/app/CMakeLists.txt index 975189659..5d9c7bf15 100644 --- a/Apps/ValidationTests/Android/app/CMakeLists.txt +++ b/Apps/ValidationTests/Android/app/CMakeLists.txt @@ -44,10 +44,6 @@ add_subdirectory(${BABYLON_NATIVE_VALIDATIONTESTS_DIR}/../../ ${BABYLON_NATIVE_V add_library(BabylonNativeJNI SHARED src/main/cpp/BabylonNativeJNI.cpp) -target_include_directories(BabylonNativeJNI - PRIVATE - ${BABYLON_NATIVE_VALIDATIONTESTS_DIR}/Shared) - add_definitions(-DANDROID_STL=c++_shared) target_link_libraries(BabylonNativeJNI @@ -65,7 +61,8 @@ target_link_libraries(BabylonNativeJNI Window ScriptLoader bgfx - XMLHttpRequest) + XMLHttpRequest + TestUtils) configure_file( "${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/${ANDROID_ABI}/libc++_shared.so" diff --git a/Apps/ValidationTests/Android/app/src/main/cpp/BabylonNativeJNI.cpp b/Apps/ValidationTests/Android/app/src/main/cpp/BabylonNativeJNI.cpp index c6fad769f..1005dd9f1 100644 --- a/Apps/ValidationTests/Android/app/src/main/cpp/BabylonNativeJNI.cpp +++ b/Apps/ValidationTests/Android/app/src/main/cpp/BabylonNativeJNI.cpp @@ -15,10 +15,10 @@ #include #include #include +#include #include #include #include -#include namespace { @@ -103,7 +103,7 @@ extern "C" Babylon::Polyfills::XMLHttpRequest::Initialize(env); - Babylon::TestUtils::CreateInstance(env, window); + Babylon::Plugins::TestUtils::Initialize(env, window); }); g_scriptLoader = std::make_unique(*g_runtime); diff --git a/Apps/ValidationTests/CMakeLists.txt b/Apps/ValidationTests/CMakeLists.txt index 146ee915c..7231ae0e0 100644 --- a/Apps/ValidationTests/CMakeLists.txt +++ b/Apps/ValidationTests/CMakeLists.txt @@ -32,7 +32,6 @@ if(WINDOWS_STORE) set_property(SOURCE ${APPX_ASSETS} PROPERTY VS_DEPLOYMENT_CONTENT 1) set_property(SOURCE ${APPX_ASSETS} PROPERTY VS_DEPLOYMENT_LOCATION "Assets") set(SOURCES - "Shared/TestUtils.h" ${APPX_FILES} ${APPX_ASSETS} "UWP/App.cpp" @@ -42,7 +41,6 @@ if(WINDOWS_STORE) add_executable(ValidationTests WIN32 ${REFERENCE_IMAGES} ${BABYLON_SCRIPTS} ${SCRIPTS} ${SOURCES} ${RESOURCE_FILES}) elseif(WIN32) set(SOURCES - "Shared/TestUtils.h" "Win32/App.cpp" "Win32/App.h" "Win32/App.ico" @@ -66,8 +64,7 @@ elseif(APPLE) "iOS/AppDelegate.swift" "iOS/ViewController.swift" "iOS/LibNativeBridge.h" - "iOS/LibNativeBridge.mm" - "Shared/TestUtils.h") + "iOS/LibNativeBridge.mm") set_source_files_properties(${SCRIPTS} ${BABYLON_SCRIPTS} PROPERTIES MACOSX_PACKAGE_LOCATION "Scripts") set_source_files_properties(${REFERENCE_IMAGES} PROPERTIES MACOSX_PACKAGE_LOCATION "ReferenceImages") else() @@ -79,8 +76,7 @@ elseif(APPLE) "macOS/AppDelegate.mm" "macOS/AppDelegate.h" "macOS/ViewController.mm" - "macOS/ViewController.h" - "Shared/TestUtils.h") + "macOS/ViewController.h") set_source_files_properties(${SCRIPTS} ${BABYLON_SCRIPTS} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/Scripts") set_source_files_properties(${REFERENCE_IMAGES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/ReferenceImages") endif() @@ -124,6 +120,7 @@ target_link_to_dependencies(ValidationTests PRIVATE Window PRIVATE ScriptLoader PRIVATE Canvas + PRIVATE TestUtils ${ADDITIONAL_LIBRARIES} PRIVATE XMLHttpRequest) diff --git a/Apps/ValidationTests/Shared/TestUtils.h b/Apps/ValidationTests/Shared/TestUtils.h deleted file mode 100644 index d98642d38..000000000 --- a/Apps/ValidationTests/Shared/TestUtils.h +++ /dev/null @@ -1,260 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include -#include -#include - -#if _MSC_VER -#pragma warning( disable : 4324 ) // 'bx::DirectoryReader': structure was padded due to alignment specifier -#endif - -#include - -#include -#include -#include -#include -#include - -namespace -{ - std::atomic doExit{}; - int errorCode{}; - -#if WIN32 && !__cplusplus_winrt - std::filesystem::path GetModulePath() - { - char buffer[1024]; - ::GetModuleFileNameA(nullptr, buffer, ARRAYSIZE(buffer)); - return std::filesystem::path{ buffer }.parent_path(); - } -#endif -} - -// can't externalize variable with ObjC++. Using a function instead. -int GetExitCode() -{ - return errorCode; -} - -namespace Babylon -{ - class TestUtils final : public Napi::ObjectWrap - { - public: - static inline constexpr const char* JS_INSTANCE_NAME{ "TestUtils" }; - - using ParentT = Napi::ObjectWrap; - - static void CreateInstance(Napi::Env env, WindowType nativeWindowPtr) - { - _nativeWindowPtr = nativeWindowPtr; - Napi::HandleScope scope{ env }; - - Napi::Function func = ParentT::DefineClass( - env, - "TestUtilsClass", - { - ParentT::InstanceMethod("exit", &TestUtils::Exit), - ParentT::InstanceMethod("updateSize", &TestUtils::UpdateSize), - ParentT::InstanceMethod("setTitle", &TestUtils::SetTitle), - ParentT::InstanceMethod("writePNG", &TestUtils::WritePNG), - ParentT::InstanceMethod("decodeImage", &TestUtils::DecodeImage), - ParentT::InstanceMethod("getImageData", &TestUtils::GetImageData), - ParentT::InstanceMethod("getOutputDirectory", &TestUtils::GetOutputDirectory), - }); - env.Global().Set(JS_INSTANCE_NAME, func.New({})); - } - - explicit TestUtils(const Napi::CallbackInfo& info) - : ParentT{ info } - { - } - - private: - static inline Napi::FunctionReference constructor{}; - - void Exit(const Napi::CallbackInfo& info) - { - const int32_t exitCode = info[0].As().Int32Value(); - doExit = true; - errorCode = exitCode; -#if defined(__cplusplus_winrt) - // ceguille: I didn't find a better way to do it for UWP - exit(errorCode); -#elif ANDROID -#elif WIN32 - PostMessageW(_nativeWindowPtr, WM_DESTROY, 0, 0); -#elif __linux__ - Display* display = XOpenDisplay(NULL); - XClientMessageEvent dummyEvent; - memset(&dummyEvent, 0, sizeof(XClientMessageEvent)); - dummyEvent.type = ClientMessage; - dummyEvent.window = (Window)_nativeWindowPtr; - dummyEvent.format = 32; - XSendEvent(display, (Window)_nativeWindowPtr, 0, 0, (XEvent*)&dummyEvent); - XFlush(display); -#elif __APPLE__ -#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR - dispatch_async(dispatch_get_main_queue(), ^{ - if (graphics) - { - graphics->FinishRenderingCurrentFrame(); - } - runtime.reset(); - graphics.reset(); - UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Validation Tests" - message:(errorCode == 0)?@"Success!":@"Errors: Check logs!" - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault - handler:^(UIAlertAction * ) {}]; - - [alert addAction:defaultAction]; - UIViewController *rootController = [[[[UIApplication sharedApplication]delegate] window] rootViewController]; - [rootController presentViewController:alert animated:YES completion:nil]; - }); -#else - dispatch_async(dispatch_get_main_queue(), ^{ - [[_nativeWindowPtr window]close]; - }); -#endif -#else - // TODO: handle exit for other platforms -#endif - } - - void UpdateSize(const Napi::CallbackInfo& info) - { -#if defined(__cplusplus_winrt) - (void)info; -#elif WIN32 - const int32_t width = info[0].As().Int32Value(); - const int32_t height = info[1].As().Int32Value(); - - HWND hwnd = _nativeWindowPtr; - RECT rc{ 0, 0, width, height }; - AdjustWindowRectEx(&rc, GetWindowStyle(hwnd), GetMenu(hwnd) != NULL, GetWindowExStyle(hwnd)); - SetWindowPos(hwnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER); -#else - // TODO: handle resize for other platforms - (void)info; -#endif - } - - void SetTitle(const Napi::CallbackInfo& info) - { - const auto title = info[0].As().Utf8Value(); -#if defined(__cplusplus_winrt) -#elif WIN32 - SetWindowTextA(_nativeWindowPtr, title.c_str()); -#elif ANDROID - (void)info; -#elif __linux__ - Display* display = XOpenDisplay(NULL); - XStoreName(display, (Window)_nativeWindowPtr, title.c_str()); -#else - // TODO: handle title for other platforms -#endif - } - - void WritePNG(const Napi::CallbackInfo& info) - { - const auto buffer = info[0].As(); - const auto width = info[1].As().Uint32Value(); - const auto height = info[2].As().Uint32Value(); - const auto filename = info[3].As().Utf8Value(); - - if (buffer.ByteLength() < (width * height * 4)) - { - return; - } - - bx::MemoryBlock mb(&allocator); - bx::FileWriter writer; - bx::FilePath filepath(filename.c_str()); - bx::Error err; - if (writer.open(filepath, false, &err)) - { - bimg::imageWritePng(&writer, width, height, width * 4, buffer.Data(), bimg::TextureFormat::RGBA8, false); - writer.close(); - } - } - - struct Image - { - Image() = default; - ~Image() - { - if (m_Image) - { - bimg::imageFree(m_Image); - m_Image = nullptr; - } - } - bimg::ImageContainer* m_Image{}; - }; - - Napi::Value DecodeImage(const Napi::CallbackInfo& info) - { - Image* image = new Image; - const auto buffer = info[0].As(); - - image->m_Image = bimg::imageParse(&allocator, buffer.Data(), static_cast(buffer.ByteLength())); - - auto finalizer = [](Napi::Env, Image* image) { delete image;}; - return Napi::External::New(info.Env(), image, std::move(finalizer)); - } - - Napi::Value GetImageData(const Napi::CallbackInfo& info) - { - const auto imageData = info[0].As>().Data(); - - if (!imageData || !imageData->m_Image || !imageData->m_Image->m_size) - { - return info.Env().Undefined(); - } - - auto data = Napi::Uint8Array::New(info.Env(), imageData->m_Image->m_size); - const auto ptr = static_cast(imageData->m_Image->m_data); - memcpy(data.Data(), ptr, imageData->m_Image->m_size); - - return Napi::Value::From(info.Env(), data); - } - - Napi::Value GetOutputDirectory(const Napi::CallbackInfo& info) - { -#if defined(__cplusplus_winrt) - using namespace Windows::Storage; - StorageFolder^ localFolder = ApplicationData::Current->LocalFolder; - std::wstring wpath = localFolder->Path->Data(); - std::string path{winrt::to_string(wpath)}; -#elif ANDROID - auto path = "/data/data/com.android.babylonnative.validationtests/cache"; -#elif __APPLE__ - std::string path = getenv("HOME"); -#elif __linux__ - char exe[1024]; - int ret = readlink("/proc/self/exe", exe, sizeof(exe)-1); - if(ret == -1) - { - throw Napi::Error::New(info.Env(), "Unable to get executable location"); - } - exe[ret] = 0; - - auto path = std::filesystem::path{exe}.parent_path().generic_string(); -#elif WIN32 - auto path = GetModulePath().parent_path().generic_string(); -#endif - return Napi::Value::From(info.Env(), path); - } - - inline static WindowType _nativeWindowPtr{}; - inline static bx::DefaultAllocator allocator{}; - }; -} diff --git a/Apps/ValidationTests/UWP/App.cpp b/Apps/ValidationTests/UWP/App.cpp index 8e2ba1d73..866725932 100644 --- a/Apps/ValidationTests/UWP/App.cpp +++ b/Apps/ValidationTests/UWP/App.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -15,8 +16,6 @@ #include -#include - using namespace Windows::ApplicationModel; using namespace Windows::ApplicationModel::Core; using namespace Windows::ApplicationModel::Activation; @@ -218,7 +217,7 @@ void App::RestartRuntime(Windows::Foundation::Rect bounds) Babylon::Plugins::NativeXr::Initialize(env); - Babylon::TestUtils::CreateInstance(env, windowPtr); + Babylon::Plugins::TestUtils::Initialize(env, windowPtr); }); Babylon::ScriptLoader loader{*m_runtime}; diff --git a/Apps/ValidationTests/Win32/App.cpp b/Apps/ValidationTests/Win32/App.cpp index 57d394935..bcc19a010 100644 --- a/Apps/ValidationTests/Win32/App.cpp +++ b/Apps/ValidationTests/Win32/App.cpp @@ -11,14 +11,13 @@ #include #include -#include - #include #include #include #include #include #include +#include #include #include #include @@ -90,7 +89,7 @@ namespace Babylon::Plugins::NativeXr::Initialize(env); - Babylon::TestUtils::CreateInstance(env, hWnd); + Babylon::Plugins::TestUtils::Initialize(env, hWnd); }); Babylon::ScriptLoader loader{ *runtime }; @@ -228,7 +227,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_DESTROY: { Uninitialize(); - PostQuitMessage(errorCode); + PostQuitMessage(Babylon::Plugins::TestUtils::errorCode); break; } default: diff --git a/Apps/ValidationTests/X11/App.cpp b/Apps/ValidationTests/X11/App.cpp index 813598e34..807d79998 100644 --- a/Apps/ValidationTests/X11/App.cpp +++ b/Apps/ValidationTests/X11/App.cpp @@ -7,13 +7,12 @@ #undef None #include -#include - #include #include #include #include #include +#include #include #include #include @@ -82,7 +81,7 @@ namespace fflush(stdout); }); - Babylon::TestUtils::CreateInstance(env, (void*)(uintptr_t)window); + Babylon::Plugins::TestUtils::Initialize(env, (void*)(uintptr_t)window); Babylon::Polyfills::Window::Initialize(env); Babylon::Polyfills::XMLHttpRequest::Initialize(env); @@ -181,6 +180,7 @@ int main(int /*_argc*/, const char* const* /*_argv*/) InitBabylon(window); UpdateWindowSize(width, height); + bool doExit{false}; while (!doExit) { if (!XPending(display) && graphics) @@ -217,5 +217,5 @@ int main(int /*_argc*/, const char* const* /*_argv*/) XUnmapWindow(display, window); XDestroyWindow(display, window); - return errorCode; + return Babylon::Plugins::TestUtils::errorCode; } diff --git a/Apps/ValidationTests/iOS/LibNativeBridge.mm b/Apps/ValidationTests/iOS/LibNativeBridge.mm index 22b8e236d..81e5a6e15 100644 --- a/Apps/ValidationTests/iOS/LibNativeBridge.mm +++ b/Apps/ValidationTests/iOS/LibNativeBridge.mm @@ -6,6 +6,7 @@ #import #import #import +#import #import #import #import @@ -14,8 +15,6 @@ std::unique_ptr graphics{}; std::unique_ptr runtime{}; -#import - @implementation LibNativeBridge - (instancetype)init @@ -60,7 +59,7 @@ - (void)init:(MTKView*)view width:(int)inWidth height:(int)inHeight // Initialize NativeXr plugin. Babylon::Plugins::NativeXr::Initialize(env); - Babylon::TestUtils::CreateInstance(env, nullptr); + Babylon::Plugins::TestUtils::Initialize(env, nullptr); }); Babylon::ScriptLoader loader{ *runtime }; diff --git a/Apps/ValidationTests/macOS/ViewController.mm b/Apps/ValidationTests/macOS/ViewController.mm index 89387c629..bd8954d3f 100644 --- a/Apps/ValidationTests/macOS/ViewController.mm +++ b/Apps/ValidationTests/macOS/ViewController.mm @@ -6,12 +6,12 @@ #import #import #import +#import #import #import #import #import #import -#import std::unique_ptr graphics{}; std::unique_ptr runtime{}; @@ -94,7 +94,7 @@ - (void)initialize { Babylon::Plugins::NativeOptimizations::Initialize(env); - Babylon::TestUtils::CreateInstance(env, engineView); + Babylon::Plugins::TestUtils::Initialize(env, engineView); }); Babylon::ScriptLoader loader{ *runtime }; diff --git a/Plugins/CMakeLists.txt b/Plugins/CMakeLists.txt index 4f53b7a4e..e4fff5122 100644 --- a/Plugins/CMakeLists.txt +++ b/Plugins/CMakeLists.txt @@ -18,4 +18,7 @@ add_subdirectory(NativeXr) add_subdirectory(NativeCamera) # Add NativeOptimizations -add_subdirectory(NativeOptimizations) \ No newline at end of file +add_subdirectory(NativeOptimizations) + +# Add TestUtils +add_subdirectory(TestUtils) \ No newline at end of file diff --git a/Plugins/TestUtils/CMakeLists.txt b/Plugins/TestUtils/CMakeLists.txt new file mode 100644 index 000000000..49f6697b3 --- /dev/null +++ b/Plugins/TestUtils/CMakeLists.txt @@ -0,0 +1,28 @@ +set(SOURCES "Include/Babylon/Plugins/TestUtils.h" + "Source/TestUtils.h" + "Source/TestUtils.cpp" + "Source/TestUtilsImplData.h") + +if(APPLE) + set(SOURCES ${SOURCES} "Source/${BABYLON_NATIVE_PLATFORM}/TestUtilsImpl.mm") +else() + set(SOURCES ${SOURCES} "Source/${BABYLON_NATIVE_PLATFORM}/TestUtilsImpl.cpp") +endif() + +add_library(TestUtils ${SOURCES}) +warnings_as_errors(TestUtils) + +if(WINDOWS_STORE) + target_compile_options(TestUtils PRIVATE /ZW) + target_compile_options(TestUtils PRIVATE /await) +endif() + +target_include_directories(TestUtils INTERFACE "Include") +target_include_directories(TestUtils PRIVATE "Source") + +target_link_to_dependencies(TestUtils + PUBLIC JsRuntime + PRIVATE GraphicsInternal) + +set_property(TARGET TestUtils PROPERTY FOLDER Plugins) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) diff --git a/Plugins/TestUtils/Include/Babylon/Plugins/TestUtils.h b/Plugins/TestUtils/Include/Babylon/Plugins/TestUtils.h new file mode 100644 index 000000000..028ec9428 --- /dev/null +++ b/Plugins/TestUtils/Include/Babylon/Plugins/TestUtils.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +namespace Babylon::Plugins::TestUtils +{ + extern int errorCode; + void Initialize(Napi::Env env, WindowType nativeWindowPtr); +} diff --git a/Plugins/TestUtils/Source/Android/TestUtilsImpl.cpp b/Plugins/TestUtils/Source/Android/TestUtilsImpl.cpp new file mode 100644 index 000000000..2bb54aad7 --- /dev/null +++ b/Plugins/TestUtils/Source/Android/TestUtilsImpl.cpp @@ -0,0 +1,31 @@ +#include "TestUtilsImplData.h" + +namespace Babylon::Plugins::Internal +{ + void TestUtils::Exit(const Napi::CallbackInfo& /*info*/) + { + } + + void TestUtils::UpdateSize(const Napi::CallbackInfo& /*info*/) + { + } + + void TestUtils::SetTitle(const Napi::CallbackInfo& /*info*/) + { + } + + Napi::Value TestUtils::GetOutputDirectory(const Napi::CallbackInfo& info) + { + auto path = "/data/data/com.android.babylonnative.validationtests/cache"; + return Napi::Value::From(info.Env(), path); + } +} + +namespace Babylon::Plugins::TestUtils +{ + void Initialize(Napi::Env env, WindowType nativeWindowPtr) + { + auto implData{std::make_shared(nativeWindowPtr)}; + Internal::TestUtils::CreateInstance(env, implData); + } +} \ No newline at end of file diff --git a/Plugins/TestUtils/Source/Apple/TestUtilsImpl.mm b/Plugins/TestUtils/Source/Apple/TestUtilsImpl.mm new file mode 100644 index 000000000..b2e81f87a --- /dev/null +++ b/Plugins/TestUtils/Source/Apple/TestUtilsImpl.mm @@ -0,0 +1,57 @@ +#import "TestUtilsImplData.h" + +// can't externalize variable with ObjC++. Using a function instead. +int errorCode{}; +int GetExitCode() +{ + return errorCode; +} + +namespace Babylon::Plugins::Internal +{ + void TestUtils::Exit(const Napi::CallbackInfo& info) + { + errorCode = info[0].As().Int32Value(); +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + dispatch_async(dispatch_get_main_queue(), ^{ + UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Validation Tests" + message:(errorCode == 0)?@"Success!":@"Errors: Check logs!" + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault + handler:^(UIAlertAction * ) {}]; + + [alert addAction:defaultAction]; + UIViewController *rootController = [[[[UIApplication sharedApplication]delegate] window] rootViewController]; + [rootController presentViewController:alert animated:YES completion:nil]; + }); +#else + dispatch_async(dispatch_get_main_queue(), ^{ + [[m_implData->m_nativeWindowPtr window]close]; + }); +#endif + } + + void TestUtils::UpdateSize(const Napi::CallbackInfo& /*info*/) + { + } + + void TestUtils::SetTitle(const Napi::CallbackInfo& /*info*/) + { + } + + Napi::Value TestUtils::GetOutputDirectory(const Napi::CallbackInfo& info) + { + std::string path = getenv("HOME"); + return Napi::Value::From(info.Env(), path); + } +} + +namespace Babylon::Plugins::TestUtils +{ + void Initialize(Napi::Env env, WindowType nativeWindowPtr) + { + auto implData{std::make_shared(nativeWindowPtr)}; + Internal::TestUtils::CreateInstance(env, implData); + } +} diff --git a/Plugins/TestUtils/Source/TestUtils.cpp b/Plugins/TestUtils/Source/TestUtils.cpp new file mode 100644 index 000000000..368c1eff5 --- /dev/null +++ b/Plugins/TestUtils/Source/TestUtils.cpp @@ -0,0 +1,63 @@ +#include "TestUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Babylon::Plugins::Internal +{ + void TestUtils::WritePNG(const Napi::CallbackInfo& info) + { + const auto buffer = info[0].As(); + const auto width = info[1].As().Uint32Value(); + const auto height = info[2].As().Uint32Value(); + const auto filename = info[3].As().Utf8Value(); + + if (buffer.ByteLength() < (width * height * 4)) + { + return; + } + + bx::MemoryBlock mb(&allocator); + bx::FileWriter writer; + bx::FilePath filepath(filename.c_str()); + bx::Error err; + if (writer.open(filepath, false, &err)) + { + bimg::imageWritePng(&writer, width, height, width * 4, buffer.Data(), bimg::TextureFormat::RGBA8, false); + writer.close(); + } + } + + Napi::Value TestUtils::DecodeImage(const Napi::CallbackInfo& info) + { + Image* image = new Image; + const auto buffer = info[0].As(); + + image->m_Image = bimg::imageParse(&allocator, buffer.Data(), static_cast(buffer.ByteLength())); + + auto finalizer = [](Napi::Env, Image* image) { delete image; }; + return Napi::External::New(info.Env(), image, std::move(finalizer)); + } + + Napi::Value TestUtils::GetImageData(const Napi::CallbackInfo& info) + { + const auto imageData = info[0].As>().Data(); + + if (!imageData || !imageData->m_Image || !imageData->m_Image->m_size) + { + return info.Env().Undefined(); + } + + auto data = Napi::Uint8Array::New(info.Env(), imageData->m_Image->m_size); + const auto ptr = static_cast(imageData->m_Image->m_data); + memcpy(data.Data(), ptr, imageData->m_Image->m_size); + + return Napi::Value::From(info.Env(), data); + } +} diff --git a/Plugins/TestUtils/Source/TestUtils.h b/Plugins/TestUtils/Source/TestUtils.h new file mode 100644 index 000000000..be8d1d3df --- /dev/null +++ b/Plugins/TestUtils/Source/TestUtils.h @@ -0,0 +1,75 @@ +#include + +#if _MSC_VER +#pragma warning( disable : 4324 ) // 'bx::DirectoryReader': structure was padded due to alignment specifier +#endif + +#include +#include + +namespace Babylon::Plugins::Internal +{ + class TestUtils final : public Napi::ObjectWrap + { + public: + class ImplData; + + static inline constexpr const char* JS_INSTANCE_NAME{ "TestUtils" }; + + using ParentT = Napi::ObjectWrap; + + static void CreateInstance(Napi::Env env, std::shared_ptr implData) + { + m_implData = std::move(implData); + Napi::HandleScope scope{ env }; + + Napi::Function func = ParentT::DefineClass( + env, + "TestUtilsClass", + { + ParentT::InstanceMethod("exit", &TestUtils::Exit), + ParentT::InstanceMethod("updateSize", &TestUtils::UpdateSize), + ParentT::InstanceMethod("setTitle", &TestUtils::SetTitle), + ParentT::InstanceMethod("writePNG", &TestUtils::WritePNG), + ParentT::InstanceMethod("decodeImage", &TestUtils::DecodeImage), + ParentT::InstanceMethod("getImageData", &TestUtils::GetImageData), + ParentT::InstanceMethod("getOutputDirectory", &TestUtils::GetOutputDirectory), + }); + env.Global().Set(JS_INSTANCE_NAME, func.New({})); + } + + explicit TestUtils(const Napi::CallbackInfo& info) + : ParentT{ info } + { + } + + private: + static inline Napi::FunctionReference constructor{}; + + inline static std::shared_ptr m_implData; + inline static bx::DefaultAllocator allocator{}; + + void Exit(const Napi::CallbackInfo& info); + void UpdateSize(const Napi::CallbackInfo& info); + void SetTitle(const Napi::CallbackInfo& info); + Napi::Value GetOutputDirectory(const Napi::CallbackInfo& info); + + void WritePNG(const Napi::CallbackInfo& info); + Napi::Value DecodeImage(const Napi::CallbackInfo& info); + Napi::Value GetImageData(const Napi::CallbackInfo& info); + + struct Image + { + Image() = default; + ~Image() + { + if (m_Image) + { + bimg::imageFree(m_Image); + m_Image = nullptr; + } + } + bimg::ImageContainer* m_Image{}; + }; + }; +} // namespace diff --git a/Plugins/TestUtils/Source/TestUtilsImplData.h b/Plugins/TestUtils/Source/TestUtilsImplData.h new file mode 100644 index 000000000..72d10e1ce --- /dev/null +++ b/Plugins/TestUtils/Source/TestUtilsImplData.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include "TestUtils.h" + +namespace Babylon::Plugins::Internal +{ + class TestUtils::ImplData final : public std::enable_shared_from_this + { + public: + ImplData(WindowType nativeWindowPtr) + : m_nativeWindowPtr{nativeWindowPtr} + { + } + + WindowType m_nativeWindowPtr{}; + }; + +} // namespace diff --git a/Plugins/TestUtils/Source/UWP/TestUtilsImpl.cpp b/Plugins/TestUtils/Source/UWP/TestUtilsImpl.cpp new file mode 100644 index 000000000..80c1f825e --- /dev/null +++ b/Plugins/TestUtils/Source/UWP/TestUtilsImpl.cpp @@ -0,0 +1,43 @@ +#include "TestUtilsImplData.h" +#include +#include +#include + + +using namespace winrt::Windows; + +namespace Babylon::Plugins::Internal +{ + void TestUtils::Exit(const Napi::CallbackInfo& info) + { + const int32_t exitCode = info[0].As().Int32Value(); + // ceguille: I didn't find a better way to do it for UWP + exit(exitCode); + } + + void TestUtils::UpdateSize(const Napi::CallbackInfo& /*info*/) + { + } + + void TestUtils::SetTitle(const Napi::CallbackInfo& /*info*/) + { + } + + Napi::Value TestUtils::GetOutputDirectory(const Napi::CallbackInfo& info) + { + using namespace Windows::Storage; + StorageFolder^ localFolder = ApplicationData::Current->LocalFolder; + std::wstring wpath = localFolder->Path->Data(); + std::string path{winrt::to_string(wpath)}; + return Napi::Value::From(info.Env(), path); + } +} + +namespace Babylon::Plugins::TestUtils +{ + void Initialize(Napi::Env env, WindowType nativeWindowPtr) + { + auto implData{std::make_shared(nativeWindowPtr)}; + Internal::TestUtils::CreateInstance(env, implData); + } +} \ No newline at end of file diff --git a/Plugins/TestUtils/Source/Unix/TestUtilsImpl.cpp b/Plugins/TestUtils/Source/Unix/TestUtilsImpl.cpp new file mode 100644 index 000000000..9c759babf --- /dev/null +++ b/Plugins/TestUtils/Source/Unix/TestUtilsImpl.cpp @@ -0,0 +1,67 @@ +#include "TestUtilsImplData.h" +#define XK_MISCELLANY +#define XK_LATIN1 +#include +#include // will include X11 which #defines None... Don't mess with order of includes. +#include +#include // syscall +#undef None +#include + +namespace Babylon::Plugins::TestUtils +{ + int errorCode{}; +} + +namespace Babylon::Plugins::Internal +{ + void TestUtils::Exit(const Napi::CallbackInfo& info) + { + auto window = (Window)m_implData->m_nativeWindowPtr; + const int32_t exitCode = info[0].As().Int32Value(); + Plugins::TestUtils::errorCode = exitCode; + Display* display = XOpenDisplay(NULL); + XClientMessageEvent dummyEvent; + memset(&dummyEvent, 0, sizeof(XClientMessageEvent)); + dummyEvent.type = ClientMessage; + dummyEvent.window = window; + dummyEvent.format = 32; + XSendEvent(display, window, 0, 0, (XEvent*)&dummyEvent); + XFlush(display); + } + + void TestUtils::UpdateSize(const Napi::CallbackInfo& /*info*/) + { + } + + void TestUtils::SetTitle(const Napi::CallbackInfo & info) + { + const auto title = info[0].As().Utf8Value(); + Display* display = XOpenDisplay(NULL); + auto window = (Window)m_implData->m_nativeWindowPtr; + XStoreName(display, window, title.c_str()); + } + + Napi::Value TestUtils::GetOutputDirectory(const Napi::CallbackInfo& info) + { + char exe[1024]; + int ret = readlink("/proc/self/exe", exe, sizeof(exe)-1); + if(ret == -1) + { + throw Napi::Error::New(info.Env(), "Unable to get executable location"); + } + exe[ret] = 0; + + auto path = std::filesystem::path{exe}.parent_path().generic_string(); + return Napi::Value::From(info.Env(), path); + } +} + +namespace Babylon::Plugins::TestUtils +{ + void Initialize(Napi::Env env, WindowType nativeWindowPtr) + { + auto implData{std::make_shared(nativeWindowPtr)}; + Internal::TestUtils::CreateInstance(env, implData); + } +} \ No newline at end of file diff --git a/Plugins/TestUtils/Source/Win32/TestUtilsImpl.cpp b/Plugins/TestUtils/Source/Win32/TestUtilsImpl.cpp new file mode 100644 index 000000000..869058a21 --- /dev/null +++ b/Plugins/TestUtils/Source/Win32/TestUtilsImpl.cpp @@ -0,0 +1,60 @@ +#include "TestUtilsImplData.h" +#include +#include + +namespace +{ + std::filesystem::path GetModulePath() + { + char buffer[1024]; + ::GetModuleFileNameA(nullptr, buffer, ARRAYSIZE(buffer)); + return std::filesystem::path{buffer}.parent_path(); + } +} + +namespace Babylon::Plugins::TestUtils +{ + int errorCode{}; +} + +namespace Babylon::Plugins::Internal +{ + void TestUtils::Exit(const Napi::CallbackInfo& info) + { + const int32_t exitCode = info[0].As().Int32Value(); + Plugins::TestUtils::errorCode = exitCode; + PostMessageW(m_implData->m_nativeWindowPtr, WM_DESTROY, 0, 0); + } + + void TestUtils::UpdateSize(const Napi::CallbackInfo& info) + { + const int32_t width = info[0].As().Int32Value(); + const int32_t height = info[1].As().Int32Value(); + + auto hwnd = m_implData->m_nativeWindowPtr; + RECT rc{ 0, 0, width, height }; + AdjustWindowRectEx(&rc, GetWindowStyle(hwnd), GetMenu(hwnd) != NULL, GetWindowExStyle(hwnd)); + SetWindowPos(hwnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER); + } + + void TestUtils::SetTitle(const Napi::CallbackInfo& info) + { + const auto title = info[0].As().Utf8Value(); + SetWindowTextA(m_implData->m_nativeWindowPtr, title.c_str()); + } + + Napi::Value TestUtils::GetOutputDirectory(const Napi::CallbackInfo& info) + { + auto path = GetModulePath().parent_path().generic_string(); + return Napi::Value::From(info.Env(), path); + } +} + +namespace Babylon::Plugins::TestUtils +{ + void Initialize(Napi::Env env, WindowType nativeWindowPtr) + { + auto implData{std::make_shared(nativeWindowPtr)}; + Internal::TestUtils::CreateInstance(env, implData); + } +} \ No newline at end of file