theorem 0.0.2 → 0.0.3

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: 2707643c05984ddc7bb49ef7f536fad90dc70ddc7fa058ec031f3945df8e4188
4
- data.tar.gz: 173b64d78b6de42218ebb90714c651a459e1ef377492840985ae8f5c0ae94a7f
3
+ metadata.gz: e3451c37e94f11e1fed722a6ab987454f8ccaa7d52edcc906ef0635cb92d177b
4
+ data.tar.gz: 544bcaef77540b26c750741bffd35ef363c331e155f1ca49c99696959ff21eae
5
5
  SHA512:
6
- metadata.gz: e1f9df341383f804e7437a2193e2c9cd6978861d8dd3351533b7f14f059e3fd8e941e865c455325edcfa724da490549af329fbf5cd7fdd460333d1105fefeccc
7
- data.tar.gz: c9fd46000ef01e28b7f63f4c7512f599732004f129938085585b3725b8bf0abbea88961fea7048bdb5d8753dffb52b2228c4c0dacc24983668e2f3fdd2293193
6
+ metadata.gz: 9963c7729eeaedbf7b9ec575931d2918630d04244eecd0c6d9b5178636edc20ec1867f4bf2a33e30527481cb76ee3e56dd140124ffd279e808d977bee25edd6b
7
+ data.tar.gz: c2224d035456e70630851b8f1d8acf1e4519b7310c1805ce1ec45c98689138b5ae0cea04cb582e4ec8aad67db376d42a68235394516589813c4264321aed7aa0
data/bin/theorize CHANGED
@@ -1,7 +1,18 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
- require_relative '../src/harness'
3
+ require 'slop'
4
+ require_relative '../src/theorem'
4
5
 
5
- path = ARGV[0] || '.'
6
+ opts = Slop.parse do |o|
7
+ o.array '--require', 'files to require', delimiter: ','
8
+ o.string '-m', '--module', 'module (default: Theorem::Hypothesis)', default: 'Theorem::Hypothesis'
9
+ o.string '-h', '--harness', 'harness (default: Theorem::Hypothesis)', default: 'Theorem::Harness'
10
+ o.array '-p', '--publisher', 'publishers (default: Theorem::StdoutReporter)', delimiter: ',', default: ['Theorem::StdoutReporter']
11
+ o.string '-d', '--directory', 'directory to load tests (default: ".")', default: '.'
12
+ o.on '--help' do
13
+ puts o
14
+ exit
15
+ end
16
+ end
6
17
 
7
- Theorem::Hypothesis::Harness.run! path
18
+ Theorem.run! opts[:directory], opts
data/src/harness.rb CHANGED
@@ -1,59 +1,43 @@
1
- require_relative './theorem'
2
- require 'extended_dir'
1
+ # frozen_string_literal: true
3
2
 
4
- # module
3
+ require_relative './theorem/harness'
4
+ require 'parallel'
5
+
6
+ # harness
5
7
  module Theorem
6
- # module
7
- module Hypothesis
8
- on_completed_test do |test|
9
- print test.failed? ? 'X' : '.'
10
- end
8
+ # default test harness
9
+ module Harness
10
+ include Control::Harness
11
+ end
11
12
 
12
- # harness
13
- class Harness
14
- def self.locate_tests(dir)
15
- ExtendedDir.require_all("./#{dir}")
13
+ # parallel harness
14
+ module ParallelHarness
15
+ include Control::Harness
16
16
 
17
- Hypothesis.registry
18
- end
17
+ on_run do |tests|
18
+ Parallel.map(tests, in_threads: 6, &:run!).flatten
19
+ end
20
+ end
19
21
 
20
- def self.get_test_count(test_cases)
21
- test_cases.map do |test_case|
22
- test_case.tests.size
23
- end.inject(&:+)
24
- end
22
+ # module retry harness
23
+ module RetryHarness
24
+ include Control::Harness
25
25
 
26
- def self.report_failures(failed_tests)
27
- failed_tests.each do |failure|
28
- puts "❌ Failure in #{failure.full_name}\nError: #{failure.error}\nBacktrace:\n------\n#{failure.error.backtrace.join("\n")}"
29
- end
30
- end
26
+ on_run do |tests|
27
+ arr_of_tests = tests.map { |test| { test: test, index: 0 } }
31
28
 
32
- def self.report_passes(passing_tests)
33
- passing_tests.each do |pass|
34
- puts "✓ #{pass.full_name}"
29
+ final_results = []
30
+ arr_of_tests.each do |test|
31
+ test[:index] += 1
32
+ results = test[:test].run!
33
+ if results.any?(&:failed?) && test[:index] <= 3
34
+ puts "Retrying iteration: #{test[:index]}\n#{results.map(&:full_name).join("\n")}"
35
+ redo
35
36
  end
37
+ final_results.concat results
36
38
  end
37
39
 
38
- def self.run!(dir)
39
- test_cases = locate_tests(dir)
40
- total_count = get_test_count(test_cases)
41
-
42
- puts "Total tests #{total_count}"
43
-
44
- results = test_cases.each_with_object([]) do |test_case, memo|
45
- memo.concat test_case.run!
46
- end
47
-
48
- puts "\n\nSummary\n-------"
49
-
50
- failed_tests, passed_tests = results.partition(&:failed?)
51
-
52
- report_passes(passed_tests)
53
- report_failures(failed_tests)
54
-
55
- exit failed_tests.any? ? 1 : 0
56
- end
40
+ final_results
57
41
  end
58
42
  end
59
43
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './theorem/reporter'
4
+
5
+ module Theorem
6
+ module StringRefinements
7
+ refine String do
8
+ # colorization
9
+ def colorize(color_code)
10
+ "\e[#{color_code}m#{self}\e[0m"
11
+ end
12
+
13
+ def red
14
+ colorize(31)
15
+ end
16
+
17
+ def green
18
+ colorize(32)
19
+ end
20
+
21
+ def yellow
22
+ colorize(33)
23
+ end
24
+
25
+ def blue
26
+ colorize(34)
27
+ end
28
+
29
+ def pink
30
+ colorize(35)
31
+ end
32
+
33
+ def light_blue
34
+ colorize(36)
35
+ end
36
+ end
37
+ end
38
+
39
+ # Default Stdout reporter
40
+ module StdoutReporter
41
+ extend Control::Reporter
42
+ using StringRefinements
43
+
44
+ subscribe :on_completed_test do |test|
45
+ print test.failed? ? 'x'.red : '.'.green
46
+ end
47
+
48
+ subscribe :on_completed_suite do |results|
49
+ puts "\n"
50
+ report_summary(results)
51
+
52
+ failed_tests = results.select(&:failed?)
53
+
54
+ report_failures(failed_tests)
55
+ end
56
+
57
+ def report_summary(tests)
58
+ tests.each do |test|
59
+ icon = test.failed? ? '❌'.red : '✓'.green
60
+ puts "#{icon} #{test.full_name.blue}"
61
+ end
62
+ end
63
+
64
+ def report_failures(tests)
65
+ tests.each do |failure|
66
+ puts "Failure in #{failure.full_name}\nError: #{failure.error.to_s.red}\nBacktrace:\n------\n#{failure.error.backtrace.map(&:red).join("\n")}"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -54,6 +54,10 @@ module Theorem
54
54
  end
55
55
  end
56
56
 
57
+ def reverse_prepare(&block)
58
+ @state.unshift block
59
+ end
60
+
57
61
  def prepare(&block)
58
62
  @state << block
59
63
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'hypothesis'
4
+ require 'extended_dir'
5
+ module Theorem
6
+ module Control
7
+ # control harness
8
+ module Harness
9
+ def self.included(mod)
10
+ mod.extend(ClassMethods)
11
+ mod.define_singleton_method :included do |inner|
12
+ inner.define_singleton_method :run! do |directory, options = {}|
13
+ tests = inner.instance_exec directory, options, &mod.test_loader
14
+ results = inner.instance_exec tests, options, &mod.run_loader
15
+ inner.completed_suite_subscribers.each do |subscriber|
16
+ subscriber.call(results)
17
+ end
18
+ exit results.any?(&:failed?) ? 1 : 0
19
+ end
20
+ end
21
+ end
22
+
23
+ # harness helpers
24
+ module ClassMethods
25
+ DEFAULT_LOADER = ->(directory, tags) do
26
+ ExtendedDir.require_all("./#{directory}")
27
+
28
+ registry
29
+ end
30
+
31
+ DEFAULT_RUNNER = ->(tests, options) do
32
+ tests.each_with_object([]) do |test, memo|
33
+ memo.concat test.run!
34
+ end
35
+ end
36
+
37
+ def load_tests(&block)
38
+ @on_load_tests = block
39
+ end
40
+
41
+ def on_run(&block)
42
+ @on_run = block
43
+ end
44
+
45
+ def run_loader
46
+ @on_run || DEFAULT_RUNNER
47
+ end
48
+
49
+ def test_loader
50
+ @on_load_tests || DEFAULT_LOADER
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -19,6 +19,8 @@ module Theorem
19
19
  klass.instance_eval do
20
20
  @before_all ||= Beaker.new
21
21
  @before_each ||= Beaker.new
22
+ @after_all ||= Beaker.new
23
+ @after_each ||= Beaker.new
22
24
  @around = Flask.new
23
25
  @tests = []
24
26
  @completed_tests = []
@@ -12,6 +12,15 @@ module Theorem
12
12
  registry << klass
13
13
  end
14
14
 
15
+ def on_extra_event(&block)
16
+ @extra_events ||= []
17
+ @extra_events << block
18
+ end
19
+
20
+ def extra_event_subscribers
21
+ @extra_events || []
22
+ end
23
+
15
24
  def on_completed_test(&block)
16
25
  @completed_tests ||= []
17
26
  @completed_tests << block
@@ -20,6 +29,15 @@ module Theorem
20
29
  def completed_test_subscribers
21
30
  @completed_tests || []
22
31
  end
32
+
33
+ def on_completed_suite(&block)
34
+ @completed_suites ||= []
35
+ @completed_suites << block
36
+ end
37
+
38
+ def completed_suite_subscribers
39
+ @completed_suites || []
40
+ end
23
41
  end
24
42
  end
25
43
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Theorem
4
+ module Control
5
+ # reporter mixin
6
+ module Reporter
7
+ def self.extended(mod)
8
+ mod.extend(mod)
9
+ mod.define_singleton_method :included do |root|
10
+ mod.subscriptions.each do |subscription, handler|
11
+ mod.instance_exec root, subscription, handler do |root, sub, handle|
12
+ root.send(sub, &handle)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ def subscribe(name, &block)
19
+ @subscriptions ||= {}
20
+ @subscriptions[name] = block
21
+ end
22
+
23
+ def subscriptions
24
+ @subscriptions
25
+ end
26
+ end
27
+ end
28
+ end
data/src/theorem/test.rb CHANGED
@@ -33,6 +33,12 @@ module Theorem
33
33
 
34
34
  @parent_before_each ||= []
35
35
  @parent_before_each << me.before_each_beaker
36
+
37
+ @parent_after_each ||= []
38
+ @parent_after_each.unshift me.after_each_beaker
39
+
40
+ @parent_after_all ||= []
41
+ @parent_after_all.unshift me.after_all_beaker
36
42
  end
37
43
  super
38
44
  end
@@ -49,6 +55,14 @@ module Theorem
49
55
  @before_each.prepare(&block)
50
56
  end
51
57
 
58
+ def after_each(&block)
59
+ @after_each.reverse_prepare(&block)
60
+ end
61
+
62
+ def after_all(&block)
63
+ @after_all.reverse_prepare(&block)
64
+ end
65
+
52
66
  def experiments(klass, **opts, &block)
53
67
  obj = Class.new
54
68
  obj.include(control)
@@ -70,6 +84,14 @@ module Theorem
70
84
  @before_each
71
85
  end
72
86
 
87
+ def after_each_beaker
88
+ @after_each
89
+ end
90
+
91
+ def after_all_beaker
92
+ @after_all
93
+ end
94
+
73
95
  def test(name, &block)
74
96
  @tests << Test.new(name, to_s, &block)
75
97
  end
@@ -84,15 +106,20 @@ module Theorem
84
106
 
85
107
  results = []
86
108
  @tests.each do |test|
87
- before_each_failures = run_before_each_beakers(test_case)
88
- return before_each_failures if before_each_failures.any?
89
-
90
- error = run_test(test, test_case)
109
+ error ||= run_before_each_beakers(test_case)
110
+ error ||= run_test(test, test_case)
111
+ error ||= run_after_each_beakers(test_case)
91
112
 
92
113
  completed_test = CompletedTest.new(test, error)
93
114
  publish_test_completion(completed_test)
94
115
  results << completed_test
95
116
  end
117
+
118
+ after_failures = run_after_all_beakers(test_case)
119
+ if after_failures.any?
120
+ return after_failures
121
+ end
122
+
96
123
  results
97
124
  end
98
125
 
@@ -113,11 +140,13 @@ module Theorem
113
140
  end
114
141
  end
115
142
 
116
- def run_before_each_beakers(test_case)
117
- @parent_before_each&.each do |beaker|
143
+ def run_after_all_beakers(test_case)
144
+ @after_all.run!(test_case)
145
+
146
+ @parent_after_all&.each do |beaker|
118
147
  beaker.run!(test_case)
119
148
  end
120
- @before_each.run!(test_case)
149
+
121
150
  []
122
151
  rescue Exception => error
123
152
  Theorem.handle_exception(error)
@@ -127,6 +156,31 @@ module Theorem
127
156
  end
128
157
  end
129
158
 
159
+ def run_after_each_beakers(test_case)
160
+ @after_each.run!(test_case)
161
+
162
+ @parent_after_each&.each do |beaker|
163
+ beaker.run!(test_case)
164
+ end
165
+ nil
166
+ rescue Exception => error
167
+ Theorem.handle_exception(error)
168
+
169
+ error
170
+ end
171
+
172
+ def run_before_each_beakers(test_case)
173
+ @parent_before_each&.each do |beaker|
174
+ beaker.run!(test_case)
175
+ end
176
+ @before_each.run!(test_case)
177
+ nil
178
+ rescue Exception => error
179
+ Theorem.handle_exception(error)
180
+
181
+ error
182
+ end
183
+
130
184
  def run_before_all_beakers(test_case)
131
185
  @parent_before_all&.each do |beaker|
132
186
  beaker.run!(test_case)
data/src/theorem.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  require_relative 'theorem/hypothesis'
2
+ require_relative 'harness'
2
3
  require_relative 'experiment'
4
+ require_relative 'stdout_reporter'
5
+ require 'json'
3
6
 
4
7
  module Theorem
5
8
  # RSpec subclasses Exception, so the only way to catch them without a dependency is to catch Exception
@@ -17,6 +20,41 @@ module Theorem
17
20
  end
18
21
  end
19
22
 
23
+ def self.run!(directory, options)
24
+ options[:require].each do |file|
25
+ require file
26
+ end
27
+
28
+ raise StandardError, "Unknown Module: #{options[:module]}" unless defined? Object.const_get(options[:module])
29
+ raise StandardError, "Unknown Harness: #{options[:harness]}" unless defined? Object.const_get(options[:harness])
30
+
31
+ mod = Object.const_get(options[:module])
32
+ harness = Object.const_get options[:harness]
33
+ mod.include harness
34
+
35
+ options[:publisher].each do |publisher|
36
+ if defined? Object.const_get(publisher)
37
+ mod.include Object.const_get(publisher)
38
+ else
39
+ raise StandardError, "Unknown Publisher: #{publisher}"
40
+ end
41
+ end
42
+
43
+ mod.run!(directory, options)
44
+ end
45
+
46
+ module JsonReporter
47
+ extend Control::Reporter
48
+
49
+ subscribe :on_completed_suite do |results|
50
+ results = results.map do |result|
51
+ { name: result.full_name, failed: result.failed? }
52
+ end
53
+ puts results
54
+ puts "\n\n"
55
+ end
56
+ end
57
+
20
58
  module Hypothesis
21
59
  include Control::Hypothesis
22
60
  end
metadata CHANGED
@@ -1,29 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: theorem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Gregory
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-08 00:00:00.000000000 Z
11
+ date: 2022-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: extended_dir
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.1.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.1.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: slop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 4.9.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 4.9.2
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rbs
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -67,7 +81,7 @@ dependencies:
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
- name: watir
84
+ name: parallel
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - ">="
@@ -80,7 +94,7 @@ dependencies:
80
94
  - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
83
- description: simple. testing. ah.
97
+ description: simple and extensible test library toolkit
84
98
  email: sean.christopher.gregory@gmail.com
85
99
  executables:
86
100
  - theorize
@@ -90,21 +104,24 @@ files:
90
104
  - bin/theorize
91
105
  - src/experiment.rb
92
106
  - src/harness.rb
107
+ - src/stdout_reporter.rb
93
108
  - src/theorem.rb
94
109
  - src/theorem/beaker.rb
95
110
  - src/theorem/completed_test.rb
111
+ - src/theorem/harness.rb
96
112
  - src/theorem/hypothesis.rb
97
113
  - src/theorem/registry.rb
114
+ - src/theorem/reporter.rb
98
115
  - src/theorem/test.rb
99
116
  homepage: https://rubygems.org/gems/theorem
100
117
  licenses:
101
118
  - MIT
102
119
  metadata:
103
- source_code_uri: https://github.com/skinnyjames/theorem
120
+ source_code_uri: https://gitlab.com/skinnyjames/theorem
104
121
  post_install_message:
105
122
  rdoc_options: []
106
123
  require_paths:
107
- - lib
124
+ - src
108
125
  required_ruby_version: !ruby/object:Gem::Requirement
109
126
  requirements:
110
127
  - - ">="
@@ -116,8 +133,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
133
  - !ruby/object:Gem::Version
117
134
  version: '0'
118
135
  requirements: []
119
- rubygems_version: 3.2.31
136
+ rubygems_version: 3.2.22
120
137
  signing_key:
121
138
  specification_version: 4
122
- summary: simple. testing. ah.
139
+ summary: simple and extensible test library toolkit
123
140
  test_files: []