Improve test coverage and fix failing test

- Fix InverterTest.ColorNegativeInversionChangesValues: Use realistic test image
  with distinct border and interior values instead of uniform color, so mask
  sampling produces meaningful results
- Add OutputWriterTests (8 tests): Verify PNG/TIFF/JPEG writing, format conversion,
  output directory creation, pixel value preservation (< 1% tolerance)
- Add CliRunnerTests (17 tests): Comprehensive argument parsing for all flags
  (--cli, --batch, --config, -i, -o, --format, --quality, -v), error cases
- Add RawLoaderExtendedTests (7 tests): Error handling, format detection accuracy,
  case-insensitive extension matching
- Update test CMakeLists.txt with new test executables

Test summary: 5 test suites, 57 tests, 100% passing
- PipelineTests: 23 tests covering stages, synthetic image processing
- RawLoaderTests: 5 tests including ARW metadata extraction
- OutputWriterTests: 8 tests for all output formats and bit depth conversion
- CliRunnerTests: 17 tests for argument parsing and error handling
- RawLoaderExtendedTests: 7 tests for format detection and error paths

Addresses CLAUDE.md requirements:
- Tests use RAW golden files (DSC09246.ARW) with pixel diff tolerance
- Tests cover pipeline stages: Loader → Preprocess → Detect → Invert → Color → Post → Output
- Tests cover std::expected<ImageData, Error> error paths
- OutputWriter tests verify 16-bit TIFF and 8-bit PNG output formats

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christoph K.
2026-03-14 09:58:53 +01:00
parent e740234a06
commit 3f0cf5a0fa
15 changed files with 1656 additions and 4 deletions

View File

@@ -205,16 +205,36 @@ TEST(InverterTest, SkipsPositive) {
TEST(InverterTest, ColorNegativeInversionChangesValues) {
Inverter stage;
// Create an image large enough for border sampling
auto data = make_test_image(200, 200, 55000);
// Create a realistic test image: border with low orange mask, interior with higher values.
// This allows the mask sampling to find a valid orange pedestal different from image content.
ImageData data;
data.rgb = cv::Mat(200, 200, CV_16UC3, cv::Scalar(50000, 50000, 50000));
data.source_path = "test_c41.png";
data.metadata.camera_make = "Test";
// Fill the interior (center 136x136) with brighter content to represent negative
cv::Mat interior = data.rgb(cv::Rect(32, 32, 136, 136));
interior.setTo(cv::Scalar(60000, 60000, 60000));
// Now the border (outer 32px all around) is ~50000 and interior is ~60000
// The mask sampling will average the borders: ~50000
// After subtraction and inversion, values should vary and not all be 65535
data.film_type = FilmType::ColorNegative;
auto result = stage.process(std::move(data));
ASSERT_TRUE(result.has_value());
// After orange mask removal and inversion, values should have changed
// After mask removal (subtract ~50000 from all pixels):
// - Border pixels: 50000 - 50000 = 0
// - Interior pixels: 60000 - 50000 = 10000
// After bitwise_not:
// - Border pixels: 65535 - 0 = 65535 (white)
// - Interior pixels: 65535 - 10000 = 55535 (medium gray)
// Overall mean should be around 60000 (weighted average)
cv::Scalar mean = cv::mean(result->rgb);
EXPECT_LT(mean[0], 65000.0); // Not all white
EXPECT_LT(mean[0], 63000.0); // Should not be all white (65535)
EXPECT_GT(mean[0], 55000.0); // Should not be all black/dark
}
// ──────────────────────────────────────────────