fit 1.1 → 1.2

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 (46) hide show
  1. data/CHANGELOG +27 -0
  2. data/{README.txt → README.rdoc} +28 -9
  3. data/Rakefile +44 -4
  4. data/bin/fit +29 -3
  5. data/doc/book/TestChatServer.html +207 -0
  6. data/doc/book/TestDiscount.html +178 -0
  7. data/doc/book/TestDiscountGroup.html +223 -0
  8. data/doc/book/TestDiscountMoney.html +179 -0
  9. data/doc/book/TestLateHours.html +245 -0
  10. data/doc/bugs/ColumnFixtureFollowedByActionFixture.html +316 -0
  11. data/doc/examples/MusicExampleWithErrors.html +1 -1
  12. data/doc/spec/annotation.html +3634 -3833
  13. data/doc/spec/index.html +1043 -947
  14. data/doc/spec/parse-windows-1252.html +3806 -0
  15. data/doc/spec/parse.html +3806 -3094
  16. data/doc/spec/ui.html +1537 -0
  17. data/lib/eg/all_files.rb +1 -1
  18. data/lib/eg/book/calculate_discount.rb +25 -0
  19. data/lib/eg/book/calculate_discount_money.rb +60 -0
  20. data/lib/eg/book/chat_server_actions.rb +85 -0
  21. data/lib/eg/book/discount_group_ordered_list.rb +59 -0
  22. data/lib/eg/book/rent/calculate_late_hours.rb +35 -0
  23. data/lib/eg/column_index.rb +4 -3
  24. data/lib/eg/music/display.rb +4 -2
  25. data/lib/eg/music/music.rb +4 -2
  26. data/lib/fat/command_line_fixture.rb +33 -0
  27. data/lib/fat/html_to_text_fixture.rb +3 -3
  28. data/lib/fit/column_fixture.rb +11 -7
  29. data/lib/fit/file_runner.rb +65 -10
  30. data/lib/fit/fixture.rb +10 -4
  31. data/lib/fit/fixture_loader.rb +29 -19
  32. data/lib/fit/parse.rb +46 -12
  33. data/lib/fit/type_adapter.rb +8 -3
  34. data/lib/fit/version.rb +26 -0
  35. data/lib/fitlibrary/comment_fixture.rb +12 -0
  36. data/lib/fitlibrary/spec/specify_fixture.rb +154 -0
  37. data/lib/fitlibrary/specify/column_fixture_under_test.rb +17 -0
  38. data/lib/fitlibrary/specify/column_fixture_under_test_with_args.rb +17 -0
  39. data/lib/fittask.rb +43 -47
  40. data/test/all_tests.rb +2 -1
  41. data/test/column_fixture_test.rb +26 -0
  42. data/test/file_runner_test.rb +2 -7
  43. data/test/fixture_loader_test.rb +16 -3
  44. data/test/parse_test.rb +18 -3
  45. data/test/type_adapter_test.rb +58 -44
  46. metadata +180 -156
@@ -2,9 +2,10 @@
2
2
  # Released under the terms of the GNU General Public License version 2 or later.
3
3
 
4
4
  require 'fit/scientific_double'
5
- require 'parsedate'
6
5
  require 'fit/fixture_loader'
7
6
 
7
+ require 'date'
8
+
8
9
  module Fit
9
10
 
10
11
  class RunTime
@@ -60,13 +61,14 @@ module Fit
60
61
  @@metadata = {}
61
62
  def Fixture.metadata; @@metadata; end
62
63
 
64
+ @@loader = FixtureLoader.new
65
+
63
66
  attr_accessor :summary, :counts, :listener, :args
64
67
 
65
68
  def initialize
66
69
  @summary = {}
67
70
  @counts = Counts.new
68
71
  @listener = FixtureListener.new
69
- @loader = FixtureLoader.new
70
72
  @args = []
71
73
  end
72
74
 
@@ -77,7 +79,7 @@ module Fit
77
79
  # Traversal
78
80
 
79
81
  def find_class class_name
80
- @loader.find_fixture_class class_name
82
+ @@loader.find_fixture_class class_name
81
83
  end
82
84
 
83
85
  def do_tables tables
@@ -91,6 +93,7 @@ module Fit
91
93
  fixture.interpret_tables tables
92
94
  rescue Exception => e
93
95
  exception fixture_name, e
96
+ @listener.table_finished(tables)
94
97
  interpret_following_tables tables
95
98
  end
96
99
  end
@@ -267,7 +270,10 @@ module Fit
267
270
  end
268
271
 
269
272
  def parse string, klass
270
- return ParseDate.parsedate(string) if klass == ParseDate
273
+ if klass == Date
274
+ d = Date._parse string
275
+ return [d[:year], d[:mon], d[:mday], d[:hour], d[:min], d[:sec], d[:zone], d[:wday]]
276
+ end
271
277
  return ScientificDouble.value_of(string) if klass == ScientificDouble
272
278
  return string if klass == String
273
279
  nil
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  module Fit
2
4
 
3
5
  class FixtureLoader
@@ -9,9 +11,10 @@ module Fit
9
11
 
10
12
  def find_fixture_class name
11
13
  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
14
+ klasses = ([camelizedName, camelizedName + 'Fixture'].collect { |n| find_class(n) }).compact
15
+ raise "Fixture #{name} not found." if klasses.length == 0
16
+ klass = klasses.find { |k| k < Fixture }
17
+ raise "#{name} is not a fixture." unless klass
15
18
  klass
16
19
  end
17
20
 
@@ -23,7 +26,7 @@ module Fit
23
26
  def find_class name
24
27
  klass = find_constant name
25
28
  unless klass
26
- ([''] + @@fixture_packages).detect do |prefix|
29
+ ([''] + @@fixture_packages.to_a).detect do |prefix|
27
30
  file_path = (prefix + name).gsub(/::/, '/').gsub(/\./, '/')
28
31
  classname = basename = File::basename(file_path)
29
32
  if basename.index('$')
@@ -34,19 +37,13 @@ module Fit
34
37
  file_basename = basename.split(/([A-Z][^A-Z]+)/).delete_if {|e| e.empty?}.collect {|e| e.downcase!}.join('_')
35
38
  file_dirname = File::dirname(file_path)
36
39
  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
40
+
41
+ require_file file_name
42
+
46
43
  if file_dirname == '.'
47
44
  klass_name = classname
48
45
  else
49
- klass_name = File::dirname(file_path).split(%r{/}).collect { |e|
46
+ klass_name = File::dirname(file_path).split(%r{/}).collect { |e|
50
47
  e.index(/[A-Z]/).nil? ? e.capitalize : e
51
48
  }.join('::') + "::#{classname}"
52
49
  end
@@ -58,18 +55,31 @@ module Fit
58
55
 
59
56
  def find_constant name
60
57
  class_name = name.gsub '.', '::'
61
- classes = []
62
- ObjectSpace.each_object(Class) { |klass| classes << klass }
63
- classes.find { |klass| klass.name == class_name }
58
+ return class_name.split('::').inject(Object) { |par, const| par.const_get(const) }
59
+ rescue NameError
60
+ return nil
61
+ end
62
+
63
+ def require_file file_name
64
+ begin
65
+ require file_name.downcase
66
+ rescue LoadError
67
+ require file_name
68
+ end
69
+ rescue LoadError
70
+ #raise "Couldn't load file #{file_name} or file #{file_name.downcase}"
64
71
  end
65
72
 
66
- @@fixture_packages=['Fit::']
73
+ @@fixture_packages = Set.new(['Fit::'])
67
74
  # This method adds the name of a module as a 'package' we should search for fixtures.
68
75
  # Supports import_fixture
69
76
  def FixtureLoader.add_fixture_package module_name
70
77
  @@fixture_packages << module_name + '::'
71
78
  end
79
+ def FixtureLoader.fixture_packages
80
+ @@fixture_packages
81
+ end
72
82
 
73
83
  end
74
84
 
75
- end
85
+ end
@@ -68,19 +68,19 @@ module Fit
68
68
  @more.nil? ? 1 : @more.size + 1
69
69
  end
70
70
 
71
- def print out
72
- out.print @leader
73
- out.print @tag
71
+ def print out, conv=nil
72
+ out.print conv.nil? ? @leader : conv.iconv(@leader)
73
+ out.print conv.nil? ? @tag : conv.iconv(@tag)
74
74
  if @parts
75
- @parts.print out
75
+ @parts.print out, conv
76
76
  else
77
- out.print @body
77
+ out.print conv.nil? ? @body : conv.iconv(@body)
78
78
  end
79
- out.print @end
79
+ out.print conv.nil? ? @end : conv.iconv(@end)
80
80
  if @more
81
- @more.print out
81
+ @more.print out, conv
82
82
  else
83
- out.print @trailer
83
+ out.print conv.nil? ? @trailer : conv.iconv(@end)
84
84
  end
85
85
  end
86
86
 
@@ -165,16 +165,50 @@ module Fit
165
165
  end
166
166
 
167
167
  def Parse.unescape s
168
- str = s.gsub %r{<br />}, "\n"
168
+ str = Parse.unescape_numeric_entities s
169
+ str = str.gsub %r{<br />}, "\n"
169
170
  # unescape HTML entities
170
171
  str = str.gsub(%r{&lt;}, '<').gsub(%r{&gt;}, '>').gsub(%r{&nbsp;}, ' ').gsub(%r{&quot;}, '"').gsub(%r{&amp;}, '&')
171
172
  # unescape smart quotes
172
- str.gsub(/\223/, '"').gsub(/\224/, '"').gsub(/\221/, "'").gsub(/\222/, "'")
173
+ left_double_quotes = [0x201c].pack('U')
174
+ right_double_quotes = [0x201d].pack('U')
175
+ left_single_quotes = [0x2018].pack('U')
176
+ right_single_quotes = [0x2019].pack('U')
177
+ str.gsub(left_double_quotes, '"').gsub(right_double_quotes, '"').gsub(left_single_quotes, "'").gsub(right_single_quotes, "'")
178
+ end
179
+
180
+ def Parse.unescape_numeric_entities s
181
+ result = ''
182
+ last_start = 0
183
+ starts_at = s.index '&#'
184
+ while not starts_at.nil?
185
+ ends_at = s.index ';', starts_at
186
+ if ends_at.nil?
187
+ starts_at = s.index('&#', starts_at + 1)
188
+ next
189
+ end
190
+ begin
191
+ entity = s[(starts_at + 2)...ends_at]
192
+ entity = '0x' + entity[1..-1] if (entity =~ /^x/ or entity =~ /^X/)
193
+ char = Integer(entity)
194
+ if char <= 0xFFFF
195
+ result += s[last_start...starts_at] + [char].pack('U')
196
+ last_start = ends_at + 1
197
+ end
198
+ rescue ArgumentError
199
+ # just loop around again
200
+ ensure
201
+ starts_at = s.index '&#', ends_at
202
+ end
203
+ end
204
+ result += s[last_start..-1]
173
205
  end
174
206
 
175
207
  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
208
+ not_breaking_space = [0x00a0].pack('U')
209
+ # Hack to work around not_breaking_space being considered
210
+ # a normal whitespace in Ruby 1.9, thus matching %r{\s+}
211
+ s.gsub(not_breaking_space, '&nbsp;').gsub(%r{\s+}, ' ').gsub(%r{&nbsp;}, ' ').strip
178
212
  end
179
213
 
180
214
  # The original implementation of footnote hardcodes the creation path,
@@ -59,7 +59,12 @@ module Fit
59
59
  end
60
60
 
61
61
  def to_s target
62
- target.to_s
62
+ case target
63
+ when Array
64
+ target.join(", ")
65
+ else
66
+ target.to_s
67
+ end
63
68
  end
64
69
 
65
70
  end
@@ -77,8 +82,8 @@ module Fit
77
82
  result = @fixture.parse value, @type
78
83
  return result unless result.nil?
79
84
  end
80
- return Integer(value) if value =~ /^-?\d+$/
81
- return Float(value) if (value =~ /^-?\d*\.\d*$/ || value =~ /^-?\d*\.\d*[e|E]\d+$/)
85
+ return Integer(value) if value =~ /\A-?\d+\Z/
86
+ return Float(value) if value =~ /\A[+-]?\d*\.\d+([eE][+-]?\d+)?\Z/
82
87
  elements = value.split(',')
83
88
  unless elements.size == 1
84
89
  array = []
@@ -0,0 +1,26 @@
1
+ # Copyright (c) 2002 Cunningham & Cunningham, Inc.
2
+ # Released under the terms of the GNU General Public License version 2 or later.
3
+
4
+ module Fit
5
+ VERSION = '1.2'
6
+
7
+ VERSION_BLURB = <<EOF_VERSION_BLURB
8
+ RubyFIT v#{Fit::VERSION}
9
+ Conforms to Fit Specification v1.2
10
+ Copyright (C) 2006-9 Giulio Piancastelli
11
+ License GPLv2+: GNU GPL version 2 or later
12
+ This is free software: you are free to change and redistribute it.
13
+ There is NO WARRANTY, to the extent permitted by law.
14
+ EOF_VERSION_BLURB
15
+
16
+ HELP_BLURB = <<EOF_HELP_BLURB
17
+ usage: #{File.basename($0)} [options] infile outfile
18
+
19
+ Options:
20
+ --encoding=CHARENC read and write files using specified character encoding
21
+ --version print version number
22
+ --help show this help screen
23
+
24
+ See http://fit.rubyforge.org for further information and to report bugs
25
+ EOF_HELP_BLURB
26
+ end
@@ -0,0 +1,12 @@
1
+ # Copyright (c) 2005 Rick Mugridge, University of Auckland, NZ
2
+ # Released under the terms of the GNU General Public License version 2 or later.
3
+
4
+ require 'fit/fixture'
5
+
6
+ module Fitlibrary
7
+
8
+ class CommentFixture < Fit::Fixture
9
+ def do_table table; end
10
+ end
11
+
12
+ end
@@ -0,0 +1,154 @@
1
+ # Copyright (c) 2005 Rick Mugridge, University of Auckland, NZ
2
+ # Released under the terms of the GNU General Public License version 2 or later.
3
+
4
+ require 'fit/fixture'
5
+
6
+ module Fitlibrary
7
+ module Spec
8
+
9
+ # Uses embedded tables to specify how fixtures work, based on simple
10
+ # subclasses of those fixtures.
11
+ # The test and the report can be in two separate rows, or in a single row.
12
+ class SpecifyFixture < Fit::Fixture
13
+
14
+ def do_table table
15
+ first_row = table.parts.more
16
+ actual = first_row.parts.parts
17
+ second_row = first_row.more
18
+ expected_cell = second_row.nil? ? first_row.parts.more : second_row.parts
19
+ expected = expected_cell.parts
20
+ Fit::Fixture.new.do_tables(actual)
21
+ if reports_equal(actual, expected)
22
+ right expected_cell
23
+ else
24
+ wrong expected_cell
25
+ print_parse actual, 'actual'
26
+ add_table_to_better_show_differences table, actual, expected
27
+ end
28
+ end
29
+
30
+ def add_table_to_better_show_differences table, actual, expected
31
+ parse_end = table.last
32
+ cells1 = Fit::ParseHolder.create('td', 'fitlibrary.CommentFixture', nil, nil)
33
+ cells2 = Fit::ParseHolder.create('td', 'actual', nil, Fit::ParseHolder.create('td', 'expected', nil, nil))
34
+ cells3 = Fit::ParseHolder.create('td', show(actual), nil, Fit::ParseHolder.create('td', show(expected), nil, nil))
35
+ rows = Fit::ParseHolder.create('tr', '', cells1, Fit::ParseHolder.create('tr', '', cells2, Fit::ParseHolder.create('tr', '', cells3, nil)))
36
+ parse_end.more = Fit::ParseHolder.create('table', '', rows, nil)
37
+ end
38
+
39
+ def show parse
40
+ return 'nil' if parse.nil?
41
+ result = "&lt;#{parse.tag[0..-2]}&gt;<ul>"
42
+ result += show_field('leader', parse.leader)
43
+ result += parse.parts.nil? ? show_field('body', parse.body) : show(parse.parts)
44
+ result += show_field('trailer', parse.trailer)
45
+ result += '</ul>'
46
+ result += show(parse.more) unless parse.more.nil?
47
+ result
48
+ end
49
+
50
+ def show_field field, value
51
+ if (not value.nil?) and (not value.strip.empty?)
52
+ "<li>#{field}: '#{no_tags(value)}'"
53
+ else
54
+ ""
55
+ end
56
+ end
57
+
58
+ def no_tags value
59
+ while true
60
+ index = value.index '<'
61
+ break if index < 0
62
+ value = value[0..index] + '&lt;' + value[index + 1..-1]
63
+ end
64
+ value
65
+ end
66
+
67
+ def reports_equal actual, expected
68
+ return expected.nil? if actual.nil?
69
+ return false if expected.nil?
70
+ massage_body_to_table actual
71
+ result = equal_tags(actual, expected) and
72
+ equal_strings(actual.leader, expected.leader) and
73
+ equal_bodies(actual, expected) and
74
+ equal_strings(actual.trailer, expected.trailer) and
75
+ reports_equal(actual.more, expected.more) and
76
+ reports_equal(actual.parts, expected.parts)
77
+ result
78
+ end
79
+
80
+ def massage_body_to_table actual
81
+ if (not actual.body.nil?) and (not actual.body.index('<table').nil?)
82
+ if actual.parts.nil?
83
+ begin
84
+ actual.parts = Fit::Parse.new actual.body
85
+ rescue Exception => e # FIXME FitParseException
86
+ # do nothing
87
+ end
88
+ end
89
+ actual.body = ''
90
+ end
91
+ end
92
+
93
+ def equal_bodies actual, expected
94
+ result = equal_bodies_22 actual, expected
95
+ puts "!SpecifyFixture#equal_bodies(\"#{actual.body}\",\"#{expected.body}\")" unless result
96
+ result
97
+ end
98
+
99
+ def equal_bodies_22 actual, expected
100
+ expected_body = canonical_string expected.body
101
+ actual_body = canonical_string actual.body
102
+ return true if expected_body == 'IGNORE'
103
+ return true if actual_body == expected_body
104
+ stack_trace = 'class="fit_stacktrace">'
105
+ start = expected_body.index stack_trace
106
+ unless start.nil?
107
+ pattern = expected_body[0, start + stack_trace.size]
108
+ return actual.body =~ pattern
109
+ end
110
+ error_message = '<span class="fit_label">'
111
+ start = expected_body.index error_message
112
+ unless start.nil?
113
+ end_span = expected_body.index '</span>', start
114
+ unless end_span.nil?
115
+ pattern = expected_body[0, end_span - 1]
116
+ return actual.body =~ pattern
117
+ end
118
+ end
119
+ false
120
+ end
121
+
122
+ def canonical_string body
123
+ s = body.nil? ? '' : body.strip
124
+ s
125
+ end
126
+
127
+ def equal_tags p1, p2
128
+ p1.tag == p2.tag
129
+ end
130
+
131
+ def equal_strings actual, expected
132
+ result = equal_strings_22 actual, expected
133
+ puts "!SpecifyFixture#equal_strings(\"#{actual}\",\"#{expected}\")" unless result
134
+ result
135
+ end
136
+
137
+ def equal_strings_22 actual, expected
138
+ return (expected.nil? or expected.strip.empty? or expected == "\n") if actual.nil?
139
+ return (actual.strip.empty? or actual == "\n") if expected.nil?
140
+ return true if expected == 'IGNORE'
141
+ actual.strip == expected.strip
142
+ end
143
+
144
+ # FIXME This should go in Fitlibrary::ParseUtility
145
+ def print_parse tables, title
146
+ puts "---------Parse tables for #{title}:----------"
147
+ tables.print(STDOUT) unless tables.nil?
148
+ puts "-------------------"
149
+ end
150
+
151
+ end
152
+
153
+ end
154
+ end
@@ -0,0 +1,17 @@
1
+ # Copyright (c) 2003 Rick Mugridge, University of Auckland, NZ
2
+ # Released under the terms of the GNU General Public License version 2 or later.
3
+
4
+ require 'fit/column_fixture'
5
+
6
+ module Fitlibrary
7
+ module Specify
8
+
9
+ class ColumnFixtureUnderTest < Fit::ColumnFixture
10
+ attr_accessor :camel_field_name
11
+ def get_camel_field_name
12
+ @camel_field_name
13
+ end
14
+ end
15
+
16
+ end
17
+ end