rspec-cli 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OWUxYmRlNWRjOTM1YjY0MDljNWI4ZWY2NWZmOWY0NDcwZTMwMzJhZg==
5
+ data.tar.gz: !binary |-
6
+ N2RhZTg3MjNiMGQ3ZWIxMGRmNjMwYzRkYmIxMjI3MTM5MmZhZmQ3OA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YTQyMzAyMmU0YTU0MjEzNjIxMDJkY2Q2Yjg1YTVlMzY5MDQwNjcyNWY5YWYz
10
+ MDljNGUxMjE4NmI0NDA4MTFkNWQxZGM1NDhmMzdhZTk2ZGU1ODg4MTUzZDYx
11
+ MzdjZjE4MWQ2YmRjNjc2Y2QxZTUxMjcwMmI2NTlkYTEwNTU3YTg=
12
+ data.tar.gz: !binary |-
13
+ MDRlMGRjN2M1N2JmNjgyYmVlMTlmYTU5ZDVhZDZjYTM5YTU2ZTI2OTg3YjIz
14
+ NTc0ZWIzYjVlZWNkZjQ3NTQ1MWM2NGEyYWYzNmEwZWM4MDZiNjE5YWFlMDEz
15
+ NTUxNjQ1MWFmMzY4MjUzMWVmZDA2MDZhMGM1YWNmOGMwNDRjZDQ=
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .#*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rspec-cli.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,6 @@
1
+ guard 'rspec', cmd: 'rspec'do
2
+
3
+ watch(%r{^spec/.+_spec\.rb$})
4
+ watch(%r{^lib/rspec/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
5
+
6
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Sia. S.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # RSpec::Cli
2
+
3
+ This is an extension to rspec to fascilitate cli testing.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'rspec-cli'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install rspec-cli
20
+
21
+ ## Usage
22
+
23
+ In your rspec config
24
+
25
+ ```ruby
26
+ RSpec.configure do |config|
27
+ config.include RSpec::Cli, type: :feature
28
+
29
+ config.alias_example_group_to :feature, :type => :feature
30
+ config.alias_example_to :scenario
31
+ end
32
+ ```
33
+ or similar.
34
+
35
+ Now your feature specs will have helper methods
36
+ ```new_cli_process(%w[echo hi there :D])``` and ```spawn_cli_process(%w[echo hi there :D])```
37
+
38
+ The first one returns a CliProcess instance that hasn't spawned your command yet.
39
+
40
+ The second one returns a CliProcess instance that has spawned your command.
41
+
42
+ The CliProcess instance has some useful methads like ```#read``` to read from it's stdout, ```#write(string)``` to write to it's stdin and ```#status``` to get the process's status.
43
+
44
+ ## Contributing
45
+
46
+ 1. Fork it ( https://github.com/[my-github-username]/rspec-cli/fork )
47
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
48
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
49
+ 4. Push to the branch (`git push origin my-new-feature`)
50
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,109 @@
1
+ require 'pty'
2
+ require 'rspec/cli/i_o_decorator'
3
+
4
+ module RSpec
5
+
6
+ module Cli
7
+
8
+ ##
9
+ # This class spawns your process and provides some
10
+ # helpers to interact with the process
11
+ class CliProcess
12
+
13
+ TERMINAL_COLOURS = /\e\[(\d+)(;\d+)*m/
14
+
15
+ attr_reader :pid
16
+
17
+ def initialize(*args)
18
+ raise ArgumentError, "wrong number of arguments" if args.empty?
19
+ args.unshift(*args.shift) if Array === args.first
20
+ @command = args
21
+ end
22
+
23
+ def flush
24
+ assert_spawned
25
+ @master.flush
26
+ end
27
+
28
+ def read_all(*args)
29
+ assert_spawned
30
+ @master.flush
31
+ @master.ready_all *args
32
+ end
33
+
34
+ def gets
35
+ assert_spawned
36
+ @master.gets
37
+ end
38
+
39
+ def puts(*args)
40
+ assert_spawned
41
+ @master.puts args
42
+ @master.flush
43
+ end
44
+
45
+ def stdout
46
+ @master
47
+ end
48
+
49
+ def stdin
50
+ @slave
51
+ end
52
+
53
+ def write(arg)
54
+ # This method can block if the argument is huge
55
+ assert_spawned
56
+ @master.write "#{arg}\n"
57
+ @master.flush
58
+ end
59
+
60
+ def run!
61
+ # Create master and slave pseudo terminal devices
62
+ master_tty, slave_tty = PTY.open
63
+ @master = IODecorator.new master_tty
64
+ @slave = slave_tty
65
+
66
+ @pid = PTY.spawn(*@command, in: @slave, out: @slave, err: @slave)[2]
67
+
68
+ @slave.close
69
+
70
+ self
71
+
72
+ end
73
+
74
+ def status
75
+
76
+ assert_spawned
77
+ PTY.check(@pid)
78
+
79
+ end
80
+
81
+ def alive?
82
+
83
+ begin
84
+ return status.nil?
85
+ rescue
86
+ return false
87
+ end
88
+
89
+ end
90
+
91
+ def kill!(signal = "TERM")
92
+
93
+ assert_spawned
94
+ @master.close
95
+ Process.kill(signal, @pid)
96
+
97
+ end
98
+
99
+ private
100
+
101
+ def assert_spawned
102
+ raise "process hasn't spawned yet" if @pid.nil?
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+
109
+ end
@@ -0,0 +1,32 @@
1
+ require 'pty'
2
+ require 'delegate'
3
+
4
+ module RSpec
5
+
6
+ module Cli
7
+
8
+ class IODecorator < SimpleDelegator
9
+
10
+ def has_data?(timeout = 0)
11
+ IO.select([self], nil, nil, timeout) ? true : false
12
+ end
13
+
14
+ def ready_all nonblocking = false
15
+ if nonblocking && !has_data?
16
+ return nil
17
+ end
18
+
19
+ # Block until data has arrived.
20
+ data = readpartial 0xFFFF
21
+ # If there is more data in the buffer, retrieve it nonblocking.
22
+ while has_data?
23
+ data << readpartial(0xFFFF)
24
+ end
25
+ data
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,5 @@
1
+ module RSpec
2
+ module Cli
3
+ VERSION = "0.2.1"
4
+ end
5
+ end
data/lib/rspec/cli.rb ADDED
@@ -0,0 +1,18 @@
1
+ require "rspec/cli/version"
2
+ require "rspec/cli/cli_process"
3
+
4
+ module RSpec
5
+
6
+ module Cli
7
+
8
+ def spawn_cli_process(*args)
9
+ new_cli_process(*args).run!
10
+ end
11
+
12
+ def new_cli_process(*args)
13
+ CliProcess.new(*args)
14
+ end
15
+
16
+ end
17
+
18
+ end
data/rspec-cli.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rspec/cli/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rspec-cli"
8
+ spec.version = RSpec::Cli::VERSION
9
+ spec.authors = ["Sia. S."]
10
+ spec.email = ["sia.s.saj@gmail.com"]
11
+ spec.summary = "rspec-cli-#{RSpec::Cli::VERSION}"
12
+ spec.description = "A set of tools to test programs on the command line"
13
+ spec.homepage = "https://github.com/quazimodo/rspec-cli"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.1"
24
+
25
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+ require 'rspec/cli/cli_process'
3
+
4
+ describe RSpec::Cli::CliProcess do
5
+
6
+ let(:looper) do
7
+ RSpec::Cli::CliProcess.new(%w[spec/support/bin/dummy --looper])
8
+ end
9
+
10
+ let(:echo) do
11
+ RSpec::Cli::CliProcess.new %w[spec/support/bin/dummy --echo "interesting repeat"]
12
+ end
13
+
14
+
15
+
16
+ it "initializes with a binary and a set of arguments" do
17
+ expect{RSpec::Cli::CliProcess.new %w[echo who are you] }.not_to raise_error
18
+ end
19
+
20
+ it "initializes without actually spawning any process" do
21
+ expect(PTY).not_to receive(:spawn)
22
+ expect(echo.pid).to eq nil
23
+ end
24
+
25
+ describe "#run!" do
26
+
27
+ it "spawns process and sets the pid" do
28
+ echo.run!
29
+ expect(echo.pid).not_to eq nil
30
+ end
31
+
32
+ it "assigns the new process's stdout to a tty by default" do
33
+ allow_any_instance_of(File).to receive(:close)
34
+ echo.run!
35
+ expect(echo.stdout).to be_a_tty
36
+ end
37
+
38
+ it "assigns the new process's stdin to a tty by default" do
39
+ allow_any_instance_of(File).to receive(:close)
40
+ echo.run!
41
+ expect(echo.stdin).to be_a_tty
42
+ end
43
+
44
+ end
45
+
46
+ describe "#status" do
47
+
48
+ it "raises an error if the process hasn't spawned yet" do
49
+ expect{echo.status}.to raise_error "process hasn't spawned yet"
50
+ end
51
+
52
+ it "returns nil if the process is still alive" do
53
+ looper.run!
54
+ expect(looper.status).to eq nil
55
+ end
56
+
57
+ it "returns a Process::Status object if the process has spawned and terminated" do
58
+ looper.run!
59
+ looper.kill!
60
+ sleep 0.1
61
+ expect(looper.status).to be_a_kind_of Process::Status
62
+ end
63
+
64
+ end
65
+
66
+ describe "#write" do
67
+
68
+ it "writes to the spawned processes stdin" do
69
+ looper.run!
70
+ looper.write "hi there"
71
+ expect(looper.gets).to include "hi there"
72
+ end
73
+
74
+ it "puts to the spawned process stdin" do
75
+ looper.run!
76
+ looper.puts "hi there"
77
+ expect(looper.gets).to include "hi there"
78
+ end
79
+
80
+ it "raises an error if the process hasn't been spawned" do
81
+ expect{looper.write "hi"}.to raise_error
82
+ end
83
+
84
+ end
85
+
86
+ describe "#kill" do
87
+
88
+ it "closes all remaining streams" do
89
+ looper.run!
90
+ pty = looper.instance_variable_get(:@master)
91
+ expect(pty).to receive(:close)
92
+ looper.kill!
93
+ end
94
+
95
+ it "raises an error if the process hasn't spawned" do
96
+ expect{looper.read}.to raise_error
97
+ end
98
+
99
+ it "terminates the process using SIGTERM" do
100
+ looper.run!
101
+ pid = looper.pid
102
+ expect(Process).to receive(:kill).with("TERM", pid).and_call_original
103
+ looper.kill!
104
+ expect(Process.wait looper.pid).to eq looper.pid
105
+ end
106
+
107
+ it "terminates the process using given signal" do
108
+ looper.run!
109
+ pid = looper.pid
110
+ expect(Process).to receive(:kill).with("KILL", pid).and_call_original
111
+ looper.kill!("KILL")
112
+ expect(Process.wait looper.pid).to eq looper.pid
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+ require 'rspec/cli/i_o_decorator'
3
+
4
+ describe RSpec::Cli::IODecorator do
5
+
6
+
7
+
8
+ let(:pipe_arry) { IO.pipe }
9
+
10
+ let(:pipe_out) { pipe_arry[0] }
11
+ let(:pipe_in) { pipe_arry[1] }
12
+
13
+ let(:io_out) { RSpec::Cli::IODecorator.new pipe_out }
14
+ let(:io_in) { RSpec::Cli::IODecorator.new pipe_in }
15
+
16
+
17
+ describe "#has_data?" do
18
+
19
+ it "returns true if io has data to read" do
20
+ io_in.puts "test_data"
21
+ expect(io_out.has_data?).to be true
22
+ end
23
+
24
+ it "returns false if io has no data to reads" do
25
+ expect(io_out.has_data?).to be false
26
+ end
27
+
28
+ end
29
+
30
+ describe "#read_all" do
31
+
32
+ context "with nonblocking reads" do
33
+
34
+ it 'returns nil if no data' do
35
+ expect(io_out.read_all(nonblocking: true)).to be nil
36
+ end
37
+
38
+ it "returns all the data if there is data" do
39
+ io_in.puts "hi, this"
40
+ io_in.puts "is test data"
41
+ expect(io_out.read_all(nonblocking: true)).to include "hi, this\nis test data"
42
+ end
43
+
44
+ it "doesn't fall into a race condition with another process interacting with it" do
45
+ master_tty, slave_tty = PTY.open
46
+
47
+ master = RSpec::Cli::IODecorator.new master_tty
48
+
49
+ command = "spec/support/bin/dummy --looper --sleeper 0.1"
50
+ @pid = PTY.spawn(command, in: slave_tty, out: slave_tty, err: slave_tty)[2]
51
+
52
+ master.write "hi there"
53
+ expect(master.read_all).to include "hi there"
54
+ end
55
+ end
56
+
57
+
58
+ context "with blocking reads" do
59
+
60
+ it 'blocks till data arrives' do
61
+ test = lambda do
62
+ Timeout::timeout(1) { io_out.read_all }
63
+ end
64
+
65
+ expect(test).to raise_error Timeout::Error
66
+ end
67
+
68
+ it 'reads all the data when available' do
69
+
70
+ data = nil
71
+ huge_string = "BC" * 0xFFFF
72
+ huge_string << "ends in awesome"
73
+
74
+ # declare the block that we will call in our test
75
+ test = lambda do
76
+ Timeout::timeout(1) { data = io_out.read_all }
77
+ end
78
+
79
+ # the huge_string might be too big for the buffer and block
80
+ # while the system waits for the buffer to be read from,
81
+ # so we do the write in a new thread.
82
+ Thread.new do
83
+ io_in.syswrite huge_string
84
+ end
85
+
86
+ expect(test).to change{data}.to huge_string
87
+
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+
94
+ end
data/spec/cli_spec.rb ADDED
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'rspec/cli'
3
+
4
+ describe RSpec::Cli do
5
+
6
+ let(:subject) { Object.new.extend RSpec::Cli }
7
+
8
+ describe "::new_cli_process" do
9
+
10
+ it "returns a CliProcess object that has not actually spawned the process yet" do
11
+ p = subject.new_cli_process %w[factor]
12
+ expect(p.pid).to eq nil
13
+ end
14
+
15
+ end
16
+
17
+ describe "::spawn_cli_process" do
18
+
19
+ it "returns a CliProcess object with a spawned process" do
20
+ p = subject.spawn_cli_process %w[factor]
21
+ expect(p.pid).not_to eq nil
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,19 @@
1
+ Dir.glob(::File.expand_path('../support/*.rb', __FILE__)).each { |f| require_relative f }
2
+
3
+ RSpec.configure do |config|
4
+
5
+ config.filter_run :focus
6
+ config.run_all_when_everything_filtered = true
7
+ config.order = :random
8
+
9
+ config.expect_with :rspec do |expectations|
10
+ expectations.syntax = :expect
11
+ end
12
+
13
+ config.mock_with :rspec do |mocks|
14
+ mocks.syntax = :expect
15
+ # Prevents you from mocking or stubbing a method that does not exist on
16
+ # a real object. This is generally recommended.
17
+ mocks.verify_partial_doubles = true
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+
4
+
5
+ options = {}
6
+ OptionParser.new do |opts|
7
+ opts.banner = "Dummy executable to help test rspec-cli"
8
+
9
+ opts.on("-l", "--looper", "start in read/print loop mode") do
10
+ options[:looper] = true
11
+ end
12
+
13
+ opts.on("-e str", "--echo str", "echo str arg") do |echo|
14
+ options[:echo] = echo
15
+ end
16
+
17
+ opts.on("-s seconds", "--sleeper seconds", "echo str arg") do |seconds|
18
+ options[:sleeper] = seconds
19
+ end
20
+ end.parse!
21
+
22
+ puts "Dummy has loaded"
23
+
24
+ if options[:looper]
25
+
26
+ loop do
27
+ i = gets
28
+ sleep(options[:sleeper].to_f) unless options[:sleeper].nil?
29
+ puts i
30
+ end
31
+
32
+ elsif options[:echo]
33
+
34
+ puts options[:echo]
35
+
36
+ end
37
+
38
+
39
+ exit 0
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Sia. S.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-10 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.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
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: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ description: A set of tools to test programs on the command line
56
+ email:
57
+ - sia.s.saj@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - .rspec
64
+ - Gemfile
65
+ - Guardfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - lib/rspec/cli.rb
70
+ - lib/rspec/cli/cli_process.rb
71
+ - lib/rspec/cli/i_o_decorator.rb
72
+ - lib/rspec/cli/version.rb
73
+ - rspec-cli.gemspec
74
+ - spec/cli/cli_process_spec.rb
75
+ - spec/cli/i_o_decorator_spec.rb
76
+ - spec/cli_spec.rb
77
+ - spec/spec_helper.rb
78
+ - spec/support/bin/dummy
79
+ homepage: https://github.com/quazimodo/rspec-cli
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.3.0
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: rspec-cli-0.2.1
103
+ test_files:
104
+ - spec/cli/cli_process_spec.rb
105
+ - spec/cli/i_o_decorator_spec.rb
106
+ - spec/cli_spec.rb
107
+ - spec/spec_helper.rb
108
+ - spec/support/bin/dummy
109
+ has_rdoc: