| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | require "bundle" | 
					
						
							| 
									
										
										
										
											2025-03-24 21:55:47 +08:00
										 |  |  | require "bundle/commands/cleanup" | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | RSpec.describe Homebrew::Bundle::Commands::Cleanup do | 
					
						
							| 
									
										
										
										
											2025-06-10 14:37:21 +00:00
										 |  |  |   describe "read Brewfile and current installation", :no_api do | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |     before do | 
					
						
							|  |  |  |       described_class.reset! | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       # don't try to load gcc/glibc | 
					
						
							|  |  |  |       allow(DevelopmentTools).to receive_messages(needs_libc_formula?: false, needs_compiler_formula?: false) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       allow_any_instance_of(Pathname).to receive(:read).and_return <<~EOS | 
					
						
							|  |  |  |         tap 'x' | 
					
						
							|  |  |  |         tap 'y' | 
					
						
							|  |  |  |         cask '123' | 
					
						
							|  |  |  |         brew 'a' | 
					
						
							|  |  |  |         brew 'b' | 
					
						
							|  |  |  |         brew 'd2' | 
					
						
							|  |  |  |         brew 'homebrew/tap/f' | 
					
						
							|  |  |  |         brew 'homebrew/tap/g' | 
					
						
							|  |  |  |         brew 'homebrew/tap/h' | 
					
						
							|  |  |  |         brew 'homebrew/tap/i2' | 
					
						
							|  |  |  |         brew 'homebrew/tap/hasdependency' | 
					
						
							|  |  |  |         brew 'hasbuilddependency1' | 
					
						
							|  |  |  |         brew 'hasbuilddependency2' | 
					
						
							|  |  |  |         mas 'appstoreapp1', id: 1
 | 
					
						
							|  |  |  |         vscode 'VsCodeExtension1' | 
					
						
							|  |  |  |       EOS | 
					
						
							|  |  |  |       %w[a b d2 homebrew/tap/f homebrew/tap/g homebrew/tap/h homebrew/tap/i2
 | 
					
						
							|  |  |  |          homebrew/tap/hasdependency hasbuilddependency1 hasbuilddependency2].each do |full_name| | 
					
						
							|  |  |  |         tap_name, _, name = full_name.rpartition("/") | 
					
						
							|  |  |  |         tap = tap_name.present? ? Tap.fetch(tap_name) : nil | 
					
						
							|  |  |  |         f = formula(name, tap:) { url "#{name}-1.0" } | 
					
						
							|  |  |  |         stub_formula_loader f, full_name | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "computes which casks to uninstall" do | 
					
						
							| 
									
										
										
										
											2025-07-10 08:05:36 +00:00
										 |  |  |       cask_123 = instance_double(Cask::Cask, to_s: "123", old_tokens: []) | 
					
						
							|  |  |  |       cask_456 = instance_double(Cask::Cask, to_s: "456", old_tokens: []) | 
					
						
							|  |  |  |       allow(Homebrew::Bundle::CaskDumper).to receive(:casks).and_return([cask_123, cask_456]) | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |       expect(described_class.casks_to_uninstall).to eql(%w[456]) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "computes which formulae to uninstall" do | 
					
						
							|  |  |  |       dependencies_arrays_hash = { dependencies: [], build_dependencies: [] } | 
					
						
							| 
									
										
										
										
											2025-04-01 15:12:12 +01:00
										 |  |  |       formulae_hash = [ | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |         { name: "a2", full_name: "a2", aliases: ["a"], dependencies: ["d"] }, | 
					
						
							|  |  |  |         { name: "c", full_name: "c" }, | 
					
						
							|  |  |  |         { name: "d", full_name: "homebrew/tap/d", aliases: ["d2"] }, | 
					
						
							|  |  |  |         { name: "e", full_name: "homebrew/tap/e" }, | 
					
						
							|  |  |  |         { name: "f", full_name: "homebrew/tap/f" }, | 
					
						
							|  |  |  |         { name: "h", full_name: "other/tap/h" }, | 
					
						
							|  |  |  |         { name: "i", full_name: "homebrew/tap/i", aliases: ["i2"] }, | 
					
						
							|  |  |  |         { name: "hasdependency", full_name: "homebrew/tap/hasdependency", dependencies: ["isdependency"] }, | 
					
						
							|  |  |  |         { name: "isdependency", full_name: "homebrew/tap/isdependency" }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           name:                "hasbuilddependency1", | 
					
						
							|  |  |  |           full_name:           "hasbuilddependency1", | 
					
						
							|  |  |  |           poured_from_bottle?: true, | 
					
						
							|  |  |  |           build_dependencies:  ["builddependency1"], | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           name:                "hasbuilddependency2", | 
					
						
							|  |  |  |           full_name:           "hasbuilddependency2", | 
					
						
							|  |  |  |           poured_from_bottle?: false, | 
					
						
							|  |  |  |           build_dependencies:  ["builddependency2"], | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         { name: "builddependency1", full_name: "builddependency1" }, | 
					
						
							|  |  |  |         { name: "builddependency2", full_name: "builddependency2" }, | 
					
						
							|  |  |  |         { name: "caskdependency", full_name: "homebrew/tap/caskdependency" }, | 
					
						
							|  |  |  |       ].map { |formula| dependencies_arrays_hash.merge(formula) } | 
					
						
							| 
									
										
										
										
											2025-07-04 11:25:41 +01:00
										 |  |  |       allow(Homebrew::Bundle::FormulaDumper).to receive(:formulae).and_return(formulae_hash) | 
					
						
							| 
									
										
										
										
											2025-04-01 15:12:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       formulae_hash.each do |hash_formula| | 
					
						
							|  |  |  |         name = hash_formula[:name] | 
					
						
							|  |  |  |         full_name = hash_formula[:full_name] | 
					
						
							|  |  |  |         tap_name = full_name.rpartition("/").first.presence || "homebrew/core" | 
					
						
							|  |  |  |         tap = Tap.fetch(tap_name) | 
					
						
							|  |  |  |         f = formula(name, tap:) { url "#{name}-1.0" } | 
					
						
							|  |  |  |         stub_formula_loader f, full_name | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |       allow(Homebrew::Bundle::CaskDumper).to receive(:formula_dependencies).and_return(%w[caskdependency]) | 
					
						
							|  |  |  |       expect(described_class.formulae_to_uninstall).to eql %w[
 | 
					
						
							|  |  |  |         c | 
					
						
							|  |  |  |         homebrew/tap/e | 
					
						
							|  |  |  |         other/tap/h | 
					
						
							|  |  |  |         builddependency1 | 
					
						
							|  |  |  |       ] | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "computes which tap to untap" do | 
					
						
							|  |  |  |       allow(Homebrew::Bundle::TapDumper).to \ | 
					
						
							| 
									
										
										
										
											2025-03-19 09:51:39 +00:00
										 |  |  |         receive(:tap_names).and_return(%w[z homebrew/core homebrew/tap]) | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |       expect(described_class.taps_to_untap).to eql(%w[z]) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "ignores unavailable formulae when computing which taps to keep" do | 
					
						
							|  |  |  |       allow(Formulary).to \ | 
					
						
							|  |  |  |         receive(:factory).and_raise(TapFormulaUnavailableError.new(Tap.fetch("homebrew/tap"), "foo")) | 
					
						
							|  |  |  |       allow(Homebrew::Bundle::TapDumper).to \ | 
					
						
							| 
									
										
										
										
											2025-03-19 09:51:39 +00:00
										 |  |  |         receive(:tap_names).and_return(%w[z homebrew/core homebrew/tap]) | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |       expect(described_class.taps_to_untap).to eql(%w[z homebrew/tap]) | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-04-01 15:12:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it "ignores formulae with .keepme references when computing which formulae to uninstall" do | 
					
						
							|  |  |  |       name = full_name ="c" | 
					
						
							| 
									
										
										
										
											2025-07-04 11:25:41 +01:00
										 |  |  |       allow(Homebrew::Bundle::FormulaDumper).to receive(:formulae).and_return([{ name:, full_name: }]) | 
					
						
							| 
									
										
										
										
											2025-04-01 15:12:12 +01:00
										 |  |  |       f = formula(name) { url "#{name}-1.0" } | 
					
						
							|  |  |  |       stub_formula_loader f, name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       keg = instance_double(Keg) | 
					
						
							|  |  |  |       allow(keg).to receive(:keepme_refs).and_return(["/some/file"]) | 
					
						
							|  |  |  |       allow(f).to receive(:installed_kegs).and_return([keg]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(described_class.formulae_to_uninstall).to be_empty | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it "computes which VSCode extensions to uninstall" do | 
					
						
							|  |  |  |       allow(Homebrew::Bundle::VscodeExtensionDumper).to receive(:extensions).and_return(%w[z]) | 
					
						
							|  |  |  |       expect(described_class.vscode_extensions_to_uninstall).to eql(%w[z]) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "computes which VSCode extensions to uninstall irrespective of case of the extension name" do | 
					
						
							|  |  |  |       allow(Homebrew::Bundle::VscodeExtensionDumper).to receive(:extensions).and_return(%w[z vscodeextension1]) | 
					
						
							|  |  |  |       expect(described_class.vscode_extensions_to_uninstall).to eql(%w[z]) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   context "when there are no formulae to uninstall and no taps to untap" do | 
					
						
							|  |  |  |     before do | 
					
						
							|  |  |  |       described_class.reset! | 
					
						
							|  |  |  |       allow(described_class).to receive_messages(casks_to_uninstall:             [], | 
					
						
							|  |  |  |                                                  formulae_to_uninstall:          [], | 
					
						
							|  |  |  |                                                  taps_to_untap:                  [], | 
					
						
							|  |  |  |                                                  vscode_extensions_to_uninstall: []) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "does nothing" do | 
					
						
							|  |  |  |       expect(Kernel).not_to receive(:system) | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							|  |  |  |       described_class.run(force: true) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   context "when there are casks to uninstall" do | 
					
						
							|  |  |  |     before do | 
					
						
							|  |  |  |       described_class.reset! | 
					
						
							|  |  |  |       allow(described_class).to receive_messages(casks_to_uninstall:             %w[a b], | 
					
						
							|  |  |  |                                                  formulae_to_uninstall:          [], | 
					
						
							|  |  |  |                                                  taps_to_untap:                  [], | 
					
						
							|  |  |  |                                                  vscode_extensions_to_uninstall: []) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "uninstalls casks" do | 
					
						
							|  |  |  |       expect(Kernel).to receive(:system).with(HOMEBREW_BREW_FILE, "uninstall", "--cask", "--force", "a", "b") | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							|  |  |  |       expect { described_class.run(force: true) }.to output(/Uninstalled 2 casks/).to_stdout | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-05-23 06:16:22 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-04 11:25:41 +01:00
										 |  |  |     it "does not uninstall casks if --formulae is disabled" do | 
					
						
							| 
									
										
										
										
											2025-05-23 06:16:22 +01:00
										 |  |  |       expect(Kernel).not_to receive(:system) | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							|  |  |  |       expect { described_class.run(force: true, casks: false) }.not_to output.to_stdout | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   context "when there are casks to zap" do | 
					
						
							|  |  |  |     before do | 
					
						
							|  |  |  |       described_class.reset! | 
					
						
							|  |  |  |       allow(described_class).to receive_messages(casks_to_uninstall:             %w[a b], | 
					
						
							|  |  |  |                                                  formulae_to_uninstall:          [], | 
					
						
							|  |  |  |                                                  taps_to_untap:                  [], | 
					
						
							|  |  |  |                                                  vscode_extensions_to_uninstall: []) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "uninstalls casks" do | 
					
						
							|  |  |  |       expect(Kernel).to receive(:system).with(HOMEBREW_BREW_FILE, "uninstall", "--cask", "--zap", "--force", "a", "b") | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							|  |  |  |       expect { described_class.run(force: true, zap: true) }.to output(/Uninstalled 2 casks/).to_stdout | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-05-23 06:16:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it "does not uninstall casks if --casks is disabled" do | 
					
						
							|  |  |  |       expect(Kernel).not_to receive(:system) | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							|  |  |  |       expect { described_class.run(force: true, zap: true, casks: false) }.not_to output.to_stdout | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   context "when there are formulae to uninstall" do | 
					
						
							|  |  |  |     before do | 
					
						
							|  |  |  |       described_class.reset! | 
					
						
							|  |  |  |       allow(described_class).to receive_messages(casks_to_uninstall:             [], | 
					
						
							|  |  |  |                                                  formulae_to_uninstall:          %w[a b], | 
					
						
							|  |  |  |                                                  taps_to_untap:                  [], | 
					
						
							|  |  |  |                                                  vscode_extensions_to_uninstall: []) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "uninstalls formulae" do | 
					
						
							|  |  |  |       expect(Kernel).to receive(:system).with(HOMEBREW_BREW_FILE, "uninstall", "--formula", "--force", "a", "b") | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							|  |  |  |       expect { described_class.run(force: true) }.to output(/Uninstalled 2 formulae/).to_stdout | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-05-23 06:16:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it "does not uninstall formulae if --casks is disabled" do | 
					
						
							|  |  |  |       expect(Kernel).not_to receive(:system) | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							| 
									
										
										
										
											2025-07-04 11:25:41 +01:00
										 |  |  |       expect { described_class.run(force: true, formulae: false) }.not_to output.to_stdout | 
					
						
							| 
									
										
										
										
											2025-05-23 06:16:22 +01:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   context "when there are taps to untap" do | 
					
						
							|  |  |  |     before do | 
					
						
							|  |  |  |       described_class.reset! | 
					
						
							|  |  |  |       allow(described_class).to receive_messages(casks_to_uninstall:             [], | 
					
						
							|  |  |  |                                                  formulae_to_uninstall:          [], | 
					
						
							|  |  |  |                                                  taps_to_untap:                  %w[a b], | 
					
						
							|  |  |  |                                                  vscode_extensions_to_uninstall: []) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "untaps taps" do | 
					
						
							|  |  |  |       expect(Kernel).to receive(:system).with(HOMEBREW_BREW_FILE, "untap", "a", "b") | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							|  |  |  |       described_class.run(force: true) | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-05-23 06:16:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it "does not untap taps if --taps is disabled" do | 
					
						
							|  |  |  |       expect(Kernel).not_to receive(:system) | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							|  |  |  |       described_class.run(force: true, taps: false) | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   context "when there are VSCode extensions to uninstall" do | 
					
						
							|  |  |  |     before do | 
					
						
							|  |  |  |       described_class.reset! | 
					
						
							| 
									
										
										
										
											2025-03-19 11:42:37 +00:00
										 |  |  |       allow(Homebrew::Bundle).to receive(:which_vscode).and_return(Pathname("code")) | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |       allow(described_class).to receive_messages(casks_to_uninstall:             [], | 
					
						
							|  |  |  |                                                  formulae_to_uninstall:          [], | 
					
						
							|  |  |  |                                                  taps_to_untap:                  [], | 
					
						
							|  |  |  |                                                  vscode_extensions_to_uninstall: %w[GitHub.codespaces]) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "uninstalls extensions" do | 
					
						
							| 
									
										
										
										
											2025-04-02 17:15:32 +01:00
										 |  |  |       expect(Kernel).to receive(:system).with("code", "--uninstall-extension", "GitHub.codespaces") | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							|  |  |  |       described_class.run(force: true) | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-05-23 06:16:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it "does not uninstall extensions if --vscode is disabled" do | 
					
						
							|  |  |  |       expect(Kernel).not_to receive(:system) | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							|  |  |  |       described_class.run(force: true, vscode: false) | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   context "when there are casks and formulae to uninstall and taps to untap but without passing `--force`" do | 
					
						
							|  |  |  |     before do | 
					
						
							|  |  |  |       described_class.reset! | 
					
						
							|  |  |  |       allow(described_class).to receive_messages(casks_to_uninstall:             %w[a b], | 
					
						
							|  |  |  |                                                  formulae_to_uninstall:          %w[a b], | 
					
						
							|  |  |  |                                                  taps_to_untap:                  %w[a b], | 
					
						
							|  |  |  |                                                  vscode_extensions_to_uninstall: %w[a b]) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "lists casks, formulae and taps" do | 
					
						
							|  |  |  |       expect(Formatter).to receive(:columns).with(%w[a b]).exactly(4).times | 
					
						
							|  |  |  |       expect(Kernel).not_to receive(:system) | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("") | 
					
						
							|  |  |  |       expect do | 
					
						
							|  |  |  |         described_class.run | 
					
						
							|  |  |  |       end.to raise_error(SystemExit) | 
					
						
							|  |  |  |         .and output(/Would uninstall formulae:.*Would untap:.*Would uninstall VSCode extensions:/m).to_stdout | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   context "when there is brew cleanup output" do | 
					
						
							|  |  |  |     before do | 
					
						
							|  |  |  |       described_class.reset! | 
					
						
							|  |  |  |       allow(described_class).to receive_messages(casks_to_uninstall:             [], | 
					
						
							|  |  |  |                                                  formulae_to_uninstall:          [], | 
					
						
							|  |  |  |                                                  taps_to_untap:                  [], | 
					
						
							|  |  |  |                                                  vscode_extensions_to_uninstall: []) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def sane? | 
					
						
							|  |  |  |       expect(described_class).to receive(:system_output_no_stderr).and_return("cleaned") | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "with --force" do | 
					
						
							|  |  |  |       it "prints output" do | 
					
						
							|  |  |  |         sane? | 
					
						
							|  |  |  |         expect { described_class.run(force: true) }.to output(/cleaned/).to_stdout | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     context "without --force" do | 
					
						
							|  |  |  |       it "prints output" do | 
					
						
							|  |  |  |         sane? | 
					
						
							|  |  |  |         expect { described_class.run }.to output(/cleaned/).to_stdout | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe "#system_output_no_stderr" do | 
					
						
							|  |  |  |     it "shells out" do | 
					
						
							|  |  |  |       expect(IO).to receive(:popen).and_return(StringIO.new("true")) | 
					
						
							|  |  |  |       described_class.system_output_no_stderr("true") | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |