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 <assert.h>
16 #include <stdio.h>
17 #ifdef _WIN32
18 #include <io.h>
19 #include <windows.h>
20 #endif
21
22 #include "disk_interface.h"
23 #include "graph.h"
24 #include "test.h"
25
26 namespace {
27
28 struct DiskInterfaceTest : public testing::Test {
SetUp__anon477a00860111::DiskInterfaceTest29 virtual void SetUp() {
30 // These tests do real disk accesses, so create a temp dir.
31 temp_dir_.CreateAndEnter("Ninja-DiskInterfaceTest");
32 }
33
TearDown__anon477a00860111::DiskInterfaceTest34 virtual void TearDown() {
35 temp_dir_.Cleanup();
36 }
37
Touch__anon477a00860111::DiskInterfaceTest38 bool Touch(const char* path) {
39 FILE *f = fopen(path, "w");
40 if (!f)
41 return false;
42 return fclose(f) == 0;
43 }
44
45 ScopedTempDir temp_dir_;
46 RealDiskInterface disk_;
47 };
48
TEST_F(DiskInterfaceTest,StatMissingFile)49 TEST_F(DiskInterfaceTest, StatMissingFile) {
50 string err;
51 EXPECT_EQ(0, disk_.Stat("nosuchfile", &err));
52 EXPECT_EQ("", err);
53
54 // On Windows, the errno for a file in a nonexistent directory
55 // is different.
56 EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err));
57 EXPECT_EQ("", err);
58
59 // On POSIX systems, the errno is different if a component of the
60 // path prefix is not a directory.
61 ASSERT_TRUE(Touch("notadir"));
62 EXPECT_EQ(0, disk_.Stat("notadir/nosuchfile", &err));
63 EXPECT_EQ("", err);
64 }
65
TEST_F(DiskInterfaceTest,StatBadPath)66 TEST_F(DiskInterfaceTest, StatBadPath) {
67 string err;
68 #ifdef _WIN32
69 string bad_path("cc:\\foo");
70 EXPECT_EQ(-1, disk_.Stat(bad_path, &err));
71 EXPECT_NE("", err);
72 #else
73 string too_long_name(512, 'x');
74 EXPECT_EQ(-1, disk_.Stat(too_long_name, &err));
75 EXPECT_NE("", err);
76 #endif
77 }
78
TEST_F(DiskInterfaceTest,StatExistingFile)79 TEST_F(DiskInterfaceTest, StatExistingFile) {
80 string err;
81 ASSERT_TRUE(Touch("file"));
82 EXPECT_GT(disk_.Stat("file", &err), 1);
83 EXPECT_EQ("", err);
84 }
85
TEST_F(DiskInterfaceTest,StatExistingDir)86 TEST_F(DiskInterfaceTest, StatExistingDir) {
87 string err;
88 ASSERT_TRUE(disk_.MakeDir("subdir"));
89 ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir"));
90 EXPECT_GT(disk_.Stat("..", &err), 1);
91 EXPECT_EQ("", err);
92 EXPECT_GT(disk_.Stat(".", &err), 1);
93 EXPECT_EQ("", err);
94 EXPECT_GT(disk_.Stat("subdir", &err), 1);
95 EXPECT_EQ("", err);
96 EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1);
97 EXPECT_EQ("", err);
98
99 EXPECT_EQ(disk_.Stat("subdir", &err),
100 disk_.Stat("subdir/.", &err));
101 EXPECT_EQ(disk_.Stat("subdir", &err),
102 disk_.Stat("subdir/subsubdir/..", &err));
103 EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err),
104 disk_.Stat("subdir/subsubdir/.", &err));
105 }
106
107 #ifdef _WIN32
TEST_F(DiskInterfaceTest,StatCache)108 TEST_F(DiskInterfaceTest, StatCache) {
109 string err;
110
111 ASSERT_TRUE(Touch("file1"));
112 ASSERT_TRUE(Touch("fiLE2"));
113 ASSERT_TRUE(disk_.MakeDir("subdir"));
114 ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir"));
115 ASSERT_TRUE(Touch("subdir\\subfile1"));
116 ASSERT_TRUE(Touch("subdir\\SUBFILE2"));
117 ASSERT_TRUE(Touch("subdir\\SUBFILE3"));
118
119 disk_.AllowStatCache(false);
120 TimeStamp parent_stat_uncached = disk_.Stat("..", &err);
121 disk_.AllowStatCache(true);
122
123 EXPECT_GT(disk_.Stat("FIle1", &err), 1);
124 EXPECT_EQ("", err);
125 EXPECT_GT(disk_.Stat("file1", &err), 1);
126 EXPECT_EQ("", err);
127
128 EXPECT_GT(disk_.Stat("subdir/subfile2", &err), 1);
129 EXPECT_EQ("", err);
130 EXPECT_GT(disk_.Stat("sUbdir\\suBFile1", &err), 1);
131 EXPECT_EQ("", err);
132
133 EXPECT_GT(disk_.Stat("..", &err), 1);
134 EXPECT_EQ("", err);
135 EXPECT_GT(disk_.Stat(".", &err), 1);
136 EXPECT_EQ("", err);
137 EXPECT_GT(disk_.Stat("subdir", &err), 1);
138 EXPECT_EQ("", err);
139 EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1);
140 EXPECT_EQ("", err);
141
142 #ifndef _MSC_VER // TODO: Investigate why. Also see https://github.com/ninja-build/ninja/pull/1423
143 EXPECT_EQ(disk_.Stat("subdir", &err),
144 disk_.Stat("subdir/.", &err));
145 EXPECT_EQ("", err);
146 EXPECT_EQ(disk_.Stat("subdir", &err),
147 disk_.Stat("subdir/subsubdir/..", &err));
148 #endif
149 EXPECT_EQ("", err);
150 EXPECT_EQ(disk_.Stat("..", &err), parent_stat_uncached);
151 EXPECT_EQ("", err);
152 EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err),
153 disk_.Stat("subdir/subsubdir/.", &err));
154 EXPECT_EQ("", err);
155
156 // Test error cases.
157 string bad_path("cc:\\foo");
158 EXPECT_EQ(-1, disk_.Stat(bad_path, &err));
159 EXPECT_NE("", err); err.clear();
160 EXPECT_EQ(-1, disk_.Stat(bad_path, &err));
161 EXPECT_NE("", err); err.clear();
162 EXPECT_EQ(0, disk_.Stat("nosuchfile", &err));
163 EXPECT_EQ("", err);
164 EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err));
165 EXPECT_EQ("", err);
166 }
167 #endif
168
TEST_F(DiskInterfaceTest,ReadFile)169 TEST_F(DiskInterfaceTest, ReadFile) {
170 string err;
171 std::string content;
172 ASSERT_EQ(DiskInterface::NotFound,
173 disk_.ReadFile("foobar", &content, &err));
174 EXPECT_EQ("", content);
175 EXPECT_NE("", err); // actual value is platform-specific
176 err.clear();
177
178 const char* kTestFile = "testfile";
179 FILE* f = fopen(kTestFile, "wb");
180 ASSERT_TRUE(f);
181 const char* kTestContent = "test content\nok";
182 fprintf(f, "%s", kTestContent);
183 ASSERT_EQ(0, fclose(f));
184
185 ASSERT_EQ(DiskInterface::Okay,
186 disk_.ReadFile(kTestFile, &content, &err));
187 EXPECT_EQ(kTestContent, content);
188 EXPECT_EQ("", err);
189 }
190
TEST_F(DiskInterfaceTest,MakeDirs)191 TEST_F(DiskInterfaceTest, MakeDirs) {
192 string path = "path/with/double//slash/";
193 EXPECT_TRUE(disk_.MakeDirs(path));
194 FILE* f = fopen((path + "a_file").c_str(), "w");
195 EXPECT_TRUE(f);
196 EXPECT_EQ(0, fclose(f));
197 #ifdef _WIN32
198 string path2 = "another\\with\\back\\\\slashes\\";
199 EXPECT_TRUE(disk_.MakeDirs(path2.c_str()));
200 FILE* f2 = fopen((path2 + "a_file").c_str(), "w");
201 EXPECT_TRUE(f2);
202 EXPECT_EQ(0, fclose(f2));
203 #endif
204 }
205
TEST_F(DiskInterfaceTest,RemoveFile)206 TEST_F(DiskInterfaceTest, RemoveFile) {
207 const char* kFileName = "file-to-remove";
208 ASSERT_TRUE(Touch(kFileName));
209 EXPECT_EQ(0, disk_.RemoveFile(kFileName));
210 EXPECT_EQ(1, disk_.RemoveFile(kFileName));
211 EXPECT_EQ(1, disk_.RemoveFile("does not exist"));
212 }
213
214 struct StatTest : public StateTestWithBuiltinRules,
215 public DiskInterface {
StatTest__anon477a00860111::StatTest216 StatTest() : scan_(&state_, NULL, NULL, this, NULL) {}
217
218 // DiskInterface implementation.
219 virtual TimeStamp Stat(const string& path, string* err) const;
WriteFile__anon477a00860111::StatTest220 virtual bool WriteFile(const string& path, const string& contents) {
221 assert(false);
222 return true;
223 }
MakeDir__anon477a00860111::StatTest224 virtual bool MakeDir(const string& path) {
225 assert(false);
226 return false;
227 }
ReadFile__anon477a00860111::StatTest228 virtual Status ReadFile(const string& path, string* contents, string* err) {
229 assert(false);
230 return NotFound;
231 }
RemoveFile__anon477a00860111::StatTest232 virtual int RemoveFile(const string& path) {
233 assert(false);
234 return 0;
235 }
236
237 DependencyScan scan_;
238 map<string, TimeStamp> mtimes_;
239 mutable vector<string> stats_;
240 };
241
Stat(const string & path,string * err) const242 TimeStamp StatTest::Stat(const string& path, string* err) const {
243 stats_.push_back(path);
244 map<string, TimeStamp>::const_iterator i = mtimes_.find(path);
245 if (i == mtimes_.end())
246 return 0; // File not found.
247 return i->second;
248 }
249
TEST_F(StatTest,Simple)250 TEST_F(StatTest, Simple) {
251 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
252 "build out: cat in\n"));
253
254 Node* out = GetNode("out");
255 string err;
256 EXPECT_TRUE(out->Stat(this, &err));
257 EXPECT_EQ("", err);
258 ASSERT_EQ(1u, stats_.size());
259 scan_.RecomputeDirty(out, NULL);
260 ASSERT_EQ(2u, stats_.size());
261 ASSERT_EQ("out", stats_[0]);
262 ASSERT_EQ("in", stats_[1]);
263 }
264
TEST_F(StatTest,TwoStep)265 TEST_F(StatTest, TwoStep) {
266 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
267 "build out: cat mid\n"
268 "build mid: cat in\n"));
269
270 Node* out = GetNode("out");
271 string err;
272 EXPECT_TRUE(out->Stat(this, &err));
273 EXPECT_EQ("", err);
274 ASSERT_EQ(1u, stats_.size());
275 scan_.RecomputeDirty(out, NULL);
276 ASSERT_EQ(3u, stats_.size());
277 ASSERT_EQ("out", stats_[0]);
278 ASSERT_TRUE(GetNode("out")->dirty());
279 ASSERT_EQ("mid", stats_[1]);
280 ASSERT_TRUE(GetNode("mid")->dirty());
281 ASSERT_EQ("in", stats_[2]);
282 }
283
TEST_F(StatTest,Tree)284 TEST_F(StatTest, Tree) {
285 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
286 "build out: cat mid1 mid2\n"
287 "build mid1: cat in11 in12\n"
288 "build mid2: cat in21 in22\n"));
289
290 Node* out = GetNode("out");
291 string err;
292 EXPECT_TRUE(out->Stat(this, &err));
293 EXPECT_EQ("", err);
294 ASSERT_EQ(1u, stats_.size());
295 scan_.RecomputeDirty(out, NULL);
296 ASSERT_EQ(1u + 6u, stats_.size());
297 ASSERT_EQ("mid1", stats_[1]);
298 ASSERT_TRUE(GetNode("mid1")->dirty());
299 ASSERT_EQ("in11", stats_[2]);
300 }
301
TEST_F(StatTest,Middle)302 TEST_F(StatTest, Middle) {
303 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
304 "build out: cat mid\n"
305 "build mid: cat in\n"));
306
307 mtimes_["in"] = 1;
308 mtimes_["mid"] = 0; // missing
309 mtimes_["out"] = 1;
310
311 Node* out = GetNode("out");
312 string err;
313 EXPECT_TRUE(out->Stat(this, &err));
314 EXPECT_EQ("", err);
315 ASSERT_EQ(1u, stats_.size());
316 scan_.RecomputeDirty(out, NULL);
317 ASSERT_FALSE(GetNode("in")->dirty());
318 ASSERT_TRUE(GetNode("mid")->dirty());
319 ASSERT_TRUE(GetNode("out")->dirty());
320 }
321
322 } // namespace
323