theorem 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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