spectre-core 2.1.2 → 2.1.4

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 (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/spectre/version.rb +1 -1
  3. data/lib/spectre.rb +147 -112
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2343f54b9e9837df93c730210d5652256643d8514b0456670292b492dc7f770e
4
- data.tar.gz: 6d934a08ecee932b5346a3d9e98fcd96bc27384f5b9f17cd01b9c08b430c0ff2
3
+ metadata.gz: 0c4d07c24a6be8fbf1ed1ff1b4ec52860294be4b4c2d8580facf183231a3c4ed
4
+ data.tar.gz: 576889b489206a424462b087bc75590aa17339b918c4bc1076f072ceab2c29af
5
5
  SHA512:
6
- metadata.gz: 137d6c2c7012d535ba7a93072b67f0cd471ca6b9c5b83225ccb9eb06a433645f078a9928a21c61598059e299c476fe2cbad9f6a4112fd5a0d2d86ed7c2f392e2
7
- data.tar.gz: ab497f00a4ef0a3ca453f304a6a7e2ac30e0ecdd4910bc27c14c533079638866a2e609e6f618641cdd0ffa337e9575a340a658b1079f2fb18b38cf9cf6adadf4
6
+ metadata.gz: 2a8c17b521b0e391942bf99a2ecd41d18349b5c79b63e5e71bdd1365284d347c1c31c0315f9b62976ff0068c29248f1a009a4e6eac8e53198ce4e41a8173f8a7
7
+ data.tar.gz: af4a92361d63d56b269a61b088635801b158dd217c4f9bd3400b5b19a0f0e20bad314589a488be101e5204935a6ed68e7d48c771ab257e450cdb6d166edd1df5
@@ -1,3 +1,3 @@
1
1
  module Spectre
2
- VERSION = '2.1.2'.freeze
2
+ VERSION = '2.1.4'.freeze
3
3
  end
data/lib/spectre.rb CHANGED
@@ -20,7 +20,7 @@ def get_call_location call_stack
20
20
  .find { |x| x.label.include? 'Spectre::Engine#load_files' or x.base_label == '<top (required)>' }
21
21
 
22
22
  [
23
- loc.path.sub(Spectre.pwd, '.'),
23
+ loc.path.sub(Dir.pwd, '.'),
24
24
  loc.lineno
25
25
  ]
26
26
  end
@@ -78,13 +78,6 @@ end
78
78
  # The main module containing all logic for the framework
79
79
  #
80
80
  module Spectre
81
- # Cache Dir.pwd to avoid repeated system calls
82
- @pwd = Dir.pwd
83
-
84
- def self.pwd
85
- @pwd
86
- end
87
-
88
81
  ##
89
82
  # Exception to throw in order to abort a spec run
90
83
  #
@@ -265,13 +258,14 @@ module Spectre
265
258
  counter = 0
266
259
 
267
260
  specs
268
- .group_by { |x| x.parent.root }
261
+ .group_by { |x| x.parent.root.name }
269
262
  .each_value do |spec_group|
270
- spec_group.sort_by!(&:name)
271
- spec_group.each do |spec|
272
- spec_id = "[#{spec.name}]".send(@colors[counter % @colors.length])
273
- @out.puts "#{spec_id} #{spec.full_desc} #{spec.tags.map { |x| "##{x}" }.join(' ').cyan}"
274
- end
263
+ spec_group
264
+ .sort
265
+ .each do |spec|
266
+ spec_id = "[#{spec.name}]".send(@colors[counter % @colors.length])
267
+ @out.puts "#{spec_id} #{spec.full_desc} #{spec.tags.map { |x| "##{x}" }.join(' ').cyan}"
268
+ end
275
269
 
276
270
  counter += 1
277
271
  end
@@ -280,15 +274,15 @@ module Spectre
280
274
  ##
281
275
  # Outputs all the specs for all contexts
282
276
  #
283
- def describe contexts, level = 0
284
- contexts.each do |context|
277
+ def describe contexts, level = 0, parent: nil
278
+ contexts.select { |x| x.parent == parent }.each do |context|
285
279
  @out.puts("#{' ' * level}#{context.desc.send(level.positive? ? :magenta : :blue)}")
286
280
 
287
281
  context.specs.each do |spec|
288
282
  @out.puts("#{' ' * (level + 1)}#{spec.desc}")
289
283
  end
290
284
 
291
- describe(context.children, level + 1)
285
+ describe(contexts, level + 1, parent: context)
292
286
  end
293
287
  end
294
288
 
@@ -327,7 +321,7 @@ module Spectre
327
321
  mixins.each_value do |mixin|
328
322
  output = "#{mixin.desc.yellow}\n"
329
323
  output += " params.....: #{mixin.params.join ', '}\n" if mixin.params.any?
330
- output += " location...: #{mixin.file.sub(Spectre.pwd, '.')}:#{mixin.line}"
324
+ output += " location...: #{mixin.file.sub(Dir.pwd, '.')}:#{mixin.line}"
331
325
  paragraphs << output
332
326
  end
333
327
 
@@ -781,6 +775,10 @@ module Spectre
781
775
  end
782
776
  end
783
777
 
778
+ ##
779
+ # The run context is passed to all specs and provides the
780
+ # DSL for assertions, expectations, logging, etc.
781
+ #
784
782
  class RunContext
785
783
  include Delegate
786
784
 
@@ -1164,7 +1162,7 @@ module Spectre
1164
1162
  # Skip the run for this spec. This can be used to skip spec runs when a certain
1165
1163
  # condition occurs.
1166
1164
  #
1167
- # skip 'subject is not yet ready to be tests' unless service_is_ready()
1165
+ # skip 'subject is not yet ready to be tested' unless service_is_ready()
1168
1166
  #
1169
1167
  def skip message
1170
1168
  @skipped = true
@@ -1174,7 +1172,7 @@ module Spectre
1174
1172
  end
1175
1173
 
1176
1174
  class Specification
1177
- attr_reader :id, :name, :desc, :full_desc, :parent, :root, :tags, :data, :file
1175
+ attr_reader :id, :name, :desc, :full_desc, :parent, :root, :tags, :data, :file, :block
1178
1176
 
1179
1177
  def initialize parent, name, desc, tags, data, file, block
1180
1178
  @parent = parent
@@ -1189,10 +1187,25 @@ module Spectre
1189
1187
  end
1190
1188
 
1191
1189
  ##
1192
- # Creates a new +RunContext+ and executes the spec,
1193
- # +before+ and +after+ blocks
1190
+ # Natural sort comparison for spec names respecting numeric parts.
1194
1191
  #
1195
- def run engine, befores, afters, bag
1192
+ def <=>(other)
1193
+ # Split on dash: text part and numeric part
1194
+ self_parts = @name.split('-')
1195
+ other_parts = other.name.split('-')
1196
+
1197
+ text_compare = self_parts[0] <=> other_parts[0]
1198
+
1199
+ # If the text parts are already different, we can return here
1200
+ return text_compare unless text_compare.zero?
1201
+
1202
+ self_parts[1].to_i <=> other_parts[1].to_i
1203
+ end
1204
+
1205
+ ##
1206
+ # Execute this specification with its before/after blocks
1207
+ #
1208
+ def run(engine, befores, afters, bag)
1196
1209
  RunContext.new(engine, self, :spec, bag) do |run_context|
1197
1210
  engine.formatter.scope(@desc, self) do
1198
1211
  befores.each do |block|
@@ -1220,13 +1233,14 @@ module Spectre
1220
1233
  class DefinitionContext
1221
1234
  include Delegate
1222
1235
 
1223
- attr_reader :id, :name, :desc, :parent, :full_desc, :children, :specs, :file
1236
+ attr_reader :id, :name, :desc, :parent, :full_desc, :specs, :file,
1237
+ :setups, :teardowns, :befores, :afters
1224
1238
 
1225
- def initialize desc, file, parent = nil
1239
+ def initialize desc, file, engine, parent = nil
1240
+ @engine = engine
1226
1241
  @parent = parent
1227
1242
  @desc = desc
1228
1243
  @file = file
1229
- @children = []
1230
1244
  @specs = []
1231
1245
 
1232
1246
  @setups = []
@@ -1239,6 +1253,8 @@ module Spectre
1239
1253
  @name = @parent.name + '-' + @name unless @parent.nil?
1240
1254
 
1241
1255
  @full_desc = @parent.nil? ? @desc : "#{@parent.full_desc} #{@desc}"
1256
+
1257
+ @engine.contexts << self
1242
1258
  end
1243
1259
 
1244
1260
  ##
@@ -1249,10 +1265,90 @@ module Spectre
1249
1265
  end
1250
1266
 
1251
1267
  ##
1252
- # A flattened list of all specs including those of child contexts.
1268
+ # Returns all direct child contexts
1253
1269
  #
1254
- def all_specs
1255
- @specs + @children.map(&:all_specs).flatten
1270
+ def children
1271
+ @engine.contexts.select { |x| x.parent == self }
1272
+ end
1273
+
1274
+ ##
1275
+ # Execute this context with its specs, setups, and teardowns.
1276
+ # Recursively executes child contexts using the tree structure.
1277
+ #
1278
+ def run(specs_set)
1279
+ runs = []
1280
+
1281
+ # Find specs in this context that should be executed
1282
+ selected = @specs.select { |x| specs_set.include? x }
1283
+
1284
+ # Check if any child contexts have specs to execute
1285
+ has_child_specs = children.any? { |child| child.specs_in_tree?(specs_set) }
1286
+
1287
+ # Skip this context if it has no matching specs and no children with specs
1288
+ return runs if selected.empty? && !has_child_specs
1289
+
1290
+ @engine.formatter.scope(@desc, self) do
1291
+ # Execute setup, specs, and teardown for this context
1292
+ if selected.any?
1293
+ setup_bag = nil
1294
+
1295
+ if @setups.any?
1296
+ setup_run = RunContext.new(@engine, self, :setup) do |run_context|
1297
+ @setups.each do |block|
1298
+ @engine.formatter.scope('setup', :setup) do
1299
+ @engine.logger.correlate do
1300
+ @engine.logger.debug("setup \"#{@desc}\"")
1301
+ run_context.execute(nil, &block)
1302
+ end
1303
+ end
1304
+ end
1305
+ end
1306
+
1307
+ setup_bag = setup_run.bag
1308
+ runs << setup_run
1309
+ end
1310
+
1311
+ # Only run specs if setup was successful
1312
+ if runs.all? { |x| x.status == :success }
1313
+ runs += selected.map do |spec|
1314
+ @engine.logger.correlate do
1315
+ spec.run(@engine, @befores, @afters, setup_bag)
1316
+ end
1317
+ end
1318
+ end
1319
+
1320
+ if @teardowns.any?
1321
+ runs << RunContext.new(@engine, self, :teardown, setup_bag) do |run_context|
1322
+ @teardowns.each do |block|
1323
+ @engine.formatter.scope('teardown', :teardown) do
1324
+ @engine.logger.correlate do
1325
+ @engine.logger.debug("teardown \"#{@desc}\"")
1326
+ run_context.execute(nil, &block)
1327
+ end
1328
+ end
1329
+ end
1330
+ end
1331
+ end
1332
+ end
1333
+
1334
+ # Recursively execute child contexts
1335
+ children.each do |child_context|
1336
+ @engine.logger.correlate do
1337
+ runs += child_context.run(specs_set)
1338
+ end
1339
+ end
1340
+ end
1341
+
1342
+ runs
1343
+ end
1344
+
1345
+ ##
1346
+ # Check recursively if this context or any of its children have specs in the filter set
1347
+ #
1348
+ def specs_in_tree?(specs_set)
1349
+ return true if @specs.any? { |x| specs_set.include? x }
1350
+
1351
+ children.any? { |child| child.specs_in_tree?(specs_set) }
1256
1352
  end
1257
1353
 
1258
1354
  ##
@@ -1262,10 +1358,9 @@ module Spectre
1262
1358
  file = caller
1263
1359
  .first
1264
1360
  .gsub(/:in .*/, '')
1265
- .gsub(Spectre.pwd, '.')
1361
+ .gsub(Dir.pwd, '.')
1266
1362
 
1267
- context = DefinitionContext.new(desc, file, self)
1268
- @children << context
1363
+ context = DefinitionContext.new(desc, file, @engine, self)
1269
1364
  context.instance_eval(&)
1270
1365
  end
1271
1366
 
@@ -1312,12 +1407,18 @@ module Spectre
1312
1407
  file = caller
1313
1408
  .first
1314
1409
  .gsub(/:in .*/, '')
1315
- .gsub(Spectre.pwd, '.')
1410
+ .gsub(Dir.pwd, '.')
1316
1411
 
1317
1412
  with ||= [nil]
1318
1413
 
1319
- with.each_with_index do |data, _index|
1320
- spec_index = root.all_specs.count + 1
1414
+ initial_index = @engine
1415
+ .contexts
1416
+ .select { |x| x.root.name == root.name }
1417
+ .flat_map(&:specs)
1418
+ .count + 1
1419
+
1420
+ with.each_with_index do |data, index|
1421
+ spec_index = initial_index + index
1321
1422
  name = "#{root.name}-#{spec_index}"
1322
1423
 
1323
1424
  spec = Specification.new(self, name, desc, tags, data, file, block)
@@ -1325,68 +1426,6 @@ module Spectre
1325
1426
  @specs << spec
1326
1427
  end
1327
1428
  end
1328
-
1329
- # :nodoc:
1330
- def run engine, specs
1331
- runs = []
1332
-
1333
- return runs unless all_specs.any? { |x| specs.include? x }
1334
-
1335
- selected = @specs.select { |x| specs.include? x }
1336
-
1337
- engine.formatter.scope(@desc, self) do
1338
- if selected.any?
1339
- setup_bag = nil
1340
-
1341
- if @setups.any?
1342
- setup_run = RunContext.new(engine, self, :setup) do |run_context|
1343
- @setups.each do |block|
1344
- engine.formatter.scope('setup', :setup) do
1345
- engine.logger.correlate do
1346
- engine.logger.debug("setup \"#{@desc}\"")
1347
- run_context.execute(nil, &block)
1348
- end
1349
- end
1350
- end
1351
- end
1352
-
1353
- setup_bag = setup_run.bag
1354
-
1355
- runs << setup_run
1356
- end
1357
-
1358
- # Only run specs if setup was successful
1359
- if runs.all? { |x| x.status == :success }
1360
- runs += selected.map do |spec|
1361
- engine.logger.correlate do
1362
- spec.run(engine, @befores, @afters, setup_bag)
1363
- end
1364
- end
1365
- end
1366
-
1367
- if @teardowns.any?
1368
- runs << RunContext.new(engine, self, :teardown, setup_bag) do |run_context|
1369
- @teardowns.each do |block|
1370
- engine.formatter.scope('teardown', :teardown) do
1371
- engine.logger.correlate do
1372
- engine.logger.debug("teardown \"#{@desc}\"")
1373
- run_context.execute(nil, &block)
1374
- end
1375
- end
1376
- end
1377
- end
1378
- end
1379
- end
1380
-
1381
- @children.each do |context|
1382
- engine.logger.correlate do
1383
- runs += context.run(engine, specs)
1384
- end
1385
- end
1386
- end
1387
-
1388
- runs
1389
- end
1390
1429
  end
1391
1430
 
1392
1431
  ##
@@ -1581,9 +1620,6 @@ module Spectre
1581
1620
  require module_name
1582
1621
  end
1583
1622
  end
1584
- ensure
1585
- # return to the previous working directory
1586
- Dir.chdir(Spectre.pwd)
1587
1623
  end
1588
1624
 
1589
1625
  # :nodoc:
@@ -1609,8 +1645,7 @@ module Spectre
1609
1645
  tag_filter = config['tags'] || []
1610
1646
 
1611
1647
  @contexts
1612
- .map(&:all_specs)
1613
- .flatten
1648
+ .flat_map(&:specs)
1614
1649
  .select do |spec|
1615
1650
  (spec_filter.empty? and tag_filter.empty?) or
1616
1651
  spec_filter.any? { |x| spec.name.match?("^#{x.gsub('*', '.*')}$") } or
@@ -1630,12 +1665,12 @@ module Spectre
1630
1665
  end
1631
1666
  end
1632
1667
 
1633
- list
1668
+ specs = list
1669
+ specs_set = Set.new(list)
1670
+
1671
+ specs
1634
1672
  .group_by { |x| x.parent.root }
1635
- .map do |context, specs|
1636
- context.run(self, specs)
1637
- end
1638
- .flatten
1673
+ .flat_map { |context, _| context.run(specs_set) }
1639
1674
  rescue Interrupt
1640
1675
  # Do nothing here
1641
1676
  end
@@ -1669,15 +1704,15 @@ module Spectre
1669
1704
  ##
1670
1705
  # Describe a test subject
1671
1706
  #
1672
- def describe(name, &)
1707
+ def describe(desc, &)
1673
1708
  file = caller
1674
1709
  .first
1675
1710
  .gsub(/:in .*/, '')
1676
- .gsub(Spectre.pwd, '.')
1711
+ .gsub(Dir.pwd, '.')
1677
1712
 
1678
- main_context = DefinitionContext.new(name, file)
1679
- @contexts << main_context
1680
- main_context.instance_eval(&)
1713
+ DefinitionContext
1714
+ .new(desc, file, self)
1715
+ .instance_eval(&)
1681
1716
  end
1682
1717
 
1683
1718
  ##
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spectre-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
4
+ version: 2.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Neubauer