summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/util/checksums.rb57
-rwxr-xr-xspec/unit/util/checksums.rb105
2 files changed, 152 insertions, 10 deletions
diff --git a/lib/puppet/util/checksums.rb b/lib/puppet/util/checksums.rb
index 6f6ea59b5..0d45c6d5a 100644
--- a/lib/puppet/util/checksums.rb
+++ b/lib/puppet/util/checksums.rb
@@ -1,37 +1,74 @@
+# A stand-alone module for calculating checksums
+# in a generic way.
module Puppet::Util::Checksums
+ # Calculate a checksum using Digest::MD5.
def md5(content)
require 'digest/md5'
Digest::MD5.hexdigest(content)
end
+ # Calculate a checksum of the first 500 chars of the content using Digest::MD5.
+ def md5lite(content)
+ md5(content[0..499])
+ end
+
+ # Calculate a checksum of a file's content using Digest::MD5.
def md5_file(filename)
require 'digest/md5'
- incr_digest = Digest::MD5.new()
- File.open(filename, 'r') do |file|
- file.each_line do |line|
- incr_digest << line
- end
- end
+ digest = Digest::MD5.new()
+ return checksum_file(digest, filename)
+ end
+
+ # Calculate a checksum of the first 500 chars of a file's content using Digest::MD5.
+ def md5lite_file(filename)
+ File.open(filename, "r") { |f| return md5(f.read(500)) }
+ end
- return incr_digest.hexdigest
+ # Return the :mtime timestamp of a file.
+ def mtime_file(filename)
+ File.stat(filename).send(:mtime)
end
+ # Calculate a checksum using Digest::SHA1.
def sha1(content)
require 'digest/sha1'
Digest::SHA1.hexdigest(content)
end
+ # Calculate a checksum of the first 500 chars of the content using Digest::SHA1.
+ def sha1lite(content)
+ sha1(content[0..499])
+ end
+
+ # Calculate a checksum of a file's content using Digest::SHA1.
def sha1_file(filename)
require 'digest/sha1'
- incr_digest = Digest::SHA1.new()
+ digest = Digest::SHA1.new()
+ return checksum_file(digest, filename)
+ end
+
+ # Calculate a checksum of the first 500 chars of a file's content using Digest::SHA1.
+ def sha1lite_file(filename)
+ File.open(filename, "r") { |f| return sha1(f.read(500)) }
+ end
+
+ # Return the :ctime of a file.
+ def timestamp_file(filename)
+ File.stat(filename).send(:ctime)
+ end
+
+ private
+
+ # Perform an incremental checksum on a file.
+ def checksum_file(digest, filename)
File.open(filename, 'r') do |file|
file.each_line do |line|
- incr_digest << line
+ digest << line
end
end
- return incr_digest.hexdigest
+ return digest.hexdigest
end
end
diff --git a/spec/unit/util/checksums.rb b/spec/unit/util/checksums.rb
new file mode 100755
index 000000000..50c213538
--- /dev/null
+++ b/spec/unit/util/checksums.rb
@@ -0,0 +1,105 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke Kanies on 2007-9-22.
+# Copyright (c) 2007. All rights reserved.
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/util/checksums'
+
+describe Puppet::Util::Checksums do
+ before do
+ @summer = Object.new
+ @summer.extend(Puppet::Util::Checksums)
+ end
+
+ class LineYielder
+ def initialize(content)
+ @content = content
+ end
+
+ def each_line
+ @content.split("\n").each { |line| yield line }
+ end
+ end
+
+ content_sums = [:md5, :md5lite, :sha1, :sha1lite]
+ file_only = [:timestamp, :mtime]
+
+ content_sums.each do |sumtype|
+ it "should be able to calculate %s sums from strings" % sumtype do
+ @summer.should be_respond_to(sumtype)
+ end
+ end
+
+ [content_sums, file_only].flatten.each do |sumtype|
+ it "should be able to calculate %s sums from files" % sumtype do
+ @summer.should be_respond_to(sumtype.to_s + "_file")
+ end
+ end
+
+ {:md5 => Digest::MD5, :sha1 => Digest::SHA1}.each do |sum, klass|
+ describe("when using %s" % sum) do
+ it "should use #{klass} to calculate string checksums" do
+ klass.expects(:hexdigest).with("mycontent").returns "whatever"
+ @summer.send(sum, "mycontent").should == "whatever"
+ end
+
+ it "should use incremental #{klass} sums to calculate file checksums" do
+ digest = mock 'digest'
+ klass.expects(:new).returns digest
+
+ file = "/path/to/my/file"
+
+ # Mocha doesn't seem to be able to mock multiple yields, yay.
+ fh = LineYielder.new("firstline\nsecondline")
+
+ File.expects(:open).with(file, "r").yields(fh)
+
+ digest.expects(:<<).with "firstline"
+ digest.expects(:<<).with "secondline"
+ digest.expects(:hexdigest).returns :mydigest
+
+ @summer.send(sum.to_s + "_file", file).should == :mydigest
+ end
+ end
+ end
+
+ {:md5lite => Digest::MD5, :sha1lite => Digest::SHA1}.each do |sum, klass|
+ describe("when using %s" % sum) do
+ it "should use #{klass} to calculate string checksums from the first 500 characters of the string" do
+ content = "this is a test" * 100
+ klass.expects(:hexdigest).with(content[0..499]).returns "whatever"
+ @summer.send(sum, content).should == "whatever"
+ end
+
+ it "should use #{klass} to calculate a sum from the first 500 characters in the file" do
+ digest = mock 'digest'
+
+ file = "/path/to/my/file"
+
+ fh = mock 'filehandle'
+ File.expects(:open).with(file, "r").yields(fh)
+
+ fh.expects(:read).with(500).returns('my content')
+
+ klass.expects(:hexdigest).with("my content").returns :mydigest
+
+ @summer.send(sum.to_s + "_file", file).should == :mydigest
+ end
+ end
+ end
+
+ {:timestamp => :ctime, :mtime => :mtime}.each do |sum, method|
+ describe("when using %s" % sum) do
+ it "should use the '#{method}' on the file to determine the timestamp" do
+ file = "/my/file"
+ stat = mock 'stat', method => "mysum"
+
+ File.expects(:stat).with(file).returns(stat)
+
+ @summer.send(sum.to_s + "_file", file).should == "mysum"
+ end
+ end
+ end
+end