ttytest2 0.9.8 → 0.9.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,79 +1,79 @@
1
- # frozen_string_literal: true
2
-
3
- require 'open3'
4
- require 'securerandom'
5
-
6
- require 'ttytest/terminal'
7
- require 'ttytest/tmux/session'
8
-
9
- module TTYtest
10
- module Tmux
11
- # tmux driver
12
- class Driver
13
- COMMAND = 'tmux'
14
- SOCKET_NAME = 'ttytest'
15
- REQUIRED_TMUX_VERSION = '1.8'
16
- DEFAULT_CONFING_FILE_PATH = File.expand_path('tmux.conf', __dir__)
17
- SLEEP_INFINITY = 'read x < /dev/fd/1'
18
-
19
- class TmuxError < StandardError; end
20
-
21
- def initialize(
22
- debug: false,
23
- command: COMMAND,
24
- socket_name: SOCKET_NAME,
25
- config_file_path: DEFAULT_CONFING_FILE_PATH
26
- )
27
- @debug = debug
28
- @tmux_cmd = command
29
- @socket_name = socket_name
30
- @config_file_path = config_file_path
31
- end
32
-
33
- def new_terminal(cmd, width: 80, height: 24)
34
- cmd = "#{cmd}\n#{SLEEP_INFINITY}"
35
-
36
- session_name = "ttytest-#{SecureRandom.uuid}"
37
- tmux(*%W[-f #{@config_file_path} new-session -s #{session_name} -d -x #{width} -y #{height} #{cmd}])
38
- session = Session.new(self, session_name)
39
- Terminal.new(session)
40
- end
41
-
42
- # @api private
43
- def tmux(*args)
44
- ensure_available
45
- puts "tmux(#{args.inspect[1...-1]})" if debug?
46
-
47
- stdout, stderr, status = Open3.capture3(@tmux_cmd, '-L', SOCKET_NAME, *args)
48
- raise TmuxError, "tmux(#{args.inspect[1...-1]}) failed\n#{stderr}" unless status.success?
49
-
50
- stdout
51
- end
52
-
53
- def available?
54
- return false unless tmux_version
55
-
56
- @available ||= (Gem::Version.new(tmux_version) >= Gem::Version.new(REQUIRED_TMUX_VERSION))
57
- end
58
-
59
- private
60
-
61
- def debug?
62
- @debug
63
- end
64
-
65
- def ensure_available
66
- return if available?
67
- raise TmuxError, 'Running `tmux -V` to determine version failed. Is tmux installed?' unless tmux_version
68
-
69
- raise TmuxError, "tmux version #{tmux_version} does not meet requirement >= #{REQUIRED_TMUX_VERSION}"
70
- end
71
-
72
- def tmux_version
73
- @tmux_version ||= `#{@tmux_cmd} -V`[/tmux (\d+.\d+)/, 1]
74
- rescue Errno::ENOENT
75
- nil
76
- end
77
- end
78
- end
79
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+ require 'securerandom'
5
+
6
+ require 'ttytest/terminal'
7
+ require 'ttytest/tmux/session'
8
+
9
+ module TTYtest
10
+ module Tmux
11
+ # tmux driver
12
+ class Driver
13
+ COMMAND = 'tmux'
14
+ SOCKET_NAME = 'ttytest'
15
+ REQUIRED_TMUX_VERSION = '1.8'
16
+ DEFAULT_CONFING_FILE_PATH = File.expand_path('tmux.conf', __dir__)
17
+ SLEEP_INFINITY = 'read x < /dev/fd/1'
18
+
19
+ class TmuxError < StandardError; end
20
+
21
+ def initialize(
22
+ debug: false,
23
+ command: COMMAND,
24
+ socket_name: SOCKET_NAME,
25
+ config_file_path: DEFAULT_CONFING_FILE_PATH
26
+ )
27
+ @debug = debug
28
+ @tmux_cmd = command
29
+ @socket_name = socket_name
30
+ @config_file_path = config_file_path
31
+ end
32
+
33
+ def new_terminal(cmd, width: 80, height: 24)
34
+ cmd = "#{cmd}\n#{SLEEP_INFINITY}"
35
+
36
+ session_name = "ttytest-#{SecureRandom.uuid}"
37
+ tmux(*%W[-f #{@config_file_path} new-session -s #{session_name} -d -x #{width} -y #{height} #{cmd}])
38
+ session = Session.new(self, session_name)
39
+ Terminal.new(session)
40
+ end
41
+
42
+ # @api private
43
+ def tmux(*args)
44
+ ensure_available
45
+ puts "tmux(#{args.inspect[1...-1]})" if debug?
46
+
47
+ stdout, stderr, status = Open3.capture3(@tmux_cmd, '-L', SOCKET_NAME, *args)
48
+ raise TmuxError, "tmux(#{args.inspect[1...-1]}) failed\n#{stderr}" unless status.success?
49
+
50
+ stdout
51
+ end
52
+
53
+ def available?
54
+ return false unless tmux_version
55
+
56
+ @available ||= (Gem::Version.new(tmux_version) >= Gem::Version.new(REQUIRED_TMUX_VERSION))
57
+ end
58
+
59
+ private
60
+
61
+ def debug?
62
+ @debug
63
+ end
64
+
65
+ def ensure_available
66
+ return if available?
67
+ raise TmuxError, 'Running `tmux -V` to determine version failed. Is tmux installed?' unless tmux_version
68
+
69
+ raise TmuxError, "tmux version #{tmux_version} does not meet requirement >= #{REQUIRED_TMUX_VERSION}"
70
+ end
71
+
72
+ def tmux_version
73
+ @tmux_version ||= `#{@tmux_cmd} -V`[/tmux (\d+.\d+)/, 1]
74
+ rescue Errno::ENOENT
75
+ nil
76
+ end
77
+ end
78
+ end
79
+ end
@@ -1,159 +1,163 @@
1
- # frozen_string_literal: true
2
-
3
- module TTYtest
4
- module Tmux
5
- # represents a tmux session and how to send output to the current tmux session
6
- class Session
7
- # @api private
8
- def initialize(driver, name)
9
- @driver = driver
10
- @name = name
11
-
12
- # ObjectSpace.define_finalizer(self, self.class.finalize(driver, name))
13
- end
14
-
15
- # @api private
16
- # def self.finalize(driver, name)
17
- # proc { driver.tmux(*%W[kill-session -t #{name}]) }
18
- # end
19
-
20
- def capture
21
- contents = driver.tmux(*%W[capture-pane -t #{name} -p])
22
- str = driver.tmux(*%W[display-message -t #{name} -p
23
- #\{cursor_x},#\{cursor_y},#\{cursor_flag},#\{pane_width},#\{pane_height},#\{pane_dead},#\{pane_dead_status},])
24
- x, y, cursor_flag, width, height, pane_dead, pane_dead_status, _newline = str.split(',')
25
-
26
- if pane_dead == '1'
27
- raise Driver::TmuxError,
28
- "Tmux pane has died\nCommand exited with status: #{pane_dead_status}\nEntire screen:\n#{contents}"
29
- end
30
-
31
- TTYtest::Capture.new(
32
- contents.chomp("\n"),
33
- cursor_x: x.to_i,
34
- cursor_y: y.to_i,
35
- width: width.to_i,
36
- height: height.to_i,
37
- cursor_visible: (cursor_flag != '0')
38
- )
39
- end
40
-
41
- # Send the array of keys as a string literal to tmux.
42
- # Will not be interpreted as send-keys values like Enter, Escape, DC for Delete, etc.
43
- # @param [%w()] keys the keys to send to tmux
44
- def send_keys(*keys)
45
- driver.tmux(*%W[send-keys -t #{name} -l], *keys)
46
- end
47
-
48
- # Send a string of keys one character at a time as literals to tmux.
49
- # @param [String] keys the keys to send one at a time to tmux
50
- def send_keys_one_at_a_time(keys)
51
- keys.split('').each do |key|
52
- driver.tmux(*%W[send-keys -t #{name} -l], key)
53
- end
54
- end
55
-
56
- def send_newline
57
- driver.tmux(*%W[send-keys -t #{name} -l], %(\n))
58
- end
59
-
60
- def send_newlines(number_of_times)
61
- while number_of_times.positive?
62
- send_newline
63
- number_of_times -= 1
64
- end
65
- end
66
-
67
- def send_delete
68
- send_keys_exact(%(DC))
69
- end
70
-
71
- def send_deletes(number_of_times)
72
- while number_of_times.positive?
73
- send_delete
74
- number_of_times -= 1
75
- end
76
- end
77
-
78
- def send_backspace
79
- send_keys_exact(%(BSpace))
80
- end
81
-
82
- def send_backspaces(number_of_times)
83
- while number_of_times.positive?
84
- send_backspace
85
- number_of_times -= 1
86
- end
87
- end
88
-
89
- def send_right_arrow
90
- send_keys(TTYtest::RIGHT_ARROW)
91
- end
92
-
93
- def send_right_arrows(number_of_times)
94
- while number_of_times.positive?
95
- send_right_arrow
96
- number_of_times -= 1
97
- end
98
- end
99
-
100
- def send_left_arrow
101
- send_keys(TTYtest::LEFT_ARROW)
102
- end
103
-
104
- def send_left_arrows(number_of_times)
105
- while number_of_times.positive?
106
- send_left_arrow
107
- number_of_times -= 1
108
- end
109
- end
110
-
111
- def send_up_arrow
112
- send_keys(TTYtest::UP_ARROW)
113
- end
114
-
115
- def send_up_arrows(number_of_times)
116
- while number_of_times.positive?
117
- send_up_arrow
118
- number_of_times -= 1
119
- end
120
- end
121
-
122
- def send_down_arrow
123
- send_keys(TTYtest::DOWN_ARROW)
124
- end
125
-
126
- def send_down_arrows(number_of_times)
127
- while number_of_times.positive?
128
- send_down_arrow
129
- number_of_times -= 1
130
- end
131
- end
132
-
133
- # Useful to send send-keys commands to tmux without sending them as a string literal.
134
- # So you can send Escape for escape key, DC for delete, etc.
135
- # Uses the same key bindings as bind-key as well. C-c represents Ctrl + C keys, F1 is F1 key, etc.
136
- # @param [String] keys the keys to send to tmux
137
- def send_keys_exact(keys)
138
- driver.tmux(*%W[send-keys -t #{name}], keys)
139
- end
140
-
141
- def send_home
142
- send_keys_exact(%(Home))
143
- end
144
-
145
- def send_end
146
- send_keys_exact(%(End))
147
- end
148
-
149
- def send_clear
150
- send_keys_one_at_a_time(TTYtest::CLEAR)
151
- send_newline
152
- end
153
-
154
- private
155
-
156
- attr_reader :driver, :name
157
- end
158
- end
159
- end
1
+ # frozen_string_literal: true
2
+
3
+ module TTYtest
4
+ module Tmux
5
+ # represents a tmux session and how to send output to the current tmux session
6
+ class Session
7
+ # @api private
8
+ def initialize(driver, name)
9
+ @driver = driver
10
+ @name = name
11
+
12
+ # ObjectSpace.define_finalizer(self, self.class.finalize(driver, name))
13
+ end
14
+
15
+ # @api private
16
+ # def self.finalize(driver, name)
17
+ # proc { driver.tmux(*%W[kill-session -t #{name}]) }
18
+ # end
19
+
20
+ def capture
21
+ contents = driver.tmux(*%W[capture-pane -t #{name} -p])
22
+ str = driver.tmux(*%W[display-message -t #{name} -p
23
+ #\{cursor_x},#\{cursor_y},#\{cursor_flag},#\{pane_width},#\{pane_height},#\{pane_dead},#\{pane_dead_status},])
24
+ x, y, cursor_flag, width, height, pane_dead, pane_dead_status, _newline = str.split(',')
25
+
26
+ if pane_dead == '1'
27
+ raise Driver::TmuxError,
28
+ "Tmux pane has died\nCommand exited with status: #{pane_dead_status}\nEntire screen:\n#{contents}"
29
+ end
30
+
31
+ TTYtest::Capture.new(
32
+ contents.chomp("\n"),
33
+ cursor_x: x.to_i,
34
+ cursor_y: y.to_i,
35
+ width: width.to_i,
36
+ height: height.to_i,
37
+ cursor_visible: (cursor_flag != '0')
38
+ )
39
+ end
40
+
41
+ # Send the array of keys as a string literal to tmux.
42
+ # Will not be interpreted as send-keys values like Enter, Escape, DC for Delete, etc.
43
+ # @param [%w()] keys the keys to send to tmux
44
+ def send_keys(*keys)
45
+ driver.tmux(*%W[send-keys -t #{name} -l], *keys)
46
+ end
47
+
48
+ # Send a string of keys one character at a time as literals to tmux.
49
+ # @param [String] keys the keys to send one at a time to tmux
50
+ def send_keys_one_at_a_time(keys)
51
+ keys.split('').each do |key|
52
+ driver.tmux(*%W[send-keys -t #{name} -l], key)
53
+ end
54
+ end
55
+
56
+ def send_newline
57
+ driver.tmux(*%W[send-keys -t #{name} -l], %(\n))
58
+ end
59
+
60
+ def send_newlines(number_of_times)
61
+ while number_of_times.positive?
62
+ send_newline
63
+ number_of_times -= 1
64
+ end
65
+ end
66
+
67
+ def send_delete
68
+ send_keys_exact(%(DC))
69
+ end
70
+
71
+ def send_deletes(number_of_times)
72
+ while number_of_times.positive?
73
+ send_delete
74
+ number_of_times -= 1
75
+ end
76
+ end
77
+
78
+ def send_backspace
79
+ send_keys_exact(%(BSpace))
80
+ end
81
+
82
+ def send_backspaces(number_of_times)
83
+ while number_of_times.positive?
84
+ send_backspace
85
+ number_of_times -= 1
86
+ end
87
+ end
88
+
89
+ def send_right_arrow
90
+ send_keys(TTYtest::RIGHT_ARROW)
91
+ end
92
+
93
+ def send_right_arrows(number_of_times)
94
+ while number_of_times.positive?
95
+ send_right_arrow
96
+ number_of_times -= 1
97
+ end
98
+ end
99
+
100
+ def send_left_arrow
101
+ send_keys(TTYtest::LEFT_ARROW)
102
+ end
103
+
104
+ def send_left_arrows(number_of_times)
105
+ while number_of_times.positive?
106
+ send_left_arrow
107
+ number_of_times -= 1
108
+ end
109
+ end
110
+
111
+ def send_up_arrow
112
+ send_keys(TTYtest::UP_ARROW)
113
+ end
114
+
115
+ def send_up_arrows(number_of_times)
116
+ while number_of_times.positive?
117
+ send_up_arrow
118
+ number_of_times -= 1
119
+ end
120
+ end
121
+
122
+ def send_down_arrow
123
+ send_keys(TTYtest::DOWN_ARROW)
124
+ end
125
+
126
+ def send_down_arrows(number_of_times)
127
+ while number_of_times.positive?
128
+ send_down_arrow
129
+ number_of_times -= 1
130
+ end
131
+ end
132
+
133
+ # Useful to send send-keys commands to tmux without sending them as a string literal.
134
+ # So you can send Escape for escape key, DC for delete, etc.
135
+ # Uses the same key bindings as bind-key as well. C-c represents Ctrl + C keys, F1 is F1 key, etc.
136
+ # @param [String] keys the keys to send to tmux
137
+ def send_keys_exact(keys)
138
+ driver.tmux(*%W[send-keys -t #{name}], keys)
139
+ end
140
+
141
+ def send_home
142
+ send_keys_exact(TTYtest::HOME_KEY)
143
+ end
144
+
145
+ def send_end
146
+ send_keys_exact(TTYtest::END_KEY)
147
+ end
148
+
149
+ def send_clear
150
+ send_keys_one_at_a_time(TTYtest::CLEAR)
151
+ send_newline
152
+ end
153
+
154
+ def send_escape
155
+ send_keys_exact(%(Escape))
156
+ end
157
+
158
+ private
159
+
160
+ attr_reader :driver, :name
161
+ end
162
+ end
163
+ end
@@ -1,3 +1,3 @@
1
- set -g status off
2
- set -g remain-on-exit on
3
- set -g default-shell /bin/sh
1
+ set -g status off
2
+ set -g remain-on-exit on
3
+ set -g default-shell /bin/sh
@@ -1,5 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
- module TTYtest
4
- VERSION = '0.9.8'
5
- end
1
+ # frozen_string_literal: true
2
+
3
+ module TTYtest
4
+ VERSION = '0.9.10'
5
+ end
data/lib/ttytest.rb CHANGED
@@ -1,27 +1,27 @@
1
- # frozen_string_literal: true
2
-
3
- require 'forwardable'
4
- require 'ttytest/constants'
5
- require 'ttytest/tmux/driver'
6
- require 'ttytest/tmux/session'
7
-
8
- # ttytest2 main module
9
- module TTYtest
10
- class << self
11
- attr_accessor :driver, :default_max_wait_time
12
-
13
- extend Forwardable
14
- # @!method new_terminal(command, width: 80, height: 24)
15
- # Create a new terminal through the current driver.
16
- # @param [String] command a valid shell command to run
17
- # @param [Integer] width width of the new terminal
18
- # @param [Integer] height height of the new terminal
19
- # @return [Terminal] a new terminal running the specified command
20
- def_delegators :driver, :new_terminal
21
- end
22
-
23
- class MatchError < StandardError; end
24
-
25
- self.driver = TTYtest::Tmux::Driver.new
26
- self.default_max_wait_time = 2
27
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'ttytest/constants'
5
+ require 'ttytest/tmux/driver'
6
+ require 'ttytest/tmux/session'
7
+
8
+ # ttytest2 main module
9
+ module TTYtest
10
+ class << self
11
+ attr_accessor :driver, :default_max_wait_time
12
+
13
+ extend Forwardable
14
+ # @!method new_terminal(command, width: 80, height: 24)
15
+ # Create a new terminal through the current driver.
16
+ # @param [String] command a valid shell command to run
17
+ # @param [Integer] width width of the new terminal
18
+ # @param [Integer] height height of the new terminal
19
+ # @return [Terminal] a new terminal running the specified command
20
+ def_delegators :driver, :new_terminal
21
+ end
22
+
23
+ class MatchError < StandardError; end
24
+
25
+ self.driver = TTYtest::Tmux::Driver.new
26
+ self.default_max_wait_time = 2
27
+ end
data/notes.txt CHANGED
@@ -1,7 +1,7 @@
1
- to push new version to github
2
- git tag v0.9.8
3
- git push origin --tags
4
-
5
- to push new version to rubygems.org
6
- gem build ttytest2.gemspec
7
- gem push ttytest2-0.9.8.gem
1
+ to push new version to github
2
+ git tag v0.9.10
3
+ git push origin --tags
4
+
5
+ to push new version to rubygems.org
6
+ gem build ttytest2.gemspec
7
+ gem push ttytest2-0.9.10.gem
data/ttytest2.gemspec CHANGED
@@ -1,31 +1,31 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'ttytest/version'
6
-
7
- Gem::Specification.new do |spec|
8
- spec.name = 'ttytest2'
9
- spec.version = TTYtest::VERSION
10
- spec.authors = ['Alex Eski']
11
- spec.email = ['alexeski@gmail.com']
12
-
13
- spec.summary = 'ttytest2 is an integration test framework for interactive console (tty) applications'
14
- spec.description = 'ttytest2 runs shell/cli applications inside of tmux and allows you to make assertions on what the output should be'
15
- spec.homepage = 'https://github.com/a-eski/ttytest2'
16
- spec.license = 'MIT'
17
-
18
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
- f.match(%r{^(test|spec|features)/})
20
- end
21
- spec.bindir = 'exe'
22
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
- spec.require_paths = ['lib']
24
-
25
- spec.required_ruby_version = '>= 3.2.3'
26
-
27
- spec.add_development_dependency 'bundler', '~> 2.4'
28
- spec.add_development_dependency 'minitest', '~> 5.0'
29
- spec.add_development_dependency 'rake', '~> 13.0'
30
- spec.add_development_dependency 'yard', '~> 0.9'
31
- end
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'ttytest/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'ttytest2'
9
+ spec.version = TTYtest::VERSION
10
+ spec.authors = ['Alex Eski']
11
+ spec.email = ['alexeski@gmail.com']
12
+
13
+ spec.summary = 'ttytest2 is an integration test framework for interactive console (tty) applications'
14
+ spec.description = 'ttytest2 runs shell/cli applications inside of tmux and allows you to make assertions on what the output should be'
15
+ spec.homepage = 'https://github.com/a-eski/ttytest2'
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
21
+ spec.bindir = 'exe'
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.required_ruby_version = '>= 3.2.3'
26
+
27
+ spec.add_development_dependency 'bundler', '~> 2.4'
28
+ spec.add_development_dependency 'minitest', '~> 5.0'
29
+ spec.add_development_dependency 'rake', '~> 13.0'
30
+ spec.add_development_dependency 'yard', '~> 0.9'
31
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ttytest2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.8
4
+ version: 0.9.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Eski
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-12-15 00:00:00.000000000 Z
11
+ date: 2024-12-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -111,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
111
  - !ruby/object:Gem::Version
112
112
  version: '0'
113
113
  requirements: []
114
- rubygems_version: 3.5.23
114
+ rubygems_version: 3.4.20
115
115
  signing_key:
116
116
  specification_version: 4
117
117
  summary: ttytest2 is an integration test framework for interactive console (tty) applications