Testing And Benchmarking in C++

Here’s the translation of the Go testing and benchmarking example to C++:

#include <iostream>
#include <vector>
#include <chrono>
#include <cassert>

// We'll be testing this simple implementation of an
// integer minimum. Typically, the code we're testing
// would be in a source file named something like
// `intutils.cpp`, and the test file for it would then
// be named `intutils_test.cpp`.

int IntMin(int a, int b) {
    if (a < b) {
        return a;
    }
    return b;
}

// A test is created by writing a function.
void TestIntMinBasic() {
    int ans = IntMin(2, -2);
    if (ans != -2) {
        throw std::runtime_error("IntMin(2, -2) = " + std::to_string(ans) + "; want -2");
    }
}

// Writing tests can be repetitive, so it's idiomatic to
// use a table-driven style, where test inputs and
// expected outputs are listed in a table and a single loop
// walks over them and performs the test logic.

struct TestCase {
    int a;
    int b;
    int want;
};

void TestIntMinTableDriven() {
    std::vector<TestCase> tests = {
        {0, 1, 0},
        {1, 0, 0},
        {2, -2, -2},
        {0, -1, -1},
        {-1, 0, -1},
    };

    for (const auto& tt : tests) {
        int ans = IntMin(tt.a, tt.b);
        if (ans != tt.want) {
            throw std::runtime_error("IntMin(" + std::to_string(tt.a) + ", " + 
                                     std::to_string(tt.b) + ") = " + 
                                     std::to_string(ans) + "; want " + 
                                     std::to_string(tt.want));
        }
    }
}

// Benchmark tests typically go in separate files and are
// named beginning with `Benchmark`. In C++, we can use
// the chrono library to measure execution time.

void BenchmarkIntMin() {
    const int N = 1000000;
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < N; i++) {
        IntMin(1, 2);
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
    std::cout << "BenchmarkIntMin: " << duration.count() / N << " ns/op" << std::endl;
}

int main() {
    try {
        std::cout << "Running TestIntMinBasic..." << std::endl;
        TestIntMinBasic();
        std::cout << "PASS" << std::endl;

        std::cout << "Running TestIntMinTableDriven..." << std::endl;
        TestIntMinTableDriven();
        std::cout << "PASS" << std::endl;

        std::cout << "Running BenchmarkIntMin..." << std::endl;
        BenchmarkIntMin();
    } catch (const std::exception& e) {
        std::cerr << "Test failed: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

To compile and run this C++ program:

$ g++ -std=c++11 -O2 test_intmin.cpp -o test_intmin
$ ./test_intmin
Running TestIntMinBasic...
PASS
Running TestIntMinTableDriven...
PASS
Running BenchmarkIntMin...
BenchmarkIntMin: 0 ns/op

This C++ version mimics the structure and functionality of the original Go example. Here are some key differences:

  1. Instead of using a testing framework, we’ve implemented simple test functions that throw exceptions on failure.

  2. The table-driven test uses a std::vector of TestCase structs instead of an anonymous struct slice.

  3. For benchmarking, we use the C++ chrono library to measure execution time, as C++ doesn’t have a built-in benchmarking tool like Go does.

  4. Error reporting is done by throwing exceptions instead of using a testing.T object.

  5. The main function runs all tests and the benchmark, whereas in Go these would typically be run by the go test command.

Remember that C++ doesn’t have a standard testing framework built into the language like Go does. In practice, you’d likely use a testing framework like Google Test or Catch2 for more comprehensive testing capabilities.