rscons 0.0.14 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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