Imported Upstream version 1.7.1
[platform/upstream/ninja.git] / src / test.cc
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
83 StateTestWithBuiltinRules::StateTestWithBuiltinRules() {
84   AddCatRule(&state_);
85 }
86
87 void StateTestWithBuiltinRules::AddCatRule(State* state) {
88   AssertParse(state,
89 "rule cat\n"
90 "  command = cat $in > $out\n");
91 }
92
93 Node* StateTestWithBuiltinRules::GetNode(const string& path) {
94   EXPECT_FALSE(strpbrk(path.c_str(), "/\\"));
95   return state_.GetNode(path, 0);
96 }
97
98 void AssertParse(State* state, const char* input) {
99   ManifestParser parser(state, NULL, kDupeEdgeActionWarn);
100   string err;
101   EXPECT_TRUE(parser.ParseTest(input, &err));
102   ASSERT_EQ("", err);
103   VerifyGraph(*state);
104 }
105
106 void AssertHash(const char* expected, uint64_t actual) {
107   ASSERT_EQ(BuildLog::LogEntry::HashCommand(expected), actual);
108 }
109
110 void VerifyGraph(const State& state) {
111   for (vector<Edge*>::const_iterator e = state.edges_.begin();
112        e != state.edges_.end(); ++e) {
113     // All edges need at least one output.
114     EXPECT_FALSE((*e)->outputs_.empty());
115     // Check that the edge's inputs have the edge as out-edge.
116     for (vector<Node*>::const_iterator in_node = (*e)->inputs_.begin();
117          in_node != (*e)->inputs_.end(); ++in_node) {
118       const vector<Edge*>& out_edges = (*in_node)->out_edges();
119       EXPECT_NE(find(out_edges.begin(), out_edges.end(), *e),
120                 out_edges.end());
121     }
122     // Check that the edge's outputs have the edge as in-edge.
123     for (vector<Node*>::const_iterator out_node = (*e)->outputs_.begin();
124          out_node != (*e)->outputs_.end(); ++out_node) {
125       EXPECT_EQ((*out_node)->in_edge(), *e);
126     }
127   }
128
129   // The union of all in- and out-edges of each nodes should be exactly edges_.
130   set<const Edge*> node_edge_set;
131   for (State::Paths::const_iterator p = state.paths_.begin();
132        p != state.paths_.end(); ++p) {
133     const Node* n = p->second;
134     if (n->in_edge())
135       node_edge_set.insert(n->in_edge());
136     node_edge_set.insert(n->out_edges().begin(), n->out_edges().end());
137   }
138   set<const Edge*> edge_set(state.edges_.begin(), state.edges_.end());
139   EXPECT_EQ(node_edge_set, edge_set);
140 }
141
142 void VirtualFileSystem::Create(const string& path,
143                                const string& contents) {
144   files_[path].mtime = now_;
145   files_[path].contents = contents;
146   files_created_.insert(path);
147 }
148
149 TimeStamp VirtualFileSystem::Stat(const string& path, string* err) const {
150   FileMap::const_iterator i = files_.find(path);
151   if (i != files_.end()) {
152     *err = i->second.stat_error;
153     return i->second.mtime;
154   }
155   return 0;
156 }
157
158 bool VirtualFileSystem::WriteFile(const string& path, const string& contents) {
159   Create(path, contents);
160   return true;
161 }
162
163 bool VirtualFileSystem::MakeDir(const string& path) {
164   directories_made_.push_back(path);
165   return true;  // success
166 }
167
168 FileReader::Status VirtualFileSystem::ReadFile(const string& path,
169                                                string* contents,
170                                                string* err) {
171   files_read_.push_back(path);
172   FileMap::iterator i = files_.find(path);
173   if (i != files_.end()) {
174     *contents = i->second.contents;
175     return Okay;
176   }
177   *err = strerror(ENOENT);
178   return NotFound;
179 }
180
181 int VirtualFileSystem::RemoveFile(const string& path) {
182   if (find(directories_made_.begin(), directories_made_.end(), path)
183       != directories_made_.end())
184     return -1;
185   FileMap::iterator i = files_.find(path);
186   if (i != files_.end()) {
187     files_.erase(i);
188     files_removed_.insert(path);
189     return 0;
190   } else {
191     return 1;
192   }
193 }
194
195 void ScopedTempDir::CreateAndEnter(const string& name) {
196   // First change into the system temp dir and save it for cleanup.
197   start_dir_ = GetSystemTempDir();
198   if (start_dir_.empty())
199     Fatal("couldn't get system temp dir");
200   if (chdir(start_dir_.c_str()) < 0)
201     Fatal("chdir: %s", strerror(errno));
202
203   // Create a temporary subdirectory of that.
204   char name_template[1024];
205   strcpy(name_template, name.c_str());
206   strcat(name_template, "-XXXXXX");
207   char* tempname = mkdtemp(name_template);
208   if (!tempname)
209     Fatal("mkdtemp: %s", strerror(errno));
210   temp_dir_name_ = tempname;
211
212   // chdir into the new temporary directory.
213   if (chdir(temp_dir_name_.c_str()) < 0)
214     Fatal("chdir: %s", strerror(errno));
215 }
216
217 void ScopedTempDir::Cleanup() {
218   if (temp_dir_name_.empty())
219     return;  // Something went wrong earlier.
220
221   // Move out of the directory we're about to clobber.
222   if (chdir(start_dir_.c_str()) < 0)
223     Fatal("chdir: %s", strerror(errno));
224
225 #ifdef _WIN32
226   string command = "rmdir /s /q " + temp_dir_name_;
227 #else
228   string command = "rm -rf " + temp_dir_name_;
229 #endif
230   if (system(command.c_str()) < 0)
231     Fatal("system: %s", strerror(errno));
232
233   temp_dir_name_.clear();
234 }