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 
27 using namespace std;
28 
29 namespace {
30 
31 const char kTestFilename[] = "DepsLogTest-tempfile";
32 
33 struct 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 
41 TEST_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 
91 TEST_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.
125 TEST_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.
179 TEST_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.
354 TEST_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.
380 TEST_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.
444 TEST_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 
517 TEST_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 
544 TEST_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.
561  RealDiskInterface disk;
562 
563  std::string original_contents;
564  ASSERT_EQ(FileReader::Okay, disk.ReadFile(kTestFilename,
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
int node_count
Definition: deps_log.h:85
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
const std::vector< Node * > & nodes() const
Used for tests.
Definition: deps_log.h:104
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
bool RecordDeps(Node *node, TimeStamp mtime, const std::vector< Node * > &nodes)
const std::vector< Deps * > & deps() const
Definition: deps_log.h:105
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
int id() const
Definition: graph.h:111
const std::string & path() const
Definition: graph.h:82
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