busser 0.9.0 → 0.9.2
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 +4 -4
- data/.github/workflows/publish.yml +22 -0
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +1 -4
- data/CHANGELOG.md +18 -0
- data/Gemfile +12 -5
- data/README.md +82 -0
- data/Rakefile +19 -1
- data/busser.gemspec +11 -8
- data/features/plugin_create_command.feature +7 -9
- data/lib/busser/command/deserialize.rb +4 -0
- data/lib/busser/command/plugin_create.rb +46 -3
- data/lib/busser/command/plugin_install.rb +40 -4
- data/lib/busser/command/plugin_list.rb +16 -4
- data/lib/busser/command/setup.rb +37 -3
- data/lib/busser/command/suite_cleanup.rb +3 -0
- data/lib/busser/command/suite_path.rb +3 -0
- data/lib/busser/command/test.rb +38 -1
- data/lib/busser/cucumber/hooks.rb +9 -0
- data/lib/busser/helpers.rb +24 -0
- data/lib/busser/plugin.rb +51 -9
- data/lib/busser/rubygems.rb +20 -0
- data/lib/busser/runner_plugin/dummy.rb +3 -0
- data/lib/busser/runner_plugin.rb +5 -0
- data/lib/busser/thor.rb +23 -0
- data/lib/busser/ui.rb +63 -0
- data/lib/busser/version.rb +2 -1
- data/release-please-config.json +3 -1
- data/spec/busser/command/deserialize_spec.rb +0 -1
- data/spec/busser/command/plugin_create_spec.rb +174 -0
- data/spec/busser/command/plugin_install_spec.rb +44 -0
- data/spec/busser/command/plugin_list_spec.rb +49 -0
- data/spec/busser/command/setup_spec.rb +120 -0
- data/spec/busser/command/test_spec.rb +36 -0
- data/spec/busser/helpers_spec.rb +10 -10
- data/spec/busser/plugin_spec.rb +0 -1
- data/spec/busser/ui_spec.rb +56 -10
- data/spec/spec_helper.rb +2 -3
- data/templates/plugin/Rakefile.erb +4 -24
- data/templates/plugin/features_env.rb.erb +4 -4
- data/templates/plugin/gemspec.erb +7 -8
- data/templates/plugin/github_workflow.yml.erb +26 -0
- metadata +13 -60
- data/templates/plugin/tailor.erb +0 -4
- data/templates/plugin/travis.yml.erb +0 -11
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
|
|
14
|
+
require_relative "../../spec_helper"
|
|
15
|
+
|
|
16
|
+
require "tmpdir"
|
|
17
|
+
|
|
18
|
+
require "busser/command/plugin_create"
|
|
19
|
+
|
|
20
|
+
# The generated project is what a plugin author starts from, so the checks
|
|
21
|
+
# worth having are the ones a new project would trip over on day one.
|
|
22
|
+
describe Busser::Command::PluginCreate do
|
|
23
|
+
|
|
24
|
+
before do
|
|
25
|
+
@tmpdir = Dir.mktmpdir("busser-plugin-create")
|
|
26
|
+
@pwd = Dir.pwd
|
|
27
|
+
Dir.chdir(@tmpdir)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
after do
|
|
31
|
+
Dir.chdir(@pwd)
|
|
32
|
+
FileUtils.rm_rf(@tmpdir)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "the generated gemspec" do
|
|
36
|
+
|
|
37
|
+
it "loads" do
|
|
38
|
+
generate
|
|
39
|
+
|
|
40
|
+
_(gemspec.name).must_equal "busser-demo"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# RubyGems validates spec.license against the SPDX list and warns on
|
|
44
|
+
# anything else. The generator used to emit "Apache 2.0", the human
|
|
45
|
+
# readable name rather than the identifier.
|
|
46
|
+
it "declares a license RubyGems recognises" do
|
|
47
|
+
generate
|
|
48
|
+
|
|
49
|
+
_(Gem::Licenses.match?(gemspec.license)).must_equal true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
{
|
|
53
|
+
"apachev2" => "Apache-2.0",
|
|
54
|
+
"mit" => "MIT",
|
|
55
|
+
"lgplv3" => "LGPL-3.0-only",
|
|
56
|
+
}.each do |option, identifier|
|
|
57
|
+
it "maps --license #{option} to #{identifier}" do
|
|
58
|
+
generate(license: option)
|
|
59
|
+
|
|
60
|
+
_(gemspec.license).must_equal identifier
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# "All rights reserved" has no SPDX identifier, so declaring nothing is
|
|
65
|
+
# the only correct option.
|
|
66
|
+
it "declares no license for an all rights reserved plugin" do
|
|
67
|
+
generate(license: "reserved")
|
|
68
|
+
|
|
69
|
+
_(gemspec.license).must_be_nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# bundler ~> 1.3 cannot be satisfied by any bundler in use today, so a
|
|
73
|
+
# freshly generated plugin failed to resolve before running anything.
|
|
74
|
+
it "does not constrain bundler" do
|
|
75
|
+
generate
|
|
76
|
+
|
|
77
|
+
_(dev_dependencies).wont_include "bundler"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# cane, tailor and countloc have been unmaintained for about a decade.
|
|
81
|
+
%w{cane tailor countloc}.each do |gem_name|
|
|
82
|
+
it "does not depend on #{gem_name}" do
|
|
83
|
+
generate
|
|
84
|
+
|
|
85
|
+
_(dev_dependencies).wont_include gem_name
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "depends on what the generated Rakefile needs" do
|
|
90
|
+
generate
|
|
91
|
+
|
|
92
|
+
_(dev_dependencies).must_include "rake"
|
|
93
|
+
_(dev_dependencies).must_include "cucumber"
|
|
94
|
+
_(dev_dependencies).must_include "aruba"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "states a supported Ruby version" do
|
|
98
|
+
generate
|
|
99
|
+
|
|
100
|
+
requirement = gemspec.required_ruby_version
|
|
101
|
+
|
|
102
|
+
_(requirement.satisfied_by?(Gem::Version.new("3.2.0"))).must_equal true
|
|
103
|
+
_(requirement.satisfied_by?(Gem::Version.new("2.7.0"))).must_equal false
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def gemspec
|
|
107
|
+
Gem::Specification.load(File.join(plugin_dir, "busser-demo.gemspec"))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def dev_dependencies
|
|
111
|
+
gemspec.development_dependencies.map(&:name)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe "the generated project" do
|
|
116
|
+
|
|
117
|
+
it "ships a CI workflow rather than a Travis config" do
|
|
118
|
+
generate
|
|
119
|
+
|
|
120
|
+
_(File.exist?(File.join(plugin_dir, ".github/workflows/test.yml")))
|
|
121
|
+
.must_equal true
|
|
122
|
+
_(File.exist?(File.join(plugin_dir, ".travis.yml"))).must_equal false
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Both linters are long dead, and the Rakefile's default task depended on
|
|
126
|
+
# them, so `rake` failed on a freshly generated plugin.
|
|
127
|
+
it "ships no dead linter configuration" do
|
|
128
|
+
generate
|
|
129
|
+
|
|
130
|
+
_(File.exist?(File.join(plugin_dir, ".tailor"))).must_equal false
|
|
131
|
+
_(File.exist?(File.join(plugin_dir, ".cane"))).must_equal false
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "has a Rakefile that only references tasks it defines" do
|
|
135
|
+
generate
|
|
136
|
+
|
|
137
|
+
rakefile = File.read(File.join(plugin_dir, "Rakefile"))
|
|
138
|
+
|
|
139
|
+
_(rakefile).wont_match(/cane|tailor|countloc/)
|
|
140
|
+
_(rakefile).must_match(/Cucumber::Rake::Task/)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# aruba 1.0 dropped @aruba_timeout_seconds, and setting it on aruba 2 is
|
|
144
|
+
# silently ignored, so the generated timeout did nothing at all.
|
|
145
|
+
it "configures aruba through the supported API" do
|
|
146
|
+
generate
|
|
147
|
+
|
|
148
|
+
env = File.read(File.join(plugin_dir, "features/support/env.rb"))
|
|
149
|
+
|
|
150
|
+
_(env).wont_match(/@aruba_timeout_seconds/)
|
|
151
|
+
_(env).must_match(/Aruba\.configure/)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def generate(license: "apachev2")
|
|
156
|
+
command = Busser::Command::PluginCreate.new(
|
|
157
|
+
["demo"], { "type" => "runner", "license" => license }
|
|
158
|
+
)
|
|
159
|
+
command.stubs(:initialize_git)
|
|
160
|
+
capture_stdout { command.create }
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def plugin_dir
|
|
164
|
+
File.join(@tmpdir, "busser-demo")
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def capture_stdout
|
|
168
|
+
original = $stdout
|
|
169
|
+
$stdout = StringIO.new
|
|
170
|
+
yield
|
|
171
|
+
ensure
|
|
172
|
+
$stdout = original
|
|
173
|
+
end
|
|
174
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require_relative "../../spec_helper"
|
|
2
|
+
|
|
3
|
+
require "busser/command/plugin_install"
|
|
4
|
+
|
|
5
|
+
describe Busser::Command::PluginInstall do
|
|
6
|
+
let(:command) { Busser::Command::PluginInstall.new([%w{busser-dummy}]) }
|
|
7
|
+
|
|
8
|
+
describe "#drop_ssl_verify_peer" do
|
|
9
|
+
# VERIFY_PEER is a process-global constant, so anything that leaves it
|
|
10
|
+
# lowered affects every later gem download in the same run. `busser plugin
|
|
11
|
+
# install` takes a list of plugins, which is exactly that situation.
|
|
12
|
+
it "lowers verification for the block" do
|
|
13
|
+
inside = nil
|
|
14
|
+
command.send(:drop_ssl_verify_peer) { inside = OpenSSL::SSL::VERIFY_PEER }
|
|
15
|
+
|
|
16
|
+
_(inside).must_equal OpenSSL::SSL::VERIFY_NONE
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "restores verification afterwards" do
|
|
20
|
+
before = OpenSSL::SSL::VERIFY_PEER
|
|
21
|
+
command.send(:drop_ssl_verify_peer) { nil }
|
|
22
|
+
|
|
23
|
+
_(OpenSSL::SSL::VERIFY_PEER).must_equal before
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The regression this guards. A postinstall raising is not exotic: it is
|
|
27
|
+
# how a plugin reports that it could not install its test framework.
|
|
28
|
+
it "restores verification when the block raises" do
|
|
29
|
+
before = OpenSSL::SSL::VERIFY_PEER
|
|
30
|
+
|
|
31
|
+
_(proc { command.send(:drop_ssl_verify_peer) { raise "postinstall blew up" } })
|
|
32
|
+
.must_raise RuntimeError
|
|
33
|
+
|
|
34
|
+
_(OpenSSL::SSL::VERIFY_PEER).must_equal before
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "restores verification when the block throws" do
|
|
38
|
+
before = OpenSSL::SSL::VERIFY_PEER
|
|
39
|
+
catch(:done) { command.send(:drop_ssl_verify_peer) { throw :done } }
|
|
40
|
+
|
|
41
|
+
_(OpenSSL::SSL::VERIFY_PEER).must_equal before
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require_relative "../../spec_helper"
|
|
2
|
+
|
|
3
|
+
require "busser/command/plugin_list"
|
|
4
|
+
|
|
5
|
+
describe Busser::Command::PluginList do
|
|
6
|
+
let(:command) { Busser::Command::PluginList.new }
|
|
7
|
+
|
|
8
|
+
describe "#plugin_data" do
|
|
9
|
+
def stub_plugins(paths, specs = {})
|
|
10
|
+
Busser::Plugin.stubs(:runner_plugins).returns(paths)
|
|
11
|
+
paths.each do |path|
|
|
12
|
+
Busser::Plugin.stubs(:gem_from_path).with(path).returns(specs[path])
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def spec_for(name, version)
|
|
17
|
+
Gem::Specification.new { |s| s.name = name; s.version = version }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "reports each plugin's short name and version" do
|
|
21
|
+
stub_plugins(["busser/runner_plugin/bash"],
|
|
22
|
+
"busser/runner_plugin/bash" => spec_for("busser-bash", "0.2.0"))
|
|
23
|
+
|
|
24
|
+
_(command.send(:plugin_data)).must_equal [["bash", Gem::Version.new("0.2.0")]]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# dummy is a fixture shipped inside busser for its own tests, not something
|
|
28
|
+
# anyone installed. `busser test` already passes over it, so listing it was
|
|
29
|
+
# inconsistent as well as confusing.
|
|
30
|
+
it "leaves out the internal dummy runner" do
|
|
31
|
+
stub_plugins(["busser/runner_plugin/dummy", "busser/runner_plugin/bash"],
|
|
32
|
+
"busser/runner_plugin/bash" => spec_for("busser-bash", "0.2.0"))
|
|
33
|
+
|
|
34
|
+
_(command.send(:plugin_data).map(&:first)).must_equal %w{bash}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "reports a nil version rather than raising when no spec is found" do
|
|
38
|
+
stub_plugins(["busser/runner_plugin/bash"])
|
|
39
|
+
|
|
40
|
+
_(command.send(:plugin_data)).must_equal [["bash", nil]]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "returns an empty list when dummy is all there is" do
|
|
44
|
+
stub_plugins(["busser/runner_plugin/dummy"])
|
|
45
|
+
|
|
46
|
+
_(command.send(:plugin_data)).must_be_empty
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
|
|
14
|
+
require_relative "../../spec_helper"
|
|
15
|
+
|
|
16
|
+
require "tmpdir"
|
|
17
|
+
|
|
18
|
+
require "busser/command/setup"
|
|
19
|
+
|
|
20
|
+
# The binstub exists to give Busser an isolated gem environment on the machine
|
|
21
|
+
# under test, so what it does to the environment before exec is the contract
|
|
22
|
+
# worth pinning down.
|
|
23
|
+
describe Busser::Command::Setup do
|
|
24
|
+
|
|
25
|
+
# Anything left set here lets the calling Ruby environment reach into
|
|
26
|
+
# Busser's. RUBYOPT was once the only way in; RubyGems now requires
|
|
27
|
+
# bundler/setup whenever BUNDLER_SETUP is present, and bundler then resets
|
|
28
|
+
# GEM_HOME to the calling project's bundle.
|
|
29
|
+
LEAKY_VARIABLES = %w{
|
|
30
|
+
RUBYOPT GEMRC RUBYLIB
|
|
31
|
+
BUNDLER_SETUP BUNDLER_VERSION
|
|
32
|
+
BUNDLE_BIN_PATH BUNDLE_GEMFILE BUNDLE_LOCKFILE BUNDLE_PATH
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
before do
|
|
36
|
+
@tmpdir = Dir.mktmpdir("busser-setup")
|
|
37
|
+
@original_root = ENV["BUSSER_ROOT"]
|
|
38
|
+
ENV["BUSSER_ROOT"] = @tmpdir
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
after do
|
|
42
|
+
ENV["BUSSER_ROOT"] = @original_root
|
|
43
|
+
FileUtils.rm_rf(@tmpdir)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe "the bourne binstub" do
|
|
47
|
+
|
|
48
|
+
it "is created and executable" do
|
|
49
|
+
run_setup
|
|
50
|
+
|
|
51
|
+
_(File.exist?(binstub)).must_equal true
|
|
52
|
+
_(File.stat(binstub).mode & 0o111).wont_equal 0
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "pins BUSSER_ROOT and the gem paths" do
|
|
56
|
+
run_setup
|
|
57
|
+
|
|
58
|
+
_(binstub_body).must_match(/BUSSER_ROOT="#{Regexp.escape(@tmpdir)}"/)
|
|
59
|
+
_(binstub_body).must_match(/^GEM_HOME=/)
|
|
60
|
+
_(binstub_body).must_match(/^GEM_PATH=/)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
LEAKY_VARIABLES.each do |name|
|
|
64
|
+
it "unsets #{name} before exec" do
|
|
65
|
+
run_setup
|
|
66
|
+
|
|
67
|
+
_(unset_variables).must_include name
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "unsets everything before handing off to ruby" do
|
|
72
|
+
run_setup
|
|
73
|
+
|
|
74
|
+
unset_line = binstub_body.index("unset ")
|
|
75
|
+
exec_line = binstub_body.index("exec ")
|
|
76
|
+
|
|
77
|
+
_(unset_line).must_be :<, exec_line
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def binstub
|
|
81
|
+
File.join(@tmpdir, "bin", "busser")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def unset_variables
|
|
85
|
+
binstub_body.scan(/^unset (.+)$/).flatten.join(" ").split(/\s+/)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "the bat binstub" do
|
|
90
|
+
|
|
91
|
+
LEAKY_VARIABLES.each do |name|
|
|
92
|
+
it "clears #{name} before exec" do
|
|
93
|
+
run_setup(type: "bat")
|
|
94
|
+
|
|
95
|
+
_(binstub_body).must_match(/^SET #{name}=\s*$/)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def binstub
|
|
100
|
+
File.join(@tmpdir, "bin", "busser.bat")
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def run_setup(type: "bourne")
|
|
105
|
+
command = Busser::Command::Setup.new([], { "type" => type })
|
|
106
|
+
capture_stdout { command.perform }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def binstub_body
|
|
110
|
+
File.read(binstub)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def capture_stdout
|
|
114
|
+
original = $stdout
|
|
115
|
+
$stdout = StringIO.new
|
|
116
|
+
yield
|
|
117
|
+
ensure
|
|
118
|
+
$stdout = original
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require_relative "../../spec_helper"
|
|
2
|
+
|
|
3
|
+
require "shellwords"
|
|
4
|
+
require "busser/command/test"
|
|
5
|
+
|
|
6
|
+
describe Busser::Command::Test do
|
|
7
|
+
describe ".prepare_sh_command" do
|
|
8
|
+
it "runs the script with sh" do
|
|
9
|
+
cmd = Busser::Command::Test.prepare_sh_command("/opt/busser/suites/bats/prepare.sh")
|
|
10
|
+
|
|
11
|
+
_(Shellwords.split(cmd)).must_equal ["/bin/sh", "/opt/busser/suites/bats/prepare.sh"]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# BUSSER_ROOT is chosen by whoever runs Busser, and Test Kitchen puts it
|
|
15
|
+
# under a path the user controls. Unquoted, a space split the path and sh
|
|
16
|
+
# was handed a fragment.
|
|
17
|
+
it "quotes a path containing spaces" do
|
|
18
|
+
cmd = Busser::Command::Test.prepare_sh_command("/tmp/my tests/suites/bats/prepare.sh")
|
|
19
|
+
|
|
20
|
+
_(Shellwords.split(cmd))
|
|
21
|
+
.must_equal ["/bin/sh", "/tmp/my tests/suites/bats/prepare.sh"]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "neutralises shell metacharacters in the path" do
|
|
25
|
+
cmd = Busser::Command::Test.prepare_sh_command("/tmp/a;touch pwned/prepare.sh")
|
|
26
|
+
|
|
27
|
+
_(Shellwords.split(cmd)).must_equal ["/bin/sh", "/tmp/a;touch pwned/prepare.sh"]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "accepts a Pathname" do
|
|
31
|
+
cmd = Busser::Command::Test.prepare_sh_command(Pathname.new("/a/prepare.sh"))
|
|
32
|
+
|
|
33
|
+
_(Shellwords.split(cmd).last).must_equal "/a/prepare.sh"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/spec/busser/helpers_spec.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "../spec_helper"
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "busser/helpers"
|
|
4
4
|
|
|
5
5
|
describe Busser::Helpers do
|
|
6
6
|
|
|
@@ -25,16 +25,16 @@ describe Busser::Helpers do
|
|
|
25
25
|
|
|
26
26
|
describe "with a custom root path" do
|
|
27
27
|
|
|
28
|
-
before { ENV[
|
|
29
|
-
after { ENV[
|
|
28
|
+
before { ENV["_SPEC_BUSSER_ROOT"] = ENV["BUSSER_ROOT"] }
|
|
29
|
+
after { ENV["BUSSER_ROOT"] = ENV.delete("_SPEC_BUSSER_ROOT") }
|
|
30
30
|
|
|
31
31
|
it "returns a base path if no suite name is given" do
|
|
32
|
-
ENV[
|
|
32
|
+
ENV["BUSSER_ROOT"] = "/path/to/busser"
|
|
33
33
|
_(suite_path.to_s).must_match %r{/path/to/busser/suites$}
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
it "returns a suite path given a suite name" do
|
|
37
|
-
ENV[
|
|
37
|
+
ENV["BUSSER_ROOT"] = "/path/to/busser"
|
|
38
38
|
_(suite_path("fuzzy").to_s).must_match %r{/path/to/busser/suites/fuzzy$}
|
|
39
39
|
end
|
|
40
40
|
end
|
|
@@ -59,16 +59,16 @@ describe Busser::Helpers do
|
|
|
59
59
|
|
|
60
60
|
describe "with a custom root path" do
|
|
61
61
|
|
|
62
|
-
before { ENV[
|
|
63
|
-
after { ENV[
|
|
62
|
+
before { ENV["_SPEC_BUSSER_ROOT"] = ENV["BUSSER_ROOT"] }
|
|
63
|
+
after { ENV["BUSSER_ROOT"] = ENV.delete("_SPEC_BUSSER_ROOT") }
|
|
64
64
|
|
|
65
65
|
it "returns a base path if no product name is given" do
|
|
66
|
-
ENV[
|
|
66
|
+
ENV["BUSSER_ROOT"] = "/path/to/busser"
|
|
67
67
|
_(vendor_path.to_s).must_match %r{/path/to/busser/vendor$}
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
it "returns a suite path given a product name" do
|
|
71
|
-
ENV[
|
|
71
|
+
ENV["BUSSER_ROOT"] = "/path/to/busser"
|
|
72
72
|
_(vendor_path("maximal").to_s).must_match \
|
|
73
73
|
%r{/path/to/busser/vendor/maximal$}
|
|
74
74
|
end
|
data/spec/busser/plugin_spec.rb
CHANGED
data/spec/busser/ui_spec.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "../spec_helper"
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "busser/ui"
|
|
4
4
|
|
|
5
5
|
# Dummy class containing diagnostic methods approximating Thor mixins
|
|
6
6
|
class SneakyUI
|
|
@@ -11,7 +11,7 @@ class SneakyUI
|
|
|
11
11
|
attr_accessor :status
|
|
12
12
|
|
|
13
13
|
def say(msg)
|
|
14
|
-
|
|
14
|
+
msg
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def error(msg)
|
|
@@ -45,10 +45,16 @@ end
|
|
|
45
45
|
# Stub that mimics a Process::Status object
|
|
46
46
|
class FakeStatus
|
|
47
47
|
|
|
48
|
-
attr_reader :exitstatus
|
|
48
|
+
attr_reader :exitstatus, :termsig
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
# A process stopped by a signal reports success? as nil rather than false,
|
|
51
|
+
# carries no exitstatus, and sets termsig instead.
|
|
52
|
+
def self.signalled(termsig)
|
|
53
|
+
new(nil, nil, termsig)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def initialize(success = true, exitstatus = 0, termsig = nil)
|
|
57
|
+
@success, @exitstatus, @termsig = success, exitstatus, termsig
|
|
52
58
|
end
|
|
53
59
|
|
|
54
60
|
def success?
|
|
@@ -105,7 +111,7 @@ describe Busser::UI do
|
|
|
105
111
|
|
|
106
112
|
_(ui.run_args).must_equal([
|
|
107
113
|
"doitpls",
|
|
108
|
-
{ :
|
|
114
|
+
{ capture: false, verbose: false },
|
|
109
115
|
])
|
|
110
116
|
end
|
|
111
117
|
|
|
@@ -138,6 +144,26 @@ describe Busser::UI do
|
|
|
138
144
|
end
|
|
139
145
|
end
|
|
140
146
|
|
|
147
|
+
# The out of memory killer stops a run with SIGKILL. exitstatus is nil
|
|
148
|
+
# there, and reporting it as an exit code used to raise a TypeError from
|
|
149
|
+
# Kernel#exit, so Busser died with a stack trace instead of saying what
|
|
150
|
+
# happened.
|
|
151
|
+
it "reports the signal when the command was killed" do
|
|
152
|
+
ui.status = FakeStatus.signalled(9)
|
|
153
|
+
output = capture_stderr { ui.invoke_run!("failwhale") }
|
|
154
|
+
|
|
155
|
+
_(output).must_match(/terminated by signal 9/)
|
|
156
|
+
_(ui.died?).must_equal true
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "terminates with 128 plus the signal when the command was killed" do
|
|
160
|
+
ui.status = FakeStatus.signalled(9)
|
|
161
|
+
|
|
162
|
+
capture_stderr do
|
|
163
|
+
_(ui.invoke_run!("failwhale")).must_equal 137
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
141
167
|
it "terminates the program if status is nil" do
|
|
142
168
|
ui.status = nil
|
|
143
169
|
output = capture_stderr { ui.invoke_run!("failwhale") }
|
|
@@ -155,16 +181,16 @@ describe Busser::UI do
|
|
|
155
181
|
|
|
156
182
|
_(ui.run_ruby_script_args).must_equal([
|
|
157
183
|
"theworks.rb",
|
|
158
|
-
{ :
|
|
184
|
+
{ capture: false, verbose: false },
|
|
159
185
|
])
|
|
160
186
|
end
|
|
161
187
|
|
|
162
188
|
it "calls #run_ruby_script with correct default options" do
|
|
163
|
-
ui.invoke_run_ruby_script!("theworks.rb", :
|
|
189
|
+
ui.invoke_run_ruby_script!("theworks.rb", verbose: true)
|
|
164
190
|
|
|
165
191
|
_(ui.run_ruby_script_args).must_equal([
|
|
166
192
|
"theworks.rb",
|
|
167
|
-
{ :
|
|
193
|
+
{ capture: false, verbose: true },
|
|
168
194
|
])
|
|
169
195
|
end
|
|
170
196
|
|
|
@@ -197,6 +223,26 @@ describe Busser::UI do
|
|
|
197
223
|
end
|
|
198
224
|
end
|
|
199
225
|
|
|
226
|
+
# The out of memory killer stops a run with SIGKILL. exitstatus is nil
|
|
227
|
+
# there, and reporting it as an exit code used to raise a TypeError from
|
|
228
|
+
# Kernel#exit, so Busser died with a stack trace instead of saying what
|
|
229
|
+
# happened.
|
|
230
|
+
it "reports the signal when the command was killed" do
|
|
231
|
+
ui.status = FakeStatus.signalled(9)
|
|
232
|
+
output = capture_stderr { ui.invoke_run_ruby_script!("nope.rb") }
|
|
233
|
+
|
|
234
|
+
_(output).must_match(/terminated by signal 9/)
|
|
235
|
+
_(ui.died?).must_equal true
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
it "terminates with 128 plus the signal when the command was killed" do
|
|
239
|
+
ui.status = FakeStatus.signalled(9)
|
|
240
|
+
|
|
241
|
+
capture_stderr do
|
|
242
|
+
_(ui.invoke_run_ruby_script!("nope.rb")).must_equal 137
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
200
246
|
it "terminates the program if status is nil" do
|
|
201
247
|
ui.status = nil
|
|
202
248
|
output = capture_stderr { ui.invoke_run_ruby_script!("nope.rb") }
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
|
4
3
|
#
|
|
@@ -16,5 +15,5 @@
|
|
|
16
15
|
# See the License for the specific language governing permissions and
|
|
17
16
|
# limitations under the License.
|
|
18
17
|
|
|
19
|
-
require
|
|
20
|
-
require
|
|
18
|
+
require "minitest/autorun"
|
|
19
|
+
require "mocha/minitest"
|
|
@@ -1,31 +1,11 @@
|
|
|
1
1
|
require "bundler/gem_tasks"
|
|
2
|
-
require
|
|
3
|
-
require 'cane/rake_task'
|
|
4
|
-
require 'tailor/rake_task'
|
|
2
|
+
require "cucumber/rake/task"
|
|
5
3
|
|
|
6
4
|
Cucumber::Rake::Task.new(:features) do |t|
|
|
7
|
-
t.cucumber_opts = [
|
|
5
|
+
t.cucumber_opts = ["features", "-x", "--format progress", "--no-color"]
|
|
8
6
|
end
|
|
9
7
|
|
|
10
8
|
desc "Run all test suites"
|
|
11
|
-
task :
|
|
9
|
+
task test: [:features]
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
Cane::RakeTask.new do |cane|
|
|
15
|
-
cane.canefile = './.cane'
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
Tailor::RakeTask.new
|
|
19
|
-
|
|
20
|
-
desc "Display LOC stats"
|
|
21
|
-
task :stats do
|
|
22
|
-
puts "\n## Production Code Stats"
|
|
23
|
-
sh "countloc -r lib"
|
|
24
|
-
puts "\n## Test Code Stats"
|
|
25
|
-
sh "countloc -r features"
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
desc "Run all quality tasks"
|
|
29
|
-
task :quality => [:cane, :tailor, :stats]
|
|
30
|
-
|
|
31
|
-
task :default => [:test, :quality]
|
|
11
|
+
task default: [:test]
|