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 #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 {
29  virtual void SetUp() {
30  // These tests do real disk accesses, so create a temp dir.
31  temp_dir_.CreateAndEnter("Ninja-DiskInterfaceTest");
32  }
33 
34  virtual void TearDown() {
35  temp_dir_.Cleanup();
36  }
37 
38  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 
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 
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 
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 
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
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 
169 TEST_F(DiskInterfaceTest, ReadFile) {
170  string err;
171  std::string content;
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 
186  disk_.ReadFile(kTestFile, &content, &err));
187  EXPECT_EQ(kTestContent, content);
188  EXPECT_EQ("", err);
189 }
190 
191 TEST_F(DiskInterfaceTest, MakeDirs) {
192  string path = "path/with/double//slash/";
193  EXPECT_TRUE(disk_.MakeDirs(path.c_str()));
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 
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 {
216  StatTest() : scan_(&state_, NULL, NULL, this, NULL) {}
217 
218  // DiskInterface implementation.
219  virtual TimeStamp Stat(const string& path, string* err) const;
220  virtual bool WriteFile(const string& path, const string& contents) {
221  assert(false);
222  return true;
223  }
224  virtual bool MakeDir(const string& path) {
225  assert(false);
226  return false;
227  }
228  virtual Status ReadFile(const string& path, string* contents, string* err) {
229  assert(false);
230  return NotFound;
231  }
232  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 
242 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 
250 TEST_F(StatTest, Simple) {
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 
265 TEST_F(StatTest, TwoStep) {
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 
284 TEST_F(StatTest, Tree) {
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 
302 TEST_F(StatTest, Middle) {
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
#define TEST_F(x, y)
Definition: test.h:61
void AssertParse(State *state, const char *input, ManifestParserOptions opts)
Definition: test.cc:98
#define EXPECT_TRUE(a)
Definition: test.h:76
virtual void SetUp()
Definition: test.h:36
Information about a node in the dependency graph: the file, whether it&#39;s dirty, mtime, etc.
Definition: graph.h:37
Interface for accessing the disk.
virtual void TearDown()
Definition: test.h:37
A base test fixture that includes a State object with a builtin "cat" rule.
Definition: test.h:113
virtual bool WriteFile(const string &path, const string &contents)=0
Create a file, with the specified name and contents Returns true on success, false on failure...
Implementation of DiskInterface that actually hits the disk.
#define EXPECT_EQ(a, b)
Definition: test.h:64
#define ASSERT_FALSE(a)
Definition: test.h:95
virtual TimeStamp Stat(const string &path, string *err) const =0
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.
int64_t TimeStamp
Definition: timestamp.h:31
int ReadFile(const string &path, string *contents, string *err)
Read a file to a string (in text mode: with CRLF conversion on Windows).
Definition: util.cc:316
#define ASSERT_EQ(a, b)
Definition: test.h:81
virtual Status ReadFile(const string &path, string *contents, string *err)=0
Read and store in given string.
DependencyScan manages the process of scanning the files in a graph and updating the dirty/outputs_re...
Definition: graph.h:254
#define EXPECT_GT(a, b)
Definition: test.h:68
#define ASSERT_NO_FATAL_FAILURE(a)
Definition: test.h:97
bool Stat(DiskInterface *disk_interface, string *err)
Return false on error.
Definition: graph.cc:30
virtual int RemoveFile(const string &path)=0
Remove the file named path.
#define ASSERT_TRUE(a)
Definition: test.h:93
virtual bool MakeDir(const string &path)=0
Create a directory, returning false on failure.
#define EXPECT_NE(a, b)
Definition: test.h:66