rscons 0.0.14 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -64,20 +64,16 @@ module Rscons
64
64
  expand_varref(ent)
65
65
  end.flatten
66
66
  else
67
- if varref =~ /^(.*)\$\[(\w+)\](.*)$/
68
- # expand array with given prefix, suffix
67
+ if varref =~ /^(.*)\$\{([^}]+)\}(.*)$/
69
68
  prefix, varname, suffix = $1, $2, $3
70
69
  varval = expand_varref(@vars[varname])
71
- unless varval.is_a?(Array)
72
- raise "Array expected for $#{varname}"
70
+ if varval.is_a?(String)
71
+ expand_varref("#{prefix}#{varval}#{suffix}")
72
+ elsif varval.is_a?(Array)
73
+ varval.map {|vv| expand_varref("#{prefix}#{vv}#{suffix}")}.flatten
74
+ else
75
+ raise "I do not know how to expand a variable reference to a #{varval.class.name}"
73
76
  end
74
- varval.map {|e| "#{prefix}#{e}#{suffix}"}
75
- elsif varref =~ /^\$(.*)$/
76
- # expand a single variable reference
77
- varname = $1
78
- varval = expand_varref(@vars[varname])
79
- varval or raise "Could not find variable #{varname.inspect}"
80
- expand_varref(varval)
81
77
  else
82
78
  varref
83
79
  end
@@ -1,4 +1,4 @@
1
1
  module Rscons
2
2
  # gem version
3
- VERSION = "0.0.14"
3
+ VERSION = "0.1.0"
4
4
  end
@@ -1,45 +1,31 @@
1
1
  require 'fileutils'
2
2
 
3
3
  describe Rscons do
4
+ BUILD_TEST_RUN_DIR = "build_test_run"
5
+
4
6
  before(:all) do
5
- FileUtils.rm_rf('build_tests_run')
7
+ FileUtils.rm_rf(BUILD_TEST_RUN_DIR)
6
8
  @owd = Dir.pwd
7
9
  end
8
10
 
9
- after(:each) do
10
- Dir.chdir(@owd)
11
- FileUtils.rm_rf('build_tests_run')
11
+ before(:each) do
12
+ @output = ""
13
+ $stdout.stub(:write) do |content|
14
+ @output += content
15
+ end
16
+ $stderr.stub(:write) do |content|
17
+ @output += content
18
+ end
12
19
  end
13
20
 
14
- def build_testdir(build_script = "build.rb")
15
- if File.exists?(build_script)
16
- build_rb = File.read(build_script)
17
- File.open(build_script, "w") do |fh|
18
- fh.puts(<<EOF + build_rb)
19
- require "simplecov"
20
-
21
- SimpleCov.start do
22
- root("#{@owd}")
23
- command_name("build_test_#{@build_test_name}")
24
- add_filter("spec")
25
- end
26
-
27
- require "rscons"
28
- EOF
29
- end
30
- IO.popen(%{ruby -I #{@owd}/lib #{build_script}}) do |io|
31
- io.readlines.reject do |line|
32
- line =~ /^Coverage report/
33
- end
34
- end.map(&:strip)
35
- end
21
+ after(:each) do
22
+ Dir.chdir(@owd)
23
+ FileUtils.rm_rf(BUILD_TEST_RUN_DIR)
36
24
  end
37
25
 
38
- def test_dir(build_test_directory, build_script = "build.rb")
39
- @build_test_name = build_test_directory
40
- FileUtils.cp_r("build_tests/#{build_test_directory}", 'build_tests_run')
41
- Dir.chdir("build_tests_run")
42
- build_testdir(build_script)
26
+ def test_dir(build_test_directory)
27
+ FileUtils.cp_r("build_tests/#{build_test_directory}", BUILD_TEST_RUN_DIR)
28
+ Dir.chdir(BUILD_TEST_RUN_DIR)
43
29
  end
44
30
 
45
31
  def file_sub(fname)
@@ -53,18 +39,31 @@ EOF
53
39
  end
54
40
  end
55
41
 
42
+ def lines
43
+ @output.lines.map(&:rstrip).tap do |v|
44
+ @output = ""
45
+ end
46
+ end
47
+
56
48
  ###########################################################################
57
49
  # Tests
58
50
  ###########################################################################
59
51
 
60
52
  it 'builds a C program with one source file' do
61
53
  test_dir('simple')
54
+ Rscons::Environment.new do |env|
55
+ env.Program('simple', Dir['*.c'])
56
+ end
62
57
  File.exists?('simple.o').should be_true
63
58
  `./simple`.should == "This is a simple C program\n"
64
59
  end
65
60
 
66
61
  it 'prints commands as they are executed' do
67
- lines = test_dir('simple')
62
+ test_dir('simple')
63
+ Rscons::Environment.new(echo: :command) do |env|
64
+ env["LD"] = "gcc"
65
+ env.Program('simple', Dir['*.c'])
66
+ end
68
67
  lines.should == [
69
68
  'gcc -c -o simple.o -MMD -MF simple.mf simple.c',
70
69
  'gcc -o simple simple.o',
@@ -72,7 +71,10 @@ EOF
72
71
  end
73
72
 
74
73
  it 'prints short representations of the commands being executed' do
75
- lines = test_dir('header')
74
+ test_dir('header')
75
+ Rscons::Environment.new do |env|
76
+ env.Program('header', Dir['*.c'])
77
+ end
76
78
  lines.should == [
77
79
  'CC header.o',
78
80
  'LD header',
@@ -81,61 +83,88 @@ EOF
81
83
 
82
84
  it 'builds a C program with one source file and one header file' do
83
85
  test_dir('header')
86
+ Rscons::Environment.new do |env|
87
+ env.Program('header', Dir['*.c'])
88
+ end
84
89
  File.exists?('header.o').should be_true
85
90
  `./header`.should == "The value is 2\n"
86
91
  end
87
92
 
88
93
  it 'rebuilds a C module when a header it depends on changes' do
89
94
  test_dir('header')
95
+ env = Rscons::Environment.new do |env|
96
+ env.Program('header', Dir['*.c'])
97
+ end
90
98
  `./header`.should == "The value is 2\n"
91
99
  file_sub('header.h') {|line| line.sub(/2/, '5')}
92
- build_testdir
100
+ env.process
93
101
  `./header`.should == "The value is 5\n"
94
102
  end
95
103
 
96
104
  it 'does not rebuild a C module when its dependencies have not changed' do
97
- lines = test_dir('header')
105
+ test_dir('header')
106
+ env = Rscons::Environment.new do |env|
107
+ env.Program('header', Dir['*.c'])
108
+ end
98
109
  `./header`.should == "The value is 2\n"
99
110
  lines.should == [
100
111
  'CC header.o',
101
112
  'LD header',
102
113
  ]
103
- lines = build_testdir
114
+ env.process
104
115
  lines.should == []
105
116
  end
106
117
 
107
118
  it "does not rebuild a C module when only the file's timestampe has changed" do
108
- lines = test_dir('header')
119
+ test_dir('header')
120
+ env = Rscons::Environment.new do |env|
121
+ env.Program('header', Dir['*.c'])
122
+ end
109
123
  `./header`.should == "The value is 2\n"
110
124
  lines.should == [
111
125
  'CC header.o',
112
126
  'LD header',
113
127
  ]
114
128
  file_sub('header.c') {|line| line}
115
- lines = build_testdir
129
+ env.process
116
130
  lines.should == []
117
131
  end
118
132
 
119
133
  it 're-links a program when the link flags have changed' do
120
- lines = test_dir('simple')
134
+ test_dir('simple')
135
+ Rscons::Environment.new(echo: :command) do |env|
136
+ env.Program('simple', Dir['*.c'])
137
+ end
121
138
  lines.should == [
122
139
  'gcc -c -o simple.o -MMD -MF simple.mf simple.c',
123
140
  'gcc -o simple simple.o',
124
141
  ]
125
- file_sub('build.rb') {|line| line.sub(/.*CHANGE.FLAGS.*/, ' env["LIBS"] += ["c"]')}
126
- lines = build_testdir
142
+ Rscons::Environment.new(echo: :command) do |env|
143
+ env["LIBS"] += ["c"]
144
+ env.Program('simple', Dir['*.c'])
145
+ end
127
146
  lines.should == ['gcc -o simple simple.o -lc']
128
147
  end
129
148
 
130
149
  it 'builds object files in a different build directory' do
131
- lines = test_dir('build_dir')
150
+ test_dir('build_dir')
151
+ Rscons::Environment.new do |env|
152
+ env.append('CPPPATH' => Dir['src/**/*/'])
153
+ env.build_dir(%r{^src/([^/]+)/}, 'build_\\1/')
154
+ env.Program('build_dir', Dir['src/**/*.c'])
155
+ end
132
156
  `./build_dir`.should == "Hello from two()\n"
133
157
  File.exists?('build_one/one.o').should be_true
134
158
  File.exists?('build_two/two.o').should be_true
135
159
  end
136
160
 
137
161
  it 'cleans built files' do
138
- lines = test_dir('build_dir')
162
+ test_dir('build_dir')
163
+ Rscons::Environment.new do |env|
164
+ env.append('CPPPATH' => Dir['src/**/*/'])
165
+ env.build_dir(%r{^src/([^/]+)/}, 'build_\\1/')
166
+ env.Program('build_dir', Dir['src/**/*.c'])
167
+ end
139
168
  `./build_dir`.should == "Hello from two()\n"
140
169
  File.exists?('build_one/one.o').should be_true
141
170
  File.exists?('build_two/two.o').should be_true
@@ -148,7 +177,12 @@ EOF
148
177
  end
149
178
 
150
179
  it 'does not clean created directories if other non-rscons-generated files reside there' do
151
- lines = test_dir('build_dir')
180
+ test_dir('build_dir')
181
+ Rscons::Environment.new do |env|
182
+ env.append('CPPPATH' => Dir['src/**/*/'])
183
+ env.build_dir(%r{^src/([^/]+)/}, 'build_\\1/')
184
+ env.Program('build_dir', Dir['src/**/*.c'])
185
+ end
152
186
  `./build_dir`.should == "Hello from two()\n"
153
187
  File.exists?('build_one/one.o').should be_true
154
188
  File.exists?('build_two/two.o').should be_true
@@ -162,14 +196,44 @@ EOF
162
196
  end
163
197
 
164
198
  it 'allows Ruby classes as custom builders to be used to construct files' do
165
- lines = test_dir('custom_builder')
199
+ test_dir('custom_builder')
200
+ class MySource < Rscons::Builder
201
+ def run(target, sources, cache, env, vars = {})
202
+ File.open(target, 'w') do |fh|
203
+ fh.puts <<EOF
204
+ #define THE_VALUE 5678
205
+ EOF
206
+ end
207
+ target
208
+ end
209
+ end
210
+
211
+ Rscons::Environment.new do |env|
212
+ env.add_builder(MySource.new)
213
+ env.MySource('inc.h', [])
214
+ env.Program('program', Dir['*.c'])
215
+ end
216
+
166
217
  lines.should == ['CC program.o', 'LD program']
167
218
  File.exists?('inc.h').should be_true
168
219
  `./program`.should == "The value is 5678\n"
169
220
  end
170
221
 
171
222
  it 'allows cloning Environment objects' do
172
- lines = test_dir('clone_env')
223
+ test_dir('clone_env')
224
+
225
+ debug = Rscons::Environment.new(echo: :command) do |env|
226
+ env.build_dir('src', 'debug')
227
+ env['CFLAGS'] = '-O2'
228
+ env['CPPFLAGS'] = '-DSTRING="Debug Version"'
229
+ env.Program('program-debug', Dir['src/*.c'])
230
+ end
231
+
232
+ release = debug.clone('CPPFLAGS' => '-DSTRING="Release Version"') do |env|
233
+ env.build_dir('src', 'release')
234
+ env.Program('program-release', Dir['src/*.c'])
235
+ end
236
+
173
237
  lines.should == [
174
238
  %q{gcc -c -o debug/program.o -MMD -MF debug/program.mf '-DSTRING="Debug Version"' -O2 src/program.c},
175
239
  %q{gcc -o program-debug debug/program.o},
@@ -180,12 +244,19 @@ EOF
180
244
 
181
245
  it 'builds a C++ program with one source file' do
182
246
  test_dir('simple_cc')
247
+ Rscons::Environment.new do |env|
248
+ env.Program('simple', Dir['*.cc'])
249
+ end
183
250
  File.exists?('simple.o').should be_true
184
251
  `./simple`.should == "This is a simple C++ program\n"
185
252
  end
186
253
 
187
254
  it 'allows overriding construction variables for individual builder calls' do
188
- lines = test_dir('two_sources')
255
+ test_dir('two_sources')
256
+ Rscons::Environment.new(echo: :command) do |env|
257
+ env.Object("one.o", "one.c", 'CPPFLAGS' => ['-DONE'])
258
+ env.Program('two_sources', ['one.o', 'two.c'])
259
+ end
189
260
  lines.should == [
190
261
  'gcc -c -o one.o -MMD -MF one.mf -DONE one.c',
191
262
  'gcc -c -o two.o -MMD -MF two.mf two.c',
@@ -196,7 +267,11 @@ EOF
196
267
  end
197
268
 
198
269
  it 'builds a static library archive' do
199
- lines = test_dir('library')
270
+ test_dir('library')
271
+ Rscons::Environment.new(echo: :command) do |env|
272
+ env.Program('library', ['lib.a', 'three.c'])
273
+ env.Library("lib.a", ['one.c', 'two.c'], 'CPPFLAGS' => ['-Dmake_lib'])
274
+ end
200
275
  lines.should == [
201
276
  'gcc -c -o one.o -MMD -MF one.mf -Dmake_lib one.c',
202
277
  'gcc -c -o two.o -MMD -MF two.mf -Dmake_lib two.c',
@@ -208,13 +283,39 @@ EOF
208
283
  `ar t lib.a`.should == "one.o\ntwo.o\n"
209
284
  end
210
285
 
211
- it 'supports tweakers to override construction variables' do
212
- lines = test_dir("build_dir", "tweaker_build.rb")
213
- `./tweaker`.should == "Hello from two()\n"
286
+ it 'supports build hooks to override construction variables' do
287
+ test_dir("build_dir")
288
+ Rscons::Environment.new(echo: :command) do |env|
289
+ env.append('CPPPATH' => Dir['src/**/*/'])
290
+ env.build_dir(%r{^src/([^/]+)/}, 'build_\\1/')
291
+ env.add_build_hook do |build_op|
292
+ if build_op[:target] =~ %r{build_one/.*\.o}
293
+ build_op[:vars]["CFLAGS"] << "-O1"
294
+ elsif build_op[:target] =~ %r{build_two/.*\.o}
295
+ build_op[:vars]["CFLAGS"] << "-O2"
296
+ end
297
+ end
298
+ env.Program('build_hook', Dir['src/**/*.c'])
299
+ end
300
+ `./build_hook`.should == "Hello from two()\n"
214
301
  lines.should =~ [
215
302
  'gcc -c -o build_one/one.o -MMD -MF build_one/one.mf -Isrc/one/ -Isrc/two/ -O1 src/one/one.c',
216
303
  'gcc -c -o build_two/two.o -MMD -MF build_two/two.mf -Isrc/one/ -Isrc/two/ -O2 src/two/two.c',
217
- 'gcc -o tweaker build_one/one.o build_two/two.o',
304
+ 'gcc -o build_hook build_one/one.o build_two/two.o',
218
305
  ]
219
306
  end
307
+
308
+ unless ENV["omit_gdc_tests"]
309
+ it "supports building D sources" do
310
+ test_dir("d")
311
+ Rscons::Environment.new(echo: :command) do |env|
312
+ env.Program("hello-d", Dir["*.d"])
313
+ end
314
+ lines.should == [
315
+ "gdc -c -o main.o main.d",
316
+ "gdc -o hello-d main.o",
317
+ ]
318
+ `./hello-d`.rstrip.should == "Hello from D!"
319
+ end
320
+ end
220
321
  end
@@ -0,0 +1,204 @@
1
+ module Rscons
2
+ describe Cache do
3
+ before do
4
+ File.stub(:read) { nil }
5
+ end
6
+
7
+ def build_from(cache)
8
+ YAML.should_receive(:load).and_return(cache)
9
+ Cache.new
10
+ end
11
+
12
+ describe ".clear" do
13
+ it "removes the cache file" do
14
+ FileUtils.should_receive(:rm_f).with(Cache::CACHE_FILE)
15
+ Cache.clear
16
+ end
17
+ end
18
+
19
+ describe "#initialize" do
20
+ context "when corrupt" do
21
+ it "prints a warning and defaults to an empty hash" do
22
+ YAML.should_receive(:load).and_return("string")
23
+ $stderr.should_receive(:puts).with(/Warning:.*was.corrupt/)
24
+ Cache.new.instance_variable_get(:@cache).is_a?(Hash).should be_true
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "#write" do
30
+ it "should fill in :version and write to file" do
31
+ cache = {}
32
+ fh = $stdout
33
+ fh.should_receive(:puts)
34
+ File.should_receive(:open).and_yield(fh)
35
+ build_from(cache).write
36
+ cache[:version].should == Rscons::VERSION
37
+ end
38
+ end
39
+
40
+ describe "#up_to_date?" do
41
+ it "returns false when target file does not exist" do
42
+ File.should_receive(:exists?).with("target").and_return(false)
43
+ build_from({}).up_to_date?("target", "command", []).should be_false
44
+ end
45
+
46
+ it "returns false when target is not registered in the cache" do
47
+ File.should_receive(:exists?).with("target").and_return(true)
48
+ build_from({}).up_to_date?("target", "command", []).should be_false
49
+ end
50
+
51
+ it "returns false when the target's checksum does not match" do
52
+ _cache = {targets: {"target" => {checksum: "abc"}}}
53
+ cache = build_from(_cache)
54
+ File.should_receive(:exists?).with("target").and_return(true)
55
+ cache.should_receive(:calculate_checksum).with("target").and_return("def")
56
+ cache.up_to_date?("target", "command", []).should be_false
57
+ end
58
+
59
+ it "returns false when the build command has changed" do
60
+ _cache = {targets: {"target" => {checksum: "abc", command: "old command"}}}
61
+ cache = build_from(_cache)
62
+ File.should_receive(:exists?).with("target").and_return(true)
63
+ cache.should_receive(:calculate_checksum).with("target").and_return("abc")
64
+ cache.up_to_date?("target", "command", []).should be_false
65
+ end
66
+
67
+ it "returns false when there is a new dependency" do
68
+ _cache = {targets: {"target" => {checksum: "abc",
69
+ command: "command",
70
+ deps: [{fname: "dep.1"}]}}}
71
+ cache = build_from(_cache)
72
+ File.should_receive(:exists?).with("target").and_return(true)
73
+ cache.should_receive(:calculate_checksum).with("target").and_return("abc")
74
+ cache.up_to_date?("target", "command", ["dep.1", "dep.2"]).should be_false
75
+ end
76
+
77
+ it "returns false when a dependency's checksum has changed" do
78
+ _cache = {targets: {"target" => {checksum: "abc",
79
+ command: "command",
80
+ deps: [{fname: "dep.1",
81
+ checksum: "dep.1.chk"},
82
+ {fname: "dep.2",
83
+ checksum: "dep.2.chk"},
84
+ {fname: "extra.dep",
85
+ checksum: "extra.dep.chk"}]}}}
86
+ cache = build_from(_cache)
87
+ File.should_receive(:exists?).with("target").and_return(true)
88
+ cache.should_receive(:calculate_checksum).with("target").and_return("abc")
89
+ cache.should_receive(:calculate_checksum).with("dep.1").and_return("dep.1.chk")
90
+ cache.should_receive(:calculate_checksum).with("dep.2").and_return("dep.2.changed")
91
+ cache.up_to_date?("target", "command", ["dep.1", "dep.2"]).should be_false
92
+ end
93
+
94
+ it "returns false with strict_deps=true when cache has an extra dependency" do
95
+ _cache = {targets: {"target" => {checksum: "abc",
96
+ command: "command",
97
+ deps: [{fname: "dep.1",
98
+ checksum: "dep.1.chk"},
99
+ {fname: "dep.2",
100
+ checksum: "dep.2.chk"},
101
+ {fname: "extra.dep",
102
+ checksum: "extra.dep.chk"}]}}}
103
+ cache = build_from(_cache)
104
+ File.should_receive(:exists?).with("target").and_return(true)
105
+ cache.should_receive(:calculate_checksum).with("target").and_return("abc")
106
+ cache.up_to_date?("target", "command", ["dep.1", "dep.2"], strict_deps: true).should be_false
107
+ end
108
+
109
+ it "returns true when no condition for false is met" do
110
+ _cache = {targets: {"target" => {checksum: "abc",
111
+ command: "command",
112
+ deps: [{fname: "dep.1",
113
+ checksum: "dep.1.chk"},
114
+ {fname: "dep.2",
115
+ checksum: "dep.2.chk"},
116
+ {fname: "extra.dep",
117
+ checksum: "extra.dep.chk"}]}}}
118
+ cache = build_from(_cache)
119
+ File.should_receive(:exists?).with("target").and_return(true)
120
+ cache.should_receive(:calculate_checksum).with("target").and_return("abc")
121
+ cache.should_receive(:calculate_checksum).with("dep.1").and_return("dep.1.chk")
122
+ cache.should_receive(:calculate_checksum).with("dep.2").and_return("dep.2.chk")
123
+ cache.should_receive(:calculate_checksum).with("extra.dep").and_return("extra.dep.chk")
124
+ cache.up_to_date?("target", "command", ["dep.1", "dep.2"]).should be_true
125
+ end
126
+ end
127
+
128
+ describe "#register_build" do
129
+ it "stores the given information in the cache" do
130
+ _cache = {}
131
+ cache = build_from(_cache)
132
+ cache.should_receive(:calculate_checksum).with("the target").and_return("the checksum")
133
+ cache.should_receive(:calculate_checksum).with("dep 1").and_return("dep 1 checksum")
134
+ cache.should_receive(:calculate_checksum).with("dep 2").and_return("dep 2 checksum")
135
+ cache.register_build("the target", "the command", ["dep 1", "dep 2"])
136
+ cached_target = cache.instance_variable_get(:@cache)[:targets]["the target"]
137
+ cached_target.should_not be_nil
138
+ cached_target[:command].should == "the command"
139
+ cached_target[:checksum].should == "the checksum"
140
+ cached_target[:deps].should == [
141
+ {fname: "dep 1", checksum: "dep 1 checksum"},
142
+ {fname: "dep 2", checksum: "dep 2 checksum"},
143
+ ]
144
+ end
145
+ end
146
+
147
+ describe "#targets" do
148
+ it "returns a list of targets that are cached" do
149
+ cache = {targets: {"t1" => {}, "t2" => {}, "t3" => {}}}
150
+ build_from(cache).targets.should == ["t1", "t2", "t3"]
151
+ end
152
+ end
153
+
154
+ describe "#mkdir_p" do
155
+ it "makes directories and records any created in the cache" do
156
+ _cache = {}
157
+ cache = build_from(_cache)
158
+ File.should_receive(:exists?).with("one").and_return(true)
159
+ File.should_receive(:exists?).with("one/two").and_return(false)
160
+ FileUtils.should_receive(:mkdir).with("one/two")
161
+ File.should_receive(:exists?).with("one/two/three").and_return(false)
162
+ FileUtils.should_receive(:mkdir).with("one/two/three")
163
+ File.should_receive(:exists?).with("one").and_return(true)
164
+ File.should_receive(:exists?).with("one/two").and_return(true)
165
+ File.should_receive(:exists?).with("one/two/four").and_return(false)
166
+ FileUtils.should_receive(:mkdir).with("one/two/four")
167
+ cache.mkdir_p("one/two/three")
168
+ cache.mkdir_p("one\\two\\four")
169
+ cache.directories.should == ["one/two", "one/two/three", "one/two/four"]
170
+ end
171
+ end
172
+
173
+ describe "#directories" do
174
+ it "returns a list of directories that are cached" do
175
+ _cache = {directories: {"dir1" => true, "dir2" => true}}
176
+ build_from(_cache).directories.should == ["dir1", "dir2"]
177
+ end
178
+ end
179
+
180
+ describe "#lookup_checksum" do
181
+ it "does not re-calculate the checksum when it is already cached" do
182
+ cache = build_from({})
183
+ cache.instance_variable_set(:@lookup_checksums, {"f1" => "f1.chk"})
184
+ cache.should_not_receive(:calculate_checksum)
185
+ cache.send(:lookup_checksum, "f1").should == "f1.chk"
186
+ end
187
+
188
+ it "calls calculate_checksum when the checksum is not cached" do
189
+ cache = build_from({})
190
+ cache.should_receive(:calculate_checksum).with("f1").and_return("ck")
191
+ cache.send(:lookup_checksum, "f1").should == "ck"
192
+ end
193
+ end
194
+
195
+ describe "#calculate_checksum" do
196
+ it "calculates the MD5 of the file contents" do
197
+ contents = "contents"
198
+ File.should_receive(:read).with("fname", mode: "rb").and_return(contents)
199
+ Digest::MD5.should_receive(:hexdigest).with(contents).and_return("the_checksum")
200
+ build_from({}).send(:calculate_checksum, "fname").should == "the_checksum"
201
+ end
202
+ end
203
+ end
204
+ end