properb 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/properb.rb ADDED
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require_relative "properb/generator"
5
+ require_relative "properb/generators"
6
+ require_relative "properb/rspec"
7
+ require_relative "properb/shrink_tree"
8
+
9
+ module Properb
10
+ # This definition is taken from RSpec, in an attempt to remain
11
+ # consistent with them.
12
+ module AllExceptionsExceptOnesWeMustNotRescue
13
+ # These exceptions are dangerous to rescue as rescuing them
14
+ # would interfere with things we should not interfere with.
15
+ AVOID_RESCUING = [NoMemoryError, SignalException, Interrupt, SystemExit].freeze
16
+
17
+ def self.===(exception)
18
+ exception.is_a?(Exception) && !AVOID_RESCUING.any? { exception.is_a?(_1) }
19
+ end
20
+ end
21
+
22
+ def self.rspec_install(config)
23
+ config.extend Properb::RSpec::ExampleGroupMethods
24
+ config.include Properb::RSpec::ExampleMethods
25
+ config.extend Properb::Generators
26
+ end
27
+
28
+ class TestRunner
29
+ def initialize(block)
30
+ @block = block
31
+ end
32
+
33
+ def run(values)
34
+ @block.call(values)
35
+ true
36
+ rescue Properb::RSpec::ExampleMethods::AssumptionViolated
37
+ # These don't count as failures, but they also need to be run
38
+ # again
39
+ false
40
+ rescue AllExceptionsExceptOnesWeMustNotRescue => e
41
+ string = values.map { |k, v| "#{k} = #{v.inspect}" }.join("\n ")
42
+ # Don't leave properb lines in the backtrace - they're not very useful.
43
+ raise e, <<~MESSAGE, e.backtrace.reject { %r{spec/properb\.rb}.match(_1) }
44
+ #{e.message}
45
+
46
+ with input: #{string}
47
+ MESSAGE
48
+ end
49
+ end
50
+
51
+ def self.for_all(generators, size: 0..10, num_tests: 100, assumption_limit: 10, &block)
52
+ test_runner = TestRunner.new(block)
53
+
54
+ random = Random.new
55
+ generated = nil
56
+ passed = Set.new
57
+ generator = Generators.hashmap(generators)
58
+ current_size = if size.is_a?(Range)
59
+ size.min
60
+ else
61
+ size
62
+ end
63
+ num_tests.times do
64
+ result = false
65
+ attempt_number = 0
66
+ until result
67
+ if attempt_number >= assumption_limit
68
+ raise StandardError,
69
+ "Could not satisfy assumptions after #{assumption_limit} attempts"
70
+ end
71
+
72
+ current_size += 1 if size.is_a?(Range) && size.include?(current_size + 1)
73
+ generated = generator.generate_value(random, current_size)
74
+ result = test_runner.run(generated.value)
75
+ attempt_number += 1
76
+ end
77
+ passed << generated.value
78
+ end
79
+ rescue AllExceptionsExceptOnesWeMustNotRescue => e
80
+ exception, = shrink(generated, e, passed, test_runner)
81
+ raise exception
82
+ end
83
+
84
+ def self.shrink(generated, exception, passed, test_runner)
85
+ smallest = generated
86
+ while smallest
87
+ begin
88
+ current = nil
89
+ smallest.children.each do |child|
90
+ current = child
91
+ next if passed.include?(current.value)
92
+
93
+ if test_runner.run(current.value)
94
+ passed << current.value
95
+ else
96
+ # If the assumptions have failed then we don't learn anything
97
+ # useful. We process this node's children before continuing,
98
+ # in the hopes that we get some information from them instead.
99
+ e, current = shrink(current, exception, passed, test_runner)
100
+ raise e unless e == exception
101
+ end
102
+ end
103
+ break
104
+ rescue AllExceptionsExceptOnesWeMustNotRescue => e
105
+ smallest = current
106
+ exception = e
107
+ end
108
+ end
109
+ [exception, smallest]
110
+ end
111
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: properb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Carlo Zancanaro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-11-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3'
27
+ description: |
28
+ A property-based testing library, in the spirit of Proper in
29
+ Erlang, with tight integration with RSpec.
30
+ email:
31
+ - carlo@zancanaro.id.au
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - COPYING
37
+ - README.md
38
+ - lib/properb.rb
39
+ - lib/properb/generator.rb
40
+ - lib/properb/generators.rb
41
+ - lib/properb/generators/array_generator.rb
42
+ - lib/properb/generators/choice_generator.rb
43
+ - lib/properb/generators/constant_generator.rb
44
+ - lib/properb/generators/deferred_generator.rb
45
+ - lib/properb/generators/frequencies_generator.rb
46
+ - lib/properb/generators/integer_generator.rb
47
+ - lib/properb/generators/mapped_generator.rb
48
+ - lib/properb/generators/recursive_generator.rb
49
+ - lib/properb/generators/selected_generator.rb
50
+ - lib/properb/generators/sized_generator.rb
51
+ - lib/properb/generators/tuple_generator.rb
52
+ - lib/properb/rspec.rb
53
+ - lib/properb/rspec/example_group_methods.rb
54
+ - lib/properb/rspec/example_methods.rb
55
+ - lib/properb/shrink_tree.rb
56
+ homepage: https://sr.ht/~czan/properb/
57
+ licenses:
58
+ - GPL-3.0-only
59
+ metadata:
60
+ allowed_push_host: https://rubygems.org
61
+ homepage_uri: https://sr.ht/~czan/properb/
62
+ source_code_uri: https://sr.ht/~czan/properb/
63
+ rubygems_mfa_required: 'true'
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '3.0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubygems_version: 3.3.26
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: Property-based testing in Ruby, with RSpec integration
83
+ test_files: []