diff options
| author | Stefan Boberg <[email protected]> | 2023-11-17 10:30:51 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-11-17 10:30:51 +0100 |
| commit | 68915961c397c9f06930f80677d131a25d045962 (patch) | |
| tree | 91a68aa26506e9314738b566645572faf76a1668 /src/zenutil | |
| parent | removed zen runtests command (#551) (diff) | |
| download | zen-68915961c397c9f06930f80677d131a25d045962.tar.xz zen-68915961c397c9f06930f80677d131a25d045962.zip | |
use dynamic port assignment for tests (#545)
this change replaces hard-coded port numbers in tests with dynamically assigned ports, to avoid potential issues around socket lifetimes and re-use policies
Diffstat (limited to 'src/zenutil')
| -rw-r--r-- | src/zenutil/include/zenutil/zenserverprocess.h | 24 | ||||
| -rw-r--r-- | src/zenutil/zenserverprocess.cpp | 97 |
2 files changed, 77 insertions, 44 deletions
diff --git a/src/zenutil/include/zenutil/zenserverprocess.h b/src/zenutil/include/zenutil/zenserverprocess.h index 14d19ad39..ce6a990f8 100644 --- a/src/zenutil/include/zenutil/zenserverprocess.h +++ b/src/zenutil/include/zenutil/zenserverprocess.h @@ -38,6 +38,7 @@ public: inline bool IsInitialized() const { return m_IsInitialized; } inline bool IsTestEnvironment() const { return m_IsTestInstance; } inline std::string_view GetServerClass() const { return m_ServerClass; } + inline uint16_t GetNewPortNumber() { return m_NextPortNumber.fetch_add(1); } private: std::filesystem::path m_ProgramBaseDir; @@ -45,6 +46,7 @@ private: bool m_IsInitialized = false; bool m_IsTestInstance = false; std::string m_ServerClass; + std::atomic_uint16_t m_NextPortNumber{20000}; }; /** Zen Server Instance management @@ -63,7 +65,7 @@ struct ZenServerInstance void Shutdown(); void SignalShutdown(); - void WaitUntilReady(); + uint16_t WaitUntilReady(); [[nodiscard]] bool WaitUntilReady(int Timeout); void EnableTermination() { m_Terminate = true; } void DisableShutdownOnDestroy() { m_ShutdownOnDestroy = false; } @@ -72,8 +74,20 @@ struct ZenServerInstance inline void SetOwnerPid(int Pid) { m_OwnerPid = Pid; } bool IsRunning(); - void SetTestDir(std::filesystem::path TestDir); - inline void SpawnServer(int BasePort = 0, std::string_view AdditionalServerArgs = std::string_view()) + void SetTestDir(std::filesystem::path TestDir); + + inline void SpawnServer(std::string_view AdditionalServerArgs = std::string_view()) + { + SpawnServer(m_Env.GetNewPortNumber(), AdditionalServerArgs, /* WaitTimeoutMs */ 0); + } + + inline uint16_t SpawnServerAndWaitUntilReady(std::string_view AdditionalServerArgs = std::string_view()) + { + SpawnServer(m_Env.GetNewPortNumber(), AdditionalServerArgs, /* WaitTimeoutMs */ 100'000); + return GetBasePort(); + } + + inline void SpawnServer(int BasePort, std::string_view AdditionalServerArgs = std::string_view()) { SpawnServer(BasePort, AdditionalServerArgs, /* WaitTimeoutMs */ 0); } @@ -85,6 +99,7 @@ struct ZenServerInstance void AttachToRunningServer(int BasePort = 0); std::string GetBaseUri() const; + uint16_t GetBasePort() const { return m_BasePort; } private: ZenServerEnvironment& m_Env; @@ -94,12 +109,13 @@ private: bool m_Terminate = false; bool m_ShutdownOnDestroy = true; std::filesystem::path m_TestDir; - int m_BasePort = 0; + uint16_t m_BasePort = 0; std::optional<int> m_OwnerPid; std::string m_Name; void CreateShutdownEvent(int BasePort); void SpawnServer(int BasePort, std::string_view AdditionalServerArgs, int WaitTimeoutMs); + void OnServerReady(); }; /** Shared system state diff --git a/src/zenutil/zenserverprocess.cpp b/src/zenutil/zenserverprocess.cpp index 2971b0fab..909692fbc 100644 --- a/src/zenutil/zenserverprocess.cpp +++ b/src/zenutil/zenserverprocess.cpp @@ -12,6 +12,8 @@ #include <atomic> +#include <gsl/gsl-lite.hpp> + #if ZEN_PLATFORM_WINDOWS # include <zencore/windows.h> #else @@ -530,8 +532,6 @@ ZenServerInstance::SpawnServer(int BasePort, std::string_view AdditionalServerAr ChildEventName << "Zen_Child_" << ChildId; NamedEvent ChildEvent{ChildEventName}; - CreateShutdownEvent(BasePort); - ExtendableStringBuilder<32> LogId; LogId << "Zen" << ChildId; m_Name = LogId.ToString(); @@ -567,7 +567,7 @@ ZenServerInstance::SpawnServer(int BasePort, std::string_view AdditionalServerAr if (BasePort) { CommandLine << " --port " << BasePort; - m_BasePort = BasePort; + m_BasePort = gsl::narrow_cast<uint16_t>(BasePort); } if (!m_TestDir.empty()) @@ -629,39 +629,6 @@ ZenServerInstance::SpawnServer(int BasePort, std::string_view AdditionalServerAr { throw std::runtime_error(fmt::format("server start of {} timeout after {}", m_Name, NiceTimeSpanMs(WaitTimeoutMs))); } - - // Determine effective base port - - ZenServerState State; - if (!State.InitializeReadOnly()) - { - // TODO: return success/error code instead? - throw std::runtime_error("no zen state found"); - } - - const ZenServerState::ZenServerEntry* Entry = nullptr; - - if (BasePort) - { - Entry = State.Lookup(BasePort); - } - else - { - State.Snapshot([&](const ZenServerState::ZenServerEntry& InEntry) { - if (InEntry.Pid == static_cast<uint32_t>(GetProcessId(ChildPid))) - { - Entry = &InEntry; - } - }); - } - - if (!Entry) - { - // TODO: return success/error code instead? - throw std::runtime_error("no server entry found"); - } - - m_BasePort = Entry->EffectiveListenPort; } } @@ -717,23 +684,73 @@ ZenServerInstance::Detach() } } -void +uint16_t ZenServerInstance::WaitUntilReady() { while (m_ReadyEvent.Wait(100) == false) { if (!m_Process.IsRunning() || !m_Process.IsValid()) { - ZEN_INFO("Wait abandoned by invalid process (running={})", m_Process.IsRunning()); - return; + ZEN_WARN("Wait abandoned by invalid process (running={})", m_Process.IsRunning()); + + return 0; } } + + OnServerReady(); + + return m_BasePort; } bool ZenServerInstance::WaitUntilReady(int Timeout) { - return m_ReadyEvent.Wait(Timeout); + if (m_ReadyEvent.Wait(Timeout)) + { + OnServerReady(); + + return true; + } + + return false; +} + +void +ZenServerInstance::OnServerReady() +{ + // Determine effective base port + + ZenServerState State; + if (!State.InitializeReadOnly()) + { + // TODO: return success/error code instead? + throw std::runtime_error("no zen state found"); + } + + const ZenServerState::ZenServerEntry* Entry = nullptr; + + if (m_BasePort) + { + Entry = State.Lookup(m_BasePort); + } + else + { + State.Snapshot([&](const ZenServerState::ZenServerEntry& InEntry) { + if (InEntry.Pid == (uint32_t)m_Process.Pid()) + { + Entry = &InEntry; + } + }); + } + + if (!Entry) + { + // TODO: return success/error code instead? + throw std::runtime_error("no server entry found"); + } + + m_BasePort = Entry->EffectiveListenPort; + CreateShutdownEvent(m_BasePort); } std::string |