funit-12 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,125 @@
1
+
2
+ module Funit
3
+
4
+ ##
5
+ # Fortran assertion macro definitions
6
+
7
+ module Assertions
8
+
9
+ ASSERTION_PATTERN =
10
+ /^\s*?(assert_(array_equal|real_equal|false|true|equal_within|equal))\(.*\)/i
11
+
12
+ def assert_true(line)
13
+ line.match(/\((.+)\)/)
14
+ @type = 'Assert_True'
15
+ @condition = ".not.(#$1)"
16
+ @message = "\"#$1 is not true\""
17
+ syntax_error("invalid body for #@type",@suite_name) unless $1=~/\S+/
18
+ write_assert
19
+ end
20
+
21
+ def assert_false(line)
22
+ line.match(/\((.+)\)/)
23
+ @type = 'Assert_False'
24
+ @condition = "#$1"
25
+ @message = "\"#$1 is not false\""
26
+ syntax_error("invalid body for #@type",@suite_name) unless $1=~/\S+/
27
+ write_assert
28
+ end
29
+
30
+ def assert_real_equal(line)
31
+ line.match(/\((.*)\)/)
32
+ expected, actual = *(get_args($1))
33
+ @type = 'Assert_Real_Equal'
34
+ @condition = ".not.( (#{expected} &\n +2*spacing(real(#{expected})) ) &\n .ge. &\n (#{actual}) &\n .and. &\n (#{expected} &\n -2*spacing(real(#{expected})) ) &\n .le. &\n (#{actual}) )"
35
+ @message = "\"#{actual} (\", &\n #{actual}, &\n \") is not\", &\n #{expected},\&\n \"within\", &\n 2*spacing(real(#{expected}))"
36
+ syntax_error("invalid body for #@type",@suite_name) unless $&
37
+ write_assert
38
+ end
39
+
40
+ def assert_equal_within(line)
41
+ line.match(/\((.*)\)/)
42
+ expected, actual, tolerance = *(get_args($1))
43
+ @type = 'Assert_Equal_Within'
44
+ @condition = ".not.((#{actual} &\n +#{tolerance}) &\n .ge. &\n (#{expected}) &\n .and. &\n (#{actual} &\n -#{tolerance}) &\n .le. &\n (#{expected}) )"
45
+ @message = "\"#{expected} (\",#{expected},\") is not\", &\n #{actual},\"within\",#{tolerance}"
46
+ syntax_error("invalid body for #@type",@suite_name) unless $&
47
+ write_assert
48
+ end
49
+
50
+ def assert_equal(line)
51
+ line.match(/\((\w+\(.*\)|[^,]+),(.+)\)/)
52
+ @type = 'Assert_Equal'
53
+ @condition = ".not.(#$1==#$2)"
54
+ @message = "\"#$1 (\",#$1,\") is not\", #$2"
55
+ syntax_error("invalid body for #@type",@suite_name) unless $&
56
+ write_assert
57
+ end
58
+
59
+ def assert_array_equal(line)
60
+ line.match(/\((\w+),(\w+)\)/)
61
+ @type = 'Assert_Array_Equal'
62
+ @condition = ".not. all(#$1==#$2)"
63
+ @message = "\"array #$1 is not #$2\""
64
+ syntax_error("invalid body for #@type",@suite_name) unless $&
65
+ write_assert
66
+ end
67
+
68
+ ##
69
+ # An argument scanner thanks to James Edward Gray II
70
+ # by way of ruby-talk mailing list.
71
+
72
+ def get_args(string)
73
+ scanner = ::StringScanner.new(string)
74
+ result = scanner.eos? ? [] : ['']
75
+ paren_depth = 0
76
+ until scanner.eos?
77
+ if scanner.scan(/[^(),]+/)
78
+ # do nothing--we found the part of the argument we need to add
79
+ elsif scanner.scan(/\(/)
80
+ paren_depth += 1
81
+ elsif scanner.scan(/\)/)
82
+ paren_depth -= 1
83
+ elsif scanner.scan(/,\s*/) and paren_depth.zero?
84
+ result << ''
85
+ next
86
+ end
87
+ result.last << scanner.matched
88
+ end
89
+ result
90
+ end
91
+
92
+ ##
93
+ # Translate the assertion to Fortran.
94
+
95
+ def write_assert
96
+ <<-OUTPUT
97
+ ! #@type assertion
98
+ numAsserts = numAsserts + 1
99
+ if (noAssertFailed) then
100
+ if (#@condition) then
101
+ print *, " *#@type failed* in test #@test_name &
102
+ &[#{@suite_name}.fun:#{@line_number.to_s}]"
103
+ print *, " ", #@message
104
+ print *, ""
105
+ noAssertFailed = .false.
106
+ numFailures = numFailures + 1
107
+ else
108
+ numAssertsTested = numAssertsTested + 1
109
+ endif
110
+ endif
111
+ OUTPUT
112
+ end
113
+
114
+ end
115
+ end
116
+
117
+ #--
118
+ # Copyright 2006-2007 United States Government as represented by
119
+ # NASA Langley Research Center. No copyright is claimed in
120
+ # the United States under Title 17, U.S. Code. All Other Rights
121
+ # Reserved.
122
+ #
123
+ # This file is governed by the NASA Open Source Agreement.
124
+ # See License.txt for details.
125
+ #++
@@ -0,0 +1,205 @@
1
+ module Funit
2
+ class C_compile
3
+ def initialize
4
+ check_c_compiler
5
+ @libraries = {}
6
+ end
7
+
8
+ def check_c_compiler
9
+ if(ENV['CXX'].nil?) then
10
+ puts <<-EOF
11
+
12
+ You have not specified a C++ compiler. Please specify a compiler via the $(CXX) environment variable.
13
+
14
+ In bash, for example:
15
+
16
+ export CXX="g++"
17
+
18
+ EOF
19
+ elsif(ENV['CC'].nil?) then
20
+ puts <<-EOF
21
+
22
+ You have not specified a C compiler. Please specify a compiler via the $(CC) environment variable.
23
+
24
+ In bash, for example:
25
+
26
+ export CC="gcc"
27
+
28
+ EOF
29
+ exit
30
+ end
31
+ end
32
+
33
+ def compile_library(dir)
34
+ puts "building: #{dir}"
35
+ Dir.chdir(dir) do
36
+ static_lib = make_objs
37
+ @libraries[dir]=static_lib.match(/lib(\w+)\.a/).captures.join
38
+ end
39
+ end
40
+
41
+ def make_objs(dir=Dir.getwd)
42
+ if File.exist?("Makefile.am")
43
+ sources = parse_automake_file
44
+ static_library = sources[0]
45
+ File.open("makeTestRunner", "w") {|file| file.puts write_custom_c_makefile(sources)}
46
+ else
47
+ static_library = "lib#{File.basename(Dir.getwd)}.a"
48
+ File.open("makeTestRunner", "w") {|file| file.puts write_generic_c_makefile(static_library)}
49
+ end
50
+ compile = "make -f makeTestRunner"
51
+ raise "Compile failed in #{dir}." unless system compile
52
+ static_library
53
+ end
54
+
55
+ def parse_automake_file
56
+ sources = []
57
+ lines = IO.readlines("Makefile.am")
58
+ while line = lines.shift
59
+ sources << $1 if line.match(/^\s*lib_LIBRARIES\s*=\s*(\w+\.a)/)
60
+ if line.match(/^\s*\w+_SOURCES\s*=/)
61
+ sources << line.scan(/\w+\.cpp/)
62
+ while line.match(/\\\s*$/)
63
+ line = lines.shift
64
+ sources << line.scan(/(\w+\.cpp)|(\w+\.c)/)
65
+ end
66
+ end
67
+ end
68
+ sources.uniq.flatten.compact
69
+ end
70
+
71
+ def print_linker_flags
72
+ output_string = ''
73
+ @libraries.each do |k,v|
74
+ output_string += " -L#{k} -l#{v}"
75
+ end
76
+ output_string += " -lstdc++"
77
+ end
78
+
79
+ def clean_c_code
80
+ @libraries.keys.each do |k|
81
+ Dir.chdir(k) do
82
+ puts "cleaning C code in #{k}"
83
+ make_clean = "make -f makeTestRunner clean"
84
+ system make_clean
85
+ FileUtils.rm "makeTestRunner"
86
+ end
87
+ end
88
+ end
89
+
90
+ def write_generic_c_makefile(library)
91
+ c_makefile = %Q{
92
+ # makefile to compile c++ code
93
+ # Add .d to Make's recognized suffixes.
94
+ SUFFIXES += .d
95
+
96
+ #Archive command and options
97
+ AR = ar
98
+ AR_OPTS = cru
99
+
100
+ LIBRARY = #{library}
101
+
102
+ #We don't need to clean up when we're making these targets
103
+ NODEPS:=clean tags svn
104
+ #Find all the C++ files in this directory
105
+ SOURCES:=$(shell find . -name "*.cpp")
106
+ SOURCES+=$(shell find . -name "*.c")
107
+
108
+ #These are the dependency files, which make will clean up after it creates them
109
+ CFILES:=$(SOURCES:.cpp=.c)
110
+ DEPFILES:=$(CFILES:.c=.d)
111
+
112
+ OBJS:=$(CFILES:.c=.o)
113
+
114
+ all: $(LIBRARY)
115
+
116
+ #Rule to create library archive
117
+ $(LIBRARY): $(OBJS)
118
+ \t$(AR) $(AR_OPTS) $@ $^
119
+
120
+ #Don't create dependencies when we're cleaning, for instance
121
+ ifeq (0, $(words $(findstring $(MAKECMDGOALS), $(NODEPS))))
122
+ #Chances are, these files don't exist. GMake will create them and
123
+ #clean up automatically afterwards
124
+ -include $(DEPFILES)
125
+ endif
126
+
127
+ #This is the rule for creating the C++ dependency files
128
+ %.d: %.cpp
129
+ \t$(CXX) $(CXXFLAGS) -MM -MT '$(patsubst %.cpp,%.o,$<)' $< -MF $@
130
+
131
+ #This is the rule for creating the C dependency files
132
+ %.d: %.c
133
+ \t$(CC) $(CFLAGS) -MM -MT '$(patsubst %.c,%.o,$<)' $< -MF $@
134
+
135
+ #This rule does the compilation for C++ files
136
+ %.o: %.cpp %.d %.h
137
+ \t$(CXX) $(CXXFLAGS) -o $@ -c $<
138
+
139
+ #This rule does the compilation for C files
140
+ %.o: %.c %.d %.h
141
+ \t$(CC) $(CFLAGS) -o $@ -c $<
142
+
143
+ clean:
144
+ \trm -rf *.o *.d *.a
145
+
146
+ }.gsub!(/^ /,'')
147
+ end
148
+
149
+ def write_custom_c_makefile(sources)
150
+ library = sources.shift
151
+ source_files = sources.join(" ")
152
+ c_makefile = %Q{
153
+ # makefile to compile c++ code
154
+ # Add .d to Make's recognized suffixes.
155
+ SUFFIXES += .d
156
+
157
+ #Archive command and options
158
+ AR = ar
159
+ AR_OPTS = cru
160
+
161
+ LIBRARY = #{library}
162
+
163
+ SOURCES = #{source_files}
164
+
165
+ #These are the dependency files, which make will clean up after it creates them
166
+ CFILES:=$(SOURCES:.cpp=.c)
167
+ DEPFILES:=$(CFILES:.c=.d)
168
+
169
+ OBJS:=$(CFILES:.c=.o)
170
+
171
+ all: $(LIBRARY)
172
+
173
+ #Rule to create library archive
174
+ $(LIBRARY): $(OBJS)
175
+ \t$(AR) $(AR_OPTS) $@ $^
176
+
177
+ #Don't create dependencies when we're cleaning, for instance
178
+ ifeq (0, $(words $(findstring $(MAKECMDGOALS), $(NODEPS))))
179
+ #Chances are, these files don't exist. GMake will create them and
180
+ #clean up automatically afterwards
181
+ -include $(DEPFILES)
182
+ endif
183
+
184
+ #This is the rule for creating the C++ dependency files
185
+ %.d: %.cpp
186
+ \t$(CXX) $(CXXFLAGS) -MM -MT '$(patsubst %.cpp,%.o,$<)' $< -MF $@
187
+
188
+ #This is the rule for creating the C dependency files
189
+ %.d: %.c
190
+ \t$(CC) $(CFLAGS) -MM -MT '$(patsubst %.c,%.o,$<)' $< -MF $@
191
+
192
+ #This rule does the compilation for C++ files
193
+ %.o: %.cpp %.d %.h
194
+ \t$(CXX) $(CXXFLAGS) -o $@ -c $<
195
+
196
+ #This rule does the compilation for C files
197
+ %.o: %.c %.d %.h
198
+ \t$(CC) $(CFLAGS) -o $@ -c $<
199
+
200
+ clean:
201
+ \trm -rf *.o *.d *.a
202
+ }.gsub!(/^ /,'')
203
+ end
204
+ end
205
+ end
@@ -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,150 @@
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
+
12
+ <% test_suites.each do |test_suite| -%>
13
+ use <%= test_suite %>_fun
14
+ <% end -%>
15
+
16
+ implicit none
17
+
18
+ integer, dimension(<%=test_suites.size%>) :: numTests, numAsserts, numAssertsTested, numFailures
19
+
20
+ <% test_suites.each_with_index do |test_suite,i| -%>
21
+ write(*,*)
22
+ write(*,*) "<%= test_suite %> test suite:"
23
+ call test_<%= test_suite %> &
24
+ ( numTests(<%= i+1 %>), numAsserts(<%= i+1 %>), numAssertsTested(<%= i+1 %>), numFailures(<%= i+1 %>) )
25
+ write(*,1) numAssertsTested(<%= i+1 %>), numAsserts(<%= i+1 %>), &
26
+ numTests(<%= i+1 %>)-numFailures(<%= i+1 %>), numTests(<%= i+1 %>)
27
+ 1 format('Passed ',i0,' of ',i0,' possible asserts comprising ',i0,' of ',i0,' tests.')
28
+ <% end -%>
29
+
30
+ write(*,*)
31
+ write(*,'(a)') "==========[ SUMMARY ]=========="
32
+ <% max_length = test_suites.empty? ? 0 : test_suites.max.length -%>
33
+ <% test_suites.each_with_index do |test_suite,i| -%>
34
+ write(*,'(a<%=max_length+2%>)',advance="no") " <%= test_suite %>:"
35
+ if ( numFailures(<%= i+1 %>) == 0 ) then
36
+ write(*,*) " passed"
37
+ else
38
+ write(*,*) " failed <<<<<"
39
+ end if
40
+ <% end -%>
41
+ write(*,*)
42
+
43
+ if ( sum(numFailures) /= 0 ) stop 1
44
+
45
+ end program TestRunner
46
+ }.gsub(/^ /,''), nil, '-' ) # turn off newlines for <% -%>
47
+
48
+ MAKEFILE = ERB.new( %q{
49
+ # makefile to compile TestRunner.f90
50
+ #
51
+ # <%= File.basename $0 %> generated this file on <%= Time.now %>.
52
+
53
+ OBJ=<%= required_objects.join(' ') %>
54
+ LDFLAGS=<%= c_code.print_linker_flags %>
55
+
56
+ all:testrunner
57
+
58
+ testrunner: $(OBJ)
59
+ <%= "\t#{ENV['FC']} #{ENV['FCFLAGS']} #{ENV['LDFLAGS']}" %> -o TestRunner $(OBJ) $(LDFLAGS)
60
+
61
+ <% file_dependencies.each do |source,dep| -%>
62
+ <%= "#{source.sub(/\.f90/i,'.o')}: #{source} #{dep.map{ |d| d.sub(/\.f90/i,'.o') }.join(' ')}" %>
63
+ <%= "\t(cd #{File.dirname(source)}; #{ENV['FC']} #{ENV['FCFLAGS']} #{sourceflag} -c #{File.basename(source)})" %>
64
+ <% end -%>
65
+ }.gsub(/^ /,''), nil, '-' ) # turn off newlines for <% -%>
66
+
67
+ def requested_modules(module_names)
68
+ if module_names.empty?
69
+ module_names = Dir["*.fun"].each{ |mod| mod.chomp! ".fun" }
70
+ end
71
+ module_names
72
+ end
73
+
74
+ def funit_exists?(module_name)
75
+ File.exists? "#{module_name}.fun"
76
+ end
77
+
78
+ def parse_command_line
79
+
80
+ module_names = requested_modules(ARGV)
81
+
82
+ if module_names.empty?
83
+ raise " *Error: no test suites found in this directory"
84
+ end
85
+
86
+ module_names.each do |mod|
87
+ unless funit_exists?(mod)
88
+ error_message = <<-FUNIT_DOES_NOT_EXIST
89
+ Error: could not find test suite #{mod}.fun
90
+ Test suites available in this directory:
91
+ #{requested_modules([]).join(' ')}
92
+
93
+ Usage: #{File.basename $0} [test names (w/o .fun suffix)]
94
+ FUNIT_DOES_NOT_EXIST
95
+ raise error_message
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+ def write_test_runner test_suites
102
+ File.open("TestRunner.f90", "w") do |file|
103
+ file.puts TEST_RUNNER.result(binding)
104
+ end
105
+ end
106
+
107
+ def syntax_error( message, test_suite )
108
+ raise "\n *Error: #{message} [#{test_suite}.fun:#$.]\n\n"
109
+ end
110
+
111
+ def warning( message, test_suite )
112
+ $stderr.puts "\n *Warning: #{message} [#{test_suite}.fun:#$.]"
113
+ end
114
+
115
+ def compile_tests(test_suites,funit_data)
116
+ prog_source_dirs = funit_data.prog_source_dirs
117
+ c_code = funit_data.c_code
118
+
119
+ puts "computing dependencies"
120
+
121
+ sourceflag = ''
122
+ if ENV['FSFLAG'] then
123
+ sourceflag = prog_source_dirs.map{|pd| ENV['FSFLAG']+pd }.join(' ')
124
+ end
125
+ dependencies = Fortran::Dependencies.new(:search_paths=>prog_source_dirs)
126
+
127
+ puts "locating associated source files and sorting for compilation"
128
+ dependencies.source_file_dependencies('TestRunner.f90')
129
+ file_dependencies = dependencies.file_dependencies
130
+ required_objects = file_dependencies.values.flatten.uniq.map{|s|s.sub(/\.f90/i,'.o')}
131
+ required_objects << 'TestRunner.o'
132
+
133
+ File.open("makeTestRunner", "w") {|file| file.puts MAKEFILE.result(binding)}
134
+
135
+ compile = "make -f makeTestRunner"
136
+
137
+ raise "Compile failed." unless system compile
138
+ end
139
+
140
+ end
141
+
142
+ #--
143
+ # Copyright 2006-2007 United States Government as represented by
144
+ # NASA Langley Research Center. No copyright is claimed in
145
+ # the United States under Title 17, U.S. Code. All Other Rights
146
+ # Reserved.
147
+ #
148
+ # This file is governed by the NASA Open Source Agreement.
149
+ # See License.txt for details.
150
+ #++