io-reactor 0.05 → 1.0.4

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.
@@ -0,0 +1,191 @@
1
+ #
2
+ # Rake tasklib for testing tasks
3
+ # $Id: testing.rb 14 2008-07-23 23:16:30Z deveiant $
4
+ #
5
+ # Authors:
6
+ # * Michael Granger <ged@FaerieMUD.org>
7
+ #
8
+
9
+ COVERAGE_MINIMUM = 85.0 unless defined?( COVERAGE_MINIMUM )
10
+ SPEC_FILES = [] unless defined?( SPEC_FILES )
11
+ TEST_FILES = [] unless defined?( TEST_FILES )
12
+
13
+ COMMON_SPEC_OPTS = ['-c', '-f', 's'] unless defined?( COMMON_SPEC_OPTS )
14
+
15
+ COVERAGE_TARGETDIR = BASEDIR + 'coverage' unless defined?( COVERAGE_TARGETDIR )
16
+ RCOV_EXCLUDES = 'spec,tests,/Library/Ruby,/var/lib,/usr/local/lib' unless
17
+ defined?( RCOV_EXCLUDES )
18
+
19
+
20
+ desc "Run all defined tests"
21
+ task :test do
22
+ unless SPEC_FILES.empty?
23
+ log "Running specs"
24
+ Rake::Task['spec:quiet'].invoke
25
+ end
26
+
27
+ unless TEST_FILES.empty?
28
+ log "Running unit tests"
29
+ Rake::Task[:unittests].invoke
30
+ end
31
+ end
32
+
33
+
34
+ ### RSpec specifications
35
+ begin
36
+ gem 'rspec', '>= 1.1.3'
37
+ require 'spec/rake/spectask'
38
+
39
+ ### Task: spec
40
+ Spec::Rake::SpecTask.new( :spec ) do |task|
41
+ task.spec_files = SPEC_FILES
42
+ task.libs += [LIBDIR]
43
+ task.spec_opts = COMMON_SPEC_OPTS
44
+ end
45
+
46
+
47
+ namespace :spec do
48
+ desc "Run rspec every time there's a change to one of the files"
49
+ task :autotest do
50
+ require 'autotest/rspec'
51
+
52
+ autotester = Autotest::Rspec.new
53
+ autotester.exceptions = %r{\.svn|\.skel}
54
+ autotester.run
55
+ end
56
+
57
+
58
+ desc "Generate quiet output"
59
+ Spec::Rake::SpecTask.new( :quiet ) do |task|
60
+ task.spec_files = SPEC_FILES
61
+ task.spec_opts = ['-f', 'p', '-D']
62
+ end
63
+
64
+ desc "Generate HTML output for a spec run"
65
+ Spec::Rake::SpecTask.new( :html ) do |task|
66
+ task.spec_files = SPEC_FILES
67
+ task.spec_opts = ['-f','h', '-D']
68
+ end
69
+
70
+ desc "Generate plain-text output for a CruiseControl.rb build"
71
+ Spec::Rake::SpecTask.new( :text ) do |task|
72
+ task.spec_files = SPEC_FILES
73
+ task.spec_opts = ['-f','p']
74
+ end
75
+ end
76
+ rescue LoadError => err
77
+ task :no_rspec do
78
+ $stderr.puts "Specification tasks not defined: %s" % [ err.message ]
79
+ end
80
+
81
+ task :spec => :no_rspec
82
+ namespace :spec do
83
+ task :autotest => :no_rspec
84
+ task :quiet => :no_rspec
85
+ task :html => :no_rspec
86
+ task :text => :no_rspec
87
+ end
88
+ end
89
+
90
+
91
+ ### Test::Unit tests
92
+ begin
93
+ require 'rake/testtask'
94
+
95
+ Rake::TestTask.new( :unittests ) do |task|
96
+ task.libs += [LIBDIR]
97
+ task.test_files = TEST_FILES
98
+ task.verbose = true
99
+ end
100
+
101
+ rescue LoadError => err
102
+ task :no_test do
103
+ $stderr.puts "Test tasks not defined: %s" % [ err.message ]
104
+ end
105
+
106
+ task :unittests => :no_rspec
107
+ end
108
+
109
+
110
+ ### RCov (via RSpec) tasks
111
+ begin
112
+ gem 'rcov'
113
+ gem 'rspec', '>= 1.1.3'
114
+
115
+ ### Task: coverage (via RCov)
116
+ ### Task: rcov
117
+ desc "Build test coverage reports"
118
+ unless SPEC_FILES.empty?
119
+ Spec::Rake::SpecTask.new( :coverage ) do |task|
120
+ task.spec_files = SPEC_FILES
121
+ task.libs += [LIBDIR]
122
+ task.spec_opts = ['-f', 'p', '-b']
123
+ task.rcov_opts = RCOV_OPTS
124
+ task.rcov = true
125
+ end
126
+ end
127
+ unless TEST_FILES.empty?
128
+ require 'rcov/rcovtask'
129
+
130
+ Rcov::RcovTask.new do |task|
131
+ task.libs += [LIBDIR]
132
+ task.test_files = TEST_FILES
133
+ task.verbose = true
134
+ task.rcov_opts = RCOV_OPTS
135
+ end
136
+ end
137
+
138
+
139
+ task :rcov => [:coverage] do; end
140
+
141
+ ### Other coverage tasks
142
+ namespace :coverage do
143
+ desc "Generate a detailed text coverage report"
144
+ Spec::Rake::SpecTask.new( :text ) do |task|
145
+ task.spec_files = SPEC_FILES
146
+ task.rcov_opts = RCOV_OPTS + ['--text-report']
147
+ task.rcov = true
148
+ end
149
+
150
+ desc "Show differences in coverage from last run"
151
+ Spec::Rake::SpecTask.new( :diff ) do |task|
152
+ task.spec_files = SPEC_FILES
153
+ task.rcov_opts = ['--text-coverage-diff']
154
+ task.rcov = true
155
+ end
156
+
157
+ ### Task: verify coverage
158
+ desc "Build coverage statistics"
159
+ VerifyTask.new( :verify => :rcov ) do |task|
160
+ task.threshold = COVERAGE_MINIMUM
161
+ end
162
+
163
+ desc "Run RCov in 'spec-only' mode to check coverage from specs"
164
+ Spec::Rake::SpecTask.new( :speconly ) do |task|
165
+ task.spec_files = SPEC_FILES
166
+ task.rcov_opts = ['--exclude', RCOV_EXCLUDES, '--text-report', '--save']
167
+ task.rcov = true
168
+ end
169
+ end
170
+
171
+ task :clobber_coverage do
172
+ rmtree( COVERAGE_TARGETDIR )
173
+ end
174
+
175
+ rescue LoadError => err
176
+ task :no_rcov do
177
+ $stderr.puts "Coverage tasks not defined: RSpec+RCov tasklib not available: %s" %
178
+ [ err.message ]
179
+ end
180
+
181
+ task :coverage => :no_rcov
182
+ task :clobber_coverage
183
+ task :rcov => :no_rcov
184
+ namespace :coverage do
185
+ task :text => :no_rcov
186
+ task :diff => :no_rcov
187
+ end
188
+ task :verify => :no_rcov
189
+ end
190
+
191
+
@@ -0,0 +1,64 @@
1
+ #####################################################################
2
+ ### S U B V E R S I O N T A S K S A N D H E L P E R S
3
+ #####################################################################
4
+
5
+ require 'rake/tasklib'
6
+
7
+ #
8
+ # Work around the inexplicable behaviour of the original RDoc::VerifyTask, which
9
+ # errors if your coverage isn't *exactly* the threshold.
10
+ #
11
+
12
+ # A task that can verify that the RCov coverage doesn't
13
+ # drop below a certain threshold. It should be run after
14
+ # running Spec::Rake::SpecTask.
15
+ class VerifyTask < Rake::TaskLib
16
+
17
+ COVERAGE_PERCENTAGE_PATTERN =
18
+ %r{<tt class='coverage_code'>(\d+\.\d+)%</tt>}
19
+
20
+ # Name of the task. Defaults to :verify_rcov
21
+ attr_accessor :name
22
+
23
+ # Path to the index.html file generated by RCov, which
24
+ # is the file containing the total coverage.
25
+ # Defaults to 'coverage/index.html'
26
+ attr_accessor :index_html
27
+
28
+ # Whether or not to output details. Defaults to true.
29
+ attr_accessor :verbose
30
+
31
+ # The threshold value (in percent) for coverage. If the
32
+ # actual coverage is not equal to this value, the task will raise an
33
+ # exception.
34
+ attr_accessor :threshold
35
+
36
+ def initialize( name=:verify )
37
+ @name = name
38
+ @index_html = 'coverage/index.html'
39
+ @verbose = true
40
+ yield self if block_given?
41
+ raise "Threshold must be set" if @threshold.nil?
42
+ define
43
+ end
44
+
45
+ def define
46
+ desc "Verify that rcov coverage is at least #{threshold}%"
47
+
48
+ task @name do
49
+ total_coverage = nil
50
+ if match = File.read( index_html ).match( COVERAGE_PERCENTAGE_PATTERN )
51
+ total_coverage = Float( match[1] )
52
+ else
53
+ raise "Couldn't find the coverage percentage in #{index_html}"
54
+ end
55
+
56
+ puts "Coverage: #{total_coverage}% (threshold: #{threshold}%)" if verbose
57
+ if total_coverage < threshold
58
+ raise "Coverage must be at least #{threshold}% but was #{total_coverage}%"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # vim: set nosta noet ts=4 sw=4:
@@ -0,0 +1,269 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+ basedir = Pathname.new( __FILE__ ).dirname.parent.parent
6
+
7
+ libdir = basedir + "lib"
8
+
9
+ $LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
10
+ }
11
+
12
+ begin
13
+ require 'tmpdir'
14
+ require 'spec/runner'
15
+ require 'io/reactor'
16
+ require 'socket'
17
+ rescue LoadError
18
+ unless Object.const_defined?( :Gem )
19
+ require 'rubygems'
20
+ retry
21
+ end
22
+ raise
23
+ end
24
+
25
+
26
+ TMPFILE = Pathname.new( Dir.tmpdir ) + "ioreactor-spec.#{Process.pid}"
27
+ HOST = 'localhost'
28
+ PORT = 5656
29
+ TEST_DATA = File.read( __FILE__ )
30
+
31
+ $stderr.sync = $stdout.sync = true
32
+
33
+
34
+ describe IO::Reactor do
35
+
36
+ before( :each ) do
37
+ @reactor = IO::Reactor.new
38
+ @reader, @writer = IO.pipe
39
+ end
40
+
41
+ after( :each ) do
42
+ @reactor.clear
43
+ end
44
+
45
+
46
+ ### #register/#registered? with no block
47
+ it "allows registration of an IO for write events" do
48
+ @reactor.register( @writer, :write )
49
+
50
+ @reactor.should be_registered( @writer )
51
+ @reactor.handles[ @writer ][ :events ].should == [:write]
52
+ end
53
+
54
+ it "allows registration of an IO for read events" do
55
+ @reactor.register( @reader, :read )
56
+
57
+ @reactor.should be_registered( @reader )
58
+ @reactor.handles[ @reader ][ :events ].should == [:read]
59
+ end
60
+
61
+ it "allows registration of an IO for error events" do
62
+ @reactor.register( @writer, :error )
63
+
64
+ @reactor.should be_registered( @writer )
65
+ @reactor.handles[ @writer ][ :events ].should == [:error]
66
+ end
67
+
68
+ it "allows registration of an IO for multiple events" do
69
+ @reactor.register( @writer, :read, :write )
70
+
71
+ @reactor.should be_registered( @writer )
72
+ @reactor.handles[ @writer ][ :events ].should have(2).members
73
+ @reactor.handles[ @writer ][ :events ].should include( :read )
74
+ @reactor.handles[ @writer ][ :events ].should include( :write )
75
+ end
76
+
77
+
78
+ it "allows registration of a handler with an IO for selected events" do
79
+ @reactor.register( @writer, :write ) {|io, event| }
80
+
81
+ @reactor.should be_registered( @writer )
82
+ @reactor.handles[ @writer ][ :events ].should == [:write]
83
+ end
84
+
85
+
86
+ it "allows registration of an argument list with a handler" do
87
+ @reactor.register( @writer, :write, "foo", :bar, :something ) {|io, event| }
88
+
89
+ @reactor.should be_registered( @writer )
90
+ @reactor.handles[ @writer ][ :events ].should == [:write]
91
+ @reactor.handles[ @writer ][ :args ].should == ["foo", :bar, :something]
92
+ end
93
+
94
+
95
+ it "allows all registered handles to be cleared" do
96
+ @reactor.register( @reader, :read )
97
+ @reactor.register( @writer, :write )
98
+
99
+ @reactor.clear
100
+
101
+ @reactor.handles.should be_empty()
102
+ @reactor.should_not be_registered( @writer )
103
+ @reactor.should_not be_registered( @reader )
104
+ end
105
+
106
+
107
+ it "calls registered handlers for events on an IO when they occur" do
108
+ data_to_send = TEST_DATA.dup
109
+ received_data = ''
110
+
111
+ @reactor.register( @reader, :read ) do |io, event|
112
+ if io.eof?
113
+ @reactor.unregister( io )
114
+ io.close
115
+ else
116
+ received_data << io.read( 256 )
117
+ end
118
+ end
119
+ @reactor.register( @writer, :write ) do |io, event|
120
+ if data_to_send.empty?
121
+ @reactor.unregister( io )
122
+ io.close
123
+ else
124
+ bytes = io.write( data_to_send )
125
+ data_to_send.slice!( 0, bytes )
126
+ end
127
+ end
128
+
129
+ @reactor.poll until @reactor.empty?
130
+
131
+ received_data.should == TEST_DATA
132
+ end
133
+
134
+
135
+ it "calls a provided fallback handler if there is no handler registered for an " +
136
+ "event when it occurs" do
137
+
138
+ data_to_send = TEST_DATA.dup
139
+ received_data = ''
140
+
141
+ @reactor.register( @reader, :read )
142
+ @reactor.register( @writer, :write )
143
+
144
+ until @reactor.empty?
145
+ @reactor.poll do |io, event|
146
+ case event
147
+ when :read
148
+ if io.eof?
149
+ @reactor.unregister( io )
150
+ io.close
151
+ else
152
+ received_data << io.read( 256 )
153
+ end
154
+
155
+ when :write
156
+ if data_to_send.empty?
157
+ @reactor.unregister( io )
158
+ io.close
159
+ else
160
+ bytes = io.write( data_to_send )
161
+ data_to_send.slice!( 0, bytes )
162
+ end
163
+
164
+ when :error
165
+ @reactor.unregister( io )
166
+ io.close
167
+ else
168
+ fail "Reactor got unexpected event %p on %p" % [ event, io ]
169
+ end
170
+ end
171
+ end
172
+
173
+ received_data.should == TEST_DATA
174
+ end
175
+
176
+
177
+ ### A class that will encapsulate how we want to read or write from the Reactor
178
+ class IOStrategy
179
+ def initialize( reactor, buffer='' )
180
+ @reactor = reactor
181
+ @buffer = buffer
182
+ end
183
+
184
+ attr_reader :buffer
185
+
186
+ def read_from( io, event )
187
+ raise ArgumentError, "expected to read, not #{event}" unless event == :read
188
+ if io.eof?
189
+ @reactor.unregister( io )
190
+ io.close
191
+ else
192
+ @buffer << io.read( 256 )
193
+ end
194
+ end
195
+
196
+ def write_to( io, event )
197
+ raise ArgumentError, "expected to write, not #{event}" unless event == :write
198
+ if @buffer.empty?
199
+ @reactor.unregister( io )
200
+ io.close
201
+ else
202
+ bytes = io.write( @buffer )
203
+ @buffer.slice!( 0, bytes )
204
+ end
205
+ end
206
+ end
207
+
208
+ it "uses method or proc handlers if those are used instead of blocks" do
209
+ reader = IOStrategy.new( @reactor )
210
+ writer = IOStrategy.new( @reactor, TEST_DATA.dup )
211
+
212
+ @reactor.register( @reader, :read, &reader.method(:read_from) )
213
+ @reactor.register( @writer, :write, &writer.method(:write_to) )
214
+ @reactor.poll until @reactor.empty?
215
+
216
+ reader.buffer.should == TEST_DATA
217
+ end
218
+
219
+
220
+ it "saves pending events if there is no handler registered for them and no fallback " +
221
+ "handler provided" do
222
+
223
+ @reactor.register( $stdout, :write )
224
+ @reactor.poll( 1.0 ).should == 1
225
+
226
+ @reactor.pending_events.should have(1).members
227
+ @reactor.pending_events.keys.should include( $stdout )
228
+ @reactor.pending_events[ $stdout ].should == [ :write ]
229
+ end
230
+
231
+ it "knows what events are enabled for which handles" do
232
+ @reactor.register( @writer )
233
+ @reactor.should_not have_event_enabled( @writer, :read )
234
+ @reactor.enable_events( @writer, :read, :write )
235
+ @reactor.should have_event_enabled( @writer, :read )
236
+ @reactor.should have_event_enabled( @writer, :write )
237
+ end
238
+
239
+
240
+ it "allows events to be unregistered for a handle" do
241
+ @reactor.register( @writer, :write, :read )
242
+ @reactor.disable_events( @writer, :read )
243
+ @reactor.should_not have_event_enabled( @writer, :read )
244
+ end
245
+
246
+
247
+ it "doesn't allow the error event to be disabled for a handle" do
248
+ @reactor.register( @writer, :write )
249
+ lambda {
250
+ @reactor.disable_events( @writer, :error )
251
+ }.should raise_error
252
+ end
253
+
254
+
255
+ it "can remove the handler for a handle" do
256
+ handler = lambda { }
257
+ @reactor.register( @writer, :write, &handler )
258
+ @reactor.remove_handler( @writer ).should == handler
259
+ end
260
+
261
+
262
+ it "can clear the handler arguments for a handle" do
263
+ @reactor.register( @writer, :write, :an_arg )
264
+ @reactor.remove_args( @writer )
265
+ @reactor.handles[ @writer ][:args].should be_empty()
266
+ end
267
+
268
+ end # class PollTestCase
269
+