mirror of
https://github.com/LizardByte/tray.git
synced 2026-10-23 03:27:02 +09:00
chore: use lizardbyte-common in c++ (#158)
This commit is contained in:
1 parent
27b67b7abd
commit
f449b4a909
12 files changed
+115
-198
No files matched your search
@@ -2,10 +2,6 @@
|
|||||||
path = third-party/doxyconfig
|
path = third-party/doxyconfig
|
||||||
url = https://github.com/LizardByte/doxyconfig.git
|
url = https://github.com/LizardByte/doxyconfig.git
|
||||||
branch = master
|
branch = master
|
||||||
[submodule "third-party/googletest"]
|
|
||||||
path = third-party/googletest
|
|
||||||
url = https://github.com/google/googletest.git
|
|
||||||
branch = v1.14.x
|
|
||||||
[submodule "third-party/lizardbyte-common"]
|
[submodule "third-party/lizardbyte-common"]
|
||||||
path = third-party/lizardbyte-common
|
path = third-party/lizardbyte-common
|
||||||
url = https://github.com/LizardByte/lizardbyte-common.git
|
url = https://github.com/LizardByte/lizardbyte-common.git
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="docs" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS=""file:\\$CMakeCurrentBuildDir$\build\html\index.html"" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="tray" TARGET_NAME="docs" CONFIG_NAME="Release" RUN_PATH="$PROJECT_DIR$/../../../../../Windows/explorer.exe">
|
||||||
|
<method v="2">
|
||||||
|
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
@@ -1,10 +1,23 @@
|
|||||||
|
<div align="center">
|
||||||
|
<img
|
||||||
|
src="tray.svg"
|
||||||
|
alt="tray icon"
|
||||||
|
width="256"
|
||||||
|
/>
|
||||||
|
<h1 align="center">tray</h1>
|
||||||
|
<h4 align="center">Cross-platform implementation of a system tray icon with a popup menu and notifications.</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<a href="https://github.com/LizardByte/tray"><img src="https://img.shields.io/github/stars/lizardbyte/tray.svg?logo=github&style=for-the-badge" alt="GitHub stars"></a>
|
||||||
|
<a href="https://github.com/LizardByte/tray/actions/workflows/ci.yml?query=branch%3Amaster"><img src="https://img.shields.io/github/actions/workflow/status/lizardbyte/tray/ci.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge" alt="GitHub Workflow Status (CI)"></a>
|
||||||
|
<a href="https://codecov.io/gh/LizardByte/tray"><img src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fapp.lizardbyte.dev%2Fdashboard%2Fshields%2Fcodecov%2Ftray.json&style=for-the-badge&logo=codecov" alt="Codecov"></a>
|
||||||
|
<a href="https://sonarcloud.io/project/overview?id=LizardByte_tray"><img src="https://img.shields.io/sonar/quality_gate/LizardByte_tray.svg?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonarqubecloud&label=sonarcloud" alt="SonarCloud"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
[](https://github.com/LizardByte/tray/actions/workflows/ci.yml?query=branch%3Amaster)
|
## ℹ️ About
|
||||||
[](https://codecov.io/gh/LizardByte/tray)
|
|
||||||
[](https://github.com/LizardByte/tray)
|
|
||||||
|
|
||||||
## About
|
|
||||||
|
|
||||||
Cross-platform, super tiny C99 implementation of a system tray icon with a popup menu and notifications.
|
Cross-platform, super tiny C99 implementation of a system tray icon with a popup menu and notifications.
|
||||||
|
|
||||||
@@ -20,23 +33,29 @@ This fork adds the following features:
|
|||||||
- refactored code, e.g., moved source code into the `src` directory
|
- refactored code, e.g., moved source code into the `src` directory
|
||||||
- doxygen documentation and readthedocs configuration
|
- doxygen documentation and readthedocs configuration
|
||||||
|
|
||||||
## Screenshots
|
## 🖼️ Screenshots
|
||||||
|
|
||||||
<div class="tabbed">
|
<div class="tabbed">
|
||||||
|
<ul>
|
||||||
- <b class="tab-title">Linux</b>
|
<li><b class="tab-title">Linux</b><br>
|
||||||
- <b class="tab-title">macOS</b>
|

|
||||||
- <b class="tab-title">Windows</b>
|
</li>
|
||||||
|
<li><b class="tab-title">macOS</b><br>
|
||||||
|

|
||||||
|
</li>
|
||||||
|
<li><b class="tab-title">Windows</b><br>
|
||||||
|

|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Supported platforms
|
## 🖥️ Supported platforms
|
||||||
|
|
||||||
* Linux/Qt (Qt5 or Qt6 Widgets)
|
* Linux/Qt (Qt5 or Qt6 Widgets)
|
||||||
* Windows XP or newer (shellapi.h)
|
* Windows XP or newer (shellapi.h)
|
||||||
* MacOS (Cocoa/AppKit)
|
* MacOS (Cocoa/AppKit)
|
||||||
|
|
||||||
## Prerequisites
|
## 📋 Prerequisites
|
||||||
|
|
||||||
* CMake
|
* CMake
|
||||||
* [Ninja](https://ninja-build.org/), to have the same build commands on all platforms.
|
* [Ninja](https://ninja-build.org/), to have the same build commands on all platforms.
|
||||||
@@ -76,7 +95,7 @@ Install either Qt6 _or_ Qt5 as well as libnotify development packages. The Linux
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Building
|
## 🛠️ Building
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
@@ -84,7 +103,7 @@ cmake -G Ninja -B build -S .
|
|||||||
ninja -C build
|
ninja -C build
|
||||||
```
|
```
|
||||||
|
|
||||||
## Python Tooling
|
## ⚙️ Python Tooling
|
||||||
|
|
||||||
Install [uv](https://docs.astral.sh/uv/) and initialize the shared tooling submodule:
|
Install [uv](https://docs.astral.sh/uv/) and initialize the shared tooling submodule:
|
||||||
|
|
||||||
@@ -94,7 +113,7 @@ uv run --project third-party/lizardbyte-common --locked --only-group lint-c \
|
|||||||
python third-party/lizardbyte-common/scripts/update_clang_format.py
|
python third-party/lizardbyte-common/scripts/update_clang_format.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Demo
|
## ▶️ Demo
|
||||||
|
|
||||||
Execute the `tray_example` application:
|
Execute the `tray_example` application:
|
||||||
|
|
||||||
@@ -102,7 +121,7 @@ Execute the `tray_example` application:
|
|||||||
./build/tray_example
|
./build/tray_example
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tests
|
## ✅ Tests
|
||||||
|
|
||||||
Execute the `tests` application:
|
Execute the `tests` application:
|
||||||
|
|
||||||
@@ -110,7 +129,7 @@ Execute the `tests` application:
|
|||||||
./build/tests/test_tray
|
./build/tests/test_tray
|
||||||
```
|
```
|
||||||
|
|
||||||
## API
|
## 📚 API
|
||||||
|
|
||||||
Tray structure defines an icon and a menu.
|
Tray structure defines an icon and a menu.
|
||||||
Menu is a NULL-terminated array of items.
|
Menu is a NULL-terminated array of items.
|
||||||
@@ -145,7 +164,7 @@ All functions are meant to be called from the UI thread only.
|
|||||||
Menu arrays must be terminated with a NULL item, e.g. the last item in the
|
Menu arrays must be terminated with a NULL item, e.g. the last item in the
|
||||||
array must have text field set to NULL.
|
array must have text field set to NULL.
|
||||||
|
|
||||||
## License
|
## 📄 License
|
||||||
|
|
||||||
This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php),
|
This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php),
|
||||||
so feel free to integrate it in your commercial products.
|
so feel free to integrate it in your commercial products.
|
||||||
|
|||||||
+3
-1
@@ -24,7 +24,9 @@
|
|||||||
# project metadata
|
# project metadata
|
||||||
DOCSET_BUNDLE_ID = dev.lizardbyte.tray
|
DOCSET_BUNDLE_ID = dev.lizardbyte.tray
|
||||||
DOCSET_PUBLISHER_ID = dev.lizardbyte.tray.documentation
|
DOCSET_PUBLISHER_ID = dev.lizardbyte.tray.documentation
|
||||||
PROJECT_BRIEF = "Cross-platform, super tiny C99 implementation of a system tray icon with a popup menu and notifications."
|
PROJECT_BRIEF = "Cross-platform implementation of a system tray icon with a popup menu and notifications."
|
||||||
|
PROJECT_ICON = ../tray.svg
|
||||||
|
PROJECT_LOGO = ../tray.svg
|
||||||
PROJECT_NAME = tray
|
PROJECT_NAME = tray
|
||||||
|
|
||||||
# project specific settings
|
# project specific settings
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.13)
|
|||||||
project(test_tray)
|
project(test_tray)
|
||||||
|
|
||||||
# Add GoogleTest directory to the project
|
# Add GoogleTest directory to the project
|
||||||
set(GTEST_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../third-party/googletest")
|
set(GTEST_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../third-party/lizardbyte-common/third-party/googletest")
|
||||||
|
|
||||||
# For Windows: prevent overriding parent compiler/linker runtime settings.
|
# For Windows: prevent overriding parent compiler/linker runtime settings.
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
@@ -16,6 +16,13 @@ set(INSTALL_GMOCK OFF)
|
|||||||
add_subdirectory("${GTEST_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/googletest")
|
add_subdirectory("${GTEST_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/googletest")
|
||||||
include_directories("${GTEST_SOURCE_DIR}/googletest/include" "${GTEST_SOURCE_DIR}")
|
include_directories("${GTEST_SOURCE_DIR}/googletest/include" "${GTEST_SOURCE_DIR}")
|
||||||
|
|
||||||
|
set(LIZARDBYTE_COMMON_BUILD_TEST_SUPPORT ON CACHE BOOL "Build lizardbyte-common GoogleTest support helpers" FORCE)
|
||||||
|
if(NOT TARGET lizardbyte::common)
|
||||||
|
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../third-party/lizardbyte-common"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/lizardbyte-common")
|
||||||
|
elseif(NOT TARGET lizardbyte::test_support)
|
||||||
|
message(FATAL_ERROR "tray tests require lizardbyte::test_support")
|
||||||
|
endif()
|
||||||
# extra libraries for tests
|
# extra libraries for tests
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
set(TEST_LIBS "-framework Cocoa")
|
set(TEST_LIBS "-framework Cocoa")
|
||||||
@@ -25,7 +32,6 @@ endif()
|
|||||||
|
|
||||||
file(GLOB_RECURSE TEST_SOURCES
|
file(GLOB_RECURSE TEST_SOURCES
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/conftest.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/conftest.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp"
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/screenshot_utils.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/screenshot_utils.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/unit/test_*.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/unit/test_*.cpp"
|
||||||
)
|
)
|
||||||
@@ -40,6 +46,7 @@ target_include_directories(${PROJECT_NAME}
|
|||||||
target_link_libraries(${PROJECT_NAME}
|
target_link_libraries(${PROJECT_NAME}
|
||||||
${TEST_LIBS}
|
${TEST_LIBS}
|
||||||
tray::tray
|
tray::tray
|
||||||
|
lizardbyte::test_support
|
||||||
gtest
|
gtest
|
||||||
gtest_main # if we use this we don't need our own main function
|
gtest_main # if we use this we don't need our own main function
|
||||||
)
|
)
|
||||||
|
|||||||
+47
-134
@@ -1,15 +1,14 @@
|
|||||||
// standard includes
|
// standard includes
|
||||||
#include <array>
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
// lib includes
|
// lib includes
|
||||||
#include <gtest/gtest.h>
|
#define LIZARDBYTE_COMMON_TESTING_KEEP_GTEST_TEST
|
||||||
|
#define LIZARDBYTE_COMMON_TESTING_NO_GLOBAL_ALIASES
|
||||||
|
#include <lizardbyte/common/testing.h>
|
||||||
|
|
||||||
// test includes
|
// test includes
|
||||||
#include "tests/screenshot_utils.h"
|
#include "tests/screenshot_utils.h"
|
||||||
#include "tests/utils.h"
|
|
||||||
|
|
||||||
// Undefine the original TEST macro
|
// Undefine the original TEST macro
|
||||||
#undef TEST
|
#undef TEST
|
||||||
|
|
||||||
@@ -20,141 +19,56 @@
|
|||||||
/**
|
/**
|
||||||
* @brief Base class for tests.
|
* @brief Base class for tests.
|
||||||
*
|
*
|
||||||
* This class provides a base test fixture for all tests.
|
* This class provides a base test fixture for all tests and adds tray-specific helpers.
|
||||||
*
|
|
||||||
* ``cout``, ``stderr``, and ``stdout`` are redirected to a buffer, and the buffer is printed if the test fails.
|
|
||||||
*
|
|
||||||
* @todo Retain the color of the original output.
|
|
||||||
*/
|
*/
|
||||||
class BaseTest: public ::testing::Test {
|
class BaseTest: public ::lizardbyte::common::testing::BaseTest {
|
||||||
protected:
|
protected:
|
||||||
// https://stackoverflow.com/a/58369622/11214013
|
|
||||||
|
|
||||||
// we can possibly use some internal googletest functions to capture stdout and stderr, but I have not tested this
|
|
||||||
// https://stackoverflow.com/a/33186201/11214013
|
|
||||||
|
|
||||||
BaseTest() = default;
|
BaseTest() = default;
|
||||||
|
|
||||||
~BaseTest() override = default;
|
~BaseTest() override = default;
|
||||||
|
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
|
::lizardbyte::common::testing::BaseTest::SetUp();
|
||||||
|
|
||||||
// todo: only run this one time, instead of every time a test is run
|
// todo: only run this one time, instead of every time a test is run
|
||||||
// see: https://stackoverflow.com/questions/2435277/googletest-accessing-the-environment-from-a-test
|
// see: https://stackoverflow.com/questions/2435277/googletest-accessing-the-environment-from-a-test
|
||||||
// get command line args from the test executable
|
// get command line args from the test executable
|
||||||
testArgs = ::testing::internal::GetArgvs();
|
testArgs_ = getArgs();
|
||||||
|
|
||||||
// then get the directory of the test executable
|
// then get the directory of the test executable
|
||||||
// std::string path = ::testing::internal::GetArgvs()[0];
|
// std::string path = ::testing::internal::GetArgvs()[0];
|
||||||
testBinary = testArgs[0];
|
testBinary_ = testArgs_[0];
|
||||||
|
|
||||||
// get the directory of the test executable
|
// get the directory of the test executable
|
||||||
testBinaryDir = std::filesystem::path(testBinary).parent_path();
|
testBinaryDir_ = std::filesystem::path(testBinary_).parent_path();
|
||||||
|
|
||||||
// If testBinaryDir is empty or `.` then set it to the current directory
|
// If testBinaryDir is empty or `.` then set it to the current directory
|
||||||
// maybe some better options here: https://stackoverflow.com/questions/875249/how-to-get-current-directory
|
// maybe some better options here: https://stackoverflow.com/questions/875249/how-to-get-current-directory
|
||||||
if (testBinaryDir.empty() || testBinaryDir.string() == ".") {
|
if (testBinaryDir_.empty() || testBinaryDir_.string() == ".") {
|
||||||
testBinaryDir = std::filesystem::current_path();
|
testBinaryDir_ = std::filesystem::current_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeScreenshotsOnce();
|
initializeScreenshotsOnce();
|
||||||
|
|
||||||
sbuf = std::cout.rdbuf(); // save cout buffer (std::cout)
|
|
||||||
std::cout.rdbuf(cout_buffer.rdbuf()); // redirect cout to buffer (std::cout)
|
|
||||||
}
|
|
||||||
|
|
||||||
void TearDown() override {
|
|
||||||
std::cout.rdbuf(sbuf); // restore cout buffer
|
|
||||||
|
|
||||||
// get test info
|
|
||||||
const ::testing::TestInfo *const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
|
||||||
|
|
||||||
if (test_info->result()->Failed()) {
|
|
||||||
std::cout << std::endl
|
|
||||||
<< "Test failed: " << test_info->name() << std::endl
|
|
||||||
<< std::endl
|
|
||||||
<< "Captured cout:" << std::endl
|
|
||||||
<< cout_buffer.str() << std::endl
|
|
||||||
<< "Captured stdout:" << std::endl
|
|
||||||
<< stdout_buffer.str() << std::endl
|
|
||||||
<< "Captured stderr:" << std::endl
|
|
||||||
<< stderr_buffer.str() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
sbuf = nullptr; // clear sbuf
|
|
||||||
if (pipe_stdout) {
|
|
||||||
pclose(pipe_stdout);
|
|
||||||
pipe_stdout = nullptr;
|
|
||||||
}
|
|
||||||
if (pipe_stderr) {
|
|
||||||
pclose(pipe_stderr);
|
|
||||||
pipe_stderr = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// functions and variables
|
|
||||||
std::vector<std::string> testArgs; // CLI arguments used
|
|
||||||
std::filesystem::path testBinary; // full path of this binary
|
|
||||||
std::filesystem::path testBinaryDir; // full directory of this binary
|
|
||||||
std::stringstream cout_buffer; // declare cout_buffer
|
|
||||||
std::stringstream stdout_buffer; // declare stdout_buffer
|
|
||||||
std::stringstream stderr_buffer; // declare stderr_buffer
|
|
||||||
std::streambuf *sbuf {nullptr};
|
|
||||||
FILE *pipe_stdout {nullptr};
|
|
||||||
FILE *pipe_stderr {nullptr};
|
|
||||||
bool screenshotsReady {false};
|
|
||||||
|
|
||||||
void initializeScreenshotsOnce() {
|
|
||||||
static std::once_flag screenshotInitFlag;
|
|
||||||
std::call_once(screenshotInitFlag, [this]() {
|
|
||||||
auto root = testBinaryDir;
|
|
||||||
if (!root.empty()) {
|
|
||||||
std::error_code ec;
|
|
||||||
std::filesystem::remove_all(root / "screenshots", ec);
|
|
||||||
}
|
|
||||||
screenshot::initialize(root);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
int exec(const char *cmd) {
|
|
||||||
std::array<char, 128> buffer {};
|
|
||||||
pipe_stdout = popen((std::string(cmd) + " 2>&1").c_str(), "r");
|
|
||||||
pipe_stderr = popen((std::string(cmd) + " 2>&1").c_str(), "r");
|
|
||||||
if (!pipe_stdout || !pipe_stderr) {
|
|
||||||
throw std::runtime_error("popen() failed!");
|
|
||||||
}
|
|
||||||
while (fgets(buffer.data(), buffer.size(), pipe_stdout) != nullptr) {
|
|
||||||
stdout_buffer << buffer.data();
|
|
||||||
}
|
|
||||||
while (fgets(buffer.data(), buffer.size(), pipe_stderr) != nullptr) {
|
|
||||||
stderr_buffer << buffer.data();
|
|
||||||
}
|
|
||||||
int returnCode = pclose(pipe_stdout);
|
|
||||||
pipe_stdout = nullptr;
|
|
||||||
if (returnCode != 0) {
|
|
||||||
std::cout << "Error: " << stderr_buffer.str() << std::endl
|
|
||||||
<< "Return code: " << returnCode << std::endl;
|
|
||||||
}
|
|
||||||
return returnCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ensureScreenshotReady() {
|
bool ensureScreenshotReady() {
|
||||||
if (screenshotsReady) {
|
if (screenshotsReady_) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (std::string reason; !screenshot::is_available(&reason)) {
|
if (std::string reason; !screenshot::is_available(&reason)) {
|
||||||
screenshotUnavailableReason = reason;
|
screenshotUnavailableReason_ = reason;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (const auto root = screenshot::output_root(); root.empty()) {
|
if (const auto root = screenshot::output_root(); root.empty()) {
|
||||||
screenshotUnavailableReason = "Screenshot output directory not initialized";
|
screenshotUnavailableReason_ = "Screenshot output directory not initialized";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
screenshotsReady = true;
|
screenshotsReady_ = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool captureScreenshot(const std::string &name) {
|
bool captureScreenshot(const std::string &name) const {
|
||||||
if (!screenshotsReady) {
|
if (!screenshotsReady_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool ok = screenshot::capture(name);
|
bool ok = screenshot::capture(name);
|
||||||
@@ -168,35 +82,34 @@ protected:
|
|||||||
return screenshot::output_root();
|
return screenshot::output_root();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string screenshotUnavailableReason;
|
[[nodiscard]] const std::filesystem::path &testBinaryDir() const {
|
||||||
|
return testBinaryDir_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const std::string &screenshotUnavailableReason() const {
|
||||||
|
return screenshotUnavailableReason_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initializeScreenshotsOnce() const {
|
||||||
|
static std::once_flag screenshotInitFlag;
|
||||||
|
std::call_once(screenshotInitFlag, [this]() {
|
||||||
|
auto root = testBinaryDir_;
|
||||||
|
if (!root.empty()) {
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::remove_all(root / "screenshots", ec);
|
||||||
|
}
|
||||||
|
screenshot::initialize(root);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> testArgs_; // CLI arguments used
|
||||||
|
std::filesystem::path testBinary_; // full path of this binary
|
||||||
|
std::filesystem::path testBinaryDir_; // full directory of this binary
|
||||||
|
bool screenshotsReady_ {false};
|
||||||
|
std::string screenshotUnavailableReason_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LinuxTest: public BaseTest {
|
using LinuxTest = ::lizardbyte::common::testing::LinuxTest;
|
||||||
protected:
|
using MacOSTest = ::lizardbyte::common::testing::MacOSTest;
|
||||||
void SetUp() override {
|
using WindowsTest = ::lizardbyte::common::testing::WindowsTest;
|
||||||
#ifndef __linux__
|
|
||||||
GTEST_SKIP_("Skipping, this test is for Linux only.");
|
|
||||||
#endif
|
|
||||||
BaseTest::SetUp();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class MacOSTest: public BaseTest {
|
|
||||||
protected:
|
|
||||||
void SetUp() override {
|
|
||||||
#if !defined(__APPLE__) || !defined(__MACH__)
|
|
||||||
GTEST_SKIP_("Skipping, this test is for macOS only.");
|
|
||||||
#endif
|
|
||||||
BaseTest::SetUp();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class WindowsTest: public BaseTest {
|
|
||||||
protected:
|
|
||||||
void SetUp() override { // NOSONAR(cpp:S1185) - contains platform skip logic, not a trivial override
|
|
||||||
#ifndef _WIN32
|
|
||||||
GTEST_SKIP_("Skipping, this test is for Windows only.");
|
|
||||||
#endif
|
|
||||||
BaseTest::SetUp();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -99,7 +99,7 @@ protected: // NOSONAR(cpp:S3656) - TEST_F generates subclasses that need access
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dismisses the open menu from a background thread.
|
// Dismisses the open menu from a background thread.
|
||||||
void closeMenu() {
|
void closeMenu() const {
|
||||||
#if defined(TRAY_WINAPI)
|
#if defined(TRAY_WINAPI)
|
||||||
PostMessage(tray_get_hwnd(), WM_CANCELMODE, 0, 0);
|
PostMessage(tray_get_hwnd(), WM_CANCELMODE, 0, 0);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
@@ -115,7 +115,7 @@ protected: // NOSONAR(cpp:S3656) - TEST_F generates subclasses that need access
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Capture a screenshot while the tray menu is open, then dismiss and exit.
|
// Capture a screenshot while the tray menu is open, then dismiss and exit.
|
||||||
void captureMenuStateAndExit(const char *screenshotName) {
|
void captureMenuStateAndExit(const char *screenshotName) const {
|
||||||
std::atomic_bool exitRequested {false};
|
std::atomic_bool exitRequested {false};
|
||||||
std::thread capture_thread([this, screenshotName, &exitRequested]() { // NOSONAR(cpp:S6168) - std::jthread is unavailable on AppleClang 17/libc++ used in CI
|
std::thread capture_thread([this, screenshotName, &exitRequested]() { // NOSONAR(cpp:S6168) - std::jthread is unavailable on AppleClang 17/libc++ used in CI
|
||||||
EXPECT_TRUE(captureScreenshot(screenshotName));
|
EXPECT_TRUE(captureScreenshot(screenshotName));
|
||||||
@@ -172,14 +172,14 @@ protected: // NOSONAR(cpp:S3656) - TEST_F generates subclasses that need access
|
|||||||
|
|
||||||
// Skip tests if screenshot tooling is not available
|
// Skip tests if screenshot tooling is not available
|
||||||
if (!ensureScreenshotReady()) {
|
if (!ensureScreenshotReady()) {
|
||||||
GTEST_SKIP() << "Screenshot tooling missing: " << screenshotUnavailableReason;
|
GTEST_SKIP() << "Screenshot tooling missing: " << screenshotUnavailableReason();
|
||||||
}
|
}
|
||||||
if (screenshot::output_root().empty()) {
|
if (screenshot::output_root().empty()) {
|
||||||
GTEST_SKIP() << "Screenshot output path not initialized";
|
GTEST_SKIP() << "Screenshot output path not initialized";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure icon files exist in test binary directory
|
// Ensure icon files exist in test binary directory
|
||||||
std::filesystem::path projectRoot = testBinaryDir.parent_path();
|
std::filesystem::path projectRoot = testBinaryDir().parent_path();
|
||||||
auto ensureIconInTestDir = [&projectRoot, this](const char *iconName) {
|
auto ensureIconInTestDir = [&projectRoot, this](const char *iconName) {
|
||||||
std::filesystem::path iconSource;
|
std::filesystem::path iconSource;
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ protected: // NOSONAR(cpp:S3656) - TEST_F generates subclasses that need access
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!iconSource.empty()) {
|
if (!iconSource.empty()) {
|
||||||
std::filesystem::path iconDest = testBinaryDir / iconName;
|
std::filesystem::path iconDest = testBinaryDir() / iconName;
|
||||||
if (!std::filesystem::exists(iconDest)) {
|
if (!std::filesystem::exists(iconDest)) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
std::filesystem::copy_file(iconSource, iconDest, ec);
|
std::filesystem::copy_file(iconSource, iconDest, ec);
|
||||||
@@ -226,7 +226,7 @@ protected: // NOSONAR(cpp:S3656) - TEST_F generates subclasses that need access
|
|||||||
|
|
||||||
// Process pending events to allow tray icon to appear.
|
// Process pending events to allow tray icon to appear.
|
||||||
// Call this ONLY before screenshots to ensure the icon is visible.
|
// Call this ONLY before screenshots to ensure the icon is visible.
|
||||||
void WaitForTrayReady() {
|
void WaitForTrayReady() const {
|
||||||
#if defined(TRAY_QT)
|
#if defined(TRAY_QT)
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
tray_loop(0);
|
tray_loop(0);
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file utils.cpp
|
|
||||||
* @brief Utility functions
|
|
||||||
*/
|
|
||||||
// test includes
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set an environment variable.
|
|
||||||
* @param name Name of the environment variable
|
|
||||||
* @param value Value of the environment variable
|
|
||||||
* @return 0 on success, non-zero error code on failure
|
|
||||||
*/
|
|
||||||
int setEnv(const std::string &name, const std::string &value) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
return _putenv_s(name.c_str(), value.c_str());
|
|
||||||
#else
|
|
||||||
return setenv(name.c_str(), value.c_str(), 1);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file utils.h
|
|
||||||
* @brief Reusable functions for tests.
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// standard includes
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
int setEnv(const std::string &name, const std::string &value);
|
|
||||||
Vendored
-1
Submodule third-party/googletest deleted from f8d7d77c06.
Vendored
+1
-1
Submodule third-party/lizardbyte-common updated: 8d7dcc97d0...06cd442b80.
Reference in new issue
Block a user