Ninja
deps_log_test.cc
Go to the documentation of this file.
1// Copyright 2012 Google Inc. All Rights Reserved.
2//
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
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
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.
14
15#include "deps_log.h"
16
17#include <sys/stat.h>
18#ifndef _WIN32
19#include <unistd.h>
20#endif
21
22#include "disk_interface.h"
23#include "graph.h"
24#include "util.h"
25#include "test.h"
26
27using namespace std;
28
29namespace {
30
31const char kTestFilename[] = "DepsLogTest-tempfile";
32
33struct DepsLogTest : public testing::Test {
34 virtual void SetUp() {
35 // In case a crashing test left a stale file behind.
37 }
38 virtual void TearDown() { platformAwareUnlink(kTestFilename); }
39};
40
41TEST_F(DepsLogTest, WriteRead) {
42 State state1;
43 DepsLog log1;
44 string err;
45 EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err));
46 ASSERT_EQ("", err);
47
48 {
49 vector<Node*> deps;
50 deps.push_back(state1.GetNode("foo.h", 0));
51 deps.push_back(state1.GetNode("bar.h", 0));
52 log1.RecordDeps(state1.GetNode("out.o", 0), 1, deps);
53
54 deps.clear();
55 deps.push_back(state1.GetNode("foo.h", 0));
56 deps.push_back(state1.GetNode("bar2.h", 0));
57 log1.RecordDeps(state1.GetNode("out2.o", 0), 2, deps);
58
59 DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o", 0));
60 ASSERT_TRUE(log_deps);
61 ASSERT_EQ(1, log_deps->mtime);
62 ASSERT_EQ(2, log_deps->node_count);
63 ASSERT_EQ("foo.h", log_deps->nodes[0]->path());
64 ASSERT_EQ("bar.h", log_deps->nodes[1]->path());
65 }
66
67 log1.Close();
68
69 State state2;
70 DepsLog log2;
71 EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err));
72 ASSERT_EQ("", err);
73
74 ASSERT_EQ(log1.nodes().size(), log2.nodes().size());
75 for (int i = 0; i < (int)log1.nodes().size(); ++i) {
76 Node* node1 = log1.nodes()[i];
77 Node* node2 = log2.nodes()[i];
78 ASSERT_EQ(i, node1->id());
79 ASSERT_EQ(node1->id(), node2->id());
80 }
81
82 // Spot-check the entries in log2.
83 DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out2.o", 0));
84 ASSERT_TRUE(log_deps);
85 ASSERT_EQ(2, log_deps->mtime);
86 ASSERT_EQ(2, log_deps->node_count);
87 ASSERT_EQ("foo.h", log_deps->nodes[0]->path());
88 ASSERT_EQ("bar2.h", log_deps->nodes[1]->path());
89}
90
91TEST_F(DepsLogTest, LotsOfDeps) {
92 const int kNumDeps = 100000; // More than 64k.
93
94 State state1;
95 DepsLog log1;
96 string err;
97 EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err));
98 ASSERT_EQ("", err);
99
100 {
101 vector<Node*> deps;
102 for (int i = 0; i < kNumDeps; ++i) {
103 char buf[32];
104 sprintf(buf, "file%d.h", i);
105 deps.push_back(state1.GetNode(buf, 0));
106 }
107 log1.RecordDeps(state1.GetNode("out.o", 0), 1, deps);
108
109 DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o", 0));
110 ASSERT_EQ(kNumDeps, log_deps->node_count);
111 }
112
113 log1.Close();
114
115 State state2;
116 DepsLog log2;
117 EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err));
118 ASSERT_EQ("", err);
119
120 DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out.o", 0));
121 ASSERT_EQ(kNumDeps, log_deps->node_count);
122}
123
124// Verify that adding the same deps twice doesn't grow the file.
125TEST_F(DepsLogTest, DoubleEntry) {
126 // Write some deps to the file and grab its size.
127 int file_size;
128 {
129 State state;
130 DepsLog log;
131 string err;
132 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
133 ASSERT_EQ("", err);
134
135 vector<Node*> deps;
136 deps.push_back(state.GetNode("foo.h", 0));
137 deps.push_back(state.GetNode("bar.h", 0));
138 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
139 log.Close();
140#ifdef __USE_LARGEFILE64
141 struct stat64 st;
142 ASSERT_EQ(0, stat64(kTestFilename, &st));
143#else
144 struct stat st;
145 ASSERT_EQ(0, stat(kTestFilename, &st));
146#endif
147 file_size = (int)st.st_size;
148 ASSERT_GT(file_size, 0);
149 }
150
151 // Now reload the file, and read the same deps.
152 {
153 State state;
154 DepsLog log;
155 string err;
156 EXPECT_TRUE(log.Load(kTestFilename, &state, &err));
157
158 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
159 ASSERT_EQ("", err);
160
161 vector<Node*> deps;
162 deps.push_back(state.GetNode("foo.h", 0));
163 deps.push_back(state.GetNode("bar.h", 0));
164 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
165 log.Close();
166#ifdef __USE_LARGEFILE64
167 struct stat64 st;
168 ASSERT_EQ(0, stat64(kTestFilename, &st));
169#else
170 struct stat st;
171 ASSERT_EQ(0, stat(kTestFilename, &st));
172#endif
173 int file_size_2 = (int)st.st_size;
174 ASSERT_EQ(file_size, file_size_2);
175 }
176}
177
178// Verify that adding the new deps works and can be compacted away.
179TEST_F(DepsLogTest, Recompact) {
180 const char kManifest[] =
181"rule cc\n"
182" command = cc\n"
183" deps = gcc\n"
184"build out.o: cc\n"
185"build other_out.o: cc\n";
186
187 // Write some deps to the file and grab its size.
188 int file_size;
189 {
190 State state;
191 ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest));
192 DepsLog log;
193 string err;
194 ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err));
195 ASSERT_EQ("", err);
196
197 vector<Node*> deps;
198 deps.push_back(state.GetNode("foo.h", 0));
199 deps.push_back(state.GetNode("bar.h", 0));
200 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
201
202 deps.clear();
203 deps.push_back(state.GetNode("foo.h", 0));
204 deps.push_back(state.GetNode("baz.h", 0));
205 log.RecordDeps(state.GetNode("other_out.o", 0), 1, deps);
206
207 log.Close();
208#ifdef __USE_LARGEFILE64
209 struct stat64 st;
210 ASSERT_EQ(0, stat64(kTestFilename, &st));
211#else
212 struct stat st;
213 ASSERT_EQ(0, stat(kTestFilename, &st));
214#endif
215 file_size = (int)st.st_size;
216 ASSERT_GT(file_size, 0);
217 }
218
219 // Now reload the file, and add slightly different deps.
220 int file_size_2;
221 {
222 State state;
223 ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest));
224 DepsLog log;
225 string err;
226 ASSERT_TRUE(log.Load(kTestFilename, &state, &err));
227
228 ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err));
229 ASSERT_EQ("", err);
230
231 vector<Node*> deps;
232 deps.push_back(state.GetNode("foo.h", 0));
233 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
234 log.Close();
235
236#ifdef __USE_LARGEFILE64
237 struct stat64 st;
238 ASSERT_EQ(0, stat64(kTestFilename, &st));
239#else
240 struct stat st;
241 ASSERT_EQ(0, stat(kTestFilename, &st));
242#endif
243 file_size_2 = (int)st.st_size;
244 // The file should grow to record the new deps.
245 ASSERT_GT(file_size_2, file_size);
246 }
247
248 // Now reload the file, verify the new deps have replaced the old, then
249 // recompact.
250 int file_size_3;
251 {
252 State state;
253 ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest));
254 DepsLog log;
255 string err;
256 ASSERT_TRUE(log.Load(kTestFilename, &state, &err));
257
258 Node* out = state.GetNode("out.o", 0);
259 DepsLog::Deps* deps = log.GetDeps(out);
260 ASSERT_TRUE(deps);
261 ASSERT_EQ(1, deps->mtime);
262 ASSERT_EQ(1, deps->node_count);
263 ASSERT_EQ("foo.h", deps->nodes[0]->path());
264
265 Node* other_out = state.GetNode("other_out.o", 0);
266 deps = log.GetDeps(other_out);
267 ASSERT_TRUE(deps);
268 ASSERT_EQ(1, deps->mtime);
269 ASSERT_EQ(2, deps->node_count);
270 ASSERT_EQ("foo.h", deps->nodes[0]->path());
271 ASSERT_EQ("baz.h", deps->nodes[1]->path());
272
273 ASSERT_TRUE(log.Recompact(kTestFilename, &err));
274
275 // The in-memory deps graph should still be valid after recompaction.
276 deps = log.GetDeps(out);
277 ASSERT_TRUE(deps);
278 ASSERT_EQ(1, deps->mtime);
279 ASSERT_EQ(1, deps->node_count);
280 ASSERT_EQ("foo.h", deps->nodes[0]->path());
281 ASSERT_EQ(out, log.nodes()[out->id()]);
282
283 deps = log.GetDeps(other_out);
284 ASSERT_TRUE(deps);
285 ASSERT_EQ(1, deps->mtime);
286 ASSERT_EQ(2, deps->node_count);
287 ASSERT_EQ("foo.h", deps->nodes[0]->path());
288 ASSERT_EQ("baz.h", deps->nodes[1]->path());
289 ASSERT_EQ(other_out, log.nodes()[other_out->id()]);
290
291 // The file should have shrunk a bit for the smaller deps.
292#ifdef __USE_LARGEFILE64
293 struct stat64 st;
294 ASSERT_EQ(0, stat64(kTestFilename, &st));
295#else
296 struct stat st;
297 ASSERT_EQ(0, stat(kTestFilename, &st));
298#endif
299 file_size_3 = (int)st.st_size;
300 ASSERT_LT(file_size_3, file_size_2);
301 }
302
303 // Now reload the file and recompact with an empty manifest. The previous
304 // entries should be removed.
305 {
306 State state;
307 // Intentionally not parsing kManifest here.
308 DepsLog log;
309 string err;
310 ASSERT_TRUE(log.Load(kTestFilename, &state, &err));
311
312 Node* out = state.GetNode("out.o", 0);
313 DepsLog::Deps* deps = log.GetDeps(out);
314 ASSERT_TRUE(deps);
315 ASSERT_EQ(1, deps->mtime);
316 ASSERT_EQ(1, deps->node_count);
317 ASSERT_EQ("foo.h", deps->nodes[0]->path());
318
319 Node* other_out = state.GetNode("other_out.o", 0);
320 deps = log.GetDeps(other_out);
321 ASSERT_TRUE(deps);
322 ASSERT_EQ(1, deps->mtime);
323 ASSERT_EQ(2, deps->node_count);
324 ASSERT_EQ("foo.h", deps->nodes[0]->path());
325 ASSERT_EQ("baz.h", deps->nodes[1]->path());
326
327 ASSERT_TRUE(log.Recompact(kTestFilename, &err));
328
329 // The previous entries should have been removed.
330 deps = log.GetDeps(out);
331 ASSERT_FALSE(deps);
332
333 deps = log.GetDeps(other_out);
334 ASSERT_FALSE(deps);
335
336 // The .h files pulled in via deps should no longer have ids either.
337 ASSERT_EQ(-1, state.LookupNode("foo.h")->id());
338 ASSERT_EQ(-1, state.LookupNode("baz.h")->id());
339
340 // The file should have shrunk more.
341#ifdef __USE_LARGEFILE64
342 struct stat64 st;
343 ASSERT_EQ(0, stat64(kTestFilename, &st));
344#else
345 struct stat st;
346 ASSERT_EQ(0, stat(kTestFilename, &st));
347#endif
348 int file_size_4 = (int)st.st_size;
349 ASSERT_LT(file_size_4, file_size_3);
350 }
351}
352
353// Verify that invalid file headers cause a new build.
354TEST_F(DepsLogTest, InvalidHeader) {
355 const char *kInvalidHeaders[] = {
356 "", // Empty file.
357 "# ninjad", // Truncated first line.
358 "# ninjadeps\n", // No version int.
359 "# ninjadeps\n\001\002", // Truncated version int.
360 "# ninjadeps\n\001\002\003\004" // Invalid version int.
361 };
362 for (size_t i = 0; i < sizeof(kInvalidHeaders) / sizeof(kInvalidHeaders[0]);
363 ++i) {
364 FILE* deps_log = fopen(kTestFilename, "wb");
365 ASSERT_TRUE(deps_log != NULL);
366 ASSERT_EQ(
367 strlen(kInvalidHeaders[i]),
368 fwrite(kInvalidHeaders[i], 1, strlen(kInvalidHeaders[i]), deps_log));
369 ASSERT_EQ(0 ,fclose(deps_log));
370
371 string err;
372 DepsLog log;
373 State state;
374 ASSERT_TRUE(log.Load(kTestFilename, &state, &err));
375 EXPECT_EQ("bad deps log signature or version; starting over", err);
376 }
377}
378
379// Simulate what happens when loading a truncated log file.
380TEST_F(DepsLogTest, Truncated) {
381 // Create a file with some entries.
382 {
383 State state;
384 DepsLog log;
385 string err;
386 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
387 ASSERT_EQ("", err);
388
389 vector<Node*> deps;
390 deps.push_back(state.GetNode("foo.h", 0));
391 deps.push_back(state.GetNode("bar.h", 0));
392 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
393
394 deps.clear();
395 deps.push_back(state.GetNode("foo.h", 0));
396 deps.push_back(state.GetNode("bar2.h", 0));
397 log.RecordDeps(state.GetNode("out2.o", 0), 2, deps);
398
399 log.Close();
400 }
401
402 // Get the file size.
403#ifdef __USE_LARGEFILE64
404 struct stat64 st;
405 ASSERT_EQ(0, stat64(kTestFilename, &st));
406#else
407 struct stat st;
408 ASSERT_EQ(0, stat(kTestFilename, &st));
409#endif
410
411 // Try reloading at truncated sizes.
412 // Track how many nodes/deps were found; they should decrease with
413 // smaller sizes.
414 int node_count = 5;
415 int deps_count = 2;
416 for (int size = (int)st.st_size; size > 0; --size) {
417 string err;
418 ASSERT_TRUE(Truncate(kTestFilename, size, &err));
419
420 State state;
421 DepsLog log;
422 EXPECT_TRUE(log.Load(kTestFilename, &state, &err));
423 if (!err.empty()) {
424 // At some point the log will be so short as to be unparsable.
425 break;
426 }
427
428 ASSERT_GE(node_count, (int)log.nodes().size());
429 node_count = static_cast<int>(log.nodes().size());
430
431 // Count how many non-NULL deps entries there are.
432 int new_deps_count = 0;
433 for (vector<DepsLog::Deps*>::const_iterator i = log.deps().begin();
434 i != log.deps().end(); ++i) {
435 if (*i)
436 ++new_deps_count;
437 }
438 ASSERT_GE(deps_count, new_deps_count);
439 deps_count = new_deps_count;
440 }
441}
442
443// Run the truncation-recovery logic.
444TEST_F(DepsLogTest, TruncatedRecovery) {
445 // Create a file with some entries.
446 {
447 State state;
448 DepsLog log;
449 string err;
450 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
451 ASSERT_EQ("", err);
452
453 vector<Node*> deps;
454 deps.push_back(state.GetNode("foo.h", 0));
455 deps.push_back(state.GetNode("bar.h", 0));
456 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
457
458 deps.clear();
459 deps.push_back(state.GetNode("foo.h", 0));
460 deps.push_back(state.GetNode("bar2.h", 0));
461 log.RecordDeps(state.GetNode("out2.o", 0), 2, deps);
462
463 log.Close();
464 }
465
466 // Shorten the file, corrupting the last record.
467 {
468#ifdef __USE_LARGEFILE64
469 struct stat64 st;
470 ASSERT_EQ(0, stat64(kTestFilename, &st));
471#else
472 struct stat st;
473 ASSERT_EQ(0, stat(kTestFilename, &st));
474#endif
475 string err;
476 ASSERT_TRUE(Truncate(kTestFilename, st.st_size - 2, &err));
477 }
478
479 // Load the file again, add an entry.
480 {
481 State state;
482 DepsLog log;
483 string err;
484 EXPECT_TRUE(log.Load(kTestFilename, &state, &err));
485 ASSERT_EQ("premature end of file; recovering", err);
486 err.clear();
487
488 // The truncated entry should've been discarded.
489 EXPECT_EQ(NULL, log.GetDeps(state.GetNode("out2.o", 0)));
490
491 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
492 ASSERT_EQ("", err);
493
494 // Add a new entry.
495 vector<Node*> deps;
496 deps.push_back(state.GetNode("foo.h", 0));
497 deps.push_back(state.GetNode("bar2.h", 0));
498 log.RecordDeps(state.GetNode("out2.o", 0), 3, deps);
499
500 log.Close();
501 }
502
503 // Load the file a third time to verify appending after a mangled
504 // entry doesn't break things.
505 {
506 State state;
507 DepsLog log;
508 string err;
509 EXPECT_TRUE(log.Load(kTestFilename, &state, &err));
510
511 // The truncated entry should exist.
512 DepsLog::Deps* deps = log.GetDeps(state.GetNode("out2.o", 0));
513 ASSERT_TRUE(deps);
514 }
515}
516
517TEST_F(DepsLogTest, ReverseDepsNodes) {
518 State state;
519 DepsLog log;
520 string err;
521 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
522 ASSERT_EQ("", err);
523
524 vector<Node*> deps;
525 deps.push_back(state.GetNode("foo.h", 0));
526 deps.push_back(state.GetNode("bar.h", 0));
527 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
528
529 deps.clear();
530 deps.push_back(state.GetNode("foo.h", 0));
531 deps.push_back(state.GetNode("bar2.h", 0));
532 log.RecordDeps(state.GetNode("out2.o", 0), 2, deps);
533
534 log.Close();
535
536 Node* rev_deps = log.GetFirstReverseDepsNode(state.GetNode("foo.h", 0));
537 EXPECT_TRUE(rev_deps == state.GetNode("out.o", 0) ||
538 rev_deps == state.GetNode("out2.o", 0));
539
540 rev_deps = log.GetFirstReverseDepsNode(state.GetNode("bar.h", 0));
541 EXPECT_TRUE(rev_deps == state.GetNode("out.o", 0));
542}
543
544TEST_F(DepsLogTest, MalformedDepsLog) {
545 std::string err;
546 {
547 State state;
548 DepsLog log;
549 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
550 ASSERT_EQ("", err);
551
552 // First, create a valid log file.
553 std::vector<Node*> deps;
554 deps.push_back(state.GetNode("foo.hh", 0));
555 deps.push_back(state.GetNode("bar.hpp", 0));
556 log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
557 log.Close();
558 }
559
560 // Now read its value, validate it a little.
562
563 std::string original_contents;
565 &original_contents, &err));
566
567 const size_t version_offset = 12;
568 ASSERT_EQ("# ninjadeps\n", original_contents.substr(0, version_offset));
569 ASSERT_EQ('\x04', original_contents[version_offset + 0]);
570 ASSERT_EQ('\x00', original_contents[version_offset + 1]);
571 ASSERT_EQ('\x00', original_contents[version_offset + 2]);
572 ASSERT_EQ('\x00', original_contents[version_offset + 3]);
573
574 // clang-format off
575 static const uint8_t kFirstRecord[] = {
576 // size field == 0x0000000c
577 0x0c, 0x00, 0x00, 0x00,
578 // name field = 'out.o' + 3 bytes of padding.
579 'o', 'u', 't', '.', 'o', 0x00, 0x00, 0x00,
580 // checksum = ~0
581 0xff, 0xff, 0xff, 0xff,
582 };
583 // clang-format on
584 const size_t kFirstRecordLen = sizeof(kFirstRecord);
585 const size_t first_offset = version_offset + 4;
586
587#define COMPARE_RECORD(start_pos, reference, len) \
588 ASSERT_EQ(original_contents.substr(start_pos, len), std::string(reinterpret_cast<const char*>(reference), len))
589
590 COMPARE_RECORD(first_offset, kFirstRecord, kFirstRecordLen);
591
592 const size_t second_offset = first_offset + kFirstRecordLen;
593 // clang-format off
594 static const uint8_t kSecondRecord[] = {
595 // size field == 0x0000000c
596 0x0c, 0x00, 0x00, 0x00,
597 // name field = 'foo.hh' + 2 bytes of padding.
598 'f', 'o', 'o', '.', 'h', 'h', 0x00, 0x00,
599 // checksum = ~1
600 0xfe, 0xff, 0xff, 0xff,
601 };
602 // clang-format on
603 const size_t kSecondRecordLen = sizeof(kSecondRecord);
604 COMPARE_RECORD(second_offset, kSecondRecord, kSecondRecordLen);
605
606 // Then start generating corrupted versions and trying to load them.
607 const char kBadLogFile[] = "DepsLogTest-corrupted.tempfile";
608
609 // Helper lambda to rewrite the bad log file with new content.
610 auto write_bad_log_file =
611 [&disk, &kBadLogFile](const std::string& bad_contents) -> bool {
612 (void)disk.RemoveFile(kBadLogFile);
613 return disk.WriteFile(kBadLogFile, bad_contents, false);
614 };
615
616 // First, corrupt the header.
617 std::string bad_contents = original_contents;
618 bad_contents[0] = '@';
619
620 ASSERT_TRUE(write_bad_log_file(bad_contents)) << strerror(errno);
621 {
622 State state;
623 DepsLog log;
624 err.clear();
625 ASSERT_EQ(LOAD_SUCCESS, log.Load(kBadLogFile, &state, &err));
626 ASSERT_EQ("bad deps log signature or version; starting over", err);
627 }
628
629 // Second, truncate the version.
630 bad_contents = original_contents.substr(0, version_offset + 3);
631 ASSERT_TRUE(write_bad_log_file(bad_contents)) << strerror(errno);
632 {
633 State state;
634 DepsLog log;
635 err.clear();
636 ASSERT_EQ(LOAD_SUCCESS, log.Load(kBadLogFile, &state, &err));
637 ASSERT_EQ("bad deps log signature or version; starting over", err);
638 }
639
640 // Truncate first record's |size| field. The loader should recover.
641 bad_contents = original_contents.substr(0, version_offset + 4 + 3);
642 ASSERT_TRUE(write_bad_log_file(bad_contents)) << strerror(errno);
643 {
644 State state;
645 DepsLog log;
646 err.clear();
647 ASSERT_EQ(LOAD_SUCCESS, log.Load(kBadLogFile, &state, &err));
648 ASSERT_EQ("", err);
649 }
650
651 // Corrupt first record |size| value.
652 bad_contents = original_contents;
653 bad_contents[first_offset + 0] = '\x55';
654 bad_contents[first_offset + 1] = '\xaa';
655 bad_contents[first_offset + 2] = '\xff';
656 bad_contents[first_offset + 3] = '\xff';
657 ASSERT_TRUE(write_bad_log_file(bad_contents)) << strerror(errno);
658 {
659 State state;
660 DepsLog log;
661 err.clear();
662 ASSERT_EQ(LOAD_SUCCESS, log.Load(kBadLogFile, &state, &err));
663 ASSERT_EQ("premature end of file; recovering", err);
664 }
665
666 // Make first record |size| less than 4.
667 bad_contents = original_contents;
668 bad_contents[first_offset] = '\x01';
669 ASSERT_TRUE(write_bad_log_file(bad_contents)) << strerror(errno);
670 {
671 State state;
672 DepsLog log;
673 err.clear();
674 ASSERT_EQ(LOAD_SUCCESS, log.Load(kBadLogFile, &state, &err));
675 ASSERT_EQ("premature end of file; recovering", err);
676 }
677}
678
679} // anonymous namespace
const char kTestFilename[]
TEST_F(PlanTest, Basic)
Definition build_test.cc:67
#define COMPARE_RECORD(start_pos, reference, len)
@ LOAD_SUCCESS
Definition load_status.h:20
Definition hash_map.h:26
TimeStamp mtime
Definition deps_log.h:84
Node ** nodes
Definition deps_log.h:86
As build commands run they can output extra dependency information (e.g.
Definition deps_log.h:68
Deps * GetDeps(Node *node)
Definition deps_log.cc:305
Node * GetFirstReverseDepsNode(Node *node)
Definition deps_log.cc:313
bool Recompact(const std::string &path, std::string *err)
Rewrite the known log entries, throwing away old data.
Definition deps_log.cc:326
void Close()
Definition deps_log.cc:147
bool OpenForWrite(const std::string &path, std::string *err)
Definition deps_log.cc:51
const std::vector< Deps * > & deps() const
Definition deps_log.h:105
bool RecordDeps(Node *node, TimeStamp mtime, const std::vector< Node * > &nodes)
const std::vector< Node * > & nodes() const
Used for tests.
Definition deps_log.h:104
LoadStatus Load(const std::string &path, State *state, std::string *err)
Definition deps_log.cc:154
Information about a node in the dependency graph: the file, whether it's dirty, mtime,...
Definition graph.h:42
const std::string & path() const
Definition graph.h:82
int id() const
Definition graph.h:111
Implementation of DiskInterface that actually hits the disk.
Status ReadFile(const std::string &path, std::string *contents, std::string *err) override
Read and store in given string.
bool WriteFile(const std::string &path, const std::string &contents, bool crlf_on_windows) override
Create a file, with the specified name and contents If crlf_on_windows is true, will be converted t...
int RemoveFile(const std::string &path) override
Remove the file named path.
Global state (file status) for a single run.
Definition state.h:95
Node * GetNode(StringPiece path, uint64_t slash_bits)
Definition state.cc:95
Node * LookupNode(StringPiece path) const
Definition state.cc:104
void AssertParse(State *state, const char *input, ManifestParserOptions opts)
Definition test.cc:100
int platformAwareUnlink(const char *filename)
Definition util.cc:1025
bool Truncate(const string &path, size_t size, string *err)
Definition util.cc:1007