Merge changes Iaa05196d,I8bd4f4de,I721e1ff9 into tizen
[platform/upstream/ninja.git] / src / build_log_test.cc
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);
185   ASSERT_NO_FATAL_FAILURE(AssertHash("command", e->command_hash));
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);
209   ASSERT_NO_FATAL_FAILURE(AssertHash("command", e->command_hash));
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);
216   ASSERT_NO_FATAL_FAILURE(AssertHash("command2", e->command_hash));
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);
244   ASSERT_NO_FATAL_FAILURE(AssertHash("command2", e->command_hash));
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