baretest 0.2.4 → 0.4.0.pre1

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 (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