Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make system tests skipable via env #37

Merged
merged 3 commits into from
May 17, 2024
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
2 changes: 1 addition & 1 deletion cmake/Boost_DD.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ include_guard(GLOBAL)

find_package(Boost 1.85)
if(NOT Boost_FOUND)
message(STATUS "Boost v1.85.x package not found in system. Falling back to Fetch.")
message(STATUS "Boost v1.85.x package not found in the system. Falling back to FetchContent.")
include(FetchContent)

# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24:
Expand Down
96 changes: 53 additions & 43 deletions tests/fixtures/fixtures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ BaseTest::BaseTest():

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

// 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
// get command line args from the test executable
Expand Down Expand Up @@ -38,6 +43,13 @@ BaseTest::SetUp() {

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;
}

display_device::Logger::get().setCustomCallback(nullptr); // restore the default callback to avoid potential leaks
std::cout.rdbuf(m_sbuf); // restore cout buffer

Expand Down Expand Up @@ -67,6 +79,28 @@ BaseTest::TearDown() {
}
}

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 {};
}

int
BaseTest::exec(const char *cmd) {
std::array<char, 128> buffer {};
Expand All @@ -81,62 +115,38 @@ BaseTest::exec(const char *cmd) {
while (fgets(buffer.data(), buffer.size(), m_pipe_stderr) != nullptr) {
m_stderr_buffer << buffer.data();
}
int returnCode = pclose(m_pipe_stdout);
int return_code = pclose(m_pipe_stdout);
m_pipe_stdout = nullptr;
if (returnCode != 0) {
if (return_code != 0) {
std::cout << "Error: " << m_stderr_buffer.str() << std::endl
<< "Return code: " << returnCode << std::endl;
<< "Return code: " << return_code << std::endl;
}
return returnCode;
return return_code;
}

void
LinuxTest::SetUp() {
std::string
LinuxTest::skipTest() const {
#ifndef __linux__
GTEST_SKIP_("Skipping, this test is for Linux only.");
return "Skipping, this test is for Linux only.";
#else
return BaseTest::skipTest();
#endif
BaseTest::SetUp();
}

void
LinuxTest::TearDown() {
#ifndef __linux__
// This a noop case to skip the teardown
return;
#endif
BaseTest::TearDown();
}

void
MacOSTest::SetUp() {
#if !defined(__APPLE__) || !defined(__MACH__)
GTEST_SKIP_("Skipping, this test is for macOS only.");
#endif
BaseTest::SetUp();
}

void
MacOSTest::TearDown() {
std::string
MacOSTest::skipTest() const {
#if !defined(__APPLE__) || !defined(__MACH__)
// This a noop case to skip the teardown
return;
return "Skipping, this test is for macOS only.";
#else
return BaseTest::skipTest();
#endif
BaseTest::TearDown();
}

void
WindowsTest::SetUp() {
#ifndef _WIN32
GTEST_SKIP_("Skipping, this test is for Windows only.");
#endif
BaseTest::SetUp();
}

void
WindowsTest::TearDown() {
std::string
WindowsTest::skipTest() const {
#ifndef _WIN32
// This a noop case to skip the teardown
return;
return "Skipping, this test is for Windows only.";
#else
return BaseTest::skipTest();
#endif
BaseTest::TearDown();
}
39 changes: 24 additions & 15 deletions tests/fixtures/fixtures.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,21 @@ class BaseTest: public ::testing::Test {
void
TearDown() override;

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

int
exec(const char *cmd);

Expand All @@ -72,31 +87,25 @@ class BaseTest: public ::testing::Test {
std::streambuf *m_sbuf;
FILE *m_pipe_stdout;
FILE *m_pipe_stderr;

private:
bool m_test_skipped_at_setup { false };
};

class LinuxTest: public BaseTest {
protected:
void
SetUp() override;

void
TearDown() override;
[[nodiscard]] std::string
skipTest() const override;
};

class MacOSTest: public BaseTest {
protected:
void
SetUp() override;

void
TearDown() override;
[[nodiscard]] std::string
skipTest() const override;
};

class WindowsTest: public BaseTest {
protected:
void
SetUp() override;

void
TearDown() override;
[[nodiscard]] std::string
skipTest() const override;
};
11 changes: 11 additions & 0 deletions tests/fixtures/testutils.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// header include
#include "testutils.h"

// system includes
#include <cstdlib>

// system includes
#include <iostream>
#include <regex>
Expand All @@ -26,3 +29,11 @@ setEnv(const std::string &name, const std::string &value) {
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;
}
17 changes: 13 additions & 4 deletions tests/fixtures/testutils.h
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
#pragma once

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

/**
* @brief Test regular expression against string.
* @return True if string matches the regex, false otherwise
* @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
* @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);
5 changes: 5 additions & 0 deletions tests/unit/windows/test_windisplaydevicetopology.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ namespace {
// Test fixture(s) for this file
class WinDisplayDeviceTopology: public BaseTest {
public:
bool
isSystemTest() const override {
return true;
}

std::optional<std::vector<std::string>>
getAvailableDevices() {
const auto all_devices { m_layer->queryDisplayConfig(display_device::QueryType::All) };
Expand Down
Loading