io-reactor 0.05 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+