rantly 0.3.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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