funit 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,33 @@
|
|
1
|
+
module Funit
|
2
|
+
|
3
|
+
##
|
4
|
+
# Fortran compiler
|
5
|
+
|
6
|
+
class Compiler
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
error_message = <<-COMPILER
|
10
|
+
Fortran compiler environment variable 'FC' not set.
|
11
|
+
|
12
|
+
For example, if you had the g95 compiler:
|
13
|
+
|
14
|
+
sh: export FC=g95
|
15
|
+
csh: setenv FC g95
|
16
|
+
windows: set FC=C:\\Program Files\\g95
|
17
|
+
COMPILER
|
18
|
+
raise error_message unless ENV['FC']
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
#--
|
26
|
+
# Copyright 2006-2007 United States Government as represented by
|
27
|
+
# NASA Langley Research Center. No copyright is claimed in
|
28
|
+
# the United States under Title 17, U.S. Code. All Other Rights
|
29
|
+
# Reserved.
|
30
|
+
#
|
31
|
+
# This file is governed by the NASA Open Source Agreement.
|
32
|
+
# See License.txt for details.
|
33
|
+
#++
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Funit
|
4
|
+
|
5
|
+
TEST_RUNNER = ERB.new %q{
|
6
|
+
! TestRunner.f90 - runs fUnit test suites
|
7
|
+
!
|
8
|
+
! <%= File.basename $0 %> generated this file on <%= Time.now %>.
|
9
|
+
|
10
|
+
program TestRunner
|
11
|
+
<%= test_suites.inject('') { |result,test_suite| result << "\n use #{test_suite}_fun" } %>
|
12
|
+
|
13
|
+
implicit none
|
14
|
+
|
15
|
+
integer :: numTests, numAsserts, numAssertsTested, numFailures
|
16
|
+
|
17
|
+
<% test_suites.each do |ts| %>
|
18
|
+
print *, ""
|
19
|
+
print *, "<%= ts %> test suite:"
|
20
|
+
call test_<%= ts %> &
|
21
|
+
( numTests, numAsserts, numAssertsTested, numFailures )
|
22
|
+
print *, "Passed", numAssertsTested, "of", numAsserts, &
|
23
|
+
"possible asserts comprising", &
|
24
|
+
numTests-numFailures, "of", numTests, "tests."
|
25
|
+
<% end %>
|
26
|
+
print *, ""
|
27
|
+
|
28
|
+
end program TestRunner
|
29
|
+
}.gsub(/^/,' '), nil, '<>' # turn off newlines due to <%%>
|
30
|
+
|
31
|
+
def requested_modules(module_names)
|
32
|
+
if (module_names.empty?)
|
33
|
+
module_names = Dir["*.fun"].each{ |mod| mod.chomp! ".fun" }
|
34
|
+
end
|
35
|
+
module_names
|
36
|
+
end
|
37
|
+
|
38
|
+
def funit_exists?(module_name)
|
39
|
+
File.exists? "#{module_name}.fun"
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_command_line
|
43
|
+
|
44
|
+
module_names = requested_modules(ARGV)
|
45
|
+
|
46
|
+
if module_names.empty?
|
47
|
+
raise " *Error: no test suites found in this directory"
|
48
|
+
end
|
49
|
+
|
50
|
+
module_names.each do |mod|
|
51
|
+
unless funit_exists?(mod)
|
52
|
+
error_message = <<-FUNITDOESNOTEXIST
|
53
|
+
Error: could not find test suite #{mod}.fun
|
54
|
+
Test suites available in this directory:
|
55
|
+
#{requested_modules([]).join(' ')}
|
56
|
+
|
57
|
+
Usage: #{File.basename $0} [test names (w/o .fun suffix)]
|
58
|
+
FUNITDOESNOTEXIST
|
59
|
+
raise error_message
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
def write_test_runner test_suites
|
66
|
+
File.open("TestRunner.f90", "w") do |file|
|
67
|
+
file.puts TEST_RUNNER.result(binding)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def syntax_error( message, test_suite )
|
72
|
+
raise "\n *Error: #{message} [#{test_suite}.fun:#$.]\n\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
def warning( message, test_suite )
|
76
|
+
$stderr.puts "\n *Warning: #{message} [#{test_suite}.fun:#$.]"
|
77
|
+
end
|
78
|
+
|
79
|
+
def compile_tests test_suites
|
80
|
+
puts "computing dependencies"
|
81
|
+
dependencies = Fortran::Dependencies.new
|
82
|
+
puts "locating associated source files and sorting for compilation"
|
83
|
+
required_sources = dependencies.required_source_files('TestRunner.f90')
|
84
|
+
|
85
|
+
puts compile = "#{ENV['FC']} #{ENV['FCFLAGS']} -o TestRunner \\\n #{required_sources.join(" \\\n ")}"
|
86
|
+
|
87
|
+
raise "Compile failed." unless system compile
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
#--
|
93
|
+
# Copyright 2006-2007 United States Government as represented by
|
94
|
+
# NASA Langley Research Center. No copyright is claimed in
|
95
|
+
# the United States under Title 17, U.S. Code. All Other Rights
|
96
|
+
# Reserved.
|
97
|
+
#
|
98
|
+
# This file is governed by the NASA Open Source Agreement.
|
99
|
+
# See License.txt for details.
|
100
|
+
#++
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'funit/assertions'
|
2
|
+
require 'funit/functions'
|
3
|
+
require 'ftools' # FIXME: use fileutils
|
4
|
+
|
5
|
+
module Funit
|
6
|
+
|
7
|
+
include Assertions # FIXME
|
8
|
+
|
9
|
+
##
|
10
|
+
# Create testsuite wrapper code
|
11
|
+
|
12
|
+
class TestSuite < File
|
13
|
+
|
14
|
+
ASSERTION_PATTERN = /Is(RealEqual|False|True|EqualWithin|Equal)\(.*\)/i
|
15
|
+
KEYWORDS = /(begin|end)(Setup|Teardown|Test)|Is(RealEqual|Equal|False|True|EqualWithin)\(.*\)/i
|
16
|
+
COMMENT_LINE = /^\s*!/
|
17
|
+
|
18
|
+
include Funit #FIXME
|
19
|
+
|
20
|
+
def initialize suite_name
|
21
|
+
@line_number = 'blank'
|
22
|
+
@suite_name = suite_name
|
23
|
+
return nil unless funit_exists?(suite_name)
|
24
|
+
File.delete(suite_name+"_fun.f90") if File.exists?(suite_name+"_fun.f90")
|
25
|
+
super(suite_name+"_fun.f90","w")
|
26
|
+
@tests, @setup, @teardown = [], [], []
|
27
|
+
top_wrapper
|
28
|
+
expand
|
29
|
+
close
|
30
|
+
end
|
31
|
+
|
32
|
+
def top_wrapper
|
33
|
+
puts <<-TOP
|
34
|
+
! #{@suite_name}_fun.f90 - a unit test suite for #{@suite_name}.f90
|
35
|
+
!
|
36
|
+
! #{File.basename $0} generated this file from #{@suite_name}.fun
|
37
|
+
! at #{Time.now}
|
38
|
+
|
39
|
+
module #{@suite_name}_fun
|
40
|
+
|
41
|
+
use #{@suite_name}
|
42
|
+
|
43
|
+
implicit none
|
44
|
+
|
45
|
+
logical :: noAssertFailed
|
46
|
+
|
47
|
+
public :: test_#@suite_name
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
integer :: numTests = 0
|
52
|
+
integer :: numAsserts = 0
|
53
|
+
integer :: numAssertsTested = 0
|
54
|
+
integer :: numFailures = 0
|
55
|
+
|
56
|
+
TOP
|
57
|
+
end
|
58
|
+
|
59
|
+
def expand
|
60
|
+
funit_file = @suite_name+".fun"
|
61
|
+
$stderr.print "expanding #{funit_file}..."
|
62
|
+
|
63
|
+
funit_contents = IO.readlines(funit_file)
|
64
|
+
@funit_total_lines = funit_contents.length
|
65
|
+
|
66
|
+
while (line = funit_contents.shift) && line !~ KEYWORDS
|
67
|
+
puts line
|
68
|
+
end
|
69
|
+
|
70
|
+
funit_contents.unshift line
|
71
|
+
|
72
|
+
puts " contains\n\n"
|
73
|
+
|
74
|
+
while (line = funit_contents.shift)
|
75
|
+
case line
|
76
|
+
when COMMENT_LINE
|
77
|
+
puts line
|
78
|
+
when /beginSetup/i
|
79
|
+
add_to_setup funit_contents
|
80
|
+
when /beginTeardown/i
|
81
|
+
add_to_teardown funit_contents
|
82
|
+
when /XbeginTest\s+(\w+)/i
|
83
|
+
ignore_test($1,funit_contents)
|
84
|
+
when /beginTest\s+(\w+)/i
|
85
|
+
a_test($1,funit_contents)
|
86
|
+
when /beginTest/i
|
87
|
+
syntax_error "no name given for beginTest", @suite_name
|
88
|
+
when /end(Setup|Teardown|Test)/i
|
89
|
+
syntax_error "no matching begin#$1 for an #$&", @suite_name
|
90
|
+
when ASSERTION_PATTERN
|
91
|
+
syntax_error "#$1 assert not in a test block", @suite_name
|
92
|
+
else
|
93
|
+
puts line
|
94
|
+
end
|
95
|
+
end
|
96
|
+
$stderr.puts "done."
|
97
|
+
end
|
98
|
+
|
99
|
+
def add_to_setup funit_contents
|
100
|
+
while (line = funit_contents.shift) && line !~ /endSetup/i
|
101
|
+
@setup.push line
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def add_to_teardown funit_contents
|
106
|
+
while (line = funit_contents.shift) && line !~ /endTeardown/i
|
107
|
+
@teardown.push line
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def ignore_test test_name, funit_contents
|
112
|
+
warning("Ignoring test: #{test_name}", @suite_name)
|
113
|
+
line = funit_contents.shift while line !~ /endTest/i
|
114
|
+
end
|
115
|
+
|
116
|
+
def a_test test_name, funit_contents
|
117
|
+
@test_name = test_name
|
118
|
+
@tests.push test_name
|
119
|
+
syntax_error("test name #@test_name not unique",@suite_name) if (@tests.uniq!)
|
120
|
+
|
121
|
+
puts " subroutine #{test_name}\n\n"
|
122
|
+
|
123
|
+
num_of_asserts = 0
|
124
|
+
|
125
|
+
while (line = funit_contents.shift) && line !~ /endTest/i
|
126
|
+
case line
|
127
|
+
when COMMENT_LINE
|
128
|
+
puts line
|
129
|
+
when /Is(RealEqual|False|True|EqualWithin|Equal)/i
|
130
|
+
@line_number = @funit_total_lines - funit_contents.length
|
131
|
+
num_of_asserts += 1
|
132
|
+
puts send( $&.downcase!, line )
|
133
|
+
else
|
134
|
+
puts line
|
135
|
+
end
|
136
|
+
end
|
137
|
+
warning("no asserts in test", @suite_name) if num_of_asserts == 0
|
138
|
+
|
139
|
+
puts "\n numTests = numTests + 1\n\n"
|
140
|
+
puts " end subroutine #{test_name}\n\n"
|
141
|
+
end
|
142
|
+
|
143
|
+
def close
|
144
|
+
puts "\n subroutine Setup"
|
145
|
+
puts @setup
|
146
|
+
puts " noAssertFailed = .true."
|
147
|
+
puts " end subroutine Setup\n\n"
|
148
|
+
|
149
|
+
puts "\n subroutine Teardown"
|
150
|
+
puts @teardown
|
151
|
+
puts " end subroutine Teardown\n\n"
|
152
|
+
|
153
|
+
puts <<-NEXTONE
|
154
|
+
|
155
|
+
subroutine test_#{@suite_name}( nTests, nAsserts, nAssertsTested, nFailures )
|
156
|
+
|
157
|
+
integer :: nTests
|
158
|
+
integer :: nAsserts
|
159
|
+
integer :: nAssertsTested
|
160
|
+
integer :: nFailures
|
161
|
+
|
162
|
+
continue
|
163
|
+
NEXTONE
|
164
|
+
|
165
|
+
@tests.each do |test_name|
|
166
|
+
puts "\n call Setup"
|
167
|
+
puts " call #{test_name}"
|
168
|
+
puts " call Teardown"
|
169
|
+
end
|
170
|
+
|
171
|
+
puts <<-LASTONE
|
172
|
+
|
173
|
+
nTests = numTests
|
174
|
+
nAsserts = numAsserts
|
175
|
+
nAssertsTested = numAssertsTested
|
176
|
+
nFailures = numFailures
|
177
|
+
|
178
|
+
end subroutine test_#{@suite_name}
|
179
|
+
|
180
|
+
end module #{@suite_name}_fun
|
181
|
+
LASTONE
|
182
|
+
super
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
#--
|
190
|
+
# Copyright 2006-2007 United States Government as represented by
|
191
|
+
# NASA Langley Research Center. No copyright is claimed in
|
192
|
+
# the United States under Title 17, U.S. Code. All Other Rights
|
193
|
+
# Reserved.
|
194
|
+
#
|
195
|
+
# This file is governed by the NASA Open Source Agreement.
|
196
|
+
# See License.txt for details.
|
197
|
+
#++
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'funit/compiler'
|
3
|
+
|
4
|
+
class TestCompiler < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_no_environment_compiler_name
|
7
|
+
begin
|
8
|
+
orig_FC = ENV['FC']
|
9
|
+
ENV['FC'] = nil
|
10
|
+
assert_raises(RuntimeError) {Funit::Compiler.new}
|
11
|
+
ensure
|
12
|
+
ENV['FC'] = orig_FC
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/test/test_funit.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'funit'
|
3
|
+
require 'ftools' # FIXME: migrate to fileutils
|
4
|
+
|
5
|
+
class TestFunit < Test::Unit::TestCase
|
6
|
+
|
7
|
+
include Funit # FIXME
|
8
|
+
include Funit::Assertions # FIXME
|
9
|
+
|
10
|
+
def setup
|
11
|
+
File.rm_f(*Dir["dummyunit*"])
|
12
|
+
File.rm_f(*Dir["unit*"])
|
13
|
+
File.rm_f(*Dir["ydsbe*"])
|
14
|
+
File.rm_f(*Dir["lmzd*"])
|
15
|
+
File.rm_f(*Dir["ldfdl*"])
|
16
|
+
File.rm_f(*Dir["ydsbe*"])
|
17
|
+
File.rm_f(*Dir["TestRunner*"])
|
18
|
+
File.rm_f(*Dir["a.out"])
|
19
|
+
end
|
20
|
+
|
21
|
+
def teardown
|
22
|
+
File.rm_f(*Dir["dummyunit*"])
|
23
|
+
File.rm_f(*Dir["unit*"])
|
24
|
+
File.rm_f(*Dir["ydsbe*"])
|
25
|
+
File.rm_f(*Dir["lmzd*"])
|
26
|
+
File.rm_f(*Dir["ldfdl*"])
|
27
|
+
File.rm_f(*Dir["ydsbe*"])
|
28
|
+
File.rm_f(*Dir["TestRunner*"])
|
29
|
+
File.rm_f(*Dir["a.out"])
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_main_driver_compiles
|
33
|
+
write_test_runner []
|
34
|
+
assert File.exists?("TestRunner.f90")
|
35
|
+
assert system("#{ENV['FC']} TestRunner.f90")
|
36
|
+
assert File.exists?("a.out")
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_is_equal
|
40
|
+
@suite_name = "dummy"
|
41
|
+
@test_name = "dummy"
|
42
|
+
@line_number = "dummy"
|
43
|
+
isequal("IsEqual(1.0,m(1,1))")
|
44
|
+
assert_equal '.not.(1.0==m(1,1))', @condition
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_is_real_equal
|
48
|
+
@suite_name = "dummy"
|
49
|
+
@test_name = "dummy"
|
50
|
+
@line_number = "dummy"
|
51
|
+
isrealequal("IsRealEqual(a,b)")
|
52
|
+
ans = <<-EOF
|
53
|
+
.not.(a+2*spacing(real(a)).ge.b &
|
54
|
+
.and.a-2*spacing(real(a)).le.b)
|
55
|
+
EOF
|
56
|
+
assert_equal ans.chomp, @condition
|
57
|
+
assert_equal '"b (",b,") is not",a,"within",2*spacing(real(a))', @message
|
58
|
+
isrealequal("IsRealEqual(1.0,m(1,1))")
|
59
|
+
ans = <<-EOF
|
60
|
+
.not.(1.0+2*spacing(real(1.0)).ge.m(1,1) &
|
61
|
+
.and.1.0-2*spacing(real(1.0)).le.m(1,1))
|
62
|
+
EOF
|
63
|
+
assert_equal ans.chomp, @condition
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_handles_dependency
|
67
|
+
File.open('unit.f90','w') do |f|
|
68
|
+
f.printf "module unit\n use unita, only : a\nend module unit\n"
|
69
|
+
end
|
70
|
+
File.open('unita.f90','w') do |f|
|
71
|
+
f.printf "module unita\n integer :: a = 5\nend module unita\n"
|
72
|
+
end
|
73
|
+
File.open('unit.fun','w') do |f|
|
74
|
+
f.printf "beginTest a_gets_set\n IsEqual(5, a)\nendTest\n"
|
75
|
+
end
|
76
|
+
assert_nothing_raised{run_tests}
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_embedded_dependencies
|
80
|
+
File.open('unit.f90','w') do |f|
|
81
|
+
f.printf "module unit\n use unita, only : a\nend module unit\n"
|
82
|
+
end
|
83
|
+
File.open('unita.f90','w') do |f|
|
84
|
+
f.printf "module unita\n use unitb, only : b \n integer :: a = b\nend module unita\n"
|
85
|
+
end
|
86
|
+
File.open('unitb.f90','w') do |f|
|
87
|
+
f.printf "module unitb\n integer,parameter :: b = 5\nend module unitb\n"
|
88
|
+
end
|
89
|
+
File.open('unit.fun','w') do |f|
|
90
|
+
f.printf "beginTest a_gets_set\n IsEqual(5, a)\nendTest\n"
|
91
|
+
end
|
92
|
+
assert_nothing_raised{run_tests}
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_requested_modules
|
96
|
+
assert_equal ["asdfga"], requested_modules(["asdfga"])
|
97
|
+
assert_equal ["asd","fga"], requested_modules(["asd","fga"])
|
98
|
+
assert requested_modules([]).empty?
|
99
|
+
modules = %w[ldfdl lmzd]
|
100
|
+
funits = modules.map{|f| f+'.fun'}.join(' ')
|
101
|
+
system "touch "+funits
|
102
|
+
assert_equal modules, requested_modules([])
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_funit_exists_method
|
106
|
+
module_name = "ydsbe"
|
107
|
+
File.rm_f(module_name+".fun")
|
108
|
+
assert_equal false, funit_exists?(module_name)
|
109
|
+
system "touch "+module_name+".fun"
|
110
|
+
assert funit_exists?(module_name)
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|