ttytest2 0.9.8 → 0.9.9

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.
@@ -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.9'
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.9
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.9.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.9
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-20 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