Merge pull request #16299 from jawshooah/refactor-uninstall-test

Refactor uninstall_test.rb (bonus: bugfixes!)
This commit is contained in:
Josh Hagins 2016-01-03 16:50:19 -05:00
commit 92015efb53
26 changed files with 800 additions and 327 deletions

View File

@ -225,19 +225,7 @@ class Hbc::Artifact::UninstallBase < Hbc::Artifact::Base
# :early_script should not delete files, better defer that to :script.
# If Cask writers never need :early_script it may be removed in the future.
def uninstall_early_script(directives)
executable, script_arguments = self.class.read_script_arguments(
directives,
'uninstall',
{:must_succeed => true, :sudo => true},
{:print_stdout => true},
:early_script
)
ohai "Running uninstall script #{executable}"
raise Hbc::CaskInvalidError.new(@cask, "#{stanza} :early_script without :executable") if executable.nil?
executable_path = @cask.staged_path.join(executable)
@command.run('/bin/chmod', :args => ['+x', executable_path]) if File.exists?(executable_path)
@command.run(executable_path, script_arguments)
sleep 1
uninstall_script(directives, directive_name: :early_script)
end
# :launchctl must come before :quit/:signal for cases where app would instantly re-launch
@ -306,7 +294,7 @@ class Hbc::Artifact::UninstallBase < Hbc::Artifact::Base
def get_unix_pids(bundle_id)
pid_string = @command.run!('/usr/bin/osascript',
:args => ['-e', %Q{tell application "System Events" to get the unix id of every process whose bundle identifier is "#{id}"}],
:args => ['-e', %Q{tell application "System Events" to get the unix id of every process whose bundle identifier is "#{bundle_id}"}],
:sudo => true).stdout.chomp
return [] unless pid_string.match(%r{\A\d+(?:\s*,\s*\d+)*\Z}) # sanity check
pid_string.split(%r{\s*,\s*}).map(&:strip).map(&:to_i)
@ -335,14 +323,17 @@ class Hbc::Artifact::UninstallBase < Hbc::Artifact::Base
end
# :script must come before :pkgutil, :delete, or :trash so that the script file is not already deleted
def uninstall_script(directives)
def uninstall_script(directives, directive_name: :script)
executable, script_arguments = self.class.read_script_arguments(directives,
'uninstall',
{:must_succeed => true, :sudo => true},
{:print_stdout => true},
:script)
raise Hbc::CaskInvalidError.new(@cask, "#{stanza} :script without :executable.") if executable.nil?
@command.run(@cask.staged_path.join(executable), script_arguments)
directive_name)
ohai "Running uninstall script #{executable}"
raise Hbc::CaskInvalidError.new(@cask, "#{stanza} :#{directive_name} without :executable.") if executable.nil?
executable_path = @cask.staged_path.join(executable)
@command.run('/bin/chmod', :args => ['+x', '--', executable_path]) if File.exists?(executable_path)
@command.run(executable_path, script_arguments)
sleep 1
end
@ -355,7 +346,7 @@ class Hbc::Artifact::UninstallBase < Hbc::Artifact::Base
end
def uninstall_delete(directives, expand_tilde=true)
Array(directives[:delete]).flatten.each_slice(PATH_ARG_SLICE_SIZE) do |path_slice|
Array(directives[:delete]).concat(Array(directives[:trash])).flatten.each_slice(PATH_ARG_SLICE_SIZE) do |path_slice|
ohai "Removing files: #{path_slice.utf8_inspect}"
path_slice = self.class.expand_path_strings(path_slice) if expand_tilde
path_slice = self.class.remove_relative_path_strings(:delete, path_slice)

View File

@ -1,16 +1,17 @@
require 'test_helper'
describe Hbc::Artifact::Uninstall do
let(:cask) { Hbc.load('with-installable') }
let(:uninstall_artifact) { Hbc::Artifact::Uninstall.new(cask, Hbc::FakeSystemCommand) }
before {
@cask = Hbc.load('with-installable')
shutup do
TestHelper.install_without_artifacts(@cask)
TestHelper.install_without_artifacts(cask)
end
}
describe 'install_phase' do
it 'does nothing, because the install_phase method is a no-op' do
uninstall_artifact = Hbc::Artifact::Uninstall.new(@cask, Hbc::FakeSystemCommand)
shutup do
uninstall_artifact.install_phase
end
@ -19,7 +20,6 @@ describe Hbc::Artifact::Uninstall do
describe 'zap_phase' do
it 'does nothing, because the zap_phase method is a no-op' do
uninstall_artifact = Hbc::Artifact::Uninstall.new(@cask, Hbc::FakeSystemCommand)
shutup do
uninstall_artifact.zap_phase
end
@ -27,168 +27,282 @@ describe Hbc::Artifact::Uninstall do
end
describe 'uninstall_phase' do
# todo: uninstall tests for :signal (implementation does not use SystemCommand)
it 'runs the specified uninstaller for the Cask' do
uninstall_artifact = Hbc::Artifact::Uninstall.new(@cask, Hbc::FakeSystemCommand)
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/usr/bin/osascript', '-e', 'tell application "System Events" to count processes whose bundle identifier is "my.fancy.package.app"'], '1')
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/usr/bin/osascript', '-e', 'tell application id "my.fancy.package.app" to quit'])
Hbc::FakeSystemCommand.expects_command(['/usr/bin/osascript', '-e', 'tell application "System Events" to delete every login item whose name is "Fancy"'])
Hbc::FakeSystemCommand.expects_command(['/usr/bin/sudo', '-E', '--', @cask.staged_path.join('MyFancyPkg','FancyUninstaller.tool'), '--please'])
Hbc::FakeSystemCommand.expects_command(['/usr/bin/sudo', '-E', '--', '/bin/rm', '-rf', '--', Pathname.new('/permissible/absolute/path'), Pathname.new('~/permissible/path/with/tilde').expand_path])
Hbc::FakeSystemCommand.expects_command(['/usr/bin/sudo', '-E', '--', '/bin/rm', '-f', '--', Pathname.new(TestHelper.local_binary_path('empty_directory')).join('.DS_Store')])
Hbc::FakeSystemCommand.expects_command(['/usr/bin/sudo', '-E', '--', '/bin/rmdir', '--', Pathname.new(TestHelper.local_binary_path('empty_directory'))])
subject {
shutup do
uninstall_artifact.uninstall_phase
end
}
describe 'when using launchctl' do
let(:cask) { Hbc.load('with-uninstall-launchctl') }
let(:launchctl_list_cmd) { %w[/bin/launchctl list my.fancy.package.service] }
let(:launchctl_remove_cmd) { %w[/bin/launchctl remove my.fancy.package.service]}
let(:unknown_response) { "launchctl list returned unknown response\n" }
let(:service_info) {
<<-PLIST.undent
{
"LimitLoadToSessionType" = "Aqua";
"Label" = "my.fancy.package.service";
"TimeOut" = 30;
"OnDemand" = true;
"LastExitStatus" = 0;
"ProgramArguments" = (
"argument";
);
};
PLIST
}
describe 'when launchctl job is owned by user' do
it 'can uninstall' do
Hbc::FakeSystemCommand.stubs_command(
launchctl_list_cmd,
service_info)
Hbc::FakeSystemCommand.stubs_command(
sudo(launchctl_list_cmd),
unknown_response)
Hbc::FakeSystemCommand.expects_command(launchctl_remove_cmd)
subject
end
end
describe 'when launchctl job is owned by system' do
it 'can uninstall' do
Hbc::FakeSystemCommand.stubs_command(
launchctl_list_cmd,
unknown_response)
Hbc::FakeSystemCommand.stubs_command(
sudo(launchctl_list_cmd),
service_info)
Hbc::FakeSystemCommand.expects_command(sudo(launchctl_remove_cmd))
subject
end
end
end
it 'can uninstall using pkgutil, launchctl, and file lists' do
cask = Hbc.load('with-pkgutil-uninstall')
uninstall_artifact = Hbc::Artifact::Uninstall.new(cask, Hbc::FakeSystemCommand)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--pkgs=my.fancy.package.*'],
[
'my.fancy.package.main',
'my.fancy.package.agent',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--only-files', '--files', 'my.fancy.package.main'],
[
'fancy/bin/fancy.exe',
'fancy/var/fancy.data',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--only-dirs', '--files', 'my.fancy.package.main'],
[
'fancy',
'fancy/bin',
'fancy/var',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--files', 'my.fancy.package.main'],
[
'fancy',
'fancy/bin',
'fancy/var',
'fancy/bin/fancy.exe',
'fancy/var/fancy.data',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--pkg-info-plist', 'my.fancy.package.main'],
<<-PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>install-location</key>
<string>tmp</string>
<key>volume</key>
<string>/</string>
</dict>
</plist>
describe 'when using pkgutil' do
let(:cask) { Hbc.load('with-uninstall-pkgutil') }
let(:main_pkg_id) { 'my.fancy.package.main' }
let(:agent_pkg_id) { 'my.fancy.package.agent' }
let(:main_files) {
%w[
fancy/bin/fancy.exe
fancy/var/fancy.data
]
}
let(:main_dirs) {
%w[
fancy
fancy/bin
fancy/var
]
}
let(:agent_files) {
%w[
fancy/agent/fancy-agent.exe
fancy/agent/fancy-agent.pid
fancy/agent/fancy-agent.log
]
}
let(:agent_dirs) {
%w[
fancy
fancy/agent
]
}
let(:pkg_info_plist) {
<<-PLIST.undent
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>install-location</key>
<string>tmp</string>
<key>volume</key>
<string>/</string>
</dict>
</plist>
PLIST
)
}
Hbc::FakeSystemCommand.stubs_command(
['/bin/launchctl', 'list', 'my.fancy.package.service'],
"launchctl list returned unknown response\n"
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/bin/sudo', '-E', '--', '/bin/launchctl', 'list', 'my.fancy.package.service'],
<<-"PLIST"
{
"LimitLoadToSessionType" = "Aqua";
"Label" = "my.fancy.package.service";
"TimeOut" = 30;
"OnDemand" = true;
"LastExitStatus" = 0;
"ProgramArguments" = (
"argument";
);
};
PLIST
)
it 'can uninstall' do
Hbc::FakeSystemCommand.stubs_command(
%w[/usr/sbin/pkgutil --pkgs=my.fancy.package.*],
"#{main_pkg_id}\n#{agent_pkg_id}")
Hbc::FakeSystemCommand.expects_command(['/usr/bin/sudo', '-E', '--', '/bin/launchctl', 'remove', 'my.fancy.package.service'])
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/usr/sbin/kextstat', '-l', '-b', 'my.fancy.package.kernelextension'], 'loaded')
Hbc::FakeSystemCommand.expects_command(['/usr/bin/sudo', '-E', '--', '/sbin/kextunload', '-b', 'my.fancy.package.kernelextension'])
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/usr/sbin/pkgutil', '--forget', 'my.fancy.package.main'])
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--only-files', '--files', 'my.fancy.package.agent'],
[
'fancy/agent/fancy-agent.exe',
'fancy/agent/fancy-agent.pid',
'fancy/agent/fancy-agent.log',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--only-dirs', '--files', 'my.fancy.package.agent'],
[
'fancy',
'fancy/agent',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--files', 'my.fancy.package.agent'],
[
'fancy',
'fancy/agent',
'fancy/agent/fancy-agent.exe',
'fancy/agent/fancy-agent.pid',
'fancy/agent/fancy-agent.log',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--pkg-info-plist', 'my.fancy.package.agent'],
<<-PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>install-location</key>
<string>tmp</string>
<key>volume</key>
<string>/</string>
</dict>
</plist>
PLIST
)
[main_pkg_id, main_files, main_dirs],
[agent_pkg_id, agent_files, agent_dirs]
].each do |pkg_id, pkg_files, pkg_dirs|
Hbc::FakeSystemCommand.stubs_command(
%W[/usr/sbin/pkgutil --only-files --files #{pkg_id}],
pkg_files.join("\n"))
%w[
/tmp/fancy
/tmp/fancy/agent
/tmp/fancy/bin
/tmp/fancy/var
].each do |dir|
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/bin/chmod', '--', '777', '#{dir}'])
Hbc::FakeSystemCommand.stubs_command(
%W[/usr/sbin/pkgutil --only-dirs --files #{pkg_id}],
pkg_dirs.join("\n"))
Hbc::FakeSystemCommand.stubs_command(
%W[/usr/sbin/pkgutil --files #{pkg_id}],
(pkg_files + pkg_dirs).join("\n"))
Hbc::FakeSystemCommand.stubs_command(
%W[/usr/sbin/pkgutil --pkg-info-plist #{pkg_id}],
pkg_info_plist)
Hbc::FakeSystemCommand.expects_command(sudo(%W[/usr/sbin/pkgutil --forget #{pkg_id}]))
Hbc::FakeSystemCommand.expects_command(
sudo(%w[/bin/rm -f --] + pkg_files.map { |path| Pathname("/tmp/#{path}")}))
end
subject
end
end
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/usr/sbin/pkgutil', '--forget', 'my.fancy.package.agent'])
describe 'when using kext' do
let(:cask) { Hbc.load('with-uninstall-kext') }
let(:kext_id) { 'my.fancy.package.kernelextension' }
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/bin/rm', '-f', '--',
Pathname.new('/tmp/fancy/bin/fancy.exe'),
Pathname.new('/tmp/fancy/var/fancy.data')])
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/bin/rm', '-f', '--',
Pathname.new('/tmp/fancy/agent/fancy-agent.exe'),
Pathname.new('/tmp/fancy/agent/fancy-agent.pid'),
Pathname.new('/tmp/fancy/agent/fancy-agent.log')])
it 'can uninstall' do
Hbc::FakeSystemCommand.stubs_command(
sudo(%W[/usr/sbin/kextstat -l -b #{kext_id}]), 'loaded')
# No assertions after call since all assertions are implicit from the interactions setup above.
# TODO: verify rmdir commands (requires setting up actual file tree or faking out .exists?
shutup do
uninstall_artifact.uninstall_phase
Hbc::FakeSystemCommand.expects_command(
sudo(%W[/sbin/kextunload -b #{kext_id}]))
subject
end
end
describe 'when using quit' do
let(:cask) { Hbc.load('with-uninstall-quit') }
let(:bundle_id) { 'my.fancy.package.app' }
let(:count_processes_script) {
'tell application "System Events" to count processes ' +
%Q{whose bundle identifier is "#{bundle_id}"}
}
let(:quit_application_script) {
%Q{tell application id "#{bundle_id}" to quit}
}
it 'can uninstall' do
Hbc::FakeSystemCommand.stubs_command(
sudo(%W[/usr/bin/osascript -e #{count_processes_script}]), '1')
Hbc::FakeSystemCommand.stubs_command(
sudo(%W[/usr/bin/osascript -e #{quit_application_script}]))
subject
end
end
describe 'when using signal' do
let(:cask) { Hbc.load('with-uninstall-signal') }
let(:bundle_id) { 'my.fancy.package.app' }
let(:signals) { %w[TERM KILL] }
let(:unix_pids) { [12345, 67890] }
let(:get_unix_pids_script) {
'tell application "System Events" to get the unix id of every process ' +
%Q{whose bundle identifier is "#{bundle_id}"}
}
it 'can uninstall' do
Hbc::FakeSystemCommand.stubs_command(
sudo(%W[/usr/bin/osascript -e #{get_unix_pids_script}]), unix_pids.join(', '))
signals.each do |signal|
Process.expects(:kill).with(signal, *unix_pids)
end
subject
end
end
describe 'when using delete' do
let(:cask) { Hbc.load('with-uninstall-delete') }
it 'can uninstall' do
Hbc::FakeSystemCommand.expects_command(
sudo(%w[/bin/rm -rf --],
Pathname.new('/permissible/absolute/path'),
Pathname.new('~/permissible/path/with/tilde').expand_path))
subject
end
end
describe 'when using trash' do
let(:cask) { Hbc.load('with-uninstall-trash') }
it 'can uninstall' do
Hbc::FakeSystemCommand.expects_command(
sudo(%w[/bin/rm -rf --],
Pathname.new('/permissible/absolute/path'),
Pathname.new('~/permissible/path/with/tilde').expand_path))
subject
end
end
describe 'when using rmdir' do
let(:cask) { Hbc.load('with-uninstall-rmdir') }
let(:dir_pathname) { Pathname(TestHelper.local_binary_path('empty_directory')) }
it 'can uninstall' do
Hbc::FakeSystemCommand.expects_command(
sudo(%w[/bin/rm -f --], dir_pathname.join('.DS_Store')))
Hbc::FakeSystemCommand.expects_command(
sudo(%w[/bin/rmdir --], dir_pathname))
subject
end
end
describe 'when using script' do
let(:cask) { Hbc.load('with-uninstall-script') }
let(:script_pathname) { cask.staged_path.join('MyFancyPkg','FancyUninstaller.tool') }
it 'can uninstall' do
Hbc::FakeSystemCommand.expects_command(%w[/bin/chmod +x --] + [script_pathname])
Hbc::FakeSystemCommand.expects_command(
sudo(cask.staged_path.join('MyFancyPkg','FancyUninstaller.tool'), '--please'))
subject
end
end
describe 'when using early_script' do
let(:cask) { Hbc.load('with-uninstall-early-script') }
let(:script_pathname) { cask.staged_path.join('MyFancyPkg','FancyUninstaller.tool') }
it 'can uninstall' do
Hbc::FakeSystemCommand.expects_command(%w[/bin/chmod +x --] + [script_pathname])
Hbc::FakeSystemCommand.expects_command(
sudo(cask.staged_path.join('MyFancyPkg','FancyUninstaller.tool'), '--please'))
subject
end
end
describe 'when using login_item' do
let(:cask) { Hbc.load('with-uninstall-login-item') }
it 'can uninstall' do
Hbc::FakeSystemCommand.expects_command(
['/usr/bin/osascript', '-e', 'tell application "System Events" to delete every login ' \
'item whose name is "Fancy"'])
subject
end
end
end

View File

@ -2,18 +2,18 @@ require 'test_helper'
# todo
# - test that zap removes an alternate version of the same Cask
describe Hbc::Artifact::Zap do
let(:cask) { Hbc.load('with-installable') }
let(:zap_artifact) { Hbc::Artifact::Zap.new(cask, Hbc::FakeSystemCommand) }
before {
@cask = Hbc.load('with-zap')
shutup do
TestHelper.install_without_artifacts(@cask)
TestHelper.install_without_artifacts(cask)
end
}
describe 'install_phase' do
it 'does nothing, because the install_phase method is a no-op' do
zap_artifact = Hbc::Artifact::Zap.new(@cask, Hbc::FakeSystemCommand)
shutup do
zap_artifact.install_phase
end
@ -22,7 +22,6 @@ describe Hbc::Artifact::Zap do
describe 'uninstall_phase' do
it 'does nothing, because the uninstall_phase method is a no-op' do
zap_artifact = Hbc::Artifact::Zap.new(@cask, Hbc::FakeSystemCommand)
shutup do
zap_artifact.uninstall_phase
end
@ -30,167 +29,282 @@ describe Hbc::Artifact::Zap do
end
describe 'zap_phase' do
# todo: zap tests for :signal (implementation does not use SystemCommand)
it 'runs the specified zap procedures for the Cask' do
zap_artifact = Hbc::Artifact::Zap.new(@cask, Hbc::FakeSystemCommand)
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/usr/bin/osascript', '-e', 'tell application "System Events" to count processes whose bundle identifier is "my.fancy.package.app"'], '1')
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/usr/bin/osascript', '-e', 'tell application id "my.fancy.package.app" to quit'])
Hbc::FakeSystemCommand.expects_command(['/usr/bin/osascript', '-e', 'tell application "System Events" to delete every login item whose name is "Fancy"'])
Hbc::FakeSystemCommand.expects_command(['/usr/bin/sudo', '-E', '--', @cask.staged_path.join('MyFancyPkg','FancyUninstaller.tool'), '--please'])
Hbc::FakeSystemCommand.expects_command(['/usr/bin/sudo', '-E', '--', '/bin/rm', '-rf', '--',
Pathname.new('~/Library/Preferences/my.fancy.app.plist').expand_path])
subject {
shutup do
zap_artifact.zap_phase
end
}
describe 'when using launchctl' do
let(:cask) { Hbc.load('with-zap-launchctl') }
let(:launchctl_list_cmd) { %w[/bin/launchctl list my.fancy.package.service] }
let(:launchctl_remove_cmd) { %w[/bin/launchctl remove my.fancy.package.service]}
let(:unknown_response) { "launchctl list returned unknown response\n" }
let(:service_info) {
<<-PLIST.undent
{
"LimitLoadToSessionType" = "Aqua";
"Label" = "my.fancy.package.service";
"TimeOut" = 30;
"OnDemand" = true;
"LastExitStatus" = 0;
"ProgramArguments" = (
"argument";
);
};
PLIST
}
describe 'when launchctl job is owned by user' do
it 'can zap' do
Hbc::FakeSystemCommand.stubs_command(
launchctl_list_cmd,
service_info)
Hbc::FakeSystemCommand.stubs_command(
sudo(launchctl_list_cmd),
unknown_response)
Hbc::FakeSystemCommand.expects_command(launchctl_remove_cmd)
subject
end
end
describe 'when launchctl job is owned by system' do
it 'can zap' do
Hbc::FakeSystemCommand.stubs_command(
launchctl_list_cmd,
unknown_response)
Hbc::FakeSystemCommand.stubs_command(
sudo(launchctl_list_cmd),
service_info)
Hbc::FakeSystemCommand.expects_command(sudo(launchctl_remove_cmd))
subject
end
end
end
it 'can zap using pkgutil, launchctl, and file lists' do
cask = Hbc.load('with-pkgutil-zap')
zap_artifact = Hbc::Artifact::Zap.new(cask, Hbc::FakeSystemCommand)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--pkgs=my.fancy.package.*'],
[
'my.fancy.package.main',
'my.fancy.package.agent',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--only-files', '--files', 'my.fancy.package.main'],
[
'fancy/bin/fancy.exe',
'fancy/var/fancy.data',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--only-dirs', '--files', 'my.fancy.package.main'],
[
'fancy',
'fancy/bin',
'fancy/var',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--files', 'my.fancy.package.main'],
[
'fancy',
'fancy/bin',
'fancy/var',
'fancy/bin/fancy.exe',
'fancy/var/fancy.data',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--pkg-info-plist', 'my.fancy.package.main'],
<<-PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>install-location</key>
<string>tmp</string>
<key>volume</key>
<string>/</string>
</dict>
</plist>
describe 'when using pkgutil' do
let(:cask) { Hbc.load('with-zap-pkgutil') }
let(:main_pkg_id) { 'my.fancy.package.main' }
let(:agent_pkg_id) { 'my.fancy.package.agent' }
let(:main_files) {
%w[
fancy/bin/fancy.exe
fancy/var/fancy.data
]
}
let(:main_dirs) {
%w[
fancy
fancy/bin
fancy/var
]
}
let(:agent_files) {
%w[
fancy/agent/fancy-agent.exe
fancy/agent/fancy-agent.pid
fancy/agent/fancy-agent.log
]
}
let(:agent_dirs) {
%w[
fancy
fancy/agent
]
}
let(:pkg_info_plist) {
<<-PLIST.undent
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>install-location</key>
<string>tmp</string>
<key>volume</key>
<string>/</string>
</dict>
</plist>
PLIST
)
}
Hbc::FakeSystemCommand.stubs_command(
['/bin/launchctl', 'list', 'my.fancy.package.service'],
"launchctl list returned unknown response\n"
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/bin/sudo', '-E', '--', '/bin/launchctl', 'list', 'my.fancy.package.service'],
<<-"PLIST"
{
"LimitLoadToSessionType" = "Aqua";
"Label" = "my.fancy.package.service";
"TimeOut" = 30;
"OnDemand" = true;
"LastExitStatus" = 0;
"ProgramArguments" = (
"argument";
);
};
PLIST
)
it 'can zap' do
Hbc::FakeSystemCommand.stubs_command(
%w[/usr/sbin/pkgutil --pkgs=my.fancy.package.*],
"#{main_pkg_id}\n#{agent_pkg_id}")
Hbc::FakeSystemCommand.expects_command(['/usr/bin/sudo', '-E', '--', '/bin/launchctl', 'remove', 'my.fancy.package.service'])
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/usr/sbin/kextstat', '-l', '-b', 'my.fancy.package.kernelextension'], 'loaded')
Hbc::FakeSystemCommand.expects_command(['/usr/bin/sudo', '-E', '--', '/sbin/kextunload', '-b', 'my.fancy.package.kernelextension'])
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/usr/sbin/pkgutil', '--forget', 'my.fancy.package.main'])
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--only-files', '--files', 'my.fancy.package.agent'],
[
'fancy/agent/fancy-agent.exe',
'fancy/agent/fancy-agent.pid',
'fancy/agent/fancy-agent.log',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--only-dirs', '--files', 'my.fancy.package.agent'],
[
'fancy',
'fancy/agent',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--files', 'my.fancy.package.agent'],
[
'fancy',
'fancy/agent',
'fancy/agent/fancy-agent.exe',
'fancy/agent/fancy-agent.pid',
'fancy/agent/fancy-agent.log',
].join("\n")
)
Hbc::FakeSystemCommand.stubs_command(
['/usr/sbin/pkgutil', '--pkg-info-plist', 'my.fancy.package.agent'],
<<-PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>install-location</key>
<string>tmp</string>
<key>volume</key>
<string>/</string>
</dict>
</plist>
PLIST
)
[main_pkg_id, main_files, main_dirs],
[agent_pkg_id, agent_files, agent_dirs]
].each do |pkg_id, pkg_files, pkg_dirs|
Hbc::FakeSystemCommand.stubs_command(
%W[/usr/sbin/pkgutil --only-files --files #{pkg_id}],
pkg_files.join("\n"))
%w[
/tmp/fancy
/tmp/fancy/agent
/tmp/fancy/bin
/tmp/fancy/var
].each do |dir|
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/bin/chmod', '--', '777', '#{dir}'])
Hbc::FakeSystemCommand.stubs_command(
%W[/usr/sbin/pkgutil --only-dirs --files #{pkg_id}],
pkg_dirs.join("\n"))
Hbc::FakeSystemCommand.stubs_command(
%W[/usr/sbin/pkgutil --files #{pkg_id}],
(pkg_files + pkg_dirs).join("\n"))
Hbc::FakeSystemCommand.stubs_command(
%W[/usr/sbin/pkgutil --pkg-info-plist #{pkg_id}],
pkg_info_plist)
Hbc::FakeSystemCommand.expects_command(sudo(%W[/usr/sbin/pkgutil --forget #{pkg_id}]))
Hbc::FakeSystemCommand.expects_command(
sudo(%w[/bin/rm -f --] + pkg_files.map { |path| Pathname("/tmp/#{path}")}))
end
subject
end
end
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/usr/sbin/pkgutil', '--forget', 'my.fancy.package.agent'])
describe 'when using kext' do
let(:cask) { Hbc.load('with-zap-kext') }
let(:kext_id) { 'my.fancy.package.kernelextension' }
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/bin/rm', '-f', '--',
Pathname.new('/tmp/fancy/bin/fancy.exe'),
Pathname.new('/tmp/fancy/var/fancy.data')])
Hbc::FakeSystemCommand.stubs_command(['/usr/bin/sudo', '-E', '--', '/bin/rm', '-f', '--',
Pathname.new('/tmp/fancy/agent/fancy-agent.exe'),
Pathname.new('/tmp/fancy/agent/fancy-agent.pid'),
Pathname.new('/tmp/fancy/agent/fancy-agent.log')])
it 'can zap' do
Hbc::FakeSystemCommand.stubs_command(
sudo(%W[/usr/sbin/kextstat -l -b #{kext_id}]), 'loaded')
# No assertions after call since all assertions are implicit from the interactions setup above.
# TODO: verify rmdir commands (requires setting up actual file tree or faking out .exists?
shutup do
zap_artifact.zap_phase
Hbc::FakeSystemCommand.expects_command(
sudo(%W[/sbin/kextunload -b #{kext_id}]))
subject
end
end
describe 'when using quit' do
let(:cask) { Hbc.load('with-zap-quit') }
let(:bundle_id) { 'my.fancy.package.app' }
let(:count_processes_script) {
'tell application "System Events" to count processes ' +
%Q{whose bundle identifier is "#{bundle_id}"}
}
let(:quit_application_script) {
%Q{tell application id "#{bundle_id}" to quit}
}
it 'can zap' do
Hbc::FakeSystemCommand.stubs_command(
sudo(%W[/usr/bin/osascript -e #{count_processes_script}]), '1')
Hbc::FakeSystemCommand.stubs_command(
sudo(%W[/usr/bin/osascript -e #{quit_application_script}]))
subject
end
end
describe 'when using signal' do
let(:cask) { Hbc.load('with-zap-signal') }
let(:bundle_id) { 'my.fancy.package.app' }
let(:signals) { %w[TERM KILL] }
let(:unix_pids) { [12345, 67890] }
let(:get_unix_pids_script) {
'tell application "System Events" to get the unix id of every process ' +
%Q{whose bundle identifier is "#{bundle_id}"}
}
it 'can zap' do
Hbc::FakeSystemCommand.stubs_command(
sudo(%W[/usr/bin/osascript -e #{get_unix_pids_script}]), unix_pids.join(', '))
signals.each do |signal|
Process.expects(:kill).with(signal, *unix_pids)
end
subject
end
end
describe 'when using delete' do
let(:cask) { Hbc.load('with-zap-delete') }
it 'can zap' do
Hbc::FakeSystemCommand.expects_command(
sudo(%w[/bin/rm -rf --],
Pathname.new('/permissible/absolute/path'),
Pathname.new('~/permissible/path/with/tilde').expand_path))
subject
end
end
describe 'when using trash' do
let(:cask) { Hbc.load('with-zap-trash') }
it 'can zap' do
Hbc::FakeSystemCommand.expects_command(
sudo(%w[/bin/rm -rf --],
Pathname.new('/permissible/absolute/path'),
Pathname.new('~/permissible/path/with/tilde').expand_path))
subject
end
end
describe 'when using rmdir' do
let(:cask) { Hbc.load('with-zap-rmdir') }
let(:dir_pathname) { Pathname(TestHelper.local_binary_path('empty_directory')) }
it 'can zap' do
Hbc::FakeSystemCommand.expects_command(
sudo(%w[/bin/rm -f --], dir_pathname.join('.DS_Store')))
Hbc::FakeSystemCommand.expects_command(
sudo(%w[/bin/rmdir --], dir_pathname))
subject
end
end
describe 'when using script' do
let(:cask) { Hbc.load('with-zap-script') }
let(:script_pathname) { cask.staged_path.join('MyFancyPkg','FancyUninstaller.tool') }
it 'can zap' do
Hbc::FakeSystemCommand.expects_command(%w[/bin/chmod +x --] + [script_pathname])
Hbc::FakeSystemCommand.expects_command(
sudo(cask.staged_path.join('MyFancyPkg','FancyUninstaller.tool'), '--please'))
subject
end
end
describe 'when using early_script' do
let(:cask) { Hbc.load('with-zap-early-script') }
let(:script_pathname) { cask.staged_path.join('MyFancyPkg','FancyUninstaller.tool') }
it 'can zap' do
Hbc::FakeSystemCommand.expects_command(%w[/bin/chmod +x --] + [script_pathname])
Hbc::FakeSystemCommand.expects_command(
sudo(cask.staged_path.join('MyFancyPkg','FancyUninstaller.tool'), '--please'))
subject
end
end
describe 'when using login_item' do
let(:cask) { Hbc.load('with-zap-login-item') }
it 'can zap' do
Hbc::FakeSystemCommand.expects_command(
['/usr/bin/osascript', '-e', 'tell application "System Events" to delete every login ' \
'item whose name is "Fancy"'])
subject
end
end
end

View File

@ -0,0 +1,16 @@
test_cask 'with-uninstall-delete' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'Fancy.pkg'
uninstall :delete => [
'/permissible/absolute/path',
'~/permissible/path/with/tilde',
'impermissible/relative/path',
'/another/impermissible/../relative/path',
]
end

View File

@ -0,0 +1,11 @@
test_cask 'with-uninstall-early-script' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
uninstall :early_script => { :executable => 'MyFancyPkg/FancyUninstaller.tool', :args => %w[--please] }
end

View File

@ -1,4 +1,4 @@
test_cask 'with-pkgutil-uninstall' do
test_cask 'with-uninstall-kext' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
@ -6,7 +6,5 @@ test_cask 'with-pkgutil-uninstall' do
homepage 'http://example.com/fancy-pkg'
pkg 'Fancy.pkg'
uninstall :pkgutil => 'my.fancy.package.*',
:kext => 'my.fancy.package.kernelextension',
:launchctl => 'my.fancy.package.service'
uninstall :kext => 'my.fancy.package.kernelextension'
end

View File

@ -0,0 +1,11 @@
test_cask 'with-uninstall-launchctl' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyApp.zip')
homepage 'http://example.com/fancy'
app 'Fancy.app'
uninstall :launchctl => 'my.fancy.package.service'
end

View File

@ -0,0 +1,11 @@
test_cask 'with-uninstall-login-item' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
uninstall :login_item => 'Fancy'
end

View File

@ -0,0 +1,10 @@
test_cask 'with-uninstall-pkgutil' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'Fancy.pkg'
uninstall :pkgutil => 'my.fancy.package.*'
end

View File

@ -0,0 +1,11 @@
test_cask 'with-uninstall-quit' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
uninstall :quit => 'my.fancy.package.app'
end

View File

@ -0,0 +1,10 @@
test_cask 'with-uninstall-rmdir' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
uninstall :rmdir => TestHelper.local_binary_path('empty_directory')
end

View File

@ -0,0 +1,11 @@
test_cask 'with-uninstall-script' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
uninstall :script => { :executable => 'MyFancyPkg/FancyUninstaller.tool', :args => %w[--please] }
end

View File

@ -0,0 +1,14 @@
test_cask 'with-uninstall-signal' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
uninstall :signal => [
['TERM', 'my.fancy.package.app'],
['KILL', 'my.fancy.package.app']
]
end

View File

@ -0,0 +1,16 @@
test_cask 'with-uninstall-trash' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'Fancy.pkg'
uninstall :trash => [
'/permissible/absolute/path',
'~/permissible/path/with/tilde',
'impermissible/relative/path',
'/another/impermissible/../relative/path',
]
end

View File

@ -0,0 +1,16 @@
test_cask 'with-zap-delete' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'Fancy.pkg'
zap :delete => [
'/permissible/absolute/path',
'~/permissible/path/with/tilde',
'impermissible/relative/path',
'/another/impermissible/../relative/path',
]
end

View File

@ -0,0 +1,11 @@
test_cask 'with-zap-early-script' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
zap :early_script => { :executable => 'MyFancyPkg/FancyUninstaller.tool', :args => %w[--please] }
end

View File

@ -0,0 +1,10 @@
test_cask 'with-zap-kext' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'Fancy.pkg'
zap :kext => 'my.fancy.package.kernelextension'
end

View File

@ -0,0 +1,11 @@
test_cask 'with-zap-launchctl' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyApp.zip')
homepage 'http://example.com/fancy'
app 'Fancy.app'
zap :launchctl => 'my.fancy.package.service'
end

View File

@ -0,0 +1,11 @@
test_cask 'with-zap-login-item' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
zap :login_item => 'Fancy'
end

View File

@ -0,0 +1,10 @@
test_cask 'with-zap-pkgutil' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'Fancy.pkg'
zap :pkgutil => 'my.fancy.package.*'
end

View File

@ -0,0 +1,11 @@
test_cask 'with-zap-quit' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
zap :quit => 'my.fancy.package.app'
end

View File

@ -0,0 +1,10 @@
test_cask 'with-zap-rmdir' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
zap :rmdir => TestHelper.local_binary_path('empty_directory')
end

View File

@ -0,0 +1,11 @@
test_cask 'with-zap-script' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
zap :script => { :executable => 'MyFancyPkg/FancyUninstaller.tool', :args => %w[--please] }
end

View File

@ -0,0 +1,14 @@
test_cask 'with-zap-signal' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'MyFancyPkg/Fancy.pkg'
zap :signal => [
['TERM', 'my.fancy.package.app'],
['KILL', 'my.fancy.package.app']
]
end

View File

@ -0,0 +1,16 @@
test_cask 'with-zap-trash' do
version '1.2.3'
sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b'
url TestHelper.local_binary_url('MyFancyPkg.zip')
homepage 'http://example.com/fancy-pkg'
pkg 'Fancy.pkg'
zap :trash => [
'/permissible/absolute/path',
'~/permissible/path/with/tilde',
'impermissible/relative/path',
'/another/impermissible/../relative/path',
]
end

View File

@ -41,6 +41,10 @@ def shutup
end
end
def sudo(*args)
%w[/usr/bin/sudo -E --] + Array(args).flatten
end
# making homebrew's cache dir allows us to actually download Casks in tests
HOMEBREW_CACHE.mkpath
HOMEBREW_CACHE.join('Casks').mkpath