Ninja
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 #ifdef _WIN32
16 #include <direct.h> // Has to be before util.h is included.
17 #endif
18 
19 #include "test.h"
20 
21 #include <algorithm>
22 
23 #include <errno.h>
24 #include <stdlib.h>
25 #ifdef _WIN32
26 #include <windows.h>
27 #else
28 #include <unistd.h>
29 #endif
30 
31 #include "build_log.h"
32 #include "graph.h"
33 #include "manifest_parser.h"
34 #include "util.h"
35 
36 namespace {
37 
38 #ifdef _WIN32
39 #ifndef _mktemp_s
40 /// mingw has no mktemp. Implement one with the same type as the one
41 /// found in the Windows API.
42 int _mktemp_s(char* templ) {
43  char* ofs = strchr(templ, 'X');
44  sprintf(ofs, "%d", rand() % 1000000);
45  return 0;
46 }
47 #endif
48 
49 /// Windows has no mkdtemp. Implement it in terms of _mktemp_s.
50 char* mkdtemp(char* name_template) {
51  int err = _mktemp_s(name_template);
52  if (err < 0) {
53  perror("_mktemp_s");
54  return NULL;
55  }
56 
57  err = _mkdir(name_template);
58  if (err < 0) {
59  perror("mkdir");
60  return NULL;
61  }
62 
63  return name_template;
64 }
65 #endif // _WIN32
66 
67 string GetSystemTempDir() {
68 #ifdef _WIN32
69  char buf[1024];
70  if (!GetTempPath(sizeof(buf), buf))
71  return "";
72  return buf;
73 #else
74  const char* tempdir = getenv("TMPDIR");
75  if (tempdir)
76  return tempdir;
77  return "/tmp";
78 #endif
79 }
80 
81 } // anonymous namespace
82 
85 }
86 
88  AssertParse(state,
89 "rule cat\n"
90 " command = cat $in > $out\n");
91 }
92 
94  EXPECT_FALSE(strpbrk(path.c_str(), "/\\"));
95  return state_.GetNode(path, 0);
96 }
97 
98 void AssertParse(State* state, const char* input,
99  ManifestParserOptions opts) {
100  ManifestParser parser(state, NULL, opts);
101  string err;
102  EXPECT_TRUE(parser.ParseTest(input, &err));
103  ASSERT_EQ("", err);
104  VerifyGraph(*state);
105 }
106 
107 void AssertHash(const char* expected, uint64_t actual) {
108  ASSERT_EQ(BuildLog::LogEntry::HashCommand(expected), actual);
109 }
110 
111 void VerifyGraph(const State& state) {
112  for (vector<Edge*>::const_iterator e = state.edges_.begin();
113  e != state.edges_.end(); ++e) {
114  // All edges need at least one output.
115  EXPECT_FALSE((*e)->outputs_.empty());
116  // Check that the edge's inputs have the edge as out-edge.
117  for (vector<Node*>::const_iterator in_node = (*e)->inputs_.begin();
118  in_node != (*e)->inputs_.end(); ++in_node) {
119  const vector<Edge*>& out_edges = (*in_node)->out_edges();
120  EXPECT_NE(find(out_edges.begin(), out_edges.end(), *e),
121  out_edges.end());
122  }
123  // Check that the edge's outputs have the edge as in-edge.
124  for (vector<Node*>::const_iterator out_node = (*e)->outputs_.begin();
125  out_node != (*e)->outputs_.end(); ++out_node) {
126  EXPECT_EQ((*out_node)->in_edge(), *e);
127  }
128  }
129 
130  // The union of all in- and out-edges of each nodes should be exactly edges_.
131  set<const Edge*> node_edge_set;
132  for (State::Paths::const_iterator p = state.paths_.begin();
133  p != state.paths_.end(); ++p) {
134  const Node* n = p->second;
135  if (n->in_edge())
136  node_edge_set.insert(n->in_edge());
137  node_edge_set.insert(n->out_edges().begin(), n->out_edges().end());
138  }
139  set<const Edge*> edge_set(state.edges_.begin(), state.edges_.end());
140  EXPECT_EQ(node_edge_set, edge_set);
141 }
142 
143 void VirtualFileSystem::Create(const string& path,
144  const string& contents) {
145  files_[path].mtime = now_;
146  files_[path].contents = contents;
147  files_created_.insert(path);
148 }
149 
150 TimeStamp VirtualFileSystem::Stat(const string& path, string* err) const {
151  FileMap::const_iterator i = files_.find(path);
152  if (i != files_.end()) {
153  *err = i->second.stat_error;
154  return i->second.mtime;
155  }
156  return 0;
157 }
158 
159 bool VirtualFileSystem::WriteFile(const string& path, const string& contents) {
160  Create(path, contents);
161  return true;
162 }
163 
164 bool VirtualFileSystem::MakeDir(const string& path) {
165  directories_made_.push_back(path);
166  return true; // success
167 }
168 
170  string* contents,
171  string* err) {
172  files_read_.push_back(path);
173  FileMap::iterator i = files_.find(path);
174  if (i != files_.end()) {
175  *contents = i->second.contents;
176  return Okay;
177  }
178  *err = strerror(ENOENT);
179  return NotFound;
180 }
181 
182 int VirtualFileSystem::RemoveFile(const string& path) {
183  if (find(directories_made_.begin(), directories_made_.end(), path)
184  != directories_made_.end())
185  return -1;
186  FileMap::iterator i = files_.find(path);
187  if (i != files_.end()) {
188  files_.erase(i);
189  files_removed_.insert(path);
190  return 0;
191  } else {
192  return 1;
193  }
194 }
195 
196 void ScopedTempDir::CreateAndEnter(const string& name) {
197  // First change into the system temp dir and save it for cleanup.
198  start_dir_ = GetSystemTempDir();
199  if (start_dir_.empty())
200  Fatal("couldn't get system temp dir");
201  if (chdir(start_dir_.c_str()) < 0)
202  Fatal("chdir: %s", strerror(errno));
203 
204  // Create a temporary subdirectory of that.
205  char name_template[1024];
206  strcpy(name_template, name.c_str());
207  strcat(name_template, "-XXXXXX");
208  char* tempname = mkdtemp(name_template);
209  if (!tempname)
210  Fatal("mkdtemp: %s", strerror(errno));
211  temp_dir_name_ = tempname;
212 
213  // chdir into the new temporary directory.
214  if (chdir(temp_dir_name_.c_str()) < 0)
215  Fatal("chdir: %s", strerror(errno));
216 }
217 
219  if (temp_dir_name_.empty())
220  return; // Something went wrong earlier.
221 
222  // Move out of the directory we're about to clobber.
223  if (chdir(start_dir_.c_str()) < 0)
224  Fatal("chdir: %s", strerror(errno));
225 
226 #ifdef _WIN32
227  string command = "rmdir /s /q " + temp_dir_name_;
228 #else
229  string command = "rm -rf " + temp_dir_name_;
230 #endif
231  if (system(command.c_str()) < 0)
232  Fatal("system: %s", strerror(errno));
233 
234  temp_dir_name_.clear();
235 }
FileMap files_
Definition: test.h:163
const vector< Edge * > & out_edges() const
Definition: graph.h:96
void AssertParse(State *state, const char *input, ManifestParserOptions opts)
Definition: test.cc:98
vector< Edge * > edges_
All the edges of the graph.
Definition: state.h:125
Node * GetNode(const string &path)
Short way to get a Node by its path from state_.
Definition: test.cc:93
Edge * in_edge() const
Definition: graph.h:90
Node * GetNode(StringPiece path, uint64_t slash_bits)
Definition: state.cc:103
Paths paths_
Definition: state.h:119
#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
set< string > files_removed_
Definition: test.h:164
#define EXPECT_FALSE(a)
Definition: test.h:78
virtual bool WriteFile(const string &path, const string &contents)
Create a file, with the specified name and contents Returns true on success, false on failure...
Definition: test.cc:159
int now_
A simple fake timestamp for file operations.
Definition: test.h:168
void Create(const string &path, const string &contents)
"Create" a file with contents.
Definition: test.cc:143
set< string > files_created_
Definition: test.h:165
vector< string > directories_made_
Definition: test.h:160
void AddCatRule(State *state)
Add a "cat" rule to state.
Definition: test.cc:87
Parses .ninja files.
#define EXPECT_EQ(a, b)
Definition: test.h:64
string start_dir_
The temp directory containing our dir.
Definition: test.h:179
virtual bool MakeDir(const string &path)
Create a directory, returning false on failure.
Definition: test.cc:164
Status
Result of ReadFile.
void VerifyGraph(const State &state)
Definition: test.cc:111
int64_t TimeStamp
Definition: timestamp.h:31
#define ASSERT_EQ(a, b)
Definition: test.h:81
void Fatal(const char *msg,...)
Log a fatal message and exit.
Definition: util.cc:57
void Cleanup()
Clean up the temporary directory.
Definition: test.cc:218
virtual TimeStamp Stat(const string &path, string *err) const
stat() a file, returning the mtime, or 0 if missing and -1 on other errors.
Definition: test.cc:150
static uint64_t HashCommand(StringPiece command)
Definition: build_log.cc:107
virtual Status ReadFile(const string &path, string *contents, string *err)
Read and store in given string.
Definition: test.cc:169
void CreateAndEnter(const string &name)
Create a temporary directory and chdir into it.
Definition: test.cc:196
Global state (file status) for a single run.
Definition: state.h:85
vector< string > files_read_
Definition: test.h:161
virtual int RemoveFile(const string &path)
Remove the file named path.
Definition: test.cc:182
unsigned long long uint64_t
Definition: win32port.h:29
#define EXPECT_NE(a, b)
Definition: test.h:66
void AssertHash(const char *expected, uint64_t actual)
Definition: test.cc:107
string temp_dir_name_
The subdirectory name for our dir, or empty if it hasn&#39;t been set up.
Definition: test.h:181
bool ParseTest(const string &input, string *err)
Parse a text string of input. Used by tests.