command_test 0.0.1
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.
- data/CHANGELOG +3 -0
- data/LICENSE +20 -0
- data/README.markdown +109 -0
- data/Rakefile +8 -0
- data/features/rspec_integration.feature +60 -0
- data/features/step_definitions/test_steps.rb +24 -0
- data/features/support/env.rb +13 -0
- data/features/test_unit_integration.feature +58 -0
- data/lib/command_test.rb +70 -0
- data/lib/command_test/adapters.rb +2 -0
- data/lib/command_test/adapters/rspec.rb +41 -0
- data/lib/command_test/adapters/test_unit.rb +43 -0
- data/lib/command_test/core_extensions.rb +76 -0
- data/lib/command_test/matcher.rb +43 -0
- data/lib/command_test/parser.rb +62 -0
- data/lib/command_test/tests.rb +39 -0
- data/lib/command_test/version.rb +11 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/temporary_directory.rb +48 -0
- data/spec/unit/command_test/core_extensions_spec.rb +82 -0
- data/spec/unit/command_test/matcher_spec.rb +139 -0
- data/spec/unit/command_test/parser_spec.rb +134 -0
- data/spec/unit/command_test/tests_spec.rb +65 -0
- data/spec/unit/command_test_spec.rb +93 -0
- metadata +115 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 George Ogata
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# Command Test
|
2
|
+
|
3
|
+
Test that your app runs or does not run commands.
|
4
|
+
|
5
|
+
## Why?
|
6
|
+
|
7
|
+
Ruby has a bucket of ways to run commands:
|
8
|
+
|
9
|
+
* Kernel#system
|
10
|
+
* Kernel#`
|
11
|
+
* Kernel.open
|
12
|
+
* IO.popen
|
13
|
+
* Open3.popen3
|
14
|
+
|
15
|
+
Mocking becomes impractical because you seldom care *how* a command is
|
16
|
+
run, just that it *is* run (or not run). Methods that run the command
|
17
|
+
through the shell are also tricky to mock correctly, as extra shell
|
18
|
+
syntax can easily throw off the test.
|
19
|
+
|
20
|
+
## How
|
21
|
+
|
22
|
+
### RSpec
|
23
|
+
|
24
|
+
lambda do
|
25
|
+
...
|
26
|
+
end.should run_command('convert', 'bad.gif', 'good.png')
|
27
|
+
|
28
|
+
### Test::Unit
|
29
|
+
|
30
|
+
assert_runs_command 'convert', 'bad.gif', 'good.png' do
|
31
|
+
...
|
32
|
+
end
|
33
|
+
|
34
|
+
assert_does_not_run_command 'convert', 'bad.gif', 'good.png' do
|
35
|
+
...
|
36
|
+
end
|
37
|
+
|
38
|
+
### Other frameworks
|
39
|
+
|
40
|
+
Not really using them myself, but I'll bet they're easy to add. Patch
|
41
|
+
me and be famous!
|
42
|
+
|
43
|
+
## Tricks
|
44
|
+
|
45
|
+
### Regexps
|
46
|
+
|
47
|
+
You can match arguments against regexps:
|
48
|
+
|
49
|
+
lambda do
|
50
|
+
...
|
51
|
+
end.should run_command('convert', /.gif\z/, /.png\z/)
|
52
|
+
|
53
|
+
### Wildcards
|
54
|
+
|
55
|
+
An integer, `n`, matches any `n` arguments:
|
56
|
+
|
57
|
+
lambda do
|
58
|
+
system 'diff', 'old.txt', 'new.txt')
|
59
|
+
end.should run_command('diff', 2)
|
60
|
+
|
61
|
+
An range matches any number of arguments in that range:
|
62
|
+
|
63
|
+
lambda do
|
64
|
+
system 'diff', '-w', 'one.txt', 'two.txt', 'three.txt'
|
65
|
+
end.should run_command('diff', '-w', 2..3)
|
66
|
+
|
67
|
+
`:*` matches any number of arguments:
|
68
|
+
|
69
|
+
lamdba do
|
70
|
+
system 'hostname'
|
71
|
+
end.should run_command('convert', :*, 'target.png')
|
72
|
+
|
73
|
+
`:+` matches at least one argument:
|
74
|
+
|
75
|
+
lamdba do
|
76
|
+
system 'hostname'
|
77
|
+
end.should run_command('gem', 'install', :+)
|
78
|
+
|
79
|
+
Any combination of wildcards can be used together.
|
80
|
+
|
81
|
+
### Recording
|
82
|
+
|
83
|
+
You can record the commands run during a block:
|
84
|
+
|
85
|
+
commands = CommandTest.record do
|
86
|
+
system 'convert', 'bad.gif', 'good.png'
|
87
|
+
system 'identify', 'good.png'
|
88
|
+
end
|
89
|
+
commands # [['convert', 'bad.gif', 'good.png'], ['identify', 'good.png']]
|
90
|
+
|
91
|
+
And then match them yourself:
|
92
|
+
|
93
|
+
CommandTest.match?(commands.first, ['convert', :*])
|
94
|
+
|
95
|
+
You can use this to check that commands were run in a certain order, a
|
96
|
+
certain number of times, in a pretty pattern, ... the possibilities
|
97
|
+
are endless!
|
98
|
+
|
99
|
+
## Contributing
|
100
|
+
|
101
|
+
* Bug reports: http://github.com/oggy/command_test/issues
|
102
|
+
* Source: http://github.com/oggy/command_test
|
103
|
+
* Patches: Fork on Github, send pull request.
|
104
|
+
* Ensure patch includes tests.
|
105
|
+
* Leave the version alone, or bump it in a separate commit.
|
106
|
+
|
107
|
+
## Copyright
|
108
|
+
|
109
|
+
Copyright (c) 2010 George Ogata. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
Feature: RSpec Integration
|
2
|
+
|
3
|
+
As a developer who uses RSpec
|
4
|
+
I want to use Command Test
|
5
|
+
So I can write beautiful tests
|
6
|
+
|
7
|
+
Scenario: Using run_command
|
8
|
+
Given I have a file "spec.rb" containing:
|
9
|
+
"""
|
10
|
+
require 'command_test'
|
11
|
+
|
12
|
+
describe "#run_command" do
|
13
|
+
describe "when using should" do
|
14
|
+
it "should pass if the command is run" do
|
15
|
+
lambda do
|
16
|
+
system 'sh', '-c', ''
|
17
|
+
end.should run_command('sh', '-c', '')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should fail if the command is not run" do
|
21
|
+
lambda do
|
22
|
+
system 'sh', '-c', 'true'
|
23
|
+
end.should run_command('sh', '-c', '')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "when using should_not" do
|
28
|
+
it "should pass if the command is not run" do
|
29
|
+
lambda do
|
30
|
+
system 'sh', '-c', 'true'
|
31
|
+
end.should_not run_command('sh', '-c', '')
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should fail if the command is run" do
|
35
|
+
lambda do
|
36
|
+
system 'sh', '-c', ''
|
37
|
+
end.should_not run_command('sh', '-c', '')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
"""
|
42
|
+
When I run "spec" on "spec.rb"
|
43
|
+
Then the output should contain ".F.F"
|
44
|
+
And the output should contain:
|
45
|
+
"""
|
46
|
+
This command should have been run, but was not:
|
47
|
+
"sh" "-c" ""
|
48
|
+
These were the commands run:
|
49
|
+
"sh" "-c" "true"
|
50
|
+
"""
|
51
|
+
And the output should contain:
|
52
|
+
"""
|
53
|
+
This command should not have been run, but was:
|
54
|
+
"sh" "-c" ""
|
55
|
+
These were the commands run:
|
56
|
+
"sh" "-c" ""
|
57
|
+
"""
|
58
|
+
And the output should contain in this order
|
59
|
+
| This command should have been run, but was not |
|
60
|
+
| This command should not have been run, but was |
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Given /^I have a file "(.*?)" containing:$/ do |file_name, content|
|
2
|
+
path = "#{TMP}/#{file_name}"
|
3
|
+
open(path, 'w'){|f| f.print content}
|
4
|
+
end
|
5
|
+
|
6
|
+
When /^I run "(.*?)" on "(.*?)"$/ do |command, file_name|
|
7
|
+
Dir.chdir ROOT do
|
8
|
+
@output = `#{command} #{TMP}/#{file_name} 2>&1`
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
Then /^the output should contain "(.*?)"$/ do |fragment|
|
13
|
+
@output.should include(fragment)
|
14
|
+
end
|
15
|
+
|
16
|
+
Then /^the output should contain:$/ do |fragment|
|
17
|
+
@output.should include(fragment)
|
18
|
+
end
|
19
|
+
|
20
|
+
Then /^the output should contain in this order$/ do |table|
|
21
|
+
table.raw.inject(0) do |index, row|
|
22
|
+
index && @output.index(row.first, index)
|
23
|
+
end.should_not be_nil
|
24
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
Feature: Test Unit Integration
|
2
|
+
|
3
|
+
As a developer who uses Test::Unit
|
4
|
+
I want to use Command Test
|
5
|
+
So I can write beautiful tests
|
6
|
+
|
7
|
+
Scenario: Using run_command
|
8
|
+
Given I have a file "test.rb" containing:
|
9
|
+
"""
|
10
|
+
require 'test/unit'
|
11
|
+
require 'command_test'
|
12
|
+
|
13
|
+
class CommandTests < Test::Unit::TestCase
|
14
|
+
def test_assert_runs_command_passes_if_the_command_is_run
|
15
|
+
assert_runs_command 'sh', '-c', '' do
|
16
|
+
system 'sh', '-c', ''
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_assert_runs_command_fails_if_the_command_is_not_run
|
21
|
+
assert_runs_command 'sh', '-c', '' do
|
22
|
+
system 'sh', '-c', 'true'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_assert_does_not_run_command_passes_if_the_command_is_not_run
|
27
|
+
assert_does_not_run_command 'sh', '-c', '' do
|
28
|
+
system 'sh', '-c', 'true'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_assert_does_not_run_command_fails_if_the_command_is_run
|
33
|
+
assert_does_not_run_command 'sh', '-c', '' do
|
34
|
+
system 'sh', '-c', ''
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
"""
|
39
|
+
When I run "ruby -Ilib" on "test.rb"
|
40
|
+
# Test::Unit sorts tests alphabetically.
|
41
|
+
Then the output should contain "F.F."
|
42
|
+
And the output should contain:
|
43
|
+
"""
|
44
|
+
This command should have been run, but was not:
|
45
|
+
"sh" "-c" ""
|
46
|
+
These were the commands run:
|
47
|
+
"sh" "-c" "true"
|
48
|
+
"""
|
49
|
+
And the output should contain:
|
50
|
+
"""
|
51
|
+
This command should not have been run, but was:
|
52
|
+
"sh" "-c" ""
|
53
|
+
These were the commands run:
|
54
|
+
"sh" "-c" ""
|
55
|
+
"""
|
56
|
+
And the output should contain in this order
|
57
|
+
| This command should not have been run, but was |
|
58
|
+
| This command should have been run, but was not |
|
data/lib/command_test.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
require 'command_test/core_extensions'
|
3
|
+
require 'command_test/parser'
|
4
|
+
require 'command_test/matcher'
|
5
|
+
require 'command_test/adapters'
|
6
|
+
require 'command_test/tests'
|
7
|
+
|
8
|
+
module CommandTest
|
9
|
+
class << self
|
10
|
+
#
|
11
|
+
# Return the list of commands run during the given block.
|
12
|
+
#
|
13
|
+
# Each command is an Array of "words" (command and arguments).
|
14
|
+
#
|
15
|
+
def record
|
16
|
+
commands = []
|
17
|
+
recorders << lambda{|c| commands << c}
|
18
|
+
begin
|
19
|
+
yield
|
20
|
+
ensure
|
21
|
+
recorders.pop
|
22
|
+
end
|
23
|
+
commands
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Return true if the given +actual+ command matches the +expected+
|
28
|
+
# spec.
|
29
|
+
#
|
30
|
+
# The elements of +expected+ may be one of these:
|
31
|
+
#
|
32
|
+
# * A String in +expected+ matches the corresponding element in
|
33
|
+
# +actual+.
|
34
|
+
# * A Regexp must match the corresponding element in +actual+.
|
35
|
+
# * An Integer, n, matches the next n elements of +actual+.
|
36
|
+
# * A Range, a...b, can match the next i elements of +actual+,
|
37
|
+
# for a <= i < b.
|
38
|
+
# * :+ can match 1 or more elements in +actual+.
|
39
|
+
# * :* can match any number of elements (including zero) in
|
40
|
+
# +actual+.
|
41
|
+
#
|
42
|
+
def match?(expected, actual)
|
43
|
+
matcher.match?(expected, actual)
|
44
|
+
end
|
45
|
+
|
46
|
+
def record_command(command, *args) # :nodoc:
|
47
|
+
words = Shellwords.shellwords(command).concat(args)
|
48
|
+
recorders.each{|r| r.call(words)}
|
49
|
+
end
|
50
|
+
|
51
|
+
def record_interpreted_command(command) # :nodoc:
|
52
|
+
words = parser.parse(command)
|
53
|
+
recorders.each{|r| r.call(words)}
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def recorders
|
59
|
+
Thread.current[:command_recorders] ||= []
|
60
|
+
end
|
61
|
+
|
62
|
+
def parser
|
63
|
+
@parser ||= Parser.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def matcher
|
67
|
+
@matcher ||= Matcher.new
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module CommandTest
|
2
|
+
module RSpec
|
3
|
+
module Matchers
|
4
|
+
#
|
5
|
+
# Matches if the proc runs the given command.
|
6
|
+
#
|
7
|
+
# lambda do
|
8
|
+
# ...
|
9
|
+
# end.should run_command('convert', 'evil.gif', 'good.png')
|
10
|
+
#
|
11
|
+
# Commands are matched according to CommandTest.match? .
|
12
|
+
#
|
13
|
+
def run_command(*command)
|
14
|
+
RunCommand.new(command)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class RunCommand
|
19
|
+
def initialize(expected)
|
20
|
+
@expected = expected
|
21
|
+
end
|
22
|
+
|
23
|
+
def matches?(proc)
|
24
|
+
@test = Tests::RunsCommand.new(@expected, &proc)
|
25
|
+
@test.matches?
|
26
|
+
end
|
27
|
+
|
28
|
+
def failure_message_for_should
|
29
|
+
@test.positive_failure_message
|
30
|
+
end
|
31
|
+
|
32
|
+
def failure_message_for_should_not
|
33
|
+
@test.negative_failure_message
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
::Spec::Runner.configure do |config|
|
38
|
+
config.send :include, Matchers
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module CommandTest
|
2
|
+
module Adapters
|
3
|
+
module TestUnit
|
4
|
+
module Assertions
|
5
|
+
#
|
6
|
+
# Passes if the block runs the given command.
|
7
|
+
#
|
8
|
+
# assert_runs_command 'convert', 'evil.gif', 'good.png' do
|
9
|
+
# ...
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# Commands are matched according to CommandTest.match? .
|
13
|
+
#
|
14
|
+
def assert_runs_command(*expected, &block)
|
15
|
+
result = Tests::RunsCommand.new(expected, &block)
|
16
|
+
matches = result.matches?
|
17
|
+
assert_block(matches ? nil : result.positive_failure_message) do
|
18
|
+
matches
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Passes if the block does not run the given command.
|
24
|
+
#
|
25
|
+
# assert_does_not_run_command 'convert', 'evil.gif', 'good.png' do
|
26
|
+
# ...
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# Commands are matched according to CommandTest.match? .
|
30
|
+
#
|
31
|
+
def assert_does_not_run_command(*expected, &block)
|
32
|
+
result = Tests::RunsCommand.new(expected, &block)
|
33
|
+
matches = result.matches?
|
34
|
+
assert_block(matches ? result.negative_failure_message : nil) do
|
35
|
+
!matches
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
::Test::Unit::TestCase.send :include, Assertions
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|