Merge branch 'master' into fix-appearance-change-css-watching
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#endif
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "fixtures/GlibTestsFixture.hpp"
|
||||
|
||||
@@ -141,3 +142,33 @@ TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal copy/move counter", "[signal][thr
|
||||
producer.join();
|
||||
REQUIRE(count == NUM_EVENTS);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(GlibTestsFixture, "SafeSignal queue stays bounded under burst load",
|
||||
"[signal][thread][util][perf]") {
|
||||
constexpr int NUM_EVENTS = 200;
|
||||
constexpr std::size_t MAX_QUEUED_EVENTS = 8;
|
||||
std::vector<int> received;
|
||||
|
||||
SafeSignal<int> test_signal;
|
||||
test_signal.set_max_queued_events(MAX_QUEUED_EVENTS);
|
||||
|
||||
setTimeout(500);
|
||||
|
||||
test_signal.connect([&](auto value) { received.push_back(value); });
|
||||
|
||||
run([&]() {
|
||||
std::thread producer([&]() {
|
||||
for (int i = 1; i <= NUM_EVENTS; ++i) {
|
||||
test_signal.emit(i);
|
||||
}
|
||||
});
|
||||
producer.join();
|
||||
|
||||
Glib::signal_timeout().connect_once([this]() { this->quit(); }, 50);
|
||||
});
|
||||
|
||||
REQUIRE(received.size() <= MAX_QUEUED_EVENTS);
|
||||
REQUIRE_FALSE(received.empty());
|
||||
REQUIRE(received.back() == NUM_EVENTS);
|
||||
REQUIRE(received.front() == NUM_EVENTS - static_cast<int>(received.size()) + 1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#if __has_include(<catch2/catch_test_macros.hpp>)
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#else
|
||||
#include <catch2/catch.hpp>
|
||||
#endif
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
|
||||
std::mutex reap_mtx;
|
||||
std::list<pid_t> reap;
|
||||
|
||||
extern "C" int waybar_test_execl(const char* path, const char* arg, ...);
|
||||
extern "C" int waybar_test_execlp(const char* file, const char* arg, ...);
|
||||
|
||||
#define execl waybar_test_execl
|
||||
#define execlp waybar_test_execlp
|
||||
#include "util/command.hpp"
|
||||
#undef execl
|
||||
#undef execlp
|
||||
|
||||
extern "C" int waybar_test_execl(const char* path, const char* arg, ...) {
|
||||
(void)path;
|
||||
(void)arg;
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern "C" int waybar_test_execlp(const char* file, const char* arg, ...) {
|
||||
(void)file;
|
||||
(void)arg;
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
TEST_CASE("command::execNoRead returns 127 when shell exec fails", "[util][command]") {
|
||||
const auto result = waybar::util::command::execNoRead("echo should-not-run");
|
||||
REQUIRE(result.exit_code == waybar::util::command::kExecFailureExitCode);
|
||||
REQUIRE(result.out.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("command::forkExec child exits 127 when shell exec fails", "[util][command]") {
|
||||
const auto pid = waybar::util::command::forkExec("echo should-not-run", "test-output");
|
||||
REQUIRE(pid > 0);
|
||||
|
||||
int status = -1;
|
||||
REQUIRE(waitpid(pid, &status, 0) == pid);
|
||||
REQUIRE(WIFEXITED(status));
|
||||
REQUIRE(WEXITSTATUS(status) == waybar::util::command::kExecFailureExitCode);
|
||||
|
||||
std::scoped_lock<std::mutex> lock(reap_mtx);
|
||||
reap.remove(pid);
|
||||
}
|
||||
@@ -13,6 +13,8 @@ test_src = files(
|
||||
'../../src/config.cpp',
|
||||
'JsonParser.cpp',
|
||||
'SafeSignal.cpp',
|
||||
'sleeper_thread.cpp',
|
||||
'command.cpp',
|
||||
'css_reload_helper.cpp',
|
||||
'../../src/util/css_reload_helper.cpp',
|
||||
)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#if __has_include(<catch2/catch_test_macros.hpp>)
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#else
|
||||
#include <catch2/catch.hpp>
|
||||
#endif
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
namespace waybar::util {
|
||||
SafeSignal<bool>& prepare_for_sleep() {
|
||||
static SafeSignal<bool> signal;
|
||||
return signal;
|
||||
}
|
||||
} // namespace waybar::util
|
||||
|
||||
namespace {
|
||||
int run_in_subprocess(int (*task)()) {
|
||||
const auto pid = fork();
|
||||
if (pid < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (pid == 0) {
|
||||
alarm(5);
|
||||
_exit(task());
|
||||
}
|
||||
|
||||
int status = -1;
|
||||
if (waitpid(pid, &status, 0) != pid) {
|
||||
return -1;
|
||||
}
|
||||
if (!WIFEXITED(status)) {
|
||||
return -1;
|
||||
}
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
int run_reassignment_regression() {
|
||||
waybar::util::SleeperThread thread;
|
||||
thread = [] { std::this_thread::sleep_for(std::chrono::milliseconds(10)); };
|
||||
thread = [] { std::this_thread::sleep_for(std::chrono::milliseconds(1)); };
|
||||
return 0;
|
||||
}
|
||||
|
||||
int run_control_flag_stress() {
|
||||
for (int i = 0; i < 200; ++i) {
|
||||
waybar::util::SleeperThread thread;
|
||||
thread = [&thread] { thread.sleep_for(std::chrono::milliseconds(1)); };
|
||||
|
||||
std::thread waker([&thread] {
|
||||
for (int j = 0; j < 100; ++j) {
|
||||
thread.wake_up();
|
||||
std::this_thread::yield();
|
||||
}
|
||||
});
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
thread.stop();
|
||||
waker.join();
|
||||
if (thread.isRunning()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("SleeperThread reassignment does not terminate process", "[util][sleeper_thread]") {
|
||||
REQUIRE(run_in_subprocess(run_reassignment_regression) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("SleeperThread control flags are stable under concurrent wake and stop",
|
||||
"[util][sleeper_thread]") {
|
||||
REQUIRE(run_in_subprocess(run_control_flag_stress) == 0);
|
||||
}
|
||||
Reference in New Issue
Block a user