From 0c2499f8fe3896a65e2d7e0187e20d3253619f1f Mon Sep 17 00:00:00 2001 From: Masonlet Date: Fri, 14 Nov 2025 21:42:27 -0500 Subject: [PATCH 1/4] Integrate GoogleTest into CMake build system - Add BUILD_TESTS option (default OFF) - Fetch GoogleTest via FetchContent - Configure test executable and CTest integration - Organize test targets in VS Explorer - Add placeholder test to verify build system - Require C++17 - Clean and refactor CMake --- CMakeLists.txt | 57 ++++++++++++++++++++++++++++++++++--------- tests/CMakeLists.txt | 16 ++++++++++++ tests/logger_test.cpp | 5 ++++ 3 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/logger_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f6a0b00..e74e4ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,24 +1,57 @@ cmake_minimum_required(VERSION 3.20) +project(starlet_logger VERSION 1.0.0) -set(LOG_NAME starlet_logger) -SET(LOG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") -set(LOG_INC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/inc") +set_property(GLOBAL PROPERTY USE_FOLDERS ON) -if(NOT TARGET ${LOG_NAME}) - add_library(${LOG_NAME} STATIC) +SET(PROJECT_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") +set(PROJECT_INC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/inc") - file(GLOB_RECURSE LOG_SRC CONFIGURE_DEPENDS ${LOG_SRC_DIR}/*.cpp) - file(GLOB_RECURSE LOG_HEADERS CONFIGURE_DEPENDS ${LOG_INC_DIR}/starlet-logger/*.hpp) +if(NOT TARGET ${PROJECT_NAME}) + add_library(${PROJECT_NAME} STATIC) - target_sources(${LOG_NAME} PRIVATE ${LOG_SRC} ${LOG_HEADERS}) + file(GLOB_RECURSE PROJECT_SRC CONFIGURE_DEPENDS + ${PROJECT_SRC_DIR}/*.cpp + ) + file(GLOB_RECURSE PROJECT_HEADERS CONFIGURE_DEPENDS + ${PROJECT_INC_DIR}/starlet-logger/*.hpp + ) - target_include_directories(${LOG_NAME} + target_sources(${PROJECT_NAME} + PRIVATE ${PROJECT_SRC} ${PROJECT_HEADERS}) + + target_include_directories(${PROJECT_NAME} PUBLIC - $ + $ $ ) + target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) + # IDE organization - source_group(TREE ${LOG_SRC_DIR} PREFIX "Source Files" FILES ${LOG_SRC}) - source_group(TREE ${LOG_INC_DIR} PREFIX "Header Files" FILES ${LOG_HEADERS}) + source_group(TREE ${PROJECT_SRC_DIR} PREFIX "Source Files" FILES ${PROJECT_SRC}) + source_group(TREE ${PROJECT_INC_DIR} PREFIX "Header Files" FILES ${PROJECT_HEADERS}) +endif() + +option(BUILD_TESTS "Build unit tests" OFF) + +if(BUILD_TESTS) + enable_testing() + + include(FetchContent) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.14.0 + ) + + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + FetchContent_MakeAvailable(googletest) + + set_target_properties(gtest gtest_main gmock gmock_main PROPERTIES + FOLDER "Tests/GoogleTest" + ) + + add_subdirectory(tests) endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..cd5547e --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +add_executable(${PROJECT_NAME}_tests + logger_test.cpp +) + +target_link_libraries(${PROJECT_NAME}_tests + PRIVATE + ${PROJECT_NAME} + GTest::gtest_main +) + +set_target_properties(${PROJECT_NAME}_tests PROPERTIES + FOLDER "Tests" +) + +include(GoogleTest) +gtest_discover_tests(${PROJECT_NAME}_tests) diff --git a/tests/logger_test.cpp b/tests/logger_test.cpp new file mode 100644 index 0000000..a2053ef --- /dev/null +++ b/tests/logger_test.cpp @@ -0,0 +1,5 @@ +#include + +TEST(LoggerTest, PlaceHolder) { + EXPECT_TRUE(true); +} From 80e17b4153d0b1bc90ff170111dd0f41adc92b50 Mon Sep 17 00:00:00 2001 From: Masonlet Date: Sat, 15 Nov 2025 17:00:52 -0500 Subject: [PATCH 2/4] Add unit tests for starlet-logger - Implement GoogleTest covering - All log levels (info, debug, warning, error) - Testing output stream routing (stderr/stdout) - Testing warning's optional retValue parameter --- inc/starlet-logger/logger.hpp | 8 ++-- src/logger.cpp | 31 +++++++++++----- tests/logger_test.cpp | 69 ++++++++++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 16 deletions(-) diff --git a/inc/starlet-logger/logger.hpp b/inc/starlet-logger/logger.hpp index ace1534..4d982b4 100644 --- a/inc/starlet-logger/logger.hpp +++ b/inc/starlet-logger/logger.hpp @@ -4,14 +4,14 @@ namespace Starlet::Logger { enum class Level { - Debug, Info, + Debug, Warning, Error }; - void log(Level level, const char* caller, const char* function, const std::string& msg); - + bool log(const char* caller, const char* function, const std::string& msg); + bool debug(const char* caller, const char* function, const std::string& msg); + bool warning(const char* caller, const char* function, const std::string& msg, bool retValue = false); bool error(const char* caller, const char* function, const std::string& msg); - bool debugLog(const char* caller, const char* function, const std::string& msg, bool returnValue = true); } diff --git a/src/logger.cpp b/src/logger.cpp index 62c048a..acf0333 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -12,21 +12,32 @@ namespace Starlet::Logger { default: return "UNKNOWN"; } } - } - void log(Level level, const char* caller, const char* function, const std::string& msg) { - FILE* output = (level == Level::Error) ? stderr : stdout; - fprintf(output, "[%s %s %s] %s\n", caller, function, lvlStr(level), msg.c_str()); + void _log(Level level, const char* caller, const char* function, const std::string& msg, bool retValue = true) { + FILE* output = (retValue == false) ? stderr : stdout; + fprintf(output, "[%s %s %s] %s\n", caller, function, lvlStr(level), msg.c_str()); + } } - bool error(const char* caller, const char* function, const std::string& msg) { - log(Level::Error, caller, function, msg); - return false; + bool log(const char* caller, const char* function, const std::string& msg) { + _log(Level::Info, caller, function, msg); + return true; } - bool debugLog(const char* caller, const char* function, const std::string& msg, bool returnValue) { + + bool debug(const char* caller, const char* function, const std::string& msg) { #ifndef NDEBUG - log(Level::Debug, caller, function, msg); + _log(Level::Debug, caller, function, msg); #endif - return returnValue; + return true; + } + + bool warning(const char* caller, const char* function, const std::string& msg, bool retValue) { + _log(Level::Warning, caller, function, msg, retValue); + return retValue; + } + + bool error(const char* caller, const char* function, const std::string& msg) { + _log(Level::Error, caller, function, msg, false); + return false; } } diff --git a/tests/logger_test.cpp b/tests/logger_test.cpp index a2053ef..c114971 100644 --- a/tests/logger_test.cpp +++ b/tests/logger_test.cpp @@ -1,5 +1,70 @@ #include +#include "starlet-logger/logger.hpp" -TEST(LoggerTest, PlaceHolder) { - EXPECT_TRUE(true); +namespace SLogger = Starlet::Logger; + +TEST(LoggerTest, BasicLog) { + testing::internal::CaptureStdout(); + SLogger::log("TestCaller", "TestFunction", "Test message"); + const std::string output = testing::internal::GetCapturedStdout(); + EXPECT_EQ(output, "[TestCaller TestFunction INFO] Test message\n"); +} +TEST(LoggerTest, BasicDebugLog) { + testing::internal::CaptureStdout(); + SLogger::debug("TestCaller", "TestFunction", "True message"); + const std::string outputT = testing::internal::GetCapturedStdout(); + EXPECT_EQ(outputT, "[TestCaller TestFunction DEBUG] True message\n"); +} +TEST(LoggerTest, BasicWarningLog) { + testing::internal::CaptureStderr(); + SLogger::warning("TestCaller", "TestFunction", "Test message"); + const std::string output = testing::internal::GetCapturedStderr(); + EXPECT_EQ(output, "[TestCaller TestFunction WARNING] Test message\n"); +} +TEST(LoggerTest, BasicErrorLog) { + testing::internal::CaptureStderr(); + SLogger::error("TestCaller", "TestFunction", "Test message"); + const std::string output = testing::internal::GetCapturedStderr(); + EXPECT_EQ(output, "[TestCaller TestFunction ERROR] Test message\n"); +} + +TEST(LoggerTest, AllLevels) { + testing::internal::CaptureStdout(); + SLogger::log("Caller", "Func", "info"); + SLogger::debug("Caller", "Func", "debug"); + SLogger::warning("Caller", "Func", "warning", true); + std::string output = testing::internal::GetCapturedStdout(); + EXPECT_NE(output.find("[Caller Func INFO] info\n"), std::string::npos); + EXPECT_NE(output.find("[Caller Func DEBUG] debug\n"), std::string::npos); + EXPECT_NE(output.find("[Caller Func WARNING] warning\n"), std::string::npos); + + testing::internal::CaptureStderr(); + SLogger::error("Caller", "Func", "error"); + SLogger::warning("Caller", "Func", "warning"); + output = testing::internal::GetCapturedStderr(); + EXPECT_NE(output.find("[Caller Func ERROR] error\n"), std::string::npos); + EXPECT_NE(output.find("[Caller Func WARNING] warning\n"), std::string::npos); } + +TEST(LoggerTest, AllErrorLevels) { + testing::internal::CaptureStderr(); + SLogger::error("Caller", "Func", "error"); + const std::string output = testing::internal::GetCapturedStderr(); + EXPECT_NE(output.find("[Caller Func ERROR] error\n"), std::string::npos); +} + +TEST(LoggerTest, WarningLogReturnValue) { + testing::internal::CaptureStdout(); + EXPECT_TRUE(SLogger::warning("C", "F", "msg", true)); + testing::internal::GetCapturedStdout(); + + testing::internal::CaptureStderr(); + EXPECT_FALSE(SLogger::warning("C", "F", "msg", false)); + testing::internal::GetCapturedStderr(); +} + +TEST(LoggerTest, ErrorReturnsFalse) { + testing::internal::CaptureStderr(); + EXPECT_FALSE(SLogger::error("C", "F", "msg")); + testing::internal::GetCapturedStderr(); +} \ No newline at end of file From 97982b254912453db51e6344597b5ce518276edc Mon Sep 17 00:00:00 2001 From: Masonlet Date: Sat, 15 Nov 2025 17:05:53 -0500 Subject: [PATCH 3/4] Add GitHub Actions for automated testing --- .github/workflows/tests.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..b809b72 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,19 @@ +name: Tests + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Configure CMake + run: cmake -B build -DBUILD_TESTS=ON + - name: Build + run: cmake --build build --config Release + - name: Run tests + run: ctest --test-dir build -C Release --output-on-failure From 8620dfcc8fc84f561a0f2d3a29d849f4aeffd3fe Mon Sep 17 00:00:00 2001 From: Masonlet Date: Sat, 15 Nov 2025 17:12:31 -0500 Subject: [PATCH 4/4] Update README with features and testing information - Add test status, C++17, and license badges - Document C++17 and CMake prerequisites - Add building and testing instructions - Cleanup formatting and structing --- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/README.md b/README.md index 70a44cc..c310e9c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,24 @@ # Starlet Logger +![Tests](https://github.com/masonlet/starlet-logger/actions/workflows/test.yml/badge.svg) +[![C++20](https://img.shields.io/badge/C%2B%2B-17-blue.svg)](https://isocpp.org/std/the-standard) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE) + A lightweight logging utility for C++ applications. +## Features +- Four log levels: Info, Debug, Warning, Error +- Automatic stream routing (stdout for info/debug, stderror for warnings/errors) +- Caller, function, and msg context in every log message +- Debug logs compiled out in release builds (NDEBUG) +- Custom warning return value for permissible warnings + +
+ +## Prerequisites +- C++17 or later +- CMake 3.20+ + ## Using as a Dependency ```cmake @@ -15,3 +32,26 @@ FetchContent_MakeAvailable(starlet_logger) target_link_libraries(app_name PRIVATE starlet_logger) ``` + +
+ +## Building and Testing +```bash +# 1. Clone starlet-logger +git clone https://github.com/masonlet/starlet-logger.git +cd starlet-logger + +# 2. Create a build directory and generate build files +mkdir build +cd build +cmake -DBUILD_TESTS=ON .. + +# 3. Build and run tests +cmake --build . +ctest +``` + +
+ +## License +MIT License - see [LICENSE](./LICENSE) for details.