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