Skip to content

Commit cb4260a

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

6 files changed

Lines changed: 458 additions & 9 deletions

File tree

.github/workflows/ci.yml

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,16 @@ 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+
trayer \
64+
ayatana-indicator-application \
65+
imagemagick \
66+
gnome-screenshot \
6067
xvfb
6168
6269
- name: Setup Dependencies macOS
@@ -67,6 +74,7 @@ jobs:
6774
cmake \
6875
doxygen \
6976
graphviz \
77+
imagemagick \
7078
ninja \
7179
node
7280
@@ -81,6 +89,7 @@ jobs:
8189
mingw-w64-ucrt-x86_64-binutils
8290
mingw-w64-ucrt-x86_64-cmake
8391
mingw-w64-ucrt-x86_64-graphviz
92+
mingw-w64-ucrt-x86_64-imagemagick
8493
mingw-w64-ucrt-x86_64-ninja
8594
mingw-w64-ucrt-x86_64-nodejs
8695
mingw-w64-ucrt-x86_64-toolchain
@@ -132,10 +141,36 @@ jobs:
132141
run: |
133142
if [ "${{ runner.os }}" = "Linux" ]; then
134143
export DISPLAY=:1
135-
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
144+
Xvfb ${DISPLAY} -screen 0 1920x1080x24 &
145+
XVFB_PID=$!
146+
sleep 2
147+
dbus-run-session -- bash -c '
148+
openbox --config-file /etc/xdg/openbox/rc.xml &
149+
WM_PID=$!
150+
xfce4-panel --disable-wm-check &
151+
PANEL_PID=$!
152+
sleep 8
153+
./test_tray --gtest_color=yes --gtest_output=xml:test_results.xml
154+
status=$?
155+
kill ${PANEL_PID} ${WM_PID} >/dev/null 2>&1 || true
156+
exit ${status}
157+
'
158+
status=$?
159+
kill ${XVFB_PID} >/dev/null 2>&1 || true
160+
exit ${status}
161+
else
162+
./test_tray --gtest_color=yes --gtest_output=xml:test_results.xml
136163
fi
137164
138-
./test_tray --gtest_color=yes --gtest_output=xml:test_results.xml
165+
- name: Upload screenshots
166+
if: >-
167+
always() &&
168+
(steps.test.outcome == 'success' || steps.test.outcome == 'failure')
169+
uses: actions/upload-artifact@v6
170+
with:
171+
name: tray-screenshots-${{ runner.os }}${{ matrix.appindicator && format('-{0}', matrix.appindicator) || '' }}
172+
path: build/tests/screenshots
173+
if-no-files-found: error
139174

140175
- name: Generate gcov report
141176
id: test_report

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,12 +1,14 @@
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
910
#include "tests/utils.h"
11+
#include "tests/screenshot_utils.h"
1012

1113
// Undefine the original TEST macro
1214
#undef TEST
@@ -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)