rantly 1.2.0 → 2.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 +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
|
-
|