1 // Copyright 2012 Google Inc. All Rights Reserved.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
28 const char kTestFilename[] = "DepsLogTest-tempfile";
30 struct DepsLogTest : public testing::Test {
31 virtual void SetUp() {
32 // In case a crashing test left a stale file behind.
33 unlink(kTestFilename);
35 virtual void TearDown() {
36 unlink(kTestFilename);
40 TEST_F(DepsLogTest, WriteRead) {
44 EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err));
49 deps.push_back(state1.GetNode("foo.h", 0));
50 deps.push_back(state1.GetNode("bar.h", 0));
51 log1.RecordDeps(state1.GetNode("out.o", 0), 1, deps);
54 deps.push_back(state1.GetNode("foo.h", 0));
55 deps.push_back(state1.GetNode("bar2.h", 0));
56 log1.RecordDeps(state1.GetNode("out2.o", 0), 2, deps);
58 DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o", 0));
59 ASSERT_TRUE(log_deps);
60 ASSERT_EQ(1, log_deps->mtime);
61 ASSERT_EQ(2, log_deps->node_count);
62 ASSERT_EQ("foo.h", log_deps->nodes[0]->path());
63 ASSERT_EQ("bar.h", log_deps->nodes[1]->path());
70 EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err));
73 ASSERT_EQ(log1.nodes().size(), log2.nodes().size());
74 for (int i = 0; i < (int)log1.nodes().size(); ++i) {
75 Node* node1 = log1.nodes()[i];
76 Node* node2 = log2.nodes()[i];
77 ASSERT_EQ(i, node1->id());
78 ASSERT_EQ(node1->id(), node2->id());
81 // Spot-check the entries in log2.
82 DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out2.o", 0));
83 ASSERT_TRUE(log_deps);
84 ASSERT_EQ(2, log_deps->mtime);
85 ASSERT_EQ(2, log_deps->node_count);
86 ASSERT_EQ("foo.h", log_deps->nodes[0]->path());
87 ASSERT_EQ("bar2.h", log_deps->nodes[1]->path());
90 TEST_F(DepsLogTest, LotsOfDeps) {
91 const int kNumDeps = 100000; // More than 64k.
96 EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err));
101 for (int i = 0; i < kNumDeps; ++i) {
103 sprintf(buf, "file%d.h", i);
104 deps.push_back(state1.GetNode(buf, 0));
106 log1.RecordDeps(state1.GetNode("out.o", 0), 1, deps);
108 DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o", 0));
109 ASSERT_EQ(kNumDeps, log_deps->node_count);
116 EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err));
119 DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out.o", 0));
120 ASSERT_EQ(kNumDeps, log_deps->node_count);
123 // Verify that adding the same deps twice doesn't grow the file.
124 TEST_F(DepsLogTest, DoubleEntry) {
125 // Write some deps to the file and grab its size.
131 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
135 deps.push_back(state.GetNode("foo.h", 0));
136 deps.push_back(state.GetNode("bar.h", 0));
137 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
141 ASSERT_EQ(0, stat(kTestFilename, &st));
142 file_size = (int)st.st_size;
143 ASSERT_GT(file_size, 0);
146 // Now reload the file, and readd the same deps.
151 EXPECT_TRUE(log.Load(kTestFilename, &state, &err));
153 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
157 deps.push_back(state.GetNode("foo.h", 0));
158 deps.push_back(state.GetNode("bar.h", 0));
159 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
163 ASSERT_EQ(0, stat(kTestFilename, &st));
164 int file_size_2 = (int)st.st_size;
165 ASSERT_EQ(file_size, file_size_2);
169 // Verify that adding the new deps works and can be compacted away.
170 TEST_F(DepsLogTest, Recompact) {
171 const char kManifest[] =
176 "build other_out.o: cc\n";
178 // Write some deps to the file and grab its size.
182 ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest));
185 ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err));
189 deps.push_back(state.GetNode("foo.h", 0));
190 deps.push_back(state.GetNode("bar.h", 0));
191 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
194 deps.push_back(state.GetNode("foo.h", 0));
195 deps.push_back(state.GetNode("baz.h", 0));
196 log.RecordDeps(state.GetNode("other_out.o", 0), 1, deps);
201 ASSERT_EQ(0, stat(kTestFilename, &st));
202 file_size = (int)st.st_size;
203 ASSERT_GT(file_size, 0);
206 // Now reload the file, and add slighly different deps.
210 ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest));
213 ASSERT_TRUE(log.Load(kTestFilename, &state, &err));
215 ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err));
219 deps.push_back(state.GetNode("foo.h", 0));
220 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
224 ASSERT_EQ(0, stat(kTestFilename, &st));
225 file_size_2 = (int)st.st_size;
226 // The file should grow to record the new deps.
227 ASSERT_GT(file_size_2, file_size);
230 // Now reload the file, verify the new deps have replaced the old, then
235 ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest));
238 ASSERT_TRUE(log.Load(kTestFilename, &state, &err));
240 Node* out = state.GetNode("out.o", 0);
241 DepsLog::Deps* deps = log.GetDeps(out);
243 ASSERT_EQ(1, deps->mtime);
244 ASSERT_EQ(1, deps->node_count);
245 ASSERT_EQ("foo.h", deps->nodes[0]->path());
247 Node* other_out = state.GetNode("other_out.o", 0);
248 deps = log.GetDeps(other_out);
250 ASSERT_EQ(1, deps->mtime);
251 ASSERT_EQ(2, deps->node_count);
252 ASSERT_EQ("foo.h", deps->nodes[0]->path());
253 ASSERT_EQ("baz.h", deps->nodes[1]->path());
255 ASSERT_TRUE(log.Recompact(kTestFilename, &err));
257 // The in-memory deps graph should still be valid after recompaction.
258 deps = log.GetDeps(out);
260 ASSERT_EQ(1, deps->mtime);
261 ASSERT_EQ(1, deps->node_count);
262 ASSERT_EQ("foo.h", deps->nodes[0]->path());
263 ASSERT_EQ(out, log.nodes()[out->id()]);
265 deps = log.GetDeps(other_out);
267 ASSERT_EQ(1, deps->mtime);
268 ASSERT_EQ(2, deps->node_count);
269 ASSERT_EQ("foo.h", deps->nodes[0]->path());
270 ASSERT_EQ("baz.h", deps->nodes[1]->path());
271 ASSERT_EQ(other_out, log.nodes()[other_out->id()]);
273 // The file should have shrunk a bit for the smaller deps.
275 ASSERT_EQ(0, stat(kTestFilename, &st));
276 file_size_3 = (int)st.st_size;
277 ASSERT_LT(file_size_3, file_size_2);
280 // Now reload the file and recompact with an empty manifest. The previous
281 // entries should be removed.
284 // Intentionally not parsing kManifest here.
287 ASSERT_TRUE(log.Load(kTestFilename, &state, &err));
289 Node* out = state.GetNode("out.o", 0);
290 DepsLog::Deps* deps = log.GetDeps(out);
292 ASSERT_EQ(1, deps->mtime);
293 ASSERT_EQ(1, deps->node_count);
294 ASSERT_EQ("foo.h", deps->nodes[0]->path());
296 Node* other_out = state.GetNode("other_out.o", 0);
297 deps = log.GetDeps(other_out);
299 ASSERT_EQ(1, deps->mtime);
300 ASSERT_EQ(2, deps->node_count);
301 ASSERT_EQ("foo.h", deps->nodes[0]->path());
302 ASSERT_EQ("baz.h", deps->nodes[1]->path());
304 ASSERT_TRUE(log.Recompact(kTestFilename, &err));
306 // The previous entries should have been removed.
307 deps = log.GetDeps(out);
310 deps = log.GetDeps(other_out);
313 // The .h files pulled in via deps should no longer have ids either.
314 ASSERT_EQ(-1, state.LookupNode("foo.h")->id());
315 ASSERT_EQ(-1, state.LookupNode("baz.h")->id());
317 // The file should have shrunk more.
319 ASSERT_EQ(0, stat(kTestFilename, &st));
320 int file_size_4 = (int)st.st_size;
321 ASSERT_LT(file_size_4, file_size_3);
325 // Verify that invalid file headers cause a new build.
326 TEST_F(DepsLogTest, InvalidHeader) {
327 const char *kInvalidHeaders[] = {
329 "# ninjad", // Truncated first line.
330 "# ninjadeps\n", // No version int.
331 "# ninjadeps\n\001\002", // Truncated version int.
332 "# ninjadeps\n\001\002\003\004" // Invalid version int.
334 for (size_t i = 0; i < sizeof(kInvalidHeaders) / sizeof(kInvalidHeaders[0]);
336 FILE* deps_log = fopen(kTestFilename, "wb");
337 ASSERT_TRUE(deps_log != NULL);
339 strlen(kInvalidHeaders[i]),
340 fwrite(kInvalidHeaders[i], 1, strlen(kInvalidHeaders[i]), deps_log));
341 ASSERT_EQ(0 ,fclose(deps_log));
346 ASSERT_TRUE(log.Load(kTestFilename, &state, &err));
347 EXPECT_EQ("bad deps log signature or version; starting over", err);
351 // Simulate what happens when loading a truncated log file.
352 TEST_F(DepsLogTest, Truncated) {
353 // Create a file with some entries.
358 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
362 deps.push_back(state.GetNode("foo.h", 0));
363 deps.push_back(state.GetNode("bar.h", 0));
364 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
367 deps.push_back(state.GetNode("foo.h", 0));
368 deps.push_back(state.GetNode("bar2.h", 0));
369 log.RecordDeps(state.GetNode("out2.o", 0), 2, deps);
374 // Get the file size.
376 ASSERT_EQ(0, stat(kTestFilename, &st));
378 // Try reloading at truncated sizes.
379 // Track how many nodes/deps were found; they should decrease with
383 for (int size = (int)st.st_size; size > 0; --size) {
385 ASSERT_TRUE(Truncate(kTestFilename, size, &err));
389 EXPECT_TRUE(log.Load(kTestFilename, &state, &err));
391 // At some point the log will be so short as to be unparseable.
395 ASSERT_GE(node_count, (int)log.nodes().size());
396 node_count = log.nodes().size();
398 // Count how many non-NULL deps entries there are.
399 int new_deps_count = 0;
400 for (vector<DepsLog::Deps*>::const_iterator i = log.deps().begin();
401 i != log.deps().end(); ++i) {
405 ASSERT_GE(deps_count, new_deps_count);
406 deps_count = new_deps_count;
410 // Run the truncation-recovery logic.
411 TEST_F(DepsLogTest, TruncatedRecovery) {
412 // Create a file with some entries.
417 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
421 deps.push_back(state.GetNode("foo.h", 0));
422 deps.push_back(state.GetNode("bar.h", 0));
423 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
426 deps.push_back(state.GetNode("foo.h", 0));
427 deps.push_back(state.GetNode("bar2.h", 0));
428 log.RecordDeps(state.GetNode("out2.o", 0), 2, deps);
433 // Shorten the file, corrupting the last record.
436 ASSERT_EQ(0, stat(kTestFilename, &st));
438 ASSERT_TRUE(Truncate(kTestFilename, st.st_size - 2, &err));
441 // Load the file again, add an entry.
446 EXPECT_TRUE(log.Load(kTestFilename, &state, &err));
447 ASSERT_EQ("premature end of file; recovering", err);
450 // The truncated entry should've been discarded.
451 EXPECT_EQ(NULL, log.GetDeps(state.GetNode("out2.o", 0)));
453 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
458 deps.push_back(state.GetNode("foo.h", 0));
459 deps.push_back(state.GetNode("bar2.h", 0));
460 log.RecordDeps(state.GetNode("out2.o", 0), 3, deps);
465 // Load the file a third time to verify appending after a mangled
466 // entry doesn't break things.
471 EXPECT_TRUE(log.Load(kTestFilename, &state, &err));
473 // The truncated entry should exist.
474 DepsLog::Deps* deps = log.GetDeps(state.GetNode("out2.o", 0));
479 } // anonymous namespace