flores 0.0.1
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 +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: []
|