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 <gtest/gtest.h>
16 
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  EXPECT_EQ(0, disk_.Stat("nosuchfile"));
51 
52  // On Windows, the errno for a file in a nonexistent directory
53  // is different.
54  EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile"));
55 
56  // On POSIX systems, the errno is different if a component of the
57  // path prefix is not a directory.
58  ASSERT_TRUE(Touch("notadir"));
59  EXPECT_EQ(0, disk_.Stat("notadir/nosuchfile"));
60 }
61 
62 TEST_F(DiskInterfaceTest, StatBadPath) {
63  disk_.quiet_ = true;
64 #ifdef _WIN32
65  string bad_path("cc:\\foo");
66  EXPECT_EQ(-1, disk_.Stat(bad_path));
67 #else
68  string too_long_name(512, 'x');
69  EXPECT_EQ(-1, disk_.Stat(too_long_name));
70 #endif
71  disk_.quiet_ = false;
72 }
73 
74 TEST_F(DiskInterfaceTest, StatExistingFile) {
75  ASSERT_TRUE(Touch("file"));
76  EXPECT_GT(disk_.Stat("file"), 1);
77 }
78 
79 TEST_F(DiskInterfaceTest, ReadFile) {
80  string err;
81  EXPECT_EQ("", disk_.ReadFile("foobar", &err));
82  EXPECT_EQ("", err);
83 
84  const char* kTestFile = "testfile";
85  FILE* f = fopen(kTestFile, "wb");
86  ASSERT_TRUE(f);
87  const char* kTestContent = "test content\nok";
88  fprintf(f, "%s", kTestContent);
89  ASSERT_EQ(0, fclose(f));
90 
91  EXPECT_EQ(kTestContent, disk_.ReadFile(kTestFile, &err));
92  EXPECT_EQ("", err);
93 }
94 
95 TEST_F(DiskInterfaceTest, MakeDirs) {
96  EXPECT_TRUE(disk_.MakeDirs("path/with/double//slash/"));
97 }
98 
99 TEST_F(DiskInterfaceTest, RemoveFile) {
100  const char* kFileName = "file-to-remove";
101  ASSERT_TRUE(Touch(kFileName));
102  EXPECT_EQ(0, disk_.RemoveFile(kFileName));
103  EXPECT_EQ(1, disk_.RemoveFile(kFileName));
104  EXPECT_EQ(1, disk_.RemoveFile("does not exist"));
105 }
106 
107 struct StatTest : public StateTestWithBuiltinRules,
108  public DiskInterface {
109  StatTest() : scan_(&state_, NULL, NULL, this) {}
110 
111  // DiskInterface implementation.
112  virtual TimeStamp Stat(const string& path);
113  virtual bool WriteFile(const string& path, const string& contents) {
114  assert(false);
115  return true;
116  }
117  virtual bool MakeDir(const string& path) {
118  assert(false);
119  return false;
120  }
121  virtual string ReadFile(const string& path, string* err) {
122  assert(false);
123  return "";
124  }
125  virtual int RemoveFile(const string& path) {
126  assert(false);
127  return 0;
128  }
129 
130  DependencyScan scan_;
131  map<string, TimeStamp> mtimes_;
132  vector<string> stats_;
133 };
134 
135 TimeStamp StatTest::Stat(const string& path) {
136  stats_.push_back(path);
137  map<string, TimeStamp>::iterator i = mtimes_.find(path);
138  if (i == mtimes_.end())
139  return 0; // File not found.
140  return i->second;
141 }
142 
143 TEST_F(StatTest, Simple) {
144  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
145 "build out: cat in\n"));
146 
147  Node* out = GetNode("out");
148  out->Stat(this);
149  ASSERT_EQ(1u, stats_.size());
150  scan_.RecomputeDirty(out->in_edge(), NULL);
151  ASSERT_EQ(2u, stats_.size());
152  ASSERT_EQ("out", stats_[0]);
153  ASSERT_EQ("in", stats_[1]);
154 }
155 
156 TEST_F(StatTest, TwoStep) {
157  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
158 "build out: cat mid\n"
159 "build mid: cat in\n"));
160 
161  Node* out = GetNode("out");
162  out->Stat(this);
163  ASSERT_EQ(1u, stats_.size());
164  scan_.RecomputeDirty(out->in_edge(), NULL);
165  ASSERT_EQ(3u, stats_.size());
166  ASSERT_EQ("out", stats_[0]);
167  ASSERT_TRUE(GetNode("out")->dirty());
168  ASSERT_EQ("mid", stats_[1]);
169  ASSERT_TRUE(GetNode("mid")->dirty());
170  ASSERT_EQ("in", stats_[2]);
171 }
172 
173 TEST_F(StatTest, Tree) {
174  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
175 "build out: cat mid1 mid2\n"
176 "build mid1: cat in11 in12\n"
177 "build mid2: cat in21 in22\n"));
178 
179  Node* out = GetNode("out");
180  out->Stat(this);
181  ASSERT_EQ(1u, stats_.size());
182  scan_.RecomputeDirty(out->in_edge(), NULL);
183  ASSERT_EQ(1u + 6u, stats_.size());
184  ASSERT_EQ("mid1", stats_[1]);
185  ASSERT_TRUE(GetNode("mid1")->dirty());
186  ASSERT_EQ("in11", stats_[2]);
187 }
188 
189 TEST_F(StatTest, Middle) {
190  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
191 "build out: cat mid\n"
192 "build mid: cat in\n"));
193 
194  mtimes_["in"] = 1;
195  mtimes_["mid"] = 0; // missing
196  mtimes_["out"] = 1;
197 
198  Node* out = GetNode("out");
199  out->Stat(this);
200  ASSERT_EQ(1u, stats_.size());
201  scan_.RecomputeDirty(out->in_edge(), NULL);
202  ASSERT_FALSE(GetNode("in")->dirty());
203  ASSERT_TRUE(GetNode("mid")->dirty());
204  ASSERT_TRUE(GetNode("out")->dirty());
205 }
206 
207 } // namespace
virtual string ReadFile(const string &path, string *err)=0
Read a file to a string. Fill in |err| on error.
Information about a node in the dependency graph: the file, whether it's dirty, mtime, etc.
Definition: graph.h:35
Interface for accessing the disk.
Edge * in_edge() const
Definition: graph.h:80
void AssertParse(State *state, const char *input)
Definition: test.cc:90
int TimeStamp
Definition: timestamp.h:22
A base test fixture that includes a State object with a builtin "cat" rule.
Definition: test.h:30
bool Stat(DiskInterface *disk_interface)
Return true if the file exists (mtime_ got a value).
Definition: graph.cc:30
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.
TEST_F(PlanTest, Basic)
Definition: build_test.cc:49
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:178
DependencyScan manages the process of scanning the files in a graph and updating the dirty/outputs_re...
Definition: graph.h:230
virtual int RemoveFile(const string &path)=0
Remove the file named path.
virtual bool MakeDir(const string &path)=0
Create a directory, returning false on failure.
virtual TimeStamp Stat(const string &path)=0
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.