Refactor Cask uninstall and zap stanza.
This commit is contained in:
parent
9105acab6b
commit
2691eb6f65
@ -34,7 +34,6 @@ module Hbc
|
||||
# stanza may not be needed as an explicit argument
|
||||
description = stanza.to_s
|
||||
if key
|
||||
arguments = arguments[key]
|
||||
description.concat(" #{key.inspect}")
|
||||
end
|
||||
|
||||
|
||||
@ -6,11 +6,6 @@ require "hbc/artifact/base"
|
||||
module Hbc
|
||||
module Artifact
|
||||
class UninstallBase < Base
|
||||
# TODO: 500 is also hardcoded in cask/pkg.rb, but much of
|
||||
# that logic is probably in the wrong location
|
||||
|
||||
PATH_ARG_SLICE_SIZE = 500
|
||||
|
||||
ORDERED_DIRECTIVES = [
|
||||
:early_script,
|
||||
:launchctl,
|
||||
@ -25,47 +20,7 @@ module Hbc
|
||||
:rmdir,
|
||||
].freeze
|
||||
|
||||
# TODO: these methods were consolidated here from separate
|
||||
# sources and should be refactored for consistency
|
||||
|
||||
def self.expand_path_strings(path_strings)
|
||||
path_strings.map do |path_string|
|
||||
path_string.start_with?("~") ? Pathname.new(path_string).expand_path : Pathname.new(path_string)
|
||||
end
|
||||
end
|
||||
|
||||
def self.expand_glob(path_strings)
|
||||
path_strings.flat_map(&Pathname.method(:glob))
|
||||
end
|
||||
|
||||
def self.remove_relative_path_strings(action, path_strings)
|
||||
relative = path_strings.map do |path_string|
|
||||
path_string if %r{/\.\.(?:/|\Z)}.match(path_string) || !%r{\A/}.match(path_string)
|
||||
end.compact
|
||||
relative.each do |path_string|
|
||||
opoo "Skipping #{action} for relative path #{path_string}"
|
||||
end
|
||||
path_strings - relative
|
||||
end
|
||||
|
||||
def self.remove_undeletable_path_strings(action, path_strings)
|
||||
undeletable = path_strings.map do |path_string|
|
||||
path_string if MacOS.undeletable?(Pathname.new(path_string))
|
||||
end.compact
|
||||
undeletable.each do |path_string|
|
||||
opoo "Skipping #{action} for undeletable path #{path_string}"
|
||||
end
|
||||
path_strings - undeletable
|
||||
end
|
||||
|
||||
def self.prepare_path_strings(action, path_strings, expand_tilde)
|
||||
path_strings = expand_path_strings(path_strings) if expand_tilde
|
||||
path_strings = remove_relative_path_strings(action, path_strings)
|
||||
path_strings = expand_glob(path_strings)
|
||||
remove_undeletable_path_strings(action, path_strings)
|
||||
end
|
||||
|
||||
def dispatch_uninstall_directives(expand_tilde: true)
|
||||
def dispatch_uninstall_directives
|
||||
directives_set = @cask.artifacts[stanza]
|
||||
ohai "Running #{stanza} process for #{@cask}; your password may be necessary"
|
||||
|
||||
@ -75,9 +30,8 @@ module Hbc
|
||||
|
||||
ORDERED_DIRECTIVES.each do |directive_sym|
|
||||
directives_set.select { |h| h.key?(directive_sym) }.each do |directives|
|
||||
args = [directives]
|
||||
args << expand_tilde if [:delete, :trash, :rmdir].include?(directive_sym)
|
||||
send("uninstall_#{directive_sym}", *args)
|
||||
args = directives[directive_sym]
|
||||
send("uninstall_#{directive_sym}", *(args.is_a?(Hash) ? [args] : args))
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -102,8 +56,8 @@ module Hbc
|
||||
end
|
||||
|
||||
# :launchctl must come before :quit/:signal for cases where app would instantly re-launch
|
||||
def uninstall_launchctl(directives)
|
||||
Array(directives[:launchctl]).each do |service|
|
||||
def uninstall_launchctl(*services)
|
||||
services.each do |service|
|
||||
ohai "Removing launchctl service #{service}"
|
||||
[false, true].each do |with_sudo|
|
||||
plist_status = @command.run("/bin/launchctl", args: ["list", service], sudo: with_sudo, print_stderr: false).stdout
|
||||
@ -127,45 +81,6 @@ module Hbc
|
||||
end
|
||||
end
|
||||
|
||||
# :quit/:signal must come before :kext so the kext will not be in use by a running process
|
||||
def uninstall_quit(directives)
|
||||
Array(directives[:quit]).each do |id|
|
||||
ohai "Quitting application ID #{id}"
|
||||
next if running_processes(id).empty?
|
||||
@command.run!("/usr/bin/osascript", args: ["-e", %Q(tell application id "#{id}" to quit)], sudo: true)
|
||||
|
||||
begin
|
||||
Timeout.timeout(3) do
|
||||
Kernel.loop do
|
||||
break if running_processes(id).empty?
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# :signal should come after :quit so it can be used as a backup when :quit fails
|
||||
def uninstall_signal(directives)
|
||||
Array(directives[:signal]).flatten.each_slice(2) do |pair|
|
||||
raise CaskInvalidError.new(@cask, "Each #{stanza} :signal must have 2 elements.") unless pair.length == 2
|
||||
signal, bundle_id = pair
|
||||
ohai "Signalling '#{signal}' to application ID '#{bundle_id}'"
|
||||
pids = running_processes(bundle_id).map(&:first)
|
||||
next unless pids.any?
|
||||
# Note that unlike :quit, signals are sent from the current user (not
|
||||
# upgraded to the superuser). This is a todo item for the future, but
|
||||
# there should be some additional thought/safety checks about that, as a
|
||||
# misapplied "kill" by root could bring down the system. The fact that we
|
||||
# learned the pid from AppleScript is already some degree of protection,
|
||||
# though indirect.
|
||||
odebug "Unix ids are #{pids.inspect} for processes with bundle identifier #{bundle_id}"
|
||||
Process.kill(signal, *pids)
|
||||
sleep 3
|
||||
end
|
||||
end
|
||||
|
||||
def running_processes(bundle_id)
|
||||
@command.run!("/bin/launchctl", args: ["list"]).stdout.lines
|
||||
.map { |line| line.chomp.split("\t") }
|
||||
@ -176,8 +91,50 @@ module Hbc
|
||||
end
|
||||
end
|
||||
|
||||
def uninstall_login_item(directives)
|
||||
Array(directives[:login_item]).each do |name|
|
||||
# :quit/:signal must come before :kext so the kext will not be in use by a running process
|
||||
def uninstall_quit(*bundle_ids)
|
||||
bundle_ids.each do |bundle_id|
|
||||
ohai "Quitting application ID #{bundle_id}"
|
||||
next if running_processes(bundle_id).empty?
|
||||
@command.run!("/usr/bin/osascript", args: ["-e", %Q(tell application id "#{bundle_id}" to quit)], sudo: true)
|
||||
|
||||
begin
|
||||
Timeout.timeout(3) do
|
||||
Kernel.loop do
|
||||
break if running_processes(bundle_id).empty?
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# :signal should come after :quit so it can be used as a backup when :quit fails
|
||||
def uninstall_signal(*signals)
|
||||
signals.flatten.each_slice(2) do |pair|
|
||||
unless pair.size == 2
|
||||
raise CaskInvalidError.new(@cask, "Each #{stanza} :signal must consist of 2 elements.")
|
||||
end
|
||||
|
||||
signal, bundle_id = pair
|
||||
ohai "Signalling '#{signal}' to application ID '#{bundle_id}'"
|
||||
pids = running_processes(bundle_id).map(&:first)
|
||||
next unless pids.any?
|
||||
# Note that unlike :quit, signals are sent from the current user (not
|
||||
# upgraded to the superuser). This is a todo item for the future, but
|
||||
# there should be some additional thought/safety checks about that, as a
|
||||
# misapplied "kill" by root could bring down the system. The fact that we
|
||||
# learned the pid from AppleScript is already some degree of protection,
|
||||
# though indirect.
|
||||
odebug "Unix ids are #{pids.inspect} for processes with bundle identifier #{bundle_id}"
|
||||
Process.kill(signal, *pids)
|
||||
sleep 3
|
||||
end
|
||||
end
|
||||
|
||||
def uninstall_login_item(*login_items)
|
||||
login_items.each do |name|
|
||||
ohai "Removing login item #{name}"
|
||||
@command.run!("/usr/bin/osascript",
|
||||
args: ["-e", %Q(tell application "System Events" to delete every login item whose name is "#{name}")],
|
||||
@ -187,8 +144,8 @@ module Hbc
|
||||
end
|
||||
|
||||
# :kext should be unloaded before attempting to delete the relevant file
|
||||
def uninstall_kext(directives)
|
||||
Array(directives[:kext]).each do |kext|
|
||||
def uninstall_kext(*kexts)
|
||||
kexts.each do |kext|
|
||||
ohai "Unloading kernel extension #{kext}"
|
||||
is_loaded = @command.run!("/usr/sbin/kextstat", args: ["-l", "-b", kext], sudo: true).stdout
|
||||
if is_loaded.length > 1
|
||||
@ -209,6 +166,7 @@ module Hbc
|
||||
{ must_succeed: true, sudo: true },
|
||||
{ print_stdout: true },
|
||||
directive_name)
|
||||
|
||||
ohai "Running uninstall script #{executable}"
|
||||
raise CaskInvalidError.new(@cask, "#{stanza} :#{directive_name} without :executable.") if executable.nil?
|
||||
executable_path = @cask.staged_path.join(executable)
|
||||
@ -225,43 +183,67 @@ module Hbc
|
||||
sleep 1
|
||||
end
|
||||
|
||||
def uninstall_pkgutil(directives)
|
||||
ohai "Removing files from pkgutil Bill-of-Materials"
|
||||
Array(directives[:pkgutil]).each do |regexp|
|
||||
pkgs = Hbc::Pkg.all_matching(regexp, @command)
|
||||
pkgs.each(&:uninstall)
|
||||
def uninstall_pkgutil(*pkgs)
|
||||
ohai "Uninstalling packages:"
|
||||
pkgs.each do |regex|
|
||||
Hbc::Pkg.all_matching(regex, @command).each do |pkg|
|
||||
puts pkg.package_id
|
||||
pkg.uninstall
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def uninstall_delete(directives, expand_tilde = true)
|
||||
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.prepare_path_strings(:delete, path_slice, expand_tilde)
|
||||
@command.run!("/bin/rm", args: path_slice.unshift("-rf", "--"), sudo: true)
|
||||
def each_resolved_path(action, paths)
|
||||
paths.each do |path|
|
||||
resolved_path = Pathname.new(path)
|
||||
|
||||
if path.start_with?("~")
|
||||
resolved_path = resolved_path.expand_path
|
||||
end
|
||||
|
||||
if resolved_path.relative? || resolved_path.split.any? { |part| part.to_s == ".." }
|
||||
opoo "Skipping #{Formatter.identifier(action)} for relative path '#{path}'."
|
||||
next
|
||||
end
|
||||
|
||||
if MacOS.undeletable?(resolved_path)
|
||||
opoo "Skipping #{Formatter.identifier(action)} for undeletable path '#{path}'."
|
||||
next
|
||||
end
|
||||
|
||||
yield path, Pathname.glob(resolved_path)
|
||||
end
|
||||
end
|
||||
|
||||
# :trash functionality is stubbed as a synonym for :delete
|
||||
# TODO: make :trash work differently, moving files to the Trash
|
||||
def uninstall_trash(directives, expand_tilde = true)
|
||||
uninstall_delete(directives, expand_tilde)
|
||||
def uninstall_delete(*paths)
|
||||
return if paths.empty?
|
||||
|
||||
ohai "Removing files:"
|
||||
each_resolved_path(:delete, paths) do |path, resolved_paths|
|
||||
puts path
|
||||
@command.run!("/usr/bin/xargs", args: ["-0", "--", "/bin/rm", "-r", "-f", "--"], input: resolved_paths.join("\0"), sudo: true)
|
||||
end
|
||||
end
|
||||
|
||||
def uninstall_rmdir(directories, expand_tilde = true)
|
||||
action = :rmdir
|
||||
self.class.prepare_path_strings(action, Array(directories[action]).flatten, expand_tilde).each do |directory|
|
||||
next if directory.to_s.empty?
|
||||
ohai "Removing directory if empty: #{directory.to_s.utf8_inspect}"
|
||||
directory = Pathname.new(directory)
|
||||
next unless directory.exist?
|
||||
@command.run!("/bin/rm",
|
||||
args: ["-f", "--", directory.join(".DS_Store")],
|
||||
sudo: true,
|
||||
print_stderr: false)
|
||||
@command.run("/bin/rmdir",
|
||||
args: ["--", directory],
|
||||
sudo: true,
|
||||
print_stderr: false)
|
||||
def uninstall_trash(*paths)
|
||||
# :trash functionality is stubbed as a synonym for :delete
|
||||
# TODO: make :trash work differently, moving files to the Trash
|
||||
uninstall_delete(*paths)
|
||||
end
|
||||
|
||||
def uninstall_rmdir(*directories)
|
||||
return if directories.empty?
|
||||
|
||||
ohai "Removing directories if empty:"
|
||||
each_resolved_path(:rmdir, directories) do |path, resolved_paths|
|
||||
puts path
|
||||
resolved_paths.select(&:directory?).each do |resolved_path|
|
||||
if (ds_store = resolved_path.join(".DS_Store")).exist?
|
||||
@command.run!("/bin/rm", args: ["-f", "--", ds_store], sudo: true, print_stderr: false)
|
||||
end
|
||||
|
||||
@command.run("/bin/rmdir", args: ["--", resolved_path], sudo: true, print_stderr: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -4,7 +4,7 @@ module Hbc
|
||||
module Artifact
|
||||
class Zap < UninstallBase
|
||||
def zap_phase
|
||||
dispatch_uninstall_directives(expand_tilde: true)
|
||||
dispatch_uninstall_directives
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,351 +1,7 @@
|
||||
require_relative "uninstall_zap_shared_examples"
|
||||
|
||||
describe Hbc::Artifact::Uninstall, :cask do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-installable.rb") }
|
||||
|
||||
let(:uninstall_artifact) {
|
||||
Hbc::Artifact::Uninstall.new(cask, command: Hbc::FakeSystemCommand)
|
||||
}
|
||||
|
||||
let(:dir) { TEST_TMPDIR }
|
||||
let(:absolute_path) { Pathname.new("#{dir}/absolute_path") }
|
||||
let(:path_with_tilde) { Pathname.new("#{dir}/path_with_tilde") }
|
||||
let(:glob_path1) { Pathname.new("#{dir}/glob_path1") }
|
||||
let(:glob_path2) { Pathname.new("#{dir}/glob_path2") }
|
||||
|
||||
around(:each) do |example|
|
||||
begin
|
||||
ENV["HOME"] = dir
|
||||
|
||||
paths = [
|
||||
absolute_path,
|
||||
path_with_tilde,
|
||||
glob_path1,
|
||||
glob_path2,
|
||||
]
|
||||
|
||||
FileUtils.touch paths
|
||||
|
||||
shutup do
|
||||
InstallHelper.install_without_artifacts(cask)
|
||||
end
|
||||
|
||||
example.run
|
||||
ensure
|
||||
FileUtils.rm_f paths
|
||||
end
|
||||
end
|
||||
|
||||
describe "uninstall_phase" do
|
||||
subject {
|
||||
shutup do
|
||||
uninstall_artifact.uninstall_phase
|
||||
end
|
||||
}
|
||||
|
||||
context "when using launchctl" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-launchctl.rb") }
|
||||
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) {
|
||||
<<-EOS.undent
|
||||
{
|
||||
"LimitLoadToSessionType" = "Aqua";
|
||||
"Label" = "my.fancy.package.service";
|
||||
"TimeOut" = 30;
|
||||
"OnDemand" = true;
|
||||
"LastExitStatus" = 0;
|
||||
"ProgramArguments" = (
|
||||
"argument";
|
||||
);
|
||||
};
|
||||
EOS
|
||||
}
|
||||
|
||||
context "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
|
||||
|
||||
context "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
|
||||
|
||||
context "when using pkgutil" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-pkgutil.rb") }
|
||||
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) {
|
||||
<<-EOS.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>
|
||||
EOS
|
||||
}
|
||||
|
||||
it "can uninstall" do
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
%w[/usr/sbin/pkgutil --pkgs=my.fancy.package.*],
|
||||
"#{main_pkg_id}\n#{agent_pkg_id}",
|
||||
)
|
||||
|
||||
[
|
||||
[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"),
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
context "when using kext" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-kext.rb") }
|
||||
let(:kext_id) { "my.fancy.package.kernelextension" }
|
||||
|
||||
it "can uninstall" do
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
sudo(%W[/usr/sbin/kextstat -l -b #{kext_id}]), "loaded"
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%W[/sbin/kextunload -b #{kext_id}]),
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%W[/usr/sbin/kextfind -b #{kext_id}]), "/Library/Extensions/FancyPackage.kext\n"
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(["/bin/rm", "-rf", "/Library/Extensions/FancyPackage.kext"]),
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using quit" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-quit.rb") }
|
||||
let(:bundle_id) { "my.fancy.package.app" }
|
||||
let(:quit_application_script) {
|
||||
%Q(tell application id "#{bundle_id}" to quit)
|
||||
}
|
||||
|
||||
it "can uninstall" do
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
%w[/bin/launchctl list], "999\t0\t#{bundle_id}\n"
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
%w[/bin/launchctl list],
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using signal" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-signal.rb") }
|
||||
let(:bundle_id) { "my.fancy.package.app" }
|
||||
let(:signals) { %w[TERM KILL] }
|
||||
let(:unix_pids) { [12_345, 67_890] }
|
||||
|
||||
it "can uninstall" do
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
%w[/bin/launchctl list], unix_pids.map { |pid| [pid, 0, bundle_id].join("\t") }.join("\n")
|
||||
)
|
||||
|
||||
signals.each do |signal|
|
||||
expect(Process).to receive(:kill).with(signal, *unix_pids)
|
||||
end
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using delete" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-delete.rb") }
|
||||
|
||||
it "can uninstall" do
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%w[/bin/rm -rf --],
|
||||
absolute_path,
|
||||
path_with_tilde,
|
||||
glob_path1,
|
||||
glob_path2),
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using trash" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-trash.rb") }
|
||||
|
||||
it "can uninstall" do
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%w[/bin/rm -rf --],
|
||||
absolute_path,
|
||||
path_with_tilde,
|
||||
glob_path1,
|
||||
glob_path2),
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using rmdir" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-rmdir.rb") }
|
||||
let(:empty_directory_path) { Pathname.new("#{TEST_TMPDIR}/empty_directory_path") }
|
||||
|
||||
before(:each) do
|
||||
empty_directory_path.mkdir
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
empty_directory_path.rmdir
|
||||
end
|
||||
|
||||
it "can uninstall" do
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%w[/bin/rm -f --], empty_directory_path/".DS_Store"),
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%w[/bin/rmdir --], empty_directory_path),
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using script" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-script.rb") }
|
||||
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
|
||||
|
||||
context "when using early_script" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-early-script.rb") }
|
||||
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
|
||||
|
||||
context "when using login_item" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-login-item.rb") }
|
||||
|
||||
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
|
||||
describe "#uninstall_phase" do
|
||||
include_examples "#uninstall_phase or #zap_phase"
|
||||
end
|
||||
end
|
||||
|
||||
@ -0,0 +1,334 @@
|
||||
shared_examples "#uninstall_phase or #zap_phase" do
|
||||
let(:artifact_name) { described_class.artifact_name }
|
||||
let(:artifact) { described_class.new(cask, command: fake_system_command) }
|
||||
let(:fake_system_command) { Hbc::FakeSystemCommand }
|
||||
|
||||
subject do
|
||||
shutup do
|
||||
artifact.public_send(:"#{artifact_name}_phase")
|
||||
end
|
||||
end
|
||||
|
||||
context "using :launchctl" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-launchctl.rb") }
|
||||
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) do
|
||||
<<-EOS.undent
|
||||
{
|
||||
"LimitLoadToSessionType" = "Aqua";
|
||||
"Label" = "my.fancy.package.service";
|
||||
"TimeOut" = 30;
|
||||
"OnDemand" = true;
|
||||
"LastExitStatus" = 0;
|
||||
"ProgramArguments" = (
|
||||
"argument";
|
||||
);
|
||||
};
|
||||
EOS
|
||||
end
|
||||
|
||||
it "works when job is owned by user" 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
|
||||
|
||||
it "works when job is owned by system" 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
|
||||
|
||||
context "using :pkgutil" do
|
||||
let(:fake_system_command) { class_double(Hbc::SystemCommand) }
|
||||
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-pkgutil.rb") }
|
||||
let(:main_pkg_id) { "my.fancy.package.main" }
|
||||
let(:agent_pkg_id) { "my.fancy.package.agent" }
|
||||
let(:main_files) do
|
||||
%w[
|
||||
fancy/bin/fancy.exe
|
||||
fancy/var/fancy.data
|
||||
]
|
||||
end
|
||||
let(:main_dirs) do
|
||||
%w[
|
||||
fancy
|
||||
fancy/bin
|
||||
fancy/var
|
||||
]
|
||||
end
|
||||
let(:agent_files) do
|
||||
%w[
|
||||
fancy/agent/fancy-agent.exe
|
||||
fancy/agent/fancy-agent.pid
|
||||
fancy/agent/fancy-agent.log
|
||||
]
|
||||
end
|
||||
let(:agent_dirs) do
|
||||
%w[
|
||||
fancy
|
||||
fancy/agent
|
||||
]
|
||||
end
|
||||
let(:pkg_info_plist) do
|
||||
<<-EOS.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>
|
||||
EOS
|
||||
end
|
||||
|
||||
it "is supported" do
|
||||
allow(fake_system_command).to receive(:run).with(
|
||||
"/usr/sbin/pkgutil",
|
||||
args: ["--pkgs=my.fancy.package.*"],
|
||||
).and_return(double(stdout: "#{main_pkg_id}\n#{agent_pkg_id}"))
|
||||
|
||||
[
|
||||
[main_pkg_id, main_files, main_dirs],
|
||||
[agent_pkg_id, agent_files, agent_dirs],
|
||||
].each do |pkg_id, pkg_files, pkg_dirs|
|
||||
|
||||
allow(fake_system_command).to receive(:run!).with(
|
||||
"/usr/sbin/pkgutil",
|
||||
args: ["--only-files", "--files", pkg_id.to_s],
|
||||
).and_return(double(stdout: pkg_files.join("\n")))
|
||||
|
||||
allow(fake_system_command).to receive(:run!).with(
|
||||
"/usr/sbin/pkgutil",
|
||||
args: ["--only-dirs", "--files", pkg_id.to_s],
|
||||
).and_return(double(stdout: pkg_dirs.join("\n")))
|
||||
|
||||
allow(fake_system_command).to receive(:run!).with(
|
||||
"/usr/sbin/pkgutil",
|
||||
args: ["--files", pkg_id.to_s],
|
||||
).and_return(double(stdout: (pkg_files + pkg_dirs).join("\n")))
|
||||
|
||||
result = Hbc::SystemCommand::Result.new(nil, pkg_info_plist, nil, 0)
|
||||
allow(fake_system_command).to receive(:run!).with(
|
||||
"/usr/sbin/pkgutil",
|
||||
args: ["--pkg-info-plist", pkg_id.to_s],
|
||||
).and_return(result)
|
||||
|
||||
expect(fake_system_command).to receive(:run).with(
|
||||
"/usr/bin/xargs",
|
||||
args: ["-0", "--", "/bin/rm", "-f", "--"],
|
||||
input: pkg_files.map { |path| "/tmp/#{path}" }.join("\0"),
|
||||
sudo: true,
|
||||
)
|
||||
|
||||
expect(fake_system_command).to receive(:run!).with(
|
||||
"/usr/sbin/pkgutil",
|
||||
args: ["--forget", pkg_id.to_s],
|
||||
sudo: true,
|
||||
)
|
||||
end
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "using :kext" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-kext.rb") }
|
||||
let(:kext_id) { "my.fancy.package.kernelextension" }
|
||||
|
||||
it "is supported" do
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
sudo(%W[/usr/sbin/kextstat -l -b #{kext_id}]), "loaded"
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%W[/sbin/kextunload -b #{kext_id}]),
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%W[/usr/sbin/kextfind -b #{kext_id}]), "/Library/Extensions/FancyPackage.kext\n"
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(["/bin/rm", "-rf", "/Library/Extensions/FancyPackage.kext"]),
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "using :quit" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-quit.rb") }
|
||||
let(:bundle_id) { "my.fancy.package.app" }
|
||||
let(:quit_application_script) do
|
||||
%Q(tell application id "#{bundle_id}" to quit)
|
||||
end
|
||||
|
||||
it "is supported" do
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
%w[/bin/launchctl list], "999\t0\t#{bundle_id}\n"
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
%w[/bin/launchctl list],
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "using :signal" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-signal.rb") }
|
||||
let(:bundle_id) { "my.fancy.package.app" }
|
||||
let(:signals) { %w[TERM KILL] }
|
||||
let(:unix_pids) { [12_345, 67_890] }
|
||||
|
||||
it "is supported" do
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
%w[/bin/launchctl list], unix_pids.map { |pid| [pid, 0, bundle_id].join("\t") }.join("\n")
|
||||
)
|
||||
|
||||
signals.each do |signal|
|
||||
expect(Process).to receive(:kill).with(signal, *unix_pids)
|
||||
end
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
[:delete, :trash].each do |directive|
|
||||
context "using :#{directive}" do
|
||||
let(:dir) { TEST_TMPDIR }
|
||||
let(:absolute_path) { Pathname.new("#{dir}/absolute_path") }
|
||||
let(:path_with_tilde) { Pathname.new("#{dir}/path_with_tilde") }
|
||||
let(:glob_path1) { Pathname.new("#{dir}/glob_path1") }
|
||||
let(:glob_path2) { Pathname.new("#{dir}/glob_path2") }
|
||||
let(:paths) { [absolute_path, path_with_tilde, glob_path1, glob_path2] }
|
||||
|
||||
around(:each) do |example|
|
||||
begin
|
||||
ENV["HOME"] = dir
|
||||
|
||||
FileUtils.touch paths
|
||||
|
||||
example.run
|
||||
ensure
|
||||
FileUtils.rm_f paths
|
||||
end
|
||||
end
|
||||
|
||||
let(:fake_system_command) { Hbc::NeverSudoSystemCommand }
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-#{directive}.rb") }
|
||||
|
||||
it "is supported" do
|
||||
paths.each do |path|
|
||||
expect(path).to exist
|
||||
end
|
||||
|
||||
subject
|
||||
|
||||
paths.each do |path|
|
||||
expect(path).not_to exist
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "using :rmdir" do
|
||||
let(:fake_system_command) { Hbc::NeverSudoSystemCommand }
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-rmdir.rb") }
|
||||
let(:empty_directory) { Pathname.new("#{TEST_TMPDIR}/empty_directory_path") }
|
||||
let(:ds_store) { empty_directory.join(".DS_Store") }
|
||||
|
||||
before(:each) do
|
||||
empty_directory.mkdir
|
||||
FileUtils.touch ds_store
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
FileUtils.rm_rf empty_directory
|
||||
end
|
||||
|
||||
it "is supported" do
|
||||
expect(empty_directory).to exist
|
||||
expect(ds_store).to exist
|
||||
|
||||
subject
|
||||
|
||||
expect(ds_store).not_to exist
|
||||
expect(empty_directory).not_to exist
|
||||
end
|
||||
end
|
||||
|
||||
context "using :script" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-script.rb") }
|
||||
let(:script_pathname) { cask.staged_path.join("MyFancyPkg", "FancyUninstaller.tool") }
|
||||
|
||||
it "is supported" do
|
||||
Hbc::FakeSystemCommand.expects_command(%w[/bin/chmod -- +x] + [script_pathname])
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(cask.staged_path.join("MyFancyPkg", "FancyUninstaller.tool"), "--please"),
|
||||
)
|
||||
|
||||
InstallHelper.install_without_artifacts(cask)
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "using :early_script" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-early-script.rb") }
|
||||
let(:script_pathname) { cask.staged_path.join("MyFancyPkg", "FancyUninstaller.tool") }
|
||||
|
||||
it "is supported" do
|
||||
Hbc::FakeSystemCommand.expects_command(%w[/bin/chmod -- +x] + [script_pathname])
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(cask.staged_path.join("MyFancyPkg", "FancyUninstaller.tool"), "--please"),
|
||||
)
|
||||
|
||||
InstallHelper.install_without_artifacts(cask)
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "using :login_item" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-login-item.rb") }
|
||||
|
||||
it "is supported" 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
|
||||
@ -1,352 +1,7 @@
|
||||
# TODO: test that zap removes an alternate version of the same Cask
|
||||
require_relative "uninstall_zap_shared_examples"
|
||||
|
||||
describe Hbc::Artifact::Zap, :cask do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-installable.rb") }
|
||||
|
||||
let(:zap_artifact) {
|
||||
Hbc::Artifact::Zap.new(cask, command: Hbc::FakeSystemCommand)
|
||||
}
|
||||
|
||||
let(:dir) { TEST_TMPDIR }
|
||||
let(:absolute_path) { Pathname.new("#{dir}/absolute_path") }
|
||||
let(:path_with_tilde) { Pathname.new("#{dir}/path_with_tilde") }
|
||||
let(:glob_path1) { Pathname.new("#{dir}/glob_path1") }
|
||||
let(:glob_path2) { Pathname.new("#{dir}/glob_path2") }
|
||||
|
||||
around(:each) do |example|
|
||||
begin
|
||||
ENV["HOME"] = dir
|
||||
|
||||
paths = [
|
||||
absolute_path,
|
||||
path_with_tilde,
|
||||
glob_path1,
|
||||
glob_path2,
|
||||
]
|
||||
|
||||
FileUtils.touch paths
|
||||
|
||||
shutup do
|
||||
InstallHelper.install_without_artifacts(cask)
|
||||
end
|
||||
|
||||
example.run
|
||||
ensure
|
||||
FileUtils.rm_f paths
|
||||
end
|
||||
end
|
||||
|
||||
describe "#zap_phase" do
|
||||
subject {
|
||||
shutup do
|
||||
zap_artifact.zap_phase
|
||||
end
|
||||
}
|
||||
|
||||
context "when using launchctl" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-zap-launchctl.rb") }
|
||||
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) {
|
||||
<<-EOS.undent
|
||||
{
|
||||
"LimitLoadToSessionType" = "Aqua";
|
||||
"Label" = "my.fancy.package.service";
|
||||
"TimeOut" = 30;
|
||||
"OnDemand" = true;
|
||||
"LastExitStatus" = 0;
|
||||
"ProgramArguments" = (
|
||||
"argument";
|
||||
);
|
||||
};
|
||||
EOS
|
||||
}
|
||||
|
||||
context "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
|
||||
|
||||
context "when using pkgutil" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-zap-pkgutil.rb") }
|
||||
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) {
|
||||
<<-EOS.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>
|
||||
EOS
|
||||
}
|
||||
|
||||
it "can zap" do
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
%w[/usr/sbin/pkgutil --pkgs=my.fancy.package.*],
|
||||
"#{main_pkg_id}\n#{agent_pkg_id}",
|
||||
)
|
||||
|
||||
[
|
||||
[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"),
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
context "when using kext" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-zap-kext.rb") }
|
||||
let(:kext_id) { "my.fancy.package.kernelextension" }
|
||||
|
||||
it "can zap" do
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
sudo(%W[/usr/sbin/kextstat -l -b #{kext_id}]), "loaded"
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%W[/sbin/kextunload -b #{kext_id}]),
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%W[/usr/sbin/kextfind -b #{kext_id}]), "/Library/Extensions/FancyPackage.kext\n"
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(["/bin/rm", "-rf", "/Library/Extensions/FancyPackage.kext"]),
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using quit" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-zap-quit.rb") }
|
||||
let(:bundle_id) { "my.fancy.package.app" }
|
||||
let(:quit_application_script) {
|
||||
%Q(tell application id "#{bundle_id}" to quit)
|
||||
}
|
||||
|
||||
it "can zap" do
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
%w[/bin/launchctl list], "999\t0\t#{bundle_id}\n"
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
%w[/bin/launchctl list],
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using signal" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-zap-signal.rb") }
|
||||
let(:bundle_id) { "my.fancy.package.app" }
|
||||
let(:signals) { %w[TERM KILL] }
|
||||
let(:unix_pids) { [12_345, 67_890] }
|
||||
|
||||
it "can zap" do
|
||||
Hbc::FakeSystemCommand.stubs_command(
|
||||
%w[/bin/launchctl list], unix_pids.map { |pid| [pid, 0, bundle_id].join("\t") }.join("\n")
|
||||
)
|
||||
|
||||
signals.each do |signal|
|
||||
expect(Process).to receive(:kill).with(signal, *unix_pids)
|
||||
end
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using delete" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-zap-delete.rb") }
|
||||
|
||||
it "can zap" do
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%w[/bin/rm -rf --],
|
||||
absolute_path,
|
||||
path_with_tilde,
|
||||
glob_path1,
|
||||
glob_path2),
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using trash" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-zap-trash.rb") }
|
||||
|
||||
it "can zap" do
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%w[/bin/rm -rf --],
|
||||
absolute_path,
|
||||
path_with_tilde,
|
||||
glob_path1,
|
||||
glob_path2),
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using rmdir" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-zap-rmdir.rb") }
|
||||
let(:empty_directory_path) { Pathname.new("#{TEST_TMPDIR}/empty_directory_path") }
|
||||
|
||||
before(:each) do
|
||||
empty_directory_path.mkdir
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
empty_directory_path.rmdir
|
||||
end
|
||||
|
||||
it "can zap" do
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%w[/bin/rm -f --], empty_directory_path/".DS_Store"),
|
||||
)
|
||||
|
||||
Hbc::FakeSystemCommand.expects_command(
|
||||
sudo(%w[/bin/rmdir --], empty_directory_path),
|
||||
)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "when using script" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-zap-script.rb") }
|
||||
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
|
||||
|
||||
context "when using early_script" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-zap-early-script.rb") }
|
||||
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
|
||||
|
||||
context "when using login_item" do
|
||||
let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-zap-login-item.rb") }
|
||||
|
||||
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
|
||||
include_examples "#uninstall_phase or #zap_phase"
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user