fit 1.1 → 1.2

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