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