prop_check 0.6.1 → 0.9.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/.tool-versions +1 -1
- data/.travis.yml +12 -1
- data/CHANGELOG.md +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +6 -4
- data/README.md +75 -15
- data/lib/prop_check.rb +23 -4
- data/lib/prop_check/generator.rb +20 -1
- data/lib/prop_check/generators.rb +87 -15
- data/lib/prop_check/helper/lazy_append.rb +18 -0
- data/lib/prop_check/lazy_tree.rb +8 -15
- data/lib/prop_check/property.rb +109 -31
- data/lib/prop_check/version.rb +1 -1
- data/prop_check.gemspec +1 -1
- metadata +9 -11
- data/lib/prop_check/property/check_evaluator.rb +0 -45
- data/lib/prop_check/rspec.rb +0 -14
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8e5aeef61ddf82569ca885327d32b9fe8fd939f2fdbef48709cb19504b5cf041
         | 
| 4 | 
            +
              data.tar.gz: d914dcac32f7a3a976661a0280c71f0a7a665411550aed9902317b04c82ee290
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 85c5b76ac37ba8e03b2c41c04c4cc359844fa2e52d0af8893787531c23f3a54b046eb7eed13b055fed44c8983f87cbfe492d1524746624a3744300a73caf01da
         | 
| 7 | 
            +
              data.tar.gz: 9beb7ac4e605c72cc8e9b3907e727ae532c2eec4a530b6a9b7c31445135e37c8849c9f186d4571aaf38c24e28989ebe856ed2e5e873baf2ed7b740a73561dae7
         | 
    
        data/.tool-versions
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            ruby 2.5 | 
| 1 | 
            +
            ruby 2.6.5
         | 
    
        data/.travis.yml
    CHANGED
    
    | @@ -4,4 +4,15 @@ language: ruby | |
| 4 4 | 
             
            cache: bundler
         | 
| 5 5 | 
             
            rvm:
         | 
| 6 6 | 
             
              - 2.5.1
         | 
| 7 | 
            -
            before_install: gem install bundler -v 2.0. | 
| 7 | 
            +
            before_install: gem install bundler -v 2.0.2
         | 
| 8 | 
            +
            env:
         | 
| 9 | 
            +
              global:
         | 
| 10 | 
            +
                - CC_TEST_REPORTER_ID=9d18f5b43e49eecd6c3da64d85ea9c765d3606c129289d7c8cadf6d448713311
         | 
| 11 | 
            +
            before_script:
         | 
| 12 | 
            +
              - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
         | 
| 13 | 
            +
              - chmod +x ./cc-test-reporter
         | 
| 14 | 
            +
              - ./cc-test-reporter before-build
         | 
| 15 | 
            +
            script:
         | 
| 16 | 
            +
              - bundle exec rspec
         | 
| 17 | 
            +
            after_script:
         | 
| 18 | 
            +
              - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            - 0.8.0 New syntax that is more explicit, passng generated values to blocks as parameters.
         | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,11 +1,12 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                prop_check (0. | 
| 4 | 
            +
                prop_check (0.9.0)
         | 
| 5 5 |  | 
| 6 6 | 
             
            GEM
         | 
| 7 7 | 
             
              remote: https://rubygems.org/
         | 
| 8 8 | 
             
              specs:
         | 
| 9 | 
            +
                awesome_print (1.8.0)
         | 
| 9 10 | 
             
                diff-lcs (1.3)
         | 
| 10 11 | 
             
                docile (1.3.2)
         | 
| 11 12 | 
             
                doctest-core (0.0.2)
         | 
| @@ -13,7 +14,7 @@ GEM | |
| 13 14 | 
             
                  doctest-core (~> 0.0.2)
         | 
| 14 15 | 
             
                  rspec
         | 
| 15 16 | 
             
                json (2.2.0)
         | 
| 16 | 
            -
                rake ( | 
| 17 | 
            +
                rake (12.3.3)
         | 
| 17 18 | 
             
                rspec (3.8.0)
         | 
| 18 19 | 
             
                  rspec-core (~> 3.8.0)
         | 
| 19 20 | 
             
                  rspec-expectations (~> 3.8.0)
         | 
| @@ -37,12 +38,13 @@ PLATFORMS | |
| 37 38 | 
             
              ruby
         | 
| 38 39 |  | 
| 39 40 | 
             
            DEPENDENCIES
         | 
| 41 | 
            +
              awesome_print
         | 
| 40 42 | 
             
              bundler (~> 2.0)
         | 
| 41 43 | 
             
              doctest-rspec
         | 
| 42 44 | 
             
              prop_check!
         | 
| 43 | 
            -
              rake (~>  | 
| 45 | 
            +
              rake (~> 12.3)
         | 
| 44 46 | 
             
              rspec (~> 3.0)
         | 
| 45 47 | 
             
              simplecov
         | 
| 46 48 |  | 
| 47 49 | 
             
            BUNDLED WITH
         | 
| 48 | 
            -
               2. | 
| 50 | 
            +
               2.1.4
         | 
    
        data/README.md
    CHANGED
    
    | @@ -2,6 +2,11 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            PropCheck allows you to do Property Testing in Ruby.
         | 
| 4 4 |  | 
| 5 | 
            +
            [](https://rubygems.org/gems/prop_check)
         | 
| 6 | 
            +
            [](https://travis-ci.org/Qqwy/ruby-prop_check)
         | 
| 7 | 
            +
            [](https://codeclimate.com/github/Qqwy/ruby-prop_check/maintainability)
         | 
| 8 | 
            +
            [](https://www.rubydoc.info/github/Qqwy/ruby-prop_check/master/PropCheck)
         | 
| 9 | 
            +
             | 
| 5 10 | 
             
            It features:
         | 
| 6 11 |  | 
| 7 12 | 
             
            - Generators for common datatypes.
         | 
| @@ -9,7 +14,7 @@ It features: | |
| 9 14 | 
             
            - Shrinking to a minimal counter-example on failure.
         | 
| 10 15 |  | 
| 11 16 |  | 
| 12 | 
            -
            ## TODOs before release
         | 
| 17 | 
            +
            ## TODOs before stable release
         | 
| 13 18 |  | 
| 14 19 | 
             
            Before releasing this gem on Rubygems, the following things need to be finished:
         | 
| 15 20 |  | 
| @@ -21,11 +26,11 @@ Before releasing this gem on Rubygems, the following things need to be finished: | |
| 21 26 | 
             
              - [x] Stop after a ludicrous amount of generator runs, to prevent malfunctioning (infinitely looping) generators from blowing up someone's computer.
         | 
| 22 27 | 
             
             - [x] Look into customization of settings from e.g. command line arguments.
         | 
| 23 28 | 
             
            - [x] Good, unicode-compliant, string generators.
         | 
| 24 | 
            -
            - [ | 
| 29 | 
            +
            - [x] Filtering generator outputs.
         | 
| 25 30 |  | 
| 26 31 | 
             
            # Nice-to-haves
         | 
| 27 32 |  | 
| 28 | 
            -
            - [ | 
| 33 | 
            +
            - [x] Basic integration with RSpec. See also https://groups.google.com/forum/#!msg/rspec/U-LmL0OnO-Y/iW_Jcd6JBAAJ for progress on this.
         | 
| 29 34 | 
             
             - [ ] `aggregate` , `resize` and similar generator-modifying calls (c.f. PropEr's variants of these) which will help with introspection/metrics.
         | 
| 30 35 | 
             
             - [ ] Integration with other Ruby test frameworks.
         | 
| 31 36 | 
             
             - Stateful property testing. If implemented at some point, will probably happen in a separate add-on library.
         | 
| @@ -61,8 +66,9 @@ _(to be precise: a method on the execution context is defined which returns the | |
| 61 66 | 
             
            Raise an exception from the block if there is a problem. If there is no problem, just return normally.
         | 
| 62 67 |  | 
| 63 68 | 
             
            ```ruby
         | 
| 69 | 
            +
            include PropCheck::Generators
         | 
| 64 70 | 
             
            # testing that Enumerable#sort sorts in ascending order
         | 
| 65 | 
            -
            PropCheck.forall( | 
| 71 | 
            +
            PropCheck.forall(array(integer)) do |numbers|
         | 
| 66 72 | 
             
              sorted_numbers = numbers.sort
         | 
| 67 73 |  | 
| 68 74 | 
             
              # Check that no number is smaller than the previous number
         | 
| @@ -72,6 +78,50 @@ PropCheck.forall(numbers: array(integer())) do | |
| 72 78 | 
             
            end
         | 
| 73 79 | 
             
            ```
         | 
| 74 80 |  | 
| 81 | 
            +
             | 
| 82 | 
            +
            Here is another example, using it inside a test case.
         | 
| 83 | 
            +
            Here we check if `naive_average` indeed always returns an integer for all arrays of numbers we can pass it:
         | 
| 84 | 
            +
             | 
| 85 | 
            +
            ```ruby
         | 
| 86 | 
            +
            # Somewhere you have this function definition:
         | 
| 87 | 
            +
            def naive_average(array)
         | 
| 88 | 
            +
              array.sum / array.length
         | 
| 89 | 
            +
            end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            # And then in a test case:
         | 
| 92 | 
            +
            include PropCheck::Generators
         | 
| 93 | 
            +
            PropCheck.forall(numbers: array(integer)) do |numbers:|
         | 
| 94 | 
            +
              result = naive_average(numbers)
         | 
| 95 | 
            +
              unless result.is_a?(Integer) do
         | 
| 96 | 
            +
                raise "Expected the average to be an integer!"
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
            end
         | 
| 99 | 
            +
            ```
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            When running this particular example PropCheck very quickly finds out that we have made a programming mistake:
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            ```ruby
         | 
| 104 | 
            +
            ZeroDivisionError: 
         | 
| 105 | 
            +
            (after 6 successful property test runs)
         | 
| 106 | 
            +
            Failed on: 
         | 
| 107 | 
            +
            `{
         | 
| 108 | 
            +
                :numbers => []
         | 
| 109 | 
            +
            }`
         | 
| 110 | 
            +
             | 
| 111 | 
            +
            Exception message:
         | 
| 112 | 
            +
            ---
         | 
| 113 | 
            +
            divided by 0
         | 
| 114 | 
            +
            ---
         | 
| 115 | 
            +
             | 
| 116 | 
            +
            (shrinking impossible)
         | 
| 117 | 
            +
            ---
         | 
| 118 | 
            +
            ```
         | 
| 119 | 
            +
             | 
| 120 | 
            +
            Clearly we forgot to handle the case of an empty array being passed to the function.
         | 
| 121 | 
            +
            This is a good example of the kind of conceptual bugs that PropCheck (and property-based testing in general)
         | 
| 122 | 
            +
            are able to check for.
         | 
| 123 | 
            +
             | 
| 124 | 
            +
             | 
| 75 125 | 
             
            #### Shrinking
         | 
| 76 126 |  | 
| 77 127 | 
             
            When a failure is found, PropCheck will re-run the block given to `forall` to test
         | 
| @@ -83,8 +133,8 @@ PropCheck will see if the failure still happens with `x = 50`. | |
| 83 133 | 
             
            If it does , it will try `x = 25`. If not, it will try `x = 75`, and so on.
         | 
| 84 134 |  | 
| 85 135 | 
             
            This means if something only goes wrong for `x = 2`, the program will try:
         | 
| 86 | 
            -
            - `x = 100`(fails) | 
| 87 | 
            -
            - x = 50`(fails), 
         | 
| 136 | 
            +
            - `x = 100`(fails),
         | 
| 137 | 
            +
            - `x = 50`(fails), 
         | 
| 88 138 | 
             
            - `x = 25`(fails), 
         | 
| 89 139 | 
             
            - `x = 12`(fails), 
         | 
| 90 140 | 
             
            - `x = 6`(fails), 
         | 
| @@ -101,21 +151,28 @@ A short summary: | |
| 101 151 | 
             
            - Arrays and hashes shrink to fewer elements, as well as shrinking their elements.
         | 
| 102 152 | 
             
            - Strings shrink to shorter strings, as well as characters earlier in their alphabet.
         | 
| 103 153 |  | 
| 154 | 
            +
            ### Builtin Generators
         | 
| 155 | 
            +
             | 
| 156 | 
            +
            PropCheck comes with [many builtin generators in the PropCheck::Generators](https://www.rubydoc.info/github/Qqwy/ruby-prop_check/master/PropCheck/Generators) module.
         | 
| 157 | 
            +
             | 
| 158 | 
            +
            It contains generators for:
         | 
| 159 | 
            +
            - (any, positive, negative, etc.) integers, 
         | 
| 160 | 
            +
            - (any, only real-valued) floats, 
         | 
| 161 | 
            +
            - (any, printable only, alphanumeric only, etc) strings and symbols
         | 
| 162 | 
            +
            - fixed-size arrays and hashes 
         | 
| 163 | 
            +
            - as well as varying-size arrays and hashes.
         | 
| 164 | 
            +
            - and many more!
         | 
| 165 | 
            +
             | 
| 166 | 
            +
            It is common to call `include PropCheck::Generators` in e.g. your testing-suite files to be able to use these.
         | 
| 167 | 
            +
            If you want to be more explicit (but somewhat more verbose) when calling these functions. feel free to e.g. create a module-alias (like `PG = PropCheck::Generators`) instead.
         | 
| 104 168 |  | 
| 105 169 | 
             
            ### Writing Custom Generators
         | 
| 106 170 |  | 
| 107 | 
            -
            PropCheck comes bundled with a bunch of common generators | 
| 108 | 
            -
            - integers
         | 
| 109 | 
            -
            - floats
         | 
| 110 | 
            -
            - strings
         | 
| 111 | 
            -
            - symbols
         | 
| 112 | 
            -
            - arrays
         | 
| 113 | 
            -
            - hashes
         | 
| 114 | 
            -
            etc.
         | 
| 171 | 
            +
            As described in the previous section, PropCheck already comes bundled with a bunch of common generators.
         | 
| 115 172 |  | 
| 116 173 | 
             
            However, you can easily adapt them to generate your own datatypes:
         | 
| 117 174 |  | 
| 118 | 
            -
            #### Generator#wrap
         | 
| 175 | 
            +
            #### Generators#constant / Generator#wrap
         | 
| 119 176 |  | 
| 120 177 | 
             
            Always returns the given value. No shrinking.
         | 
| 121 178 |  | 
| @@ -157,6 +214,9 @@ you can use `Generators.frequency` which takes a hash of (integer_frequency => g | |
| 157 214 | 
             
            There are even more functions in the `Generator` class and the `Generators` module that you might want to use,
         | 
| 158 215 | 
             
            although above are the most generally useful ones.
         | 
| 159 216 |  | 
| 217 | 
            +
            [PropCheck::Generator documentation](https://www.rubydoc.info/github/Qqwy/ruby-prop_check/master/PropCheck/Generator)
         | 
| 218 | 
            +
            [PropCheck::Generators documentation](https://www.rubydoc.info/github/Qqwy/ruby-prop_check/master/PropCheck/Generators)
         | 
| 219 | 
            +
             | 
| 160 220 | 
             
            ## Development
         | 
| 161 221 |  | 
| 162 222 | 
             
            After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
         | 
    
        data/lib/prop_check.rb
    CHANGED
    
    | @@ -3,14 +3,33 @@ require 'prop_check/property' | |
| 3 3 | 
             
            require 'prop_check/generator'
         | 
| 4 4 | 
             
            require 'prop_check/generators'
         | 
| 5 5 | 
             
            require 'prop_check/helper'
         | 
| 6 | 
            +
            ##
         | 
| 7 | 
            +
            # Main module of the PropCheck library.
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            # You probably want to look at the documentation of
         | 
| 10 | 
            +
            # PropCheck::Generator and PropCheck::Generators
         | 
| 11 | 
            +
            # to find out more about how to use generators.
         | 
| 12 | 
            +
            #
         | 
| 13 | 
            +
            # Common usage is to call `extend PropCheck` in your (testing) modules.
         | 
| 14 | 
            +
            #
         | 
| 15 | 
            +
            # This will:
         | 
| 16 | 
            +
            # 1. Add the local method `forall` which  will call `PropCheck.forall`
         | 
| 17 | 
            +
            # 2. `include PropCheck::Generators`.
         | 
| 18 | 
            +
            #
         | 
| 6 19 | 
             
            module PropCheck
         | 
| 7 | 
            -
               | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 20 | 
            +
              module Errors
         | 
| 21 | 
            +
                class Error < StandardError; end
         | 
| 22 | 
            +
                class UserError < Error; end
         | 
| 23 | 
            +
                class GeneratorExhaustedError < UserError; end
         | 
| 24 | 
            +
                class MaxShrinkStepsExceededError < UserError; end
         | 
| 25 | 
            +
              end
         | 
| 11 26 |  | 
| 12 27 | 
             
              extend self
         | 
| 13 28 |  | 
| 29 | 
            +
              ##
         | 
| 30 | 
            +
              # Runs a property.
         | 
| 31 | 
            +
              #
         | 
| 32 | 
            +
              # See the README for more details.
         | 
| 14 33 | 
             
              def forall(*args, **kwargs, &block)
         | 
| 15 34 | 
             
                PropCheck::Property.forall(*args, **kwargs, &block)
         | 
| 16 35 | 
             
              end
         | 
    
        data/lib/prop_check/generator.rb
    CHANGED
    
    | @@ -72,7 +72,7 @@ module PropCheck | |
| 72 72 | 
             
                  #   end.flatten
         | 
| 73 73 | 
             
                  # end
         | 
| 74 74 | 
             
                  Generator.new do |size, rng|
         | 
| 75 | 
            -
                    outer_result = generate(size, rng)
         | 
| 75 | 
            +
                    outer_result = self.generate(size, rng)
         | 
| 76 76 | 
             
                    outer_result.bind do |outer_val|
         | 
| 77 77 | 
             
                      inner_generator = generator_proc.call(outer_val)
         | 
| 78 78 | 
             
                      inner_generator.generate(size, rng)
         | 
| @@ -91,5 +91,24 @@ module PropCheck | |
| 91 91 | 
             
                    result.map(&proc)
         | 
| 92 92 | 
             
                  end
         | 
| 93 93 | 
             
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                ##
         | 
| 96 | 
            +
                # Creates a new Generator that only produces a value when the block `condition` returns a truthy value.
         | 
| 97 | 
            +
                def where(&condition)
         | 
| 98 | 
            +
                  self.map do |result|
         | 
| 99 | 
            +
                    if condition.call(*result)
         | 
| 100 | 
            +
                      result
         | 
| 101 | 
            +
                    else
         | 
| 102 | 
            +
                      :"_PropCheck.filter_me"
         | 
| 103 | 
            +
                    end
         | 
| 104 | 
            +
                  end
         | 
| 105 | 
            +
                  # self.map do |*result|
         | 
| 106 | 
            +
                  #   if condition.call(*result)
         | 
| 107 | 
            +
                  #     result
         | 
| 108 | 
            +
                  #   else
         | 
| 109 | 
            +
                  #     :'_PropCheck.filter_me'
         | 
| 110 | 
            +
                  #   end
         | 
| 111 | 
            +
                  # end
         | 
| 112 | 
            +
                end
         | 
| 94 113 | 
             
              end
         | 
| 95 114 | 
             
            end
         | 
| @@ -1,3 +1,6 @@ | |
| 1 | 
            +
            # coding: utf-8
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 1 4 | 
             
            require 'prop_check/generator'
         | 
| 2 5 | 
             
            require 'prop_check/lazy_tree'
         | 
| 3 6 | 
             
            module PropCheck
         | 
| @@ -116,27 +119,40 @@ module PropCheck | |
| 116 119 | 
             
                end
         | 
| 117 120 |  | 
| 118 121 | 
             
                ##
         | 
| 119 | 
            -
                # Generates floating | 
| 122 | 
            +
                # Generates floating-point numbers
         | 
| 120 123 | 
             
                # These start small (around 0)
         | 
| 121 124 | 
             
                # and become more extreme (large positive and large negative numbers)
         | 
| 122 125 | 
             
                #
         | 
| 126 | 
            +
                # Will only generate 'reals',
         | 
| 127 | 
            +
                # that is: no infinity, no NaN,
         | 
| 128 | 
            +
                # no numbers testing the limits of floating-point arithmetic.
         | 
| 123 129 | 
             
                #
         | 
| 124 130 | 
             
                # Shrinks to numbers closer to zero.
         | 
| 125 131 | 
             
                #
         | 
| 126 | 
            -
                #  | 
| 127 | 
            -
                 | 
| 128 | 
            -
             | 
| 129 | 
            -
                  #   integer.bind do |b|
         | 
| 130 | 
            -
                  #     integer.bind do |c|
         | 
| 131 | 
            -
                  #       Generator.wrap(fraction(a, b, c))
         | 
| 132 | 
            -
                  #     end
         | 
| 133 | 
            -
                  #   end
         | 
| 134 | 
            -
                  # end
         | 
| 132 | 
            +
                #    >> Generators.real_float().sample(10, size: 10, rng: Random.new(42))
         | 
| 133 | 
            +
                #    => [-2.2, -0.2727272727272727, 4.0, 1.25, -3.7272727272727275, -8.833333333333334, -8.090909090909092, 1.1428571428571428, 0.0, 8.0]
         | 
| 134 | 
            +
                def real_float
         | 
| 135 135 | 
             
                  tuple(integer, integer, integer).map do |a, b, c|
         | 
| 136 136 | 
             
                    fraction(a, b, c)
         | 
| 137 137 | 
             
                  end
         | 
| 138 138 | 
             
                end
         | 
| 139 139 |  | 
| 140 | 
            +
                @special_floats = [Float::NAN, Float::INFINITY, -Float::INFINITY, Float::MAX, Float::MIN, 0.0.next_float, 0.0.prev_float]
         | 
| 141 | 
            +
                ##
         | 
| 142 | 
            +
                # Generates floating-point numbers
         | 
| 143 | 
            +
                # Will generate NaN, Infinity, -Infinity,
         | 
| 144 | 
            +
                # as well as Float::EPSILON, Float::MAX, Float::MIN,
         | 
| 145 | 
            +
                # 0.0.next_float, 0.0.prev_float,
         | 
| 146 | 
            +
                # to test the handling of floating-point edge cases.
         | 
| 147 | 
            +
                # Approx. 1/100 generated numbers is a special one.
         | 
| 148 | 
            +
                #
         | 
| 149 | 
            +
                # Shrinks to smaller, real floats.
         | 
| 150 | 
            +
                #    >> Generators.float().sample(10, size: 10, rng: Random.new(42))
         | 
| 151 | 
            +
                #    => [4.0, 9.555555555555555, 0.0, -Float::INFINITY, 5.5, -5.818181818181818, 1.1428571428571428, 0.0, 8.0, 7.857142857142858]
         | 
| 152 | 
            +
                def float
         | 
| 153 | 
            +
                  frequency(99 => real_float, 1 => one_of(*@special_floats.map(&method(:constant))))
         | 
| 154 | 
            +
                end
         | 
| 155 | 
            +
             | 
| 140 156 | 
             
                ##
         | 
| 141 157 | 
             
                # Picks one of the given generators in `choices` at random uniformly every time.
         | 
| 142 158 | 
             
                #
         | 
| @@ -156,6 +172,9 @@ module PropCheck | |
| 156 172 | 
             
                # (representing the relative frequency of this generator)
         | 
| 157 173 | 
             
                # and values to be generators.
         | 
| 158 174 | 
             
                #
         | 
| 175 | 
            +
                # Side note: If you want to use the same frequency number for multiple generators,
         | 
| 176 | 
            +
                # Ruby syntax requires you to send an array of two-element arrays instead of a hash.
         | 
| 177 | 
            +
                #
         | 
| 159 178 | 
             
                # Shrinks to arbitrary elements (since hashes are not ordered).
         | 
| 160 179 | 
             
                #
         | 
| 161 180 | 
             
                #   >> Generators.frequency(5 => Generators.integer, 1 => Generators.printable_ascii_char).sample(size: 10, rng: Random.new(42))
         | 
| @@ -174,7 +193,7 @@ module PropCheck | |
| 174 193 | 
             
                #
         | 
| 175 194 | 
             
                # Shrinks element generators, one at a time (trying last one first).
         | 
| 176 195 | 
             
                #
         | 
| 177 | 
            -
                #   >> Generators.tuple(Generators.integer, Generators. | 
| 196 | 
            +
                #   >> Generators.tuple(Generators.integer, Generators.real_float).call(10, Random.new(42))
         | 
| 178 197 | 
             
                #   => [-4, 13.0]
         | 
| 179 198 | 
             
                def tuple(*generators)
         | 
| 180 199 | 
             
                  Generator.new do |size, rng|
         | 
| @@ -191,7 +210,7 @@ module PropCheck | |
| 191 210 | 
             
                #
         | 
| 192 211 | 
             
                # Shrinks element generators.
         | 
| 193 212 | 
             
                #
         | 
| 194 | 
            -
                #    >> Generators.fixed_hash(a: Generators.integer(), b: Generators. | 
| 213 | 
            +
                #    >> Generators.fixed_hash(a: Generators.integer(), b: Generators.real_float(), c: Generators.integer()).call(10, Random.new(42))
         | 
| 195 214 | 
             
                #    => {:a=>-4, :b=>13.0, :c=>-3}
         | 
| 196 215 | 
             
                def fixed_hash(hash)
         | 
| 197 216 | 
             
                  keypair_generators =
         | 
| @@ -242,7 +261,9 @@ module PropCheck | |
| 242 261 | 
             
                # containing one of a..z, A..Z, 0..9
         | 
| 243 262 | 
             
                #
         | 
| 244 263 | 
             
                # Shrinks towards lowercase 'a'.
         | 
| 245 | 
            -
                # | 
| 264 | 
            +
                #
         | 
| 265 | 
            +
                #    >> Generators.alphanumeric_char.sample(5, size: 10, rng: Random.new(42))
         | 
| 266 | 
            +
                #    => ["M", "Z", "C", "o", "Q"]
         | 
| 246 267 | 
             
                def alphanumeric_char
         | 
| 247 268 | 
             
                  one_of(*@alphanumeric_chars.map(&method(:constant)))
         | 
| 248 269 | 
             
                end
         | 
| @@ -250,7 +271,11 @@ module PropCheck | |
| 250 271 | 
             
                ##
         | 
| 251 272 | 
             
                # Generates a string
         | 
| 252 273 | 
             
                # containing only the characters a..z, A..Z, 0..9
         | 
| 274 | 
            +
                #
         | 
| 253 275 | 
             
                # Shrinks towards fewer characters, and towards lowercase 'a'.
         | 
| 276 | 
            +
                #
         | 
| 277 | 
            +
                #    >> Generators.alphanumeric_string.sample(5, size: 10, rng: Random.new(42))
         | 
| 278 | 
            +
                #    => ["ZCoQ", "8uM", "wkkx0JNx", "v0bxRDLb", "Gl5v8RyWA6"]
         | 
| 254 279 | 
             
                def alphanumeric_string
         | 
| 255 280 | 
             
                  array(alphanumeric_char).map(&:join)
         | 
| 256 281 | 
             
                end
         | 
| @@ -272,7 +297,11 @@ module PropCheck | |
| 272 297 | 
             
                ##
         | 
| 273 298 | 
             
                # Generates strings
         | 
| 274 299 | 
             
                # from the printable ASCII character set.
         | 
| 300 | 
            +
                #
         | 
| 275 301 | 
             
                # Shrinks towards fewer characters, and towards ' '.
         | 
| 302 | 
            +
                #
         | 
| 303 | 
            +
                #    >> Generators.printable_ascii_string.sample(5, size: 10, rng: Random.new(42))
         | 
| 304 | 
            +
                #    => ["S|.g", "rvjjw7\"5T!", "=", "!_[4@", "Y"]
         | 
| 276 305 | 
             
                def printable_ascii_string
         | 
| 277 306 | 
             
                  array(printable_ascii_char).map(&:join)
         | 
| 278 307 | 
             
                end
         | 
| @@ -295,7 +324,11 @@ module PropCheck | |
| 295 324 | 
             
                ##
         | 
| 296 325 | 
             
                # Generates a single-character string
         | 
| 297 326 | 
             
                # from the printable ASCII character set.
         | 
| 327 | 
            +
                #
         | 
| 298 328 | 
             
                # Shrinks towards '\n'.
         | 
| 329 | 
            +
                #
         | 
| 330 | 
            +
                #    >> Generators.ascii_char.sample(size: 10, rng: Random.new(42))
         | 
| 331 | 
            +
                #    => ["d", "S", "|", ".", "g", "\\", "4", "d", "r", "v"]
         | 
| 299 332 | 
             
                def ascii_char
         | 
| 300 333 | 
             
                  one_of(*@ascii_chars.map(&method(:constant)))
         | 
| 301 334 | 
             
                end
         | 
| @@ -303,7 +336,11 @@ module PropCheck | |
| 303 336 | 
             
                ##
         | 
| 304 337 | 
             
                # Generates strings
         | 
| 305 338 | 
             
                # from the printable ASCII character set.
         | 
| 339 | 
            +
                #
         | 
| 306 340 | 
             
                # Shrinks towards fewer characters, and towards '\n'.
         | 
| 341 | 
            +
                #
         | 
| 342 | 
            +
                #    >> Generators.ascii_string.sample(5, size: 10, rng: Random.new(42))
         | 
| 343 | 
            +
                #    => ["S|.g", "drvjjw\b\a7\"", "!w=E!_[4@k", "x", "zZI{[o"]
         | 
| 307 344 | 
             
                def ascii_string
         | 
| 308 345 | 
             
                  array(ascii_char).map(&:join)
         | 
| 309 346 | 
             
                end
         | 
| @@ -321,6 +358,8 @@ module PropCheck | |
| 321 358 | 
             
                #
         | 
| 322 359 | 
             
                # Shrinks towards characters with lower codepoints, e.g. ASCII
         | 
| 323 360 | 
             
                #
         | 
| 361 | 
            +
                #    >> Generators.printable_char.sample(size: 10, rng: Random.new(42))
         | 
| 362 | 
            +
                #    => ["吏", "", "", "", "", "", "", "", "", "Ȍ"]
         | 
| 324 363 | 
             
                def printable_char
         | 
| 325 364 | 
             
                  one_of(*@printable_chars.map(&method(:constant)))
         | 
| 326 365 | 
             
                end
         | 
| @@ -331,6 +370,8 @@ module PropCheck | |
| 331 370 | 
             
                #
         | 
| 332 371 | 
             
                # Shrinks towards shorter strings, and towards characters with lower codepoints, e.g. ASCII
         | 
| 333 372 | 
             
                #
         | 
| 373 | 
            +
                #    >> Generators.printable_string.sample(5, size: 10, rng: Random.new(42))
         | 
| 374 | 
            +
                #    => ["", "Ȍ", "𐁂", "Ȕ", ""]
         | 
| 334 375 | 
             
                def printable_string
         | 
| 335 376 | 
             
                  array(printable_char).map(&:join)
         | 
| 336 377 | 
             
                end
         | 
| @@ -341,6 +382,8 @@ module PropCheck | |
| 341 382 | 
             
                #
         | 
| 342 383 | 
             
                # Shrinks towards characters with lower codepoints, e.g. ASCII
         | 
| 343 384 | 
             
                #
         | 
| 385 | 
            +
                #    >> Generators.printable_char.sample(size: 10, rng: Random.new(42))
         | 
| 386 | 
            +
                #    => ["吏", "", "", "", "", "", "", "", "", "Ȍ"]
         | 
| 344 387 | 
             
                def char
         | 
| 345 388 | 
             
                  choose(0..0x10FFFF).map do |num|
         | 
| 346 389 | 
             
                    [num].pack('U')
         | 
| @@ -353,6 +396,8 @@ module PropCheck | |
| 353 396 | 
             
                #
         | 
| 354 397 | 
             
                # Shrinks towards characters with lower codepoints, e.g. ASCII
         | 
| 355 398 | 
             
                #
         | 
| 399 | 
            +
                #    >> Generators.string.sample(5, size: 10, rng: Random.new(42))
         | 
| 400 | 
            +
                #    => ["\u{A3DB3}𠍜\u{3F46A}\u{1AEBC}", "𡡹\u{DED74}𪱣\u{43E97}ꂂ\u{50695}\u{C0301}", "\u{4FD9D}", "\u{C14BF}\u{193BB}𭇋\u{76B58}", "𦐺\u{9FDDB}\u{80ABB}\u{9E3CF}𐂽\u{14AAE}"]
         | 
| 356 401 | 
             
                def string
         | 
| 357 402 | 
             
                  array(char).map(&:join)
         | 
| 358 403 | 
             
                end
         | 
| @@ -361,7 +406,9 @@ module PropCheck | |
| 361 406 | 
             
                # Generates either `true` or `false`
         | 
| 362 407 | 
             
                #
         | 
| 363 408 | 
             
                # Shrinks towards `false`
         | 
| 364 | 
            -
                # | 
| 409 | 
            +
                #
         | 
| 410 | 
            +
                #    >> Generators.boolean.sample(5, size: 10, rng: Random.new(42))
         | 
| 411 | 
            +
                #    => [false, true, false, false, false]
         | 
| 365 412 | 
             
                def boolean
         | 
| 366 413 | 
             
                  one_of(constant(false), constant(true))
         | 
| 367 414 | 
             
                end
         | 
| @@ -370,6 +417,9 @@ module PropCheck | |
| 370 417 | 
             
                # Generates always `nil`.
         | 
| 371 418 | 
             
                #
         | 
| 372 419 | 
             
                # Does not shrink.
         | 
| 420 | 
            +
                #
         | 
| 421 | 
            +
                #    >> Generators.nil.sample(5, size: 10, rng: Random.new(42))
         | 
| 422 | 
            +
                #    => [nil, nil, nil, nil, nil]
         | 
| 373 423 | 
             
                def nil
         | 
| 374 424 | 
             
                  constant(nil)
         | 
| 375 425 | 
             
                end
         | 
| @@ -379,15 +429,34 @@ module PropCheck | |
| 379 429 | 
             
                #
         | 
| 380 430 | 
             
                # Shrinks towards `nil`.
         | 
| 381 431 | 
             
                #
         | 
| 432 | 
            +
                #    >> Generators.falsey.sample(5, size: 10, rng: Random.new(42))
         | 
| 433 | 
            +
                #    => [nil, false, nil, nil, nil]
         | 
| 382 434 | 
             
                def falsey
         | 
| 383 435 | 
             
                  one_of(constant(nil), constant(false))
         | 
| 384 436 | 
             
                end
         | 
| 385 437 |  | 
| 438 | 
            +
                ##
         | 
| 439 | 
            +
                # Generates symbols consisting of lowercase letters and potentially underscores.
         | 
| 440 | 
            +
                #
         | 
| 441 | 
            +
                # Shrinks towards shorter symbols and the letter 'a'.
         | 
| 442 | 
            +
                #
         | 
| 443 | 
            +
                #    >> Generators.simple_symbol.sample(5, size: 10, rng: Random.new(42))
         | 
| 444 | 
            +
                #    => [:tokh, :gzswkkxudh, :vubxlfbu, :lzvlyq__jp, :oslw]
         | 
| 445 | 
            +
                def simple_symbol
         | 
| 446 | 
            +
                  alphabet = ('a'..'z').to_a
         | 
| 447 | 
            +
                  alphabet << '_'
         | 
| 448 | 
            +
                  array(one_of(*alphabet.map(&method(:constant))))
         | 
| 449 | 
            +
                    .map(&:join)
         | 
| 450 | 
            +
                    .map(&:to_sym)
         | 
| 451 | 
            +
                end
         | 
| 452 | 
            +
             | 
| 386 453 | 
             
                ##
         | 
| 387 454 | 
             
                # Generates common terms that are not `nil` or `false`.
         | 
| 388 455 | 
             
                #
         | 
| 389 456 | 
             
                # Shrinks towards simpler terms, like `true`, an empty array, a single character or an integer.
         | 
| 390 457 | 
             
                #
         | 
| 458 | 
            +
                #    >> Generators.truthy.sample(5, size: 10, rng: Random.new(42))
         | 
| 459 | 
            +
                #    => [[4, 0, -3, 10, -4, 8, 0, 0, 10], -3, [5.5, -5.818181818181818, 1.1428571428571428, 0.0, 8.0, 7.857142857142858, -0.6666666666666665, 5.25], [], ["\u{9E553}\u{DD56E}\u{A5BBB}\u{8BDAB}\u{3E9FC}\u{C4307}\u{DAFAE}\u{1A022}\u{938CD}\u{70631}", "\u{C4C01}\u{32D85}\u{425DC}"]]
         | 
| 391 460 | 
             
                def truthy
         | 
| 392 461 | 
             
                  one_of(constant(true),
         | 
| 393 462 | 
             
                         constant([]),
         | 
| @@ -399,7 +468,7 @@ module PropCheck | |
| 399 468 | 
             
                         array(float),
         | 
| 400 469 | 
             
                         array(char),
         | 
| 401 470 | 
             
                         array(string),
         | 
| 402 | 
            -
                         hash( | 
| 471 | 
            +
                         hash(simple_symbol, integer),
         | 
| 403 472 | 
             
                         hash(string, integer),
         | 
| 404 473 | 
             
                         hash(string, string)
         | 
| 405 474 | 
             
                        )
         | 
| @@ -408,6 +477,9 @@ module PropCheck | |
| 408 477 | 
             
                ##
         | 
| 409 478 | 
             
                # Generates whatever `other_generator` generates
         | 
| 410 479 | 
             
                # but sometimes instead `nil`.`
         | 
| 480 | 
            +
                #
         | 
| 481 | 
            +
                #    >> Generators.nillable(Generators.integer).sample(20, size: 10, rng: Random.new(42))
         | 
| 482 | 
            +
                #    => [9, 10, 8, 0, 10, -3, -8, 10, 1, -9, -10, nil, 1, 6, nil, 1, 9, -8, 8, 10]
         | 
| 411 483 | 
             
                def nillable(other_generator)
         | 
| 412 484 | 
             
                  frequency(9 => other_generator, 1 => constant(nil))
         | 
| 413 485 | 
             
                end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            module PropCheck
         | 
| 2 | 
            +
              module Helper
         | 
| 3 | 
            +
                ##
         | 
| 4 | 
            +
                # A refinement for enumerators
         | 
| 5 | 
            +
                # to allow lazy appending of two (potentially lazy) enumerators:
         | 
| 6 | 
            +
                #   >> [1,2,3].lazy_append([4,5.6]).to_a
         | 
| 7 | 
            +
                #   => [1,2,3,4,5,6]
         | 
| 8 | 
            +
                module LazyAppend
         | 
| 9 | 
            +
                  refine Enumerable do
         | 
| 10 | 
            +
                    ##   >> [1,2,3].lazy_append([4,5.6]).to_a
         | 
| 11 | 
            +
                    ##   => [1,2,3,4,5,6]
         | 
| 12 | 
            +
                    def lazy_append(other_enumerator)
         | 
| 13 | 
            +
                      [self, other_enumerator].lazy.flat_map(&:lazy)
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
    
        data/lib/prop_check/lazy_tree.rb
    CHANGED
    
    | @@ -1,22 +1,13 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
            module LazyAppend
         | 
| 5 | 
            -
              refine Enumerable do
         | 
| 6 | 
            -
                ##   >> [1,2,3].lazy_append([4,5.6]).to_a
         | 
| 7 | 
            -
                ##   => [1,2,3,4,5,6]
         | 
| 8 | 
            -
                def lazy_append(other_enumerator)
         | 
| 9 | 
            -
                  [self, other_enumerator].lazy.flat_map(&:lazy)
         | 
| 10 | 
            -
                end
         | 
| 11 | 
            -
              end
         | 
| 12 | 
            -
            end
         | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'prop_check/helper/lazy_append'
         | 
| 13 4 |  | 
| 14 5 | 
             
            module PropCheck
         | 
| 15 6 | 
             
              ##
         | 
| 16 7 | 
             
              # A Rose tree with the root being eager,
         | 
| 17 8 | 
             
              # and the children computed lazily, on demand.
         | 
| 18 9 | 
             
              class LazyTree
         | 
| 19 | 
            -
                using LazyAppend
         | 
| 10 | 
            +
                using PropCheck::Helper::LazyAppend
         | 
| 20 11 |  | 
| 21 12 | 
             
                attr_accessor :root, :children
         | 
| 22 13 | 
             
                def initialize(root, children = [].lazy)
         | 
| @@ -82,7 +73,8 @@ module PropCheck | |
| 82 73 | 
             
                    [tree.root].lazy_append(new_children)
         | 
| 83 74 | 
             
                  end
         | 
| 84 75 |  | 
| 85 | 
            -
                  squish | 
| 76 | 
            +
                  squish
         | 
| 77 | 
            +
                    .call(self, [])
         | 
| 86 78 |  | 
| 87 79 | 
             
                  # base = [root]
         | 
| 88 80 | 
             
                  # recursive = children.map(&:each)
         | 
| @@ -108,7 +100,8 @@ module PropCheck | |
| 108 100 | 
             
                #   >> LazyTree.new(1, [LazyTree.new(2, [LazyTree.new(3)]), LazyTree.new(4)]).to_a
         | 
| 109 101 | 
             
                #   => [1, 4, 2, 3]
         | 
| 110 102 | 
             
                def to_a
         | 
| 111 | 
            -
                  each | 
| 103 | 
            +
                  each
         | 
| 104 | 
            +
                    .force
         | 
| 112 105 | 
             
                end
         | 
| 113 106 |  | 
| 114 107 | 
             
                # TODO: fix implementation
         | 
    
        data/lib/prop_check/property.rb
    CHANGED
    
    | @@ -1,41 +1,90 @@ | |
| 1 1 | 
             
            require 'stringio'
         | 
| 2 | 
            +
            require "awesome_print"
         | 
| 2 3 |  | 
| 3 4 | 
             
            require 'prop_check/property/configuration'
         | 
| 4 | 
            -
            require 'prop_check/property/check_evaluator'
         | 
| 5 5 | 
             
            module PropCheck
         | 
| 6 | 
            +
              ##
         | 
| 7 | 
            +
              # Run properties
         | 
| 6 8 | 
             
              class Property
         | 
| 7 9 |  | 
| 8 | 
            -
                 | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 10 | 
            +
                ##
         | 
| 11 | 
            +
                # Main entry-point to create (and possibly immediately run) a property-test.
         | 
| 12 | 
            +
                #
         | 
| 13 | 
            +
                # This method accepts a list of generators and a block.
         | 
| 14 | 
            +
                # The block will then be executed many times, passing the values generated by the generators
         | 
| 15 | 
            +
                # as respective arguments:
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # ```
         | 
| 18 | 
            +
                # include PropCheck::Generators
         | 
| 19 | 
            +
                # PropCheck.forall(integer(), float()) { |x, y| ... }
         | 
| 20 | 
            +
                # ```
         | 
| 21 | 
            +
                #
         | 
| 22 | 
            +
                # It is also possible (and recommended when having more than a few generators) to use a keyword-list
         | 
| 23 | 
            +
                # of generators instead:
         | 
| 24 | 
            +
                #
         | 
| 25 | 
            +
                # ```
         | 
| 26 | 
            +
                # include PropCheck::Generators
         | 
| 27 | 
            +
                # PropCheck.forall(x: integer(), y: float()) { |x:, y:| ... }
         | 
| 28 | 
            +
                # ```
         | 
| 29 | 
            +
                #
         | 
| 30 | 
            +
                #
         | 
| 31 | 
            +
                # If you do not pass a block right away,
         | 
| 32 | 
            +
                # a Property object is returned, which you can call the other instance methods
         | 
| 33 | 
            +
                # of this class on before finally passing a block to it using `#check`.
         | 
| 34 | 
            +
                # (so `forall(Generators.integer) do |val| ... end` and forall(Generators.integer).check do |val| ... end` are the same)
         | 
| 35 | 
            +
                def self.forall(*bindings, &block)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  property = new(*bindings)
         | 
| 11 38 |  | 
| 12 39 | 
             
                  return property.check(&block) if block_given?
         | 
| 13 40 |  | 
| 14 41 | 
             
                  property
         | 
| 15 42 | 
             
                end
         | 
| 16 43 |  | 
| 44 | 
            +
                ##
         | 
| 45 | 
            +
                # Returns the default configuration of the library as it is configured right now
         | 
| 46 | 
            +
                # for introspection.
         | 
| 47 | 
            +
                #
         | 
| 48 | 
            +
                # For the configuration of a single property, check its `configuration` instance method.
         | 
| 49 | 
            +
                # See PropCheck::Property::Configuration for more info on available settings.
         | 
| 17 50 | 
             
                def self.configuration
         | 
| 18 51 | 
             
                  @configuration ||= Configuration.new
         | 
| 19 52 | 
             
                end
         | 
| 20 53 |  | 
| 54 | 
            +
                ##
         | 
| 55 | 
            +
                # Yields the library's configuration object for you to alter.
         | 
| 56 | 
            +
                # See PropCheck::Property::Configuration for more info on available settings.
         | 
| 21 57 | 
             
                def self.configure
         | 
| 22 58 | 
             
                  yield(configuration)
         | 
| 23 59 | 
             
                end
         | 
| 24 60 |  | 
| 25 61 | 
             
                attr_reader :bindings, :condition
         | 
| 26 62 |  | 
| 27 | 
            -
                def initialize(** | 
| 28 | 
            -
                  raise ArgumentError, 'No bindings specified!' if bindings.empty?
         | 
| 63 | 
            +
                def initialize(*bindings, **kwbindings)
         | 
| 64 | 
            +
                  raise ArgumentError, 'No bindings specified!' if bindings.empty? && kwbindings.empty?
         | 
| 29 65 |  | 
| 30 66 | 
             
                  @bindings = bindings
         | 
| 31 | 
            -
                  @ | 
| 67 | 
            +
                  @kwbindings = kwbindings
         | 
| 68 | 
            +
                  @condition = proc { true }
         | 
| 32 69 | 
             
                  @config = self.class.configuration
         | 
| 33 70 | 
             
                end
         | 
| 34 71 |  | 
| 72 | 
            +
                ##
         | 
| 73 | 
            +
                # Returns the configuration of this property
         | 
| 74 | 
            +
                # for introspection.
         | 
| 75 | 
            +
                #
         | 
| 76 | 
            +
                # See PropCheck::Property::Configuration for more info on available settings.
         | 
| 35 77 | 
             
                def configuration
         | 
| 36 78 | 
             
                  @config
         | 
| 37 79 | 
             
                end
         | 
| 38 80 |  | 
| 81 | 
            +
                ##
         | 
| 82 | 
            +
                # Allows you to override the configuration of this property
         | 
| 83 | 
            +
                # by giving a hash with new settings.
         | 
| 84 | 
            +
                #
         | 
| 85 | 
            +
                # If no other changes need to occur before you want to check the property,
         | 
| 86 | 
            +
                # you can immediately pass a block to this method.
         | 
| 87 | 
            +
                # (so `forall(a: Generators.integer).with_config(verbose: true) do ... end` is the same as `forall(a: Generators.integer).with_config(verbose: true).check do ... end`)
         | 
| 39 88 | 
             
                def with_config(**config, &block)
         | 
| 40 89 | 
             
                  @config = @config.merge(config)
         | 
| 41 90 |  | 
| @@ -44,15 +93,35 @@ module PropCheck | |
| 44 93 | 
             
                  self
         | 
| 45 94 | 
             
                end
         | 
| 46 95 |  | 
| 47 | 
            -
                 | 
| 96 | 
            +
                ##
         | 
| 97 | 
            +
                # filters the generator using the  given `condition`.
         | 
| 98 | 
            +
                # The final property checking block will only be run if the condition is truthy.
         | 
| 99 | 
            +
                #
         | 
| 100 | 
            +
                # If wanted, multiple `where`-conditions can be specified on a property.
         | 
| 101 | 
            +
                # Be aware that if you filter away too much generated inputs,
         | 
| 102 | 
            +
                # you might encounter a GeneratorExhaustedError.
         | 
| 103 | 
            +
                # Only filter if you have few inputs to reject. Otherwise, improve your generators.
         | 
| 104 | 
            +
                def where(&condition)
         | 
| 48 105 | 
             
                  original_condition = @condition.dup
         | 
| 49 | 
            -
                  @condition =  | 
| 106 | 
            +
                  @condition = proc do |**kwargs|
         | 
| 107 | 
            +
                    original_condition.call(**kwargs) && condition.call(**kwargs)
         | 
| 108 | 
            +
                  end
         | 
| 50 109 |  | 
| 51 110 | 
             
                  self
         | 
| 52 111 | 
             
                end
         | 
| 53 112 |  | 
| 113 | 
            +
                ##
         | 
| 114 | 
            +
                # Checks the property (after settings have been altered using the other instance methods in this class.)
         | 
| 54 115 | 
             
                def check(&block)
         | 
| 55 | 
            -
                   | 
| 116 | 
            +
                  gens =
         | 
| 117 | 
            +
                    if @kwbindings != {}
         | 
| 118 | 
            +
                      kwbinding_generator = PropCheck::Generators.fixed_hash(**@kwbindings)
         | 
| 119 | 
            +
                      @bindings + [kwbinding_generator]
         | 
| 120 | 
            +
                    else
         | 
| 121 | 
            +
                      @bindings
         | 
| 122 | 
            +
                    end
         | 
| 123 | 
            +
                  binding_generator = PropCheck::Generators.tuple(*gens)
         | 
| 124 | 
            +
                  # binding_generator = PropCheck::Generators.fixed_hash(**@kwbindings)
         | 
| 56 125 |  | 
| 57 126 | 
             
                  n_runs = 0
         | 
| 58 127 | 
             
                  n_successful = 0
         | 
| @@ -70,7 +139,11 @@ module PropCheck | |
| 70 139 | 
             
                private def ensure_not_exhausted!(n_runs)
         | 
| 71 140 | 
             
                  return if n_runs >= @config.n_runs
         | 
| 72 141 |  | 
| 73 | 
            -
                   | 
| 142 | 
            +
                  raise_generator_exhausted!
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                private def raise_generator_exhausted!()
         | 
| 146 | 
            +
                  raise Errors::GeneratorExhaustedError, """
         | 
| 74 147 | 
             
                    Could not perform `n_runs = #{@config.n_runs}` runs,
         | 
| 75 148 | 
             
                    (exhausted #{@config.max_generate_attempts} tries)
         | 
| 76 149 | 
             
                    because too few generator results were adhering to
         | 
| @@ -81,7 +154,7 @@ module PropCheck | |
| 81 154 | 
             
                end
         | 
| 82 155 |  | 
| 83 156 | 
             
                private def check_attempt(generator_result, n_successful, &block)
         | 
| 84 | 
            -
                   | 
| 157 | 
            +
                  block.call(*generator_result.root)
         | 
| 85 158 |  | 
| 86 159 | 
             
                # immediately stop (without shrinnking) for when the app is asked
         | 
| 87 160 | 
             
                # to close by outside intervention
         | 
| @@ -117,8 +190,8 @@ module PropCheck | |
| 117 190 | 
             
                  (0...@config.max_generate_attempts)
         | 
| 118 191 | 
             
                    .lazy
         | 
| 119 192 | 
             
                    .map { binding_generator.generate(size, rng) }
         | 
| 120 | 
            -
                    .reject { |val| val.root == :"_PropCheck.filter_me" }
         | 
| 121 | 
            -
                    .select { |val|  | 
| 193 | 
            +
                    .reject { |val| val.root.any? { |elem| elem == :"_PropCheck.filter_me" }}
         | 
| 194 | 
            +
                    .select { |val| @condition.call(*val.root) }
         | 
| 122 195 | 
             
                    .map do |result|
         | 
| 123 196 | 
             
                      n_runs += 1
         | 
| 124 197 | 
             
                      size += 1
         | 
| @@ -131,7 +204,7 @@ module PropCheck | |
| 131 204 | 
             
                private def show_problem_output(problem, generator_results, n_successful, &block)
         | 
| 132 205 | 
             
                  output = @config.verbose ? STDOUT : StringIO.new
         | 
| 133 206 | 
             
                  output = pre_output(output, n_successful, generator_results.root, problem)
         | 
| 134 | 
            -
                  shrunken_result, shrunken_exception, n_shrink_steps =  | 
| 207 | 
            +
                  shrunken_result, shrunken_exception, n_shrink_steps = shrink(generator_results, output, &block)
         | 
| 135 208 | 
             
                  output = post_output(output, n_shrink_steps, shrunken_result, shrunken_exception)
         | 
| 136 209 |  | 
| 137 210 | 
             
                  [output, shrunken_result, shrunken_exception, n_shrink_steps]
         | 
| @@ -151,24 +224,29 @@ module PropCheck | |
| 151 224 | 
             
                end
         | 
| 152 225 |  | 
| 153 226 | 
             
                private def post_output(output, n_shrink_steps, shrunken_result, shrunken_exception)
         | 
| 154 | 
            -
                   | 
| 155 | 
            -
             | 
| 156 | 
            -
                   | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 161 | 
            -
             | 
| 227 | 
            +
                  if n_shrink_steps == 0
         | 
| 228 | 
            +
                    output.puts '(shrinking impossible)'
         | 
| 229 | 
            +
                  else
         | 
| 230 | 
            +
                    output.puts ''
         | 
| 231 | 
            +
                    output.puts "Shrunken input (after #{n_shrink_steps} shrink steps):"
         | 
| 232 | 
            +
                    output.puts "`#{print_roots(shrunken_result)}`"
         | 
| 233 | 
            +
                    output.puts ""
         | 
| 234 | 
            +
                    output.puts "Shrunken exception:\n---\n#{shrunken_exception}"
         | 
| 235 | 
            +
                    output.puts "---"
         | 
| 236 | 
            +
                    output.puts ""
         | 
| 237 | 
            +
                  end
         | 
| 162 238 | 
             
                  output
         | 
| 163 239 | 
             
                end
         | 
| 164 240 |  | 
| 165 | 
            -
                private def print_roots( | 
| 166 | 
            -
                   | 
| 167 | 
            -
                     | 
| 168 | 
            -
                   | 
| 241 | 
            +
                private def print_roots(lazy_tree_val)
         | 
| 242 | 
            +
                  if lazy_tree_val.is_a?(Array) && lazy_tree_val.length == 1 && lazy_tree_val[0].is_a?(Hash)
         | 
| 243 | 
            +
                    lazy_tree_val[0].ai
         | 
| 244 | 
            +
                  else
         | 
| 245 | 
            +
                    lazy_tree_val.ai
         | 
| 246 | 
            +
                  end
         | 
| 169 247 | 
             
                end
         | 
| 170 248 |  | 
| 171 | 
            -
                private def  | 
| 249 | 
            +
                private def shrink(bindings_tree, io, &block)
         | 
| 172 250 | 
             
                  io.puts 'Shrinking...' if @config.verbose
         | 
| 173 251 | 
             
                  problem_child = bindings_tree
         | 
| 174 252 | 
             
                  siblings = problem_child.children.lazy
         | 
| @@ -190,12 +268,12 @@ module PropCheck | |
| 190 268 | 
             
                    io.print '.' if @config.verbose
         | 
| 191 269 |  | 
| 192 270 | 
             
                    begin
         | 
| 193 | 
            -
                       | 
| 194 | 
            -
                    rescue Exception =>  | 
| 271 | 
            +
                      block.call(*sibling.root)
         | 
| 272 | 
            +
                    rescue Exception => e
         | 
| 195 273 | 
             
                      problem_child = sibling
         | 
| 196 274 | 
             
                      parent_siblings = siblings
         | 
| 197 275 | 
             
                      siblings = problem_child.children.lazy
         | 
| 198 | 
            -
                      problem_exception =  | 
| 276 | 
            +
                      problem_exception = e
         | 
| 199 277 | 
             
                    end
         | 
| 200 278 | 
             
                  end
         | 
| 201 279 |  | 
    
        data/lib/prop_check/version.rb
    CHANGED
    
    
    
        data/prop_check.gemspec
    CHANGED
    
    | @@ -37,6 +37,6 @@ Gem::Specification.new do |spec| | |
| 37 37 | 
             
              spec.required_ruby_version = '>= 2.5.1'
         | 
| 38 38 |  | 
| 39 39 | 
             
              spec.add_development_dependency "bundler", "~> 2.0"
         | 
| 40 | 
            -
              spec.add_development_dependency "rake", "~>  | 
| 40 | 
            +
              spec.add_development_dependency "rake", "~> 12.3"
         | 
| 41 41 | 
             
              spec.add_development_dependency "rspec", "~> 3.0"
         | 
| 42 42 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: prop_check
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.9.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Qqwy/Wiebe-Marten Wijnja
         | 
| 8 | 
            -
            autorequire: | 
| 8 | 
            +
            autorequire:
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2020-07-21 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bundler
         | 
| @@ -30,14 +30,14 @@ dependencies: | |
| 30 30 | 
             
                requirements:
         | 
| 31 31 | 
             
                - - "~>"
         | 
| 32 32 | 
             
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            -
                    version: ' | 
| 33 | 
            +
                    version: '12.3'
         | 
| 34 34 | 
             
              type: :development
         | 
| 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: '12.3'
         | 
| 41 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 42 42 | 
             
              name: rspec
         | 
| 43 43 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -81,11 +81,10 @@ files: | |
| 81 81 | 
             
            - lib/prop_check/generator.rb
         | 
| 82 82 | 
             
            - lib/prop_check/generators.rb
         | 
| 83 83 | 
             
            - lib/prop_check/helper.rb
         | 
| 84 | 
            +
            - lib/prop_check/helper/lazy_append.rb
         | 
| 84 85 | 
             
            - lib/prop_check/lazy_tree.rb
         | 
| 85 86 | 
             
            - lib/prop_check/property.rb
         | 
| 86 | 
            -
            - lib/prop_check/property/check_evaluator.rb
         | 
| 87 87 | 
             
            - lib/prop_check/property/configuration.rb
         | 
| 88 | 
            -
            - lib/prop_check/rspec.rb
         | 
| 89 88 | 
             
            - lib/prop_check/version.rb
         | 
| 90 89 | 
             
            - prop_check.gemspec
         | 
| 91 90 | 
             
            homepage: https://github.com/Qqwy/ruby-prop_check/
         | 
| @@ -95,7 +94,7 @@ metadata: | |
| 95 94 | 
             
              homepage_uri: https://github.com/Qqwy/ruby-prop_check/
         | 
| 96 95 | 
             
              source_code_uri: https://github.com/Qqwy/ruby-prop_check/
         | 
| 97 96 | 
             
              changelog_uri: https://github.com/Qqwy/ruby-prop_check/CHANGELOG.md
         | 
| 98 | 
            -
            post_install_message: | 
| 97 | 
            +
            post_install_message:
         | 
| 99 98 | 
             
            rdoc_options: []
         | 
| 100 99 | 
             
            require_paths:
         | 
| 101 100 | 
             
            - lib
         | 
| @@ -110,9 +109,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 110 109 | 
             
                - !ruby/object:Gem::Version
         | 
| 111 110 | 
             
                  version: '0'
         | 
| 112 111 | 
             
            requirements: []
         | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
            signing_key: 
         | 
| 112 | 
            +
            rubygems_version: 3.0.3
         | 
| 113 | 
            +
            signing_key:
         | 
| 116 114 | 
             
            specification_version: 4
         | 
| 117 115 | 
             
            summary: PropCheck allows you to do property-based testing, including shrinking.
         | 
| 118 116 | 
             
            test_files: []
         | 
| @@ -1,45 +0,0 @@ | |
| 1 | 
            -
            module PropCheck
         | 
| 2 | 
            -
              class Property
         | 
| 3 | 
            -
                ##
         | 
| 4 | 
            -
                # A wrapper class that implements the 'Cloaker' concept
         | 
| 5 | 
            -
                # which allows us to refer to variables set in 'bindings',
         | 
| 6 | 
            -
                # while still being able to access things that are only in scope
         | 
| 7 | 
            -
                # in the creator of '&block'.
         | 
| 8 | 
            -
                #
         | 
| 9 | 
            -
                # This allows us to bind the variables specified in `bindings`
         | 
| 10 | 
            -
                # one way during checking and another way during shrinking.
         | 
| 11 | 
            -
                class CheckEvaluator
         | 
| 12 | 
            -
                  include RSpec::Matchers if Object.const_defined?('RSpec')
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                  def initialize(bindings, &block)
         | 
| 15 | 
            -
                    @caller = block.binding.receiver
         | 
| 16 | 
            -
                    @block = block
         | 
| 17 | 
            -
                    define_named_instance_methods(bindings)
         | 
| 18 | 
            -
                  end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                  def call
         | 
| 21 | 
            -
                    self.instance_exec(&@block)
         | 
| 22 | 
            -
                  end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                  private def define_named_instance_methods(results)
         | 
| 25 | 
            -
                    results.each do |name, result|
         | 
| 26 | 
            -
                      define_singleton_method(name) { result }
         | 
| 27 | 
            -
                    end
         | 
| 28 | 
            -
                  end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                  ##
         | 
| 31 | 
            -
                  # Dispatches to caller whenever something is not part of `bindings`.
         | 
| 32 | 
            -
                  # (No need to invoke this method manually)
         | 
| 33 | 
            -
                  def method_missing(method, *args, &block)
         | 
| 34 | 
            -
                    @caller.__send__(method, *args, &block) || super
         | 
| 35 | 
            -
                  end
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                  ##
         | 
| 38 | 
            -
                  # Checks respond_to of caller whenever something is not part of `bindings`.
         | 
| 39 | 
            -
                  # (No need to invoke this method manually)
         | 
| 40 | 
            -
                  def respond_to_missing?(*args)
         | 
| 41 | 
            -
                    @caller.respond_to?(*args) || super
         | 
| 42 | 
            -
                  end
         | 
| 43 | 
            -
                end
         | 
| 44 | 
            -
              end
         | 
| 45 | 
            -
            end
         | 
    
        data/lib/prop_check/rspec.rb
    DELETED
    
    | @@ -1,14 +0,0 @@ | |
| 1 | 
            -
            module PropCheck
         | 
| 2 | 
            -
              ##
         | 
| 3 | 
            -
              # Integration with RSpec
         | 
| 4 | 
            -
              module RSpec
         | 
| 5 | 
            -
                # To make it available within examples
         | 
| 6 | 
            -
                def self.extend_object(obj)
         | 
| 7 | 
            -
                  obj.define_method(:forall) do |*args, **kwargs, &block|
         | 
| 8 | 
            -
                    PropCheck::Property.forall(*args, **kwargs) do
         | 
| 9 | 
            -
                      instance_exec(self, &block)
         | 
| 10 | 
            -
                    end
         | 
| 11 | 
            -
                  end
         | 
| 12 | 
            -
                end
         | 
| 13 | 
            -
              end
         | 
| 14 | 
            -
            end
         |