Ninja
|
00001 // Copyright 2011 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 "build_log.h" 00016 00017 #include "util.h" 00018 #include "test.h" 00019 00020 #ifdef _WIN32 00021 #include <fcntl.h> 00022 #include <share.h> 00023 #else 00024 #include <sys/types.h> 00025 #include <sys/stat.h> 00026 #include <unistd.h> 00027 #endif 00028 00029 namespace { 00030 00031 const char kTestFilename[] = "BuildLogTest-tempfile"; 00032 00033 struct BuildLogTest : public StateTestWithBuiltinRules { 00034 virtual void SetUp() { 00035 // In case a crashing test left a stale file behind. 00036 unlink(kTestFilename); 00037 } 00038 virtual void TearDown() { 00039 unlink(kTestFilename); 00040 } 00041 }; 00042 00043 TEST_F(BuildLogTest, WriteRead) { 00044 AssertParse(&state_, 00045 "build out: cat mid\n" 00046 "build mid: cat in\n"); 00047 00048 BuildLog log1; 00049 string err; 00050 EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err)); 00051 ASSERT_EQ("", err); 00052 log1.RecordCommand(state_.edges_[0], 15, 18); 00053 log1.RecordCommand(state_.edges_[1], 20, 25); 00054 log1.Close(); 00055 00056 BuildLog log2; 00057 EXPECT_TRUE(log2.Load(kTestFilename, &err)); 00058 ASSERT_EQ("", err); 00059 00060 ASSERT_EQ(2u, log1.entries().size()); 00061 ASSERT_EQ(2u, log2.entries().size()); 00062 BuildLog::LogEntry* e1 = log1.LookupByOutput("out"); 00063 ASSERT_TRUE(e1); 00064 BuildLog::LogEntry* e2 = log2.LookupByOutput("out"); 00065 ASSERT_TRUE(e2); 00066 ASSERT_TRUE(*e1 == *e2); 00067 ASSERT_EQ(15, e1->start_time); 00068 ASSERT_EQ("out", e1->output); 00069 } 00070 00071 TEST_F(BuildLogTest, FirstWriteAddsSignature) { 00072 const char kExpectedVersion[] = "# ninja log vX\n"; 00073 const size_t kVersionPos = strlen(kExpectedVersion) - 2; // Points at 'X'. 00074 00075 BuildLog log; 00076 string contents, err; 00077 00078 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 00079 ASSERT_EQ("", err); 00080 log.Close(); 00081 00082 ASSERT_EQ(0, ReadFile(kTestFilename, &contents, &err)); 00083 ASSERT_EQ("", err); 00084 if (contents.size() >= kVersionPos) 00085 contents[kVersionPos] = 'X'; 00086 EXPECT_EQ(kExpectedVersion, contents); 00087 00088 // Opening the file anew shouldn't add a second version string. 00089 EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err)); 00090 ASSERT_EQ("", err); 00091 log.Close(); 00092 00093 contents.clear(); 00094 ASSERT_EQ(0, ReadFile(kTestFilename, &contents, &err)); 00095 ASSERT_EQ("", err); 00096 if (contents.size() >= kVersionPos) 00097 contents[kVersionPos] = 'X'; 00098 EXPECT_EQ(kExpectedVersion, contents); 00099 } 00100 00101 TEST_F(BuildLogTest, DoubleEntry) { 00102 FILE* f = fopen(kTestFilename, "wb"); 00103 fprintf(f, "# ninja log v4\n"); 00104 fprintf(f, "0\t1\t2\tout\tcommand abc\n"); 00105 fprintf(f, "3\t4\t5\tout\tcommand def\n"); 00106 fclose(f); 00107 00108 string err; 00109 BuildLog log; 00110 EXPECT_TRUE(log.Load(kTestFilename, &err)); 00111 ASSERT_EQ("", err); 00112 00113 BuildLog::LogEntry* e = log.LookupByOutput("out"); 00114 ASSERT_TRUE(e); 00115 ASSERT_NO_FATAL_FAILURE(AssertHash("command def", e->command_hash)); 00116 } 00117 00118 TEST_F(BuildLogTest, Truncate) { 00119 AssertParse(&state_, 00120 "build out: cat mid\n" 00121 "build mid: cat in\n"); 00122 00123 BuildLog log1; 00124 string err; 00125 EXPECT_TRUE(log1.OpenForWrite(kTestFilename, &err)); 00126 ASSERT_EQ("", err); 00127 log1.RecordCommand(state_.edges_[0], 15, 18); 00128 log1.RecordCommand(state_.edges_[1], 20, 25); 00129 log1.Close(); 00130 00131 struct stat statbuf; 00132 ASSERT_EQ(0, stat(kTestFilename, &statbuf)); 00133 ASSERT_GT(statbuf.st_size, 0); 00134 00135 // For all possible truncations of the input file, assert that we don't 00136 // crash when parsing. 00137 for (off_t size = statbuf.st_size; size > 0; --size) { 00138 BuildLog log2; 00139 string err; 00140 EXPECT_TRUE(log2.OpenForWrite(kTestFilename, &err)); 00141 ASSERT_EQ("", err); 00142 log2.RecordCommand(state_.edges_[0], 15, 18); 00143 log2.RecordCommand(state_.edges_[1], 20, 25); 00144 log2.Close(); 00145 00146 ASSERT_TRUE(Truncate(kTestFilename, size, &err)); 00147 00148 BuildLog log3; 00149 err.clear(); 00150 ASSERT_TRUE(log3.Load(kTestFilename, &err) || !err.empty()); 00151 } 00152 } 00153 00154 TEST_F(BuildLogTest, ObsoleteOldVersion) { 00155 FILE* f = fopen(kTestFilename, "wb"); 00156 fprintf(f, "# ninja log v3\n"); 00157 fprintf(f, "123 456 0 out command\n"); 00158 fclose(f); 00159 00160 string err; 00161 BuildLog log; 00162 EXPECT_TRUE(log.Load(kTestFilename, &err)); 00163 ASSERT_NE(err.find("version"), string::npos); 00164 } 00165 00166 TEST_F(BuildLogTest, SpacesInOutputV4) { 00167 FILE* f = fopen(kTestFilename, "wb"); 00168 fprintf(f, "# ninja log v4\n"); 00169 fprintf(f, "123\t456\t456\tout with space\tcommand\n"); 00170 fclose(f); 00171 00172 string err; 00173 BuildLog log; 00174 EXPECT_TRUE(log.Load(kTestFilename, &err)); 00175 ASSERT_EQ("", err); 00176 00177 BuildLog::LogEntry* e = log.LookupByOutput("out with space"); 00178 ASSERT_TRUE(e); 00179 ASSERT_EQ(123, e->start_time); 00180 ASSERT_EQ(456, e->end_time); 00181 ASSERT_EQ(456, e->restat_mtime); 00182 ASSERT_NO_FATAL_FAILURE(AssertHash("command", e->command_hash)); 00183 } 00184 00185 TEST_F(BuildLogTest, DuplicateVersionHeader) { 00186 // Old versions of ninja accidentally wrote multiple version headers to the 00187 // build log on Windows. This shouldn't crash, and the second version header 00188 // should be ignored. 00189 FILE* f = fopen(kTestFilename, "wb"); 00190 fprintf(f, "# ninja log v4\n"); 00191 fprintf(f, "123\t456\t456\tout\tcommand\n"); 00192 fprintf(f, "# ninja log v4\n"); 00193 fprintf(f, "456\t789\t789\tout2\tcommand2\n"); 00194 fclose(f); 00195 00196 string err; 00197 BuildLog log; 00198 EXPECT_TRUE(log.Load(kTestFilename, &err)); 00199 ASSERT_EQ("", err); 00200 00201 BuildLog::LogEntry* e = log.LookupByOutput("out"); 00202 ASSERT_TRUE(e); 00203 ASSERT_EQ(123, e->start_time); 00204 ASSERT_EQ(456, e->end_time); 00205 ASSERT_EQ(456, e->restat_mtime); 00206 ASSERT_NO_FATAL_FAILURE(AssertHash("command", e->command_hash)); 00207 00208 e = log.LookupByOutput("out2"); 00209 ASSERT_TRUE(e); 00210 ASSERT_EQ(456, e->start_time); 00211 ASSERT_EQ(789, e->end_time); 00212 ASSERT_EQ(789, e->restat_mtime); 00213 ASSERT_NO_FATAL_FAILURE(AssertHash("command2", e->command_hash)); 00214 } 00215 00216 TEST_F(BuildLogTest, VeryLongInputLine) { 00217 // Ninja's build log buffer is currently 256kB. Lines longer than that are 00218 // silently ignored, but don't affect parsing of other lines. 00219 FILE* f = fopen(kTestFilename, "wb"); 00220 fprintf(f, "# ninja log v4\n"); 00221 fprintf(f, "123\t456\t456\tout\tcommand start"); 00222 for (size_t i = 0; i < (512 << 10) / strlen(" more_command"); ++i) 00223 fputs(" more_command", f); 00224 fprintf(f, "\n"); 00225 fprintf(f, "456\t789\t789\tout2\tcommand2\n"); 00226 fclose(f); 00227 00228 string err; 00229 BuildLog log; 00230 EXPECT_TRUE(log.Load(kTestFilename, &err)); 00231 ASSERT_EQ("", err); 00232 00233 BuildLog::LogEntry* e = log.LookupByOutput("out"); 00234 ASSERT_EQ(NULL, e); 00235 00236 e = log.LookupByOutput("out2"); 00237 ASSERT_TRUE(e); 00238 ASSERT_EQ(456, e->start_time); 00239 ASSERT_EQ(789, e->end_time); 00240 ASSERT_EQ(789, e->restat_mtime); 00241 ASSERT_NO_FATAL_FAILURE(AssertHash("command2", e->command_hash)); 00242 } 00243 00244 TEST_F(BuildLogTest, MultiTargetEdge) { 00245 AssertParse(&state_, 00246 "build out out.d: cat\n"); 00247 00248 BuildLog log; 00249 log.RecordCommand(state_.edges_[0], 21, 22); 00250 00251 ASSERT_EQ(2u, log.entries().size()); 00252 BuildLog::LogEntry* e1 = log.LookupByOutput("out"); 00253 ASSERT_TRUE(e1); 00254 BuildLog::LogEntry* e2 = log.LookupByOutput("out.d"); 00255 ASSERT_TRUE(e2); 00256 ASSERT_EQ("out", e1->output); 00257 ASSERT_EQ("out.d", e2->output); 00258 ASSERT_EQ(21, e1->start_time); 00259 ASSERT_EQ(21, e2->start_time); 00260 ASSERT_EQ(22, e2->end_time); 00261 ASSERT_EQ(22, e2->end_time); 00262 } 00263 00264 } // anonymous namespace