Add types for SystemCommand.

This commit is contained in:
Markus Reiter 2020-11-23 02:05:50 +01:00
parent d96b85e512
commit d5b184d17a
14 changed files with 132 additions and 156 deletions

View File

@ -34,15 +34,15 @@ module Context
end end
def debug? def debug?
@debug @debug == true
end end
def quiet? def quiet?
@quiet @quiet == true
end end
def verbose? def verbose?
@verbose @verbose == true
end end
end end

View File

@ -3,7 +3,8 @@
source = ARGV[5] source = ARGV[5]
/\busing +Magic\b/.match(source) do |_| case source[/\Ausing\s+(.*)\Z/, 1]
when "Magic"
puts <<-RUBY puts <<-RUBY
# typed: strict # typed: strict
@ -18,4 +19,13 @@ source = ARGV[5]
def zipinfo; end def zipinfo; end
end end
RUBY RUBY
when "HashValidator"
puts <<-RUBY
# typed: strict
class ::Hash
sig { params(valid_keys: T.untyped).void }
def assert_valid_keys!(*valid_keys); end
end
RUBY
end end

View File

@ -28559,6 +28559,7 @@ class Socket
IPV6_PATHMTU = ::T.let(nil, ::T.untyped) IPV6_PATHMTU = ::T.let(nil, ::T.untyped)
IPV6_RECVPATHMTU = ::T.let(nil, ::T.untyped) IPV6_RECVPATHMTU = ::T.let(nil, ::T.untyped)
IPV6_USE_MIN_MTU = ::T.let(nil, ::T.untyped) IPV6_USE_MIN_MTU = ::T.let(nil, ::T.untyped)
IP_DONTFRAG = ::T.let(nil, ::T.untyped)
IP_PORTRANGE = ::T.let(nil, ::T.untyped) IP_PORTRANGE = ::T.let(nil, ::T.untyped)
IP_RECVDSTADDR = ::T.let(nil, ::T.untyped) IP_RECVDSTADDR = ::T.let(nil, ::T.untyped)
IP_RECVIF = ::T.let(nil, ::T.untyped) IP_RECVIF = ::T.let(nil, ::T.untyped)
@ -28650,6 +28651,7 @@ module Socket::Constants
IPV6_PATHMTU = ::T.let(nil, ::T.untyped) IPV6_PATHMTU = ::T.let(nil, ::T.untyped)
IPV6_RECVPATHMTU = ::T.let(nil, ::T.untyped) IPV6_RECVPATHMTU = ::T.let(nil, ::T.untyped)
IPV6_USE_MIN_MTU = ::T.let(nil, ::T.untyped) IPV6_USE_MIN_MTU = ::T.let(nil, ::T.untyped)
IP_DONTFRAG = ::T.let(nil, ::T.untyped)
IP_PORTRANGE = ::T.let(nil, ::T.untyped) IP_PORTRANGE = ::T.let(nil, ::T.untyped)
IP_RECVDSTADDR = ::T.let(nil, ::T.untyped) IP_RECVDSTADDR = ::T.let(nil, ::T.untyped)
IP_RECVIF = ::T.let(nil, ::T.untyped) IP_RECVIF = ::T.let(nil, ::T.untyped)
@ -29210,6 +29212,11 @@ end
class SynchronizedDelegator class SynchronizedDelegator
end end
class SystemCommand::Result
extend ::T::Private::Methods::MethodHooks
extend ::T::Private::Methods::SingletonMethodHooks
end
class SystemCommand class SystemCommand
extend ::T::Private::Methods::MethodHooks extend ::T::Private::Methods::MethodHooks
extend ::T::Private::Methods::SingletonMethodHooks extend ::T::Private::Methods::SingletonMethodHooks

View File

@ -2,6 +2,6 @@ ruby_extra_args:
- --disable-gems - --disable-gems
triggers: triggers:
using: sorbet/plugins/unpack_strategy_magic.rb using: sorbet/plugins/using.rb
attr_predicate: sorbet/plugins/attr_predicate.rb attr_predicate: sorbet/plugins/attr_predicate.rb
delegate: sorbet/plugins/delegate.rb delegate: sorbet/plugins/delegate.rb

View File

@ -9,7 +9,6 @@ require "shellwords"
require "extend/io" require "extend/io"
require "extend/predicable" require "extend/predicable"
require "extend/hash_validator" require "extend/hash_validator"
using HashValidator
# Class for running sub-processes and capturing their output and exit status. # Class for running sub-processes and capturing their output and exit status.
# #
@ -17,14 +16,16 @@ using HashValidator
class SystemCommand class SystemCommand
extend T::Sig extend T::Sig
using HashValidator
# Helper functions for calling {SystemCommand.run}. # Helper functions for calling {SystemCommand.run}.
module Mixin module Mixin
def system_command(*args) def system_command(*args)
SystemCommand.run(*args) T.unsafe(SystemCommand).run(*args)
end end
def system_command!(*args) def system_command!(*args)
SystemCommand.run!(*args) T.unsafe(SystemCommand).run!(*args)
end end
end end
@ -34,11 +35,11 @@ class SystemCommand
attr_reader :pid attr_reader :pid
def self.run(executable, **options) def self.run(executable, **options)
new(executable, **options).run! T.unsafe(self).new(executable, **options).run!
end end
def self.run!(command, **options) def self.run!(command, **options)
run(command, **options, must_succeed: true) T.unsafe(self).run(command, **options, must_succeed: true)
end end
sig { returns(SystemCommand::Result) } sig { returns(SystemCommand::Result) }
@ -63,45 +64,61 @@ class SystemCommand
result result
end end
sig do
params(
executable: T.any(String, Pathname),
args: T::Array[T.any(String, Integer, Float, URI::Generic)],
sudo: T::Boolean,
env: T::Hash[String, String],
input: T.any(String, T::Array[String]),
must_succeed: T::Boolean,
print_stdout: T::Boolean,
print_stderr: T::Boolean,
verbose: T::Boolean,
secrets: T::Array[String],
chdir: T.any(String, Pathname),
).void
end
def initialize(executable, args: [], sudo: false, env: {}, input: [], must_succeed: false, def initialize(executable, args: [], sudo: false, env: {}, input: [], must_succeed: false,
print_stdout: false, print_stderr: true, verbose: false, secrets: [], **options) print_stdout: false, print_stderr: true, verbose: false, secrets: [], chdir: T.unsafe(nil))
require "extend/ENV" require "extend/ENV"
@executable = executable @executable = executable
@args = args @args = args
@sudo = sudo @sudo = sudo
@input = Array(input) env.each_key do |name|
@print_stdout = print_stdout
@print_stderr = print_stderr
@verbose = verbose
@secrets = (Array(secrets) + ENV.sensitive_environment.values).uniq
@must_succeed = must_succeed
options.assert_valid_keys!(:chdir)
@options = options
@env = env
@env.each_key do |name|
next if /^[\w&&\D]\w*$/.match?(name) next if /^[\w&&\D]\w*$/.match?(name)
raise ArgumentError, "Invalid variable name: '#{name}'" raise ArgumentError, "Invalid variable name: '#{name}'"
end end
@env = env
@input = Array(input)
@must_succeed = must_succeed
@print_stdout = print_stdout
@print_stderr = print_stderr
@verbose = verbose
@secrets = (Array(secrets) + ENV.sensitive_environment.values).uniq
@chdir = chdir
end end
sig { returns(T::Array[String]) }
def command def command
[*sudo_prefix, *env_args, executable.to_s, *expanded_args] [*sudo_prefix, *env_args, executable.to_s, *expanded_args]
end end
private private
attr_reader :executable, :args, :input, :options, :env attr_reader :executable, :args, :input, :chdir, :env
attr_predicate :sudo?, :print_stdout?, :print_stderr?, :must_succeed? attr_predicate :sudo?, :print_stdout?, :print_stderr?, :must_succeed?
sig { returns(T::Boolean) }
def verbose? def verbose?
return super if @verbose.nil? return super if @verbose.nil?
@verbose @verbose
end end
sig { returns(T::Array[String]) }
def env_args def env_args
set_variables = env.compact.map do |name, value| set_variables = env.compact.map do |name, value|
sanitized_name = Shellwords.escape(name) sanitized_name = Shellwords.escape(name)
@ -114,6 +131,7 @@ class SystemCommand
["/usr/bin/env", *set_variables] ["/usr/bin/env", *set_variables]
end end
sig { returns(T::Array[String]) }
def sudo_prefix def sudo_prefix
return [] unless sudo? return [] unless sudo?
@ -121,11 +139,12 @@ class SystemCommand
["/usr/bin/sudo", *askpass_flags, "-E", "--"] ["/usr/bin/sudo", *askpass_flags, "-E", "--"]
end end
sig { returns(T::Array[String]) }
def expanded_args def expanded_args
@expanded_args ||= args.map do |arg| @expanded_args ||= args.map do |arg|
if arg.respond_to?(:to_path) if arg.respond_to?(:to_path)
File.absolute_path(arg) File.absolute_path(arg)
elsif arg.is_a?(Integer) || arg.is_a?(Float) || arg.is_a?(URI) elsif arg.is_a?(Integer) || arg.is_a?(Float) || arg.is_a?(URI::Generic)
arg.to_s arg.to_s
else else
arg.to_str arg.to_str
@ -137,7 +156,7 @@ class SystemCommand
executable, *args = command executable, *args = command
raw_stdin, raw_stdout, raw_stderr, raw_wait_thr = raw_stdin, raw_stdout, raw_stderr, raw_wait_thr =
Open3.popen3(env, [executable, executable], *args, **options) T.unsafe(Open3).popen3(env, [executable, executable], *args, **{ chdir: chdir }.compact)
@pid = raw_wait_thr.pid @pid = raw_wait_thr.pid
write_input_to(raw_stdin) write_input_to(raw_stdin)
@ -158,7 +177,7 @@ class SystemCommand
loop do loop do
readable_sources, = IO.select(sources) readable_sources, = IO.select(sources)
readable_sources = readable_sources.reject(&:eof?) readable_sources = T.must(readable_sources).reject(&:eof?)
break if readable_sources.empty? break if readable_sources.empty?
@ -176,10 +195,20 @@ class SystemCommand
# Result containing the output and exit status of a finished sub-process. # Result containing the output and exit status of a finished sub-process.
class Result class Result
extend T::Sig
include Context include Context
attr_accessor :command, :status, :exit_status attr_accessor :command, :status, :exit_status
sig do
params(
command: T::Array[String],
output: T::Array[[Symbol, String]],
status: Process::Status,
secrets: T::Array[String],
).void
end
def initialize(command, output, status, secrets:) def initialize(command, output, status, secrets:)
@command = command @command = command
@output = output @output = output
@ -188,57 +217,65 @@ class SystemCommand
@secrets = secrets @secrets = secrets
end end
sig { void }
def assert_success! def assert_success!
return if @status.success? return if @status.success?
raise ErrorDuringExecution.new(command, status: @status, output: @output, secrets: @secrets) raise ErrorDuringExecution.new(command, status: @status, output: @output, secrets: @secrets)
end end
sig { returns(String) }
def stdout def stdout
@stdout ||= @output.select { |type,| type == :stdout } @stdout ||= @output.select { |type,| type == :stdout }
.map { |_, line| line } .map { |_, line| line }
.join .join
end end
sig { returns(String) }
def stderr def stderr
@stderr ||= @output.select { |type,| type == :stderr } @stderr ||= @output.select { |type,| type == :stderr }
.map { |_, line| line } .map { |_, line| line }
.join .join
end end
sig { returns(String) }
def merged_output def merged_output
@merged_output ||= @output.map { |_, line| line } @merged_output ||= @output.map { |_, line| line }
.join .join
end end
sig { returns(T::Boolean) }
def success? def success?
return false if @exit_status.nil? return false if @exit_status.nil?
@exit_status.zero? @exit_status.zero?
end end
sig { returns([String, String, Process::Status]) }
def to_ary def to_ary
[stdout, stderr, status] [stdout, stderr, status]
end end
sig { returns(T.nilable(T.any(Array, Hash))) }
def plist def plist
@plist ||= begin @plist ||= begin
output = stdout output = stdout
if /\A(?<garbage>.*?)<\?\s*xml/m =~ output output = output.sub(/\A(.*?)(\s*<\?\s*xml)/m) do
output = output.sub(/\A#{Regexp.escape(garbage)}/m, "") warn_plist_garbage(T.must(Regexp.last_match(1)))
warn_plist_garbage(garbage) Regexp.last_match(2)
end end
if %r{<\s*/\s*plist\s*>(?<garbage>.*?)\Z}m =~ output output = output.sub(%r{(<\s*/\s*plist\s*>\s*)(.*?)\Z}m) do
output = output.sub(/#{Regexp.escape(garbage)}\Z/, "") warn_plist_garbage(T.must(Regexp.last_match(2)))
warn_plist_garbage(garbage) Regexp.last_match(1)
end end
Plist.parse_xml(output) Plist.parse_xml(output)
end end
end end
sig { params(garbage: String).void }
def warn_plist_garbage(garbage) def warn_plist_garbage(garbage)
return unless verbose? return unless verbose?
return unless garbage.match?(/\S/) return unless garbage.match?(/\S/)

View File

@ -8,7 +8,7 @@ shared_examples "#uninstall_phase or #zap_phase" do
let(:artifact_dsl_key) { described_class.dsl_key } let(:artifact_dsl_key) { described_class.dsl_key }
let(:artifact) { cask.artifacts.find { |a| a.is_a?(described_class) } } let(:artifact) { cask.artifacts.find { |a| a.is_a?(described_class) } }
let(:fake_system_command) { FakeSystemCommand } let(:fake_system_command) { class_double(SystemCommand) }
context "using :launchctl" do context "using :launchctl" do
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-launchctl")) } let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-launchctl")) }
@ -31,41 +31,37 @@ shared_examples "#uninstall_phase or #zap_phase" do
end end
it "works when job is owned by user" do it "works when job is owned by user" do
FakeSystemCommand.stubs_command( allow(fake_system_command).to receive(:run)
launchctl_list_cmd, .with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: false)
service_info, .and_return(instance_double(SystemCommand::Result, stdout: service_info))
) allow(fake_system_command).to receive(:run)
.with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: true)
.and_return(instance_double(SystemCommand::Result, stdout: unknown_response))
FakeSystemCommand.stubs_command( expect(fake_system_command).to receive(:run!)
sudo(launchctl_list_cmd), .with("/bin/launchctl", args: ["remove", "my.fancy.package.service"], sudo: false)
unknown_response, .and_return(instance_double(SystemCommand::Result))
)
FakeSystemCommand.expects_command(launchctl_remove_cmd)
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command) subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
end end
it "works when job is owned by system" do it "works when job is owned by system" do
FakeSystemCommand.stubs_command( allow(fake_system_command).to receive(:run)
launchctl_list_cmd, .with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: false)
unknown_response, .and_return(instance_double(SystemCommand::Result, stdout: unknown_response))
) allow(fake_system_command).to receive(:run)
.with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: true)
.and_return(instance_double(SystemCommand::Result, stdout: service_info))
FakeSystemCommand.stubs_command( expect(fake_system_command).to receive(:run!)
sudo(launchctl_list_cmd), .with("/bin/launchctl", args: ["remove", "my.fancy.package.service"], sudo: true)
service_info, .and_return(instance_double(SystemCommand::Result))
)
FakeSystemCommand.expects_command(sudo(launchctl_remove_cmd))
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command) subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
end end
end end
context "using :pkgutil" do context "using :pkgutil" do
let(:fake_system_command) { class_double(SystemCommand) }
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-pkgutil")) } let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-pkgutil")) }
let(:main_pkg_id) { "my.fancy.package.main" } let(:main_pkg_id) { "my.fancy.package.main" }

View File

@ -6,7 +6,8 @@ require "test/cask/dsl/shared_examples/staged"
describe Cask::DSL::Postflight, :cask do describe Cask::DSL::Postflight, :cask do
let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) } let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) }
let(:dsl) { described_class.new(cask, FakeSystemCommand) } let(:fake_system_command) { class_double(SystemCommand) }
let(:dsl) { described_class.new(cask, fake_system_command) }
it_behaves_like Cask::DSL::Base it_behaves_like Cask::DSL::Base

View File

@ -6,7 +6,8 @@ require "test/cask/dsl/shared_examples/staged"
describe Cask::DSL::Preflight, :cask do describe Cask::DSL::Preflight, :cask do
let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) } let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) }
let(:dsl) { described_class.new(cask, FakeSystemCommand) } let(:fake_system_command) { class_double(SystemCommand) }
let(:dsl) { described_class.new(cask, fake_system_command) }
it_behaves_like Cask::DSL::Base it_behaves_like Cask::DSL::Base

View File

@ -4,8 +4,8 @@
require "cask/staged" require "cask/staged"
shared_examples Cask::Staged do shared_examples Cask::Staged do
let(:existing_path) { Pathname.new("/path/to/file/that/exists") } let(:existing_path) { Pathname("/path/to/file/that/exists") }
let(:non_existent_path) { Pathname.new("/path/to/file/that/does/not/exist") } let(:non_existent_path) { Pathname("/path/to/file/that/does/not/exist") }
before do before do
allow(existing_path).to receive(:exist?).and_return(true) allow(existing_path).to receive(:exist?).and_return(true)
@ -17,9 +17,8 @@ shared_examples Cask::Staged do
end end
it "can run system commands with list-form arguments" do it "can run system commands with list-form arguments" do
FakeSystemCommand.expects_command( expect(fake_system_command).to receive(:run!)
["echo", "homebrew-cask", "rocks!"], .with("echo", args: ["homebrew-cask", "rocks!"])
)
staged.system_command("echo", args: ["homebrew-cask", "rocks!"]) staged.system_command("echo", args: ["homebrew-cask", "rocks!"])
end end
@ -28,9 +27,8 @@ shared_examples Cask::Staged do
fake_pathname = existing_path fake_pathname = existing_path
allow(staged).to receive(:Pathname).and_return(fake_pathname) allow(staged).to receive(:Pathname).and_return(fake_pathname)
FakeSystemCommand.expects_command( expect(fake_system_command).to receive(:run!)
["/bin/chmod", "-R", "--", "777", fake_pathname], .with("/bin/chmod", args: ["-R", "--", "777", fake_pathname], sudo: false)
)
staged.set_permissions(fake_pathname.to_s, "777") staged.set_permissions(fake_pathname.to_s, "777")
end end
@ -39,9 +37,8 @@ shared_examples Cask::Staged do
fake_pathname = existing_path fake_pathname = existing_path
allow(staged).to receive(:Pathname).and_return(fake_pathname) allow(staged).to receive(:Pathname).and_return(fake_pathname)
FakeSystemCommand.expects_command( expect(fake_system_command).to receive(:run!)
["/bin/chmod", "-R", "--", "777", fake_pathname, fake_pathname], .with("/bin/chmod", args: ["-R", "--", "777", fake_pathname, fake_pathname], sudo: false)
)
staged.set_permissions([fake_pathname.to_s, fake_pathname.to_s], "777") staged.set_permissions([fake_pathname.to_s, fake_pathname.to_s], "777")
end end
@ -58,9 +55,8 @@ shared_examples Cask::Staged do
allow(User).to receive(:current).and_return(User.new("fake_user")) allow(User).to receive(:current).and_return(User.new("fake_user"))
allow(staged).to receive(:Pathname).and_return(fake_pathname) allow(staged).to receive(:Pathname).and_return(fake_pathname)
FakeSystemCommand.expects_command( expect(fake_system_command).to receive(:run!)
sudo("/usr/sbin/chown", "-R", "--", "fake_user:staff", fake_pathname), .with("/usr/sbin/chown", args: ["-R", "--", "fake_user:staff", fake_pathname], sudo: true)
)
staged.set_ownership(fake_pathname.to_s) staged.set_ownership(fake_pathname.to_s)
end end
@ -71,9 +67,8 @@ shared_examples Cask::Staged do
allow(User).to receive(:current).and_return(User.new("fake_user")) allow(User).to receive(:current).and_return(User.new("fake_user"))
allow(staged).to receive(:Pathname).and_return(fake_pathname) allow(staged).to receive(:Pathname).and_return(fake_pathname)
FakeSystemCommand.expects_command( expect(fake_system_command).to receive(:run!)
sudo("/usr/sbin/chown", "-R", "--", "fake_user:staff", fake_pathname, fake_pathname), .with("/usr/sbin/chown", args: ["-R", "--", "fake_user:staff", fake_pathname, fake_pathname], sudo: true)
)
staged.set_ownership([fake_pathname.to_s, fake_pathname.to_s]) staged.set_ownership([fake_pathname.to_s, fake_pathname.to_s])
end end
@ -83,9 +78,8 @@ shared_examples Cask::Staged do
allow(staged).to receive(:Pathname).and_return(fake_pathname) allow(staged).to receive(:Pathname).and_return(fake_pathname)
FakeSystemCommand.expects_command( expect(fake_system_command).to receive(:run!)
sudo("/usr/sbin/chown", "-R", "--", "other_user:other_group", fake_pathname), .with("/usr/sbin/chown", args: ["-R", "--", "other_user:other_group", fake_pathname], sudo: true)
)
staged.set_ownership(fake_pathname.to_s, user: "other_user", group: "other_group") staged.set_ownership(fake_pathname.to_s, user: "other_user", group: "other_group")
end end

View File

@ -5,7 +5,7 @@ require "test/cask/dsl/shared_examples/base"
describe Cask::DSL::UninstallPostflight, :cask do describe Cask::DSL::UninstallPostflight, :cask do
let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) } let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) }
let(:dsl) { described_class.new(cask, FakeSystemCommand) } let(:dsl) { described_class.new(cask, class_double(SystemCommand)) }
it_behaves_like Cask::DSL::Base it_behaves_like Cask::DSL::Base
end end

View File

@ -6,7 +6,8 @@ require "test/cask/dsl/shared_examples/staged"
describe Cask::DSL::UninstallPreflight, :cask do describe Cask::DSL::UninstallPreflight, :cask do
let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) } let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) }
let(:dsl) { described_class.new(cask, FakeSystemCommand) } let(:fake_system_command) { class_double(SystemCommand) }
let(:dsl) { described_class.new(cask, fake_system_command) }
it_behaves_like Cask::DSL::Base it_behaves_like Cask::DSL::Base

View File

@ -153,8 +153,12 @@ describe Cask::Pkg, :cask do
"/usr/sbin/pkgutil", "/usr/sbin/pkgutil",
args: ["--pkg-info-plist", pkg_id], args: ["--pkg-info-plist", pkg_id],
).and_return( ).and_return(
SystemCommand::Result.new(nil, [[:stdout, pkg_info_plist]], instance_double(Process::Status, exitstatus: 0), SystemCommand::Result.new(
secrets: []), ["/usr/sbin/pkgutil", "--pkg-info-plist", pkg_id],
[[:stdout, pkg_info_plist]],
instance_double(Process::Status, exitstatus: 0),
secrets: [],
),
) )
info = pkg.info info = pkg.info

View File

@ -1,74 +0,0 @@
# typed: true
# frozen_string_literal: true
def sudo(*args)
["/usr/bin/sudo", "-E", "--"] + args.flatten
end
class FakeSystemCommand
def self.responses
@responses ||= {}
end
def self.expectations
@expectations ||= {}
end
def self.system_calls
@system_calls ||= Hash.new(0)
end
def self.clear
@responses = nil
@expectations = nil
@system_calls = nil
end
def self.stubs_command(command, response = "")
command = command.map(&:to_s)
responses[command] = response
end
def self.expects_command(command, response = "", times = 1)
command = command.map(&:to_s)
stubs_command(command, response)
expectations[command] = times
end
def self.verify_expectations!
expectations.each do |command, times|
unless system_calls[command] == times
raise("expected #{command.inspect} to be run #{times} times, but got #{system_calls[command]}")
end
end
end
def self.run(command_string, options = {})
command = SystemCommand.new(command_string, options).command
puts command
unless responses.key?(command)
raise("no response faked for #{command.inspect}, faked responses are: #{responses.inspect}")
end
system_calls[command] += 1
response = responses[command]
if response.respond_to?(:call)
response.call(command_string, options)
else
SystemCommand::Result.new(command, [[:stdout, response]], OpenStruct.new(exitstatus: 0), secrets: [])
end
end
def self.run!(command, options = {})
run(command, options.merge(must_succeed: true))
end
end
RSpec.configure do |config|
config.after do
FakeSystemCommand.verify_expectations!
ensure
FakeSystemCommand.clear
end
end

View File

@ -4,7 +4,6 @@
require "cask/config" require "cask/config"
require "cask/cache" require "cask/cache"
require "test/support/helper/cask/fake_system_command"
require "test/support/helper/cask/install_helper" require "test/support/helper/cask/install_helper"
require "test/support/helper/cask/never_sudo_system_command" require "test/support/helper/cask/never_sudo_system_command"