• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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