Ninja
disk_interface_test.cc
Go to the documentation of this file.
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#include <direct.h>
21#endif
22
23#include "disk_interface.h"
24#include "graph.h"
25#include "test.h"
26
27using namespace std;
28
29namespace {
30
31struct DiskInterfaceTest : public testing::Test {
32 virtual void SetUp() {
33 // These tests do real disk accesses, so create a temp dir.
34 temp_dir_.CreateAndEnter("Ninja-DiskInterfaceTest");
35 }
36
37 virtual void TearDown() {
38 temp_dir_.Cleanup();
39 }
40
41 bool Touch(const char* path) {
42 FILE *f = fopen(path, "w");
43 if (!f)
44 return false;
45 return fclose(f) == 0;
46 }
47
48 ScopedTempDir temp_dir_;
49 RealDiskInterface disk_;
50};
51
52TEST_F(DiskInterfaceTest, StatMissingFile) {
53 string err;
54 EXPECT_EQ(0, disk_.Stat("nosuchfile", &err));
55 EXPECT_EQ("", err);
56
57 // On Windows, the errno for a file in a nonexistent directory
58 // is different.
59 EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err));
60 EXPECT_EQ("", err);
61
62 // On POSIX systems, the errno is different if a component of the
63 // path prefix is not a directory.
64 ASSERT_TRUE(Touch("notadir"));
65 EXPECT_EQ(0, disk_.Stat("notadir/nosuchfile", &err));
66 EXPECT_EQ("", err);
67}
68
69TEST_F(DiskInterfaceTest, StatMissingFileWithCache) {
70 disk_.AllowStatCache(true);
71 string err;
72
73 // On Windows, the errno for FindFirstFileExA, which is used when the stat
74 // cache is enabled, is different when the directory name is not a directory.
75 ASSERT_TRUE(Touch("notadir"));
76 EXPECT_EQ(0, disk_.Stat("notadir/nosuchfile", &err));
77 EXPECT_EQ("", err);
78}
79
80TEST_F(DiskInterfaceTest, StatBadPath) {
81 string err;
82#ifdef _WIN32
83 string bad_path("cc:\\foo");
84 EXPECT_EQ(-1, disk_.Stat(bad_path, &err));
85 EXPECT_NE("", err);
86#else
87 string too_long_name(512, 'x');
88 EXPECT_EQ(-1, disk_.Stat(too_long_name, &err));
89 EXPECT_NE("", err);
90#endif
91}
92
93TEST_F(DiskInterfaceTest, StatExistingFile) {
94 string err;
95 ASSERT_TRUE(Touch("file"));
96 EXPECT_GT(disk_.Stat("file", &err), 1);
97 EXPECT_EQ("", err);
98}
99
100#ifdef _WIN32
101TEST_F(DiskInterfaceTest, StatExistingFileWithLongPath) {
102 string err;
103 char currentdir[32767];
104 _getcwd(currentdir, sizeof(currentdir));
105 const string filename = string(currentdir) +
106"\\filename_with_256_characters_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\
107xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\
108xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\
109xxxxxxxxxxxxxxxxxxxxx";
110 const string prefixed = "\\\\?\\" + filename;
111 ASSERT_TRUE(Touch(prefixed.c_str()));
112 EXPECT_GT(disk_.Stat(disk_.AreLongPathsEnabled() ?
113 filename : prefixed, &err), 1);
114 EXPECT_EQ("", err);
115}
116#endif
117
118TEST_F(DiskInterfaceTest, StatExistingDir) {
119 string err;
120 ASSERT_TRUE(disk_.MakeDir("subdir"));
121 ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir"));
122 EXPECT_GT(disk_.Stat("..", &err), 1);
123 EXPECT_EQ("", err);
124 EXPECT_GT(disk_.Stat(".", &err), 1);
125 EXPECT_EQ("", err);
126 EXPECT_GT(disk_.Stat("subdir", &err), 1);
127 EXPECT_EQ("", err);
128 EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1);
129 EXPECT_EQ("", err);
130
131 EXPECT_EQ(disk_.Stat("subdir", &err),
132 disk_.Stat("subdir/.", &err));
133 EXPECT_EQ(disk_.Stat("subdir", &err),
134 disk_.Stat("subdir/subsubdir/..", &err));
135 EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err),
136 disk_.Stat("subdir/subsubdir/.", &err));
137}
138
139#ifdef _WIN32
140TEST_F(DiskInterfaceTest, StatCache) {
141 string err;
142
143 ASSERT_TRUE(Touch("file1"));
144 ASSERT_TRUE(Touch("fiLE2"));
145 ASSERT_TRUE(disk_.MakeDir("subdir"));
146 ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir"));
147 ASSERT_TRUE(Touch("subdir\\subfile1"));
148 ASSERT_TRUE(Touch("subdir\\SUBFILE2"));
149 ASSERT_TRUE(Touch("subdir\\SUBFILE3"));
150
151 disk_.AllowStatCache(false);
152 TimeStamp parent_stat_uncached = disk_.Stat("..", &err);
153 disk_.AllowStatCache(true);
154
155 EXPECT_GT(disk_.Stat("FIle1", &err), 1);
156 EXPECT_EQ("", err);
157 EXPECT_GT(disk_.Stat("file1", &err), 1);
158 EXPECT_EQ("", err);
159
160 EXPECT_GT(disk_.Stat("subdir/subfile2", &err), 1);
161 EXPECT_EQ("", err);
162 EXPECT_GT(disk_.Stat("sUbdir\\suBFile1", &err), 1);
163 EXPECT_EQ("", err);
164
165 EXPECT_GT(disk_.Stat("..", &err), 1);
166 EXPECT_EQ("", err);
167 EXPECT_GT(disk_.Stat(".", &err), 1);
168 EXPECT_EQ("", err);
169 EXPECT_GT(disk_.Stat("subdir", &err), 1);
170 EXPECT_EQ("", err);
171 EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1);
172 EXPECT_EQ("", err);
173
174#ifndef _MSC_VER // TODO: Investigate why. Also see https://github.com/ninja-build/ninja/pull/1423
175 EXPECT_EQ(disk_.Stat("subdir", &err),
176 disk_.Stat("subdir/.", &err));
177 EXPECT_EQ("", err);
178 EXPECT_EQ(disk_.Stat("subdir", &err),
179 disk_.Stat("subdir/subsubdir/..", &err));
180#endif
181 EXPECT_EQ("", err);
182 EXPECT_EQ(disk_.Stat("..", &err), parent_stat_uncached);
183 EXPECT_EQ("", err);
184 EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err),
185 disk_.Stat("subdir/subsubdir/.", &err));
186 EXPECT_EQ("", err);
187
188 // Test error cases.
189 string bad_path("cc:\\foo");
190 EXPECT_EQ(-1, disk_.Stat(bad_path, &err));
191 EXPECT_NE("", err); err.clear();
192 EXPECT_EQ(-1, disk_.Stat(bad_path, &err));
193 EXPECT_NE("", err); err.clear();
194 EXPECT_EQ(0, disk_.Stat("nosuchfile", &err));
195 EXPECT_EQ("", err);
196 EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err));
197 EXPECT_EQ("", err);
198}
199#endif
200
201TEST_F(DiskInterfaceTest, ReadFile) {
202 string err;
203 std::string content;
204 ASSERT_EQ(DiskInterface::NotFound,
205 disk_.ReadFile("foobar", &content, &err));
206 EXPECT_EQ("", content);
207 EXPECT_NE("", err); // actual value is platform-specific
208 err.clear();
209
210 const char* kTestFile = "testfile";
211 FILE* f = fopen(kTestFile, "wb");
212 ASSERT_TRUE(f);
213 const char* kTestContent = "test content\nok";
214 fprintf(f, "%s", kTestContent);
215 ASSERT_EQ(0, fclose(f));
216
217 ASSERT_EQ(DiskInterface::Okay,
218 disk_.ReadFile(kTestFile, &content, &err));
219 EXPECT_EQ(kTestContent, content);
220 EXPECT_EQ("", err);
221}
222
223TEST_F(DiskInterfaceTest, MakeDirs) {
224 string path = "path/with/double//slash/";
225 EXPECT_TRUE(disk_.MakeDirs(path));
226 FILE* f = fopen((path + "a_file").c_str(), "w");
227 EXPECT_TRUE(f);
228 EXPECT_EQ(0, fclose(f));
229#ifdef _WIN32
230 string path2 = "another\\with\\back\\\\slashes\\";
231 EXPECT_TRUE(disk_.MakeDirs(path2));
232 FILE* f2 = fopen((path2 + "a_file").c_str(), "w");
233 EXPECT_TRUE(f2);
234 EXPECT_EQ(0, fclose(f2));
235#endif
236}
237
238TEST_F(DiskInterfaceTest, RemoveFile) {
239 const char* kFileName = "file-to-remove";
240 ASSERT_TRUE(Touch(kFileName));
241 EXPECT_EQ(0, disk_.RemoveFile(kFileName));
242 EXPECT_EQ(1, disk_.RemoveFile(kFileName));
243 EXPECT_EQ(1, disk_.RemoveFile("does not exist"));
244#ifdef _WIN32
245 ASSERT_TRUE(Touch(kFileName));
246 EXPECT_EQ(0, system((std::string("attrib +R ") + kFileName).c_str()));
247 EXPECT_EQ(0, disk_.RemoveFile(kFileName));
248 EXPECT_EQ(1, disk_.RemoveFile(kFileName));
249#endif
250}
251
252TEST_F(DiskInterfaceTest, RemoveDirectory) {
253 const char* kDirectoryName = "directory-to-remove";
254 EXPECT_TRUE(disk_.MakeDir(kDirectoryName));
255 EXPECT_EQ(0, disk_.RemoveFile(kDirectoryName));
256 EXPECT_EQ(1, disk_.RemoveFile(kDirectoryName));
257 EXPECT_EQ(1, disk_.RemoveFile("does not exist"));
258}
259
260struct StatTest : public StateTestWithBuiltinRules,
261 public DiskInterface {
262 StatTest() : scan_(&state_, NULL, NULL, this, NULL, NULL) {}
263
264 // DiskInterface implementation.
265 TimeStamp Stat(const string& path, string* err) const override;
266 bool WriteFile(const string& path, const string& contents,
267 bool /*crlf_on_windows*/) override {
268 assert(false);
269 return true;
270 }
271 bool MakeDir(const string& path) override {
272 assert(false);
273 return false;
274 }
275 Status ReadFile(const string& path, string* contents, string* err) override {
276 assert(false);
277 return NotFound;
278 }
279 int RemoveFile(const string& path) override {
280 assert(false);
281 return 0;
282 }
283
284 DependencyScan scan_;
285 map<string, TimeStamp> mtimes_;
286 mutable vector<string> stats_;
287};
288
289TimeStamp StatTest::Stat(const string& path, string* err) const {
290 stats_.push_back(path);
291 map<string, TimeStamp>::const_iterator i = mtimes_.find(path);
292 if (i == mtimes_.end())
293 return 0; // File not found.
294 return i->second;
295}
296
297TEST_F(StatTest, Simple) {
298 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
299"build out: cat in\n"));
300
301 Node* out = GetNode("out");
302 string err;
303 EXPECT_TRUE(out->Stat(this, &err));
304 EXPECT_EQ("", err);
305 ASSERT_EQ(1u, stats_.size());
306 scan_.RecomputeDirty(out, NULL, NULL);
307 ASSERT_EQ(2u, stats_.size());
308 ASSERT_EQ("out", stats_[0]);
309 ASSERT_EQ("in", stats_[1]);
310}
311
312TEST_F(StatTest, TwoStep) {
313 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
314"build out: cat mid\n"
315"build mid: cat in\n"));
316
317 Node* out = GetNode("out");
318 string err;
319 EXPECT_TRUE(out->Stat(this, &err));
320 EXPECT_EQ("", err);
321 ASSERT_EQ(1u, stats_.size());
322 scan_.RecomputeDirty(out, NULL, NULL);
323 ASSERT_EQ(3u, stats_.size());
324 ASSERT_EQ("out", stats_[0]);
325 ASSERT_TRUE(GetNode("out")->dirty());
326 ASSERT_EQ("mid", stats_[1]);
327 ASSERT_TRUE(GetNode("mid")->dirty());
328 ASSERT_EQ("in", stats_[2]);
329}
330
331TEST_F(StatTest, Tree) {
332 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
333"build out: cat mid1 mid2\n"
334"build mid1: cat in11 in12\n"
335"build mid2: cat in21 in22\n"));
336
337 Node* out = GetNode("out");
338 string err;
339 EXPECT_TRUE(out->Stat(this, &err));
340 EXPECT_EQ("", err);
341 ASSERT_EQ(1u, stats_.size());
342 scan_.RecomputeDirty(out, NULL, NULL);
343 ASSERT_EQ(1u + 6u, stats_.size());
344 ASSERT_EQ("mid1", stats_[1]);
345 ASSERT_TRUE(GetNode("mid1")->dirty());
346 ASSERT_EQ("in11", stats_[2]);
347}
348
349TEST_F(StatTest, Middle) {
350 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
351"build out: cat mid\n"
352"build mid: cat in\n"));
353
354 mtimes_["in"] = 1;
355 mtimes_["mid"] = 0; // missing
356 mtimes_["out"] = 1;
357
358 Node* out = GetNode("out");
359 string err;
360 EXPECT_TRUE(out->Stat(this, &err));
361 EXPECT_EQ("", err);
362 ASSERT_EQ(1u, stats_.size());
363 scan_.RecomputeDirty(out, NULL, NULL);
364 ASSERT_FALSE(GetNode("in")->dirty());
365 ASSERT_TRUE(GetNode("mid")->dirty());
366 ASSERT_TRUE(GetNode("out")->dirty());
367}
368
369} // namespace
TEST_F(PlanTest, Basic)
Definition build_test.cc:67
Definition hash_map.h:26
Interface for accessing the disk.
bool Stat(DiskInterface *disk_interface, std::string *err)
Return false on error.
Definition graph.cc:34
A base test fixture that includes a State object with a builtin "cat" rule.
Definition test.h:30
void AssertParse(State *state, const char *input, ManifestParserOptions opts)
Definition test.cc:100
int64_t TimeStamp
Definition timestamp.h:31
int ReadFile(const string &path, string *contents, string *err)
Definition util.cc:415