theorem 0.0.2 → 1.1.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: 2707643c05984ddc7bb49ef7f536fad90dc70ddc7fa058ec031f3945df8e4188
4
- data.tar.gz: 173b64d78b6de42218ebb90714c651a459e1ef377492840985ae8f5c0ae94a7f
3
+ metadata.gz: 561cfd58d73936664ce10aad722bab0fc21d97a28b421a5cc0e154bbf8f14bfa
4
+ data.tar.gz: 9e392733f121689bb139d908730ad2e7008f263e713d5193ede30d31f2309612
5
5
  SHA512:
6
- metadata.gz: e1f9df341383f804e7437a2193e2c9cd6978861d8dd3351533b7f14f059e3fd8e941e865c455325edcfa724da490549af329fbf5cd7fdd460333d1105fefeccc
7
- data.tar.gz: c9fd46000ef01e28b7f63f4c7512f599732004f129938085585b3725b8bf0abbea88961fea7048bdb5d8753dffb52b2228c4c0dacc24983668e2f3fdd2293193
6
+ metadata.gz: 7980418d3772ed5c314893f44cec5cd3cc166d3cf4e193580a0f1c65484e7020119c32db80ebba07e2a53e880e9f3ad5d83e45d6910dd3b3a08561642071abba
7
+ data.tar.gz: 25e8132963cab65e4d5822e15c1aa3dc5a8f28f225330a06f9a08bcff8d64a273bcdc86ed42035429e0bd1ac8e79676e0677061d898ba7ad460c781c6e16d7a4
data/bin/theorize CHANGED
@@ -1,7 +1,21 @@
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::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.array '-i', '--include', 'tags to specify what tests should be included (default: wont filter)', default: []
13
+ o.array '-e', '--exclude', 'tags the specify what tests should be excluded (default: wont filter)', default: []
14
+ o.string '--meta', 'string of metadata to pass to the harness', default: ''
15
+ o.on '--help' do
16
+ puts o
17
+ exit
18
+ end
19
+ end
6
20
 
7
- Theorem::Hypothesis::Harness.run! path
21
+ Theorem.run! opts.to_h
data/src/experiment.rb CHANGED
@@ -9,9 +9,9 @@ module Theorem
9
9
  @tests << { name: name, block: block }
10
10
  end
11
11
 
12
- def tests(**opts)
12
+ def tests(_experiment_namespace: to_s, **opts)
13
13
  @tests.map do |hash|
14
- Control::Test.new(hash[:name], to_s, **opts, &hash[:block])
14
+ Control::Test.new(hash[:name], _experiment_namespace, **opts, &hash[:block])
15
15
  end
16
16
  end
17
17
  end
data/src/harness.rb CHANGED
@@ -1,59 +1,41 @@
1
- require_relative './theorem'
2
- require 'extended_dir'
1
+ # frozen_string_literal: true
3
2
 
4
- # module
3
+ require_relative './theorem/harness'
4
+
5
+ # harness
5
6
  module Theorem
6
- # module
7
- module Hypothesis
8
- on_completed_test do |test|
9
- print test.failed? ? 'X' : '.'
10
- end
7
+ # default test harness
8
+ module Harness
9
+ include Control::Harness
11
10
 
12
- # harness
13
- class Harness
14
- def self.locate_tests(dir)
15
- ExtendedDir.require_all("./#{dir}")
11
+ load_tests do |options|
12
+ directory = options[:directory] || '.'
16
13
 
17
- Hypothesis.registry
18
- end
19
-
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
14
+ ExtendedDir.require_all("./#{directory}")
25
15
 
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
31
-
32
- def self.report_passes(passing_tests)
33
- passing_tests.each do |pass|
34
- puts "✓ #{pass.full_name}"
35
- end
36
- end
16
+ filtered_registry(options)
17
+ end
18
+ end
37
19
 
38
- def self.run!(dir)
39
- test_cases = locate_tests(dir)
40
- total_count = get_test_count(test_cases)
20
+ # module retry harness
21
+ module RetryHarness
22
+ include Control::Harness
41
23
 
42
- puts "Total tests #{total_count}"
24
+ on_run do |tests|
25
+ arr_of_tests = tests.map { |test| { test: test, index: 0 } }
43
26
 
44
- results = test_cases.each_with_object([]) do |test_case, memo|
45
- memo.concat test_case.run!
27
+ final_results = []
28
+ arr_of_tests.each do |test|
29
+ test[:index] += 1
30
+ results = test[:test].run!
31
+ if results.any?(&:failed?) && test[:index] <= 3
32
+ puts "Retrying iteration: #{test[:index]}\n#{results.map(&:full_name).join("\n")}"
33
+ redo
46
34
  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
35
+ final_results.concat results
56
36
  end
37
+
38
+ final_results
57
39
  end
58
40
  end
59
41
  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
@@ -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,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
@@ -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 = []
@@ -8,10 +8,35 @@ module Theorem
8
8
  @registry ||= []
9
9
  end
10
10
 
11
+ def filtered_registry(options)
12
+ registry.each do |test_class|
13
+ if options[:include]&.any?
14
+ test_class.tests.select! do |test|
15
+ test.metadata[:tags]&.intersection(options[:include])&.any?
16
+ end
17
+ end
18
+
19
+ next unless options[:exclude]&.any?
20
+
21
+ test_class.tests.reject! do |test|
22
+ test.metadata[:tags]&.intersection(options[:include])&.any?
23
+ end
24
+ end
25
+ end
26
+
11
27
  def add_to_registry(klass)
12
28
  registry << klass
13
29
  end
14
30
 
31
+ def on_extra_event(&block)
32
+ @extra_events ||= []
33
+ @extra_events << block
34
+ end
35
+
36
+ def extra_event_subscribers
37
+ @extra_events || []
38
+ end
39
+
15
40
  def on_completed_test(&block)
16
41
  @completed_tests ||= []
17
42
  @completed_tests << block
@@ -20,6 +45,15 @@ module Theorem
20
45
  def completed_test_subscribers
21
46
  @completed_tests || []
22
47
  end
48
+
49
+ def on_completed_suite(&block)
50
+ @completed_suites ||= []
51
+ @completed_suites << block
52
+ end
53
+
54
+ def completed_suite_subscribers
55
+ @completed_suites || []
56
+ end
23
57
  end
24
58
  end
25
59
  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
@@ -5,14 +5,15 @@ module Theorem
5
5
  module Control
6
6
  # test new
7
7
  class Test
8
- def initialize(name, namespace, **opts, &block)
8
+ def initialize(name, namespace, arguments: {}, **metadata, &block)
9
9
  @name = name
10
10
  @namespace = namespace
11
11
  @block = block
12
- @arguments = opts
12
+ @arguments = arguments
13
+ @metadata = metadata
13
14
  end
14
15
 
15
- attr_reader :block, :name, :arguments, :namespace
16
+ attr_reader :block, :name, :arguments, :namespace, :metadata
16
17
 
17
18
  def full_name
18
19
  "#{namespace} #{name}"
@@ -33,6 +34,12 @@ module Theorem
33
34
 
34
35
  @parent_before_each ||= []
35
36
  @parent_before_each << me.before_each_beaker
37
+
38
+ @parent_after_each ||= []
39
+ @parent_after_each.unshift me.after_each_beaker
40
+
41
+ @parent_after_all ||= []
42
+ @parent_after_all.unshift me.after_all_beaker
36
43
  end
37
44
  super
38
45
  end
@@ -49,12 +56,20 @@ module Theorem
49
56
  @before_each.prepare(&block)
50
57
  end
51
58
 
59
+ def after_each(&block)
60
+ @after_each.reverse_prepare(&block)
61
+ end
62
+
63
+ def after_all(&block)
64
+ @after_all.reverse_prepare(&block)
65
+ end
66
+
52
67
  def experiments(klass, **opts, &block)
53
68
  obj = Class.new
54
69
  obj.include(control)
55
70
  obj.instance_eval &block if block
56
- obj.instance_exec klass, opts do |experiment_klass, params|
57
- @tests.concat experiment_klass.tests(**params)
71
+ obj.instance_exec self, klass, opts do |consumer, experiment_klass, params|
72
+ @tests.concat experiment_klass.tests(_experiment_namespace: consumer.to_s, arguments: params)
58
73
  end
59
74
  end
60
75
 
@@ -70,8 +85,16 @@ module Theorem
70
85
  @before_each
71
86
  end
72
87
 
73
- def test(name, &block)
74
- @tests << Test.new(name, to_s, &block)
88
+ def after_each_beaker
89
+ @after_each
90
+ end
91
+
92
+ def after_all_beaker
93
+ @after_all
94
+ end
95
+
96
+ def test(name, **hargs, &block)
97
+ @tests << Test.new(name, to_s, **hargs, &block)
75
98
  end
76
99
 
77
100
  def run!
@@ -84,15 +107,20 @@ module Theorem
84
107
 
85
108
  results = []
86
109
  @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)
110
+ error ||= run_before_each_beakers(test_case)
111
+ error ||= run_test(test, test_case)
112
+ error ||= run_after_each_beakers(test_case)
91
113
 
92
114
  completed_test = CompletedTest.new(test, error)
93
115
  publish_test_completion(completed_test)
94
116
  results << completed_test
95
117
  end
118
+
119
+ after_failures = run_after_all_beakers(test_case)
120
+ if after_failures.any?
121
+ return after_failures
122
+ end
123
+
96
124
  results
97
125
  end
98
126
 
@@ -113,11 +141,13 @@ module Theorem
113
141
  end
114
142
  end
115
143
 
116
- def run_before_each_beakers(test_case)
117
- @parent_before_each&.each do |beaker|
144
+ def run_after_all_beakers(test_case)
145
+ @after_all.run!(test_case)
146
+
147
+ @parent_after_all&.each do |beaker|
118
148
  beaker.run!(test_case)
119
149
  end
120
- @before_each.run!(test_case)
150
+
121
151
  []
122
152
  rescue Exception => error
123
153
  Theorem.handle_exception(error)
@@ -127,6 +157,31 @@ module Theorem
127
157
  end
128
158
  end
129
159
 
160
+ def run_after_each_beakers(test_case)
161
+ @after_each.run!(test_case)
162
+
163
+ @parent_after_each&.each do |beaker|
164
+ beaker.run!(test_case)
165
+ end
166
+ nil
167
+ rescue Exception => error
168
+ Theorem.handle_exception(error)
169
+
170
+ error
171
+ end
172
+
173
+ def run_before_each_beakers(test_case)
174
+ @parent_before_each&.each do |beaker|
175
+ beaker.run!(test_case)
176
+ end
177
+ @before_each.run!(test_case)
178
+ nil
179
+ rescue Exception => error
180
+ Theorem.handle_exception(error)
181
+
182
+ error
183
+ end
184
+
130
185
  def run_before_all_beakers(test_case)
131
186
  @parent_before_all&.each do |beaker|
132
187
  beaker.run!(test_case)
data/src/theorem.rb CHANGED
@@ -1,5 +1,10 @@
1
1
  require_relative 'theorem/hypothesis'
2
+ require_relative 'harness'
2
3
  require_relative 'experiment'
4
+ require_relative 'stdout_reporter'
5
+
6
+ require_relative 'theorem/harness'
7
+ require 'json'
3
8
 
4
9
  module Theorem
5
10
  # RSpec subclasses Exception, so the only way to catch them without a dependency is to catch Exception
@@ -17,6 +22,41 @@ module Theorem
17
22
  end
18
23
  end
19
24
 
25
+ def self.run!(options)
26
+ options[:require].each do |file|
27
+ require file
28
+ end
29
+
30
+ raise StandardError, "Unknown Module: #{options[:module]}" unless defined? Object.const_get(options[:module])
31
+ raise StandardError, "Unknown Harness: #{options[:harness]}" unless defined? Object.const_get(options[:harness])
32
+
33
+ mod = Object.const_get(options[:module])
34
+ harness = Object.const_get options[:harness]
35
+ mod.include harness
36
+
37
+ options[:publisher].each do |publisher|
38
+ if defined? Object.const_get(publisher)
39
+ mod.include Object.const_get(publisher)
40
+ else
41
+ raise StandardError, "Unknown Publisher: #{publisher}"
42
+ end
43
+ end
44
+
45
+ mod.run!(options: options)
46
+ end
47
+
48
+ module JsonReporter
49
+ extend Control::Reporter
50
+
51
+ subscribe :on_completed_suite do |results|
52
+ results = results.map do |result|
53
+ { name: result.full_name, failed: result.failed? }
54
+ end
55
+ puts results.to_json
56
+ puts "\n\n"
57
+ end
58
+ end
59
+
20
60
  module Hypothesis
21
61
  include Control::Hypothesis
22
62
  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.2
4
+ version: 1.1.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-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
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
@@ -90,21 +90,24 @@ files:
90
90
  - bin/theorize
91
91
  - src/experiment.rb
92
92
  - src/harness.rb
93
+ - src/stdout_reporter.rb
93
94
  - src/theorem.rb
94
95
  - src/theorem/beaker.rb
95
96
  - src/theorem/completed_test.rb
97
+ - src/theorem/harness.rb
96
98
  - src/theorem/hypothesis.rb
97
99
  - src/theorem/registry.rb
100
+ - src/theorem/reporter.rb
98
101
  - src/theorem/test.rb
99
102
  homepage: https://rubygems.org/gems/theorem
100
103
  licenses:
101
104
  - MIT
102
105
  metadata:
103
- source_code_uri: https://github.com/skinnyjames/theorem
106
+ source_code_uri: https://gitlab.com/skinnyjames/theorem
104
107
  post_install_message:
105
108
  rdoc_options: []
106
109
  require_paths:
107
- - lib
110
+ - src
108
111
  required_ruby_version: !ruby/object:Gem::Requirement
109
112
  requirements:
110
113
  - - ">="
@@ -119,5 +122,5 @@ requirements: []
119
122
  rubygems_version: 3.2.31
120
123
  signing_key:
121
124
  specification_version: 4
122
- summary: simple. testing. ah.
125
+ summary: simple and extensible test library toolkit
123
126
  test_files: []