lib/prop_check/property.rb in prop_check-0.6.1 vs lib/prop_check/property.rb in prop_check-0.6.2

- old
+ new

@@ -1,25 +1,45 @@ require 'stringio' require 'prop_check/property/configuration' require 'prop_check/property/check_evaluator' module PropCheck + ## + # Run properties class Property + ## + # Call this with a keyword argument list of (symbol => generators) and a block. + # The block will then be executed many times, with the respective symbol names + # being defined as having a single generated value. + # + # If you do not pass a block right away, + # a Property object is returned, which you can call the other instance methods + # of this class on before finally passing a block to it using `#check`. + # (so `forall(a: Generators.integer) do ... end` and forall(a: Generators.integer).check do ... end` are the same) def self.forall(**bindings, &block) property = new(bindings) return property.check(&block) if block_given? property end + ## + # Returns the default configuration of the library as it is configured right now + # for introspection. + # + # For the configuration of a single property, check its `configuration` instance method. + # See PropCheck::Property::Configuration for more info on available settings. def self.configuration @configuration ||= Configuration.new end + ## + # Yields the library's configuration object for you to alter. + # See PropCheck::Property::Configuration for more info on available settings. def self.configure yield(configuration) end attr_reader :bindings, :condition @@ -30,29 +50,51 @@ @bindings = bindings @condition = -> { true } @config = self.class.configuration end + ## + # Returns the configuration of this property + # for introspection. + # + # See PropCheck::Property::Configuration for more info on available settings. def configuration @config end + ## + # Allows you to override the configuration of this property + # by giving a hash with new settings. + # + # If no other changes need to occur before you want to check the property, + # you can immediately pass a block to this method. + # (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`) def with_config(**config, &block) @config = @config.merge(config) return self.check(&block) if block_given? self end - def where(&new_condition) + ## + # filters the generator using the given `condition`. + # The final property checking block will only be run if the condition is truthy. + # + # If wanted, multiple `where`-conditions can be specified on a property. + # Be aware that if you filter away too much generated inputs, + # you might encounter a GeneratorExhaustedError. + # Only filter if you have few inputs to reject. Otherwise, improve your generators. + def where(&condition) original_condition = @condition.dup - @condition = -> { instance_exec(&original_condition) && instance_exec(&new_condition) } + @condition = -> { instance_exec(&original_condition) && instance_exec(&condition) } self end + ## + # Checks the property (after settings have been altered using the other instance methods in this class.) def check(&block) binding_generator = PropCheck::Generators.fixed_hash(bindings) n_runs = 0 n_successful = 0 @@ -68,11 +110,11 @@ end private def ensure_not_exhausted!(n_runs) return if n_runs >= @config.n_runs - raise GeneratorExhaustedError, """ + raise Errors::GeneratorExhaustedError, """ Could not perform `n_runs = #{@config.n_runs}` runs, (exhausted #{@config.max_generate_attempts} tries) because too few generator results were adhering to the `where` condition. @@ -129,11 +171,11 @@ end private def show_problem_output(problem, generator_results, n_successful, &block) output = @config.verbose ? STDOUT : StringIO.new output = pre_output(output, n_successful, generator_results.root, problem) - shrunken_result, shrunken_exception, n_shrink_steps = shrink2(generator_results, output, &block) + shrunken_result, shrunken_exception, n_shrink_steps = shrink(generator_results, output, &block) output = post_output(output, n_shrink_steps, shrunken_result, shrunken_exception) [output, shrunken_result, shrunken_exception, n_shrink_steps] end @@ -166,11 +208,11 @@ lazy_tree_hash.map do |name, val| "#{name} = #{val.inspect}" end.join(", ") end - private def shrink2(bindings_tree, io, &fun) + private def shrink(bindings_tree, io, &fun) io.puts 'Shrinking...' if @config.verbose problem_child = bindings_tree siblings = problem_child.children.lazy parent_siblings = nil problem_exception = nil @@ -189,14 +231,14 @@ shrink_steps += 1 io.print '.' if @config.verbose begin CheckEvaluator.new(sibling.root, &fun).call - rescue Exception => problem + rescue Exception => e problem_child = sibling parent_siblings = siblings siblings = problem_child.children.lazy - problem_exception = problem + problem_exception = e end end io.puts "(Note: Exceeded #{@config.max_shrink_steps} shrinking steps, the maximum.)" if shrink_steps >= @config.max_shrink_steps