Ninja
|
00001 // Copyright 2012 Google Inc. All Rights Reserved. 00002 // 00003 // Licensed under the Apache License, Version 2.0 (the "License"); 00004 // you may not use this file except in compliance with the License. 00005 // You may obtain a copy of the License at 00006 // 00007 // http://www.apache.org/licenses/LICENSE-2.0 00008 // 00009 // Unless required by applicable law or agreed to in writing, software 00010 // distributed under the License is distributed on an "AS IS" BASIS, 00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00012 // See the License for the specific language governing permissions and 00013 // limitations under the License. 00014 00015 #include "deps_log.h" 00016 00017 #include "graph.h" 00018 #include "util.h" 00019 #include "test.h" 00020 00021 namespace { 00022 00023 const char kTestFilename[] = "DepsLogTest-tempfile"; 00024 00025 struct DepsLogTest : public testing::Test { 00026 virtual void SetUp() { 00027 // In case a crashing test left a stale file behind. 00028 unlink(kTestFilename); 00029 } 00030 virtual void TearDown() { 00031 unlink(kTestFilename); 00032 } 00033 }; 00034 00035 TEST_F(DepsLogTest, WriteRead) { 00036 State state1; 00037 DepsLog log1; 00038 string err; 00039 EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err)); 00040 ASSERT_EQ("", err); 00041 00042 { 00043 vector<Node*> deps; 00044 deps.push_back(state1.GetNode("foo.h")); 00045 deps.push_back(state1.GetNode("bar.h")); 00046 log1.RecordDeps(state1.GetNode("out.o"), 1, deps); 00047 00048 deps.clear(); 00049 deps.push_back(state1.GetNode("foo.h")); 00050 deps.push_back(state1.GetNode("bar2.h")); 00051 log1.RecordDeps(state1.GetNode("out2.o"), 2, deps); 00052 00053 DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o")); 00054 ASSERT_TRUE(log_deps); 00055 ASSERT_EQ(1, log_deps->mtime); 00056 ASSERT_EQ(2, log_deps->node_count); 00057 ASSERT_EQ("foo.h", log_deps->nodes[0]->path()); 00058 ASSERT_EQ("bar.h", log_deps->nodes[1]->path()); 00059 } 00060 00061 log1.Close(); 00062 00063 State state2; 00064 DepsLog log2; 00065 EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err)); 00066 ASSERT_EQ("", err); 00067 00068 ASSERT_EQ(log1.nodes().size(), log2.nodes().size()); 00069 for (int i = 0; i < (int)log1.nodes().size(); ++i) { 00070 Node* node1 = log1.nodes()[i]; 00071 Node* node2 = log2.nodes()[i]; 00072 ASSERT_EQ(i, node1->id()); 00073 ASSERT_EQ(node1->id(), node2->id()); 00074 } 00075 00076 // Spot-check the entries in log2. 00077 DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out2.o")); 00078 ASSERT_TRUE(log_deps); 00079 ASSERT_EQ(2, log_deps->mtime); 00080 ASSERT_EQ(2, log_deps->node_count); 00081 ASSERT_EQ("foo.h", log_deps->nodes[0]->path()); 00082 ASSERT_EQ("bar2.h", log_deps->nodes[1]->path()); 00083 } 00084 00085 // Verify that adding the same deps twice doesn't grow the file. 00086 TEST_F(DepsLogTest, DoubleEntry) { 00087 // Write some deps to the file and grab its size. 00088 int file_size; 00089 { 00090 State state; 00091 DepsLog log; 00092 string err; 00093 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 00094 ASSERT_EQ("", err); 00095 00096 vector<Node*> deps; 00097 deps.push_back(state.GetNode("foo.h")); 00098 deps.push_back(state.GetNode("bar.h")); 00099 log.RecordDeps(state.GetNode("out.o"), 1, deps); 00100 log.Close(); 00101 00102 struct stat st; 00103 ASSERT_EQ(0, stat(kTestFilename, &st)); 00104 file_size = (int)st.st_size; 00105 ASSERT_GT(file_size, 0); 00106 } 00107 00108 // Now reload the file, and readd the same deps. 00109 { 00110 State state; 00111 DepsLog log; 00112 string err; 00113 EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); 00114 00115 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 00116 ASSERT_EQ("", err); 00117 00118 vector<Node*> deps; 00119 deps.push_back(state.GetNode("foo.h")); 00120 deps.push_back(state.GetNode("bar.h")); 00121 log.RecordDeps(state.GetNode("out.o"), 1, deps); 00122 log.Close(); 00123 00124 struct stat st; 00125 ASSERT_EQ(0, stat(kTestFilename, &st)); 00126 int file_size_2 = (int)st.st_size; 00127 ASSERT_EQ(file_size, file_size_2); 00128 } 00129 } 00130 00131 // Verify that adding the new deps works and can be compacted away. 00132 TEST_F(DepsLogTest, Recompact) { 00133 // Write some deps to the file and grab its size. 00134 int file_size; 00135 { 00136 State state; 00137 DepsLog log; 00138 string err; 00139 ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err)); 00140 ASSERT_EQ("", err); 00141 00142 vector<Node*> deps; 00143 deps.push_back(state.GetNode("foo.h")); 00144 deps.push_back(state.GetNode("bar.h")); 00145 log.RecordDeps(state.GetNode("out.o"), 1, deps); 00146 00147 deps.clear(); 00148 deps.push_back(state.GetNode("foo.h")); 00149 deps.push_back(state.GetNode("baz.h")); 00150 log.RecordDeps(state.GetNode("other_out.o"), 1, deps); 00151 00152 log.Close(); 00153 00154 struct stat st; 00155 ASSERT_EQ(0, stat(kTestFilename, &st)); 00156 file_size = (int)st.st_size; 00157 ASSERT_GT(file_size, 0); 00158 } 00159 00160 // Now reload the file, and add slighly different deps. 00161 int file_size_2; 00162 { 00163 State state; 00164 DepsLog log; 00165 string err; 00166 ASSERT_TRUE(log.Load(kTestFilename, &state, &err)); 00167 00168 ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err)); 00169 ASSERT_EQ("", err); 00170 00171 vector<Node*> deps; 00172 deps.push_back(state.GetNode("foo.h")); 00173 log.RecordDeps(state.GetNode("out.o"), 1, deps); 00174 log.Close(); 00175 00176 struct stat st; 00177 ASSERT_EQ(0, stat(kTestFilename, &st)); 00178 file_size_2 = (int)st.st_size; 00179 // The file should grow to record the new deps. 00180 ASSERT_GT(file_size_2, file_size); 00181 } 00182 00183 // Now reload the file, verify the new deps have replaced the old, then 00184 // recompact. 00185 { 00186 State state; 00187 DepsLog log; 00188 string err; 00189 ASSERT_TRUE(log.Load(kTestFilename, &state, &err)); 00190 00191 Node* out = state.GetNode("out.o"); 00192 DepsLog::Deps* deps = log.GetDeps(out); 00193 ASSERT_TRUE(deps); 00194 ASSERT_EQ(1, deps->mtime); 00195 ASSERT_EQ(1, deps->node_count); 00196 ASSERT_EQ("foo.h", deps->nodes[0]->path()); 00197 00198 Node* other_out = state.GetNode("other_out.o"); 00199 deps = log.GetDeps(other_out); 00200 ASSERT_TRUE(deps); 00201 ASSERT_EQ(1, deps->mtime); 00202 ASSERT_EQ(2, deps->node_count); 00203 ASSERT_EQ("foo.h", deps->nodes[0]->path()); 00204 ASSERT_EQ("baz.h", deps->nodes[1]->path()); 00205 00206 ASSERT_TRUE(log.Recompact(kTestFilename, &err)); 00207 00208 // The in-memory deps graph should still be valid after recompaction. 00209 deps = log.GetDeps(out); 00210 ASSERT_TRUE(deps); 00211 ASSERT_EQ(1, deps->mtime); 00212 ASSERT_EQ(1, deps->node_count); 00213 ASSERT_EQ("foo.h", deps->nodes[0]->path()); 00214 ASSERT_EQ(out, log.nodes()[out->id()]); 00215 00216 deps = log.GetDeps(other_out); 00217 ASSERT_TRUE(deps); 00218 ASSERT_EQ(1, deps->mtime); 00219 ASSERT_EQ(2, deps->node_count); 00220 ASSERT_EQ("foo.h", deps->nodes[0]->path()); 00221 ASSERT_EQ("baz.h", deps->nodes[1]->path()); 00222 ASSERT_EQ(other_out, log.nodes()[other_out->id()]); 00223 00224 // The file should have shrunk a bit for the smaller deps. 00225 struct stat st; 00226 ASSERT_EQ(0, stat(kTestFilename, &st)); 00227 int file_size_3 = (int)st.st_size; 00228 ASSERT_LT(file_size_3, file_size_2); 00229 } 00230 } 00231 00232 // Verify that invalid file headers cause a new build. 00233 TEST_F(DepsLogTest, InvalidHeader) { 00234 const char *kInvalidHeaders[] = { 00235 "", // Empty file. 00236 "# ninjad", // Truncated first line. 00237 "# ninjadeps\n", // No version int. 00238 "# ninjadeps\n\001\002", // Truncated version int. 00239 "# ninjadeps\n\001\002\003\004" // Invalid version int. 00240 }; 00241 for (size_t i = 0; i < sizeof(kInvalidHeaders) / sizeof(kInvalidHeaders[0]); 00242 ++i) { 00243 FILE* deps_log = fopen(kTestFilename, "wb"); 00244 ASSERT_TRUE(deps_log != NULL); 00245 ASSERT_EQ( 00246 strlen(kInvalidHeaders[i]), 00247 fwrite(kInvalidHeaders[i], 1, strlen(kInvalidHeaders[i]), deps_log)); 00248 ASSERT_EQ(0 ,fclose(deps_log)); 00249 00250 string err; 00251 DepsLog log; 00252 State state; 00253 ASSERT_TRUE(log.Load(kTestFilename, &state, &err)); 00254 EXPECT_EQ("bad deps log signature or version; starting over", err); 00255 } 00256 } 00257 00258 // Simulate what happens when loading a truncated log file. 00259 TEST_F(DepsLogTest, Truncated) { 00260 // Create a file with some entries. 00261 { 00262 State state; 00263 DepsLog log; 00264 string err; 00265 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 00266 ASSERT_EQ("", err); 00267 00268 vector<Node*> deps; 00269 deps.push_back(state.GetNode("foo.h")); 00270 deps.push_back(state.GetNode("bar.h")); 00271 log.RecordDeps(state.GetNode("out.o"), 1, deps); 00272 00273 deps.clear(); 00274 deps.push_back(state.GetNode("foo.h")); 00275 deps.push_back(state.GetNode("bar2.h")); 00276 log.RecordDeps(state.GetNode("out2.o"), 2, deps); 00277 00278 log.Close(); 00279 } 00280 00281 // Get the file size. 00282 struct stat st; 00283 ASSERT_EQ(0, stat(kTestFilename, &st)); 00284 00285 // Try reloading at truncated sizes. 00286 // Track how many nodes/deps were found; they should decrease with 00287 // smaller sizes. 00288 int node_count = 5; 00289 int deps_count = 2; 00290 for (int size = (int)st.st_size; size > 0; --size) { 00291 string err; 00292 ASSERT_TRUE(Truncate(kTestFilename, size, &err)); 00293 00294 State state; 00295 DepsLog log; 00296 EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); 00297 if (!err.empty()) { 00298 // At some point the log will be so short as to be unparseable. 00299 break; 00300 } 00301 00302 ASSERT_GE(node_count, (int)log.nodes().size()); 00303 node_count = log.nodes().size(); 00304 00305 // Count how many non-NULL deps entries there are. 00306 int new_deps_count = 0; 00307 for (vector<DepsLog::Deps*>::const_iterator i = log.deps().begin(); 00308 i != log.deps().end(); ++i) { 00309 if (*i) 00310 ++new_deps_count; 00311 } 00312 ASSERT_GE(deps_count, new_deps_count); 00313 deps_count = new_deps_count; 00314 } 00315 } 00316 00317 // Run the truncation-recovery logic. 00318 TEST_F(DepsLogTest, TruncatedRecovery) { 00319 // Create a file with some entries. 00320 { 00321 State state; 00322 DepsLog log; 00323 string err; 00324 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 00325 ASSERT_EQ("", err); 00326 00327 vector<Node*> deps; 00328 deps.push_back(state.GetNode("foo.h")); 00329 deps.push_back(state.GetNode("bar.h")); 00330 log.RecordDeps(state.GetNode("out.o"), 1, deps); 00331 00332 deps.clear(); 00333 deps.push_back(state.GetNode("foo.h")); 00334 deps.push_back(state.GetNode("bar2.h")); 00335 log.RecordDeps(state.GetNode("out2.o"), 2, deps); 00336 00337 log.Close(); 00338 } 00339 00340 // Shorten the file, corrupting the last record. 00341 struct stat st; 00342 ASSERT_EQ(0, stat(kTestFilename, &st)); 00343 string err; 00344 ASSERT_TRUE(Truncate(kTestFilename, st.st_size - 2, &err)); 00345 00346 // Load the file again, add an entry. 00347 { 00348 State state; 00349 DepsLog log; 00350 string err; 00351 EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); 00352 ASSERT_EQ("premature end of file; recovering", err); 00353 err.clear(); 00354 00355 // The truncated entry should've been discarded. 00356 EXPECT_EQ(NULL, log.GetDeps(state.GetNode("out2.o"))); 00357 00358 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 00359 ASSERT_EQ("", err); 00360 00361 // Add a new entry. 00362 vector<Node*> deps; 00363 deps.push_back(state.GetNode("foo.h")); 00364 deps.push_back(state.GetNode("bar2.h")); 00365 log.RecordDeps(state.GetNode("out2.o"), 3, deps); 00366 00367 log.Close(); 00368 } 00369 00370 // Load the file a third time to verify appending after a mangled 00371 // entry doesn't break things. 00372 { 00373 State state; 00374 DepsLog log; 00375 string err; 00376 EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); 00377 00378 // The truncated entry should exist. 00379 DepsLog::Deps* deps = log.GetDeps(state.GetNode("out2.o")); 00380 ASSERT_TRUE(deps); 00381 } 00382 } 00383 00384 } // anonymous namespace