module fortplot_test_output_helpers !! Utilities for directing test artifact output into structured directories use fortplot_system_runtime, only: create_directory_runtime use, intrinsic :: iso_fortran_env, only: error_unit, int64 implicit none private character(len=*), parameter :: preferred_output_dir = 'build/test/output/' character(len=*), parameter :: fallback_output_dir = 'build/test/output/' public :: ensure_test_output_dir public :: assert_pdf_file_valid contains subroutine ensure_test_output_dir(subdir, dir) !! Ensure a test output directory exists and return its normalized path character(len=*), intent(in) :: subdir character(len=:), allocatable, intent(out) :: dir character(len=:), allocatable :: target character(len=:), allocatable :: fallback_target logical :: ok, fallback_ok call build_target_path(preferred_output_dir, subdir, target) call create_directory_runtime(target, ok) if (ok) then dir = target return end if call build_target_path(fallback_output_dir, subdir, fallback_target) call create_directory_runtime(fallback_target, fallback_ok) if (.not. fallback_ok) then write (error_unit, '(A)') & 'ERROR: failed to create test output directory: '// & trim(target) write (error_unit, '(A)') & 'ERROR: failed to create fallback test output directory: '// & trim(fallback_target) error stop 1 end if dir = fallback_target contains subroutine build_target_path(base_dir, subdir_name, path) character(len=*), intent(in) :: base_dir character(len=*), intent(in) :: subdir_name character(len=:), allocatable, intent(out) :: path integer :: last_char path = trim(base_dir) if (len_trim(subdir_name) > 0) then path = path//trim(subdir_name) end if last_char = len_trim(path) if (last_char == 0) then write (error_unit, '(A)') & 'ERROR: computed output directory path is empty' error stop 1 end if if (path(last_char:last_char) /= '/') then path = path//'/' end if end subroutine build_target_path end subroutine ensure_test_output_dir subroutine assert_pdf_file_valid(path) character(len=*), intent(in) :: path character(len=5) :: header integer :: unit integer(int64) :: size_bytes integer :: io_read integer :: io_stat logical :: exists inquire (file=trim(path), exist=exists, size=size_bytes) if (.not. exists) then write (error_unit, '(A)') 'ERROR: expected PDF file missing: '// & trim(path) error stop 1 end if if (size_bytes < 512_int64) then write (error_unit, '(A,I0)') & 'ERROR: PDF file too small (bytes): ', size_bytes write (error_unit, '(A)') 'Path: '//trim(path) error stop 1 end if open (newunit=unit, file=trim(path), access='stream', & form='unformatted', status='old', action='read', iostat=io_stat) if (io_stat /= 0) then write (error_unit, '(A,I0)') & 'ERROR: failed to open PDF file for reading, iostat: ', io_stat write (error_unit, '(A)') 'Path: '//trim(path) error stop 1 end if read (unit, iostat=io_read) header close (unit, iostat=io_stat) if (io_read /= 0) then write (error_unit, '(A,I0)') & 'ERROR: failed to read PDF header, iostat: ', io_read write (error_unit, '(A)') 'Path: '//trim(path) error stop 1 end if if (io_stat /= 0) then write (error_unit, '(A,I0)') & 'ERROR: failed to close PDF file, iostat: ', io_stat write (error_unit, '(A)') 'Path: '//trim(path) error stop 1 end if if (header /= '%PDF-') then write (error_unit, '(A)') & 'ERROR: file does not look like a PDF: '//trim(path) error stop 1 end if end subroutine assert_pdf_file_valid end module fortplot_test_output_helpers