uh-wm 0.0.6 → 0.0.7

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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/lib/uh/wm/actions_handler.rb +9 -0
  3. data/lib/uh/wm/cli.rb +1 -1
  4. data/lib/uh/wm/client.rb +14 -0
  5. data/lib/uh/wm/env.rb +3 -1
  6. data/lib/uh/wm/env_logging.rb +1 -1
  7. data/lib/uh/wm/launcher.rb +73 -0
  8. data/lib/uh/wm/run_control.rb +10 -0
  9. data/lib/uh/wm/runner.rb +26 -5
  10. data/lib/uh/wm/testing/acceptance_helpers.rb +36 -65
  11. data/lib/uh/wm/testing/x_client.rb +63 -0
  12. data/lib/uh/wm/version.rb +1 -1
  13. data/lib/uh/wm/workers/base.rb +1 -1
  14. data/lib/uh/wm/workers/mux.rb +3 -1
  15. data/lib/uh/wm.rb +5 -3
  16. metadata +9 -111
  17. data/.gitignore +0 -3
  18. data/.rspec +0 -1
  19. data/.travis.yml +0 -15
  20. data/Gemfile +0 -5
  21. data/Guardfile +0 -12
  22. data/LICENSE +0 -30
  23. data/Rakefile +0 -40
  24. data/config/cucumber.yaml +0 -1
  25. data/features/actions/execute.feature +0 -9
  26. data/features/actions/layout_delegation.feature +0 -31
  27. data/features/actions/quit.feature +0 -9
  28. data/features/cli/debug.feature +0 -5
  29. data/features/cli/layout.feature +0 -15
  30. data/features/cli/require.feature +0 -5
  31. data/features/cli/run_control.feature +0 -9
  32. data/features/cli/usage.feature +0 -11
  33. data/features/cli/verbose.feature +0 -5
  34. data/features/cli/version.feature +0 -6
  35. data/features/cli/worker.feature +0 -9
  36. data/features/layout/manage.feature +0 -12
  37. data/features/layout/protocol.feature +0 -56
  38. data/features/layout/unmanage.feature +0 -10
  39. data/features/manager/change.feature +0 -7
  40. data/features/manager/check_other_wm.feature +0 -8
  41. data/features/manager/expose.feature +0 -5
  42. data/features/manager/input_events.feature +0 -8
  43. data/features/manager/manage.feature +0 -14
  44. data/features/manager/unmanage.feature +0 -13
  45. data/features/manager/x_errors.feature +0 -17
  46. data/features/run_control/evaluation.feature +0 -18
  47. data/features/run_control/key.feature +0 -33
  48. data/features/run_control/layout.feature +0 -39
  49. data/features/run_control/modifier.feature +0 -10
  50. data/features/run_control/worker.feature +0 -9
  51. data/features/session/connection.feature +0 -5
  52. data/features/session/termination.feature +0 -12
  53. data/features/steps/filesystem_steps.rb +0 -3
  54. data/features/steps/output_steps.rb +0 -55
  55. data/features/steps/run_control_steps.rb +0 -3
  56. data/features/steps/run_steps.rb +0 -41
  57. data/features/steps/x_steps.rb +0 -58
  58. data/features/support/env.rb +0 -33
  59. data/features/workers/block.feature +0 -15
  60. data/features/workers/mux.feature +0 -15
  61. data/spec/spec_helper.rb +0 -30
  62. data/spec/support/exit_helpers.rb +0 -6
  63. data/spec/support/factories.rb +0 -27
  64. data/spec/support/filesystem_helpers.rb +0 -11
  65. data/spec/uh/wm/actions_handler_spec.rb +0 -35
  66. data/spec/uh/wm/cli_spec.rb +0 -214
  67. data/spec/uh/wm/client_spec.rb +0 -148
  68. data/spec/uh/wm/dispatcher_spec.rb +0 -76
  69. data/spec/uh/wm/env_spec.rb +0 -154
  70. data/spec/uh/wm/manager_spec.rb +0 -386
  71. data/spec/uh/wm/run_control_spec.rb +0 -126
  72. data/spec/uh/wm/runner_spec.rb +0 -196
  73. data/uh-wm.gemspec +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f36b28780f56841b401c23d062a1a46bb420ad91
4
- data.tar.gz: d42c2d7864fd8211115c0f975436f6aa57184a0e
3
+ metadata.gz: a3607acc6f3a156a4fe6ecfdd4c960daecdde3bc
4
+ data.tar.gz: 9aac8a32b1d1619e055e0c5af14e28d8033dfe9f
5
5
  SHA512:
6
- metadata.gz: 789e6b27ddb37be96a2d766843139256334fb7891967b3ab8a9a051fe0163e09315a7ea216ec5158028d1c0e9ea7fc55889d688c0ef2df3886023fb466d39c52
7
- data.tar.gz: 7d3d6b8fdbd28b553a474777a1fa5316fbfa8c73521f9e4efbeb2eebc438754c4374e98124e60562699ea681ebf51571995a2aac83df389fc1eb98b97a4c97ba
6
+ metadata.gz: f332e7a7d0b81b91d356f7e6345f073eb551d0ea6cabe361285df90c62a14b3cecb29c32f339b5fb032d47c343135b7bb676fb026111ef66463bd38f548a504e
7
+ data.tar.gz: 2e0fbe32e68ef89136a64045f55180fcf3211bd9dc1a4b6dfba461356e3a8076be7d64dcbb989fcacd07d4058dd756c6748315a484c0376d1cacbf304b134a98
@@ -38,6 +38,15 @@ module Uh
38
38
  Process.waitpid pid
39
39
  end
40
40
 
41
+ def kill_current
42
+ return unless layout.current_client
43
+ layout.current_client.kill
44
+ end
45
+
46
+ def log_separator
47
+ log '- ' * 24
48
+ end
49
+
41
50
  def method_missing(m, *args, &block)
42
51
  if respond_to? m
43
52
  meth = layout_method m
data/lib/uh/wm/cli.rb CHANGED
@@ -35,7 +35,7 @@ module Uh
35
35
  def parse_arguments!
36
36
  option_parser.parse! @arguments
37
37
  rescue OptionParser::InvalidOption => e
38
- fail ArgumentError, option_parser
38
+ raise ArgumentError, option_parser
39
39
  end
40
40
 
41
41
  def run
data/lib/uh/wm/client.rb CHANGED
@@ -66,6 +66,20 @@ module Uh
66
66
  @window.focus
67
67
  self
68
68
  end
69
+
70
+ def kill
71
+ if @window.icccm_wm_protocols.include? :WM_DELETE_WINDOW
72
+ @window.icccm_wm_delete
73
+ else
74
+ @window.kill
75
+ end
76
+ self
77
+ end
78
+
79
+ def kill!
80
+ window.kill
81
+ self
82
+ end
69
83
  end
70
84
  end
71
85
  end
data/lib/uh/wm/env.rb CHANGED
@@ -16,13 +16,14 @@ module Uh
16
16
 
17
17
  extend Forwardable
18
18
  def_delegator :logger, :info, :log
19
+ def_delegator :logger, :fatal, :log_fatal
19
20
  def_delegator :logger, :error, :log_error
20
21
  def_delegator :logger, :debug, :log_debug
21
22
  def_delegators :@output, :print, :puts
22
23
 
23
24
  attr_reader :output, :keybinds
24
25
  attr_accessor :verbose, :debug, :rc_path, :modifier, :worker,
25
- :layout, :layout_class
26
+ :layout, :layout_class, :rules, :launch
26
27
 
27
28
  def initialize output
28
29
  @output = output
@@ -30,6 +31,7 @@ module Uh
30
31
  @modifier = MODIFIER
31
32
  @keybinds = KEYBINDS.dup
32
33
  @worker = :block
34
+ @rules = {}
33
35
  end
34
36
 
35
37
  def verbose?
@@ -2,7 +2,7 @@ module Uh
2
2
  module WM
3
3
  module EnvLogging
4
4
  extend Forwardable
5
- def_delegators :@env, :log, :log_error, :log_debug
5
+ def_delegators :@env, :log, :log_fatal, :log_error, :log_debug
6
6
  end
7
7
  end
8
8
  end
@@ -0,0 +1,73 @@
1
+ module Uh
2
+ module WM
3
+ class Launcher
4
+ class << self
5
+ def launch runner, instructions
6
+ new(runner.actions, runner.rules, runner.method(:run_until))
7
+ .launch instructions
8
+ end
9
+ end
10
+
11
+ def initialize actions, rules, run_until
12
+ @actions = actions
13
+ @rules = rules
14
+ @run_until = run_until
15
+ end
16
+
17
+ def launch instructions
18
+ with_clean_rules do
19
+ rules = @rules
20
+ DSL.new(@actions).evaluate(instructions).each do |m, args, block|
21
+ if m == :execute!
22
+ @rules[//] = -> { rules.clear }
23
+ @actions.execute *args, &block
24
+ @run_until.call { @rules.empty? }
25
+ else
26
+ @actions.send m, *args, &block
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+
33
+ private
34
+
35
+ def with_clean_rules
36
+ original_rules = @rules.dup
37
+ @rules.clear
38
+ yield
39
+ @rules.clear
40
+ @rules.merge! original_rules
41
+ end
42
+
43
+
44
+ class DSL
45
+ def initialize actions
46
+ @actions = actions
47
+ @messages = []
48
+ end
49
+
50
+ def evaluate instructions
51
+ instance_eval &instructions
52
+ self
53
+ end
54
+
55
+ def each
56
+ @messages.each { |m| yield *m }
57
+ end
58
+
59
+ def method_missing m, *args, &block
60
+ if respond_to? m
61
+ @messages << [m, args, block]
62
+ else
63
+ super
64
+ end
65
+ end
66
+
67
+ def respond_to_missing? m, _
68
+ m == :execute! || @actions.respond_to?(m) || super
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -22,6 +22,8 @@ module Uh
22
22
 
23
23
  def evaluate code, path
24
24
  instance_eval code, path
25
+ rescue ::StandardError, ::ScriptError => e
26
+ raise RunControlEvaluationError, e.message, e.backtrace
25
27
  end
26
28
 
27
29
  def modifier mod
@@ -48,6 +50,14 @@ module Uh
48
50
  @env.worker = [type, options]
49
51
  end
50
52
 
53
+ def rule selectors = '', &block
54
+ [*selectors].each { |selector| @env.rules[/\A#{selector}/i] = block }
55
+ end
56
+
57
+ def launch &block
58
+ @env.launch = block
59
+ end
60
+
51
61
 
52
62
  private
53
63
 
data/lib/uh/wm/runner.rb CHANGED
@@ -17,7 +17,7 @@ module Uh
17
17
  extend Forwardable
18
18
  def_delegator :@env, :layout
19
19
 
20
- attr_reader :env, :events, :actions
20
+ attr_reader :env, :events, :actions, :rules
21
21
 
22
22
  def initialize env, manager: nil, stopped: false
23
23
  @env = env
@@ -25,6 +25,7 @@ module Uh
25
25
  @manager = manager
26
26
  @actions = ActionsHandler.new(@env, @events)
27
27
  @stopped = stopped
28
+ @rules = @env.rules
28
29
  end
29
30
 
30
31
  def stopped?
@@ -44,7 +45,7 @@ module Uh
44
45
  end
45
46
 
46
47
  def register_event_hooks
47
- %w[runner manager layout keybinds]
48
+ %w[runner manager layout keybinds rules launcher]
48
49
  .map { |e| "register_#{e}_hooks".to_sym }
49
50
  .each { |e| send e }
50
51
  end
@@ -74,7 +75,7 @@ module Uh
74
75
  end
75
76
 
76
77
  def terminate
77
- log "Terminating..."
78
+ log 'Terminating...'
78
79
  manager.disconnect
79
80
  end
80
81
 
@@ -92,9 +93,15 @@ module Uh
92
93
  @events.on :connected do |display|
93
94
  log "Connected to X server on `#{display}'"
94
95
  end
95
- @events.on(:disconnected) { log "Disconnected from X server" }
96
+ @events.on(:disconnected) { log 'Disconnected from X server' }
96
97
  @events.on(:xevent) { |event| XEventLogger.new(env).log_event event }
97
- @events.on(:xerror) { |*error| XEventLogger.new(env).log_xerror *error }
98
+ @events.on(:xerror) do |*error|
99
+ if error.none?
100
+ log_fatal 'Fatal X IO Error received'
101
+ else
102
+ XEventLogger.new(env).log_xerror *error
103
+ end
104
+ end
98
105
  end
99
106
 
100
107
  def register_layout_hooks
@@ -130,6 +137,20 @@ module Uh
130
137
  end
131
138
  end
132
139
 
140
+ def register_rules_hooks
141
+ @events.on :manage do |client|
142
+ @rules.each do |selector, code|
143
+ @actions.evaluate code if client.wclass =~ selector
144
+ end
145
+ end
146
+ end
147
+
148
+ def register_launcher_hooks
149
+ @events.on :connected do
150
+ Launcher.launch(self, @env.launch) if @env.launch
151
+ end
152
+ end
153
+
133
154
 
134
155
  class XEventLogger
135
156
  include EnvLogging
@@ -1,4 +1,5 @@
1
1
  require 'uh'
2
+ require 'uh/wm/testing/x_client'
2
3
 
3
4
  module Uh
4
5
  module WM
@@ -8,6 +9,29 @@ module Uh
8
9
  QUIT_KEYBINDING = 'alt+shift+q'.freeze
9
10
  LOG_READY = 'Working events'.freeze
10
11
 
12
+ def build_regexp pattern, options
13
+ Regexp.new(pattern, options.each_char.inject(0) do |m, e|
14
+ m | case e
15
+ when ?i then Regexp::IGNORECASE
16
+ when ?m then Regexp::MULTILINE
17
+ when ?x then Regexp::EXTENDED
18
+ end
19
+ end)
20
+ end
21
+
22
+ def icccm_window_start
23
+ @icccm_window = ChildProcess.build(*%w[xmessage window])
24
+ @icccm_window.start
25
+ end
26
+
27
+ def icccm_window_ensure_stop
28
+ @icccm_window.stop
29
+ end
30
+
31
+ def icccm_window_name
32
+ 'xmessage'
33
+ end
34
+
11
35
  def uhwm_run options = '-v'
12
36
  command = %w[uhwm]
13
37
  command << options if options
@@ -39,7 +63,7 @@ module Uh
39
63
  end
40
64
  value
41
65
  rescue TimeoutError => e
42
- fail <<-eoh
66
+ raise <<-eoh
43
67
  expected `#{message}' (#{times}) not seen after #{e.timeout} seconds in:
44
68
  ```\n#{output.call.lines.map { |e| " #{e}" }.join} ```
45
69
  eoh
@@ -85,21 +109,19 @@ expected `#{message}' (#{times}) not seen after #{e.timeout} seconds in:
85
109
  .grep /Mask\z/
86
110
  end
87
111
 
88
- def x_key k
112
+ def x_key *k, delay: 12
113
+ k = k.join " key --delay #{delay} "
89
114
  fail "cannot simulate X key `#{k}'" unless system "xdotool key #{k}"
90
115
  end
91
116
 
92
- def x_socket_check pid
93
- case RbConfig::CONFIG['host_os']
94
- when /linux/
95
- `netstat -xp 2> /dev/null`.lines.grep /\s+#{pid}\/ruby/
96
- else
97
- `sockstat -u`.lines.grep /\s+ruby.+\s+#{pid}/
98
- end.any?
99
- end
100
-
101
- def x_window_map_state window_id
102
- `xwininfo -id #{window_id}`[/Map State: (\w+)/, 1]
117
+ def x_window_map_state window_selector
118
+ select_args = case window_selector
119
+ when Integer then "-id #{window_selector}"
120
+ when String then "-name #{window_selector}"
121
+ else fail ArgumentError,
122
+ "not an Integer nor a String: `#{window_selector.inspect}'"
123
+ end
124
+ `xwininfo #{select_args} 2> /dev/null`[/Map State: (\w+)/, 1]
103
125
  end
104
126
 
105
127
 
@@ -116,7 +138,7 @@ expected `#{message}' (#{times}) not seen after #{e.timeout} seconds in:
116
138
  end
117
139
  end
118
140
  rescue Timeout::Error
119
- fail TimeoutError.new(message % timeout, timeout)
141
+ raise TimeoutError.new(message % timeout, timeout)
120
142
  end
121
143
 
122
144
 
@@ -128,57 +150,6 @@ expected `#{message}' (#{times}) not seen after #{e.timeout} seconds in:
128
150
  @timeout = timeout
129
151
  end
130
152
  end
131
-
132
- class XClient
133
- attr_reader :name
134
-
135
- def initialize name = object_id
136
- @name = "#{self.class.name.split('::').last}/#{name}"
137
- @geo = Geo.new(0, 0, 640, 480)
138
- @display = Display.new.tap { |o| o.open }
139
- end
140
-
141
- def terminate
142
- @display.close
143
- end
144
-
145
- def sync
146
- @display.sync false
147
- end
148
-
149
- def window
150
- @window ||= @display.create_window(@geo).tap { |o| o.name = @name }
151
- end
152
-
153
- def window_id
154
- @window.id
155
- end
156
-
157
- def window_name
158
- @name
159
- end
160
-
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 }
168
- window.map
169
- self
170
- end
171
-
172
- def unmap
173
- window.unmap
174
- self
175
- end
176
-
177
- def destroy
178
- window.destroy
179
- self
180
- end
181
- end
182
153
  end
183
154
  end
184
155
  end
@@ -0,0 +1,63 @@
1
+ require 'uh'
2
+
3
+ module Uh
4
+ module WM
5
+ module Testing
6
+ class XClient
7
+ attr_reader :name
8
+
9
+ def initialize name = object_id
10
+ @name = "#{self.class.name.split('::').last}/#{name}"
11
+ @geo = Geo.new(0, 0, 640, 480)
12
+ @display = Display.new.tap &:open
13
+ end
14
+
15
+ def terminate
16
+ @display.close
17
+ end
18
+
19
+ def sync
20
+ @display.sync false
21
+ self
22
+ end
23
+
24
+ def window
25
+ @window ||= @display.create_window(@geo).tap { |o| o.name = @name }
26
+ end
27
+
28
+ def window_id
29
+ @window.id
30
+ end
31
+
32
+ def window_name
33
+ @name
34
+ end
35
+
36
+ def window_name= name
37
+ @name = window.name = name
38
+ window.name
39
+ end
40
+
41
+ def window_class= wclass
42
+ window.wclass = [wclass] * 2
43
+ end
44
+
45
+ def map times: 1
46
+ times.times { window.map }
47
+ window.map
48
+ self
49
+ end
50
+
51
+ def unmap
52
+ window.unmap
53
+ self
54
+ end
55
+
56
+ def destroy
57
+ window.destroy
58
+ self
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
data/lib/uh/wm/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Uh
2
2
  module WM
3
- VERSION = '0.0.6'
3
+ VERSION = '0.0.7'
4
4
  end
5
5
  end
@@ -9,7 +9,7 @@ module Uh
9
9
  end
10
10
 
11
11
  def watch io
12
- @ios << io
12
+ @ios << io.to_io
13
13
  end
14
14
 
15
15
  CALLBACKS.each do |m|
@@ -2,7 +2,9 @@ module Uh
2
2
  module WM
3
3
  module Workers
4
4
  class Mux < Base
5
- def initialize timeout: 1
5
+ TIMEOUT_DEFAULT = 1
6
+
7
+ def initialize timeout: TIMEOUT_DEFAULT
6
8
  super
7
9
  @timeout = timeout
8
10
  end
data/lib/uh/wm.rb CHANGED
@@ -10,6 +10,7 @@ require 'uh/wm/cli'
10
10
  require 'uh/wm/client'
11
11
  require 'uh/wm/dispatcher'
12
12
  require 'uh/wm/env'
13
+ require 'uh/wm/launcher'
13
14
  require 'uh/wm/logger_formatter'
14
15
  require 'uh/wm/manager'
15
16
  require 'uh/wm/run_control'
@@ -21,9 +22,10 @@ require 'uh/wm/workers/mux'
21
22
 
22
23
  module Uh
23
24
  module WM
24
- Error = Class.new(StandardError)
25
- RuntimeError = Class.new(RuntimeError)
26
- ArgumentError = Class.new(Error)
25
+ Error = Class.new(StandardError)
26
+ RuntimeError = Class.new(RuntimeError)
27
+ ArgumentError = Class.new(Error)
28
+ RunControlEvaluationError = Class.new(RuntimeError)
27
29
 
28
30
  class OtherWMRunningError < RuntimeError
29
31
  def message