cpp

examples

examples.cpp⚙️
/**
 * Unit Testing Examples with Google Test
 * 
 * To compile with Google Test:
 * 1. Install gtest: sudo apt install libgtest-dev
 * 2. Compile: g++ -std=c++17 examples.cpp -lgtest -lgtest_main -pthread -o test_examples
 * 3. Run: ./test_examples
 * 
 * Or use CMake (recommended - see CMakeLists.txt example)
 */

#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <stdexcept>
#include <memory>
#include <algorithm>

// Uncomment when Google Test is available:
// #include <gtest/gtest.h>

using std::cout;
using std::endl;

// ============================================================================
// CODE UNDER TEST - Calculator Class
// ============================================================================

class Calculator {
public:
    double add(double a, double b) const { return a + b; }
    double subtract(double a, double b) const { return a - b; }
    double multiply(double a, double b) const { return a * b; }
    
    double divide(double a, double b) const {
        if (b == 0) {
            throw std::invalid_argument("Division by zero");
        }
        return a / b;
    }
    
    double power(double base, int exp) const {
        return std::pow(base, exp);
    }
    
    double sqrt(double x) const {
        if (x < 0) {
            throw std::domain_error("Square root of negative number");
        }
        return std::sqrt(x);
    }
    
    // Memory functions
    void memoryStore(double value) { memory_ = value; }
    double memoryRecall() const { return memory_; }
    void memoryClear() { memory_ = 0; }
    
private:
    double memory_ = 0;
};

// ============================================================================
// EXAMPLE 1: Basic Tests
// ============================================================================

/*
TEST(CalculatorTest, AddsTwoPositiveNumbers) {
    Calculator calc;
    EXPECT_DOUBLE_EQ(calc.add(2.0, 3.0), 5.0);
}

TEST(CalculatorTest, AddsPositiveAndNegative) {
    Calculator calc;
    EXPECT_DOUBLE_EQ(calc.add(5.0, -3.0), 2.0);
}

TEST(CalculatorTest, AddsTwoNegativeNumbers) {
    Calculator calc;
    EXPECT_DOUBLE_EQ(calc.add(-2.0, -3.0), -5.0);
}

TEST(CalculatorTest, AddsWithZero) {
    Calculator calc;
    EXPECT_DOUBLE_EQ(calc.add(5.0, 0.0), 5.0);
    EXPECT_DOUBLE_EQ(calc.add(0.0, 5.0), 5.0);
    EXPECT_DOUBLE_EQ(calc.add(0.0, 0.0), 0.0);
}
*/

// ============================================================================
// EXAMPLE 2: Test Fixture
// ============================================================================

/*
class CalculatorFixtureTest : public ::testing::Test {
protected:
    Calculator calc;
    
    void SetUp() override {
        // Reset calculator state before each test
        calc.memoryClear();
    }
    
    void TearDown() override {
        // Cleanup after each test (if needed)
    }
};

TEST_F(CalculatorFixtureTest, MemoryStoreAndRecall) {
    calc.memoryStore(42.0);
    EXPECT_DOUBLE_EQ(calc.memoryRecall(), 42.0);
}

TEST_F(CalculatorFixtureTest, MemoryClear) {
    calc.memoryStore(100.0);
    calc.memoryClear();
    EXPECT_DOUBLE_EQ(calc.memoryRecall(), 0.0);
}

TEST_F(CalculatorFixtureTest, MemoryStartsAtZero) {
    EXPECT_DOUBLE_EQ(calc.memoryRecall(), 0.0);
}
*/

// ============================================================================
// EXAMPLE 3: Exception Testing
// ============================================================================

/*
TEST(CalculatorExceptionTest, DivisionByZeroThrows) {
    Calculator calc;
    EXPECT_THROW(calc.divide(10.0, 0.0), std::invalid_argument);
}

TEST(CalculatorExceptionTest, SqrtOfNegativeThrows) {
    Calculator calc;
    EXPECT_THROW(calc.sqrt(-1.0), std::domain_error);
}

TEST(CalculatorExceptionTest, ValidDivisionNoThrow) {
    Calculator calc;
    EXPECT_NO_THROW(calc.divide(10.0, 2.0));
}

TEST(CalculatorExceptionTest, ExceptionMessage) {
    Calculator calc;
    try {
        calc.divide(1.0, 0.0);
        FAIL() << "Expected std::invalid_argument";
    } catch (const std::invalid_argument& e) {
        EXPECT_STREQ(e.what(), "Division by zero");
    }
}
*/

// ============================================================================
// EXAMPLE 4: Floating Point Comparisons
// ============================================================================

/*
TEST(FloatingPointTest, UsesDoubleEq) {
    Calculator calc;
    double result = calc.divide(10.0, 3.0);
    EXPECT_DOUBLE_EQ(result, 10.0 / 3.0);
}

TEST(FloatingPointTest, UsesNear) {
    Calculator calc;
    double result = calc.sqrt(2.0);
    EXPECT_NEAR(result, 1.414, 0.001);  // Within 0.001 tolerance
}

TEST(FloatingPointTest, ComparesWithTolerance) {
    double a = 0.1 + 0.2;
    double b = 0.3;
    
    // This might fail due to floating point errors:
    // EXPECT_EQ(a, b);
    
    // Use EXPECT_NEAR instead:
    EXPECT_NEAR(a, b, 1e-10);
}
*/

// ============================================================================
// CODE UNDER TEST - StringUtils
// ============================================================================

class StringUtils {
public:
    static std::string toUpper(const std::string& str) {
        std::string result = str;
        std::transform(result.begin(), result.end(), result.begin(), ::toupper);
        return result;
    }
    
    static std::string toLower(const std::string& str) {
        std::string result = str;
        std::transform(result.begin(), result.end(), result.begin(), ::tolower);
        return result;
    }
    
    static std::string trim(const std::string& str) {
        size_t start = str.find_first_not_of(" \t\n\r");
        if (start == std::string::npos) return "";
        size_t end = str.find_last_not_of(" \t\n\r");
        return str.substr(start, end - start + 1);
    }
    
    static bool startsWith(const std::string& str, const std::string& prefix) {
        return str.rfind(prefix, 0) == 0;
    }
    
    static bool endsWith(const std::string& str, const std::string& suffix) {
        if (suffix.size() > str.size()) return false;
        return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
    }
    
    static std::vector<std::string> split(const std::string& str, char delimiter) {
        std::vector<std::string> result;
        size_t start = 0;
        size_t end = str.find(delimiter);
        
        while (end != std::string::npos) {
            result.push_back(str.substr(start, end - start));
            start = end + 1;
            end = str.find(delimiter, start);
        }
        result.push_back(str.substr(start));
        
        return result;
    }
};

// ============================================================================
// EXAMPLE 5: String Testing
// ============================================================================

/*
TEST(StringUtilsTest, ToUpperConvertsLowercase) {
    EXPECT_EQ(StringUtils::toUpper("hello"), "HELLO");
}

TEST(StringUtilsTest, ToUpperPreservesUppercase) {
    EXPECT_EQ(StringUtils::toUpper("HELLO"), "HELLO");
}

TEST(StringUtilsTest, ToUpperHandlesMixedCase) {
    EXPECT_EQ(StringUtils::toUpper("HeLLo WoRLD"), "HELLO WORLD");
}

TEST(StringUtilsTest, ToUpperHandlesEmpty) {
    EXPECT_EQ(StringUtils::toUpper(""), "");
}

TEST(StringUtilsTest, TrimRemovesLeadingSpaces) {
    EXPECT_EQ(StringUtils::trim("   hello"), "hello");
}

TEST(StringUtilsTest, TrimRemovesTrailingSpaces) {
    EXPECT_EQ(StringUtils::trim("hello   "), "hello");
}

TEST(StringUtilsTest, TrimRemovesBothSides) {
    EXPECT_EQ(StringUtils::trim("  hello  "), "hello");
}

TEST(StringUtilsTest, TrimHandlesAllWhitespace) {
    EXPECT_EQ(StringUtils::trim("   "), "");
}

TEST(StringUtilsTest, StartsWithTrue) {
    EXPECT_TRUE(StringUtils::startsWith("hello world", "hello"));
}

TEST(StringUtilsTest, StartsWithFalse) {
    EXPECT_FALSE(StringUtils::startsWith("hello world", "world"));
}

TEST(StringUtilsTest, SplitBasic) {
    auto parts = StringUtils::split("a,b,c", ',');
    ASSERT_EQ(parts.size(), 3);
    EXPECT_EQ(parts[0], "a");
    EXPECT_EQ(parts[1], "b");
    EXPECT_EQ(parts[2], "c");
}
*/

// ============================================================================
// EXAMPLE 6: Parameterized Tests
// ============================================================================

/*
class AdditionTest : public ::testing::TestWithParam<std::tuple<double, double, double>> {
protected:
    Calculator calc;
};

TEST_P(AdditionTest, AddsCorrectly) {
    auto [a, b, expected] = GetParam();
    EXPECT_DOUBLE_EQ(calc.add(a, b), expected);
}

INSTANTIATE_TEST_SUITE_P(
    AdditionTests,
    AdditionTest,
    ::testing::Values(
        std::make_tuple(0.0, 0.0, 0.0),
        std::make_tuple(1.0, 1.0, 2.0),
        std::make_tuple(-1.0, 1.0, 0.0),
        std::make_tuple(100.0, 200.0, 300.0),
        std::make_tuple(-5.0, -5.0, -10.0),
        std::make_tuple(3.14, 2.86, 6.0)
    )
);
*/

// ============================================================================
// EXAMPLE 7: Test with Containers
// ============================================================================

/*
TEST(VectorTest, EmptyVectorHasZeroSize) {
    std::vector<int> v;
    EXPECT_TRUE(v.empty());
    EXPECT_EQ(v.size(), 0);
}

TEST(VectorTest, PushBackIncreasesSize) {
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    
    EXPECT_FALSE(v.empty());
    EXPECT_EQ(v.size(), 2);
}

TEST(VectorTest, ElementAccess) {
    std::vector<int> v = {10, 20, 30};
    
    EXPECT_EQ(v[0], 10);
    EXPECT_EQ(v[1], 20);
    EXPECT_EQ(v[2], 30);
    EXPECT_EQ(v.front(), 10);
    EXPECT_EQ(v.back(), 30);
}

TEST(VectorTest, ThrowsOnInvalidAt) {
    std::vector<int> v = {1, 2, 3};
    EXPECT_THROW(v.at(10), std::out_of_range);
}
*/

// ============================================================================
// CODE UNDER TEST - BankAccount
// ============================================================================

class BankAccount {
public:
    explicit BankAccount(double initialBalance = 0.0) 
        : balance_(initialBalance) {
        if (initialBalance < 0) {
            throw std::invalid_argument("Initial balance cannot be negative");
        }
    }
    
    double getBalance() const { return balance_; }
    
    void deposit(double amount) {
        if (amount <= 0) {
            throw std::invalid_argument("Deposit amount must be positive");
        }
        balance_ += amount;
    }
    
    void withdraw(double amount) {
        if (amount <= 0) {
            throw std::invalid_argument("Withdrawal amount must be positive");
        }
        if (amount > balance_) {
            throw std::runtime_error("Insufficient funds");
        }
        balance_ -= amount;
    }
    
    bool transfer(BankAccount& to, double amount) {
        try {
            withdraw(amount);
            to.deposit(amount);
            return true;
        } catch (const std::exception&) {
            return false;
        }
    }
    
private:
    double balance_;
};

// ============================================================================
// EXAMPLE 8: BankAccount Tests
// ============================================================================

/*
class BankAccountTest : public ::testing::Test {
protected:
    BankAccount account{100.0};  // Start with $100
};

TEST_F(BankAccountTest, InitialBalanceIsCorrect) {
    EXPECT_DOUBLE_EQ(account.getBalance(), 100.0);
}

TEST_F(BankAccountTest, DepositIncreasesBalance) {
    account.deposit(50.0);
    EXPECT_DOUBLE_EQ(account.getBalance(), 150.0);
}

TEST_F(BankAccountTest, WithdrawDecreasesBalance) {
    account.withdraw(30.0);
    EXPECT_DOUBLE_EQ(account.getBalance(), 70.0);
}

TEST_F(BankAccountTest, CannotWithdrawMoreThanBalance) {
    EXPECT_THROW(account.withdraw(200.0), std::runtime_error);
}

TEST_F(BankAccountTest, CannotDepositNegative) {
    EXPECT_THROW(account.deposit(-10.0), std::invalid_argument);
}

TEST_F(BankAccountTest, CannotWithdrawNegative) {
    EXPECT_THROW(account.withdraw(-10.0), std::invalid_argument);
}

TEST_F(BankAccountTest, TransferBetweenAccounts) {
    BankAccount other{50.0};
    
    EXPECT_TRUE(account.transfer(other, 30.0));
    EXPECT_DOUBLE_EQ(account.getBalance(), 70.0);
    EXPECT_DOUBLE_EQ(other.getBalance(), 80.0);
}

TEST_F(BankAccountTest, TransferFailsWithInsufficientFunds) {
    BankAccount other{0.0};
    
    EXPECT_FALSE(account.transfer(other, 200.0));
    // Balances should be unchanged
    EXPECT_DOUBLE_EQ(account.getBalance(), 100.0);
    EXPECT_DOUBLE_EQ(other.getBalance(), 0.0);
}
*/

// ============================================================================
// EXAMPLE 9: Death Tests
// ============================================================================

/*
void mustNotBeNull(int* ptr) {
    if (ptr == nullptr) {
        std::abort();  // or std::exit(1)
    }
    *ptr = 42;
}

TEST(DeathTest, AbortOnNull) {
    EXPECT_DEATH(mustNotBeNull(nullptr), "");
}

// Or with assertions:
void assertPositive(int x) {
    assert(x > 0 && "x must be positive");
}

TEST(DeathTest, AssertFails) {
    // Only works if NDEBUG is not defined
    // EXPECT_DEBUG_DEATH(assertPositive(-1), "x must be positive");
}
*/

// ============================================================================
// EXAMPLE 10: Test Utilities
// ============================================================================

/*
// Custom matcher example
MATCHER_P(IsBetween, range, "") {
    return arg >= std::get<0>(range) && arg <= std::get<1>(range);
}

TEST(MatcherTest, CustomMatcher) {
    int value = 50;
    EXPECT_THAT(value, IsBetween(std::make_tuple(0, 100)));
}

// Test with SCOPED_TRACE for better error messages
TEST(ScopedTraceTest, ShowsContextOnFailure) {
    Calculator calc;
    
    for (int i = 0; i < 5; ++i) {
        SCOPED_TRACE(::testing::Message() << "Iteration " << i);
        EXPECT_GE(calc.add(i, 1), i);
    }
}
*/

// ============================================================================
// CMakeLists.txt for this project
// ============================================================================

/*
cmake_minimum_required(VERSION 3.16)
project(UnitTestExamples)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)
FetchContent_Declare(
    googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG v1.14.0
)
FetchContent_MakeAvailable(googletest)

enable_testing()

add_executable(unit_tests examples.cpp)
target_link_libraries(unit_tests GTest::gtest_main)

include(GoogleTest)
gtest_discover_tests(unit_tests)
*/

// ============================================================================
// MAIN - Demo without Google Test
// ============================================================================

int main() {
    cout << "╔══════════════════════════════════════════════════════════════╗" << endl;
    cout << "║              GOOGLE TEST EXAMPLES                             ║" << endl;
    cout << "╚══════════════════════════════════════════════════════════════╝" << endl;
    
    cout << "\nThis file contains Google Test examples." << endl;
    cout << "To run the tests, uncomment the test code and compile with gtest." << endl;
    
    cout << "\n=== Demo: Calculator ===" << endl;
    Calculator calc;
    cout << "  5 + 3 = " << calc.add(5, 3) << endl;
    cout << "  10 / 2 = " << calc.divide(10, 2) << endl;
    cout << "  sqrt(16) = " << calc.sqrt(16) << endl;
    
    cout << "\n=== Demo: StringUtils ===" << endl;
    cout << "  toUpper('hello') = " << StringUtils::toUpper("hello") << endl;
    cout << "  trim('  hello  ') = '" << StringUtils::trim("  hello  ") << "'" << endl;
    cout << "  startsWith('hello', 'he') = " << StringUtils::startsWith("hello", "he") << endl;
    
    cout << "\n=== Demo: BankAccount ===" << endl;
    BankAccount account(100.0);
    account.deposit(50.0);
    cout << "  After deposit $50: $" << account.getBalance() << endl;
    account.withdraw(30.0);
    cout << "  After withdraw $30: $" << account.getBalance() << endl;
    
    cout << "\n═══════════════════════════════════════════════════════════════" << endl;
    cout << "To compile with Google Test:" << endl;
    cout << "  mkdir build && cd build" << endl;
    cout << "  cmake .." << endl;
    cout << "  cmake --build ." << endl;
    cout << "  ctest --output-on-failure" << endl;
    
    return 0;
}
Examples - C++ Tutorial | DeepML