sleepy_penguin 3.4.1 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.document +1 -0
  3. data/.olddoc.yml +3 -4
  4. data/GIT-VERSION-GEN +1 -1
  5. data/LICENSE +3 -3
  6. data/README +7 -4
  7. data/TODO +1 -0
  8. data/ext/sleepy_penguin/cfr.c +62 -0
  9. data/ext/sleepy_penguin/epoll.c +34 -24
  10. data/ext/sleepy_penguin/eventfd.c +6 -5
  11. data/ext/sleepy_penguin/extconf.rb +6 -0
  12. data/ext/sleepy_penguin/init.c +83 -12
  13. data/ext/sleepy_penguin/inotify.c +48 -36
  14. data/ext/sleepy_penguin/kqueue.c +22 -21
  15. data/ext/sleepy_penguin/sendfile.c +120 -0
  16. data/ext/sleepy_penguin/sleepy_penguin.h +15 -28
  17. data/ext/sleepy_penguin/sp_copy.h +33 -0
  18. data/ext/sleepy_penguin/splice.c +174 -0
  19. data/ext/sleepy_penguin/timerfd.c +1 -5
  20. data/ext/sleepy_penguin/util.c +12 -0
  21. data/lib/sleepy_penguin.rb +28 -0
  22. data/lib/sleepy_penguin/cfr.rb +29 -0
  23. data/lib/sleepy_penguin/epoll.rb +13 -10
  24. data/lib/sleepy_penguin/kqueue.rb +6 -6
  25. data/lib/sleepy_penguin/sp.rb +1 -1
  26. data/lib/sleepy_penguin/splice.rb +125 -0
  27. data/pkg.mk +5 -12
  28. data/sleepy_penguin.gemspec +13 -15
  29. data/test/helper.rb +2 -7
  30. data/test/test_cfr.rb +35 -0
  31. data/test/test_constants.rb +2 -4
  32. data/test/test_epoll.rb +35 -6
  33. data/test/test_epoll_gc.rb +2 -5
  34. data/test/test_epoll_io.rb +3 -6
  35. data/test/test_epoll_optimizations.rb +2 -2
  36. data/test/test_eventfd.rb +2 -5
  37. data/test/test_inotify.rb +2 -4
  38. data/test/test_kqueue.rb +35 -7
  39. data/test/test_kqueue_io.rb +2 -5
  40. data/test/test_pipesize.rb +22 -0
  41. data/test/test_sendfile.rb +26 -0
  42. data/test/test_splice.rb +250 -0
  43. data/test/test_splice_eintr.rb +31 -0
  44. data/test/test_timerfd.rb +2 -5
  45. metadata +27 -34
  46. data/lib/sleepy_penguin/epoll/io.rb +0 -28
  47. data/lib/sleepy_penguin/kqueue/io.rb +0 -30
data/pkg.mk CHANGED
@@ -13,14 +13,6 @@ RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
13
13
  RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))')
14
14
  lib := lib
15
15
 
16
- ifeq ($(shell test -f script/isolate_for_tests && echo t),t)
17
- isolate_libs := tmp/isolate/$(RUBY_ENGINE)-$(RUBY_VERSION)/isolate.mk
18
- $(isolate_libs): script/isolate_for_tests
19
- @$(RUBY) script/isolate_for_tests
20
- -include $(isolate_libs)
21
- lib := $(lib):$(ISOLATE_LIBS)
22
- endif
23
-
24
16
  ext := $(firstword $(wildcard ext/*))
25
17
  ifneq ($(ext),)
26
18
  ext_pfx := tmp/ext/$(RUBY_ENGINE)-$(RUBY_VERSION)
@@ -94,7 +86,7 @@ fix-perms:
94
86
  gem: $(pkggem)
95
87
 
96
88
  install-gem: $(pkggem)
97
- gem install $(CURDIR)/$<
89
+ gem install --local $(CURDIR)/$<
98
90
 
99
91
  $(pkggem): manifest fix-perms
100
92
  gem build $(rfpackage).gemspec
@@ -121,9 +113,10 @@ gem install-gem: GIT-VERSION-FILE
121
113
  $(MAKE) $@ VERSION=$(GIT_VERSION)
122
114
  endif
123
115
 
124
- all:: test
116
+ all:: check
125
117
  test_units := $(wildcard test/test_*.rb)
126
- test: test-unit
118
+ test: check
119
+ check: test-unit
127
120
  test-unit: $(test_units)
128
121
  $(test_units): build
129
122
  $(RUBY) -I $(lib) $@ $(RUBY_TEST_OPTS)
@@ -153,5 +146,5 @@ $(PLACEHOLDERS):
153
146
  echo olddoc_placeholder > $@
154
147
  endif
155
148
 
156
- .PHONY: all .FORCE-GIT-VERSION-FILE doc test $(test_units) manifest
149
+ .PHONY: all .FORCE-GIT-VERSION-FILE doc check test $(test_units) manifest
157
150
  .PHONY: check-warnings
@@ -1,24 +1,22 @@
1
- ENV["VERSION"] or abort "VERSION= must be specified"
2
- manifest = File.readlines('.manifest').map! { |x| x.chomp! }
3
- require 'olddoc'
4
- extend Olddoc::Gemspec
5
- name, summary, title = readme_metadata
1
+ manifest = File.exist?('.manifest') ?
2
+ IO.readlines('.manifest').map!(&:chomp!) : `git ls-files`.split("\n")
6
3
 
7
4
  Gem::Specification.new do |s|
8
5
  s.name = %q{sleepy_penguin}
9
- s.version = ENV["VERSION"].dup
10
- s.homepage = Olddoc.config['rdoc_url']
11
- s.authors = ["#{name} hackers"]
12
- s.description = readme_description
6
+ s.version = (ENV['VERSION'] || '3.5.0').dup
7
+ s.homepage = 'https://bogomips.org/sleepy_penguin/'
8
+ s.authors = ['sleepy_penguin hackers']
9
+ s.description = File.read('README').split("\n\n")[1]
13
10
  s.email = %q{sleepy-penguin@bogomips.org}
14
- s.extra_rdoc_files = extra_rdoc_files(manifest)
15
11
  s.files = manifest
16
- s.summary = summary
12
+ s.summary = 'Linux I/O events for Ruby'
17
13
  s.test_files = Dir['test/test_*.rb']
18
14
  s.extensions = %w(ext/sleepy_penguin/extconf.rb)
19
- s.add_development_dependency('minitest', '~> 5.0')
20
- s.add_development_dependency('olddoc', '~> 1.0')
15
+ s.extra_rdoc_files = IO.readlines('.document').map!(&:chomp!).keep_if do |f|
16
+ File.exist?(f)
17
+ end
18
+ s.add_development_dependency('test-unit', '~> 3.0')
21
19
  s.add_development_dependency('strace_me', '~> 1.0')
22
-
23
- s.licenses = %w(LGPLv2.1+)
20
+ s.required_ruby_version = '>= 2.0'
21
+ s.licenses = %w(LGPL-2.1+)
24
22
  end
@@ -1,11 +1,6 @@
1
1
  $-w = $stdout.sync = $stderr.sync = Thread.abort_on_exception = true
2
- gem 'minitest'
3
- require 'minitest/autorun'
4
- Testcase = begin
5
- Minitest::Test # minitest 5
6
- rescue NameError
7
- Minitest::Unit::TestCase # minitest 4
8
- end
2
+ require 'test/unit'
3
+ require 'sleepy_penguin'
9
4
 
10
5
  def check_cloexec(io)
11
6
  pipe = IO.pipe
@@ -0,0 +1,35 @@
1
+ # -*- encoding: binary -*-
2
+ require_relative 'helper'
3
+ require 'tempfile'
4
+
5
+ class TestCfr < Test::Unit::TestCase
6
+ def test_copy_file_range
7
+ str = 'abcde'
8
+ size = 5
9
+ src = Tempfile.new('ruby_cfr_src')
10
+ dst = Tempfile.new('ruby_cfr_dst')
11
+ assert_equal 5, src.syswrite(str)
12
+ src.sysseek(0)
13
+ begin
14
+ nr = SleepyPenguin.copy_file_range(src, dst, size)
15
+ rescue Errno::EINVAL
16
+ warn 'copy_file_range not supported (requires Linux 4.5+)'
17
+ warn "We have: #{`uname -a`}"
18
+ return
19
+ end
20
+ assert_equal nr, 5
21
+ dst.sysseek(0)
22
+ assert_equal str, dst.sysread(5)
23
+
24
+ nr = SleepyPenguin.copy_file_range(src, dst, size, off_in: 1, off_out: 0)
25
+ assert_equal 4, nr
26
+ dst.sysseek(0)
27
+ assert_equal 'bcde', dst.sysread(4)
28
+
29
+ nr = SleepyPenguin.copy_file_range(src, dst, size, off_in: 9)
30
+ assert_equal 0, nr, 'no EOFError'
31
+ ensure
32
+ dst.close!
33
+ src.close!
34
+ end
35
+ end if SleepyPenguin.respond_to?(:copy_file_range)
@@ -1,9 +1,7 @@
1
- require './test/helper'
2
- $-w = true
3
- Thread.abort_on_exception = true
4
1
  require 'sleepy_penguin/sp'
2
+ require_relative 'helper'
5
3
 
6
- class TestConstants < Testcase
4
+ class TestConstants < Test::Unit::TestCase
7
5
  def test_constants
8
6
  assert_equal SleepyPenguin::SLEEPY_PENGUIN_VERSION,
9
7
  SP::SLEEPY_PENGUIN_VERSION
@@ -1,13 +1,9 @@
1
- require './test/helper'
1
+ require_relative 'helper'
2
2
  require 'fcntl'
3
3
  require 'socket'
4
4
  require 'thread'
5
- $-w = true
6
- Thread.abort_on_exception = true
7
5
 
8
- require 'sleepy_penguin'
9
-
10
- class TestEpoll < Testcase
6
+ class TestEpoll < Test::Unit::TestCase
11
7
  include SleepyPenguin
12
8
 
13
9
  def setup
@@ -343,6 +339,13 @@ class TestEpoll < Testcase
343
339
  @ep.close
344
340
  io = Epoll.new(Epoll::CLOEXEC).to_io
345
341
  assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == Fcntl::FD_CLOEXEC)
342
+ io.close
343
+
344
+ # prettier, slower, but more memory efficient due to lack of caching
345
+ # due to the constant cache:
346
+ io = Epoll.new(:CLOEXEC).to_io
347
+
348
+ assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == Fcntl::FD_CLOEXEC)
346
349
  end
347
350
 
348
351
  def test_new
@@ -531,4 +534,30 @@ class TestEpoll < Testcase
531
534
  end
532
535
  @ep.wait(1) { |flags, io| assert_equal(first[0], io) }
533
536
  end
537
+
538
+ def test_epoll_nest
539
+ ep2 = Epoll.new
540
+ r, w = IO.pipe
541
+ @ep.add(@rd, :IN)
542
+ @ep.add(@wr, :OUT)
543
+ ep2.add(r, :IN)
544
+ ep2.add(w, :OUT)
545
+ w.write('.')
546
+ @wr.write('.')
547
+ outer = []
548
+ inner = []
549
+ nr = 0
550
+ @ep.wait(2) do |_, io|
551
+ outer << io
552
+ ep2.wait(2) do |_, io2|
553
+ (inner[nr] ||= []) << io2
554
+ end
555
+ nr += 1
556
+ end
557
+ assert_equal [ @rd, @wr ].sort_by(&:fileno), outer.sort_by(&:fileno)
558
+ exp = [ r, w ].sort_by(&:fileno)
559
+ assert_equal [ exp, exp ], inner.map { |x| x.sort_by(&:fileno) }
560
+ ensure
561
+ [ r, w, ep2 ].compact.each(&:close)
562
+ end
534
563
  end if defined?(SleepyPenguin::Epoll)
@@ -1,9 +1,6 @@
1
- require './test/helper'
2
- $-w = true
1
+ require_relative 'helper'
3
2
 
4
- require 'sleepy_penguin'
5
-
6
- class TestEpollGC < Testcase
3
+ class TestEpollGC < Test::Unit::TestCase
7
4
  include SleepyPenguin
8
5
 
9
6
  def setup
@@ -1,12 +1,9 @@
1
- require './test/helper'
1
+ require_relative 'helper'
2
2
  require 'fcntl'
3
3
  require 'socket'
4
4
  require 'thread'
5
- $-w = true
6
- Thread.abort_on_exception = true
7
- require 'sleepy_penguin'
8
5
 
9
- class TestEpollIO < Testcase
6
+ class TestEpollIO < Test::Unit::TestCase
10
7
  include SleepyPenguin
11
8
 
12
9
  def setup
@@ -23,7 +20,7 @@ class TestEpollIO < Testcase
23
20
 
24
21
  class EpSub < Epoll::IO
25
22
  def self.new
26
- super(SleepyPenguin::Epoll::CLOEXEC)
23
+ super(:CLOEXEC)
27
24
  end
28
25
  end
29
26
 
@@ -1,4 +1,4 @@
1
- require './test/helper'
1
+ require_relative 'helper'
2
2
  begin
3
3
  require 'strace'
4
4
  rescue LoadError
@@ -7,7 +7,7 @@ $-w = true
7
7
 
8
8
  require 'sleepy_penguin'
9
9
 
10
- class TestEpollOptimizations < Testcase
10
+ class TestEpollOptimizations < Test::Unit::TestCase
11
11
  include SleepyPenguin
12
12
  IO_PURGATORY = []
13
13
 
@@ -1,10 +1,7 @@
1
- require './test/helper'
1
+ require_relative 'helper'
2
2
  require 'fcntl'
3
- $-w = true
4
3
 
5
- require 'sleepy_penguin'
6
-
7
- class TestEventFD < Testcase
4
+ class TestEventFD < Test::Unit::TestCase
8
5
  include SleepyPenguin
9
6
 
10
7
  def test_constants
@@ -1,11 +1,9 @@
1
- require './test/helper'
1
+ require_relative 'helper'
2
2
  require 'fcntl'
3
3
  require 'tempfile'
4
4
  require 'set'
5
- $-w = true
6
- require 'sleepy_penguin'
7
5
 
8
- class TestInotify < Testcase
6
+ class TestInotify < Test::Unit::TestCase
9
7
  include SleepyPenguin
10
8
  attr_reader :ino
11
9
 
@@ -1,9 +1,6 @@
1
- require './test/helper'
2
- $-w = true
3
- Thread.abort_on_exception = true
4
- require 'sleepy_penguin'
1
+ require_relative 'helper'
5
2
 
6
- class TestKqueue < Testcase
3
+ class TestKqueue < Test::Unit::TestCase
7
4
  include SleepyPenguin
8
5
 
9
6
  def test_kqueue
@@ -71,5 +68,36 @@ class TestKqueue < Testcase
71
68
  ensure
72
69
  kq.close
73
70
  end
74
- end if defined?(SleepyPenguin::Kqueue) &&
75
- IO.instance_methods.include?(:autoclose=)
71
+
72
+ def test_epoll_nest
73
+ kq1 = Kqueue.new
74
+ kq2 = Kqueue.new
75
+ r1, w1 = IO.pipe
76
+ r2, w2 = IO.pipe
77
+ w1.write '.'
78
+ w2.write '.'
79
+ kq1.kevent([
80
+ Kevent[r1.fileno, EvFilt::READ, Ev::ADD, 0, 0, r1],
81
+ Kevent[w1.fileno, EvFilt::WRITE, Ev::ADD, 0, 0, w1]
82
+ ])
83
+ kq2.kevent([
84
+ Kevent[r2.fileno, EvFilt::READ, Ev::ADD, 0, 0, r2],
85
+ Kevent[w2.fileno, EvFilt::WRITE, Ev::ADD, 0, 0, w2]
86
+ ])
87
+ outer = []
88
+ inner = []
89
+ nr = 0
90
+ kq1.kevent(nil, 2) do |kev1|
91
+ outer << kev1.udata
92
+ kq2.kevent(nil, 2) do |kev2|
93
+ (inner[nr] ||= []) << kev2.udata
94
+ end
95
+ nr += 1
96
+ end
97
+ assert_equal [ r1, w1 ].sort_by(&:fileno), outer.sort_by(&:fileno)
98
+ exp = [ r2, w2 ].sort_by(&:fileno)
99
+ assert_equal [ exp, exp ], inner.map { |x| x.sort_by(&:fileno) }
100
+ ensure
101
+ [ r1, w1, r2, w2, kq1, kq2 ].compact.each(&:close)
102
+ end
103
+ end if defined?(SleepyPenguin::Kqueue)
@@ -1,9 +1,6 @@
1
- require './test/helper'
2
- $-w = true
3
- Thread.abort_on_exception = true
4
- require 'sleepy_penguin'
1
+ require_relative 'helper'
5
2
 
6
- class TestKqueueIO < Testcase
3
+ class TestKqueueIO < Test::Unit::TestCase
7
4
  include SleepyPenguin
8
5
 
9
6
  def setup
@@ -0,0 +1,22 @@
1
+ # -*- encoding: binary -*-
2
+ require_relative 'helper'
3
+ require 'fcntl'
4
+
5
+ class TestPipesize < Test::Unit::TestCase
6
+ def test_pipe_size
7
+ return unless RUBY_PLATFORM =~ /linux/
8
+ [ :F_GETPIPE_SZ, :F_SETPIPE_SZ ].each do |c|
9
+ return unless SleepyPenguin.const_defined?(c)
10
+ end
11
+ r, w = pipe = IO.pipe
12
+ nr = r.fcntl(SleepyPenguin::F_GETPIPE_SZ)
13
+ assert_kind_of Integer, nr
14
+ assert_operator nr, :>, 0
15
+
16
+ set = 131072
17
+ r.fcntl(SleepyPenguin::F_SETPIPE_SZ, set)
18
+ assert_equal set, r.fcntl(SleepyPenguin::F_GETPIPE_SZ)
19
+ ensure
20
+ pipe.each(&:close) if pipe
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: binary -*-
2
+ require_relative 'helper'
3
+ require 'tempfile'
4
+ require 'socket'
5
+
6
+ class TestSendfile < Test::Unit::TestCase
7
+ def test_linux_sendfile
8
+ rd, wr = UNIXSocket.pair
9
+ size = 5
10
+ src = Tempfile.new('ruby_sf_src')
11
+ assert_equal 0, SleepyPenguin.linux_sendfile(wr, src, size)
12
+ str = 'abcde'.freeze
13
+ assert_equal str.bytesize, src.syswrite(str)
14
+ assert_equal 0, SleepyPenguin.linux_sendfile(wr, src, size)
15
+ src.sysseek(0, IO::SEEK_SET)
16
+ assert_equal str.bytesize,
17
+ SleepyPenguin.linux_sendfile(wr, src, size, offset: 0)
18
+ assert_equal str, rd.read(size)
19
+ assert_equal 0, src.sysseek(0, IO::SEEK_CUR), 'handle offset not changed'
20
+ assert_equal 3, SleepyPenguin.linux_sendfile(wr, src, 3)
21
+ assert_equal 3, src.sysseek(0, IO::SEEK_CUR), 'handle offset changed'
22
+ ensure
23
+ [ rd, wr ].compact.each(&:close)
24
+ src.close! if src
25
+ end
26
+ end
@@ -0,0 +1,250 @@
1
+ # -*- encoding: binary -*-
2
+ require_relative 'helper'
3
+ require 'tempfile'
4
+ require 'socket'
5
+ require 'io/nonblock'
6
+ require 'timeout'
7
+
8
+ class TestSplice < Test::Unit::TestCase
9
+
10
+ def test_splice
11
+ str = 'abcde'
12
+ size = 5
13
+ rd, wr = IO.pipe
14
+ tmp = Tempfile.new('ruby_splice')
15
+
16
+ assert_equal 5, tmp.syswrite(str)
17
+ tmp.sysseek(0)
18
+
19
+ nr = SleepyPenguin.splice(tmp.fileno, wr.fileno, size)
20
+ assert_equal size, nr
21
+ assert_equal str, rd.sysread(size)
22
+ end
23
+
24
+ def test_splice_io
25
+ str = 'abcde'
26
+ size = 5
27
+ rd, wr = IO.pipe
28
+ tmp = Tempfile.new('ruby_splice')
29
+
30
+ assert_equal 5, tmp.syswrite(str)
31
+ tmp.sysseek(0)
32
+
33
+ nr = SleepyPenguin.splice(tmp, wr, size)
34
+ assert_equal size, nr
35
+ assert_equal str, rd.sysread(size)
36
+ end
37
+
38
+ def test_splice_io_noflags
39
+ str = 'abcde'
40
+ size = 5
41
+ rd, wr = IO.pipe
42
+ tmp = Tempfile.new('ruby_splice')
43
+
44
+ assert_equal 5, tmp.syswrite(str)
45
+ tmp.sysseek(0)
46
+
47
+ nr = SleepyPenguin.splice(tmp, wr, size)
48
+ assert_equal size, nr
49
+ assert_equal str, rd.sysread(size)
50
+ end
51
+
52
+ def test_trysplice_io_noflags
53
+ str = 'abcde'
54
+ size = 5
55
+ rd, wr = IO.pipe
56
+ tmp = Tempfile.new('ruby_splice')
57
+
58
+ assert_equal 5, tmp.syswrite(str)
59
+ tmp.sysseek(0)
60
+
61
+ nr = SleepyPenguin.splice(tmp, wr, size, :nonblock, exception: false)
62
+ assert_equal size, nr
63
+ assert_equal str, rd.sysread(size)
64
+ end
65
+
66
+ def test_splice_io_ish
67
+ str = 'abcde'
68
+ size = 5
69
+ rd, wr = IO.pipe
70
+ tmp = Tempfile.new('ruby_splice')
71
+ io_ish = [ tmp ]
72
+ def io_ish.to_io
73
+ first.to_io
74
+ end
75
+
76
+ assert_equal 5, tmp.syswrite(str)
77
+ tmp.sysseek(0)
78
+
79
+ nr = SleepyPenguin.splice(io_ish, wr, size)
80
+ assert_equal size, nr
81
+ assert_equal str, rd.sysread(size)
82
+ end
83
+
84
+ def test_splice_in_offset
85
+ str = 'abcde'
86
+ off = 3
87
+ len = 2
88
+ rd, wr = IO.pipe
89
+ tmp = Tempfile.new('ruby_splice')
90
+
91
+ assert_equal 5, tmp.syswrite(str)
92
+ tmp.sysseek(0)
93
+
94
+ nr = SleepyPenguin.splice(tmp.fileno, wr.fileno, len, off_in: off)
95
+ assert_equal len, nr
96
+ assert_equal 'de', rd.sysread(len)
97
+ end
98
+
99
+ def test_splice_out_offset
100
+ str = 'abcde'
101
+ rd, wr = IO.pipe
102
+ tmp = Tempfile.new('ruby_splice')
103
+
104
+ assert_equal 5, wr.syswrite(str)
105
+ nr = SleepyPenguin.splice(rd.fileno, tmp.fileno, str.size, off_out: 3)
106
+ assert_equal 5, nr
107
+ tmp.sysseek(0)
108
+ assert_equal "\0\0\0abcde", tmp.sysread(9)
109
+ end
110
+
111
+ def test_splice_nonblock
112
+ rd, wr = IO.pipe
113
+ tmp = Tempfile.new('ruby_splice')
114
+
115
+ assert_raises(Errno::EAGAIN) {
116
+ SleepyPenguin.splice(rd.fileno, tmp.fileno, 5, :nonblock, off_out: 0)
117
+ }
118
+ end
119
+
120
+ def test_trysplice_nonblock
121
+ rd, wr = IO.pipe
122
+ tmp = Tempfile.new('ruby_splice')
123
+ assert_equal :EAGAIN, SleepyPenguin.splice(rd, tmp, 5, :nonblock,
124
+ off_out: 0, exception: false)
125
+ end
126
+
127
+ def test_trysplice_nonblock_noargs
128
+ rd, wr = IO.pipe
129
+ tmp = Tempfile.new('ruby_splice')
130
+ assert_equal :EAGAIN, SleepyPenguin.splice(rd, tmp, 5, :nonblock,
131
+ off_out: 0, exception: false)
132
+ assert_equal :EAGAIN, SleepyPenguin.splice(rd, tmp, 5, [:more,:nonblock],
133
+ off_out: 0,
134
+ exception: false)
135
+ end
136
+
137
+ def test_splice_eof
138
+ rd, wr = IO.pipe
139
+ tmp = Tempfile.new('ruby_splice')
140
+ wr.syswrite 'abc'
141
+ wr.close
142
+
143
+ nr = SleepyPenguin.splice(rd.fileno, tmp.fileno, 5, :nonblock, off_out: 0)
144
+ assert_equal 3, nr
145
+ assert_raises(EOFError) {
146
+ SleepyPenguin.splice(rd.fileno, tmp.fileno, 5, :nonblock, off_out: 0)
147
+ }
148
+ end
149
+
150
+ def test_trysplice_eof
151
+ rd, wr = IO.pipe
152
+ tmp = Tempfile.new('ruby_splice')
153
+ wr.syswrite 'abc'
154
+ wr.close
155
+
156
+ nr = SleepyPenguin.splice(rd, tmp, 5, off_out: 0, exception: false)
157
+ assert_equal 3, nr
158
+ assert_nil SleepyPenguin.splice(rd, tmp, 5, :nonblock,
159
+ off_out: 0, exception: false)
160
+ end
161
+
162
+ def test_splice_nonblock_socket
163
+ server = TCPServer.new('127.0.0.1', 0)
164
+ port = server.addr[1]
165
+ rp, wp = IO.pipe
166
+ rs = TCPSocket.new('127.0.0.1', port)
167
+ rs.nonblock = true
168
+ assert_raises(Errno::EAGAIN) {
169
+ SleepyPenguin.splice(rs, wp, 1024)
170
+ }
171
+ rs.close
172
+ server.close
173
+ end
174
+
175
+ def test_tee
176
+ str = 'abcde'
177
+ size = 5
178
+ rda, wra = IO.pipe
179
+ rdb, wrb = IO.pipe
180
+
181
+ assert_equal 5, wra.syswrite(str)
182
+ nr = SleepyPenguin.tee(rda.fileno, wrb.fileno, size)
183
+ assert_equal 5, nr
184
+ assert_equal str, rdb.sysread(5)
185
+ assert_equal str, rda.sysread(5)
186
+ end
187
+
188
+ def test_trytee
189
+ str = 'abcde'
190
+ size = 5
191
+ rda, wra = IO.pipe
192
+ rdb, wrb = IO.pipe
193
+
194
+ assert_equal 5, wra.syswrite(str)
195
+ nr = SleepyPenguin.tee(rda, wrb, size, :nonblock, exception: false)
196
+ assert_equal 5, nr
197
+ assert_equal str, rdb.sysread(5)
198
+ assert_equal str, rda.sysread(5)
199
+ end
200
+
201
+ def test_tee_eof
202
+ rda, wra = IO.pipe
203
+ rdb, wrb = IO.pipe
204
+ wra.close
205
+ assert_raises(EOFError) {
206
+ SleepyPenguin.tee(rda.fileno, wrb.fileno, 4096)
207
+ }
208
+ end
209
+
210
+ def test_trytee_eof
211
+ rda, wra = IO.pipe
212
+ rdb, wrb = IO.pipe
213
+ wra.close
214
+ assert_nil SleepyPenguin.tee(rda, wrb, 4096, :nonblock, exception: false)
215
+ end
216
+
217
+ def test_tee_nonblock
218
+ rda, wra = IO.pipe
219
+ rdb, wrb = IO.pipe
220
+ assert_raises(Errno::EAGAIN) {
221
+ SleepyPenguin.tee(rda.fileno, wrb.fileno, 4096, SleepyPenguin::F_NONBLOCK)
222
+ }
223
+ end
224
+
225
+ def test_trytee_nonblock
226
+ rda, wra = IO.pipe
227
+ rdb, wrb = IO.pipe
228
+ assert_equal :EAGAIN, SleepyPenguin.tee(rda, wrb, 4096, :nonblock,
229
+ exception: false)
230
+ end
231
+
232
+ def test_tee_io
233
+ str = 'abcde'
234
+ size = 5
235
+ rda, wra = IO.pipe
236
+ rdb, wrb = IO.pipe
237
+
238
+ assert_equal 5, wra.syswrite(str)
239
+ nr = SleepyPenguin.tee(rda, wrb, size)
240
+ assert_equal 5, nr
241
+ assert_equal str, rdb.sysread(5)
242
+ assert_equal str, rda.sysread(5)
243
+ end
244
+
245
+ def test_constants
246
+ %w(move nonblock more).each { |x|
247
+ assert Integer === SleepyPenguin.const_get("F_#{x.upcase}")
248
+ }
249
+ end
250
+ end if SleepyPenguin.respond_to?(:splice)