lwes 0.7.0 → 0.8.0pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/ChangeLog +4 -0
- data/README +10 -40
- data/Rakefile +7 -4
- data/ext/lwes_ext/.gitignore +2 -0
- data/ext/{lwes → lwes_ext}/emitter.c +63 -53
- data/ext/{lwes → lwes_ext}/event.c +121 -67
- data/ext/{lwes → lwes_ext}/extconf.rb +3 -2
- data/ext/lwes_ext/listener.c +165 -0
- data/ext/{lwes → lwes_ext}/lwes-0.23.1.tar.gz +0 -0
- data/ext/{lwes → lwes_ext}/lwes.c +1 -0
- data/ext/{lwes → lwes_ext}/lwes_ruby.h +13 -6
- data/ext/{lwes → lwes_ext}/numeric.c +7 -71
- data/ext/lwes_ext/type_conv.c +99 -0
- data/ext/{lwes → lwes_ext}/type_db.c +18 -30
- data/lib/lwes/class_maker.rb +50 -0
- data/lib/lwes/event.rb +53 -3
- data/lib/lwes/listener.rb +26 -0
- data/lib/lwes/struct.rb +113 -146
- data/lib/lwes/type_db.rb +33 -30
- data/lib/lwes.rb +41 -2
- data/lwes.gemspec +7 -37
- data/test/test_helper.rb +2 -2
- data/test/unit/test_emit_struct.rb +1 -1
- data/test/unit/test_event.rb +80 -0
- data/test/unit/test_listener.rb +82 -0
- data/test/unit/test_struct.rb +7 -3
- data/test/unit/test_type_db.rb +4 -3
- data/test/unit/test_type_db_events.rb +47 -0
- metadata +39 -27
data/lib/lwes/struct.rb
CHANGED
@@ -1,172 +1,139 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# LWES Events in an ESF file can be automatically turned into
|
2
|
+
# Ruby ::Struct-based objects and emitted directly through LWES::Emitter.
|
3
|
+
#
|
4
|
+
# LWES::Struct is may be more memory-efficient and faster if your application
|
5
|
+
# uses all or most of the fields in the event definition. LWES::Event should
|
6
|
+
# be used in cases where many event fields are unused in a definition.
|
7
|
+
class LWES::Struct
|
8
|
+
extend LWES::ClassMaker
|
3
9
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
test ?r, file or
|
34
|
-
raise ArgumentError, "file #{file.inspect} not readable"
|
35
|
-
TypeDB.new(file)
|
36
|
-
end
|
37
|
-
dump = db.to_hash
|
38
|
-
klass = options[:class] || begin
|
39
|
-
# make it easier to deal with single event files
|
40
|
-
events = (dump.keys - [ :MetaEventInfo ])
|
41
|
-
events.size > 1 and
|
42
|
-
raise RuntimeError,
|
43
|
-
"multiple event defs available: #{events.inspect}\n" \
|
44
|
-
"pick one with :class"
|
45
|
-
events.first
|
46
|
-
end
|
10
|
+
# creates a new Struct-based class, takes the following
|
11
|
+
# options hash:
|
12
|
+
#
|
13
|
+
# :db - pre-created LWES::TypeDB object
|
14
|
+
# this is required unless :file is given
|
15
|
+
# :file - pathname to the ESF file,
|
16
|
+
# this is required unless :db is given
|
17
|
+
# :class - Ruby base class name, if the ESF file only has one
|
18
|
+
# event defined (besides MetaEventInfo), then specifying
|
19
|
+
# it is optional, otherwise it is required when multiple
|
20
|
+
# events are defined in the same ESF :file given above
|
21
|
+
# :parent - parent class or module, the default is 'Object' putting
|
22
|
+
# the new class in the global namespace. May be +nil+ for
|
23
|
+
# creating anonymous classes
|
24
|
+
# :name - event name if it differs from the Ruby base class name
|
25
|
+
# given (or inferred) above. For DRY-ness, you are
|
26
|
+
# recommended to keep your event names and Ruby class
|
27
|
+
# names in sync and not need this option.
|
28
|
+
# :skip - Array of field names to skip from the Event defininition
|
29
|
+
# entirely, these could include fields that are only
|
30
|
+
# implemented by the Listener. This may also be a
|
31
|
+
# regular expression.
|
32
|
+
# :defaults - hash of default key -> value pairs to set at
|
33
|
+
# creation time
|
34
|
+
#
|
35
|
+
def self.new(options, &block)
|
36
|
+
db = type_db(options)
|
37
|
+
dump = db.to_hash
|
38
|
+
klass, name, event_def = class_for(options, dump)
|
47
39
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
meta_event_info
|
55
|
-
|
56
|
-
event_def = event_def.sort(&alpha)
|
57
|
-
if meta_event_info
|
58
|
-
seen = event_def.map { |(field, _)| field }
|
59
|
-
meta_event_info.sort(&alpha).each do |field_type|
|
60
|
-
seen.include?(field_type.first) or event_def << field_type
|
61
|
-
end
|
40
|
+
# merge MetaEventInfo fields in
|
41
|
+
meta_event_info = dump[:MetaEventInfo]
|
42
|
+
alpha = proc { |a,b| a.first.to_s <=> b.first.to_s }
|
43
|
+
event_def = event_def.sort(&alpha)
|
44
|
+
if meta_event_info
|
45
|
+
seen = event_def.map { |(field, _)| field }
|
46
|
+
meta_event_info.sort(&alpha).each do |field_type|
|
47
|
+
seen.include?(field_type.first) or event_def << field_type
|
62
48
|
end
|
49
|
+
end
|
63
50
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
74
|
-
else
|
75
|
-
event_def.delete_if { |(f,_)| f == x.to_sym }
|
51
|
+
Array(options[:skip]).each do |x|
|
52
|
+
if Regexp === x
|
53
|
+
event_def.delete_if { |(f,_)| x =~ f.to_s }
|
54
|
+
else
|
55
|
+
if x.to_sym == :MetaEventInfo
|
56
|
+
meta_event_info.nil? and
|
57
|
+
raise RuntimeError, "MetaEventInfo not defined in #{file}"
|
58
|
+
meta_event_info.each do |(field,_)|
|
59
|
+
event_def.delete_if { |(f,_)| field == f }
|
76
60
|
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
tmp = ::Struct.new(*(event_def.map { |(field,_)| field }))
|
81
|
-
components = klass.to_s.split(/::/)
|
82
|
-
components.each_with_index do |component, i|
|
83
|
-
if i == (components.size - 1)
|
84
|
-
tmp = parent.const_set(component, tmp)
|
85
61
|
else
|
86
|
-
|
87
|
-
parent.const_get(component)
|
88
|
-
rescue NameError
|
89
|
-
eval "module #{parent.name}::#{component}; end"
|
90
|
-
parent.const_get(component)
|
91
|
-
end
|
62
|
+
event_def.delete_if { |(f,_)| f == x.to_sym }
|
92
63
|
end
|
93
64
|
end
|
94
|
-
|
95
|
-
tmp.const_set :NAME, name.to_s
|
96
|
-
ed = tmp.const_set :EVENT_DEF, {}
|
97
|
-
event_def.each { |(field,type)| ed[field] = type }
|
65
|
+
end
|
98
66
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
tmp.const_set :TYPE_LIST, type_list
|
67
|
+
tmp = ::Struct.new(*(event_def.map { |(field,_)| field }))
|
68
|
+
set_constants(tmp, db, klass, name, options)
|
69
|
+
ed = tmp.const_set :EVENT_DEF, {}
|
70
|
+
event_def.each { |(field,type)| ed[field] = type }
|
104
71
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
72
|
+
# freeze since emitter.c can segfault if this ever changes
|
73
|
+
type_list = event_def.map do |(field,type)|
|
74
|
+
[ field, field.to_s.freeze, type ].freeze
|
75
|
+
end.freeze
|
76
|
+
tmp.const_set :TYPE_LIST, type_list
|
109
77
|
|
110
|
-
|
111
|
-
|
78
|
+
aref_map = tmp.const_set :AREF_MAP, {}
|
79
|
+
type_list.each_with_index do |(field_sym,field_str,_),idx|
|
80
|
+
aref_map[field_sym] = aref_map[field_str] = idx
|
81
|
+
end
|
112
82
|
|
113
|
-
|
114
|
-
|
115
|
-
tmp.class_eval(&block) if block_given?
|
83
|
+
tmp.const_set :HAVE_ENCODING,
|
84
|
+
type_list.include?([ :enc, 'enc', LWES::INT_16 ])
|
116
85
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
86
|
+
tmp.class_eval(&block) if block_given?
|
87
|
+
|
88
|
+
# define a parent-level method, eval is faster than define_method
|
89
|
+
tmp.class_eval <<EOS
|
90
|
+
class << self
|
91
|
+
alias _new new
|
92
|
+
undef_method :new
|
93
|
+
def new(*args)
|
94
|
+
if Hash === (init = args.first)
|
95
|
+
rv = _new()
|
96
|
+
DEFAULTS.merge(init).each_pair { |k,v| rv[k] = v }
|
97
|
+
rv
|
98
|
+
else
|
99
|
+
rv = _new(*args)
|
100
|
+
DEFAULTS.each_pair { |k,v| rv[k] ||= v }
|
101
|
+
rv
|
133
102
|
end
|
134
103
|
end
|
135
104
|
end
|
136
105
|
EOS
|
137
106
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
end
|
107
|
+
# avoid linear scans for large structs, not sure if 50 is a good enough
|
108
|
+
# threshold but it won't help for anything <= 10 since Ruby (or at least
|
109
|
+
# MRI) already optimizes those cases
|
110
|
+
if event_def.size > 50
|
111
|
+
tmp.class_eval <<EOS
|
112
|
+
alias __aref []
|
113
|
+
alias __aset []=
|
114
|
+
def [](key)
|
115
|
+
__aref(key.kind_of?(Fixnum) ? key : AREF_MAP[key])
|
116
|
+
end
|
149
117
|
|
150
|
-
|
151
|
-
|
152
|
-
|
118
|
+
def []=(key, value)
|
119
|
+
__aset(key.kind_of?(Fixnum) ? key : AREF_MAP[key], value)
|
120
|
+
end
|
153
121
|
end
|
154
122
|
EOS
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
161
|
-
fast_methods << "undef_method :#{fld}, :#{fld}=\n"
|
162
|
-
fast_methods << "\ndef #{fld}; __aref #{idx}; end\n"
|
163
|
-
fast_methods << "\ndef #{fld}=(val); __aset #{idx}, val ; end\n"
|
123
|
+
fast_methods = []
|
124
|
+
event_def.each_with_index do |(fld,_), idx|
|
125
|
+
next if idx <= 9
|
126
|
+
if idx != aref_map[fld]
|
127
|
+
raise LoadError, "event_def corrupt: #{event_def}"
|
164
128
|
end
|
165
|
-
|
166
|
-
|
129
|
+
fast_methods << "undef_method :#{fld}, :#{fld}=\n"
|
130
|
+
fast_methods << "\ndef #{fld}; __aref #{idx}; end\n"
|
131
|
+
fast_methods << "\ndef #{fld}=(val); __aset #{idx}, val ; end\n"
|
167
132
|
end
|
168
133
|
|
169
|
-
tmp
|
170
|
-
|
134
|
+
tmp.class_eval fast_methods.join("\n")
|
135
|
+
end
|
136
|
+
|
137
|
+
tmp
|
171
138
|
end
|
172
139
|
end
|
data/lib/lwes/type_db.rb
CHANGED
@@ -1,35 +1,38 @@
|
|
1
|
-
|
2
|
-
class TypeDB
|
1
|
+
class LWES::TypeDB
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def clone
|
31
|
-
self
|
3
|
+
# create LWES::Struct-derived classes based on the contents
|
4
|
+
# of the TypeDB object. It is possible to place all classes
|
5
|
+
# into a namespace by specifying the :parent option to point
|
6
|
+
# to a class or module:
|
7
|
+
#
|
8
|
+
# module MyEvents; end
|
9
|
+
#
|
10
|
+
# type_db = LWES::TypeDB.new("my_events.esf")
|
11
|
+
# type_db.create_classes!(:parent => MyEvents)
|
12
|
+
# type_db.create_classes!(:sparse => true)
|
13
|
+
#
|
14
|
+
# Assuming you had "Event1" and "Event2" defined in your "my_events.esf"
|
15
|
+
# file, then the classes MyEvents::Event1 and MyEvents::Event2 should
|
16
|
+
# now be accessible.
|
17
|
+
#
|
18
|
+
# :parent - parent class or module, the default is 'Object' putting
|
19
|
+
# the new class in the global namespace. May be +nil+ for
|
20
|
+
# creating anonymous classes.
|
21
|
+
# :sparse - If +true+, this will subclass from LWES::Event instead of
|
22
|
+
# :Struct for better memory efficiency when dealing with
|
23
|
+
# events with many unused fields. Default is +false+.
|
24
|
+
def create_classes!(options = {})
|
25
|
+
classes = to_hash.keys - [ :MetaEventInfo ]
|
26
|
+
classes.sort { |a,b| a.to_s.size <=> b.to_s.size }.map! do |klass|
|
27
|
+
opts = { :db => self, :class => klass }.merge!(options)
|
28
|
+
opts[:sparse] ? LWES::Event.subclass(opts) : LWES::Struct.new(opts)
|
32
29
|
end
|
30
|
+
end
|
33
31
|
|
32
|
+
# :stopdoc:
|
33
|
+
# avoid GC mis-free-ing nuked objects
|
34
|
+
def dup
|
35
|
+
self
|
34
36
|
end
|
37
|
+
alias clone dup
|
35
38
|
end
|
data/lib/lwes.rb
CHANGED
@@ -1,10 +1,49 @@
|
|
1
|
+
##
|
2
|
+
# = Light Weight Event System bindings for Ruby
|
3
|
+
#
|
4
|
+
# require 'lwes'
|
5
|
+
#
|
6
|
+
# # create an Emitter which may be used for the lifetime of your process
|
7
|
+
# emitter = LWES::Emitter.new(:address => '224.1.1.11',
|
8
|
+
# :port => 12345,
|
9
|
+
# :heartbeat => 30, # nil to disable
|
10
|
+
# :ttl => 1) # nil for default TTL(3)
|
11
|
+
#
|
12
|
+
# # parse your ESF file at startup, the example below assumes you
|
13
|
+
# # have "Event1" and "Event2" defined in your ESF file:
|
14
|
+
# type_db = LWES::TypeDB.new("my_events.esf")
|
15
|
+
#
|
16
|
+
# # create classes to use, by default and to encourage DRY-ness,
|
17
|
+
# # we map each event in the ESF file to a class
|
18
|
+
# # Optionally, you may specify :parent => module/namespace
|
19
|
+
# type_db.create_classes! :parent => MyApp
|
20
|
+
#
|
21
|
+
# # inside your application, you may now do this to slowly build up
|
22
|
+
# # fields for the event
|
23
|
+
# my_event = MyApp::Event1.new
|
24
|
+
# my_event.started = Time.now.to_i
|
25
|
+
# my_event.remote_addr = "192.168.0.1"
|
26
|
+
# # ...do a lot of stuff here in between...
|
27
|
+
# my_event.field1 = value1
|
28
|
+
# my_event.field2 = value2
|
29
|
+
# my_event.field3 = value3
|
30
|
+
# my_event.finished = Time.now.to_i
|
31
|
+
# emitter << my_event
|
32
|
+
#
|
33
|
+
# # Alternatively, if you know ahead of time all the fields you want to
|
34
|
+
# # set for an event, you can emit an event in one step:
|
35
|
+
#
|
36
|
+
# emitter.emit MyApp::Event2, :field1 => value1, :field2 => value2 # ...
|
37
|
+
#
|
1
38
|
module LWES
|
2
|
-
# version of our library, currently 0.
|
3
|
-
VERSION = "0.
|
39
|
+
# version of our library, currently 0.8.0pre1
|
40
|
+
VERSION = "0.8.0pre1"
|
4
41
|
|
42
|
+
autoload :ClassMaker, "lwes/class_maker"
|
5
43
|
autoload :TypeDB, "lwes/type_db"
|
6
44
|
autoload :Struct, "lwes/struct"
|
7
45
|
autoload :Emitter, "lwes/emitter"
|
8
46
|
autoload :Event, "lwes/event"
|
47
|
+
autoload :Listener, "lwes/listener"
|
9
48
|
end
|
10
49
|
require "lwes_ext"
|
data/lwes.gemspec
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = %q{lwes}
|
3
|
-
s.version = "0.
|
3
|
+
s.version = "0.8.0pre1"
|
4
4
|
s.date = Time.now
|
5
5
|
s.authors = ["Erik S. Chang", "Frank Maritato"]
|
6
6
|
s.email = %q{lwes-devel@lists.sourceforge.net}
|
7
|
-
s.summary = %q{Ruby
|
8
|
-
s.homepage = %q{http://
|
9
|
-
s.extensions = %w(ext/
|
7
|
+
s.summary = %q{Ruby bindings for the Light Weight Event System}
|
8
|
+
s.homepage = %q{http://lwes.rubyforge.org/}
|
9
|
+
s.extensions = %w(ext/lwes_ext/extconf.rb)
|
10
10
|
s.description = %q{
|
11
11
|
The LWES Light-Weight Event System is a framework for allowing the exchange of
|
12
12
|
information from many machines to many machines in a controlled, platform
|
@@ -14,39 +14,9 @@ neutral, language neutral way. The exchange of information is done in a
|
|
14
14
|
connectless fashion using multicast or unicast UDP, and using self describing
|
15
15
|
data so that any platform or language can translate it to it's local dialect.
|
16
16
|
}.strip
|
17
|
-
s.files =
|
18
|
-
|
19
|
-
ChangeLog
|
20
|
-
README
|
21
|
-
Rakefile
|
22
|
-
examples/demo.rb
|
23
|
-
examples/my_events.esf
|
24
|
-
ext/lwes/emitter.c
|
25
|
-
ext/lwes/event.c
|
26
|
-
ext/lwes/extconf.rb
|
27
|
-
ext/lwes/lwes.c
|
28
|
-
ext/lwes/lwes_ruby.h
|
29
|
-
ext/lwes/numeric.c
|
30
|
-
ext/lwes/type_db.c
|
31
|
-
lib/lwes.rb
|
32
|
-
lib/lwes/emitter.rb
|
33
|
-
lib/lwes/event.rb
|
34
|
-
lib/lwes/struct.rb
|
35
|
-
lib/lwes/type_db.rb
|
36
|
-
lwes.gemspec
|
37
|
-
test/test_helper.rb
|
38
|
-
test/unit/meta_only.esf
|
39
|
-
test/unit/namespaced.esf
|
40
|
-
test/unit/test1.esf
|
41
|
-
test/unit/test2.esf
|
42
|
-
test/unit/test_emit_struct.rb
|
43
|
-
test/unit/test_emitter.rb
|
44
|
-
test/unit/test_event.rb
|
45
|
-
test/unit/test_struct.rb
|
46
|
-
test/unit/test_type_db.rb
|
47
|
-
) + %w(
|
48
|
-
ext/lwes/lwes-0.23.1.tar.gz
|
49
|
-
)
|
17
|
+
s.files = `git ls-files`.split(/\n/) +
|
18
|
+
%w(ext/lwes_ext/lwes-0.23.1.tar.gz)
|
50
19
|
s.rubyforge_project = 'lwes'
|
51
20
|
s.test_files = s.files.grep(%r{\Atest/unit/test_})
|
21
|
+
s.add_development_dependency(%q<rake-compiler>, [">= 0.7.6"])
|
52
22
|
end
|
data/test/test_helper.rb
CHANGED
@@ -133,7 +133,7 @@ class TestEmitStruct < Test::Unit::TestCase
|
|
133
133
|
|
134
134
|
def test_emit_from_class_bad_type
|
135
135
|
out = lwes_listener do
|
136
|
-
|
136
|
+
assert_raises(TypeError) do
|
137
137
|
emitter = LWES::Emitter.new(@options)
|
138
138
|
opt = {
|
139
139
|
:t_int16 => -1000,
|
data/test/unit/test_event.rb
CHANGED
@@ -66,4 +66,84 @@ class TestEvent < Test::Unit::TestCase
|
|
66
66
|
ensure
|
67
67
|
receiver.close
|
68
68
|
end
|
69
|
+
|
70
|
+
def test_subclass_aset_aref
|
71
|
+
tdb = LWES::TypeDB.new("#{File.dirname(__FILE__)}/test1.esf")
|
72
|
+
tmp = LWES::Event.subclass :name => "Event1", :db => tdb
|
73
|
+
e = tmp.new
|
74
|
+
assert_equal({}, e.to_hash)
|
75
|
+
vals = {
|
76
|
+
:t_bool => true,
|
77
|
+
:t_int16 => -1000,
|
78
|
+
:t_uint16 => 1000,
|
79
|
+
:t_int32 => -64444,
|
80
|
+
:t_uint32 => 64444,
|
81
|
+
:t_int64 => 10_000_000_000,
|
82
|
+
:t_uint64 => 10_000_000_000,
|
83
|
+
:t_ip_addr => "192.168.0.1",
|
84
|
+
:t_string => "STRING",
|
85
|
+
:enc => 0,
|
86
|
+
:st => "ruby",
|
87
|
+
}
|
88
|
+
vals.each do |k,v|
|
89
|
+
assert_nothing_raised { e[k.to_s] = v }
|
90
|
+
assert_equal v, e[k.to_s], e.to_hash.inspect
|
91
|
+
end
|
92
|
+
|
93
|
+
e2 = tmp.new
|
94
|
+
vals.each do |k,v|
|
95
|
+
assert_nothing_raised { e2[k] = v }
|
96
|
+
assert_equal v, e2[k], e2.to_hash.inspect
|
97
|
+
end
|
98
|
+
assert_equal e2.to_hash, e.to_hash
|
99
|
+
e3 = tmp.new
|
100
|
+
vals.each do |k,v|
|
101
|
+
assert_nothing_raised { e3.__send__ "#{k}=", v }
|
102
|
+
assert_equal v, e3.__send__(k), e3.to_hash.inspect
|
103
|
+
end
|
104
|
+
assert_equal e3.to_hash, e.to_hash
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_merge
|
108
|
+
tdb = LWES::TypeDB.new("#{File.dirname(__FILE__)}/test1.esf")
|
109
|
+
tmp = LWES::Event.subclass :name => "Event1", :db => tdb
|
110
|
+
e = tmp.new.merge :t_string => "merged"
|
111
|
+
assert_equal "merged", e.t_string
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_init_copy
|
115
|
+
tdb = LWES::TypeDB.new("#{File.dirname(__FILE__)}/test1.esf")
|
116
|
+
tmp = LWES::Event.subclass :name => "Event1", :db => tdb
|
117
|
+
a = tmp.new
|
118
|
+
b = a.dup
|
119
|
+
assert_equal a.to_hash, b.to_hash
|
120
|
+
a.t_string = "HELLO"
|
121
|
+
assert_equal "HELLO", a.t_string
|
122
|
+
assert_nil b.t_string
|
123
|
+
c = a.dup
|
124
|
+
assert_equal "HELLO", c.t_string
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_emit_receive_subclassed
|
128
|
+
receiver = UDPSocket.new
|
129
|
+
receiver.bind(nil, @options[:port])
|
130
|
+
emitter = LWES::Emitter.new(@options)
|
131
|
+
tmp = { :t_string => 'hello' }
|
132
|
+
|
133
|
+
tdb = LWES::TypeDB.new("#{File.dirname(__FILE__)}/test1.esf")
|
134
|
+
ev1 = LWES::Event.subclass :name => "Event1", :db => tdb
|
135
|
+
emitter.emit "Event1", tmp
|
136
|
+
buf, _ = receiver.recvfrom(65536)
|
137
|
+
parsed = LWES::Event.parse(buf)
|
138
|
+
assert_instance_of ev1, parsed
|
139
|
+
assert_equal parsed.to_hash, ev1.new(tmp).to_hash
|
140
|
+
ensure
|
141
|
+
receiver.close
|
142
|
+
end
|
143
|
+
|
144
|
+
def teardown
|
145
|
+
new_classes = LWES::Event::CLASSES
|
146
|
+
new_classes.each_key { |k| Object.__send__ :remove_const, k.to_sym }
|
147
|
+
new_classes.clear
|
148
|
+
end
|
69
149
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "#{File.expand_path(File.dirname(__FILE__))}/../test_helper"
|
2
|
+
|
3
|
+
class TestListener < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@options = LISTENER_DEFAULTS.dup
|
6
|
+
@listener = @emitter = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def teardown
|
10
|
+
assert_nil(@listener.close) if @listener
|
11
|
+
assert_nil(@emitter.close) if @emitter
|
12
|
+
LWES::Event::CLASSES.clear
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_listen_and_close
|
16
|
+
listener = LWES::Listener.new @options.merge(:iface => nil)
|
17
|
+
assert_nil listener.close
|
18
|
+
assert_raises(IOError) { listener.close }
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_listen_and_close_with_iface
|
22
|
+
listener = LWES::Listener.new @options
|
23
|
+
assert_nil listener.close
|
24
|
+
assert_raises(IOError) { listener.close }
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_listen_emit_recv_and_close
|
28
|
+
@listener = LWES::Listener.new @options
|
29
|
+
@emitter = LWES::Emitter.new @options
|
30
|
+
@emitter.emit("E1", { :hello => "WORLD"})
|
31
|
+
event = @listener.recv.to_hash
|
32
|
+
assert_equal "E1", event[:name]
|
33
|
+
assert event[:SenderPort] > 0
|
34
|
+
assert event[:ReceiptTime] > (Time.now.to_i * 1000)
|
35
|
+
assert_equal "WORLD", event[:hello]
|
36
|
+
assert_equal @options[:address], event[:SenderIP]
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_listen_emit_custom_event_recv_and_close
|
40
|
+
@listener = LWES::Listener.new @options
|
41
|
+
tdb = LWES::TypeDB.new("#{File.dirname(__FILE__)}/test1.esf")
|
42
|
+
tmp = LWES::Event.subclass :name => "Event1", :db => tdb, :parent => nil
|
43
|
+
@emitter = LWES::Emitter.new @options
|
44
|
+
@emitter << tmp.new(:t_string => "HI")
|
45
|
+
event = @listener.recv
|
46
|
+
assert_instance_of tmp, event
|
47
|
+
assert_equal "HI", event.t_string
|
48
|
+
assert event.SenderPort > 0
|
49
|
+
assert event.ReceiptTime > (Time.now.to_i * 1000)
|
50
|
+
assert_equal @options[:address], event.SenderIP
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_listen_recv_timeout
|
54
|
+
@listener = LWES::Listener.new @options
|
55
|
+
t0 = Time.now.to_f
|
56
|
+
event = @listener.recv 10
|
57
|
+
assert_nil event
|
58
|
+
delta = Time.now.to_f - t0
|
59
|
+
assert(delta >= 0.01, "delta=#{delta}")
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_listen_each_signal
|
63
|
+
signaled = false
|
64
|
+
handler = trap(:USR1) { signaled = true }
|
65
|
+
@listener = LWES::Listener.new @options
|
66
|
+
@emitter = LWES::Emitter.new @options
|
67
|
+
tmp = []
|
68
|
+
thr = Thread.new do
|
69
|
+
sleep 0.1
|
70
|
+
Process.kill :USR1, $$
|
71
|
+
@emitter.emit("E1", :hello => "WORLD")
|
72
|
+
:OK
|
73
|
+
end
|
74
|
+
@listener.each { |event| tmp << event and break }
|
75
|
+
assert thr.join
|
76
|
+
assert_equal :OK, thr.value
|
77
|
+
assert_equal 1, tmp.size
|
78
|
+
assert_equal "WORLD", tmp[0].to_hash[:hello]
|
79
|
+
ensure
|
80
|
+
trap(:USR1, handler)
|
81
|
+
end
|
82
|
+
end if LWES::Listener.method_defined?(:recv)
|