File: selector_spec.rb

package info (click to toggle)
ruby-nio4r 1.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 528 kB
  • ctags: 1,100
  • sloc: ansic: 5,635; ruby: 679; java: 348; makefile: 5
file content (170 lines) | stat: -rw-r--r-- 4,530 bytes parent folder | download
1
2
3
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
require 'spec_helper'

# Timeouts should be at least this precise (in seconds) to pass the tests
# Typical precision should be better than this, but if it's worse it will fail
# the tests
TIMEOUT_PRECISION = 0.1

describe NIO::Selector do
  let(:pair)   { IO.pipe }
  let(:reader) { pair.first }
  let(:writer) { pair.last }

  context "register" do
    it "registers IO objects" do
      monitor = subject.register(reader, :r)
      monitor.should_not be_closed
    end

    it "raises TypeError if asked to register non-IO objects" do
      expect { subject.register(42, :r) }.to raise_exception TypeError
    end

    it "raises when asked to register after closing" do
      subject.close
      expect { subject.register(reader, :r) }.to raise_exception IOError
    end
  end

  it "knows which IO objects are registered" do
    subject.register(reader, :r)
    subject.should be_registered(reader)
    subject.should_not be_registered(writer)
  end

  it "deregisters IO objects" do
    subject.register(reader, :r)

    monitor = subject.deregister(reader)
    subject.should_not be_registered(reader)
    monitor.should be_closed
  end

  it "reports if it is empty" do
    subject.should be_empty

    monitor = subject.register(reader, :r)

    subject.should_not be_empty
  end

  # This spec might seem a bit silly, but this actually something the
  # Java NIO API specifically precludes that we need to work around
  it "allows reregistration of the same IO object across select calls" do
    monitor = subject.register(reader, :r)
    writer << "ohai"

    subject.select.should include monitor
    reader.read(4).should == "ohai"
    subject.deregister(reader)

    new_monitor = subject.register(reader, :r)
    writer << "thar"
    subject.select.should include new_monitor
    reader.read(4).should == "thar"
  end

  context "timeouts" do
    it "waits for a timeout when selecting" do
      monitor = subject.register(reader, :r)

      payload = "hi there"
      writer << payload

      timeout = 0.5
      started_at = Time.now
      subject.select(timeout).should include monitor
      (Time.now - started_at).should be_within(TIMEOUT_PRECISION).of(0)
      reader.read_nonblock(payload.size)

      started_at = Time.now
      subject.select(timeout).should be_nil
      (Time.now - started_at).should be_within(TIMEOUT_PRECISION).of(timeout)
    end

    it "raises ArgumentError if given a negative timeout" do
      subject.register(reader, :r)

      expect { subject.select(-1) }.to raise_exception(ArgumentError)
    end
  end

  context "wakeup" do
    it "wakes up if signaled to from another thread" do
      subject.register(reader, :r)

      thread = Thread.new do
        started_at = Time.now
        subject.select.should be_nil
        Time.now - started_at
      end

      timeout = 0.1
      sleep timeout
      subject.wakeup

      thread.value.should be_within(TIMEOUT_PRECISION).of(timeout)
    end

    it "raises IOError if asked to wake up a closed selector" do
      subject.close
      subject.should be_closed

      expect { subject.wakeup }.to raise_exception IOError
    end
  end

  context "select" do
    it "selects IO objects" do
      writer << "ohai"
      unready, _ = IO.pipe

      reader_monitor  = subject.register(reader, :r)
      unready_monitor = subject.register(unready, :r)

      selected = subject.select(0)
      selected.size.should == 1
      selected.should include reader_monitor
      selected.should_not include unready_monitor
    end

    it "selects closed IO objects" do
      monitor = subject.register(reader, :r)
      subject.select(0).should be_nil

      thread = Thread.new { subject.select }
      Thread.pass while thread.status && thread.status != "sleep"

      writer.close
      selected = thread.value
      selected.should include monitor
    end

    it "iterates across selected objects with a block" do
      readable1, writer = IO.pipe
      writer << "ohai"

      readable2, writer = IO.pipe
      writer << "ohai"

      unreadable, _ = IO.pipe

      monitor1 = subject.register(readable1, :r)
      monitor2 = subject.register(readable2, :r)
      monitor3 = subject.register(unreadable, :r)

      readables = []
      result = subject.select { |monitor| readables << monitor }
      result.should == 2

      readables.should include monitor1
      readables.should include monitor2
      readables.should_not include monitor3
    end
  end

  it "closes" do
    subject.close
    subject.should be_closed
  end
end