uh-wm 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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