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 #ifdef _WIN32
21 #include <fcntl.h>
22 #include <share.h>
23 #else
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #endif
28 
29 namespace {
30 
31 const char kTestFilename[] = "BuildLogTest-tempfile";
32 
33 struct BuildLogTest : public StateTestWithBuiltinRules {
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 };
42 
43 TEST_F(BuildLogTest, WriteRead) {
44  AssertParse(&state_,
45 "build out: cat mid\n"
46 "build mid: cat in\n");
47 
48  BuildLog log1;
49  string err;
50  EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err));
51  ASSERT_EQ("", err);
52  log1.RecordCommand(state_.edges_[0], 15, 18);
53  log1.RecordCommand(state_.edges_[1], 20, 25);
54  log1.Close();
55 
56  BuildLog log2;
57  EXPECT_TRUE(log2.Load(kTestFilename, &err));
58  ASSERT_EQ("", err);
59 
60  ASSERT_EQ(2u, log1.entries().size());
61  ASSERT_EQ(2u, log2.entries().size());
62  BuildLog::LogEntry* e1 = log1.LookupByOutput("out");
63  ASSERT_TRUE(e1);
64  BuildLog::LogEntry* e2 = log2.LookupByOutput("out");
65  ASSERT_TRUE(e2);
66  ASSERT_TRUE(*e1 == *e2);
67  ASSERT_EQ(15, e1->start_time);
68  ASSERT_EQ("out", e1->output);
69 }
70 
71 TEST_F(BuildLogTest, FirstWriteAddsSignature) {
72  const char kExpectedVersion[] = "# ninja log vX\n";
73  const size_t kVersionPos = strlen(kExpectedVersion) - 2; // Points at 'X'.
74 
75  BuildLog log;
76  string contents, err;
77 
78  EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
79  ASSERT_EQ("", err);
80  log.Close();
81 
82  ASSERT_EQ(0, ReadFile(kTestFilename, &contents, &err));
83  ASSERT_EQ("", err);
84  if (contents.size() >= kVersionPos)
85  contents[kVersionPos] = 'X';
86  EXPECT_EQ(kExpectedVersion, contents);
87 
88  // Opening the file anew shouldn't add a second version string.
89  EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
90  ASSERT_EQ("", err);
91  log.Close();
92 
93  contents.clear();
94  ASSERT_EQ(0, ReadFile(kTestFilename, &contents, &err));
95  ASSERT_EQ("", err);
96  if (contents.size() >= kVersionPos)
97  contents[kVersionPos] = 'X';
98  EXPECT_EQ(kExpectedVersion, contents);
99 }
100 
101 TEST_F(BuildLogTest, DoubleEntry) {
102  FILE* f = fopen(kTestFilename, "wb");
103  fprintf(f, "# ninja log v4\n");
104  fprintf(f, "0\t1\t2\tout\tcommand abc\n");
105  fprintf(f, "3\t4\t5\tout\tcommand def\n");
106  fclose(f);
107 
108  string err;
109  BuildLog log;
110  EXPECT_TRUE(log.Load(kTestFilename, &err));
111  ASSERT_EQ("", err);
112 
113  BuildLog::LogEntry* e = log.LookupByOutput("out");
114  ASSERT_TRUE(e);
115  ASSERT_NO_FATAL_FAILURE(AssertHash("command def", e->command_hash));
116 }
117 
118 TEST_F(BuildLogTest, Truncate) {
119  AssertParse(&state_,
120 "build out: cat mid\n"
121 "build mid: cat in\n");
122 
123  BuildLog log1;
124  string err;
125  EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err));
126  ASSERT_EQ("", err);
127  log1.RecordCommand(state_.edges_[0], 15, 18);
128  log1.RecordCommand(state_.edges_[1], 20, 25);
129  log1.Close();
130 
131  struct stat statbuf;
132  ASSERT_EQ(0, stat(kTestFilename, &statbuf));
133  ASSERT_GT(statbuf.st_size, 0);
134 
135  // For all possible truncations of the input file, assert that we don't
136  // crash when parsing.
137  for (off_t size = statbuf.st_size; size > 0; --size) {
138  BuildLog log2;
139  string err;
140  EXPECT_TRUE(log2.OpenForWrite(kTestFilename, &err));
141  ASSERT_EQ("", err);
142  log2.RecordCommand(state_.edges_[0], 15, 18);
143  log2.RecordCommand(state_.edges_[1], 20, 25);
144  log2.Close();
145 
146  ASSERT_TRUE(Truncate(kTestFilename, size, &err));
147 
148  BuildLog log3;
149  err.clear();
150  ASSERT_TRUE(log3.Load(kTestFilename, &err) || !err.empty());
151  }
152 }
153 
154 TEST_F(BuildLogTest, ObsoleteOldVersion) {
155  FILE* f = fopen(kTestFilename, "wb");
156  fprintf(f, "# ninja log v3\n");
157  fprintf(f, "123 456 0 out command\n");
158  fclose(f);
159 
160  string err;
161  BuildLog log;
162  EXPECT_TRUE(log.Load(kTestFilename, &err));
163  ASSERT_NE(err.find("version"), string::npos);
164 }
165 
166 TEST_F(BuildLogTest, SpacesInOutputV4) {
167  FILE* f = fopen(kTestFilename, "wb");
168  fprintf(f, "# ninja log v4\n");
169  fprintf(f, "123\t456\t456\tout with space\tcommand\n");
170  fclose(f);
171 
172  string err;
173  BuildLog log;
174  EXPECT_TRUE(log.Load(kTestFilename, &err));
175  ASSERT_EQ("", err);
176 
177  BuildLog::LogEntry* e = log.LookupByOutput("out with space");
178  ASSERT_TRUE(e);
179  ASSERT_EQ(123, e->start_time);
180  ASSERT_EQ(456, e->end_time);
181  ASSERT_EQ(456, e->restat_mtime);
182  ASSERT_NO_FATAL_FAILURE(AssertHash("command", e->command_hash));
183 }
184 
185 TEST_F(BuildLogTest, DuplicateVersionHeader) {
186  // Old versions of ninja accidentally wrote multiple version headers to the
187  // build log on Windows. This shouldn't crash, and the second version header
188  // should be ignored.
189  FILE* f = fopen(kTestFilename, "wb");
190  fprintf(f, "# ninja log v4\n");
191  fprintf(f, "123\t456\t456\tout\tcommand\n");
192  fprintf(f, "# ninja log v4\n");
193  fprintf(f, "456\t789\t789\tout2\tcommand2\n");
194  fclose(f);
195 
196  string err;
197  BuildLog log;
198  EXPECT_TRUE(log.Load(kTestFilename, &err));
199  ASSERT_EQ("", err);
200 
201  BuildLog::LogEntry* e = log.LookupByOutput("out");
202  ASSERT_TRUE(e);
203  ASSERT_EQ(123, e->start_time);
204  ASSERT_EQ(456, e->end_time);
205  ASSERT_EQ(456, e->restat_mtime);
206  ASSERT_NO_FATAL_FAILURE(AssertHash("command", e->command_hash));
207 
208  e = log.LookupByOutput("out2");
209  ASSERT_TRUE(e);
210  ASSERT_EQ(456, e->start_time);
211  ASSERT_EQ(789, e->end_time);
212  ASSERT_EQ(789, e->restat_mtime);
213  ASSERT_NO_FATAL_FAILURE(AssertHash("command2", e->command_hash));
214 }
215 
216 TEST_F(BuildLogTest, VeryLongInputLine) {
217  // Ninja's build log buffer is currently 256kB. Lines longer than that are
218  // silently ignored, but don't affect parsing of other lines.
219  FILE* f = fopen(kTestFilename, "wb");
220  fprintf(f, "# ninja log v4\n");
221  fprintf(f, "123\t456\t456\tout\tcommand start");
222  for (size_t i = 0; i < (512 << 10) / strlen(" more_command"); ++i)
223  fputs(" more_command", f);
224  fprintf(f, "\n");
225  fprintf(f, "456\t789\t789\tout2\tcommand2\n");
226  fclose(f);
227 
228  string err;
229  BuildLog log;
230  EXPECT_TRUE(log.Load(kTestFilename, &err));
231  ASSERT_EQ("", err);
232 
233  BuildLog::LogEntry* e = log.LookupByOutput("out");
234  ASSERT_EQ(NULL, e);
235 
236  e = log.LookupByOutput("out2");
237  ASSERT_TRUE(e);
238  ASSERT_EQ(456, e->start_time);
239  ASSERT_EQ(789, e->end_time);
240  ASSERT_EQ(789, e->restat_mtime);
241  ASSERT_NO_FATAL_FAILURE(AssertHash("command2", e->command_hash));
242 }
243 
244 TEST_F(BuildLogTest, MultiTargetEdge) {
245  AssertParse(&state_,
246 "build out out.d: cat\n");
247 
248  BuildLog log;
249  log.RecordCommand(state_.edges_[0], 21, 22);
250 
251  ASSERT_EQ(2u, log.entries().size());
252  BuildLog::LogEntry* e1 = log.LookupByOutput("out");
253  ASSERT_TRUE(e1);
254  BuildLog::LogEntry* e2 = log.LookupByOutput("out.d");
255  ASSERT_TRUE(e2);
256  ASSERT_EQ("out", e1->output);
257  ASSERT_EQ("out.d", e2->output);
258  ASSERT_EQ(21, e1->start_time);
259  ASSERT_EQ(21, e2->start_time);
260  ASSERT_EQ(22, e2->end_time);
261  ASSERT_EQ(22, e2->end_time);
262 }
263 
264 } // anonymous namespace
bool OpenForWrite(const string &path, string *err)
Definition: build_log.cc:110
const Entries & entries() const
Definition: build_log.h:78
TimeStamp restat_mtime
Definition: build_log.h:52
void AssertParse(State *state, const char *input)
Definition: test.cc:90
void Close()
Definition: build_log.cc:164
Store a log of every command ran for every build.
Definition: build_log.h:35
A base test fixture that includes a State object with a builtin "cat" rule.
Definition: test.h:30
uint64_t command_hash
Definition: build_log.h:49
void RecordCommand(Edge *edge, int start_time, int end_time, TimeStamp restat_mtime=0)
Definition: build_log.cc:139
TEST_F(PlanTest, Basic)
Definition: build_test.cc:49
const char kTestFilename[]
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:178
bool Truncate(const string &path, size_t size, string *err)
Truncates a file to the given size.
Definition: util.cc:363
LogEntry * LookupByOutput(const string &path)
Lookup a previously-run command by its output path.
Definition: build_log.cc:337
bool Load(const string &path, string *err)
Load the on-disk log.
Definition: build_log.cc:221
void AssertHash(const char *expected, uint64_t actual)
Definition: test.cc:97