2020-10-10 14:16:11 +02:00
|
|
|
# typed: false
|
2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
require "benchmark"
|
|
|
|
|
2017-03-08 03:03:36 +01:00
|
|
|
shared_examples "#uninstall_phase or #zap_phase" do
|
2018-11-05 22:40:07 +01:00
|
|
|
subject { artifact }
|
|
|
|
|
2017-04-06 00:33:31 +02:00
|
|
|
let(:artifact_dsl_key) { described_class.dsl_key }
|
2017-10-04 17:54:52 +02:00
|
|
|
let(:artifact) { cask.artifacts.find { |a| a.is_a?(described_class) } }
|
2020-11-23 02:05:50 +01:00
|
|
|
let(:fake_system_command) { class_double(SystemCommand) }
|
2017-03-08 03:03:36 +01:00
|
|
|
|
|
|
|
context "using :launchctl" do
|
2018-09-06 08:29:14 +02:00
|
|
|
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-launchctl")) }
|
2017-03-08 03:03:36 +01:00
|
|
|
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
|
2017-10-15 02:28:32 +02:00
|
|
|
<<~EOS
|
2017-03-08 03:03:36 +01:00
|
|
|
{
|
|
|
|
"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
|
2020-11-23 02:05:50 +01:00
|
|
|
allow(fake_system_command).to receive(:run)
|
|
|
|
.with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: false)
|
|
|
|
.and_return(instance_double(SystemCommand::Result, stdout: service_info))
|
|
|
|
allow(fake_system_command).to receive(:run)
|
|
|
|
.with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: true)
|
|
|
|
.and_return(instance_double(SystemCommand::Result, stdout: unknown_response))
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2020-11-23 02:05:50 +01:00
|
|
|
expect(fake_system_command).to receive(:run!)
|
|
|
|
.with("/bin/launchctl", args: ["remove", "my.fancy.package.service"], sudo: false)
|
|
|
|
.and_return(instance_double(SystemCommand::Result))
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
2017-03-08 03:03:36 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
it "works when job is owned by system" do
|
2020-11-23 02:05:50 +01:00
|
|
|
allow(fake_system_command).to receive(:run)
|
|
|
|
.with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: false)
|
|
|
|
.and_return(instance_double(SystemCommand::Result, stdout: unknown_response))
|
|
|
|
allow(fake_system_command).to receive(:run)
|
|
|
|
.with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: true)
|
|
|
|
.and_return(instance_double(SystemCommand::Result, stdout: service_info))
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2020-11-23 02:05:50 +01:00
|
|
|
expect(fake_system_command).to receive(:run!)
|
|
|
|
.with("/bin/launchctl", args: ["remove", "my.fancy.package.service"], sudo: true)
|
|
|
|
.and_return(instance_double(SystemCommand::Result))
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
2017-03-08 03:03:36 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-12-28 12:50:16 +11:00
|
|
|
context "using :launchctl with regex wildcard" do
|
2022-12-13 00:00:43 +11:00
|
|
|
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-launchctl-wildcard")) }
|
2022-12-28 12:50:16 +11:00
|
|
|
let(:launchctl_regex) { "my.fancy.package.service.*" }
|
|
|
|
let(:unknown_response) { "launchctl list returned unknown response\n" }
|
|
|
|
let(:service_info) do
|
|
|
|
<<~EOS
|
|
|
|
{
|
|
|
|
"LimitLoadToSessionType" = "Aqua";
|
|
|
|
"Label" = "my.fancy.package.service.12345";
|
|
|
|
"TimeOut" = 30;
|
|
|
|
"OnDemand" = true;
|
|
|
|
"LastExitStatus" = 0;
|
|
|
|
"ProgramArguments" = (
|
|
|
|
"argument";
|
|
|
|
);
|
|
|
|
};
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
|
|
|
it "searches installed launchctl items" do
|
|
|
|
expect(subject).to receive(:find_launchctl_with_wildcard)
|
|
|
|
.with(launchctl_regex)
|
|
|
|
.and_return(["my.fancy.package.service.12345"])
|
|
|
|
|
|
|
|
allow(fake_system_command).to receive(:run)
|
|
|
|
.with("/bin/launchctl", args: ["list", "my.fancy.package.service.12345"], print_stderr: false, sudo: false)
|
|
|
|
.and_return(instance_double(SystemCommand::Result, stdout: unknown_response))
|
|
|
|
allow(fake_system_command).to receive(:run)
|
|
|
|
.with("/bin/launchctl", args: ["list", "my.fancy.package.service.12345"], print_stderr: false, sudo: true)
|
|
|
|
.and_return(instance_double(SystemCommand::Result, stdout: service_info))
|
2022-12-13 00:00:43 +11:00
|
|
|
|
2022-12-28 12:50:16 +11:00
|
|
|
expect(fake_system_command).to receive(:run!)
|
|
|
|
.with("/bin/launchctl", args: ["remove", "my.fancy.package.service.12345"], sudo: true)
|
2022-12-13 00:00:43 +11:00
|
|
|
.and_return(instance_double(SystemCommand::Result))
|
|
|
|
|
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-03-08 03:03:36 +01:00
|
|
|
context "using :pkgutil" do
|
2018-09-06 08:29:14 +02:00
|
|
|
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-pkgutil")) }
|
2017-03-09 22:01:46 +01:00
|
|
|
|
2017-03-08 03:03:36 +01:00
|
|
|
let(:main_pkg_id) { "my.fancy.package.main" }
|
|
|
|
let(:agent_pkg_id) { "my.fancy.package.agent" }
|
|
|
|
|
|
|
|
it "is supported" do
|
2018-09-06 08:29:14 +02:00
|
|
|
main_pkg = Cask::Pkg.new(main_pkg_id, fake_system_command)
|
|
|
|
agent_pkg = Cask::Pkg.new(agent_pkg_id, fake_system_command)
|
2017-03-09 22:01:46 +01:00
|
|
|
|
2018-09-06 08:29:14 +02:00
|
|
|
expect(Cask::Pkg).to receive(:all_matching).and_return(
|
2017-03-09 22:01:46 +01:00
|
|
|
[
|
|
|
|
main_pkg,
|
|
|
|
agent_pkg,
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(main_pkg).to receive(:uninstall)
|
|
|
|
expect(agent_pkg).to receive(:uninstall)
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
2017-03-08 03:03:36 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "using :kext" do
|
2018-09-06 08:29:14 +02:00
|
|
|
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-kext")) }
|
2017-03-08 03:03:36 +01:00
|
|
|
let(:kext_id) { "my.fancy.package.kernelextension" }
|
|
|
|
|
|
|
|
it "is supported" do
|
2018-11-05 22:40:07 +01:00
|
|
|
allow(subject).to receive(:system_command!)
|
|
|
|
.with("/usr/sbin/kextstat", args: ["-l", "-b", kext_id], sudo: true)
|
|
|
|
.and_return(instance_double("SystemCommand::Result", stdout: "loaded"))
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
expect(subject).to receive(:system_command!)
|
|
|
|
.with("/sbin/kextunload", args: ["-b", kext_id], sudo: true)
|
|
|
|
.and_return(instance_double("SystemCommand::Result"))
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
expect(subject).to receive(:system_command!)
|
|
|
|
.with("/usr/sbin/kextfind", args: ["-b", kext_id], sudo: true)
|
|
|
|
.and_return(instance_double("SystemCommand::Result", stdout: "/Library/Extensions/FancyPackage.kext\n"))
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
expect(subject).to receive(:system_command!)
|
|
|
|
.with("/bin/rm", args: ["-rf", "/Library/Extensions/FancyPackage.kext"], sudo: true)
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
2017-03-08 03:03:36 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "using :quit" do
|
2018-09-06 08:29:14 +02:00
|
|
|
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-quit")) }
|
2017-03-08 03:03:36 +01:00
|
|
|
let(:bundle_id) { "my.fancy.package.app" }
|
2018-11-05 22:40:07 +01:00
|
|
|
|
|
|
|
it "is skipped when the user is not a GUI user" do
|
|
|
|
allow(User.current).to receive(:gui?).and_return false
|
2019-09-15 01:08:04 +02:00
|
|
|
allow(subject).to receive(:running?).with(bundle_id).and_return(true)
|
2018-11-05 22:40:07 +01:00
|
|
|
|
2022-12-13 00:00:43 +11:00
|
|
|
expect do
|
2018-11-05 22:40:07 +01:00
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
2022-12-13 00:00:43 +11:00
|
|
|
end.to output(/Not logged into a GUI; skipping quitting application ID 'my.fancy.package.app'\./).to_stderr
|
2017-03-08 03:03:36 +01:00
|
|
|
end
|
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
it "quits a running application" do
|
|
|
|
allow(User.current).to receive(:gui?).and_return true
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2019-09-15 01:08:04 +02:00
|
|
|
expect(subject).to receive(:running?).with(bundle_id).ordered.and_return(true)
|
2018-11-05 22:40:07 +01:00
|
|
|
expect(subject).to receive(:quit).with(bundle_id)
|
|
|
|
.and_return(instance_double("SystemCommand::Result", success?: true))
|
2019-09-15 01:08:04 +02:00
|
|
|
expect(subject).to receive(:running?).with(bundle_id).ordered.and_return(false)
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2022-12-13 00:00:43 +11:00
|
|
|
expect do
|
2018-11-05 22:40:07 +01:00
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
2022-12-13 00:00:43 +11:00
|
|
|
end.to output(/Application 'my.fancy.package.app' quit successfully\./).to_stdout
|
2018-11-05 22:40:07 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
it "tries to quit the application for 10 seconds" do
|
|
|
|
allow(User.current).to receive(:gui?).and_return true
|
|
|
|
|
2019-09-15 01:08:04 +02:00
|
|
|
allow(subject).to receive(:running?).with(bundle_id).and_return(true)
|
2018-11-05 22:40:07 +01:00
|
|
|
allow(subject).to receive(:quit).with(bundle_id)
|
|
|
|
.and_return(instance_double("SystemCommand::Result", success?: false))
|
|
|
|
|
|
|
|
time = Benchmark.measure do
|
2022-12-13 00:00:43 +11:00
|
|
|
expect do
|
2018-11-05 22:40:07 +01:00
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
2022-12-13 00:00:43 +11:00
|
|
|
end.to output(/Application 'my.fancy.package.app' did not quit\./).to_stderr
|
2018-11-05 22:40:07 +01:00
|
|
|
end
|
|
|
|
|
2018-11-07 21:13:12 +01:00
|
|
|
expect(time.real).to be_within(3).of(10)
|
2017-03-08 03:03:36 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "using :signal" do
|
2018-09-06 08:29:14 +02:00
|
|
|
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-signal")) }
|
2017-03-08 03:03:36 +01:00
|
|
|
let(:bundle_id) { "my.fancy.package.app" }
|
|
|
|
let(:signals) { %w[TERM KILL] }
|
|
|
|
let(:unix_pids) { [12_345, 67_890] }
|
|
|
|
|
|
|
|
it "is supported" do
|
2018-11-05 22:40:07 +01:00
|
|
|
allow(subject).to receive(:running_processes).with(bundle_id)
|
|
|
|
.and_return(unix_pids.map { |pid| [pid, 0, bundle_id] })
|
2017-03-08 03:03:36 +01:00
|
|
|
|
|
|
|
signals.each do |signal|
|
|
|
|
expect(Process).to receive(:kill).with(signal, *unix_pids)
|
|
|
|
end
|
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
2017-03-08 03:03:36 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
[:delete, :trash].each do |directive|
|
2017-10-01 22:26:04 +02:00
|
|
|
next if directive == :trash && ENV["HOMEBREW_TESTS_COVERAGE"].nil?
|
|
|
|
|
2017-03-08 03:03:36 +01:00
|
|
|
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] }
|
2018-10-12 23:47:55 +02:00
|
|
|
let(:fake_system_command) { NeverSudoSystemCommand }
|
|
|
|
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-#{directive}")) }
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
around do |example|
|
2019-10-13 10:03:26 +01:00
|
|
|
ENV["HOME"] = dir
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2019-10-13 10:03:26 +01:00
|
|
|
FileUtils.touch paths
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2019-10-13 10:03:26 +01:00
|
|
|
example.run
|
|
|
|
ensure
|
|
|
|
FileUtils.rm_f paths
|
2017-03-08 03:03:36 +01:00
|
|
|
end
|
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
before do
|
2018-09-06 08:29:14 +02:00
|
|
|
allow_any_instance_of(Cask::Artifact::AbstractUninstall).to receive(:trash_paths)
|
2017-06-24 08:34:01 +02:00
|
|
|
.and_wrap_original do |method, *args|
|
2019-10-23 05:21:21 +02:00
|
|
|
method.call(*args).tap do |trashed, _|
|
|
|
|
FileUtils.rm_r trashed
|
2018-11-05 22:40:07 +01:00
|
|
|
end
|
2017-06-24 08:34:01 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-03-08 03:03:36 +01:00
|
|
|
it "is supported" do
|
2018-10-13 07:23:57 -07:00
|
|
|
expect(paths).to all(exist)
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
2017-03-08 03:03:36 +01:00
|
|
|
|
|
|
|
paths.each do |path|
|
|
|
|
expect(path).not_to exist
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-03-12 22:09:13 +01:00
|
|
|
[:script, :early_script].each do |script_type|
|
|
|
|
context "using #{script_type.inspect}" do
|
2018-07-19 23:56:51 +02:00
|
|
|
let(:fake_system_command) { NeverSudoSystemCommand }
|
2017-04-06 00:33:31 +02:00
|
|
|
let(:token) { "with-#{artifact_dsl_key}-#{script_type}".tr("_", "-") }
|
2018-09-06 08:29:14 +02:00
|
|
|
let(:cask) { Cask::CaskLoader.load(cask_path(token.to_s)) }
|
2017-03-12 22:09:13 +01:00
|
|
|
let(:script_pathname) { cask.staged_path.join("MyFancyPkg", "FancyUninstaller.tool") }
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2017-03-12 22:09:13 +01:00
|
|
|
it "is supported" do
|
|
|
|
allow(fake_system_command).to receive(:run).with(any_args).and_call_original
|
|
|
|
|
|
|
|
expect(fake_system_command).to receive(:run).with(
|
|
|
|
cask.staged_path.join("MyFancyPkg", "FancyUninstaller.tool"),
|
2018-11-02 17:18:07 +00:00
|
|
|
args: ["--please"],
|
2017-03-12 22:09:13 +01:00
|
|
|
must_succeed: true,
|
|
|
|
print_stdout: true,
|
2018-11-02 17:18:07 +00:00
|
|
|
sudo: false,
|
2017-03-12 22:09:13 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
InstallHelper.install_without_artifacts(cask)
|
2018-11-05 22:40:07 +01:00
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
2017-03-12 22:09:13 +01:00
|
|
|
end
|
2017-03-08 03:03:36 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "using :login_item" do
|
2018-09-06 08:29:14 +02:00
|
|
|
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-login-item")) }
|
2017-03-08 03:03:36 +01:00
|
|
|
|
|
|
|
it "is supported" do
|
2019-10-22 05:34:39 +02:00
|
|
|
expect(subject).to receive(:system_command)
|
2018-11-05 22:40:07 +01:00
|
|
|
.with(
|
|
|
|
"osascript",
|
2019-04-30 08:44:35 +01:00
|
|
|
args: ["-e", 'tell application "System Events" to delete every login item whose name is "Fancy"'],
|
2019-02-11 09:50:09 +00:00
|
|
|
)
|
2019-10-22 05:34:39 +02:00
|
|
|
.and_return(instance_double("SystemCommand::Result", success?: true))
|
2017-03-08 03:03:36 +01:00
|
|
|
|
2018-11-05 22:40:07 +01:00
|
|
|
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
|
2017-03-08 03:03:36 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|