thoreau 0.2.1 → 0.2.2
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 +4 -4
- data/lib/thoreau/auto_run.rb +5 -0
- data/lib/thoreau/case/case_builder.rb +114 -0
- data/lib/thoreau/case/context_builder.rb +3 -4
- data/lib/thoreau/case/multi_clan_case_builder.rb +27 -0
- data/lib/thoreau/case/suite_runner.rb +61 -0
- data/lib/thoreau/configuration.rb +9 -0
- data/lib/thoreau/dsl/appendix.rb +5 -5
- data/lib/thoreau/dsl/clan.rb +94 -0
- data/lib/thoreau/dsl/suite_context.rb +46 -0
- data/lib/thoreau/dsl/test_cases.rb +20 -0
- data/lib/thoreau/dsl.rb +39 -26
- data/lib/thoreau/errors.rb +15 -0
- data/lib/thoreau/legacy_expected_outcomes.rb +53 -0
- data/lib/thoreau/logging.rb +36 -0
- data/lib/thoreau/models/appendix.rb +17 -0
- data/lib/thoreau/models/outcome.rb +33 -0
- data/lib/thoreau/models/setup.rb +17 -0
- data/lib/thoreau/models/test_case.rb +110 -0
- data/lib/thoreau/models/test_clan.rb +37 -0
- data/lib/thoreau/models/test_family.rb +53 -0
- data/lib/thoreau/test_suite.rb +17 -11
- data/lib/thoreau/test_suite_data.rb +27 -0
- data/lib/thoreau/util.rb +29 -0
- data/lib/thoreau/version.rb +1 -1
- data/lib/thoreau.rb +8 -2
- metadata +34 -11
- data/lib/thoreau/case/builder.rb +0 -110
- data/lib/thoreau/case/runner.rb +0 -42
- data/lib/thoreau/case.rb +0 -97
- data/lib/thoreau/dsl/context.rb +0 -55
- data/lib/thoreau/dsl/groups.rb +0 -20
- data/lib/thoreau/dsl/groups_support.rb +0 -58
- data/lib/thoreau/legacy_results.rb +0 -8
- data/lib/thoreau/setup.rb +0 -15
- data/lib/thoreau/spec_group.rb +0 -45
data/lib/thoreau/util.rb
CHANGED
@@ -10,3 +10,32 @@ def combos_of(entries)
|
|
10
10
|
combos_of_rest.map { |r| r.merge(f) }
|
11
11
|
end
|
12
12
|
end
|
13
|
+
|
14
|
+
module Thoreau
|
15
|
+
class HashUtil
|
16
|
+
|
17
|
+
# prop_map is a map from canonical property name to all versions
|
18
|
+
def self.normalize_props(hash, prop_map, include_all_normalized_props = true)
|
19
|
+
prop_map.reduce(Hash.new) do |memo, (k, v)|
|
20
|
+
value = one_of_these(hash, v, include_all_normalized_props ? nil : :secret_default_value)
|
21
|
+
memo[k] = value unless value == :secret_default_value
|
22
|
+
memo
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.one_of_these(hash, pick_one_of_these_keys, default_value = nil)
|
27
|
+
keys_present = pick_one_of_these_keys.intersection hash.keys
|
28
|
+
|
29
|
+
if keys_present.size > 1
|
30
|
+
logger.error "Only of of these keys is allowed: #{keys_present.to_sentence}"
|
31
|
+
end
|
32
|
+
|
33
|
+
pick_one_of_these_keys.each do |k|
|
34
|
+
return hash[k] if hash.key?(k)
|
35
|
+
end
|
36
|
+
|
37
|
+
default_value
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
data/lib/thoreau/version.rb
CHANGED
data/lib/thoreau.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
require_relative 'thoreau/version'
|
2
2
|
require_relative 'thoreau/dsl'
|
3
|
+
require_relative 'thoreau/configuration'
|
3
4
|
|
4
5
|
module Thoreau
|
5
|
-
|
6
|
+
def self.configure &block
|
7
|
+
block.call configuration
|
6
8
|
end
|
7
|
-
|
9
|
+
|
10
|
+
def self.configuration
|
11
|
+
@configuration ||= Configuration.new
|
12
|
+
end
|
13
|
+
|
8
14
|
end
|
9
15
|
|
10
16
|
if defined?(RSpec)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thoreau
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Peterson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -30,6 +30,20 @@ dependencies:
|
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '7'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rainbow
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
33
47
|
- !ruby/object:Gem::Dependency
|
34
48
|
name: bundler
|
35
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,23 +122,32 @@ extensions: []
|
|
108
122
|
extra_rdoc_files: []
|
109
123
|
files:
|
110
124
|
- lib/thoreau.rb
|
111
|
-
- lib/thoreau/
|
112
|
-
- lib/thoreau/case/
|
125
|
+
- lib/thoreau/auto_run.rb
|
126
|
+
- lib/thoreau/case/case_builder.rb
|
113
127
|
- lib/thoreau/case/context_builder.rb
|
114
|
-
- lib/thoreau/case/
|
128
|
+
- lib/thoreau/case/multi_clan_case_builder.rb
|
129
|
+
- lib/thoreau/case/suite_runner.rb
|
130
|
+
- lib/thoreau/configuration.rb
|
115
131
|
- lib/thoreau/dsl.rb
|
116
132
|
- lib/thoreau/dsl/appendix.rb
|
117
|
-
- lib/thoreau/dsl/
|
133
|
+
- lib/thoreau/dsl/clan.rb
|
118
134
|
- lib/thoreau/dsl/expanded.rb
|
119
|
-
- lib/thoreau/dsl/
|
120
|
-
- lib/thoreau/dsl/
|
121
|
-
- lib/thoreau/
|
135
|
+
- lib/thoreau/dsl/suite_context.rb
|
136
|
+
- lib/thoreau/dsl/test_cases.rb
|
137
|
+
- lib/thoreau/errors.rb
|
138
|
+
- lib/thoreau/legacy_expected_outcomes.rb
|
139
|
+
- lib/thoreau/logging.rb
|
140
|
+
- lib/thoreau/models/appendix.rb
|
141
|
+
- lib/thoreau/models/outcome.rb
|
142
|
+
- lib/thoreau/models/setup.rb
|
143
|
+
- lib/thoreau/models/test_case.rb
|
144
|
+
- lib/thoreau/models/test_clan.rb
|
145
|
+
- lib/thoreau/models/test_family.rb
|
122
146
|
- lib/thoreau/rspec.rb
|
123
147
|
- lib/thoreau/rspec/configuration.rb
|
124
148
|
- lib/thoreau/rspec/example_helpers.rb
|
125
|
-
- lib/thoreau/setup.rb
|
126
|
-
- lib/thoreau/spec_group.rb
|
127
149
|
- lib/thoreau/test_suite.rb
|
150
|
+
- lib/thoreau/test_suite_data.rb
|
128
151
|
- lib/thoreau/util.rb
|
129
152
|
- lib/thoreau/version.rb
|
130
153
|
homepage: https://github.com/ndp/thoreau
|
data/lib/thoreau/case/builder.rb
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
module Thoreau
|
2
|
-
class Case
|
3
|
-
# Build test cases.
|
4
|
-
#
|
5
|
-
# It is responsible for:
|
6
|
-
# - building an list of Test::Case objects based
|
7
|
-
# on the groups provided.
|
8
|
-
# - expanding input specs in the groups into multiple cases
|
9
|
-
# - skipping unfocused tests, if any are focused
|
10
|
-
# - returning a count of those skipped
|
11
|
-
class Builder
|
12
|
-
|
13
|
-
def initialize(groups, suite_context)
|
14
|
-
@groups = groups
|
15
|
-
@suite_context = suite_context
|
16
|
-
end
|
17
|
-
|
18
|
-
def logger
|
19
|
-
@suite_context.logger
|
20
|
-
end
|
21
|
-
|
22
|
-
def any_focused?
|
23
|
-
@groups.count(&:focused?) > 0
|
24
|
-
end
|
25
|
-
|
26
|
-
def skipped_count
|
27
|
-
return 0 unless any_focused?
|
28
|
-
@groups.count - @groups.count(&:focused?)
|
29
|
-
end
|
30
|
-
|
31
|
-
def build_test_cases!
|
32
|
-
logger.debug "build_test_cases!"
|
33
|
-
|
34
|
-
@groups
|
35
|
-
.select { |g| any_focused? && g.focused? || !any_focused? }
|
36
|
-
.flat_map do |g|
|
37
|
-
build_group_cases g
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def setup_key_to_inputs key
|
44
|
-
setup = @suite_context.setups[key.to_s]
|
45
|
-
raise "Unrecognized setup context '#{key}'. Available: #{@suite_setups.keys.to_sentence}" if setup.nil?
|
46
|
-
|
47
|
-
return setup.values if setup.block.nil?
|
48
|
-
|
49
|
-
result = Class.new.new.instance_eval(&setup.block)
|
50
|
-
logger.error "Setup #{key} did not return a hash object" unless result.is_a?(Hash)
|
51
|
-
result
|
52
|
-
end
|
53
|
-
|
54
|
-
def build_group_cases g
|
55
|
-
# We have "specs" for the inputs. These may be actual
|
56
|
-
# values, or they may be enumerables that need to execute.
|
57
|
-
# So we need to "explode" (or enumerate) the values,
|
58
|
-
# generating a single test for each combination.
|
59
|
-
#
|
60
|
-
setup_values = g.setups
|
61
|
-
.map { |key| setup_key_to_inputs key }
|
62
|
-
.reduce(Hash.new) { |m, h| m.merge(h) }
|
63
|
-
|
64
|
-
input_sets = g.input_specs
|
65
|
-
.map { |is| setup_values.merge(is) }
|
66
|
-
.flat_map do |input_spec|
|
67
|
-
explode_input_specs(input_spec.keys, input_spec)
|
68
|
-
end
|
69
|
-
|
70
|
-
input_sets.map do |input_set|
|
71
|
-
Thoreau::Case.new(
|
72
|
-
group: g,
|
73
|
-
input: input_set,
|
74
|
-
action: @suite_context.data.action,
|
75
|
-
expected_output: g.expected_output,
|
76
|
-
expected_exception: g.expected_exception,
|
77
|
-
asserts: g.asserts,
|
78
|
-
suite_context: @suite_context,
|
79
|
-
logger: logger)
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
# Expand any values that are enumerators (Thoreau::DSL::Expanded),
|
85
|
-
# creating a list of objects, where all the combinations
|
86
|
-
# of enumerated values are present.
|
87
|
-
def explode_input_specs(keys, input_spec)
|
88
|
-
k = keys.pop
|
89
|
-
|
90
|
-
value_spec = input_spec[k]
|
91
|
-
specs = if value_spec.is_a?(Thoreau::DSL::Expanded)
|
92
|
-
value_spec.map do |v|
|
93
|
-
input_spec.merge(k => v)
|
94
|
-
end
|
95
|
-
else
|
96
|
-
[input_spec]
|
97
|
-
end
|
98
|
-
|
99
|
-
# Are we done?
|
100
|
-
return specs if keys.empty?
|
101
|
-
|
102
|
-
specs.flat_map do |spec|
|
103
|
-
explode_input_specs(keys, spec) # recurse!
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
data/lib/thoreau/case/runner.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
module Thoreau
|
2
|
-
class Case
|
3
|
-
class Runner
|
4
|
-
|
5
|
-
def initialize(context)
|
6
|
-
@context = context
|
7
|
-
end
|
8
|
-
|
9
|
-
def logger
|
10
|
-
@context.logger
|
11
|
-
end
|
12
|
-
|
13
|
-
def run_test_cases! cases, skipped
|
14
|
-
logger.info " § #{@context.name} §"
|
15
|
-
cases.each do |c|
|
16
|
-
if c.ok?
|
17
|
-
logger.info " ✓ #{c.desc}"
|
18
|
-
else
|
19
|
-
logger.error "❓ #{c.desc}, #{c.problem}"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
logger.info (summary cases, skipped)
|
23
|
-
logger.info ""
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
def summary cases, skipped
|
28
|
-
ok = cases.count(&:ok?)
|
29
|
-
total = cases.count
|
30
|
-
failed = cases.count(&:failed?)
|
31
|
-
if failed == 0
|
32
|
-
" ∴ All OK 👌🏾 #{skipped > 0 ? "#{skipped} skipped." : ""}"
|
33
|
-
else
|
34
|
-
" 🛑 #{failed} problem(s) detected. [#{ok} of #{total} OK#{skipped > 0 ? ", #{skipped} skipped" : ""}.]"
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|
data/lib/thoreau/case.rb
DELETED
@@ -1,97 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/module/delegation'
|
2
|
-
require_relative './case/context_builder'
|
3
|
-
|
4
|
-
module Thoreau
|
5
|
-
class Case
|
6
|
-
def initialize group:,
|
7
|
-
input:,
|
8
|
-
action:,
|
9
|
-
expected_output:,
|
10
|
-
expected_exception:,
|
11
|
-
asserts:,
|
12
|
-
logger:,
|
13
|
-
suite_context:
|
14
|
-
@group = group
|
15
|
-
@input = input
|
16
|
-
@action = action
|
17
|
-
if expected_output.is_a?(Proc)
|
18
|
-
@expected_output_proc = expected_output
|
19
|
-
else
|
20
|
-
@expected_output = expected_output
|
21
|
-
end
|
22
|
-
@expected_exception = expected_exception
|
23
|
-
@assert_proc = asserts
|
24
|
-
@logger = logger
|
25
|
-
@suite_context = suite_context
|
26
|
-
@ran = false
|
27
|
-
end
|
28
|
-
|
29
|
-
delegate :failure_expected?, to: :@group
|
30
|
-
|
31
|
-
def desc
|
32
|
-
"#{@group.kind}: #{@group.desc} #{(@input == {} ? nil : @input.sort.to_h) || @expected_exception || "(no args)"}"
|
33
|
-
end
|
34
|
-
|
35
|
-
def problem
|
36
|
-
run unless @ran
|
37
|
-
if @expected_exception
|
38
|
-
|
39
|
-
logger.debug " -> @expected_exception:#{@expected_exception} @raised_exception:#{@raised_exception}"
|
40
|
-
|
41
|
-
if @raised_exception.to_s == @expected_exception.to_s
|
42
|
-
nil
|
43
|
-
elsif @raised_exception.nil?
|
44
|
-
"Expected exception, but none raised"
|
45
|
-
elsif @raised_exception.is_a?(NameError)
|
46
|
-
"Did you forget to define an input? Error: #{@raised_exception}"
|
47
|
-
else
|
48
|
-
"Expected '#{@expected_exception}' exception, but raised '#{@raised_exception}' (#{@raised_exception.class.name})"
|
49
|
-
end
|
50
|
-
|
51
|
-
elsif @assert_proc
|
52
|
-
|
53
|
-
logger.debug " -> @assert_result: #{@assert_result}"
|
54
|
-
|
55
|
-
@assert_result ? nil : "Assertion failed. (got #{@assert_result})"
|
56
|
-
else
|
57
|
-
|
58
|
-
logger.debug " -> @result: #{@result} @expected_output: #{@expected_output} @raised_exception:#{@raised_exception}"
|
59
|
-
|
60
|
-
if @raised_exception
|
61
|
-
"Expected output, but raised exception '#{@raised_exception}'"
|
62
|
-
elsif @expected_output != @result
|
63
|
-
"Expected '#{@expected_output}', but got '#{@result}'"
|
64
|
-
else
|
65
|
-
nil
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def ok?
|
71
|
-
failure_expected? ^ !!problem.nil?
|
72
|
-
end
|
73
|
-
|
74
|
-
def failed?
|
75
|
-
!ok?
|
76
|
-
end
|
77
|
-
|
78
|
-
def run
|
79
|
-
logger.debug("create_context for #{desc} -> ")
|
80
|
-
context_builder = Case::ContextBuilder.new(group: @group, input: @input)
|
81
|
-
context = context_builder.create_context
|
82
|
-
begin
|
83
|
-
# Only capture exceptions around the action itself.
|
84
|
-
@result = context.instance_exec(&(@action))
|
85
|
-
rescue Exception => e
|
86
|
-
@raised_exception = e
|
87
|
-
return
|
88
|
-
ensure
|
89
|
-
@ran = true
|
90
|
-
end
|
91
|
-
|
92
|
-
@assert_result = context.instance_exec(@result, &(@assert_proc)) if @assert_proc
|
93
|
-
@expected_output = context.instance_exec(@result, &(@expected_output_proc)) if @expected_output_proc
|
94
|
-
end
|
95
|
-
|
96
|
-
end
|
97
|
-
end
|
data/lib/thoreau/dsl/context.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
module Thoreau
|
2
|
-
module DSL
|
3
|
-
class Data
|
4
|
-
attr_accessor :action
|
5
|
-
attr_accessor :cases
|
6
|
-
attr_accessor :appendix
|
7
|
-
attr_accessor :groups
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@groups = []
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class Context
|
15
|
-
|
16
|
-
attr_reader :data
|
17
|
-
attr_reader :name
|
18
|
-
attr_reader :logger
|
19
|
-
attr_reader :setups
|
20
|
-
|
21
|
-
def initialize name, logger
|
22
|
-
@name = name
|
23
|
-
@logger = logger
|
24
|
-
@data = Data.new
|
25
|
-
@setups = {}
|
26
|
-
end
|
27
|
-
|
28
|
-
def action(&block)
|
29
|
-
logger.debug "adding action"
|
30
|
-
@data.action = block
|
31
|
-
end
|
32
|
-
|
33
|
-
alias testing action
|
34
|
-
alias subject action
|
35
|
-
|
36
|
-
def cases(&block)
|
37
|
-
logger.debug "adding cases"
|
38
|
-
@data.cases = block
|
39
|
-
end
|
40
|
-
|
41
|
-
alias test_cases cases
|
42
|
-
|
43
|
-
def appendix(&block)
|
44
|
-
logger.debug "adding appendix"
|
45
|
-
@data.appendix = block
|
46
|
-
end
|
47
|
-
|
48
|
-
def context
|
49
|
-
self
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
end
|