funit-12 0.12.1

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.
@@ -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
+ #++