cake-tester 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 +7 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +24 -0
- data/.solargraph.yml +22 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +79 -0
- data/LICENSE +373 -0
- data/README.md +88 -0
- data/Rakefile +10 -0
- data/bin/cake +34 -0
- data/bin/collector.rb +90 -0
- data/bin/runner.rb +133 -0
- data/bin/settings.rb +36 -0
- data/cake-tester.gemspec +18 -0
- data/lib/cake.rb +6 -0
- data/lib/contextual/child.rb +20 -0
- data/lib/contextual/context.rb +32 -0
- data/lib/contextual/group.rb +45 -0
- data/lib/contextual/node.rb +80 -0
- data/lib/contextual/parent.rb +191 -0
- data/lib/contextual/test.rb +151 -0
- data/lib/contextual/test_runner.rb +71 -0
- data/lib/expect.rb +154 -0
- data/lib/helpers/filter_settings.rb +94 -0
- data/lib/helpers/printer.rb +146 -0
- data/lib/test_failure.rb +54 -0
- data/lib/test_message.rb +23 -0
- data/lib/test_neutral.rb +31 -0
- data/lib/test_options.rb +25 -0
- data/lib/test_pass.rb +23 -0
- data/lib/test_result.rb +70 -0
- data/test/test_basic.cake.rb +12 -0
- data/test/test_context.cake.rb +102 -0
- data/test/test_expects.cake.rb +48 -0
- data/test/test_failures.cake.rb +46 -0
- data/test/test_group.cake.rb +20 -0
- data/test/test_runner.cake.rb +20 -0
- data/test/test_skip.cake.rb +37 -0
- metadata +83 -0
data/bin/collector.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../lib/helpers/printer'
|
4
|
+
|
5
|
+
# Collector holds totals for all collectors and prints a summary
|
6
|
+
class Collector
|
7
|
+
# @return [Integer]
|
8
|
+
attr_reader :total, :end_index
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@collectors = []
|
12
|
+
@total = 0
|
13
|
+
@successes = 0
|
14
|
+
@failures = 0
|
15
|
+
@neutrals = 0
|
16
|
+
@end_index = 0
|
17
|
+
end
|
18
|
+
|
19
|
+
# Adds a collector and updates the total, successes, failures, and neutrals
|
20
|
+
# @param collector [TestRunnerCollector]
|
21
|
+
def add_collector(collector)
|
22
|
+
@collectors << collector
|
23
|
+
@total += collector.total
|
24
|
+
@successes += collector.successes
|
25
|
+
@failures += collector.failures
|
26
|
+
@neutrals += collector.neutrals
|
27
|
+
@end_index = collector.end_index
|
28
|
+
end
|
29
|
+
|
30
|
+
def print_message(verbose)
|
31
|
+
summary = Printer.summary(@total, @successes, @failures, @neutrals)
|
32
|
+
|
33
|
+
if @failures.positive?
|
34
|
+
Printer.fail(summary)
|
35
|
+
@collectors.each(&:print_errors)
|
36
|
+
elsif @successes.zero?
|
37
|
+
Printer.neutral(summary)
|
38
|
+
else
|
39
|
+
Printer.pass(summary)
|
40
|
+
end
|
41
|
+
|
42
|
+
return unless verbose
|
43
|
+
|
44
|
+
@collectors.each(&:print_message)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# TestRunnerCollector holds totals for a single test runner
|
49
|
+
class Test_Runner_Controller
|
50
|
+
# @return [Integer]
|
51
|
+
attr_reader :total
|
52
|
+
# @return [Integer]
|
53
|
+
attr_reader :successes
|
54
|
+
# @return [Integer]
|
55
|
+
attr_reader :failures
|
56
|
+
# @return [Integer]
|
57
|
+
attr_reader :neutrals
|
58
|
+
# @return [Integer]
|
59
|
+
attr_reader :end_index
|
60
|
+
|
61
|
+
# Initializes a new instance of the class.
|
62
|
+
#
|
63
|
+
# @param output [Array<String>] Total output of the TestRunner
|
64
|
+
# @param total [Integer] Total amount of tests from the TestRunner
|
65
|
+
# @param successes [Integer] Total amount of successes from the TestRunner
|
66
|
+
# @param failures [Integer] Total amount of failures from the TestRunner
|
67
|
+
# @param neutrals [Integer] Total amount of neutrals from the TestRunner
|
68
|
+
# @param end_index [Integer] Last line given by the TestRunner
|
69
|
+
def initialize(output, total:, successes:, failures:, neutrals:, end_index:)
|
70
|
+
@output = output
|
71
|
+
@total = total
|
72
|
+
@successes = successes
|
73
|
+
@failures = failures
|
74
|
+
@neutrals = neutrals
|
75
|
+
@end_index = end_index
|
76
|
+
end
|
77
|
+
|
78
|
+
def print_message
|
79
|
+
# This already has formatting from the printer, just pass through the original message
|
80
|
+
@output.each do |message|
|
81
|
+
puts message
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def print_errors
|
86
|
+
return unless @failures.positive?
|
87
|
+
|
88
|
+
print_message
|
89
|
+
end
|
90
|
+
end
|
data/bin/runner.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'collector'
|
4
|
+
|
5
|
+
# Controls IO and output
|
6
|
+
class Runner
|
7
|
+
# Retrieves all files with a cake.rb extension in the current directory
|
8
|
+
# @return [Array<String>]
|
9
|
+
def cake_file_list
|
10
|
+
# Get all files in the current directory, including subdirectories
|
11
|
+
Dir.glob('**/*.cake.rb')
|
12
|
+
end
|
13
|
+
|
14
|
+
# Runs the tests
|
15
|
+
# @param settings [Settings]
|
16
|
+
# @param cake_list [Array<String>]
|
17
|
+
def run(settings, cake_list)
|
18
|
+
collector = Collector.new
|
19
|
+
process_args = settings.test_filter.to_properties
|
20
|
+
|
21
|
+
cake_list.each do |file|
|
22
|
+
next if settings.file_filter && !file.include?(settings.file_filter)
|
23
|
+
|
24
|
+
r, w = IO.pipe
|
25
|
+
pid = Process.spawn('ruby', file, process_args.join(' '), out: w, err: %i[child out])
|
26
|
+
w.close
|
27
|
+
pid, status = Process.wait2
|
28
|
+
output = r.read
|
29
|
+
r.close
|
30
|
+
|
31
|
+
collectors = test_runner_output_parser(output)
|
32
|
+
collectors.each do |c|
|
33
|
+
collector.add_collector(c)
|
34
|
+
end
|
35
|
+
|
36
|
+
puts "#{collector.total} tests found in #{file}..." if settings.verbose
|
37
|
+
end
|
38
|
+
|
39
|
+
collector.print_message(settings.verbose || settings.vs_code)
|
40
|
+
return if settings.vs_code
|
41
|
+
|
42
|
+
# This makes sure to clear out any color changes
|
43
|
+
Printer.neutral('')
|
44
|
+
end
|
45
|
+
|
46
|
+
def show_help
|
47
|
+
puts 'Usage: cake-tester [options]'
|
48
|
+
puts ' -h, --help Show this help message'
|
49
|
+
puts ' -i, --interactive Interactive mode'
|
50
|
+
puts ' -v, --verbose Verbose output'
|
51
|
+
puts ''
|
52
|
+
puts 'Test Filters: '
|
53
|
+
puts <<~HELP
|
54
|
+
-t General search: -t "foo" Run all tests, groups, and runners with "foo" in the title
|
55
|
+
--tt Test search: --tt "cool test" Run all tests with the phrase "cool test" in the title
|
56
|
+
--tte Test search, exact: --tte "should pass when true" Runs only the test that matches the phrase exactly.
|
57
|
+
--tg Group search: --tg "bar" Run all groups matching "bar" in the title
|
58
|
+
--tge Group search, exact: --tge "API Endpoints" Runs all groups exactly matching the phrase "API Endpoints"
|
59
|
+
--tr Test Runner search: --tr "Models" Runs all test runners with "Models" in the title
|
60
|
+
--tre Test Runner search, exact: --tre "Models - User" Runs test runners that exactly match the phrase "Models - User"
|
61
|
+
HELP
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
# Parses output from the process
|
67
|
+
# @param output [String]
|
68
|
+
# @return [Array<TestRunnerCollector>]
|
69
|
+
def test_runner_output_parser(output)
|
70
|
+
test_runner_collectors = []
|
71
|
+
lines = output.lines
|
72
|
+
cursor = 0
|
73
|
+
|
74
|
+
while cursor < (lines.length - 1)
|
75
|
+
line = lines[cursor]
|
76
|
+
test_runner_collector = test_runner_output_parse(lines, cursor)
|
77
|
+
cursor = test_runner_collector.end_index
|
78
|
+
test_runner_collectors << test_runner_collector
|
79
|
+
end
|
80
|
+
|
81
|
+
test_runner_collectors
|
82
|
+
end
|
83
|
+
|
84
|
+
# @param lines [Array<String>] All output lines
|
85
|
+
# @param cursor [Integer] Current index of the cursor in the output
|
86
|
+
# @return [TestRunnerCollector]
|
87
|
+
def test_runner_output_parse(lines, cursor)
|
88
|
+
test_output = []
|
89
|
+
total = 0
|
90
|
+
successes = 0
|
91
|
+
failures = 0
|
92
|
+
neutrals = 0
|
93
|
+
at_summary_line = -1
|
94
|
+
summary_line = ' - Summary: ---------------'
|
95
|
+
total_line = /(\d*) tests ran\./
|
96
|
+
success_line = /(\d*) passed\./
|
97
|
+
failed_line = /(\d*) failed\./
|
98
|
+
neutral_line = %r{(\d*) skipped/inconclusive\.}
|
99
|
+
i = cursor
|
100
|
+
while i < lines.length - 1 || at_summary_line == 7
|
101
|
+
line = lines[i]
|
102
|
+
at_summary_line = 0 if line.include? summary_line
|
103
|
+
|
104
|
+
case at_summary_line
|
105
|
+
when 2
|
106
|
+
total = total_line.match(line)[1].to_i
|
107
|
+
when 3
|
108
|
+
successes = success_line.match(line)[1].to_i
|
109
|
+
when 4
|
110
|
+
failures = failed_line.match(line)[1].to_i
|
111
|
+
when 5
|
112
|
+
neutrals = neutral_line.match(line)[1].to_i
|
113
|
+
end
|
114
|
+
|
115
|
+
if at_summary_line > -1
|
116
|
+
at_summary_line += 1
|
117
|
+
else
|
118
|
+
test_output << line
|
119
|
+
end
|
120
|
+
|
121
|
+
i += 1
|
122
|
+
end
|
123
|
+
|
124
|
+
Test_Runner_Controller.new(
|
125
|
+
test_output,
|
126
|
+
total: total,
|
127
|
+
successes: successes,
|
128
|
+
failures: failures,
|
129
|
+
neutrals: neutrals,
|
130
|
+
end_index: i
|
131
|
+
)
|
132
|
+
end
|
133
|
+
end
|
data/bin/settings.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../lib/helpers/filter_settings'
|
4
|
+
|
5
|
+
# Converts args into easily accessible settings
|
6
|
+
class Settings
|
7
|
+
attr_reader :verbose, :file_filter, :vs_code, :interactive, :test_filter, :show_help
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@verbose = ARGV.include?('-v') || ARGV.include?('--verbose')
|
11
|
+
@file_filter = get_from_args('-f')
|
12
|
+
@vs_code = ARGV.include? '--vs-code'
|
13
|
+
@interactive = ARGV.include?('-i') || ARGV.include?('--interactive')
|
14
|
+
@show_help = ARGV.include?('-h') || ARGV.include?('--help')
|
15
|
+
@test_filter = FilterSettings.new(
|
16
|
+
general_search_term: get_from_args('-t'),
|
17
|
+
test_filter_term: get_from_args('--tt'),
|
18
|
+
test_search_for: get_from_args('--tte'),
|
19
|
+
group_filter_term: get_from_args('--tg'),
|
20
|
+
group_search_for: get_from_args('--tge'),
|
21
|
+
test_runner_filter_term: get_from_args('--tr'),
|
22
|
+
test_runner_search_for: get_from_args('--tre')
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# @param [String] flag
|
29
|
+
# @return [String, Nil]
|
30
|
+
def get_from_args(flag)
|
31
|
+
index = ARGV.find_index(flag)
|
32
|
+
return ARGV[index + 1] if index && index != ARGV.length - 1
|
33
|
+
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
data/cake-tester.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'cake-tester'
|
5
|
+
gem.version = '0.2.0'
|
6
|
+
gem.authors = ['Polyhedra', 'C. Lee Spruit']
|
7
|
+
gem.summary = 'The lightweight, explicit testing framework for Ruby.'
|
8
|
+
gem.description = ''
|
9
|
+
gem.homepage = 'https://github.com/Polyhedra-Studio/Cake-Ruby'
|
10
|
+
gem.license = 'MPL-2.0'
|
11
|
+
gem.required_ruby_version = '>= 2.7.0'
|
12
|
+
gem.metadata['rubygems_mfa_required'] = 'true'
|
13
|
+
|
14
|
+
gem.files = `git ls-files -z`.split("\x0")
|
15
|
+
# gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
16
|
+
gem.executables << 'cake'
|
17
|
+
gem.require_paths = ['lib']
|
18
|
+
end
|
data/lib/cake.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../test_options'
|
4
|
+
|
5
|
+
# Contextual is a wrapper for Node and Node attributes
|
6
|
+
module Contextual
|
7
|
+
# A Child is a Node that has a parent
|
8
|
+
module Child
|
9
|
+
# Handles assigning parent information to child
|
10
|
+
#
|
11
|
+
# @param parent_options [TestOptions] Options from parent to copy to child
|
12
|
+
def assign_parent(parent_options)
|
13
|
+
@parent_count = 0 if @parent_count.nil?
|
14
|
+
@parent_count += 1
|
15
|
+
|
16
|
+
@options = TestOptions.new if @options.nil?
|
17
|
+
@options.map_from_parent(parent_options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
# The Context is used to pass information between different stages of a Test.
|
6
|
+
# Context is inherited from parent to child, but not sibling to sibling.
|
7
|
+
# The Context object is an OpenStruct, which means you can dynamically assign
|
8
|
+
# as needed.
|
9
|
+
class Context < OpenStruct
|
10
|
+
# @return [Object] actual expected object, ideally will be set during the
|
11
|
+
# Contexual::Node#run_action step
|
12
|
+
attr_accessor :actual
|
13
|
+
|
14
|
+
# @return [Object] expected
|
15
|
+
attr_accessor :expected
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
super
|
19
|
+
@expected = nil
|
20
|
+
@actual = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [Context] parent_context
|
24
|
+
def apply(parent_context)
|
25
|
+
@expected = parent_context.expected
|
26
|
+
@actual = parent_context.actual
|
27
|
+
|
28
|
+
parent_context.each_pair do |key, value|
|
29
|
+
self[key] = value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'child'
|
4
|
+
require_relative 'node'
|
5
|
+
require_relative 'parent'
|
6
|
+
|
7
|
+
# A Group is a organizational class that holds other tests. You can nest as many
|
8
|
+
# groups as you like.
|
9
|
+
class Group < Contextual::Node
|
10
|
+
include Contextual::Child
|
11
|
+
include Contextual::Parent
|
12
|
+
|
13
|
+
def initialize(
|
14
|
+
title,
|
15
|
+
children = [],
|
16
|
+
setup: nil,
|
17
|
+
teardown: nil,
|
18
|
+
options: nil,
|
19
|
+
skip: false
|
20
|
+
)
|
21
|
+
super(title, setup: setup, teardown: teardown, options: options, skip: skip)
|
22
|
+
set_parent(children)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Should this run with the current filter settings
|
26
|
+
# @param filter_settings [FilterSettings]
|
27
|
+
# @return [Boolean]
|
28
|
+
def should_run_with_filter(filter_settings)
|
29
|
+
# This should run failrly close to testRunner's version
|
30
|
+
return @title == filter_settings.group_search_for if filter_settings.has_group_search_for
|
31
|
+
return @title.include? filter_settings.group_filter_term if filter_settings.has_group_filter_term
|
32
|
+
|
33
|
+
should_run_with_search_term_with_children(filter_settings)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Report results, if any
|
37
|
+
# @param filter_settings [FilterSettings]
|
38
|
+
# @return [TestResult, Nil]
|
39
|
+
def report(filter_settings)
|
40
|
+
@result.report(@parent_count)
|
41
|
+
return if @skip
|
42
|
+
|
43
|
+
report_children(filter_settings)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../test_options'
|
4
|
+
|
5
|
+
module Contextual
|
6
|
+
# @abstract Base class for testing nodes, like TestRunner, Group, and Test.
|
7
|
+
class Node
|
8
|
+
attr_reader :skip, :result
|
9
|
+
|
10
|
+
def initialize(title, setup: nil, teardown: nil, options: nil, skip: false)
|
11
|
+
@title = title
|
12
|
+
@setup = setup
|
13
|
+
@teardown = teardown
|
14
|
+
@options = options.nil? ? TestOptions.new : options
|
15
|
+
@skip = skip
|
16
|
+
@context = Context.new
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param current_context [Context] Parent or root context
|
20
|
+
# @param filter_settings [FilterSettings]
|
21
|
+
# @return [TestResult]
|
22
|
+
def run(current_context, filter_settings)
|
23
|
+
if @skip
|
24
|
+
@result = TestNeutral.new(@title, 'Skipped')
|
25
|
+
else
|
26
|
+
@context.apply(current_context)
|
27
|
+
@result = get_result(filter_settings)
|
28
|
+
end
|
29
|
+
@result
|
30
|
+
end
|
31
|
+
|
32
|
+
# Runs the setup hook
|
33
|
+
# @return [TestResult, Nil] Returns a TestFailure if the setup fails
|
34
|
+
def run_setup
|
35
|
+
return if @setup.nil?
|
36
|
+
|
37
|
+
begin
|
38
|
+
@setup.call(@context)
|
39
|
+
nil
|
40
|
+
rescue StandardError => e
|
41
|
+
@result = TestFailure.new(@title, 'Failed during setup', e)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Runs the teardown hook
|
46
|
+
# @return [TestResult, Nil] Returns a TestFailure if the teardown fails
|
47
|
+
def run_teardown
|
48
|
+
return if @teardown.nil?
|
49
|
+
|
50
|
+
begin
|
51
|
+
@teardown.call(@context)
|
52
|
+
nil
|
53
|
+
rescue StandardError => e
|
54
|
+
if @result.successes.positive?
|
55
|
+
TestFailure.new(@title, 'Tests passed, but failed during teardown.', e)
|
56
|
+
else
|
57
|
+
TestFailure.new(@title, 'Failed during teardown', e)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @abstract Override this method in your subclass
|
63
|
+
# @param _filter_settings [FilterSettings] not used
|
64
|
+
# @return [TestResult]
|
65
|
+
def get_result(_filter_settings)
|
66
|
+
# This has been marked as skipped - do nothing
|
67
|
+
TestNeutral.new(@title, 'Skipped') if @skip
|
68
|
+
end
|
69
|
+
|
70
|
+
# Should this run with the current filter settings, checking general search
|
71
|
+
# @param filter_settings [FilterSettings]
|
72
|
+
# @return [Boolean]
|
73
|
+
def should_run_with_search_term(filter_settings)
|
74
|
+
# Check if the general search term applies here
|
75
|
+
return @title.include?(filter_settings.general_search_term) if filter_settings.has_general_search_term
|
76
|
+
|
77
|
+
true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../test_failure'
|
4
|
+
require_relative '../test_neutral'
|
5
|
+
require_relative '../test_pass'
|
6
|
+
|
7
|
+
module Contextual
|
8
|
+
# A Parent is a Node that can have children
|
9
|
+
module Parent
|
10
|
+
# @return [Array<Node>]
|
11
|
+
attr_reader :children
|
12
|
+
|
13
|
+
# @param children [Array<Node>]
|
14
|
+
def set_parent(children = [])
|
15
|
+
@children = children
|
16
|
+
@test_fail_count = 0
|
17
|
+
@test_success_count = 0
|
18
|
+
@test_neutral_count = 0
|
19
|
+
@filter_applies_to_children = false
|
20
|
+
assign_children
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param parent_options [TestOptions]
|
24
|
+
# @return [void]
|
25
|
+
def assign_parent(parent_options)
|
26
|
+
super
|
27
|
+
@children.each do |child|
|
28
|
+
next unless child.respond_to?(:assign_parent)
|
29
|
+
|
30
|
+
child.assign_parent(parent_options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param filter_settings [FilterSettings]
|
35
|
+
# @return [Boolean]
|
36
|
+
def should_run_with_search_term_with_children(filter_settings)
|
37
|
+
# Check if the general search term applies here
|
38
|
+
return true if should_run_with_search_term(filter_settings)
|
39
|
+
|
40
|
+
if filter_settings.has_test_filter_term ||
|
41
|
+
filter_settings.has_test_search_for
|
42
|
+
should_run_filter_on_children(filter_settings)
|
43
|
+
end
|
44
|
+
|
45
|
+
# No applicable filter
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param filter_settings [FilterSettings]
|
50
|
+
# @return [void]
|
51
|
+
def report_children(filter_settings)
|
52
|
+
@children.each do |child|
|
53
|
+
if @filter_applies_to_children
|
54
|
+
child.report(filter_settings) if child.should_run_with_filter(filter_settings)
|
55
|
+
else
|
56
|
+
child.report(filter_settings)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param filter_settings [FilterSettings]
|
62
|
+
# @return [TestResult]
|
63
|
+
def get_result(filter_settings)
|
64
|
+
super
|
65
|
+
|
66
|
+
# This is just a stub if there's no children - do nothing
|
67
|
+
return TestNeutral.new(@title, 'Empty - no tests') if @children.empty?
|
68
|
+
|
69
|
+
@result = run_setup
|
70
|
+
|
71
|
+
return @result unless @result.nil?
|
72
|
+
|
73
|
+
@result = get_result_children(filter_settings)
|
74
|
+
|
75
|
+
teardown_failure = run_teardown
|
76
|
+
@result = teardown_failure unless teardown_failure.nil?
|
77
|
+
|
78
|
+
@result
|
79
|
+
end
|
80
|
+
|
81
|
+
# Counts all successes, including children
|
82
|
+
# @return [Integer]
|
83
|
+
def successes
|
84
|
+
current_success_count = @test_success_count
|
85
|
+
@children.each do |child|
|
86
|
+
current_success_count += child.successes if child.instance_of? Group
|
87
|
+
end
|
88
|
+
current_success_count
|
89
|
+
end
|
90
|
+
|
91
|
+
# Counts all failures, including children
|
92
|
+
# @return [Integer]
|
93
|
+
def failures
|
94
|
+
current_fail_count = @test_fail_count
|
95
|
+
@children.each do |child|
|
96
|
+
current_fail_count += child.failures if child.instance_of? Group
|
97
|
+
end
|
98
|
+
current_fail_count
|
99
|
+
end
|
100
|
+
|
101
|
+
# Counts all neutrals, including children
|
102
|
+
# @return [Integer]
|
103
|
+
def neutrals
|
104
|
+
current_neutral_count = @test_neutral_count
|
105
|
+
@children.each do |child|
|
106
|
+
current_neutral_count += child.neutrals if child.instance_of? Group
|
107
|
+
end
|
108
|
+
current_neutral_count
|
109
|
+
end
|
110
|
+
|
111
|
+
# Counts all tests, including children
|
112
|
+
# @return [Integer]
|
113
|
+
def total
|
114
|
+
test_count = 0
|
115
|
+
@children.each do |child|
|
116
|
+
if child.instance_of? Group
|
117
|
+
test_count += child.total
|
118
|
+
elsif child.instance_of?(Test) && child.ran_successfully
|
119
|
+
test_count += 1
|
120
|
+
end
|
121
|
+
end
|
122
|
+
test_count
|
123
|
+
end
|
124
|
+
|
125
|
+
# Handles when there is a error that makes it impossible to run
|
126
|
+
# @return [TestFailure]
|
127
|
+
def critical_inconclusive
|
128
|
+
super
|
129
|
+
@children.each do |child|
|
130
|
+
child.critical_inconclusive
|
131
|
+
@test_neutral_count += 1
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
# Notifies the children that they have a parent
|
138
|
+
# @return [void]
|
139
|
+
def assign_children
|
140
|
+
@children.each do |child|
|
141
|
+
if child.respond_to?(:assign_parent)
|
142
|
+
child.assign_parent(@options)
|
143
|
+
else
|
144
|
+
# Someone tried to add something that isn't a child into their test suite
|
145
|
+
throw 'Only objects like Group or Test can be children of another node.'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# @param filter_settings [FilterSettings]
|
151
|
+
# @return [Boolean]
|
152
|
+
def should_run_filter_on_children(filter_settings)
|
153
|
+
@filter_applies_to_children = true
|
154
|
+
@children.any? { |child| child.should_run_with_filter(filter_settings) }
|
155
|
+
end
|
156
|
+
|
157
|
+
# @param filter_settings [FilterSettings]
|
158
|
+
# @return [TestResult]
|
159
|
+
def get_result_children(filter_settings)
|
160
|
+
child_success_count = 0
|
161
|
+
child_fail_count = 0
|
162
|
+
@children.each do |child|
|
163
|
+
result = get_result_child(filter_settings, child)
|
164
|
+
next if result.nil?
|
165
|
+
|
166
|
+
child_success_count += result.successes
|
167
|
+
child_fail_count += result.failures
|
168
|
+
|
169
|
+
next unless child.instance_of? Test
|
170
|
+
|
171
|
+
@test_success_count += result.successes
|
172
|
+
@test_fail_count += result.failures
|
173
|
+
@test_neutral_count += result.neutrals
|
174
|
+
end
|
175
|
+
|
176
|
+
return TestFailure.new(@title, 'Some tests failed') if child_fail_count.positive?
|
177
|
+
return TestNeutral.new(@title) if child_success_count.zero?
|
178
|
+
|
179
|
+
TestPass.new(@title)
|
180
|
+
end
|
181
|
+
|
182
|
+
# @param filter_settings [FilterSettings]
|
183
|
+
# @param child [Node]
|
184
|
+
# @return [TestResult, nil]
|
185
|
+
def get_result_child(filter_settings, child)
|
186
|
+
return if @filter_applies_to_children && !child.should_run_with_filter(filter_settings)
|
187
|
+
|
188
|
+
child.run(@context.dup, filter_settings)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|