xspec 0.1.0 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5dba5d7629f6d87591f9abe883290f50372dc64
4
- data.tar.gz: ead15780cdec15272984c1580051e7d7f2eadded
3
+ metadata.gz: b3831933eef297e842aeb3d87c7fd269f15e1377
4
+ data.tar.gz: 72891d3ec2b7a439cbedc809c98393591e673d46
5
5
  SHA512:
6
- metadata.gz: b37433a77450176d0cf1ebeae40d5ad24ef28ce8095887126edfd5dfd0040fdcfbf418a1726862a60eb7f275f347d19a371720331b5376145e256bff08374ef5
7
- data.tar.gz: 963b1b472454aeaeb03aa956a1e38ff3a9b1a9bb4bf78fcd8c9eb06aae26a31d4591ed8e7b9a4fa1c11f43a7413a2d37112d2bd140c182145fcadc8745922b91
6
+ metadata.gz: 3825fe02479cf05849b66351a846adb91d0af0f895b1b5a1e12e555626fca1724066d759466f8b41e7149691b5e290288098e50dff3877beb1f220549d55f9cc
7
+ data.tar.gz: e4e573a3aca9af04bfa5f9b4db9af146c29f56d1112c3ed410b78e5d5ab785db6c53abc59a74be333dc1fc92d29b1c2bfc20d147882ec194dfb9746fc6650b50
data/README.md CHANGED
@@ -21,7 +21,7 @@ extend XSpec.dsl # Use defaults
21
21
  describe 'my application' do
22
22
  it 'does math' do
23
23
  double = instance_double('Calculator')
24
- expect(double).add(1, 1) { 2 }
24
+ stub(double).add(1, 1) { 2 }
25
25
 
26
26
  assert_equal 2, double.add(1, 1)
27
27
  end
@@ -43,9 +43,9 @@ see the colors in this README, but trust me they are quite lovely.
43
43
  > xspec example.rb
44
44
 
45
45
  my application
46
- 0.000s does math
47
- 0.011s is slow sometimes
48
- 0.000s fails - FAILED
46
+ 0.000s 3l1 does math
47
+ 0.011s f0j is slow sometimes
48
+ 0.000s juj fails - FAILED
49
49
 
50
50
  Timings:
51
51
  0.001 #################### 2
@@ -54,11 +54,25 @@ my application
54
54
  0.1 ########## 1
55
55
 
56
56
 
57
- my application fails:
57
+ juj - my application fails
58
58
  "fruit" not present in: "punch"
59
59
 
60
- example.rb:18:in `block (2 levels) in <top (required)>'
61
- bin/xspec:19:in `<main>'
60
+ test.rb:17:in `block (2 levels) in <top (required)>'
61
+ bin/xspec:44:in `<main>'
62
+ ```
63
+
64
+ The three-character tag next to each test is its short id. You can use it to
65
+ run a single test:
66
+
67
+ ```
68
+ > xspec -f 3l1
69
+
70
+ my application
71
+ 0.000s 3l1 does math
72
+
73
+ Timings:
74
+ 0.001 #################### 1
75
+
62
76
  ```
63
77
 
64
78
  ### Customization
@@ -73,11 +87,11 @@ expectations. You could do that:
73
87
  require 'xspec'
74
88
 
75
89
  extend XSpec.dsl(
76
- assertion_context: XSpec::AssertionContext.stack {
77
- include XSpec::AssertionContext::RSpecExpectations
90
+ evaluator_context: XSpec::Evaluator.stack {
91
+ include XSpec::Evaluator::RSpecExpectations
78
92
  },
79
- notifier: XSpec::Notifiers::Character.new +
80
- XSpec::Notifiers::FailuresAtEnd.new
93
+ notifier: XSpec::Notifier::Character.new +
94
+ XSpec::Notifier::FailuresAtEnd.new
81
95
  )
82
96
 
83
97
  describe '...' do
@@ -86,15 +100,15 @@ end
86
100
  ```
87
101
 
88
102
  Of course, you can make your own extension classes as well. For details, see
89
- the "Configuration" section of the documentation.
103
+ the API documentation.
90
104
 
91
105
  Documentation
92
106
  -------------
93
107
 
94
108
  There are two major sources of documentation:
95
109
 
96
- * [Main API documentation.](https://xaviershay.github.io/xspec/api.html)
97
- * [Literate source code.](https://xaviershay.github.io/xspec/)
110
+ * [Main API documentation.](https://xaviershay.github.io/xspec/docs/api.html)
111
+ * [Literate source code.](https://xaviershay.github.io/xspec/docs/xspec.html)
98
112
 
99
113
  It is expected that regular users of XSpec will read both at least once. There
100
114
  isn't much to them, and they will give you a useful mental model of how XSpec
data/bin/xspec CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'optparse'
4
+
3
5
  $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
4
6
 
5
7
  require 'xspec'
@@ -7,6 +9,27 @@ require 'xspec'
7
9
  $LOAD_PATH.unshift "spec"
8
10
  $LOAD_PATH.unshift "lib"
9
11
 
12
+ filter = nil
13
+ short_ids = []
14
+
15
+ parser = OptionParser.new
16
+ parser.banner = "Usage: xspec [options] [files]"
17
+ parser.separator ""
18
+ parser.on('-e FILTER', "Run specs with full name including FILTER.") do |f|
19
+ filter = Regexp.new(f)
20
+ end
21
+ parser.on('-f ID', "Run spec with short id ID. Use multiple times to specify more than one id.") do |f|
22
+ short_ids << f
23
+ end
24
+ parser.on("-h", "--help", "Show this message.") do
25
+ $stderr.puts parser
26
+ exit
27
+ end
28
+ parser.separator ""
29
+
30
+ parser.parse!
31
+
32
+
10
33
  files = if ARGV.any? {|x| x.length > 0 }
11
34
  ARGV
12
35
  else
@@ -18,7 +41,25 @@ files.each do |f|
18
41
  end
19
42
 
20
43
  if respond_to?(:run!)
21
- exit 1 unless run!
44
+ result = run! {|config|
45
+ config.update(scheduler: XSpec::Scheduler::Filter.new(
46
+ scheduler: config.fetch(:scheduler),
47
+ filter: -> uow {
48
+ inc = true
49
+
50
+ if short_ids.any?
51
+ inc &&= short_ids.include?(config.fetch(:short_id).(uow))
52
+ end
53
+
54
+ if filter
55
+ inc &&= uow.full_name =~ filter
56
+ end
57
+
58
+ inc
59
+ }
60
+ ))
61
+ }
62
+ exit 1 unless result
22
63
  else
23
64
  $stderr.puts "This script can only be used when XSpec.dsl is mixed in to " +
24
65
  "global scope."
@@ -12,8 +12,8 @@ module XSpec
12
12
  # This enables different options to be specified per DSL, which is at the
13
13
  # heart of XSpec's modularity. It is easy to change every component to your
14
14
  # liking.
15
- def dsl(options = {})
16
- options = XSpec.add_defaults(options)
15
+ def dsl(config = {})
16
+ config = XSpec.add_defaults(config)
17
17
 
18
18
  Module.new do
19
19
  # Each DSL provides a standard set of methods provided by the [DSL
@@ -24,22 +24,26 @@ module XSpec
24
24
  # described in detail in the
25
25
  # [`data_structures.rb`](data_structures.html).
26
26
  def __xspec_context
27
- evaluator = __xspec_opts.fetch(:evaluator)
27
+ evaluator = __xspec_config.fetch(:evaluator)
28
28
  @__xspec_context ||= XSpec::Context.root(evaluator)
29
29
  end
30
30
 
31
- # Some meta-magic is needed to enable the options from local scope above
31
+ # Some meta-magic is needed to enable the config from local scope above
32
32
  # to be available inside the module.
33
- define_method(:__xspec_opts) { options }
33
+ define_method(:__xspec_config) { config }
34
34
 
35
35
  # `run!` is where the magic happens. Typically called at the end of a
36
36
  # file (or by `autorun!`), this method takes all the data that was
37
37
  # accumulated by the DSL methods above and runs it through the scheduler.
38
- def run!
39
- notifier = __xspec_opts.fetch(:notifier)
40
- scheduler = __xspec_opts.fetch(:scheduler)
38
+ #
39
+ # It takes an optional block that can be used to override any options
40
+ # set in the initial `XSpec.dsl` call.
41
+ def run!(&overrides)
42
+ overrides ||= -> x { x }
43
+ config = overrides.(__xspec_config)
44
+ scheduler = config.fetch(:scheduler)
41
45
 
42
- scheduler.run(__xspec_context, notifier)
46
+ scheduler.run(__xspec_context, config)
43
47
  end
44
48
 
45
49
  # It is often convenient to trigger a run after all files have been
@@ -2,7 +2,7 @@
2
2
 
3
3
  # XSpec data structures are very dumb. They:
4
4
  #
5
- # * Only contain iteration and creation logic.
5
+ # * Only contain iteration, property, and creation logic.
6
6
  # * Do not store recursive references ("everything flows downhill").
7
7
  module XSpec
8
8
  # A unit of work, usually created by the `it` DSL method, is a labeled,
@@ -85,7 +85,7 @@ module XSpec
85
85
  # A shared context is a floating context that isn't part of any context
86
86
  # heirachy, so its units of work will not be visible to the root node. It
87
87
  # can be brought into any point in the heirachy using `copy_into_tree`
88
- # (aliased as `it_behaves_like_a` in the DSL), and this can be done
88
+ # (aliased as `include_context` in the DSL), and this can be done
89
89
  # multiple times, which allows definitions to be reused.
90
90
  #
91
91
  # This is leaky abstraction, since only units of work are copied from
@@ -156,6 +156,10 @@ module XSpec
156
156
  def block; unit_of_work.block; end
157
157
  def name; unit_of_work.name; end
158
158
 
159
+ def full_name
160
+ (parents + [self]).map(&:name).compact.join(' ')
161
+ end
162
+
159
163
  def immediate_parent
160
164
  parents.last
161
165
  end
@@ -170,6 +174,7 @@ module XSpec
170
174
  ExecutedUnitOfWork = Struct.new(:nested_unit_of_work, :errors, :duration) do
171
175
  def name; nested_unit_of_work.name end
172
176
  def parents; nested_unit_of_work.parents end
177
+ def full_name; nested_unit_of_work.full_name end
173
178
  end
174
179
 
175
180
  # A test failure will be reported as a `Failure`, which includes contextual
@@ -7,7 +7,21 @@ require 'xspec/schedulers'
7
7
  require 'xspec/evaluators'
8
8
  require 'xspec/notifiers'
9
9
 
10
+ require 'digest/sha1'
11
+
10
12
  module XSpec
13
+ def default_short_id(uow)
14
+ length = 3
15
+ base = 32
16
+ digest = Digest::SHA1.hexdigest(uow.full_name).hex
17
+ bottom = base ** (length-1)
18
+ top = base ** length
19
+ shifted = digest % (top - bottom) + bottom
20
+
21
+ shifted.to_s(base)
22
+ end
23
+ module_function :default_short_id
24
+
11
25
  def add_defaults(options = {})
12
26
  # A notifier makes it possible to observe the state of the system, be that
13
27
  # progress or details of failing tests.
@@ -20,6 +34,9 @@ module XSpec
20
34
  # used.
21
35
  options[:evaluator] ||= Evaluator::DEFAULT
22
36
 
37
+ options[:short_id] ||= XSpec.method(:default_short_id)
38
+
39
+
23
40
  # An scheduler is responsible for scheduling units of work and handing them
24
41
  # off to the assertion context. Any logic regarding threads, remote
25
42
  # execution or the like belongs in a scheduler.
@@ -24,7 +24,7 @@ module XSpec
24
24
  __xspec_context.create_shared_context(*args, &block)
25
25
  end
26
26
 
27
- def it_behaves_like_a(context)
27
+ def include_context(context)
28
28
  __xspec_context.copy_into_tree(context)
29
29
  end
30
30
  end
@@ -33,28 +33,24 @@ module XSpec
33
33
  module Top
34
34
  def call(unit_of_work)
35
35
  super
36
+ rescue EvaluateFailed => e
37
+ [Failure.new(unit_of_work, e.message, e.backtrace)]
36
38
  rescue => e
37
39
  [CodeException.new(unit_of_work, e.message, e.backtrace)]
38
40
  end
39
41
  end
40
42
 
43
+ # As long as the `Top` evaluator is used, evaluators can raise
44
+ # `EvaluateFailed` to indicate a failure separate from a normal code
45
+ # exception.
46
+ EvaluateFailed = Class.new(RuntimeError)
47
+
41
48
  # ### Simple Assertions
42
49
  #
43
50
  # This simple evaluator provides very straight-forward assertion methods.
44
51
  module Simple
45
- class AssertionFailed < RuntimeError
46
- attr_reader :message, :backtrace
47
-
48
- def initialize(message, backtrace)
49
- @message = message
50
- @backtrace = backtrace
51
- end
52
- end
53
-
54
52
  def call(unit_of_work)
55
53
  super
56
- rescue AssertionFailed => e
57
- [Failure.new(unit_of_work, e.message, e.backtrace)]
58
54
  end
59
55
 
60
56
  def assert(proposition, message=nil)
@@ -90,7 +86,7 @@ EOS
90
86
  private
91
87
 
92
88
  def _raise(message)
93
- raise AssertionFailed.new(message, caller)
89
+ raise EvaluateFailed, message
94
90
  end
95
91
  end
96
92
 
@@ -99,12 +95,8 @@ EOS
99
95
  # The doubles module provides test doubles that can be used in-place of
100
96
  # real objects.
101
97
  module Doubles
102
- DoubleFailure = Class.new(RuntimeError)
103
-
104
98
  def call(unit_of_work)
105
99
  super
106
- rescue DoubleFailure => e
107
- [Failure.new(unit_of_work, e.message, e.backtrace)]
108
100
  end
109
101
 
110
102
  # It can be configured with the following options:
@@ -227,7 +219,7 @@ EOS
227
219
  @received.delete_at(i)
228
220
  else
229
221
  name, rest = *args
230
- ::Kernel.raise DoubleFailure,
222
+ ::Kernel.raise EvaluateFailed,
231
223
  "Did not receive: %s(%s)\nDid receive:%s\n" % [
232
224
  name,
233
225
  [*rest].map(&:inspect).join(", "),
@@ -269,7 +261,7 @@ EOS
269
261
  name, rest = *args
270
262
 
271
263
  unless @klass.respond_to?(name)
272
- raise DoubleFailure,
264
+ raise EvaluateFailed,
273
265
  "#{@klass}.#{name} is unimplemented or not public"
274
266
  end
275
267
  end
@@ -280,7 +272,7 @@ EOS
280
272
  name, rest = *args
281
273
 
282
274
  unless @klass.public_instance_methods.include?(name)
283
- raise DoubleFailure,
275
+ raise EvaluateFailed,
284
276
  "#{@klass}##{name} is unimplemented or not public"
285
277
  end
286
278
  end
@@ -293,7 +285,7 @@ EOS
293
285
  ref = if self.class.const_defined?(klass)
294
286
  type.new(self.class.const_get(klass))
295
287
  else
296
- raise DoubleFailure, "#{klass} is not a valid class name"
288
+ raise EvaluateFailed, "#{klass} is not a valid class name"
297
289
  end
298
290
 
299
291
  super
@@ -5,6 +5,17 @@
5
5
  # summarizing the run when it finished.
6
6
  module XSpec
7
7
  module Notifier
8
+ # A formatter must implement at least four methods. `run_start` and
9
+ # `run_finish` are called at the beginning and end of the full spec run
10
+ # respectively, while `evaluate_start` and `evaluate_finish` are called for
11
+ # each test. See [API docs](api.html#notifiers) for more information.
12
+ module EmptyFormatter
13
+ def run_start(*_); end
14
+ def evaluate_start(*_); end
15
+ def evaluate_finish(*_); end
16
+ def run_finish(*_); true; end
17
+ end
18
+
8
19
  # Many notifiers play nice with others, and can be composed together in a
9
20
  # way that each notifier will have its callback run in turn.
10
21
  module Composable
@@ -20,8 +31,8 @@ module XSpec
20
31
  @notifiers = notifiers
21
32
  end
22
33
 
23
- def run_start
24
- notifiers.each(&:run_start)
34
+ def run_start(*args)
35
+ notifiers.each {|x| x.run_start(*args) }
25
36
  end
26
37
 
27
38
  def evaluate_start(*args)
@@ -44,16 +55,13 @@ module XSpec
44
55
  # Outputs a single character for each executed unit of work representing
45
56
  # the result.
46
57
  class Character
58
+ include EmptyFormatter
47
59
  include Composable
48
60
 
49
61
  def initialize(out = $stdout)
50
62
  @out = out
51
63
  end
52
64
 
53
- def run_start; end
54
-
55
- def evaluate_start(*_); end
56
-
57
65
  def evaluate_finish(result)
58
66
  @out.print label_for_failure(result.errors[0])
59
67
  @failed ||= result.errors.any?
@@ -73,11 +81,11 @@ module XSpec
73
81
  else '.'
74
82
  end
75
83
  end
76
-
77
84
  end
78
85
 
79
86
  # Renders a histogram of test durations after the entire run is complete.
80
87
  class TimingsAtEnd
88
+ include EmptyFormatter
81
89
  include Composable
82
90
 
83
91
  DEFAULT_SPLITS = [0.001, 0.005, 0.01, 0.1, 1.0, Float::INFINITY]
@@ -92,11 +100,6 @@ module XSpec
92
100
  @out = out
93
101
  end
94
102
 
95
- def run_start(*_); end
96
-
97
- def evaluate_start(_)
98
- end
99
-
100
103
  def evaluate_finish(result)
101
104
  timings[result] = result.duration
102
105
  end
@@ -148,19 +151,29 @@ module XSpec
148
151
  end
149
152
  end
150
153
 
154
+ # Provides convenience methods for working with short ids.
155
+ module ShortIdSupport
156
+ def run_start(config)
157
+ super
158
+ @short_id = config.fetch(:short_id)
159
+ end
160
+
161
+ def short_id_for(x)
162
+ @short_id.(x)
163
+ end
164
+ end
165
+
151
166
  # Outputs error messages and backtraces after the entire run is complete.
152
167
  class FailuresAtEnd
168
+ include EmptyFormatter
153
169
  include Composable
170
+ include ShortIdSupport
154
171
 
155
172
  def initialize(out = $stdout)
156
173
  @errors = []
157
174
  @out = out
158
175
  end
159
176
 
160
- def run_start; end
161
-
162
- def evaluate_start(*_); end
163
-
164
177
  def evaluate_finish(result)
165
178
  self.errors += result.errors
166
179
  end
@@ -170,7 +183,11 @@ module XSpec
170
183
 
171
184
  out.puts
172
185
  errors.each do |error|
173
- out.puts "%s:\n%s\n\n" % [full_name(error.unit_of_work), error.message.lines.map {|x| " #{x}"}.join("")]
186
+ out.puts "%s - %s\n%s\n\n" % [
187
+ short_id_for(error.unit_of_work),
188
+ error.unit_of_work.full_name,
189
+ error.message.lines.map {|x| " #{x}"}.join("")
190
+ ]
174
191
  clean_backtrace(error.caller).each do |line|
175
192
  out.puts " %s" % line
176
193
  end
@@ -182,10 +199,6 @@ module XSpec
182
199
 
183
200
  private
184
201
 
185
- def full_name(unit_of_work)
186
- (unit_of_work.parents + [unit_of_work]).map(&:name).compact.join(' ')
187
- end
188
-
189
202
  # A standard backtrace contains many entries for XSpec itself which are
190
203
  # not useful for debugging your tests, so they are stripped out.
191
204
  def clean_backtrace(backtrace)
@@ -204,9 +217,9 @@ module XSpec
204
217
  # Includes nicely formatted names and durations of each test in the output,
205
218
  # with color.
206
219
  class ColoredDocumentation
207
- require 'set'
208
-
220
+ include EmptyFormatter
209
221
  include Composable
222
+ include ShortIdSupport
210
223
 
211
224
  VT100_COLORS = {
212
225
  :black => 30,
@@ -219,24 +232,6 @@ module XSpec
219
232
  :white => 37
220
233
  }
221
234
 
222
- def color_code_for(color)
223
- VT100_COLORS.fetch(color)
224
- end
225
-
226
- def colorize(text, color)
227
- "\e[#{color_code_for(color)}m#{text}\e[0m"
228
- end
229
-
230
- def decorate(result)
231
- name = result.name
232
- out = if result.errors.any?
233
- colorize(append_failed(name), :red)
234
- else
235
- colorize(name , :green)
236
- end
237
- "%.3fs " % result.duration + out
238
- end
239
-
240
235
  def initialize(out = $stdout)
241
236
  self.indent = 2
242
237
  self.last_seen_names = []
@@ -244,10 +239,6 @@ module XSpec
244
239
  self.out = out
245
240
  end
246
241
 
247
- def run_start; end
248
-
249
- def evaluate_start(*_); end
250
-
251
242
  def evaluate_finish(result)
252
243
  output_context_header! result.parents.map(&:name).compact
253
244
 
@@ -267,6 +258,28 @@ module XSpec
267
258
 
268
259
  attr_accessor :last_seen_names, :indent, :failed, :out
269
260
 
261
+ def color_code_for(color)
262
+ VT100_COLORS.fetch(color)
263
+ end
264
+
265
+ def colorize(text, color)
266
+ "\e[#{color_code_for(color)}m#{text}\e[0m"
267
+ end
268
+
269
+ def decorate(result)
270
+ name = result.name
271
+ out = if result.errors.any?
272
+ colorize(append_failed(name), :red)
273
+ else
274
+ colorize(name , :green)
275
+ end
276
+ "%.3fs %s %s" % [
277
+ result.duration,
278
+ short_id_for(result),
279
+ out,
280
+ ]
281
+ end
282
+
270
283
  def output_context_header!(parent_names)
271
284
  if parent_names != last_seen_names
272
285
  tail = parent_names - last_seen_names
@@ -299,11 +312,7 @@ module XSpec
299
312
  # Useful as a parent class for other notifiers or for testing.
300
313
  class Null
301
314
  include Composable
302
-
303
- def run_start; end
304
- def evaluate_start(*_); end
305
- def evaluate_finish(*_); end
306
- def run_finish; true; end
315
+ include EmptyFormatter
307
316
  end
308
317
 
309
318
  DEFAULT =
@@ -12,8 +12,9 @@ module XSpec
12
12
  @clock = opts.fetch(:clock, ->{ Time.now.to_f })
13
13
  end
14
14
 
15
- def run(context, notifier)
16
- notifier.run_start
15
+ def run(context, config)
16
+ notifier = config.fetch(:notifier)
17
+ notifier.run_start(config)
17
18
 
18
19
  context.nested_units_of_work.each do |x|
19
20
  notifier.evaluate_start(x)
@@ -34,6 +35,25 @@ module XSpec
34
35
  attr_reader :clock
35
36
  end
36
37
 
38
+ class Filter
39
+ def initialize(scheduler:, filter:)
40
+ @scheduler = scheduler
41
+ @filter = filter
42
+ end
43
+
44
+ def run(context, config)
45
+ scheduler.run(FilteredContext.new(context, filter), config)
46
+ end
47
+
48
+ FilteredContext = Struct.new(:context, :filter) do
49
+ def nested_units_of_work
50
+ context.nested_units_of_work.select(&filter)
51
+ end
52
+ end
53
+
54
+ attr_reader :scheduler, :filter
55
+ end
56
+
37
57
  DEFAULT = Serial.new
38
58
  end
39
59
  end
@@ -12,7 +12,7 @@ extend XSpec.dsl(
12
12
  def assert_errors_from_run(context, expected_error_messages)
13
13
  context.run!
14
14
 
15
- notifier = context.__xspec_opts.fetch(:notifier)
15
+ notifier = context.__xspec_config.fetch(:notifier)
16
16
  assert_equal expected_error_messages, notifier.errors.flatten.map(&:message)
17
17
  end
18
18
 
@@ -14,7 +14,7 @@ describe 'simple assertion context' do
14
14
  begin
15
15
  subject.assert(false)
16
16
  fail "Assertion did not fail"
17
- rescue XSpec::Evaluator::Simple::AssertionFailed => e
17
+ rescue XSpec::Evaluator::EvaluateFailed => e
18
18
  assert_equal "assertion failed", e.message
19
19
  end
20
20
  end
@@ -23,7 +23,7 @@ describe 'simple assertion context' do
23
23
  begin
24
24
  subject.assert(false, "nope")
25
25
  fail "Assertion did not fail"
26
- rescue XSpec::Evaluator::Simple::AssertionFailed => e
26
+ rescue XSpec::Evaluator::EvaluateFailed => e
27
27
  assert_equal "nope", e.message
28
28
  end
29
29
  end
@@ -38,7 +38,7 @@ describe 'simple assertion context' do
38
38
  begin
39
39
  subject.assert_equal("a", "b")
40
40
  fail "Assertion did not fail"
41
- rescue XSpec::Evaluator::Simple::AssertionFailed => e
41
+ rescue XSpec::Evaluator::EvaluateFailed => e
42
42
  assert_include 'want: "a"', e.message
43
43
  assert_include 'got: "b"', e.message
44
44
  end
@@ -50,7 +50,7 @@ describe 'simple assertion context' do
50
50
  begin
51
51
  subject.fail
52
52
  assert false, "fail did not fail"
53
- rescue XSpec::Evaluator::Simple::AssertionFailed => e
53
+ rescue XSpec::Evaluator::EvaluateFailed => e
54
54
  assert_equal "failed", e.message
55
55
  end
56
56
  end
@@ -59,7 +59,7 @@ describe 'simple assertion context' do
59
59
  begin
60
60
  subject.fail ":("
61
61
  assert false, "fail did not fail"
62
- rescue XSpec::Evaluator::Simple::AssertionFailed => e
62
+ rescue XSpec::Evaluator::EvaluateFailed => e
63
63
  assert_equal ":(", e.message
64
64
  end
65
65
  end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'short ids' do
4
+ def short_id_for(name)
5
+ XSpec.default_short_id \
6
+ XSpec::NestedUnitOfWork.new([], XSpec::UnitOfWork.new(name))
7
+ end
8
+
9
+ it 'is the same for same names' do
10
+ assert_equal short_id_for("abc"), short_id_for("abc")
11
+ end
12
+
13
+ it 'is always the same length' do
14
+ 100.times do
15
+ name = rand.to_s
16
+ assert 3 == short_id_for(name).length, "#{name} had bad short id"
17
+ end
18
+ end
19
+ end
@@ -12,7 +12,7 @@ describe 'doubles assertion context' do
12
12
 
13
13
  it 'converts double exceptions to failures' do
14
14
  result = subject.call(XSpec::UnitOfWork.new(nil, ->{
15
- raise XSpec::Evaluator::Doubles::DoubleFailure, "nope"
15
+ raise XSpec::Evaluator::EvaluateFailed, "nope"
16
16
  }))
17
17
  assert_equal "nope", result[0].message
18
18
  end
@@ -81,7 +81,7 @@ describe 'doubles assertion context' do
81
81
  verify(double).foo("b")
82
82
  }
83
83
  fail "no error raised"
84
- rescue XSpec::Evaluator::Doubles::DoubleFailure => e
84
+ rescue XSpec::Evaluator::EvaluateFailed => e
85
85
  assert_include "Did not receive", e.message
86
86
  assert_include 'foo("b")', e.message
87
87
  end
@@ -96,7 +96,7 @@ describe 'doubles assertion context' do
96
96
  verify(double).foo("c")
97
97
  }
98
98
  fail "no error raised"
99
- rescue XSpec::Evaluator::Doubles::DoubleFailure => e
99
+ rescue XSpec::Evaluator::EvaluateFailed => e
100
100
  assert_include "Did not receive", e.message
101
101
  assert_include 'foo("b")', e.message
102
102
  assert_include "Did receive", e.message
@@ -122,7 +122,7 @@ describe 'doubles assertion context' do
122
122
  stub(double).bogus_method { 123 }
123
123
  }
124
124
  fail "no error raised"
125
- rescue XSpec::Evaluator::Doubles::DoubleFailure => e
125
+ rescue XSpec::Evaluator::EvaluateFailed => e
126
126
  assert_include "LoadedClass#bogus_method", e.message
127
127
  end
128
128
  end
@@ -134,7 +134,7 @@ describe 'doubles assertion context' do
134
134
  verify(double).bogus_method { 123 }
135
135
  }
136
136
  fail "no error raised"
137
- rescue XSpec::Evaluator::Doubles::DoubleFailure => e
137
+ rescue XSpec::Evaluator::EvaluateFailed => e
138
138
  assert_include "LoadedClass#bogus_method", e.message
139
139
  end
140
140
  end
@@ -157,7 +157,7 @@ describe 'doubles assertion context' do
157
157
  stub(double).bogus_method { 123 }
158
158
  }
159
159
  fail "no error raised"
160
- rescue XSpec::Evaluator::Doubles::DoubleFailure => e
160
+ rescue XSpec::Evaluator::EvaluateFailed => e
161
161
  assert_include "LoadedClass.bogus_method", e.message
162
162
  end
163
163
  end
@@ -169,7 +169,7 @@ describe 'doubles assertion context' do
169
169
  verify(double).bogus_method
170
170
  }
171
171
  fail "no error raised"
172
- rescue XSpec::Evaluator::Doubles::DoubleFailure => e
172
+ rescue XSpec::Evaluator::EvaluateFailed => e
173
173
  assert_include "LoadedClass.bogus_method", e.message
174
174
  end
175
175
  end
@@ -190,7 +190,7 @@ describe 'strict doubles assertion context' do
190
190
  begin
191
191
  subject.instance_double("Bogus")
192
192
  fail "no error raised"
193
- rescue XSpec::Evaluator::Doubles::DoubleFailure => e
193
+ rescue XSpec::Evaluator::EvaluateFailed => e
194
194
  assert_include "Bogus", e.message
195
195
  end
196
196
  end
@@ -10,6 +10,7 @@ end
10
10
  describe 'failures at end notifier' do
11
11
  let(:out) { StringIO.new }
12
12
  let(:notifier) { XSpec::Notifier::FailuresAtEnd.new(out) }
13
+ let(:config) {{ short_id: -> _ { "XXX" }}}
13
14
 
14
15
  it 'returns true when no errors are observed' do
15
16
  assert notifier.run_finish
@@ -21,10 +22,11 @@ describe 'failures at end notifier' do
21
22
  "failed",
22
23
  []
23
24
  )
25
+ notifier.run_start(config)
24
26
  notifier.evaluate_finish(make_executed_test errors: [failure])
25
27
 
26
28
  assert !notifier.run_finish
27
- assert_include "a b c:\n failed", out.string
29
+ assert_include "a b c\n failed", out.string
28
30
  end
29
31
 
30
32
  it 'cleans lib entries out of backtrace' do
@@ -33,13 +35,14 @@ describe 'failures at end notifier' do
33
35
  "failed",
34
36
  [File.expand_path('../../../lib', __FILE__) + '/bogus.rb']
35
37
  )
38
+ notifier.run_start(config)
36
39
  notifier.evaluate_finish(make_executed_test errors: [failure])
37
40
 
38
41
  assert !notifier.run_finish
39
42
  assert !out.string.include?('bogus.rb')
40
43
  end
41
44
 
42
- it_behaves_like_a ComposableNotifier
45
+ include_context ComposableNotifier
43
46
  end
44
47
 
45
48
  describe 'character notifier' do
@@ -68,25 +71,27 @@ describe 'character notifier' do
68
71
  assert out.string == "E\n"
69
72
  end
70
73
 
71
- it_behaves_like_a ComposableNotifier
74
+ include_context ComposableNotifier
72
75
  end
73
76
 
74
77
  describe 'documentation notifier' do
75
78
  let(:notifier) { XSpec::Notifier::Documentation.new(out) }
76
79
  let(:out) { StringIO.new }
80
+ let(:config) {{ short_id: -> uow { 'XXX' }}}
77
81
 
78
82
  def evaluate_finish(args)
83
+ notifier.run_start(config)
79
84
  notifier.evaluate_finish(make_executed_test args)
80
85
  out.string
81
86
  end
82
87
 
83
88
  it 'outputs each context with a header and individual tests' do
84
- assert_equal "\na\n 0.001s b\n",
89
+ assert_equal "\na\n 0.001s XXX b\n",
85
90
  evaluate_finish(parents: ['a'], name: 'b')
86
91
  end
87
92
 
88
93
  it 'adds an indent for each nested context' do
89
- assert_equal "\na\n b\n 0.001s c\n",
94
+ assert_equal "\na\n b\n 0.001s XXX c\n",
90
95
  evaluate_finish(parents: ['a', 'b'], name: 'c')
91
96
  end
92
97
 
@@ -94,11 +99,11 @@ describe 'documentation notifier' do
94
99
  evaluate_finish(parents: ['a', 'b'], name: 'c')
95
100
  evaluate_finish(parents: ['a', 'd'], name: 'e')
96
101
 
97
- assert_equal "\na\n b\n 0.001s c\n\n d\n 0.001s e\n", out.string
102
+ assert_equal "\na\n b\n 0.001s XXX c\n\n d\n 0.001s XXX e\n", out.string
98
103
  end
99
104
 
100
105
  it 'ignores contexts with no name' do
101
- assert_equal "\na\n 0.001s b\n",
106
+ assert_equal "\na\n 0.001s XXX b\n",
102
107
  evaluate_finish(parents: [nil, 'a', nil], name: 'b')
103
108
  end
104
109
 
@@ -116,32 +121,35 @@ describe 'documentation notifier' do
116
121
  assert_include "FAILED", evaluate_finish(errors: [make_error])
117
122
  end
118
123
 
119
- it_behaves_like_a ComposableNotifier
124
+ include_context ComposableNotifier
120
125
  end
121
126
 
122
127
  describe 'colored documentation notifier' do
123
128
  let(:notifier) { XSpec::Notifier::ColoredDocumentation.new(out) }
124
129
  let(:out) { StringIO.new }
130
+ let(:config) {{ short_id: -> uow { 'XXX' }}}
125
131
 
126
132
  it 'colors successful tests green' do
133
+ notifier.run_start(config)
127
134
  notifier.evaluate_finish(make_executed_test errors: [])
128
135
 
129
136
  assert_include "\e[32m\e[0m\n", out.string
130
137
  end
131
138
 
132
139
  it 'colors failed and errored tests red' do
140
+ notifier.run_start(config)
133
141
  notifier.evaluate_finish(make_executed_test errors: [make_failure])
134
142
 
135
143
  assert_include "\e[31mFAILED\e[0m", out.string
136
144
  end
137
145
 
138
- it_behaves_like_a ComposableNotifier
146
+ include_context ComposableNotifier
139
147
  end
140
148
 
141
149
  describe 'composable notifier' do
142
150
  let(:notifier) { XSpec::Notifier::Composite.new }
143
151
 
144
- it_behaves_like_a ComposableNotifier
152
+ include_context ComposableNotifier
145
153
  end
146
154
 
147
155
  describe 'null notifier' do
@@ -151,17 +159,18 @@ describe 'null notifier' do
151
159
  assert notifier.run_finish
152
160
  end
153
161
 
154
- it_behaves_like_a ComposableNotifier
162
+ include_context ComposableNotifier
155
163
  end
156
164
 
157
165
  describe 'timings at end' do
158
- let(:notifier) { XSpec::Notifier::TimingsAtEnd.new }
166
+ let(:out) { StringIO.new }
167
+ let(:notifier) { XSpec::Notifier::TimingsAtEnd.new(out: out) }
159
168
 
160
169
  it 'always returns true' do
161
170
  assert notifier.run_finish
162
171
  end
163
172
 
164
- it_behaves_like_a ComposableNotifier
173
+ include_context ComposableNotifier
165
174
  end
166
175
 
167
176
  def make_nested_test(parent_names = [], work_name = nil)
@@ -22,6 +22,6 @@ Gem::Specification.new do |gem|
22
22
  gem.bindir = "bin"
23
23
  gem.executables << "xspec"
24
24
  gem.license = "Apache 2.0"
25
- gem.version = "0.1.0"
25
+ gem.version = "0.2.0"
26
26
  gem.has_rdoc = false
27
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Xavier Shay
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-29 00:00:00.000000000 Z
11
+ date: 2014-08-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: yeah
14
14
  email:
@@ -32,6 +32,7 @@ files:
32
32
  - spec/integration/rspec_expectations_spec.rb
33
33
  - spec/spec_helper.rb
34
34
  - spec/unit/assertion_spec.rb
35
+ - spec/unit/data_structures_spec.rb
35
36
  - spec/unit/doubles_spec.rb
36
37
  - spec/unit/let_spec.rb
37
38
  - spec/unit/notifiers_spec.rb
@@ -66,6 +67,7 @@ test_files:
66
67
  - spec/integration/rspec_expectations_spec.rb
67
68
  - spec/spec_helper.rb
68
69
  - spec/unit/assertion_spec.rb
70
+ - spec/unit/data_structures_spec.rb
69
71
  - spec/unit/doubles_spec.rb
70
72
  - spec/unit/let_spec.rb
71
73
  - spec/unit/notifiers_spec.rb