Skip to content

Commit 5d082c1

Browse files
test: add screenshots
1 parent 09c5784 commit 5d082c1

File tree

13 files changed

+1065
-24
lines changed

13 files changed

+1065
-24
lines changed

.github/copilot-instructions.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
On Windows we use msys2 and ucrt64 to compile.
2+
You need to prefix commands with `C:\msys64\msys2_shell.cmd -defterm -here -no-start -ucrt64 -c`.
3+
4+
Prefix build directories with `cmake-build-`.
5+
6+
The test executable is named `test_tray` and will be located inside the `tests` directory within
7+
the build directory.
8+
9+
The project uses gtest as a test framework.
10+
11+
Always follow the style guidelines defined in .clang-format for c/c++ code.

.github/workflows/ci.yml

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,18 @@ jobs:
5454
build-essential \
5555
cmake \
5656
${{ matrix.appindicator }} \
57+
imagemagick \
5758
libglib2.0-dev \
5859
libnotify-dev \
5960
ninja-build \
6061
xvfb
6162
63+
- name: Setup virtual desktop
64+
if: runner.os == 'Linux'
65+
uses: LizardByte/actions/actions/setup_virtual_desktop@feat/actions/linux-display # todo: pin version
66+
with:
67+
environment: mate
68+
6269
- name: Setup Dependencies macOS
6370
if: runner.os == 'macOS'
6471
run: |
@@ -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
@@ -103,7 +112,7 @@ jobs:
103112
104113
# step output
105114
echo "python-path=${python_path}"
106-
echo "python-path=${python_path}" >> $GITHUB_OUTPUT
115+
echo "python-path=${python_path}" >> "${GITHUB_OUTPUT}"
107116
108117
- name: Build
109118
run: |
@@ -124,18 +133,57 @@ jobs:
124133
-S .
125134
ninja -C build
126135
136+
- name: Init tray icon (Windows)
137+
if: runner.os == 'Windows'
138+
working-directory: build/tests
139+
run: ./test_tray --gtest_color=yes --gtest_filter=TrayTest.TestTrayInit
140+
141+
- name: Configure Windows
142+
if: runner.os == 'Windows'
143+
shell: pwsh
144+
run: |
145+
echo "::group::Enable all tray icons"
146+
Invoke-WebRequest `
147+
-Uri "https://raw.githubusercontent.com/paulmann/windows-show-all-tray-icons/main/Enable-AllTrayIcons.ps1" `
148+
-OutFile "Enable-AllTrayIcons.ps1"
149+
.\Enable-AllTrayIcons.ps1 -Action Enable -Force # Enable with comprehensive method (resets ALL icon settings)
150+
echo "::endgroup::"
151+
152+
echo "::group::Disable Do Not Disturb"
153+
Add-Type -AssemblyName System.Windows.Forms
154+
Start-Process "ms-settings:notifications"
155+
Start-Sleep -Seconds 2
156+
[System.Windows.Forms.SendKeys]::SendWait("{TAB}")
157+
[System.Windows.Forms.SendKeys]::SendWait("{TAB}")
158+
[System.Windows.Forms.SendKeys]::SendWait(" ")
159+
echo "::endgroup::"
160+
161+
echo "::group::Minimize all windows"
162+
$shell = New-Object -ComObject Shell.Application
163+
$shell.MinimizeAll()
164+
echo "::endgroup::"
165+
166+
echo "::group::Set Date - Hack for Quiet Time"
167+
$newDate = (Get-Date).AddHours(2)
168+
Set-Date -Date $newDate
169+
echo "::endgroup::"
170+
127171
- name: Run tests
128172
id: test
129173
# TODO: tests randomly hang on Linux, https://github.com/LizardByte/tray/issues/45
130-
timeout-minutes: 1
174+
timeout-minutes: 3
131175
working-directory: build/tests
132-
run: |
133-
if [ "${{ runner.os }}" = "Linux" ]; then
134-
export DISPLAY=:1
135-
Xvfb ${DISPLAY} -screen 0 1024x768x24 &
136-
fi
176+
run: ./test_tray --gtest_color=yes --gtest_output=xml:test_results.xml
137177

138-
./test_tray --gtest_color=yes --gtest_output=xml:test_results.xml
178+
- name: Upload screenshots
179+
if: >-
180+
always() &&
181+
(steps.test.outcome == 'success' || steps.test.outcome == 'failure')
182+
uses: actions/upload-artifact@v6
183+
with:
184+
name: tray-screenshots-${{ runner.os }}${{ matrix.appindicator && format('-{0}', matrix.appindicator) || '' }}
185+
path: build/tests/screenshots
186+
if-no-files-found: error
139187

140188
- name: Generate gcov report
141189
id: test_report
@@ -164,7 +212,7 @@ jobs:
164212
if [ -n "${{ matrix.appindicator }}" ]; then
165213
flags="${flags},${{ matrix.appindicator }}"
166214
fi
167-
echo "flags=${flags}" >> $GITHUB_OUTPUT
215+
echo "flags=${flags}" >> "${GITHUB_OUTPUT}"
168216
169217
- name: Upload coverage
170218
# any except canceled or skipped
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
---
2+
name: Publish Screenshots
3+
4+
on:
5+
workflow_run:
6+
workflows: ["CI"]
7+
types:
8+
- completed
9+
10+
permissions:
11+
contents: write
12+
pull-requests: write
13+
14+
jobs:
15+
publish:
16+
if: github.event.workflow_run.conclusion == 'success'
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Download Artifacts
20+
uses: actions/download-artifact@v7
21+
with:
22+
path: screenshots
23+
pattern: tray-screenshots-*
24+
run-id: ${{ github.event.workflow_run.id }}
25+
26+
- name: Debug screenshots
27+
run: ls -R screenshots
28+
29+
- name: Determine Branch and Path
30+
id: determine
31+
env:
32+
PULL_REQUEST_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }}
33+
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
34+
run: |
35+
if [ -n "${PULL_REQUEST_NUMBER}" ]; then
36+
PR_NUMBER=${PULL_REQUEST_NUMBER}
37+
BRANCH_PATH="PR-${PULL_REQUEST_NUMBER}"
38+
is_pr=true
39+
else
40+
BRANCH_NAME=$(echo "${HEAD_BRANCH}" | sed 's/\//-/g')
41+
BRANCH_PATH="${BRANCH_NAME}"
42+
is_pr=false
43+
fi
44+
45+
{
46+
echo "branch_path=${BRANCH_PATH}"
47+
echo "is_pr=${is_pr}"
48+
echo "pr_number=${PR_NUMBER}"
49+
} >> "${GITHUB_OUTPUT}"
50+
51+
# debug outputs
52+
cat "${GITHUB_OUTPUT}"
53+
54+
- name: Checkout Screenshots Branch
55+
uses: actions/checkout@v6
56+
with:
57+
ref: screenshots
58+
path: screenshots-repo

docs/Doxyfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ PROJECT_NAME = tray
3131
DOT_GRAPH_MAX_NODES = 50
3232
IMAGE_PATH = ../docs/images
3333
INCLUDE_PATH =
34+
PREDEFINED += TRAY_WINAPI
3435

3536
# files and directories to process
3637
USE_MDFILE_AS_MAINPAGE = ../README.md

src/tray.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
#ifndef TRAY_H
66
#define TRAY_H
77

8+
#if defined(TRAY_WINAPI)
9+
#include <Windows.h>
10+
#endif
11+
812
#ifdef __cplusplus
913
extern "C" {
1014
#endif
@@ -64,11 +68,24 @@ extern "C" {
6468
*/
6569
void tray_update(struct tray *tray);
6670

71+
/**
72+
* @brief Force show the tray menu (for testing purposes).
73+
*/
74+
void tray_show_menu(void);
75+
6776
/**
6877
* @brief Terminate UI loop.
6978
*/
7079
void tray_exit(void);
7180

81+
#if defined(TRAY_WINAPI)
82+
/**
83+
* @brief Get the tray window handle.
84+
* @return The window handle.
85+
*/
86+
HWND tray_get_hwnd(void);
87+
#endif
88+
7289
#ifdef __cplusplus
7390
} // extern "C"
7491
#endif

src/tray_darwin.m

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,26 @@ - (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

46+
static void drain_quit_events(void) {
47+
while (YES) {
48+
NSEvent *event = [app nextEventMatchingMask:ULONG_MAX
49+
untilDate:[NSDate distantPast]
50+
inMode:[NSString stringWithUTF8String:"kCFRunLoopDefaultMode"]
51+
dequeue:TRUE];
52+
if (event == nil) {
53+
break;
54+
}
55+
if (event.type == NSEventTypeApplicationDefined && event.subtype == QUIT_EVENT_SUBTYPE) {
56+
continue;
57+
}
58+
[app sendEvent:event];
59+
}
60+
}
61+
4562
static NSMenu *_tray_menu(struct tray_menu *m) {
4663
NSMenu *menu = [[NSMenu alloc] init];
4764
[menu setAutoenablesItems:FALSE];
@@ -67,13 +84,15 @@ - (IBAction)menuCallback:(id)sender {
6784
}
6885

6986
int tray_init(struct tray *tray) {
87+
loopResult = 0;
7088
AppDelegate *delegate = [[AppDelegate alloc] init];
7189
app = [NSApplication sharedApplication];
7290
[app setDelegate:delegate];
7391
statusBar = [NSStatusBar systemStatusBar];
7492
statusItem = [statusBar statusItemWithLength:NSVariableStatusItemLength];
7593
tray_update(tray);
7694
[app activateIgnoringOtherApps:TRUE];
95+
drain_quit_events();
7796
return 0;
7897
}
7998

@@ -85,12 +104,13 @@ int tray_loop(int blocking) {
85104
dequeue:TRUE];
86105
if (event) {
87106
if (event.type == NSEventTypeApplicationDefined && event.subtype == QUIT_EVENT_SUBTYPE) {
88-
return -1;
107+
loopResult = -1;
108+
return loopResult;
89109
}
90110

91111
[app sendEvent:event];
92112
}
93-
return 0;
113+
return loopResult;
94114
}
95115

96116
void tray_update(struct tray *tray) {
@@ -101,6 +121,10 @@ void tray_update(struct tray *tray) {
101121
[statusItem setMenu:_tray_menu(tray->menu)];
102122
}
103123

124+
void tray_show_menu(void) {
125+
[statusItem popUpStatusItemMenu:statusItem.menu];
126+
}
127+
104128
void tray_exit(void) {
105129
NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
106130
location:NSMakePoint(0, 0)

0 commit comments

Comments
 (0)