baretest 0.2.4 → 0.4.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/LICENSE.txt +6 -6
  2. data/MANIFEST.txt +40 -18
  3. data/README.rdoc +8 -1
  4. data/bin/baretest +126 -118
  5. data/doc/baretest.rdoc +1 -1
  6. data/doc/mocking_stubbing_test_doubles.rdoc +31 -3
  7. data/doc/news/news-0.3.0.rdoc +7 -0
  8. data/doc/quickref.rdoc +74 -28
  9. data/doc/whats_going_on.rdoc +5 -0
  10. data/doc/writing_tests.rdoc +25 -13
  11. data/examples/components/rack-test.rb +17 -0
  12. data/examples/{tests/irb_mode → irb_mode}/failures.rb +0 -0
  13. data/examples/rake/test.rake +40 -0
  14. data/examples/tests/01_basics_I.rb +34 -0
  15. data/examples/tests/02_basics_II_helpers.rb +25 -0
  16. data/examples/tests/03_basics_III_setup_and_teardown.rb +53 -0
  17. data/examples/tests/04_advanced_I_dependencies.rb +31 -0
  18. data/examples/tests/05_advanced_II_tags.rb +12 -0
  19. data/examples/tests/06_advanced_III_requires.rb +21 -0
  20. data/examples/tests/07_advanced_IV_components.rb +48 -0
  21. data/examples/tests/08_expert_I_setup_variants.rb +46 -0
  22. data/lib/baretest.rb +142 -21
  23. data/lib/baretest/assertion.rb +83 -92
  24. data/lib/baretest/assertion/context.rb +9 -0
  25. data/lib/baretest/assertion/support.rb +88 -61
  26. data/lib/baretest/commandline.rb +268 -0
  27. data/lib/baretest/formatter.rb +58 -0
  28. data/lib/baretest/invalidselectors.rb +24 -0
  29. data/lib/baretest/irb_mode.rb +100 -58
  30. data/lib/baretest/persistence.rb +94 -0
  31. data/lib/baretest/run.rb +138 -37
  32. data/lib/baretest/run/cli.rb +97 -43
  33. data/lib/baretest/run/minimal.rb +2 -1
  34. data/lib/baretest/run/none.rb +21 -0
  35. data/lib/baretest/run/xml.rb +21 -19
  36. data/lib/baretest/setup.rb +2 -0
  37. data/lib/baretest/status.rb +93 -0
  38. data/lib/baretest/suite.rb +185 -59
  39. data/lib/baretest/uid.rb +51 -0
  40. data/lib/baretest/use/mocha.rb +24 -0
  41. data/lib/baretest/use/rack_test.rb +9 -0
  42. data/lib/baretest/use/rr.rb +17 -0
  43. data/lib/baretest/version.rb +18 -4
  44. data/lib/command.rb +36 -0
  45. data/lib/command/argument.rb +11 -0
  46. data/lib/command/decoratinghash.rb +31 -0
  47. data/lib/command/definition.rb +294 -0
  48. data/lib/command/directorynotfounderror.rb +11 -0
  49. data/lib/command/env.rb +11 -0
  50. data/lib/command/filenotfounderror.rb +11 -0
  51. data/lib/command/kernel.rb +14 -0
  52. data/lib/command/nodirectoryerror.rb +11 -0
  53. data/lib/command/nofileerror.rb +11 -0
  54. data/lib/command/option.rb +16 -0
  55. data/lib/command/parser.rb +145 -0
  56. data/lib/command/result.rb +11 -0
  57. data/lib/command/types.rb +33 -0
  58. data/lib/command/version.rb +28 -0
  59. data/test/setup.rb +3 -0
  60. data/test/suite/lib/baretest.rb +0 -178
  61. data/test/suite/lib/baretest/assertion.rb +133 -112
  62. data/test/suite/lib/baretest/assertion/context.rb +40 -0
  63. data/test/suite/lib/baretest/assertion/failure.rb +19 -0
  64. data/test/suite/lib/baretest/assertion/skip.rb +19 -0
  65. data/test/suite/lib/baretest/assertion/support.rb +366 -84
  66. data/test/suite/lib/baretest/run.rb +114 -15
  67. data/test/suite/lib/baretest/suite.rb +70 -29
  68. metadata +46 -24
  69. data/examples/test.rake +0 -65
  70. data/examples/tests/mock_developer/test/helper/mocks.rb +0 -0
  71. data/examples/tests/mock_developer/test/setup.rb +0 -57
  72. data/examples/tests/mock_developer/test/suite/mock_demo.rb +0 -19
  73. data/examples/tests/overview/test.rb +0 -89
  74. data/examples/tests/variations/variations_01.rb +0 -14
  75. data/examples/tests/variations/variations_02.rb +0 -19
  76. data/examples/tests/variations/variations_03.rb +0 -19
  77. data/lib/baretest/mocha.rb +0 -18
  78. data/lib/baretest/rr.rb +0 -16
  79. data/lib/baretest/run/errors.rb +0 -49
  80. data/lib/baretest/skipped.rb +0 -15
  81. data/lib/baretest/skipped/assertion.rb +0 -20
  82. data/lib/baretest/skipped/suite.rb +0 -49
  83. data/test/external/bootstraptest.rb +0 -5
  84. data/test/external/bootstrapwrap.rb +0 -2
  85. data/test/helper/mocks.rb +0 -0
@@ -0,0 +1,51 @@
1
+ #--
2
+ # Copyright 2010 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module BareTest
10
+
11
+ # Generate a unique ID.
12
+ # Not using uuid simply to avoid two dependencies (uuid, and uuid's dependency
13
+ # 'macaddr').
14
+ class UID
15
+
16
+ # :nodoc:
17
+ Epoch = Time.utc(2000,1,1).to_i
18
+
19
+ # The numeric value of the uid (an Integer)
20
+ attr_reader :value
21
+
22
+ # Returns a 32byte long String containing a new 16 byte random value
23
+ # in hex representation
24
+ def self.hex_uid
25
+ new.hex
26
+ end
27
+
28
+ # Create a 128bit (16 Byte) long random number
29
+ def initialize
30
+ now = Time.now
31
+
32
+ # Works for the next 100 years - should be enough
33
+ # after that, it'll wrap around.
34
+ time_part_52 = (((now.to_i-Epoch) & 0xffffffff) << 20) + now.usec
35
+
36
+ # Not solid, but for the purposes, should work
37
+ process_part32 = (Thread.current.object_id ^ Process.pid) & 0xfffffffff
38
+
39
+ # Add a bit of random noise
40
+ random_part44 = Kernel.rand(0x100000000000)
41
+
42
+ @value = (random_part44 << 84) + (process_part32 << 52) + time_part_52
43
+ end
44
+
45
+ # Returns a 32byte long String containing the 16 byte random value
46
+ # in hex representation
47
+ def hex
48
+ "%032x" % @value
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,24 @@
1
+ # Original by dominikh
2
+ # Adapted to :use framework by apeiros
3
+
4
+ # This file provides integration with the "mocha" mocking framework.
5
+
6
+
7
+ # Load this file and get integration with the "rr" mocking framework.
8
+
9
+ BareTest.new_component :mocha do
10
+ require 'mocha'
11
+
12
+ BareTest::Assertion::Context.send :include Mocha::API
13
+
14
+ teardown do
15
+ begin
16
+ mocha_verify
17
+ rescue Mocha::ExpectationError => e
18
+ @reason = e.message
19
+ @status = :failure
20
+ ensure
21
+ mocha_teardown
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ BareTest.new_component :rack_test do
2
+ begin
3
+ require 'rubygems'
4
+ rescue LoadError; end
5
+ require 'rack/test'
6
+
7
+ BareTest::Assertion::Context.send :include, Rack::Test::Methods
8
+ BareTest::Assertion::Context.send :attr_reader, :app
9
+ end
@@ -0,0 +1,17 @@
1
+ # Original by Tass`
2
+ # Adapted to :use framework by apeiros
3
+
4
+ BareTest.new_component :rr do
5
+ require 'rr'
6
+
7
+ BareTest::Assertion::Context.send :include, RR::Adapters::RRMethods
8
+
9
+ teardown do
10
+ begin
11
+ ::RR.verify
12
+ rescue ::RR::Errors => e
13
+ @reason = e.message
14
+ @status = :failure
15
+ end
16
+ end
17
+ end
@@ -12,17 +12,31 @@ module BareTest
12
12
  module VERSION
13
13
 
14
14
  # The major version number
15
- MAJOR = 0
15
+ MAJOR = 0
16
16
 
17
17
  # The minor version number
18
- MINOR = 2
18
+ MINOR = 4
19
19
 
20
20
  # The tiny version number
21
- TINY = 4
21
+ TINY = 0
22
+
23
+ # Prerelease number - nil for release versions
24
+ PRERELEASE = 1
25
+
26
+ # The version as a string
27
+ STRING = %{#{MAJOR}.#{MINOR||0}.#{TINY||0}#{".pre#{PRERELEASE}" if PRERELEASE}}
28
+
29
+ # The version as an array
30
+ ARRAY = [MAJOR || 0, MINOR || 0, TINY || 0, PRERELEASE].compact
22
31
 
23
32
  # The version as a string
24
33
  def self.to_s
25
- "#{MAJOR}.#{MINOR||0}.#{TINY||0}"
34
+ STRING
35
+ end
36
+
37
+ # The version as an array
38
+ def self.to_a
39
+ ARRAY
26
40
  end
27
41
  end
28
42
  end
data/lib/command.rb ADDED
@@ -0,0 +1,36 @@
1
+ #--
2
+ # Copyright 2010 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'command/kernel'
10
+ require 'command/nofileerror'
11
+ require 'command/nodirectoryerror'
12
+ require 'command/filenotfounderror'
13
+ require 'command/directorynotfounderror'
14
+ require 'command/types'
15
+ require 'command/argument'
16
+ require 'command/option'
17
+ require 'command/env'
18
+ require 'command/result'
19
+ require 'command/decoratinghash'
20
+ require 'command/definition'
21
+ require 'command/parser'
22
+
23
+
24
+
25
+ module Command
26
+ def self.define(*args, &block)
27
+ @main = Definition.new(*args, &block)
28
+ @main
29
+ end
30
+
31
+ def self.with(argv, &block)
32
+ parser = Parser.new($command, argv)
33
+ parser.instance_eval(&block)
34
+ parser
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ #--
2
+ # Copyright 2009-2010 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Command
10
+ Argument = Struct.new(:name, :bare, :usage, :type, :description)
11
+ end
@@ -0,0 +1,31 @@
1
+ #--
2
+ # Copyright 2009-2010 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Command
10
+ class DecoratingHash < Hash
11
+ attr_accessor :target
12
+
13
+ def self.new(target)
14
+ obj = super() do |h,k| h.target && h.target[k] end
15
+ obj.target = target
16
+ obj
17
+ end
18
+
19
+ alias own_size size unless method_defined? :own_size
20
+ alias own_length own_size
21
+
22
+ def size
23
+ @target ? keys.size : super
24
+ end
25
+ alias length size
26
+
27
+ def keys
28
+ @target ? (super | @target.keys) : super
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,294 @@
1
+ #--
2
+ # Copyright 2010 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Command
10
+ class Definition
11
+ RequiredOptionArgument = /[A-Z][A-Z_]*/
12
+ OptionArgument = /\[#{RequiredOptionArgument}\]|#{RequiredOptionArgument}/
13
+ ShortOption = /\A-[A-Za-z](?: #{OptionArgument})?\z/
14
+ NegationSequence = /\[(?:no-|with-|without-)\]/
15
+ LongOption = /\A--
16
+ (?:
17
+ #{NegationSequence}?[A-Za-z][A-Za-z0-9_-]* |
18
+ [A-Za-z][A-Za-z0-9_-]*#{NegationSequence}[A-Za-z0-9][A-Za-z0-9_-]
19
+ )
20
+ (?:\x20#{OptionArgument})?\z
21
+ /x
22
+
23
+ def self.create_argument(*args)
24
+ name = Symbol === args.first && args.shift
25
+ usage = args.shift
26
+ bare = usage[/\w+/]
27
+ type = Symbol === args.first && args.shift
28
+ description = args.shift
29
+
30
+ Argument.new(name, bare, usage, type, description)
31
+ end
32
+
33
+ # valid arguments:
34
+ # name # --> copy from parent
35
+ # name, short[, long][, type][, description]
36
+ # name, long[, type][, description]
37
+ #
38
+ # short can be
39
+ # * '-?' (short option without argument)
40
+ # * '-? REQUIRED' (short option with required argument)
41
+ # * '-? [OPTIONAL]' (short option with optional
42
+ # where ? is any of A-Za-z
43
+ # examples:
44
+ # * '-a'
45
+ # * '-a [OPTIONAL_ARG]'
46
+ # * '-a REQUIRED_ARG'
47
+ #
48
+ # long can be
49
+ # * '--?' (short option without argument)
50
+ # * '--? REQUIRED' (short option with required argument)
51
+ # * '--? [OPTIONAL]' (short option with optional
52
+ # where ? is [A-Za-z0-9][A-Za-z0-9-_]*
53
+ # It may contain a negation sequence, which is one of '[no-]', '[with-]', '[without-]'
54
+ # examples:
55
+ # * '--colored'
56
+ # * '--[no-]colors'
57
+ # * '--port PORT'
58
+ # * '--foo [OPTIONAL_ARG]'
59
+ #
60
+ # Only one of short and long may have the argument declared.
61
+ # Usually you'll have the argument in long and only have it in short if
62
+ # there's no long at all.
63
+ #
64
+ # type can be
65
+ # * :Virtual
66
+ # * :Boolean
67
+ # * :String (default)
68
+ # * :Integer
69
+ # * :Float
70
+ # * :Octal
71
+ # * :Hex
72
+ # * :File - requires the provided path to exist and be a file
73
+ # * :Directory - requires the provided path to exist and be a directory
74
+ # * :Path
75
+ #
76
+ # Exceptions (incomplete):
77
+ # * ArgumentError with single argument if it doesn't identify an inheritable option
78
+ # * ArgumentError on invalid short definition
79
+ # * ArgumentError on invalid long definition
80
+ # * ArgumentError on option argument declaration in both, short and long definition
81
+ # * ArgumentError on invalid/unsupported type
82
+ def self.create_option(name, *args)
83
+ case args.first when nil, /\A-[^- ]/ then
84
+ short = args.shift
85
+ end
86
+ case args.first when nil, /\A--[^- ]/ then
87
+ long = args.shift
88
+ end
89
+ case args.first when nil, Symbol then
90
+ type = args.shift
91
+ end
92
+ declaration = short ? [short,long].compact.join(", ") : " #{long}"
93
+ description = args.shift
94
+
95
+ raise ArgumentError, "Too many arguments" unless args.empty?
96
+ raise ArgumentError, "Invalid short declaration: #{short.inspect}" unless (short.nil? || short =~ ShortOption)
97
+ raise ArgumentError, "Invalid long declaration: #{long.inspect}" unless (long.nil? || long =~ LongOption)
98
+ raise ArgumentError, "Argument declaration must only be in one of short and long" if (short && long && short =~ /\s/ && long =~ /\s/)
99
+
100
+ necessity = :none
101
+ extract_argument = nil
102
+ extract_argument = short if short =~ /\s/
103
+ extract_argument = long if long =~ /\s/
104
+ if extract_argument then
105
+ flag, *argument = extract_argument.split(/ /)
106
+ extract_argument.replace(flag) # long/short should only contain the flag, not the argument declaration as well
107
+ raise ArgumentError, "Multiple arguments for an option not yet supported" if argument.size > 1
108
+ if argument.empty?
109
+ necessity = :none
110
+ elsif argument.first =~ /\A\[.*\]\z/ then
111
+ necessity = :optional
112
+ else
113
+ necessity = :required
114
+ end
115
+ end
116
+
117
+ negated = nil
118
+ if long =~ NegationSequence then
119
+ negated = long.delete('[]')
120
+ long = long.gsub(NegationSequence, '')
121
+ end
122
+
123
+ Option.new(name, short, long, negated, necessity, type, declaration, description)
124
+ end
125
+
126
+
127
+ attr_reader :arguments
128
+ attr_reader :arguments_by_name
129
+ attr_reader :default_options
130
+ attr_reader :options_by_name
131
+ attr_reader :options_by_flag
132
+ attr_reader :commands_by_name
133
+ attr_reader :default_command
134
+ attr_reader :argument_position
135
+ attr_reader :env_by_variable
136
+ attr_reader :parent # parent= must update the DecoratingHashes
137
+
138
+ def initialize(parent=nil, default_command=nil, default_options={}, &block)
139
+ @default_command = default_command
140
+ @default_options = DecoratingHash.new(@parent && @parent.default_options).update(default_options)
141
+ @elements = []
142
+ @parent = parent
143
+ @arguments_by_name = DecoratingHash.new(@parent && @parent.arguments_by_name)
144
+ @options_by_name = DecoratingHash.new(@parent && @parent.options_by_name)
145
+ @options_by_flag = DecoratingHash.new(@parent && @parent.options_by_flag)
146
+ @commands_by_name = DecoratingHash.new(@parent && @parent.commands_by_name)
147
+ @env_by_variable = DecoratingHash.new(@parent && @parent.env_by_variable)
148
+ @argument_position = {}
149
+ @text = []
150
+ @placeholders = {}
151
+ @content_for = [@elements]
152
+ instance_eval(&block) if block
153
+ end
154
+
155
+ def [](command)
156
+ command ? @commands_by_name[command] : self
157
+ end
158
+
159
+ def content_for(placeholder)
160
+ @placeholders[placeholder] ||= []
161
+ @content_for << @placeholders[placeholder]
162
+ yield(self)
163
+ @content_for.pop
164
+ end
165
+
166
+ def usage_text(elements=@elements)
167
+ longest_arg_bare = elements.grep(Argument).max { |a,b|
168
+ a.bare.size <=> b.bare.size
169
+ }
170
+ longest_option = elements.grep(Option).max { |a,b|
171
+ a.declaration.size <=> b.declaration.size
172
+ }
173
+ longest_env_name = elements.grep(Env).max { |a,b|
174
+ a.variable.size <=> b.variable.size
175
+ }
176
+ longest_arg_bare = longest_arg_bare && longest_arg_bare.bare.size
177
+ longest_option = longest_option && longest_option.declaration.size
178
+ longest_env_name = longest_env_name && longest_env_name.variable.size
179
+ arguments = elements.grep(Argument)
180
+
181
+ elements.map { |e|
182
+ case e
183
+ when :usage
184
+ "Usage: #{File.basename($0)} #{arguments.map{|a|a.usage}.join(' ')}\n"
185
+ when Symbol # placeholder
186
+ usage_text(@placeholders[e])
187
+ when Option
188
+ sprintf " %*s%s\n",
189
+ -(longest_option+2),
190
+ e.declaration,
191
+ e.description
192
+ when Env
193
+ sprintf "* %*s%s\n",
194
+ -longest_env_name-2,
195
+ e.variable,
196
+ @options_by_name[e.name].description
197
+ when String
198
+ e+"\n"
199
+ when Argument
200
+ indent = "\n "+(" "*longest_arg_bare)
201
+ sprintf " %*s%s\n",
202
+ -(longest_arg_bare+3),
203
+ "#{e.bare}:",
204
+ e.description.to_s.gsub(/\n/, indent)
205
+ when Definition
206
+ else
207
+ "unimplemented(#{e.class})"
208
+ end
209
+ }.join('')
210
+ end
211
+
212
+ def usage
213
+ @content_for.last << :usage
214
+ end
215
+
216
+ def argument(*args)
217
+ unless @argument_position[args.first]
218
+ @argument_position[args.first] = @argument_position.size
219
+ end
220
+
221
+ if args.size == 1 then
222
+ argument = @arguments_by_name[args.first]
223
+ raise ArgumentError, "No argument with name #{args.first.inspect} in any parent found." unless argument
224
+ else
225
+ argument = self.class.create_argument(*args)
226
+ @arguments_by_name[argument.name] = argument
227
+ end
228
+ @content_for.last << argument
229
+
230
+ argument
231
+ end
232
+
233
+ def virtual_argument(*args)
234
+ if args.size == 1 then
235
+ argument = @arguments_by_name[args.first]
236
+ raise ArgumentError, "No argument with name #{args.first.inspect} in any parent found." unless argument
237
+ else
238
+ argument = self.class.create_argument(*args)
239
+ @arguments_by_name[argument.name] = argument
240
+ end
241
+
242
+ @content_for.last << argument
243
+ argument
244
+ end
245
+
246
+ def option(*args)
247
+ if args.size == 1 then
248
+ inherited_option = @options_by_name[args.first]
249
+ raise ArgumentError, "No inherited option #{args.first.inspect}" unless inherited_option
250
+ @content_for.last << inherited_option
251
+
252
+ inherited_option
253
+ else
254
+ option = self.class.create_option(*args)
255
+
256
+ @options_by_name[option.name] = option
257
+ @options_by_flag[option.short] = option
258
+ @options_by_flag[option.long] = option
259
+ @options_by_flag[option.negated] = option
260
+ @content_for.last << option
261
+
262
+ option
263
+ end
264
+ end
265
+ alias o option
266
+
267
+ def text(*args)
268
+ if args.size == 2 then
269
+ indent, text = *args
270
+ text = text.gsub(/^/, indent)
271
+ else
272
+ text = args.first
273
+ end
274
+ @text << text
275
+ @content_for.last << text
276
+ end
277
+
278
+ def placeholder(identifier)
279
+ @content_for.last << identifier
280
+ end
281
+
282
+ def env_option(name, variable)
283
+ env = Env.new(name, variable)
284
+ @env_by_variable[variable] = env
285
+ @content_for.last << env
286
+ end
287
+
288
+ def command(*args, &block)
289
+ definition = Definition.new(self, *args, &block)
290
+ @commands_by_name[args.first] = definition
291
+ @content_for.last << definition
292
+ end
293
+ end
294
+ end