require File.expand_path(File.dirname(__FILE__) + '/spec_helper') describe "Checksummer" do class ChecksummerFile end let(:checksum_to) { "/data" } let(:cs) { cs = Checksummer.new(checksum_to) } describe "#intiailize" do it "sets the correct chcksum_to" do cs = Checksummer.new("/data") cs.checksum_to.should == "/data" end it "calls expans path on checksum_to" do dir_double = double("some double") File.should_receive(:expand_path).with(dir_double).and_return "some path" cs = Checksummer.new(dir_double) end end describe "#checksum_directory" do let(:file1) { double("checksummer file 1", :checksum_to! => :copied, :path => "/path1") } let(:file2) { double("checksummer file 2", :checksum_to! => :symlinked, :path => "/path2") } it "calls find with correct parameters" do cs.should_receive(:find).with("path1", "-mtime +1").and_return [] cs.checksum_directory("path1", "-mtime +1") end it "calls checksum on all ChecksummerFiles" do file1.should_receive(:checksum_to!).with("/data").and_return true file2.should_receive(:checksum_to!).with("/data").and_return true cs.stub!(:find).and_return [file1, file2] cs.checksum_directory("path1") end it "yields all checksum status messages and files" do yielder = double("yielder") yielder.should_receive(:got_file).with(:copied, file1, 0, 2) yielder.should_receive(:got_file).with(:symlinked, file2, 1, 2) cs.stub!(:find).and_return [file1, file2] cs.checksum_directory("path1") do |status, file, index, total| yielder.got_file(status, file, index, total) end end end describe "#find" do let(:files) do [ "2010-09-14+10:16:26.4888617110 66 d /v1/incoming/ ", "2010-07-08+17:52:08.6168163060 76 d /v1/incoming/rsync_from_delivery_host ", "2010-12-06+18:12:13.0000000000 737280 d /v1/incoming/rsync_from_delivery_host/finetunes ", "2010-07-24+19:46:38.0000000000 30 d /v1/incoming/rsync_from_delivery_host/finetunes/1069604473114 ", "2009-10-31+16:29:51.0000000000 12032 f /v1/incoming/rsync_from_delivery_host/finetunes/1069604473114/1069604473114.xml ", "2010-07-24+19:46:38.0000000000 30 d /v1/incoming/rsync_from_delivery_host/finetunes/1069604681256 " ].join("\n") end before(:each) do Kernel.stub(:`).and_return(files) end it "calls system command with correct arguments" do Kernel.should_receive(:`).with(%(find path1 -type f -printf "%T+\t%s\t%Y\t%p\t%l\n")) cs.find("path1") end it "uses find options" do Kernel.should_receive(:`).with(%(find path1 -mtime +1 -type f -printf "%T+\t%s\t%Y\t%p\t%l\n")) cs.find("path1", "-mtime +1") end it "returns the correct amount of files" do cs.find("path1").length.should == 6 end it "returns objects of type ChecksummerFile" do cs.find("path1").first.should be_an_instance_of(ChecksummerFile) end it "sets the correct atributes" do cs.find("path1").first.path.should == "/v1/incoming" end end describe "#run_for_args" do before(:each) do Checksummer.stub!(:puts) end it "prints usage when no checksum_to found" do Checksummer.should_receive(:puts).with Checksummer.usage Checksummer.run_for_args([]) end it "prints usage when checksum dir is not a directory" do Checksummer.should_receive(:puts).with Checksummer.usage Checksummer.run_for_args(["/path/to/something"]) end it "prints usage when checksum_to is no directory" do Checksummer.should_receive(:puts).with Checksummer.usage Checksummer.run_for_args(["/tmp", "/some/path"]) end it "initializes checksummer with correct checksum_to dir" do Checksummer.should_receive(:new).with("/tmp").and_return(double("checksummer", :checksum_directory => true)) Checksummer.run_for_args(["/tmp", "/tmp"]) end it "calls checksum_directory wirh correct find options" do checksummer = double("checksummer") Checksummer.stub!(:new).and_return checksummer checksummer.should_receive(:checksum_directory).with("/tmp", %(-name "*.mp3")) Checksummer.run_for_args(["/tmp", "/tmp", "-name", %("*.mp3")]) end it "does not call checksum_directory with sleep parameter" do checksummer = double("checksummer") Checksummer.stub!(:new).and_return checksummer checksummer.should_receive(:checksum_directory).with("/tmp", %(-mtime +1)) Checksummer.run_for_args(["/tmp", "/tmp", "-mtime", "+1", "--sleep", "10"]) end describe "with a cs double" do before(:each) do @cs_double = double("cs") Checksummer.stub(:new).with("/tmp").and_return @cs_double def @cs_double.checksum_directory(path, find_options) file1 = ChecksummerFile.new(:path => "/path1.txt") file2 = ChecksummerFile.new(:path => "/path2.txt") yield(:copied, file1, 0, 20) yield(:symlinked, file2, 1, 20) end end it "calls puts with a correct string" do Time.stub(:now).and_return Time.local(2010, 9, 10, 11, 12) Checksummer.should_receive(:puts).with("2010-09-10 11:12:00\t00/20\tcopied\t/path1.txt") Checksummer.should_receive(:puts).with("2010-09-10 11:12:00\t01/20\tsymlinked\t/path2.txt") Checksummer.run_for_args(["/tmp", "/tmp"]) end it "calls sleep with 0.1 when between 8 and 23:59" do now = double("now", :hour => 8, :strftime => "some time") Time.stub!(:now).and_return(now) Checksummer.should_receive(:sleep).with(0.1).at_least(1).times Checksummer.run_for_args(["/tmp", "/tmp"]) end it "calls sleep with 0.5 when between 8 and 23:59 and custom sleep given" do now = double("now", :hour => 8, :strftime => "some time") Time.stub!(:now).and_return(now) Checksummer.should_receive(:sleep).with(0.5).at_least(1).times Checksummer.run_for_args(["/tmp", "/tmp", "--sleep", "500"]) end it "does not call sleep with 0.1 when between 00:00 and 07:59" do now = double("now", :hour => 7, :strftime => "some time") Time.stub!(:now).and_return(now) Checksummer.should_not_receive(:sleep) Checksummer.run_for_args(["/tmp", "/tmp"]) end end end describe "#integration" do let(:root) { File.expand_path("tmp", File.dirname(__FILE__)) } def time_for_index(index) Time.local(2010, 11, 12, 13, 14, index) end before(:each) do FileUtils.rm_rf(root) FileUtils.mkdir_p("#{root}/source") FileUtils.mkdir_p("#{root}/data") FileUtils.mkdir_p("#{root}/data/a/b/c/d/abcdefg") 1.upto(3).each do |i| path = "#{root}/source/file#{i}.txt" File.open(path, "w") { |f| f.puts "file #{i}" } FileUtils.touch("#{root}/source/file#{i}.txt", :mtime => time_for_index(i)) end FileUtils.ln_sf("#{root}/data/a/b/c/d/abcdefg", "#{root}/source/file4.txt") FileUtils.touch("#{root}/data/a/b/c/d/abcdefg", :mtime => time_for_index(4)) end it "checksums all files" do Checksummer.stub!(:puts) Checksummer.run_for_args(["#{root}/source", "#{root}/data"]) { "4/3/4/9/4349cfeff8e2eb74dffc369bb5fd084e" => ["file2.txt", time_for_index(2)], "9/c/3/8/9c38e8324dbf031557c89d53a39f0b26" => ["file3.txt", time_for_index(3)], "e/2/4/3/e243bb39c844b3543a7726576c869caf" => ["file1.txt", time_for_index(1)], "a/b/c/d/abcdefg" => ["file4.txt", time_for_index(4)] }.each do |data_file, (original_file, mtime)| original = Pathname.new("#{root}/source/#{original_file}") data = Pathname.new("#{root}/data/#{data_file}") File.should be_exists(original.to_s) File.should be_exists(data.to_s) original.realpath.to_s.should match(/\//) original.realpath.should == data File.mtime(original).should == mtime end end it "only runs on specific files when option given" do File.open("#{root}/source/test.mp3", "w") { |f| f.puts "some mp3" } Checksummer.stub!(:puts) Checksummer.run_for_args(["#{root}/source", "#{root}/data", "-name", %("*.mp3")]) File.should be_a_symlink("#{root}/source/test.mp3") File.should be_exists("#{root}/source/file1.txt") File.should_not be_a_symlink("#{root}/source/file1.txt") end end end