Skip to content

Commit e10b0fe

Browse files
test: add screenshots
1 parent 09c5784 commit e10b0fe

8 files changed

Lines changed: 481 additions & 12 deletions

File tree

.github/workflows/ci.yml

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,14 @@ jobs:
5454
build-essential \
5555
cmake \
5656
${{ matrix.appindicator }} \
57+
dbus-x11 \
5758
libglib2.0-dev \
5859
libnotify-dev \
5960
ninja-build \
61+
xfce4-panel \
62+
openbox \
63+
imagemagick \
64+
gnome-screenshot \
6065
xvfb
6166
6267
- name: Setup Dependencies macOS
@@ -67,6 +72,7 @@ jobs:
6772
cmake \
6873
doxygen \
6974
graphviz \
75+
imagemagick \
7076
ninja \
7177
node
7278
@@ -81,6 +87,7 @@ jobs:
8187
mingw-w64-ucrt-x86_64-binutils
8288
mingw-w64-ucrt-x86_64-cmake
8389
mingw-w64-ucrt-x86_64-graphviz
90+
mingw-w64-ucrt-x86_64-imagemagick
8491
mingw-w64-ucrt-x86_64-ninja
8592
mingw-w64-ucrt-x86_64-nodejs
8693
mingw-w64-ucrt-x86_64-toolchain
@@ -132,10 +139,36 @@ jobs:
132139
run: |
133140
if [ "${{ runner.os }}" = "Linux" ]; then
134141
export DISPLAY=:1
135-
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
142+
Xvfb ${DISPLAY} -screen 0 1920x1080x24 &
143+
XVFB_PID=$!
144+
sleep 2
145+
dbus-run-session -- bash -c '
146+
openbox --config-file /etc/xdg/openbox/rc.xml &
147+
WM_PID=$!
148+
xfce4-panel --disable-wm-check &
149+
PANEL_PID=$!
150+
sleep 8
151+
./test_tray --gtest_color=yes --gtest_output=xml:test_results.xml
152+
status=$?
153+
kill ${PANEL_PID} ${WM_PID} >/dev/null 2>&1 || true
154+
exit ${status}
155+
'
156+
status=$?
157+
kill ${XVFB_PID} >/dev/null 2>&1 || true
158+
exit ${status}
159+
else
160+
./test_tray --gtest_color=yes --gtest_output=xml:test_results.xml
136161
fi
137162
138-
./test_tray --gtest_color=yes --gtest_output=xml:test_results.xml
163+
- name: Upload screenshots
164+
if: >-
165+
always() &&
166+
(steps.test.outcome == 'success' || steps.test.outcome == 'failure')
167+
uses: actions/upload-artifact@v6
168+
with:
169+
name: tray-screenshots-${{ runner.os }}${{ matrix.appindicator && format('-{0}', matrix.appindicator) || '' }}
170+
path: build/tests/screenshots
171+
if-no-files-found: error
139172

140173
- name: Generate gcov report
141174
id: test_report

src/tray_darwin.m

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ - (IBAction)menuCallback:(id)sender {
3939
static NSApplication *app;
4040
static NSStatusBar *statusBar;
4141
static NSStatusItem *statusItem;
42+
static int loopResult = 0;
4243

4344
#define QUIT_EVENT_SUBTYPE 0x0DED ///< NSEvent subtype used to signal exit.
4445

@@ -67,6 +68,7 @@ - (IBAction)menuCallback:(id)sender {
6768
}
6869

6970
int tray_init(struct tray *tray) {
71+
loopResult = 0;
7072
AppDelegate *delegate = [[AppDelegate alloc] init];
7173
app = [NSApplication sharedApplication];
7274
[app setDelegate:delegate];
@@ -85,12 +87,13 @@ int tray_loop(int blocking) {
8587
dequeue:TRUE];
8688
if (event) {
8789
if (event.type == NSEventTypeApplicationDefined && event.subtype == QUIT_EVENT_SUBTYPE) {
88-
return -1;
90+
loopResult = -1;
91+
return loopResult;
8992
}
9093

9194
[app sendEvent:event];
9295
}
93-
return 0;
96+
return loopResult;
9497
}
9598

9699
void tray_update(struct tray *tray) {

src/tray_linux.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ int tray_init(struct tray *tray) {
6767
if (gtk_init_check(0, NULL) == FALSE) {
6868
return -1;
6969
}
70+
loop_result = 0;
7071
notify_init("tray-icon");
7172
indicator = app_indicator_new(TRAY_APPINDICATOR_ID, tray->icon, APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
7273
if (indicator == NULL || !IS_APP_INDICATOR(indicator)) {

tests/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@ if (WIN32)
1818
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # cmake-lint: disable=C0103
1919
endif ()
2020

21+
# extra libraries for tests
22+
if (APPLE)
23+
set(TEST_LIBS "-framework Cocoa")
24+
elseif (WIN32)
25+
set(TEST_LIBS gdi32 gdiplus)
26+
endif()
27+
2128
file(GLOB_RECURSE TEST_SOURCES
2229
${CMAKE_SOURCE_DIR}/tests/conftest.cpp
2330
${CMAKE_SOURCE_DIR}/tests/utils.cpp
31+
${CMAKE_SOURCE_DIR}/tests/screenshot_utils.cpp
2432
${CMAKE_SOURCE_DIR}/tests/test_*.cpp)
2533

2634
add_executable(${PROJECT_NAME}
@@ -29,6 +37,7 @@ add_executable(${PROJECT_NAME}
2937
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17)
3038
target_link_directories(${PROJECT_NAME} PRIVATE ${TRAY_EXTERNAL_DIRECTORIES})
3139
target_link_libraries(${PROJECT_NAME}
40+
${TEST_LIBS}
3241
${TRAY_EXTERNAL_LIBRARIES}
3342
gtest
3443
gtest_main) # if we use this we don't need our own main function

tests/conftest.cpp

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
// standard includes
22
#include <array>
33
#include <filesystem>
4+
#include <mutex>
45

56
// lib includes
67
#include <gtest/gtest.h>
78

89
// test includes
10+
#include "tests/screenshot_utils.h"
911
#include "tests/utils.h"
1012

1113
// Undefine the original TEST macro
@@ -34,7 +36,8 @@ class BaseTest: public ::testing::Test {
3436
BaseTest():
3537
sbuf {nullptr},
3638
pipe_stdout {nullptr},
37-
pipe_stderr {nullptr} {
39+
pipe_stderr {nullptr},
40+
screenshotsReady {false} {
3841
// intentionally empty
3942
}
4043

@@ -59,6 +62,8 @@ class BaseTest: public ::testing::Test {
5962
testBinaryDir = std::filesystem::current_path();
6063
}
6164

65+
initializeScreenshotsOnce();
66+
6267
sbuf = std::cout.rdbuf(); // save cout buffer (std::cout)
6368
std::cout.rdbuf(cout_buffer.rdbuf()); // redirect cout to buffer (std::cout)
6469
}
@@ -102,6 +107,19 @@ class BaseTest: public ::testing::Test {
102107
std::streambuf *sbuf;
103108
FILE *pipe_stdout;
104109
FILE *pipe_stderr;
110+
bool screenshotsReady;
111+
112+
void initializeScreenshotsOnce() {
113+
static std::once_flag screenshotInitFlag;
114+
std::call_once(screenshotInitFlag, [this]() {
115+
auto root = testBinaryDir;
116+
if (!root.empty()) {
117+
std::error_code ec;
118+
std::filesystem::remove_all(root / "screenshots", ec);
119+
}
120+
screenshot::initialize(root);
121+
});
122+
}
105123

106124
int exec(const char *cmd) {
107125
std::array<char, 128> buffer {};
@@ -124,6 +142,41 @@ class BaseTest: public ::testing::Test {
124142
}
125143
return returnCode;
126144
}
145+
146+
bool ensureScreenshotReady() {
147+
if (screenshotsReady) {
148+
return true;
149+
}
150+
std::string reason;
151+
if (!screenshot::is_available(&reason)) {
152+
screenshotUnavailableReason = reason;
153+
return false;
154+
}
155+
auto root = screenshot::output_root();
156+
if (root.empty()) {
157+
screenshotUnavailableReason = "Screenshot output directory not initialized";
158+
return false;
159+
}
160+
screenshotsReady = true;
161+
return true;
162+
}
163+
164+
bool captureScreenshot(const std::string &name) {
165+
if (!screenshotsReady) {
166+
return false;
167+
}
168+
bool ok = screenshot::capture(name);
169+
if (!ok) {
170+
std::cout << "Failed to capture screenshot: " << name << std::endl;
171+
}
172+
return ok;
173+
}
174+
175+
std::filesystem::path screenshotsRoot() const {
176+
return screenshot::output_root();
177+
}
178+
179+
std::string screenshotUnavailableReason;
127180
};
128181

129182
class LinuxTest: public BaseTest {

0 commit comments

Comments
 (0)