aboutsummaryrefslogtreecommitdiff
path: root/zenutil
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2021-08-06 17:02:17 +0200
committerStefan Boberg <[email protected]>2021-08-06 17:03:47 +0200
commit4d70af00d66cbfbd9f52afdfce7bcb4ec1881121 (patch)
tree95108f43f576adceb6cd394238ae6caca74a711d /zenutil
parentzen::Process -> zen::ProcessHandle (diff)
downloadzen-4d70af00d66cbfbd9f52afdfce7bcb4ec1881121.tar.xz
zen-4d70af00d66cbfbd9f52afdfce7bcb4ec1881121.zip
Repurposing test utility code to enable server control via zen
Diffstat (limited to 'zenutil')
-rw-r--r--zenutil/include/zenserverprocess.h56
-rw-r--r--zenutil/zenserverprocess.cpp173
-rw-r--r--zenutil/zenutil.vcxproj106
-rw-r--r--zenutil/zenutil.vcxproj.filters9
4 files changed, 344 insertions, 0 deletions
diff --git a/zenutil/include/zenserverprocess.h b/zenutil/include/zenserverprocess.h
new file mode 100644
index 000000000..fbe48abe3
--- /dev/null
+++ b/zenutil/include/zenserverprocess.h
@@ -0,0 +1,56 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include <zencore/thread.h>
+
+#include <spdlog/spdlog.h>
+
+#include <filesystem>
+
+class ZenServerEnvironment
+{
+public:
+ ZenServerEnvironment();
+ ~ZenServerEnvironment();
+
+ void Initialize(std::filesystem::path ProgramBaseDir, std::filesystem::path TestBaseDir);
+
+ std::filesystem::path CreateNewTestDir();
+ std::filesystem::path ProgramBaseDir() const { return m_ProgramBaseDir; }
+ std::filesystem::path RootDir(std::string_view Path);
+ bool IsInitialized() const { return m_IsInitialized; }
+
+private:
+ std::filesystem::path m_ProgramBaseDir;
+ std::filesystem::path m_TestBaseDir;
+ bool m_IsInitialized = false;
+};
+
+struct ZenServerInstance
+{
+ ZenServerInstance(ZenServerEnvironment& TestEnvironment);
+ ~ZenServerInstance();
+
+ void SignalShutdown() { m_ShutdownEvent.Set(); }
+ void WaitUntilReady() { m_ReadyEvent.Wait(); }
+ void EnableTermination() { m_Terminate = true; }
+ void EnableMesh() { m_MeshEnabled = true; }
+
+ void SetTestDir(std::filesystem::path TestDir)
+ {
+ ZEN_ASSERT(!m_Process.IsValid());
+ m_TestDir = TestDir;
+ }
+
+ void SpawnServer(int BasePort);
+
+private:
+ ZenServerEnvironment& m_Env;
+ zen::ProcessHandle m_Process;
+ zen::Event m_ReadyEvent;
+ zen::Event m_ShutdownEvent;
+ bool m_Terminate = false;
+ std::filesystem::path m_TestDir;
+ bool m_MeshEnabled = false;
+};
diff --git a/zenutil/zenserverprocess.cpp b/zenutil/zenserverprocess.cpp
new file mode 100644
index 000000000..f58a9f166
--- /dev/null
+++ b/zenutil/zenserverprocess.cpp
@@ -0,0 +1,173 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "zenserverprocess.h"
+
+#include <zencore/filesystem.h>
+#include <zencore/fmtutils.h>
+#include <zencore/string.h>
+
+#include <spdlog/spdlog.h>
+
+//////////////////////////////////////////////////////////////////////////
+
+std::atomic<int> TestCounter{0};
+
+ZenServerEnvironment::ZenServerEnvironment()
+{
+}
+
+ZenServerEnvironment::~ZenServerEnvironment()
+{
+}
+
+void
+ZenServerEnvironment::Initialize(std::filesystem::path ProgramBaseDir, std::filesystem::path TestBaseDir)
+{
+ m_ProgramBaseDir = ProgramBaseDir;
+ m_TestBaseDir = TestBaseDir;
+
+ spdlog::info("Program base dir is '{}'", ProgramBaseDir);
+ spdlog::info("Cleaning test base dir '{}'", TestBaseDir);
+ zen::DeleteDirectories(TestBaseDir.c_str());
+
+ m_IsInitialized = true;
+}
+
+std::filesystem::path
+ZenServerEnvironment::CreateNewTestDir()
+{
+ using namespace std::literals;
+
+ zen::ExtendableWideStringBuilder<256> TestDir;
+ TestDir << "test"sv << int64_t(++TestCounter);
+
+ std::filesystem::path TestPath = m_TestBaseDir / TestDir.c_str();
+
+ spdlog::info("Creating new test dir @ '{}'", TestPath);
+
+ zen::CreateDirectories(TestPath.c_str());
+
+ return TestPath;
+}
+
+std::filesystem::path
+ZenServerEnvironment::RootDir(std::string_view Path)
+{
+ std::filesystem::path Root = m_ProgramBaseDir.parent_path().parent_path();
+
+ std::filesystem::path Relative{Path};
+
+ return Root / Relative;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+std::atomic<int> ChildIdCounter{0};
+
+ZenServerInstance::ZenServerInstance(ZenServerEnvironment& TestEnvironment) : m_Env(TestEnvironment)
+{
+ ZEN_ASSERT(TestEnvironment.IsInitialized());
+}
+
+ZenServerInstance::~ZenServerInstance()
+{
+ if (m_Process.IsValid())
+ {
+ if (m_Terminate)
+ {
+ spdlog::info("Terminating zenserver process");
+ m_Process.Terminate(111);
+ }
+ else
+ {
+ SignalShutdown();
+ m_Process.Wait();
+ }
+ }
+}
+
+void
+ZenServerInstance::SpawnServer(int BasePort)
+{
+ ZEN_ASSERT(!m_Process.IsValid()); // Only spawn once
+
+ const std::filesystem::path BaseDir = m_Env.ProgramBaseDir();
+ const std::filesystem::path Executable = BaseDir / "zenserver.exe";
+
+ const int MyPid = _getpid();
+ const int ChildId = ++ChildIdCounter;
+
+ zen::ExtendableStringBuilder<32> ChildEventName;
+ ChildEventName << "Zen_Child_" << ChildId;
+ zen::NamedEvent ChildEvent{ChildEventName};
+
+ zen::ExtendableStringBuilder<32> ChildShutdownEventName;
+ ChildShutdownEventName << "Zen_Child_" << ChildId;
+ ChildShutdownEventName << "_Shutdown";
+ zen::NamedEvent ChildShutdownEvent{ChildShutdownEventName};
+
+ zen::ExtendableStringBuilder<32> LogId;
+ LogId << "Zen" << ChildId;
+
+ zen::ExtendableWideStringBuilder<128> CommandLine;
+ CommandLine << "\"";
+ CommandLine.Append(Executable.c_str());
+ CommandLine << "\" --test --owner-pid ";
+ CommandLine << MyPid;
+ CommandLine << " ";
+ CommandLine << "--port " << BasePort;
+ CommandLine << " --child-id " << ChildEventName;
+ CommandLine << " --log-id " << LogId;
+
+ if (!m_TestDir.empty())
+ {
+ CommandLine << " --data-dir ";
+ CommandLine << m_TestDir.c_str();
+ }
+
+ if (m_MeshEnabled)
+ {
+ CommandLine << " --mesh";
+ }
+
+ std::filesystem::path CurrentDirectory = std::filesystem::current_path();
+
+ spdlog::debug("Spawning server");
+
+ PROCESS_INFORMATION ProcessInfo{};
+ STARTUPINFO Sinfo{.cb = sizeof(STARTUPINFO)};
+
+ DWORD CreationFlags = 0; // CREATE_NEW_CONSOLE;
+ const bool InheritHandles = false;
+ void* Environment = nullptr;
+ LPSECURITY_ATTRIBUTES ProcessAttributes = nullptr;
+ LPSECURITY_ATTRIBUTES ThreadAttributes = nullptr;
+
+ BOOL Success = CreateProcessW(Executable.c_str(),
+ (LPWSTR)CommandLine.c_str(),
+ ProcessAttributes,
+ ThreadAttributes,
+ InheritHandles,
+ CreationFlags,
+ Environment,
+ CurrentDirectory.c_str(),
+ &Sinfo,
+ &ProcessInfo);
+
+ if (Success == FALSE)
+ {
+ std::error_code err(::GetLastError(), std::system_category());
+
+ spdlog::error("Server spawn failed: {}", err.message());
+
+ throw std::system_error(err, "failed to create server process");
+ }
+
+ spdlog::debug("Server spawned OK");
+
+ CloseHandle(ProcessInfo.hThread);
+
+ m_Process.Initialize(ProcessInfo.hProcess);
+ m_ReadyEvent = std::move(ChildEvent);
+ m_ShutdownEvent = std::move(ChildShutdownEvent);
+}
diff --git a/zenutil/zenutil.vcxproj b/zenutil/zenutil.vcxproj
new file mode 100644
index 000000000..c851d5ec6
--- /dev/null
+++ b/zenutil/zenutil.vcxproj
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>16.0</VCProjectVersion>
+ <Keyword>Win32Proj</Keyword>
+ <ProjectGuid>{77f8315d-b21d-4db0-9a6f-2d3359f88a70}</ProjectGuid>
+ <RootNamespace>zenutil</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\zen_base_debug.props" />
+ <Import Project="..\zenfs_common.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="..\zen_base_release.props" />
+ <Import Project="..\zenfs_common.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>true</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgUseStatic>true</VcpkgUseStatic>
+ <VcpkgAdditionalInstallOptions>--overlay-ports=$(SolutionDir)vcpkg_overlay-ports</VcpkgAdditionalInstallOptions>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgUseStatic>true</VcpkgUseStatic>
+ <VcpkgAdditionalInstallOptions>--overlay-ports=$(SolutionDir)vcpkg_overlay-ports</VcpkgAdditionalInstallOptions>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\zencore\include;..\zenstore\include;.\include</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>..\zencore\include;..\zenstore\include;.\include</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="zenserverprocess.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="include\zenserverprocess.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/zenutil/zenutil.vcxproj.filters b/zenutil/zenutil.vcxproj.filters
new file mode 100644
index 000000000..ca1414842
--- /dev/null
+++ b/zenutil/zenutil.vcxproj.filters
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="zenserverprocess.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="include\zenserverprocess.h" />
+ </ItemGroup>
+</Project> \ No newline at end of file