omf_ec 6.0.0.pre.4 → 6.0.0.pre.5
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.
- data/bin/omf_ec +75 -57
- data/example/engine_oedl.rb +20 -20
- data/example/engine_test.rb +51 -73
- data/example/test_exp/test00.rb +25 -5
- data/example/test_exp/test01.rb +4 -5
- data/example/test_exp/test02.rb +2 -5
- data/example/test_exp/test03.rb +15 -28
- data/lib/omf_ec/app_definition.rb +3 -3
- data/lib/omf_ec/backward/default_events.rb +13 -8
- data/lib/omf_ec/backward/dsl.rb +4 -36
- data/lib/omf_ec/backward/exp/testbed.rb +7 -7
- data/lib/omf_ec/backward/group.rb +23 -9
- data/lib/omf_ec/context/app_context.rb +40 -9
- data/lib/omf_ec/context/group_context.rb +18 -47
- data/lib/omf_ec/context/net_context.rb +3 -3
- data/lib/omf_ec/dsl.rb +55 -40
- data/lib/omf_ec/experiment.rb +112 -20
- data/lib/omf_ec/experiment_property.rb +222 -0
- data/lib/omf_ec/group.rb +54 -104
- data/lib/omf_ec/version.rb +1 -1
- data/lib/omf_ec.rb +59 -7
- data/omf_ec.gemspec +4 -2
- data/test/omf_ec/dsl_spec.rb +53 -0
- data/test/omf_ec/experiment_property_spec.rb +81 -0
- data/test/omf_ec/experiment_spec.rb +41 -0
- data/test/omf_ec/group_spec.rb +45 -2
- metadata +35 -19
- data/example/test_exp/test04.rb +0 -105
- data/example/test_exp/test05.rb +0 -87
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module OmfEc
|
5
|
+
#
|
6
|
+
# This class defines an Experiment Property, and also holds all of the
|
7
|
+
# Experiment Properties defined for a given experiment.
|
8
|
+
# Most of this implementation is re-used from OMF 5.4
|
9
|
+
#
|
10
|
+
class ExperimentProperty
|
11
|
+
|
12
|
+
# Contains all the experiment properties
|
13
|
+
@@properties = Hashie::Mash.new
|
14
|
+
|
15
|
+
# Holds all observers on any Experiment Property creation
|
16
|
+
@@creation_observers = []
|
17
|
+
|
18
|
+
#
|
19
|
+
# Returns a given property
|
20
|
+
# - name =nameof the property to return
|
21
|
+
#
|
22
|
+
# [Return] a property
|
23
|
+
#
|
24
|
+
def self.[](name)
|
25
|
+
p = @@properties[name.to_s.to_sym]
|
26
|
+
if p.nil?
|
27
|
+
raise OEDLCommandException.new(name,
|
28
|
+
"Unknown experiment property '#{name}'\n\tKnown properties are "+
|
29
|
+
"'#{ExperimentProperty.names.join(', ')}'")
|
30
|
+
end
|
31
|
+
return p
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.[]=(name, val)
|
35
|
+
p = ExperimentProperty[name.to_sym]
|
36
|
+
p.set(val)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.length; @@properties.length end
|
40
|
+
|
41
|
+
# Minitest needs to be able to turn this Class into a string, this is
|
42
|
+
# normally done through the default 'method_missing' of the Classe
|
43
|
+
# but we redefined that... so to run minitest we need to explicitly
|
44
|
+
# define 'to_str' for this Class
|
45
|
+
def self.to_str; "ExperimentProperty" end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Handles missing method, allows to access an existing Experiment
|
49
|
+
# Property with the syntax 'propcontext.propname'
|
50
|
+
#
|
51
|
+
def self.method_missing(name, args = nil)
|
52
|
+
name = name.to_s
|
53
|
+
if setter = (name[-1] == ?=)
|
54
|
+
name.chop!
|
55
|
+
end
|
56
|
+
p = ExperimentProperty[name.to_sym]
|
57
|
+
if setter
|
58
|
+
p.set(args)
|
59
|
+
else
|
60
|
+
return p
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Iterate over all Experiment Properties. The block
|
65
|
+
# will be called with the respective property as single
|
66
|
+
# argument
|
67
|
+
#
|
68
|
+
# - sort_names = if 'true' sort the properties (default: true)
|
69
|
+
# - &block = the block of commands to call
|
70
|
+
#
|
71
|
+
def self.each(sort_names = false, &block)
|
72
|
+
names = @@properties.keys
|
73
|
+
names = names.sort_by {|sym| sym.to_s} if (sort_names)
|
74
|
+
names.each { |n| block.call(@@properties[n]) }
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return an existing Experiment Property, or create a new one
|
78
|
+
#
|
79
|
+
# - name = name of the property to create/return
|
80
|
+
# - value = value to assign to this property
|
81
|
+
# - description = short string description for this property
|
82
|
+
#
|
83
|
+
# [Return] an Experiment Property
|
84
|
+
#
|
85
|
+
def self.create(name, value = nil, description = nil)
|
86
|
+
name = name.to_s
|
87
|
+
# http://stackoverflow.com/questions/4378670/what-is-a-ruby-regex-to-match-a-function-name
|
88
|
+
if /[@$"]/ =~ name.to_sym.inspect
|
89
|
+
raise OEDLCommandException.new("ExperimentProperty.create",
|
90
|
+
"Cannot create property '#{name}', its name is not a valid Ruby name")
|
91
|
+
end
|
92
|
+
p = nil
|
93
|
+
name = name.to_sym
|
94
|
+
if (p = @@properties[name]) != nil
|
95
|
+
p.set(value) if value != nil
|
96
|
+
p.description = description if description != nil
|
97
|
+
else
|
98
|
+
p = ExperimentProperty.new(name, value, description)
|
99
|
+
@@properties[name] = p
|
100
|
+
# Let the observers know that we created a new property
|
101
|
+
@@creation_observers.each { |proc| proc.call(:create, p) }
|
102
|
+
end
|
103
|
+
return p
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Return the names of the all defined Experiment Properties
|
108
|
+
#
|
109
|
+
# [Return] an Array with the names of all defined Experiment Properties
|
110
|
+
#
|
111
|
+
def self.names() return @@properties.keys end
|
112
|
+
|
113
|
+
# Add an observer for any creation of a new Experiment Property
|
114
|
+
#
|
115
|
+
# - proc = block to execute when a new Experiment Property is created
|
116
|
+
#
|
117
|
+
def self.add_observer(&proc) @@creation_observers << proc end
|
118
|
+
|
119
|
+
attr_reader :name, :value, :id
|
120
|
+
attr_accessor :description
|
121
|
+
|
122
|
+
private :initialize
|
123
|
+
|
124
|
+
#
|
125
|
+
# Create a new Experiment Property
|
126
|
+
#
|
127
|
+
# - name = name of the property to create/return
|
128
|
+
# - value = value to assign to this property
|
129
|
+
# - description = short string description for this property
|
130
|
+
#
|
131
|
+
def initialize(name, value = nil, description = nil)
|
132
|
+
@name = name.to_s
|
133
|
+
@description = description
|
134
|
+
@change_observers = Array.new
|
135
|
+
set(value)
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Add a block of command to the list of actions to do
|
140
|
+
# when this property is being changed
|
141
|
+
#
|
142
|
+
# - &block = the block of command to add
|
143
|
+
#
|
144
|
+
def on_change (&block)
|
145
|
+
debug "Somebody bound to me"
|
146
|
+
@change_observers << block
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# Update the value of this Experiment Property
|
151
|
+
#
|
152
|
+
# - value = new value for this property
|
153
|
+
#
|
154
|
+
def set(value)
|
155
|
+
@value = value
|
156
|
+
info "#{name} = #{value.inspect} (#{value.class})"
|
157
|
+
@change_observers.each { |proc| proc.call(value) }
|
158
|
+
end
|
159
|
+
|
160
|
+
# Implicit conversion to String (required for + operator)
|
161
|
+
def to_str() @value.to_s end
|
162
|
+
|
163
|
+
# Explicit conversion to String
|
164
|
+
alias_method :to_s, :to_str
|
165
|
+
|
166
|
+
# Division operator for Integer and Float properties
|
167
|
+
def /(right)
|
168
|
+
if @value.kind_of?(Integer) || @value.kind_of?(Float)
|
169
|
+
return (@value / right)
|
170
|
+
else
|
171
|
+
raise OEDLCommandException.new("/", "Illegal operation, "+
|
172
|
+
"the value of Experiment Property '#{@name}' is not numerical "+
|
173
|
+
"(current value is of type #{value.class})")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Multiplication operator for Integer and Float properties
|
178
|
+
def *(right)
|
179
|
+
if @value.kind_of?(Integer) || @value.kind_of?(Float)
|
180
|
+
return (@value * right)
|
181
|
+
else
|
182
|
+
raise OEDLCommandException.new("*", "Illegal operation, "+
|
183
|
+
"the value of Experiment Property '#{@name}' is not numerical "+
|
184
|
+
"(current value is of type #{value.class})")
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Substraction operator for Integer and Float properties
|
189
|
+
def -(right)
|
190
|
+
if @value.kind_of?(Integer) || @value.kind_of?(Float)
|
191
|
+
return (@value - right)
|
192
|
+
else
|
193
|
+
raise OEDLCommandException.new("-", "Illegal operation, "+
|
194
|
+
"the value of Experiment Property '#{@name}' is not numerical "+
|
195
|
+
"(current value is of type #{value.class})")
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Addition operator for Integer, Float, and String properties
|
200
|
+
def +(right)
|
201
|
+
if @value.kind_of?(Integer) || @value.kind_of?(Float) || @value.kind_of?(String)
|
202
|
+
return (@value + right)
|
203
|
+
else
|
204
|
+
raise OEDLCommandException.new("+", "Illegal operation, "+
|
205
|
+
"The value of Experiment Property '#{@name}' does not support addition "+
|
206
|
+
"(current value is of type #{value.class})")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Explicit Coercion for Integer, Float, and String properties
|
211
|
+
# (allow property to be on the right-hand of an operator such as +)
|
212
|
+
def coerce(other)
|
213
|
+
if @value.kind_of?(Integer) || @value.kind_of?(Float) || @value.kind_of?(String)
|
214
|
+
return other, @value
|
215
|
+
else
|
216
|
+
raise OEDLCommandException.new("coercion", "Illegal operation, "+
|
217
|
+
"The value of Experiment Property '#{@name}' cannot be coerced to allow "+
|
218
|
+
" the requested operation (current value is of type #{value.class})")
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
data/lib/omf_ec/group.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'securerandom'
|
2
|
+
require 'monitor'
|
2
3
|
|
3
4
|
module OmfEc
|
4
5
|
# Group instance used in experiment script
|
@@ -9,12 +10,15 @@ module OmfEc
|
|
9
10
|
# @!attribute members [Array] holding members to be added to group
|
10
11
|
# @!attribute apps [Array] holding applications to be added to group
|
11
12
|
class Group
|
13
|
+
include MonitorMixin
|
14
|
+
|
12
15
|
attr_accessor :name, :id, :net_ifs, :members, :app_contexts
|
16
|
+
attr_reader :topic
|
13
17
|
|
14
18
|
# @param [String] name name of the group
|
15
19
|
# @param [Hash] opts
|
16
20
|
# @option opts [Boolean] :unique Should the group be unique or not, default is true
|
17
|
-
def initialize(name, opts = {})
|
21
|
+
def initialize(name, opts = {}, &block)
|
18
22
|
@opts = {unique: true}.merge!(opts)
|
19
23
|
self.name = name
|
20
24
|
self.id = @opts[:unique] ? SecureRandom.uuid : self.name
|
@@ -22,57 +26,45 @@ module OmfEc
|
|
22
26
|
self.net_ifs = []
|
23
27
|
self.members = []
|
24
28
|
self.app_contexts = []
|
25
|
-
end
|
26
|
-
|
27
|
-
# Add existing resources to the group
|
28
|
-
#
|
29
|
-
# Resources to be added could be a list of resources, groups, or the mixture of both.
|
30
|
-
def add_resource(*names)
|
31
|
-
names.each do |name|
|
32
|
-
# resource to add is a group
|
33
|
-
if OmfEc.exp.groups.any? { |v| v.name == name }
|
34
|
-
self.add_resource(*group(name).members.uniq)
|
35
|
-
else
|
36
|
-
OmfEc.comm.subscribe(name) do |m|
|
37
|
-
unless m.error?
|
38
|
-
# resource with uid: name is available
|
39
|
-
unless OmfEc.exp.state.any? { |v| v[:uid] == name }
|
40
|
-
OmfEc.exp.state << { uid: name }
|
41
|
-
end
|
42
|
-
|
43
|
-
r = OmfEc.comm.get_topic(name)
|
44
29
|
|
45
|
-
|
46
|
-
r = OmfEc.exp.state.find { |v| v[:uid] == i.read_property(:uid) }
|
47
|
-
unless r.nil?
|
48
|
-
i.each_property do |p|
|
49
|
-
key = p.attr('key').to_sym
|
50
|
-
r[key] = i.read_property(key)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
Experiment.instance.process_events
|
54
|
-
end
|
30
|
+
@resource_topics = {}
|
55
31
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
32
|
+
OmfEc.subscribe_and_monitor(id, self, &block)
|
33
|
+
super()
|
34
|
+
end
|
60
35
|
|
61
|
-
|
62
|
-
|
63
|
-
|
36
|
+
def associate_topic(topic)
|
37
|
+
self.synchronize do
|
38
|
+
@topic = topic
|
39
|
+
end
|
40
|
+
end
|
64
41
|
|
65
|
-
|
42
|
+
def associate_resource_topic(name, res_topic)
|
43
|
+
self.synchronize do
|
44
|
+
@resource_topics[name] = res_topic
|
45
|
+
end
|
46
|
+
end
|
66
47
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
Experiment.instance.process_events
|
71
|
-
end
|
48
|
+
def resource_topic(name)
|
49
|
+
@resource_topics[name]
|
50
|
+
end
|
72
51
|
|
73
|
-
|
74
|
-
|
75
|
-
|
52
|
+
# Add existing resources to the group
|
53
|
+
#
|
54
|
+
# Resources to be added could be a list of resources, groups, or the mixture of both.
|
55
|
+
def add_resource(*names)
|
56
|
+
self.synchronize do
|
57
|
+
# Recording membership first, used for ALL_UP event
|
58
|
+
names.each do |name|
|
59
|
+
g = OmfEc.experiment.group(name)
|
60
|
+
if g # resource to add is a group
|
61
|
+
@members += g.members
|
62
|
+
self.add_resource(*g.members.uniq)
|
63
|
+
else
|
64
|
+
@members << name.to_s
|
65
|
+
OmfEc.subscribe_and_monitor(name) do |res|
|
66
|
+
info "Config #{name} to join #{self.name}"
|
67
|
+
res.configure(membership: self.id)
|
76
68
|
end
|
77
69
|
end
|
78
70
|
end
|
@@ -84,73 +76,31 @@ module OmfEc
|
|
84
76
|
# @param [String] name
|
85
77
|
# @param [Hash] opts to be used to create new resources
|
86
78
|
def create_resource(name, opts, &block)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
# Naming convention of child resource group
|
96
|
-
resource_group_name = "#{self.id}_#{opts[:type]}"
|
97
|
-
|
98
|
-
|
99
|
-
unless OmfEc.exp.sub_groups.include?(resource_group_name)
|
100
|
-
OmfEc.exp.sub_groups << resource_group_name
|
101
|
-
|
102
|
-
rg = OmfEc.comm.get_topic(resource_group_name)
|
103
|
-
# Receive status inform message
|
104
|
-
rg.on_message lambda {|m| m.operation == :inform && m.read_content('inform_type') == 'STATUS' && m.context_id.nil? } do |i|
|
105
|
-
r = OmfEc.exp.state.find { |v| v[:uid] == i.read_property(:uid) }
|
106
|
-
unless r.nil?
|
107
|
-
if i.read_property("status_type") == 'APP_EVENT'
|
108
|
-
info "APP_EVENT #{i.read_property('event')} "+
|
109
|
-
"from app #{i.read_property("app")} - msg: #{i.read_property("msg")}"
|
110
|
-
end
|
111
|
-
i.each_property do |p|
|
112
|
-
r[p.attr('key').to_sym] = p.content.ducktype
|
113
|
-
end
|
114
|
-
end
|
115
|
-
Experiment.instance.process_events
|
116
|
-
end
|
117
|
-
|
118
|
-
# Receive failed inform message
|
119
|
-
rg.on_message lambda {|m| m.operation == :inform && m.read_content('inform_type') == 'FAILED' && m.context_id.nil? } do |i|
|
120
|
-
warn "RC reports failure: '#{i.read_content("reason")}'"
|
79
|
+
self.synchronize do
|
80
|
+
raise ArgumentError, "Option :type is required for creating resource" if opts[:type].nil?
|
81
|
+
|
82
|
+
# Make a deep copy of opts in case it contains structures of structures
|
83
|
+
begin
|
84
|
+
opts = Marshal.load ( Marshal.dump(opts.merge(hrn: name)))
|
85
|
+
rescue Exception => e
|
86
|
+
raise "#{e.message} - Could not deep copy opts: '#{opts.inspect}'"
|
121
87
|
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# We create another group topic for new resouce
|
125
|
-
OmfEc.comm.subscribe(resource_group_name, create_if_non_existent: true) do |m|
|
126
|
-
unless m.error?
|
127
88
|
|
128
|
-
|
129
|
-
|
130
|
-
opts.each_pair do |k, v|
|
131
|
-
m.property(k, v)
|
132
|
-
end
|
133
|
-
end
|
89
|
+
# Naming convention of child resource group
|
90
|
+
resource_group_name = "#{self.id}_#{opts[:type].to_s}"
|
134
91
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
block.call if block
|
141
|
-
Experiment.instance.process_events
|
142
|
-
end
|
143
|
-
|
144
|
-
c.on_inform_failed do |i|
|
145
|
-
warn "RC reports failure: '#{i.read_content("reason")}'"
|
146
|
-
end
|
92
|
+
OmfEc.subscribe_and_monitor(resource_group_name) do |res_group|
|
93
|
+
associate_resource_topic(opts[:type].to_s, res_group)
|
94
|
+
# Send create message to group
|
95
|
+
r_type = opts.delete(:type)
|
96
|
+
@topic.create(r_type, opts.merge(membership: resource_group_name))
|
147
97
|
end
|
148
98
|
end
|
149
99
|
end
|
150
100
|
|
151
101
|
# @return [OmfEc::Context::GroupContext]
|
152
102
|
def resources
|
153
|
-
OmfEc::Context::GroupContext.new(group: self
|
103
|
+
OmfEc::Context::GroupContext.new(group: self)
|
154
104
|
end
|
155
105
|
|
156
106
|
include OmfEc::Backward::Group
|
data/lib/omf_ec/version.rb
CHANGED
data/lib/omf_ec.rb
CHANGED
@@ -5,6 +5,7 @@ require 'omf_ec/backward/app_definition'
|
|
5
5
|
require 'omf_ec/backward/default_events'
|
6
6
|
require 'omf_ec/backward/core_ext/array'
|
7
7
|
require "omf_ec/version"
|
8
|
+
require "omf_ec/experiment_property"
|
8
9
|
require "omf_ec/experiment"
|
9
10
|
require "omf_ec/group"
|
10
11
|
require "omf_ec/app_definition"
|
@@ -22,17 +23,68 @@ module OmfEc
|
|
22
23
|
|
23
24
|
alias_method :exp, :experiment
|
24
25
|
|
25
|
-
# Experiment's communicator instance
|
26
|
-
# @return [OmfCommon::Comm]
|
27
|
-
def communicator
|
28
|
-
Experiment.instance.comm
|
29
|
-
end
|
30
|
-
|
31
26
|
# Full path of lib directory
|
32
27
|
def lib_root
|
33
28
|
File.expand_path("../..", "#{__FILE__}/lib")
|
34
29
|
end
|
35
30
|
|
36
|
-
|
31
|
+
def register_default_callback(topic)
|
32
|
+
topic.on_creation_failed do |msg|
|
33
|
+
warn "RC reports creation.failed: '#{msg[:reason]}'"
|
34
|
+
debug msg
|
35
|
+
end
|
36
|
+
|
37
|
+
topic.on_error do |msg|
|
38
|
+
warn "RC reports error: '#{msg[:reason]}'"
|
39
|
+
debug msg
|
40
|
+
end
|
41
|
+
|
42
|
+
topic.on_warn do |msg|
|
43
|
+
warn "RC reports warning: '#{msg[:reason]}'"
|
44
|
+
debug msg
|
45
|
+
end
|
46
|
+
|
47
|
+
topic.on_creation_ok do |msg|
|
48
|
+
debug "Received CREATION.OK via #{topic.id}"
|
49
|
+
info "Resource #{msg[:res_id]} created"
|
50
|
+
|
51
|
+
OmfEc.experiment.add_or_update_resource_state(msg[:uid], msg.properties)
|
52
|
+
|
53
|
+
OmfEc.experiment.process_events
|
54
|
+
end
|
55
|
+
|
56
|
+
topic.on_status do |msg|
|
57
|
+
props = []
|
58
|
+
msg.each_property { |k, v| props << "#{k}: #{v}" }
|
59
|
+
debug "#{topic.id} >> inform: #{props.join(", ")}"
|
60
|
+
|
61
|
+
if msg[:status_type] == 'APP_EVENT'
|
62
|
+
info "APP_EVENT #{msg[:event]} from app #{msg[:app]} - msg: #{msg[:msg]}"
|
63
|
+
end
|
64
|
+
|
65
|
+
OmfEc.experiment.add_or_update_resource_state(msg[:uid], msg.properties)
|
66
|
+
OmfEc.experiment.process_events
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
#TODO: Could we find a better name for this method?
|
71
|
+
def subscribe_and_monitor(topic_id, context_obj = nil, &block)
|
72
|
+
topic = OmfCommon::Comm::Topic[topic_id]
|
73
|
+
if topic.nil?
|
74
|
+
OmfCommon.comm.subscribe(topic_id) do |topic|
|
75
|
+
if topic.error?
|
76
|
+
error "Failed to subscribe #{topic_id}"
|
77
|
+
else
|
78
|
+
info "Subscribed to #{topic_id}"
|
79
|
+
context_obj.associate_topic(topic) if context_obj
|
80
|
+
block.call(context_obj || topic) if block
|
81
|
+
register_default_callback(topic)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
else
|
85
|
+
context_obj.associate_topic(topic) if context_obj
|
86
|
+
block.call(context_obj || topic) if block
|
87
|
+
end
|
88
|
+
end
|
37
89
|
end
|
38
90
|
end
|
data/omf_ec.gemspec
CHANGED
@@ -7,9 +7,11 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = OmfEc::VERSION
|
8
8
|
s.authors = ["NICTA"]
|
9
9
|
s.email = ["omf-user@lists.nicta.com.au"]
|
10
|
-
s.homepage = "
|
10
|
+
s.homepage = "http://omf.mytestbed.net"
|
11
11
|
s.summary = %q{OMF experiment controller}
|
12
12
|
s.description = %q{Experiment controller of OMF, a generic framework for controlling and managing networking testbeds.}
|
13
|
+
s.required_ruby_version = '>= 1.9.3'
|
14
|
+
s.license = 'MIT'
|
13
15
|
|
14
16
|
s.rubyforge_project = "omf_ec"
|
15
17
|
|
@@ -21,5 +23,5 @@ Gem::Specification.new do |s|
|
|
21
23
|
# specify any dependencies here; for example:
|
22
24
|
s.add_development_dependency "minitest", "~> 3.2"
|
23
25
|
s.add_runtime_dependency "omf_common", "~> 6.0.0.pre"
|
24
|
-
s.add_runtime_dependency "gli", "~> 2.
|
26
|
+
s.add_runtime_dependency "gli", "~> 2.5.3"
|
25
27
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'omf_ec/dsl'
|
3
|
+
|
4
|
+
describe OmfEc::DSL do
|
5
|
+
describe "when included" do
|
6
|
+
|
7
|
+
include OmfEc::DSL
|
8
|
+
|
9
|
+
it "must respond to after and every" do
|
10
|
+
respond_to?(:after).must_equal true
|
11
|
+
respond_to?(:every).must_equal true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "must respond to def_property" do
|
15
|
+
@exp = MiniTest::Mock.new
|
16
|
+
@exp.expect(:add_property, true, [String, Object, String])
|
17
|
+
|
18
|
+
OmfEc.stub :experiment, @exp do
|
19
|
+
def_property('name', 'default', 'testing')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "must respond to def_application" do
|
24
|
+
block = proc { 1 }
|
25
|
+
def_application('bob', &block).must_equal 1
|
26
|
+
OmfEc.experiment.app_definitions.key?('bob').must_equal true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "must respond to def_group" do
|
30
|
+
block = proc { 1 }
|
31
|
+
OmfEc.stub :subscribe_and_monitor, true do
|
32
|
+
def_group('bob', &block).must_be_kind_of OmfEc::Group
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "must respond to all_groups iterator" do
|
37
|
+
block = proc { 1 }
|
38
|
+
all_groups(&block)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "must respond to all_groups?" do
|
42
|
+
OmfEc.stub :subscribe_and_monitor, true do
|
43
|
+
OmfEc.experiment.stub :groups, [] do
|
44
|
+
all_groups? { true }.must_equal false
|
45
|
+
end
|
46
|
+
def_group('bob')
|
47
|
+
all_groups? { |g| g.name == 'bob' }.must_equal true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'omf_ec/experiment_property'
|
3
|
+
require 'omf_ec/dsl'
|
4
|
+
include OmfEc::DSL
|
5
|
+
|
6
|
+
describe OmfEc::ExperimentProperty do
|
7
|
+
|
8
|
+
describe "when a new ExperimentProperty is created" do
|
9
|
+
it "must raise an error if it is given an invalid name" do
|
10
|
+
created_properties = 0
|
11
|
+
# Test only a few common invalid name patterns
|
12
|
+
%w(12 a=b a/b 1a .a a.b a?b a!b !a ?a #a $a @a %a).each do |name|
|
13
|
+
begin
|
14
|
+
OmfEc::ExperimentProperty.create(name)
|
15
|
+
created_properties = created_properties + 1
|
16
|
+
rescue Exception => ex
|
17
|
+
ex.must_be_kind_of OEDLCommandException
|
18
|
+
end
|
19
|
+
end
|
20
|
+
created_properties.must_equal 0
|
21
|
+
end
|
22
|
+
|
23
|
+
it "must not create a new property if one already exist with the same name" do
|
24
|
+
size_before = OmfEc::ExperimentProperty.length
|
25
|
+
OmfEc::ExperimentProperty.create('bar','a')
|
26
|
+
OmfEc::ExperimentProperty[:bar].value.must_equal 'a'
|
27
|
+
OmfEc::ExperimentProperty.create('bar','b')
|
28
|
+
OmfEc::ExperimentProperty[:bar].value.must_equal 'b'
|
29
|
+
OmfEc::ExperimentProperty.length.must_equal (size_before + 1)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "must return a properly set ExperimentProperty object" do
|
33
|
+
size_before = OmfEc::ExperimentProperty.length
|
34
|
+
OmfEc::ExperimentProperty.create('foo', 1, 'abc')
|
35
|
+
OmfEc::ExperimentProperty[:foo].name.must_equal 'foo'
|
36
|
+
OmfEc::ExperimentProperty[:foo].value.must_equal 1
|
37
|
+
OmfEc::ExperimentProperty[:foo].description.must_equal 'abc'
|
38
|
+
OmfEc::ExperimentProperty.length.must_equal (size_before + 1)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "must inform all of its observers when its value changes" do
|
42
|
+
value = 2
|
43
|
+
foobar = OmfEc::ExperimentProperty.create('foobar',1)
|
44
|
+
foobar.on_change { |v| v.must_equal value }
|
45
|
+
foobar.on_change { |v| (v*2).must_equal value*2 }
|
46
|
+
OmfEc::ExperimentProperty[:foobar] = value
|
47
|
+
OmfEc::ExperimentProperty[:foobar].value.must_equal value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "when a the Class ExperimentProperty is creating a new property" do
|
52
|
+
it "must inform all of its observers" do
|
53
|
+
size_before = OmfEc::ExperimentProperty.length
|
54
|
+
OmfEc::ExperimentProperty.add_observer do |c,p|
|
55
|
+
p.name.must_equal 'barfoo'
|
56
|
+
p.value.must_equal 123
|
57
|
+
p.description.must_equal 'abc'
|
58
|
+
end
|
59
|
+
OmfEc::ExperimentProperty.create('barfoo', 123, 'abc')
|
60
|
+
OmfEc::ExperimentProperty.length.must_equal (size_before + 1)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "when an operation involves an ExperimentProperty" do
|
65
|
+
it "must return the expected result" do
|
66
|
+
OmfEc::ExperimentProperty[:foo] = 2
|
67
|
+
(OmfEc::ExperimentProperty[:foo] + 1).must_equal 3
|
68
|
+
(1 + OmfEc::ExperimentProperty[:foo]).must_equal 3
|
69
|
+
(OmfEc::ExperimentProperty[:foo] - 1).must_equal 1
|
70
|
+
(1 - OmfEc::ExperimentProperty[:foo]).must_equal -1
|
71
|
+
(OmfEc::ExperimentProperty[:foo] * 2).must_equal 4
|
72
|
+
(2 * OmfEc::ExperimentProperty[:foo]).must_equal 4
|
73
|
+
(OmfEc::ExperimentProperty[:foo] / 1).must_equal 2
|
74
|
+
(2 / OmfEc::ExperimentProperty[:foo]).must_equal 1
|
75
|
+
OmfEc::ExperimentProperty[:bar] = 'a'
|
76
|
+
(OmfEc::ExperimentProperty[:bar] + "b").must_equal 'ab'
|
77
|
+
('b' + OmfEc::ExperimentProperty[:bar]).must_equal 'ba'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|