1 // Copyright 2011 Google Inc. All Rights Reserved.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include "disk_interface.h"
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");
34 virtual void TearDown() {
38 bool Touch(const char* path) {
39 FILE *f = fopen(path, "w");
42 return fclose(f) == 0;
45 ScopedTempDir temp_dir_;
46 RealDiskInterface disk_;
49 TEST_F(DiskInterfaceTest, StatMissingFile) {
51 EXPECT_EQ(0, disk_.Stat("nosuchfile", &err));
54 // On Windows, the errno for a file in a nonexistent directory
56 EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err));
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));
66 TEST_F(DiskInterfaceTest, StatBadPath) {
69 string bad_path("cc:\\foo");
70 EXPECT_EQ(-1, disk_.Stat(bad_path, &err));
73 string too_long_name(512, 'x');
74 EXPECT_EQ(-1, disk_.Stat(too_long_name, &err));
79 TEST_F(DiskInterfaceTest, StatExistingFile) {
81 ASSERT_TRUE(Touch("file"));
82 EXPECT_GT(disk_.Stat("file", &err), 1);
86 TEST_F(DiskInterfaceTest, StatExistingDir) {
88 ASSERT_TRUE(disk_.MakeDir("subdir"));
89 ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir"));
90 EXPECT_GT(disk_.Stat(".", &err), 1);
92 EXPECT_GT(disk_.Stat("subdir", &err), 1);
94 EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1);
97 EXPECT_EQ(disk_.Stat("subdir", &err),
98 disk_.Stat("subdir/.", &err));
99 EXPECT_EQ(disk_.Stat("subdir", &err),
100 disk_.Stat("subdir/subsubdir/..", &err));
101 EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err),
102 disk_.Stat("subdir/subsubdir/.", &err));
106 TEST_F(DiskInterfaceTest, StatCache) {
108 disk_.AllowStatCache(true);
110 ASSERT_TRUE(Touch("file1"));
111 ASSERT_TRUE(Touch("fiLE2"));
112 ASSERT_TRUE(disk_.MakeDir("subdir"));
113 ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir"));
114 ASSERT_TRUE(Touch("subdir\\subfile1"));
115 ASSERT_TRUE(Touch("subdir\\SUBFILE2"));
116 ASSERT_TRUE(Touch("subdir\\SUBFILE3"));
118 EXPECT_GT(disk_.Stat("FIle1", &err), 1);
120 EXPECT_GT(disk_.Stat("file1", &err), 1);
123 EXPECT_GT(disk_.Stat("subdir/subfile2", &err), 1);
125 EXPECT_GT(disk_.Stat("sUbdir\\suBFile1", &err), 1);
128 EXPECT_GT(disk_.Stat(".", &err), 1);
130 EXPECT_GT(disk_.Stat("subdir", &err), 1);
132 EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1);
135 EXPECT_EQ(disk_.Stat("subdir", &err),
136 disk_.Stat("subdir/.", &err));
138 EXPECT_EQ(disk_.Stat("subdir", &err),
139 disk_.Stat("subdir/subsubdir/..", &err));
141 EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err),
142 disk_.Stat("subdir/subsubdir/.", &err));
146 string bad_path("cc:\\foo");
147 EXPECT_EQ(-1, disk_.Stat(bad_path, &err));
148 EXPECT_NE("", err); err.clear();
149 EXPECT_EQ(-1, disk_.Stat(bad_path, &err));
150 EXPECT_NE("", err); err.clear();
151 EXPECT_EQ(0, disk_.Stat("nosuchfile", &err));
153 EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err));
158 TEST_F(DiskInterfaceTest, ReadFile) {
161 ASSERT_EQ(DiskInterface::NotFound,
162 disk_.ReadFile("foobar", &content, &err));
163 EXPECT_EQ("", content);
164 EXPECT_NE("", err); // actual value is platform-specific
167 const char* kTestFile = "testfile";
168 FILE* f = fopen(kTestFile, "wb");
170 const char* kTestContent = "test content\nok";
171 fprintf(f, "%s", kTestContent);
172 ASSERT_EQ(0, fclose(f));
174 ASSERT_EQ(DiskInterface::Okay,
175 disk_.ReadFile(kTestFile, &content, &err));
176 EXPECT_EQ(kTestContent, content);
180 TEST_F(DiskInterfaceTest, MakeDirs) {
181 string path = "path/with/double//slash/";
182 EXPECT_TRUE(disk_.MakeDirs(path.c_str()));
183 FILE* f = fopen((path + "a_file").c_str(), "w");
185 EXPECT_EQ(0, fclose(f));
187 string path2 = "another\\with\\back\\\\slashes\\";
188 EXPECT_TRUE(disk_.MakeDirs(path2.c_str()));
189 FILE* f2 = fopen((path2 + "a_file").c_str(), "w");
191 EXPECT_EQ(0, fclose(f2));
195 TEST_F(DiskInterfaceTest, RemoveFile) {
196 const char* kFileName = "file-to-remove";
197 ASSERT_TRUE(Touch(kFileName));
198 EXPECT_EQ(0, disk_.RemoveFile(kFileName));
199 EXPECT_EQ(1, disk_.RemoveFile(kFileName));
200 EXPECT_EQ(1, disk_.RemoveFile("does not exist"));
203 struct StatTest : public StateTestWithBuiltinRules,
204 public DiskInterface {
205 StatTest() : scan_(&state_, NULL, NULL, this) {}
207 // DiskInterface implementation.
208 virtual TimeStamp Stat(const string& path, string* err) const;
209 virtual bool WriteFile(const string& path, const string& contents) {
213 virtual bool MakeDir(const string& path) {
217 virtual Status ReadFile(const string& path, string* contents, string* err) {
221 virtual int RemoveFile(const string& path) {
226 DependencyScan scan_;
227 map<string, TimeStamp> mtimes_;
228 mutable vector<string> stats_;
231 TimeStamp StatTest::Stat(const string& path, string* err) const {
232 stats_.push_back(path);
233 map<string, TimeStamp>::const_iterator i = mtimes_.find(path);
234 if (i == mtimes_.end())
235 return 0; // File not found.
239 TEST_F(StatTest, Simple) {
240 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
241 "build out: cat in\n"));
243 Node* out = GetNode("out");
245 EXPECT_TRUE(out->Stat(this, &err));
247 ASSERT_EQ(1u, stats_.size());
248 scan_.RecomputeDirty(out->in_edge(), NULL);
249 ASSERT_EQ(2u, stats_.size());
250 ASSERT_EQ("out", stats_[0]);
251 ASSERT_EQ("in", stats_[1]);
254 TEST_F(StatTest, TwoStep) {
255 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
256 "build out: cat mid\n"
257 "build mid: cat in\n"));
259 Node* out = GetNode("out");
261 EXPECT_TRUE(out->Stat(this, &err));
263 ASSERT_EQ(1u, stats_.size());
264 scan_.RecomputeDirty(out->in_edge(), NULL);
265 ASSERT_EQ(3u, stats_.size());
266 ASSERT_EQ("out", stats_[0]);
267 ASSERT_TRUE(GetNode("out")->dirty());
268 ASSERT_EQ("mid", stats_[1]);
269 ASSERT_TRUE(GetNode("mid")->dirty());
270 ASSERT_EQ("in", stats_[2]);
273 TEST_F(StatTest, Tree) {
274 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
275 "build out: cat mid1 mid2\n"
276 "build mid1: cat in11 in12\n"
277 "build mid2: cat in21 in22\n"));
279 Node* out = GetNode("out");
281 EXPECT_TRUE(out->Stat(this, &err));
283 ASSERT_EQ(1u, stats_.size());
284 scan_.RecomputeDirty(out->in_edge(), NULL);
285 ASSERT_EQ(1u + 6u, stats_.size());
286 ASSERT_EQ("mid1", stats_[1]);
287 ASSERT_TRUE(GetNode("mid1")->dirty());
288 ASSERT_EQ("in11", stats_[2]);
291 TEST_F(StatTest, Middle) {
292 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
293 "build out: cat mid\n"
294 "build mid: cat in\n"));
297 mtimes_["mid"] = 0; // missing
300 Node* out = GetNode("out");
302 EXPECT_TRUE(out->Stat(this, &err));
304 ASSERT_EQ(1u, stats_.size());
305 scan_.RecomputeDirty(out->in_edge(), NULL);
306 ASSERT_FALSE(GetNode("in")->dirty());
307 ASSERT_TRUE(GetNode("mid")->dirty());
308 ASSERT_TRUE(GetNode("out")->dirty());