Development/incremental test coverage expansion [Test Branch] - #2149
Development/incremental test coverage expansion [Test Branch]#2149smanes0213 wants to merge 84 commits into
Conversation
Add and extend tests for CyclicBuffer, Library, ServiceAdministrator, ProxyType, JSONRPC::Handler, SocketPort, and WorkerPool to close identified gaps from the test coverage gap analysis. Signed-off-by: smanes0213 <sankalpmaneshwar46@outlook.com>
…suffix from test file name
…ure tampering - test_jsonrpc_websocket.cpp, test_jsonrpc_http.cpp: Increase maxWaitTimeMs (4000->8000), maxInitTime (2000->4000), and IPTestAdministrator waitTime (8->20) to prevent Signal/Wait race conditions on slow CI runners - test_jwt.cpp: Tamper 5th character of signature instead of last character, which may only affect Base64 padding bits without changing the decoded HMAC
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
Suppressed comments (6)
Tests/unit/core/test_controller.cpp:126
- These tests assume an external "Dictionary" plugin is available in the ThunderTestRuntime environment. If it is not configured/installed, Controller.activate will return Core::ERROR_UNKNOWN_KEY (see Source/Thunder/Controller.cpp:1009-1011) and this test will fail deterministically. Consider skipping when Dictionary is unavailable so the unit test suite is self-contained and not environment-dependent.
TEST_F(ControllerTest, ActivateDictionary_TransitionsToActivated)
{
// Ensure Dictionary starts deactivated.
string response;
uint32_t result = _runtime.Invoke(
"Controller.activate",
R"({"callsign":"Dictionary"})",
response);
EXPECT_EQ(result, Core::ERROR_NONE);
}
Tests/unit/core/test_controller.cpp:139
- This test assumes the "Dictionary" plugin exists in the runtime. If it is not configured/installed, Controller.deactivate will return Core::ERROR_UNKNOWN_KEY and the test will fail. Skip the test when Dictionary is not present to avoid environment-dependent failures.
TEST_F(ControllerTest, DeactivateDictionary_TransitionsToDeactivated)
{
// Ensure Dictionary starts activated.
string response;
uint32_t result = _runtime.Invoke(
"Controller.deactivate",
R"({"callsign":"Dictionary"})",
response);
EXPECT_EQ(result, Core::ERROR_NONE);
}
Tests/unit/core/test_websocket_protocol.cpp:234
- RunTextServer() unlinks the Unix socket path before starting, but never removes it after the test completes. Because each test uses a unique /tmp path, this can leave behind many stale socket files across runs. Consider unlinking the connector path in stopServer() after joining the server thread.
// Ensure the server thread is always joined before returning
auto stopServer = [&]() {
clientDone = true;
if (serverThread.joinable()) {
serverThread.join();
}
::Thunder::Core::Singleton::Dispose();
};
Tests/unit/core/test_websocket_protocol.cpp:391
- This test also leaves the Unix domain socket file behind (it unlinks before Open(), but never unlinks at the end). Unlinking in stopServer() keeps /tmp clean and avoids accumulating stale socket files.
// RAII guard: always signal and join the server thread on exit
auto stopServer = [&]() {
clientsDone = true;
if (serverThread.joinable()) {
serverThread.join();
}
::Thunder::Core::Singleton::Dispose();
};
Tests/unit/core/test_controller.cpp:166
- This configuration test hard-codes Dictionary-specific configuration keys/values. If the Dictionary plugin is not configured/installed, Controller.1.configuration@Dictionary will fail (Core::ERROR_UNKNOWN_KEY). Skip when Dictionary is unavailable to keep the unit test suite robust.
TEST_F(ControllerTest, ConfigurationQuery_ReturnsDictionaryConfiguration)
{
string response;
const uint32_t result = _runtime.Invoke(
"Controller.1.configuration@Dictionary",
"{}",
response);
ASSERT_EQ(result, Core::ERROR_NONE);
ASSERT_FALSE(response.empty());
JsonObject configuration;
ASSERT_TRUE(configuration.FromString(response));
EXPECT_EQ(configuration["storage"].String(), "DataModel.json");
EXPECT_EQ(configuration["lingertime"].Number(), 10);
}
Tests/unit/core/test_tls.cpp:396
- CertificateStore::Add returns void and Thunder is typically built with exceptions disabled, so EXPECT_NO_THROW doesn't provide meaningful coverage here (it becomes a no-op when exceptions are off). Consider calling Add() directly and relying on the subsequent X509_verify_cert() assertions to validate success.
// Add generated cert into a writable store.
EXPECT_NO_THROW(store.Add(cert));
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.
Suppressed comments (4)
Tests/unit/core/test_comrpc.cpp:894
IRemoteConnection::INotificationinheritsCore::IUnknown, soRelease()is expected to return the new reference count, not an error code. ReturningCore::ERROR_*values violates the interface contract and makes the ref-counting semantics misleading in this test.
uint32_t AddRef() const override { return (++_refCount); }
uint32_t Release() const override {
uint32_t result = (--_refCount);
return (result == 0 ? ::Thunder::Core::ERROR_DESTRUCTION_SUCCEEDED : ::Thunder::Core::ERROR_NONE);
}
Tests/unit/core/test_websocket_protocol.cpp:240
- The wait predicate only becomes true when
serverReadyis true, so ifserver.Open()fails quickly the main thread still waits the full 10s timeout even thoughserverOpenResultis already set and the condition variable has been notified. Waiting forserverOpenResultto change from its sentinel value avoids the unnecessary delay and yields the correct failure message.
ready = readyCV.wait_for(lk, std::chrono::seconds(10),
[&]{ return serverReady.load(); });
Tests/unit/core/test_websocket_protocol.cpp:367
- Avoid calling GoogleTest assertions from the server thread. GTest assertions are intended to run on the test thread; reporting failures from worker threads can be unreliable. The parent thread already checks readiness and fails the test if the server did not open.
bool opened = (server.Open(maxWait) == ::Thunder::Core::ERROR_NONE);
EXPECT_TRUE(opened) << "server.Open() failed";
Tests/unit/core/test_socketport.cpp:1404
- Using
ASSERT_EQonserver.Close()before signaling the parent means a close failure will abort the child callback and the parent will block inWait()until the futex timeout. Prefer a non-fatal check here so the closure signal is still sent and the test can fail without an extra 16s delay.
ASSERT_EQ(server.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE);
// Signal parent that server has closed.
ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries),
::Thunder::Core::ERROR_NONE);
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
Suppressed comments (4)
Tests/unit/core/test_assertionunit.cpp:28
std::atomicis used but<atomic>is not included, which can cause a compile error depending on transitive includes.
#include <gtest/gtest.h>
#ifndef MODULE_NAME
#include "../Module.h"
#endif
#include <core/core.h>
#include <unistd.h>
Tests/unit/core/test_websocket_protocol.cpp:232
UniqueSocketPath()creates a new socket filename per run, but the path is only unlinked before creating the server. This leaves a growing number of/tmp/wpe_ws_*entries across repeated test runs. Consider unlinking the socket path during teardown as well.
auto stopServer = [&]() {
clientDone = true;
if (serverThread.joinable()) {
serverThread.join();
}
Tests/unit/core/test_tls.cpp:414
EXPECT_NO_THROWexpands totry/catch; this repo is built with-fno-exceptionsby default, so this assertion can fail to compile. SinceCertificateStore::Add()isvoidand the test already verifies the cert viaX509_verify_cert, just callstore.Add(cert)here.
// Add generated cert into a writable store.
EXPECT_NO_THROW(store.Add(cert));
Tests/unit/core/test_websocket_protocol.cpp:389
- Same as above: this test uses a unique Unix socket path and only unlinks it before starting the server, so repeated runs can leave many stale socket files under
/tmp. Unlink the socket path instopServer()after the server thread is joined.
auto stopServer = [&]() {
clientsDone = true;
if (serverThread.joinable()) {
serverThread.join();
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
Suppressed comments (3)
Tests/unit/core/test_comrpc.cpp:894
- ConnectionNotificationTracker::AddRef() returns the incremented refcount, but Thunder's IReferenceCounted implementations conventionally return Core::ERROR_NONE (see Source/com/IUnknown.h:214-219). Returning a non-zero value can be misinterpreted as an error code by callers that check the return value.
// IReferenceCounted — stack-allocated, use a simple counter
uint32_t AddRef() const override { return (++_refCount); }
uint32_t Release() const override {
uint32_t result = (--_refCount);
return (result == 0 ? ::Thunder::Core::ERROR_DESTRUCTION_SUCCEEDED : ::Thunder::Core::ERROR_NONE);
}
Tests/unit/core/test_controller.cpp:126
- These tests expect activating/deactivating a "Dictionary" plugin to succeed, but ThunderTestRuntime is initialized with an empty plugins list, so only the Controller service is configured. Controller::Activate/Deactivate returns ERROR_UNKNOWN_KEY for unknown callsigns (Source/Thunder/Controller.cpp:1009-1011), so this will fail in CI unless Dictionary is configured externally.
TEST_F(ControllerTest, ActivateDictionary_TransitionsToActivated)
{
// Ensure Dictionary starts deactivated.
string response;
uint32_t result = _runtime.Invoke(
"Controller.activate",
R"({"callsign":"Dictionary"})",
response);
EXPECT_EQ(result, Core::ERROR_NONE);
}
Tests/unit/core/test_controller.cpp:166
- Same issue as ActivateDictionary_*: this test assumes the Dictionary service exists and is configured with specific settings, but ThunderTestRuntime is initialized with no plugins. If Dictionary isn't configured/installed in the test environment, this assertion will fail with ERROR_UNKNOWN_KEY.
TEST_F(ControllerTest, ConfigurationQuery_ReturnsDictionaryConfiguration)
{
string response;
const uint32_t result = _runtime.Invoke(
"Controller.1.configuration@Dictionary",
"{}",
response);
ASSERT_EQ(result, Core::ERROR_NONE);
ASSERT_FALSE(response.empty());
JsonObject configuration;
ASSERT_TRUE(configuration.FromString(response));
EXPECT_EQ(configuration["storage"].String(), "DataModel.json");
EXPECT_EQ(configuration["lingertime"].Number(), 10);
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.
Suppressed comments (5)
Tests/unit/core/test_comrpc.cpp:894
ConnectionNotificationTracker::AddRef()returns the incremented refcount, but most ThunderCore::IUnknownimplementations returnCore::ERROR_NONEand useCore::ERROR_DESTRUCTION_SUCCEEDEDfromRelease()as the only meaningful return value (e.g.,Source/core/Proxy.h:131-153). Returning the refcount here is inconsistent and can confuse/ break code that interprets non-zero as an error code.
uint32_t AddRef() const override { return (++_refCount); }
uint32_t Release() const override {
uint32_t result = (--_refCount);
return (result == 0 ? ::Thunder::Core::ERROR_DESTRUCTION_SUCCEEDED : ::Thunder::Core::ERROR_NONE);
}
Tests/unit/core/test_websocket_protocol.cpp:234
- The Unix-domain socket path created in
RunTextServer()is unlinked before bind, but never cleaned up after the test completes. SinceUniqueSocketPath()generates a new path per test run, this can leave many stale/tmp/wpe_ws_*files behind on CI/dev machines.
auto stopServer = [&]() {
clientDone = true;
if (serverThread.joinable()) {
serverThread.join();
}
::Thunder::Core::Singleton::Dispose();
};
Tests/unit/core/test_websocket_protocol.cpp:241
RunTextServer()waits up to 10s forserverReadyto become true, butserverReadyis only set on success. Ifserver.Open()fails quickly, the test still waits the full timeout before failing, slowing CI. Consider waking based on “Open finished” (e.g.,serverOpenResultchanged from its sentinel value) and then checking the result.
std::unique_lock<std::mutex> lk(readyMutex);
ready = readyCV.wait_for(lk, std::chrono::seconds(10),
[&]{ return serverReady.load(); });
}
Tests/unit/core/test_controller.cpp:138
Controller.deactivatecan returnERROR_ILLEGAL_STATEorERROR_INPROGRESSdepending on current plugin state (not onlyERROR_NONE). With a shared runtime across the fixture this makes the test flaky if the Dictionary plugin is already inactive or deactivation is still progressing.
uint32_t result = _runtime.Invoke(
"Controller.deactivate",
R"({"callsign":"Dictionary"})",
response);
EXPECT_EQ(result, Core::ERROR_NONE);
Tests/unit/core/test_controller.cpp:125
Controller.activatecan returnERROR_ILLEGAL_STATE,ERROR_INPROGRESS, orERROR_PENDING_CONDITIONSdepending on current plugin state (not onlyERROR_NONE). With a shared runtime across the fixture this makes the test flaky if the Dictionary plugin is already active or activation is still progressing.
uint32_t result = _runtime.Invoke(
"Controller.activate",
R"({"callsign":"Dictionary"})",
response);
EXPECT_EQ(result, Core::ERROR_NONE);
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
Suppressed comments (4)
Tests/unit/core/test_websocket_protocol.cpp:241
- The condition_variable predicate only returns true on success (
serverReady == true). Ifserver.Open()fails, the notify happens but the wait keeps sleeping until timeout. Wait for "open completed" (via a sentinel value) and then check the result separately.
std::unique_lock<std::mutex> lk(readyMutex);
ready = readyCV.wait_for(lk, std::chrono::seconds(10),
[&]{ return serverReady.load(); });
}
Tests/unit/core/test_websocket_protocol.cpp:362
- MultipleClientsSequential():
serverReadyis only set true on success, but the wait predicate also only unblocks onserverReady==true. Ifserver.Open()fails, the server thread still notifies, yet the wait blocks until timeout. A tri-state (e.g. -1/0/1) lets the wait unblock on both success and failure and then assert success explicitly.
std::atomic<bool> serverReady{false};
std::mutex readyMutex;
std::condition_variable readyCV;
std::atomic<bool> clientsDone{false};
Tests/unit/core/test_websocket_protocol.cpp:399
- MultipleClientsSequential(): once
serverReadybecomes tri-state, the wait predicate should unblock when the server thread has signaled (serverReady != -1), then fail fast ifserverReady != 1. As written, the predicate only unblocks on success.
std::unique_lock<std::mutex> lk(readyMutex);
ready = readyCV.wait_for(lk, std::chrono::seconds(10),
[&]{ return serverReady.load(); });
}
if (!ready || !serverReady.load()) {
Tests/unit/core/test_websocket_protocol.cpp:200
- RunTextServer() waits on
serverReady(which is only set true on success), so ifserver.Open()fails the parent thread will still block until the full timeout and then report a misleading "did not become ready" error. UsingserverOpenResultas the wait completion signal avoids the 10s stall and makes failures report immediately.
This issue also appears in the following locations of the same file:
- line 238
- line 358
- line 395
std::atomic<uint32_t> serverOpenResult{::Thunder::Core::ERROR_GENERAL};
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
Tests/unit/core/test_websocket_protocol.cpp:241
- This wait predicate should track completion of server.Open() rather than success-only readiness; otherwise, a fast Open() failure still blocks for up to 10 seconds. After switching to a completion flag (see earlier hunk), update the predicate here accordingly.
bool ready = false;
{
std::unique_lock<std::mutex> lk(readyMutex);
ready = readyCV.wait_for(lk, std::chrono::seconds(10),
[&]{ return serverReady.load(); });
}
| std::atomic<bool> serverReady{false}; | ||
| std::mutex readyMutex; | ||
| std::condition_variable readyCV; | ||
| std::atomic<bool> clientDone{false}; | ||
|
|
||
| std::atomic<uint32_t> serverOpenResult{::Thunder::Core::ERROR_GENERAL}; | ||
|
|
||
| std::thread serverThread([&]() { | ||
| ::Thunder::Core::SocketServerType<ProtoTextServer> server( | ||
| ::Thunder::Core::NodeId(connector.c_str())); | ||
|
|
||
| const uint32_t result = server.Open(maxWait); | ||
| serverOpenResult = result; | ||
|
|
||
| { | ||
| std::lock_guard<std::mutex> lk(readyMutex); | ||
| serverReady = (result == ::Thunder::Core::ERROR_NONE); | ||
| } | ||
| readyCV.notify_one(); |
No description provided.