File: observable_shared.rb

package info (click to toggle)
ruby-concurrent 1.1.6%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 30,284 kB
  • sloc: ruby: 30,875; java: 6,117; ansic: 288; makefile: 9; sh: 6
file content (176 lines) | stat: -rw-r--r-- 4,964 bytes parent folder | download | duplicates (2)
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
171
172
173
174
175
176
RSpec.shared_examples :observable do

  let(:observer_set) do
    subject.instance_variable_get(:@observers)
  end

  let(:observer_class) do
    Class.new do
      def initialize(&block)
        @block = block
      end
      def update(*args)
        @block.call(*args) if @block
      end
    end
  end

  let(:observer){ observer_class.new }

  let!(:observer_func){ :notify }

  let(:observer_with_func_class) do
    Class.new do
      def initialize(&block)
        @block = block
      end
      def notify(*args)
        @block.call(*args) if @block
      end
    end
  end

  let(:observer_with_func){ observer_with_func_class.new }

  context '#add_observer' do

    it 'adds an observer if called before first notification' do
      expect(observer_set).to receive(:add_observer).with(any_args)
      subject.add_observer(observer)
    end

    it 'adds an observer with :func if called before first notification' do
      expect(observer_set).to receive(:add_observer).with(observer_with_func, :notify)
      subject.add_observer(observer_with_func, observer_func)
    end

    it 'creates an observer from a block if called before first notification' do
      block = proc{ nil }
      expect(observer_set).to receive(:add_observer).with(any_args)
      subject.add_observer(&block)
    end

    it 'raises an exception if not given an observer or a block' do
      expect {
        subject.add_observer
      }.to raise_error(ArgumentError)
    end

    it 'raises an exception when given both an observer and a block' do
      expect {
        subject.add_observer(observer){ nil }
      }.to raise_error(ArgumentError)
    end
  end

  context '#delete_observer' do

    it 'deletes the given observer if called before first notification' do
      expect(subject.count_observers).to eq 0
      subject.add_observer(observer)
      expect(subject.count_observers).to eq 1
      subject.delete_observer(observer)
      expect(subject.count_observers).to eq 0
    end

    it 'returns the removed observer if found in the observer set' do
      subject.add_observer(observer)
      expect(subject.delete_observer(observer)).to eq observer
    end

    it 'returns the given observer even when not found in the observer set' do
      expect(subject.delete_observer(observer)).to eq observer
    end
  end

  context '#delete_observers' do

    it 'deletes all observers when called before first notification' do
      5.times{ subject.add_observer(observer_class.new) }
      expect(subject.count_observers).to eq 5
      subject.delete_observers
      expect(subject.count_observers).to eq 0
    end

    it 'returns self' do
      expect(subject.delete_observers).to eq subject
    end
  end

  context '#count_observers' do

    it 'returns zero for a new observable object' do
      expect(subject.count_observers).to eq 0
    end

    it 'returns a count of registered observers if called before first notification' do
      5.times{ subject.add_observer(observer_class.new) }
      expect(subject.count_observers).to eq 5
    end

    it 'returns zero after #delete_observers has been called' do
      5.times{ subject.add_observer(observer_class.new) }
      subject.delete_observers
      expect(subject.count_observers).to eq 0
    end
  end

  context 'first notification' do

    it 'calls the #update method on all observers without a specified :func' do
      latch = Concurrent::CountDownLatch.new(5)
      5.times do
        subject.add_observer(observer_class.new{ latch.count_down })
      end
      trigger_observable(subject)
      latch.wait(1)
      expect(latch.count).to eq 0
    end

    it 'calls the appropriate function on all observers which specified a :func' do
      latch = Concurrent::CountDownLatch.new(5)
      5.times do
        obs = observer_with_func_class.new{ latch.count_down }
        subject.add_observer(obs, observer_func)
      end
      trigger_observable(subject)
      latch.wait(1)
      expect(latch.count).to eq 0
    end

    it 'calls the proc for all observers added as a block' do
      latch = Concurrent::CountDownLatch.new(5)
      5.times do
        subject.add_observer{ latch.count_down }
      end
      trigger_observable(subject)
      latch.wait(1)
      expect(latch.count).to eq 0
    end

    it 'does not notify any observers removed with #delete_observer' do
      latch = Concurrent::CountDownLatch.new(5)

      obs = observer_class.new{ latch.count_down }
      subject.add_observer(obs)
      subject.delete_observer(obs)

      trigger_observable(subject)
      latch.wait(1)
      expect(latch.count).to eq 5
    end

    it 'does not notify any observers after #delete_observers called' do
      latch = Concurrent::CountDownLatch.new(5)
      5.times do
        subject.add_observer(observer_class.new{ latch.count_down })
      end

      subject.delete_observers

      trigger_observable(subject)
      latch.wait(1)
      expect(latch.count).to eq 5
    end
  end
end