rantly 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +9 -6
- data/CHANGELOG.md +33 -0
- data/Gemfile +4 -2
- data/README.md +10 -1
- data/Rakefile +15 -2
- data/VERSION.yml +2 -2
- data/lib/rantly.rb +4 -4
- data/lib/rantly/data.rb +1 -1
- data/lib/rantly/generator.rb +87 -95
- data/lib/rantly/property.rb +18 -21
- data/lib/rantly/shrinks.rb +40 -45
- data/lib/rantly/silly.rb +40 -38
- data/lib/rantly/spec.rb +4 -4
- data/rantly.gemspec +37 -42
- data/test/rantly_test.rb +85 -216
- data/test/shrinks_test.rb +38 -42
- metadata +5 -62
data/lib/rantly/property.rb
CHANGED
@@ -3,10 +3,10 @@ require 'pp'
|
|
3
3
|
require 'stringio'
|
4
4
|
|
5
5
|
class Rantly::Property
|
6
|
-
attr_reader :failed_data, :shrunk_failed_data
|
6
|
+
attr_reader :failed_data, :shrunk_failed_data
|
7
7
|
|
8
|
-
VERBOSITY = ENV.fetch('RANTLY_VERBOSE'){ 1 }.to_i
|
9
|
-
RANTLY_COUNT = ENV.fetch('RANTLY_COUNT'){ 100 }.to_i
|
8
|
+
VERBOSITY = ENV.fetch('RANTLY_VERBOSE') { 1 }.to_i
|
9
|
+
RANTLY_COUNT = ENV.fetch('RANTLY_COUNT') { 100 }.to_i
|
10
10
|
|
11
11
|
def io
|
12
12
|
@io ||= if VERBOSITY >= 1
|
@@ -24,46 +24,43 @@ class Rantly::Property
|
|
24
24
|
@property = property
|
25
25
|
end
|
26
26
|
|
27
|
-
def check(n=RANTLY_COUNT,limit=10
|
27
|
+
def check(n = RANTLY_COUNT, limit = 10, &assertion)
|
28
28
|
i = 0
|
29
29
|
test_data = nil
|
30
30
|
begin
|
31
|
-
Rantly.singleton.generate(n,limit
|
31
|
+
Rantly.singleton.generate(n, limit, @property) do |val|
|
32
32
|
test_data = val
|
33
|
-
|
34
|
-
io.puts
|
35
|
-
io.print
|
33
|
+
yield(val) if assertion
|
34
|
+
io.puts '' if (i % 100).zero?
|
35
|
+
io.print '.' if (i % 10).zero?
|
36
36
|
i += 1
|
37
37
|
end
|
38
38
|
io.puts
|
39
|
-
io.puts "
|
39
|
+
io.puts "SUCCESS - #{i} successful tests"
|
40
40
|
rescue Rantly::TooManyTries => e
|
41
41
|
io.puts
|
42
|
-
io.puts "too many tries: #{e.tries}"
|
43
|
-
raise e
|
42
|
+
io.puts "FAILURE - #{i} successful tests, too many tries: #{e.tries}"
|
43
|
+
raise e.exception("#{i} successful tests, too many tries: #{e.tries} (limit: #{e.limit})")
|
44
44
|
rescue Exception => boom
|
45
45
|
io.puts
|
46
|
-
io.puts "
|
46
|
+
io.puts "FAILURE - #{i} successful tests, failed on:"
|
47
47
|
pretty_print test_data
|
48
48
|
@failed_data = test_data
|
49
49
|
if @failed_data.respond_to?(:shrink)
|
50
50
|
@shrunk_failed_data, @depth = shrinkify(assertion, @failed_data)
|
51
|
-
io.puts "
|
51
|
+
io.puts "Minimal failed data (depth #{@depth}) is:"
|
52
52
|
pretty_print @shrunk_failed_data
|
53
53
|
end
|
54
|
-
raise
|
54
|
+
raise boom.exception("#{i} successful tests, failed on:\n#{test_data}\n\n#{boom}\n")
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
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
|
-
|
59
|
+
def shrinkify(assertion, data, depth = 0, iteration = 0)
|
63
60
|
min_data = data
|
64
61
|
max_depth = depth
|
65
62
|
if data.shrinkable?
|
66
|
-
while iteration < 1024
|
63
|
+
while iteration < 1024
|
67
64
|
# We assume that data.shrink is non-destructive
|
68
65
|
shrunk_data = data.shrink
|
69
66
|
begin
|
@@ -76,9 +73,9 @@ class Rantly::Property
|
|
76
73
|
max_depth = branch_depth
|
77
74
|
end
|
78
75
|
end
|
79
|
-
break
|
76
|
+
break unless data.retry?
|
80
77
|
end
|
81
78
|
end
|
82
|
-
|
79
|
+
[min_data, max_depth, iteration]
|
83
80
|
end
|
84
81
|
end
|
data/lib/rantly/shrinks.rb
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
class Integer
|
3
3
|
def shrink
|
4
4
|
shrunk = if self > 8
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
12
|
+
else
|
13
|
+
0
|
14
14
|
end
|
15
|
-
|
15
|
+
shrunk
|
16
16
|
end
|
17
17
|
|
18
18
|
def retry?
|
@@ -27,12 +27,12 @@ end
|
|
27
27
|
# String : shrink to ""
|
28
28
|
class String
|
29
29
|
def shrink
|
30
|
-
shrunk =
|
31
|
-
|
32
|
-
idx = Random
|
33
|
-
shrunk[idx] =
|
30
|
+
shrunk = dup
|
31
|
+
unless empty?
|
32
|
+
idx = Random.rand(size)
|
33
|
+
shrunk[idx] = ''
|
34
34
|
end
|
35
|
-
|
35
|
+
shrunk
|
36
36
|
end
|
37
37
|
|
38
38
|
def retry?
|
@@ -40,7 +40,7 @@ class String
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def shrinkable?
|
43
|
-
self !=
|
43
|
+
self != ''
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
@@ -64,39 +64,36 @@ class Tuple
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def size
|
67
|
-
|
67
|
+
length
|
68
68
|
end
|
69
69
|
|
70
70
|
def to_s
|
71
|
-
@array.to_s.insert(1,
|
71
|
+
@array.to_s.insert(1, 'T ')
|
72
72
|
end
|
73
73
|
|
74
74
|
def inspect
|
75
|
-
|
75
|
+
to_s
|
76
76
|
end
|
77
77
|
|
78
78
|
def each(&block)
|
79
79
|
@array.each(&block)
|
80
80
|
end
|
81
81
|
|
82
|
-
|
83
|
-
return @array
|
84
|
-
end
|
82
|
+
attr_reader :array
|
85
83
|
|
86
84
|
def shrink
|
87
85
|
shrunk = @array.dup
|
88
86
|
while @position >= 0
|
89
87
|
e = @array.at(@position)
|
90
|
-
if e.respond_to?(:shrinkable?) && e.shrinkable?
|
91
|
-
|
92
|
-
end
|
88
|
+
break if e.respond_to?(:shrinkable?) && e.shrinkable?
|
89
|
+
|
93
90
|
@position -= 1
|
94
91
|
end
|
95
92
|
if @position >= 0
|
96
93
|
shrunk[@position] = e.shrink
|
97
94
|
@position -= 1
|
98
95
|
end
|
99
|
-
|
96
|
+
Tuple.new(shrunk)
|
100
97
|
end
|
101
98
|
|
102
99
|
def retry?
|
@@ -104,7 +101,7 @@ class Tuple
|
|
104
101
|
end
|
105
102
|
|
106
103
|
def shrinkable?
|
107
|
-
@array.any? {|e| e.respond_to?(:shrinkable?) && e.shrinkable? }
|
104
|
+
@array.any? { |e| e.respond_to?(:shrinkable?) && e.shrinkable? }
|
108
105
|
end
|
109
106
|
end
|
110
107
|
|
@@ -128,24 +125,22 @@ class Deflating
|
|
128
125
|
end
|
129
126
|
|
130
127
|
def size
|
131
|
-
|
128
|
+
length
|
132
129
|
end
|
133
130
|
|
134
131
|
def to_s
|
135
|
-
@array.to_s.insert(1,
|
132
|
+
@array.to_s.insert(1, 'D ')
|
136
133
|
end
|
137
134
|
|
138
135
|
def inspect
|
139
|
-
|
136
|
+
to_s
|
140
137
|
end
|
141
138
|
|
142
139
|
def each(&block)
|
143
140
|
@array.each(&block)
|
144
141
|
end
|
145
142
|
|
146
|
-
|
147
|
-
return @array
|
148
|
-
end
|
143
|
+
attr_reader :array
|
149
144
|
|
150
145
|
def shrink
|
151
146
|
shrunk = @array.dup
|
@@ -158,7 +153,7 @@ class Deflating
|
|
158
153
|
end
|
159
154
|
@position -= 1
|
160
155
|
end
|
161
|
-
|
156
|
+
Deflating.new(shrunk)
|
162
157
|
end
|
163
158
|
|
164
159
|
def retry?
|
@@ -172,24 +167,24 @@ end
|
|
172
167
|
|
173
168
|
class Hash
|
174
169
|
def shrink
|
175
|
-
if
|
176
|
-
key,
|
177
|
-
clone =
|
170
|
+
if any? { |_, v| v.respond_to?(:shrinkable?) && v.shrinkable? }
|
171
|
+
key, = detect { |_, v| v.respond_to?(:shrinkable?) && v.shrinkable? }
|
172
|
+
clone = dup
|
178
173
|
clone[key] = clone[key].shrink
|
179
|
-
|
180
|
-
elsif !
|
181
|
-
key =
|
182
|
-
h2 =
|
174
|
+
clone
|
175
|
+
elsif !empty?
|
176
|
+
key = keys.first
|
177
|
+
h2 = dup
|
183
178
|
h2.delete(key)
|
184
|
-
|
179
|
+
h2
|
185
180
|
else
|
186
|
-
|
181
|
+
self
|
187
182
|
end
|
188
183
|
end
|
189
184
|
|
190
185
|
def shrinkable?
|
191
|
-
|
192
|
-
!
|
186
|
+
any? { |_, v| v.respond_to?(:shrinkable?) && v.shrinkable? } ||
|
187
|
+
!empty?
|
193
188
|
end
|
194
189
|
|
195
190
|
def retry?
|
data/lib/rantly/silly.rb
CHANGED
@@ -8,18 +8,17 @@ module Rantly::Silly
|
|
8
8
|
end
|
9
9
|
|
10
10
|
module Rantly::Silly::Love
|
11
|
+
def letter(n = 3)
|
12
|
+
body = array(n) { paragraph }.join "\n\n"
|
13
|
+
<<~EOS
|
14
|
+
#{address}:
|
11
15
|
|
12
|
-
|
13
|
-
body = array(n){paragraph}.join "\n\n"
|
14
|
-
<<-EOS
|
15
|
-
#{address}:
|
16
|
+
#{body}
|
16
17
|
|
17
|
-
#{
|
18
|
+
#{sign}
|
18
19
|
|
19
|
-
#{
|
20
|
-
|
21
|
-
#{post_script}
|
22
|
-
EOS
|
20
|
+
#{post_script}
|
21
|
+
EOS
|
23
22
|
end
|
24
23
|
|
25
24
|
def address
|
@@ -27,31 +26,34 @@ EOS
|
|
27
26
|
end
|
28
27
|
|
29
28
|
def extremifier
|
30
|
-
choose
|
29
|
+
choose 'most', 'ultimate', 'unbelievable', 'incredible', 'burning'
|
31
30
|
end
|
32
31
|
|
33
32
|
def pedestal_label
|
34
|
-
choose
|
33
|
+
choose 'beloved', 'desire', 'dove', 'virgin goddess', 'existential solution', 'lighthouse', 'beacon', 'holy mother', 'queen', 'mistress'
|
35
34
|
end
|
36
35
|
|
37
36
|
def double_plus_good
|
38
|
-
choose
|
37
|
+
choose 'holy', 'shiny', 'glittering', 'joyous', 'delicious'
|
39
38
|
end
|
40
39
|
|
41
40
|
def how_i_feel
|
42
|
-
choose
|
41
|
+
choose 'my heart aches', 'my spine pines', 'my spirit wanders and wonders', 'my soul is awed', 'my loin burns'
|
43
42
|
end
|
44
43
|
|
45
44
|
def paragraph
|
46
|
-
array(range(2,4)){ sentence}.join
|
45
|
+
array(range(2, 4)) { sentence }.join ' '
|
47
46
|
end
|
48
47
|
|
49
48
|
def sentence
|
50
49
|
freq \
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
proc {
|
51
|
+
"when #{how_i_feel}, my #{pedestal_label}, i feel the need to #{stalk_action},"\
|
52
|
+
"but this is not because #{how_i_feel}, but rather a symptom of my being your #{whoami}."
|
53
|
+
},
|
54
|
+
proc { "because you are my #{pedestal_label}, and i am your #{whoami}, no, rather your #{whoami}, #{fragment}." },
|
55
|
+
proc { "do not think that saying '#{how_i_feel}' suffices to show the depth of how #{how_i_feel}, because more than that, #{fantasy}" },
|
56
|
+
proc { "as a #{whoami}, that #{how_i_feel} is never quite enough for you, my #{double_plus_good} #{pedestal_label}." }
|
55
57
|
end
|
56
58
|
|
57
59
|
def fragment
|
@@ -59,8 +61,7 @@ EOS
|
|
59
61
|
choose "i hope to god #{fun}", "i believe #{fun}", "i will that #{fun}"
|
60
62
|
end
|
61
63
|
|
62
|
-
def caused_by
|
63
|
-
end
|
64
|
+
def caused_by; end
|
64
65
|
|
65
66
|
def whoami
|
66
67
|
"#{extremifier} #{humbleizer} #{groveler}"
|
@@ -71,11 +72,11 @@ EOS
|
|
71
72
|
end
|
72
73
|
|
73
74
|
def humbleizer
|
74
|
-
choose
|
75
|
+
choose 'undeserving', 'insignificant', 'unremarkable', 'fearful', 'menial'
|
75
76
|
end
|
76
77
|
|
77
78
|
def groveler
|
78
|
-
choose
|
79
|
+
choose 'slave', 'servant', 'captive', 'lapdog'
|
79
80
|
end
|
80
81
|
|
81
82
|
def post_script
|
@@ -88,27 +89,28 @@ EOS
|
|
88
89
|
|
89
90
|
def fantasy
|
90
91
|
freq \
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
92
|
+
proc {
|
93
|
+
make = choose 'raise', 'nurture', 'bring into the world'
|
94
|
+
babies = choose 'brood of babies', "#{double_plus_good} angels"
|
95
|
+
good = double_plus_good
|
96
|
+
effect = choose "the world becomes all the more #{good}",
|
97
|
+
"we may at the end of our lives rest in #{good} peace.",
|
98
|
+
"you, my #{pedestal_label}, would continue to live."
|
99
|
+
"we would #{make} #{babies}, so #{effect}."
|
100
|
+
},
|
101
|
+
proc {
|
102
|
+
do_thing = choose('kiss', 'hug', 'read poetry to each other', 'massage', "whisper empty nothings into each others' ears",
|
103
|
+
'be with each other, and oblivious to the entire world')
|
104
|
+
affect = choose 'joy', 'mindfulness', 'calm', 'sanctity'
|
105
|
+
"we would #{do_thing} with #{double_plus_good} #{affect}"
|
106
|
+
}
|
105
107
|
end
|
106
108
|
|
107
109
|
def stalk_action
|
108
|
-
choose
|
110
|
+
choose 'think of you', 'dream of us together', 'look at your picture and sigh'
|
109
111
|
end
|
110
112
|
|
111
113
|
def time_duration
|
112
|
-
choose
|
114
|
+
choose 'once in a while', 'night', 'day', 'hour', 'minute'
|
113
115
|
end
|
114
116
|
end
|
data/lib/rantly/spec.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'rantly'
|
2
2
|
module Rantly::Check
|
3
|
-
def check(n=100
|
4
|
-
Rantly.gen.each(n
|
3
|
+
def check(n = 100, &block)
|
4
|
+
Rantly.gen.each(n, &block)
|
5
5
|
end
|
6
6
|
|
7
|
-
def sample(n=100
|
8
|
-
Rantly.gen.map(n
|
7
|
+
def sample(n = 100, &block)
|
8
|
+
Rantly.gen.map(n, &block)
|
9
9
|
end
|
10
10
|
end
|
data/rantly.gemspec
CHANGED
@@ -1,47 +1,42 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
|
-
s.name =
|
3
|
-
s.summary =
|
4
|
-
s.homepage =
|
5
|
-
s.version =
|
6
|
-
s.license =
|
7
|
-
s.require_paths = [
|
8
|
-
s.authors = [
|
9
|
-
s.email = [
|
2
|
+
s.name = 'rantly'
|
3
|
+
s.summary = 'Ruby Imperative Random Data Generator and Quickcheck'
|
4
|
+
s.homepage = 'https://github.com/rantly-rb/rantly'
|
5
|
+
s.version = '2.0.0'
|
6
|
+
s.license = 'MIT'
|
7
|
+
s.require_paths = ['lib']
|
8
|
+
s.authors = ['Ana María Martínez Gómez', 'Howard Yeh', 'Anthony Bargnesi', 'Eric Bischoff']
|
9
|
+
s.email = ['anamma06@gmail.com', 'hayeah@gmail.com', 'abargnesi@gmail.com', 'ebischoff@nerim.net']
|
10
10
|
s.extra_rdoc_files = [
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
'LICENSE',
|
12
|
+
'README.md',
|
13
|
+
'CHANGELOG.md'
|
14
14
|
]
|
15
|
-
s.files
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
15
|
+
s.files = [
|
16
|
+
'.document',
|
17
|
+
'.travis.yml',
|
18
|
+
'Gemfile',
|
19
|
+
'LICENSE',
|
20
|
+
'README.md',
|
21
|
+
'CHANGELOG.md',
|
22
|
+
'Rakefile',
|
23
|
+
'VERSION.yml',
|
24
|
+
'lib/rantly.rb',
|
25
|
+
'lib/rantly/data.rb',
|
26
|
+
'lib/rantly/generator.rb',
|
27
|
+
'lib/rantly/minitest.rb',
|
28
|
+
'lib/rantly/minitest_extensions.rb',
|
29
|
+
'lib/rantly/property.rb',
|
30
|
+
'lib/rantly/rspec.rb',
|
31
|
+
'lib/rantly/rspec_extensions.rb',
|
32
|
+
'lib/rantly/shrinks.rb',
|
33
|
+
'lib/rantly/silly.rb',
|
34
|
+
'lib/rantly/spec.rb',
|
35
|
+
'lib/rantly/testunit_extensions.rb',
|
36
|
+
'rantly.gemspec',
|
37
|
+
'test/rantly_test.rb',
|
38
|
+
'test/shrinks_test.rb',
|
39
|
+
'test/test_helper.rb'
|
40
40
|
]
|
41
|
-
|
42
|
-
s.add_development_dependency('rake', '~> 12.0.0')
|
43
|
-
s.add_development_dependency('minitest', '~> 5.10.0')
|
44
|
-
s.add_development_dependency('simplecov', '>= 0')
|
45
|
-
s.add_development_dependency('coveralls', '>= 0')
|
41
|
+
s.required_ruby_version = '>= 2.4.0'
|
46
42
|
end
|
47
|
-
|