fuzzbert 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+
2
+ module FuzzBert::Generation
3
+
4
+ def generator
5
+ -> { to_data }
6
+ end
7
+
8
+ end
@@ -0,0 +1,18 @@
1
+
2
+ class FuzzBert::Generator
3
+ include FuzzBert::Generation
4
+
5
+ attr_reader :description
6
+
7
+ def initialize(desc, generator=nil, &blk)
8
+ @description = desc
9
+ @generator = generator || blk
10
+ raise RuntimeError.new("No generator given") unless @generator
11
+ end
12
+
13
+ def to_data
14
+ @generator.call
15
+ end
16
+
17
+ end
18
+
@@ -0,0 +1,70 @@
1
+ require 'base64'
2
+
3
+ module FuzzBert::Generators
4
+
5
+ class << self
6
+
7
+ def random(limit=1024)
8
+ -> { random_bytes(limit) { |data| data } }
9
+ end
10
+
11
+ def random_b64(limit=1024)
12
+ -> { random_bytes(b64_len(limit)) { |data| Base64.encode64(data) } }
13
+ end
14
+
15
+ def random_hex(limit=1024)
16
+ -> { random_bytes(hex_len(limit)) { |data| data.unpack("H*")[0] } }
17
+ end
18
+
19
+ def random_fixlen(len)
20
+ -> { random_bytes_fixlen(len) { |data| data } }
21
+ end
22
+
23
+ def random_b64_fixlen(len)
24
+ -> { random_bytes_fixlen(b64_len(len)) { |data| Base64.encode(data) } }
25
+ end
26
+
27
+ def random_hex_fixlen(len)
28
+ -> { random_bytes_fixlen(hex_len(len)) { |data| data.unpack("H*")[0] } }
29
+ end
30
+
31
+ def cycle(range)
32
+ ary = range.to_a
33
+ i = 0
34
+ lambda do
35
+ ret = ary[i]
36
+ i = (i + 1) % ary.size
37
+ ret
38
+ end
39
+ end
40
+
41
+ def fixed(data)
42
+ -> { data }
43
+ end
44
+
45
+ def sample(ary)
46
+ -> { ary.sample }
47
+ end
48
+
49
+ private
50
+
51
+ def hex_len(len)
52
+ len / 2
53
+ end
54
+
55
+ def b64_len(len)
56
+ len * 3 / 4
57
+ end
58
+
59
+ def random_bytes(limit)
60
+ len = FuzzBert::PRNG.rand(1..limit)
61
+ yield FuzzBert::PRNG.bytes(len)
62
+ end
63
+
64
+ def random_bytes_fixlen(len)
65
+ yield FuzzBert::PRNG.bytes(len)
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,19 @@
1
+
2
+ class FuzzBert::Mutator < FuzzBert::Generator
3
+
4
+ def initialize(value)
5
+ orig = value.dup
6
+ orig.force_encoding(Encoding::BINARY)
7
+ super("Mutator") do
8
+ #select a byte
9
+ i = FuzzBert::PRNG.rand(value.size)
10
+ old = orig[i].ord
11
+ #map a random value from 0..254 to 0..255 excluding the current value
12
+ b = FuzzBert::PRNG.rand(255)
13
+ b = b < old ? b : b + 1
14
+ orig.dup.tap { |s| s.setbyte(i, b) }
15
+ end
16
+ end
17
+
18
+ end
19
+
@@ -0,0 +1,81 @@
1
+ require 'fuzzbert'
2
+ require 'rake'
3
+ require 'rake/tasklib'
4
+
5
+ class FuzzBert::RakeTask < ::Rake::TaskLib
6
+ include ::Rake::DSL if defined?(::Rake::DSL)
7
+
8
+ # Name of task.
9
+ #
10
+ # default:
11
+ # :fuzz
12
+ attr_accessor :name
13
+
14
+ # Glob pattern to match files.
15
+ #
16
+ # default:
17
+ # 'fuzz/**/fuzz_*.rb'
18
+ attr_accessor :pattern
19
+
20
+ # Command line options to pass to ruby.
21
+ #
22
+ # default:
23
+ # nil
24
+ attr_accessor :ruby_opts
25
+
26
+ # Path to FuzzBert
27
+ #
28
+ # default:
29
+ # 'fuzzbert'
30
+ attr_accessor :fuzzbert_path
31
+
32
+ # Command line options to pass to fuzzbert.
33
+ #
34
+ # default:
35
+ # nil
36
+ attr_accessor :fuzzbert_opts
37
+
38
+ def initialize(*args)
39
+ #configure the rake task
40
+ setup_ivars(args)
41
+ yield self if block_given?
42
+
43
+ desc "Run FuzzBert random test suite" unless ::Rake.application.last_comment
44
+
45
+ task name do
46
+ run_task
47
+ end
48
+ end
49
+
50
+ def setup_ivars(*args)
51
+ @name = args.shift || :fuzz
52
+ @ruby_opts, @fuzzbert_opts = nil, nil
53
+ @fuzzbert_path = 'fuzzbert'
54
+ @pattern = 'fuzz/**/fuzz_*.rb'
55
+ end
56
+
57
+ def run_task
58
+ begin
59
+ system(command)
60
+ rescue
61
+ #silent, user could have interrupted a permanent session
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def command
68
+ cmd_parts = []
69
+ cmd_parts << RUBY
70
+ cmd_parts << ruby_opts
71
+ cmd_parts << "-S" << fuzzbert_path
72
+ cmd_parts << fuzzbert_opts
73
+ cmd_parts << pattern
74
+ cmd_parts.flatten.reject(&blank).join(" ")
75
+ end
76
+
77
+ def blank
78
+ lambda {|s| s.nil? || s == ""}
79
+ end
80
+ end
81
+
@@ -0,0 +1,154 @@
1
+ require 'stringio'
2
+
3
+ class FuzzBert::Template
4
+ include FuzzBert::Generation
5
+
6
+ def initialize(template)
7
+ @template = Parser.new(template).parse
8
+ @callbacks = {}
9
+ end
10
+
11
+ def set(name, &blk)
12
+ @callbacks[name] = blk
13
+ end
14
+
15
+ def to_data
16
+ "".tap do |buf|
17
+ @template.each { |t| buf << t.to_data(@callbacks) }
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ class Parser
24
+
25
+ def initialize(template)
26
+ @io = StringIO.new(template)
27
+ @template = []
28
+ end
29
+
30
+ def parse
31
+ @state = determine_state
32
+ while token = parse_token
33
+ @template << token
34
+ end
35
+ @template
36
+ end
37
+
38
+ def parse_token
39
+ case @state
40
+ when :TEXT
41
+ parse_text
42
+ when :IDENTIFIER
43
+ parse_identifier
44
+ else
45
+ nil
46
+ end
47
+ end
48
+
49
+ def determine_state
50
+ begin
51
+ @buf = @io.readchar
52
+
53
+ case @buf
54
+ when '$'
55
+ c = @io.readchar
56
+ if c == "{"
57
+ @buf = ""
58
+ :IDENTIFIER
59
+ else
60
+ @buf << c
61
+ :TEXT
62
+ end
63
+ when '\\'
64
+ @buf = ""
65
+ :TEXT
66
+ else
67
+ :TEXT
68
+ end
69
+ rescue EOFError
70
+ :EOF
71
+ end
72
+ end
73
+
74
+ def parse_identifier
75
+ name = ""
76
+ begin
77
+ until (c = @io.readchar) == '}'
78
+ name << c
79
+ end
80
+
81
+ if name.empty?
82
+ raise RuntimeError.new("No identifier name given")
83
+ end
84
+
85
+ @state = determine_state
86
+ Identifier.new(name)
87
+ rescue EOFError
88
+ raise RuntimeError.new("Unclosed identifier")
89
+ end
90
+ end
91
+
92
+ def parse_text
93
+ text = @buf
94
+ begin
95
+ loop do
96
+ until (c = @io.readchar) == '$'
97
+ if c == '\\'
98
+ text << parse_escape
99
+ else
100
+ text << c
101
+ end
102
+ end
103
+
104
+ d = @io.readchar
105
+ if d == "{"
106
+ @state = :IDENTIFIER
107
+ return Text.new(text)
108
+ else
109
+ text << c
110
+ text << d
111
+ end
112
+ end
113
+ rescue EOFError
114
+ @state = :EOF
115
+ Text.new(text)
116
+ end
117
+ end
118
+
119
+ def parse_escape
120
+ begin
121
+ @io.readchar
122
+ rescue EOFError
123
+ '\\'
124
+ end
125
+ end
126
+
127
+ end
128
+
129
+ class Text
130
+
131
+ def initialize(text)
132
+ @text = text
133
+ end
134
+
135
+ def to_data(callbacks)
136
+ @text
137
+ end
138
+
139
+ end
140
+
141
+ class Identifier
142
+
143
+ def initialize(name)
144
+ @name = name.to_sym
145
+ end
146
+
147
+ def to_data(callbacks)
148
+ callbacks[@name].call
149
+ end
150
+
151
+ end
152
+
153
+ end
154
+
@@ -0,0 +1,13 @@
1
+
2
+ class FuzzBert::Test
3
+
4
+ def initialize(runner)
5
+ @runner = runner
6
+ end
7
+
8
+ def run(data)
9
+ @runner.call data
10
+ end
11
+
12
+ end
13
+
@@ -0,0 +1,25 @@
1
+
2
+ class FuzzBert::TestSuite
3
+
4
+ attr_reader :description, :test, :generators
5
+
6
+ def initialize(desc)
7
+ @description = desc
8
+ @generators = []
9
+ end
10
+
11
+ def deploy(&blk)
12
+ @test = FuzzBert::Test.new(blk)
13
+ end
14
+
15
+ def data(desc, &blk)
16
+ @generators << FuzzBert::Generator.new(desc, blk.call)
17
+ end
18
+
19
+ def self.create(desc, &blk)
20
+ obj = self.new(desc)
21
+ obj.instance_eval(&blk) if blk
22
+ obj
23
+ end
24
+
25
+ end
@@ -0,0 +1,4 @@
1
+ module FuzzBert
2
+ VERSION = "1.0.0"
3
+ end
4
+
@@ -0,0 +1,39 @@
1
+ require 'rspec'
2
+ require 'fuzzbert'
3
+
4
+ describe FuzzBert::AutoRun do
5
+
6
+ let(:handler) do
7
+ c = Class.new
8
+ def c.handle(id, data, pid, status)
9
+ raise RuntimeError.new
10
+ end
11
+ c
12
+ end
13
+
14
+ value = "test"
15
+ called = false
16
+
17
+ fuzz "autorun" do
18
+
19
+ deploy do |data|
20
+ data.should == value
21
+ end
22
+
23
+ data "1" do
24
+ called = true
25
+ -> { value }
26
+ end
27
+
28
+ end
29
+
30
+ it "has added a TestSuite to AutoRun" do
31
+ suite = FuzzBert::AutoRun::TEST_CASES
32
+ suite.size.should == 1
33
+ FuzzBert::Executor.new(suite, pool_size: 1, limit: 1, handler: handler).run
34
+ called.should == true
35
+ end
36
+
37
+ end
38
+
39
+
@@ -0,0 +1,88 @@
1
+ require 'rspec'
2
+ require 'fuzzbert'
3
+
4
+ describe FuzzBert::TestSuite do
5
+ describe "fuzz" do
6
+ context "with one generator" do
7
+ let(:suite) do
8
+ FuzzBert::TestSuite.create "test" do
9
+ deploy { |data| data }
10
+
11
+ data("1") { FuzzBert::Generators.random }
12
+ end
13
+ end
14
+
15
+ it "returns an instance of TestSuite" do
16
+ suite.should be_an_instance_of(FuzzBert::TestSuite)
17
+ suite.description.should == "test"
18
+ end
19
+
20
+ it "defines a test that executes the block in deploy" do
21
+ suite.test.should_not be_nil
22
+ suite.test.should be_an_instance_of(FuzzBert::Test)
23
+ ["a", 10, true, Object.new].each { |o| suite.test.run(o).should == o }
24
+ end
25
+
26
+ it "defines one generator" do
27
+ suite.generators.size.should == 1
28
+ gen = suite.generators.first
29
+ gen.should be_an_instance_of(FuzzBert::Generator)
30
+ gen.description.should == "1"
31
+ gen.to_data.should be_an_instance_of(String)
32
+ end
33
+
34
+ end
35
+
36
+ context "with two generators" do
37
+ let(:suite) do
38
+ FuzzBert::TestSuite.create "test" do
39
+ deploy { |data| data }
40
+
41
+ data("1") { FuzzBert::Generators.fixed(1) }
42
+ data("2") { FuzzBert::Generators.fixed(2) }
43
+ end
44
+ end
45
+
46
+ it "defines two generators" do
47
+ suite.generators.size.should == 2
48
+ gen = suite.generators.first
49
+ gen.should be_an_instance_of(FuzzBert::Generator)
50
+ gen.description.should == "1"
51
+ gen.to_data.should == 1
52
+
53
+ gen = suite.generators[1]
54
+ gen.should be_an_instance_of(FuzzBert::Generator)
55
+ gen.description.should == "2"
56
+ gen.to_data.should == 2
57
+ end
58
+
59
+ end
60
+
61
+ context "with complex data" do
62
+ let(:suite) do
63
+ FuzzBert::TestSuite.create "test" do
64
+ deploy { |data| data }
65
+
66
+ data "1" do
67
+ c = FuzzBert::Container.new
68
+ c << FuzzBert::Generators.fixed("1")
69
+ c << FuzzBert::Generators.fixed("2")
70
+ c << FuzzBert::Generators.fixed("3")
71
+ c.generator
72
+ end
73
+
74
+ end
75
+ end
76
+
77
+ it "applies the container generators in sequence" do
78
+ suite.generators.size.should == 1
79
+ gen = suite.generators.first
80
+ gen.should be_an_instance_of(FuzzBert::Generator)
81
+ gen.description.should == "1"
82
+ gen.to_data.should == "123"
83
+ end
84
+ end
85
+ end
86
+
87
+ end
88
+