Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reprostim-*capture -- start adding tests via ctests framework #78

Merged
merged 17 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/ccpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Install build dependencies
run: |
sudo apt update
sudo apt install -y libyaml-cpp-dev libspdlog-dev libasound2-dev libv4l-dev libudev-dev libopencv-dev cmake g++
sudo apt install -y libyaml-cpp-dev libspdlog-dev catch2 libasound2-dev libv4l-dev libudev-dev libopencv-dev cmake g++

- name: Build
run: |
Expand All @@ -41,3 +41,8 @@ jobs:
./reprostim-screencapture -V
./reprostim-screencapture -h
working-directory: Capture/build/screencapture

- name: Run tests with CTest
run: |
ctest --output-on-failure
working-directory: Capture/build
20 changes: 19 additions & 1 deletion Capture/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ cmake_minimum_required (VERSION 3.10)

project(Capture)

option(CTEST_ENABLED "Specify CTest build and run are enabled" ON)

# hook to reload version.txt file
set(CAPTURE_VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/version.txt")
configure_file(${CAPTURE_VERSION_FILE}
Expand Down Expand Up @@ -46,4 +48,20 @@ link_directories(${MWCAPTURE_SDK_HOME}/Lib/${ARCH})
# Add projects
add_subdirectory(capturelib)
add_subdirectory(screencapture)
add_subdirectory(videocapture)
add_subdirectory(videocapture)

# Add tests optionally
if(CTEST_ENABLED)
message(STATUS "CTests are ENABLED")
enable_testing()

find_package(Catch2 REQUIRED)
include_directories(${Catch2_INCLUDE_DIRS})

add_subdirectory(test)
add_subdirectory(capturelib/test)
add_subdirectory(screencapture/test)
add_subdirectory(videocapture/test)
else()
message(STATUS "CTests are DISABLED")
endif()
28 changes: 25 additions & 3 deletions Capture/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ USB Capture devices and save it to a file. It is a part of the ReproStim project

On Debian:

apt-get install -y ffmpeg libudev-dev libasound-dev libv4l-dev libyaml-cpp-dev libspdlog-dev v4l-utils libopencv-dev cmake g++
apt-get install -y ffmpeg libudev-dev libasound-dev libv4l-dev libyaml-cpp-dev libspdlog-dev catch2 v4l-utils libopencv-dev cmake g++

Project requirements:
- OS Linux
Expand All @@ -24,6 +24,7 @@ Project requirements:
- libyaml-cpp-dev
- libspdlog-dev
- libopencv-dev
- catch2
- v4l-utils
- ffmpeg

Expand Down Expand Up @@ -53,7 +54,12 @@ utility. The program captures video/audio streams from Magewell USB Capture devi
and saves it as video file (*.mkv).


Both utilities use `capturelib` as a shared library.
Both utilities use `capturelib` as a shared library.

All projects C++ code live in the `reprostim` namespace. So in general, the project
structure looks like this:

![Project Structure](docs/images/project_structure.png)

## Versioning

Expand Down Expand Up @@ -81,4 +87,20 @@ E.g. in CLion IDE, you can add "version-auto-inc.cmake" as External Tool under
![External Tools in CLion](docs/images/clion_version_auto_inc.png)

Then you can run it from the IDE to increment build number manually or integrate it
with build process (as pre-build hook) to increment build number automatically.
with build process (as pre-build hook) to increment build number automatically.

## Testing

In short words project uses CMake + CTest + Catch2 for unit testing. To run tests,
build project and execute the following command:

cd Capture/build
ctest

Root project and each subproject have their own tests located in the "test" directory
along with CMakeList.txt and C++ sources.

Tests build process controlled by CMake option `CTEST_ENABLED`. If it is set to ON
(default value), tests will be built. If it is set to OFF, tests will be skipped (can
be useful for development in IDE under some circumstances to skip tests and reduce
compilation time).
6 changes: 3 additions & 3 deletions Capture/capturelib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
project (capturelib)

# version header generator
configure_file(${PROJECT_SOURCE_DIR}/include/CaptureVer.h.in
${PROJECT_SOURCE_DIR}/include/CaptureVer.h
configure_file(${PROJECT_SOURCE_DIR}/include/reprostim/CaptureVer.h.in
${PROJECT_SOURCE_DIR}/include/reprostim/CaptureVer.h
)

add_library(${PROJECT_NAME} STATIC
src/CaptureLib.cpp
src/CaptureLog.cpp
src/CaptureApp.cpp
include/CaptureVer.h.in
include/reprostim/CaptureVer.h.in
)
add_library(reprostim::capturelib ALIAS ${PROJECT_NAME})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#ifndef CAPTURE_CAPTUREAPP_H
#define CAPTURE_CAPTUREAPP_H

#include "CaptureLib.h"
#include "CaptureThreading.h"
#include <unistd.h>
#include "reprostim/CaptureLib.h"
#include "reprostim/CaptureThreading.h"
#include "yaml-cpp/yaml.h"

namespace reprostim {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
#include <chrono>
#include <thread>
#include "LibMWCapture/MWCapture.h"
#include "CaptureVer.h"
#include "CaptureLog.h"
#include "reprostim/CaptureVer.h"
#include "reprostim/CaptureLog.h"

/*########################### Common macros ############################*/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <atomic>
#include <thread>
#include <mutex>
#include "CaptureLib.h"
#include "reprostim/CaptureLib.h"

//////////////////////////////////////////////////////////////////////////
// Macros
Expand Down
2 changes: 1 addition & 1 deletion Capture/capturelib/src/CaptureApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <csignal>
#include <thread>
#include <sysexits.h>
#include "CaptureApp.h"
#include "reprostim/CaptureApp.h"

namespace reprostim {

Expand Down
2 changes: 1 addition & 1 deletion Capture/capturelib/src/CaptureLib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include <csignal>
#include <sysexits.h>
#include <alsa/asoundlib.h>
#include "CaptureLib.h"
#include "reprostim/CaptureLib.h"


namespace fs = std::filesystem;
Expand Down
2 changes: 1 addition & 1 deletion Capture/capturelib/src/CaptureLog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include <filesystem>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include "CaptureLib.h"
#include "reprostim/CaptureLib.h"

namespace fs = std::filesystem;

Expand Down
20 changes: 20 additions & 0 deletions Capture/capturelib/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# CTests for capturelib project

project(reprostim-capturelib-tests)

add_executable(${PROJECT_NAME}
TestCaptureLib.cpp
TestCaptureLog.cpp
TestCaptureThreading.cpp
TestCaptureApp.cpp
)

target_link_libraries(
${PROJECT_NAME}
capturelib
Catch2::Catch2
)

include(CTest)
include(Catch)
catch_discover_tests(${PROJECT_NAME})
13 changes: 13 additions & 0 deletions Capture/capturelib/test/TestCaptureApp.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "reprostim/CaptureLib.h"
#include "reprostim/CaptureApp.h"
#include <catch2/catch.hpp>

using namespace reprostim;

// test for CaptureApp
TEST_CASE("TestCaptureApp_constructor_destructor",
"[capturelib][CaptureApp][constructor][destructor]") {
std::unique_ptr<CaptureApp> pApp = std::make_unique<CaptureApp>();
REQUIRE(pApp != nullptr);
pApp = nullptr;
}
63 changes: 63 additions & 0 deletions Capture/capturelib/test/TestCaptureLib.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#define CATCH_CONFIG_MAIN
#include "reprostim/CaptureLib.h"
#include <catch2/catch.hpp>

using namespace reprostim;
// Function to test
int add(int a, int b) {
return a + b;
}

TEST_CASE("TestCaptureLib_add",
"[capturelib][add]") {
// Test case 1
REQUIRE(add(1, 2) == 3);

// Test case 2
REQUIRE(add(3, 5) == 8);
}

TEST_CASE("TestCaptureLib_getTimeStr",
"[capturelib][getTimeStr]") {
std::string ts = getTimeStr();
REQUIRE(ts.length() == 23);

std::regex pattern(R"(\d{4}\.\d{2}\.\d{2}\.\d{2}\.\d{2}\.\d{2}\.\d{3})");

std::smatch match;
ts = getTimeStr();
REQUIRE(std::regex_search(ts, match, pattern));
}

// test for isSysBreakExec
TEST_CASE("TestCaptureLib_isSysBreakExec",
"[capturelib][isSysBreakExec]") {
REQUIRE(isSysBreakExec() == false);
setSysBreakExec(true);
REQUIRE(isSysBreakExec() == true);
setSysBreakExec(false);
}

// test for mmwcSdkVersion
TEST_CASE("TestCaptureLib_mwcSdkVersion",
"[capturelib][mwcSdkVersion]") {
std::string version = mwcSdkVersion();
REQUIRE(version.length() > 0);
REQUIRE(version == "3.3.1313");
}

// test for checkOutDir
TEST_CASE("TestCaptureLib_checkOutDir",
"[capturelib][checkOutDir]") {
std::string outDir = "/tmp";
REQUIRE(checkOutDir(outDir) == true);
}

// test for currentTimeMs
TEST_CASE("TestCaptureLib_currentTimeMs",
"[capturelib][currentTimeMs]") {
auto t1 = currentTimeMs();
SLEEP_MS(20);
auto t2 = currentTimeMs();
REQUIRE((t2 - t1) >= 20);
}
32 changes: 32 additions & 0 deletions Capture/capturelib/test/TestCaptureLog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include <iostream>
#include <fstream>
#include <filesystem>
#include "reprostim/CaptureLib.h"
#include "reprostim/CaptureLog.h"
#include <catch2/catch.hpp>

using namespace reprostim;

// test case for FileLogger
TEST_CASE("TestCaptureLog_FileLogger",
"[capturelib][CaptureLog][FileLogger]") {
FileLogger logger;
std::string fileName = "reprostim_test_capturelog_"+getTimeStr() + ".log";
std::filesystem::path logPath = std::filesystem::temp_directory_path() / fileName;
logger.open("test", logPath, LogLevel::INFO);
REQUIRE(logger.getName() == "test");
REQUIRE(logger.getFilePath() == logPath.string());
REQUIRE(logger.isDebugEnabled() == false);
REQUIRE(logger.isErrorEnabled() == true);
REQUIRE(logger.isInfoEnabled() == true);
REQUIRE(logger.isWarnEnabled() == true);
logger.debug_("debug message");
logger.info("info message");
logger.warn("warn message");
logger.error("error message");
logger.close();
REQUIRE(std::filesystem::exists(logPath));
if( std::filesystem::exists(logPath) ) {
std::filesystem::remove(logPath);
}
}
63 changes: 63 additions & 0 deletions Capture/capturelib/test/TestCaptureThreading.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "reprostim/CaptureLib.h"
#include "reprostim/CaptureThreading.h"
#include <catch2/catch.hpp>

using namespace reprostim;

using TestWorkerThread = WorkerThread<std::string>;
using TestSingleThreadExecutor = SingleThreadExecutor<TestWorkerThread>;

// override TestWorkerThread::run implementation
template<>
void TestWorkerThread::run() {
_INFO("run() enter");
while (true) {
if( isTerminated() ) {
_INFO("terminated " << m_params);
break;
}
_INFO("running... " << m_params);
SLEEP_MS(30);
}
_INFO("run() leave");
}

// test TestWorkerThread
TEST_CASE("TestCaptureThreading_WorkerThread",
"[capturelib][CaptureThreading][WorkerThread]") {
std::string params = "worker_"+getTimeStr();
TestWorkerThread* p = TestWorkerThread::newInstance(params);
REQUIRE(p->isRunning() == false);
REQUIRE(p->isTerminated() == false);
p->start();
REQUIRE(p->isRunning() == true);
REQUIRE(p->isTerminated() == false);
p->stop();
REQUIRE(p->isRunning() == false);
REQUIRE(p->isTerminated() == true);
TestWorkerThread::deleteInstance(p);
}

// test TestSingleThreadExecutor
TEST_CASE("TestCaptureThreading_SingleThreadExecutor",
"[capturelib][CaptureThreading][SingleThreadExecutor]") {
TestSingleThreadExecutor executor;
TestWorkerThread* pA = TestWorkerThread::newInstance("workerA_"+getTimeStr());
TestWorkerThread* pB = TestWorkerThread::newInstance("workerB_"+getTimeStr());

executor.schedule(pA);
REQUIRE(pA->isRunning() == true);
REQUIRE(pA->isTerminated() == false);

executor.schedule(pB);
REQUIRE(pA->isRunning() == false);
REQUIRE(pA->isTerminated() == true);
REQUIRE(pB->isRunning() == true);
REQUIRE(pB->isTerminated() == false);

executor.schedule(nullptr);
REQUIRE(pA->isRunning() == false);
REQUIRE(pA->isTerminated() == true);
REQUIRE(pB->isRunning() == false);
REQUIRE(pB->isTerminated() == true);
}
Binary file added Capture/docs/images/project_structure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading