ttytest 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 58e7cfbcc044cae295f1667619da61dbff469b00
4
+ data.tar.gz: b7a3fde532a3a2eb07098e648fefb472a7996d3f
5
+ SHA512:
6
+ metadata.gz: 9f99ff25102a617841af7ede0537860c71a431826082666d3fe605d55fad9208ff2c2edcc3eb6d2ee505bf3d56f290f52c287c0ca505e53e0ba90f5bf3f99404
7
+ data.tar.gz: a9acb9d56b24443740c51df19f338aaa77279e56876b4ac170976922605d783db214d7ae6f829e0d06e2d2750e802bc71e3c7d8aee3a84a76dc13916127f8efe
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ pkg/
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ dist: trusty
2
+ language: ruby
3
+ cache: bundler
4
+ sudo: false
5
+ before_script:
6
+ - tmux -V
7
+ rvm:
8
+ - 2.2.6
9
+ - 2.3.3
10
+ - 2.4.0
11
+ addons:
12
+ apt:
13
+ packages:
14
+ - tmux
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ TTYtest is an integration test framework for interactive tty applications. It's like [capybara](https://github.com/teamcapybara/capybara) for the terminal.
2
+
3
+ [![Build Status](https://travis-ci.org/jhawthorn/ttytest.svg?branch=master)](https://travis-ci.org/jhawthorn/ttytest)
4
+
5
+ ## Requirements
6
+
7
+ * tmux >= 1.8
8
+ * Ruby >= 2.1
9
+
10
+ ## Usage
11
+
12
+ ``` ruby
13
+ class TTYtestTest < Minitest::Test
14
+ def test_shell_hello_world
15
+ @tty = TTYtest.driver.new_terminal(%{PS1='$ ' /bin/sh})
16
+ @tty.assert_row(0, '$')
17
+
18
+ @tty.send_raw('echo "Hello, world"', "\n")
19
+
20
+ @tty.assert_row(0, '$ echo "Hello, world"')
21
+ @tty.assert_row(1, 'Hello, world')
22
+ @tty.assert_cursor_position(2, 2)
23
+ end
24
+ end
25
+ ```
26
+
27
+ ## TravisCI
28
+
29
+ TTYtest can run on [TravisCI](https://travis-ci.org/), but the version of tmux made available with their default ubuntu 12.04 environment is too old. However the TravisCI ubuntu 14.04 "trusty" image provides tmux 1.8, which works great.
30
+
31
+ Ensure the following is in your `.travis.yml` (see [this project's .travis.yml](./.travis.yml) for an example)
32
+
33
+ ``` yaml
34
+ dist: trusty
35
+ addons:
36
+ apt:
37
+ packages:
38
+ - tmux
39
+ ```
40
+
41
+ ## Contributing
42
+
43
+ Bug reports and pull requests are welcome on GitHub at https://github.com/jhawthorn/execjs-fastnode.
44
+
45
+ ## License
46
+
47
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/lib/ttytest.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'ttytest/tmux/driver'
2
+ require 'ttytest/tmux/session'
3
+
4
+ module TTYtest
5
+ class << self
6
+ attr_accessor :driver
7
+ attr_accessor :default_max_wait_time
8
+ end
9
+
10
+ class MatchError < StandardError; end
11
+
12
+ self.driver = TTYtest::Tmux::Driver.new
13
+ self.default_max_wait_time = 2
14
+ end
@@ -0,0 +1,36 @@
1
+ module TTYtest
2
+ class Capture
3
+ include TTYtest::Matchers
4
+
5
+ attr_reader :cursor_x, :cursor_y, :cursor_visible
6
+
7
+ def initialize(contents, cursor_x: 0, cursor_y: 0, cursor_visible: true)
8
+ @rows = (contents+"\nEND").split("\n")[0...-1].map do |row|
9
+ row || ""
10
+ end
11
+ @cursor_x = cursor_x
12
+ @cursor_y = cursor_y
13
+ @cursor_visible = cursor_visible
14
+ end
15
+
16
+ def cursor_position
17
+ [@cursor_x, @cursor_y]
18
+ end
19
+
20
+ def rows
21
+ @rows
22
+ end
23
+
24
+ def row(row)
25
+ rows[row]
26
+ end
27
+
28
+ def capture
29
+ self
30
+ end
31
+
32
+ def to_s
33
+ rows.join("\n")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ module TTYtest
2
+ class Dummy
3
+ attr_accessor :contents, :cursor_position
4
+
5
+ def initialize
6
+ @contents = "\n"*23
7
+ @cursor_position = [0,0]
8
+ end
9
+
10
+ def capture
11
+ x, y = cursor_position
12
+ Capture.new(contents, cursor_x: x, cursor_y: y)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ module TTYtest
2
+ module Matchers
3
+ def assert_row(row_number, expected)
4
+ actual = row(row_number)
5
+ if actual != expected
6
+ raise MatchError, "expected row #{row_number} to be #{expected.inspect} but got #{actual.inspect}\nEntire screen:\n#{to_s}"
7
+ end
8
+ end
9
+
10
+ def assert_cursor_position(x, y)
11
+ expected = [x, y]
12
+ actual = cursor_position
13
+ if actual != expected
14
+ raise MatchError, "expected cursor to be at #{expected.inspect} but was at #{actual.inspect}\nEntire screen:\n#{to_s}"
15
+ end
16
+ end
17
+
18
+ METHODS = public_instance_methods
19
+ end
20
+ end
@@ -0,0 +1,40 @@
1
+ require 'forwardable'
2
+ require 'ttytest/matchers'
3
+ require 'ttytest/capture'
4
+
5
+ module TTYtest
6
+ class Terminal
7
+ extend Forwardable
8
+
9
+ def initialize(driver_terminal, max_wait_time: nil)
10
+ @driver_terminal = driver_terminal
11
+ @max_wait_time = max_wait_time
12
+ end
13
+
14
+ def max_wait_time
15
+ @max_wait_time || TTYtest.default_max_wait_time
16
+ end
17
+
18
+ def_delegators :@driver_terminal, :send_keys, :send_raw, :capture
19
+ def_delegators :capture, :rows, :row, :cursor_position
20
+
21
+ def synchronize(seconds=max_wait_time)
22
+ start_time = Time.now
23
+ begin
24
+ yield
25
+ rescue MatchError => e
26
+ raise e if (Time.now - start_time) >= seconds
27
+ sleep 0.05
28
+ retry
29
+ end
30
+ end
31
+
32
+ TTYtest::Matchers::METHODS.each do |matcher_name|
33
+ define_method matcher_name do |*args|
34
+ synchronize do
35
+ capture.public_send(matcher_name, *args)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,67 @@
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
+ class Driver
12
+ COMMAND = 'tmux'
13
+ SOCKET_NAME = 'ttytest'
14
+ REQUIRED_TMUX_VERSION = '1.8'
15
+
16
+ class TmuxError < StandardError; end
17
+
18
+ def initialize(debug: false, command: COMMAND, socket_name: SOCKET_NAME)
19
+ @debug = debug
20
+ @tmux_cmd = command
21
+ @socket_name = socket_name
22
+ end
23
+
24
+ def new_terminal(cmd, width: 80, height: 24)
25
+ session_name = "ttytest-#{SecureRandom.uuid}"
26
+ tmux(*%W[new-session -s #{session_name} -d -x #{width} -y #{height} #{cmd}])
27
+ session = Session.new(self, session_name)
28
+ Terminal.new(session)
29
+ end
30
+
31
+ def tmux(*args)
32
+ ensure_available
33
+ puts "tmux(#{args.inspect[1...-1]})" if debug?
34
+
35
+ stdout, stderr, status = Open3.capture3(@tmux_cmd, '-L', SOCKET_NAME, *args)
36
+ raise TmuxError, "tmux(#{args.inspect[1...-1]}) failed\n#{stderr}" unless status.success?
37
+ stdout
38
+ end
39
+
40
+ def available?
41
+ @available ||= (Gem::Version.new(tmux_version) >= Gem::Version.new(REQUIRED_TMUX_VERSION))
42
+ end
43
+
44
+ def debug?
45
+ @debug
46
+ end
47
+
48
+ private
49
+
50
+ def ensure_available
51
+ if !available?
52
+ if !tmux_version
53
+ raise TmuxError, "tmux doesn't seem to be unstalled" unless available?
54
+ else
55
+ raise TmuxError, "tmux version #{tmux_version} does not meet requirement >= #{REQUIRED_TMUX_VERSION}" unless available?
56
+ end
57
+ end
58
+ end
59
+
60
+ def tmux_version
61
+ @tmux_version ||= `#{@tmux_cmd} -V`[/tmux (\d+.\d+)/, 1]
62
+ rescue Errno::ENOENT
63
+ nil
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTYtest
4
+ module Tmux
5
+ class Session
6
+ attr_reader :driver, :name
7
+
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
+ def self.finalize(driver, name)
16
+ proc { driver.tmux(*%W[kill-session -t #{name}]) }
17
+ end
18
+
19
+ def capture
20
+ contents = driver.tmux(*%W[capture-pane -t #{name} -p])
21
+ str = driver.tmux(*%W[display-message -t #{name} -p #\{cursor_x},#\{cursor_y},#\{cursor_flag}])
22
+ x, y, cursor_flag = str.split(',').map(&:to_i)
23
+ cursor_visible = (cursor_flag != 0)
24
+ TTYtest::Capture.new(contents, cursor_x: x, cursor_y: y, cursor_visible: cursor_visible)
25
+ end
26
+
27
+ def send_keys(*keys)
28
+ driver.tmux(*%W[send-keys -t #{name}], *keys)
29
+ end
30
+
31
+ def send_raw(*keys)
32
+ driver.tmux(*%W[send-keys -t #{name} -l], *keys)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module TTYtest
2
+ VERSION = '0.1.0'
3
+ end
data/ttytest.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ttytest/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ttytest"
8
+ spec.version = TTYtest::VERSION
9
+ spec.authors = ["John Hawthorn"]
10
+ spec.email = ["john.hawthorn@gmail.com"]
11
+
12
+ spec.summary = %q{TTYtest is an integration test framework for interactive tty applications}
13
+ spec.description = %q{TTYtest allows running applications inside of a terminal emulator (like tmux) and making assertions on the output.}
14
+ spec.homepage = "https://github.com/jhawthorn/ttytest"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "minitest", "~> 5.0"
27
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ttytest
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - John Hawthorn
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-12-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ description: TTYtest allows running applications inside of a terminal emulator (like
56
+ tmux) and making assertions on the output.
57
+ email:
58
+ - john.hawthorn@gmail.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - README.md
67
+ - Rakefile
68
+ - lib/ttytest.rb
69
+ - lib/ttytest/capture.rb
70
+ - lib/ttytest/dummy.rb
71
+ - lib/ttytest/matchers.rb
72
+ - lib/ttytest/terminal.rb
73
+ - lib/ttytest/tmux/driver.rb
74
+ - lib/ttytest/tmux/session.rb
75
+ - lib/ttytest/version.rb
76
+ - ttytest.gemspec
77
+ homepage: https://github.com/jhawthorn/ttytest
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.5.2
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: TTYtest is an integration test framework for interactive tty applications
101
+ test_files: []