uh-wm 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,6 +5,8 @@ module Uh
5
5
  module Testing
6
6
  module AcceptanceHelpers
7
7
  TIMEOUT_DEFAULT = 2
8
+ QUIT_KEYBINDING = 'alt+shift+q'.freeze
9
+ LOG_READY = 'Working events'.freeze
8
10
 
9
11
  def uhwm_run options = '-v'
10
12
  command = %w[uhwm]
@@ -12,6 +14,14 @@ module Uh
12
14
  @interactive = @process = run command.join ' '
13
15
  end
14
16
 
17
+ def uhwm
18
+ @process
19
+ end
20
+
21
+ def uhwm_request_quit
22
+ x_key QUIT_KEYBINDING
23
+ end
24
+
15
25
  def uhwm_ensure_stop
16
26
  if @process
17
27
  x_key 'alt+shift+q'
@@ -19,32 +29,29 @@ module Uh
19
29
  end
20
30
  end
21
31
 
22
- def uhwm_pid
23
- @process.pid
24
- end
25
-
26
- def uhwm_output
27
- @process.stdout
28
- end
29
-
30
- def uhwm_wait_output message
32
+ def uhwm_wait_output message, times = 1, value = nil
31
33
  output = -> { @process.stdout + @process.stderr }
32
34
  timeout_until do
33
35
  case message
34
- when Regexp then output.call =~ message
35
- when String then output.call.include? message
36
+ when Regexp then (value = output.call.scan(message)).size >= times
37
+ when String then output.call.include? message
36
38
  end
37
39
  end
40
+ value
38
41
  rescue TimeoutError => e
39
- fail [
40
- "expected `#{message}' not seen after #{e.timeout} seconds in:",
41
- " ```\n#{output.call.lines.map { |e| " #{e}" }.join} ```"
42
- ].join "\n"
42
+ fail <<-eoh
43
+ expected `#{message}' (#{times}) not seen after #{e.timeout} seconds in:
44
+ ```\n#{output.call.lines.map { |e| " #{e}" }.join} ```
45
+ eoh
46
+ end
47
+
48
+ def uhwm_wait_ready
49
+ uhwm_wait_output LOG_READY
43
50
  end
44
51
 
45
52
  def uhwm_run_wait_ready options = nil
46
53
  if options then uhwm_run options else uhwm_run end
47
- uhwm_wait_output 'Connected to'
54
+ uhwm_wait_ready
48
55
  end
49
56
 
50
57
  def with_other_wm
@@ -58,21 +65,28 @@ module Uh
58
65
  @other_wm
59
66
  end
60
67
 
61
- def x_client ident: :default
62
- @x_clients ||= {}
68
+ def x_client ident = nil
69
+ ident ||= :default
70
+ @x_clients ||= {}
63
71
  @x_clients[ident] ||= XClient.new(ident)
64
72
  end
65
73
 
74
+ def x_clients_ensure_stop
75
+ @x_clients and @x_clients.any? and @x_clients.values.each &:terminate
76
+ end
77
+
66
78
  def x_focused_window_id
67
79
  Integer(`xdpyinfo`[/^focus:\s+window\s+(0x\h+)/, 1])
68
80
  end
69
81
 
70
82
  def x_input_event_masks
71
- `xdpyinfo`[/current input event mask:\s+0x\h+([\w\s]+):/, 1].split(/\s+/).grep /Mask\z/
83
+ `xdpyinfo`[/current input event mask:\s+0x\h+([\w\s]+):/, 1]
84
+ .split(/\s+/)
85
+ .grep /Mask\z/
72
86
  end
73
87
 
74
- def x_key key
75
- fail "cannot simulate X key `#{key}'" unless system "xdotool key #{key}"
88
+ def x_key k
89
+ fail "cannot simulate X key `#{k}'" unless system "xdotool key #{k}"
76
90
  end
77
91
 
78
92
  def x_socket_check pid
@@ -84,41 +98,14 @@ module Uh
84
98
  end.any?
85
99
  end
86
100
 
87
- def x_window_id **options
88
- x_client(options).window_id
89
- end
90
-
91
- def x_window_name
92
- x_client.window_name
93
- end
94
-
95
- def x_window_map times: 1, **options
96
- times.times { x_client(options).map }
97
- x_client(options).sync
98
- end
99
-
100
- def x_window_map_state **options
101
- `xwininfo -id #{x_window_id options}`[/Map State: (\w+)/, 1]
102
- end
103
-
104
- def x_window_unmap **options
105
- x_client(options).unmap
106
- x_client(options).sync
107
- end
108
-
109
- def x_window_destroy **options
110
- x_client(options).destroy
111
- x_client(options).sync
112
- end
113
-
114
- def x_clients_ensure_stop
115
- @x_clients and @x_clients.any? and @x_clients.values.each &:terminate
101
+ def x_window_map_state window_id
102
+ `xwininfo -id #{window_id}`[/Map State: (\w+)/, 1]
116
103
  end
117
104
 
118
105
 
119
106
  private
120
107
 
121
- def timeout_until
108
+ def timeout_until message = 'condition not met after %d seconds'
122
109
  timeout = ENV.key?('UHWMTEST_TIMEOUT') ?
123
110
  ENV['UHWMTEST_TIMEOUT'].to_i :
124
111
  TIMEOUT_DEFAULT
@@ -129,7 +116,7 @@ module Uh
129
116
  end
130
117
  end
131
118
  rescue Timeout::Error
132
- fail TimeoutError.new('execution expired', timeout)
119
+ fail TimeoutError.new(message % timeout, timeout)
133
120
  end
134
121
 
135
122
 
@@ -160,9 +147,7 @@ module Uh
160
147
  end
161
148
 
162
149
  def window
163
- @window ||= @display.create_window(@geo).tap do |o|
164
- o.name = @name
165
- end
150
+ @window ||= @display.create_window(@geo).tap { |o| o.name = @name }
166
151
  end
167
152
 
168
153
  def window_id
@@ -173,7 +158,13 @@ module Uh
173
158
  @name
174
159
  end
175
160
 
176
- def map
161
+ def window_name= name
162
+ @name = @window.name = name
163
+ window.name
164
+ end
165
+
166
+ def map times: 1
167
+ times.times { window.map }
177
168
  window.map
178
169
  self
179
170
  end
data/lib/uh/wm/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Uh
2
2
  module WM
3
- VERSION = '0.0.2'
3
+ VERSION = '0.0.3'
4
4
  end
5
5
  end
@@ -2,7 +2,7 @@ module Uh
2
2
  module WM
3
3
  module Workers
4
4
  class Base
5
- CALLBACKS = %w[before_wait on_timeout on_read on_read_next].freeze
5
+ CALLBACKS = %w[before_watch on_timeout on_read on_read_next].freeze
6
6
 
7
7
  def initialize **options
8
8
  @ios = []
@@ -8,7 +8,7 @@ module Uh
8
8
  end
9
9
 
10
10
  def work_events
11
- @before_wait.call if @before_wait
11
+ @before_watch.call if @before_watch
12
12
  if res = select(@ios, [], [], @timeout) then @on_read.call res
13
13
  else @on_timeout.call if @on_timeout end
14
14
  end
data/spec/spec_helper.rb CHANGED
@@ -2,7 +2,11 @@ require 'headless'
2
2
 
3
3
  require 'uh/wm'
4
4
 
5
+ Dir['spec/support/**/*.rb'].map { |e| require e.gsub 'spec/', '' }
6
+
5
7
  RSpec.configure do |config|
8
+ config.include Factories
9
+
6
10
  config.expect_with :rspec do |expectations|
7
11
  expectations.include_chain_clauses_in_custom_matcher_descriptions = true
8
12
  end
@@ -0,0 +1,27 @@
1
+ module Factories
2
+ def build_geo x = 0, y = 0, width = 640, height = 480
3
+ Uh::Geo.new(x, y, width, height)
4
+ end
5
+
6
+ def build_client window = mock_window
7
+ Uh::WM::Client.new(window)
8
+ end
9
+
10
+ def mock_event type = :xany, **options
11
+ double 'event', type: type, **options
12
+ end
13
+
14
+ def mock_event_key_press key, modifier_mask
15
+ mock_event :key_press,
16
+ key: 'f',
17
+ modifier_mask: modifier_mask
18
+ end
19
+
20
+ def mock_window override_redirect: false
21
+ instance_spy Uh::Window, 'window',
22
+ to_s: 'wid',
23
+ name: 'wname',
24
+ wclass: 'wclass',
25
+ override_redirect?: override_redirect
26
+ end
27
+ end
@@ -6,10 +6,15 @@ module Uh
6
6
  subject(:actions) { described_class.new env, events }
7
7
 
8
8
  describe '#evaluate' do
9
- it 'evaluates given code' do
9
+ it 'evaluates code given as Proc argument' do
10
10
  expect { actions.evaluate proc { throw :action_code } }
11
11
  .to throw_symbol :action_code
12
12
  end
13
+
14
+ it 'evaluates code given as block' do
15
+ expect { actions.evaluate { throw :action_code } }
16
+ .to throw_symbol :action_code
17
+ end
13
18
  end
14
19
 
15
20
  describe '#quit' do
@@ -1,11 +1,8 @@
1
1
  module Uh
2
2
  module WM
3
3
  RSpec.describe Client do
4
- let(:geo) { Geo.new(0, 0, 640, 480) }
5
- let(:window) do
6
- instance_spy Window, 'window', to_s: 'wid',
7
- name: 'wname', wclass: 'wclass'
8
- end
4
+ let(:window) { mock_window }
5
+ let(:geo) { build_geo }
9
6
  subject(:client) { described_class.new window, geo }
10
7
 
11
8
  it 'is not visible' do
@@ -33,7 +30,7 @@ module Uh
33
30
  expect(client.to_s).to include geo.to_s
34
31
  end
35
32
 
36
- it 'includes window id' do
33
+ it 'includes window string representation' do
37
34
  expect(client.to_s).to include 'wid'
38
35
  end
39
36
  end
@@ -50,6 +47,24 @@ module Uh
50
47
  end
51
48
  end
52
49
 
50
+ describe '#update_window_properties' do
51
+ it 'updates the cached window name' do
52
+ client.name
53
+ allow(window).to receive(:name) { 'new name' }
54
+ expect { client.update_window_properties }
55
+ .to change { client.name }
56
+ .from('wname').to 'new name'
57
+ end
58
+
59
+ it 'updates the cached window class' do
60
+ client.wclass
61
+ allow(window).to receive(:wclass) { 'new class' }
62
+ expect { client.update_window_properties }
63
+ .to change { client.wclass }
64
+ .from('wclass').to 'new class'
65
+ end
66
+ end
67
+
53
68
  describe '#configure' do
54
69
  it 'configures the window with client geo' do
55
70
  expect(window).to receive(:configure).with geo
@@ -1,7 +1,7 @@
1
1
  module Uh
2
2
  module WM
3
3
  RSpec.describe Dispatcher do
4
- subject(:dispatcher) { described_class.new }
4
+ subject(:dispatcher) { described_class.new }
5
5
 
6
6
  describe '#[]' do
7
7
  context 'when given key for existing hook' do
@@ -70,19 +70,27 @@ module Uh
70
70
  end
71
71
 
72
72
  describe '#layout' do
73
- context 'when a layout class is set' do
74
- let(:some_layout) { Class.new }
73
+ let(:some_layout_class) { Class.new }
74
+
75
+ it 'returns the default layout' do
76
+ expect(env.layout).to be_an_instance_of ::Uh::Layout
77
+ end
75
78
 
76
- before { env.layout_class = some_layout }
79
+ context 'when a layout is set' do
80
+ let(:some_layout) { some_layout_class.new }
77
81
 
78
- it 'returns a new instance of this layout class' do
79
- expect(env.layout).to be_an_instance_of some_layout
82
+ before { env.layout = some_layout }
83
+
84
+ it 'returns the assigned layout' do
85
+ expect(env.layout).to be some_layout
80
86
  end
81
87
  end
82
88
 
83
- context 'when a layout class is not set' do
84
- it 'returns an instance of the default layout' do
85
- expect(env.layout).to be_an_instance_of ::Uh::Layout
89
+ context 'when a layout class is set' do
90
+ before { env.layout_class = some_layout_class }
91
+
92
+ it 'returns a new instance of this layout class' do
93
+ expect(env.layout).to be_an_instance_of some_layout_class
86
94
  end
87
95
  end
88
96
  end
@@ -129,7 +137,8 @@ module Uh
129
137
 
130
138
  describe '#log_logger_level' do
131
139
  it 'logs the logger level' do
132
- expect(env.logger).to receive(:info).with /log.+(warn|info|debug).+level/i
140
+ expect(env.logger)
141
+ .to receive(:info).with /log.+(warn|info|debug).+level/i
133
142
  env.log_logger_level
134
143
  end
135
144
  end
@@ -2,16 +2,13 @@ module Uh
2
2
  module WM
3
3
  RSpec.describe Manager do
4
4
  let(:block) { proc { } }
5
- let(:window) { instance_spy Window, override_redirect?: false }
5
+ let(:window) { mock_window }
6
+ let(:client) { build_client window }
6
7
  let(:events) { Dispatcher.new }
7
8
  let(:modifier) { :mod1 }
8
9
  let(:display) { Display.new }
9
10
  subject(:manager) { described_class.new events, modifier, display }
10
11
 
11
- it 'has a new display' do
12
- expect(manager.display).to be_a Display
13
- end
14
-
15
12
  it 'has no clients' do
16
13
  expect(manager.clients).to be_empty
17
14
  end
@@ -111,13 +108,13 @@ module Uh
111
108
  context 'with new window' do
112
109
  it 'sends a configure event to the window with a default geo' do
113
110
  expect(window)
114
- .to receive(:configure_event).with(Geo.new(0, 0, 320, 240))
111
+ .to receive(:configure_event).with(build_geo 0, 0, 320, 240)
115
112
  manager.configure window
116
113
  end
117
114
 
118
115
  context 'when :configure event returns a geo' do
119
116
  it 'sends a configure event with geo returned by event' do
120
- geo = Geo.new(0, 0, 42, 42)
117
+ geo = build_geo 0, 0, 42, 42
121
118
  events.on(:configure) { geo }
122
119
  expect(window).to receive(:configure_event).with geo
123
120
  manager.configure window
@@ -126,16 +123,18 @@ module Uh
126
123
  end
127
124
 
128
125
  context 'with known window' do
129
- before { manager.map window }
126
+ before { manager.clients << client }
130
127
 
131
128
  it 'tells the client to configure' do
132
- expect(manager.clients[0]).to receive :configure
129
+ expect(client).to receive :configure
133
130
  manager.configure window
134
131
  end
135
132
  end
136
133
  end
137
134
 
138
135
  describe '#map' do
136
+ let(:display) { instance_spy Display }
137
+
139
138
  it 'registers a new client wrapping the given window' do
140
139
  manager.map window
141
140
  expect(manager.clients[0])
@@ -162,40 +161,46 @@ module Uh
162
161
  end
163
162
  manager.map window
164
163
  end
164
+
165
+ it 'listens for property notify events on given window' do
166
+ expect(display)
167
+ .to receive(:listen_events)
168
+ .with window, Events::PROPERTY_CHANGE_MASK
169
+ manager.map window
170
+ end
165
171
  end
166
172
 
167
173
  describe '#unmap' do
168
- before { manager.map window }
174
+ before { manager.clients << client }
169
175
 
170
176
  context 'when client unmap count is 0 or less' do
171
177
  it 'preserves the client unmap count' do
172
- client = manager.clients[0]
173
- expect { manager.unmap window }
174
- .not_to change { client.unmap_count }
178
+ expect { manager.unmap window }.not_to change { client.unmap_count }
175
179
  end
176
180
 
177
181
  it 'unregisters the client' do
178
- expect { manager.unmap window }
179
- .to change { manager.clients.size }.from(1).to 0
182
+ manager.unmap window
183
+ expect(manager.clients).not_to include client
180
184
  end
181
185
 
182
186
  it 'emits :unmanage event with the client' do
183
187
  events.on :unmanage, &block
184
- expect(block).to receive(:call).with manager.clients[0]
188
+ expect(block).to receive(:call).with client
185
189
  manager.unmap window
186
190
  end
187
191
  end
188
192
 
189
193
  context 'when client unmap count is strictly positive' do
190
- before { manager.clients[0].unmap_count += 1 }
194
+ before { client.unmap_count += 1 }
191
195
 
192
196
  it 'does not unregister the client' do
193
- expect { manager.unmap window }.not_to change { manager.clients }
197
+ manager.unmap window
198
+ expect(manager.clients).to include client
194
199
  end
195
200
 
196
201
  it 'decrements the unmap count' do
197
202
  manager.unmap window
198
- expect(manager.clients[0].unmap_count).to eq 0
203
+ expect(client.unmap_count).to eq 0
199
204
  end
200
205
  end
201
206
 
@@ -215,16 +220,16 @@ module Uh
215
220
  end
216
221
 
217
222
  describe '#destroy' do
218
- before { manager.map window }
223
+ before { manager.clients << client }
219
224
 
220
225
  it 'unregisters the client' do
221
- expect { manager.destroy window }
222
- .to change { manager.clients.size }.from(1).to 0
226
+ manager.destroy window
227
+ expect(manager.clients).not_to include client
223
228
  end
224
229
 
225
230
  it 'emits :unmanage event with the client' do
226
231
  events.on :unmanage, &block
227
- expect(block).to receive(:call).with manager.clients[0]
232
+ expect(block).to receive(:call).with client
228
233
  manager.destroy window
229
234
  end
230
235
 
@@ -243,6 +248,30 @@ module Uh
243
248
  end
244
249
  end
245
250
 
251
+ describe '#update_properties' do
252
+ context 'with known window' do
253
+ before { manager.clients << client }
254
+
255
+ it 'tells the client to update its window properties' do
256
+ expect(client).to receive :update_window_properties
257
+ manager.update_properties window
258
+ end
259
+
260
+ it 'emits :change event with the client' do
261
+ events.on :change, &block
262
+ expect(block).to receive(:call).with client
263
+ manager.update_properties window
264
+ end
265
+ end
266
+
267
+ context 'with unknown window' do
268
+ it 'does not emit any event' do
269
+ expect(events).not_to receive :emit
270
+ manager.update_properties window
271
+ end
272
+ end
273
+ end
274
+
246
275
  describe '#handle_next_event' do
247
276
  it 'handles the next available event on display' do
248
277
  event = double 'event'
@@ -253,7 +282,7 @@ module Uh
253
282
  end
254
283
 
255
284
  describe '#handle_pending_events' do
256
- let(:event) { double 'event' }
285
+ let(:event) { mock_event }
257
286
 
258
287
  context 'when an event is pending on display' do
259
288
  before do
@@ -281,7 +310,7 @@ module Uh
281
310
  end
282
311
 
283
312
  describe '#handle' do
284
- let(:event) { double 'event', type: :any }
313
+ let(:event) { mock_event }
285
314
 
286
315
  it 'emits :xevent event with the X event' do
287
316
  events.on :xevent, &block
@@ -289,13 +318,8 @@ module Uh
289
318
  end
290
319
 
291
320
  context 'when key_press event is given' do
292
- let(:mod_mask) { KEY_MODIFIERS[modifier] }
293
- let(:event) do
294
- double 'event',
295
- type: :key_press,
296
- key: 'f',
297
- modifier_mask: mod_mask
298
- end
321
+ let(:mod_mask) { KEY_MODIFIERS[modifier] }
322
+ let(:event) { mock_event_key_press 'f', mod_mask }
299
323
 
300
324
  it 'emits :key event with the corresponding key' do
301
325
  events.on(:key, :f) { throw :key_press_code }
@@ -313,9 +337,7 @@ module Uh
313
337
  end
314
338
 
315
339
  context 'when configure request event is given' do
316
- let(:event) do
317
- double 'event', type: :configure_request, window: :window
318
- end
340
+ let(:event) { mock_event :configure_request, window: :window }
319
341
 
320
342
  it 'configures the event window' do
321
343
  expect(manager).to receive(:configure).with :window
@@ -324,7 +346,7 @@ module Uh
324
346
  end
325
347
 
326
348
  context 'when destroy_notify event is given' do
327
- let(:event) { double 'event', type: :destroy_notify, window: :window }
349
+ let(:event) { mock_event :destroy_notify, window: :window }
328
350
 
329
351
  it 'destroy the event window' do
330
352
  expect(manager).to receive(:destroy).with :window
@@ -333,7 +355,7 @@ module Uh
333
355
  end
334
356
 
335
357
  context 'when map_request event is given' do
336
- let(:event) { double 'event', type: :map_request, window: :window }
358
+ let(:event) { mock_event :map_request, window: :window }
337
359
 
338
360
  it 'maps the event window' do
339
361
  expect(manager).to receive(:map).with :window
@@ -342,13 +364,22 @@ module Uh
342
364
  end
343
365
 
344
366
  context 'when unmap_notify event is given' do
345
- let(:event) { double 'event', type: :unmap_notify, window: :window }
367
+ let(:event) { mock_event :unmap_notify, window: :window }
346
368
 
347
- it 'unmap the event window' do
369
+ it 'unmaps the event window' do
348
370
  expect(manager).to receive(:unmap).with :window
349
371
  manager.handle event
350
372
  end
351
373
  end
374
+
375
+ context 'when property_notify event is given' do
376
+ let(:event) { mock_event :property_notify, window: :window }
377
+
378
+ it 'updates event window properties' do
379
+ expect(manager).to receive(:update_properties).with :window
380
+ manager.handle event
381
+ end
382
+ end
352
383
  end
353
384
  end
354
385
  end
@@ -5,7 +5,6 @@ module Uh
5
5
  RSpec.describe RunControl do
6
6
  include FileSystemHelpers
7
7
 
8
- let(:code) { :run_control_code }
9
8
  let(:env) { Env.new(StringIO.new) }
10
9
  subject(:rc) { described_class.new env }
11
10
 
@@ -86,6 +85,31 @@ module Uh
86
85
  end
87
86
  end
88
87
 
88
+ describe '#layout' do
89
+ context 'when given a class' do
90
+ let(:layout_class) { Class.new }
91
+
92
+ it 'sets a layout class in the env' do
93
+ rc.layout layout_class
94
+ expect(env.layout_class).to be layout_class
95
+ end
96
+
97
+ context 'when given options' do
98
+ it 'instantiates the class with given options' do
99
+ expect(layout_class).to receive(:new).with(foo: :bar)
100
+ rc.layout layout_class, foo: :bar
101
+ end
102
+ end
103
+ end
104
+
105
+ context 'when given an object' do
106
+ it 'sets a layout instance in the env' do
107
+ rc.layout layout = Object.new
108
+ expect(env.layout).to eq layout
109
+ end
110
+ end
111
+ end
112
+
89
113
  describe '#worker' do
90
114
  it 'sets the worker type in the env' do
91
115
  rc.worker :some_worker