smartest 0.1.0.alpha3 → 0.2.0.alpha1

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
  SHA256:
3
- metadata.gz: c1ce8aea4b74c7ca0de7f3a2b1472b79ea86849be91801e11922883b6423afdf
4
- data.tar.gz: be5f8e88c1bd93eb061eb0a854b46a6c6094627b36cd22f8778026e35161992a
3
+ metadata.gz: ee5c1b7be401fa3dc4a8d88a25a241fc2b0bad486e8a3163a83c6c2340ae7c3c
4
+ data.tar.gz: f063204cd31bcd99b4654c26bb8591d566dcd424a2a312d9a7c9cbbea4292b01
5
5
  SHA512:
6
- metadata.gz: 864cea53cff77bf90e73bdf6528c9e6ff7ba633c89c8edbf011bd50552d9bf1aea606c0a9e4af83352847f88b1ca8571433987d2f933d20b50270b2d2b81079d
7
- data.tar.gz: d587ffbf743f4fe75d8977346ae653fe2616ab70e4480453442f4677f7d4d6a4f84166f64c17b50417616693232149ef36214c8c12d530de41a9ff5f36c75a51
6
+ metadata.gz: 18f117d867c86f47ff3d517729c0e7cbae4af5933b7432fa96ad30b1aea58a76b48e7ae4d3c129e72594598d19297751513d49501e5481c9ef07259a5350dafb
7
+ data.tar.gz: '085a5a10a7d5248d7888fd6267edae7c4dd88077f5b413d45385d4c05245b0eae771975ab67a50befc8ca14d8acb3d148d9718d94b431caf9800d2a2daa78627'
data/DEVELOPMENT.md CHANGED
@@ -102,7 +102,8 @@ Responsibilities:
102
102
 
103
103
  - test registry
104
104
  - fixture class registry
105
- - hook registry, if hooks are implemented later
105
+ - matcher registry
106
+ - `around_suite` hook registry
106
107
 
107
108
  Example shape:
108
109
 
@@ -125,10 +126,14 @@ Required methods:
125
126
 
126
127
  ```ruby
127
128
  test(name, **metadata, &block)
128
- use_fixture(klass)
129
- use_matcher(matcher_module)
129
+ around_suite(&block)
130
+ around_test(&block)
130
131
  ```
131
132
 
133
+ `use_fixture(klass)` and `use_matcher(matcher_module)` are not top-level DSL
134
+ methods. They are available only from hook execution contexts: `around_suite`
135
+ and `around_test`.
136
+
132
137
  Possible later methods:
133
138
 
134
139
  ```ruby
@@ -263,7 +268,10 @@ Responsibilities:
263
268
  Example:
264
269
 
265
270
  ```ruby
266
- use_fixture AppFixture
271
+ around_suite do |suite|
272
+ use_fixture AppFixture
273
+ suite.run
274
+ end
267
275
  ```
268
276
 
269
277
  should register `AppFixture`.
@@ -314,6 +322,7 @@ Runs tests.
314
322
  Responsibilities:
315
323
 
316
324
  - iterate over registered test cases
325
+ - run registered `around_suite` hooks around the suite body
317
326
  - create a lazy suite-scoped `FixtureSet`
318
327
  - create a fresh `ExecutionContext` per test
319
328
  - create a fresh `FixtureSet` per test
@@ -324,6 +333,11 @@ Responsibilities:
324
333
  - produce `TestResult`
325
334
  - notify reporter
326
335
 
336
+ `around_suite` hooks receive a run target and must call `suite.run` exactly once.
337
+ Registered hooks compose in registration order, so the first hook is the
338
+ outermost wrapper. If a hook raises or does not call `suite.run`, the runner
339
+ reports a suite failure and exits with status `1`.
340
+
327
341
  Pseudo-code:
328
342
 
329
343
  ```ruby
@@ -577,7 +591,7 @@ A practical approach:
577
591
 
578
592
  - `Smartest::Fixture`
579
593
  - `fixture :name do ... end`
580
- - `use_fixture`
594
+ - `use_fixture` from a hook context
581
595
  - test block keyword fixture resolution
582
596
 
583
597
  ### Phase 3: Fixture dependencies
@@ -620,6 +634,21 @@ A practical approach:
620
634
  - generate a `smartest/test_helper.rb` that loads `smartest/fixtures/**/*.rb`
621
635
  - exit code 0 on success, 1 on failure
622
636
 
637
+ ### Phase 7: Suite hooks
638
+
639
+ - `around_suite do |suite| ... end`
640
+ - run hooks around the full suite body
641
+ - include suite fixture cleanup inside the wrapped body
642
+ - report hook failures as suite failures
643
+
644
+ ### Phase 8: Test hooks
645
+
646
+ - `around_test do |test| ... end`
647
+ - snapshot file-local hooks when each test is registered
648
+ - run hooks around fixture setup, test body, and fixture cleanup
649
+ - expose `use_fixture` and `use_matcher` only inside hook contexts
650
+ - make `around_test` registered from `around_suite` suite-wide
651
+
623
652
  ## MVP API rules
624
653
 
625
654
  Supported:
@@ -648,6 +677,18 @@ end
648
677
  cleanup { ... }
649
678
  ```
650
679
 
680
+ ```ruby
681
+ around_suite do |suite|
682
+ suite.run
683
+ end
684
+ ```
685
+
686
+ ```ruby
687
+ around_test do |test|
688
+ test.run
689
+ end
690
+ ```
691
+
651
692
  Not supported in MVP:
652
693
 
653
694
  ```ruby
data/README.md CHANGED
@@ -173,9 +173,9 @@ be_nil
173
173
  raise_error(ErrorClass)
174
174
  ```
175
175
 
176
- Custom matcher modules can be registered with `use_matcher`. The generated
177
- scaffold includes a `PredicateMatcher` custom matcher for `be_<predicate>` calls.
178
- See [Matchers](documentation/docs/matchers.md).
176
+ Custom matcher modules can be registered from `around_suite` or `around_test`
177
+ with `use_matcher`. The generated scaffold includes a `PredicateMatcher` custom
178
+ matcher for `be_<predicate>` calls. See [Matchers](documentation/docs/matchers.md).
179
179
 
180
180
  ## Fixtures
181
181
 
@@ -192,10 +192,13 @@ class AppFixture < Smartest::Fixture
192
192
  end
193
193
  ```
194
194
 
195
- Register fixture classes from `smartest/test_helper.rb`:
195
+ Register fixture classes from `around_suite` in `smartest/test_helper.rb`:
196
196
 
197
197
  ```ruby
198
- use_fixture AppFixture
198
+ around_suite do |suite|
199
+ use_fixture AppFixture
200
+ suite.run
201
+ end
199
202
  ```
200
203
 
201
204
  Tests request fixtures by keyword:
@@ -274,6 +277,100 @@ Suite fixtures are lazy: setup runs the first time a test requests the fixture,
274
277
  and cleanup runs once after all tests finish. Test-scoped fixtures can depend on
275
278
  suite fixtures, but suite fixtures cannot depend on test-scoped fixtures.
276
279
 
280
+ ## Suite hooks
281
+
282
+ Use `around_suite` when the full test run must execute inside another block:
283
+
284
+ ```ruby
285
+ around_suite do |suite|
286
+ Async do
287
+ suite.run
288
+ end
289
+ end
290
+ ```
291
+
292
+ The hook receives a run target and must call `suite.run` exactly once. The block
293
+ wraps every test, test-scoped fixture setup and cleanup, suite fixture setup, and
294
+ suite fixture cleanup.
295
+
296
+ Fixture and matcher registrations made before `suite.run` are applied to that
297
+ run:
298
+
299
+ ```ruby
300
+ around_suite do |suite|
301
+ use_fixture GlobalFixture
302
+ suite.run
303
+ end
304
+ ```
305
+
306
+ Multiple `around_suite` hooks run in registration order. The first hook is the
307
+ outermost wrapper:
308
+
309
+ ```ruby
310
+ around_suite do |suite|
311
+ with_outer_resource { suite.run }
312
+ end
313
+
314
+ around_suite do |suite|
315
+ with_inner_resource { suite.run }
316
+ end
317
+ ```
318
+
319
+ If an `around_suite` hook raises or does not call `suite.run`, Smartest reports a
320
+ suite failure and exits with status `1`.
321
+
322
+ ## Test hooks
323
+
324
+ Use `around_test` when each test needs to run inside another block:
325
+
326
+ ```ruby
327
+ around_test do |test|
328
+ SomeAutoCloseResource.new do
329
+ test.run
330
+ end
331
+ end
332
+ ```
333
+
334
+ The hook receives a run target and must call `test.run` exactly once. It wraps
335
+ fixture setup, the test body, and fixture cleanup.
336
+
337
+ `around_test` is file-scoped when it is written directly in a test file. Smartest
338
+ copies the current file's `around_test` hooks when each `test` is registered, so
339
+ hooks apply to tests defined later in the same file.
340
+
341
+ Define `around_test` inside `around_suite` when the hook should apply to the
342
+ whole run:
343
+
344
+ ```ruby
345
+ around_suite do |suite|
346
+ around_test do |test|
347
+ with_some_resource do
348
+ test.run
349
+ end
350
+ end
351
+
352
+ suite.run
353
+ end
354
+ ```
355
+
356
+ `around_test` can also register fixture classes or matcher modules for that test
357
+ run:
358
+
359
+ ```ruby
360
+ around_test do |test|
361
+ use_fixture LocalFixture
362
+ use_matcher LocalMatcher
363
+ test.run
364
+ end
365
+ ```
366
+
367
+ Fixture classes registered from `around_test` must define only test-scoped
368
+ fixtures. If a class defines `suite_fixture`, register it from `around_suite`
369
+ instead so its cache and cleanup belong to the suite lifecycle.
370
+
371
+ `use_fixture` and `use_matcher` are only available inside `around_suite` or
372
+ `around_test` blocks. They are not top-level DSL methods.
373
+
277
374
  ## Fixtures with teardown
278
375
 
279
376
  Not every fixture needs teardown. For fixtures that do, use `cleanup`.
@@ -355,7 +452,10 @@ end
355
452
 
356
453
  ```ruby
357
454
  # smartest/test_helper.rb
358
- use_fixture WebFixture
455
+ around_suite do |suite|
456
+ use_fixture WebFixture
457
+ suite.run
458
+ end
359
459
  ```
360
460
 
361
461
  ```ruby
@@ -391,41 +491,34 @@ server cleanup
391
491
 
392
492
  ## Registering fixture classes
393
493
 
394
- Use `use_fixture` from `smartest/test_helper.rb`:
494
+ Use `use_fixture` inside `around_suite` from `smartest/test_helper.rb`:
395
495
 
396
496
  ```ruby
397
- use_fixture AppFixture
497
+ around_suite do |suite|
498
+ use_fixture AppFixture
499
+ suite.run
500
+ end
398
501
  ```
399
502
 
400
503
  Multiple fixture classes can be registered:
401
504
 
402
505
  ```ruby
403
- use_fixture UserFixture
404
- use_fixture WebFixture
405
- use_fixture ApiFixture
506
+ around_suite do |suite|
507
+ use_fixture UserFixture
508
+ use_fixture WebFixture
509
+ use_fixture ApiFixture
510
+ suite.run
511
+ end
406
512
  ```
407
513
 
408
514
  Fixture names must be unique across registered fixture classes.
409
515
 
410
516
  If two fixture classes define the same fixture name, Smartest raises an error.
411
517
 
412
- ## Hooks
413
-
414
- Smartest may support simple hooks:
415
-
416
- ```ruby
417
- before do
418
- DatabaseCleaner.start
419
- end
518
+ ## Suite hooks and fixture cleanup
420
519
 
421
- after do
422
- DatabaseCleaner.clean
423
- end
424
- ```
425
-
426
- Hooks are separate from fixture cleanup.
427
-
428
- Use fixture cleanup for resource-specific teardown:
520
+ Suite hooks are separate from fixture cleanup. Use fixture cleanup for
521
+ resource-specific teardown:
429
522
 
430
523
  ```ruby
431
524
  fixture :server do
@@ -435,11 +528,11 @@ fixture :server do
435
528
  end
436
529
  ```
437
530
 
438
- Use hooks for broad test-level behavior:
531
+ Use `around_suite` for broad suite-level execution context:
439
532
 
440
533
  ```ruby
441
- before do
442
- reset_global_state
534
+ around_suite do |suite|
535
+ Async { suite.run }
443
536
  end
444
537
  ```
445
538
 
@@ -469,13 +562,16 @@ Dir[File.join(__dir__, "matchers", "**", "*.rb")].sort.each do |matcher_file|
469
562
  require matcher_file
470
563
  end
471
564
 
472
- use_fixture WebFixture
473
- use_matcher PredicateMatcher
565
+ around_suite do |suite|
566
+ use_fixture WebFixture
567
+ use_matcher PredicateMatcher
568
+ suite.run
569
+ end
474
570
  ```
475
571
 
476
572
  The generated helper loads Ruby files under `smartest/fixtures/` and
477
- `smartest/matchers/` in sorted order. Register fixture classes and matcher
478
- modules from the helper with `use_fixture` and `use_matcher`.
573
+ `smartest/matchers/` in sorted order. Register suite-wide fixture classes and
574
+ matcher modules from `around_suite` with `use_fixture` and `use_matcher`.
479
575
 
480
576
  Example:
481
577
 
@@ -550,6 +646,8 @@ The intended MVP includes:
550
646
  - keyword-argument fixture injection
551
647
  - fixture dependencies through keyword arguments
552
648
  - fixture cleanup
649
+ - suite hooks with `around_suite`
650
+ - test hooks with `around_test`
553
651
  - `expect(...).to eq(...)`
554
652
  - console reporter
555
653
  - CLI runner
data/SMARTEST_DESIGN.md CHANGED
@@ -50,7 +50,10 @@ class WebFixture < Smartest::Fixture
50
50
  end
51
51
  end
52
52
 
53
- use_fixture WebFixture
53
+ around_suite do |suite|
54
+ use_fixture WebFixture
55
+ suite.run
56
+ end
54
57
  ```
55
58
 
56
59
  The core decision is:
@@ -294,7 +297,9 @@ context.instance_exec(**fixtures, &block)
294
297
 
295
298
  This keeps the top-level DSL small.
296
299
 
297
- Only `test`, `fixture`, `use_fixture`, and `use_matcher` need to be globally available when using `smartest/autorun`.
300
+ Only `test`, `around_suite`, and `around_test` should be globally available when
301
+ using `smartest/autorun`. `use_fixture` and `use_matcher` are available only
302
+ inside hook execution contexts.
298
303
 
299
304
  ## Core architecture
300
305
 
@@ -543,8 +548,11 @@ class AdminFixture < Smartest::Fixture
543
548
  end
544
549
  end
545
550
 
546
- use_fixture UserFixture
547
- use_fixture AdminFixture
551
+ around_suite do |suite|
552
+ use_fixture UserFixture
553
+ use_fixture AdminFixture
554
+ suite.run
555
+ end
548
556
  ```
549
557
 
550
558
  Error:
@@ -853,7 +861,70 @@ Useful metadata later:
853
861
 
854
862
  Hooks are separate from fixtures.
855
863
 
856
- Potential API:
864
+ Supported suite API:
865
+
866
+ ```ruby
867
+ around_suite do |suite|
868
+ Async do
869
+ suite.run
870
+ end
871
+ end
872
+ ```
873
+
874
+ `around_suite` wraps the full suite body, including all tests and suite fixture
875
+ cleanup. The hook receives a run target and must call `suite.run` exactly once.
876
+ Multiple hooks compose in registration order, with the first hook as the
877
+ outermost wrapper.
878
+
879
+ Supported per-test API:
880
+
881
+ ```ruby
882
+ around_test do |test|
883
+ SomeAutoCloseResource.new do
884
+ test.run
885
+ end
886
+ end
887
+ ```
888
+
889
+ When `around_test` is written directly in a test file, Smartest treats it as
890
+ file-scoped by snapshotting the file's current around-test hooks into each
891
+ `TestCase` at test registration time. Hooks defined later in the file apply only
892
+ to later tests.
893
+
894
+ `around_test` can also be registered inside `around_suite`. In that case the hook
895
+ is suite-wide and applies when `suite.run` executes:
896
+
897
+ ```ruby
898
+ around_suite do |suite|
899
+ around_test do |test|
900
+ TestServer.run do
901
+ test.run
902
+ end
903
+ end
904
+
905
+ suite.run
906
+ end
907
+ ```
908
+
909
+ `use_fixture` and `use_matcher` are not top-level DSL methods. They are available
910
+ inside `around_suite` and `around_test` contexts:
911
+
912
+ ```ruby
913
+ around_test do |test|
914
+ use_fixture LocalFixture
915
+ use_matcher LocalMatcher
916
+ test.run
917
+ end
918
+ ```
919
+
920
+ For `around_test`, those registrations are test-run local and must happen before
921
+ `test.run`.
922
+
923
+ Fixture classes registered from `around_test` must not define `suite_fixture`.
924
+ Suite-scoped fixtures need suite-level cache and cleanup ownership, so classes
925
+ with suite-scoped fixtures must be registered from `around_suite`.
926
+
927
+ Potential simpler per-test API:
857
928
 
858
929
  ```ruby
859
930
  before do
@@ -889,10 +960,29 @@ fixture cleanup
889
960
 
890
961
  This needs a final decision later.
891
962
 
892
- For MVP, hooks can be omitted.
893
-
894
963
  Fixture cleanup already handles resource-specific teardown.
895
964
 
965
+ ### Around-test parallelism note
966
+
967
+ The file-scoped `around_test` design is intended to remain compatible with
968
+ future parallel execution if it is hardened in these directions:
969
+
970
+ - keep registration protected by a `Mutex` when tests may be loaded from
971
+ multiple threads
972
+ - use copy-on-write arrays for `around_test` hooks by file
973
+ - snapshot the current file-local hook stack into each `TestCase` at registration
974
+ time
975
+ - treat `TestCase` as immutable after registration
976
+ - expose `use_fixture` and `use_matcher` through a per-run `TestRun` object, not
977
+ by mutating the global suite registry during test execution
978
+ - snapshot the suite's fixture classes and matcher modules before a test starts
979
+ - keep test-run fixture and matcher additions local to that run
980
+
981
+ That shape lets multiple tests execute concurrently by reading immutable
982
+ `TestCase` state and using per-test fixture/matcher registries. The current
983
+ implementation can be simpler, but it should not rely on refinements or global
984
+ method rewriting for file-local behavior.
985
+
896
986
  ## Scoping
897
987
 
898
988
  Fixtures are test-scoped by default.
@@ -1073,6 +1163,11 @@ require "smartest/autorun"
1073
1163
  Dir[File.join(__dir__, "fixtures", "**", "*.rb")].sort.each do |fixture_file|
1074
1164
  require fixture_file
1075
1165
  end
1166
+
1167
+ around_suite do |suite|
1168
+ use_fixture AppFixture
1169
+ suite.run
1170
+ end
1076
1171
  ```
1077
1172
 
1078
1173
  ```ruby
@@ -1098,8 +1193,6 @@ end
1098
1193
  # smartest/example_test.rb
1099
1194
  require "test_helper"
1100
1195
 
1101
- use_fixture AppFixture
1102
-
1103
1196
  test("GET /health") do |client:|
1104
1197
  expect(client.get("/health").status).to eq(200)
1105
1198
  end
@@ -1117,7 +1210,6 @@ Possible future features:
1117
1210
  - richer matchers
1118
1211
  - block expectations
1119
1212
  - `raise_error`
1120
- - hooks
1121
1213
  - file-scoped fixtures
1122
1214
  - parallel execution
1123
1215
  - watch mode
data/lib/smartest/dsl.rb CHANGED
@@ -3,24 +3,31 @@
3
3
  module Smartest
4
4
  module DSL
5
5
  def test(name, **metadata, &block)
6
+ location = caller_locations(1, 1).first
7
+
6
8
  Smartest.suite.tests.add(
7
9
  TestCase.new(
8
10
  name: name,
9
11
  metadata: metadata,
10
12
  block: block,
11
- location: caller_locations(1, 1).first
13
+ location: location,
14
+ around_test_hooks: Smartest.suite.around_test_hooks_for(location)
12
15
  )
13
16
  )
14
17
  end
15
18
 
16
- def use_fixture(klass)
17
- Smartest.suite.fixture_classes.add(klass)
19
+ def around_suite(&block)
20
+ raise ArgumentError, "around_suite block is required" unless block
21
+
22
+ Smartest.suite.around_suite_hooks << block
18
23
  end
19
24
 
20
- def use_matcher(matcher_module)
21
- Smartest.suite.matcher_modules.add(matcher_module)
25
+ def around_test(&block)
26
+ raise ArgumentError, "around_test block is required" unless block
27
+
28
+ Smartest.suite.add_around_test_hook(caller_locations(1, 1).first, block)
22
29
  end
23
30
 
24
- private :test, :use_fixture, :use_matcher
31
+ private :test, :around_suite, :around_test
25
32
  end
26
33
  end
@@ -48,5 +48,21 @@ module Smartest
48
48
 
49
49
  class InvalidFixtureParameterError < Error; end
50
50
 
51
+ class AroundSuiteRunError < Error; end
52
+
53
+ class AroundTestFixtureScopeError < Error
54
+ def initialize(fixture_class, fixture_names)
55
+ class_name = fixture_class.name || fixture_class.inspect
56
+ names = fixture_names.map { |fixture_name| ":#{fixture_name}" }.join(", ")
57
+
58
+ super(
59
+ "#{class_name} cannot be registered from around_test because it defines suite-scoped fixtures: #{names}. " \
60
+ "Register fixture classes with suite_fixture from around_suite instead."
61
+ )
62
+ end
63
+ end
64
+
65
+ class AroundTestRunError < Error; end
66
+
51
67
  class AssertionFailed < Error; end
52
68
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smartest
4
+ class AroundSuiteContext
5
+ def initialize(suite)
6
+ @suite = suite
7
+ end
8
+
9
+ def call(hook, suite_run)
10
+ @suite.around_suite_hook do
11
+ instance_exec(suite_run, &hook)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def use_fixture(klass)
18
+ @suite.fixture_classes.add(klass)
19
+ end
20
+
21
+ def use_matcher(matcher_module)
22
+ @suite.matcher_modules.add(matcher_module)
23
+ end
24
+
25
+ def around_test(&block)
26
+ raise ArgumentError, "around_test block is required" unless block
27
+
28
+ @suite.add_around_test_hook(caller_locations(1, 1).first, block)
29
+ end
30
+ end
31
+
32
+ class AroundTestContext
33
+ def initialize(test_run)
34
+ @test_run = test_run
35
+ end
36
+
37
+ def call(hook, run_target = @test_run)
38
+ instance_exec(run_target, &hook)
39
+ end
40
+
41
+ private
42
+
43
+ def use_fixture(klass)
44
+ @test_run.add_fixture_class(klass)
45
+ end
46
+
47
+ def use_matcher(matcher_module)
48
+ @test_run.add_matcher_module(matcher_module)
49
+ end
50
+ end
51
+ end
@@ -18,7 +18,10 @@ module Smartest
18
18
  require matcher_file
19
19
  end
20
20
 
21
- use_matcher PredicateMatcher
21
+ around_suite do |suite|
22
+ use_matcher PredicateMatcher
23
+ suite.run
24
+ end
22
25
  RUBY
23
26
  "smartest/matchers/predicate_matcher.rb" => <<~RUBY,
24
27
  # frozen_string_literal: true
@@ -19,14 +19,19 @@ module Smartest
19
19
  @io.puts "#{mark} #{result.test_case.name}"
20
20
  end
21
21
 
22
- def finish(results, suite_cleanup_errors: [])
22
+ def finish(results, suite_cleanup_errors: [], suite_errors: [])
23
23
  failures = results.select(&:failed?)
24
24
 
25
25
  report_failures(failures) if failures.any?
26
+ report_suite_errors(suite_errors) if suite_errors.any?
26
27
  report_suite_cleanup_errors(suite_cleanup_errors) if suite_cleanup_errors.any?
27
28
 
28
29
  @io.puts
29
30
  summary = "#{results.count} #{results.count == 1 ? 'test' : 'tests'}, #{results.count(&:passed?)} passed, #{failures.count} failed"
31
+ if suite_errors.any?
32
+ suite_label = suite_errors.count == 1 ? "suite failure" : "suite failures"
33
+ summary = "#{summary}, #{suite_errors.count} #{suite_label}"
34
+ end
30
35
  if suite_cleanup_errors.any?
31
36
  cleanup_label = suite_cleanup_errors.count == 1 ? "suite cleanup" : "suite cleanups"
32
37
  summary = "#{summary}, #{suite_cleanup_errors.count} #{cleanup_label} failed"
@@ -50,6 +55,18 @@ module Smartest
50
55
  end
51
56
  end
52
57
 
58
+ def report_suite_errors(errors)
59
+ @io.puts
60
+ @io.puts "Suite failures:"
61
+ @io.puts
62
+
63
+ errors.each_with_index do |error, index|
64
+ @io.puts "#{index + 1}) suite"
65
+ report_error(error)
66
+ @io.puts
67
+ end
68
+ end
69
+
53
70
  def report_suite_cleanup_errors(errors)
54
71
  @io.puts
55
72
  @io.puts "Suite cleanup failures:"