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/lib/lwes/struct.rb CHANGED
@@ -1,172 +1,139 @@
1
- module LWES
2
- class Struct
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
- # creates a new Struct-based class, takes the following
5
- # options hash:
6
- #
7
- # :db - pre-created LWES::TypeDB object
8
- # this is required unless :file is given
9
- # :file - pathname to the ESF file,
10
- # this is required unless :db is given
11
- # :class - Ruby base class name, if the ESF file only has one
12
- # event defined (besides MetaEventInfo), then specifying
13
- # it is optional, otherwise it is required when multiple
14
- # events are defined in the same ESF :file given above
15
- # :parent - parent class or module, the default is 'Object' putting
16
- # the new class in the global namespace.
17
- # :name - event name if it differs from the Ruby base class name
18
- # given (or inferred) above. For DRY-ness, you are
19
- # recommended to keep your event names and Ruby class
20
- # names in sync and not need this option.
21
- # :skip - Array of field names to skip from the Event defininition
22
- # entirely, these could include fields that are only
23
- # implemented by the Listener. This may also be a
24
- # regular expression.
25
- # :defaults - hash of default key -> value pairs to set at
26
- # creation time
27
- #
28
- def self.new(options, &block)
29
- db = options[:db]
30
- db ||= begin
31
- file = options[:file] or
32
- raise ArgumentError, "TypeDB :db or ESF :file missing"
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
- name = options[:name] || klass
49
- parent = options[:parent] || Object
50
- event_def = dump[name.to_sym] or
51
- raise RuntimeError, "#{name.inspect} not defined in #{file}"
52
-
53
- # merge MetaEventInfo fields in
54
- meta_event_info = dump[:MetaEventInfo]
55
- alpha = proc { |a,b| a.first.to_s <=> b.first.to_s }
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
- Array(options[:skip]).each do |x|
65
- if Regexp === x
66
- event_def.delete_if { |(f,_)| x =~ f.to_s }
67
- else
68
- if x.to_sym == :MetaEventInfo
69
- meta_event_info.nil? and
70
- raise RuntimeError, "MetaEventInfo not defined in #{file}"
71
- meta_event_info.each do |(field,_)|
72
- event_def.delete_if { |(f,_)| field == f }
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
- parent = begin
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
- tmp.const_set :TYPE_DB, db
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
- # freeze since emitter.c can segfault if this ever changes
100
- type_list = event_def.map do |(field,type)|
101
- [ field, field.to_s.freeze, type ].freeze
102
- end.freeze
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
- aref_map = tmp.const_set :AREF_MAP, {}
106
- type_list.each_with_index do |(field_sym,field_str,_),idx|
107
- aref_map[field_sym] = aref_map[field_str] = idx
108
- end
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
- tmp.const_set :HAVE_ENCODING,
111
- type_list.include?([ :enc, 'enc', LWES::INT_16 ])
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
- defaults = options[:defaults] || {}
114
- defaults = tmp.const_set :DEFAULTS, defaults.dup
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
- # define a parent-level method, eval is faster than define_method
118
- eval <<EOS
119
- class ::#{tmp.name}
120
- class << self
121
- alias _new new
122
- undef_method :new
123
- def new(*args)
124
- if Hash === (init = args.first)
125
- rv = _new()
126
- DEFAULTS.merge(init).each_pair { |k,v| rv[k] = v }
127
- rv
128
- else
129
- rv = _new(*args)
130
- DEFAULTS.each_pair { |k,v| rv[k] ||= v }
131
- rv
132
- end
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
- # avoid linear scans for large structs, not sure if 50 is a good enough
139
- # threshold but it won't help for anything <= 10 since Ruby (or at least
140
- # MRI) already optimizes those cases
141
- if event_def.size > 50
142
- eval <<EOS
143
- class ::#{tmp.name}
144
- alias __aref []
145
- alias __aset []=
146
- def [](key)
147
- __aref(key.kind_of?(Fixnum) ? key : AREF_MAP[key])
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
- def []=(key, value)
151
- __aset(key.kind_of?(Fixnum) ? key : AREF_MAP[key], value)
152
- end
118
+ def []=(key, value)
119
+ __aset(key.kind_of?(Fixnum) ? key : AREF_MAP[key], value)
120
+ end
153
121
  end
154
122
  EOS
155
- fast_methods = []
156
- event_def.each_with_index do |(fld,type), idx|
157
- next if idx <= 9
158
- if idx != aref_map[fld]
159
- raise LoadError, "event_def corrupt: #{event_def}"
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
- eval("class ::#{tmp.name}; #{fast_methods.join("\n")}\n end")
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
- end
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
- module LWES
2
- class TypeDB
1
+ class LWES::TypeDB
3
2
 
4
- # create LWES::Struct-derived classes based on the contents
5
- # of the TypeDB object. It is possible to place all classes
6
- # into a namespace by specifying the :parent option to point
7
- # to a class or module:
8
- #
9
- # module MyEvents; end
10
- #
11
- # type_db = LWES::TypeDB.new("my_events.esf")
12
- # type_db.create_classes!(:parent => MyEvents)
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
- def create_classes!(options = {})
18
- classes = to_hash.keys - [ :MetaEventInfo ]
19
- classes.sort { |a,b| a.to_s.size <=> b.to_s.size }.map do |klass|
20
- LWES::Struct.new({ :db => self, :class => klass }.merge(options))
21
- end
22
- end
23
-
24
- # :stopdoc:
25
- # avoid GC mis-free-ing nuked objects
26
- def dup
27
- self
28
- end
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.7.0
3
- VERSION = "0.7.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.7.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 API for the Light Weight Event System}
8
- s.homepage = %q{http://www.lwes.org/}
9
- s.extensions = %w(ext/lwes/extconf.rb)
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 = %w(
18
- COPYING
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
@@ -15,8 +15,8 @@ unless defined?(LISTENER_DEFAULTS)
15
15
  }
16
16
  end
17
17
 
18
- private_bin = "ext/lwes/.inst/bin"
19
- if test ?d, private_bin
18
+ private_bin = "ext/lwes_ext/.inst/bin"
19
+ if File.directory? private_bin
20
20
  ENV['PATH'] = "#{private_bin}:#{ENV['PATH']}"
21
21
  end
22
22
 
@@ -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
- e = assert_raises(TypeError) do
136
+ assert_raises(TypeError) do
137
137
  emitter = LWES::Emitter.new(@options)
138
138
  opt = {
139
139
  :t_int16 => -1000,
@@ -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)