chore: initial project scaffold from architecture design

- Add CLAUDE.md with project overview, tech stack, build commands,
  architecture description, coding standards, and sample images section
- Add full directory structure: src/, docs/, tests/, import/
- Add CMakeLists.txt with C++20, OpenCV/LibRaw/Qt6 dependencies,
  converter_core static lib, optional GUI, and GTest tests
- Add architecture documentation: ARCHITECTURE.md, PIPELINE.md, MODULES.md
- Add source skeletons for all pipeline stages:
  RawLoader, Preprocessor, NegativeDetector, Inverter, ColorCorrector,
  CropProcessor, OutputWriter, Pipeline, MainWindow, CliRunner, main.cpp
- Add initial test stubs for pipeline and rawloader
- Add sample ARW files in import/ for integration testing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christoph K.
2026-03-14 09:28:32 +01:00
commit 65b411b23d
34 changed files with 3191 additions and 0 deletions

102
tests/test_rawloader.cpp Normal file
View File

@@ -0,0 +1,102 @@
#include <gtest/gtest.h>
#include "converter/rawloader/RawLoader.h"
#include <filesystem>
using namespace photoconv;
#ifndef TEST_DATA_DIR
#define TEST_DATA_DIR "import"
#endif
static const std::filesystem::path kTestDataDir{TEST_DATA_DIR};
// ──────────────────────────────────────────────
// File validation tests
// ──────────────────────────────────────────────
TEST(RawLoaderTest, RejectsNonexistentFile) {
RawLoader loader;
auto result = loader.load("/nonexistent/file.arw");
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, ErrorCode::FileNotFound);
}
TEST(RawLoaderTest, RejectsUnsupportedFormat) {
// Create a temporary file with unsupported extension
auto temp = std::filesystem::temp_directory_path() / "test.xyz";
{
std::ofstream f(temp);
f << "dummy";
}
RawLoader loader;
auto result = loader.load(temp);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, ErrorCode::UnsupportedFormat);
std::filesystem::remove(temp);
}
// ──────────────────────────────────────────────
// RAW loading integration tests (require test data)
// ──────────────────────────────────────────────
TEST(RawLoaderTest, LoadsArwFile) {
auto arw_path = kTestDataDir / "DSC09246.ARW";
if (!std::filesystem::exists(arw_path)) {
GTEST_SKIP() << "Test data not available: " << arw_path;
}
RawLoader loader;
auto result = loader.load(arw_path);
ASSERT_TRUE(result.has_value()) << result.error().format();
// Verify 16-bit BGR output
EXPECT_EQ(result->rgb.type(), CV_16UC3);
EXPECT_GT(result->rgb.cols, 0);
EXPECT_GT(result->rgb.rows, 0);
// Verify metadata was populated
EXPECT_FALSE(result->metadata.camera_make.empty());
EXPECT_GT(result->metadata.raw_width, 0);
EXPECT_GT(result->metadata.raw_height, 0);
}
TEST(RawLoaderTest, MetadataContainsSonyMake) {
auto arw_path = kTestDataDir / "DSC09246.ARW";
if (!std::filesystem::exists(arw_path)) {
GTEST_SKIP() << "Test data not available: " << arw_path;
}
RawLoader loader;
auto result = loader.load(arw_path);
ASSERT_TRUE(result.has_value());
// Sony ARW files should have "Sony" as make
EXPECT_EQ(result->metadata.camera_make, "Sony");
}
// ──────────────────────────────────────────────
// Pixel integrity tests
// ──────────────────────────────────────────────
TEST(RawLoaderTest, OutputIsNonTrivial) {
auto arw_path = kTestDataDir / "DSC09246.ARW";
if (!std::filesystem::exists(arw_path)) {
GTEST_SKIP() << "Test data not available: " << arw_path;
}
RawLoader loader;
auto result = loader.load(arw_path);
ASSERT_TRUE(result.has_value());
// Image should have non-zero content (not all black or all white)
cv::Scalar mean_val = cv::mean(result->rgb);
EXPECT_GT(mean_val[0], 100.0); // Not all black
EXPECT_LT(mean_val[0], 65000.0); // Not all white
}