bare-rb 0.1.5 → 0.2.3
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/lib/bare-rb.rb +43 -47
- data/lib/dfs.rb +14 -0
- data/lib/exceptions.rb +18 -0
- data/lib/generative_testing/gen.rb +26 -0
- data/lib/generative_testing/grammar_util.rb +22 -0
- data/lib/generative_testing/monkey_patch.rb +262 -0
- data/lib/lexer.rb +3 -2
- data/lib/parser.rb +1 -1
- data/lib/types.rb +442 -249
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13ba9c8343a1abde7872789f4cff8eb90db79d35bd3db3a7e68bf18685393b5e
|
4
|
+
data.tar.gz: a5f91e83d28214adb66b3501e67fce4705da02065c0627c907558cc100edcffb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1116e309452df189de03b21a88128f89847fd0b85ffcacc98b243e04abb00fd0b0201e55278430316e93a18d89162d7fa19c4adcd562fe23dd83a9d327118093
|
7
|
+
data.tar.gz: 18e1327481364fe6e0588f58b79d92fb8baac862ed980d4f6781d1d1642f1012fbbc28fa318b8784748166b0db77128b9b300b8375ef0d2227d96a02d216a2ef
|
data/lib/bare-rb.rb
CHANGED
@@ -2,66 +2,65 @@ require 'set'
|
|
2
2
|
require_relative "types"
|
3
3
|
require_relative "lexer"
|
4
4
|
require_relative "parser"
|
5
|
+
require_relative 'dfs'
|
6
|
+
require_relative 'generative_testing/gen'
|
7
|
+
require_relative 'generative_testing/monkey_patch'
|
5
8
|
|
6
9
|
class Bare
|
7
|
-
def self.encode(msg, schema, type=nil)
|
8
|
-
|
9
|
-
|
10
|
-
schema
|
10
|
+
def self.encode(msg, schema, type = nil)
|
11
|
+
buffer = "".b
|
12
|
+
if schema.is_a?(BareTypes::Schema)
|
13
|
+
raise NoTypeProvided.new("To encode with a schema as opposed to a raw type you must specify which type in the schema you want to encode as a symbol.\nBare.encode(msg, schema, :Type)") if type.nil?
|
14
|
+
unless schema.types.include?(type)
|
15
|
+
raise("#{type} is not a type found in this schema. Choose from #{schema.types.keys}")
|
16
|
+
end
|
17
|
+
schema[type].encode(msg, buffer)
|
11
18
|
else
|
12
|
-
schema.encode(msg)
|
19
|
+
schema.encode(msg, buffer)
|
13
20
|
end
|
21
|
+
buffer
|
14
22
|
end
|
15
23
|
|
16
|
-
def self.decode(msg, schema, type=nil)
|
17
|
-
if schema.is_a?(
|
18
|
-
raise NoTypeProvided("To decode with a schema as opposed to a raw type you must specify which type in the same you want to encode as a symbol.\nBare.encode(msg, schema, :Type)") if type.nil?
|
19
|
-
value,
|
24
|
+
def self.decode(msg, schema, type = nil)
|
25
|
+
if schema.is_a?(BareTypes::Schema)
|
26
|
+
raise NoTypeProvided.new("To decode with a schema as opposed to a raw type you must specify which type in the same you want to encode as a symbol.\nBare.encode(msg, schema, :Type)") if type.nil?
|
27
|
+
value, _ = schema[type].decode(msg)
|
20
28
|
value
|
21
29
|
else
|
22
|
-
value,
|
23
|
-
|
30
|
+
value, _ = schema.decode(msg)
|
31
|
+
value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a schema and a binary input
|
36
|
+
# optionally write these to files
|
37
|
+
def self.generative_test(schema_path=nil, binary_path=nil)
|
38
|
+
schema = BareTypes::Schema.make
|
39
|
+
input = schema.create_input
|
40
|
+
key = schema.types.keys[0]
|
41
|
+
binary = Bare.encode(input[key], schema, key)
|
42
|
+
unless binary_path.nil?
|
43
|
+
file = File.open(binary_path, 'wb+')
|
44
|
+
file.write(binary)
|
45
|
+
file.close
|
24
46
|
end
|
47
|
+
unless schema_path.nil?
|
48
|
+
file = File.open(schema_path, 'w+')
|
49
|
+
file.write(schema.to_s)
|
50
|
+
file.close
|
51
|
+
end
|
52
|
+
return schema, binary, key
|
25
53
|
end
|
26
54
|
|
27
55
|
def self.parse_schema(path)
|
28
|
-
# Hash of class names to BARE
|
56
|
+
# Hash of class names to BARE types
|
29
57
|
# Eg. types['Customer'] == Bare.i32
|
30
|
-
|
31
|
-
Bare.Schema(
|
58
|
+
parsed = parser(lexer(path))
|
59
|
+
Bare.Schema(parsed)
|
32
60
|
end
|
33
61
|
|
34
62
|
def self.Schema(hash)
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
class Schema
|
39
|
-
def ==(otherSchema)
|
40
|
-
return false unless otherSchema.is_a?(Bare::Schema)
|
41
|
-
@types == otherSchema.types
|
42
|
-
end
|
43
|
-
|
44
|
-
def types
|
45
|
-
@types
|
46
|
-
end
|
47
|
-
|
48
|
-
def [](key)
|
49
|
-
return @types[key]
|
50
|
-
end
|
51
|
-
|
52
|
-
def initialize(types)
|
53
|
-
@types = types
|
54
|
-
@types.keys.each do |key|
|
55
|
-
if @types[key].is_a?(Symbol)
|
56
|
-
@types[key] = @types[@types[key]]
|
57
|
-
else
|
58
|
-
# Users may use symbols to reference not yet defined types
|
59
|
-
# here we recursively call our bare classes to finalize their types
|
60
|
-
# replacing Symbols like :SomeType with a reference to the other type
|
61
|
-
@types[key].finalize_references(@types)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
63
|
+
BareTypes::Schema.new(hash)
|
65
64
|
end
|
66
65
|
|
67
66
|
# These classes are wrapped in methods for ergonomics.
|
@@ -162,7 +161,4 @@ class Bare
|
|
162
161
|
def self.Enum(*opts)
|
163
162
|
return BareTypes::Enum.new(*opts)
|
164
163
|
end
|
165
|
-
|
166
|
-
|
167
164
|
end
|
168
|
-
|
data/lib/dfs.rb
ADDED
data/lib/exceptions.rb
CHANGED
@@ -5,6 +5,24 @@ class BareException < StandardError
|
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
8
|
+
class InvalidBool < BareException
|
9
|
+
def initialize(msg = nil)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class CircularSchema < BareException
|
15
|
+
def initialize(msg = nil)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class ReferenceException < BareException
|
21
|
+
def initialize(msg=nil)
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
8
26
|
class FixedDataSizeWrong < BareException
|
9
27
|
def initialize(msg=nil)
|
10
28
|
super
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative '../bare-rb'
|
2
|
+
require_relative './monkey_patch'
|
3
|
+
require_relative './grammar_util'
|
4
|
+
|
5
|
+
def get_type(depth, names = [], can_be_symbol = true)
|
6
|
+
if names.size == 0
|
7
|
+
can_be_symbol = false
|
8
|
+
end
|
9
|
+
terminators = [BareTypes::Data, BareTypes::DataFixedLen,
|
10
|
+
BareTypes::U8, BareTypes::U16, BareTypes::U32, BareTypes::U64,
|
11
|
+
BareTypes::I8, BareTypes::I16, BareTypes::I32, BareTypes::I64,
|
12
|
+
BareTypes::F32, BareTypes::F64]
|
13
|
+
aggregates = [BareTypes::Array, BareTypes::ArrayFixedLen,
|
14
|
+
BareTypes::Struct]
|
15
|
+
|
16
|
+
all = terminators + aggregates
|
17
|
+
|
18
|
+
# 1/5 changes of a reference
|
19
|
+
if rand(5) == 0 && names.size != 1 && can_be_symbol
|
20
|
+
names[rand(names.size)]
|
21
|
+
elsif depth >= 10 # if depth >= 10 only use terminating types
|
22
|
+
all[rand(terminators.size)].make(depth + 1, names)
|
23
|
+
else
|
24
|
+
all[rand(all.size)].make(depth + 1, names)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
def create_user_type_name
|
2
|
+
upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
3
|
+
lower = upper.downcase
|
4
|
+
digit = "0123456789"
|
5
|
+
|
6
|
+
name = upper[rand(upper.size)]
|
7
|
+
loop do
|
8
|
+
if rand(50) < 5
|
9
|
+
break
|
10
|
+
end
|
11
|
+
num = rand(3)
|
12
|
+
if num == 0
|
13
|
+
name << upper[rand(upper.size)]
|
14
|
+
elsif num == 1
|
15
|
+
name << lower[rand(lower.size)]
|
16
|
+
else
|
17
|
+
name << digit[rand(digit.size)]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
name.to_sym
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,262 @@
|
|
1
|
+
require_relative '../bare-rb'
|
2
|
+
require_relative './grammar_util'
|
3
|
+
|
4
|
+
# 10MB max data size
|
5
|
+
DATA_MAX_SIZE = 30000
|
6
|
+
ARRAY_MAX_SIZE = 40
|
7
|
+
STRUCT_FIELDS_MAX = 5
|
8
|
+
|
9
|
+
# Monkey patch every bare class to include make and create_input
|
10
|
+
# make - a factory to create a random variant of the bare class
|
11
|
+
# create_input - creates an input that could be used with Bare.Encode for this type
|
12
|
+
|
13
|
+
class BareTypes::Reference
|
14
|
+
def create_input
|
15
|
+
self.ref.create_input
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.make(depth, names)
|
19
|
+
self.ref.make(depth, names)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# region Integers
|
24
|
+
class BareTypes::U8
|
25
|
+
def self.make(depth, names)
|
26
|
+
BareTypes::U8.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_input
|
30
|
+
rand(256)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class BareTypes::U16
|
35
|
+
def self.make(depth, names)
|
36
|
+
BareTypes::U16.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_input
|
40
|
+
rand(2 ** 16)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class BareTypes::U32
|
45
|
+
def self.make(depth, names)
|
46
|
+
BareTypes::U32.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_input
|
50
|
+
rand(2 ** 32)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class BareTypes::U64
|
55
|
+
def self.make(depth, names)
|
56
|
+
BareTypes::U64.new
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_input
|
60
|
+
rand(2 ** 64)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class BareTypes::I8
|
65
|
+
def self.make(depth, names)
|
66
|
+
BareTypes::I8.new
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_input
|
70
|
+
rand(2 ** 8) - (2 ** 7)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class BareTypes::I16
|
75
|
+
def self.make(depth, names)
|
76
|
+
BareTypes::I16.new
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_input
|
80
|
+
rand(2 ** 16) - (2 ** 15)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class BareTypes::I32
|
85
|
+
def self.make(depth, names)
|
86
|
+
BareTypes::I32.new
|
87
|
+
end
|
88
|
+
|
89
|
+
def create_input
|
90
|
+
rand(2 ** 32) - (2 ** 31)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class BareTypes::I64
|
95
|
+
def self.make(depth, names)
|
96
|
+
BareTypes::I64.new
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_input
|
100
|
+
rand(2 ** 64) - (2 ** 63)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# endregion
|
105
|
+
|
106
|
+
#region Floats
|
107
|
+
class BareTypes::F32
|
108
|
+
def self.make(depth, names)
|
109
|
+
self.new
|
110
|
+
end
|
111
|
+
|
112
|
+
def create_input
|
113
|
+
float = nil
|
114
|
+
loop do
|
115
|
+
input = [rand(266), rand(266), rand(266), rand(266)]
|
116
|
+
float = input.pack("cccc").unpack('e')
|
117
|
+
if float[0] == float[0] && !float[0].nan?
|
118
|
+
break
|
119
|
+
end
|
120
|
+
end
|
121
|
+
float[0]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class BareTypes::F64
|
126
|
+
def self.make(depth, names)
|
127
|
+
self.new
|
128
|
+
end
|
129
|
+
|
130
|
+
def create_input
|
131
|
+
float = nil
|
132
|
+
loop do
|
133
|
+
input = [rand(266), rand(266), rand(266), rand(266), rand(266), rand(266), rand(266), rand(266)]
|
134
|
+
float = input.pack("cccccccc").unpack('E')
|
135
|
+
if float[0] == float[0] && !float[0].nan?
|
136
|
+
break
|
137
|
+
end
|
138
|
+
end
|
139
|
+
float[0]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
#endregion
|
144
|
+
|
145
|
+
#region Data
|
146
|
+
class BareTypes::DataFixedLen
|
147
|
+
def self.make(depth, names)
|
148
|
+
length = rand(max = DATA_MAX_SIZE) + 1
|
149
|
+
self.new(length)
|
150
|
+
end
|
151
|
+
|
152
|
+
def create_input
|
153
|
+
# 100 random bytes
|
154
|
+
arr = []
|
155
|
+
0.upto(length - 1).each do |i|
|
156
|
+
arr << i % 256
|
157
|
+
end
|
158
|
+
arr.pack('c*')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class BareTypes::Data
|
163
|
+
def self.make(depth, names)
|
164
|
+
self.new
|
165
|
+
end
|
166
|
+
|
167
|
+
def create_input
|
168
|
+
arr = []
|
169
|
+
0.upto(rand(DATA_MAX_SIZE)).each do |i|
|
170
|
+
arr << i % 256
|
171
|
+
end
|
172
|
+
arr.pack('c*')
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
#endregion
|
177
|
+
|
178
|
+
#region Array
|
179
|
+
class BareTypes::Array
|
180
|
+
def self.make(depth, names)
|
181
|
+
BareTypes::Array.new(get_type(depth + 1, names))
|
182
|
+
end
|
183
|
+
|
184
|
+
def create_input
|
185
|
+
count = rand(ARRAY_MAX_SIZE) + 1
|
186
|
+
arr = []
|
187
|
+
0.upto(count) do
|
188
|
+
arr << @type.create_input
|
189
|
+
end
|
190
|
+
arr
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
class BareTypes::ArrayFixedLen
|
195
|
+
def self.make(depth, names)
|
196
|
+
self.new(get_type(depth + 1, names,), rand(ARRAY_MAX_SIZE) + 1)
|
197
|
+
end
|
198
|
+
|
199
|
+
def create_input
|
200
|
+
arr = []
|
201
|
+
0.upto(@size - 1) do
|
202
|
+
arr << @type.create_input
|
203
|
+
end
|
204
|
+
arr
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
#endregion
|
209
|
+
|
210
|
+
#region Agg Types
|
211
|
+
|
212
|
+
class BareTypes::Struct
|
213
|
+
def self.make(depth, names)
|
214
|
+
hash = {}
|
215
|
+
0.upto(rand(STRUCT_FIELDS_MAX) + 1) do
|
216
|
+
hash[create_user_type_name.to_sym] = get_type(depth + 1, names)
|
217
|
+
end
|
218
|
+
self.new(hash)
|
219
|
+
end
|
220
|
+
|
221
|
+
def create_input
|
222
|
+
input = {}
|
223
|
+
@mapping.keys.each do |name|
|
224
|
+
input[name] = @mapping[name].create_input
|
225
|
+
end
|
226
|
+
input
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# endregion
|
231
|
+
|
232
|
+
class BareTypes::Schema
|
233
|
+
def create_input
|
234
|
+
input = {}
|
235
|
+
@types.each do |key, type|
|
236
|
+
input[key] = type.create_input
|
237
|
+
end
|
238
|
+
input
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.make
|
242
|
+
schema = nil
|
243
|
+
loop do
|
244
|
+
names = []
|
245
|
+
schema = {}
|
246
|
+
0.upto(rand(10)+1) do
|
247
|
+
names << create_user_type_name.to_sym
|
248
|
+
end
|
249
|
+
names.each do |name|
|
250
|
+
without_this_name = names.select { |n| n != name }
|
251
|
+
schema[name] = get_type(0, without_this_name, false)
|
252
|
+
end
|
253
|
+
begin
|
254
|
+
schema = Bare.Schema(schema)
|
255
|
+
rescue CircularSchema
|
256
|
+
next
|
257
|
+
end
|
258
|
+
break
|
259
|
+
end
|
260
|
+
schema
|
261
|
+
end
|
262
|
+
end
|
data/lib/lexer.rb
CHANGED
@@ -3,7 +3,8 @@ require_relative './exceptions'
|
|
3
3
|
def lexer(path)
|
4
4
|
tokens = []
|
5
5
|
line_num = 0
|
6
|
-
File.open(path)
|
6
|
+
file = File.open(path)
|
7
|
+
file.each do |line|
|
7
8
|
while line.size > 0
|
8
9
|
if /^#/.match(line)
|
9
10
|
break
|
@@ -46,7 +47,7 @@ def lexer(path)
|
|
46
47
|
tokens << match[0].to_i
|
47
48
|
line = line[(match[0].size)..line.size]
|
48
49
|
next
|
49
|
-
elsif match = /^[a-z,A-Z,_][_,a-z,A-Z,0-9]
|
50
|
+
elsif match = /^[a-z,A-Z,_][_,a-z,A-Z,0-9]*/.match(line)
|
50
51
|
tokens << match[0]
|
51
52
|
line = line[(match[0].size)..line.size]
|
52
53
|
elsif /:/.match(line)
|
data/lib/parser.rb
CHANGED
@@ -132,7 +132,7 @@ class Parser
|
|
132
132
|
type = @primitives[tokens[0]]
|
133
133
|
return tokens[1..tokens.size], type
|
134
134
|
elsif @definitions.keys.include?(tokens[0].to_sym) # User defined type
|
135
|
-
return tokens[1..tokens.size],
|
135
|
+
return tokens[1..tokens.size], tokens[0].to_sym
|
136
136
|
elsif tokens[0].is_a?(String) && tokens[0][0].upcase == tokens[0][0] # Not yet defined user type
|
137
137
|
return tokens[1..tokens.size], tokens[0].to_sym
|
138
138
|
else
|