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,58 @@
1
+ #--
2
+ # Copyright 2009-2010 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module BareTest
10
+
11
+ # Extend Formatters that have custom options with this module to gain
12
+ # convenience methods to define the custom options.
13
+ # See Command's documentation for more information.
14
+ module Formatter
15
+
16
+ # Invoke Formatter#initialize_options
17
+ def self.extended(obj) # :nodoc:
18
+ obj.initialize_options
19
+ end
20
+
21
+ # Provides access to the command/options/arguments related information.
22
+ attr_reader :command
23
+
24
+ # Initialize some instance variables needed for the DSL.
25
+ def initialize_options # :nodoc:
26
+ @command = {
27
+ :option_defaults => {},
28
+ :elements => [],
29
+ }
30
+ end
31
+
32
+ # Define default values for options.
33
+ # Example:
34
+ # option_defaults :colors => false,
35
+ # :indent => 3
36
+ def option_defaults(defaults={})
37
+ @command[:option_defaults].update(defaults)
38
+ end
39
+
40
+ # Inject a piece of text into the helptext.
41
+ def text(*args)
42
+ @command[:elements] << [:text, args]
43
+ end
44
+
45
+ # Use an env-variable and map it to an option.
46
+ # Example:
47
+ # env_option :indent, "INDENT"
48
+ def env_option(*args)
49
+ @command[:elements] << [:env_option, args]
50
+ end
51
+
52
+ # Define a formatter-specific option.
53
+ # See Command::Definition#option
54
+ def option(*args)
55
+ @command[:elements] << [:option, args]
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,24 @@
1
+ #--
2
+ # Copyright 2009-2010 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module BareTest
10
+
11
+ # Raised by BareTest.process_selectors if one or more invalid selectors are
12
+ # passed in.
13
+ class InvalidSelectors < StandardError
14
+
15
+ # The selectors that are invalid
16
+ attr_reader :selectors
17
+
18
+ # Generates a standard message
19
+ def initialize(selectors) # :nodoc:
20
+ @selectors = selectors
21
+ super("Invalid selectors: #{selectors.join(', ')}")
22
+ end
23
+ end
24
+ end
@@ -24,6 +24,17 @@ module BareTest
24
24
  # See BareTest::IRBMode::AssertionContext for some methods IRBMode adds to Assertion for
25
25
  # use within the irb session.
26
26
  module IRBMode # :nodoc:
27
+ RemoveGlobals = %w[
28
+ $! $" $$ $& $' $* $+ $, $-0 $-F $-I $-K $-a $-d $-i $-l $-p $-v $-w $. $/
29
+ $0 $: $; $< $= $> $? $@ $FS $NR $OFS $ORS $PID $RS $\\ $_ $` $~
30
+ $ARGV $CHILD_STATUS $DEBUG $DEFAULT_INPUT $DEFAULT_OUTPUT $ERROR_INFO
31
+ $ERROR_POSITION $FIELD_SEPARATOR $FILENAME $IGNORECASE $INPUT_LINE_NUMBER
32
+ $INPUT_RECORD_SEPARATOR $KCODE $LAST_MATCH_INFO $LAST_PAREN_MATCH
33
+ $LAST_READ_LINE $LOADED_FEATURES $LOAD_PATH $MATCH $OUTPUT_FIELD_SEPARATOR
34
+ $OUTPUT_RECORD_SEPARATOR $POSTMATCH $PREMATCH $PROCESS_ID $PROGRAM_NAME
35
+ $SAFE $VERBOSE $stderr $stdin $stdout
36
+ ]
37
+
27
38
  @irb_setup = false
28
39
 
29
40
  def self.irb_setup! # :nodoc:
@@ -38,35 +49,52 @@ module BareTest
38
49
  # Adds several methods over plain Assertion.
39
50
  module IRBContext
40
51
 
41
- attr_accessor :__original__
52
+ # Provides access the assertions' original status
53
+ attr_accessor :__status__
42
54
 
43
55
  # Prints a list of available helper methods
44
56
  def help
45
57
  puts "Available methods:",
46
- "s! - the original assertions' status",
47
- "e! - prints the error message and full backtrace",
48
- "em! - prints the error message",
49
- "bt! - prints the full backtrace",
50
- "iv! - lists all available instance variables",
51
- "cv! - lists all available class variables",
52
- "gv! - lists all available global variables",
53
- "file - the file this assertion was defined in",
54
- "line - the line number in the file where this assertion's definition starts",
55
- "nesting - a >-separated list of suite descriptions this assertion is nested in",
56
- "description - this assertion's description",
57
- "code - the code of this assertion",
58
+ "s! - the assertions' original status",
59
+ "sc! - the assertions' original status code",
60
+ "e! - prints the error message and full backtrace",
61
+ "em! - prints the error message",
62
+ "bt! - prints the full backtrace",
63
+ "lv! - lists all available local variables",
64
+ "iv! - lists all available instance variables",
65
+ "cv! - lists all available class variables",
66
+ "gv! - lists all available global variables, per default dropping rubys",
67
+ " standard globals (use gv!(false) to avoid that)",
68
+ "file! - the file this assertion was defined in",
69
+ "line! - the line number in the file where this assertion's definition",
70
+ " starts",
71
+ "nesting! - a >-separated list of suite descriptions this assertion is nested",
72
+ "description! - this assertion's description",
73
+ "code! - the code of this assertion",
58
74
  #"restart! - Restart this irb session, resetting everything",
59
- "irb_help - irb's original help",
60
- "help - this text you're reading right now"
75
+ "irb_help - irb's original help",
76
+ "q - Quit - alias to irb's exit",
77
+ "help - this text you're reading right now"
61
78
  end
79
+ alias help! help
62
80
 
63
81
  def to_s # :nodoc:
64
82
  "Context"
65
83
  end
66
84
 
85
+ # Quit - an alias to irb's exit
86
+ def q
87
+ exit
88
+ end
89
+
67
90
  # Returns the original assertion's status
68
91
  def s!
69
- p @__original__.status
92
+ @__status__
93
+ end
94
+
95
+ # Returns the original assertion's status code
96
+ def sc!
97
+ @__status__.status
70
98
  end
71
99
 
72
100
  # Prints the original assertion's error message and backtrace
@@ -77,20 +105,20 @@ module BareTest
77
105
 
78
106
  # Prints the original assertion's error message
79
107
  def em!
80
- if @__original__.exception then
81
- puts @__original__.exception.message
82
- elsif @__original__.reason
83
- puts @__original__.reason
108
+ if @__status__.exception then
109
+ puts @__status__.exception.message
110
+ elsif @__status__.failure_reason
111
+ puts @__status__.failure_reason
84
112
  else
85
- puts "No exception occurred, therefore no error message is available"
113
+ puts "No exception or failure reason available"
86
114
  end
87
115
  end
88
116
 
89
117
  # Prints the original assertion's backtrace
90
118
  def bt!(size=nil)
91
- if @__original__.exception then
119
+ if @__status__.exception then
92
120
  size ||= caller.size+3
93
- puts @__original__.exception.backtrace[0..-size]
121
+ puts @__status__.exception.backtrace[0..-size]
94
122
  else
95
123
  puts "No exception occurred, therefore no backtrace is available"
96
124
  end
@@ -107,31 +135,47 @@ module BareTest
107
135
  end
108
136
 
109
137
  # Returns an array of all global variable names
110
- def gv!
111
- puts *global_variables.sort
138
+ def gv!(remove_standard=true)
139
+ puts *(global_variables-(remove_standard ? IRBMode::RemoveGlobals : [])).sort
140
+ end
141
+
142
+ # Returns the original assertion's file
143
+ def file!
144
+ puts @__assertion__.file
145
+ end
146
+
147
+ # Returns the original assertion's line
148
+ def line!
149
+ puts @__assertion__.line
150
+ end
151
+
152
+ # Returns the original assertion's line
153
+ def open!
154
+ `bbedit '#{@__assertion__.file}:#{@__assertion__.line}'`
112
155
  end
113
156
 
114
157
  # Prints a string of the original assertion's nesting within suites
115
- def description
116
- puts @__original__.description
158
+ def description!
159
+ puts @__assertion__.description
117
160
  end
118
161
 
119
162
  # Prints a string of the original assertion's nesting within suites
120
- def nesting
121
- puts @__original__.suite.ancestors[0..-2].reverse.map { |s| s.description }.join(' > ')
163
+ def nesting!
164
+ puts @__assertion__.suite.ancestors[0..-2].reverse.map { |s| s.description }.join(' > ')
122
165
  end
123
166
 
124
167
  # Prints the code of the assertion
125
168
  # Be aware that this relies on your code being properly indented.
126
169
  def code!
127
- if code = @__original__.code then
128
- puts(insert_line_numbers(code, @__original__.line-1))
170
+ if code = @__assertion__.code then
171
+ puts(insert_line_numbers(code, @__assertion__.line-1))
129
172
  else
130
173
  puts "Code could not be extracted"
131
174
  end
132
175
  end
133
176
 
134
- def insert_line_numbers(code, start_line=1)
177
+ # Prepend the line number in front of ever line
178
+ def insert_line_numbers(code, start_line=1) # :nodoc:
135
179
  digits = Math.log10(start_line+code.count("\n")).floor+1
136
180
  current_line = start_line-1
137
181
  code.gsub(/^/) { sprintf ' %0*d ', digits, current_line+=1 }
@@ -152,48 +196,50 @@ module BareTest
152
196
  # Formatter callback.
153
197
  # Invoked once for every assertion.
154
198
  # Gets the assertion to run as single argument.
155
- def run_test(assertion, setup)
199
+ def run_test(assertion, with_setup)
156
200
  rv = super
157
201
  # drop into irb if assertion failed
158
202
  case rv.status
159
203
  when :failure
160
- start_irb_failure_mode(assertion)
161
- irb_mode_for_assertion(assertion)
162
- stop_irb_mode(assertion)
204
+ start_irb_failure_mode(assertion, rv)
205
+ irb_mode_for_assertion(assertion, rv, with_setup)
206
+ stop_irb_mode
163
207
  when :error
164
- start_irb_error_mode(assertion)
165
- irb_mode_for_assertion(assertion)
166
- stop_irb_mode(assertion)
208
+ start_irb_error_mode(assertion, rv)
209
+ irb_mode_for_assertion(assertion, rv, with_setup)
210
+ stop_irb_mode
211
+ # with other states, irb-mode is not started
167
212
  end
168
213
 
169
214
  rv
170
215
  end
171
216
 
172
217
  # Invoked when we have to drop into irb mode due to a failure
173
- def start_irb_failure_mode(assertion) # :nodoc:
218
+ def start_irb_failure_mode(assertion, status) # :nodoc:
174
219
  ancestry = assertion.suite.ancestors.reverse.map { |suite| suite.description }
175
220
 
176
221
  puts
177
- puts "#{assertion.status.to_s.capitalize} in: #{ancestry[1..-1].join(' > ')}"
222
+ puts "#{status.status.to_s.capitalize} in: #{ancestry[1..-1].join(' > ')}"
178
223
  puts "Description: #{assertion.description}"
179
224
  if file = assertion.file then
180
- code = irb_code_reindented(file, assertion.line-1,20)
225
+ code = irb_code_reindented(file, assertion.line-1,25)
181
226
  match = code.match(/\n^ [^ ]/)
182
- code[-(match.post_match.size-3)..-1] = ""
227
+ code[-(match.post_match.size-3)..-1] = "" if match
228
+ code << "\n... (only showing first 25 lines)" unless match
183
229
  assertion.code = code
184
230
  puts "Code (#{file}):", insert_line_numbers(code, assertion.line-1)
185
231
  end
186
232
  end
187
233
 
188
234
  # Invoked when we have to drop into irb mode due to an error
189
- def start_irb_error_mode(assertion) # :nodoc:
235
+ def start_irb_error_mode(assertion, status) # :nodoc:
190
236
  ancestry = assertion.suite.ancestors.reverse.map { |suite| suite.description }
191
237
 
192
238
  puts
193
- puts "#{assertion.status.to_s.capitalize} in: #{ancestry[1..-1].join(' > ')}"
239
+ puts "#{status.status.to_s.capitalize} in: #{ancestry[1..-1].join(' > ')}"
194
240
  puts "Description: #{assertion.description}"
195
- puts "Exception: #{assertion.exception} in file #{assertion.exception.backtrace.first}"
196
- if assertion.file && match = assertion.exception.backtrace.first.match(/^([^:]+):(\d+)(?:$|:in .*)/) then
241
+ puts "Exception: #{status.exception} in file #{status.exception.backtrace.first}"
242
+ if assertion.file && match = status.exception.backtrace.first.match(/^([^:]+):(\d+)(?:$|:in .*)/) then
197
243
  file, line = match.captures
198
244
  file = File.expand_path(file)
199
245
  if assertion.file == file then
@@ -225,13 +271,11 @@ module BareTest
225
271
  # Drop into an irb shell in the context of the assertion passed as an argument.
226
272
  # Uses Assertion#clean_copy(AssertionContext) to create the context.
227
273
  # Adds the code into irb's history.
228
- def irb_mode_for_assertion(original_assertion) # :nodoc:
229
- assertion = original_assertion.clone
230
- assertion.reset
231
- irb_context = assertion.context
274
+ def irb_mode_for_assertion(assertion, status, with_setup) # :nodoc:
275
+ irb_context = ::BareTest::Assertion::Context.new(assertion)
232
276
  irb_context.extend IRBContext
233
- irb_context.__original__ = original_assertion
234
- assertion.setup
277
+ irb_context.__status__ = status
278
+ assertion.execute_phase(:setup, irb_context, with_setup.map { |s| s.block })
235
279
 
236
280
  $stdout = StringIO.new # HAX - silencing 'irb: warn: can't alias help from irb_help.' - find a better way
237
281
  irb = IRB::Irb.new(IRB::WorkSpace.new(irb_context))
@@ -243,21 +287,19 @@ module BareTest
243
287
 
244
288
  trap("SIGINT") do irb.signal_handle end
245
289
 
246
- if code = original_assertion.code then
290
+ if code = assertion.code then
247
291
  #irb_context.code = code
248
292
  Readline::HISTORY.push(*code.split("\n")[1..-2])
249
293
  end
250
294
 
251
295
  catch(:IRB_EXIT) do irb.eval_input end
252
296
 
253
- assertion.teardown
297
+ assertion.execute_phase(:teardown, irb_context, assertion.suite.ancestry_teardown)
254
298
  end
255
299
 
256
300
  # Invoked when we leave the irb session
257
- def stop_irb_mode(assertion) # :nodoc:
301
+ def stop_irb_mode # :nodoc:
258
302
  puts
259
- super
260
- rescue NoMethodError # HAX, not happy about that. necessary due to order of extend
261
303
  end
262
304
  end
263
305
  end
@@ -0,0 +1,94 @@
1
+ #--
2
+ # Copyright 2009-2010 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'baretest/uid'
10
+ require 'yaml'
11
+ require 'fileutils'
12
+
13
+
14
+
15
+ module BareTest
16
+
17
+ class Persistence
18
+
19
+ # The default storage path base (~/.baretest)
20
+ def self.storage_path
21
+ File.expand_path('~/.baretest')
22
+ end
23
+
24
+ # BareTest uses a file of the form '.baretest_id_*' (where * is a 32 digits
25
+ # long hex) to uniquely identify a project. This ID is then used to
26
+ # associate stored data with the project.
27
+ def self.determine_project_id(project_dir)
28
+ found = Dir.glob("#{project_dir}/.baretest_id_*") { |path|
29
+ break $1 if File.file?(path) && path =~ /id_([A-Fa-f0-9]{32})$/
30
+ }
31
+ unless found then
32
+ found = UID.hex_uid
33
+ File.open(".baretest_id_#{found}", "w") {} # no File::touch, evil
34
+ end
35
+
36
+ found
37
+ end
38
+
39
+ # The directory this Persistence instance stores its data
40
+ attr_reader :storage_dir
41
+
42
+ # The directory of the project this Persistence instance is attached to
43
+ attr_reader :project_dir
44
+
45
+ # The id of the project this Persistence instance is attached to
46
+ attr_reader :project_id
47
+
48
+ # Arguments:
49
+ # project_dir:: The directory of the project
50
+ # storage_dir:: The directory where this Persistence instance should store
51
+ # its data
52
+ def initialize(project_dir=nil, storage_dir=nil)
53
+ @storage_dir = File.expand_path(storage_dir || self.class.storage_path)
54
+ @project_dir = File.expand_path(project_dir || ".")
55
+ @project_id = self.class.determine_project_id(@project_dir)
56
+ end
57
+
58
+ # Stores data to a file.
59
+ #
60
+ # === Arguments
61
+ # filename:: A relative path. Directories are created on the fly if
62
+ # necessary. Must not be an absolute path. The path is relative
63
+ # to Persistence#storage_dir
64
+ # data:: The data to store. Anything that can be serialized by YAML.
65
+ # This excludes IOs and Procs.
66
+ def store(filename, data)
67
+ raise "Invalid filename: #{filename}" unless filename =~ %r{\A[A-Za-z0-9_-][A-Za-z0-9_-]*\z}
68
+ dir = "#{@storage_dir}/#{@project_id}"
69
+ FileUtils.mkdir_p(dir)
70
+ File.open("#{dir}/#{filename}.yaml", "w") do |fh|
71
+ fh.write(data.to_yaml)
72
+ end
73
+ end
74
+
75
+ # Reads and deserializes the data in a given filename.
76
+ # filename:: A relative path. Directories are created on the fly if
77
+ # necessary. Must not be an absolute path. The path is relative
78
+ # to Persistence#storage_dir
79
+ # default:: The value to return in case the file does not exist.
80
+ # Alternatively you can pass a block that calculates the default.
81
+ def read(filename, default=nil)
82
+ raise "Invalid filename: #{filename}" if filename =~ %r{\A\.\./|/\.\./\z}
83
+ path = "#{@storage_dir}/#{@project_id}/#{filename}.yaml"
84
+
85
+ if File.exist?(path)
86
+ YAML.load_file(path)
87
+ elsif block_given?
88
+ yield
89
+ else
90
+ default
91
+ end
92
+ end
93
+ end
94
+ end