program help_test ! note hardcoded len=k1 instead of len=: in this test is a work-around a gfortran bug in old ! pre-v8.3 versions use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, stdout=>output_unit, stderr=>error_unit use fpm_filesystem, only : dirname, join_path, exists use fpm_environment, only : get_os_type, OS_WINDOWS implicit none integer :: i, j integer :: be, af character(len=:),allocatable :: path integer :: estat, cstat integer,parameter :: k1=132 character(len=k1) :: message logical,allocatable :: tally(:) !intel-bug!character(len=:),allocatable :: book1(:), book2(:) character(len=k1),allocatable :: book1(:), book2(:) !intel-bug!character(len=:),allocatable :: page1(:) character(len=k1),allocatable :: page1(:) integer :: lines integer :: chars ! run a variety of "fpm help" variations and verify expected files are generated character(len=*),parameter :: cmds(*) = [character(len=80) :: & ! build manual as pieces using various help commands ! debug version ' --version ',& ! verify fpm version being used ' --help > fpm_scratch_help.txt',& ' help new >> fpm_scratch_help.txt',& ' help update >> fpm_scratch_help.txt',& ' build --help >> fpm_scratch_help.txt',& ' help run >> fpm_scratch_help.txt',& ' help test >> fpm_scratch_help.txt',& ' help runner >> fpm_scratch_help.txt',& ' help install >> fpm_scratch_help.txt',& ' help list >> fpm_scratch_help.txt',& ' help help >> fpm_scratch_help.txt',& ' --version >> fpm_scratch_help.txt',& ! generate manual ' help manual > fpm_scratch_manual.txt'] !'fpm run >> fpm_scratch_help.txt',& !'fpm run -- --list >> fpm_scratch_help.txt',& !'fpm run -- list --list >> fpm_scratch_help.txt',& character(len=*),parameter :: names(*)=[character(len=10) ::& 'fpm','new','update','build','run','test','runner','install','list','help'] character(len=:), allocatable :: prog integer :: length ! FIXME: Super hacky way to get the name of the fpm executable, ! it works better than invoking fpm again but should be replaced ASAP. call get_command_argument(0, length=length) allocate(character(len=length) :: prog) call get_command_argument(0, prog) path = dirname(prog) if (get_os_type() == OS_WINDOWS) then prog = join_path(path, "..", "app", "fpm.exe") if (.not.exists(prog)) then prog = join_path(path, "..", "..", "app", "fpm.exe") end if else prog = join_path(path, "..", "app", "fpm") if (.not.exists(prog)) then prog = join_path(path, "..", "..", "app", "fpm") end if end if write(*,'(g0:,1x)')'TEST help SUBCOMMAND STARTED' if(allocated(tally))deallocate(tally) allocate(tally(0)) call wipe('fpm_scratch_help.txt') call wipe('fpm_scratch_manual.txt') ! check that output has NAME SYNOPSIS DESCRIPTION do i=1,size(names) write(*,*)'check '//names(i)//' for NAME SYNOPSIS DESCRIPTION' path= prog // ' help '//names(i)//' >fpm_scratch_help.txt' message='' call execute_command_line(path,exitstat=estat,cmdstat=cstat,cmdmsg=message) write(*,'(*(g0))')'CMD=',path,' EXITSTAT=',estat,' CMDSTAT=',cstat,' MESSAGE=',trim(message) tally=[tally,all([estat.eq.0,cstat.eq.0])] call swallow('fpm_scratch_help.txt',page1) if(size(page1).lt.3)then write(*,*)'help for '//names(i)//' ridiculiously small' tally=[tally,.false.] exit endif !!write(*,*)findloc(page1,'NAME').eq.1 be=count(.not.tally) tally=[tally,count(page1.eq.'NAME').eq.1] tally=[tally,count(page1.eq.'SYNOPSIS').eq.1] tally=[tally,count(page1.eq.'DESCRIPTION').eq.1] af=count(.not.tally) if(be.ne.af)then write(*,*)'missing expected sections in ',names(i) write(*,*)page1(1) ! assuming at least size 1 for debugging mingw write(*,*)count(page1.eq.'NAME') write(*,*)count(page1.eq.'SYNOPSIS') write(*,*)count(page1.eq.'DESCRIPTION') write(*,'(a)')page1 endif write(*,*)'have completed ',count(tally),' tests' call wipe('fpm_scratch_help.txt') enddo ! execute the fpm(1) commands do i=1,size(cmds) message='' path= prog // cmds(i) call execute_command_line(path,exitstat=estat,cmdstat=cstat,cmdmsg=message) write(*,'(*(g0))')'CMD=',path,' EXITSTAT=',estat,' CMDSTAT=',cstat,' MESSAGE=',trim(message) tally=[tally,all([estat.eq.0,cstat.eq.0])] enddo ! compare book written in fragments with manual call swallow('fpm_scratch_help.txt',book1) call swallow('fpm_scratch_manual.txt',book2) ! get rid of lines from run() which is not on stderr at the moment book1=pack(book1,index(book1,' + build/').eq.0) book2=pack(book1,index(book2,' + build/').eq.0) write(*,*)'book1 ',size(book1), len(book1) write(*,*)'book2 ',size(book2), len(book2) if(size(book1).ne.size(book2))then write(*,*)'manual and "debug" appended pages are not the same size' tally=[tally,.false.] else if(all(book1.ne.book2))then tally=[tally,.false.] write(*,*)'manual and "debug" appended pages are not the same' else write(*,*)'manual and "debug" appended pages are the same' tally=[tally,.true.] endif endif ! overall size of manual !chars=size(book2) !lines=max(count(char(10).eq.book2),count(char(13).eq.book2)) chars=sum(len_trim(book2)) ! SUM TRIMMED LENGTH lines=size(book2) if( (chars.lt.12000) .or. (lines.lt.350) )then write(*,*)'"debug" manual is suspiciously small, bytes=',chars,' lines=',lines tally=[tally,.false.] else write(*,*)'"debug" manual size in bytes=',chars,' lines=',lines tally=[tally,.true.] endif write(*,'("HELP TEST TALLY=",*(g0))')tally call wipe('fpm_scratch_help.txt') call wipe('fpm_scratch_manual.txt') if(all(tally))then write(*,'(*(g0))')'PASSED: all ',count(tally),' tests passed ' else write(*,*)'FAILED: PASSED=',count(tally),' FAILED=',count(.not.tally) stop 5 endif write(*,'(g0:,1x)')'TEST help SUBCOMMAND COMPLETE' contains subroutine wipe(filename) character(len=*),intent(in) :: filename integer :: ios integer :: lun character(len=k1) :: message open(file=filename,newunit=lun,iostat=ios,iomsg=message) if(ios.eq.0)then close(unit=lun,iostat=ios,status='delete',iomsg=message) if(ios.ne.0)then write(*,*)''//trim(message) endif else write(*,*)''//trim(message) endif end subroutine wipe subroutine slurp(filename,text) implicit none !$@(#) M_io::slurp(3f): allocate text array and read file filename into it character(*),intent(in) :: filename ! filename to shlep character(len=1),allocatable,intent(out) :: text(:) ! array to hold file integer :: nchars, igetunit, ios character(len=k1) :: message character(len=4096) :: local_filename ios=0 nchars=0 message='' open(newunit=igetunit, file=trim(filename), action="read", iomsg=message,& &form="unformatted", access="stream",status='old',iostat=ios) local_filename=filename if(ios.eq.0)then ! if file was successfully opened inquire(unit=igetunit, size=nchars) if(nchars.le.0)then call stderr_local( '*slurp* empty file '//trim(local_filename) ) return endif ! read file into text array if(allocated(text))deallocate(text) ! make sure text array not allocated allocate ( text(nchars) ) ! make enough storage to hold file read(igetunit,iostat=ios,iomsg=message) text ! load input file -> text array if(ios.ne.0)then call stderr_local( '*slurp* bad read of '//trim(local_filename)//':'//trim(message) ) endif else call stderr_local('*slurp* '//message) allocate ( text(0) ) ! make enough storage to hold file endif close(iostat=ios,unit=igetunit) ! close if opened successfully or not end subroutine slurp subroutine stderr_local(message) character(len=*) :: message write(*,'(a)')trim(message) ! write message to standard error end subroutine stderr_local subroutine swallow(FILENAME,pageout) implicit none character(len=*),intent(in) :: FILENAME ! file to read !intel-bug!character(len=:),allocatable,intent(out) :: pageout(:) ! page to hold file in memory character(len=k1),allocatable,intent(out) :: pageout(:) ! page to hold file in memory character(len=1),allocatable :: text(:) ! array to hold file in memory call slurp(FILENAME,text) ! allocate character array and copy file into it if(.not.allocated(text))then write(*,*)'*swallow* failed to load file '//FILENAME else ! convert array of characters to array of lines pageout=page(text) deallocate(text) ! release memory endif end subroutine swallow function page(array) result (table) !$@(#) M_strings::page(3fp): function to copy char array to page of text character(len=1),intent(in) :: array(:) !intel-bug!character(len=:),allocatable :: table(:) character(len=k1),allocatable :: table(:) integer :: i integer :: linelength integer :: length integer :: lines integer :: linecount integer :: position integer :: sz !!character(len=1),parameter :: nl=new_line('A') character(len=1),parameter :: nl=char(10) character(len=1),parameter :: cr=char(13) lines=0 linelength=0 length=0 sz=size(array) do i=1,sz if(array(i).eq.nl)then linelength=max(linelength,length) lines=lines+1 length=0 else length=length+1 endif enddo if(sz.gt.0)then if(array(sz).ne.nl)then lines=lines+1 endif endif if(allocated(table))deallocate(table) !intel-bug!allocate(character(len=linelength) :: table(lines)) allocate(character(len=k1) :: table(lines)) table=' ' linecount=1 position=1 do i=1,sz if(array(i).eq.nl)then linecount=linecount+1 position=1 elseif(array(i).eq.cr)then elseif(linelength.ne.0)then if(position.gt.len(table))then write(*,*)' adding character past edge of text',table(linecount),array(i) elseif(linecount.gt.size(table))then write(*,*)' adding line past end of text',linecount,size(table) else table(linecount)(position:position)=array(i) endif position=position+1 endif enddo end function page end program help_test