theorem 1.0.0 → 1.2.1
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 +4 -4
- data/bin/theorize +4 -1
- data/src/harness.rb +4 -18
- data/src/publishers/stdout_reporter.rb +111 -0
- data/src/theorem/beaker.rb +8 -0
- data/src/theorem/completed_test.rb +5 -2
- data/src/theorem/harness.rb +36 -17
- data/src/theorem/hypothesis.rb +13 -3
- data/src/theorem/notation.rb +33 -0
- data/src/theorem/registry.rb +23 -21
- data/src/theorem/test.rb +66 -16
- data/src/theorem.rb +2 -13
- metadata +32 -3
- data/src/stdout_reporter.rb +0 -70
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '09d0acbeb340968a19cb27122ba6c852b66b2cc4347fdfca7f6ba6a9f7781ce0'
|
|
4
|
+
data.tar.gz: aef702741a59ff1a11f08e6225b0804b30b813ed0cc8eb5184cdd50c54587901
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6055b29fd80529f514b73363a18582122a5e23b312fda318b3d83a546bb7d8b062fcaf87f57858340e432bc1327a183f1b494c107d0970861b9548974f34996f
|
|
7
|
+
data.tar.gz: 40c0dc5d7660db43bdf6606aae81a43c48cb5015dd29eacd63a4f2afc2e0daa2d70b34f1ce04560f190d2dd282caef3bc67d4c37236c5bcda3abfce5bed65aad
|
data/bin/theorize
CHANGED
|
@@ -9,10 +9,13 @@ opts = Slop.parse do |o|
|
|
|
9
9
|
o.string '-h', '--harness', 'harness (default: Theorem::Harness)', default: 'Theorem::Harness'
|
|
10
10
|
o.array '-p', '--publisher', 'publishers (default: Theorem::StdoutReporter)', delimiter: ',', default: ['Theorem::StdoutReporter']
|
|
11
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: ''
|
|
12
15
|
o.on '--help' do
|
|
13
16
|
puts o
|
|
14
17
|
exit
|
|
15
18
|
end
|
|
16
19
|
end
|
|
17
20
|
|
|
18
|
-
Theorem.run! opts
|
|
21
|
+
Theorem.run! opts.to_h
|
data/src/harness.rb
CHANGED
|
@@ -7,27 +7,13 @@ module Theorem
|
|
|
7
7
|
# default test harness
|
|
8
8
|
module Harness
|
|
9
9
|
include Control::Harness
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# module retry harness
|
|
13
|
-
module RetryHarness
|
|
14
|
-
include Control::Harness
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
load_tests do |options|
|
|
12
|
+
directory = options[:directory] || '.'
|
|
18
13
|
|
|
19
|
-
|
|
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
|
|
26
|
-
end
|
|
27
|
-
final_results.concat results
|
|
28
|
-
end
|
|
14
|
+
ExtendedDir.require_all("./#{directory}")
|
|
29
15
|
|
|
30
|
-
|
|
16
|
+
filtered_registry(options)
|
|
31
17
|
end
|
|
32
18
|
end
|
|
33
19
|
end
|
|
@@ -0,0 +1,111 @@
|
|
|
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 :test_finished do |test|
|
|
45
|
+
print test.failed? ? 'x'.red : '.'.green
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
subscribe :suite_finished do |results, duration|
|
|
49
|
+
puts "\n"
|
|
50
|
+
report_summary(results)
|
|
51
|
+
|
|
52
|
+
failed_tests = results.select(&:failed?)
|
|
53
|
+
|
|
54
|
+
report_failures(failed_tests)
|
|
55
|
+
|
|
56
|
+
puts "\nTotal time: #{duration} seconds"
|
|
57
|
+
puts "Total tests: #{results.size}\n"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def inflate_percentiles(tests)
|
|
61
|
+
sorted = tests.reject { |t| t.duration.nil? }.sort_by(&:duration)
|
|
62
|
+
tests.each_with_object([]) do |test, arr|
|
|
63
|
+
_, below = sorted.partition do |duration_test|
|
|
64
|
+
if test.duration.nil?
|
|
65
|
+
true
|
|
66
|
+
else
|
|
67
|
+
test.duration >= duration_test.duration
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
hash = {}
|
|
71
|
+
hash[:percentile] = (below.size.to_f / sorted.size.to_f) * 100
|
|
72
|
+
hash[:test] = test
|
|
73
|
+
arr << hash
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def report_summary(tests)
|
|
78
|
+
inflated = inflate_percentiles(tests)
|
|
79
|
+
|
|
80
|
+
top_20 = 80 / (100.0 * (tests.size + 1).to_f)
|
|
81
|
+
lowest_20 = 20 / (100.0 * (tests.size + 1).to_f)
|
|
82
|
+
|
|
83
|
+
inflated.each do |test|
|
|
84
|
+
icon = test[:test].failed? ? '❌'.red : '✓'.green
|
|
85
|
+
puts "#{icon} #{test[:test].full_name.blue} : #{duration(test)}"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def duration(test)
|
|
90
|
+
return 'Not run' if test[:test].duration.nil?
|
|
91
|
+
|
|
92
|
+
str = "#{format('%<num>0.10f', num: test[:test].duration)} seconds"
|
|
93
|
+
rank = ((test[:percentile] / 100) * (test[:test].duration + 1)) * 100
|
|
94
|
+
if rank < 5
|
|
95
|
+
str.red
|
|
96
|
+
elsif rank > 95
|
|
97
|
+
str.green
|
|
98
|
+
elsif rank < 10
|
|
99
|
+
str.yellow
|
|
100
|
+
else
|
|
101
|
+
str
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def report_failures(tests)
|
|
106
|
+
tests.each do |failure|
|
|
107
|
+
puts "\n\nFailure in #{failure.full_name}\nError: #{failure.error.message.to_s.red}\nBacktrace:\n------\n#{failure.error.backtrace.map(&:red).join("\n")}"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
data/src/theorem/beaker.rb
CHANGED
|
@@ -9,6 +9,10 @@ module Theorem
|
|
|
9
9
|
@ctx = ctx
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
def name
|
|
13
|
+
@test.name
|
|
14
|
+
end
|
|
15
|
+
|
|
12
16
|
def run!
|
|
13
17
|
@test.run!(@ctx)
|
|
14
18
|
end
|
|
@@ -54,6 +58,10 @@ module Theorem
|
|
|
54
58
|
end
|
|
55
59
|
end
|
|
56
60
|
|
|
61
|
+
def empty?
|
|
62
|
+
@state.empty?
|
|
63
|
+
end
|
|
64
|
+
|
|
57
65
|
def reverse_prepare(&block)
|
|
58
66
|
@state.unshift block
|
|
59
67
|
end
|
|
@@ -4,11 +4,14 @@ module Theorem
|
|
|
4
4
|
module Control
|
|
5
5
|
# error class
|
|
6
6
|
class CompletedTest
|
|
7
|
-
attr_reader :test, :
|
|
7
|
+
attr_reader :test, :duration
|
|
8
|
+
attr_accessor :error, :notary
|
|
8
9
|
|
|
9
|
-
def initialize(test, error = nil)
|
|
10
|
+
def initialize(test, error = nil, notary:, duration: nil)
|
|
10
11
|
@test = test
|
|
11
12
|
@error = error
|
|
13
|
+
@notary = notary
|
|
14
|
+
@duration = duration
|
|
12
15
|
end
|
|
13
16
|
|
|
14
17
|
def full_name
|
data/src/theorem/harness.rb
CHANGED
|
@@ -11,9 +11,21 @@ module Theorem
|
|
|
11
11
|
mod.define_singleton_method :included do |inner|
|
|
12
12
|
inner.define_singleton_method :run! do |options: {}|
|
|
13
13
|
tests = inner.instance_exec options, &mod.test_loader
|
|
14
|
+
|
|
15
|
+
inner.suite_started_subscribers.each do |subscriber|
|
|
16
|
+
subscriber.call tests.map(&:tests).flatten.map do |test|
|
|
17
|
+
{ name: test.name, metadata: test.metadata }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
14
22
|
results = inner.instance_exec tests, options, &mod.run_loader
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
24
|
+
|
|
25
|
+
duration = ending - starting
|
|
26
|
+
|
|
27
|
+
inner.suite_finished_subscribers.each do |subscriber|
|
|
28
|
+
subscriber.call(results, duration)
|
|
17
29
|
end
|
|
18
30
|
exit results.any?(&:failed?) ? 1 : 0
|
|
19
31
|
end
|
|
@@ -22,19 +34,6 @@ module Theorem
|
|
|
22
34
|
|
|
23
35
|
# harness helpers
|
|
24
36
|
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
37
|
|
|
39
38
|
def load_tests(&block)
|
|
40
39
|
@on_load_tests = block
|
|
@@ -45,11 +44,31 @@ module Theorem
|
|
|
45
44
|
end
|
|
46
45
|
|
|
47
46
|
def run_loader
|
|
48
|
-
@on_run ||
|
|
47
|
+
@on_run || default_runner
|
|
49
48
|
end
|
|
50
49
|
|
|
51
50
|
def test_loader
|
|
52
|
-
@on_load_tests ||
|
|
51
|
+
@on_load_tests || default_loader
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def default_loader
|
|
57
|
+
lambda do |options|
|
|
58
|
+
directory = options[:directory] || '.'
|
|
59
|
+
|
|
60
|
+
ExtendedDir.require_all("./#{directory}")
|
|
61
|
+
|
|
62
|
+
registry
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def default_runner
|
|
67
|
+
lambda do |tests, options|
|
|
68
|
+
tests.each_with_object([]) do |test, memo|
|
|
69
|
+
memo.concat test.run!
|
|
70
|
+
end
|
|
71
|
+
end
|
|
53
72
|
end
|
|
54
73
|
end
|
|
55
74
|
end
|
data/src/theorem/hypothesis.rb
CHANGED
|
@@ -15,6 +15,16 @@ module Theorem
|
|
|
15
15
|
mod
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
klass.attr_reader :notary
|
|
19
|
+
|
|
20
|
+
klass.define_method :initialize do
|
|
21
|
+
@notary = Notation.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
klass.define_method :notate do |&block|
|
|
25
|
+
block.call(@notary)
|
|
26
|
+
end
|
|
27
|
+
|
|
18
28
|
klass.extend ClassMethods
|
|
19
29
|
klass.instance_eval do
|
|
20
30
|
@before_all ||= Beaker.new
|
|
@@ -29,9 +39,9 @@ module Theorem
|
|
|
29
39
|
mod.add_to_registry(klass)
|
|
30
40
|
end
|
|
31
41
|
|
|
32
|
-
mod.const_set(:Beaker, Beaker)
|
|
33
|
-
mod.const_set(:Test, Test)
|
|
34
|
-
mod.const_set(:CompletedTest, CompletedTest)
|
|
42
|
+
mod.const_set(:Beaker, Beaker) unless mod.const_defined?(:Beaker)
|
|
43
|
+
mod.const_set(:Test, Test) unless mod.const_defined?(:Test)
|
|
44
|
+
mod.const_set(:CompletedTest, CompletedTest) unless mod.const_defined?(:CompletedTest)
|
|
35
45
|
mod.extend(Registry)
|
|
36
46
|
end
|
|
37
47
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Theorem
|
|
4
|
+
module Control
|
|
5
|
+
class Notation
|
|
6
|
+
def initialize(state = {})
|
|
7
|
+
@state = state
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def write(key, value)
|
|
11
|
+
@state[key] = value
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def read(key)
|
|
15
|
+
@state[key]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def dump
|
|
19
|
+
@state
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def merge(notary)
|
|
23
|
+
Notation.new(@state.merge(notary.dump))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def edit(key, &block)
|
|
27
|
+
data = read(key)
|
|
28
|
+
block.call(data)
|
|
29
|
+
write(key, data)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/src/theorem/registry.rb
CHANGED
|
@@ -8,35 +8,37 @@ module Theorem
|
|
|
8
8
|
@registry ||= []
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def
|
|
12
|
-
registry
|
|
13
|
-
|
|
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
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
@extra_events ||= []
|
|
17
|
-
@extra_events << block
|
|
18
|
-
end
|
|
19
|
+
next unless options[:exclude]&.any?
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
test_class.tests.reject! do |test|
|
|
22
|
+
test.metadata[:tags]&.intersection(options[:include])&.any?
|
|
23
|
+
end
|
|
24
|
+
end
|
|
22
25
|
end
|
|
23
26
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
@completed_tests << block
|
|
27
|
+
def add_to_registry(klass)
|
|
28
|
+
registry << klass
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
%i[suite_started test_started test_finished suite_finished].each do |method|
|
|
32
|
+
define_method method do |&block|
|
|
33
|
+
instance_variable_set("@#{method}_subscribers", []) unless instance_variable_get("@#{method}_subscribers")
|
|
34
|
+
instance_variable_get("@#{method}_subscribers").append(block)
|
|
35
|
+
end
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@completed_suites << block
|
|
36
|
-
end
|
|
37
|
+
define_method "#{method}_subscribers" do
|
|
38
|
+
return [] unless instance_variable_get("@#{method}_subscribers")
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
instance_variable_get("@#{method}_subscribers")
|
|
41
|
+
end
|
|
40
42
|
end
|
|
41
43
|
end
|
|
42
44
|
end
|
data/src/theorem/test.rb
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require_relative './beaker'
|
|
3
|
+
require_relative 'notation'
|
|
3
4
|
|
|
4
5
|
module Theorem
|
|
5
6
|
module Control
|
|
6
7
|
# test new
|
|
7
8
|
class Test
|
|
8
|
-
def initialize(name, namespace, **
|
|
9
|
+
def initialize(name, namespace, arguments: {}, **metadata, &block)
|
|
9
10
|
@name = name
|
|
10
11
|
@namespace = namespace
|
|
11
12
|
@block = block
|
|
12
|
-
@arguments =
|
|
13
|
+
@arguments = arguments
|
|
14
|
+
@metadata = metadata
|
|
15
|
+
@notary = Notation.new
|
|
13
16
|
end
|
|
14
17
|
|
|
15
|
-
attr_reader :block, :name, :arguments, :namespace
|
|
18
|
+
attr_reader :block, :name, :arguments, :namespace, :metadata, :notary
|
|
16
19
|
|
|
17
20
|
def full_name
|
|
18
21
|
"#{namespace} #{name}"
|
|
19
22
|
end
|
|
20
23
|
|
|
24
|
+
def notate(&block)
|
|
25
|
+
block.call(notary)
|
|
26
|
+
end
|
|
27
|
+
|
|
21
28
|
def run!(ctx)
|
|
22
29
|
ctx.instance_exec self, **arguments, &block
|
|
23
30
|
end
|
|
@@ -68,7 +75,7 @@ module Theorem
|
|
|
68
75
|
obj.include(control)
|
|
69
76
|
obj.instance_eval &block if block
|
|
70
77
|
obj.instance_exec self, klass, opts do |consumer, experiment_klass, params|
|
|
71
|
-
@tests.concat experiment_klass.tests(_experiment_namespace: consumer.to_s,
|
|
78
|
+
@tests.concat experiment_klass.tests(_experiment_namespace: consumer.to_s, arguments: params)
|
|
72
79
|
end
|
|
73
80
|
end
|
|
74
81
|
|
|
@@ -92,39 +99,74 @@ module Theorem
|
|
|
92
99
|
@after_all
|
|
93
100
|
end
|
|
94
101
|
|
|
95
|
-
def test(name, &block)
|
|
96
|
-
@tests << Test.new(name, to_s, &block)
|
|
102
|
+
def test(name, **hargs, &block)
|
|
103
|
+
@tests << Test.new(name, to_s, **hargs, &block)
|
|
97
104
|
end
|
|
98
105
|
|
|
99
106
|
def run!
|
|
100
107
|
test_case = new
|
|
101
108
|
|
|
109
|
+
# run before all beakers to create state in test case
|
|
102
110
|
before_failures = run_before_all_beakers(test_case)
|
|
111
|
+
|
|
103
112
|
if before_failures.any?
|
|
113
|
+
before_failures.each do |failure|
|
|
114
|
+
publish_test_completion(failure)
|
|
115
|
+
end
|
|
104
116
|
return before_failures
|
|
105
117
|
end
|
|
106
118
|
|
|
119
|
+
# duplicate the before_all arrangement for the after all hook
|
|
120
|
+
duplicate_test_case = test_case.clone
|
|
121
|
+
|
|
107
122
|
results = []
|
|
108
123
|
@tests.each do |test|
|
|
124
|
+
test_start = clock_time
|
|
125
|
+
|
|
126
|
+
publish_test_start(test)
|
|
127
|
+
|
|
109
128
|
error ||= run_before_each_beakers(test_case)
|
|
110
|
-
error ||= run_test(test, test_case)
|
|
111
|
-
error ||= run_after_each_beakers(test_case)
|
|
112
129
|
|
|
113
|
-
|
|
114
|
-
|
|
130
|
+
before_test_case = test_case.clone
|
|
131
|
+
error ||= run_test(test, before_test_case)
|
|
132
|
+
error ||= run_after_each_beakers(before_test_case)
|
|
133
|
+
|
|
134
|
+
notary = test_case.notary.merge(test.notary)
|
|
135
|
+
|
|
136
|
+
duration = clock_time - test_start
|
|
137
|
+
|
|
138
|
+
completed_test = CompletedTest.new(test, error, duration: duration, notary: notary.dump)
|
|
139
|
+
|
|
140
|
+
# publish_early if there are no after_all beakers
|
|
141
|
+
publish_test_completion(completed_test) if @after_all.empty?
|
|
142
|
+
|
|
115
143
|
results << completed_test
|
|
116
144
|
end
|
|
117
145
|
|
|
118
|
-
after_failures = run_after_all_beakers(
|
|
146
|
+
after_failures = run_after_all_beakers(results, duplicate_test_case)
|
|
147
|
+
|
|
119
148
|
if after_failures.any?
|
|
149
|
+
after_failures.each do |failure|
|
|
150
|
+
publish_test_completion(failure)
|
|
151
|
+
end
|
|
120
152
|
return after_failures
|
|
121
153
|
end
|
|
122
154
|
|
|
155
|
+
results.each do |completed_test|
|
|
156
|
+
# merge any after_all notations
|
|
157
|
+
completed_test.notary.merge!(duplicate_test_case.notary.dump)
|
|
158
|
+
publish_test_completion(completed_test) unless @after_all.empty?
|
|
159
|
+
end
|
|
160
|
+
|
|
123
161
|
results
|
|
124
162
|
end
|
|
125
163
|
|
|
126
164
|
private
|
|
127
165
|
|
|
166
|
+
def clock_time
|
|
167
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
168
|
+
end
|
|
169
|
+
|
|
128
170
|
def run_test(test, test_case)
|
|
129
171
|
if @around.empty?
|
|
130
172
|
begin
|
|
@@ -140,7 +182,7 @@ module Theorem
|
|
|
140
182
|
end
|
|
141
183
|
end
|
|
142
184
|
|
|
143
|
-
def run_after_all_beakers(test_case)
|
|
185
|
+
def run_after_all_beakers(results, test_case)
|
|
144
186
|
@after_all.run!(test_case)
|
|
145
187
|
|
|
146
188
|
@parent_after_all&.each do |beaker|
|
|
@@ -151,9 +193,12 @@ module Theorem
|
|
|
151
193
|
rescue Exception => error
|
|
152
194
|
Theorem.handle_exception(error)
|
|
153
195
|
|
|
154
|
-
|
|
155
|
-
|
|
196
|
+
results.each do |test|
|
|
197
|
+
test.error = error
|
|
198
|
+
test.notary = test_case.notary
|
|
156
199
|
end
|
|
200
|
+
|
|
201
|
+
results
|
|
157
202
|
end
|
|
158
203
|
|
|
159
204
|
def run_after_each_beakers(test_case)
|
|
@@ -191,16 +236,21 @@ module Theorem
|
|
|
191
236
|
Theorem.handle_exception(error)
|
|
192
237
|
|
|
193
238
|
@tests.map do |test|
|
|
194
|
-
CompletedTest.new(test, error)
|
|
239
|
+
CompletedTest.new(test, error, notary: test_case.notary)
|
|
195
240
|
end
|
|
196
241
|
end
|
|
197
242
|
|
|
198
243
|
def publish_test_completion(completed_test)
|
|
199
|
-
control.
|
|
244
|
+
control.test_finished_subscribers.each do |subscriber|
|
|
200
245
|
subscriber.call(completed_test)
|
|
201
246
|
end
|
|
202
247
|
end
|
|
203
248
|
|
|
249
|
+
def publish_test_start(test)
|
|
250
|
+
control.test_started_subscribers.each do |subscriber|
|
|
251
|
+
subscriber.call(test)
|
|
252
|
+
end
|
|
253
|
+
end
|
|
204
254
|
end
|
|
205
255
|
end
|
|
206
256
|
end
|
data/src/theorem.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
require_relative 'theorem/hypothesis'
|
|
2
2
|
require_relative 'harness'
|
|
3
3
|
require_relative 'experiment'
|
|
4
|
-
require_relative 'stdout_reporter'
|
|
4
|
+
require_relative 'publishers/stdout_reporter'
|
|
5
|
+
require_relative 'theorem/harness'
|
|
5
6
|
require 'json'
|
|
6
7
|
|
|
7
8
|
module Theorem
|
|
@@ -43,18 +44,6 @@ module Theorem
|
|
|
43
44
|
mod.run!(options: options)
|
|
44
45
|
end
|
|
45
46
|
|
|
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
47
|
module Hypothesis
|
|
59
48
|
include Control::Hypothesis
|
|
60
49
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: theorem
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.1
|
|
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-
|
|
11
|
+
date: 2022-04-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: extended_dir
|
|
@@ -80,6 +80,34 @@ dependencies:
|
|
|
80
80
|
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: simplecov
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: simplecov-cobertura
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
83
111
|
description: simple and extensible test library toolkit
|
|
84
112
|
email: sean.christopher.gregory@gmail.com
|
|
85
113
|
executables:
|
|
@@ -90,12 +118,13 @@ files:
|
|
|
90
118
|
- bin/theorize
|
|
91
119
|
- src/experiment.rb
|
|
92
120
|
- src/harness.rb
|
|
93
|
-
- src/stdout_reporter.rb
|
|
121
|
+
- src/publishers/stdout_reporter.rb
|
|
94
122
|
- src/theorem.rb
|
|
95
123
|
- src/theorem/beaker.rb
|
|
96
124
|
- src/theorem/completed_test.rb
|
|
97
125
|
- src/theorem/harness.rb
|
|
98
126
|
- src/theorem/hypothesis.rb
|
|
127
|
+
- src/theorem/notation.rb
|
|
99
128
|
- src/theorem/registry.rb
|
|
100
129
|
- src/theorem/reporter.rb
|
|
101
130
|
- src/theorem/test.rb
|
data/src/stdout_reporter.rb
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
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
|