flores 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +23 -0
- data/Makefile +50 -0
- data/examples/analyze_number.rb +13 -0
- data/examples/socket_acceptance_spec.rb +85 -0
- data/examples/socket_analyze_spec.rb +21 -0
- data/examples/socket_stress_spec.rb +49 -0
- data/flores.gemspec +23 -0
- data/lib/randomized.rb +67 -0
- data/lib/rspec/stress_it.rb +129 -0
- data/spec/randomized_spec.rb +95 -0
- metadata +62 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f3e39f321f6d9a2ab60eeb8b478e8c140137b54c
|
4
|
+
data.tar.gz: a46b1752bbc387b7007b212a6b84388d69cd75c8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 25aabaf1ef95b9fa0c0ddca1a137edc2c3677420bebae35b6f3c697b5dd298ce4a3246b69210111f9de2ade5a1b80adc21bf01420bcaa8f0783a8c26bdd3391c
|
7
|
+
data.tar.gz: 6e4ce72dd9e879c63b66aa12a6585c11b1c9011fcbb9afb2121870c52be7a866996f8bedbdec949112310974f0280c969662ad78cc7ae4a80d35f82af68f987b
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.2.5)
|
5
|
+
rspec (3.2.0)
|
6
|
+
rspec-core (~> 3.2.0)
|
7
|
+
rspec-expectations (~> 3.2.0)
|
8
|
+
rspec-mocks (~> 3.2.0)
|
9
|
+
rspec-core (3.2.0)
|
10
|
+
rspec-support (~> 3.2.0)
|
11
|
+
rspec-expectations (3.2.0)
|
12
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
13
|
+
rspec-support (~> 3.2.0)
|
14
|
+
rspec-mocks (3.2.0)
|
15
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
16
|
+
rspec-support (~> 3.2.0)
|
17
|
+
rspec-support (3.2.1)
|
18
|
+
|
19
|
+
PLATFORMS
|
20
|
+
ruby
|
21
|
+
|
22
|
+
DEPENDENCIES
|
23
|
+
rspec (>= 3.0.0)
|
data/Makefile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
GEMSPEC=$(shell ls *.gemspec)
|
2
|
+
VERSION=$(shell awk -F\" '/spec.version/ { print $$2 }' $(GEMSPEC))
|
3
|
+
NAME=$(shell awk -F\" '/spec.name/ { print $$2 }' $(GEMSPEC))
|
4
|
+
GEM=$(NAME)-$(VERSION).gem
|
5
|
+
|
6
|
+
.PHONY: test
|
7
|
+
test:
|
8
|
+
sh notify-failure.sh ruby test/all.rb
|
9
|
+
|
10
|
+
.PHONY: testloop
|
11
|
+
testloop:
|
12
|
+
while true; do \
|
13
|
+
$(MAKE) test; \
|
14
|
+
$(MAKE) wait-for-changes; \
|
15
|
+
done
|
16
|
+
|
17
|
+
.PHONY: serve-coverage
|
18
|
+
serve-coverage:
|
19
|
+
cd coverage; python -mSimpleHTTPServer
|
20
|
+
|
21
|
+
.PHONY: wait-for-changes
|
22
|
+
wait-for-changes:
|
23
|
+
-inotifywait --exclude '\.swp' -e modify $$(find $(DIRS) -name '*.rb'; find $(DIRS) -type d)
|
24
|
+
|
25
|
+
.PHONY: package
|
26
|
+
package: | $(GEM)
|
27
|
+
|
28
|
+
.PHONY: gem
|
29
|
+
gem: $(GEM)
|
30
|
+
|
31
|
+
$(GEM):
|
32
|
+
gem build $(GEMSPEC)
|
33
|
+
|
34
|
+
.PHONY: test-package
|
35
|
+
test-package: $(GEM)
|
36
|
+
# Sometimes 'gem build' makes a faulty gem.
|
37
|
+
gem unpack $(GEM)
|
38
|
+
rm -rf $(NAME)-$(VERSION)/
|
39
|
+
|
40
|
+
.PHONY: publish
|
41
|
+
publish: test-package
|
42
|
+
gem push $(GEM)
|
43
|
+
|
44
|
+
.PHONY: install
|
45
|
+
install: $(GEM)
|
46
|
+
gem install $(GEM)
|
47
|
+
|
48
|
+
.PHONY: clean
|
49
|
+
clean:
|
50
|
+
-rm -rf .yardoc $(GEM) $(NAME)-$(VERSION)/
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "randomized"
|
2
|
+
require "rspec/stress_it"
|
3
|
+
|
4
|
+
RSpec.configure do |c|
|
5
|
+
c.extend RSpec::StressIt
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "number" do
|
9
|
+
let(:number) { Randomized.number(0..200) }
|
10
|
+
analyze_it "should be less than 100", [:number] do
|
11
|
+
expect(number).to(be < 100)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require "randomized"
|
2
|
+
require "socket"
|
3
|
+
require "rspec/stress_it"
|
4
|
+
|
5
|
+
RSpec.configure do |c|
|
6
|
+
c.extend RSpec::StressIt
|
7
|
+
end
|
8
|
+
|
9
|
+
class TCPIntegrationTestFactory
|
10
|
+
def initialize(port)
|
11
|
+
@listener = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
12
|
+
@client = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
13
|
+
@port = port
|
14
|
+
end
|
15
|
+
|
16
|
+
def teardown
|
17
|
+
@listener.close unless @listener.closed?
|
18
|
+
@client.close unless @listener.closed?
|
19
|
+
end
|
20
|
+
|
21
|
+
def sockaddr
|
22
|
+
Socket.sockaddr_in(@port, "127.0.0.1")
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup
|
26
|
+
@listener.bind(sockaddr)
|
27
|
+
@listener.listen(5)
|
28
|
+
end
|
29
|
+
|
30
|
+
def send_and_receive(text)
|
31
|
+
@client.connect(sockaddr)
|
32
|
+
server, _ = @listener.accept
|
33
|
+
|
34
|
+
@client.syswrite(text)
|
35
|
+
@client.close
|
36
|
+
#expect(client.syswrite(text)).to(be == text.bytesize)
|
37
|
+
server.read
|
38
|
+
ensure
|
39
|
+
@client.close unless @client.closed?
|
40
|
+
server.close unless server.nil? || server.closed?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "TCPServer+TCPSocket" do
|
45
|
+
let(:port) { Randomized.number(1024..65535) }
|
46
|
+
let(:text) { Randomized.text(1..10000) }
|
47
|
+
subject { TCPIntegrationTestFactory.new(port) }
|
48
|
+
|
49
|
+
#describe "using before/after and stress_it2" do
|
50
|
+
#before do
|
51
|
+
#begin
|
52
|
+
#subject.setup
|
53
|
+
#rescue Errno::EADDRINUSE
|
54
|
+
## We chose a random port that was already in use, let's skip this test.
|
55
|
+
#skip("Port #{port} is in use by another process, skipping")
|
56
|
+
#end
|
57
|
+
#end
|
58
|
+
|
59
|
+
#after do
|
60
|
+
#subject.teardown
|
61
|
+
#end
|
62
|
+
|
63
|
+
#stress_it2 "should send data correctly" do
|
64
|
+
#received = subject.send_and_receive(text)
|
65
|
+
#expect(received).to(be == text)
|
66
|
+
#end
|
67
|
+
#end
|
68
|
+
|
69
|
+
describe "using stress_it" do
|
70
|
+
stress_it "should send data correctly" do
|
71
|
+
begin
|
72
|
+
subject.setup
|
73
|
+
rescue Errno::EADDRINUSE
|
74
|
+
next # Skip port bindings that are in use
|
75
|
+
end
|
76
|
+
|
77
|
+
begin
|
78
|
+
received = subject.send_and_receive(text)
|
79
|
+
expect(received).to(be == text)
|
80
|
+
ensure
|
81
|
+
subject.teardown
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "randomized"
|
2
|
+
require "socket"
|
3
|
+
require "rspec/stress_it"
|
4
|
+
|
5
|
+
RSpec.configure do |c|
|
6
|
+
c.extend RSpec::StressIt
|
7
|
+
end
|
8
|
+
|
9
|
+
describe TCPServer do
|
10
|
+
subject(:socket) { Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) }
|
11
|
+
let(:sockaddr) { Socket.sockaddr_in(port, "127.0.0.1") }
|
12
|
+
after { socket.close }
|
13
|
+
|
14
|
+
context "on a random port" do
|
15
|
+
let(:port) { Randomized.number(-100000..100000) }
|
16
|
+
analyze_it "should bind successfully", [:port] do
|
17
|
+
socket.bind(sockaddr)
|
18
|
+
expect(socket.local_address.ip_port).to(be == port)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "randomized"
|
2
|
+
require "socket"
|
3
|
+
require "rspec/stress_it"
|
4
|
+
|
5
|
+
RSpec.configure do |c|
|
6
|
+
c.extend RSpec::StressIt
|
7
|
+
end
|
8
|
+
|
9
|
+
describe TCPServer do
|
10
|
+
subject(:socket) { Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) }
|
11
|
+
let(:sockaddr) { Socket.sockaddr_in(port, "127.0.0.1") }
|
12
|
+
let(:ignore_eaddrinuse) do
|
13
|
+
proc do |m, *args|
|
14
|
+
begin
|
15
|
+
m.call(*args)
|
16
|
+
rescue Errno::EADDRINUSE
|
17
|
+
# ignore
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
after do
|
23
|
+
socket.close
|
24
|
+
end
|
25
|
+
|
26
|
+
context "on privileged ports" do
|
27
|
+
let(:port) { Randomized.number(1..1023) }
|
28
|
+
stress_it "should raise Errno::EACCESS" do
|
29
|
+
expect { socket.bind(sockaddr) }.to(raise_error(Errno::EACCES))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "on unprivileged ports" do
|
34
|
+
let(:port) { Randomized.number(1025..65535) }
|
35
|
+
stress_it "should bind on a port" do
|
36
|
+
# EADDRINUSE is expected since we are picking ports at random
|
37
|
+
# Let's ignore this specific exception
|
38
|
+
allow(socket).to(receive(:bind).and_wrap_original(&ignore_eaddrinuse))
|
39
|
+
expect { socket.bind(sockaddr) }.to_not(raise_error)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "on port 0" do
|
44
|
+
let(:port) { 0 }
|
45
|
+
stress_it "should bind successfully" do
|
46
|
+
expect { socket.bind(sockaddr) }.to_not(raise_error)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/flores.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
files = %x{git ls-files}.split("\n")
|
3
|
+
|
4
|
+
spec.name = "flores"
|
5
|
+
spec.version = "0.0.1"
|
6
|
+
spec.summary = "Fuzz, randomize, and stress your tests"
|
7
|
+
spec.description = <<-DESCRIPTION
|
8
|
+
Add fuzzing, randomization, and stress to your tests.
|
9
|
+
|
10
|
+
This library is an exploration to build the tools to let you write tests
|
11
|
+
that find bugs.
|
12
|
+
|
13
|
+
In memory of Carlo Flores.
|
14
|
+
DESCRIPTION
|
15
|
+
spec.license = "AGPL 3.0 - http://www.gnu.org/licenses/agpl-3.0.html"
|
16
|
+
|
17
|
+
spec.files = files
|
18
|
+
spec.require_paths << "lib"
|
19
|
+
|
20
|
+
spec.authors = ["Jordan Sissel"]
|
21
|
+
spec.email = ["jls@semicomplete.com"]
|
22
|
+
end
|
23
|
+
|
data/lib/randomized.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# A collection of methods intended for use in randomized testing.
|
2
|
+
module Randomized
|
3
|
+
# Generates text with random characters of a given length (or within a length range)
|
4
|
+
#
|
5
|
+
# * The length can be a number or a range `x..y`. If a range, it must be ascending (x < y)
|
6
|
+
# * Negative lengths are not permitted and will raise an ArgumentError
|
7
|
+
#
|
8
|
+
# @param length [Fixnum or Range] the length of text to generate
|
9
|
+
# @return [String] the
|
10
|
+
def self.text(length)
|
11
|
+
if length.is_a?(Range)
|
12
|
+
raise ArgumentError, "Requires ascending range, you gave #{length}." if length.end < length.begin
|
13
|
+
raise ArgumentError, "A negative length is not permitted, I received range #{length}" if length.begin < 0
|
14
|
+
|
15
|
+
length = integer(length)
|
16
|
+
else
|
17
|
+
raise ArgumentError, "A negative length is not permitted, I received #{length}" if length < 0
|
18
|
+
end
|
19
|
+
|
20
|
+
length.times.collect { character }.join
|
21
|
+
end # def text
|
22
|
+
|
23
|
+
# Generates a random character (A string of length 1)
|
24
|
+
#
|
25
|
+
# @return [String]
|
26
|
+
def self.character
|
27
|
+
# TODO(sissel): Add support to generate valid UTF-8. I started reading
|
28
|
+
# Unicode 7 (http://www.unicode.org/versions/Unicode7.0.0/) and after much
|
29
|
+
# reading, I realized I wasn't in my house anymore but had somehow lost
|
30
|
+
# track of time and was alone in a field. Civilization had fallen centuries
|
31
|
+
# ago. :P
|
32
|
+
|
33
|
+
# Until UTF-8 is supported, just return a random lower ASCII character
|
34
|
+
integer(32..127).chr
|
35
|
+
end # def character
|
36
|
+
|
37
|
+
# Return a random integer value within a given range.
|
38
|
+
#
|
39
|
+
# @param range [Range]
|
40
|
+
def self.integer(range)
|
41
|
+
raise ArgumentError, "Range not given, got #{range.class}: #{range.inspect}" if !range.is_a?(Range)
|
42
|
+
rand(range)
|
43
|
+
end # def integer
|
44
|
+
|
45
|
+
# Return a random number within a given range.
|
46
|
+
#
|
47
|
+
# @param range [Range]
|
48
|
+
def self.number(range)
|
49
|
+
raise ArgumentError, "Range not given, got #{range.class}: #{range.inspect}" if !range.is_a?(Range)
|
50
|
+
# Range#size returns the number of elements in the range, not the length of the range.
|
51
|
+
# This makes #size return (abs(range.begin - range.end) + 1), and we want the length, so subtract 1.
|
52
|
+
rand * (range.size - 1) + range.begin
|
53
|
+
end # def number
|
54
|
+
|
55
|
+
# Run a block a random number of times.
|
56
|
+
#
|
57
|
+
# @param range [Fixnum of Range] same meaning as #integer(range)
|
58
|
+
def self.iterations(range, &block)
|
59
|
+
range = 0..range if range.is_a?(Numeric)
|
60
|
+
if block_given?
|
61
|
+
integer(range).times(&block)
|
62
|
+
nil
|
63
|
+
else
|
64
|
+
integer(range).times
|
65
|
+
end
|
66
|
+
end # def iterations
|
67
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "rspec/core"
|
3
|
+
|
4
|
+
module RSpec::StressIt
|
5
|
+
DEFAULT_ITERATIONS = 1..5000
|
6
|
+
|
7
|
+
# Wraps `it` and runs the block many times. Each run has will clear the `let` cache.
|
8
|
+
#
|
9
|
+
# The intent of this is to allow randomized testing for fuzzing and stress testing
|
10
|
+
# of APIs to help find edge cases and weird behavior.
|
11
|
+
#
|
12
|
+
# The default number of iterations is randomly selected between 1 and 1000 inclusive
|
13
|
+
def stress_it(name, options={}, &block)
|
14
|
+
__iterations = Randomized.iterations(options.delete(:stress_iterations) || DEFAULT_ITERATIONS)
|
15
|
+
it(name, options) do
|
16
|
+
# Run the block of an example many times
|
17
|
+
__iterations.each do |i|
|
18
|
+
# Run the block within 'it' scope
|
19
|
+
instance_eval(&block)
|
20
|
+
|
21
|
+
# clear the internal rspec `let` cache this lets us run a test
|
22
|
+
# repeatedly with fresh `let` evaluations.
|
23
|
+
# Reference: https://github.com/rspec/rspec-core/blob/5fc29a15b9af9dc1c9815e278caca869c4769767/lib/rspec/core/memoized_helpers.rb#L124-L127
|
24
|
+
__memoized.clear
|
25
|
+
end
|
26
|
+
end # it ...
|
27
|
+
end # def stress_it
|
28
|
+
|
29
|
+
# Generate a random number of copies of a given example.
|
30
|
+
# The idea is to take 1 `it` and run it N times to help tease out failures.
|
31
|
+
# Of course, the teasing requires you have randomized `let` usage, for example:
|
32
|
+
#
|
33
|
+
# let(:number) { Randomized.number(0..200) }
|
34
|
+
# it "should be less than 100" do
|
35
|
+
# expect(number).to(be < 100)
|
36
|
+
# end
|
37
|
+
def stress_it2(name, options={}, &block)
|
38
|
+
__iterations = Randomized.iterations(options.delete(:stress_iterations) || DEFAULT_ITERATIONS)
|
39
|
+
__iterations.each do |i|
|
40
|
+
it(example_name + " [#{i}]", *args) do
|
41
|
+
instance_eval(&block)
|
42
|
+
end # it ...
|
43
|
+
end # .times
|
44
|
+
end
|
45
|
+
|
46
|
+
# Perform analysis on failure scenarios of a given example
|
47
|
+
#
|
48
|
+
# This will run the given example a random number of times and aggregate the
|
49
|
+
# results. If any failures occur, the spec will fail and a report will be
|
50
|
+
# given on that test.
|
51
|
+
#
|
52
|
+
# Example spec:
|
53
|
+
#
|
54
|
+
# let(:number) { Randomized.number(0..200) }
|
55
|
+
# fuzz "should be less than 100" do
|
56
|
+
# expect(number).to(be < 100)
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# Example report:
|
60
|
+
def analyze_it(name, variables, &block)
|
61
|
+
it(name) do
|
62
|
+
results = Hash.new { |h,k| h[k] = [] }
|
63
|
+
iterations = Randomized.iterations(DEFAULT_ITERATIONS)
|
64
|
+
iterations.each do |i|
|
65
|
+
state = Hash[variables.collect { |l| [l, __send__(l)] }]
|
66
|
+
begin
|
67
|
+
instance_eval(&block)
|
68
|
+
results[:success] << [state, nil]
|
69
|
+
rescue => e
|
70
|
+
results[e.class] << [state, e]
|
71
|
+
rescue Exception => e
|
72
|
+
results[e.class] << [state, e]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Clear `let` memoizations
|
76
|
+
__memoized.clear
|
77
|
+
end
|
78
|
+
|
79
|
+
if results[:success] != iterations
|
80
|
+
raise Analysis.new(results)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Analysis < StandardError
|
86
|
+
def initialize(results)
|
87
|
+
@results = results
|
88
|
+
end
|
89
|
+
|
90
|
+
def total
|
91
|
+
@results.reduce(0) { |m, (k,v)| m + v.length }
|
92
|
+
end
|
93
|
+
|
94
|
+
def success_count
|
95
|
+
if @results.include?(:success)
|
96
|
+
@results[:success].length
|
97
|
+
else
|
98
|
+
0
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def percent(count)
|
103
|
+
return (count + 0.0) / total
|
104
|
+
end
|
105
|
+
|
106
|
+
def percent_s(count)
|
107
|
+
return sprintf("%.2f%%", percent(count) * 100)
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_s
|
111
|
+
# This method is crazy complex for a formatter. Should refactor this significantly.
|
112
|
+
report = ["#{percent_s(success_count)} tests successful of #{total} tests"]
|
113
|
+
if success_count < total
|
114
|
+
report << "Failure analysis:"
|
115
|
+
report += @results.sort_by { |k,v| -v.length }.reject { |k,v| k == :success }.collect do |k, v|
|
116
|
+
sample = v.sample(5).collect { |v| v.first }.join(", ")
|
117
|
+
[
|
118
|
+
" #{percent_s(v.length)} -> [#{v.length}] #{k}",
|
119
|
+
" Sample exception:",
|
120
|
+
v.sample(1).first[1].to_s.gsub(/^/, " "),
|
121
|
+
" Samples causing #{k}:",
|
122
|
+
*v.sample(5).collect { |state, _exception| " #{state}" }
|
123
|
+
]
|
124
|
+
end.flatten
|
125
|
+
end
|
126
|
+
report.join("\n")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end # module RSpec::StressIt
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require "randomized"
|
2
|
+
require "rspec/stress_it"
|
3
|
+
|
4
|
+
RSpec.configure do |c|
|
5
|
+
c.extend RSpec::StressIt
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Randomized do
|
9
|
+
describe "#text" do
|
10
|
+
context "with no arguments" do
|
11
|
+
it "should raise ArgumentError" do
|
12
|
+
expect { subject.text }.to(raise_error(ArgumentError))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "with 1 length argument" do
|
17
|
+
subject { described_class.text(length) }
|
18
|
+
|
19
|
+
context "that is positive" do
|
20
|
+
let(:length) { rand(1..1000) }
|
21
|
+
stress_it "should give a string with that length" do
|
22
|
+
expect(subject).to(be_a(String))
|
23
|
+
expect(subject.length).to(eq(length))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "that is negative" do
|
28
|
+
let(:length) { -1 * rand(1..1000) }
|
29
|
+
stress_it "should raise ArgumentError" do
|
30
|
+
expect { subject }.to(raise_error(ArgumentError))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with 1 range argument" do
|
36
|
+
let(:start) { rand(1..1000) }
|
37
|
+
let(:length) { rand(1..1000) }
|
38
|
+
subject { described_class.text(range) }
|
39
|
+
|
40
|
+
context "that is ascending" do
|
41
|
+
let(:range) { start .. (start + length) }
|
42
|
+
stress_it "should give a string within that length range" do
|
43
|
+
expect(subject).to(be_a(String))
|
44
|
+
expect(range).to(include(subject.length))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "that is descending" do
|
49
|
+
let(:range) { start .. (start - length) }
|
50
|
+
stress_it "should raise ArgumentError" do
|
51
|
+
expect { subject }.to(raise_error(ArgumentError))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#character" do
|
58
|
+
subject { described_class.character }
|
59
|
+
stress_it "returns a string of length 1" do
|
60
|
+
expect(subject.length).to(be == 1)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
shared_examples_for "random numbers within a range" do
|
65
|
+
let(:start) { Randomized.integer(-100000 .. 100000) }
|
66
|
+
let(:length) { Randomized.integer(1 .. 100000) }
|
67
|
+
let(:range) { start .. (start + length) }
|
68
|
+
|
69
|
+
stress_it "should be a Numeric" do
|
70
|
+
expect(subject).to(be_a(Numeric))
|
71
|
+
end
|
72
|
+
|
73
|
+
stress_it "should be within the bounds of the given range" do
|
74
|
+
expect(range).to(include(subject))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#integer" do
|
79
|
+
it_behaves_like "random numbers within a range" do
|
80
|
+
subject { Randomized.integer(range) }
|
81
|
+
stress_it "is a Fixnum" do
|
82
|
+
expect(subject).to(be_a(Fixnum))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
describe "#number" do
|
87
|
+
it_behaves_like "random numbers within a range" do
|
88
|
+
subject { Randomized.number(range) }
|
89
|
+
|
90
|
+
stress_it "is a Float" do
|
91
|
+
expect(subject).to(be_a(Float))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flores
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jordan Sissel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-19 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |2
|
14
|
+
Add fuzzing, randomization, and stress to your tests.
|
15
|
+
|
16
|
+
This library is an exploration to build the tools to let you write tests
|
17
|
+
that find bugs.
|
18
|
+
|
19
|
+
In memory of Carlo Flores.
|
20
|
+
email:
|
21
|
+
- jls@semicomplete.com
|
22
|
+
executables: []
|
23
|
+
extensions: []
|
24
|
+
extra_rdoc_files: []
|
25
|
+
files:
|
26
|
+
- Gemfile
|
27
|
+
- Gemfile.lock
|
28
|
+
- Makefile
|
29
|
+
- examples/analyze_number.rb
|
30
|
+
- examples/socket_acceptance_spec.rb
|
31
|
+
- examples/socket_analyze_spec.rb
|
32
|
+
- examples/socket_stress_spec.rb
|
33
|
+
- flores.gemspec
|
34
|
+
- lib/randomized.rb
|
35
|
+
- lib/rspec/stress_it.rb
|
36
|
+
- spec/randomized_spec.rb
|
37
|
+
homepage:
|
38
|
+
licenses:
|
39
|
+
- AGPL 3.0 - http://www.gnu.org/licenses/agpl-3.0.html
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 2.4.3
|
59
|
+
signing_key:
|
60
|
+
specification_version: 4
|
61
|
+
summary: Fuzz, randomize, and stress your tests
|
62
|
+
test_files: []
|