| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  | require "extend/pathname" | 
					
						
							|  |  |  | require "install_renamed" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 15:11:11 -08:00
										 |  |  | RSpec.describe Pathname do | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |   include FileUtils | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-28 14:50:46 +01:00
										 |  |  |   let(:src) { mktmpdir } | 
					
						
							|  |  |  |   let(:dst) { mktmpdir } | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |   let(:file) { src/"foo" } | 
					
						
							|  |  |  |   let(:dir) { src/"bar" } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe DiskUsageExtension do | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  |     before do | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |       mkdir_p dir/"a-directory" | 
					
						
							|  |  |  |       touch [dir/".DS_Store", dir/"a-file"] | 
					
						
							|  |  |  |       File.truncate(dir/"a-file", 1_048_576) | 
					
						
							|  |  |  |       ln_s dir/"a-file", dir/"a-symlink" | 
					
						
							|  |  |  |       ln dir/"a-file", dir/"a-hardlink" | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     describe "#file_count" do | 
					
						
							|  |  |  |       it "returns the number of files in a directory" do | 
					
						
							|  |  |  |         expect(dir.file_count).to eq(3) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     describe "#abv" do | 
					
						
							|  |  |  |       context "when called on a directory" do | 
					
						
							|  |  |  |         it "returns a string with the file count and disk usage" do | 
					
						
							| 
									
										
										
										
											2017-02-25 11:09:57 +00:00
										 |  |  |           expect(dir.abv).to eq("3 files, 1MB") | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       context "when called on a file" do | 
					
						
							|  |  |  |         it "returns the disk usage" do | 
					
						
							| 
									
										
										
										
											2017-02-25 11:09:57 +00:00
										 |  |  |           expect((dir/"a-file").abv).to eq("1MB") | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe "#rmdir_if_possible" do | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  |     before { mkdir_p dir } | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it "returns true and removes a directory if it doesn't contain files" do | 
					
						
							|  |  |  |       expect(dir.rmdir_if_possible).to be true | 
					
						
							|  |  |  |       expect(dir).not_to exist | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "returns false and doesn't delete a directory if it contains files" do | 
					
						
							|  |  |  |       touch dir/"foo" | 
					
						
							|  |  |  |       expect(dir.rmdir_if_possible).to be false | 
					
						
							|  |  |  |       expect(dir).to be_a_directory | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "ignores .DS_Store files" do | 
					
						
							|  |  |  |       touch dir/".DS_Store" | 
					
						
							|  |  |  |       expect(dir.rmdir_if_possible).to be true | 
					
						
							|  |  |  |       expect(dir).not_to exist | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe "#append_lines" do | 
					
						
							|  |  |  |     it "appends lines to a file" do | 
					
						
							|  |  |  |       touch file | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       file.append_lines("CONTENT") | 
					
						
							| 
									
										
										
										
											2017-10-15 02:28:32 +02:00
										 |  |  |       expect(File.read(file)).to eq <<~EOS | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |         CONTENT | 
					
						
							|  |  |  |       EOS | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       file.append_lines("CONTENTS") | 
					
						
							| 
									
										
										
										
											2017-10-15 02:28:32 +02:00
										 |  |  |       expect(File.read(file)).to eq <<~EOS | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |         CONTENT | 
					
						
							|  |  |  |         CONTENTS | 
					
						
							|  |  |  |       EOS | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "raises an error if the file does not exist" do | 
					
						
							|  |  |  |       expect(file).not_to exist | 
					
						
							|  |  |  |       expect { file.append_lines("CONTENT") }.to raise_error(RuntimeError) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe "#atomic_write" do | 
					
						
							|  |  |  |     it "atomically replaces a file" do | 
					
						
							|  |  |  |       touch file | 
					
						
							|  |  |  |       file.atomic_write("CONTENT") | 
					
						
							|  |  |  |       expect(File.read(file)).to eq("CONTENT") | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "preserves permissions" do | 
					
						
							| 
									
										
										
										
											2023-03-08 23:14:46 +00:00
										 |  |  |       File.open(file, "w", 0100777) do | 
					
						
							| 
									
										
										
										
											2023-01-26 21:18:24 -08:00
										 |  |  |         # do nothing | 
					
						
							| 
									
										
										
										
											2023-03-08 23:14:46 +00:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |       file.atomic_write("CONTENT") | 
					
						
							| 
									
										
										
										
											2023-01-02 19:18:51 +00:00
										 |  |  |       expect(file.stat.mode.to_s(8)).to eq((~File.umask & 0100777).to_s(8)) | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "preserves default permissions" do | 
					
						
							|  |  |  |       file.atomic_write("CONTENT") | 
					
						
							| 
									
										
										
										
											2018-09-17 15:23:17 +01:00
										 |  |  |       sentinel = file.dirname.join("sentinel") | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |       touch sentinel | 
					
						
							| 
									
										
										
										
											2018-09-17 15:23:17 +01:00
										 |  |  |       expect(file.stat.mode.to_s(8)).to eq(sentinel.stat.mode.to_s(8)) | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe "#ensure_writable" do | 
					
						
							|  |  |  |     it "makes a file writable and restores permissions afterwards" do | 
					
						
							| 
									
										
										
										
											2019-02-16 12:16:02 -08:00
										 |  |  |       skip "User is root so everything is writable." if Process.euid.zero? | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |       touch file | 
					
						
							|  |  |  |       chmod 0555, file | 
					
						
							|  |  |  |       expect(file).not_to be_writable | 
					
						
							|  |  |  |       file.ensure_writable do | 
					
						
							|  |  |  |         expect(file).to be_writable | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |       expect(file).not_to be_writable | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe "#extname" do | 
					
						
							|  |  |  |     it "supports common multi-level archives" do | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  |       expect(described_class.new("foo-0.1.tar.gz").extname).to eq(".tar.gz") | 
					
						
							|  |  |  |       expect(described_class.new("foo-0.1.cpio.gz").extname).to eq(".cpio.gz") | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2018-08-10 00:54:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it "does not treat version numbers as extensions" do | 
					
						
							|  |  |  |       expect(described_class.new("foo-0.1").extname).to eq("") | 
					
						
							|  |  |  |       expect(described_class.new("foo-1.0-rc1").extname).to eq("") | 
					
						
							| 
									
										
										
										
											2018-09-29 10:32:18 +02:00
										 |  |  |       expect(described_class.new("foo-1.2.3").extname).to eq "" | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "supports `.7z` with version numbers" do | 
					
						
							|  |  |  |       expect(described_class.new("snap7-full-1.4.2.7z").extname).to eq ".7z" | 
					
						
							| 
									
										
										
										
											2018-08-10 00:54:03 +02:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe "#stem" do | 
					
						
							|  |  |  |     it "returns the basename without double extensions" do | 
					
						
							|  |  |  |       expect(Pathname("foo-0.1.tar.gz").stem).to eq("foo-0.1") | 
					
						
							|  |  |  |       expect(Pathname("foo-0.1.cpio.gz").stem).to eq("foo-0.1") | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe "#install" do | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  |     before do | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |       (src/"a.txt").write "This is sample file a." | 
					
						
							|  |  |  |       (src/"b.txt").write "This is sample file b." | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "raises an error if the file doesn't exist" do | 
					
						
							|  |  |  |       expect { dst.install "non_existent_file" }.to raise_error(Errno::ENOENT) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "installs a file to a directory with its basename" do | 
					
						
							|  |  |  |       touch file | 
					
						
							|  |  |  |       dst.install(file) | 
					
						
							|  |  |  |       expect(dst/file.basename).to exist | 
					
						
							|  |  |  |       expect(file).not_to exist | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "creates intermediate directories" do | 
					
						
							|  |  |  |       touch file | 
					
						
							|  |  |  |       expect(dir).not_to be_a_directory | 
					
						
							|  |  |  |       dir.install(file) | 
					
						
							|  |  |  |       expect(dir).to be_a_directory | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "can install a file" do | 
					
						
							|  |  |  |       dst.install src/"a.txt" | 
					
						
							|  |  |  |       expect(dst/"a.txt").to exist, "a.txt was not installed" | 
					
						
							|  |  |  |       expect(dst/"b.txt").not_to exist, "b.txt was installed." | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "can install an array of files" do | 
					
						
							|  |  |  |       dst.install [src/"a.txt", src/"b.txt"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(dst/"a.txt").to exist, "a.txt was not installed" | 
					
						
							|  |  |  |       expect(dst/"b.txt").to exist, "b.txt was not installed" | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "can install a directory" do | 
					
						
							|  |  |  |       bin = src/"bin" | 
					
						
							|  |  |  |       bin.mkpath | 
					
						
							|  |  |  |       mv Dir[src/"*.txt"], bin | 
					
						
							|  |  |  |       dst.install bin | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(dst/"bin/a.txt").to exist, "a.txt was not installed" | 
					
						
							|  |  |  |       expect(dst/"bin/b.txt").to exist, "b.txt was not installed" | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "supports renaming files" do | 
					
						
							|  |  |  |       dst.install src/"a.txt" => "c.txt" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(dst/"c.txt").to exist, "c.txt was not installed" | 
					
						
							|  |  |  |       expect(dst/"a.txt").not_to exist, "a.txt was installed but not renamed" | 
					
						
							|  |  |  |       expect(dst/"b.txt").not_to exist, "b.txt was installed" | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "supports renaming multiple files" do | 
					
						
							|  |  |  |       dst.install(src/"a.txt" => "c.txt", src/"b.txt" => "d.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(dst/"c.txt").to exist, "c.txt was not installed" | 
					
						
							|  |  |  |       expect(dst/"d.txt").to exist, "d.txt was not installed" | 
					
						
							|  |  |  |       expect(dst/"a.txt").not_to exist, "a.txt was installed but not renamed" | 
					
						
							|  |  |  |       expect(dst/"b.txt").not_to exist, "b.txt was installed but not renamed" | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "supports renaming directories" do | 
					
						
							|  |  |  |       bin = src/"bin" | 
					
						
							|  |  |  |       bin.mkpath | 
					
						
							|  |  |  |       mv Dir[src/"*.txt"], bin | 
					
						
							|  |  |  |       dst.install bin => "libexec" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(dst/"bin").not_to exist, "bin was installed but not renamed" | 
					
						
							|  |  |  |       expect(dst/"libexec/a.txt").to exist, "a.txt was not installed" | 
					
						
							|  |  |  |       expect(dst/"libexec/b.txt").to exist, "b.txt was not installed" | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "can install directories as relative symlinks" do | 
					
						
							|  |  |  |       bin = src/"bin" | 
					
						
							|  |  |  |       bin.mkpath | 
					
						
							|  |  |  |       mv Dir[src/"*.txt"], bin | 
					
						
							|  |  |  |       dst.install_symlink bin | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(dst/"bin").to be_a_symlink | 
					
						
							|  |  |  |       expect(dst/"bin").to be_a_directory | 
					
						
							|  |  |  |       expect(dst/"bin/a.txt").to exist | 
					
						
							|  |  |  |       expect(dst/"bin/b.txt").to exist | 
					
						
							|  |  |  |       expect((dst/"bin").readlink).to be_relative | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "can install relative paths as symlinks" do | 
					
						
							|  |  |  |       dst.install_symlink "foo" => "bar" | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  |       expect((dst/"bar").readlink).to eq(described_class.new("foo")) | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2019-02-22 13:18:47 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     it "can install relative symlinks in a symlinked directory" do | 
					
						
							|  |  |  |       mkdir_p dst/"1/2" | 
					
						
							|  |  |  |       dst.install_symlink "1/2" => "12" | 
					
						
							|  |  |  |       expect((dst/"12").readlink).to eq(described_class.new("1/2")) | 
					
						
							|  |  |  |       (dst/"12").install_symlink dst/"foo" | 
					
						
							|  |  |  |       expect((dst/"12/foo").readlink).to eq(described_class.new("../../foo")) | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe InstallRenamed do | 
					
						
							| 
									
										
										
										
											2018-03-25 13:30:37 +01:00
										 |  |  |     before do | 
					
						
							| 
									
										
										
										
											2019-07-23 17:09:57 +01:00
										 |  |  |       dst.extend(described_class) | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "renames the installed file if it already exists" do | 
					
						
							|  |  |  |       file.write "a" | 
					
						
							|  |  |  |       dst.install file | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       file.write "b" | 
					
						
							|  |  |  |       dst.install file | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(File.read(dst/file.basename)).to eq("a") | 
					
						
							|  |  |  |       expect(File.read(dst/"#{file.basename}.default")).to eq("b") | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "renames the installed directory" do | 
					
						
							|  |  |  |       file.write "a" | 
					
						
							|  |  |  |       dst.install src | 
					
						
							|  |  |  |       expect(File.read(dst/src.basename/file.basename)).to eq("a") | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "recursively renames directories" do | 
					
						
							|  |  |  |       (dst/dir.basename).mkpath | 
					
						
							|  |  |  |       (dst/dir.basename/"another_file").write "a" | 
					
						
							|  |  |  |       dir.mkpath | 
					
						
							|  |  |  |       (dir/"another_file").write "b" | 
					
						
							|  |  |  |       dst.install dir | 
					
						
							|  |  |  |       expect(File.read(dst/dir.basename/"another_file.default")).to eq("b") | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe "#cp_path_sub" do | 
					
						
							|  |  |  |     it "copies a file and replaces the given pattern" do | 
					
						
							|  |  |  |       file.write "a" | 
					
						
							|  |  |  |       file.cp_path_sub src, dst | 
					
						
							|  |  |  |       expect(File.read(dst/file.basename)).to eq("a") | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it "copies a directory and replaces the given pattern" do | 
					
						
							|  |  |  |       dir.mkpath | 
					
						
							|  |  |  |       dir.cp_path_sub src, dst | 
					
						
							|  |  |  |       expect(dst/dir.basename).to be_a_directory | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2017-02-27 14:23:53 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |   describe "#ds_store?" do | 
					
						
							|  |  |  |     it "returns whether a file is .DS_Store or not" do | 
					
						
							|  |  |  |       expect(file).not_to be_ds_store | 
					
						
							|  |  |  |       expect(file/".DS_Store").to be_ds_store | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2017-02-15 02:02:25 +01:00
										 |  |  | end |