Ninja
build_log_test.cc
Go to the documentation of this file.
1 // Copyright 2011 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 "build_log.h"
16 
17 #include "util.h"
18 #include "test.h"
19 
20 #include <sys/stat.h>
21 #ifdef _WIN32
22 #include <fcntl.h>
23 #include <share.h>
24 #else
25 #include <sys/types.h>
26 #include <unistd.h>
27 #endif
28 #include <cassert>
29 
30 using namespace std;
31 
32 namespace {
33 
34 const char kTestFilename[] = "BuildLogTest-tempfile";
35 
36 struct BuildLogTest : public StateTestWithBuiltinRules, public BuildLogUser {
37  virtual void SetUp() {
38  // In case a crashing test left a stale file behind.
39  unlink(kTestFilename);
40  }
41  virtual void TearDown() {
42  unlink(kTestFilename);
43  }
44  virtual bool IsPathDead(StringPiece s) const { return false; }
45 };
46 
47 TEST_F(BuildLogTest, WriteRead) {
48  AssertParse(&state_,
49 "build out: cat mid\n"
50 "build mid: cat in\n");
51 
52  BuildLog log1;
53  string err;
54  EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err));
55  ASSERT_EQ("", err);
56  log1.RecordCommand(state_.edges_[0], 15, 18);
57  log1.RecordCommand(state_.edges_[1], 20, 25);
58  log1.Close();
59 
60  BuildLog log2;
61  EXPECT_TRUE(log2.Load(kTestFilename, &err));
62  ASSERT_EQ("", err);
63 
64  ASSERT_EQ(2u, log1.entries().size());
65  ASSERT_EQ(2u, log2.entries().size());
66  BuildLog::LogEntry* e1 = log1.LookupByOutput("out");
67  ASSERT_TRUE(e1);
68  BuildLog::LogEntry* e2 = log2.LookupByOutput("out");
69  ASSERT_TRUE(e2);
70  ASSERT_TRUE(*e1 == *e2);
71  ASSERT_EQ(15, e1->start_time);
72  ASSERT_EQ("out", e1->output);
73 }
74 
75 TEST_F(BuildLogTest, FirstWriteAddsSignature) {
76  const char kExpectedVersion[] = "# ninja log vX\n";
77  const size_t kVersionPos = strlen(kExpectedVersion) - 2; // Points at 'X'.
78 
79  BuildLog log;
80  string contents, err;
81 
82  EXPECT_TRUE(log.OpenForWrite(kTestFilename, *this, &err));
83  ASSERT_EQ("", err);
84  log.Close();
85 
86  ASSERT_EQ(0, ReadFile(kTestFilename, &contents, &err));
87  ASSERT_EQ("", err);
88  if (contents.size() >= kVersionPos)
89  contents[kVersionPos] = 'X';
90  EXPECT_EQ(kExpectedVersion, contents);
91 
92  // Opening the file anew shouldn't add a second version string.
93  EXPECT_TRUE(log.OpenForWrite(kTestFilename, *this, &err));
94  ASSERT_EQ("", err);
95  log.Close();
96 
97  contents.clear();
98  ASSERT_EQ(0, ReadFile(kTestFilename, &contents, &err));
99  ASSERT_EQ("", err);
100  if (contents.size() >= kVersionPos)
101  contents[kVersionPos] = 'X';
102  EXPECT_EQ(kExpectedVersion, contents);
103 }
104 
105 TEST_F(BuildLogTest, DoubleEntry) {
106  FILE* f = fopen(kTestFilename, "wb");
107  fprintf(f, "# ninja log v4\n");
108  fprintf(f, "0\t1\t2\tout\tcommand abc\n");
109  fprintf(f, "3\t4\t5\tout\tcommand def\n");
110  fclose(f);
111 
112  string err;
113  BuildLog log;
114  EXPECT_TRUE(log.Load(kTestFilename, &err));
115  ASSERT_EQ("", err);
116 
117  BuildLog::LogEntry* e = log.LookupByOutput("out");
118  ASSERT_TRUE(e);
119  ASSERT_NO_FATAL_FAILURE(AssertHash("command def", e->command_hash));
120 }
121 
122 TEST_F(BuildLogTest, Truncate) {
123  AssertParse(&state_,
124 "build out: cat mid\n"
125 "build mid: cat in\n");
126 
127  {
128  BuildLog log1;
129  string err;
130  EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err));
131  ASSERT_EQ("", err);
132  log1.RecordCommand(state_.edges_[0], 15, 18);
133  log1.RecordCommand(state_.edges_[1], 20, 25);
134  log1.Close();
135  }
136 
137  struct stat statbuf;
138  ASSERT_EQ(0, stat(kTestFilename, &statbuf));
139  ASSERT_GT(statbuf.st_size, 0);
140 
141  // For all possible truncations of the input file, assert that we don't
142  // crash when parsing.
143  for (off_t size = statbuf.st_size; size > 0; --size) {
144  BuildLog log2;
145  string err;
146  EXPECT_TRUE(log2.OpenForWrite(kTestFilename, *this, &err));
147  ASSERT_EQ("", err);
148  log2.RecordCommand(state_.edges_[0], 15, 18);
149  log2.RecordCommand(state_.edges_[1], 20, 25);
150  log2.Close();
151 
152  ASSERT_TRUE(Truncate(kTestFilename, size, &err));
153 
154  BuildLog log3;
155  err.clear();
156  ASSERT_TRUE(log3.Load(kTestFilename, &err) == LOAD_SUCCESS || !err.empty());
157  }
158 }
159 
160 TEST_F(BuildLogTest, ObsoleteOldVersion) {
161  FILE* f = fopen(kTestFilename, "wb");
162  fprintf(f, "# ninja log v3\n");
163  fprintf(f, "123 456 0 out command\n");
164  fclose(f);
165 
166  string err;
167  BuildLog log;
168  EXPECT_TRUE(log.Load(kTestFilename, &err));
169  ASSERT_NE(err.find("version"), string::npos);
170 }
171 
172 TEST_F(BuildLogTest, SpacesInOutputV4) {
173  FILE* f = fopen(kTestFilename, "wb");
174  fprintf(f, "# ninja log v4\n");
175  fprintf(f, "123\t456\t456\tout with space\tcommand\n");
176  fclose(f);
177 
178  string err;
179  BuildLog log;
180  EXPECT_TRUE(log.Load(kTestFilename, &err));
181  ASSERT_EQ("", err);
182 
183  BuildLog::LogEntry* e = log.LookupByOutput("out with space");
184  ASSERT_TRUE(e);
185  ASSERT_EQ(123, e->start_time);
186  ASSERT_EQ(456, e->end_time);
187  ASSERT_EQ(456, e->mtime);
189 }
190 
191 TEST_F(BuildLogTest, DuplicateVersionHeader) {
192  // Old versions of ninja accidentally wrote multiple version headers to the
193  // build log on Windows. This shouldn't crash, and the second version header
194  // should be ignored.
195  FILE* f = fopen(kTestFilename, "wb");
196  fprintf(f, "# ninja log v4\n");
197  fprintf(f, "123\t456\t456\tout\tcommand\n");
198  fprintf(f, "# ninja log v4\n");
199  fprintf(f, "456\t789\t789\tout2\tcommand2\n");
200  fclose(f);
201 
202  string err;
203  BuildLog log;
204  EXPECT_TRUE(log.Load(kTestFilename, &err));
205  ASSERT_EQ("", err);
206 
207  BuildLog::LogEntry* e = log.LookupByOutput("out");
208  ASSERT_TRUE(e);
209  ASSERT_EQ(123, e->start_time);
210  ASSERT_EQ(456, e->end_time);
211  ASSERT_EQ(456, e->mtime);
213 
214  e = log.LookupByOutput("out2");
215  ASSERT_TRUE(e);
216  ASSERT_EQ(456, e->start_time);
217  ASSERT_EQ(789, e->end_time);
218  ASSERT_EQ(789, e->mtime);
220 }
221 
222 struct TestDiskInterface : public DiskInterface {
223  virtual TimeStamp Stat(const string& path, string* err) const {
224  return 4;
225  }
226  virtual bool WriteFile(const string& path, const string& contents) {
227  assert(false);
228  return true;
229  }
230  virtual bool MakeDir(const string& path) {
231  assert(false);
232  return false;
233  }
234  virtual Status ReadFile(const string& path, string* contents, string* err) {
235  assert(false);
236  return NotFound;
237  }
238  virtual int RemoveFile(const string& path) {
239  assert(false);
240  return 0;
241  }
242 };
243 
244 TEST_F(BuildLogTest, Restat) {
245  FILE* f = fopen(kTestFilename, "wb");
246  fprintf(f, "# ninja log v4\n"
247  "1\t2\t3\tout\tcommand\n");
248  fclose(f);
249  std::string err;
250  BuildLog log;
251  EXPECT_TRUE(log.Load(kTestFilename, &err));
252  ASSERT_EQ("", err);
253  BuildLog::LogEntry* e = log.LookupByOutput("out");
254  ASSERT_EQ(3, e->mtime);
255 
256  TestDiskInterface testDiskInterface;
257  char out2[] = { 'o', 'u', 't', '2', 0 };
258  char* filter2[] = { out2 };
259  EXPECT_TRUE(log.Restat(kTestFilename, testDiskInterface, 1, filter2, &err));
260  ASSERT_EQ("", err);
261  e = log.LookupByOutput("out");
262  ASSERT_EQ(3, e->mtime); // unchanged, since the filter doesn't match
263 
264  EXPECT_TRUE(log.Restat(kTestFilename, testDiskInterface, 0, NULL, &err));
265  ASSERT_EQ("", err);
266  e = log.LookupByOutput("out");
267  ASSERT_EQ(4, e->mtime);
268 }
269 
270 TEST_F(BuildLogTest, VeryLongInputLine) {
271  // Ninja's build log buffer is currently 256kB. Lines longer than that are
272  // silently ignored, but don't affect parsing of other lines.
273  FILE* f = fopen(kTestFilename, "wb");
274  fprintf(f, "# ninja log v4\n");
275  fprintf(f, "123\t456\t456\tout\tcommand start");
276  for (size_t i = 0; i < (512 << 10) / strlen(" more_command"); ++i)
277  fputs(" more_command", f);
278  fprintf(f, "\n");
279  fprintf(f, "456\t789\t789\tout2\tcommand2\n");
280  fclose(f);
281 
282  string err;
283  BuildLog log;
284  EXPECT_TRUE(log.Load(kTestFilename, &err));
285  ASSERT_EQ("", err);
286 
287  BuildLog::LogEntry* e = log.LookupByOutput("out");
288  ASSERT_EQ(NULL, e);
289 
290  e = log.LookupByOutput("out2");
291  ASSERT_TRUE(e);
292  ASSERT_EQ(456, e->start_time);
293  ASSERT_EQ(789, e->end_time);
294  ASSERT_EQ(789, e->mtime);
296 }
297 
298 TEST_F(BuildLogTest, MultiTargetEdge) {
299  AssertParse(&state_,
300 "build out out.d: cat\n");
301 
302  BuildLog log;
303  log.RecordCommand(state_.edges_[0], 21, 22);
304 
305  ASSERT_EQ(2u, log.entries().size());
306  BuildLog::LogEntry* e1 = log.LookupByOutput("out");
307  ASSERT_TRUE(e1);
308  BuildLog::LogEntry* e2 = log.LookupByOutput("out.d");
309  ASSERT_TRUE(e2);
310  ASSERT_EQ("out", e1->output);
311  ASSERT_EQ("out.d", e2->output);
312  ASSERT_EQ(21, e1->start_time);
313  ASSERT_EQ(21, e2->start_time);
314  ASSERT_EQ(22, e2->end_time);
315  ASSERT_EQ(22, e2->end_time);
316 }
317 
318 struct BuildLogRecompactTest : public BuildLogTest {
319  virtual bool IsPathDead(StringPiece s) const { return s == "out2"; }
320 };
321 
322 TEST_F(BuildLogRecompactTest, Recompact) {
323  AssertParse(&state_,
324 "build out: cat in\n"
325 "build out2: cat in\n");
326 
327  BuildLog log1;
328  string err;
329  EXPECT_TRUE(log1.OpenForWrite(kTestFilename, *this, &err));
330  ASSERT_EQ("", err);
331  // Record the same edge several times, to trigger recompaction
332  // the next time the log is opened.
333  for (int i = 0; i < 200; ++i)
334  log1.RecordCommand(state_.edges_[0], 15, 18 + i);
335  log1.RecordCommand(state_.edges_[1], 21, 22);
336  log1.Close();
337 
338  // Load...
339  BuildLog log2;
340  EXPECT_TRUE(log2.Load(kTestFilename, &err));
341  ASSERT_EQ("", err);
342  ASSERT_EQ(2u, log2.entries().size());
343  ASSERT_TRUE(log2.LookupByOutput("out"));
344  ASSERT_TRUE(log2.LookupByOutput("out2"));
345  // ...and force a recompaction.
346  EXPECT_TRUE(log2.OpenForWrite(kTestFilename, *this, &err));
347  log2.Close();
348 
349  // "out2" is dead, it should've been removed.
350  BuildLog log3;
351  EXPECT_TRUE(log2.Load(kTestFilename, &err));
352  ASSERT_EQ("", err);
353  ASSERT_EQ(1u, log2.entries().size());
354  ASSERT_TRUE(log2.LookupByOutput("out"));
355  ASSERT_FALSE(log2.LookupByOutput("out2"));
356 }
357 
358 } // anonymous namespace
bool OpenForWrite(const std::string &path, const BuildLogUser &user, std::string *err)
Prepares writing to the log file without actually opening it - that will happen when/if it&#39;s needed...
Definition: build_log.cc:131
#define TEST_F(x, y)
Definition: test.h:61
const Entries & entries() const
Definition: build_log.h:94
LoadStatus Load(const std::string &path, std::string *err)
Load the on-disk log.
Definition: build_log.cc:261
void AssertParse(State *state, const char *input, ManifestParserOptions opts)
Definition: test.cc:100
bool RecordCommand(Edge *edge, int start_time, int end_time, TimeStamp mtime=0)
Definition: build_log.cc:144
std::string output
Definition: build_log.h:59
bool Restat(StringPiece path, const DiskInterface &disk_interface, int output_count, char **outputs, std::string *err)
Restat all outputs in the log.
Definition: build_log.cc:439
StringPiece represents a slice of a string whose memory is managed externally.
Definition: string_piece.h:25
#define EXPECT_TRUE(a)
Definition: test.h:76
Interface for accessing the disk.
void Close()
Definition: build_log.cc:178
Store a log of every command ran for every build.
Definition: build_log.h:43
A base test fixture that includes a State object with a builtin "cat" rule.
Definition: test.h:113
uint64_t command_hash
Definition: build_log.h:60
#define EXPECT_EQ(a, b)
Definition: test.h:64
#define ASSERT_GT(a, b)
Definition: test.h:85
const char kTestFilename[]
#define ASSERT_FALSE(a)
Definition: test.h:95
int64_t TimeStamp
Definition: timestamp.h:31
int ReadFile(const string &path, string *contents, string *err)
Definition: util.cc:318
#define ASSERT_EQ(a, b)
Definition: test.h:81
bool Truncate(const string &path, size_t size, string *err)
Definition: util.cc:618
#define ASSERT_NE(a, b)
Definition: test.h:83
TimeStamp mtime
Definition: build_log.h:63
#define ASSERT_NO_FATAL_FAILURE(a)
Definition: test.h:97
LogEntry * LookupByOutput(const std::string &path)
Lookup a previously-run command by its output path.
Definition: build_log.cc:377
#define ASSERT_TRUE(a)
Definition: test.h:93
void AssertHash(const char *expected, uint64_t actual)
Definition: test.cc:109
Can answer questions about the manifest for the BuildLog.
Definition: build_log.h:30