Skip to content

Commit

Permalink
feat: support unit test setup and teardown once #10
Browse files Browse the repository at this point in the history
  • Loading branch information
lymslive committed Apr 26, 2024
1 parent e3dea8e commit f59657b
Show file tree
Hide file tree
Showing 3 changed files with 334 additions and 4 deletions.
11 changes: 7 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ add_executable(utCoutTast
utest/test-tinytast.cpp
utest/test-file-compare.cpp
utest/test-classtast.cpp
utest/test-unittast.cpp
utest/test-systemtast.cpp
)

Expand All @@ -65,6 +66,7 @@ target_link_libraries(utCoutTast
add_executable(utCxx98Tast
utest/test-tinytast.cpp
utest/test-classtast.cpp
utest/test-unittast.cpp
)

target_compile_definitions(utCxx98Tast PRIVATE USE_TINY_MAIN)
Expand All @@ -91,15 +93,16 @@ endif()
set(COUTTAST_HEADER
include/tinytast.hpp
include/couttast.hpp
include/tinymain.hpp
include/unittast.hpp
include/classtast.hpp
include/tastargv.hpp
include/coutstd.hpp
include/extra-macros.hpp
include/gtest-macros.hpp
include/tinymain.hpp
include/coutdebug.hpp
include/logprint.hpp
include/private-access.hpp
include/classtast.hpp
include/tastargv.hpp
include/coutdebug.hpp
include/couttast.h
include/tinyini.h
include/systemtast.h
Expand Down
151 changes: 151 additions & 0 deletions include/unittast.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/**
* @file classtast.hpp
* @author lymslive
* @date 2024-04-25
* @brief Support group test suite based on file unit or namespace in the file.
* @details A testing source file or compile unit is considered as a test
* module. The test cases defined in one unit may have some relation and share some
* expensive resource, such as global(better static) varibles. So an unit
* usually need run a `setup()` function once before run any test case, and
* it is better to have another opposite `teardown()` function to run after
* all tast case.
* @note The setup and teardown function handle auto tast(like from `DEF_TAST`)
* and manual tool(like from `DEF_TOOL`) differently. So we use `tast` and
* `tool` word for each type, and `test` for both.
* @note Thought it is no encouraged, for large source file, you can have
* multiple namespace in one file, and each namespace is can become seperate
* test unit. But it is not easy to share test unit in the same namespace
* across different source file.
* */
#pragma once
#ifndef UNITTAST_HPP__
#define UNITTAST_HPP__

#include "tinytast.hpp"

namespace tast
{

/// The state of current unit.
struct CUnitState
{
int tastCount; //< Count of tast(but not tool) in this unit defined.
int runCount; //< Count of tast finished run.
voidfun_t setup; //< Function to run once before the first test/tool.
voidfun_t teardown; //< Function to run after the last tast.
bool ready; //< The setup function has been called.

CUnitState(voidfun_t fn = NULL)
: tastCount(0), runCount(0), setup(fn), teardown(NULL), ready(false)
{}

/// Add count of tast in the unit.
struct AddTast
{
AddTast(CUnitState& unit, bool autoRun)
{
if (autoRun)
{
unit.tastCount++;
}
}
};

/// Define teardown function in the unit.
struct Defer
{
Defer(CUnitState& unit, voidfun_t fn)
{
unit.teardown = fn;
}
};

/// Check to run setup function once before any tast/tool.
void CheckSetupFirst()
{
if (!ready && setup != NULL)
{
setup();
ready = true;
}
}

/// Check to run teardown function after all tast.
void CheckTeardownLast()
{
if (teardown == NULL)
{
return;
}
if (G_TASTMGR->GetTastRun()->m_autoRun == false)
{
return;
}
runCount++;
if (runCount >= tastCount)
{
teardown();
ready = false;
}
}
};

/// The base class wrapper for test in current refered unit.
struct CUnitTast
{
CUnitState& unit;

CUnitTast(CUnitState& refer) : unit(refer)
{}
};

/// The running entrance function for each test.
template <typename testT>
void run_unit_tast()
{
testT self;
self.unit.CheckSetupFirst();
self.run();
self.unit.CheckTeardownLast();
}

} /* tast:: */

/// Setup current unit for following test cases.
/// Must use this macro the eable test unit tracking.
#define SETUP() \
static void s_setup(); \
static tast::CUnitState s_unit(s_setup); \
void s_setup()

/// Define optional teardown function for current test unit.
#define TEARDOWN() \
static void s_teardown(); \
static tast::CUnitState::Defer s_unitDefer(s_unit, s_teardown); \
void s_teardown()


/// Helper macros to simply the unit test case definition.
#define UTAST_COUNTER(name) tast_##name##_counter
#define UTAST_CLASS(name) CUnitTast_ ## name
#define UTAST_RUNNER(name) tast::run_unit_tast<UTAST_CLASS(name)>

/// Define a test case in current unit.
#define DEU_TAST_IMPL(name, autoRun, ...) \
static void TAST_RUNNER(name)(); \
struct UTAST_CLASS(name) : public tast::CUnitTast \
{ \
UTAST_CLASS(name)() : tast::CUnitTast(s_unit) {} \
void run(); \
}; \
static tast::CTastBuilder TAST_BUILDER(name)(UTAST_RUNNER(name), #name, __FILE__, __LINE__, autoRun, ## __VA_ARGS__); \
static tast::CUnitState::AddTast UTAST_COUNTER(name)(s_unit, autoRun); \
void UTAST_CLASS(name)::run()

/// Define an auto tast case. Must use after SETUP unit.
#define DEU_TAST(name, ...) DEU_TAST_IMPL(name, true, ## __VA_ARGS__)

/// Define a manual tool case. Must use after SETUP unit.
#define DEU_TOOL(name, ...) DEU_TAST_IMPL(name, false, ## __VA_ARGS__)

#endif /* end of include guard: UNITTAST_HPP__ */
176 changes: 176 additions & 0 deletions utest/test-unittast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
#include "unittast.hpp"

int* s_pBuffer = NULL;
void cout_counter();

SETUP()
{
s_pBuffer = new int[8];
DESC("file SETUP called");
cout_counter();
s_pBuffer[0] = s_unit.tastCount;
}

TEARDOWN()
{
delete[] s_pBuffer;
s_pBuffer = NULL;
DESC("file TEARDOWN called");
cout_counter();
}

void cout_counter()
{
COUT(s_pBuffer);
COUT(s_unit.tastCount);
COUT(s_unit.runCount);
}

DEF_TAST(unit_normal, "normal test not in uint")
{
cout_counter();
// s_pBuffer may not inited.
// COUT(s_pBuffer[0], s_unit.tastCount);
}

DEU_TAST(unit_auto1, "test in uint")
{
cout_counter();
COUT(s_pBuffer[0], s_unit.tastCount);
int* pBuffer = new int[16];
pBuffer[15] = 1;
COUT(pBuffer);
delete[] pBuffer;
}

DEU_TAST(unit_auto2, "test in uint")
{
cout_counter();
COUT(s_pBuffer[0], s_unit.tastCount);
}

DEU_TOOL(unit_tool1, "tool in uint")
{
cout_counter();
COUT(s_pBuffer[0], s_unit.tastCount);
}

DEU_TOOL(unit_tool2, "tool in uint")
{
cout_counter();
COUT(s_pBuffer[0], s_unit.tastCount);
}

namespace unest
{

double* s_pBuffer = NULL;
void cout_counter();

SETUP()
{
s_pBuffer = new double[4];
DESC("unest::SETUP called");
cout_counter();
s_pBuffer[0] = 2.0;
}

TEARDOWN()
{
delete[] s_pBuffer;
s_pBuffer = NULL;
DESC("unest::TEARDOWN called");
cout_counter();
}

void cout_counter()
{
COUT(s_pBuffer);
COUT(s_unit.tastCount);
COUT(s_unit.runCount);
}

DEU_TAST(unest_auto1, "test in namespace uint")
{
cout_counter();
COUT(s_pBuffer[0], s_unit.tastCount);
int* pBuffer = new int[16];
pBuffer[15] = 1;
COUT(pBuffer);
delete[] pBuffer;
}

DEU_TAST(unest_auto2, "test in namespace uint")
{
cout_counter();
COUT(s_pBuffer[0], s_unit.tastCount);
}

} // endof unest::

namespace umore
{

double* s_pBuffer = NULL;
void cout_counter();

SETUP()
{
s_pBuffer = new double[4];
DESC("umore::SETUP called");
cout_counter();
s_pBuffer[0] = 3.0;
}

TEARDOWN()
{
delete[] s_pBuffer;
s_pBuffer = NULL;
DESC("umore::TEARDOWN called");
cout_counter();
}

void cout_counter()
{
COUT(s_pBuffer);
COUT(s_unit.tastCount);
COUT(s_unit.runCount);
}

// yes, the test name is the same as above unest_auto1
DEU_TAST(unest_auto1, "test in namespace uint")
{
cout_counter();
COUT(s_pBuffer[0], s_unit.tastCount);
}

DEU_TAST(unest_auto2, "test in namespace uint")
{
cout_counter();
COUT(s_pBuffer[0], s_unit.tastCount);
}

DEU_TAST(unest_auto3, "test in namespace uint")
{
cout_counter();
COUT(s_pBuffer[0], s_unit.tastCount);
}

} // end of umore::

namespace unot
{

DEU_TAST(unit_auto2, "test in uint")
{
cout_counter();
COUT(s_pBuffer[0], s_unit.tastCount);
}

DEU_TAST(unit_auto3, "test in uint")
{
cout_counter();
COUT(s_pBuffer[0], s_unit.tastCount);
}

} // end of umore

0 comments on commit f59657b

Please sign in to comment.