funit 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +10 -0
- data/License.txt +263 -0
- data/Manifest.txt +25 -0
- data/README.txt +124 -0
- data/Rakefile +21 -0
- data/bin/funit +22 -0
- data/examples/CFD/FluxFunctions.f90 +34 -0
- data/examples/CFD/FluxFunctions.fun +47 -0
- data/examples/CFD/Gammas.f90 +7 -0
- data/examples/CFD/GasModel.f90 +16 -0
- data/examples/CFD/GasModel.fun +20 -0
- data/examples/ReadData/time_series_data.f90 +27 -0
- data/examples/ReadData/time_series_data.fun +28 -0
- data/examples/StopWatch/StopWatch.f90 +50 -0
- data/examples/StopWatch/StopWatch.fun +73 -0
- data/lib/funit.rb +35 -0
- data/lib/funit/assertions.rb +114 -0
- data/lib/funit/compiler.rb +33 -0
- data/lib/funit/functions.rb +100 -0
- data/lib/funit/testsuite.rb +197 -0
- data/test/test_compiler.rb +16 -0
- data/test/test_functions.rb +10 -0
- data/test/test_funit.rb +113 -0
- data/test/test_testsuite.rb +106 -0
- data/utils/funit-mode.el +111 -0
- metadata +98 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module FluxFunctions
|
2
|
+
|
3
|
+
implicit none
|
4
|
+
|
5
|
+
contains
|
6
|
+
|
7
|
+
subroutine CentralFlux( LeftState, RightState, InterfaceFlux )
|
8
|
+
real, intent(in) :: leftState
|
9
|
+
real, intent(in) :: rightState
|
10
|
+
real, intent(out) :: interfaceFlux
|
11
|
+
interfaceFlux = 0.5*(Flux(leftState)+Flux(rightState))
|
12
|
+
end subroutine CentralFlux
|
13
|
+
|
14
|
+
subroutine RoeFlux( LeftState, RightState, InterfaceFlux )
|
15
|
+
real, intent(in) :: leftState
|
16
|
+
real, intent(in) :: rightState
|
17
|
+
real, intent(out) :: interfaceFlux
|
18
|
+
interfaceFlux = 0.5*(Flux(leftState)+Flux(rightState)) &
|
19
|
+
- 0.5*RoeAvg(leftState,rightState)*(rightState-leftState)
|
20
|
+
end subroutine RoeFlux
|
21
|
+
|
22
|
+
function Flux( state )
|
23
|
+
real :: Flux
|
24
|
+
real, intent(in) :: state
|
25
|
+
Flux = 0.5*state**2
|
26
|
+
end function Flux
|
27
|
+
|
28
|
+
function RoeAvg( leftState, rightState )
|
29
|
+
real :: RoeAvg
|
30
|
+
real, intent(in) :: leftState, rightState
|
31
|
+
RoeAvg = 0.5*(leftState+rightState)
|
32
|
+
end function RoeAvg
|
33
|
+
|
34
|
+
end module FluxFunctions
|
@@ -0,0 +1,47 @@
|
|
1
|
+
real :: leftState, rightState, interfaceFlux
|
2
|
+
|
3
|
+
beginsetup
|
4
|
+
leftState = 0
|
5
|
+
rightState = 1
|
6
|
+
endsetup
|
7
|
+
|
8
|
+
beginTest FluxZero
|
9
|
+
real :: state
|
10
|
+
state = 0
|
11
|
+
IsEqualWithin( 0, Flux(state), 0.00001 )
|
12
|
+
endTest
|
13
|
+
|
14
|
+
beginTest FluxOne
|
15
|
+
real :: state = 1
|
16
|
+
IsEqualWithin( 0.5, Flux(state), 0.00001 )
|
17
|
+
endTest
|
18
|
+
|
19
|
+
beginTest RoeAvgZero
|
20
|
+
IsRealEqual( 0, RoeAvg(0.0,0.0) )
|
21
|
+
IsFalse( RoeAvg(0.0,0.0)==1 )
|
22
|
+
endTest
|
23
|
+
|
24
|
+
beginTest RoeAvgKnown
|
25
|
+
IsRealEqual( 0.5, RoeAvg(leftState,rightState) )
|
26
|
+
IsTrue( RoeAvg(leftState,rightState) > 0 )
|
27
|
+
endTest
|
28
|
+
|
29
|
+
beginTest CentralFluxKnown
|
30
|
+
call CentralFlux( leftState, rightState, interfaceFlux )
|
31
|
+
IsEqualWithin( 0.25, interfaceFlux, 0.001 )
|
32
|
+
IsEqualWithin( 0.25, interfaceFlux, 0.00000001 )
|
33
|
+
IsEqual( 0.25, interfaceFlux )
|
34
|
+
endTest
|
35
|
+
|
36
|
+
beginTest RoeFluxExpansionShock
|
37
|
+
leftState = -1
|
38
|
+
call RoeFlux( leftState, rightState, interfaceFlux )
|
39
|
+
IsEqual( 0.5, interfaceFlux )
|
40
|
+
endTest
|
41
|
+
|
42
|
+
beginTest RoeFluxZero
|
43
|
+
rightState = 0
|
44
|
+
call RoeFlux( leftState, rightState, interfaceFlux )
|
45
|
+
IsRealEqual( 0, interfaceFlux )
|
46
|
+
IsEqual( 0, interfaceFlux )
|
47
|
+
endTest
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module GasModel
|
2
|
+
|
3
|
+
use Gammas
|
4
|
+
|
5
|
+
implicit none
|
6
|
+
|
7
|
+
contains
|
8
|
+
|
9
|
+
subroutine PerfectP (Density, Energy, Pressure)
|
10
|
+
real, intent(in) :: Density
|
11
|
+
real, intent(in) :: Energy
|
12
|
+
real, intent(out) :: Pressure
|
13
|
+
Pressure = Density * Energy * ( Gamma - 1.0 )
|
14
|
+
end subroutine PerfectP
|
15
|
+
|
16
|
+
end module GasModel
|
@@ -0,0 +1,20 @@
|
|
1
|
+
real :: Pressure, Density, Energy
|
2
|
+
|
3
|
+
beginTest PerfectPZeroed
|
4
|
+
real, parameter :: zero = 0
|
5
|
+
call PerfectP (zero, zero, Pressure)
|
6
|
+
isRealEqual ( 0, Pressure )
|
7
|
+
IsEqualwithin ( 0, Pressure, 0.0000000001 )
|
8
|
+
endTest
|
9
|
+
|
10
|
+
begintest Warbler
|
11
|
+
endtest
|
12
|
+
|
13
|
+
beginTest PerfectPKnown
|
14
|
+
real :: Density = 1
|
15
|
+
Energy = 1
|
16
|
+
call PerfectP( Density, Energy, Pressure )
|
17
|
+
IsRealEqual( 0.4, Pressure )
|
18
|
+
IsTrue ( Pressure > 0 )
|
19
|
+
IsFalse( Pressure < 0 )
|
20
|
+
endTest
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module time_series_data
|
2
|
+
implicit none
|
3
|
+
integer, parameter :: MAX_POINTS = 100000 ! or use allocate
|
4
|
+
type date_time
|
5
|
+
integer :: year, month, day, hour, minute
|
6
|
+
end type date_time
|
7
|
+
type time_series
|
8
|
+
type(date_time) :: date_time
|
9
|
+
real :: value = 0.0
|
10
|
+
end type time_series
|
11
|
+
type(time_series), dimension(MAX_POINTS), save :: ts_data
|
12
|
+
contains
|
13
|
+
subroutine read_time_series( filename )
|
14
|
+
character(len=*), intent(in) :: filename
|
15
|
+
integer :: i, ios
|
16
|
+
open(10, file=filename, iostat=ios)
|
17
|
+
if (ios/=0) then
|
18
|
+
print *, 'failed to open file: >>', filename, '<<'
|
19
|
+
else
|
20
|
+
do i = 1, MAX_POINTS
|
21
|
+
read( 10, fmt='(i4,i2,i2,i2,i2,e20.12)', end=20 ) ts_data(i)
|
22
|
+
end do
|
23
|
+
print *, 'quit reading data after ', MAX_POINTS, ' points'
|
24
|
+
20 continue
|
25
|
+
end if
|
26
|
+
end subroutine read_time_series
|
27
|
+
end module time_series_data
|
@@ -0,0 +1,28 @@
|
|
1
|
+
character(len=*), parameter :: FILE = 'values.txt'
|
2
|
+
|
3
|
+
beginSetup
|
4
|
+
open(8, file=FILE)
|
5
|
+
write(8,'(a)'), '200609300000 0.223200546265E+003'
|
6
|
+
write(8,'(a)'), '200609300132 0.226001495361E+003'
|
7
|
+
close(8)
|
8
|
+
endSetup
|
9
|
+
|
10
|
+
beginTest load_time_series_data_from_file
|
11
|
+
call read_time_series( FILE )
|
12
|
+
IsEqual( 2006, ts_data(1)%date_time%year )
|
13
|
+
IsEqual( 9, ts_data(1)%date_time%month )
|
14
|
+
IsEqual( 30, ts_data(1)%date_time%day )
|
15
|
+
IsEqual( 0, ts_data(1)%date_time%hour )
|
16
|
+
IsEqual( 0, ts_data(1)%date_time%minute )
|
17
|
+
IsEqualWithin( 223.2, ts_data(1)%value, 0.1 )
|
18
|
+
IsEqual( 2006, ts_data(2)%date_time%year )
|
19
|
+
IsEqual( 9, ts_data(2)%date_time%month )
|
20
|
+
IsEqual( 30, ts_data(2)%date_time%day )
|
21
|
+
IsEqual( 1, ts_data(2)%date_time%hour )
|
22
|
+
IsEqual( 32, ts_data(2)%date_time%minute )
|
23
|
+
IsEqualWithin( 226.0, ts_data(2)%value, 0.1 )
|
24
|
+
endTest
|
25
|
+
|
26
|
+
beginTeardown
|
27
|
+
call system('rm '//FILE)
|
28
|
+
endTeardown
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module StopWatch
|
2
|
+
|
3
|
+
implicit none
|
4
|
+
|
5
|
+
public
|
6
|
+
|
7
|
+
logical :: notInitialized = .TRUE.
|
8
|
+
|
9
|
+
integer, dimension(8) :: last
|
10
|
+
|
11
|
+
contains
|
12
|
+
|
13
|
+
real function SecBetween(beginDnT, endDnT)
|
14
|
+
integer, dimension(8), intent(in) :: beginDnT, endDnT
|
15
|
+
real :: days, hours, minutes, seconds
|
16
|
+
integer, parameter :: yr=1, mo=2, day=3, utc=4, hr=5, mn=6, s=7, ms=8
|
17
|
+
|
18
|
+
continue
|
19
|
+
|
20
|
+
if ( endDnT(day) == beginDnT(day) ) then
|
21
|
+
days = 0
|
22
|
+
else
|
23
|
+
days = 1 ! note: assuming one day
|
24
|
+
endif
|
25
|
+
|
26
|
+
hours = endDnT(hr) - beginDnT(hr) + 24*days
|
27
|
+
minutes = endDnT(mn) - beginDnT(mn) + 60*hours
|
28
|
+
seconds = endDnT(s) - beginDnT(s) + 60*minutes
|
29
|
+
|
30
|
+
SecBetween = seconds + ( endDnT(ms) - beginDnT(ms) ) / 1000.
|
31
|
+
|
32
|
+
end function SecBetween
|
33
|
+
|
34
|
+
real function secSinceLast()
|
35
|
+
|
36
|
+
integer, dimension(8) :: now
|
37
|
+
|
38
|
+
if (notInitialized) then
|
39
|
+
notInitialized = .FALSE.
|
40
|
+
secSinceLast = 0.0
|
41
|
+
call date_and_time(values=last)
|
42
|
+
else
|
43
|
+
call date_and_time(values=now)
|
44
|
+
secSinceLast = secBetween(last, now)
|
45
|
+
last = now
|
46
|
+
endif
|
47
|
+
|
48
|
+
end function secSinceLast
|
49
|
+
|
50
|
+
end module StopWatch
|
@@ -0,0 +1,73 @@
|
|
1
|
+
integer, dimension(8) :: dateAndTime1, dateAndTime2
|
2
|
+
real :: seconds
|
3
|
+
|
4
|
+
beginSetup
|
5
|
+
NotInitialized = .TRUE.
|
6
|
+
last = 0
|
7
|
+
seconds = HUGE(0.0)
|
8
|
+
endSetup
|
9
|
+
|
10
|
+
beginTest SystemDateAndTimeWorks
|
11
|
+
call date_and_time(values=dateAndTime1)
|
12
|
+
IsTrue( dateAndTime1(1) /= -huge(0) )
|
13
|
+
IsTrue( size(dateAndTime1,1) == 8 )
|
14
|
+
endTest
|
15
|
+
|
16
|
+
! test secBetween
|
17
|
+
beginTest OneMSecDifference
|
18
|
+
dateAndTime1 = (/ 2000, 1, 1, 0, 0, 0, 0, 0 /)
|
19
|
+
dateAndTime2 = (/ 2000, 1, 1, 0, 0, 0, 0, 1 /)
|
20
|
+
seconds = SecBetween(dateAndTime1, dateAndTime2)
|
21
|
+
IsRealEqual( 0.001, seconds)
|
22
|
+
endTest
|
23
|
+
|
24
|
+
beginTest MinuteRollover
|
25
|
+
dateAndTime1 = (/ 2000, 1, 1, 0, 0, 0,59, 0 /)
|
26
|
+
dateAndTime2 = (/ 2000, 1, 1, 0, 0, 1, 0, 0 /)
|
27
|
+
seconds = SecBetween(dateAndTime1, dateAndTime2)
|
28
|
+
IsRealEqual( 1.0, seconds )
|
29
|
+
endTest
|
30
|
+
|
31
|
+
! test secSinceLast
|
32
|
+
beginTest InitializationState
|
33
|
+
IsTrue(notInitialized)
|
34
|
+
seconds = secSinceLast()
|
35
|
+
IsFalse(notInitialized)
|
36
|
+
seconds = secSinceLast()
|
37
|
+
IsFalse(notInitialized)
|
38
|
+
endTest
|
39
|
+
|
40
|
+
beginTest InitiallyReturnsZero
|
41
|
+
seconds = secSinceLast()
|
42
|
+
IsRealEqual( 0.0, seconds )
|
43
|
+
call timeDelay(seconds)
|
44
|
+
seconds = secSinceLast()
|
45
|
+
IsTrue( seconds /= 0.0 )
|
46
|
+
endTest
|
47
|
+
|
48
|
+
subroutine timeDelay (sum)
|
49
|
+
integer :: i
|
50
|
+
real :: sum
|
51
|
+
do i = 1, 1000000
|
52
|
+
sum = sum + i
|
53
|
+
enddo
|
54
|
+
end subroutine timeDelay
|
55
|
+
|
56
|
+
beginTest ComputesSeconds
|
57
|
+
seconds = secSinceLast()
|
58
|
+
call timeDelay (seconds)
|
59
|
+
seconds = secSinceLast()
|
60
|
+
IsTrue( seconds > 0.0 )
|
61
|
+
endTest
|
62
|
+
|
63
|
+
beginTest ComputesSecondsSpecial
|
64
|
+
real :: expectedSeconds
|
65
|
+
|
66
|
+
seconds = secSinceLast()
|
67
|
+
dateAndTime1 = last
|
68
|
+
call timeDelay (seconds)
|
69
|
+
seconds = secSinceLast()
|
70
|
+
dateAndTime2 = last
|
71
|
+
expectedSeconds = secBetween(dateAndTime1,dateAndTime2)
|
72
|
+
IsRealEqual( expectedSeconds, seconds )
|
73
|
+
endTest
|
data/lib/funit.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
require 'funit/compiler'
|
3
|
+
require 'funit/functions'
|
4
|
+
require 'funit/assertions'
|
5
|
+
require 'funit/testsuite'
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'fortran'
|
9
|
+
|
10
|
+
module Funit
|
11
|
+
|
12
|
+
VERSION = '0.9.0'
|
13
|
+
|
14
|
+
##
|
15
|
+
# run all tests
|
16
|
+
|
17
|
+
def run_tests
|
18
|
+
Compiler.new# a test for compiler env set (FIXME: remove this later)
|
19
|
+
write_test_runner( test_suites = parse_command_line )
|
20
|
+
test_suites.each{ |test_suite| TestSuite.new test_suite }
|
21
|
+
compile_tests test_suites
|
22
|
+
raise "TestRunner failed to execute." unless system './TestRunner'
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
#--
|
28
|
+
# Copyright 2006-2007 United States Government as represented by
|
29
|
+
# NASA Langley Research Center. No copyright is claimed in
|
30
|
+
# the United States under Title 17, U.S. Code. All Other Rights
|
31
|
+
# Reserved.
|
32
|
+
#
|
33
|
+
# This file is governed by the NASA Open Source Agreement.
|
34
|
+
# See License.txt for details.
|
35
|
+
#++
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
module Funit
|
4
|
+
|
5
|
+
##
|
6
|
+
# Fortran assertion macro definitions
|
7
|
+
|
8
|
+
module Assertions
|
9
|
+
|
10
|
+
def istrue(line)
|
11
|
+
line.match(/\((.+)\)/)
|
12
|
+
@type = 'IsTrue'
|
13
|
+
@condition = ".not.(#$1)"
|
14
|
+
@message = "\"#$1 is not true\""
|
15
|
+
syntax_error("invalid body for #@type",@suite_name) unless $1=~/\S+/
|
16
|
+
write_assert
|
17
|
+
end
|
18
|
+
|
19
|
+
def isfalse(line)
|
20
|
+
line.match(/\((.+)\)/)
|
21
|
+
@type = 'IsFalse'
|
22
|
+
@condition = "#$1"
|
23
|
+
@message = "\"#$1 is not false\""
|
24
|
+
syntax_error("invalid body for #@type",@suite_name) unless $1=~/\S+/
|
25
|
+
write_assert
|
26
|
+
end
|
27
|
+
|
28
|
+
def isrealequal(line)
|
29
|
+
line.match(/\((.*)\)/)
|
30
|
+
expected, actual = *(get_args($1))
|
31
|
+
@type = 'IsRealEqual'
|
32
|
+
@condition = ".not.(#{expected}+2*spacing(real(#{expected})).ge.#{actual} &\n .and.#{expected}-2*spacing(real(#{expected})).le.#{actual})"
|
33
|
+
@message = "\"#{actual} (\",#{actual},\") is not\",#{expected},\"within\",2*spacing(real(#{expected}))"
|
34
|
+
syntax_error("invalid body for #@type",@suite_name) unless $&
|
35
|
+
write_assert
|
36
|
+
end
|
37
|
+
|
38
|
+
def isequalwithin(line)
|
39
|
+
line.match(/\((.*)\)/)
|
40
|
+
expected, actual, tolerance = *(get_args($1))
|
41
|
+
@type = 'IsEqualWithin'
|
42
|
+
@condition = ".not.(#{actual}+#{tolerance}.ge.#{expected} &\n .and.#{actual}-#{tolerance}.le.#{expected})"
|
43
|
+
@message = "\"#{expected} (\",#{expected},\") is not\",#{actual},\"within\",#{tolerance}"
|
44
|
+
syntax_error("invalid body for #@type",@suite_name) unless $&
|
45
|
+
write_assert
|
46
|
+
end
|
47
|
+
|
48
|
+
def isequal(line)
|
49
|
+
line.match(/\((\w+\(.*\)|[^,]+),(.+)\)/)
|
50
|
+
@type = 'IsEqual'
|
51
|
+
@condition = ".not.(#$1==#$2)"
|
52
|
+
@message = "\"#$1 (\",#$1,\") is not\", #$2"
|
53
|
+
syntax_error("invalid body for #@type",@suite_name) unless $&
|
54
|
+
write_assert
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# An argument scanner thanks to James Edward Gray II
|
59
|
+
# by way of ruby-talk mailing list.
|
60
|
+
|
61
|
+
def get_args(string)
|
62
|
+
scanner = ::StringScanner.new(string)
|
63
|
+
result = scanner.eos? ? [] : ['']
|
64
|
+
paren_depth = 0
|
65
|
+
until scanner.eos?
|
66
|
+
if scanner.scan(/[^(),]+/)
|
67
|
+
# do nothing--we found the part of the argument we need to add
|
68
|
+
elsif scanner.scan(/\(/)
|
69
|
+
paren_depth += 1
|
70
|
+
elsif scanner.scan(/\)/)
|
71
|
+
paren_depth -= 1
|
72
|
+
elsif scanner.scan(/,\s*/) and paren_depth.zero?
|
73
|
+
result << ''
|
74
|
+
next
|
75
|
+
end
|
76
|
+
result.last << scanner.matched
|
77
|
+
end
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Translate the assertion to Fortran.
|
83
|
+
|
84
|
+
def write_assert
|
85
|
+
<<-OUTPUT
|
86
|
+
! #@type assertion
|
87
|
+
numAsserts = numAsserts + 1
|
88
|
+
if (noAssertFailed) then
|
89
|
+
if (#@condition) then
|
90
|
+
print *, " *#@type failed* in test #@test_name &
|
91
|
+
&[#{@suite_name}.fun:#{@line_number.to_s}]"
|
92
|
+
print *, " ", #@message
|
93
|
+
print *, ""
|
94
|
+
noAssertFailed = .false.
|
95
|
+
numFailures = numFailures + 1
|
96
|
+
else
|
97
|
+
numAssertsTested = numAssertsTested + 1
|
98
|
+
endif
|
99
|
+
endif
|
100
|
+
OUTPUT
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
#--
|
107
|
+
# Copyright 2006-2007 United States Government as represented by
|
108
|
+
# NASA Langley Research Center. No copyright is claimed in
|
109
|
+
# the United States under Title 17, U.S. Code. All Other Rights
|
110
|
+
# Reserved.
|
111
|
+
#
|
112
|
+
# This file is governed by the NASA Open Source Agreement.
|
113
|
+
# See License.txt for details.
|
114
|
+
#++
|