fit 1.1

Sign up to get free protection for your applications and to get access to all the features.
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