mirror of
https://github.com/tkmxqrdxddd/davinci-video-converter
synced 2026-03-29 15:25:33 +02:00
test: add unit tests for parser, validator, and converter modules
- Add test_parser.cpp with argument parsing tests - Add test_validator.cpp with configuration validation tests - Add test_converter.cpp with FFmpeg command building tests - Update Makefile with test targets for unit tests - Update CI workflow to run make test - Add test binaries and media files to .gitignore
This commit is contained in:
21
.github/workflows/ci.yml
vendored
21
.github/workflows/ci.yml
vendored
@@ -9,25 +9,16 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: sudo apt-get install -y build-essential ffmpeg wget
|
run: sudo apt-get update && sudo apt-get install -y build-essential ffmpeg
|
||||||
|
|
||||||
- name: Create Tests Directory
|
|
||||||
run: mkdir -p tests # Create the tests directory if it doesn't exist
|
|
||||||
|
|
||||||
- name: Download Sample Video
|
|
||||||
run: wget -O tests/sample_video.mp4 https://www.pexels.com/video/3195394/download/
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: make
|
||||||
g++ -o davinci-convert src/main.cpp -lstdc++fs
|
|
||||||
|
|
||||||
- name: Run
|
- name: Run Unit Tests
|
||||||
run: |
|
run: make test
|
||||||
echo "Running the Video Converter..."
|
|
||||||
./davinci-convert tests/sample_video.mp4 --output tests/output/video.mov
|
|
||||||
|
|||||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -36,4 +36,12 @@
|
|||||||
PKGBUILD
|
PKGBUILD
|
||||||
|
|
||||||
# Devcontainer
|
# Devcontainer
|
||||||
.devcontainer/.vscode
|
.devcontainer/.vscode
|
||||||
|
|
||||||
|
# Test files
|
||||||
|
tests/*.mp4
|
||||||
|
tests/*.mov
|
||||||
|
tests/*.mkv
|
||||||
|
tests/test_parser
|
||||||
|
tests/test_validator
|
||||||
|
tests/test_converter
|
||||||
27
Makefile
27
Makefile
@@ -28,11 +28,32 @@ install: $(TARGET)
|
|||||||
uninstall:
|
uninstall:
|
||||||
rm -f /usr/local/bin/$(TARGET)
|
rm -f /usr/local/bin/$(TARGET)
|
||||||
|
|
||||||
test: $(TARGET)
|
test: $(TARGET) build_tests
|
||||||
@echo "Running tests..."
|
@echo "Running integration tests..."
|
||||||
@./$(TARGET) --help
|
@./$(TARGET) --help
|
||||||
@echo "Test: Help command passed"
|
@echo "Test: Help command passed"
|
||||||
@./$(TARGET) 2>&1 | grep -q "Usage:" && echo "Test: No args passed" || (echo "Test failed" && exit 1)
|
@./$(TARGET) 2>&1 | grep -q "Usage:" && echo "Test: No args passed" || (echo "Test failed" && exit 1)
|
||||||
|
@echo "Running unit tests..."
|
||||||
|
@./tests/test_parser
|
||||||
|
@./tests/test_validator
|
||||||
|
@./tests/test_converter
|
||||||
@echo "All tests passed!"
|
@echo "All tests passed!"
|
||||||
|
|
||||||
.PHONY: all clean install uninstall test
|
build_tests: test_parser_bin test_validator_bin test_converter_bin
|
||||||
|
|
||||||
|
test_parser_bin: tests/test_parser.cpp src/parser.cpp src/include/config.hpp
|
||||||
|
@mkdir -p tests
|
||||||
|
$(CXX) $(CXXFLAGS) $(INCLUDES) -o tests/test_parser tests/test_parser.cpp src/parser.cpp
|
||||||
|
|
||||||
|
test_validator_bin: tests/test_validator.cpp src/validator.cpp src/include/config.hpp
|
||||||
|
@mkdir -p tests
|
||||||
|
$(CXX) $(CXXFLAGS) $(INCLUDES) -o tests/test_validator tests/test_validator.cpp src/validator.cpp
|
||||||
|
|
||||||
|
test_converter_bin: tests/test_converter.cpp src/converter.cpp src/include/config.hpp
|
||||||
|
@mkdir -p tests
|
||||||
|
$(CXX) $(CXXFLAGS) $(INCLUDES) -o tests/test_converter tests/test_converter.cpp src/converter.cpp
|
||||||
|
|
||||||
|
clean-tests:
|
||||||
|
rm -f tests/test_parser tests/test_validator tests/test_converter
|
||||||
|
|
||||||
|
.PHONY: all clean install uninstall test build_tests clean-tests
|
||||||
|
|||||||
141
tests/test_converter.cpp
Normal file
141
tests/test_converter.cpp
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#include "../src/include/converter.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
void test_build_command_basic() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "input.mp4";
|
||||||
|
config.output_path = "output.mp4";
|
||||||
|
|
||||||
|
std::string command = build_ffmpeg_command(config);
|
||||||
|
|
||||||
|
assert(command.find("ffmpeg") != std::string::npos);
|
||||||
|
assert(command.find("-i \"input.mp4\"") != std::string::npos);
|
||||||
|
assert(command.find("-c:v libx264") != std::string::npos);
|
||||||
|
assert(command.find("\"output.mp4\"") != std::string::npos);
|
||||||
|
|
||||||
|
std::cout << "test_build_command_basic: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_build_command_custom_codec() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "input.mov";
|
||||||
|
config.output_path = "output.mkv";
|
||||||
|
config.codec = "h265";
|
||||||
|
|
||||||
|
std::string command = build_ffmpeg_command(config);
|
||||||
|
|
||||||
|
assert(command.find("-c:v libx265") != std::string::npos);
|
||||||
|
assert(command.find("-i \"input.mov\"") != std::string::npos);
|
||||||
|
assert(command.find("\"output.mkv\"") != std::string::npos);
|
||||||
|
|
||||||
|
std::cout << "test_build_command_custom_codec: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_build_command_with_crf() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "input.mp4";
|
||||||
|
config.output_path = "output.mp4";
|
||||||
|
config.crf = 18;
|
||||||
|
|
||||||
|
std::string command = build_ffmpeg_command(config);
|
||||||
|
|
||||||
|
assert(command.find("-crf 18") != std::string::npos);
|
||||||
|
|
||||||
|
std::cout << "test_build_command_with_crf: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_build_command_quality_preset() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "input.mp4";
|
||||||
|
config.output_path = "output.mp4";
|
||||||
|
config.quality = "slow";
|
||||||
|
|
||||||
|
std::string command = build_ffmpeg_command(config);
|
||||||
|
|
||||||
|
assert(command.find("-preset slow") != std::string::npos);
|
||||||
|
|
||||||
|
std::cout << "test_build_command_quality_preset: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_build_command_fast_quality() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "input.mp4";
|
||||||
|
config.output_path = "output.mp4";
|
||||||
|
config.quality = "fast";
|
||||||
|
|
||||||
|
std::string command = build_ffmpeg_command(config);
|
||||||
|
|
||||||
|
assert(command.find("-preset fast") != std::string::npos);
|
||||||
|
|
||||||
|
std::cout << "test_build_command_fast_quality: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_build_command_medium_quality() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "input.mp4";
|
||||||
|
config.output_path = "output.mp4";
|
||||||
|
config.quality = "medium";
|
||||||
|
|
||||||
|
std::string command = build_ffmpeg_command(config);
|
||||||
|
|
||||||
|
assert(command.find("-preset medium") != std::string::npos);
|
||||||
|
|
||||||
|
std::cout << "test_build_command_medium_quality: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_build_command_prores() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "input.mov";
|
||||||
|
config.output_path = "output.mov";
|
||||||
|
config.codec = "prores";
|
||||||
|
|
||||||
|
std::string command = build_ffmpeg_command(config);
|
||||||
|
|
||||||
|
assert(command.find("-c:v prores_ks") != std::string::npos);
|
||||||
|
|
||||||
|
std::cout << "test_build_command_prores: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_build_command_verbose() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "input.mp4";
|
||||||
|
config.output_path = "output.mp4";
|
||||||
|
config.verbose = true;
|
||||||
|
|
||||||
|
std::string command = build_ffmpeg_command(config);
|
||||||
|
|
||||||
|
assert(command.find("-loglevel verbose") != std::string::npos);
|
||||||
|
|
||||||
|
std::cout << "test_build_command_verbose: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_build_command_audio_copy() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "input.mp4";
|
||||||
|
config.output_path = "output.mp4";
|
||||||
|
|
||||||
|
std::string command = build_ffmpeg_command(config);
|
||||||
|
|
||||||
|
assert(command.find("-c:a copy") != std::string::npos);
|
||||||
|
|
||||||
|
std::cout << "test_build_command_audio_copy: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "=== Converter Tests ===" << std::endl;
|
||||||
|
|
||||||
|
test_build_command_basic();
|
||||||
|
test_build_command_custom_codec();
|
||||||
|
test_build_command_with_crf();
|
||||||
|
test_build_command_quality_preset();
|
||||||
|
test_build_command_fast_quality();
|
||||||
|
test_build_command_medium_quality();
|
||||||
|
test_build_command_prores();
|
||||||
|
test_build_command_verbose();
|
||||||
|
test_build_command_audio_copy();
|
||||||
|
|
||||||
|
std::cout << "All converter tests passed!" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
80
tests/test_parser.cpp
Normal file
80
tests/test_parser.cpp
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#include "../src/include/parser.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
void test_parse_help() {
|
||||||
|
// Help triggers print_usage and exits, so we skip this test
|
||||||
|
std::cout << "test_parse_help: skipped (exits on --help)" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_parse_default_values() {
|
||||||
|
char* args[] = {(char*)"program", (char*)"input.mp4", (char*)"output.mp4", nullptr};
|
||||||
|
Config config = parse_arguments(3, args);
|
||||||
|
|
||||||
|
assert(config.input_path == "input.mp4");
|
||||||
|
assert(config.output_path == "output.mp4");
|
||||||
|
assert(config.codec == "h264");
|
||||||
|
assert(config.quality == "medium");
|
||||||
|
assert(config.crf == 23);
|
||||||
|
assert(config.verbose == false);
|
||||||
|
|
||||||
|
std::cout << "test_parse_default_values: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_parse_custom_values() {
|
||||||
|
char* args[] = {
|
||||||
|
(char*)"program",
|
||||||
|
(char*)"input.mov",
|
||||||
|
(char*)"output.mkv",
|
||||||
|
(char*)"-c", (char*)"hevc",
|
||||||
|
(char*)"-q", (char*)"slow",
|
||||||
|
(char*)"-r", (char*)"18",
|
||||||
|
(char*)"-v",
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
Config config = parse_arguments(10, args);
|
||||||
|
|
||||||
|
assert(config.input_path == "input.mov");
|
||||||
|
assert(config.output_path == "output.mkv");
|
||||||
|
assert(config.codec == "hevc");
|
||||||
|
assert(config.quality == "slow");
|
||||||
|
assert(config.crf == 18);
|
||||||
|
assert(config.verbose == true);
|
||||||
|
|
||||||
|
std::cout << "test_parse_custom_values: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_parse_long_options() {
|
||||||
|
char* args[] = {
|
||||||
|
(char*)"program",
|
||||||
|
(char*)"src.mp4",
|
||||||
|
(char*)"dst.mp4",
|
||||||
|
(char*)"--codec", (char*)"prores",
|
||||||
|
(char*)"--quality", (char*)"fast",
|
||||||
|
(char*)"--crf", (char*)"28",
|
||||||
|
(char*)"--verbose",
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
Config config = parse_arguments(10, args);
|
||||||
|
|
||||||
|
assert(config.input_path == "src.mp4");
|
||||||
|
assert(config.output_path == "dst.mp4");
|
||||||
|
assert(config.codec == "prores");
|
||||||
|
assert(config.quality == "fast");
|
||||||
|
assert(config.crf == 28);
|
||||||
|
assert(config.verbose == true);
|
||||||
|
|
||||||
|
std::cout << "test_parse_long_options: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "=== Parser Tests ===" << std::endl;
|
||||||
|
|
||||||
|
test_parse_default_values();
|
||||||
|
test_parse_custom_values();
|
||||||
|
test_parse_long_options();
|
||||||
|
|
||||||
|
std::cout << "All parser tests passed!" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
150
tests/test_validator.cpp
Normal file
150
tests/test_validator.cpp
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#include "../src/include/validator.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
void test_valid_config() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "tests/input.mp4";
|
||||||
|
config.output_path = "tests/output.mp4";
|
||||||
|
config.codec = "h264";
|
||||||
|
config.quality = "medium";
|
||||||
|
config.crf = 23;
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
bool result = validate_config(config, error);
|
||||||
|
|
||||||
|
assert(result == true);
|
||||||
|
assert(error.empty());
|
||||||
|
|
||||||
|
std::cout << "test_valid_config: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_missing_input() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "";
|
||||||
|
config.output_path = "tests/output.mp4";
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
bool result = validate_config(config, error);
|
||||||
|
|
||||||
|
assert(result == false);
|
||||||
|
assert(!error.empty());
|
||||||
|
|
||||||
|
std::cout << "test_missing_input: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_missing_output() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "tests/input.mp4";
|
||||||
|
config.output_path = "";
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
bool result = validate_config(config, error);
|
||||||
|
|
||||||
|
assert(result == false);
|
||||||
|
assert(!error.empty());
|
||||||
|
|
||||||
|
std::cout << "test_missing_output: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_invalid_codec() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "tests/input.mp4";
|
||||||
|
config.output_path = "tests/output.mp4";
|
||||||
|
config.codec = "invalid_codec";
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
bool result = validate_config(config, error);
|
||||||
|
|
||||||
|
assert(result == false);
|
||||||
|
assert(!error.empty());
|
||||||
|
|
||||||
|
std::cout << "test_invalid_codec: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_invalid_quality() {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "tests/input.mp4";
|
||||||
|
config.output_path = "tests/output.mp4";
|
||||||
|
config.quality = "ultra_invalid";
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
bool result = validate_config(config, error);
|
||||||
|
|
||||||
|
assert(result == false);
|
||||||
|
assert(!error.empty());
|
||||||
|
|
||||||
|
std::cout << "test_invalid_quality: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_crf_range() {
|
||||||
|
// CRF too low
|
||||||
|
Config config1;
|
||||||
|
config1.input_path = "tests/input.mp4";
|
||||||
|
config1.output_path = "tests/output.mp4";
|
||||||
|
config1.crf = -1;
|
||||||
|
|
||||||
|
std::string error1;
|
||||||
|
assert(validate_config(config1, error1) == false);
|
||||||
|
|
||||||
|
// CRF too high
|
||||||
|
Config config2;
|
||||||
|
config2.input_path = "tests/input.mp4";
|
||||||
|
config2.output_path = "tests/output.mp4";
|
||||||
|
config2.crf = 60;
|
||||||
|
|
||||||
|
std::string error2;
|
||||||
|
assert(validate_config(config2, error2) == false);
|
||||||
|
|
||||||
|
std::cout << "test_crf_range: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_valid_codecs() {
|
||||||
|
const char* valid_codecs[] = {"h264", "h265", "prores"};
|
||||||
|
|
||||||
|
for (const char* codec : valid_codecs) {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "tests/input.mp4";
|
||||||
|
config.output_path = "tests/output.mp4";
|
||||||
|
config.codec = codec;
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
bool result = validate_config(config, error);
|
||||||
|
assert(result == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "test_valid_codecs: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_valid_qualities() {
|
||||||
|
const char* valid_qualities[] = {"fast", "medium", "slow"};
|
||||||
|
|
||||||
|
for (const char* quality : valid_qualities) {
|
||||||
|
Config config;
|
||||||
|
config.input_path = "tests/input.mp4";
|
||||||
|
config.output_path = "tests/output.mp4";
|
||||||
|
config.quality = quality;
|
||||||
|
|
||||||
|
std::string error;
|
||||||
|
bool result = validate_config(config, error);
|
||||||
|
assert(result == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "test_valid_qualities: PASSED" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "=== Validator Tests ===" << std::endl;
|
||||||
|
|
||||||
|
test_valid_config();
|
||||||
|
test_missing_input();
|
||||||
|
test_missing_output();
|
||||||
|
test_invalid_codec();
|
||||||
|
test_invalid_quality();
|
||||||
|
test_crf_range();
|
||||||
|
test_valid_codecs();
|
||||||
|
test_valid_qualities();
|
||||||
|
|
||||||
|
std::cout << "All validator tests passed!" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user