diff options
| author | AnastasiaSulyagina | 2016-08-18 22:11:42 +0300 |
|---|---|---|
| committer | AnastasiaSulyagina | 2016-08-19 14:50:14 +0300 |
| commit | e81f4ab7deeb40308f240be5ea00091fc8786d7a (patch) | |
| tree | b5418f9149de71c0f05f90cb2b39ab47f46e27b4 /Library/Homebrew/cask/spec | |
| parent | 5c7c9de669025bbe4cad9829be39c5cf3b31ad25 (diff) | |
| download | brew-e81f4ab7deeb40308f240be5ea00091fc8786d7a.tar.bz2 | |
init
Diffstat (limited to 'Library/Homebrew/cask/spec')
53 files changed, 2142 insertions, 0 deletions
diff --git a/Library/Homebrew/cask/spec/cask/artifact/binary_spec.rb b/Library/Homebrew/cask/spec/cask/artifact/binary_spec.rb new file mode 100644 index 000000000..81e5c9026 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/artifact/binary_spec.rb @@ -0,0 +1,88 @@ +require "spec_helper" + +describe Hbc::Artifact::Binary do + let(:cask) { + Hbc.load("with-binary").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(FileHelper.valid_alias?(expected_path)).to be true + end + + it "avoids clobbering an existing binary by linking over it" do + FileUtils.touch expected_path + + shutup do + Hbc::Artifact::Binary.new(cask).install_phase + end + + expect(expected_path).to_not 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.load("with-embedded-binary").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(FileHelper.valid_alias?(expected_path)).to be true + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/audit_spec.rb b/Library/Homebrew/cask/spec/cask/audit_spec.rb new file mode 100644 index 000000000..65a1335a5 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/audit_spec.rb @@ -0,0 +1,333 @@ +require "spec_helper" + +describe Hbc::Audit do + include AuditMatchers + include Sha256Helper + + let(:cask) { instance_double(Hbc::Cask) } + let(:download) { false } + let(:check_token_conflicts) { false } + let(:fake_system_command) { class_double(Hbc::SystemCommand) } + let(:audit) { + Hbc::Audit.new(cask, download: download, + check_token_conflicts: check_token_conflicts, + command: fake_system_command) + } + + describe "#result" do + subject { audit.result } + + context "when there are errors" do + before do + audit.add_error "bad" + end + + it { should match(%r{failed}) } + end + + context "when there are warnings" do + before do + audit.add_warning "eh" + end + + it { should match(%r{warning}) } + end + + context "when there are errors and warnings" do + before do + audit.add_error "bad" + audit.add_warning "eh" + end + + it { should match(%r{failed}) } + end + + context "when there are no errors or warnings" do + it { should match(%r{passed}) } + end + end + + describe "#run!" do + let(:cask) { Hbc.load(cask_token) } + subject { audit.run! } + + describe "required stanzas" do + %w[version sha256 url name homepage license].each do |stanza| + context "when missing #{stanza}" do + let(:cask_token) { "missing-#{stanza}" } + it { should fail_with(%r{#{stanza} stanza is required}) } + end + end + end + + describe "version checks" do + let(:error_msg) { "you should use version :latest instead of version 'latest'" } + + context "when version is 'latest'" do + let(:cask_token) { "version-latest-string" } + it { should fail_with(error_msg) } + end + + context "when version is :latest" do + let(:cask_token) { "version-latest-with-checksum" } + it { should_not fail_with(error_msg) } + end + end + + describe "sha256 checks" do + context "when version is :latest and sha256 is not :no_check" do + let(:cask_token) { "version-latest-with-checksum" } + it { should fail_with("you should use sha256 :no_check when version is :latest") } + end + + context "when sha256 is not a legal SHA-256 digest" do + let(:cask_token) { "invalid-sha256" } + it { should fail_with("sha256 string must be of 64 hexadecimal characters") } + end + + context "when sha256 is sha256 for empty string" do + let(:cask_token) { "sha256-for-empty-string" } + it { should fail_with(%r{cannot use the sha256 for an empty string}) } + end + end + + describe "appcast checks" do + context "when appcast has no sha256" do + let(:cask_token) { "appcast-missing-checkpoint" } + it { should fail_with(%r{checkpoint sha256 is required for appcast}) } + end + + context "when appcast checkpoint is not a string of 64 hexadecimal characters" do + let(:cask_token) { "appcast-invalid-checkpoint" } + it { should fail_with(%r{string must be of 64 hexadecimal characters}) } + end + + context "when appcast checkpoint is sha256 for empty string" do + let(:cask_token) { "appcast-checkpoint-sha256-for-empty-string" } + it { should fail_with(%r{cannot use the sha256 for an empty string}) } + end + + context "when appcast checkpoint is valid sha256" do + let(:cask_token) { "appcast-valid-checkpoint" } + it { should_not fail_with(%r{appcast :checkpoint}) } + end + + context "when verifying appcast HTTP code" do + let(:cask_token) { "appcast-valid-checkpoint" } + let(:download) { instance_double(Hbc::Download) } + let(:wrong_code_msg) { %r{unexpected HTTP response code} } + let(:curl_error_msg) { %r{error retrieving appcast} } + let(:fake_curl_result) { instance_double(Hbc::SystemCommand::Result) } + + before do + allow(audit).to receive(:check_appcast_checkpoint_accuracy) + allow(fake_system_command).to receive(:run).and_return(fake_curl_result) + allow(fake_curl_result).to receive(:success?).and_return(success) + end + + context "when curl succeeds" do + let(:success) { true } + + before do + allow(fake_curl_result).to receive(:stdout).and_return(stdout) + end + + context "when HTTP code is 200" do + let(:stdout) { "200" } + it { should_not warn_with(wrong_code_msg) } + end + + context "when HTTP code is not 200" do + let(:stdout) { "404" } + it { should warn_with(wrong_code_msg) } + end + end + + context "when curl fails" do + let(:success) { false } + + before do + allow(fake_curl_result).to receive(:stderr).and_return("Some curl error") + end + + it { should warn_with(curl_error_msg) } + end + end + + context "when verifying appcast checkpoint" do + let(:cask_token) { "appcast-valid-checkpoint" } + let(:download) { instance_double(Hbc::Download) } + let(:mismatch_msg) { %r{appcast checkpoint mismatch} } + let(:curl_error_msg) { %r{error retrieving appcast} } + let(:fake_curl_result) { instance_double(Hbc::SystemCommand::Result) } + let(:expected_checkpoint) { "d5b2dfbef7ea28c25f7a77cd7fa14d013d82b626db1d82e00e25822464ba19e2" } + + before do + allow(audit).to receive(:check_appcast_http_code) + allow(fake_system_command).to receive(:run).and_return(fake_curl_result) + allow(fake_curl_result).to receive(:success?).and_return(success) + end + + context "when appcast download succeeds" do + let(:success) { true } + let(:appcast_text) { instance_double(::String) } + + before do + allow(fake_curl_result).to receive(:stdout).and_return(appcast_text) + allow(appcast_text).to receive(:gsub).and_return(appcast_text) + allow(appcast_text).to receive(:end_with?).with("\n").and_return(true) + allow(Digest::SHA2).to receive(:hexdigest).and_return(actual_checkpoint) + end + + context "when appcast checkpoint is out of date" do + let(:actual_checkpoint) { random_sha256 } + it { should warn_with(mismatch_msg) } + it { should_not warn_with(curl_error_msg) } + end + + context "when appcast checkpoint is up to date" do + let(:actual_checkpoint) { expected_checkpoint } + it { should_not warn_with(mismatch_msg) } + it { should_not warn_with(curl_error_msg) } + end + end + + context "when appcast download fails" do + let(:success) { false } + + before do + allow(fake_curl_result).to receive(:stderr).and_return("Some curl error") + end + + it { should warn_with(curl_error_msg) } + end + end + end + + describe "preferred download URL formats" do + let(:warning_msg) { %r{URL format incorrect} } + + context "with incorrect SourceForge URL format" do + let(:cask_token) { "sourceforge-incorrect-url-format" } + it { should warn_with(warning_msg) } + end + + context "with correct SourceForge URL format" do + let(:cask_token) { "sourceforge-correct-url-format" } + it { should_not warn_with(warning_msg) } + end + + context "with correct SourceForge URL format for version :latest" do + let(:cask_token) { "sourceforge-version-latest-correct-url-format" } + it { should_not warn_with(warning_msg) } + end + + context "with incorrect OSDN URL format" do + let(:cask_token) { "osdn-incorrect-url-format" } + it { should warn_with(warning_msg) } + end + + context "with correct OSDN URL format" do + let(:cask_token) { "osdn-correct-url-format" } + it { should_not warn_with(warning_msg) } + end + end + + describe "generic artifact checks" do + context "with no target" do + let(:cask_token) { "generic-artifact-no-target" } + it { should fail_with(%r{target required for generic artifact}) } + end + + context "with relative target" do + let(:cask_token) { "generic-artifact-relative-target" } + it { should fail_with(%r{target must be absolute path for generic artifact}) } + end + + context "with absolute target" do + let(:cask_token) { "generic-artifact-absolute-target" } + it { should_not fail_with(%r{target required for generic artifact}) } + end + end + + describe "url checks" do + context "given a block" do + let(:cask_token) { "booby-trap" } + + context "when loading the cask" do + it "does not evaluate the block" do + expect { cask }.to_not raise_error + end + end + + context "when doing the audit" do + it "evaluates the block" do + expect(subject).to fail_with(%r{Boom}) + end + end + end + end + + describe "token conflicts" do + let(:cask_token) { "with-binary" } + let(:check_token_conflicts) { true } + + before do + expect(audit).to receive(:core_formula_names).and_return(formula_names) + end + + context "when cask token conflicts with a core formula" do + let(:formula_names) { %w[with-binary other-formula] } + it { should warn_with(%r{possible duplicate}) } + end + + context "when cask token does not conflict with a core formula" do + let(:formula_names) { %w[other-formula] } + it { should_not warn_with(%r{possible duplicate}) } + end + end + + describe "audit of downloads" do + let(:cask_token) { "with-binary" } + let(:cask) { Hbc.load(cask_token) } + let(:download) { instance_double(Hbc::Download) } + let(:verify) { class_double(Hbc::Verify).as_stubbed_const } + let(:error_msg) { "Download Failed" } + + context "when download and verification succeed" do + before do + download.expects(:perform) + verify.expects(:all) + end + + it { should_not fail_with(%r{#{error_msg}}) } + end + + context "when download fails" do + before do + download.expects(:perform).raises(StandardError.new(error_msg)) + end + + it { should fail_with(%r{#{error_msg}}) } + end + + context "when verification fails" do + before do + download.expects(:perform) + verify.expects(:all).raises(StandardError.new(error_msg)) + end + + it { should fail_with(%r{#{error_msg}}) } + end + end + + context "when an exception is raised" do + let(:cask) { instance_double(Hbc::Cask) } + before do + cask.expects(:version).raises(StandardError.new) + end + + it { should fail_with(%r{exception while auditing}) } + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/cask_spec.rb b/Library/Homebrew/cask/spec/cask/cask_spec.rb new file mode 100644 index 000000000..f31bee023 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/cask_spec.rb @@ -0,0 +1,25 @@ +require "spec_helper" + +describe Hbc::Cask do + let(:cask) { described_class.new("versioned-cask") } + + context "when multiple versions are installed" do + describe "#versions" do + context "and there are duplicate versions" do + it "uses the last unique version" do + allow(cask).to receive(:timestamped_versions).and_return([ + ["1.2.2", "0999"], + ["1.2.3", "1000"], + ["1.2.2", "1001"], + ]) + + expect(cask).to receive(:timestamped_versions) + expect(cask.versions).to eq([ + "1.2.3", + "1.2.2", + ]) + end + end + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/cli/cleanup_spec.rb b/Library/Homebrew/cask/spec/cask/cli/cleanup_spec.rb new file mode 100644 index 000000000..e77576ae3 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/cli/cleanup_spec.rb @@ -0,0 +1,68 @@ +require "spec_helper" + +describe Hbc::CLI::Cleanup do + let(:cache_location) { Pathname.new(Dir.mktmpdir).realpath } + let(:cleanup_outdated) { false } + + subject { described_class.new(cache_location, cleanup_outdated) } + + after do + cache_location.rmtree + end + + describe "cleanup!" do + it "removes cached downloads" do + cached_download = cache_location.join("SomeDownload.dmg") + FileUtils.touch(cached_download) + cleanup_size = subject.disk_cleanup_size + + expect { + subject.cleanup! + }.to output(<<-OUTPUT.undent).to_stdout + ==> Removing cached downloads + #{cached_download} + ==> This operation has freed approximately #{disk_usage_readable(cleanup_size)} of disk space. + OUTPUT + + expect(cached_download.exist?).to eq(false) + end + + it "does not removed locked files" do + cached_download = cache_location.join("SomeDownload.dmg") + FileUtils.touch(cached_download) + cleanup_size = subject.disk_cleanup_size + + File.new(cached_download).flock(File::LOCK_EX) + + expect(Hbc::Utils).to be_file_locked(cached_download) + + expect { + subject.cleanup! + }.to output(<<-OUTPUT.undent).to_stdout + ==> Removing cached downloads + skipping: #{cached_download} is locked + ==> This operation has freed approximately #{disk_usage_readable(cleanup_size)} of disk space. + OUTPUT + + expect(cached_download.exist?).to eq(true) + end + + context "when cleanup_outdated is specified" do + let(:cleanup_outdated) { true } + + it "does not remove cache files newer than 10 days old" do + cached_download = cache_location.join("SomeNewDownload.dmg") + FileUtils.touch(cached_download) + + expect { + subject.cleanup! + }.to output(<<-OUTPUT.undent).to_stdout + ==> Removing cached downloads older than 10 days old + Nothing to do + OUTPUT + + expect(cached_download.exist?).to eq(true) + end + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/cli/doctor_spec.rb b/Library/Homebrew/cask/spec/cask/cli/doctor_spec.rb new file mode 100644 index 000000000..3c9c66150 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/cli/doctor_spec.rb @@ -0,0 +1,16 @@ +require "spec_helper" +require "hbc/version" + +describe Hbc::CLI::Doctor do + it "displays some nice info about the environment" do + expect { + Hbc::CLI::Doctor.run + }.to output(%r{\A==> macOS Release:}).to_stdout + end + + it "raises an exception when arguments are given" do + expect { + Hbc::CLI::Doctor.run("argument") + }.to raise_error(ArgumentError) + end +end diff --git a/Library/Homebrew/cask/spec/cask/cli/style_spec.rb b/Library/Homebrew/cask/spec/cask/cli/style_spec.rb new file mode 100644 index 000000000..0e53d62a2 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/cli/style_spec.rb @@ -0,0 +1,224 @@ +require "English" +require "spec_helper" + +describe Hbc::CLI::Style do + let(:args) { [] } + let(:cli) { described_class.new(args) } + + around do |example| + shutup { example.run } + end + + describe ".run" do + subject { described_class.run(args) } + + before do + allow(described_class).to receive(:new).and_return(cli) + allow(cli).to receive(:run).and_return(retval) + end + + context "when rubocop succeeds" do + let(:retval) { true } + + it "exits successfully" do + subject + end + end + + context "when rubocop fails" do + let(:retval) { false } + + it "raises an exception" do + expect { subject }.to raise_error(Hbc::CaskError) + end + end + end + + describe "#run" do + subject { cli.run } + + before do + allow(cli).to receive_messages(install_rubocop: nil, + system: nil, + rubocop_args: nil, + cask_paths: nil) + allow($CHILD_STATUS).to receive(:success?).and_return(success) + end + + context "when rubocop succeeds" do + let(:success) { true } + it { is_expected.to be_truthy } + end + + context "when rubocop fails" do + let(:success) { false } + it { is_expected.to be_falsey } + end + end + + describe "#install_rubocop" do + subject { cli.install_rubocop } + + context "when installation succeeds" do + before do + allow(Homebrew).to receive(:install_gem_setup_path!) + end + + it "exits successfully" do + expect { subject }.to_not raise_error + end + end + + context "when installation fails" do + before do + allow(Homebrew).to receive(:install_gem_setup_path!).and_raise(SystemExit) + end + + it "raises an error" do + expect { subject }.to raise_error(Hbc::CaskError) + end + end + end + + describe "#cask_paths" do + subject { cli.cask_paths } + + before do + allow(cli).to receive(:cask_tokens).and_return(tokens) + end + + context "when no cask tokens are given" do + let(:tokens) { [] } + + before do + allow(Hbc).to receive(:all_tapped_cask_dirs).and_return(%w[Casks MoreCasks]) + end + + it { is_expected.to eq(%w[Casks MoreCasks]) } + end + + context "when at least one cask token is a path that exists" do + let(:tokens) { ["adium", "Casks/dropbox.rb"] } + before do + allow(File).to receive(:exist?).and_return(false, true) + end + + it "treats all tokens as paths" do + expect(subject).to eq(tokens) + end + end + + context "when no cask tokens are paths that exist" do + let(:tokens) { %w[adium dropbox] } + before do + allow(File).to receive(:exist?).and_return(false) + end + + it "tries to find paths for all tokens" do + expect(Hbc).to receive(:path).twice + subject + end + end + end + + describe "#cask_tokens" do + subject { cli.cask_tokens } + + context "when no args are given" do + let(:args) { [] } + it { is_expected.to be_empty } + end + + context "when only flags are given" do + let(:args) { ["--fix"] } + it { is_expected.to be_empty } + end + + context "when only empty args are given" do + let(:args) { ["", ""] } + it { is_expected.to be_empty } + end + + context "when a cask token is given" do + let(:args) { ["adium"] } + it { is_expected.to eq(["adium"]) } + end + + context "when multiple cask tokens are given" do + let(:args) { %w[adium dropbox] } + it { is_expected.to eq(%w[adium dropbox]) } + end + + context "when cask tokens are given with flags" do + let(:args) { ["adium", "dropbox", "--fix"] } + it { is_expected.to eq(%w[adium dropbox]) } + end + end + + describe "#rubocop_args" do + subject { cli.rubocop_args } + + before do + allow(cli).to receive(:fix?).and_return(fix) + end + + context "when fix? is true" do + let(:fix) { true } + it { is_expected.to include("--auto-correct") } + end + + context "when fix? is false" do + let(:fix) { false } + it { is_expected.not_to include("--auto-correct") } + end + end + + describe "#default_args" do + subject { cli.default_args } + let(:rubocop_config) { ".rubocop.yml" } + before do + allow(cli).to receive(:rubocop_config).and_return(rubocop_config) + end + + it { is_expected.to include("--format", "simple", "--force-exclusion", "--config", rubocop_config) } + end + + describe "#autocorrect_args" do + subject { cli.autocorrect_args } + let(:default_args) { ["--format", "simple"] } + + it "should add --auto-correct to default args" do + allow(cli).to receive(:default_args).and_return(default_args) + expect(subject).to include("--auto-correct", *default_args) + end + end + + describe "#fix?" do + subject { cli.fix? } + + context "when --fix is passed as an argument" do + let(:args) { ["adium", "--fix"] } + it { should be_truthy } + end + + context "when --correct is passed as an argument" do + let(:args) { ["adium", "--correct"] } + it { should be_truthy } + end + + context "when --auto-correct is passed as an argument" do + let(:args) { ["adium", "--auto-correct"] } + it { should be_truthy } + end + + context "when --auto-correct is misspelled as --autocorrect" do + let(:args) { ["adium", "--autocorrect"] } + it { should be_truthy } + end + + context "when no flag equivalent to --fix is passed as an argument" do + let(:args) { ["adium"] } + it { should be_falsey } + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/cli_spec.rb b/Library/Homebrew/cask/spec/cask/cli_spec.rb new file mode 100644 index 000000000..cb21dbd25 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/cli_spec.rb @@ -0,0 +1,63 @@ +require "spec_helper" + +describe Hbc::CLI do + it "lists the taps for Casks that show up in two taps" do + listing = Hbc::CLI.nice_listing(%w[ + caskroom/cask/adium + caskroom/cask/google-chrome + passcod/homebrew-cask/adium + ]) + + expect(listing).to eq(%w[ + caskroom/cask/adium + google-chrome + passcod/cask/adium + ]) + end + + context ".process" do + let(:noop_command) { double("CLI::Noop") } + + before do + allow(Hbc).to receive(:init) + allow(described_class).to receive(:lookup_command).with("noop").and_return(noop_command) + allow(noop_command).to receive(:run) + end + + around do |example| + shutup { example.run } + end + + it "passes `--version` along to the subcommand" do + expect(described_class).to receive(:run_command).with(noop_command, "--version") + described_class.process(%w[noop --version]) + end + + it "prints help output when subcommand receives `--help` flag" do + expect(described_class).to receive(:run_command).with("help") + described_class.process(%w[noop --help]) + expect(Hbc.help).to eq(true) + Hbc.help = false + end + + it "respects the env variable when choosing what appdir to create" do + EnvHelper.with_env_var("HOMEBREW_CASK_OPTS", "--appdir=/custom/appdir") do + expect(Hbc).to receive(:appdir=).with(Pathname("/custom/appdir")) + described_class.process("noop") + end + end + + it "respects the env variable when choosing a non-default Caskroom location" do + EnvHelper.with_env_var "HOMEBREW_CASK_OPTS", "--caskroom=/custom/caskdir" do + expect(Hbc).to receive(:caskroom=).with(Pathname("/custom/caskdir")) + described_class.process("noop") + end + end + + it "exits with a status of 1 when something goes wrong" do + allow(described_class).to receive(:lookup_command).and_raise(Hbc::CaskError) + expect(described_class).to receive(:exit).with(1) + described_class.process("noop") + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/download_strategy_spec.rb b/Library/Homebrew/cask/spec/cask/download_strategy_spec.rb new file mode 100644 index 000000000..9b1c4d8e3 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/download_strategy_spec.rb @@ -0,0 +1,308 @@ +require "spec_helper" + +describe "download strategies" do + let(:url) { "http://example.com/cask.dmg" } + let(:url_options) { Hash.new } + let(:cask) { + instance_double(Hbc::Cask, token: "some-cask", + url: Hbc::URL.new(url, url_options), + version: "1.2.3.4") + } + + describe Hbc::CurlDownloadStrategy do + let(:downloader) { Hbc::CurlDownloadStrategy.new(cask) } + + before do + allow(downloader.temporary_path).to receive(:rename) + end + + it "properly assigns a name and uri based on the Cask" do + expect(downloader.name).to eq("some-cask") + expect(downloader.url).to eq("http://example.com/cask.dmg") + expect(downloader.version.to_s).to eq("1.2.3.4") + end + + it "calls curl with default arguments for a simple Cask" do + allow(downloader).to receive(:curl) + + shutup do + downloader.fetch + end + + expect(downloader).to have_received(:curl).with( + cask.url.to_s, + "-C", 0, + "-o", kind_of(Pathname) + ) + end + + context "with an explicit user agent" do + let(:url_options) { { user_agent: "Mozilla/25.0.1" } } + + it "adds the appropriate curl args" do + curl_args = [] + allow(downloader).to receive(:curl) { |*args| curl_args = args } + + shutup do + downloader.fetch + end + + expect(curl_args.each_cons(2)).to include(["-A", "Mozilla/25.0.1"]) + end + end + + context "with a generalized fake user agent" do + let(:url_options) { { user_agent: :fake } } + + it "adds the appropriate curl args" do + curl_args = [] + allow(downloader).to receive(:curl) { |*args| curl_args = args } + + shutup do + downloader.fetch + end + + expect(curl_args.each_cons(2)).to include(["-A", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10) http://caskroom.io"]) + end + end + + context "with cookies set" do + let(:url_options) { + { + cookies: { + coo: "kie", + mon: "ster", + }, + } + } + + it "adds curl args for cookies" do + curl_args = [] + allow(downloader).to receive(:curl) { |*args| curl_args = args } + + shutup do + downloader.fetch + end + + expect(curl_args.each_cons(2)).to include(["-b", "coo=kie;mon=ster"]) + end + end + + context "with referer set" do + let(:url_options) { { referer: "http://somehost/also" } } + + it "adds curl args for referer" do + curl_args = [] + allow(downloader).to receive(:curl) { |*args| curl_args = args } + + shutup do + downloader.fetch + end + + expect(curl_args.each_cons(2)).to include(["-e", "http://somehost/also"]) + end + end + end + + describe Hbc::CurlPostDownloadStrategy do + let(:downloader) { Hbc::CurlPostDownloadStrategy.new(cask) } + + before do + allow(downloader.temporary_path).to receive(:rename) + end + + context "with :using and :data specified" do + let(:url_options) { + { + using: :post, + data: { + form: "data", + is: "good", + }, + } + } + + it "adds curl args for post arguments" do + curl_args = [] + allow(downloader).to receive(:curl) { |*args| curl_args = args } + + shutup do + downloader.fetch + end + + expect(curl_args.each_cons(2)).to include(["-d", "form=data"]) + expect(curl_args.each_cons(2)).to include(["-d", "is=good"]) + end + end + + context "with :using but no :data" do + let(:url_options) { { using: :post } } + + it "adds curl args for a POST request" do + curl_args = [] + allow(downloader).to receive(:curl) { |*args| curl_args = args } + + shutup do + downloader.fetch + end + + expect(curl_args.each_cons(2)).to include(["-X", "POST"]) + end + end + end + + describe Hbc::SubversionDownloadStrategy do + let(:url_options) { { using: :svn } } + let(:fake_system_command) { class_double(Hbc::SystemCommand) } + let(:downloader) { Hbc::SubversionDownloadStrategy.new(cask, fake_system_command) } + before do + allow(fake_system_command).to receive(:run!) + end + + it "returns a tarball path on fetch" do + allow(downloader).to receive(:compress) + allow(downloader).to receive(:fetch_repo) + + retval = shutup { downloader.fetch } + + expect(retval).to equal(downloader.tarball_path) + end + + it "calls fetch_repo with default arguments for a simple Cask" do + allow(downloader).to receive(:compress) + allow(downloader).to receive(:fetch_repo) + + shutup do + downloader.fetch + end + + expect(downloader).to have_received(:fetch_repo).with( + downloader.cached_location, + cask.url.to_s + ) + end + + it "calls svn with default arguments for a simple Cask" do + allow(downloader).to receive(:compress) + + shutup do + downloader.fetch + end + + expect(fake_system_command).to have_received(:run!).with( + "/usr/bin/svn", + hash_including(args: [ + "checkout", + "--force", + "--config-option", + "config:miscellany:use-commit-times=yes", + cask.url.to_s, + downloader.cached_location, + ]) + ) + end + + context "with trust_cert set on the URL" do + let(:url_options) { + { + using: :svn, + trust_cert: true, + } + } + + it "adds svn arguments for :trust_cert" do + allow(downloader).to receive(:compress) + + shutup do + downloader.fetch + end + + expect(fake_system_command).to have_received(:run!).with( + "/usr/bin/svn", + hash_including(args: [ + "checkout", + "--force", + "--config-option", + "config:miscellany:use-commit-times=yes", + "--trust-server-cert", + "--non-interactive", + cask.url.to_s, + downloader.cached_location, + ]) + ) + end + end + + context "with :revision set on url" do + let(:url_options) { + { + using: :svn, + revision: "10", + } + } + + it "adds svn arguments for :revision" do + allow(downloader).to receive(:compress) + + shutup do + downloader.fetch + end + + expect(fake_system_command).to have_received(:run!).with( + "/usr/bin/svn", + hash_including(args: [ + "checkout", + "--force", + "--config-option", + "config:miscellany:use-commit-times=yes", + cask.url.to_s, + downloader.cached_location, + "-r", + "10", + ]) + ) + end + end + + it "runs tar to serialize svn downloads" do + # sneaky stub to remake the directory, since homebrew code removes it + # before tar is called + allow(downloader).to receive(:fetch_repo) { + downloader.cached_location.mkdir + } + + shutup do + downloader.fetch + end + + expect(fake_system_command).to have_received(:run!).with( + "/usr/bin/tar", + hash_including(args: [ + '-s/^\\.//', + "--exclude", + ".svn", + "-cf", + downloader.tarball_path, + "--", + ".", + ]) + ) + end + end + + # does not work yet, because (for unknown reasons), the tar command + # returns an error code when running under the test suite + # it 'creates a tarball matching the expected checksum' do + # cask = Hbc.load('svn-download-check-cask') + # downloader = Hbc::SubversionDownloadStrategy.new(cask) + # # special mocking required for tar to have something to work with + # def downloader.fetch_repo(target, url, revision = nil, ignore_externals=false) + # target.mkpath + # FileUtils.touch(target.join('empty_file.txt')) + # File.utime(1000,1000,target.join('empty_file.txt')) + # end + # expect(shutup { downloader.fetch }).to equal(downloader.tarball_path) + # d = Hbc::Download.new(cask) + # d.send(:_check_sums, downloader.tarball_path, cask.sums) + # end +end diff --git a/Library/Homebrew/cask/spec/cask/dsl/stanza_proxy_spec.rb b/Library/Homebrew/cask/spec/cask/dsl/stanza_proxy_spec.rb new file mode 100644 index 000000000..8516f0d38 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/dsl/stanza_proxy_spec.rb @@ -0,0 +1,32 @@ +describe Hbc::DSL::StanzaProxy do + let(:stanza_proxy) { + described_class.new(Array) { %i{foo bar cake} } + } + + subject { stanza_proxy } + it { is_expected.to be_a_proxy } + it { is_expected.to respond_to(:pop) } + its(:pop) { is_expected.to eq(:cake) } + its(:type) { is_expected.to eq(Array) } + its(:to_s) { is_expected.to eq("[:foo, :bar, :cake]") } + + describe "when initialized" do + let(:initializing) { + proc { |b| described_class.new(Array, &b) } + } + + it "does not evaluate the block" do + expect(&initializing).not_to yield_control + end + end + + describe "when receiving a message" do + let(:receiving_a_message) { + proc { |b| described_class.new(Array, &b).to_s } + } + + it "evaluates the block" do + expect(&receiving_a_message).to yield_with_no_args + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/dsl/version_spec.rb b/Library/Homebrew/cask/spec/cask/dsl/version_spec.rb new file mode 100644 index 000000000..04dcd06ca --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/dsl/version_spec.rb @@ -0,0 +1,227 @@ +require "spec_helper" + +describe Hbc::DSL::Version do + include ExpectationsHashHelper + + let(:version) { described_class.new(raw_version) } + + shared_examples "version equality" do + let(:raw_version) { "1.2.3" } + + context "when other is nil" do + let(:other) { nil } + it { should == false } + end + + context "when other is a String" do + context "when other == self.raw_version" do + let(:other) { "1.2.3" } + it { should == true } + end + + context "when other != self.raw_version" do + let(:other) { "1.2.3.4" } + it { should == false } + end + end + + context "when other is a #{described_class}" do + context "when other.raw_version == self.raw_version" do + let(:other) { described_class.new("1.2.3") } + it { should == true } + end + + context "when other.raw_version != self.raw_version" do + let(:other) { described_class.new("1.2.3.4") } + it { should == false } + end + end + end + + describe "#==" do + subject { version == other } + include_examples "version equality" + end + + describe "#eql?" do + subject { version.eql?(other) } + include_examples "version equality" + end + + shared_examples "version expectations hash" do |method, hash| + subject { version.send(method) } + include_examples "expectations hash", :raw_version, + { :latest => "latest", + "latest" => "latest", + "" => "", + nil => "" }.merge(hash) + end + + describe "#latest?" do + include_examples "version expectations hash", :latest?, + :latest => true, + "latest" => true, + "" => false, + nil => false, + "1.2.3" => false + end + + describe "string manipulation helpers" do + describe "#major" do + include_examples "version expectations hash", :major, + "1" => "1", + "1.2" => "1", + "1.2.3" => "1", + "1.2.3_4-5" => "1" + end + + describe "#minor" do + include_examples "version expectations hash", :minor, + "1" => "", + "1.2" => "2", + "1.2.3" => "2", + "1.2.3_4-5" => "2" + end + + describe "#patch" do + include_examples "version expectations hash", :patch, + "1" => "", + "1.2" => "", + "1.2.3" => "3", + "1.2.3_4-5" => "3" + end + + describe "#major_minor" do + include_examples "version expectations hash", :major_minor, + "1" => "1", + "1.2" => "1.2", + "1.2.3" => "1.2", + "1.2.3_4-5" => "1.2" + end + + describe "#major_minor_patch" do + include_examples "version expectations hash", :major_minor_patch, + "1" => "1", + "1.2" => "1.2", + "1.2.3" => "1.2.3", + "1.2.3_4-5" => "1.2.3" + end + + describe "#before_comma" do + include_examples "version expectations hash", :before_comma, + "1.2.3" => "1.2.3", + "1.2.3," => "1.2.3", + ",abc" => "", + "1.2.3,abc" => "1.2.3" + end + + describe "#after_comma" do + include_examples "version expectations hash", :after_comma, + "1.2.3" => "", + "1.2.3," => "", + ",abc" => "abc", + "1.2.3,abc" => "abc" + end + + describe "#before_colon" do + include_examples "version expectations hash", :before_colon, + "1.2.3" => "1.2.3", + "1.2.3:" => "1.2.3", + ":abc" => "", + "1.2.3:abc" => "1.2.3" + end + + describe "#after_colon" do + include_examples "version expectations hash", :after_colon, + "1.2.3" => "", + "1.2.3:" => "", + ":abc" => "abc", + "1.2.3:abc" => "abc" + end + + describe "#dots_to_hyphens" do + include_examples "version expectations hash", :dots_to_hyphens, + "1.2.3_4-5" => "1-2-3_4-5" + end + + describe "#dots_to_underscores" do + include_examples "version expectations hash", :dots_to_underscores, + "1.2.3_4-5" => "1_2_3_4-5" + end + + describe "#dots_to_slashes" do + include_examples "version expectations hash", :dots_to_slashes, + "1.2.3_4-5" => "1/2/3_4-5" + end + + describe "#hyphens_to_dots" do + include_examples "version expectations hash", :hyphens_to_dots, + "1.2.3_4-5" => "1.2.3_4.5" + end + + describe "#hyphens_to_underscores" do + include_examples "version expectations hash", :hyphens_to_underscores, + "1.2.3_4-5" => "1.2.3_4_5" + end + + describe "#hyphens_to_slashes" do + include_examples "version expectations hash", :hyphens_to_slashes, + "1.2.3_4-5" => "1.2.3_4/5" + end + + describe "#underscores_to_dots" do + include_examples "version expectations hash", :underscores_to_dots, + "1.2.3_4-5" => "1.2.3.4-5" + end + + describe "#underscores_to_hyphens" do + include_examples "version expectations hash", :underscores_to_hyphens, + "1.2.3_4-5" => "1.2.3-4-5" + end + + describe "#underscores_to_slashes" do + include_examples "version expectations hash", :underscores_to_slashes, + "1.2.3_4-5" => "1.2.3/4-5" + end + + describe "#slashes_to_dots" do + include_examples "version expectations hash", :slashes_to_dots, + "1.2.3/abc" => "1.2.3.abc" + end + + describe "#slashes_to_hyphens" do + include_examples "version expectations hash", :slashes_to_hyphens, + "1.2.3/abc" => "1.2.3-abc" + end + + describe "#slashes_to_underscores" do + include_examples "version expectations hash", :slashes_to_underscores, + "1.2.3/abc" => "1.2.3_abc" + end + + describe "#no_dots" do + include_examples "version expectations hash", :no_dots, + "1.2.3_4-5" => "123_4-5" + end + + describe "#no_hyphens" do + include_examples "version expectations hash", :no_hyphens, + "1.2.3_4-5" => "1.2.3_45" + end + + describe "#no_underscores" do + include_examples "version expectations hash", :no_underscores, + "1.2.3_4-5" => "1.2.34-5" + end + + describe "#no_slashes" do + include_examples "version expectations hash", :no_slashes, + "1.2.3/abc" => "1.2.3abc" + end + + describe "#no_dividers" do + include_examples "version expectations hash", :no_dividers, + "1.2.3_4-5" => "12345" + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/macos_spec.rb b/Library/Homebrew/cask/spec/cask/macos_spec.rb new file mode 100644 index 000000000..902c80d7a --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/macos_spec.rb @@ -0,0 +1,69 @@ +require "spec_helper" + +describe MacOS do + it "says '/' is undeletable" do + expect(MacOS).to be_undeletable( + "/" + ) + expect(MacOS).to be_undeletable( + "/." + ) + expect(MacOS).to be_undeletable( + "/usr/local/Library/Taps/../../../.." + ) + end + + it "says '/Applications' is undeletable" do + expect(MacOS).to be_undeletable( + "/Applications" + ) + expect(MacOS).to be_undeletable( + "/Applications/" + ) + expect(MacOS).to be_undeletable( + "/Applications/." + ) + expect(MacOS).to be_undeletable( + "/Applications/Mail.app/.." + ) + end + + it "says the home directory is undeletable" do + expect(MacOS).to be_undeletable( + Dir.home + ) + expect(MacOS).to be_undeletable( + "#{Dir.home}/" + ) + expect(MacOS).to be_undeletable( + "#{Dir.home}/Documents/.." + ) + end + + it "says the user library directory is undeletable" do + expect(MacOS).to be_undeletable( + "#{Dir.home}/Library" + ) + expect(MacOS).to be_undeletable( + "#{Dir.home}/Library/" + ) + expect(MacOS).to be_undeletable( + "#{Dir.home}/Library/." + ) + expect(MacOS).to be_undeletable( + "#{Dir.home}/Library/Preferences/.." + ) + end + + it "says '/Applications/.app' is deletable" do + expect(MacOS).not_to be_undeletable( + "/Applications/.app" + ) + end + + it "says '/Applications/SnakeOil Professional.app' is deletable" do + expect(MacOS).not_to be_undeletable( + "/Applications/SnakeOil Professional.app" + ) + end +end diff --git a/Library/Homebrew/cask/spec/cask/scopes_spec.rb b/Library/Homebrew/cask/spec/cask/scopes_spec.rb new file mode 100644 index 000000000..0e592c990 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/scopes_spec.rb @@ -0,0 +1,47 @@ +require "spec_helper" + +describe Hbc::Scopes do + describe "installed" do + let(:fake_caskroom) { Pathname(Dir.mktmpdir) } + + before do + allow(Hbc).to receive(:caskroom) { fake_caskroom } + end + + after do + fake_caskroom.rmtree + end + + it "returns a list installed Casks by loading Casks for all the dirs that exist in the caskroom" do + allow(Hbc).to receive(:load) { |token| "loaded-#{token}" } + + fake_caskroom.join("cask-bar").mkdir + fake_caskroom.join("cask-foo").mkdir + + installed_casks = Hbc.installed + + expect(Hbc).to have_received(:load).with("cask-bar") + expect(Hbc).to have_received(:load).with("cask-foo") + expect(installed_casks).to eq(%w[ + loaded-cask-bar + loaded-cask-foo + ]) + end + + it "optimizes performance by resolving to a fully qualified path before calling Hbc.load" do + fake_tapped_cask_dir = Pathname(Dir.mktmpdir).join("Casks") + absolute_path_to_cask = fake_tapped_cask_dir.join("some-cask.rb") + + allow(Hbc).to receive(:load) + allow(Hbc).to receive(:all_tapped_cask_dirs) { [fake_tapped_cask_dir] } + + fake_caskroom.join("some-cask").mkdir + fake_tapped_cask_dir.mkdir + FileUtils.touch(absolute_path_to_cask) + + Hbc.installed + + expect(Hbc).to have_received(:load).with(absolute_path_to_cask) + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/system_command_spec.rb b/Library/Homebrew/cask/spec/cask/system_command_spec.rb new file mode 100644 index 000000000..e85a1976a --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/system_command_spec.rb @@ -0,0 +1,141 @@ +require "spec_helper" + +describe Hbc::SystemCommand do + describe "when the exit code is 0" do + describe "its result" do + subject { described_class.run("/usr/bin/true") } + + it { is_expected.to be_a_success } + its(:exit_status) { is_expected.to eq(0) } + end + end + + describe "when the exit code is 1" do + let(:command) { "/usr/bin/false" } + + describe "and the command must succeed" do + it "throws an error" do + expect { + described_class.run!(command) + }.to raise_error(Hbc::CaskCommandFailedError) + end + end + + describe "and the command does not have to succeed" do + describe "its result" do + subject { described_class.run(command) } + + it { is_expected.not_to be_a_success } + its(:exit_status) { is_expected.to eq(1) } + end + end + end + + describe "given a pathname" do + let(:command) { "/bin/ls" } + let(:path) { Pathname(Dir.mktmpdir) } + + before do + FileUtils.touch(path.join("somefile")) + end + + describe "its result" do + subject { described_class.run(command, args: [path]) } + + it { is_expected.to be_a_success } + its(:stdout) { is_expected.to eq("somefile\n") } + end + end + + describe "with both STDOUT and STDERR output from upstream" do + let(:command) { "/bin/bash" } + let(:options) { + { args: [ + "-c", + "for i in $(seq 1 2 5); do echo $i; echo $(($i + 1)) >&2; done", + ] } + } + + shared_examples "it returns '1 2 3 4 5 6'" do + describe "its result" do + subject { shutup { described_class.run(command, options) } } + + it { is_expected.to be_a_success } + its(:stdout) { is_expected.to eq([1, 3, 5, nil].join("\n")) } + its(:stderr) { is_expected.to eq([2, 4, 6, nil].join("\n")) } + end + end + + describe "with default options" do + it "echoes only STDERR" do + expected = [2, 4, 6].map { |i| "==> #{i}\n" }.join("") + expect { + described_class.run(command, options) + }.to output(expected).to_stdout + end + + include_examples("it returns '1 2 3 4 5 6'") + end + + describe "with print_stdout" do + before do + options.merge!(print_stdout: true) + end + + it "echoes both STDOUT and STDERR" do + (1..6).each do |i| + expect { + described_class.run(command, options) + }.to output(%r{==> #{ i }}).to_stdout + end + end + + include_examples("it returns '1 2 3 4 5 6'") + end + + describe "without print_stderr" do + before do + options.merge!(print_stderr: false) + end + + it "echoes nothing" do + expect { + described_class.run(command, options) + }.to output("").to_stdout + end + + include_examples("it returns '1 2 3 4 5 6'") + end + + describe "with print_stdout but without print_stderr" do + before do + options.merge!(print_stdout: true, print_stderr: false) + end + + it "echoes only STDOUT" do + expected = [1, 3, 5].map { |i| "==> #{i}\n" }.join("") + expect { + described_class.run(command, options) + }.to output(expected).to_stdout + end + + include_examples("it returns '1 2 3 4 5 6'") + end + end + + describe "with a very long STDERR output" do + let(:command) { "/bin/bash" } + let(:options) { + { args: [ + "-c", + "for i in $(seq 1 2 100000); do echo $i; echo $(($i + 1)) >&2; done", + ] } + } + + it "returns without deadlocking" do + wait(10).for { + shutup { described_class.run(command, options) } + }.to be_a_success + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/underscore_supporting_uri_spec.rb b/Library/Homebrew/cask/spec/cask/underscore_supporting_uri_spec.rb new file mode 100644 index 000000000..a8756fde0 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/underscore_supporting_uri_spec.rb @@ -0,0 +1,16 @@ +require "spec_helper" + +describe Hbc::UnderscoreSupportingURI do + describe "parse" do + it "works like normal on normal URLs" do + uri = Hbc::UnderscoreSupportingURI.parse("http://example.com/TestCask.dmg") + expect(uri).to eq(URI("http://example.com/TestCask.dmg")) + end + + it "works just fine on URIs with underscores" do + uri = Hbc::UnderscoreSupportingURI.parse("http://dl_dir.qq.com/qqfile/qq/QQforMac/QQ_V3.0.0.dmg") + expect(uri.host).to include("_") + expect(uri.to_s).to eq("http://dl_dir.qq.com/qqfile/qq/QQforMac/QQ_V3.0.0.dmg") + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/verify/checksum_spec.rb b/Library/Homebrew/cask/spec/cask/verify/checksum_spec.rb new file mode 100644 index 000000000..7209556c9 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/verify/checksum_spec.rb @@ -0,0 +1,95 @@ +require "spec_helper" + +describe Hbc::Verify::Checksum do + include Sha256Helper + + let(:cask) { double("cask") } + let(:downloaded_path) { double("downloaded_path") } + let(:verification) { described_class.new(cask, downloaded_path) } + + before do + allow(cask).to receive(:sha256).and_return(sha256) + end + + around do |example| + shutup { example.run } + end + + describe ".me?" do + subject { described_class.me?(cask) } + + context "sha256 is :no_check" do + let(:sha256) { :no_check } + + it { should == false } + end + + context "sha256 is nil" do + let(:sha256) { nil } + + it { should == true } + end + + context "sha256 is empty" do + let(:sha256) { "" } + + it { should == true } + end + + context "sha256 is a valid shasum" do + let(:sha256) { random_sha256 } + + it { should == true } + end + end + + describe "#verify" do + subject { verification.verify } + + let(:computed) { random_sha256 } + + before do + allow(verification).to receive(:computed).and_return(computed) + end + + context "sha256 matches computed" do + let(:sha256) { computed } + + it "does not raise an error" do + expect { subject }.to_not raise_error + end + end + + context "sha256 is :no_check" do + let(:sha256) { :no_check } + + it "does not raise an error" do + expect { subject }.to_not raise_error + end + end + + context "sha256 does not match computed" do + let(:sha256) { random_sha256 } + + it "raises an error" do + expect { subject }.to raise_error(Hbc::CaskSha256MismatchError) + end + end + + context "sha256 is nil" do + let(:sha256) { nil } + + it "raises an error" do + expect { subject }.to raise_error(Hbc::CaskSha256MissingError) + end + end + + context "sha256 is empty" do + let(:sha256) { "" } + + it "raises an error" do + expect { subject }.to raise_error(Hbc::CaskSha256MissingError) + end + end + end +end diff --git a/Library/Homebrew/cask/spec/cask/verify_spec.rb b/Library/Homebrew/cask/spec/cask/verify_spec.rb new file mode 100644 index 000000000..7df77fea1 --- /dev/null +++ b/Library/Homebrew/cask/spec/cask/verify_spec.rb @@ -0,0 +1,65 @@ +require "spec_helper" + +describe Hbc::Verify do + let(:cask) { double("cask") } + + let(:verification_classes) { + [ + applicable_verification_class, + inapplicable_verification_class, + ] + } + + let(:applicable_verification_class) { + double("applicable_verification_class", me?: true) + } + + let(:inapplicable_verification_class) { + double("inapplicable_verification_class", me?: false) + } + + before do + allow(described_class).to receive(:verifications) + .and_return(verification_classes) + end + + describe ".for_cask" do + subject { described_class.for_cask(cask) } + + it "checks applicability of each verification" do + verification_classes.each do |verify_class| + expect(verify_class).to receive(:me?).with(cask) + end + subject + end + + it "includes applicable verifications" do + expect(subject).to include(applicable_verification_class) + end + + it "excludes inapplicable verifications" do + expect(subject).to_not include(inapplicable_verification_class) + end + end + + describe ".all" do + let(:downloaded_path) { double("downloaded_path") } + let(:applicable_verification) { double("applicable_verification") } + let(:inapplicable_verification) { double("inapplicable_verification") } + + subject { described_class.all(cask, downloaded_path) } + + before do + allow(applicable_verification_class).to receive(:new) + .and_return(applicable_verification) + allow(inapplicable_verification_class).to receive(:new) + .and_return(inapplicable_verification) + end + + it "runs only applicable verifications" do + expect(applicable_verification).to receive(:verify) + expect(inapplicable_verification).to_not receive(:verify) + subject + end + end +end diff --git a/Library/Homebrew/cask/spec/spec_helper.rb b/Library/Homebrew/cask/spec/spec_helper.rb new file mode 100644 index 000000000..e86e20027 --- /dev/null +++ b/Library/Homebrew/cask/spec/spec_helper.rb @@ -0,0 +1,67 @@ +require "pathname" +require "rspec/its" +require "rspec/wait" + +if ENV["COVERAGE"] + require "coveralls" + Coveralls.wear_merged! +end + +project_root = Pathname.new(File.expand_path("../..", __FILE__)) + +# add Homebrew to load path +$LOAD_PATH.unshift(File.expand_path("#{ENV['HOMEBREW_REPOSITORY']}/Library/Homebrew")) + +require "global" +require "extend/pathname" + +# add Homebrew-Cask to load path +$LOAD_PATH.push(project_root.join("lib").to_s) + +# force some environment variables +ENV["HOMEBREW_NO_EMOJI"] = "1" +ENV["HOMEBREW_CASK_OPTS"] = nil + +Dir["#{project_root}/spec/support/*.rb"].each(&method(:require)) + +# from Homebrew. Provides expects method. +require "mocha/api" + +require "hbc" + +class Hbc::TestCask < Hbc::Cask; end + +TEST_TMPDIR = Dir.mktmpdir("homebrew_cask_tests") +at_exit do + FileUtils.remove_entry(TEST_TMPDIR) +end + +# override Homebrew locations +Hbc.homebrew_prefix = Pathname.new(TEST_TMPDIR).join("prefix") +Hbc.homebrew_repository = Hbc.homebrew_prefix +Hbc.binarydir = Hbc.homebrew_prefix.join("binarydir", "bin") +Hbc.appdir = Pathname.new(TEST_TMPDIR).join("appdir") + +# Override Tap::TAP_DIRECTORY to use our test Tap directory. +class Tap + send(:remove_const, :TAP_DIRECTORY) + TAP_DIRECTORY = Hbc.homebrew_repository.join("Library", "Taps") +end + +Hbc.default_tap = Tap.fetch("caskroom", "speccasks") +Hbc.default_tap.path.dirname.mkpath + +# also jack in some test Casks +FileUtils.ln_s project_root.join("spec", "support"), Tap::TAP_DIRECTORY.join("caskroom", "homebrew-speccasks") + +# create cache directory +Hbc.homebrew_cache = Pathname.new(TEST_TMPDIR).join("cache") +Hbc.cache.mkpath + +# our own testy caskroom +Hbc.caskroom = Hbc.homebrew_prefix.join("TestCaskroom") + +RSpec.configure do |config| + config.order = :random + config.include ShutupHelper +end diff --git a/Library/Homebrew/cask/spec/support/Casks/.rubocop.yml b/Library/Homebrew/cask/spec/support/Casks/.rubocop.yml new file mode 120000 index 000000000..ee5c2b948 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/.rubocop.yml @@ -0,0 +1 @@ +../../../Casks/.rubocop.yml
\ No newline at end of file diff --git a/Library/Homebrew/cask/spec/support/Casks/appcast-checkpoint-sha256-for-empty-string.rb b/Library/Homebrew/cask/spec/support/Casks/appcast-checkpoint-sha256-for-empty-string.rb new file mode 100644 index 000000000..f40f244f2 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/appcast-checkpoint-sha256-for-empty-string.rb @@ -0,0 +1,4 @@ +test_cask 'appcast-checkpoint-sha256-for-empty-string' do + appcast 'http://localhost/appcast.xml', + checkpoint: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/appcast-invalid-checkpoint.rb b/Library/Homebrew/cask/spec/support/Casks/appcast-invalid-checkpoint.rb new file mode 100644 index 000000000..e182c2389 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/appcast-invalid-checkpoint.rb @@ -0,0 +1,4 @@ +test_cask 'appcast-invalid-checkpoint' do + appcast 'http://localhost/appcast.xml', + checkpoint: 'not a valid shasum' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/appcast-missing-checkpoint.rb b/Library/Homebrew/cask/spec/support/Casks/appcast-missing-checkpoint.rb new file mode 100644 index 000000000..5ab2c0665 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/appcast-missing-checkpoint.rb @@ -0,0 +1,3 @@ +test_cask 'appcast-missing-checkpoint' do + appcast 'http://localhost/appcast.xml' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/appcast-valid-checkpoint.rb b/Library/Homebrew/cask/spec/support/Casks/appcast-valid-checkpoint.rb new file mode 100644 index 000000000..96d7edbba --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/appcast-valid-checkpoint.rb @@ -0,0 +1,4 @@ +test_cask 'appcast-valid-checkpoint' do + appcast 'http://localhost/appcast.xml', + checkpoint: 'd5b2dfbef7ea28c25f7a77cd7fa14d013d82b626db1d82e00e25822464ba19e2' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/booby-trap.rb b/Library/Homebrew/cask/spec/support/Casks/booby-trap.rb new file mode 100644 index 000000000..21bd97952 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/booby-trap.rb @@ -0,0 +1,8 @@ +cask 'booby-trap' do + version '0.0.7' + + url do + # to be lazily evaluated + fail 'Boom' + end +end diff --git a/Library/Homebrew/cask/spec/support/Casks/generic-artifact-absolute-target.rb b/Library/Homebrew/cask/spec/support/Casks/generic-artifact-absolute-target.rb new file mode 100644 index 000000000..475fb055f --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/generic-artifact-absolute-target.rb @@ -0,0 +1,3 @@ +test_cask 'generic-artifact-absolute-target' do + artifact 'Caffeine.app', target: "#{Hbc.appdir}/Caffeine.app" +end diff --git a/Library/Homebrew/cask/spec/support/Casks/generic-artifact-no-target.rb b/Library/Homebrew/cask/spec/support/Casks/generic-artifact-no-target.rb new file mode 100644 index 000000000..f7657dbb5 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/generic-artifact-no-target.rb @@ -0,0 +1,3 @@ +test_cask 'generic-artifact-no-target' do + artifact 'Caffeine.app' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/generic-artifact-relative-target.rb b/Library/Homebrew/cask/spec/support/Casks/generic-artifact-relative-target.rb new file mode 100644 index 000000000..f97441751 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/generic-artifact-relative-target.rb @@ -0,0 +1,3 @@ +test_cask 'generic-artifact-relative-target' do + artifact 'Caffeine.app', target: 'Caffeine.app' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/invalid-sha256.rb b/Library/Homebrew/cask/spec/support/Casks/invalid-sha256.rb new file mode 100644 index 000000000..aac00f495 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/invalid-sha256.rb @@ -0,0 +1,4 @@ +test_cask 'invalid-sha256' do + version '1.2.3' + sha256 'not a valid shasum' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/missing-homepage.rb b/Library/Homebrew/cask/spec/support/Casks/missing-homepage.rb new file mode 100644 index 000000000..ff924541d --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/missing-homepage.rb @@ -0,0 +1,5 @@ +test_cask 'missing-homepage' do + version '1.2.3' + + url 'http://localhost/something.dmg' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/missing-license.rb b/Library/Homebrew/cask/spec/support/Casks/missing-license.rb new file mode 100644 index 000000000..30f3791c7 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/missing-license.rb @@ -0,0 +1,5 @@ +test_cask 'missing-license' do + version '1.2.3' + + url 'http://localhost/something.dmg' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/missing-name.rb b/Library/Homebrew/cask/spec/support/Casks/missing-name.rb new file mode 100644 index 000000000..a5265b379 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/missing-name.rb @@ -0,0 +1,5 @@ +test_cask 'missing-name' do + version '1.2.3' + + url 'http://localhost/something.dmg' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/missing-sha256.rb b/Library/Homebrew/cask/spec/support/Casks/missing-sha256.rb new file mode 100644 index 000000000..7f8027907 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/missing-sha256.rb @@ -0,0 +1,5 @@ +test_cask 'missing-sha256' do + version '1.2.3' + + url 'http://localhost/something.dmg' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/missing-url.rb b/Library/Homebrew/cask/spec/support/Casks/missing-url.rb new file mode 100644 index 000000000..1b3e76b9c --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/missing-url.rb @@ -0,0 +1,5 @@ +test_cask 'missing-url' do + version '1.2.3' + + homepage 'http://example.com' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/missing-version.rb b/Library/Homebrew/cask/spec/support/Casks/missing-version.rb new file mode 100644 index 000000000..da2160bce --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/missing-version.rb @@ -0,0 +1,4 @@ +test_cask 'missing-version' do + url 'http://localhost/something.dmg' + homepage 'http://example.com' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/osdn-correct-url-format.rb b/Library/Homebrew/cask/spec/support/Casks/osdn-correct-url-format.rb new file mode 100644 index 000000000..da6ff0fcd --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/osdn-correct-url-format.rb @@ -0,0 +1,6 @@ +test_cask 'osdn-correct-url-format' do + version '1.2.3' + + url 'http://user.dl.osdn.jp/something/id/Something-1.2.3.dmg' + homepage 'http://osdn.jp/projects/something/' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/osdn-incorrect-url-format.rb b/Library/Homebrew/cask/spec/support/Casks/osdn-incorrect-url-format.rb new file mode 100644 index 000000000..8400159a1 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/osdn-incorrect-url-format.rb @@ -0,0 +1,6 @@ +test_cask 'osdn-incorrect-url-format' do + version '1.2.3' + + url 'http://osdn.jp/projects/something/files/Something-1.2.3.dmg/download' + homepage 'http://osdn.jp/projects/something/' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/sha256-for-empty-string.rb b/Library/Homebrew/cask/spec/support/Casks/sha256-for-empty-string.rb new file mode 100644 index 000000000..b97764071 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/sha256-for-empty-string.rb @@ -0,0 +1,4 @@ +test_cask 'sha256-for-empty-string' do + version '1.2.3' + sha256 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/sourceforge-correct-url-format.rb b/Library/Homebrew/cask/spec/support/Casks/sourceforge-correct-url-format.rb new file mode 100644 index 000000000..41ef73500 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/sourceforge-correct-url-format.rb @@ -0,0 +1,6 @@ +test_cask 'sourceforge-correct-url-format' do + version '1.2.3' + + url 'https://downloads.sourceforge.net/something/Something-1.2.3.dmg' + homepage 'https://sourceforge.net/projects/something/' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/sourceforge-incorrect-url-format.rb b/Library/Homebrew/cask/spec/support/Casks/sourceforge-incorrect-url-format.rb new file mode 100644 index 000000000..27b5490b7 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/sourceforge-incorrect-url-format.rb @@ -0,0 +1,6 @@ +test_cask 'sourceforge-incorrect-url-format' do + version '1.2.3' + + url 'http://sourceforge.net/projects/something/files/Something-1.2.3.dmg/download' + homepage 'http://sourceforge.net/projects/something/' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/sourceforge-version-latest-correct-url-format.rb b/Library/Homebrew/cask/spec/support/Casks/sourceforge-version-latest-correct-url-format.rb new file mode 100644 index 000000000..d9546c5e1 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/sourceforge-version-latest-correct-url-format.rb @@ -0,0 +1,6 @@ +test_cask 'sourceforge-version-latest-correct-url-format' do + version :latest + + url 'https://sourceforge.net/projects/something/files/latest/download' + homepage 'https://sourceforge.net/projects/something/' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/version-latest-string.rb b/Library/Homebrew/cask/spec/support/Casks/version-latest-string.rb new file mode 100644 index 000000000..3b4723f45 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/version-latest-string.rb @@ -0,0 +1,4 @@ +test_cask 'version-latest-string' do + version 'latest' + sha256 :no_check +end diff --git a/Library/Homebrew/cask/spec/support/Casks/version-latest-with-checksum.rb b/Library/Homebrew/cask/spec/support/Casks/version-latest-with-checksum.rb new file mode 100644 index 000000000..8dbca1a69 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/version-latest-with-checksum.rb @@ -0,0 +1,4 @@ +test_cask 'version-latest-with-checksum' do + version :latest + sha256 '9203c30951f9aab41ac294bbeb1dcef7bed401ff0b353dcb34d68af32ea51853' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/with-binary.rb b/Library/Homebrew/cask/spec/support/Casks/with-binary.rb new file mode 100644 index 000000000..a06787125 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/with-binary.rb @@ -0,0 +1,10 @@ +test_cask 'with-binary' do + version '1.2.3' + sha256 'd5b2dfbef7ea28c25f7a77cd7fa14d013d82b626db1d82e00e25822464ba19e2' + + url FileHelper.local_binary_url('AppWithBinary.zip') + homepage 'http://example.com/with-binary' + + app 'App.app' + binary 'binary' +end diff --git a/Library/Homebrew/cask/spec/support/Casks/with-embedded-binary.rb b/Library/Homebrew/cask/spec/support/Casks/with-embedded-binary.rb new file mode 100644 index 000000000..796f28c78 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/Casks/with-embedded-binary.rb @@ -0,0 +1,10 @@ +test_cask 'with-embedded-binary' do + version '1.2.3' + sha256 'fe052d3e77d92676775fd916ddb8942e72a565b844ea7f6d055474c99bb4e47b' + + url FileHelper.local_binary_url('AppWithEmbeddedBinary.zip') + homepage 'http://example.com/with-binary' + + app 'App.app' + binary "#{appdir}/App.app/Contents/MacOS/App/binary" +end diff --git a/Library/Homebrew/cask/spec/support/audit_matchers.rb b/Library/Homebrew/cask/spec/support/audit_matchers.rb new file mode 100644 index 000000000..fc1e0e876 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/audit_matchers.rb @@ -0,0 +1,39 @@ +module AuditMatchers + extend RSpec::Matchers::DSL + + matcher :pass do + match do |audit| + !audit.errors? && !audit.warnings? + end + end + + matcher :fail do + match(&:errors?) + end + + matcher :warn do + match do |audit| + audit.warnings? && !audit.errors? + end + end + + matcher :fail_with do |error_msg| + match do |audit| + include_msg?(audit.errors, error_msg) + end + end + + matcher :warn_with do |warning_msg| + match do |audit| + include_msg?(audit.warnings, warning_msg) + end + end + + def include_msg?(messages, msg) + if msg.is_a?(Regexp) + Array(messages).any? { |m| m =~ msg } + else + Array(messages).include?(msg) + end + end +end diff --git a/Library/Homebrew/cask/spec/support/binaries/AppWithBinary.zip b/Library/Homebrew/cask/spec/support/binaries/AppWithBinary.zip Binary files differnew file mode 100644 index 000000000..4a5b318ba --- /dev/null +++ b/Library/Homebrew/cask/spec/support/binaries/AppWithBinary.zip diff --git a/Library/Homebrew/cask/spec/support/binaries/AppWithEmbeddedBinary.zip b/Library/Homebrew/cask/spec/support/binaries/AppWithEmbeddedBinary.zip Binary files differnew file mode 100644 index 000000000..7c708038c --- /dev/null +++ b/Library/Homebrew/cask/spec/support/binaries/AppWithEmbeddedBinary.zip diff --git a/Library/Homebrew/cask/spec/support/env_helper.rb b/Library/Homebrew/cask/spec/support/env_helper.rb new file mode 100644 index 000000000..0a302ef45 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/env_helper.rb @@ -0,0 +1,16 @@ +module EnvHelper + class << self + def with_env_var(key, val) + was_defined = ENV.key? "key" + old_value = ENV["key"] + ENV[key] = val + yield + ensure + if was_defined + ENV[key] = old_value + else + ENV.delete(key) + end + end + end +end diff --git a/Library/Homebrew/cask/spec/support/expectations_hash_helper.rb b/Library/Homebrew/cask/spec/support/expectations_hash_helper.rb new file mode 100644 index 000000000..8d386767d --- /dev/null +++ b/Library/Homebrew/cask/spec/support/expectations_hash_helper.rb @@ -0,0 +1,10 @@ +module ExpectationsHashHelper + shared_examples "expectations hash" do |input_name, expectations| + expectations.each do |input_value, expected_output| + context "when #{input_name} is #{input_value.inspect}" do + let(input_name.to_sym) { input_value } + it { should == expected_output } + end + end + end +end diff --git a/Library/Homebrew/cask/spec/support/file_helper.rb b/Library/Homebrew/cask/spec/support/file_helper.rb new file mode 100644 index 000000000..8eb78f759 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/file_helper.rb @@ -0,0 +1,16 @@ +module FileHelper + class << self + def local_binary_path(name) + File.expand_path(File.join(File.dirname(__FILE__), "binaries", name)) + end + + def local_binary_url(name) + "file://" + local_binary_path(name) + end + + def valid_alias?(candidate) + return false unless candidate.symlink? + candidate.readlink.exist? + end + end +end diff --git a/Library/Homebrew/cask/spec/support/install_helper.rb b/Library/Homebrew/cask/spec/support/install_helper.rb new file mode 100644 index 000000000..c8023c66b --- /dev/null +++ b/Library/Homebrew/cask/spec/support/install_helper.rb @@ -0,0 +1,10 @@ +module InstallHelper + class << self + def install_without_artifacts(cask) + Hbc::Installer.new(cask).tap do |i| + i.download + i.extract_primary_container + end + end + end +end diff --git a/Library/Homebrew/cask/spec/support/kernel_at_exit_hacks.rb b/Library/Homebrew/cask/spec/support/kernel_at_exit_hacks.rb new file mode 100644 index 000000000..b5c84869b --- /dev/null +++ b/Library/Homebrew/cask/spec/support/kernel_at_exit_hacks.rb @@ -0,0 +1,13 @@ +module Kernel + alias real_at_exit at_exit + + def at_exit(&block) + real_at_exit(&block) unless ENV["DISABLE_AT_EXIT"] + end + + def with_disabled_at_exit + ENV["DISABLE_AT_EXIT"] = "1" + yield + ENV.delete("DISABLE_AT_EXIT") + end +end diff --git a/Library/Homebrew/cask/spec/support/sha256_helper.rb b/Library/Homebrew/cask/spec/support/sha256_helper.rb new file mode 100644 index 000000000..14f2a2519 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/sha256_helper.rb @@ -0,0 +1,8 @@ +require"digest" + +module Sha256Helper + def random_sha256 + seed = "--#{rand(10_000)}--#{Time.now}--" + Digest::SHA2.hexdigest(seed) + end +end diff --git a/Library/Homebrew/cask/spec/support/shutup_helper.rb b/Library/Homebrew/cask/spec/support/shutup_helper.rb new file mode 100644 index 000000000..98dde01a6 --- /dev/null +++ b/Library/Homebrew/cask/spec/support/shutup_helper.rb @@ -0,0 +1,18 @@ +module ShutupHelper + def shutup + if ENV.key?("VERBOSE_TESTS") + yield + else + begin + tmperr = $stderr.clone + tmpout = $stdout.clone + $stderr.reopen "/dev/null", "w" + $stdout.reopen "/dev/null", "w" + yield + ensure + $stderr.reopen tmperr + $stdout.reopen tmpout + end + end + end +end |
