fit 1.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.
Files changed (110) hide show
  1. data/README.txt +203 -0
  2. data/Rakefile +111 -0
  3. data/bin/FitServer.rb +6 -0
  4. data/bin/fit +10 -0
  5. data/bin/fit.cgi +36 -0
  6. data/doc/examples/AllCombinations.html +55 -0
  7. data/doc/examples/AllFiles.html +60 -0
  8. data/doc/examples/AllPairs/function/cosine.html +57 -0
  9. data/doc/examples/AllPairs/function/sine.html +57 -0
  10. data/doc/examples/AllPairs/magnitude/180+30.html +45 -0
  11. data/doc/examples/AllPairs/magnitude/30.html +33 -0
  12. data/doc/examples/AllPairs/magnitude/360+30.html +45 -0
  13. data/doc/examples/AllPairs/magnitude/90-30.html +45 -0
  14. data/doc/examples/AllPairs/sign/change-sign.html +27 -0
  15. data/doc/examples/AllPairs/sign/multiply.html +31 -0
  16. data/doc/examples/AllPairs/sign/no-change.html +23 -0
  17. data/doc/examples/AllPairs.html +51 -0
  18. data/doc/examples/BinaryChop.html +89 -0
  19. data/doc/examples/CalculatorExample.html +108 -0
  20. data/doc/examples/ColumnIndex.html +43 -0
  21. data/doc/examples/ExampleTests.html +43 -0
  22. data/doc/examples/FitAcceptanceTests.html +53 -0
  23. data/doc/examples/GeoCoordinate.html +87 -0
  24. data/doc/examples/MusicExample.html +143 -0
  25. data/doc/examples/MusicExampleWithErrors.html +128 -0
  26. data/doc/examples/NetworkExample.html +47 -0
  27. data/doc/examples/WebPageExample.html +92 -0
  28. data/doc/examples/arithmetic.html +211 -0
  29. data/doc/examples/files/hp35bk.jpg +0 -0
  30. data/doc/examples/logo.gif +0 -0
  31. data/doc/fitnesse/FitNesse.RubY.AcceptanceTests.FixtureParameters.html +81 -0
  32. data/doc/fitnesse/FitNesse.RubY.AcceptanceTests.GracefulFixtureNames.html +87 -0
  33. data/doc/fitnesse/FitNesse.RubY.AcceptanceTests.GracefulMemberNames.html +73 -0
  34. data/doc/fitnesse/FitNesse.RubY.AcceptanceTests.ImportFixture.html +61 -0
  35. data/doc/fitnesse/FitNesse.RubY.AcceptanceTests.WaysToSpecifyaFixtureNamespace.html +81 -0
  36. data/doc/spec/annotation.html +3833 -0
  37. data/doc/spec/extensions.html +302 -0
  38. data/doc/spec/fixtures.html +5181 -0
  39. data/doc/spec/index.html +947 -0
  40. data/doc/spec/parse.html +3094 -0
  41. data/lib/eg/all_combinations.rb +44 -0
  42. data/lib/eg/all_files.rb +94 -0
  43. data/lib/eg/all_pairs.rb +172 -0
  44. data/lib/eg/arithmetic_column_fixture.rb +35 -0
  45. data/lib/eg/arithmetic_fixture.rb +29 -0
  46. data/lib/eg/binary_chop.rb +100 -0
  47. data/lib/eg/calculator.rb +69 -0
  48. data/lib/eg/column_index.rb +85 -0
  49. data/lib/eg/division.rb +13 -0
  50. data/lib/eg/echo_args_fixture.rb +9 -0
  51. data/lib/eg/example_tests.rb +84 -0
  52. data/lib/eg/music/Music.txt +38 -0
  53. data/lib/eg/music/browser.rb +60 -0
  54. data/lib/eg/music/display.rb +24 -0
  55. data/lib/eg/music/music.rb +67 -0
  56. data/lib/eg/music/music_library.rb +70 -0
  57. data/lib/eg/music/music_player.rb +77 -0
  58. data/lib/eg/music/realtime.rb +39 -0
  59. data/lib/eg/music/simulator.rb +81 -0
  60. data/lib/eg/nested/bob.rb +12 -0
  61. data/lib/eg/nested/bob_the_builder_fixture.rb +11 -0
  62. data/lib/eg/net/simulator.rb +69 -0
  63. data/lib/eg/page.rb +91 -0
  64. data/lib/eg/sqrt.rb +19 -0
  65. data/lib/fat/annotation_fixture.rb +83 -0
  66. data/lib/fat/color.rb +45 -0
  67. data/lib/fat/divide.rb +13 -0
  68. data/lib/fat/document_parse_fixture.rb +67 -0
  69. data/lib/fat/equals.rb +59 -0
  70. data/lib/fat/fixture_name_fixture.rb +67 -0
  71. data/lib/fat/html_to_text_fixture.rb +20 -0
  72. data/lib/fat/money.rb +18 -0
  73. data/lib/fat/output_fixture.rb +32 -0
  74. data/lib/fat/parse_fixture.rb +92 -0
  75. data/lib/fat/reference_fixture.rb +31 -0
  76. data/lib/fat/standard_annotation_fixture.rb +58 -0
  77. data/lib/fat/string_writer.rb +12 -0
  78. data/lib/fat/table.rb +23 -0
  79. data/lib/fat/table_parse_fixture.rb +33 -0
  80. data/lib/fat/text_to_html_fixture.rb +21 -0
  81. data/lib/fit/action_fixture.rb +65 -0
  82. data/lib/fit/column_fixture.rb +104 -0
  83. data/lib/fit/file_runner.rb +80 -0
  84. data/lib/fit/fit_protocol.rb +62 -0
  85. data/lib/fit/fit_server.rb +173 -0
  86. data/lib/fit/fixture.rb +309 -0
  87. data/lib/fit/fixture_loader.rb +75 -0
  88. data/lib/fit/import_fixture.rb +9 -0
  89. data/lib/fit/parse.rb +206 -0
  90. data/lib/fit/primitive_fixture.rb +52 -0
  91. data/lib/fit/row_fixture.rb +188 -0
  92. data/lib/fit/scientific_double.rb +70 -0
  93. data/lib/fit/summary.rb +46 -0
  94. data/lib/fit/timed_action_fixture.rb +35 -0
  95. data/lib/fit/type_adapter.rb +95 -0
  96. data/lib/fit/wiki_runner.rb +15 -0
  97. data/lib/fittask.rb +184 -0
  98. data/test/all_tests.rb +13 -0
  99. data/test/file_runner_test.rb +52 -0
  100. data/test/fit_server_test.rb +214 -0
  101. data/test/fixture_loader_test.rb +71 -0
  102. data/test/fixture_test.rb +41 -0
  103. data/test/fixtures/fail_fixture.rb +9 -0
  104. data/test/fixtures/pass_fixture.rb +9 -0
  105. data/test/framework_test.rb +51 -0
  106. data/test/parse_test.rb +101 -0
  107. data/test/row_fixture_test.rb +44 -0
  108. data/test/scientific_double_test.rb +35 -0
  109. data/test/type_adapter_test.rb +120 -0
  110. metadata +165 -0
@@ -0,0 +1,309 @@
1
+ # Copyright (c) 2002 Cunningham & Cunningham, Inc.
2
+ # Released under the terms of the GNU General Public License version 2 or later.
3
+
4
+ require 'fit/scientific_double'
5
+ require 'parsedate'
6
+ require 'fit/fixture_loader'
7
+
8
+ module Fit
9
+
10
+ class RunTime
11
+ def initialize
12
+ @elapsed = 0
13
+ @start = Time.now
14
+ end
15
+ def to_s
16
+ @elapsed = Time.now.to_f - @start.to_f
17
+ if @elapsed > 1
18
+ @elapsed = @elapsed.round
19
+ return d(3600) + ':' + d(600) + d(60) + ':' + d(10) + d(1)
20
+ else
21
+ @elapsed = (@elapsed * 100).round
22
+ return d(6000) + ':' + d(1000) + d(100) + '.' + d(10) + d(1)
23
+ end
24
+ end
25
+ def d scale
26
+ report = @elapsed / scale
27
+ @elapsed -= report * scale
28
+ report.to_s
29
+ end
30
+ end
31
+
32
+ class Counts
33
+ attr_accessor :right, :wrong, :ignores, :exceptions
34
+ def initialize
35
+ @right, @wrong, @ignores, @exceptions = 0, 0, 0, 0
36
+ end
37
+ def to_s
38
+ "#@right right, #@wrong wrong, #@ignores ignored, #@exceptions exceptions"
39
+ end
40
+ def tally c
41
+ @right += c.right
42
+ @wrong += c.wrong
43
+ @ignores += c.ignores
44
+ @exceptions += c.exceptions
45
+ end
46
+ def total_errors
47
+ @wrong + @exceptions
48
+ end
49
+ end
50
+
51
+ class FixtureListener
52
+ def tables_finished(finalCounts)
53
+ end
54
+
55
+ def table_finished(tableParse)
56
+ end
57
+ end
58
+
59
+ class Fixture
60
+ @@metadata = {}
61
+ def Fixture.metadata; @@metadata; end
62
+
63
+ attr_accessor :summary, :counts, :listener, :args
64
+
65
+ def initialize
66
+ @summary = {}
67
+ @counts = Counts.new
68
+ @listener = FixtureListener.new
69
+ @loader = FixtureLoader.new
70
+ @args = []
71
+ end
72
+
73
+ def total_errors
74
+ @counts.total_errors
75
+ end
76
+
77
+ # Traversal
78
+
79
+ def find_class class_name
80
+ @loader.find_fixture_class class_name
81
+ end
82
+
83
+ def do_tables tables
84
+ @summary['run date'] = Time.now.to_s
85
+ @summary['run elapsed time'] = RunTime.new
86
+ unless tables.nil?
87
+ fixture_name = fixture_name tables
88
+ unless fixture_name.nil?
89
+ begin
90
+ fixture = get_linked_fixture_with_args tables
91
+ fixture.interpret_tables tables
92
+ rescue Exception => e
93
+ exception fixture_name, e
94
+ interpret_following_tables tables
95
+ end
96
+ end
97
+ end
98
+ @listener.tables_finished(@counts)
99
+ end
100
+
101
+ def interpret_tables tables
102
+ # don't create the first fixture again, because creation may do something important
103
+ begin
104
+ get_args_for_table(tables) # get them again for the new fixture object
105
+ do_table tables
106
+ rescue Exception => e
107
+ exception fixture_name(tables), e
108
+ return
109
+ end
110
+ @listener.table_finished(tables)
111
+ interpret_following_tables tables
112
+ end
113
+
114
+ def interpret_following_tables tables
115
+ tables = tables.more
116
+ until tables.nil?
117
+ fixture_name = fixture_name(tables)
118
+ unless fixture_name.nil?
119
+ begin
120
+ fixture = get_linked_fixture_with_args tables
121
+ fixture.do_table tables
122
+ rescue Exception => e
123
+ exception fixture_name, e
124
+ end
125
+ end
126
+ @listener.table_finished(tables)
127
+ tables = tables.more
128
+ end
129
+ end
130
+
131
+ def get_linked_fixture_with_args tables
132
+ header = fixture_name tables
133
+ unless header.nil?
134
+ begin
135
+ fixture = (find_class header.text).new # using reflection
136
+ fixture.summary = @summary
137
+ fixture.counts = @counts
138
+ fixture.listener = @listener
139
+ fixture.get_args_for_table tables
140
+ return fixture
141
+ rescue Exception => e
142
+ exception header, e
143
+ end
144
+ end
145
+ end
146
+
147
+ def get_args_for_table table
148
+ arg = table.parts.parts.more
149
+ args = []
150
+ while arg
151
+ args << arg.text
152
+ arg = arg.more
153
+ end
154
+ @args = args
155
+ end
156
+
157
+ def fixture_name tables
158
+ tables.at 0, 0, 0
159
+ end
160
+
161
+ def do_table table
162
+ do_rows table.parts.more
163
+ end
164
+
165
+ def do_rows rows
166
+ until rows.nil?
167
+ more = rows.more
168
+ do_row rows
169
+ rows = more
170
+ end
171
+ end
172
+
173
+ def do_row row
174
+ do_cells row.parts
175
+ end
176
+
177
+ def do_cells cells
178
+ i = 0
179
+ until cells.nil?
180
+ begin
181
+ do_cell cells, i
182
+ rescue Exception => e
183
+ exception cells, e
184
+ end
185
+ cells = cells.more
186
+ i += 1
187
+ end
188
+ end
189
+
190
+ def do_cell cell, column_number
191
+ ignore cell
192
+ end
193
+
194
+ # Annotation methods
195
+ GREEN = '#cfffcf'
196
+ RED = '#ffcfcf'
197
+ GRAY = '#efefef'
198
+ YELLOW = '#ffffcf'
199
+
200
+ def right cell
201
+ cell.add_to_tag(' bgcolor="' + GREEN + '"')
202
+ @counts.right += 1
203
+ end
204
+
205
+ def wrong cell, actual = nil
206
+ cell.add_to_tag(' bgcolor="' + RED + '"')
207
+ cell.body = Fixture.escape(cell.text)
208
+ @counts.wrong += 1
209
+ unless actual.nil?
210
+ cell.add_to_body(Fixture.label('expected') + '<hr>' + Fixture.escape(actual) + Fixture.label('actual'))
211
+ end
212
+ end
213
+
214
+ def ignore cell
215
+ cell.add_to_tag(' bgcolor="' + GRAY + '"')
216
+ @counts.ignores += 1
217
+ end
218
+
219
+ def exception cell, e
220
+ stacktrace = e.backtrace.join "\n"
221
+ message = e.message.gsub(/>/, '&gt;').gsub(/</, '&lt;')
222
+ cell.add_to_body "<hr><font size=-2><pre>#{message}\n#{stacktrace}</pre></font>"
223
+ cell.add_to_tag(' bgcolor="' + YELLOW + '"')
224
+ @counts.exceptions += 1
225
+ end
226
+
227
+ def info cell, message
228
+ string = ' <font color="#808080">' + Fixture.escape(message) + '</font>'
229
+ cell.add_to_body string
230
+ end
231
+
232
+ def error cell, message
233
+ cell.body = Fixture.escape(cell.text)
234
+ cell.add_to_body('<hr><pre>' + Fixture.escape(message) + '</pre>')
235
+ cell.add_to_tag(' bgcolor="' + YELLOW + '"')
236
+ @counts.exceptions += 1
237
+ end
238
+
239
+ # Utility methods
240
+
241
+ # This method was originally called counts, but the name has been
242
+ # changed to avoid shadowing the counts accessor attribute.
243
+ def totals
244
+ @counts.to_s
245
+ end
246
+
247
+ def Fixture.label s
248
+ ' <font size=-1 color="#c08080"><i>' + s + '</i></font>'
249
+ end
250
+
251
+ def Fixture.gray s
252
+ ' <font color="#808080">' + s + '</font>'
253
+ end
254
+
255
+ def Fixture.escape s
256
+ str = s.gsub(/&/, '&amp;').gsub(/</, '&lt;').gsub(/ /, ' &nbsp;')
257
+ # str.gsub(/\r\n/, '<br />').gsub(/\n\r/, '<br />').gsub(/\r/, '<br />').gsub(/\n/, '<br />')
258
+ str.gsub(/\r\n/, '<br />').gsub(/\r/, '<br />').gsub(/\n/, '<br />')
259
+ end
260
+
261
+ # Originally, this method built the name of a Java method from multiple,
262
+ # space-separated, downcased words, morphing them into a single camelcased
263
+ # word. Ruby just needs those words to be joined with an underscore. For
264
+ # historical reasons, this method still maintains its original name.
265
+ def Fixture.camel name
266
+ name.gsub(/ /, '_')
267
+ end
268
+
269
+ def parse string, klass
270
+ return ParseDate.parsedate(string) if klass == ParseDate
271
+ return ScientificDouble.value_of(string) if klass == ScientificDouble
272
+ return string if klass == String
273
+ nil
274
+ end
275
+
276
+ def check cell, adapter
277
+ text = cell.text
278
+ if text.empty?
279
+ begin
280
+ info cell, adapter.to_s(adapter.get)
281
+ rescue Exception
282
+ info cell, 'error'
283
+ end
284
+ elsif adapter.nil?
285
+ ignore cell
286
+ elsif text == 'error'
287
+ begin
288
+ result = adapter.invoke # TypeAdapter.invoke does not exist...
289
+ wrong cell, adapter.to_s(result)
290
+ rescue Exception => e
291
+ right cell # no IllegalAccessException?
292
+ end
293
+ else
294
+ begin
295
+ result = adapter.get
296
+ if adapter.equals(adapter.parse(text), result)
297
+ right cell
298
+ else
299
+ wrong cell, adapter.to_s(result)
300
+ end
301
+ rescue Exception => e
302
+ exception cell, e
303
+ end
304
+ end
305
+ end
306
+
307
+ end
308
+
309
+ end
@@ -0,0 +1,75 @@
1
+ module Fit
2
+
3
+ class FixtureLoader
4
+
5
+ def load name
6
+ klass = find_fixture_class name
7
+ klass.new
8
+ end
9
+
10
+ def find_fixture_class name
11
+ camelizedName = (name.split(/[^a-zA-Z0-9.:$]/).collect { |word| first = word.slice!(0,1).upcase; first + word }).join.chomp('.')
12
+ klass = ([camelizedName, camelizedName + 'Fixture'].collect { |n| find_class n }).find { |fixtureKlass| fixtureKlass }
13
+ raise "Fixture #{name} not found." unless klass
14
+ raise "#{name} is not a fixture." unless klass < Fixture
15
+ klass
16
+ end
17
+
18
+ # Try to load the named class. We first see if it's already loaded by looking
19
+ # for the class name. If not, we convert the class name to a file name (by
20
+ # changing '::' to '/', then try to require that file. We then look for the
21
+ # constant again. This means that the class Example::Sqrt must be in the file
22
+ # Example/sqrt.rb or the file example/sqrt.rb.
23
+ def find_class name
24
+ klass = find_constant name
25
+ unless klass
26
+ ([''] + @@fixture_packages).detect do |prefix|
27
+ file_path = (prefix + name).gsub(/::/, '/').gsub(/\./, '/')
28
+ classname = basename = File::basename(file_path)
29
+ if basename.index('$')
30
+ basename_parts = basename.split(/\$/)
31
+ basename = basename_parts[0]
32
+ classname = basename_parts.join('::')
33
+ end
34
+ file_basename = basename.split(/([A-Z][^A-Z]+)/).delete_if {|e| e.empty?}.collect {|e| e.downcase!}.join('_')
35
+ file_dirname = File::dirname(file_path)
36
+ file_name = (file_dirname == '.' ? '' : file_dirname + '/') + file_basename
37
+ begin
38
+ begin
39
+ require file_name
40
+ rescue LoadError
41
+ require file_name.downcase
42
+ end
43
+ rescue LoadError
44
+ #raise "Couldn't load file #{file_name} or file #{file_name.downcase}"
45
+ end
46
+ if file_dirname == '.'
47
+ klass_name = classname
48
+ else
49
+ klass_name = File::dirname(file_path).split(%r{/}).collect { |e|
50
+ e.index(/[A-Z]/).nil? ? e.capitalize : e
51
+ }.join('::') + "::#{classname}"
52
+ end
53
+ klass = find_constant klass_name
54
+ end
55
+ end
56
+ klass
57
+ end
58
+
59
+ def find_constant name
60
+ class_name = name.gsub '.', '::'
61
+ classes = []
62
+ ObjectSpace.each_object(Class) { |klass| classes << klass }
63
+ classes.find { |klass| klass.name == class_name }
64
+ end
65
+
66
+ @@fixture_packages=['Fit::']
67
+ # This method adds the name of a module as a 'package' we should search for fixtures.
68
+ # Supports import_fixture
69
+ def FixtureLoader.add_fixture_package module_name
70
+ @@fixture_packages << module_name + '::'
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,9 @@
1
+ require 'fit/fixture'
2
+
3
+ module Fit
4
+ class ImportFixture < Fixture
5
+ def do_row row
6
+ FixtureLoader.add_fixture_package row.parts.text.gsub('.','::')
7
+ end
8
+ end
9
+ end
data/lib/fit/parse.rb ADDED
@@ -0,0 +1,206 @@
1
+ # Copyright (c) 2002 Cunningham & Cunningham, Inc.
2
+ # Released under the terms of the GNU General Public License version 2 or later.
3
+
4
+ require 'fileutils' # for footnotes generation
5
+
6
+ module Fit
7
+
8
+ class ParseException < Exception
9
+ attr_reader :error_offset
10
+ def initialize message, offset
11
+ super message
12
+ @error_offset = offset
13
+ end
14
+ end
15
+
16
+ # This ParseHolder class is necessary to cope with the different constructors
17
+ # in the Java version of FIT. One of them is the constructor for the Parse Ruby
18
+ # class, the other is a static method in the ParseHolder class.
19
+ #
20
+ # ParseHolder also maintain all the methods belonging to Parse which are instead
21
+ # called whenever in FIT a ParseHolder is created instead of a Parse.
22
+ class ParseHolder
23
+
24
+ attr_accessor :leader, :tag, :body, :end, :trailer
25
+ attr_accessor :more, :parts
26
+
27
+ def ParseHolder.create tag, body, parts, more
28
+ p = new
29
+ p.leader = "\n"
30
+ p.tag = "<#{tag}>"
31
+ p.body = body
32
+ p.end = "</#{tag}>"
33
+ p.trailer = ""
34
+ p.parts = parts
35
+ p.more = more
36
+ p
37
+ end
38
+
39
+ def leaf
40
+ @parts.nil? ? self : @parts.leaf
41
+ end
42
+
43
+ def add_to_tag text
44
+ @tag = @tag[0..-2] + "#{text}>"
45
+ end
46
+
47
+ def add_to_body text
48
+ @body += text
49
+ end
50
+
51
+ def at i, *rest
52
+ node = (i == 0 || @more.nil?) ? self : @more.at(i - 1)
53
+ rest.each do |j|
54
+ node = node.parts.at(j)
55
+ end
56
+ node
57
+ end
58
+
59
+ def text
60
+ Parse.html_to_text @body
61
+ end
62
+
63
+ def last
64
+ @more.nil? ? self : @more.last
65
+ end
66
+
67
+ def size
68
+ @more.nil? ? 1 : @more.size + 1
69
+ end
70
+
71
+ def print out
72
+ out.print @leader
73
+ out.print @tag
74
+ if @parts
75
+ @parts.print out
76
+ else
77
+ out.print @body
78
+ end
79
+ out.print @end
80
+ if @more
81
+ @more.print out
82
+ else
83
+ out.print @trailer
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ class Parse < ParseHolder
90
+
91
+ MAX_INT = 2147483647 # hardcoded java.lang.Integer.MAX_VALUE
92
+ DEFAULT_TAGS = ['table', 'tr', 'td']
93
+
94
+ @@footnote_files = 0
95
+
96
+ def initialize text, tags = DEFAULT_TAGS, level = 0, offset = 0
97
+ tag = tags[level]
98
+ lc = text.downcase
99
+ start_tag = lc.index "<#{tag}"
100
+ raise ParseException.new("Can't find tag: #{tag}", offset) if start_tag.nil?
101
+ end_tag = lc.index('>', start_tag)
102
+ raise ParseException.new("Can't find tag: #{tag}", offset) if end_tag.nil?
103
+ end_tag += 1
104
+ start_end = find_matching_end_tag lc, end_tag, tag, offset
105
+ raise ParseException.new("Can't find tag: #{tag}", offset) if start_end.nil?
106
+ end_end = lc.index('>', start_end)
107
+ raise ParseException.new("Can't find tag: #{tag}", offset) if end_end.nil?
108
+ end_end += 1
109
+ start_more = lc.index "<#{tag}", end_end
110
+
111
+ @leader = text[0...start_tag]
112
+ @tag = text[start_tag...end_tag]
113
+ @body = text[end_tag...start_end]
114
+ @end = text[start_end...end_end]
115
+ @trailer = text[end_end..-1]
116
+
117
+ if level + 1 < tags.size
118
+ @parts = Parse.new @body, tags, level + 1, offset + end_tag
119
+ @body = nil
120
+ else # check for nested table
121
+ index = @body.index "<#{tags[0]}"
122
+ unless index.nil?
123
+ @parts = Parse.new @body, tags, 0, offset + end_tag
124
+ @body = ''
125
+ end
126
+ end
127
+
128
+ unless start_more.nil?
129
+ @more = Parse.new @trailer, tags, level, offset + end_end
130
+ @trailer = nil
131
+ end
132
+ end
133
+
134
+ def find_matching_end_tag lc, match_from_here, tag, offset
135
+ from_here = match_from_here
136
+ count = 1
137
+ start_end = 0
138
+ while count > 0
139
+ embedded_tag = lc.index "<#{tag}", from_here
140
+ embedded_tag_end = lc.index "</#{tag}", from_here
141
+ # which one is closer?
142
+ raise ParseException.new("Can't find tag: #{tag}", offset) if embedded_tag.nil? and embedded_tag_end.nil?
143
+ embedded_tag = MAX_INT if embedded_tag.nil?
144
+ embedded_tag_end = MAX_INT if embedded_tag_end.nil?
145
+ if embedded_tag < embedded_tag_end
146
+ count += 1
147
+ start_end = embedded_tag
148
+ from_here = lc.index('>', embedded_tag) + 1
149
+ elsif embedded_tag_end < embedded_tag
150
+ count -= 1
151
+ start_end = embedded_tag_end
152
+ from_here = lc.index('>', embedded_tag_end) + 1
153
+ end
154
+ end
155
+ start_end
156
+ end
157
+
158
+ def Parse.html_to_text s
159
+ str = s.gsub(%r{<\s*br\s*/?\s*>}, '<br />').gsub(%r{<\s*/\s*p\s*>\s*<\s*p( .*?)?\s*>}, '<br />')
160
+ unescape(condense_whitespace(remove_tags(str)))
161
+ end
162
+
163
+ def Parse.remove_tags s
164
+ s.gsub(/<.*?>/m) { $& == '<br />' ? $& : '' }
165
+ end
166
+
167
+ def Parse.unescape s
168
+ str = s.gsub %r{<br />}, "\n"
169
+ # unescape HTML entities
170
+ str = str.gsub(%r{&lt;}, '<').gsub(%r{&gt;}, '>').gsub(%r{&nbsp;}, ' ').gsub(%r{&quot;}, '"').gsub(%r{&amp;}, '&')
171
+ # unescape smart quotes
172
+ str.gsub(/\223/, '"').gsub(/\224/, '"').gsub(/\221/, "'").gsub(/\222/, "'")
173
+ end
174
+
175
+ def Parse.condense_whitespace s
176
+ # NOT_BREAKING_SPACE is decimal character 160, hex a0, oct 240
177
+ s.gsub(%r{\s+}, ' ').gsub(/\240/, ' ').gsub(%r{&nbsp;}, ' ').strip
178
+ end
179
+
180
+ # The original implementation of footnote hardcodes the creation path,
181
+ # hence is somewhat broken. The use of a class variable lets external
182
+ # clients (like Rake and FitTask) decide where to generate footnotes.
183
+ @@footnote_path = 'Reports/'
184
+ def Parse.footnote_path=(path); @@footnote_path = path; end
185
+ def Parse.footnote_path; @@footnote_path; end
186
+ def footnote
187
+ return '[-]' if @@footnote_files >= 25
188
+ begin
189
+ this_footnote = (@@footnote_files += 1)
190
+ html = "footnotes/#{this_footnote}.html"
191
+ path = "#{@@footnote_path}#{html}"
192
+ FileUtils.mkpath File.dirname(path)
193
+ f = File.new(path, 'w')
194
+ print f
195
+ f.close
196
+ return "<a href=#{@@footnote_path}/#{html}>[#{this_footnote}]</a>"
197
+ rescue Exception => e
198
+ puts e.message
199
+ puts e.backtrace
200
+ return '[!]'
201
+ end
202
+ end
203
+
204
+ end
205
+
206
+ end
@@ -0,0 +1,52 @@
1
+ # Copyright (c) 2002 Cunningham & Cunningham, Inc.
2
+ # Released under the terms of the GNU General Public License version 2 or later.
3
+
4
+ require 'fit/fixture'
5
+
6
+ module Fit
7
+
8
+ class PrimitiveFixture < Fixture
9
+
10
+ # Format converters
11
+
12
+ def parse_integer cell
13
+ Integer cell.text
14
+ end
15
+
16
+ def parse_double cell
17
+ Float cell.text
18
+ end
19
+
20
+ def parse_boolean cell
21
+ cell.text.downcase == 'true' ? true : false
22
+ end
23
+
24
+ # Answer comparison
25
+
26
+ def check_value cell, value
27
+ if value.to_s == cell.text
28
+ right cell
29
+ else
30
+ wrong cell, value.to_s
31
+ end
32
+ end
33
+
34
+ def check_boolean cell, value
35
+ if value == parse_boolean(cell)
36
+ right cell
37
+ else
38
+ wrong cell, value.to_s
39
+ end
40
+ end
41
+
42
+ def check cell, expected, value
43
+ if expected == value
44
+ right cell
45
+ else
46
+ wrong cell, value
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ end