Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
path = third-party/doxyconfig
url = https://github.com/LizardByte/doxyconfig.git
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"]
path = third-party/lizardbyte-common
url = https://github.com/LizardByte/lizardbyte-common.git
Expand Down
7 changes: 7 additions & 0 deletions .run/docs.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="docs" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="&quot;file:\\$CMakeCurrentBuildDir$\build\html\index.html&quot;" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="libdisplaydevice" 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>
31 changes: 22 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
# Overview
<div align="center">
<img
src="https://raw.githubusercontent.com/LizardByte/.github/refs/heads/master/branding/logos/logo.svg"
alt="LizardByte icon"
width="256"
/>
<h1 align="center">libdisplaydevice</h1>
<h4 align="center">C++ library to modify display devices.</h4>
</div>

<div align="center">
<a href="https://github.com/LizardByte/libdisplaydevice"><img src="https://img.shields.io/github/stars/lizardbyte/libdisplaydevice.svg?logo=github&style=for-the-badge" alt="GitHub stars"></a>
<a href="https://github.com/LizardByte/libdisplaydevice/actions/workflows/ci.yml?query=branch%3Amaster"><img src="https://img.shields.io/github/actions/workflow/status/lizardbyte/libdisplaydevice/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/libdisplaydevice"><img src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fapp.lizardbyte.dev%2Fdashboard%2Fshields%2Fcodecov%2Flibdisplaydevice.json&style=for-the-badge&logo=codecov" alt="Codecov"></a>
<a href="https://sonarcloud.io/project/overview?id=LizardByte_libdisplaydevice"><img src="https://img.shields.io/sonar/quality_gate/LizardByte_libdisplaydevice.svg?server=https%3A%2F%2Fsonarcloud.io&style=for-the-badge&logo=sonarqubecloud&label=sonarcloud" alt="SonarCloud"></a>
</div>

[![GitHub Workflow Status (CI)](https://img.shields.io/github/actions/workflow/status/lizardbyte/libdisplaydevice/ci.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/libdisplaydevice/actions/workflows/ci.yml?query=branch%3Amaster)
[![Codecov](https://img.shields.io/codecov/c/gh/LizardByte/libdisplaydevice?token=goyvmDl6J5&style=for-the-badge&logo=codecov&label=codecov)](https://codecov.io/gh/LizardByte/libdisplaydevice)
[![GitHub stars](https://img.shields.io/github/stars/lizardbyte/libdisplaydevice.svg?logo=github&style=for-the-badge)](https://github.com/LizardByte/libdisplaydevice)
# Overview

## About
## ℹ️ About

LizardByte has the full documentation hosted on [Read the Docs](https://libdisplaydevice.readthedocs.io/).

libdisplaydevice is a WIP library that provides a common interface for interacting with display devices.
libdisplaydevice is a library that provides a common interface for interacting with display devices.
It is intended to be used by applications that need to interact with displays, such as screen capture software,
remote desktop software, and video players.

Initial support is planned for Windows, but could be expanded to other platforms in the future.
Windows and macOS are currently supported. Support for Linux is planned for the future.

## Build
## 🛠️ Build

### Clone

Expand Down Expand Up @@ -76,7 +89,7 @@ ninja -C build
./build/tests/test_libdisplaydevice
```

## Support
## Support

Our support methods are listed in our [LizardByte Docs](https://lizardbyte.readthedocs.io/latest/about/support.html).

Expand Down
9 changes: 8 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@
set(INSTALL_GTEST OFF)
set(INSTALL_GMOCK OFF)
include(GoogleTest)
add_subdirectory("${PROJECT_SOURCE_DIR}/third-party/googletest" "third-party/googletest")
add_subdirectory("${PROJECT_SOURCE_DIR}/third-party/lizardbyte-common/third-party/googletest" "third-party/googletest")

set(LIZARDBYTE_COMMON_BUILD_TEST_SUPPORT ON CACHE BOOL "Build lizardbyte-common GoogleTest support helpers" FORCE)
if(NOT TARGET lizardbyte::common)
add_subdirectory("${PROJECT_SOURCE_DIR}/third-party/lizardbyte-common"
"third-party/lizardbyte-common")
elseif(NOT TARGET lizardbyte::test_support)
message(FATAL_ERROR "libdisplaydevice tests require lizardbyte::test_support")
endif()
if (WIN32)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "Always use msvcrt.dll" FORCE) # cmake-lint: disable=C0103
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ include(Boost_DD)
target_link_libraries(${MODULE}
PUBLIC
Boost::preprocessor
lizardbyte::test_support

PRIVATE
gtest
libdisplaydevice::common
)
90 changes: 12 additions & 78 deletions tests/fixtures/fixtures.cpp
Original file line number Diff line number Diff line change
@@ -1,102 +1,36 @@
// header include
#include "fixtures/fixtures.h"

// system includes
#include <regex>
// lib includes
#include <lizardbyte/common/env.h>

void BaseTest::SetUp() {
if (const auto skip_reason {skipTest()}; !skip_reason.empty()) {
m_test_skipped_at_setup = true;
GTEST_SKIP() << skip_reason;
}

if (isOutputSuppressed()) {
// See https://stackoverflow.com/a/58369622/11214013
m_sbuf = std::cout.rdbuf(); // save cout buffer (std::cout)
std::cout.rdbuf(m_cout_buffer.rdbuf()); // redirect cout to buffer (std::cout)
m_test_skipped_at_setup = false;
::lizardbyte::common::testing::BaseTest::SetUp();
m_test_skipped_at_setup = ::testing::Test::IsSkipped();
if (m_test_skipped_at_setup) {
return;
}

// Set the default log level, before the test starts. Will default to verbose in case nothing was specified.
display_device::Logger::get().setLogLevel(getDefaultLogLevel().value_or(display_device::Logger::LogLevel::verbose));
}

void BaseTest::TearDown() {
if (m_test_skipped_at_setup) {
// We are not using the IsSkipped() state here. Here we are skipping
// teardown, because we have skipped the setup entirely, but during normal
// skips we still want to do teardown.
return;
if (!m_test_skipped_at_setup) {
// reset the callback to avoid potential leaks
display_device::Logger::get().setCustomCallback(nullptr);
}

// reset the callback to avoid potential leaks
display_device::Logger::get().setCustomCallback(nullptr);

// Restore cout buffer and print the suppressed output out in case we have failed :/
if (isOutputSuppressed()) {
std::cout.rdbuf(m_sbuf);
m_sbuf = nullptr;

const auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
if (test_info && test_info->result()->Failed()) {
std::cout << std::endl
<< "Test failed: " << test_info->name() << std::endl
<< std::endl
<< "Captured cout:" << std::endl
<< m_cout_buffer.str() << std::endl;
}
}
}

const std::vector<std::string> &BaseTest::getArgs() const {
static const auto args {::testing::internal::GetArgvs()};
return args;
}

std::optional<std::string> BaseTest::getArgWithMatchingPattern(const std::string &pattern, bool remove_match) const {
if (const auto &args {getArgs()}; !args.empty()) {
const std::regex re_pattern {pattern};

// We are skipping the first arg which is always binary name/path.
for (auto it {std::next(std::begin(args))}; it != std::end(args); ++it) {
if (std::smatch match; std::regex_search(*it, match, re_pattern)) {
return remove_match ? std::regex_replace(*it, re_pattern, "") : *it;
}
}
}

return std::nullopt;
}

bool BaseTest::isOutputSuppressed() const {
return true;
}

bool BaseTest::isSystemTest() const {
return false;
}

std::string BaseTest::skipTest() const {
if (isSystemTest()) {
const static bool is_system_test_skippable {
[]() {
const auto value {getEnv("SKIP_SYSTEM_TESTS")};
return value == "1";
}()
};

if (is_system_test_skippable) {
return "Skipping, this system test is disabled via SKIP_SYSTEM_TESTS=1 env.";
}
}
return {};
::lizardbyte::common::testing::BaseTest::TearDown();
}

std::optional<display_device::Logger::LogLevel> BaseTest::getDefaultLogLevel() const {
const static auto default_log_level {
[]() -> std::optional<display_device::Logger::LogLevel> {
using enum display_device::Logger::LogLevel;

const auto value {getEnv("LOG_LEVEL")};
const auto value {lizardbyte::common::get_env("LOG_LEVEL")};
if (value == "verbose") {
return verbose;
}
Expand Down
47 changes: 5 additions & 42 deletions tests/fixtures/include/fixtures/fixtures.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
#include <boost/preprocessor/seq/fold_left.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>
#include <filesystem>
#include <gtest/gtest.h>

#define LIZARDBYTE_COMMON_TESTING_KEEP_GTEST_TEST
#define LIZARDBYTE_COMMON_TESTING_NO_GLOBAL_ALIASES
#include <lizardbyte/common/testing.h>

// local includes
#include "display_device/logging.h"
Expand Down Expand Up @@ -39,61 +42,21 @@
*
* This class provides a base test fixture for all tests.
*/
class BaseTest: public ::testing::Test {
class BaseTest: public ::lizardbyte::common::testing::BaseTest {
protected:
~BaseTest() override = default;

void SetUp() override;

void TearDown() override;

/**
* @brief Get available command line arguments.
* @return Command line args from GTest.
*/
[[nodiscard]] virtual const std::vector<std::string> &getArgs() const;

/**
* @brief Get the command line argument that matches the pattern.
* @param pattern Pattern to look for.
* @param remove_match Specify if the matched pattern should be removed before returning argument.
* @return Matching command line argument or null optional if nothing matched.
*/
[[nodiscard]] virtual std::optional<std::string> getArgWithMatchingPattern(const std::string &pattern, bool remove_match) const;

/**
* @brief Check if the test output is to be redirected and printed out only if test fails.
* @return True if output is to be suppressed, false otherwise.
* @note It is useful for suppressing noise in automatic tests, but not so much in manual ones.
*/
[[nodiscard]] virtual bool isOutputSuppressed() const;

/**
* @brief Check if the test interacts/modifies with the system settings.
* @returns True if it does, false otherwise.
* @note By setting SKIP_SYSTEM_TESTS=1 env, these tests will be skipped (useful during development).
*/
[[nodiscard]] virtual bool isSystemTest() const;

/**
* @brief Skip the test by specifying the reason.
* @returns A non-empty string (reason) if test needs to be skipped, empty string otherwise.
*/
[[nodiscard]] virtual std::string skipTest() const;

/**
* @brief Get the default log level for the test base.
* @returns A log level set in the env OR null optional if fallback should be used (verbose).
* @note By setting LOG_LEVEL=<level> env you can change the level (e.g. LOG_LEVEL=error).
*/
[[nodiscard]] virtual std::optional<display_device::Logger::LogLevel> getDefaultLogLevel() const;

[[nodiscard]] std::stringstream &coutBuffer() {
return m_cout_buffer;
}

private:
std::stringstream m_cout_buffer; /**< Stores the cout in case the output is suppressed. */
std::streambuf *m_sbuf {nullptr}; /**< Stores the handle to the original cout stream. */
bool m_test_skipped_at_setup {false}; /**< Indicates whether the SetUp method was skipped. */
};
16 changes: 0 additions & 16 deletions tests/fixtures/include/fixtures/test_utils.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

// system includes
#include <optional>
#include <string>
#include <vector>

Expand All @@ -22,18 +21,3 @@ namespace ut_consts {
* @return True if string matches the regex, false otherwise.
*/
bool testRegex(const std::string &test_pattern, const std::string &regex_pattern);

/**
* @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);

/**
* @brief Get an environment variable.
* @param name Name of the environment variable.
* @return String value of the variable or an empty optional otherwise.
*/
std::optional<std::string> getEnv(const std::string &name);
18 changes: 0 additions & 18 deletions tests/fixtures/test_utils.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// header include
#include "fixtures/test_utils.h"

// system includes
#include <cstdlib>

// system includes
#include <cstdint>
#include <iostream>
Expand Down Expand Up @@ -51,18 +48,3 @@ bool testRegex(const std::string &input, const std::string &pattern) {
}
return true;
}

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
}

std::optional<std::string> getEnv(const std::string &name) {
if (const auto value {std::getenv(name.c_str())}; value) {
return std::string {value};
}
return std::nullopt;
}
1 change: 0 additions & 1 deletion third-party/googletest
Submodule googletest deleted from f8d7d7
Loading