github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/src/deps_log_test.cc (about) 1 // Copyright 2012 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 "deps_log.h" 16 17 #include <sys/stat.h> 18 #ifndef _WIN32 19 #include <unistd.h> 20 #endif 21 22 #include "graph.h" 23 #include "util.h" 24 #include "test.h" 25 26 using namespace std; 27 28 namespace { 29 30 const char kTestFilename[] = "DepsLogTest-tempfile"; 31 32 struct DepsLogTest : public testing::Test { 33 virtual void SetUp() { 34 // In case a crashing test left a stale file behind. 35 unlink(kTestFilename); 36 } 37 virtual void TearDown() { 38 unlink(kTestFilename); 39 } 40 }; 41 42 TEST_F(DepsLogTest, WriteRead) { 43 State state1; 44 DepsLog log1; 45 string err; 46 EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err)); 47 ASSERT_EQ("", err); 48 49 { 50 vector<Node*> deps; 51 deps.push_back(state1.GetNode("foo.h", 0)); 52 deps.push_back(state1.GetNode("bar.h", 0)); 53 log1.RecordDeps(state1.GetNode("out.o", 0), 1, deps); 54 55 deps.clear(); 56 deps.push_back(state1.GetNode("foo.h", 0)); 57 deps.push_back(state1.GetNode("bar2.h", 0)); 58 log1.RecordDeps(state1.GetNode("out2.o", 0), 2, deps); 59 60 DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o", 0)); 61 ASSERT_TRUE(log_deps); 62 ASSERT_EQ(1, log_deps->mtime); 63 ASSERT_EQ(2, log_deps->node_count); 64 ASSERT_EQ("foo.h", log_deps->nodes[0]->path()); 65 ASSERT_EQ("bar.h", log_deps->nodes[1]->path()); 66 } 67 68 log1.Close(); 69 70 State state2; 71 DepsLog log2; 72 EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err)); 73 ASSERT_EQ("", err); 74 75 ASSERT_EQ(log1.nodes().size(), log2.nodes().size()); 76 for (int i = 0; i < (int)log1.nodes().size(); ++i) { 77 Node* node1 = log1.nodes()[i]; 78 Node* node2 = log2.nodes()[i]; 79 ASSERT_EQ(i, node1->id()); 80 ASSERT_EQ(node1->id(), node2->id()); 81 } 82 83 // Spot-check the entries in log2. 84 DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out2.o", 0)); 85 ASSERT_TRUE(log_deps); 86 ASSERT_EQ(2, log_deps->mtime); 87 ASSERT_EQ(2, log_deps->node_count); 88 ASSERT_EQ("foo.h", log_deps->nodes[0]->path()); 89 ASSERT_EQ("bar2.h", log_deps->nodes[1]->path()); 90 } 91 92 TEST_F(DepsLogTest, LotsOfDeps) { 93 const int kNumDeps = 100000; // More than 64k. 94 95 State state1; 96 DepsLog log1; 97 string err; 98 EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err)); 99 ASSERT_EQ("", err); 100 101 { 102 vector<Node*> deps; 103 for (int i = 0; i < kNumDeps; ++i) { 104 char buf[32]; 105 sprintf(buf, "file%d.h", i); 106 deps.push_back(state1.GetNode(buf, 0)); 107 } 108 log1.RecordDeps(state1.GetNode("out.o", 0), 1, deps); 109 110 DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o", 0)); 111 ASSERT_EQ(kNumDeps, log_deps->node_count); 112 } 113 114 log1.Close(); 115 116 State state2; 117 DepsLog log2; 118 EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err)); 119 ASSERT_EQ("", err); 120 121 DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out.o", 0)); 122 ASSERT_EQ(kNumDeps, log_deps->node_count); 123 } 124 125 // Verify that adding the same deps twice doesn't grow the file. 126 TEST_F(DepsLogTest, DoubleEntry) { 127 // Write some deps to the file and grab its size. 128 int file_size; 129 { 130 State state; 131 DepsLog log; 132 string err; 133 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 134 ASSERT_EQ("", err); 135 136 vector<Node*> deps; 137 deps.push_back(state.GetNode("foo.h", 0)); 138 deps.push_back(state.GetNode("bar.h", 0)); 139 log.RecordDeps(state.GetNode("out.o", 0), 1, deps); 140 log.Close(); 141 142 struct stat st; 143 ASSERT_EQ(0, stat(kTestFilename, &st)); 144 file_size = (int)st.st_size; 145 ASSERT_GT(file_size, 0); 146 } 147 148 // Now reload the file, and read the same deps. 149 { 150 State state; 151 DepsLog log; 152 string err; 153 EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); 154 155 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 156 ASSERT_EQ("", err); 157 158 vector<Node*> deps; 159 deps.push_back(state.GetNode("foo.h", 0)); 160 deps.push_back(state.GetNode("bar.h", 0)); 161 log.RecordDeps(state.GetNode("out.o", 0), 1, deps); 162 log.Close(); 163 164 struct stat st; 165 ASSERT_EQ(0, stat(kTestFilename, &st)); 166 int file_size_2 = (int)st.st_size; 167 ASSERT_EQ(file_size, file_size_2); 168 } 169 } 170 171 // Verify that adding the new deps works and can be compacted away. 172 TEST_F(DepsLogTest, Recompact) { 173 const char kManifest[] = 174 "rule cc\n" 175 " command = cc\n" 176 " deps = gcc\n" 177 "build out.o: cc\n" 178 "build other_out.o: cc\n"; 179 180 // Write some deps to the file and grab its size. 181 int file_size; 182 { 183 State state; 184 ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest)); 185 DepsLog log; 186 string err; 187 ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err)); 188 ASSERT_EQ("", err); 189 190 vector<Node*> deps; 191 deps.push_back(state.GetNode("foo.h", 0)); 192 deps.push_back(state.GetNode("bar.h", 0)); 193 log.RecordDeps(state.GetNode("out.o", 0), 1, deps); 194 195 deps.clear(); 196 deps.push_back(state.GetNode("foo.h", 0)); 197 deps.push_back(state.GetNode("baz.h", 0)); 198 log.RecordDeps(state.GetNode("other_out.o", 0), 1, deps); 199 200 log.Close(); 201 202 struct stat st; 203 ASSERT_EQ(0, stat(kTestFilename, &st)); 204 file_size = (int)st.st_size; 205 ASSERT_GT(file_size, 0); 206 } 207 208 // Now reload the file, and add slightly different deps. 209 int file_size_2; 210 { 211 State state; 212 ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest)); 213 DepsLog log; 214 string err; 215 ASSERT_TRUE(log.Load(kTestFilename, &state, &err)); 216 217 ASSERT_TRUE(log.OpenForWrite(kTestFilename, &err)); 218 ASSERT_EQ("", err); 219 220 vector<Node*> deps; 221 deps.push_back(state.GetNode("foo.h", 0)); 222 log.RecordDeps(state.GetNode("out.o", 0), 1, deps); 223 log.Close(); 224 225 struct stat st; 226 ASSERT_EQ(0, stat(kTestFilename, &st)); 227 file_size_2 = (int)st.st_size; 228 // The file should grow to record the new deps. 229 ASSERT_GT(file_size_2, file_size); 230 } 231 232 // Now reload the file, verify the new deps have replaced the old, then 233 // recompact. 234 int file_size_3; 235 { 236 State state; 237 ASSERT_NO_FATAL_FAILURE(AssertParse(&state, kManifest)); 238 DepsLog log; 239 string err; 240 ASSERT_TRUE(log.Load(kTestFilename, &state, &err)); 241 242 Node* out = state.GetNode("out.o", 0); 243 DepsLog::Deps* deps = log.GetDeps(out); 244 ASSERT_TRUE(deps); 245 ASSERT_EQ(1, deps->mtime); 246 ASSERT_EQ(1, deps->node_count); 247 ASSERT_EQ("foo.h", deps->nodes[0]->path()); 248 249 Node* other_out = state.GetNode("other_out.o", 0); 250 deps = log.GetDeps(other_out); 251 ASSERT_TRUE(deps); 252 ASSERT_EQ(1, deps->mtime); 253 ASSERT_EQ(2, deps->node_count); 254 ASSERT_EQ("foo.h", deps->nodes[0]->path()); 255 ASSERT_EQ("baz.h", deps->nodes[1]->path()); 256 257 ASSERT_TRUE(log.Recompact(kTestFilename, &err)); 258 259 // The in-memory deps graph should still be valid after recompaction. 260 deps = log.GetDeps(out); 261 ASSERT_TRUE(deps); 262 ASSERT_EQ(1, deps->mtime); 263 ASSERT_EQ(1, deps->node_count); 264 ASSERT_EQ("foo.h", deps->nodes[0]->path()); 265 ASSERT_EQ(out, log.nodes()[out->id()]); 266 267 deps = log.GetDeps(other_out); 268 ASSERT_TRUE(deps); 269 ASSERT_EQ(1, deps->mtime); 270 ASSERT_EQ(2, deps->node_count); 271 ASSERT_EQ("foo.h", deps->nodes[0]->path()); 272 ASSERT_EQ("baz.h", deps->nodes[1]->path()); 273 ASSERT_EQ(other_out, log.nodes()[other_out->id()]); 274 275 // The file should have shrunk a bit for the smaller deps. 276 struct stat st; 277 ASSERT_EQ(0, stat(kTestFilename, &st)); 278 file_size_3 = (int)st.st_size; 279 ASSERT_LT(file_size_3, file_size_2); 280 } 281 282 // Now reload the file and recompact with an empty manifest. The previous 283 // entries should be removed. 284 { 285 State state; 286 // Intentionally not parsing kManifest here. 287 DepsLog log; 288 string err; 289 ASSERT_TRUE(log.Load(kTestFilename, &state, &err)); 290 291 Node* out = state.GetNode("out.o", 0); 292 DepsLog::Deps* deps = log.GetDeps(out); 293 ASSERT_TRUE(deps); 294 ASSERT_EQ(1, deps->mtime); 295 ASSERT_EQ(1, deps->node_count); 296 ASSERT_EQ("foo.h", deps->nodes[0]->path()); 297 298 Node* other_out = state.GetNode("other_out.o", 0); 299 deps = log.GetDeps(other_out); 300 ASSERT_TRUE(deps); 301 ASSERT_EQ(1, deps->mtime); 302 ASSERT_EQ(2, deps->node_count); 303 ASSERT_EQ("foo.h", deps->nodes[0]->path()); 304 ASSERT_EQ("baz.h", deps->nodes[1]->path()); 305 306 ASSERT_TRUE(log.Recompact(kTestFilename, &err)); 307 308 // The previous entries should have been removed. 309 deps = log.GetDeps(out); 310 ASSERT_FALSE(deps); 311 312 deps = log.GetDeps(other_out); 313 ASSERT_FALSE(deps); 314 315 // The .h files pulled in via deps should no longer have ids either. 316 ASSERT_EQ(-1, state.LookupNode("foo.h")->id()); 317 ASSERT_EQ(-1, state.LookupNode("baz.h")->id()); 318 319 // The file should have shrunk more. 320 struct stat st; 321 ASSERT_EQ(0, stat(kTestFilename, &st)); 322 int file_size_4 = (int)st.st_size; 323 ASSERT_LT(file_size_4, file_size_3); 324 } 325 } 326 327 // Verify that invalid file headers cause a new build. 328 TEST_F(DepsLogTest, InvalidHeader) { 329 const char *kInvalidHeaders[] = { 330 "", // Empty file. 331 "# ninjad", // Truncated first line. 332 "# ninjadeps\n", // No version int. 333 "# ninjadeps\n\001\002", // Truncated version int. 334 "# ninjadeps\n\001\002\003\004" // Invalid version int. 335 }; 336 for (size_t i = 0; i < sizeof(kInvalidHeaders) / sizeof(kInvalidHeaders[0]); 337 ++i) { 338 FILE* deps_log = fopen(kTestFilename, "wb"); 339 ASSERT_TRUE(deps_log != NULL); 340 ASSERT_EQ( 341 strlen(kInvalidHeaders[i]), 342 fwrite(kInvalidHeaders[i], 1, strlen(kInvalidHeaders[i]), deps_log)); 343 ASSERT_EQ(0 ,fclose(deps_log)); 344 345 string err; 346 DepsLog log; 347 State state; 348 ASSERT_TRUE(log.Load(kTestFilename, &state, &err)); 349 EXPECT_EQ("bad deps log signature or version; starting over", err); 350 } 351 } 352 353 // Simulate what happens when loading a truncated log file. 354 TEST_F(DepsLogTest, Truncated) { 355 // Create a file with some entries. 356 { 357 State state; 358 DepsLog log; 359 string err; 360 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 361 ASSERT_EQ("", err); 362 363 vector<Node*> deps; 364 deps.push_back(state.GetNode("foo.h", 0)); 365 deps.push_back(state.GetNode("bar.h", 0)); 366 log.RecordDeps(state.GetNode("out.o", 0), 1, deps); 367 368 deps.clear(); 369 deps.push_back(state.GetNode("foo.h", 0)); 370 deps.push_back(state.GetNode("bar2.h", 0)); 371 log.RecordDeps(state.GetNode("out2.o", 0), 2, deps); 372 373 log.Close(); 374 } 375 376 // Get the file size. 377 struct stat st; 378 ASSERT_EQ(0, stat(kTestFilename, &st)); 379 380 // Try reloading at truncated sizes. 381 // Track how many nodes/deps were found; they should decrease with 382 // smaller sizes. 383 int node_count = 5; 384 int deps_count = 2; 385 for (int size = (int)st.st_size; size > 0; --size) { 386 string err; 387 ASSERT_TRUE(Truncate(kTestFilename, size, &err)); 388 389 State state; 390 DepsLog log; 391 EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); 392 if (!err.empty()) { 393 // At some point the log will be so short as to be unparsable. 394 break; 395 } 396 397 ASSERT_GE(node_count, (int)log.nodes().size()); 398 node_count = log.nodes().size(); 399 400 // Count how many non-NULL deps entries there are. 401 int new_deps_count = 0; 402 for (vector<DepsLog::Deps*>::const_iterator i = log.deps().begin(); 403 i != log.deps().end(); ++i) { 404 if (*i) 405 ++new_deps_count; 406 } 407 ASSERT_GE(deps_count, new_deps_count); 408 deps_count = new_deps_count; 409 } 410 } 411 412 // Run the truncation-recovery logic. 413 TEST_F(DepsLogTest, TruncatedRecovery) { 414 // Create a file with some entries. 415 { 416 State state; 417 DepsLog log; 418 string err; 419 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 420 ASSERT_EQ("", err); 421 422 vector<Node*> deps; 423 deps.push_back(state.GetNode("foo.h", 0)); 424 deps.push_back(state.GetNode("bar.h", 0)); 425 log.RecordDeps(state.GetNode("out.o", 0), 1, deps); 426 427 deps.clear(); 428 deps.push_back(state.GetNode("foo.h", 0)); 429 deps.push_back(state.GetNode("bar2.h", 0)); 430 log.RecordDeps(state.GetNode("out2.o", 0), 2, deps); 431 432 log.Close(); 433 } 434 435 // Shorten the file, corrupting the last record. 436 { 437 struct stat st; 438 ASSERT_EQ(0, stat(kTestFilename, &st)); 439 string err; 440 ASSERT_TRUE(Truncate(kTestFilename, st.st_size - 2, &err)); 441 } 442 443 // Load the file again, add an entry. 444 { 445 State state; 446 DepsLog log; 447 string err; 448 EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); 449 ASSERT_EQ("premature end of file; recovering", err); 450 err.clear(); 451 452 // The truncated entry should've been discarded. 453 EXPECT_EQ(NULL, log.GetDeps(state.GetNode("out2.o", 0))); 454 455 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 456 ASSERT_EQ("", err); 457 458 // Add a new entry. 459 vector<Node*> deps; 460 deps.push_back(state.GetNode("foo.h", 0)); 461 deps.push_back(state.GetNode("bar2.h", 0)); 462 log.RecordDeps(state.GetNode("out2.o", 0), 3, deps); 463 464 log.Close(); 465 } 466 467 // Load the file a third time to verify appending after a mangled 468 // entry doesn't break things. 469 { 470 State state; 471 DepsLog log; 472 string err; 473 EXPECT_TRUE(log.Load(kTestFilename, &state, &err)); 474 475 // The truncated entry should exist. 476 DepsLog::Deps* deps = log.GetDeps(state.GetNode("out2.o", 0)); 477 ASSERT_TRUE(deps); 478 } 479 } 480 481 TEST_F(DepsLogTest, ReverseDepsNodes) { 482 State state; 483 DepsLog log; 484 string err; 485 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 486 ASSERT_EQ("", err); 487 488 vector<Node*> deps; 489 deps.push_back(state.GetNode("foo.h", 0)); 490 deps.push_back(state.GetNode("bar.h", 0)); 491 log.RecordDeps(state.GetNode("out.o", 0), 1, deps); 492 493 deps.clear(); 494 deps.push_back(state.GetNode("foo.h", 0)); 495 deps.push_back(state.GetNode("bar2.h", 0)); 496 log.RecordDeps(state.GetNode("out2.o", 0), 2, deps); 497 498 log.Close(); 499 500 Node* rev_deps = log.GetFirstReverseDepsNode(state.GetNode("foo.h", 0)); 501 EXPECT_TRUE(rev_deps == state.GetNode("out.o", 0) || 502 rev_deps == state.GetNode("out2.o", 0)); 503 504 rev_deps = log.GetFirstReverseDepsNode(state.GetNode("bar.h", 0)); 505 EXPECT_TRUE(rev_deps == state.GetNode("out.o", 0)); 506 } 507 508 } // anonymous namespace