diff --git a/.github/workflows/build-makefile.yml b/.github/workflows/build-makefile.yml index 2fd487c..c302274 100644 --- a/.github/workflows/build-makefile.yml +++ b/.github/workflows/build-makefile.yml @@ -5,6 +5,12 @@ on: pull_request: workflow_dispatch: +permissions: + # Required by dorny/test-reporter@v1 + id-token: write + contents: read + checks: write + concurrency: group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' cancel-in-progress: true @@ -53,16 +59,84 @@ jobs: run: | ./pngcheck -h - # - name: Prepare artifacts (Ubuntu) - # run: | - # mkdir -p artifacts - # cp pngcheck artifacts/pngcheck-${{ matrix.name }}-makefile - # cd artifacts - # if command -v sha256sum >/dev/null 2>&1; then - # sha256sum * > checksums.txt - # elif command -v shasum >/dev/null 2>&1; then - # shasum -a 256 * > checksums.txt - # fi + # Setup Ruby 3.4 for Ceedling + - name: Setup Ruby 3.4 + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.4' + bundler-cache: true + + # Cache PNG suite test files + - name: Cache PNG suite test files + uses: actions/cache@v4 + with: + path: test/fixtures/pngsuite + key: pngsuite-fixtures-${{ hashFiles('test/generate_expectations.sh') }} + restore-keys: | + pngsuite-fixtures- + + # Run tests using Ceedling + - name: Run pngcheck test suite with Ceedling (Ubuntu) + env: + PNGCHECK_EXECUTABLE: ${{ github.workspace }}/pngcheck + PNGSUITE_PATH: test/fixtures/pngsuite + EXPECTATIONS_PATH: expectations + run: | + bundle exec ceedling test:all + + - name: List out all files in directory + shell: bash + run: | + echo "Current directory: $(pwd)" + echo "Directory contents:" + ls -laR || echo "No files found" + + # Upload test reports as artifacts + - name: Upload test reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-reports-${{ matrix.name }}-makefile + path: | + build/artifacts/test/ + build/logs/ + *.xml + *.json + retention-days: 30 + + # Upload test results for GitHub's test reporting + - name: Publish Test Results + if: always() + uses: dorny/test-reporter@v1 + with: + name: Unity Tests (${{ matrix.name }}, Makefile) + path: 'build/artifacts/test/junit_tests_report.xml' + reporter: java-junit + fail-on-error: true + + # Upload test results on failure for debugging + - name: Upload test results on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: test-results-${{ matrix.name }}-makefile + path: | + test/fixtures/ + test/expectations/ + *.log + retention-days: 7 + + # Artifacts + - name: Prepare artifacts (Ubuntu) + run: | + mkdir -p artifacts + cp pngcheck artifacts/pngcheck-${{ matrix.name }}-makefile + cd artifacts + if command -v sha256sum >/dev/null 2>&1; then + sha256sum * > checksums.txt + elif command -v shasum >/dev/null 2>&1; then + shasum -a 256 * > checksums.txt + fi # - name: Upload artifacts # uses: actions/upload-artifact@v4 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec66ce8..fcc9fea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,10 +1,16 @@ -name: build-cmake +name: build on: push: branches: [ main ] pull_request: workflow_dispatch: +permissions: + # Required by dorny/test-reporter@v1 + id-token: write + contents: read + checks: write + concurrency: group: '${{ github.workflow }}-${{ github.job }}-${{ github.head_ref || github.ref_name }}' cancel-in-progress: true @@ -24,8 +30,8 @@ jobs: run: | echo "matrix=$(jq -c '.' matrix.json)" >> $GITHUB_OUTPUT - build-cmake: - name: ${{ matrix.name }}-cmake + build: + name: Build ${{ matrix.name }} runs-on: ${{ matrix.runner }} needs: load-matrix @@ -65,7 +71,12 @@ jobs: - name: Install dependencies (macOS) if: matrix.os == 'macos' run: | - brew list zlib || brew install zlib + # Check if zlib is installed, install if not + if ! brew list zlib &>/dev/null; then + brew install zlib + else + echo "zlib already installed" + fi # Windows dependencies - name: Setup MSVC (Windows) @@ -78,56 +89,59 @@ jobs: $arch = if ("${{ matrix.runner }}" -eq "windows-11-arm") { "arm64" } else { "x64" } vcpkg install zlib:$arch-windows - # CMake builds - - name: Build with CMake (Ubuntu/macOS) - if: matrix.os == 'ubuntu' || matrix.os == 'macos' + # CMake builds using presets + - name: Build with CMake (macOS) + if: matrix.os == 'macos' + run: | + cmake --preset Debug \ + -DCMAKE_OSX_ARCHITECTURES=${{ matrix.runner == 'macos-13' && 'x86_64' || 'arm64' }} + cmake --build build --preset Debug + + - name: Build with CMake (Ubuntu) + if: matrix.os == 'ubuntu' run: | - cmake -B build-cmake \ - -DCMAKE_BUILD_TYPE=Release \ - -DPNGCHECK_USE_SYSTEM_ZLIB=ON - cmake --build build-cmake --config Release + cmake --preset Debug + cmake --build build --preset Debug - name: Build with CMake (Windows MSVC) if: matrix.os == 'windows' run: | $arch = if ("${{ matrix.runner }}" -eq "windows-11-arm") { "ARM64" } else { "x64" } - cmake -B build-cmake -A $arch -DCMAKE_BUILD_TYPE=Release -DPNGCHECK_USE_SYSTEM_ZLIB=ON -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake - cmake --build build-cmake --config Release + cmake --preset Debug -A $arch -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake + cmake --build build --preset Debug - name: Build with CMake (Windows MSYS2) if: matrix.os == 'windows-msys2' shell: msys2 {0} run: | - cmake -B build-cmake \ - -G "MSYS Makefiles" \ - -DCMAKE_BUILD_TYPE=Release \ - -DPNGCHECK_USE_SYSTEM_ZLIB=ON - cmake --build build-cmake --config Release + cmake --preset Debug -G "MSYS Makefiles" + cmake --build build --preset Debug - # Testing CMake builds - - name: Test CMake build (MSYS2) + # Test that the executable works + - name: Test executable (MSYS2) if: matrix.os == 'windows-msys2' shell: msys2 {0} run: | - build-cmake/pngcheck.exe -h + build/Debug/pngcheck.exe -h - - name: Test CMake build (Windows MSVC) + # TODO: No idea why MSVC builds at Debug/Debug/pngcheck.exe... + - name: Test executable (Windows MSVC) if: matrix.os == 'windows' run: | - build-cmake/Release/pngcheck.exe -h + build/Debug/Debug/pngcheck.exe -h - - name: Test CMake build (Unix/macOS) + - name: Test executable (Unix/macOS) if: matrix.os == 'ubuntu' || matrix.os == 'macos' run: | - ./build-cmake/pngcheck -h + ./build/Debug/pngcheck -h - # Artifacts + # Prepare and upload build artifacts - name: Prepare artifacts (MSYS2) if: matrix.os == 'windows-msys2' shell: msys2 {0} run: | mkdir -p artifacts - cp build-cmake/pngcheck.exe artifacts/pngcheck-${{ matrix.name }}-cmake.exe + cp build/Debug/pngcheck.exe artifacts/pngcheck-${{ matrix.name }}-cmake.exe cd artifacts sha256sum * > checksums.txt @@ -135,7 +149,7 @@ jobs: if: matrix.os == 'windows' run: | mkdir -p artifacts - cp build-cmake/Release/pngcheck.exe artifacts/pngcheck-${{ matrix.name }}-cmake.exe + cp build/Debug/Debug/pngcheck.exe artifacts/pngcheck-${{ matrix.name }}-cmake.exe cd artifacts Get-FileHash -Algorithm SHA256 *.exe | ForEach-Object { "$($_.Hash.ToLower()) $($_.Path | Split-Path -Leaf)" } > checksums.txt @@ -143,7 +157,7 @@ jobs: if: matrix.os == 'ubuntu' || matrix.os == 'macos' run: | mkdir -p artifacts - cp build-cmake/pngcheck artifacts/pngcheck-${{ matrix.name }}-cmake + cp build/Debug/pngcheck artifacts/pngcheck-${{ matrix.name }}-cmake cd artifacts if command -v sha256sum >/dev/null 2>&1; then sha256sum * > checksums.txt @@ -151,9 +165,237 @@ jobs: shasum -a 256 * > checksums.txt fi - - name: Upload artifacts + - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: pngcheck-${{ matrix.name }}-cmake path: artifacts/ retention-days: 30 + + load-test-matrix: + name: Load Test Matrix + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.filter-matrix.outputs.matrix }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Filter matrix for testing + id: filter-matrix + run: | + # Filter out MSYS2 entries from the matrix for testing + echo "matrix=$(jq -c '[.[] | select(.os != "windows-msys2")]' matrix.json)" >> $GITHUB_OUTPUT + + test: + name: Test ${{ matrix.name }} + runs-on: ${{ matrix.runner }} + needs: [load-test-matrix, build] + + strategy: + fail-fast: false + matrix: + include: ${{ fromJson(needs.load-test-matrix.outputs.matrix) }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + # Ubuntu dependencies + - name: Install dependencies (Ubuntu) + if: matrix.os == 'ubuntu' + run: | + sudo apt-get update + sudo apt-get install -y cmake build-essential zlib1g-dev + + # macOS dependencies + - name: Install dependencies (macOS) + if: matrix.os == 'macos' + run: | + # Check if zlib is installed, install if not + if ! brew list zlib &>/dev/null; then + brew install zlib + else + echo "zlib already installed" + fi + + # Windows dependencies + - name: Setup MSVC (Windows) + if: matrix.os == 'windows' + uses: microsoft/setup-msbuild@v2 + + - name: Install vcpkg dependencies (Windows) + if: matrix.os == 'windows' + run: | + $arch = if ("${{ matrix.runner }}" -eq "windows-11-arm") { "arm64" } else { "x64" } + vcpkg install zlib:$arch-windows + + # Download build artifacts from the build job + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: pngcheck-${{ matrix.name }}-cmake + path: artifacts/ + + # Rebuild for testing (needed for CMake test targets) + - name: Build with CMake (macOS) + if: matrix.os == 'macos' + run: | + cmake --preset Debug \ + -DCMAKE_OSX_ARCHITECTURES=${{ matrix.runner == 'macos-13' && 'x86_64' || 'arm64' }} + cmake --build build --preset Debug + + - name: Build with CMake (Ubuntu) + if: matrix.os == 'ubuntu' + run: | + cmake --preset Debug + cmake --build build --preset Debug + + - name: Build with CMake (Windows MSVC) + if: matrix.os == 'windows' + run: | + $arch = if ("${{ matrix.runner }}" -eq "windows-11-arm") { "ARM64" } else { "x64" } + cmake --preset Debug -A $arch -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake + cmake --build build --preset Debug + + # Setup Ruby for testing + - name: Setup Ruby 3.4 + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.4' + bundler-cache: true + + # Cache PNG suite test files + - name: Cache PNG suite test files + uses: actions/cache@v4 + with: + path: test/fixtures/pngsuite + key: pngsuite-fixtures-${{ hashFiles('test/generate_expectations.sh') }} + restore-keys: | + pngsuite-fixtures- + + # Run tests using CMake targets + - name: Run tests with CMake (Unix/macOS) + if: matrix.os == 'ubuntu' || matrix.os == 'macos' + env: + PNGCHECK_EXECUTABLE: ${{ github.workspace }}/build/Debug/pngcheck + run: | + cmake --build build --target test-all --preset Debug + + # TODO: Unable to fix CMake when using the test/bin/pngcheck-test script on Windows + # It keeps saying "no such file or directory" when we try to execute the script. + # Hence we directly run Ceedling. + - name: Run tests with CMake (Windows) + if: matrix.os == 'windows' + env: + PNGCHECK_EXECUTABLE: ${{ github.workspace }}\build\Debug\Debug\pngcheck.exe + run: | + bundle exec ceedling test:all + + # Upload test reports as artifacts + - name: Upload test reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-reports-${{ matrix.name }}-cmake + path: | + build/artifacts/test/ + build/logs/ + *.xml + *.json + retention-days: 30 + + # Upload test results for GitHub's test reporting + - name: Publish Test Results + if: always() + uses: dorny/test-reporter@v1 + with: + name: Unity Tests (${{ matrix.name }}, CMake) + path: 'build/artifacts/test/junit_tests_report.xml' + reporter: java-junit + fail-on-error: true + + # Upload test results on failure for debugging + - name: Upload test results on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: test-results-${{ matrix.name }} + path: | + test/fixtures/ + test/expectations/ + *.log + retention-days: 7 + + coverage: + name: Coverage report + runs-on: ubuntu-latest + needs: build + if: github.event_name == 'push' || github.event_name == 'pull_request' + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + # Ubuntu dependencies + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y cmake build-essential zlib1g-dev + + # Setup Ruby 3.4 for Ceedling + - name: Setup Ruby 3.4 + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.4' + bundler-cache: true + + # Install gcovr for coverage reporting + - name: Install gcovr for coverage reporting + run: | + pip install gcovr + + # Cache PNG suite test files + - name: Cache PNG suite test files + uses: actions/cache@v4 + with: + path: test/fixtures/pngsuite + key: pngsuite-fixtures-${{ hashFiles('test/generate_expectations.sh') }} + restore-keys: | + pngsuite-fixtures- + + # Build with coverage flags + - name: Build with coverage + run: | + cmake --preset Debug + cmake --build build --preset Debug + + # Run tests with coverage analysis + - name: Run tests with coverage + continue-on-error: true + run: | + cmake --build build --target test-coverage --preset Debug + + # Upload coverage report + - name: Upload coverage report + if: always() + continue-on-error: true + uses: actions/upload-artifact@v4 + with: + name: coverage-report-html + path: build/artifacts/gcov/ + retention-days: 30 + + # # Upload coverage to Codecov (optional) + # - name: Upload coverage to Codecov + # if: always() + # continue-on-error: true + # uses: codecov/codecov-action@v4 + # with: + # files: build/artifacts/gcov/coverage.xml + # fail_ci_if_error: false + # verbose: true diff --git a/.gitignore b/.gitignore index cad8d2e..0be6c3b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ *.obj *.res a.out +pngcheck # Debugging artifacts .gdbinit @@ -94,6 +95,9 @@ xcuserdata/ [Bb]ackup*/ CMakeLists.txt.user* CMakeUserPresets.json +CMakeCache.txt +CMakeFiles/ +_deps/ # Build, test and CI output directories [._]build*/ @@ -103,3 +107,17 @@ CMakeUserPresets.json [Dd]ebug*/ *[Rr]elease/ [Rr]elease*/ + +# Test expectations (generated during tests) +test/expectations/pngsuite + +# Unity/Ceedling build artifacts +.ceedling/ +Gemfile.lock +test/build/ +test/CMakeFiles/ +test/Makefile +test/unity/build/ +test/unity/test_pngcheck +test/unity/test_pngcheck_suite +vendor/ diff --git a/CHANGELOG b/CHANGELOG index 727d271..50d9cc4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -291,7 +291,7 @@ * 20250604 CT: Required the zlib library as a non-optional dependency, and removed the need to define the USE_ZLIB macro externally * 20250605 CT: Added third_party/wildargs to auto-expand wildcard arguments - * 20250707 RT: Prevent hard fails on chunks of unknown type in compliance with - PNG spec. By Maxim Samsonov (Ribose), metanorma/pngcheck-metanorma#1. * 20250708 RT: Added CI workflows and Windows Makefiles (MSVC and MinGW) for GitHub Actions on Linux, macOS, and Windows. + * 20250718 RT: Added test framework with command-line tests with CI integration + for CMake (all platforms) and Makefile (Ubuntu) builds. diff --git a/CMakeLists.txt b/CMakeLists.txt index 84cdbdb..f9cb14e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,21 @@ project(pngcheck LANGUAGES C ) +# Build strategy selection +if(NOT DEFINED BUILD_STRATEGY) + set(BUILD_STRATEGY "RELEASE" CACHE STRING "Build strategy: RELEASE or TEST") +endif() + +if(BUILD_STRATEGY STREQUAL "TEST") + message(STATUS "TEST strategy selected - Use Ceedling for testing") + message(STATUS "Run: ceedling test:all") + message(FATAL_ERROR "Please use Ceedling for testing. This CMake configuration is for release builds only.") +elseif(BUILD_STRATEGY STREQUAL "RELEASE") + message(STATUS "RELEASE strategy selected - Building production executable") +else() + message(FATAL_ERROR "Invalid BUILD_STRATEGY: ${BUILD_STRATEGY}. Must be RELEASE or TEST.") +endif() + # Options for zlib support option(PNGCHECK_USE_SYSTEM_ZLIB "Use the system-installed zlib" ON) if(PNGCHECK_USE_ZLIB) @@ -21,16 +36,27 @@ endif() # Executables add_executable(pngcheck ${PNGCHECK_SOURCES}) +# Compiler flags for release build +if(MSVC) + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /W3") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Zi /W3") +else() + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 -Wall") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -Wall") +endif() + # Dependency handling if(PNGCHECK_USE_SYSTEM_ZLIB) find_package(ZLIB) if(ZLIB_FOUND) target_link_libraries(pngcheck PRIVATE ZLIB::ZLIB) + message(STATUS "Using system ZLIB: ${ZLIB_LIBRARIES}") else() message(WARNING "System ZLIB not found, falling back to FetchContent") set(PNGCHECK_USE_SYSTEM_ZLIB OFF) endif() endif() + if(NOT PNGCHECK_USE_SYSTEM_ZLIB) include(FetchContent) FetchContent_Declare(zlib @@ -47,6 +73,11 @@ if(NOT PNGCHECK_USE_SYSTEM_ZLIB) target_link_libraries(pngcheck PRIVATE zlibstatic) endif() +# Math library for some platforms +if(UNIX AND NOT APPLE) + target_link_libraries(pngcheck PRIVATE m) +endif() + # Installation rules include(GNUInstallDirs) install(TARGETS pngcheck @@ -64,3 +95,39 @@ set(CPACK_PACKAGE_NAME ${PROJECT_NAME}) set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_DESCRIPTION}) include(CPack) + +# Include test subdirectory for Ceedling integration +add_subdirectory(test) + +# Alias targets for backward compatibility +add_custom_target(test + DEPENDS test-all + COMMENT "Running tests with Ceedling (alias for test-all)" +) + +add_custom_target(clean-test + DEPENDS test-clean + COMMENT "Cleaning Ceedling build artifacts (alias for test-clean)" +) + +# Print build information +message(STATUS "") +message(STATUS "=== pngcheck Build Configuration ===") +message(STATUS "Build Strategy: ${BUILD_STRATEGY}") +message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}") +message(STATUS "C Compiler: ${CMAKE_C_COMPILER}") +message(STATUS "System ZLIB: ${PNGCHECK_USE_SYSTEM_ZLIB}") +if(PNGCHECK_USE_SYSTEM_ZLIB AND ZLIB_FOUND) + message(STATUS "ZLIB Version: ${ZLIB_VERSION_STRING}") +endif() +message(STATUS "") +message(STATUS "Available targets:") +message(STATUS " pngcheck - Build the main executable") +message(STATUS " test - Run Ceedling tests (alias for test-all)") +message(STATUS " test-all - Run all Ceedling tests") +message(STATUS " test-verbose - Run Ceedling tests with verbose output") +message(STATUS " test-clean - Clean Ceedling build artifacts") +message(STATUS " test-coverage - Run tests with coverage analysis and generate HTML report") +message(STATUS " clean-test - Clean Ceedling build artifacts (alias)") +message(STATUS " install - Install pngcheck and man page") +message(STATUS "") diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..974dd18 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,63 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "hidden": true, + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "PNGCHECK_USE_SYSTEM_ZLIB": "ON" + }, + "environment": { + "PNGSUITE_PATH": "test/fixtures/pngsuite", + "EXPECTATIONS_PATH": "expectations", + "ZLIB_CFLAGS": "", + "ZLIB_LIBS": "-lz" + } + }, + { + "name": "Debug", + "inherits": "default", + "displayName": "Debug Build", + "description": "Debug build with symbols and no optimization" + }, + { + "name": "Release", + "inherits": "default", + "displayName": "Release Build", + "description": "Optimized release build" + } + ], + "buildPresets": [ + { + "name": "Debug", + "configurePreset": "Debug", + "displayName": "Build Debug" + }, + { + "name": "Release", + "configurePreset": "Release", + "displayName": "Build Release" + } + ], + "testPresets": [ + { + "name": "test-all", + "configurePreset": "Debug", + "displayName": "Run All Tests", + "description": "Run all Ceedling tests" + }, + { + "name": "test-coverage", + "configurePreset": "Debug", + "displayName": "Run Tests with Coverage", + "description": "Run tests and generate coverage report" + } + ] +} diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..947a6ca --- /dev/null +++ b/Gemfile @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gem 'base64' +gem 'ceedling', '~> 1.0' +gem 'liquid', '~> 5.4' +gem 'paint', '~> 2.3' +gem 'rake' +gem 'rubyzip', '~> 2.3' +gem 'thor', '~> 1.3' diff --git a/INSTALL.md b/INSTALL.md index 4acd151..782d840 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -12,33 +12,33 @@ #### Unix/Linux/etc. -1. Create a build directory: +1. Prepare the build directory through the CMake preset: ```sh - mkdir build && cd build + cmake --preset Release ``` -2. Configure with one of these options: +2. Configure if necessary with one of these options: ```sh # Default build with the system-installed zlib (if available) - cmake .. + # No additional configuration needed # Build with a locally-downloaded zlib - cmake -DPNGCHECK_USE_SYSTEM_ZLIB=OFF .. + cmake -DPNGCHECK_USE_SYSTEM_ZLIB=OFF --preset Release ``` 3. Build: ```sh - cmake --build . + cmake --build build --target pngcheck --preset Release ``` 4. Install (optional): ```sh # Depending your system setup, you might need to use 'sudo' - cmake --install . + cmake --install build --preset Release ``` #### Windows (MSVC with vcpkg) @@ -53,9 +53,9 @@ 2. Configure and build: ```cmd - cmake -B build -A x64 -DCMAKE_BUILD_TYPE=Release -DPNGCHECK_USE_SYSTEM_ZLIB=ON -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake - # For ARM64: cmake -B build -A ARM64 -DCMAKE_BUILD_TYPE=Release -DPNGCHECK_USE_SYSTEM_ZLIB=ON -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake - cmake --build build --config Release + cmake -B build -A x64 --preset Release -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake + # For ARM64: cmake -B build -A ARM64 --preset Release -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake + cmake --build build --preset Release ``` #### Windows (MSYS2/MinGW) @@ -70,8 +70,8 @@ 2. Configure and build: ```sh - cmake -B build -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release -DPNGCHECK_USE_SYSTEM_ZLIB=ON - cmake --build build --config Release + cmake -B build -G "MSYS Makefiles" --preset Release + cmake --build build --preset Release ``` #### macOS (with Homebrew zlib) diff --git a/Makefile b/Makefile index 4964720..6dc2559 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,26 @@ pngcheck$(EXEEXT): pngcheck.o $(LD) $(ALL_LDFLAGS) -o $@ pngcheck.c $(LIBS) +# test targets -------------------------------------------------------------- + +test: $(EXES) + cd test && ceedling test:all + +test-cli: $(EXES) + cd test && ceedling test:test_pngcheck_cli + +test-suite: $(EXES) + cd test && ceedling test:test_pngcheck_suite + +test-clean: + cd test && ceedling clean + +test-setup: + cd test && bundle exec test/bin/pngcheck-test setup + # maintenance --------------------------------------------------------------- clean: $(RM_F) $(EXES) $(OBJS) + +clean-all: clean test-clean diff --git a/README.md b/README.md index c8597c9..747b09d 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ and is [no longer valid](https://w3c.github.io/png/#5ChunkOrdering) in PNG Third Sample usage: -```text +```sh $ pngcheck -c -v test_pattern-PQ.png File: test_pattern-PQ.png (12033 bytes) chunk IHDR at offset 0x0000c, length 13 @@ -40,3 +40,12 @@ File: test_pattern-PQ.png (12033 bytes) Old version of CLLI, do not use ERRORS DETECTED in test_pattern-PQ.png ``` + +## Topics + +- [Installation](INSTALL.md) +- [Testing](TESTING.md) + +## Copyright and license + +See [LICENSE](LICENSE) for license information. diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..7a8a36e --- /dev/null +++ b/TESTING.md @@ -0,0 +1,411 @@ +# Testing pngcheck + +## Introduction + +This document describes the comprehensive testing framework for the `pngcheck` +utility, which uses a hybrid CMake/Ceedling/Unity approach to ensure robust PNG +validation across all platforms. + +## Architecture + +The `test/` directory contains the test suite for the `pngcheck` utility, which +is used to guard against regressions on PNG validation. + +The test files are sourced from the [PngSuite](http://www.schaik.com/pngsuite/) +collection, which provides a comprehensive set of PNG test files designed to +test the robustness and compliance of PNG processing tools. + +The test framework uses [Unity](https://github.com/ThrowTheSwitch/Unity) C +testing framework with [Ceedling](https://github.com/ThrowTheSwitch/Ceedling) +build system to run comprehensive tests against the `pngcheck` executable. + +The CMake build infrastructure seamlessly integrates with Ceedling, allowing you +to run tests directly from CMake without needing to manually invoke Ceedling +commands. + +**Note:** The CMake/Ceedling integration architecture follows best practices +adopted from the structure of the +[apm32-ceedling-example](https://github.com/LuckkMaker/apm32-ceedling-example) +repository. + + +``` +┌────────────────────────────┬──────────────────────────────┬──────────────────────────────┐ +│ CMake (build) │ CMake Targets (bridge) │ Ceedling (test) │ +├────────────────────────────┼──────────────────────────────┼──────────────────────────────┤ +│ ┌────────────────────────┐ │ ┌──────────────────────────┐ │ ┌──────────────────────────┐ │ +│ │ Cross-platform │ │ │ Test targets │ │ │ Unity framework │ │ +│ │ compilation │ │ │ │ │ │ (C testing library) │ │ +│ │ │ │ │ • test-all │ │ │ │ │ +│ │ • linux (gcc) │ │ │ • test-verbose │ │ │ • assertions │ │ +│ │ • macos (clang) │ │ │ • test-coverage │ │ │ • test runners │ │ +│ │ • windows (msvc/msys2) │ │ │ • test-clean │ │ │ • mocks (unused) │ │ +│ └────────────────────────┘ │ └──────────────────────────┘ │ └──────────────────────────┘ │ +│ ┌────────────────────────┐ │ ┌──────────────────────────┐ │ ┌──────────────────────────┐ │ +│ │ Build dependencies │ │ │ Integration │ │ │ Test management │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ • system zlib │ │ │ • auto executable path │ │ │ • fixtures │ │ +│ │ • automatic fallback │ │ │ • dependency management │ │ │ • expectation generation │ │ +│ │ • vcpkg (windows) │ │ │ • environment setup │ │ │ • report generation │ │ +│ └────────────────────────┘ │ └──────────────────────────┘ │ └──────────────────────────┘ │ +└────────────────────────────┴──────────────────────────────┴──────────────────────────────┘ +``` + +## Overview + +- **Test files**: C test files for CLI and PNG suite tests +- **Fixtures**: PNG files from the PngSuite collection +- **Expectations**: Expected outputs for each test case +- **CLI tests**: Static tests for command-line interface functionality +- **PNG suite tests**: Auto-generated tests using the PngSuite image test suite +- **Helper functions**: Reusable test utilities and assertions +- **Configuration**: Ceedling project configuration file (`project.yml`) +- **Management tool**: Ruby CLI to manage test files and expectations + +As the Ceedling framework is written in Ruby, the test suite uses a Ruby +command-line tool (`test/bin/pngcheck-test`) to manage the test files, +expectations, and to run the tests. + + +## Prerequisites + +### Overview + +- **CMake**: Required for building the project and running tests +- **Ruby**: Required for running the Ceedling test framework +- **Ceedling**: Ruby-based testing framework (installed via Bundler) +- **Unity**: C testing framework (included with Ceedling) +- **Python**: Required for coverage analysis (optional, for coverage targets) +- **gcovr**: Python package for coverage analysis (optional, for coverage targets) + +### Installation + +- **CMake**: Install from your package manager or download from + [cmake.org](https://cmake.org/download/) + +- **Ruby**: Install from your package manager or download from + [ruby-lang.org](https://www.ruby-lang.org/en/downloads/) + +- **Bundler**: Install via RubyGems: + ```bash + gem install bundler + ``` + +- **Ceedling**: Install via Bundler: + ```bash + bundle install + ``` + +- **Python**: Install from your package manager or download from + [python.org](https://www.python.org/downloads/) + +- **gcovr**: Install via pip: + ```bash + pip install gcovr + ``` + + +## Quick start + +### Using CMake (Recommended) + +```bash +# Configure pngcheck +cmake --preset Debug + +# Build pngcheck +cmake --build build --target pngcheck --preset Debug + +# Run all tests (PNGCHECK_EXECUTABLE is set automatically) +cmake --build build --preset Debug --target test-all + +# Run tests with verbose output +cmake --build build --preset Debug --target test-verbose + +# Run specific test suites +cmake --build build --preset Debug --target test-pngcheck-cli # CLI tests +cmake --build build --preset Debug --target test-pngcheck-suite # PNG suite tests + +# Run tests with coverage analysis and generate HTML report (requires gcovr) +cmake --build build --preset Debug --target test-coverage + +# Clean test artifacts +cmake --build build --preset Debug --target test-clean +``` + +### Using Ceedling directly + +```bash +# Install dependencies +bundle install + +# Run all tests (requires pngcheck in PATH or PNGCHECK_EXECUTABLE set) +bundle exec ceedling test:all + +# Run specific test suites +bundle exec ceedling test:test_pngcheck_cli # CLI tests +bundle exec ceedling test:test_pngcheck_suite # PNG suite tests +``` + + +## Running tests in CMake + +The project integrates CMake with Ceedling tests such that all test targets are +available via CMake. This allows you to run tests without needing to interact +with Ceedling. + + +### Available CMake targets + +#### Test targets + +##### `test-all` + +Run all Ceedling tests (alias: `test`). + +The JUnit XML test report gets generated at +`build/artifacts/test/junit_tests_report.xml`. + +If Ceedling is not installed, the target provides helpful instructions +on how to install it. + +Ceedling is a Ruby-based testing framework, so you need to have Ruby and +Bundler installed. You can install Ceedling using Bundler: + +```bash +# Ensure you have Ruby installed +ruby --version + +# Install Bundler if not already installed +gem install bundler + +# Install Ceedling through Bundler +# At the root of the project, run: +bundle install +``` + +##### `test-verbose` + +Run all tests with verbose output. + +##### `test-clean` + +Clean Ceedling test artifacts (alias: `clean-test`). + +##### `test-coverage` + +Run tests with coverage analysis and generate HTML report (requires `gcovr`). + +When `gcovr` is installed, coverage targets generate both console output and +HTML reports in `build/artifacts/gcov/`. The HTML report is located at +`build/artifacts/gcov/index.html`. + +**Note:** Prerequisites need to be satisfied to use this tool (see +[prerequisites](#prerequisites)). + +##### `test-pngcheck-cli` + +Run CLI-specific tests only. + +##### `test-pngcheck-suite` + +Run PNG suite tests only. + +#### Targets for test case generation + +##### `pngsuite-status` + +Check the status of PNG suite test files. Shows which files are present and missing. + +##### `generate-pngsuite-expectations` + +Generate expected output files for PNG suite tests. This target runs pngcheck +against all PNG files in the suite and captures the expected outputs to +`test/expectations/pngsuite/`. + +Use this target when pngcheck behavior changes and you need to update the test +expectations. + +##### `generate-pngsuite-tests` + +Generate the `test/test/test_pngcheck_suite.c` file based on the PNG files and +expectations. This target creates the actual C test functions that will be +executed by the test framework. + + + +### CMake presets + +The project includes the following CMake presets: + +- **Debug** - Debug build with symbols +- **Release** - Optimized release build + +They can be used to configure and build the project easily: + +```bash +# Configure with presets +cmake --preset Debug # Debug build with symbols +cmake --preset Release # Optimized release build + +# Build with presets +cmake --build --preset Debug +cmake --build --preset Release +``` + +### Integration with Ceedling + +A two-file approach is used to bridge the CMake build system with Ceedling's +test framework: + +- **`CMakeLists.txt`** - Main CMake configuration file that includes the test subdirectory +- **`test/CMakeLists.txt`** - CMake configuration for the test suite + +This integration provides the following features: + +- Instead of needing to manually set the `PNGCHECK_EXECUTABLE` environment +variable, the CMake integration automatically locates the built `pngcheck` +executable and sets the environment variable for you. + +- CMake automatically manages dependencies, ensuring that the `pngcheck` +executable is built before running tests. + +- The CMake targets provide a consistent interface for running tests across +different platforms, without needing to manually invoke Ceedling commands. + +- The CMake integration provides clear error messages if dependencies are missing, +such as `ceedling` or `gcovr`, guiding users to install them. + + +## Test structure + +### Test files + +- `test/support/test_helpers.c` - Common test functions +- `test/support/test_helpers.h` - Header for common test functions +- `test/test/test_pngcheck_cli.c` - CLI functionality tests (manual) +- `test/test/test_pngcheck_suite.c` - PNG validation tests (auto-generated) + +### Directory layout + +``` +pngcheck/ +├── CMakeLists.txt # Production build configuration +├── project.yml # Ceedling test configuration +├── TESTING.md # This documentation +└── test/ + ├── CMakeLists.txt # CMake test configuration + ├── bin/ # Ruby CLI tool for managing tests + │ └── pngcheck-test # Ruby script to manage tests and expectations + ├── lib/ # Ruby libraries for `pngcheck-test` + ├── test/ # Test files + │ ├── test_pngcheck_cli.c # CLI tests (manual) + │ ├── test_pngcheck_suite.c # PNG suite tests (generated by test/bin/pngcheck-test) + │ └── support/ # Test helpers + ├── fixtures/pngsuite/ # PNG test files (176 files) + └── expectations/pngsuite/ # Expected outputs +``` + +## Test approach + +Tests validate pngcheck behavior using **simple prefix matching and exit codes**: + +- **Exit code**: 0 for valid PNGs, non-zero for errors +- **Output prefix**: "OK:" for valid files, "ERROR:" for invalid files + +This approach focuses on core functionality rather than exact output matching. + +### CLI tests + +Static tests for command-line options: +- Help option (`-h`) +- File handling (valid, invalid, nonexistent) +- Output options (`-v`, `-q`, `-t`, `-p`, `-c`, `-7`) + +### PNG suite tests + +Auto-generated tests using the [PngSuite](http://www.schaik.com/pngsuite/) collection: +- **Valid PNGs**: Various formats, bit depths, interlacing +- **Invalid PNGs**: Corrupted headers, bad checksums, truncated files + +**Note:** The PngSuite files are committed to the repository at +`test/fixtures/pngsuite/`, where its LICENSE also resides. + + +## Test management + +The test suite includes a Ruby-based management tool. + +**Note:** Prerequisites need to be satisfied to use this tool (see +[prerequisites](#prerequisites)). + +To manage tests, run the Ruby CLI tool: + +```bash +# Check status +bundle exec test/bin/pngcheck-test status + +# Regenerate expectations (when pngcheck behavior changes) (add --force to overwrite) +bundle exec test/bin/pngcheck-test expectations + +# Generate test/test/test_pngcheck_suite.c +bundle exec test/bin/pngcheck-test generate +``` + +**Note:**: On Windows the command would be `bundle exec ruby +test/bin/pngcheck-test` instead of `bundle exec test/bin/pngcheck-test` as the +Ruby script is not executable on Windows. + +## Adding tests + +### CLI tests +Edit `test/test_pngcheck_cli.c` and use helper functions: + +```c +void test_my_feature(void) { + test_pngcheck_with_options("-option", "file.png", 0, "expected_content"); +} +``` + +### PNG tests +1. Add PNG files to `fixtures/pngsuite/` +2. Run `bundle exec test/bin/pngcheck-test expectations` +3. Run `bundle exec test/bin/pngcheck-test generate` + + +## CI troubles + +### Windows CI (windows-2022, windows-11-arm) + +For some reason, the CMake custom target (in `test/CMakeList.txt`) fails to find +the `test/bin/pngcheck-test` script on Windows. Yet it works on Ubuntu and macOS. + +In addition, Windows builds the `pngcheck` executable under the +`build/{preset-name}/{preset-name}/` directory, while on Linux and macOS it is +under `build/{preset-name}/`. + +As a result, the `build.yml` workflow does not use CMake to run tests: + +- directly runs the `test/bin/pngcheck-test` script and uses the +`PNGCHECK_EXECUTABLE` environment variable to point to the built `pngcheck` +executable. + +- directly uses the `ceedling` command to run tests instead of the CMake targets: +`cmake --build build --preset Debug --target test-all`. + + + +## Troubleshooting + +**pngcheck not found**: Build with `make` or `cmake` +**Ruby issues**: Run `bundle install` + + +## Acknowledgements + +The PngSuite test images are created by +[Willem van Schaik](http://www.schaik.com/pngsuite/), and distributed under its +original license reproduced at `test/fixtures/pngsuite/PngSuite.LICENSE`. + +We thank Willem van Schaik for providing these test images that are essential +in validating the behavior of `pngcheck`. diff --git a/project.yml b/project.yml new file mode 100644 index 0000000..e3f41e8 --- /dev/null +++ b/project.yml @@ -0,0 +1,119 @@ +--- + +# Notes: +# Sample project C code is not presently written to produce a release artifact. +# As such, release build options are disabled. +# This sample, therefore, only demonstrates running a collection of unit tests. + +:project: + :use_exceptions: FALSE + :use_test_preprocessor: :none + :use_auxiliary_dependencies: TRUE + :build_root: build + :release_build: TRUE + :test_file_prefix: test_ + :which_ceedling: gem + :ceedling_version: '>= 1.0.0' + :default_tasks: + - clobber + - test:all + +:test_build: + :graceful_fail: FALSE # Ensure proper exit codes for CI (exit 1 on test failures) + +:release_build: + :output: pngcheck + :use_assembly: FALSE + :artifacts: + - build/release/pngcheck + +:environment: + # Platform detection + - CEEDLING_PLATFORM: "`uname -s 2>/dev/null || echo Windows`" + # Zlib configuration with cross-platform fallbacks + - ZLIB_CFLAGS: "`pkg-config --cflags zlib 2>/dev/null || echo ''`" + - ZLIB_LIBS: "`pkg-config --libs zlib 2>/dev/null || echo '-lz'`" + # Test configuration + - PNGSUITE_PATH: "#{ENV['PNGSUITE_PATH'] || 'test/fixtures/pngsuite'}" + - EXPECTATIONS_PATH: "#{ENV['EXPECTATIONS_PATH'] || 'test/expectations'}" + - PNGCHECK_EXECUTABLE: "#{ENV['PNGCHECK_EXECUTABLE'] || 'pngcheck'}" + +:extension: + :executable: .exe + +:paths: + :test: + - +:test/test/** + - -:test/test/support + :source: + - . + :include: + - . + - test + :support: + - test/test/support + :libraries: [] + +:flags: + :release: + :compile: + - -O2 + - -Wall + - -Wextra + - "${ZLIB_CFLAGS}" + :link: + - "${ZLIB_LIBS}" + - -lm + +:defines: + # in order to add common defines: + # 1) remove the trailing [] from the :common: section + # 2) add entries to the :common: section (e.g. :test: has TEST defined) + :common: &common_defines [] + :test: + - *common_defines + - TEST + :test_preprocess: + - *common_defines + - TEST + +# pngcheck does not use cmock yet, this is default configuration +:cmock: + :mock_prefix: mock_ + :when_no_prototypes: :warn + :enforce_strict_ordering: TRUE + :plugins: + - :ignore + - :callback + :treat_as: + uint8: HEX8 + uint16: HEX16 + uint32: UINT32 + int8: INT8 + bool: UINT8 + +# gcov is for code coverage analysis, it is enabled in the plugins section +# You will need to have gcov and gcovr both installed to make it work. +# For more information on these options, see docs in plugins/gcov +:gcov: + :reports: + - HtmlDetailed + :gcovr: + :html_medium_threshold: 75 + :html_high_threshold: 90 + +# junit output is supported on GitHub Actions for individual test reporting +:report_tests_log_factory: + :reports: + - junit # JUnit XML format for CI systems + # - json # JSON format for programmatic processing + # - cppunit # CppUnit XML format alternative + +:plugins: + :enabled: + - module_generator + - report_tests_gtestlike_stdout # Google Test-like output for CI tools + - report_tests_log_factory # Generate JSON/JUnit/CppUnit reports for CI + - report_build_warnings_log # Collect build warnings + - gcov # Code coverage analysis + # - compile_commands_json_db # Generate compile_commands.json for IDEs diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..e494d8a --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,172 @@ +cmake_minimum_required(VERSION 3.14) + +project(pngcheck-tests) + +# Find Ruby executable +find_program(RUBY_EXECUTABLE NAMES ruby) + +if(NOT RUBY_EXECUTABLE) + message(WARNING "Ruby executable not found. Test targets will be disabled.") + set(TESTING_ENABLED FALSE) +else() + # Check if bundler is available + execute_process(COMMAND ${RUBY_EXECUTABLE} -e "require 'bundler'" + RESULT_VARIABLE BUNDLER_CHECK_RESULT + OUTPUT_QUIET + ERROR_QUIET) + + if(BUNDLER_CHECK_RESULT EQUAL 0) + message(STATUS "Found Ruby with Bundler support") + message(STATUS "Using bundler to manage Ceedling dependency") + set(TESTING_ENABLED TRUE) + else() + message(STATUS "Bundler not available, trying to find Ceedling directly") + + # Find ceedling + find_program(CEEDLING_COMMAND ceedling) + + if(NOT CEEDLING_COMMAND) + message(WARNING "Ceedling not found and Bundler not available. Test targets will be disabled.") + message(STATUS "To enable testing, install Ceedling or use bundler.") + set(TESTING_ENABLED FALSE) + else() + message(STATUS "Found Ceedling at: ${CEEDLING_COMMAND}") + set(TESTING_ENABLED TRUE) + endif() + endif() +endif() + +if(TESTING_ENABLED) + # Determine the correct ceedling command to use + if(BUNDLER_CHECK_RESULT EQUAL 0) + # Use bundler to run ceedling and test utilities + set(CEEDLING_CMD bundle exec ceedling) + set(TESTUTIL_CMD bundle exec ruby test/bin/pngcheck-test) + else() + set(CEEDLING_CMD ceedling) + set(TESTUTIL_CMD ruby test/bin/pngcheck-test) + endif() + + # ceedling clean + add_custom_target(test-clean + COMMAND ${CMAKE_COMMAND} -E echo "Cleaning Ceedling artifacts..." + COMMAND ${CEEDLING_CMD} clobber || true + COMMAND ${CMAKE_COMMAND} -E echo "Cleaning Ceedling build artifacts completed." + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Removing Ceedling build artifacts" + ) + + # ceedling test:all + add_custom_target(test-all + COMMAND ${CMAKE_COMMAND} -E env "PNGCHECK_EXECUTABLE=$" ${CEEDLING_CMD} test:all + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + DEPENDS pngcheck generate-pngsuite-tests + COMMENT "Running all unit tests" + ) + + # ceedling test:all with verbose output + add_custom_target(test-verbose + COMMAND ${CMAKE_COMMAND} -E env "PNGCHECK_EXECUTABLE=$" ${CEEDLING_CMD} test:all --verbosity=obnoxious + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + DEPENDS pngcheck generate-pngsuite-tests + COMMENT "Running all unit tests with verbose output" + ) + + # Check for gcovr availability + find_program(GCOVR_EXECUTABLE gcovr) + + # ceedling gcov:all for coverage report + if(GCOVR_EXECUTABLE) + add_custom_target(test-coverage + COMMAND ${CMAKE_COMMAND} -E env "PNGCHECK_EXECUTABLE=$" ${CEEDLING_CMD} gcov:all + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_SOURCE_DIR}/build/artifacts/gcov + COMMAND ${GCOVR_EXECUTABLE} -r ${CMAKE_SOURCE_DIR} --html --html-details -o ${CMAKE_SOURCE_DIR}/build/artifacts/gcov/index.html + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + DEPENDS pngcheck generate-pngsuite-tests + COMMENT "Running tests with coverage analysis and generating HTML report" + ) + else() + # Fallback target that provides helpful error message + add_custom_target(test-coverage + COMMAND ${CMAKE_COMMAND} -E echo "ERROR: gcovr not found. Install with: pip install gcovr" + COMMAND ${CMAKE_COMMAND} -E echo "Alternative: Run '${CEEDLING_CMD} gcov:all' directly for basic coverage" + COMMAND ${CMAKE_COMMAND} -E false + COMMENT "Coverage analysis requires gcovr" + ) + endif() + + # Individual test targets + add_custom_target(test-pngcheck-suite + COMMAND ${CMAKE_COMMAND} -E env "PNGCHECK_EXECUTABLE=$" ${CEEDLING_CMD} test:test_pngcheck_suite + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + DEPENDS pngcheck generate-pngsuite-tests + COMMENT "Running pngcheck suite tests" + ) + + # PNG Suite utility targets + add_custom_target(pngsuite-status + COMMAND ${CMAKE_COMMAND} -E env "PNGCHECK_EXECUTABLE=$" ${TESTUTIL_CMD} status + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + DEPENDS pngcheck + COMMENT "Check PNG suite status" + ) + + add_custom_target(generate-pngsuite-expectations + COMMAND ${CMAKE_COMMAND} -E env "PNGCHECK_EXECUTABLE=$" ${TESTUTIL_CMD} expectations + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + DEPENDS pngcheck + COMMENT "Generate PNG suite expectations" + ) + + add_custom_target(generate-pngsuite-tests + COMMAND ${CMAKE_COMMAND} -E env "PNGCHECK_EXECUTABLE=$" ${TESTUTIL_CMD} generate + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + DEPENDS pngcheck + COMMENT "Generate PNG suite test files" + ) +else() + # Create dummy targets when testing is disabled + add_custom_target(test-all + COMMAND ${CMAKE_COMMAND} -E echo "Testing is disabled. Ruby/Ceedling not available." + COMMENT "Testing disabled - Ruby/Ceedling not found" + ) + + add_custom_target(test-clean + COMMAND ${CMAKE_COMMAND} -E echo "Testing is disabled. Ruby/Ceedling not available." + COMMENT "Testing disabled - Ruby/Ceedling not found" + ) +endif() + +# Print test information +message(STATUS "") +message(STATUS "=== Ceedling Test Configuration ===") +if(TESTING_ENABLED) + if(CEEDLING_COMMAND) + message(STATUS "Ceedling found at: ${CEEDLING_COMMAND}") + else() + message(STATUS "Ceedling will be installed automatically via bundler") + endif() + message(STATUS "Testing: ENABLED") + message(STATUS "") + message(STATUS "Available test targets:") + message(STATUS " test-all - Run all Ceedling tests") + message(STATUS " test-verbose - Run all tests with verbose output") + message(STATUS " test-clean - Clean all test build artifacts") + message(STATUS " test-coverage - Run tests with coverage analysis and generate HTML report") + message(STATUS " test-pngcheck-cli - Run CLI-specific tests") + message(STATUS " test-pngcheck-suite - Run suite-specific tests") + message(STATUS "") + message(STATUS "Available PNG suite utility targets:") + message(STATUS " pngsuite-status - Check PNG suite status") + message(STATUS " generate-pngsuite-expectations - Generate PNG suite expectations") + message(STATUS " generate-pngsuite-tests - Generate PNG suite test files") +else() + message(STATUS "Testing: DISABLED (Ruby/Ceedling not available)") + message(STATUS "") + message(STATUS "Available targets (disabled):") + message(STATUS " test-all - Shows testing disabled message") + message(STATUS " test-clean - Shows testing disabled message") + message(STATUS "") + message(STATUS "To enable testing, install Ruby and run 'bundle install' or install Ceedling directly.") +endif() +message(STATUS "") diff --git a/test/bin/pngcheck-test b/test/bin/pngcheck-test new file mode 100755 index 0000000..b8c494c --- /dev/null +++ b/test/bin/pngcheck-test @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Add lib directory to load path +lib_path = File.expand_path('../lib', __dir__) +$LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path) + +require 'pngcheck_test/cli' + +# Run the CLI +PngcheckTest::CLI.start(ARGV) diff --git a/test/fixtures/pngsuite/PngSuite.LICENSE b/test/fixtures/pngsuite/PngSuite.LICENSE new file mode 100644 index 0000000..8d4d1d0 --- /dev/null +++ b/test/fixtures/pngsuite/PngSuite.LICENSE @@ -0,0 +1,9 @@ +PngSuite +-------- + +Permission to use, copy, modify and distribute these images for any +purpose and without fee is hereby granted. + + +(c) Willem van Schaik, 1996, 2011 + diff --git a/test/fixtures/pngsuite/PngSuite.README b/test/fixtures/pngsuite/PngSuite.README new file mode 100644 index 0000000..94df6cc --- /dev/null +++ b/test/fixtures/pngsuite/PngSuite.README @@ -0,0 +1,25 @@ + PNGSUITE +---------------- + + testset for PNG-(de)coders + created by Willem van Schaik +------------------------------------ + +This is a collection of graphics images created to test the png applications +like viewers, converters and editors. All (as far as that is possible) +formats supported by the PNG standard are represented. + +The suite consists of the following files: + +- PngSuite.README - this file +- PngSuite.LICENSE - the PngSuite is freeware +- PngSuite.png - image with PngSuite logo +- PngSuite.tgz - archive of all PNG testfiles +- PngSuite.zip - same in .zip format for PCs + + +-------- + (c) Willem van Schaik + willem@schaik.com + Calgary, April 2011 + diff --git a/test/fixtures/pngsuite/PngSuite.png b/test/fixtures/pngsuite/PngSuite.png new file mode 100644 index 0000000..205460d Binary files /dev/null and b/test/fixtures/pngsuite/PngSuite.png differ diff --git a/test/fixtures/pngsuite/basi0g01.png b/test/fixtures/pngsuite/basi0g01.png new file mode 100644 index 0000000..556fa72 Binary files /dev/null and b/test/fixtures/pngsuite/basi0g01.png differ diff --git a/test/fixtures/pngsuite/basi0g02.png b/test/fixtures/pngsuite/basi0g02.png new file mode 100644 index 0000000..ce09821 Binary files /dev/null and b/test/fixtures/pngsuite/basi0g02.png differ diff --git a/test/fixtures/pngsuite/basi0g04.png b/test/fixtures/pngsuite/basi0g04.png new file mode 100644 index 0000000..3853273 Binary files /dev/null and b/test/fixtures/pngsuite/basi0g04.png differ diff --git a/test/fixtures/pngsuite/basi0g08.png b/test/fixtures/pngsuite/basi0g08.png new file mode 100644 index 0000000..faed8be Binary files /dev/null and b/test/fixtures/pngsuite/basi0g08.png differ diff --git a/test/fixtures/pngsuite/basi0g16.png b/test/fixtures/pngsuite/basi0g16.png new file mode 100644 index 0000000..a9f2816 Binary files /dev/null and b/test/fixtures/pngsuite/basi0g16.png differ diff --git a/test/fixtures/pngsuite/basi2c08.png b/test/fixtures/pngsuite/basi2c08.png new file mode 100644 index 0000000..2aab44d Binary files /dev/null and b/test/fixtures/pngsuite/basi2c08.png differ diff --git a/test/fixtures/pngsuite/basi2c16.png b/test/fixtures/pngsuite/basi2c16.png new file mode 100644 index 0000000..cd7e50f Binary files /dev/null and b/test/fixtures/pngsuite/basi2c16.png differ diff --git a/test/fixtures/pngsuite/basi3p01.png b/test/fixtures/pngsuite/basi3p01.png new file mode 100644 index 0000000..00a7cea Binary files /dev/null and b/test/fixtures/pngsuite/basi3p01.png differ diff --git a/test/fixtures/pngsuite/basi3p02.png b/test/fixtures/pngsuite/basi3p02.png new file mode 100644 index 0000000..bb16b44 Binary files /dev/null and b/test/fixtures/pngsuite/basi3p02.png differ diff --git a/test/fixtures/pngsuite/basi3p04.png b/test/fixtures/pngsuite/basi3p04.png new file mode 100644 index 0000000..b4e888e Binary files /dev/null and b/test/fixtures/pngsuite/basi3p04.png differ diff --git a/test/fixtures/pngsuite/basi3p08.png b/test/fixtures/pngsuite/basi3p08.png new file mode 100644 index 0000000..50a6d1c Binary files /dev/null and b/test/fixtures/pngsuite/basi3p08.png differ diff --git a/test/fixtures/pngsuite/basi4a08.png b/test/fixtures/pngsuite/basi4a08.png new file mode 100644 index 0000000..398132b Binary files /dev/null and b/test/fixtures/pngsuite/basi4a08.png differ diff --git a/test/fixtures/pngsuite/basi4a16.png b/test/fixtures/pngsuite/basi4a16.png new file mode 100644 index 0000000..51192e7 Binary files /dev/null and b/test/fixtures/pngsuite/basi4a16.png differ diff --git a/test/fixtures/pngsuite/basi6a08.png b/test/fixtures/pngsuite/basi6a08.png new file mode 100644 index 0000000..aecb32e Binary files /dev/null and b/test/fixtures/pngsuite/basi6a08.png differ diff --git a/test/fixtures/pngsuite/basi6a16.png b/test/fixtures/pngsuite/basi6a16.png new file mode 100644 index 0000000..4181533 Binary files /dev/null and b/test/fixtures/pngsuite/basi6a16.png differ diff --git a/test/fixtures/pngsuite/basn0g01.png b/test/fixtures/pngsuite/basn0g01.png new file mode 100644 index 0000000..1d72242 Binary files /dev/null and b/test/fixtures/pngsuite/basn0g01.png differ diff --git a/test/fixtures/pngsuite/basn0g02.png b/test/fixtures/pngsuite/basn0g02.png new file mode 100644 index 0000000..5083324 Binary files /dev/null and b/test/fixtures/pngsuite/basn0g02.png differ diff --git a/test/fixtures/pngsuite/basn0g04.png b/test/fixtures/pngsuite/basn0g04.png new file mode 100644 index 0000000..0bf3687 Binary files /dev/null and b/test/fixtures/pngsuite/basn0g04.png differ diff --git a/test/fixtures/pngsuite/basn0g08.png b/test/fixtures/pngsuite/basn0g08.png new file mode 100644 index 0000000..23c8237 Binary files /dev/null and b/test/fixtures/pngsuite/basn0g08.png differ diff --git a/test/fixtures/pngsuite/basn0g16.png b/test/fixtures/pngsuite/basn0g16.png new file mode 100644 index 0000000..e7c82f7 Binary files /dev/null and b/test/fixtures/pngsuite/basn0g16.png differ diff --git a/test/fixtures/pngsuite/basn2c08.png b/test/fixtures/pngsuite/basn2c08.png new file mode 100644 index 0000000..db5ad15 Binary files /dev/null and b/test/fixtures/pngsuite/basn2c08.png differ diff --git a/test/fixtures/pngsuite/basn2c16.png b/test/fixtures/pngsuite/basn2c16.png new file mode 100644 index 0000000..50c1cb9 Binary files /dev/null and b/test/fixtures/pngsuite/basn2c16.png differ diff --git a/test/fixtures/pngsuite/basn3p01.png b/test/fixtures/pngsuite/basn3p01.png new file mode 100644 index 0000000..b145c2b Binary files /dev/null and b/test/fixtures/pngsuite/basn3p01.png differ diff --git a/test/fixtures/pngsuite/basn3p02.png b/test/fixtures/pngsuite/basn3p02.png new file mode 100644 index 0000000..8985b3d Binary files /dev/null and b/test/fixtures/pngsuite/basn3p02.png differ diff --git a/test/fixtures/pngsuite/basn3p04.png b/test/fixtures/pngsuite/basn3p04.png new file mode 100644 index 0000000..0fbf9e8 Binary files /dev/null and b/test/fixtures/pngsuite/basn3p04.png differ diff --git a/test/fixtures/pngsuite/basn3p08.png b/test/fixtures/pngsuite/basn3p08.png new file mode 100644 index 0000000..0ddad07 Binary files /dev/null and b/test/fixtures/pngsuite/basn3p08.png differ diff --git a/test/fixtures/pngsuite/basn4a08.png b/test/fixtures/pngsuite/basn4a08.png new file mode 100644 index 0000000..3e13052 Binary files /dev/null and b/test/fixtures/pngsuite/basn4a08.png differ diff --git a/test/fixtures/pngsuite/basn4a16.png b/test/fixtures/pngsuite/basn4a16.png new file mode 100644 index 0000000..8243644 Binary files /dev/null and b/test/fixtures/pngsuite/basn4a16.png differ diff --git a/test/fixtures/pngsuite/basn6a08.png b/test/fixtures/pngsuite/basn6a08.png new file mode 100644 index 0000000..e608738 Binary files /dev/null and b/test/fixtures/pngsuite/basn6a08.png differ diff --git a/test/fixtures/pngsuite/basn6a16.png b/test/fixtures/pngsuite/basn6a16.png new file mode 100644 index 0000000..984a995 Binary files /dev/null and b/test/fixtures/pngsuite/basn6a16.png differ diff --git a/test/fixtures/pngsuite/bgai4a08.png b/test/fixtures/pngsuite/bgai4a08.png new file mode 100644 index 0000000..398132b Binary files /dev/null and b/test/fixtures/pngsuite/bgai4a08.png differ diff --git a/test/fixtures/pngsuite/bgai4a16.png b/test/fixtures/pngsuite/bgai4a16.png new file mode 100644 index 0000000..51192e7 Binary files /dev/null and b/test/fixtures/pngsuite/bgai4a16.png differ diff --git a/test/fixtures/pngsuite/bgan6a08.png b/test/fixtures/pngsuite/bgan6a08.png new file mode 100644 index 0000000..e608738 Binary files /dev/null and b/test/fixtures/pngsuite/bgan6a08.png differ diff --git a/test/fixtures/pngsuite/bgan6a16.png b/test/fixtures/pngsuite/bgan6a16.png new file mode 100644 index 0000000..984a995 Binary files /dev/null and b/test/fixtures/pngsuite/bgan6a16.png differ diff --git a/test/fixtures/pngsuite/bgbn4a08.png b/test/fixtures/pngsuite/bgbn4a08.png new file mode 100644 index 0000000..7cbefc3 Binary files /dev/null and b/test/fixtures/pngsuite/bgbn4a08.png differ diff --git a/test/fixtures/pngsuite/bggn4a16.png b/test/fixtures/pngsuite/bggn4a16.png new file mode 100644 index 0000000..13fd85b Binary files /dev/null and b/test/fixtures/pngsuite/bggn4a16.png differ diff --git a/test/fixtures/pngsuite/bgwn6a08.png b/test/fixtures/pngsuite/bgwn6a08.png new file mode 100644 index 0000000..a67ff20 Binary files /dev/null and b/test/fixtures/pngsuite/bgwn6a08.png differ diff --git a/test/fixtures/pngsuite/bgyn6a16.png b/test/fixtures/pngsuite/bgyn6a16.png new file mode 100644 index 0000000..ae3e9be Binary files /dev/null and b/test/fixtures/pngsuite/bgyn6a16.png differ diff --git a/test/fixtures/pngsuite/ccwn2c08.png b/test/fixtures/pngsuite/ccwn2c08.png new file mode 100644 index 0000000..47c2481 Binary files /dev/null and b/test/fixtures/pngsuite/ccwn2c08.png differ diff --git a/test/fixtures/pngsuite/ccwn3p08.png b/test/fixtures/pngsuite/ccwn3p08.png new file mode 100644 index 0000000..8bb2c10 Binary files /dev/null and b/test/fixtures/pngsuite/ccwn3p08.png differ diff --git a/test/fixtures/pngsuite/cdfn2c08.png b/test/fixtures/pngsuite/cdfn2c08.png new file mode 100644 index 0000000..559e526 Binary files /dev/null and b/test/fixtures/pngsuite/cdfn2c08.png differ diff --git a/test/fixtures/pngsuite/cdhn2c08.png b/test/fixtures/pngsuite/cdhn2c08.png new file mode 100644 index 0000000..3e07e8e Binary files /dev/null and b/test/fixtures/pngsuite/cdhn2c08.png differ diff --git a/test/fixtures/pngsuite/cdsn2c08.png b/test/fixtures/pngsuite/cdsn2c08.png new file mode 100644 index 0000000..076c32c Binary files /dev/null and b/test/fixtures/pngsuite/cdsn2c08.png differ diff --git a/test/fixtures/pngsuite/cdun2c08.png b/test/fixtures/pngsuite/cdun2c08.png new file mode 100644 index 0000000..846033b Binary files /dev/null and b/test/fixtures/pngsuite/cdun2c08.png differ diff --git a/test/fixtures/pngsuite/ch1n3p04.png b/test/fixtures/pngsuite/ch1n3p04.png new file mode 100644 index 0000000..17cd12d Binary files /dev/null and b/test/fixtures/pngsuite/ch1n3p04.png differ diff --git a/test/fixtures/pngsuite/ch2n3p08.png b/test/fixtures/pngsuite/ch2n3p08.png new file mode 100644 index 0000000..25c1798 Binary files /dev/null and b/test/fixtures/pngsuite/ch2n3p08.png differ diff --git a/test/fixtures/pngsuite/cm0n0g04.png b/test/fixtures/pngsuite/cm0n0g04.png new file mode 100644 index 0000000..9fba5db Binary files /dev/null and b/test/fixtures/pngsuite/cm0n0g04.png differ diff --git a/test/fixtures/pngsuite/cm7n0g04.png b/test/fixtures/pngsuite/cm7n0g04.png new file mode 100644 index 0000000..f7dc46e Binary files /dev/null and b/test/fixtures/pngsuite/cm7n0g04.png differ diff --git a/test/fixtures/pngsuite/cm9n0g04.png b/test/fixtures/pngsuite/cm9n0g04.png new file mode 100644 index 0000000..dd70911 Binary files /dev/null and b/test/fixtures/pngsuite/cm9n0g04.png differ diff --git a/test/fixtures/pngsuite/cs3n2c16.png b/test/fixtures/pngsuite/cs3n2c16.png new file mode 100644 index 0000000..bf5fd20 Binary files /dev/null and b/test/fixtures/pngsuite/cs3n2c16.png differ diff --git a/test/fixtures/pngsuite/cs3n3p08.png b/test/fixtures/pngsuite/cs3n3p08.png new file mode 100644 index 0000000..f4a6623 Binary files /dev/null and b/test/fixtures/pngsuite/cs3n3p08.png differ diff --git a/test/fixtures/pngsuite/cs5n2c08.png b/test/fixtures/pngsuite/cs5n2c08.png new file mode 100644 index 0000000..40f947c Binary files /dev/null and b/test/fixtures/pngsuite/cs5n2c08.png differ diff --git a/test/fixtures/pngsuite/cs5n3p08.png b/test/fixtures/pngsuite/cs5n3p08.png new file mode 100644 index 0000000..dfd6e6e Binary files /dev/null and b/test/fixtures/pngsuite/cs5n3p08.png differ diff --git a/test/fixtures/pngsuite/cs8n2c08.png b/test/fixtures/pngsuite/cs8n2c08.png new file mode 100644 index 0000000..8e01d32 Binary files /dev/null and b/test/fixtures/pngsuite/cs8n2c08.png differ diff --git a/test/fixtures/pngsuite/cs8n3p08.png b/test/fixtures/pngsuite/cs8n3p08.png new file mode 100644 index 0000000..a44066e Binary files /dev/null and b/test/fixtures/pngsuite/cs8n3p08.png differ diff --git a/test/fixtures/pngsuite/ct0n0g04.png b/test/fixtures/pngsuite/ct0n0g04.png new file mode 100644 index 0000000..40d1e06 Binary files /dev/null and b/test/fixtures/pngsuite/ct0n0g04.png differ diff --git a/test/fixtures/pngsuite/ct1n0g04.png b/test/fixtures/pngsuite/ct1n0g04.png new file mode 100644 index 0000000..3ba110a Binary files /dev/null and b/test/fixtures/pngsuite/ct1n0g04.png differ diff --git a/test/fixtures/pngsuite/cten0g04.png b/test/fixtures/pngsuite/cten0g04.png new file mode 100644 index 0000000..a6a56fa Binary files /dev/null and b/test/fixtures/pngsuite/cten0g04.png differ diff --git a/test/fixtures/pngsuite/ctfn0g04.png b/test/fixtures/pngsuite/ctfn0g04.png new file mode 100644 index 0000000..353873e Binary files /dev/null and b/test/fixtures/pngsuite/ctfn0g04.png differ diff --git a/test/fixtures/pngsuite/ctgn0g04.png b/test/fixtures/pngsuite/ctgn0g04.png new file mode 100644 index 0000000..453f2b0 Binary files /dev/null and b/test/fixtures/pngsuite/ctgn0g04.png differ diff --git a/test/fixtures/pngsuite/cthn0g04.png b/test/fixtures/pngsuite/cthn0g04.png new file mode 100644 index 0000000..8fce253 Binary files /dev/null and b/test/fixtures/pngsuite/cthn0g04.png differ diff --git a/test/fixtures/pngsuite/ctjn0g04.png b/test/fixtures/pngsuite/ctjn0g04.png new file mode 100644 index 0000000..a77b8d2 Binary files /dev/null and b/test/fixtures/pngsuite/ctjn0g04.png differ diff --git a/test/fixtures/pngsuite/ctzn0g04.png b/test/fixtures/pngsuite/ctzn0g04.png new file mode 100644 index 0000000..b4401c9 Binary files /dev/null and b/test/fixtures/pngsuite/ctzn0g04.png differ diff --git a/test/fixtures/pngsuite/exif2c08.png b/test/fixtures/pngsuite/exif2c08.png new file mode 100644 index 0000000..56eb734 Binary files /dev/null and b/test/fixtures/pngsuite/exif2c08.png differ diff --git a/test/fixtures/pngsuite/f00n0g08.png b/test/fixtures/pngsuite/f00n0g08.png new file mode 100644 index 0000000..45a0075 Binary files /dev/null and b/test/fixtures/pngsuite/f00n0g08.png differ diff --git a/test/fixtures/pngsuite/f00n2c08.png b/test/fixtures/pngsuite/f00n2c08.png new file mode 100644 index 0000000..d6a1fff Binary files /dev/null and b/test/fixtures/pngsuite/f00n2c08.png differ diff --git a/test/fixtures/pngsuite/f01n0g08.png b/test/fixtures/pngsuite/f01n0g08.png new file mode 100644 index 0000000..4a1107b Binary files /dev/null and b/test/fixtures/pngsuite/f01n0g08.png differ diff --git a/test/fixtures/pngsuite/f01n2c08.png b/test/fixtures/pngsuite/f01n2c08.png new file mode 100644 index 0000000..26fee95 Binary files /dev/null and b/test/fixtures/pngsuite/f01n2c08.png differ diff --git a/test/fixtures/pngsuite/f02n0g08.png b/test/fixtures/pngsuite/f02n0g08.png new file mode 100644 index 0000000..bfe410c Binary files /dev/null and b/test/fixtures/pngsuite/f02n0g08.png differ diff --git a/test/fixtures/pngsuite/f02n2c08.png b/test/fixtures/pngsuite/f02n2c08.png new file mode 100644 index 0000000..e590f12 Binary files /dev/null and b/test/fixtures/pngsuite/f02n2c08.png differ diff --git a/test/fixtures/pngsuite/f03n0g08.png b/test/fixtures/pngsuite/f03n0g08.png new file mode 100644 index 0000000..ed01e29 Binary files /dev/null and b/test/fixtures/pngsuite/f03n0g08.png differ diff --git a/test/fixtures/pngsuite/f03n2c08.png b/test/fixtures/pngsuite/f03n2c08.png new file mode 100644 index 0000000..7581150 Binary files /dev/null and b/test/fixtures/pngsuite/f03n2c08.png differ diff --git a/test/fixtures/pngsuite/f04n0g08.png b/test/fixtures/pngsuite/f04n0g08.png new file mode 100644 index 0000000..663fdae Binary files /dev/null and b/test/fixtures/pngsuite/f04n0g08.png differ diff --git a/test/fixtures/pngsuite/f04n2c08.png b/test/fixtures/pngsuite/f04n2c08.png new file mode 100644 index 0000000..3c8b511 Binary files /dev/null and b/test/fixtures/pngsuite/f04n2c08.png differ diff --git a/test/fixtures/pngsuite/f99n0g04.png b/test/fixtures/pngsuite/f99n0g04.png new file mode 100644 index 0000000..0b521c1 Binary files /dev/null and b/test/fixtures/pngsuite/f99n0g04.png differ diff --git a/test/fixtures/pngsuite/g03n0g16.png b/test/fixtures/pngsuite/g03n0g16.png new file mode 100644 index 0000000..41083ca Binary files /dev/null and b/test/fixtures/pngsuite/g03n0g16.png differ diff --git a/test/fixtures/pngsuite/g03n2c08.png b/test/fixtures/pngsuite/g03n2c08.png new file mode 100644 index 0000000..a9354db Binary files /dev/null and b/test/fixtures/pngsuite/g03n2c08.png differ diff --git a/test/fixtures/pngsuite/g03n3p04.png b/test/fixtures/pngsuite/g03n3p04.png new file mode 100644 index 0000000..60396c9 Binary files /dev/null and b/test/fixtures/pngsuite/g03n3p04.png differ diff --git a/test/fixtures/pngsuite/g04n0g16.png b/test/fixtures/pngsuite/g04n0g16.png new file mode 100644 index 0000000..32395b7 Binary files /dev/null and b/test/fixtures/pngsuite/g04n0g16.png differ diff --git a/test/fixtures/pngsuite/g04n2c08.png b/test/fixtures/pngsuite/g04n2c08.png new file mode 100644 index 0000000..a652b0c Binary files /dev/null and b/test/fixtures/pngsuite/g04n2c08.png differ diff --git a/test/fixtures/pngsuite/g04n3p04.png b/test/fixtures/pngsuite/g04n3p04.png new file mode 100644 index 0000000..5661cc3 Binary files /dev/null and b/test/fixtures/pngsuite/g04n3p04.png differ diff --git a/test/fixtures/pngsuite/g05n0g16.png b/test/fixtures/pngsuite/g05n0g16.png new file mode 100644 index 0000000..70b37f0 Binary files /dev/null and b/test/fixtures/pngsuite/g05n0g16.png differ diff --git a/test/fixtures/pngsuite/g05n2c08.png b/test/fixtures/pngsuite/g05n2c08.png new file mode 100644 index 0000000..932c136 Binary files /dev/null and b/test/fixtures/pngsuite/g05n2c08.png differ diff --git a/test/fixtures/pngsuite/g05n3p04.png b/test/fixtures/pngsuite/g05n3p04.png new file mode 100644 index 0000000..9619930 Binary files /dev/null and b/test/fixtures/pngsuite/g05n3p04.png differ diff --git a/test/fixtures/pngsuite/g07n0g16.png b/test/fixtures/pngsuite/g07n0g16.png new file mode 100644 index 0000000..d6a47c2 Binary files /dev/null and b/test/fixtures/pngsuite/g07n0g16.png differ diff --git a/test/fixtures/pngsuite/g07n2c08.png b/test/fixtures/pngsuite/g07n2c08.png new file mode 100644 index 0000000..5973464 Binary files /dev/null and b/test/fixtures/pngsuite/g07n2c08.png differ diff --git a/test/fixtures/pngsuite/g07n3p04.png b/test/fixtures/pngsuite/g07n3p04.png new file mode 100644 index 0000000..c73fb61 Binary files /dev/null and b/test/fixtures/pngsuite/g07n3p04.png differ diff --git a/test/fixtures/pngsuite/g10n0g16.png b/test/fixtures/pngsuite/g10n0g16.png new file mode 100644 index 0000000..85f2c95 Binary files /dev/null and b/test/fixtures/pngsuite/g10n0g16.png differ diff --git a/test/fixtures/pngsuite/g10n2c08.png b/test/fixtures/pngsuite/g10n2c08.png new file mode 100644 index 0000000..b303997 Binary files /dev/null and b/test/fixtures/pngsuite/g10n2c08.png differ diff --git a/test/fixtures/pngsuite/g10n3p04.png b/test/fixtures/pngsuite/g10n3p04.png new file mode 100644 index 0000000..1b6a6be Binary files /dev/null and b/test/fixtures/pngsuite/g10n3p04.png differ diff --git a/test/fixtures/pngsuite/g25n0g16.png b/test/fixtures/pngsuite/g25n0g16.png new file mode 100644 index 0000000..a9f6787 Binary files /dev/null and b/test/fixtures/pngsuite/g25n0g16.png differ diff --git a/test/fixtures/pngsuite/g25n2c08.png b/test/fixtures/pngsuite/g25n2c08.png new file mode 100644 index 0000000..03f505a Binary files /dev/null and b/test/fixtures/pngsuite/g25n2c08.png differ diff --git a/test/fixtures/pngsuite/g25n3p04.png b/test/fixtures/pngsuite/g25n3p04.png new file mode 100644 index 0000000..4f943c6 Binary files /dev/null and b/test/fixtures/pngsuite/g25n3p04.png differ diff --git a/test/fixtures/pngsuite/oi1n0g16.png b/test/fixtures/pngsuite/oi1n0g16.png new file mode 100644 index 0000000..e7c82f7 Binary files /dev/null and b/test/fixtures/pngsuite/oi1n0g16.png differ diff --git a/test/fixtures/pngsuite/oi1n2c16.png b/test/fixtures/pngsuite/oi1n2c16.png new file mode 100644 index 0000000..50c1cb9 Binary files /dev/null and b/test/fixtures/pngsuite/oi1n2c16.png differ diff --git a/test/fixtures/pngsuite/oi2n0g16.png b/test/fixtures/pngsuite/oi2n0g16.png new file mode 100644 index 0000000..14d64c5 Binary files /dev/null and b/test/fixtures/pngsuite/oi2n0g16.png differ diff --git a/test/fixtures/pngsuite/oi2n2c16.png b/test/fixtures/pngsuite/oi2n2c16.png new file mode 100644 index 0000000..4c2e3e3 Binary files /dev/null and b/test/fixtures/pngsuite/oi2n2c16.png differ diff --git a/test/fixtures/pngsuite/oi4n0g16.png b/test/fixtures/pngsuite/oi4n0g16.png new file mode 100644 index 0000000..69e73ed Binary files /dev/null and b/test/fixtures/pngsuite/oi4n0g16.png differ diff --git a/test/fixtures/pngsuite/oi4n2c16.png b/test/fixtures/pngsuite/oi4n2c16.png new file mode 100644 index 0000000..93691e3 Binary files /dev/null and b/test/fixtures/pngsuite/oi4n2c16.png differ diff --git a/test/fixtures/pngsuite/oi9n0g16.png b/test/fixtures/pngsuite/oi9n0g16.png new file mode 100644 index 0000000..9248413 Binary files /dev/null and b/test/fixtures/pngsuite/oi9n0g16.png differ diff --git a/test/fixtures/pngsuite/oi9n2c16.png b/test/fixtures/pngsuite/oi9n2c16.png new file mode 100644 index 0000000..f0512e4 Binary files /dev/null and b/test/fixtures/pngsuite/oi9n2c16.png differ diff --git a/test/fixtures/pngsuite/pp0n2c16.png b/test/fixtures/pngsuite/pp0n2c16.png new file mode 100644 index 0000000..8f2aad7 Binary files /dev/null and b/test/fixtures/pngsuite/pp0n2c16.png differ diff --git a/test/fixtures/pngsuite/pp0n6a08.png b/test/fixtures/pngsuite/pp0n6a08.png new file mode 100644 index 0000000..4ed7a30 Binary files /dev/null and b/test/fixtures/pngsuite/pp0n6a08.png differ diff --git a/test/fixtures/pngsuite/ps1n0g08.png b/test/fixtures/pngsuite/ps1n0g08.png new file mode 100644 index 0000000..99625fa Binary files /dev/null and b/test/fixtures/pngsuite/ps1n0g08.png differ diff --git a/test/fixtures/pngsuite/ps1n2c16.png b/test/fixtures/pngsuite/ps1n2c16.png new file mode 100644 index 0000000..0c7a6b3 Binary files /dev/null and b/test/fixtures/pngsuite/ps1n2c16.png differ diff --git a/test/fixtures/pngsuite/ps2n0g08.png b/test/fixtures/pngsuite/ps2n0g08.png new file mode 100644 index 0000000..90b2979 Binary files /dev/null and b/test/fixtures/pngsuite/ps2n0g08.png differ diff --git a/test/fixtures/pngsuite/ps2n2c16.png b/test/fixtures/pngsuite/ps2n2c16.png new file mode 100644 index 0000000..a4a181e Binary files /dev/null and b/test/fixtures/pngsuite/ps2n2c16.png differ diff --git a/test/fixtures/pngsuite/s01i3p01.png b/test/fixtures/pngsuite/s01i3p01.png new file mode 100644 index 0000000..6c0fad1 Binary files /dev/null and b/test/fixtures/pngsuite/s01i3p01.png differ diff --git a/test/fixtures/pngsuite/s01n3p01.png b/test/fixtures/pngsuite/s01n3p01.png new file mode 100644 index 0000000..cb2c8c7 Binary files /dev/null and b/test/fixtures/pngsuite/s01n3p01.png differ diff --git a/test/fixtures/pngsuite/s02i3p01.png b/test/fixtures/pngsuite/s02i3p01.png new file mode 100644 index 0000000..2defaed Binary files /dev/null and b/test/fixtures/pngsuite/s02i3p01.png differ diff --git a/test/fixtures/pngsuite/s02n3p01.png b/test/fixtures/pngsuite/s02n3p01.png new file mode 100644 index 0000000..2b1b669 Binary files /dev/null and b/test/fixtures/pngsuite/s02n3p01.png differ diff --git a/test/fixtures/pngsuite/s03i3p01.png b/test/fixtures/pngsuite/s03i3p01.png new file mode 100644 index 0000000..c23fdc4 Binary files /dev/null and b/test/fixtures/pngsuite/s03i3p01.png differ diff --git a/test/fixtures/pngsuite/s03n3p01.png b/test/fixtures/pngsuite/s03n3p01.png new file mode 100644 index 0000000..6d96ee4 Binary files /dev/null and b/test/fixtures/pngsuite/s03n3p01.png differ diff --git a/test/fixtures/pngsuite/s04i3p01.png b/test/fixtures/pngsuite/s04i3p01.png new file mode 100644 index 0000000..0e710c2 Binary files /dev/null and b/test/fixtures/pngsuite/s04i3p01.png differ diff --git a/test/fixtures/pngsuite/s04n3p01.png b/test/fixtures/pngsuite/s04n3p01.png new file mode 100644 index 0000000..956396c Binary files /dev/null and b/test/fixtures/pngsuite/s04n3p01.png differ diff --git a/test/fixtures/pngsuite/s05i3p02.png b/test/fixtures/pngsuite/s05i3p02.png new file mode 100644 index 0000000..d14cbd3 Binary files /dev/null and b/test/fixtures/pngsuite/s05i3p02.png differ diff --git a/test/fixtures/pngsuite/s05n3p02.png b/test/fixtures/pngsuite/s05n3p02.png new file mode 100644 index 0000000..bf940f0 Binary files /dev/null and b/test/fixtures/pngsuite/s05n3p02.png differ diff --git a/test/fixtures/pngsuite/s06i3p02.png b/test/fixtures/pngsuite/s06i3p02.png new file mode 100644 index 0000000..456ada3 Binary files /dev/null and b/test/fixtures/pngsuite/s06i3p02.png differ diff --git a/test/fixtures/pngsuite/s06n3p02.png b/test/fixtures/pngsuite/s06n3p02.png new file mode 100644 index 0000000..501064d Binary files /dev/null and b/test/fixtures/pngsuite/s06n3p02.png differ diff --git a/test/fixtures/pngsuite/s07i3p02.png b/test/fixtures/pngsuite/s07i3p02.png new file mode 100644 index 0000000..44b66ba Binary files /dev/null and b/test/fixtures/pngsuite/s07i3p02.png differ diff --git a/test/fixtures/pngsuite/s07n3p02.png b/test/fixtures/pngsuite/s07n3p02.png new file mode 100644 index 0000000..6a58259 Binary files /dev/null and b/test/fixtures/pngsuite/s07n3p02.png differ diff --git a/test/fixtures/pngsuite/s08i3p02.png b/test/fixtures/pngsuite/s08i3p02.png new file mode 100644 index 0000000..acf74f3 Binary files /dev/null and b/test/fixtures/pngsuite/s08i3p02.png differ diff --git a/test/fixtures/pngsuite/s08n3p02.png b/test/fixtures/pngsuite/s08n3p02.png new file mode 100644 index 0000000..b7094e1 Binary files /dev/null and b/test/fixtures/pngsuite/s08n3p02.png differ diff --git a/test/fixtures/pngsuite/s09i3p02.png b/test/fixtures/pngsuite/s09i3p02.png new file mode 100644 index 0000000..0bfae8e Binary files /dev/null and b/test/fixtures/pngsuite/s09i3p02.png differ diff --git a/test/fixtures/pngsuite/s09n3p02.png b/test/fixtures/pngsuite/s09n3p02.png new file mode 100644 index 0000000..711ab82 Binary files /dev/null and b/test/fixtures/pngsuite/s09n3p02.png differ diff --git a/test/fixtures/pngsuite/s32i3p04.png b/test/fixtures/pngsuite/s32i3p04.png new file mode 100644 index 0000000..0841910 Binary files /dev/null and b/test/fixtures/pngsuite/s32i3p04.png differ diff --git a/test/fixtures/pngsuite/s32n3p04.png b/test/fixtures/pngsuite/s32n3p04.png new file mode 100644 index 0000000..fa58e3e Binary files /dev/null and b/test/fixtures/pngsuite/s32n3p04.png differ diff --git a/test/fixtures/pngsuite/s33i3p04.png b/test/fixtures/pngsuite/s33i3p04.png new file mode 100644 index 0000000..ab0dc14 Binary files /dev/null and b/test/fixtures/pngsuite/s33i3p04.png differ diff --git a/test/fixtures/pngsuite/s33n3p04.png b/test/fixtures/pngsuite/s33n3p04.png new file mode 100644 index 0000000..764f1a3 Binary files /dev/null and b/test/fixtures/pngsuite/s33n3p04.png differ diff --git a/test/fixtures/pngsuite/s34i3p04.png b/test/fixtures/pngsuite/s34i3p04.png new file mode 100644 index 0000000..bd99039 Binary files /dev/null and b/test/fixtures/pngsuite/s34i3p04.png differ diff --git a/test/fixtures/pngsuite/s34n3p04.png b/test/fixtures/pngsuite/s34n3p04.png new file mode 100644 index 0000000..9cbc68b Binary files /dev/null and b/test/fixtures/pngsuite/s34n3p04.png differ diff --git a/test/fixtures/pngsuite/s35i3p04.png b/test/fixtures/pngsuite/s35i3p04.png new file mode 100644 index 0000000..e2a5e0a Binary files /dev/null and b/test/fixtures/pngsuite/s35i3p04.png differ diff --git a/test/fixtures/pngsuite/s35n3p04.png b/test/fixtures/pngsuite/s35n3p04.png new file mode 100644 index 0000000..90b892e Binary files /dev/null and b/test/fixtures/pngsuite/s35n3p04.png differ diff --git a/test/fixtures/pngsuite/s36i3p04.png b/test/fixtures/pngsuite/s36i3p04.png new file mode 100644 index 0000000..eb61b6f Binary files /dev/null and b/test/fixtures/pngsuite/s36i3p04.png differ diff --git a/test/fixtures/pngsuite/s36n3p04.png b/test/fixtures/pngsuite/s36n3p04.png new file mode 100644 index 0000000..b38d179 Binary files /dev/null and b/test/fixtures/pngsuite/s36n3p04.png differ diff --git a/test/fixtures/pngsuite/s37i3p04.png b/test/fixtures/pngsuite/s37i3p04.png new file mode 100644 index 0000000..6e2b1e9 Binary files /dev/null and b/test/fixtures/pngsuite/s37i3p04.png differ diff --git a/test/fixtures/pngsuite/s37n3p04.png b/test/fixtures/pngsuite/s37n3p04.png new file mode 100644 index 0000000..4d3054d Binary files /dev/null and b/test/fixtures/pngsuite/s37n3p04.png differ diff --git a/test/fixtures/pngsuite/s38i3p04.png b/test/fixtures/pngsuite/s38i3p04.png new file mode 100644 index 0000000..a0a8a14 Binary files /dev/null and b/test/fixtures/pngsuite/s38i3p04.png differ diff --git a/test/fixtures/pngsuite/s38n3p04.png b/test/fixtures/pngsuite/s38n3p04.png new file mode 100644 index 0000000..1233ed0 Binary files /dev/null and b/test/fixtures/pngsuite/s38n3p04.png differ diff --git a/test/fixtures/pngsuite/s39i3p04.png b/test/fixtures/pngsuite/s39i3p04.png new file mode 100644 index 0000000..04fee93 Binary files /dev/null and b/test/fixtures/pngsuite/s39i3p04.png differ diff --git a/test/fixtures/pngsuite/s39n3p04.png b/test/fixtures/pngsuite/s39n3p04.png new file mode 100644 index 0000000..c750100 Binary files /dev/null and b/test/fixtures/pngsuite/s39n3p04.png differ diff --git a/test/fixtures/pngsuite/s40i3p04.png b/test/fixtures/pngsuite/s40i3p04.png new file mode 100644 index 0000000..68f358b Binary files /dev/null and b/test/fixtures/pngsuite/s40i3p04.png differ diff --git a/test/fixtures/pngsuite/s40n3p04.png b/test/fixtures/pngsuite/s40n3p04.png new file mode 100644 index 0000000..864b6b9 Binary files /dev/null and b/test/fixtures/pngsuite/s40n3p04.png differ diff --git a/test/fixtures/pngsuite/tbbn0g04.png b/test/fixtures/pngsuite/tbbn0g04.png new file mode 100644 index 0000000..39a7050 Binary files /dev/null and b/test/fixtures/pngsuite/tbbn0g04.png differ diff --git a/test/fixtures/pngsuite/tbbn2c16.png b/test/fixtures/pngsuite/tbbn2c16.png new file mode 100644 index 0000000..dd3168e Binary files /dev/null and b/test/fixtures/pngsuite/tbbn2c16.png differ diff --git a/test/fixtures/pngsuite/tbbn3p08.png b/test/fixtures/pngsuite/tbbn3p08.png new file mode 100644 index 0000000..0ede357 Binary files /dev/null and b/test/fixtures/pngsuite/tbbn3p08.png differ diff --git a/test/fixtures/pngsuite/tbgn2c16.png b/test/fixtures/pngsuite/tbgn2c16.png new file mode 100644 index 0000000..85cec39 Binary files /dev/null and b/test/fixtures/pngsuite/tbgn2c16.png differ diff --git a/test/fixtures/pngsuite/tbgn3p08.png b/test/fixtures/pngsuite/tbgn3p08.png new file mode 100644 index 0000000..8cf2e6f Binary files /dev/null and b/test/fixtures/pngsuite/tbgn3p08.png differ diff --git a/test/fixtures/pngsuite/tbrn2c08.png b/test/fixtures/pngsuite/tbrn2c08.png new file mode 100644 index 0000000..5cca0d6 Binary files /dev/null and b/test/fixtures/pngsuite/tbrn2c08.png differ diff --git a/test/fixtures/pngsuite/tbwn0g16.png b/test/fixtures/pngsuite/tbwn0g16.png new file mode 100644 index 0000000..99bdeed Binary files /dev/null and b/test/fixtures/pngsuite/tbwn0g16.png differ diff --git a/test/fixtures/pngsuite/tbwn3p08.png b/test/fixtures/pngsuite/tbwn3p08.png new file mode 100644 index 0000000..eacab7a Binary files /dev/null and b/test/fixtures/pngsuite/tbwn3p08.png differ diff --git a/test/fixtures/pngsuite/tbyn3p08.png b/test/fixtures/pngsuite/tbyn3p08.png new file mode 100644 index 0000000..656db09 Binary files /dev/null and b/test/fixtures/pngsuite/tbyn3p08.png differ diff --git a/test/fixtures/pngsuite/tm3n3p02.png b/test/fixtures/pngsuite/tm3n3p02.png new file mode 100644 index 0000000..fb3ef1d Binary files /dev/null and b/test/fixtures/pngsuite/tm3n3p02.png differ diff --git a/test/fixtures/pngsuite/tp0n0g08.png b/test/fixtures/pngsuite/tp0n0g08.png new file mode 100644 index 0000000..333465f Binary files /dev/null and b/test/fixtures/pngsuite/tp0n0g08.png differ diff --git a/test/fixtures/pngsuite/tp0n2c08.png b/test/fixtures/pngsuite/tp0n2c08.png new file mode 100644 index 0000000..fc6e42c Binary files /dev/null and b/test/fixtures/pngsuite/tp0n2c08.png differ diff --git a/test/fixtures/pngsuite/tp0n3p08.png b/test/fixtures/pngsuite/tp0n3p08.png new file mode 100644 index 0000000..69a69e5 Binary files /dev/null and b/test/fixtures/pngsuite/tp0n3p08.png differ diff --git a/test/fixtures/pngsuite/tp1n3p08.png b/test/fixtures/pngsuite/tp1n3p08.png new file mode 100644 index 0000000..a6c9f35 Binary files /dev/null and b/test/fixtures/pngsuite/tp1n3p08.png differ diff --git a/test/fixtures/pngsuite/xc1n0g08.png b/test/fixtures/pngsuite/xc1n0g08.png new file mode 100644 index 0000000..9404227 Binary files /dev/null and b/test/fixtures/pngsuite/xc1n0g08.png differ diff --git a/test/fixtures/pngsuite/xc9n2c08.png b/test/fixtures/pngsuite/xc9n2c08.png new file mode 100644 index 0000000..b11c2a7 Binary files /dev/null and b/test/fixtures/pngsuite/xc9n2c08.png differ diff --git a/test/fixtures/pngsuite/xcrn0g04.png b/test/fixtures/pngsuite/xcrn0g04.png new file mode 100644 index 0000000..48abba1 Binary files /dev/null and b/test/fixtures/pngsuite/xcrn0g04.png differ diff --git a/test/fixtures/pngsuite/xcsn0g01.png b/test/fixtures/pngsuite/xcsn0g01.png new file mode 100644 index 0000000..9863a26 Binary files /dev/null and b/test/fixtures/pngsuite/xcsn0g01.png differ diff --git a/test/fixtures/pngsuite/xd0n2c08.png b/test/fixtures/pngsuite/xd0n2c08.png new file mode 100644 index 0000000..2f00161 Binary files /dev/null and b/test/fixtures/pngsuite/xd0n2c08.png differ diff --git a/test/fixtures/pngsuite/xd3n2c08.png b/test/fixtures/pngsuite/xd3n2c08.png new file mode 100644 index 0000000..9e4a3ff Binary files /dev/null and b/test/fixtures/pngsuite/xd3n2c08.png differ diff --git a/test/fixtures/pngsuite/xd9n2c08.png b/test/fixtures/pngsuite/xd9n2c08.png new file mode 100644 index 0000000..2c3b91a Binary files /dev/null and b/test/fixtures/pngsuite/xd9n2c08.png differ diff --git a/test/fixtures/pngsuite/xdtn0g01.png b/test/fixtures/pngsuite/xdtn0g01.png new file mode 100644 index 0000000..1a81abe Binary files /dev/null and b/test/fixtures/pngsuite/xdtn0g01.png differ diff --git a/test/fixtures/pngsuite/xhdn0g08.png b/test/fixtures/pngsuite/xhdn0g08.png new file mode 100644 index 0000000..fcb8737 Binary files /dev/null and b/test/fixtures/pngsuite/xhdn0g08.png differ diff --git a/test/fixtures/pngsuite/xlfn0g04.png b/test/fixtures/pngsuite/xlfn0g04.png new file mode 100644 index 0000000..d9ec53e Binary files /dev/null and b/test/fixtures/pngsuite/xlfn0g04.png differ diff --git a/test/fixtures/pngsuite/xs1n0g01.png b/test/fixtures/pngsuite/xs1n0g01.png new file mode 100644 index 0000000..1817c51 Binary files /dev/null and b/test/fixtures/pngsuite/xs1n0g01.png differ diff --git a/test/fixtures/pngsuite/xs2n0g01.png b/test/fixtures/pngsuite/xs2n0g01.png new file mode 100644 index 0000000..b8147f2 Binary files /dev/null and b/test/fixtures/pngsuite/xs2n0g01.png differ diff --git a/test/fixtures/pngsuite/xs4n0g01.png b/test/fixtures/pngsuite/xs4n0g01.png new file mode 100644 index 0000000..45237a1 Binary files /dev/null and b/test/fixtures/pngsuite/xs4n0g01.png differ diff --git a/test/fixtures/pngsuite/xs7n0g01.png b/test/fixtures/pngsuite/xs7n0g01.png new file mode 100644 index 0000000..3f307f1 Binary files /dev/null and b/test/fixtures/pngsuite/xs7n0g01.png differ diff --git a/test/fixtures/pngsuite/z00n2c08.png b/test/fixtures/pngsuite/z00n2c08.png new file mode 100644 index 0000000..7669eb8 Binary files /dev/null and b/test/fixtures/pngsuite/z00n2c08.png differ diff --git a/test/fixtures/pngsuite/z03n2c08.png b/test/fixtures/pngsuite/z03n2c08.png new file mode 100644 index 0000000..bfb10de Binary files /dev/null and b/test/fixtures/pngsuite/z03n2c08.png differ diff --git a/test/fixtures/pngsuite/z06n2c08.png b/test/fixtures/pngsuite/z06n2c08.png new file mode 100644 index 0000000..b90ebc1 Binary files /dev/null and b/test/fixtures/pngsuite/z06n2c08.png differ diff --git a/test/fixtures/pngsuite/z09n2c08.png b/test/fixtures/pngsuite/z09n2c08.png new file mode 100644 index 0000000..5f191a7 Binary files /dev/null and b/test/fixtures/pngsuite/z09n2c08.png differ diff --git a/test/lib/pngcheck_test.rb b/test/lib/pngcheck_test.rb new file mode 100644 index 0000000..560da95 --- /dev/null +++ b/test/lib/pngcheck_test.rb @@ -0,0 +1,10 @@ +require_relative 'pngcheck_test/version' +require_relative 'pngcheck_test/models/png_suite' +require_relative 'pngcheck_test/models/pngcheck' +require_relative 'pngcheck_test/models/repository' +require_relative 'pngcheck_test/models/test_generator' +require_relative 'pngcheck_test/cli' + +module PngcheckTest + # Main module for pngcheck test generation +end diff --git a/test/lib/pngcheck_test/cli.rb b/test/lib/pngcheck_test/cli.rb new file mode 100644 index 0000000..c0fb2d6 --- /dev/null +++ b/test/lib/pngcheck_test/cli.rb @@ -0,0 +1,205 @@ +# frozen_string_literal: true + +require 'thor' +require 'paint' +require_relative 'version' +require_relative 'models/repository' +require_relative 'models/pngcheck' +require_relative 'models/png_suite' +require_relative 'models/test_generator' + +module PngcheckTest + class CLI < Thor + include Thor::Actions + + def self.exit_on_failure? + true + end + + desc 'expectations', 'Generate expected output files for PNG test suite' + option :force, type: :boolean, default: false, desc: 'Force regeneration of existing expectations' + def expectations + setup_models + ensure_pngcheck! + + unless @png_suite.available? + say Paint["❌ Test PNG files missing from #{@png_suite.png_dir}", :red] + exit 1 + end + + say Paint["🔧 Generating expected outputs...", :cyan, :bold] + + results = @png_suite.generate_expectations!(@pngcheck, force: options[:force]) + + say Paint["✅ Processed #{results[:processed]} files", :green] + say Paint["✅ Set expectations for #{results[:success]} successes", :green] if results[:success] > 0 + say Paint["❌ Set expectations for #{results[:errors]} errors", :red] if results[:errors] > 0 + + if options[:verbose] + results[:files].each do |file_result| + status_color = case file_result[:status] + when :success then :green + when :error then :red + when :skipped then :yellow + end + say " #{Paint[file_result[:file], status_color]} - #{file_result[:message]}" + end + end + rescue => e + handle_error("Failed to generate expectations", e) + end + + desc 'generate', 'Generate Unity C test suite file' + def generate + setup_models + + unless @png_suite.available? + say Paint["❌ Test PNG files missing from #{@png_suite.png_dir}", :red] + exit 1 + end + + unless @png_suite.expectation_files.any? + say Paint["📝 No expectations found. Generating first...", :yellow] + invoke :expectations + end + + say Paint["🏗️ Generating Unity test suite...", :cyan, :bold] + + result = @test_generator.generate! + + if result[:success] + say Paint["✅ Generated test suite: #{result[:file].relative_path_from(@repository.root_path)}", :green] + + stats = result[:statistics] + say Paint["📊 Test Statistics:", :blue, :bold] + say " #{Paint['Valid PNGs:', :green]} #{stats[:valid]}" + say " #{Paint['Invalid PNGs:', :red]} #{stats[:invalid]}" + say " #{Paint['Warning PNGs:', :yellow]} #{stats[:warning]}" + say " #{Paint['Unknown PNGs:', :gray]} #{stats[:unknown]}" if stats[:unknown] > 0 + say " #{Paint['Total Tests:', :blue, :bold]} #{stats[:total]}" + else + say Paint["❌ Failed to generate test suite", :red] + exit 1 + end + rescue => e + handle_error("Failed to generate test suite", e) + end + + desc 'setup', 'Complete setup: generate expectations, and create test suite' + option :force, type: :boolean, default: false, desc: 'Force regeneration of all files' + def setup + say Paint["🚀 Setting up complete PNG test suite...", :cyan, :bold] + + invoke :expectations, [], force: options[:force] + invoke :generate + + say Paint["🎉 Setup complete! You can now run tests with:", :green, :bold] + say Paint[" bundle exec ceedling test:all", :blue] + rescue => e + handle_error("Setup failed", e) + end + + desc 'status', 'Show current status of test suite components' + def status + setup_models + + say Paint["📋 PNG Test Suite Status", :cyan, :bold] + say + + # Repository info + say Paint["📁 Repository:", :blue, :bold] + say " Root: #{@repository.root_path}" + say " Valid: #{@repository.valid? ? Paint['✅ Yes', :green] : Paint['❌ No', :red]}" + say + + # PngCheck executable + say Paint["🔧 PngCheck Executable:", :blue, :bold] + if @pngcheck && @pngcheck.valid? + say " Path: #{Paint[@pngcheck.executable_path, :green]}" + version = @pngcheck.version + say " Version: #{version}" if version + else + say Paint[" ❌ Not found or invalid", :red] + say " Run 'make' to build pngcheck or set PNGCHECK_EXECUTABLE" + end + say + + # PNG Suite files + say Paint["🖼️ PNG Suite Files:", :blue, :bold] + if @png_suite.available? + stats = @png_suite.statistics + say " Available: #{Paint['✅ Yes', :green]} (#{stats[:total_files]} files)" + say " Expectations: #{stats[:expectations_generated]} generated" + + if stats[:total_files] > 0 + say " Categories:" + say " Valid: #{Paint[stats[:valid], :green]}" + say " Invalid: #{Paint[stats[:invalid], :red]}" + say " Warning: #{Paint[stats[:warning], :yellow]}" + say " Unknown: #{Paint[stats[:unknown], :gray]}" if stats[:unknown] > 0 + end + else + say Paint[" ❌ Test PNG files missing from #{@png_suite.png_dir}", :red] + end + say + + # Test suite file + say Paint["🧪 Test Suite File:", :blue, :bold] + if @repository.test_suite_file.exist? + say " Generated: #{Paint['✅ Yes', :green]}" + say " File: #{@repository.test_suite_file.relative_path_from(@repository.root_path)}" + say " Size: #{@repository.test_suite_file.size} bytes" + say " Modified: #{@repository.test_suite_file.mtime.strftime('%Y-%m-%d %H:%M:%S')}" + else + say Paint[" ❌ Not generated", :red] + say " Run 'pngcheck-test generate' to create test suite" + end + rescue => e + handle_error("Failed to get status", e) + end + + desc 'version', 'Show version information' + def version + say Paint["pngcheck-test version #{VERSION}", :blue, :bold] + end + + private + + def setup_models + @repository = Models::Repository.new + @png_suite = Models::PngSuite.new(@repository) + @test_generator = Models::TestGenerator.new(@repository, @png_suite) + + # Try to initialize pngcheck, but don't fail if it's not found for status command + begin + @pngcheck = Models::PngCheck.new(@repository) + rescue Models::PngCheck::ExecutableNotFoundError => e + @pngcheck = nil + @pngcheck_error = e.message + end + rescue Models::Repository::RepositoryNotFoundError => e + say Paint["❌ #{e.message}", :red] + say Paint["Please run this command from within the pngcheck repository.", :yellow] + exit 1 + end + + def ensure_pngcheck! + return if @pngcheck + + say Paint["❌ #{@pngcheck_error}", :red] + exit 1 + end + + def handle_error(message, error) + say Paint["❌ #{message}: #{error.message}", :red] + say Paint["💡 Run with --verbose for more details", :yellow] unless options[:verbose] + + if options[:verbose] + say Paint["🔍 Error details:", :gray] + say error.backtrace.join("\n") + end + + exit 1 + end + end +end diff --git a/test/lib/pngcheck_test/models/png_suite.rb b/test/lib/pngcheck_test/models/png_suite.rb new file mode 100644 index 0000000..f9a6528 --- /dev/null +++ b/test/lib/pngcheck_test/models/png_suite.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true + +require 'fileutils' +require 'open3' +require 'zip' + +module PngcheckTest + module Models + class PngSuite + PNGSUITE_DIR = 'PngSuite' + + attr_reader :repository + + def initialize(repository) + @repository = repository + end + + # Check if PNG suite is downloaded + def available? + @repository.fixtures_path.exist? && png_files.any? + end + + # Get all PNG files + def png_files + return [] unless @repository.fixtures_path.exist? + + @repository.fixtures_path.glob('*.png').sort + end + + # Generate expectations for all PNG files + def generate_expectations!(pngcheck, force: false) + @repository.ensure_directories! + + results = { + processed: 0, + success: 0, + errors: 0, + files: [] + } + + png_files.each do |png_file| + expectation_file = expectation_file_for(png_file) + + # Skip if expectation exists and not forcing + if expectation_file.exist? && !force + results[:files] << { file: png_file.basename, status: :skipped, message: 'Already exists' } + next + end + + result = pngcheck.run(png_file) + expectation_file.write(result[:output]) + + results[:processed] += 1 + if result[:success] + results[:success] += 1 + results[:files] << { file: png_file.basename, status: :success, message: 'Generated' } + else + results[:errors] += 1 + results[:files] << { file: png_file.basename, status: :error, message: "Exit code: #{result[:exit_code]}" } + end + end + + results + end + + # Get expectation files + def expectation_files + return [] unless @repository.expectations_path.exist? + + @repository.expectations_path.glob('*.out').sort + end + + # Get expectation file path for a PNG file + def expectation_file_for(png_file) + @repository.expectations_path / "#{png_file.basename}.out" + end + + # Categorize a PNG file based on its expected output + def categorize_png(png_file) + expectation_file = expectation_file_for(png_file) + return :unknown unless expectation_file.exist? + + content = expectation_file.read + + if content.include?('ERROR:') || content.include?('ERRORS DETECTED') + :invalid + elsif content.include?('additional data after IEND chunk') || + content.include?('private (invalid?) PLTE chunk') || + content.include?('invalid') + :warning + else + :valid + end + end + + # Get statistics about PNG files + def statistics + files = png_files + expectations = expectation_files + + categories = files.group_by { |f| categorize_png(f) } + + { + total_files: files.count, + expectations_generated: expectations.count, + valid: categories[:valid]&.count || 0, + invalid: categories[:invalid]&.count || 0, + warning: categories[:warning]&.count || 0, + unknown: categories[:unknown]&.count || 0 + } + end + + end + end +end diff --git a/test/lib/pngcheck_test/models/pngcheck.rb b/test/lib/pngcheck_test/models/pngcheck.rb new file mode 100644 index 0000000..efc3184 --- /dev/null +++ b/test/lib/pngcheck_test/models/pngcheck.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +require 'open3' + +module PngcheckTest + module Models + class PngCheck + class ExecutableNotFoundError < StandardError; end + + attr_reader :repository, :executable_path + + def initialize(repository) + @repository = repository + @executable_path = find_executable + raise ExecutableNotFoundError, executable_not_found_message unless @executable_path + end + + # Run pngcheck on a file and return output + def run(png_file) + stdout, stderr, status = Open3.capture3(@executable_path.to_s, png_file.to_s) + + # Combine stdout and stderr for complete output + output = stdout + stderr + + { + output: output, + exit_code: status.exitstatus, + success: status.success? || status.exitstatus == 1 # pngcheck returns 1 for invalid files + } + end + + # Check if executable exists and is working + def valid? + return false unless @executable_path&.exist? + + # Try to run pngcheck with no arguments to verify it works + # pngcheck will try to read from stdin and exit with code 2, which is expected + begin + _, _, status = Open3.capture3(@executable_path.to_s) + # pngcheck exits with 2 when no arguments provided (tries to read stdin), which is normal + status.exitstatus == 2 + rescue + false + end + end + + # Get version information + def version + return nil unless valid? + + begin + # Use an invalid option to trigger help output which includes version + stdout, stderr, _ = Open3.capture3(@executable_path.to_s, '--invalid-option') + output = stdout + stderr + # Extract version from help output - note the comma after version number + if output =~ /PNGcheck, version ([\d.]+),/ + $1 + else + 'unknown' + end + rescue + nil + end + end + + private + + def find_executable + # Check environment variable first + env_path = ENV['PNGCHECK_EXECUTABLE'] + if env_path && Pathname.new(env_path).exist? + return Pathname.new(env_path) + end + + # Try repository-relative candidates + @repository.pngcheck_candidates.each do |candidate| + return candidate if candidate.exist? + end + + # Try system PATH + system_executable = find_in_system_path + return Pathname.new(system_executable) if system_executable + + nil + end + + def find_in_system_path + begin + stdout, _, status = Open3.capture3('which', 'pngcheck') + return stdout.strip if status.success? && !stdout.strip.empty? + rescue + # which command not available, try where on Windows + begin + stdout, _, status = Open3.capture3('where', 'pngcheck') + return stdout.lines.first&.strip if status.success? && !stdout.strip.empty? + rescue + # Neither which nor where available + end + end + + nil + end + + def executable_not_found_message + candidates = @repository.pngcheck_candidates.map(&:to_s) + + <<~MESSAGE + pngcheck executable not found! + + Please build pngcheck first or set PNGCHECK_EXECUTABLE environment variable. + + Searched locations: + #{candidates.map { |c| " - #{c}" }.join("\n")} + + To build pngcheck: + make + + Or set environment variable: + export PNGCHECK_EXECUTABLE=/path/to/pngcheck + MESSAGE + end + end + end +end diff --git a/test/lib/pngcheck_test/models/repository.rb b/test/lib/pngcheck_test/models/repository.rb new file mode 100644 index 0000000..1e2e391 --- /dev/null +++ b/test/lib/pngcheck_test/models/repository.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'pathname' + +module PngcheckTest + module Models + class Repository + class RepositoryNotFoundError < StandardError; end + + attr_reader :root_path + + def initialize(start_path = nil) + # Repository root is 4 levels up from this file: ../../../.. + @root_path = Pathname.new("#{__dir__}/../../../..").expand_path + puts "Repository root path: #{@root_path}" + raise RepositoryNotFoundError, "Could not find repository root" unless @root_path && valid? + end + + # Core paths relative to repository root + def fixtures_path + @root_path / 'test' / 'fixtures' / 'pngsuite' + end + + def expectations_path + @root_path / 'test' / 'expectations' / 'pngsuite' + end + + def unity_path + @root_path / 'test' / 'test' + end + + def test_suite_file + unity_path / 'test_pngcheck_suite.c' + end + + # Pngcheck executable candidates + def pngcheck_candidates + Dir.glob(@root_path / '**' / '{pngcheck,pngcheck.exe}').map { |path| Pathname.new(path) } + end + + # Ensure directories exist + def ensure_directories! + [fixtures_path, expectations_path, unity_path].each do |path| + path.mkpath unless path.exist? + end + end + + # Check if we're in a valid repository + def valid? + @root_path && (@root_path / 'pngcheck.c').exist? + end + + end + end +end diff --git a/test/lib/pngcheck_test/models/test_generator.rb b/test/lib/pngcheck_test/models/test_generator.rb new file mode 100644 index 0000000..1fec749 --- /dev/null +++ b/test/lib/pngcheck_test/models/test_generator.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'liquid' + +module PngcheckTest + module Models + class TestGenerator + attr_reader :repository, :png_suite + + def initialize(repository, png_suite) + @repository = repository + @png_suite = png_suite + end + + # Generate the test suite C file + def generate! + png_files_data = prepare_png_files_data + statistics = calculate_statistics + + template_path = template_file_path + template_content = template_path.read + template = Liquid::Template.parse(template_content) + + template_data = { + 'png_files' => png_files_data, + 'statistics' => statistics, + 'generated_at' => Time.now.strftime('%Y-%m-%d %H:%M:%S') + } + + content = template.render(template_data) + + @repository.test_suite_file.write(content) + + { + success: true, + file: @repository.test_suite_file, + statistics: statistics + } + end + + private + + def prepare_png_files_data + @png_suite.png_files.map do |png_file| + category = @png_suite.categorize_png(png_file) + test_name = sanitize_test_name(png_file.basename('.png').to_s) + png_filename = png_file.basename.to_s + + { + 'test_name' => test_name, + 'filename' => png_filename, + 'category' => category.to_s + } + end + end + + def sanitize_test_name(filename) + # Convert filename to valid C identifier + filename.gsub(/[^a-zA-Z0-9_]/, '_').gsub(/_+/, '_').gsub(/^_|_$/, '') + end + + def calculate_statistics + stats = @png_suite.statistics + { + valid: stats[:valid], + invalid: stats[:invalid], + warning: stats[:warning], + unknown: stats[:unknown], + total: @png_suite.png_files.length + } + end + + def template_file_path + # Template is located relative to this file + Pathname.new(__dir__).parent / 'templates' / 'test_suite.c.liquid' + end + end + end +end diff --git a/test/lib/pngcheck_test/templates/test_suite.c.liquid b/test/lib/pngcheck_test/templates/test_suite.c.liquid new file mode 100644 index 0000000..f043ada --- /dev/null +++ b/test/lib/pngcheck_test/templates/test_suite.c.liquid @@ -0,0 +1,23 @@ +#include "unity.h" +#include "test_helpers.h" + +void setUp(void) { + // Set up code here +} + +void tearDown(void) { + // Tear down code here +} + +{% for png_file in png_files -%} +void test_{{ png_file.test_name }}(void) { +{%- if png_file.category == "invalid" %} + test_png_file("{{ png_file.filename }}", 2, "ERROR"); +{%- elsif png_file.category == "warning" %} + test_png_file("{{ png_file.filename }}", 0, "OK"); +{%- else %} + test_png_file("{{ png_file.filename }}", 0, "OK"); +{%- endif %} +} + +{% endfor -%} diff --git a/test/lib/pngcheck_test/version.rb b/test/lib/pngcheck_test/version.rb new file mode 100644 index 0000000..5baa979 --- /dev/null +++ b/test/lib/pngcheck_test/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module PngcheckTest + VERSION = '1.0.0' +end diff --git a/test/test/support/test_helpers.c b/test/test/support/test_helpers.c new file mode 100644 index 0000000..b7a2f66 --- /dev/null +++ b/test/test/support/test_helpers.c @@ -0,0 +1,266 @@ +#include "test_helpers.h" +#include "unity.h" +#include +#include +#include +#include + +#ifdef _WIN32 + #define WIFEXITED(status) 1 + #define WEXITSTATUS(status) (status) + #define IS_WINDOWS 1 +#else + #include + #include + #define IS_WINDOWS 0 +#endif + +#define MAX_PATH 1024 +#define MAX_OUTPUT 4096 + +const char *get_pngsuite_path(void) { + static char path[MAX_PATH]; + static bool initialized = false; + if (!initialized) { + const char *env_path = getenv("PNGSUITE_PATH"); + if (env_path) { + strncpy(path, env_path, sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + } else { + fprintf(stderr, "ERROR: PNGSUITE_PATH environment variable not set\n"); + exit(1); + } + initialized = true; + } + return path; +} + +const char *get_expectations_path(void) { + static char path[MAX_PATH]; + static bool initialized = false; + if (!initialized) { + const char *env_path = getenv("EXPECTATIONS_PATH"); + if (env_path) { + strncpy(path, env_path, sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + } else { + fprintf(stderr, "ERROR: EXPECTATIONS_PATH environment variable not set\n"); + exit(1); + } + initialized = true; + } + return path; +} + +static int run_command(const char *command, char *output, size_t output_size) { + FILE *fp = popen(command, "r"); + if (!fp) { + strcpy(output, "ERROR: Failed to run command"); + return -1; + } + + size_t total_read = 0; + while (total_read < output_size - 1 && + fgets(output + total_read, output_size - total_read, fp)) { + total_read = strlen(output); + } + + int exit_code = pclose(fp); + return WIFEXITED(exit_code) ? WEXITSTATUS(exit_code) : exit_code; +} + +static const char *clean_output(const char *output) { + static char cleaned[MAX_OUTPUT]; + char *dest = cleaned; + const char *line = output; + + while (*line) { + const char *line_start = line; + const char *line_end = strchr(line, '\n'); + if (!line_end) line_end = line + strlen(line); + + // Skip warning lines and empty lines + bool skip = false; + if (strstr(line_start, "warning:") || strstr(line_start, "zlib warning:")) { + skip = true; + } else { + // Check if empty (only whitespace) + skip = true; + for (const char *p = line_start; p < line_end; p++) { + if (!isspace(*p)) { + skip = false; + break; + } + } + } + + if (!skip) { + size_t line_len = line_end - line_start; + if (dest + line_len + 1 < cleaned + sizeof(cleaned)) { + memcpy(dest, line_start, line_len); + dest += line_len; + if (*line_end == '\n') *dest++ = '\n'; + } + } + + line = (*line_end == '\n') ? line_end + 1 : line_end; + if (*line_end == '\0') break; + } + + *dest = '\0'; + return cleaned; +} + +static void escape_shell_arg(const char *input, char *output, size_t output_size) { + if (!input || !output || output_size < 3) { + if (output && output_size > 0) output[0] = '\0'; + return; + } + + size_t pos = 0; + output[pos++] = '"'; + + for (const char *p = input; *p && pos < output_size - 2; p++) { + if (*p == '"' || *p == '\\' || *p == '$' || *p == '`') { + if (pos < output_size - 3) { + output[pos++] = '\\'; + output[pos++] = *p; + } + } else { + output[pos++] = *p; + } + } + + output[pos++] = '"'; + output[pos] = '\0'; +} + +static int build_command(char *command, size_t command_size, const char *pngcheck_exe, + const char *options, const char *png_filename) { + char escaped_filename[MAX_PATH * 2]; + char escaped_options[MAX_PATH * 2]; + + if (!command || !pngcheck_exe) { + return -1; + } + + // Escape the filename if provided + if (png_filename) { + char full_path[MAX_PATH]; + snprintf(full_path, sizeof(full_path), "%s/%s", get_pngsuite_path(), png_filename); + escape_shell_arg(full_path, escaped_filename, sizeof(escaped_filename)); + } + + // Escape options if provided + if (options) { + escape_shell_arg(options, escaped_options, sizeof(escaped_options)); + } + + // Build the command safely + if (png_filename && options) { + snprintf(command, command_size, "%s %s %s 2>&1", + pngcheck_exe, escaped_options, escaped_filename); + } else if (png_filename) { + snprintf(command, command_size, "%s %s 2>&1", + pngcheck_exe, escaped_filename); + } else if (options) { + snprintf(command, command_size, "%s %s 2>&1", + pngcheck_exe, escaped_options); + } else { + snprintf(command, command_size, "%s 2>&1", pngcheck_exe); + } + + return 0; +} + +void test_png_file(const char *png_filename, int expected_exit_code, const char *expected_prefix) { + char command[MAX_PATH * 2]; + char output[MAX_OUTPUT]; + + const char *pngcheck_exe = getenv("PNGCHECK_EXECUTABLE"); + if (!pngcheck_exe) { + TEST_FAIL_MESSAGE("PNGCHECK_EXECUTABLE environment variable not set"); + return; + } + + if (build_command(command, sizeof(command), pngcheck_exe, NULL, png_filename) != 0) { + TEST_FAIL_MESSAGE("Failed to build command"); + return; + } + + int actual_exit_code = run_command(command, output, sizeof(output)); + + // Check exit code (skip on Windows) + if (!IS_WINDOWS && expected_exit_code >= 0 && actual_exit_code != expected_exit_code) { + char message[MAX_OUTPUT]; + snprintf(message, sizeof(message), + "Exit code mismatch for %s: expected %d, got %d\nOutput: %s", + png_filename, expected_exit_code, actual_exit_code, output); + TEST_FAIL_MESSAGE(message); + } + + // Check status output + if (expected_prefix) { + const char *cleaned = clean_output(output); + bool ok = false; + + if (strcmp(expected_prefix, "OK") == 0) { + ok = strncmp(cleaned, "OK:", 3) == 0; + } else if (strcmp(expected_prefix, "ERROR") == 0) { + ok = strstr(cleaned, "ERROR:") != NULL; + } else { + ok = strncmp(cleaned, expected_prefix, strlen(expected_prefix)) == 0; + } + + if (!ok) { + char message[MAX_OUTPUT]; + snprintf(message, sizeof(message), + "Output mismatch for %s: expected '%s'\nActual: %s\nCleaned: %s", + png_filename, expected_prefix, output, cleaned); + TEST_FAIL_MESSAGE(message); + } + } +} + +void test_pngcheck_with_options(const char *options, const char *png_filename, int expected_exit_code, const char *expected_content) { + char command[MAX_PATH * 2]; + char output[MAX_OUTPUT]; + + const char *pngcheck_exe = getenv("PNGCHECK_EXECUTABLE"); + if (!pngcheck_exe) { + TEST_FAIL_MESSAGE("PNGCHECK_EXECUTABLE environment variable not set"); + return; + } + + if (build_command(command, sizeof(command), pngcheck_exe, options, png_filename) != 0) { + TEST_FAIL_MESSAGE("Failed to build command"); + return; + } + + int exit_code = run_command(command, output, sizeof(output)); + + // Check exit code (skip on Windows) + if (!IS_WINDOWS && expected_exit_code >= 0 && exit_code != expected_exit_code) { + char message[MAX_OUTPUT]; + snprintf(message, sizeof(message), + "Exit code mismatch for options '%s': expected %d, got %d\nOutput: %s", + options ? options : "", expected_exit_code, exit_code, output); + TEST_FAIL_MESSAGE(message); + } + + // Check content + if (expected_content && strlen(expected_content) > 0) { + const char *cleaned = clean_output(output); + if (!strstr(cleaned, expected_content)) { + char message[MAX_OUTPUT]; + snprintf(message, sizeof(message), + "Expected content '%s' not found for options '%s'\nActual: %s\nCleaned: %s", + expected_content, options ? options : "", output, cleaned); + TEST_FAIL_MESSAGE(message); + } + } +} + +void test_pngcheck_help(void) { + test_pngcheck_with_options("-h", NULL, 0, "Usage:"); +} diff --git a/test/test/support/test_helpers.h b/test/test/support/test_helpers.h new file mode 100644 index 0000000..99fccb5 --- /dev/null +++ b/test/test/support/test_helpers.h @@ -0,0 +1,19 @@ +#ifndef TEST_HELPERS_H +#define TEST_HELPERS_H + +#include + +// Test a PNG file against expected return code and output prefix +void test_png_file(const char *png_filename, int expected_exit_code, const char *expected_prefix); + +// Test pngcheck with command-line options +void test_pngcheck_with_options(const char *options, const char *png_filename, int expected_exit_code, const char *expected_content); + +// Test pngcheck help option +void test_pngcheck_help(void); + +// Utility functions +const char *get_pngsuite_path(void); +const char *get_expectations_path(void); + +#endif // TEST_HELPERS_H diff --git a/test/test/test_pngcheck_cli.c b/test/test/test_pngcheck_cli.c new file mode 100644 index 0000000..f4e8e22 --- /dev/null +++ b/test/test/test_pngcheck_cli.c @@ -0,0 +1,60 @@ +#include "unity.h" +#include "test_helpers.h" + +void setUp(void) { + // Set up code here +} + +void tearDown(void) { + // Tear down code here +} + +void test_pngcheck_help_option(void) { + // Test that pngcheck shows help when -h option is used + test_pngcheck_help(); +} + +void test_pngcheck_nonexistent_file(void) { + // Test behavior with nonexistent file + test_pngcheck_with_options("", "nonexistent_file.png", 2, "No such file"); +} + +void test_pngcheck_invalid_file(void) { + // Test behavior with invalid PNG file + test_png_file("xc1n0g08.png", 2, "ERROR"); +} + +void test_pngcheck_valid_png(void) { + // Test with a valid PNG file + test_png_file("basn0g01.png", 0, "OK"); +} + +void test_pngcheck_verbose_option(void) { + // Test verbose output with -v option + test_pngcheck_with_options("-v", "basn0g01.png", 0, "chunk"); +} + +void test_pngcheck_quiet_option(void) { + // Test quiet output with -q option (should only show errors) + test_pngcheck_with_options("-q", "basn0g01.png", 0, ""); +} + +void test_pngcheck_palette_option(void) { + // Test palette info with -p option on a palette image + test_pngcheck_with_options("-p", "basn3p01.png", 0, "PLTE"); +} + +void test_pngcheck_text_option(void) { + // Test text chunk display with -t option + test_pngcheck_with_options("-t", "ct1n0g04.png", 0, "Title:"); +} + +void test_pngcheck_colorize_option(void) { + // Test colorized output with -c option (just check it doesn't crash) + test_pngcheck_with_options("-c", "basn0g01.png", 0, "OK"); +} + +void test_pngcheck_seven_bit_option(void) { + // Test 7-bit text display with -7 option + test_pngcheck_with_options("-7", "ct1n0g04.png", 0, "Title:"); +} diff --git a/test/test/test_pngcheck_suite.c b/test/test/test_pngcheck_suite.c new file mode 100644 index 0000000..3365677 --- /dev/null +++ b/test/test/test_pngcheck_suite.c @@ -0,0 +1,715 @@ +#include "unity.h" +#include "test_helpers.h" + +void setUp(void) { + // Set up code here +} + +void tearDown(void) { + // Tear down code here +} + +void test_PngSuite(void) { + test_png_file("PngSuite.png", 0, "OK"); +} + +void test_basi0g01(void) { + test_png_file("basi0g01.png", 0, "OK"); +} + +void test_basi0g02(void) { + test_png_file("basi0g02.png", 0, "OK"); +} + +void test_basi0g04(void) { + test_png_file("basi0g04.png", 0, "OK"); +} + +void test_basi0g08(void) { + test_png_file("basi0g08.png", 0, "OK"); +} + +void test_basi0g16(void) { + test_png_file("basi0g16.png", 0, "OK"); +} + +void test_basi2c08(void) { + test_png_file("basi2c08.png", 0, "OK"); +} + +void test_basi2c16(void) { + test_png_file("basi2c16.png", 0, "OK"); +} + +void test_basi3p01(void) { + test_png_file("basi3p01.png", 0, "OK"); +} + +void test_basi3p02(void) { + test_png_file("basi3p02.png", 0, "OK"); +} + +void test_basi3p04(void) { + test_png_file("basi3p04.png", 0, "OK"); +} + +void test_basi3p08(void) { + test_png_file("basi3p08.png", 0, "OK"); +} + +void test_basi4a08(void) { + test_png_file("basi4a08.png", 0, "OK"); +} + +void test_basi4a16(void) { + test_png_file("basi4a16.png", 0, "OK"); +} + +void test_basi6a08(void) { + test_png_file("basi6a08.png", 0, "OK"); +} + +void test_basi6a16(void) { + test_png_file("basi6a16.png", 0, "OK"); +} + +void test_basn0g01(void) { + test_png_file("basn0g01.png", 0, "OK"); +} + +void test_basn0g02(void) { + test_png_file("basn0g02.png", 0, "OK"); +} + +void test_basn0g04(void) { + test_png_file("basn0g04.png", 0, "OK"); +} + +void test_basn0g08(void) { + test_png_file("basn0g08.png", 0, "OK"); +} + +void test_basn0g16(void) { + test_png_file("basn0g16.png", 0, "OK"); +} + +void test_basn2c08(void) { + test_png_file("basn2c08.png", 0, "OK"); +} + +void test_basn2c16(void) { + test_png_file("basn2c16.png", 0, "OK"); +} + +void test_basn3p01(void) { + test_png_file("basn3p01.png", 0, "OK"); +} + +void test_basn3p02(void) { + test_png_file("basn3p02.png", 0, "OK"); +} + +void test_basn3p04(void) { + test_png_file("basn3p04.png", 0, "OK"); +} + +void test_basn3p08(void) { + test_png_file("basn3p08.png", 0, "OK"); +} + +void test_basn4a08(void) { + test_png_file("basn4a08.png", 0, "OK"); +} + +void test_basn4a16(void) { + test_png_file("basn4a16.png", 0, "OK"); +} + +void test_basn6a08(void) { + test_png_file("basn6a08.png", 0, "OK"); +} + +void test_basn6a16(void) { + test_png_file("basn6a16.png", 0, "OK"); +} + +void test_bgai4a08(void) { + test_png_file("bgai4a08.png", 0, "OK"); +} + +void test_bgai4a16(void) { + test_png_file("bgai4a16.png", 0, "OK"); +} + +void test_bgan6a08(void) { + test_png_file("bgan6a08.png", 0, "OK"); +} + +void test_bgan6a16(void) { + test_png_file("bgan6a16.png", 0, "OK"); +} + +void test_bgbn4a08(void) { + test_png_file("bgbn4a08.png", 0, "OK"); +} + +void test_bggn4a16(void) { + test_png_file("bggn4a16.png", 0, "OK"); +} + +void test_bgwn6a08(void) { + test_png_file("bgwn6a08.png", 0, "OK"); +} + +void test_bgyn6a16(void) { + test_png_file("bgyn6a16.png", 0, "OK"); +} + +void test_ccwn2c08(void) { + test_png_file("ccwn2c08.png", 0, "OK"); +} + +void test_ccwn3p08(void) { + test_png_file("ccwn3p08.png", 0, "OK"); +} + +void test_cdfn2c08(void) { + test_png_file("cdfn2c08.png", 0, "OK"); +} + +void test_cdhn2c08(void) { + test_png_file("cdhn2c08.png", 0, "OK"); +} + +void test_cdsn2c08(void) { + test_png_file("cdsn2c08.png", 0, "OK"); +} + +void test_cdun2c08(void) { + test_png_file("cdun2c08.png", 0, "OK"); +} + +void test_ch1n3p04(void) { + test_png_file("ch1n3p04.png", 0, "OK"); +} + +void test_ch2n3p08(void) { + test_png_file("ch2n3p08.png", 0, "OK"); +} + +void test_cm0n0g04(void) { + test_png_file("cm0n0g04.png", 0, "OK"); +} + +void test_cm7n0g04(void) { + test_png_file("cm7n0g04.png", 2, "ERROR"); +} + +void test_cm9n0g04(void) { + test_png_file("cm9n0g04.png", 0, "OK"); +} + +void test_cs3n2c16(void) { + test_png_file("cs3n2c16.png", 0, "OK"); +} + +void test_cs3n3p08(void) { + test_png_file("cs3n3p08.png", 0, "OK"); +} + +void test_cs5n2c08(void) { + test_png_file("cs5n2c08.png", 0, "OK"); +} + +void test_cs5n3p08(void) { + test_png_file("cs5n3p08.png", 0, "OK"); +} + +void test_cs8n2c08(void) { + test_png_file("cs8n2c08.png", 0, "OK"); +} + +void test_cs8n3p08(void) { + test_png_file("cs8n3p08.png", 0, "OK"); +} + +void test_ct0n0g04(void) { + test_png_file("ct0n0g04.png", 0, "OK"); +} + +void test_ct1n0g04(void) { + test_png_file("ct1n0g04.png", 0, "OK"); +} + +void test_cten0g04(void) { + test_png_file("cten0g04.png", 0, "OK"); +} + +void test_ctfn0g04(void) { + test_png_file("ctfn0g04.png", 0, "OK"); +} + +void test_ctgn0g04(void) { + test_png_file("ctgn0g04.png", 0, "OK"); +} + +void test_cthn0g04(void) { + test_png_file("cthn0g04.png", 0, "OK"); +} + +void test_ctjn0g04(void) { + test_png_file("ctjn0g04.png", 0, "OK"); +} + +void test_ctzn0g04(void) { + test_png_file("ctzn0g04.png", 0, "OK"); +} + +void test_exif2c08(void) { + test_png_file("exif2c08.png", 0, "OK"); +} + +void test_f00n0g08(void) { + test_png_file("f00n0g08.png", 0, "OK"); +} + +void test_f00n2c08(void) { + test_png_file("f00n2c08.png", 0, "OK"); +} + +void test_f01n0g08(void) { + test_png_file("f01n0g08.png", 0, "OK"); +} + +void test_f01n2c08(void) { + test_png_file("f01n2c08.png", 0, "OK"); +} + +void test_f02n0g08(void) { + test_png_file("f02n0g08.png", 0, "OK"); +} + +void test_f02n2c08(void) { + test_png_file("f02n2c08.png", 0, "OK"); +} + +void test_f03n0g08(void) { + test_png_file("f03n0g08.png", 0, "OK"); +} + +void test_f03n2c08(void) { + test_png_file("f03n2c08.png", 0, "OK"); +} + +void test_f04n0g08(void) { + test_png_file("f04n0g08.png", 0, "OK"); +} + +void test_f04n2c08(void) { + test_png_file("f04n2c08.png", 0, "OK"); +} + +void test_f99n0g04(void) { + test_png_file("f99n0g04.png", 0, "OK"); +} + +void test_g03n0g16(void) { + test_png_file("g03n0g16.png", 0, "OK"); +} + +void test_g03n2c08(void) { + test_png_file("g03n2c08.png", 0, "OK"); +} + +void test_g03n3p04(void) { + test_png_file("g03n3p04.png", 0, "OK"); +} + +void test_g04n0g16(void) { + test_png_file("g04n0g16.png", 0, "OK"); +} + +void test_g04n2c08(void) { + test_png_file("g04n2c08.png", 0, "OK"); +} + +void test_g04n3p04(void) { + test_png_file("g04n3p04.png", 0, "OK"); +} + +void test_g05n0g16(void) { + test_png_file("g05n0g16.png", 0, "OK"); +} + +void test_g05n2c08(void) { + test_png_file("g05n2c08.png", 0, "OK"); +} + +void test_g05n3p04(void) { + test_png_file("g05n3p04.png", 0, "OK"); +} + +void test_g07n0g16(void) { + test_png_file("g07n0g16.png", 0, "OK"); +} + +void test_g07n2c08(void) { + test_png_file("g07n2c08.png", 0, "OK"); +} + +void test_g07n3p04(void) { + test_png_file("g07n3p04.png", 0, "OK"); +} + +void test_g10n0g16(void) { + test_png_file("g10n0g16.png", 0, "OK"); +} + +void test_g10n2c08(void) { + test_png_file("g10n2c08.png", 0, "OK"); +} + +void test_g10n3p04(void) { + test_png_file("g10n3p04.png", 0, "OK"); +} + +void test_g25n0g16(void) { + test_png_file("g25n0g16.png", 0, "OK"); +} + +void test_g25n2c08(void) { + test_png_file("g25n2c08.png", 0, "OK"); +} + +void test_g25n3p04(void) { + test_png_file("g25n3p04.png", 0, "OK"); +} + +void test_oi1n0g16(void) { + test_png_file("oi1n0g16.png", 0, "OK"); +} + +void test_oi1n2c16(void) { + test_png_file("oi1n2c16.png", 0, "OK"); +} + +void test_oi2n0g16(void) { + test_png_file("oi2n0g16.png", 0, "OK"); +} + +void test_oi2n2c16(void) { + test_png_file("oi2n2c16.png", 0, "OK"); +} + +void test_oi4n0g16(void) { + test_png_file("oi4n0g16.png", 0, "OK"); +} + +void test_oi4n2c16(void) { + test_png_file("oi4n2c16.png", 0, "OK"); +} + +void test_oi9n0g16(void) { + test_png_file("oi9n0g16.png", 0, "OK"); +} + +void test_oi9n2c16(void) { + test_png_file("oi9n2c16.png", 0, "OK"); +} + +void test_pp0n2c16(void) { + test_png_file("pp0n2c16.png", 0, "OK"); +} + +void test_pp0n6a08(void) { + test_png_file("pp0n6a08.png", 0, "OK"); +} + +void test_ps1n0g08(void) { + test_png_file("ps1n0g08.png", 0, "OK"); +} + +void test_ps1n2c16(void) { + test_png_file("ps1n2c16.png", 0, "OK"); +} + +void test_ps2n0g08(void) { + test_png_file("ps2n0g08.png", 0, "OK"); +} + +void test_ps2n2c16(void) { + test_png_file("ps2n2c16.png", 0, "OK"); +} + +void test_s01i3p01(void) { + test_png_file("s01i3p01.png", 0, "OK"); +} + +void test_s01n3p01(void) { + test_png_file("s01n3p01.png", 0, "OK"); +} + +void test_s02i3p01(void) { + test_png_file("s02i3p01.png", 0, "OK"); +} + +void test_s02n3p01(void) { + test_png_file("s02n3p01.png", 0, "OK"); +} + +void test_s03i3p01(void) { + test_png_file("s03i3p01.png", 0, "OK"); +} + +void test_s03n3p01(void) { + test_png_file("s03n3p01.png", 0, "OK"); +} + +void test_s04i3p01(void) { + test_png_file("s04i3p01.png", 0, "OK"); +} + +void test_s04n3p01(void) { + test_png_file("s04n3p01.png", 0, "OK"); +} + +void test_s05i3p02(void) { + test_png_file("s05i3p02.png", 0, "OK"); +} + +void test_s05n3p02(void) { + test_png_file("s05n3p02.png", 0, "OK"); +} + +void test_s06i3p02(void) { + test_png_file("s06i3p02.png", 0, "OK"); +} + +void test_s06n3p02(void) { + test_png_file("s06n3p02.png", 0, "OK"); +} + +void test_s07i3p02(void) { + test_png_file("s07i3p02.png", 0, "OK"); +} + +void test_s07n3p02(void) { + test_png_file("s07n3p02.png", 0, "OK"); +} + +void test_s08i3p02(void) { + test_png_file("s08i3p02.png", 0, "OK"); +} + +void test_s08n3p02(void) { + test_png_file("s08n3p02.png", 0, "OK"); +} + +void test_s09i3p02(void) { + test_png_file("s09i3p02.png", 0, "OK"); +} + +void test_s09n3p02(void) { + test_png_file("s09n3p02.png", 0, "OK"); +} + +void test_s32i3p04(void) { + test_png_file("s32i3p04.png", 0, "OK"); +} + +void test_s32n3p04(void) { + test_png_file("s32n3p04.png", 0, "OK"); +} + +void test_s33i3p04(void) { + test_png_file("s33i3p04.png", 0, "OK"); +} + +void test_s33n3p04(void) { + test_png_file("s33n3p04.png", 0, "OK"); +} + +void test_s34i3p04(void) { + test_png_file("s34i3p04.png", 0, "OK"); +} + +void test_s34n3p04(void) { + test_png_file("s34n3p04.png", 0, "OK"); +} + +void test_s35i3p04(void) { + test_png_file("s35i3p04.png", 0, "OK"); +} + +void test_s35n3p04(void) { + test_png_file("s35n3p04.png", 0, "OK"); +} + +void test_s36i3p04(void) { + test_png_file("s36i3p04.png", 0, "OK"); +} + +void test_s36n3p04(void) { + test_png_file("s36n3p04.png", 0, "OK"); +} + +void test_s37i3p04(void) { + test_png_file("s37i3p04.png", 0, "OK"); +} + +void test_s37n3p04(void) { + test_png_file("s37n3p04.png", 0, "OK"); +} + +void test_s38i3p04(void) { + test_png_file("s38i3p04.png", 0, "OK"); +} + +void test_s38n3p04(void) { + test_png_file("s38n3p04.png", 0, "OK"); +} + +void test_s39i3p04(void) { + test_png_file("s39i3p04.png", 0, "OK"); +} + +void test_s39n3p04(void) { + test_png_file("s39n3p04.png", 0, "OK"); +} + +void test_s40i3p04(void) { + test_png_file("s40i3p04.png", 0, "OK"); +} + +void test_s40n3p04(void) { + test_png_file("s40n3p04.png", 0, "OK"); +} + +void test_tbbn0g04(void) { + test_png_file("tbbn0g04.png", 0, "OK"); +} + +void test_tbbn2c16(void) { + test_png_file("tbbn2c16.png", 0, "OK"); +} + +void test_tbbn3p08(void) { + test_png_file("tbbn3p08.png", 0, "OK"); +} + +void test_tbgn2c16(void) { + test_png_file("tbgn2c16.png", 0, "OK"); +} + +void test_tbgn3p08(void) { + test_png_file("tbgn3p08.png", 0, "OK"); +} + +void test_tbrn2c08(void) { + test_png_file("tbrn2c08.png", 0, "OK"); +} + +void test_tbwn0g16(void) { + test_png_file("tbwn0g16.png", 0, "OK"); +} + +void test_tbwn3p08(void) { + test_png_file("tbwn3p08.png", 0, "OK"); +} + +void test_tbyn3p08(void) { + test_png_file("tbyn3p08.png", 0, "OK"); +} + +void test_tm3n3p02(void) { + test_png_file("tm3n3p02.png", 0, "OK"); +} + +void test_tp0n0g08(void) { + test_png_file("tp0n0g08.png", 0, "OK"); +} + +void test_tp0n2c08(void) { + test_png_file("tp0n2c08.png", 0, "OK"); +} + +void test_tp0n3p08(void) { + test_png_file("tp0n3p08.png", 0, "OK"); +} + +void test_tp1n3p08(void) { + test_png_file("tp1n3p08.png", 0, "OK"); +} + +void test_xc1n0g08(void) { + test_png_file("xc1n0g08.png", 2, "ERROR"); +} + +void test_xc9n2c08(void) { + test_png_file("xc9n2c08.png", 2, "ERROR"); +} + +void test_xcrn0g04(void) { + test_png_file("xcrn0g04.png", 2, "ERROR"); +} + +void test_xcsn0g01(void) { + test_png_file("xcsn0g01.png", 2, "ERROR"); +} + +void test_xd0n2c08(void) { + test_png_file("xd0n2c08.png", 2, "ERROR"); +} + +void test_xd3n2c08(void) { + test_png_file("xd3n2c08.png", 2, "ERROR"); +} + +void test_xd9n2c08(void) { + test_png_file("xd9n2c08.png", 2, "ERROR"); +} + +void test_xdtn0g01(void) { + test_png_file("xdtn0g01.png", 2, "ERROR"); +} + +void test_xhdn0g08(void) { + test_png_file("xhdn0g08.png", 2, "ERROR"); +} + +void test_xlfn0g04(void) { + test_png_file("xlfn0g04.png", 2, "ERROR"); +} + +void test_xs1n0g01(void) { + test_png_file("xs1n0g01.png", 2, "ERROR"); +} + +void test_xs2n0g01(void) { + test_png_file("xs2n0g01.png", 2, "ERROR"); +} + +void test_xs4n0g01(void) { + test_png_file("xs4n0g01.png", 2, "ERROR"); +} + +void test_xs7n0g01(void) { + test_png_file("xs7n0g01.png", 2, "ERROR"); +} + +void test_z00n2c08(void) { + test_png_file("z00n2c08.png", 0, "OK"); +} + +void test_z03n2c08(void) { + test_png_file("z03n2c08.png", 0, "OK"); +} + +void test_z06n2c08(void) { + test_png_file("z06n2c08.png", 0, "OK"); +} + +void test_z09n2c08(void) { + test_png_file("z09n2c08.png", 0, "OK"); +} +