rantly 0.3.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8db5bd7182d7f9e17c430588587617eaa712dd6d
4
- data.tar.gz: 5812264178d1640909ba3ea004b777ecea8e78e6
3
+ metadata.gz: 9f2c8ec7d03b419778a6671029bf86bf946d4e06
4
+ data.tar.gz: 3363b988ec1f4a7ee3a03f22f99f41b3783c20eb
5
5
  SHA512:
6
- metadata.gz: 36ffb950c9e4f23e5a7e02183bf7a4e7de2c35eac5431ffb4adb96266289097133c44ee7de1dd56db18a6553f45e36b580de1f4c91b9f1ad413adfae6b9a3744
7
- data.tar.gz: b04342e87f28f5ef185ee6798bb8e7a119444c27d4b1fb0146cccd3e43ca13654fba3925ecc7914501b3336ead47055fa147d74f83874fb26b236a2522474f14
6
+ metadata.gz: aa41245990c513af23ca3830db8403d4fb7e3108ef2216aa562399e6c590baafc11d3be1970b0c9fdc8c785366535c67901bd4cd6e61a39b135e887a3da60ae5
7
+ data.tar.gz: 593f20b00e41d467c213372b031578921fdbd4bb6bd5295ae85ffb389c1c62c9b8a7e981034fb9b52b1c356dc552acbb38aaa6af3d5c9d629eaf487617d5ecbf
@@ -0,0 +1,30 @@
1
+ # Change Log
2
+ All notable changes to rantly will be documented in this file. The curated log begins at changes to version 0.4.0.
3
+
4
+ This project adheres to [Semantic Versioning](http://semver.org/).
5
+
6
+ ## [1.0.0][1.0.0] - 2016-07-06
7
+ ### Added
8
+ - Trying harder to shrink collections instead of giving up on first success of property (thanks [Eric Bischoff][Eric Bischoff]).
9
+ - Added convenience classes Deflating and Tuple for more control on shrinking (thanks [Eric Bischoff][Eric Bischoff]).
10
+ - Added usage examples for Deflating and Tuple shrinking strategies (thanks [Oleksii Fedorov][Oleksii Fedorov]).
11
+ - `Property#check` will now use the `RANTLY_COUNT` environment variable to control the number of values generated (thanks [Jamie English][Jamie English]).
12
+
13
+ ### Major changes
14
+ - Array shrink was removed in favor of Tuple and Deflating.
15
+
16
+ ## [0.3.2][0.3.2] - 2015-09-16
17
+ ### Added
18
+ - Ability to shrink an object (`Integer`, `String`, `Array`, `Hash`). This is useful in finding the minimum value that fails a property check condition.
19
+
20
+ ### Changed
21
+ - Improved RSpec and Minitest test extensions.
22
+ - Improved readability and execution of test suite ([Issue #4][4]).
23
+ - Updates to documentation.
24
+
25
+ [1.0.0]: https://github.com/abargnesi/rantly/compare/0.3.2...1.0.0
26
+ [0.3.2]: https://github.com/abargnesi/rantly/compare/0.3.1...0.3.2
27
+ [4]: https://github.com/abargnesi/rantly/issues/13
28
+ [Eric Bischoff]: https://github.com/Bischoff
29
+ [Jamie English]: https://github.com/english
30
+ [Oleksii Fedorov]: https://github.com/waterlink
@@ -1,26 +1,12 @@
1
- !https://travis-ci.org/hayeah/rantly.png?branch=master!:https://travis-ci.org/hayeah/rantly
1
+ !https://badge.fury.io/rb/rantly.svg!:https://badge.fury.io/rb/rantly
2
2
 
3
- h1. Repository move (Beginning 2015-07-07)
4
-
5
- The official _rantly_ repository has moved from "hayeah/rantly":https://github.com/hayeah/rantly to "abargnesi/rantly":https://github.com/abargnesi/rantly. The creator of _rantly_ is "Howard Yeh":https://github.com/hayeah. "Anthony Bargnesi":https://github.com/abargnesi is the current maintainer.
6
-
7
- New development will occur in "abargnesi/rantly":https://github.com/abargnesi/rantly.
8
-
9
- The transition plan is as follows:
10
-
11
- * [x] Investigate and move open issues from "hayeah/rantly":https://github.com/hayeah/rantly to "abargnesi/rantly":https://github.com/abargnesi/rantly.
12
- * [x] Evaluate and likely merge pull requests into hayeah/rantly. Push these changes to "abargnesi/rantly":https://github.com/abargnesi/rantly.
13
- * [x] Move over any closed issues that have relevant discourse.
14
- * [ ] Fix open issues.
15
- * [x] Push new gem version after testing functionality.
16
- * [ ] Continue new development; Changes will be pushed back upstream to "hayeah/rantly":https://github.com/hayeah/rantly over the course of the next month.
17
- * [x] Added CHANGELOG markdown file. Includes what was added between 0.3.1 (2011-12-15) and 0.3.2 (2015-09-16).
3
+ !https://travis-ci.org/abargnesi/rantly.svg?branch=master!:https://travis-ci.org/abargnesi/rantly
18
4
 
19
5
  h1. Imperative Random Data Generator and Quickcheck
20
6
 
21
- You can use Rant to generate random test data, and use its Test::Unit extension for property-based testing.
7
+ You can use Rantly to generate random test data, and use its Test::Unit extension for property-based testing.
22
8
 
23
- Rant is basically a recursive descent interpreter, each of its method returns a random value of some type (string, integer, float, etc.).
9
+ Rantly is basically a recursive descent interpreter, each of its method returns a random value of some type (string, integer, float, etc.).
24
10
 
25
11
  Its implementation has no alien mathematics inside. Completely side-effect-free-free.
26
12
 
@@ -331,6 +317,26 @@ property_of {
331
317
  }
332
318
  </code></pre>
333
319
 
320
+ To control the number of property tests to generate, you have three options. In order of precedence:
321
+
322
+ # Pass an integer argument to @check@
323
+
324
+ <pre><code>
325
+ property_of {
326
+ integer
327
+ }.check(9000) { |i|
328
+ assert_kind_of Integer, i
329
+ }
330
+ </code></pre>
331
+
332
+ #_ Set the @RANTLY_COUNT@ environment variable
333
+
334
+ <pre><code>
335
+ RANTLY_COUNT=9000 ruby my_property_test.rb
336
+ </code></pre>
337
+
338
+ #_ If neither of the above are set, the default will be to run the @check@ block 100 times.
339
+
334
340
  If you wish to have quiet output from Rantly, set environmental variable:
335
341
  <pre><code>
336
342
  RANTLY_VERBOSE=0 # silent
@@ -345,13 +351,19 @@ Shrinking reduces the value of common types to some terminal lower bound. These
345
351
  For example a <code>String</code> is shrinkable until it is empty (e.g. <code>""</code>),
346
352
 
347
353
  <pre><code>
348
- "foo".shrink # => "fo"
349
- "foo".shrink.shrink # => "f"
350
- "foo".shrink.shrink.shrink # => ""
351
- "".shrinkable? # => false
354
+ "foo".shrinkable? # => true
355
+ "foo".shrink # => "fo"
356
+ "fo".shrink # => "f"
357
+ "f".shrink # => ""
358
+ "".shrinkable? # => false
352
359
  </code></pre>
353
360
 
354
- Shrinking allows <code>Property#check</code> to find the minimum value that still fails the condition.
361
+ Shrinking allows <code>Property#check</code> to find a reduced value that still fails the condition. The value is not truely minimal because:
362
+
363
+ * we do not perform a complete in-depth traversal of the failure tree
364
+ * we limit the search to a maximum 1024 shrinking operations
365
+
366
+ but is usually reduced enough to start debugging.
355
367
 
356
368
  Enable shrinking with
357
369
 
@@ -359,8 +371,29 @@ Enable shrinking with
359
371
  require 'rantly/shrinks'
360
372
  </code></pre>
361
373
 
374
+ Use <code>Tuple</code> class if you want an array whose elements are individually shrinked, but are not removed. Example:
375
+
376
+ <pre><code>
377
+ property_of {
378
+ len = range(0, 10)
379
+ Tuple.new( array(len) { integer } )
380
+ }.check {
381
+ # .. property check here ..
382
+ }
383
+ </code></pre>
384
+
385
+ Use <code>Deflating</code> class if you want an array whose elements are individully shrinked whenever possible, and removed otherwise. Example:
386
+
387
+ <pre><code>
388
+ property_of {
389
+ len = range(0, 10)
390
+ Deflated.new( array(len) { integer } )
391
+ }.check {
392
+ # .. property check here ..
393
+ }
394
+ </code></pre>
362
395
 
363
- That's about it. Enjoy :)
396
+ Normal arrays or hashes are not shrinked.
364
397
 
365
398
 
366
399
  h1. Copyright
data/Rakefile CHANGED
@@ -6,9 +6,9 @@ begin
6
6
  Jeweler::Tasks.new do |gem|
7
7
  gem.name = "rantly"
8
8
  gem.summary = %Q{Ruby Imperative Random Data Generator and Quickcheck}
9
- gem.email = ["hayeah@gmail.com", "abargnesi@gmail.com"]
9
+ gem.email = ["hayeah@gmail.com", "abargnesi@gmail.com", "ebischoff@nerim.net"]
10
10
  gem.homepage = "https://github.com/abargnesi/rantly"
11
- gem.authors = ["Howard Yeh", "Anthony Bargnesi"]
11
+ gem.authors = ["Howard Yeh", "Anthony Bargnesi", "Eric Bischoff"]
12
12
 
13
13
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
14
  end
@@ -1,5 +1,5 @@
1
1
  ---
2
2
  :build:
3
- :major: 0
4
- :minor: 3
5
- :patch: 2
3
+ :major: 1
4
+ :minor: 0
5
+ :patch: 0
@@ -6,6 +6,7 @@ class Rantly::Property
6
6
  attr_reader :failed_data, :shrunk_failed_data, :io
7
7
 
8
8
  VERBOSITY = ENV.fetch('RANTLY_VERBOSE'){ 1 }.to_i
9
+ RANTLY_COUNT = ENV.fetch('RANTLY_COUNT'){ 100 }.to_i
9
10
 
10
11
  def io
11
12
  @io ||= if VERBOSITY >= 1
@@ -23,7 +24,7 @@ class Rantly::Property
23
24
  @property = property
24
25
  end
25
26
 
26
- def check(n=100,limit=10,&assertion)
27
+ def check(n=RANTLY_COUNT,limit=10,&assertion)
27
28
  i = 0
28
29
  test_data = nil
29
30
  begin
@@ -46,30 +47,39 @@ class Rantly::Property
46
47
  pretty_print test_data
47
48
  @failed_data = test_data
48
49
  if @failed_data.respond_to?(:shrink)
49
- @shrunk_failed_data = shrinkify(assertion, @failed_data)
50
- io.puts "minimal failed data is:"
50
+ @shrunk_failed_data, @depth = shrinkify(assertion, @failed_data)
51
+ io.puts "minimal failed data (depth #{@depth}) is:"
51
52
  pretty_print @shrunk_failed_data
52
53
  end
53
54
  raise boom
54
55
  end
55
56
  end
56
57
 
57
- # return the first success case
58
- def shrinkify(assertion, data)
59
- # We assume that data.shrink is non-destructive
60
- return data if !data.shrinkable?
61
- val = data.shrink
62
- begin
63
- assertion.call(val)
64
- io.puts "found a reduced success:"
65
- pretty_print val
66
- return data
67
- rescue Exception
68
- io.puts "found a reduced failure case:"
69
- pretty_print val
70
- # recursively shrink failure case
71
- return shrinkify(assertion,val)
58
+ # Explore the failures tree
59
+ def shrinkify(assertion, data, depth=0, iteration=0)
60
+ io.puts "Shrinking at depth #{depth}:"
61
+ pretty_print data
62
+
63
+ min_data = data
64
+ max_depth = depth
65
+ if data.shrinkable?
66
+ while iteration < 1024 do
67
+ # We assume that data.shrink is non-destructive
68
+ shrunk_data = data.shrink
69
+ begin
70
+ assertion.call(shrunk_data)
71
+ rescue Exception
72
+ # If the assertion was verified, recursively shrink failure case
73
+ branch_data, branch_depth, iteration = shrinkify(assertion, shrunk_data, depth + 1, iteration + 1)
74
+ if branch_depth > max_depth
75
+ min_data = branch_data
76
+ max_depth = branch_depth
77
+ end
78
+ end
79
+ break if !data.retry?
80
+ end
72
81
  end
82
+ return min_data, max_depth, iteration
73
83
  end
74
84
 
75
85
  def report
@@ -1,11 +1,22 @@
1
+ # Integer : shrink to zero
1
2
  class Integer
2
3
  def shrink
3
- if self < 0 then (self / 2).floor + 1
4
- elsif self <= 10 && self > 0 then self - 1
5
- elsif self > 0 then ((self + 1) / 2).ceil - 1
4
+ shrunk = if self > 8
5
+ self / 2
6
+ elsif self > 0
7
+ self - 1
8
+ elsif self < -8
9
+ (self + 1) / 2
10
+ elsif self < 0
11
+ self + 1
6
12
  else
7
- return 0
13
+ 0
8
14
  end
15
+ return shrunk
16
+ end
17
+
18
+ def retry?
19
+ false
9
20
  end
10
21
 
11
22
  def shrinkable?
@@ -13,16 +24,19 @@ class Integer
13
24
  end
14
25
  end
15
26
 
27
+ # String : shrink to ""
16
28
  class String
17
29
  def shrink
30
+ shrunk = self.dup
18
31
  if self.size > 0
19
32
  idx = Random::rand(self.size)
20
- shrunk = self.dup
21
33
  shrunk[idx] = ""
22
- return shrunk
23
- else
24
- return ""
25
34
  end
35
+ return shrunk
36
+ end
37
+
38
+ def retry?
39
+ false
26
40
  end
27
41
 
28
42
  def shrinkable?
@@ -30,26 +44,129 @@ class String
30
44
  end
31
45
  end
32
46
 
33
- class Array
47
+ # Array where elements can be shrunk but not removed
48
+ class Tuple
49
+ def initialize(a)
50
+ @array = a
51
+ @position = a.size - 1
52
+ end
53
+
54
+ def [](i)
55
+ @array[i]
56
+ end
57
+
58
+ def []=(i, value)
59
+ @array[i] = value
60
+ end
61
+
62
+ def length
63
+ @array.length
64
+ end
65
+
66
+ def size
67
+ self.length
68
+ end
69
+
70
+ def to_s
71
+ @array.to_s.insert(1, "T ")
72
+ end
73
+
74
+ def inspect
75
+ self.to_s
76
+ end
77
+
78
+ def each(&block)
79
+ @array.each(&block)
80
+ end
81
+
82
+ def array
83
+ return @array
84
+ end
85
+
34
86
  def shrink
35
- idx = find_index{|e| e.respond_to?(:shrinkable?) && e.shrinkable?}
36
- if idx != nil
37
- clone = self.dup
38
- clone[idx] = clone[idx].shrink
39
- return clone
40
- elsif !self.empty?
41
- i = Random::rand(self.length)
42
- a2 = self.dup
43
- a2.delete_at(i)
44
- return a2
45
- else
46
- return self
87
+ shrunk = @array.dup
88
+ while @position >= 0
89
+ e = @array.at(@position)
90
+ if e.respond_to?(:shrinkable?) && e.shrinkable?
91
+ break
92
+ end
93
+ @position -= 1
94
+ end
95
+ if @position >= 0
96
+ shrunk[@position] = e.shrink
97
+ @position -= 1
98
+ end
99
+ return Tuple.new(shrunk)
100
+ end
101
+
102
+ def retry?
103
+ @position >= 0
104
+ end
105
+
106
+ def shrinkable?
107
+ @array.any? {|e| e.respond_to?(:shrinkable?) && e.shrinkable? }
108
+ end
109
+ end
110
+
111
+ # Array where the elements that can't be shrunk are removed
112
+ class Deflating
113
+ def initialize(a)
114
+ @array = a
115
+ @position = a.size - 1
116
+ end
117
+
118
+ def [](i)
119
+ @array[i]
120
+ end
121
+
122
+ def []=(i, value)
123
+ @array[i] = value
124
+ end
125
+
126
+ def length
127
+ @array.length
128
+ end
129
+
130
+ def size
131
+ self.length
132
+ end
133
+
134
+ def to_s
135
+ @array.to_s.insert(1, "D ")
136
+ end
137
+
138
+ def inspect
139
+ self.to_s
140
+ end
141
+
142
+ def each(&block)
143
+ @array.each(&block)
144
+ end
145
+
146
+ def array
147
+ return @array
148
+ end
149
+
150
+ def shrink
151
+ shrunk = @array.dup
152
+ if @position >= 0
153
+ e = @array.at(@position)
154
+ if e.respond_to?(:shrinkable?) && e.shrinkable?
155
+ shrunk[@position] = e.shrink
156
+ else
157
+ shrunk.delete_at(@position)
158
+ end
159
+ @position -= 1
47
160
  end
161
+ return Deflating.new(shrunk)
162
+ end
163
+
164
+ def retry?
165
+ @position >= 0
48
166
  end
49
167
 
50
168
  def shrinkable?
51
- self.any?{|e| e.respond_to?(:shrinkable?) && e.shrinkable? } ||
52
- !self.empty?
169
+ !@array.empty?
53
170
  end
54
171
  end
55
172
 
@@ -2,20 +2,21 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: rantly 0.3.2 ruby lib
5
+ # stub: rantly 1.0.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "rantly"
9
- s.version = "0.3.2"
9
+ s.version = "1.0.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
- s.authors = ["Howard Yeh", "Anthony Bargnesi"]
14
- s.date = "2015-09-16"
15
- s.email = ["hayeah@gmail.com", "abargnesi@gmail.com"]
13
+ s.authors = ["Howard Yeh", "Anthony Bargnesi", "Eric Bischoff"]
14
+ s.date = "2016-07-06"
15
+ s.email = ["hayeah@gmail.com", "abargnesi@gmail.com", "ebischoff@nerim.net"]
16
16
  s.extra_rdoc_files = [
17
17
  "LICENSE",
18
- "README.textile"
18
+ "README.textile",
19
+ "CHANGELOG.md"
19
20
  ]
20
21
  s.files = [
21
22
  ".document",
@@ -23,6 +24,7 @@ Gem::Specification.new do |s|
23
24
  "Gemfile",
24
25
  "LICENSE",
25
26
  "README.textile",
27
+ "CHANGELOG.md",
26
28
  "Rakefile",
27
29
  "VERSION.yml",
28
30
  "lib/rantly.rb",
@@ -47,24 +47,24 @@ describe String do
47
47
  end
48
48
  end
49
49
 
50
- describe Array do
50
+ describe Tuple do
51
51
 
52
- it "not be able to shrink empty array" do
53
- assert ![].shrinkable?
52
+ it "not be able to shrink empty tuple" do
53
+ assert !Tuple.new([]).shrinkable?
54
54
  end
55
55
 
56
- it "shrink array by trying to shrink the first shrinkable element available" do
57
- assert_equal [0,1], [1,1].shrink
58
- assert_equal [0,0,1], [0,1,1].shrink
56
+ it "shrink tuple by trying to shrink the last shrinkable element available" do
57
+ assert_equal [1,0], Tuple.new([1,1]).shrink.array
58
+ assert_equal [1,0,0], Tuple.new([1,1,0]).shrink.array
59
59
  end
60
60
 
61
- it "shrink array by 1 if none of the element in it is shrinkable" do
61
+ it "do not remove element from array when no element is shrinkable" do
62
62
  property_of {
63
63
  n = integer(1..10)
64
- a = Array.new(n,0)
64
+ a = Tuple.new(Array.new(n,0))
65
65
  [n,a]
66
66
  }.check { |n,a|
67
- assert_equal n-1, a.shrink.length
67
+ assert_equal n, a.shrink.length
68
68
  }
69
69
  end
70
70
  end
@@ -92,17 +92,17 @@ describe "Shrinker Test" do
92
92
  # The property we try to test is that non of the element is
93
93
  # larger than 1, and the array's length is less than 4.
94
94
  test = property_of {
95
- a = Array.new(10,1)
95
+ a = Deflating.new(Array.new(10,1))
96
96
  i = Random::rand(a.length)
97
97
  a[i] = 1
98
98
  a
99
99
  }
100
100
  assert_raises MiniTest::Assertion do
101
101
  test.check { |a|
102
- assert(!a.any? { |e| e > 0 } && a.length < 4,"contains 1")
102
+ assert(!a.array.any? { |e| e > 0 } && a.length < 4,"contains 1")
103
103
  }
104
104
  end
105
105
 
106
- assert_equal [0,0,0,0], test.shrunk_failed_data
106
+ assert_equal [1], test.shrunk_failed_data.array
107
107
  end
108
108
  end
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rantly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Howard Yeh
8
8
  - Anthony Bargnesi
9
+ - Eric Bischoff
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2015-09-16 00:00:00.000000000 Z
13
+ date: 2016-07-06 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: minitest
@@ -71,14 +72,17 @@ description:
71
72
  email:
72
73
  - hayeah@gmail.com
73
74
  - abargnesi@gmail.com
75
+ - ebischoff@nerim.net
74
76
  executables: []
75
77
  extensions: []
76
78
  extra_rdoc_files:
77
79
  - LICENSE
78
80
  - README.textile
81
+ - CHANGELOG.md
79
82
  files:
80
83
  - ".document"
81
84
  - ".travis.yml"
85
+ - CHANGELOG.md
82
86
  - Gemfile
83
87
  - LICENSE
84
88
  - README.textile
@@ -119,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
123
  version: '0'
120
124
  requirements: []
121
125
  rubyforge_project:
122
- rubygems_version: 2.4.5.1
126
+ rubygems_version: 2.5.1
123
127
  signing_key:
124
128
  specification_version: 4
125
129
  summary: Ruby Imperative Random Data Generator and Quickcheck