reflekt 0.9.7 → 1.0.2
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/Accessor.rb +9 -10
- data/lib/Aggregator.rb +251 -0
- data/lib/Clone.rb +31 -0
- data/lib/Config.rb +42 -0
- data/lib/Control.rb +62 -14
- data/lib/Execution.rb +15 -6
- data/lib/Meta.rb +71 -0
- data/lib/MetaBuilder.rb +84 -0
- data/lib/Reflection.rb +109 -89
- data/lib/Reflekt.rb +118 -90
- data/lib/Renderer.rb +5 -0
- data/lib/Rule.rb +37 -17
- data/lib/RuleSet.rb +72 -42
- data/lib/ShadowStack.rb +9 -10
- data/lib/meta/ArrayMeta.rb +34 -0
- data/lib/meta/BooleanMeta.rb +26 -0
- data/lib/meta/FloatMeta.rb +26 -0
- data/lib/meta/IntegerMeta.rb +26 -0
- data/lib/meta/NullMeta.rb +34 -0
- data/lib/meta/StringMeta.rb +26 -0
- data/lib/rules/ArrayRule.rb +88 -0
- data/lib/rules/BooleanRule.rb +47 -0
- data/lib/rules/FloatRule.rb +40 -11
- data/lib/rules/IntegerRule.rb +36 -10
- data/lib/rules/NullRule.rb +36 -0
- data/lib/rules/StringRule.rb +60 -8
- data/lib/web/README.md +3 -3
- data/lib/web/bundle.js +5 -5
- data/lib/web/gitignore.txt +7 -0
- data/lib/web/index.html +3 -0
- data/lib/web/server.js +45 -49
- metadata +22 -8
- data/lib/Ruler.rb +0 -192
data/lib/Execution.rb
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
################################################################################
|
2
|
+
# A shadow execution.
|
3
|
+
#
|
4
|
+
# @hierachy
|
5
|
+
# 1. Execution <- YOU ARE HERE
|
6
|
+
# 2. Reflection
|
7
|
+
# 3. Meta
|
8
|
+
################################################################################
|
9
|
+
|
1
10
|
class Execution
|
2
11
|
|
3
12
|
attr_accessor :unique_id
|
@@ -17,12 +26,12 @@ class Execution
|
|
17
26
|
##
|
18
27
|
# Create Execution.
|
19
28
|
#
|
20
|
-
# @param
|
21
|
-
# @param
|
22
|
-
# @param Integer
|
23
|
-
# @param
|
29
|
+
# @param object [Object] The calling object.
|
30
|
+
# @param method [Symbol] The calling method.
|
31
|
+
# @param reflect_amount [Integer] The number of reflections to create per execution.
|
32
|
+
# @param stack [ShadowStack] The shadow execution call stack.
|
24
33
|
##
|
25
|
-
def initialize(caller_object, method,
|
34
|
+
def initialize(caller_object, method, reflect_amount, stack)
|
26
35
|
|
27
36
|
@time = Time.now.to_i
|
28
37
|
@unique_id = @time + rand(1..99999)
|
@@ -42,7 +51,7 @@ class Execution
|
|
42
51
|
|
43
52
|
# Reflections.
|
44
53
|
@control = nil
|
45
|
-
@reflections = Array.new(
|
54
|
+
@reflections = Array.new(reflect_amount)
|
46
55
|
|
47
56
|
# State.
|
48
57
|
if @stack.peek() == nil
|
data/lib/Meta.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
################################################################################
|
2
|
+
# Metadata for input and output.
|
3
|
+
#
|
4
|
+
# @pattern Abstract class
|
5
|
+
# @see lib/meta for each meta.
|
6
|
+
#
|
7
|
+
# @hierachy
|
8
|
+
# 1. Execution
|
9
|
+
# 2. Reflection
|
10
|
+
# 3. Meta <- YOU ARE HERE
|
11
|
+
################################################################################
|
12
|
+
|
13
|
+
class Meta
|
14
|
+
|
15
|
+
##
|
16
|
+
# Each meta defines its type.
|
17
|
+
##
|
18
|
+
def initialize()
|
19
|
+
@type = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Each meta loads values.
|
24
|
+
#
|
25
|
+
# @param value [Dynamic]
|
26
|
+
##
|
27
|
+
def load(value)
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Each meta serializes metadata.
|
32
|
+
#
|
33
|
+
# @return [Hash]
|
34
|
+
##
|
35
|
+
def serialize()
|
36
|
+
{}
|
37
|
+
end
|
38
|
+
|
39
|
+
##############################################################################
|
40
|
+
# CLASS
|
41
|
+
##############################################################################
|
42
|
+
|
43
|
+
##
|
44
|
+
# Deserialize metadata.
|
45
|
+
#
|
46
|
+
# @todo Deserialize should create a Meta object.
|
47
|
+
# @todo Require each Meta type to handle its own deserialization.
|
48
|
+
#
|
49
|
+
# @param meta [Hash] The metadata to deserialize.
|
50
|
+
# @param meta [Hash]
|
51
|
+
##
|
52
|
+
def self.deserialize(meta)
|
53
|
+
|
54
|
+
# Convert nil meta into NullMeta.
|
55
|
+
# Meta is nil when there are no @inputs or @output on the method.
|
56
|
+
if meta.nil?
|
57
|
+
return NullMeta.new().serialize()
|
58
|
+
end
|
59
|
+
|
60
|
+
# Symbolize keys.
|
61
|
+
# TODO: Remove once "Fix Rowdb.get(path)" bug fixed.
|
62
|
+
meta = meta.transform_keys(&:to_sym)
|
63
|
+
|
64
|
+
# Symbolize type value.
|
65
|
+
meta[:type] = meta[:type].to_sym
|
66
|
+
|
67
|
+
return meta
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/lib/MetaBuilder.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
################################################################################
|
2
|
+
# Create metadata.
|
3
|
+
#
|
4
|
+
# @pattern Builder
|
5
|
+
# @see lib/meta for each meta.
|
6
|
+
################################################################################
|
7
|
+
|
8
|
+
require 'Meta'
|
9
|
+
# Require all meta.
|
10
|
+
Dir[File.join(__dir__, 'meta', '*.rb')].each { |file| require file }
|
11
|
+
|
12
|
+
class MetaBuilder
|
13
|
+
|
14
|
+
##
|
15
|
+
# Create meta.
|
16
|
+
#
|
17
|
+
# @param value
|
18
|
+
##
|
19
|
+
def self.create(value)
|
20
|
+
|
21
|
+
meta = nil
|
22
|
+
data_type = value.class.to_s
|
23
|
+
|
24
|
+
# Create meta type for matching data type.
|
25
|
+
case data_type
|
26
|
+
when "Array"
|
27
|
+
meta = ArrayMeta.new()
|
28
|
+
when "TrueClass", "FalseClass"
|
29
|
+
meta = BooleanMeta.new()
|
30
|
+
when "Float"
|
31
|
+
meta = FloatMeta.new()
|
32
|
+
when "Integer"
|
33
|
+
meta = IntegerMeta.new()
|
34
|
+
when "String"
|
35
|
+
meta = StringMeta.new()
|
36
|
+
end
|
37
|
+
|
38
|
+
unless meta.nil?
|
39
|
+
meta.load(value)
|
40
|
+
end
|
41
|
+
|
42
|
+
return meta
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Create meta for multiple values.
|
48
|
+
#
|
49
|
+
# @param values
|
50
|
+
##
|
51
|
+
def self.create_many(values)
|
52
|
+
|
53
|
+
meta = []
|
54
|
+
|
55
|
+
values.each do |value|
|
56
|
+
meta << self.create(value)
|
57
|
+
end
|
58
|
+
|
59
|
+
return meta
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# @param data_type [Type]
|
65
|
+
##
|
66
|
+
def self.data_type_to_meta_type(value)
|
67
|
+
|
68
|
+
data_type = value.class
|
69
|
+
|
70
|
+
meta_types = {
|
71
|
+
Array => :array,
|
72
|
+
TrueClass => :bool,
|
73
|
+
FalseClass => :bool,
|
74
|
+
Float => :float,
|
75
|
+
Integer => :int,
|
76
|
+
NilClass => :null,
|
77
|
+
String => :string
|
78
|
+
}
|
79
|
+
|
80
|
+
return meta_types[data_type]
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/lib/Reflection.rb
CHANGED
@@ -1,95 +1,158 @@
|
|
1
|
+
################################################################################
|
2
|
+
# A snapshot of simulated data.
|
3
|
+
#
|
4
|
+
# @note
|
5
|
+
# A reflection's random value is within the bounds of aggregated control rule sets
|
6
|
+
# as well as as the arg type being inputted into the current control reflection.
|
7
|
+
#
|
8
|
+
# @nomenclature
|
9
|
+
# args, inputs/output and meta represent different stages of a value.
|
10
|
+
#
|
11
|
+
# @hierachy
|
12
|
+
# 1. Execution
|
13
|
+
# 2. Reflection <- YOU ARE HERE
|
14
|
+
# 3. Meta
|
15
|
+
#
|
16
|
+
# @status
|
17
|
+
# - :pass [Symbol] The reflection passes the rules.
|
18
|
+
# - :fail [Symbol] The reflection fails the rules or produces a system error.
|
19
|
+
# - :error [Symbol] The control reflection produces a system error.
|
20
|
+
################################################################################
|
21
|
+
|
22
|
+
require 'Clone'
|
23
|
+
require 'MetaBuilder'
|
24
|
+
|
1
25
|
class Reflection
|
2
26
|
|
3
|
-
|
27
|
+
attr_reader :status
|
4
28
|
|
5
29
|
##
|
6
30
|
# Create a Reflection.
|
7
31
|
#
|
8
|
-
# @param
|
9
|
-
# @param
|
10
|
-
# @param
|
32
|
+
# @param execution [Execution] The Execution that created this Reflection.
|
33
|
+
# @param number [Integer] Multiple Reflections can be created per Execution.
|
34
|
+
# @param aggregator [Aggregator] The aggregated RuleSet for this class/method.
|
11
35
|
##
|
12
|
-
def initialize(execution, number,
|
36
|
+
def initialize(execution, number, aggregator)
|
13
37
|
|
14
38
|
@execution = execution
|
15
39
|
@unique_id = execution.unique_id + number
|
16
40
|
@number = number
|
17
41
|
|
18
42
|
# Dependency.
|
19
|
-
@
|
43
|
+
@aggregator = aggregator
|
20
44
|
|
21
45
|
# Caller.
|
22
46
|
@klass = execution.klass
|
23
47
|
@method = execution.method
|
24
48
|
|
25
|
-
#
|
26
|
-
@inputs =
|
49
|
+
# Metadata.
|
50
|
+
@inputs = nil
|
27
51
|
@output = nil
|
28
52
|
|
29
53
|
# Clone the execution's calling object.
|
54
|
+
# TODO: Abstract away into Clone class.
|
30
55
|
@clone = execution.caller_object.clone
|
31
|
-
@clone_id = nil
|
32
56
|
|
33
57
|
# Result.
|
34
58
|
@status = :pass
|
35
59
|
@time = Time.now.to_i
|
60
|
+
@message = nil
|
36
61
|
|
37
62
|
end
|
38
63
|
|
39
64
|
##
|
40
65
|
# Reflect on a method.
|
41
66
|
#
|
42
|
-
# Creates a shadow execution
|
43
|
-
#
|
44
|
-
# @param *args - The method's arguments.
|
45
|
-
#
|
46
|
-
# @return - A reflection hash.
|
67
|
+
# Creates a shadow execution.
|
68
|
+
# @param *args [Dynamic] The method's arguments.
|
47
69
|
##
|
48
70
|
def reflect(*args)
|
49
71
|
|
50
|
-
# Get
|
51
|
-
input_rule_sets = @
|
52
|
-
output_rule_set = @
|
53
|
-
|
54
|
-
# Create deviated arguments.
|
55
|
-
args.each do |arg|
|
56
|
-
case arg
|
57
|
-
when Integer
|
58
|
-
@inputs << rand(999)
|
59
|
-
else
|
60
|
-
@inputs << arg
|
61
|
-
end
|
62
|
-
end
|
72
|
+
# Get aggregated rule sets.
|
73
|
+
input_rule_sets = @aggregator.get_input_rule_sets(@klass, @method)
|
74
|
+
output_rule_set = @aggregator.get_output_rule_set(@klass, @method)
|
63
75
|
|
64
|
-
#
|
65
|
-
|
76
|
+
# When arguments exist.
|
77
|
+
unless args.size == 0
|
66
78
|
|
67
|
-
#
|
79
|
+
# Create random arguments from aggregated rule sets.
|
68
80
|
unless input_rule_sets.nil?
|
69
|
-
|
81
|
+
|
82
|
+
# Base random arguments on the types of the current arguments.
|
83
|
+
if Aggregator.testable?(args, input_rule_sets)
|
84
|
+
|
85
|
+
args = randomize(args, input_rule_sets)
|
86
|
+
|
87
|
+
# TODO: Fallback to argument types from aggregated control rule sets
|
88
|
+
# when arg types not testable or reflect_amount above 3.
|
89
|
+
else
|
70
90
|
@status = :fail
|
71
91
|
end
|
92
|
+
|
72
93
|
end
|
73
94
|
|
95
|
+
# Create metadata for each argument.
|
96
|
+
# TODO: Create metadata for other inputs such as properties on the instance.
|
97
|
+
@inputs = MetaBuilder.create_many(args)
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
# Action method with new/old arguments.
|
102
|
+
begin
|
103
|
+
|
74
104
|
# Run reflection.
|
75
|
-
|
105
|
+
output = @clone.send(@method, *args)
|
106
|
+
@output = MetaBuilder.create(output)
|
76
107
|
|
77
|
-
# Validate output
|
108
|
+
# Validate output against aggregated control rule sets.
|
78
109
|
unless output_rule_set.nil?
|
79
|
-
unless @
|
110
|
+
unless @aggregator.test_output(output, output_rule_set)
|
80
111
|
@status = :fail
|
81
112
|
end
|
82
113
|
end
|
83
114
|
|
84
|
-
# When
|
115
|
+
# When a system error occurs.
|
85
116
|
rescue StandardError => message
|
117
|
+
|
86
118
|
@status = :fail
|
87
119
|
@message = message
|
120
|
+
|
88
121
|
end
|
89
122
|
|
90
123
|
end
|
91
124
|
|
92
|
-
|
125
|
+
##
|
126
|
+
# Create random values for each argument from control reflections.
|
127
|
+
#
|
128
|
+
# @param args [Dynamic] The arguments to create random values for.
|
129
|
+
# @param input_rule_sets [Array] Aggregated rule sets for each argument.
|
130
|
+
#
|
131
|
+
# @return [Dynamic] Random arguments.
|
132
|
+
##
|
133
|
+
def randomize(args, input_rule_sets)
|
134
|
+
|
135
|
+
random_args = []
|
136
|
+
|
137
|
+
args.each_with_index do |arg, arg_num|
|
138
|
+
|
139
|
+
rule_type = Aggregator.value_to_rule_type(arg)
|
140
|
+
agg_rule = input_rule_sets[arg_num].rules[rule_type]
|
141
|
+
|
142
|
+
random_args << agg_rule.random()
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
return random_args
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Get the results of the reflection.
|
152
|
+
#
|
153
|
+
# @return [Hash] Reflection metadata.
|
154
|
+
##
|
155
|
+
def serialize()
|
93
156
|
|
94
157
|
# The ID of the first execution in the ShadowStack.
|
95
158
|
base_id = nil
|
@@ -107,66 +170,23 @@ class Reflection
|
|
107
170
|
:class => @klass,
|
108
171
|
:method => @method,
|
109
172
|
:status => @status,
|
110
|
-
:
|
111
|
-
:
|
112
|
-
:
|
173
|
+
:message => @message,
|
174
|
+
:inputs => nil,
|
175
|
+
:output => nil,
|
113
176
|
}
|
114
177
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
# Normalize inputs.
|
120
|
-
#
|
121
|
-
# @param args - The actual inputs.
|
122
|
-
# @return - A generic inputs representation.
|
123
|
-
##
|
124
|
-
def normalize_input(args)
|
125
|
-
inputs = []
|
126
|
-
args.each do |arg|
|
127
|
-
input = {
|
128
|
-
:type => arg.class.to_s
|
129
|
-
}
|
130
|
-
if (arg.class == Array)
|
131
|
-
input[:count] = arg.count
|
178
|
+
unless @inputs.nil?
|
179
|
+
reflection[:inputs] = []
|
180
|
+
@inputs.each do |meta|
|
181
|
+
reflection[:inputs] << meta.serialize()
|
132
182
|
end
|
133
|
-
inputs << input
|
134
183
|
end
|
135
|
-
inputs
|
136
|
-
end
|
137
184
|
|
138
|
-
|
139
|
-
|
140
|
-
#
|
141
|
-
# @param input - The actual output.
|
142
|
-
# @return - A generic output representation.
|
143
|
-
##
|
144
|
-
def normalize_output(arg)
|
145
|
-
|
146
|
-
input = {
|
147
|
-
:type => arg.class.to_s
|
148
|
-
}
|
149
|
-
|
150
|
-
if (arg.class == Array || arg.class == Hash)
|
151
|
-
input[:count] = arg.count
|
152
|
-
elsif (arg.class == TrueClass || arg.class == FalseClass)
|
153
|
-
input[:type] = :Boolean
|
185
|
+
unless @output.nil?
|
186
|
+
reflection[:output] = @output.serialize()
|
154
187
|
end
|
155
188
|
|
156
|
-
return
|
157
|
-
|
158
|
-
end
|
159
|
-
|
160
|
-
def normalize_value(value)
|
161
|
-
|
162
|
-
unless value.nil?
|
163
|
-
value = value.to_s.gsub(/\r?\n/, " ").to_s
|
164
|
-
if value.length >= 30
|
165
|
-
value = value[0, value.rindex(/\s/,30)].rstrip() + '...'
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
return value
|
189
|
+
return reflection
|
170
190
|
|
171
191
|
end
|
172
192
|
|