ttytest 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []