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 
27 using namespace std;
28 
29 namespace {
30 
31 struct 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 
52 TEST_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 
69 TEST_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 
80 TEST_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 
93 TEST_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
101 TEST_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\
107 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\
108 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\
109 xxxxxxxxxxxxxxxxxxxxx";
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 
118 TEST_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
140 TEST_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 
201 TEST_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 
223 TEST_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 
238 TEST_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 
252 TEST_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 
260 struct 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 
289 TimeStamp 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 
297 TEST_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 
312 TEST_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 
331 TEST_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 
349 TEST_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
DependencyScan manages the process of scanning the files in a graph and updating the dirty/outputs_re...
Definition: graph.h:332
Interface for accessing the disk.
Information about a node in the dependency graph: the file, whether it's dirty, mtime,...
Definition: graph.h:42
bool Stat(DiskInterface *disk_interface, std::string *err)
Return false on error.
Definition: graph.cc:34
Implementation of DiskInterface that actually hits the disk.
A base test fixture that includes a State object with a builtin "cat" rule.
Definition: test.h:30
Abstract interface to object that tracks the status of a build: completion fraction,...
Definition: status.h:27
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