You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 11 Current »

1. Example project structure

.
├── fruit
│   └── fruit.f90
├── mod
├── src
│   └── main
│       └── <your_module>.f90
└── test 
    ├── <your_module>_test.f90
    └── fruit_driver.f90
  • fruit.f90 - fruit source code.
  • mod - directory used to store .mod files.
  • <your_module>.f90 - your actual module.
  • <your_module>_test.f90 - file with unit tests for your module.
  • fruit_driver.f90 - file with test driving program. You can change this file name, but remember to modify Makefile, so it could find it.

2.  Install requirements

2.1. Load gfortran

module load itm-gcc/7.3.0

2.2. Setup fruit library

Download fruit.f90 https://github.com/mortele/FRUIT/blob/master/src/fruit.f90 into  .<project_dir>/fruit directory.

cd <project_dir>/fruit
wget https://raw.githubusercontent.com/mortele/FRUIT/master/src/fruit.f90

NOTE: While compiling fruit.f90 an error "Logicals must be compared with .eqv. instead of .eq." may occure. In this case you will need to modify fruit.f90 lines 912 and 913 editing ".eq." to ".eqv.".

3. Create project files

3.1. Create file with unit tests

Create file named <your_module>_test.f90 and put it into ./test directory.

This is you file that contains unit tests for your program.

!file: <project_dir>/test/calculator_test.f90  

module calculator_test
use fruit

contains
subroutine test_calculator
	!use <your_module_name>
	use calculator
	integer :: result

	!test add subroutine
	call add (2,2,result)
	call assert_equals(4,result)

end subroutine test_calculator
end module calculator_test

3.2. Create unit test driver

Create file named fruit_driver.f90. It will call your tests and handle fruit init and summary. You will need to modify it everytime you add new tests.

!file: <project_dir>/test/fruit_driver.f90

program fruit_driver
	use fruit
	use calculator_test
	call init_fruit

	!if you want to generate XML result
	!call init_fruit_xml

	call test_calculator

	!call fruit_summary_xml
	call fruit_summary
end program fruit_driver

3.3. Create your module

Create file named <your_module>.f90 and put it into ./src directory. This file will be tested by module created in step 3.1.

!file: <project_dir>/src/calculator.f90

module calculator
implicit none
contains
	subroutine add(a,b,output)
		integer, intent (in) :: a,b
		integer, intent (out) :: output
		output=a+b
	end subroutine add

end module calculator

3.4.  Create your main program

Create file named main.f90 and put it into ./src directory. We won't use this file, but it is needed to compile and build calculator executable.

!file: <project_dir>/src/main.f90
program main
use calculator
        integer :: result
        call add(2,2,result)
end program main

4. Finalization

4.1. Create mod directory

This directory will contains built modules. Unlikely GNU make will not create it itself.

cd <project_dir>
mkdir mod

4.2. Create Makefile

It is a build instruction that makes compiling your project easy. 

To use this code without modification you will need to follow convention:

  • Put your tests in /test directory and name them *_test.f90
  • Name your main program main.f90
  • Name your test driver "fruit_driver.f90"

Otherwise you will need to modify some of it's lines.

#file: <project_dir>/Makefile
C=gfortran -O3
FFLAGS=-c -J ./mod

OBJS=$(patsubst %.f90,%.o,$(wildcard src/*.f90))

TESTED_OBJS=$(filter-out src/main.o, $(OBJS))

TESTS=$(patsubst %.f90,%.o,$(wildcard test/*test.f90))

LIB=$(patsubst %.f90,%.o,$(wildcard fruit/*.f90))

DRIVER_SOURCE=test/fruit_driver.f90
DRIVER_OBJ=$(patsubst %.f90,%.o, $(DRIVER_SOURCE))

all: run

run: $(OBJS)
        @echo "Building executable"
        $(FC) $(OBJS) -o $@

$(OBJS): src/%.o: src/%.f90
        @echo "Compiling objects"
        $(FC) $(FFLAGS) $< -o $@



test: fruit/fruit.o $(TESTED_OBJS) $(TESTS) $(DRIVER_OBJ)
        @echo "Building executable"
        $(FC) $^ -o run_test

$(TESTS): test/%.o: test/%.f90
        @echo "Compiling tests"
        $(FC) $(FFLAGS) $< -o $@

$(DRIVER_OBJ): $(DRIVER_SOURCE)
        @echo "Compiling driver"
        $(FC) $(FFLAGS) $< -o $@

fruit/fruit.o: fruit/fruit.f90
        @echo "Compiling FRUIT library"
        $(FC) $(FFLAGS) $< -o $@


clean:
        rm mod/*.mod -f
        rm src/*.o -f
        rm test/*.o -f
        rm fruit/*.o -f
        rm run -f

4.3. Run tests

cd <project_dir>

#to compile and run tests
make test
./run_test

#to clean after build
make clean

#to compile and run you program without tests
make
./run


5. Using setup and teardown subroutines

NOTE: This step is optional and is needed to be done only if you want to use setup and teardown subroutines.

NOTE: Using setup and teardown subroutines with this library is complex and unellegant. Consider using another framework or FRUIT+Ruby version.

5.1.  Add setup and teardown subroutines

First you need to define setup and teardown subroutines.

!file: <project_dir>/test/calculator_test.f90

module calculator_test
use fruit

contains
subroutine setup
	!...
end  subroutine setup
	
subroutine teardown
	!...
end  subroutine teardown

subroutine test_calculator
	use calculator
	integer :: result

	!test add subroutine
	call add (2,2,result)
	call assert_equals(4,result)

end subroutine test_calculator
end module calculator_test

5.2. Create basket file

Basket file handles setup and teardown subroutines calls and also captures responsibility of running tests from test_driver module. It should be created in ./test directory. 

!file: <project_dir>/test/fruit_basket.f90

module fruit_basket
  use fruit
contains
  subroutine calculator_test_all_tests
    use calculator_test

    call setup
    write (*, *) "  ..running test: test_calculator"
    call set_unit_name('test_calculator')
    call run_test_case (test_calculator, "test_calculator")
    if (.not. is_case_passed()) then
      write(*,*) 
      write(*,*) '  Un-satisfied spec:'
      write(*,*) '  -- calculator'
      write(*,*) 
      call case_failed_xml("test_calculator", &
      & "calculator_test")
    else
      call case_passed_xml("test_calculator", &
      & "calculator_test")
    end if
    call teardown

  end subroutine calculator_test_all_tests

  subroutine fruit_basket
    call calculator_test_all_tests
  end subroutine fruit_basket

end module fruit_basket


Pay attention to code lines presented below. They come from code above. You can use them to call tests from desired test module.

call set_unit_name('test_calculator')
call run_test_case (test_calculator, "test_calculator")

5.3. Modify test driver

Now test subroutines calls are moved from test_driver to test_basket. You need to import test_basket module and call it's main subroutine. In this case it is "fruit_basket" subroutine.

!file: <project_dir>/test/fruit_driver.f90

program fruit_driver
	use fruit
	use fruit_basket
	call init_fruit

	call fruit_basket

	call fruit_summary
end program fruit_driver

5.4. Modify Makefile

Add lines presented below at the end of the Makefile.

test/fruit_basket.o: test/fruit_basket.f90
	@echo "Compiling FRUIT basket file"
    $(FC) $(FFLAGS) $< -o $@


Also you need to modify this line:

test: fruit/fruit.o $(TESTED_OBJS) $(TESTS) $(DRIVER_OBJ)

as follows:

test: fruit/fruit.o $(TESTED_OBJS) $(TESTS) test/fruit_basket.o $(DRIVER_OBJ)

6. Example output

6.1.  Output created with project described above

6.2. Output created with different project

NOTE: FRUIT prints "." for every assert passed and "F" for every assert failed, not for every procedure call.

NOTE: Screenshoot below contains list of failed assertions and messages defined by user as optional assert subroutine argument.



  • No labels