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.
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
@@ -1,3 +1,3 @@
1
1
  module Thoreau
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
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
- class Error < StandardError;
6
+ def self.configure &block
7
+ block.call configuration
6
8
  end
7
- # Your code goes here...
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.1
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-02 00:00:00.000000000 Z
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/case.rb
112
- - lib/thoreau/case/builder.rb
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/runner.rb
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/context.rb
133
+ - lib/thoreau/dsl/clan.rb
118
134
  - lib/thoreau/dsl/expanded.rb
119
- - lib/thoreau/dsl/groups.rb
120
- - lib/thoreau/dsl/groups_support.rb
121
- - lib/thoreau/legacy_results.rb
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
@@ -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
@@ -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
@@ -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