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 +4 -4
- data/bin/theorize +15 -2
- data/src/experiment.rb +19 -0
- data/src/harness.rb +23 -30
- data/src/stdout_reporter.rb +70 -0
- data/src/theorem/beaker.rb +66 -0
- data/src/theorem/completed_test.rb +27 -0
- data/src/theorem/harness.rb +57 -0
- data/src/theorem/hypothesis.rb +40 -0
- data/src/theorem/registry.rb +43 -0
- data/src/theorem/reporter.rb +28 -0
- data/src/theorem/test.rb +206 -0
- data/src/theorem.rb +61 -0
- metadata +29 -20
- data/src/hypothesis.rb +0 -137
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 91647688dc2f743ee8d64461f26ee5f2265be617c82623a97e0cfd7cdec58aa8
         | 
| 4 | 
            +
              data.tar.gz: 410086697fb9d6718c3f05a64cbac277b59fa4ced15cf679484b9eed11bb9edf
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 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 | 
            -
             | 
| 3 | 
            +
            require 'slop'
         | 
| 4 | 
            +
            require_relative '../src/theorem'
         | 
| 4 5 |  | 
| 5 | 
            -
             | 
| 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 | 
            -
             | 
| 2 | 
            -
            require 'extended_dir'
         | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 3 2 |  | 
| 4 | 
            -
             | 
| 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 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 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 | 
            -
             | 
| 12 | 
            +
              # module retry harness
         | 
| 13 | 
            +
              module RetryHarness
         | 
| 14 | 
            +
                include Control::Harness
         | 
| 28 15 |  | 
| 29 | 
            -
             | 
| 16 | 
            +
                on_run do |tests|
         | 
| 17 | 
            +
                  arr_of_tests = tests.map { |test| { test: test, index: 0 } }
         | 
| 30 18 |  | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 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
         | 
    
        data/src/theorem/test.rb
    ADDED
    
    | @@ -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 | 
| 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- | 
| 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:  | 
| 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:  | 
| 26 | 
            +
                    version: 0.1.1
         | 
| 27 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            -
              name:  | 
| 28 | 
            +
              name: slop
         | 
| 29 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 30 | 
             
                requirements:
         | 
| 31 | 
            -
                - - " | 
| 31 | 
            +
                - - "~>"
         | 
| 32 32 | 
             
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            -
                    version:  | 
| 34 | 
            -
              type: : | 
| 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:  | 
| 40 | 
            +
                    version: 4.9.2
         | 
| 41 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            -
              name:  | 
| 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:  | 
| 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:  | 
| 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 | 
| 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/ | 
| 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:// | 
| 106 | 
            +
              source_code_uri: https://gitlab.com/skinnyjames/theorem
         | 
| 98 107 | 
             
            post_install_message:
         | 
| 99 108 | 
             
            rdoc_options: []
         | 
| 100 109 | 
             
            require_paths:
         | 
| 101 | 
            -
            -  | 
| 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 | 
| 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
         |