diff options
| author | Markus Reiter | 2017-03-05 06:31:36 +0100 |
|---|---|---|
| committer | Markus Reiter | 2017-03-05 23:08:14 +0100 |
| commit | 9fc6c7b2be300ff35dc52d80f4dc38d36d52ddc2 (patch) | |
| tree | 43e99a683329471c1dc965dcc92daccb57df7e8d /Library/Homebrew/test/cask/artifact | |
| parent | 67ec76d1492fbb03959a782a85c4fb985d6a5884 (diff) | |
| download | brew-9fc6c7b2be300ff35dc52d80f4dc38d36d52ddc2.tar.bz2 | |
Move Cask specs into `brew tests`.
Diffstat (limited to 'Library/Homebrew/test/cask/artifact')
14 files changed, 1486 insertions, 0 deletions
diff --git a/Library/Homebrew/test/cask/artifact/alt_target_spec.rb b/Library/Homebrew/test/cask/artifact/alt_target_spec.rb new file mode 100644 index 000000000..9bcdd27a1 --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/alt_target_spec.rb @@ -0,0 +1,77 @@ +describe Hbc::Artifact::App, :cask do + describe "activate to alternate target" do + let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-alt-target.rb") } + + let(:install_phase) { + -> { Hbc::Artifact::App.new(cask).install_phase } + } + + let(:source_path) { cask.staged_path.join("Caffeine.app") } + let(:target_path) { Hbc.appdir.join("AnotherName.app") } + + before do + InstallHelper.install_without_artifacts(cask) + end + + it "installs the given apps using the proper target directory" do + expect(source_path).to be_a_directory + expect(target_path).not_to exist + + shutup do + install_phase.call + end + + expect(target_path).to be_a_directory + expect(source_path).not_to exist + end + + describe "when app is in a subdirectory" do + let(:cask) { + Hbc::Cask.new("subdir") do + url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip" + homepage "http://example.com/local-caffeine" + version "1.2.3" + sha256 "67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94" + app "subdir/Caffeine.app", target: "AnotherName.app" + end + } + + it "installs the given apps using the proper target directory" do + appsubdir = cask.staged_path.join("subdir").tap(&:mkpath) + FileUtils.mv(source_path, appsubdir) + + shutup do + install_phase.call + end + + expect(target_path).to be_a_directory + expect(appsubdir.join("Caffeine.app")).not_to exist + end + end + + it "only uses apps when they are specified" do + staged_app_copy = source_path.sub("Caffeine.app", "Caffeine Deluxe.app") + FileUtils.cp_r source_path, staged_app_copy + + shutup do + install_phase.call + end + + expect(target_path).to be_a_directory + expect(source_path).not_to exist + + expect(Hbc.appdir.join("Caffeine Deluxe.app")).not_to exist + expect(cask.staged_path.join("Caffeine Deluxe.app")).to be_a_directory + end + + it "avoids clobbering an existing app by moving over it" do + target_path.mkpath + + expect(install_phase).to raise_error(Hbc::CaskError, "It seems there is already an App at '#{target_path}'.") + + expect(source_path).to be_a_directory + expect(target_path).to be_a_directory + expect(File.identical?(source_path, target_path)).to be false + end + end +end diff --git a/Library/Homebrew/test/cask/artifact/app_spec.rb b/Library/Homebrew/test/cask/artifact/app_spec.rb new file mode 100644 index 000000000..bfd2d5cd4 --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/app_spec.rb @@ -0,0 +1,243 @@ +describe Hbc::Artifact::App, :cask do + let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") } + let(:command) { Hbc::SystemCommand } + let(:force) { false } + let(:app) { Hbc::Artifact::App.new(cask, command: command, force: force) } + + let(:source_path) { cask.staged_path.join("Caffeine.app") } + let(:target_path) { Hbc.appdir.join("Caffeine.app") } + + let(:install_phase) { -> { app.install_phase } } + let(:uninstall_phase) { -> { app.uninstall_phase } } + + before(:each) do + InstallHelper.install_without_artifacts(cask) + end + + describe "install_phase" do + it "installs the given app using the proper target directory" do + shutup do + install_phase.call + end + + expect(target_path).to be_a_directory + expect(source_path).not_to exist + end + + describe "when app is in a subdirectory" do + let(:cask) { + Hbc::Cask.new("subdir") do + url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip" + homepage "http://example.com/local-caffeine" + version "1.2.3" + sha256 "67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94" + app "subdir/Caffeine.app" + end + } + + it "installs the given app using the proper target directory" do + appsubdir = cask.staged_path.join("subdir").tap(&:mkpath) + FileUtils.mv(source_path, appsubdir) + + shutup do + install_phase.call + end + + expect(target_path).to be_a_directory + expect(appsubdir.join("Caffeine.app")).not_to exist + end + end + + it "only uses apps when they are specified" do + staged_app_copy = source_path.sub("Caffeine.app", "Caffeine Deluxe.app") + FileUtils.cp_r source_path, staged_app_copy + + shutup do + install_phase.call + end + + expect(target_path).to be_a_directory + expect(source_path).not_to exist + + expect(Hbc.appdir.join("Caffeine Deluxe.app")).not_to exist + expect(cask.staged_path.join("Caffeine Deluxe.app")).to exist + end + + describe "when the target already exists" do + before(:each) do + target_path.mkpath + end + + it "avoids clobbering an existing app" do + expect(install_phase).to raise_error(Hbc::CaskError, "It seems there is already an App at '#{target_path}'.") + + expect(source_path).to be_a_directory + expect(target_path).to be_a_directory + expect(File.identical?(source_path, target_path)).to be false + + contents_path = target_path.join("Contents/Info.plist") + expect(contents_path).not_to exist + end + + describe "given the force option" do + let(:force) { true } + + before(:each) do + allow(Hbc::Utils).to receive(:current_user).and_return("fake_user") + end + + describe "target is both writable and user-owned" do + it "overwrites the existing app" do + stdout = <<-EOS.undent + ==> Removing App: '#{target_path}' + ==> Moving App 'Caffeine.app' to '#{target_path}' + EOS + + stderr = <<-EOS.undent + Warning: It seems there is already an App at '#{target_path}'; overwriting. + EOS + + expect { + expect(install_phase).to output(stdout).to_stdout + }.to output(stderr).to_stderr + + expect(source_path).not_to exist + expect(target_path).to be_a_directory + + contents_path = target_path.join("Contents/Info.plist") + expect(contents_path).to exist + end + end + + describe "target is user-owned but contains read-only files" do + before(:each) do + system "/usr/bin/touch", "--", "#{target_path}/foo" + system "/bin/chmod", "--", "0555", target_path + end + + it "overwrites the existing app" do + expect(command).to receive(:run).with("/bin/chmod", args: ["-R", "--", "u+rwx", target_path], must_succeed: false) + .and_call_original + expect(command).to receive(:run).with("/bin/chmod", args: ["-R", "-N", target_path], must_succeed: false) + .and_call_original + expect(command).to receive(:run).with("/usr/bin/chflags", args: ["-R", "--", "000", target_path], must_succeed: false) + .and_call_original + + stdout = <<-EOS.undent + ==> Removing App: '#{target_path}' + ==> Moving App 'Caffeine.app' to '#{target_path}' + EOS + + stderr = <<-EOS.undent + Warning: It seems there is already an App at '#{target_path}'; overwriting. + EOS + + expect { + expect(install_phase).to output(stdout).to_stdout + }.to output(stderr).to_stderr + + expect(source_path).not_to exist + expect(target_path).to be_a_directory + + contents_path = target_path.join("Contents/Info.plist") + expect(contents_path).to exist + end + + after(:each) do + system "/bin/chmod", "--", "0755", target_path + end + end + end + end + + describe "when the target is a broken symlink" do + let(:deleted_path) { cask.staged_path.join("Deleted.app") } + + before(:each) do + deleted_path.mkdir + File.symlink(deleted_path, target_path) + deleted_path.rmdir + end + + it "leaves the target alone" do + expect(install_phase).to raise_error(Hbc::CaskError, "It seems there is already an App at '#{target_path}'.") + expect(target_path).to be_a_symlink + end + + describe "given the force option" do + let(:force) { true } + + it "overwrites the existing app" do + stdout = <<-EOS.undent + ==> Removing App: '#{target_path}' + ==> Moving App 'Caffeine.app' to '#{target_path}' + EOS + + stderr = <<-EOS.undent + Warning: It seems there is already an App at '#{target_path}'; overwriting. + EOS + + expect { + expect(install_phase).to output(stdout).to_stdout + }.to output(stderr).to_stderr + + expect(source_path).not_to exist + expect(target_path).to be_a_directory + + contents_path = target_path.join("Contents/Info.plist") + expect(contents_path).to exist + end + end + end + + it "gives a warning if the source doesn't exist" do + source_path.rmtree + + message = "It seems the App source is not there: '#{source_path}'" + + expect(install_phase).to raise_error(Hbc::CaskError, message) + end + end + + describe "uninstall_phase" do + it "deletes managed apps" do + shutup do + install_phase.call + end + + expect(target_path).to exist + + shutup do + uninstall_phase.call + end + + expect(target_path).not_to exist + end + end + + describe "summary" do + let(:description) { app.summary[:english_description] } + let(:contents) { app.summary[:contents] } + + it "returns the correct english_description" do + expect(description).to eq("Apps") + end + + describe "app is correctly installed" do + it "returns the path to the app" do + shutup do + install_phase.call + end + + expect(contents).to eq(["#{target_path} (#{target_path.abv})"]) + end + end + + describe "app is missing" do + it "returns a warning and the supposed path to the app" do + expect(contents.size).to eq(1) + expect(contents[0]).to match(/.*Missing App.*: #{target_path}/) + end + end + end +end diff --git a/Library/Homebrew/test/cask/artifact/binary_spec.rb b/Library/Homebrew/test/cask/artifact/binary_spec.rb new file mode 100644 index 000000000..1b26773ca --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/binary_spec.rb @@ -0,0 +1,90 @@ +describe Hbc::Artifact::Binary, :cask do + let(:cask) { + Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-binary.rb").tap do |cask| + shutup do + InstallHelper.install_without_artifacts(cask) + end + end + } + let(:expected_path) { + Hbc.binarydir.join("binary") + } + before(:each) do + Hbc.binarydir.mkpath + end + after(:each) do + FileUtils.rm expected_path if expected_path.exist? + end + + it "links the binary to the proper directory" do + shutup do + Hbc::Artifact::Binary.new(cask).install_phase + end + expect(expected_path).to be_a_symlink + expect(expected_path.readlink).to exist + end + + it "avoids clobbering an existing binary by linking over it" do + FileUtils.touch expected_path + + expect { + shutup do + Hbc::Artifact::Binary.new(cask).install_phase + end + }.to raise_error(Hbc::CaskError) + + expect(expected_path).not_to be :symlink? + end + + it "clobbers an existing symlink" do + expected_path.make_symlink("/tmp") + + shutup do + Hbc::Artifact::Binary.new(cask).install_phase + end + + expect(File.readlink(expected_path)).not_to eq("/tmp") + end + + it "respects --no-binaries flag" do + Hbc.no_binaries = true + + shutup do + Hbc::Artifact::Binary.new(cask).install_phase + end + + expect(expected_path.exist?).to be false + + Hbc.no_binaries = false + end + + it "creates parent directory if it doesn't exist" do + FileUtils.rmdir Hbc.binarydir + + shutup do + Hbc::Artifact::Binary.new(cask).install_phase + end + + expect(expected_path.exist?).to be true + end + + context "binary is inside an app package" do + let(:cask) { + Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-embedded-binary.rb").tap do |cask| + shutup do + InstallHelper.install_without_artifacts(cask) + end + end + } + + it "links the binary to the proper directory" do + shutup do + Hbc::Artifact::App.new(cask).install_phase + Hbc::Artifact::Binary.new(cask).install_phase + end + + expect(expected_path).to be_a_symlink + expect(expected_path.readlink).to exist + end + end +end diff --git a/Library/Homebrew/test/cask/artifact/generic_artifact_spec.rb b/Library/Homebrew/test/cask/artifact/generic_artifact_spec.rb new file mode 100644 index 000000000..b383e2d4e --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/generic_artifact_spec.rb @@ -0,0 +1,45 @@ +describe Hbc::Artifact::Artifact, :cask do + let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-generic-artifact.rb") } + + let(:install_phase) { + -> { Hbc::Artifact::Artifact.new(cask).install_phase } + } + + let(:source_path) { cask.staged_path.join("Caffeine.app") } + let(:target_path) { Hbc.appdir.join("Caffeine.app") } + + before do + InstallHelper.install_without_artifacts(cask) + end + + describe "with no target" do + let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-generic-artifact-no-target.rb") } + + it "fails to install with no target" do + expect(install_phase).to raise_error(Hbc::CaskInvalidError) + end + end + + it "moves the artifact to the proper directory" do + shutup do + install_phase.call + end + + expect(target_path).to be_a_directory + expect(source_path).not_to exist + end + + it "avoids clobbering an existing artifact" do + target_path.mkpath + + expect { + shutup do + install_phase.call + end + }.to raise_error(Hbc::CaskError) + + expect(source_path).to be_a_directory + expect(target_path).to be_a_directory + expect(File.identical?(source_path, target_path)).to be false + end +end diff --git a/Library/Homebrew/test/cask/artifact/nested_container_spec.rb b/Library/Homebrew/test/cask/artifact/nested_container_spec.rb new file mode 100644 index 000000000..3e9a549ea --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/nested_container_spec.rb @@ -0,0 +1,15 @@ +describe Hbc::Artifact::NestedContainer, :cask do + describe "install" do + it "extracts the specified paths as containers" do + cask = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/nested-app.rb").tap do |c| + InstallHelper.install_without_artifacts(c) + end + + shutup do + Hbc::Artifact::NestedContainer.new(cask).install_phase + end + + expect(cask.staged_path.join("MyNestedApp.app")).to be_a_directory + end + end +end diff --git a/Library/Homebrew/test/cask/artifact/pkg_spec.rb b/Library/Homebrew/test/cask/artifact/pkg_spec.rb new file mode 100644 index 000000000..249439900 --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/pkg_spec.rb @@ -0,0 +1,69 @@ +describe Hbc::Artifact::Pkg, :cask do + let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-installable.rb") } + let(:fake_system_command) { class_double(Hbc::SystemCommand) } + + before(:each) do + shutup do + InstallHelper.install_without_artifacts(cask) + end + end + + describe "install_phase" do + it "runs the system installer on the specified pkgs" do + pkg = Hbc::Artifact::Pkg.new(cask, command: fake_system_command) + + expect(fake_system_command).to receive(:run!).with( + "/usr/sbin/installer", + args: ["-pkg", cask.staged_path.join("MyFancyPkg", "Fancy.pkg"), "-target", "/"], + sudo: true, + print_stdout: true, + ) + + shutup do + pkg.install_phase + end + end + end + + describe "choices" do + let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-choices.rb") } + + it "passes the choice changes xml to the system installer" do + pkg = Hbc::Artifact::Pkg.new(cask, command: fake_system_command) + + file = double(path: Pathname.new("/tmp/choices.xml")) + + expect(file).to receive(:write).with(<<-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"> + <array> + \t<dict> + \t\t<key>attributeSetting</key> + \t\t<integer>1</integer> + \t\t<key>choiceAttribute</key> + \t\t<string>selected</string> + \t\t<key>choiceIdentifier</key> + \t\t<string>choice1</string> + \t</dict> + </array> + </plist> + EOS + + expect(file).to receive(:close) + expect(file).to receive(:unlink) + expect(Tempfile).to receive(:open).and_yield(file) + + expect(fake_system_command).to receive(:run!).with( + "/usr/sbin/installer", + args: ["-pkg", cask.staged_path.join("MyFancyPkg", "Fancy.pkg"), "-target", "/", "-applyChoiceChangesXML", cask.staged_path.join("/tmp/choices.xml")], + sudo: true, + print_stdout: true, + ) + + shutup do + pkg.install_phase + end + end + end +end diff --git a/Library/Homebrew/test/cask/artifact/postflight_block_spec.rb b/Library/Homebrew/test/cask/artifact/postflight_block_spec.rb new file mode 100644 index 000000000..51b1431f0 --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/postflight_block_spec.rb @@ -0,0 +1,39 @@ +describe Hbc::Artifact::PostflightBlock, :cask do + describe "install_phase" do + it "calls the specified block after installing, passing a Cask mini-dsl" do + called = false + yielded_arg = nil + + cask = Hbc::Cask.new("with-postflight") do + postflight do |c| + called = true + yielded_arg = c + end + end + + described_class.new(cask).install_phase + + expect(called).to be true + expect(yielded_arg).to be_kind_of(Hbc::DSL::Postflight) + end + end + + describe "uninstall_phase" do + it "calls the specified block after uninstalling, passing a Cask mini-dsl" do + called = false + yielded_arg = nil + + cask = Hbc::Cask.new("with-uninstall-postflight") do + uninstall_postflight do |c| + called = true + yielded_arg = c + end + end + + described_class.new(cask).uninstall_phase + + expect(called).to be true + expect(yielded_arg).to be_kind_of(Hbc::DSL::UninstallPostflight) + end + end +end diff --git a/Library/Homebrew/test/cask/artifact/preflight_block_spec.rb b/Library/Homebrew/test/cask/artifact/preflight_block_spec.rb new file mode 100644 index 000000000..b13c4ab9d --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/preflight_block_spec.rb @@ -0,0 +1,39 @@ +describe Hbc::Artifact::PreflightBlock, :cask do + describe "install_phase" do + it "calls the specified block before installing, passing a Cask mini-dsl" do + called = false + yielded_arg = nil + + cask = Hbc::Cask.new("with-preflight") do + preflight do |c| + called = true + yielded_arg = c + end + end + + described_class.new(cask).install_phase + + expect(called).to be true + expect(yielded_arg).to be_kind_of Hbc::DSL::Preflight + end + end + + describe "uninstall_phase" do + it "calls the specified block before uninstalling, passing a Cask mini-dsl" do + called = false + yielded_arg = nil + + cask = Hbc::Cask.new("with-uninstall-preflight") do + uninstall_preflight do |c| + called = true + yielded_arg = c + end + end + + described_class.new(cask).uninstall_phase + + expect(called).to be true + expect(yielded_arg).to be_kind_of Hbc::DSL::UninstallPreflight + end + end +end diff --git a/Library/Homebrew/test/cask/artifact/suite_spec.rb b/Library/Homebrew/test/cask/artifact/suite_spec.rb new file mode 100644 index 000000000..98ae93311 --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/suite_spec.rb @@ -0,0 +1,47 @@ +describe Hbc::Artifact::Suite, :cask do + let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-suite.rb") } + + let(:install_phase) { -> { Hbc::Artifact::Suite.new(cask).install_phase } } + + let(:target_path) { Hbc.appdir.join("Caffeine") } + let(:source_path) { cask.staged_path.join("Caffeine") } + + before(:each) do + InstallHelper.install_without_artifacts(cask) + end + + it "moves the suite to the proper directory" do + skip("flaky test") # FIXME + + shutup do + install_phase.call + end + + expect(target_path).to be_a_directory + expect(target_path).to be_a_symlink + expect(target_path.readlink).to exist + expect(source_path).not_to exist + end + + it "creates a suite containing the expected app" do + shutup do + install_phase.call + end + + expect(target_path.join("Caffeine.app")).to exist + end + + it "avoids clobbering an existing suite by moving over it" do + target_path.mkpath + + expect { + shutup do + install_phase.call + end + }.to raise_error(Hbc::CaskError) + + expect(source_path).to be_a_directory + expect(target_path).to be_a_directory + expect(File.identical?(source_path, target_path)).to be false + end +end diff --git a/Library/Homebrew/test/cask/artifact/two_apps_correct_spec.rb b/Library/Homebrew/test/cask/artifact/two_apps_correct_spec.rb new file mode 100644 index 000000000..9db22b2a3 --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/two_apps_correct_spec.rb @@ -0,0 +1,91 @@ +describe Hbc::Artifact::App, :cask do + describe "multiple apps" do + let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-two-apps-correct.rb") } + + let(:install_phase) { + -> { Hbc::Artifact::App.new(cask).install_phase } + } + + let(:source_path_mini) { cask.staged_path.join("Caffeine Mini.app") } + let(:target_path_mini) { Hbc.appdir.join("Caffeine Mini.app") } + + let(:source_path_pro) { cask.staged_path.join("Caffeine Pro.app") } + let(:target_path_pro) { Hbc.appdir.join("Caffeine Pro.app") } + + before(:each) do + InstallHelper.install_without_artifacts(cask) + end + + it "installs both apps using the proper target directory" do + shutup do + install_phase.call + end + + expect(target_path_mini).to be_a_directory + expect(source_path_mini).not_to exist + + expect(target_path_pro).to be_a_directory + expect(source_path_pro).not_to exist + end + + describe "when apps are in a subdirectory" do + let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-two-apps-subdir.rb") } + + it "installs both apps using the proper target directory" do + shutup do + install_phase.call + end + + expect(target_path_mini).to be_a_directory + expect(source_path_mini).not_to exist + + expect(target_path_pro).to be_a_directory + expect(source_path_pro).not_to exist + end + end + + it "only uses apps when they are specified" do + FileUtils.cp_r source_path_mini, source_path_mini.sub("Caffeine Mini.app", "Caffeine Deluxe.app") + + shutup do + install_phase.call + end + + expect(target_path_mini).to be_a_directory + expect(source_path_mini).not_to exist + + expect(Hbc.appdir.join("Caffeine Deluxe.app")).not_to exist + expect(cask.staged_path.join("Caffeine Deluxe.app")).to exist + end + + describe "avoids clobbering an existing app" do + it "when the first app of two already exists" do + target_path_mini.mkpath + + expect { + expect(install_phase).to output(<<-EOS.undent).to_stdout + ==> Moving App 'Caffeine Pro.app' to '#{target_path_pro}' + EOS + }.to raise_error(Hbc::CaskError, "It seems there is already an App at '#{target_path_mini}'.") + + expect(source_path_mini).to be_a_directory + expect(target_path_mini).to be_a_directory + expect(File.identical?(source_path_mini, target_path_mini)).to be false + end + + it "when the second app of two already exists" do + target_path_pro.mkpath + + expect { + expect(install_phase).to output(<<-EOS.undent).to_stdout + ==> Moving App 'Caffeine Mini.app' to '#{target_path_mini}' + EOS + }.to raise_error(Hbc::CaskError, "It seems there is already an App at '#{target_path_pro}'.") + + expect(source_path_pro).to be_a_directory + expect(target_path_pro).to be_a_directory + expect(File.identical?(source_path_pro, target_path_pro)).to be false + end + end + end +end diff --git a/Library/Homebrew/test/cask/artifact/two_apps_incorrect_spec.rb b/Library/Homebrew/test/cask/artifact/two_apps_incorrect_spec.rb new file mode 100644 index 000000000..6427ec32c --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/two_apps_incorrect_spec.rb @@ -0,0 +1,9 @@ +describe Hbc::Artifact::App, :cask do + # FIXME: Doesn't actually raise because the `app` stanza is not evaluated on load. + # it "must raise" do + # lambda { + # Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-two-apps-incorrect.rb") + # }.must_raise + # # TODO: later give the user a nice exception for this case and check for it here + # end +end diff --git a/Library/Homebrew/test/cask/artifact/uninstall_no_zap_spec.rb b/Library/Homebrew/test/cask/artifact/uninstall_no_zap_spec.rb new file mode 100644 index 000000000..f88aaa49d --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/uninstall_no_zap_spec.rb @@ -0,0 +1,19 @@ +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) + } + + before do + shutup do + InstallHelper.install_without_artifacts(cask) + end + end + + describe "#uninstall_phase" do + subject { zap_artifact } + + it { is_expected.not_to respond_to(:uninstall_phase) } + end +end diff --git a/Library/Homebrew/test/cask/artifact/uninstall_spec.rb b/Library/Homebrew/test/cask/artifact/uninstall_spec.rb new file mode 100644 index 000000000..b7deb4575 --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/uninstall_spec.rb @@ -0,0 +1,351 @@ +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 + end +end diff --git a/Library/Homebrew/test/cask/artifact/zap_spec.rb b/Library/Homebrew/test/cask/artifact/zap_spec.rb new file mode 100644 index 000000000..fdf2e4f9d --- /dev/null +++ b/Library/Homebrew/test/cask/artifact/zap_spec.rb @@ -0,0 +1,352 @@ +# TODO: test that zap removes an alternate version of the same Cask +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 + end +end |
