theorem 0.0.1 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 82f4b3d0f32345396cdbc66fc1d28d00d014f3576c8cf36e18eb355913fd34cd
4
- data.tar.gz: ac2241928677901a46a9e12fb980e7a787f9e43ede2c7bf862a0bb3277fe6527
3
+ metadata.gz: 91647688dc2f743ee8d64461f26ee5f2265be617c82623a97e0cfd7cdec58aa8
4
+ data.tar.gz: 410086697fb9d6718c3f05a64cbac277b59fa4ced15cf679484b9eed11bb9edf
5
5
  SHA512:
6
- metadata.gz: 2233e6daab124321a5f83dda68b09b8159f1561afb5b9cb8c2f1b626170dce551ed1a833164fc2e58cc5a02a86f216b0be95057b4f8aee6b2c986c3928c10837
7
- data.tar.gz: 7b4432d63c2a4bdc6c18875b7fdecc27cb14412fcdbaf8d3867fd31863ff8173faa21d2e1b37db65848af72571e7656ebf5ce0a31ed8d21697f2412a9f052976
6
+ metadata.gz: f66749de7a52cee707d0c149e7e9b273d05b46248a69e7acb4f3669d400e6a833fcac9d268eb0f87acd6f5b3cbc9d8c5bfaa836491d705c8666563ae9dfe6fe8
7
+ data.tar.gz: dc737567dc4303c362f5dcb3bfa6a75474981d6b355367b6d8d711c4ad5170c8f3e0c0cf252197f4007ef16f9e403234f75277559e3d9ff8ce908cc5faff94e9
data/bin/theorize CHANGED
@@ -1,5 +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
- Theorem::Hypothesis::Harness.run! 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::Harness)', 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
17
+
18
+ Theorem.run! opts
data/src/experiment.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Theorem
4
+ # shared examples
5
+ class Experiment
6
+ class << self
7
+ def test(name, &block)
8
+ @tests ||= []
9
+ @tests << { name: name, block: block }
10
+ end
11
+
12
+ def tests(_experiment_namespace: to_s, **opts)
13
+ @tests.map do |hash|
14
+ Control::Test.new(hash[:name], _experiment_namespace, **opts, &hash[:block])
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
data/src/harness.rb CHANGED
@@ -1,40 +1,33 @@
1
- require_relative './hypothesis'
2
- require 'extended_dir'
1
+ # frozen_string_literal: true
3
2
 
4
- # module
5
- module Theorem
6
- module Hypothesis
7
- on_completed_test do |test|
8
- print test.failed? ? 'X' : '.'
9
- end
10
-
11
- # harness
12
- class Harness
13
- def self.locate_tests(dir)
14
- ExtendedDir.require_all("./#{dir}")
15
-
16
- Hypothesis.registry
17
- end
3
+ require_relative './theorem/harness'
18
4
 
19
- def self.run!(dir)
20
- test_cases = locate_tests(dir)
21
- total_count = test_cases.map do |test_case|
22
- test_case.tests.size
23
- end.inject(&:+)
24
-
25
- puts "Total tests #{total_count}"
5
+ # harness
6
+ module Theorem
7
+ # default test harness
8
+ module Harness
9
+ include Control::Harness
10
+ end
26
11
 
27
- results = test_cases.map(&:run!).flatten
12
+ # module retry harness
13
+ module RetryHarness
14
+ include Control::Harness
28
15
 
29
- puts "\n\nSummary\n-------"
16
+ on_run do |tests|
17
+ arr_of_tests = tests.map { |test| { test: test, index: 0 } }
30
18
 
31
- failed_tests = results.select(&:failed?)
32
- failed_tests.each do |failure|
33
- puts "Failure in #{failure.name}\nError: #{failure.error}\nBacktrace:\n------\n#{failure.error.backtrace.join("\n")}"
19
+ final_results = []
20
+ arr_of_tests.each do |test|
21
+ test[:index] += 1
22
+ results = test[:test].run!
23
+ if results.any?(&:failed?) && test[:index] <= 3
24
+ puts "Retrying iteration: #{test[:index]}\n#{results.map(&:full_name).join("\n")}"
25
+ redo
34
26
  end
35
-
36
- exit failed_tests.any? ? 1 : 0
27
+ final_results.concat results
37
28
  end
29
+
30
+ final_results
38
31
  end
39
32
  end
40
33
  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.message.to_s.red}\nBacktrace:\n------\n#{failure.error.backtrace.map(&:red).join("\n")}"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Theorem
4
+ module Control
5
+ # test object for around hooks
6
+ class FlaskTest
7
+ def initialize(test, ctx)
8
+ @test = test
9
+ @ctx = ctx
10
+ end
11
+
12
+ def run!
13
+ @test.run!(@ctx)
14
+ end
15
+ end
16
+
17
+ # single use container
18
+ class Flask
19
+ attr_reader :state
20
+
21
+ def initialize
22
+ @state = nil
23
+ end
24
+
25
+ def run!(test, ctx, flask_test: FlaskTest.new(test, ctx))
26
+ ctx.instance_exec flask_test, &@state
27
+ nil
28
+ rescue Exception => error
29
+ Theorem.handle_exception(error)
30
+
31
+ error
32
+ end
33
+
34
+ def empty?
35
+ @state.nil?
36
+ end
37
+
38
+ def prepare(&block)
39
+ @state = block
40
+ end
41
+ end
42
+
43
+ # reusable container
44
+ class Beaker
45
+ def initialize
46
+ @state = []
47
+ end
48
+
49
+ def run!(ctx)
50
+ ctx.instance_exec @state, ctx do |state, ctx|
51
+ state.each do |b|
52
+ ctx.instance_eval &b
53
+ end
54
+ end
55
+ end
56
+
57
+ def reverse_prepare(&block)
58
+ @state.unshift block
59
+ end
60
+
61
+ def prepare(&block)
62
+ @state << block
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Theorem
4
+ module Control
5
+ # error class
6
+ class CompletedTest
7
+ attr_reader :test, :error
8
+
9
+ def initialize(test, error = nil)
10
+ @test = test
11
+ @error = error
12
+ end
13
+
14
+ def full_name
15
+ test.full_name
16
+ end
17
+
18
+ def name
19
+ test.name
20
+ end
21
+
22
+ def failed?
23
+ !@error.nil?
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,57 @@
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 |options: {}|
13
+ tests = inner.instance_exec 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 = ->(options) do
26
+ directory = options[:directory] || '.'
27
+
28
+ ExtendedDir.require_all("./#{directory}")
29
+
30
+ registry
31
+ end
32
+
33
+ DEFAULT_RUNNER = ->(tests, options) do
34
+ tests.each_with_object([]) do |test, memo|
35
+ memo.concat test.run!
36
+ end
37
+ end
38
+
39
+ def load_tests(&block)
40
+ @on_load_tests = block
41
+ end
42
+
43
+ def on_run(&block)
44
+ @on_run = block
45
+ end
46
+
47
+ def run_loader
48
+ @on_run || DEFAULT_RUNNER
49
+ end
50
+
51
+ def test_loader
52
+ @on_load_tests || DEFAULT_LOADER
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'completed_test'
4
+ require_relative 'beaker'
5
+ require_relative 'registry'
6
+ require_relative 'test'
7
+
8
+ module Theorem
9
+ module Control
10
+ # control hypothesis
11
+ module Hypothesis
12
+ def self.included(mod)
13
+ mod.define_singleton_method(:included) do |klass|
14
+ klass.define_singleton_method(:control) do
15
+ mod
16
+ end
17
+
18
+ klass.extend ClassMethods
19
+ klass.instance_eval do
20
+ @before_all ||= Beaker.new
21
+ @before_each ||= Beaker.new
22
+ @after_all ||= Beaker.new
23
+ @after_each ||= Beaker.new
24
+ @around = Flask.new
25
+ @tests = []
26
+ @completed_tests = []
27
+ @self = new
28
+ end
29
+ mod.add_to_registry(klass)
30
+ end
31
+
32
+ mod.const_set(:Beaker, Beaker)
33
+ mod.const_set(:Test, Test)
34
+ mod.const_set(:CompletedTest, CompletedTest)
35
+ mod.extend(Registry)
36
+ end
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Theorem
4
+ module Control
5
+ module Registry
6
+ # beaker
7
+ def registry
8
+ @registry ||= []
9
+ end
10
+
11
+ def add_to_registry(klass)
12
+ registry << klass
13
+ end
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
+
24
+ def on_completed_test(&block)
25
+ @completed_tests ||= []
26
+ @completed_tests << block
27
+ end
28
+
29
+ def completed_test_subscribers
30
+ @completed_tests || []
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
41
+ end
42
+ end
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
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+ require_relative './beaker'
3
+
4
+ module Theorem
5
+ module Control
6
+ # test new
7
+ class Test
8
+ def initialize(name, namespace, **opts, &block)
9
+ @name = name
10
+ @namespace = namespace
11
+ @block = block
12
+ @arguments = opts
13
+ end
14
+
15
+ attr_reader :block, :name, :arguments, :namespace
16
+
17
+ def full_name
18
+ "#{namespace} #{name}"
19
+ end
20
+
21
+ def run!(ctx)
22
+ ctx.instance_exec self, **arguments, &block
23
+ end
24
+ end
25
+
26
+ # module
27
+ module ClassMethods
28
+ def inherited(klass)
29
+ klass.include(control)
30
+ klass.instance_exec self do |me|
31
+ @parent_before_all ||= []
32
+ @parent_before_all << me.before_all_beaker
33
+
34
+ @parent_before_each ||= []
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
42
+ end
43
+ super
44
+ end
45
+
46
+ def before_all(&block)
47
+ @before_all.prepare(&block)
48
+ end
49
+
50
+ def around(&block)
51
+ @around.prepare(&block)
52
+ end
53
+
54
+ def before_each(&block)
55
+ @before_each.prepare(&block)
56
+ end
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
+
66
+ def experiments(klass, **opts, &block)
67
+ obj = Class.new
68
+ obj.include(control)
69
+ obj.instance_eval &block if block
70
+ obj.instance_exec self, klass, opts do |consumer, experiment_klass, params|
71
+ @tests.concat experiment_klass.tests(_experiment_namespace: consumer.to_s, **params)
72
+ end
73
+ end
74
+
75
+ def tests
76
+ @tests
77
+ end
78
+
79
+ def before_all_beaker
80
+ @before_all
81
+ end
82
+
83
+ def before_each_beaker
84
+ @before_each
85
+ end
86
+
87
+ def after_each_beaker
88
+ @after_each
89
+ end
90
+
91
+ def after_all_beaker
92
+ @after_all
93
+ end
94
+
95
+ def test(name, &block)
96
+ @tests << Test.new(name, to_s, &block)
97
+ end
98
+
99
+ def run!
100
+ test_case = new
101
+
102
+ before_failures = run_before_all_beakers(test_case)
103
+ if before_failures.any?
104
+ return before_failures
105
+ end
106
+
107
+ results = []
108
+ @tests.each do |test|
109
+ error ||= run_before_each_beakers(test_case)
110
+ error ||= run_test(test, test_case)
111
+ error ||= run_after_each_beakers(test_case)
112
+
113
+ completed_test = CompletedTest.new(test, error)
114
+ publish_test_completion(completed_test)
115
+ results << completed_test
116
+ end
117
+
118
+ after_failures = run_after_all_beakers(test_case)
119
+ if after_failures.any?
120
+ return after_failures
121
+ end
122
+
123
+ results
124
+ end
125
+
126
+ private
127
+
128
+ def run_test(test, test_case)
129
+ if @around.empty?
130
+ begin
131
+ test.run!(test_case)
132
+ nil
133
+ rescue Exception => error
134
+ Theorem.handle_exception(error)
135
+
136
+ error
137
+ end
138
+ else
139
+ @around.run!(test, test_case)
140
+ end
141
+ end
142
+
143
+ def run_after_all_beakers(test_case)
144
+ @after_all.run!(test_case)
145
+
146
+ @parent_after_all&.each do |beaker|
147
+ beaker.run!(test_case)
148
+ end
149
+
150
+ []
151
+ rescue Exception => error
152
+ Theorem.handle_exception(error)
153
+
154
+ @tests.map do |test|
155
+ CompletedTest.new(test, error)
156
+ end
157
+ end
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
+
184
+ def run_before_all_beakers(test_case)
185
+ @parent_before_all&.each do |beaker|
186
+ beaker.run!(test_case)
187
+ end
188
+ @before_all.run!(test_case)
189
+ []
190
+ rescue Exception => error
191
+ Theorem.handle_exception(error)
192
+
193
+ @tests.map do |test|
194
+ CompletedTest.new(test, error)
195
+ end
196
+ end
197
+
198
+ def publish_test_completion(completed_test)
199
+ control.completed_test_subscribers.each do |subscriber|
200
+ subscriber.call(completed_test)
201
+ end
202
+ end
203
+
204
+ end
205
+ end
206
+ end
data/src/theorem.rb ADDED
@@ -0,0 +1,61 @@
1
+ require_relative 'theorem/hypothesis'
2
+ require_relative 'harness'
3
+ require_relative 'experiment'
4
+ require_relative 'stdout_reporter'
5
+ require 'json'
6
+
7
+ module Theorem
8
+ # RSpec subclasses Exception, so the only way to catch them without a dependency is to catch Exception
9
+ def self.custom_exceptions
10
+ errors = []
11
+ if defined? RSpec::Expectations
12
+ errors.concat [RSpec::Expectations::ExpectationNotMetError, RSpec::Expectations::MultipleExpectationsNotMetError]
13
+ end
14
+ errors
15
+ end
16
+
17
+ def self.handle_exception(error)
18
+ unless error.is_a?(StandardError) || custom_exceptions.include?(error.class)
19
+ raise error
20
+ end
21
+ end
22
+
23
+ def self.run!(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!(options: 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.to_json
54
+ puts "\n\n"
55
+ end
56
+ end
57
+
58
+ module Hypothesis
59
+ include Control::Hypothesis
60
+ end
61
+ end
metadata CHANGED
@@ -1,45 +1,45 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: theorem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
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-05 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
27
  - !ruby/object:Gem::Dependency
28
- name: rbs
28
+ name: slop
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
33
+ version: 4.9.2
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 4.9.2
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec-expectations
42
+ name: rbs
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rubocop
56
+ name: rspec-expectations
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: watir
70
+ name: rubocop
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -80,7 +80,7 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- description: simple. testing. ah.
83
+ description: simple and extensible test library toolkit
84
84
  email: sean.christopher.gregory@gmail.com
85
85
  executables:
86
86
  - theorize
@@ -88,17 +88,26 @@ extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
90
  - bin/theorize
91
+ - src/experiment.rb
91
92
  - src/harness.rb
92
- - src/hypothesis.rb
93
+ - src/stdout_reporter.rb
94
+ - src/theorem.rb
95
+ - src/theorem/beaker.rb
96
+ - src/theorem/completed_test.rb
97
+ - src/theorem/harness.rb
98
+ - src/theorem/hypothesis.rb
99
+ - src/theorem/registry.rb
100
+ - src/theorem/reporter.rb
101
+ - src/theorem/test.rb
93
102
  homepage: https://rubygems.org/gems/theorem
94
103
  licenses:
95
104
  - MIT
96
105
  metadata:
97
- source_code_uri: https://github.com/skinnyjames/theorem
106
+ source_code_uri: https://gitlab.com/skinnyjames/theorem
98
107
  post_install_message:
99
108
  rdoc_options: []
100
109
  require_paths:
101
- - lib
110
+ - src
102
111
  required_ruby_version: !ruby/object:Gem::Requirement
103
112
  requirements:
104
113
  - - ">="
@@ -113,5 +122,5 @@ requirements: []
113
122
  rubygems_version: 3.2.31
114
123
  signing_key:
115
124
  specification_version: 4
116
- summary: simple. testing. ah.
125
+ summary: simple and extensible test library toolkit
117
126
  test_files: []
data/src/hypothesis.rb DELETED
@@ -1,137 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Theorem
4
- # entrypoint
5
- module Hypothesis
6
- # beaker
7
- def self.registry
8
- @registry ||= []
9
- end
10
-
11
- def self.add_to_registry(klass)
12
- registry << klass
13
- end
14
-
15
- def self.on_completed_test(&block)
16
- @completed_tests ||= []
17
- @completed_tests << block
18
- end
19
-
20
- def self.completed_test_subscribers
21
- @completed_tests
22
- end
23
-
24
- class Beaker
25
- def initialize
26
- @state = []
27
- end
28
-
29
- def run!(ctx)
30
- ctx.instance_exec @state, ctx do |state, ctx|
31
- state.each do |b|
32
- ctx.instance_eval &b
33
- end
34
- end
35
- end
36
-
37
- def prepare(&block)
38
- @state << block
39
- end
40
- end
41
-
42
- # error class
43
- class CompletedTest
44
- attr_reader :name, :error
45
-
46
- def initialize(name, error = nil)
47
- @name = name
48
- @error = error
49
- end
50
-
51
- def failed?
52
- !@error.nil?
53
- end
54
- end
55
-
56
-
57
- # test
58
- class Test
59
- def initialize(name, beaker, &block)
60
- @name = name
61
- @block = block
62
- end
63
-
64
- attr_reader :block, :name
65
-
66
- def run!(ctx)
67
- ctx.instance_exec self, &block
68
- nil
69
- rescue Exception => ex
70
- ex
71
- end
72
- end
73
-
74
- def self.included(klass)
75
- klass.extend ClassMethods
76
- klass.instance_eval do
77
- @before_all ||= Beaker.new
78
- @tests = []
79
- @completed_tests = []
80
- @self = new
81
- end
82
- Hypothesis.add_to_registry(klass)
83
- end
84
-
85
- # module
86
- module ClassMethods
87
- def inherited(klass)
88
- klass.include(Hypothesis)
89
- klass.instance_exec self do |me|
90
- @parent_before_all ||= []
91
- @parent_before_all << me.before_all_beaker
92
- end
93
- super
94
- end
95
-
96
- def before_all(&block)
97
- @before_all.prepare(&block)
98
- end
99
-
100
- def tests
101
- @tests
102
- end
103
-
104
- def before_all_beaker
105
- @before_all
106
- end
107
-
108
- def test(name, &block)
109
- @tests << Test.new(name, @before_all, &block)
110
- end
111
-
112
- def run!
113
- test_case = new
114
- @parent_before_all&.each do |beaker|
115
- beaker.run!(test_case)
116
- end
117
- @before_all.run!(test_case)
118
- results = []
119
- @tests.each do |test|
120
- error = test.run!(test_case)
121
- completed_test = CompletedTest.new(test.name, error)
122
- publish_test_completion(completed_test)
123
- results << completed_test
124
- end
125
- results
126
- end
127
-
128
- private
129
-
130
- def publish_test_completion(completed_test)
131
- Hypothesis.completed_test_subscribers.each do |subscriber|
132
- subscriber.call(completed_test)
133
- end
134
- end
135
- end
136
- end
137
- end