diff --git a/test/coverage/.bazelrc b/test/coverage/.bazelrc new file mode 100644 index 0000000..94b9560 --- /dev/null +++ b/test/coverage/.bazelrc @@ -0,0 +1,2 @@ +common --registry=https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/ +common --registry=https://bcr.bazel.build diff --git a/test/coverage/BUILD b/test/coverage/BUILD new file mode 100644 index 0000000..5dc076b --- /dev/null +++ b/test/coverage/BUILD @@ -0,0 +1,34 @@ +cc_library( + name = "coverage_lib", + srcs = ["coverage.cpp"], + hdrs = ["coverage.h"], + deps = ["//greeting:greeting"], +) + +cc_binary( + name = "coverage", + srcs = ["main.cpp"], + deps = [ + ":coverage_lib", + ], +) + +cc_test( + name = "coverage_test", + srcs = ["coverage_test.cpp"], + deps = [ + ":coverage_lib", + "//greeting:greeting", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], + size="small", +) + +# Test suite for all components (individual tests) +test_suite( + name = "all_tests", + tests = [ + ":coverage_test", + ], +) diff --git a/test/coverage/MODULE.bazel b/test/coverage/MODULE.bazel new file mode 100644 index 0000000..ea26d7b --- /dev/null +++ b/test/coverage/MODULE.bazel @@ -0,0 +1,23 @@ +module( + name = "coverage_test", + version = "0.1.0", + compatibility_level = 0, +) + +# bazel cc rules +bazel_dep(name = "rules_cc", version = "0.1.1") + +#score gcc toolchain +bazel_dep(name = "score_toolchains_gcc", version = "0.5", dev_dependency = False) + +gcc = use_extension("@score_toolchains_gcc//extentions:gcc.bzl", "gcc", dev_dependency = False) +gcc.toolchain( + sha256 = "8fa85c2a93a6bef1cf866fa658495a2416dfeec692e4246063b791abf18da083", + strip_prefix = "x86_64-unknown-linux-gnu", + url = "https://github.com/eclipse-score/toolchains_gcc_packages/releases/download/v0.0.3/x86_64-unknown-linux-gnu_gcc12.tar.gz", +) +use_repo(gcc, "gcc_toolchain", "gcc_toolchain_gcc") + +register_toolchains("@gcc_toolchain//:all") + +bazel_dep(name = "googletest", version = "1.17.0.bcr.1", dev_dependency = True) diff --git a/test/coverage/README.md b/test/coverage/README.md new file mode 100644 index 0000000..b50dd1a --- /dev/null +++ b/test/coverage/README.md @@ -0,0 +1,50 @@ +Sample to generate coverage data +================================ + +This directory contains a sample C++ project with test coverage capabilities using Bazel and GCC's gcov/lcov tools. + +## Prerequisites + +Ensure you have the following tools installed: +- Bazel +- lcov (Linux Test Project coverage tools) + +On Ubuntu/Debian: +```bash +sudo apt-get install lcov +``` + +## Running Tests with Coverage + +### Step 1: Run tests with coverage collection + +To generate coverage data for the `all_tests` target, run: + +```bash +$ bazel coverage --combined_report=lcov --subcommands -- //:all_tests +``` + +### Step 2: View coverage summary with lcov + +To get a quick coverage summary using lcov, you need to find the generated coverage files and use lcov: + +```bash +$ lcov --summary --rc branch_coverage=1 bazel-out/_coverage/_coverage_report.dat +Summary coverage rate: + lines......: 100.0% (10 of 10 lines) + functions..: 100.0% (2 of 2 functions) + branches...: 100.0% (4 of 4 branches) +``` + +Alternatively, you can use the coverage files directly from the bazel-out directory: + +```bash +# Find all .gcno and .gcda files +find -L bazel-out/ -name "*.gcda" -o -name "*.gcno" | head -10 + +# Generate coverage info file +lcov --capture --directory bazel-out --output-file coverage.info + +# Show coverage summary +lcov --summary coverage.info +``` diff --git a/test/coverage/coverage.cpp b/test/coverage/coverage.cpp new file mode 100644 index 0000000..6642288 --- /dev/null +++ b/test/coverage/coverage.cpp @@ -0,0 +1,15 @@ +#include "coverage.h" +#include "greeting/greeting.h" + +namespace coverage { + +// Extracted main logic for testing +void runMain(int argc, char* argv[]) noexcept { + if (argc > 1) { + greeting::printGreeting(argv[1]); + } else { + greeting::printGreeting("World"); + } +} + +} // namespace coverage \ No newline at end of file diff --git a/test/coverage/coverage.h b/test/coverage/coverage.h new file mode 100644 index 0000000..a852271 --- /dev/null +++ b/test/coverage/coverage.h @@ -0,0 +1,11 @@ +#ifndef COVERAGE_COVERAGE_H +#define COVERAGE_COVERAGE_H + +namespace coverage { + +// Function to run main logic (extracted for testing) +void runMain(int argc, char* argv[]) noexcept; + +} // namespace coverage + +#endif // COVERAGE_COVERAGE_H \ No newline at end of file diff --git a/test/coverage/coverage_test.cpp b/test/coverage/coverage_test.cpp new file mode 100644 index 0000000..a2077a7 --- /dev/null +++ b/test/coverage/coverage_test.cpp @@ -0,0 +1,189 @@ +#include "coverage.h" +#include "greeting/greeting.h" +#include +#include +#include + +// Helper class to capture cout output +class CoutCapture { +public: + CoutCapture() { + old_cout_buffer = std::cout.rdbuf(); + std::cout.rdbuf(captured_output.rdbuf()); + } + + ~CoutCapture() { + std::cout.rdbuf(old_cout_buffer); + } + + std::string getOutput() { + return captured_output.str(); + } + +private: + std::streambuf* old_cout_buffer; + std::ostringstream captured_output; +}; + +// ============================================================================= +// MAIN FUNCTION TESTS +// ============================================================================= + +// Test fixture class for main functionality +class CoverageTest : public ::testing::Test { +protected: + void SetUp() override { + // Setup code if needed + } + + void TearDown() override { + // Cleanup code if needed + } +}; + +// Test main function with no arguments (argc = 1) - should greet "World" +TEST_F(CoverageTest, MainWithNoArguments) { + CoutCapture capture; + const char* argv[] = {"./hello"}; + coverage::runMain(1, const_cast(argv)); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, World!\n"); +} + +// Test main function with one argument - should greet that argument +TEST_F(CoverageTest, MainWithOneArgument) { + CoutCapture capture; + const char* argv[] = {"./hello", "Alice"}; + coverage::runMain(2, const_cast(argv)); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, Alice!\n"); +} + +// Test main function with "Uwe" argument - should trigger special greeting +TEST_F(CoverageTest, MainWithUweArgument) { + CoutCapture capture; + const char* argv[] = {"./hello", "Uwe"}; + coverage::runMain(2, const_cast(argv)); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello there, Uwe! Great to see you!\n"); +} + +// Test main function with multiple arguments - should only use the first one +TEST_F(CoverageTest, MainWithMultipleArguments) { + CoutCapture capture; + const char* argv[] = {"./hello", "Bob", "Charlie", "David"}; + coverage::runMain(4, const_cast(argv)); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, Bob!\n"); +} + +// Test edge case: argc = 0 (unusual but possible) +TEST_F(CoverageTest, MainWithZeroArgc) { + CoutCapture capture; + const char* argv[] = {}; + coverage::runMain(0, const_cast(argv)); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, World!\n"); +} + +// ============================================================================= +// GREETING LIBRARY TESTS +// ============================================================================= + +// Test fixture class for greeting library +class GreetingTest : public ::testing::Test { +protected: + void SetUp() override { + // Setup code if needed + } + + void TearDown() override { + // Cleanup code if needed + } +}; + +// Test case for greeting "Uwe" - should trigger special greeting +TEST_F(GreetingTest, GreetingForUwe) { + CoutCapture capture; + greeting::printGreeting("Uwe"); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello there, Uwe! Great to see you!\n"); +} + +// Test case for greeting any other name - should trigger regular greeting +TEST_F(GreetingTest, GreetingForRegularName) { + CoutCapture capture; + greeting::printGreeting("Alice"); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, Alice!\n"); +} + +// Test case for empty string +TEST_F(GreetingTest, GreetingForEmptyString) { + CoutCapture capture; + greeting::printGreeting(""); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, !\n"); +} + +// Test case for "World" - typical default case +TEST_F(GreetingTest, GreetingForWorld) { + CoutCapture capture; + greeting::printGreeting("World"); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, World!\n"); +} + +// Test case for case sensitivity - "uwe" (lowercase) should not trigger special greeting +TEST_F(GreetingTest, GreetingForLowercaseUwe) { + CoutCapture capture; + greeting::printGreeting("uwe"); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, uwe!\n"); +} + +// Test case for case sensitivity - "UWE" (uppercase) should not trigger special greeting +TEST_F(GreetingTest, GreetingForUppercaseUWE) { + CoutCapture capture; + greeting::printGreeting("UWE"); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, UWE!\n"); +} + +// Test case for string containing "Uwe" but not exactly "Uwe" +TEST_F(GreetingTest, GreetingForStringContainingUwe) { + CoutCapture capture; + greeting::printGreeting("Uwe Müller"); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, Uwe Müller!\n"); +} + +// Test case for numeric input +TEST_F(GreetingTest, GreetingForNumericInput) { + CoutCapture capture; + greeting::printGreeting("123"); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, 123!\n"); +} + +// Test case for special characters +TEST_F(GreetingTest, GreetingForSpecialCharacters) { + CoutCapture capture; + greeting::printGreeting("@#$%"); + std::string output = capture.getOutput(); + + EXPECT_EQ(output, "Hello, @#$%!\n"); +} diff --git a/test/coverage/greeting/BUILD b/test/coverage/greeting/BUILD new file mode 100644 index 0000000..45cd073 --- /dev/null +++ b/test/coverage/greeting/BUILD @@ -0,0 +1,6 @@ +cc_library( + name = "greeting", + srcs = ["greeting.cpp"], + hdrs = ["greeting.h"], + visibility = ["//:__subpackages__"], +) diff --git a/test/coverage/greeting/greeting.cpp b/test/coverage/greeting/greeting.cpp new file mode 100644 index 0000000..dfd32ee --- /dev/null +++ b/test/coverage/greeting/greeting.cpp @@ -0,0 +1,14 @@ +#include "greeting.h" +#include + +namespace greeting { + +void printGreeting(const std::string& name) noexcept { + if (name == "Uwe") { + std::cout << "Hello there, Uwe! Great to see you!" << std::endl; + } else { + std::cout << "Hello, " << name << "!" << std::endl; + } +} + +} // namespace greeting diff --git a/test/coverage/greeting/greeting.h b/test/coverage/greeting/greeting.h new file mode 100644 index 0000000..c3cc63e --- /dev/null +++ b/test/coverage/greeting/greeting.h @@ -0,0 +1,12 @@ +#ifndef GREETING_GREETING_H +#define GREETING_GREETING_H + +#include + +namespace greeting { + +void printGreeting(const std::string& name) noexcept; + +} // namespace greeting + +#endif // GREETING_GREETING_H diff --git a/test/coverage/main.cpp b/test/coverage/main.cpp new file mode 100644 index 0000000..611f9a1 --- /dev/null +++ b/test/coverage/main.cpp @@ -0,0 +1,6 @@ +#include "coverage.h" + +int main(int argc, char* argv[]) { + coverage::runMain(argc, argv); + return 0; +}