aboutsummaryrefslogtreecommitdiff
path: root/src/zenutil
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-11-17 10:30:51 +0100
committerGitHub <[email protected]>2023-11-17 10:30:51 +0100
commit68915961c397c9f06930f80677d131a25d045962 (patch)
tree91a68aa26506e9314738b566645572faf76a1668 /src/zenutil
parentremoved zen runtests command (#551) (diff)
downloadzen-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.h24
-rw-r--r--src/zenutil/zenserverprocess.cpp97
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