Example: Unit Test for Vector & Matrix in Binary File (class ver.)

CMakeLists.txt

cmake_minimum_required(VERSION 3.14)
project(lecture18_ex)

# GoogleTest requires at least C++11
set(CMAKE_CXX_STANDARD 11)

include(FetchContent)
FetchContent_Declare(
  googletest
  URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)

# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

enable_testing()

add_executable(
  lecture18_unittest
  lecture18_unittest.cpp
)
target_link_libraries(
  lecture18_unittest
  gtest_main
)

include(GoogleTest)
gtest_discover_tests(lecture18_unittest)

lecture18_ex.h

#include <iostream>
#include <fstream>
#include <cmath>

#ifndef LECTURE18_EX_H

using namespace std;

class Vector_3D; // forward declaration

class Point_3D // represents a point in 3D space
{
private:
    // record the x coordinate
    double m_x;
    // record the y coordinate
    double m_y;
    // record the z coordinate
    double m_z;

public:
    Point_3D(const double &arg_x = 0, const double &arg_y = 0,
             const double &arg_z = 0);
    Point_3D(const Point_3D &arg_point);

    // Destructor
    ~Point_3D() // destructor
    {
        // do nothing, because we don't
        // have any dynamic memory
    }

    // modify the x, y and z coordinate
    void set_x(const double &arg_x);
    void set_y(const double &arg_y);
    void set_z(const double &arg_z);
    void set(const double &arg_x, const double &arg_y, const double &arg_z);
    // get the x, y and z coordinate
    double get_x() const;
    double get_y() const;
    double get_z() const;

    // calculate the distance between two points
    // in form of a vector
    Vector_3D operator-(const Point_3D &arg_point) const;

    // friend class
    friend class Vector_3D;
    // print the x and y coordinate in format (x, y, z)
    // accessable to const object
    friend std::ostream &operator<<(std::ostream &arg_os, const Point_3D &arg_point);
    // read the coordinate of a point from the input,
    // format: (x, y, z) and ignore space
    friend std::istream &operator>>(std::istream &arg_is, Point_3D &arg_point);
};

// Vector_3D class declaration
class Vector_3D
{
private:
    // define the three factor of the basis vectors
    // on the x, y, z axes
    double m_x, m_y, m_z;

public:
    // Constructor
    Vector_3D(const double &arg_x = 0.0, const double &arg_y = 0.0,
              const double &arg_z = 0.0);
    Vector_3D(const Point_3D &arg_point1, const Point_3D &arg_point2);

    // Copy constructor
    Vector_3D(const Vector_3D &arg_vector);

    // Destructor
    ~Vector_3D()
    {
        // do nothing
    }

    // set the three factor of the basis vectors
    void set_vector(const double &arg_x, const double &arg_y,
                    const double &arg_z);
    void set_vector(const Point_3D &arg_point1, const Point_3D &arg_point2);
    void set_x(const double &arg_x);
    void set_y(const double &arg_y);
    void set_z(const double &arg_z);
    // get the three factor of the basis vectors
    double get_x() const;
    double get_y() const;
    double get_z() const;

    // assign operator
    Vector_3D &operator=(const Vector_3D &arg_vector);

    // calculate the length of the Vector_3D
    double length();
    // calculate the dot product of two vectors
    int dot(const Vector_3D &);
    // calculate the cross product of two vectors
    Vector_3D cross(const Vector_3D &);

    // write the Vector_3D in binary format
    void write_binary(std::ofstream &arg_ofs);
    // read the Vector_3D in binary format
    void read_binary(std::ifstream &arg_ifs);
    // print the Vector_3D in format (x, y, z)
    // accessable to const object
    friend std::ostream &operator<<(std::ostream &arg_os,
                                    const Vector_3D &arg_point);
    // read the factors of a vector from the input,
    // format: (x, y, z) and ignore space
    friend std::istream &operator>>(std::istream &arg_is,
                                    Vector_3D &arg_point);
};

// Point_3D class implementation

// Constructor & Default constructor
// initialize data members, with default values
Point_3D::Point_3D(const double &arg_x, const double &arg_y,
                   const double &arg_z)
    : m_x(arg_x),
      m_y(arg_y),
      m_z(arg_z)
{
}

// Copy constructor
// copy the data members from the given object
Point_3D::Point_3D(const Point_3D &arg_point)
    : m_x(arg_point.m_x),
      m_y(arg_point.m_y),
      m_z(arg_point.m_z)
{
}

// modify the x and y coordinate
// we use `arg_` to indicate the arguments
// and re-write with setter
void Point_3D::set_x(const double &arg_x)
{
    m_x = arg_x;
}
void Point_3D::set_y(const double &arg_y)
{
    m_y = arg_y;
}
void Point_3D::set(const double &arg_x, const double &arg_y,
                   const double &arg_z)
{
    m_x = arg_x;
    m_y = arg_y;
    m_z = arg_z;
}

// get the x coordinate and y coordinate
// accessable to const object
double Point_3D::get_x() const
{
    return m_x;
}
double Point_3D::get_y() const
{
    return m_y;
}
double Point_3D::get_z() const
{
    return m_z;
}

// calculate the distance between two points
// in form of a vector
Vector_3D Point_3D::operator-(const Point_3D &arg_point) const
{
    return Vector_3D(*this, arg_point);
}

// print the point in the format (x, y, z)
std::ostream &operator<<(std::ostream &arg_os, const Point_3D &arg_point)
{
    arg_os << "(" << arg_point.m_x << ", " << arg_point.m_y
           << ", " << arg_point.m_z << ")";
    return arg_os;
}

// read the coordinate of a point from the input,
// format: (x, y, z) and ignore space
std::istream &operator>>(std::istream &arg_is, Point_3D &arg_point)
{
    string str_x, str_y, str_z, temp;

    if (getline(arg_is, temp, '(')) // ignore the first '('
    {
        if (getline(arg_is, str_x, ','))
        {
            if (getline(arg_is, str_y, ','))
            {
                if (getline(arg_is, str_z, ')'))
                {
                    arg_point.m_x = stod(str_x);
                    arg_point.m_y = stod(str_y);
                    arg_point.m_z = stod(str_z);
                }
            }
        }
    }
    return arg_is;
}

// Vector_3D class implementation

// Constructor
Vector_3D::Vector_3D(const double &arg_x, const double &arg_y,
                     const double &arg_z)
    : m_x(arg_x), m_y(arg_y), m_z(arg_z)
{
}

Vector_3D::Vector_3D(const Point_3D &arg_point1, const Point_3D &arg_point2)
    : m_x(arg_point2.m_x - arg_point1.m_x),
      m_y(arg_point2.m_y - arg_point1.m_y),
      m_z(arg_point2.m_z - arg_point1.m_z)
{
}

// Copy constructor
Vector_3D::Vector_3D(const Vector_3D &arg_vector)
    : m_x(arg_vector.m_x), m_y(arg_vector.m_y), m_z(arg_vector.m_z)
{
}

// set the three factor of the basis vectors
void Vector_3D::set_vector(const double &arg_x, const double &arg_y,
                           const double &arg_z)
{
    m_x = arg_x;
    m_y = arg_y;
    m_z = arg_z;
}
void Vector_3D::set_vector(const Point_3D &arg_point1,
                           const Point_3D &arg_point2)
{
    m_x = arg_point2.m_x - arg_point1.m_x;
    m_y = arg_point2.m_y - arg_point1.m_y;
    m_z = arg_point2.m_z - arg_point1.m_z;
}
void Vector_3D::set_x(const double &arg_x)
{
    m_x = arg_x;
}
void Vector_3D::set_y(const double &arg_y)
{
    m_y = arg_y;
}
void Vector_3D::set_z(const double &arg_z)
{
    m_z = arg_z;
}
// get the three factor of the basis vectors
double Vector_3D::get_x() const
{
    return m_x;
}
double Vector_3D::get_y() const
{
    return m_y;
}
double Vector_3D::get_z() const
{
    return m_z;
}

// assign operator
Vector_3D &Vector_3D::operator=(const Vector_3D &arg_vector)
{
    m_x = arg_vector.m_x;
    m_y = arg_vector.m_y;
    m_z = arg_vector.m_z;
    return *this;
}

// calculate the length of the Vector_3D
double Vector_3D::length()
{
    // calculate the length of the Vector_3D
    return sqrt(m_x * m_x + m_y * m_y + m_z * m_z);
}

// calculate the dot product of two vectors
int Vector_3D::dot(const Vector_3D &arg_v)
{
    // calculate the dot product of two vectors
    return m_x * arg_v.m_x + m_y * arg_v.m_y + m_z * arg_v.m_z;
}

// calculate the cross product of two vectors
Vector_3D Vector_3D::cross(const Vector_3D &arg_v)
{
    // calculate the cross product of two vectors
    Vector_3D v;
    v.m_x = m_y * arg_v.m_z - m_z * arg_v.m_y;
    v.m_y = m_z * arg_v.m_x - m_x * arg_v.m_z;
    v.m_z = m_x * arg_v.m_y - m_y * arg_v.m_x;
    return v;
}

// write the vector to the file in binary format
void Vector_3D::write_binary(std::ofstream &arg_ofs)
{
    arg_ofs.write((char *)&m_x, sizeof(double));
    arg_ofs.write((char *)&m_y, sizeof(double));
    arg_ofs.write((char *)&m_z, sizeof(double));
}

// read the vector from the file in binary format
void Vector_3D::read_binary(std::ifstream &arg_ifs)
{
    arg_ifs.read((char *)&m_x, sizeof(double));
    arg_ifs.read((char *)&m_y, sizeof(double));
    arg_ifs.read((char *)&m_z, sizeof(double));
}

// print the point in the format (x, y, z)
std::ostream &operator<<(std::ostream &arg_os, const Vector_3D &arg_vector)
{
    arg_os << "(" << arg_vector.m_x << ", " << arg_vector.m_y
           << ", " << arg_vector.m_z << ")";
    return arg_os;
}

// read the factors of a vector from the input,
// format: (x, y, z) and ignore space
std::istream &operator>>(std::istream &arg_is, Vector_3D &arg_vector)
{
    string str_x, str_y, str_z, temp;

    if (getline(arg_is, temp, '(')) // ignore the first '('
    {
        if (getline(arg_is, str_x, ','))
        {
            if (getline(arg_is, str_y, ','))
            {
                if (getline(arg_is, str_z, ')'))
                {
                    arg_vector.m_x = stod(str_x);
                    arg_vector.m_y = stod(str_y);
                    arg_vector.m_z = stod(str_z);
                }
            }
        }
    }
    return arg_is;
}

#endif // LECTURE18_EX_H

lecture18_ex.cpp

#include <iostream>
#include <fstream>
#include <cmath>

#include "lecture18_ex.h"

using namespace std;


// main function

int main()
{
    Vector_3D v1(1, 2, 3);
    Vector_3D v2(4, 5, 6);

    cout << "v1 = " << v1 << endl;
    cout << "v2 = " << v2 << endl;

    // save the vector to the file in binary format
    ofstream ofs("vector.bin", ios::binary | ios::trunc);
    v1.write_binary(ofs);
    v2.write_binary(ofs);
    ofs.close();

    // read the vector from the file in binary format
    ifstream ifs("vector.bin", ios::binary);
    Vector_3D v3, v4;
    v3.read_binary(ifs);
    v4.read_binary(ifs);

    cout << "v3 = " << v3 << endl;
    cout << "v4 = " << v4 << endl;

    // or read/write whole object
    ofstream ofs2("vector_whole.bin", ios::binary | ios::trunc);
    ofs2.write((char *)&v1, sizeof(Vector_3D));
    ofs2.write((char *)&v2, sizeof(Vector_3D));
    ofs2.close();

    ifstream ifs2("vector_whole.bin", ios::binary);
    Vector_3D v5, v6;
    ifs2.read((char *)&v5, sizeof(Vector_3D));
    ifs2.read((char *)&v6, sizeof(Vector_3D));

    cout << "v5 = " << v5 << endl;
    cout << "v6 = " << v6 << endl;

    // NOTE: compaire "vector.bin" and "vector_whole.bin"

    return 0;
}

lecture18_unittest.cpp

#include "lecture18_ex.h"
#include "gtest/gtest.h"

namespace
{

    TEST(Vector_3D, Init)
    {
        Vector_3D v1(1, 2, 3);

        EXPECT_EQ(1, v1.get_x());
        EXPECT_EQ(2, v1.get_y());
        EXPECT_EQ(3, v1.get_z());
    }

    TEST(Vector_3D, WriteFile)
    {
        Vector_3D v1(1, 2, 3);
        Vector_3D v2(4, 5, 6);

        ofstream ofs("vector_writetest.bin", ios::binary | ios::trunc);
        v1.write_binary(ofs);
        v2.write_binary(ofs);
        ofs.close();

        ifstream ifs("vector_writetest.bin", ios::binary);

        // test v1
        double x, y, z;
        ifs.read((char *)&x, sizeof(double));
        ifs.read((char *)&y, sizeof(double));
        ifs.read((char *)&z, sizeof(double));
        EXPECT_EQ(x, v1.get_x());
        EXPECT_EQ(y, v1.get_y());
        EXPECT_EQ(z, v1.get_z());

        // test v2
        ifs.read((char *)&x, sizeof(double));
        ifs.read((char *)&y, sizeof(double));
        ifs.read((char *)&z, sizeof(double));
        EXPECT_EQ(x, v2.get_x());
        EXPECT_EQ(y, v2.get_y());
        EXPECT_EQ(z, v2.get_z());

        ifs.close();
    }

    TEST(Vector_3D, ReadFile)
    {
        Vector_3D v1(1, 2, 3);
        Vector_3D v2(4, 5, 6);

        ofstream ofs("vector_readtest.bin", ios::binary | ios::trunc);
        v1.write_binary(ofs);
        v2.write_binary(ofs);
        ofs.close();

        ifstream ifs("vector_readtest.bin", ios::binary);

        // test v1
        Vector_3D v3;
        v3.read_binary(ifs);
        EXPECT_EQ(v1.get_x(), v3.get_x());
        EXPECT_EQ(v1.get_y(), v3.get_y());
        EXPECT_EQ(v1.get_z(), v3.get_z());

        // test v2
        v3.read_binary(ifs);
        EXPECT_EQ(v2.get_x(), v3.get_x());
        EXPECT_EQ(v2.get_y(), v3.get_y());
        EXPECT_EQ(v2.get_z(), v3.get_z());

        ifs.close();
    }

} // namespace

Result

Running main() from C:\Users\stevenokm\googletest\build\_deps\googletest-src\googletest\src\gtest_main.cc
[==========] Running 3 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 3 tests from Vector_3D
[ RUN      ] Vector_3D.Init
[       OK ] Vector_3D.Init (0 ms)
[ RUN      ] Vector_3D.WriteFile
[       OK ] Vector_3D.WriteFile (6 ms)
[ RUN      ] Vector_3D.ReadFile
[       OK ] Vector_3D.ReadFile (1 ms)
[----------] 3 tests from Vector_3D (15 ms total)

[----------] Global test environment tear-down
[==========] 3 tests from 1 test suite ran. (22 ms total)
[  PASSED  ] 3 tests.