Ninja
deps_log_test.cc
Go to the documentation of this file.
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