|
Ninja
|
00001 // Copyright 2011 Google Inc. All Rights Reserved. 00002 // 00003 // Licensed under the Apache License, Version 2.0 (the "License"); 00004 // you may not use this file except in compliance with the License. 00005 // You may obtain a copy of the License at 00006 // 00007 // http://www.apache.org/licenses/LICENSE-2.0 00008 // 00009 // Unless required by applicable law or agreed to in writing, software 00010 // distributed under the License is distributed on an "AS IS" BASIS, 00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00012 // See the License for the specific language governing permissions and 00013 // limitations under the License. 00014 00015 #include <gtest/gtest.h> 00016 00017 #ifdef _WIN32 00018 #include <io.h> 00019 #include <windows.h> 00020 #endif 00021 00022 #include "disk_interface.h" 00023 #include "graph.h" 00024 #include "test.h" 00025 00026 using namespace std; 00027 00028 namespace { 00029 00030 #ifdef _WIN32 00031 #ifndef _mktemp_s 00032 /// mingw has no mktemp. Implement one with the same type as the one 00033 /// found in the Windows API. 00034 int _mktemp_s(char* templ) { 00035 char* ofs = strchr(templ, 'X'); 00036 sprintf(ofs, "%d", rand() % 1000000); 00037 return 0; 00038 } 00039 #endif 00040 00041 /// Windows has no mkdtemp. Implement it in terms of _mktemp_s. 00042 char* mkdtemp(char* name_template) { 00043 int err = _mktemp_s(name_template); 00044 if (err < 0) { 00045 perror("_mktemp_s"); 00046 return NULL; 00047 } 00048 00049 err = _mkdir(name_template); 00050 if (err < 0) { 00051 perror("mkdir"); 00052 return NULL; 00053 } 00054 00055 return name_template; 00056 } 00057 #endif 00058 00059 class DiskInterfaceTest : public testing::Test { 00060 public: 00061 virtual void SetUp() { 00062 // Because we do real disk accesses, we create a temp dir within 00063 // the system temporary directory. 00064 00065 // First change into the system temp dir and save it for cleanup. 00066 start_dir_ = GetSystemTempDir(); 00067 ASSERT_EQ(0, chdir(start_dir_.c_str())); 00068 00069 // Then create and change into a temporary subdirectory of that. 00070 temp_dir_name_ = MakeTempDir(); 00071 ASSERT_FALSE(temp_dir_name_.empty()); 00072 ASSERT_EQ(0, chdir(temp_dir_name_.c_str())); 00073 } 00074 00075 virtual void TearDown() { 00076 // Move out of the directory we're about to clobber. 00077 ASSERT_EQ(0, chdir(start_dir_.c_str())); 00078 #ifdef _WIN32 00079 ASSERT_EQ(0, system(("rmdir /s /q " + temp_dir_name_).c_str())); 00080 #else 00081 ASSERT_EQ(0, system(("rm -rf " + temp_dir_name_).c_str())); 00082 #endif 00083 } 00084 00085 string GetSystemTempDir() { 00086 #ifdef _WIN32 00087 char buf[1024]; 00088 if (!GetTempPath(sizeof(buf), buf)) 00089 return ""; 00090 return buf; 00091 #else 00092 const char* tempdir = getenv("TMPDIR"); 00093 if (tempdir) 00094 return tempdir; 00095 return "/tmp"; 00096 #endif 00097 } 00098 00099 string MakeTempDir() { 00100 char name_template[] = "DiskInterfaceTest-XXXXXX"; 00101 char* name = mkdtemp(name_template); 00102 return name ? name : ""; 00103 } 00104 00105 string start_dir_; 00106 string temp_dir_name_; 00107 RealDiskInterface disk_; 00108 }; 00109 00110 TEST_F(DiskInterfaceTest, Stat) { 00111 EXPECT_EQ(0, disk_.Stat("nosuchfile")); 00112 00113 #ifdef _WIN32 00114 // TODO: find something that stat fails on for Windows. 00115 #else 00116 string too_long_name(512, 'x'); 00117 EXPECT_EQ(-1, disk_.Stat(too_long_name)); 00118 #endif 00119 00120 #ifdef _WIN32 00121 ASSERT_EQ(0, system("cmd.exe /c echo hi > file")); 00122 #else 00123 ASSERT_EQ(0, system("touch file")); 00124 #endif 00125 EXPECT_GT(disk_.Stat("file"), 1); 00126 } 00127 00128 TEST_F(DiskInterfaceTest, ReadFile) { 00129 string err; 00130 EXPECT_EQ("", disk_.ReadFile("foobar", &err)); 00131 EXPECT_EQ("", err); 00132 00133 const char* kTestFile = "testfile"; 00134 FILE* f = fopen(kTestFile, "wb"); 00135 ASSERT_TRUE(f); 00136 const char* kTestContent = "test content\nok"; 00137 fprintf(f, "%s", kTestContent); 00138 ASSERT_EQ(0, fclose(f)); 00139 00140 EXPECT_EQ(kTestContent, disk_.ReadFile(kTestFile, &err)); 00141 EXPECT_EQ("", err); 00142 } 00143 00144 TEST_F(DiskInterfaceTest, MakeDirs) { 00145 EXPECT_TRUE(disk_.MakeDirs("path/with/double//slash/")); 00146 } 00147 00148 TEST_F(DiskInterfaceTest, RemoveFile) { 00149 const char* kFileName = "file-to-remove"; 00150 #ifdef _WIN32 00151 string cmd = "cmd /c echo hi > "; 00152 #else 00153 string cmd = "touch "; 00154 #endif 00155 cmd += kFileName; 00156 ASSERT_EQ(0, system(cmd.c_str())); 00157 EXPECT_EQ(0, disk_.RemoveFile(kFileName)); 00158 EXPECT_EQ(1, disk_.RemoveFile(kFileName)); 00159 EXPECT_EQ(1, disk_.RemoveFile("does not exist")); 00160 } 00161 00162 struct StatTest : public StateTestWithBuiltinRules, 00163 public DiskInterface { 00164 // DiskInterface implementation. 00165 virtual int Stat(const string& path); 00166 virtual bool MakeDir(const string& path) { 00167 assert(false); 00168 return false; 00169 } 00170 virtual string ReadFile(const string& path, string* err) { 00171 assert(false); 00172 return ""; 00173 } 00174 virtual int RemoveFile(const string& path) { 00175 assert(false); 00176 return 0; 00177 } 00178 00179 map<string, time_t> mtimes_; 00180 vector<string> stats_; 00181 }; 00182 00183 int StatTest::Stat(const string& path) { 00184 stats_.push_back(path); 00185 map<string, time_t>::iterator i = mtimes_.find(path); 00186 if (i == mtimes_.end()) 00187 return 0; // File not found. 00188 return i->second; 00189 } 00190 00191 TEST_F(StatTest, Simple) { 00192 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 00193 "build out: cat in\n")); 00194 00195 Node* out = GetNode("out"); 00196 out->file_->Stat(this); 00197 ASSERT_EQ(1u, stats_.size()); 00198 Edge* edge = out->in_edge_; 00199 edge->RecomputeDirty(NULL, this, NULL); 00200 ASSERT_EQ(2u, stats_.size()); 00201 ASSERT_EQ("out", stats_[0]); 00202 ASSERT_EQ("in", stats_[1]); 00203 } 00204 00205 TEST_F(StatTest, TwoStep) { 00206 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 00207 "build out: cat mid\n" 00208 "build mid: cat in\n")); 00209 00210 Node* out = GetNode("out"); 00211 out->file_->Stat(this); 00212 ASSERT_EQ(1u, stats_.size()); 00213 Edge* edge = out->in_edge_; 00214 edge->RecomputeDirty(NULL, this, NULL); 00215 ASSERT_EQ(3u, stats_.size()); 00216 ASSERT_EQ("out", stats_[0]); 00217 ASSERT_TRUE(GetNode("out")->dirty_); 00218 ASSERT_EQ("mid", stats_[1]); 00219 ASSERT_TRUE(GetNode("mid")->dirty_); 00220 ASSERT_EQ("in", stats_[2]); 00221 } 00222 00223 TEST_F(StatTest, Tree) { 00224 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 00225 "build out: cat mid1 mid2\n" 00226 "build mid1: cat in11 in12\n" 00227 "build mid2: cat in21 in22\n")); 00228 00229 Node* out = GetNode("out"); 00230 out->file_->Stat(this); 00231 ASSERT_EQ(1u, stats_.size()); 00232 Edge* edge = out->in_edge_; 00233 edge->RecomputeDirty(NULL, this, NULL); 00234 ASSERT_EQ(1u + 6u, stats_.size()); 00235 ASSERT_EQ("mid1", stats_[1]); 00236 ASSERT_TRUE(GetNode("mid1")->dirty_); 00237 ASSERT_EQ("in11", stats_[2]); 00238 } 00239 00240 TEST_F(StatTest, Middle) { 00241 ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 00242 "build out: cat mid\n" 00243 "build mid: cat in\n")); 00244 00245 mtimes_["in"] = 1; 00246 mtimes_["mid"] = 0; // missing 00247 mtimes_["out"] = 1; 00248 00249 Node* out = GetNode("out"); 00250 out->file_->Stat(this); 00251 ASSERT_EQ(1u, stats_.size()); 00252 Edge* edge = out->in_edge_; 00253 edge->RecomputeDirty(NULL, this, NULL); 00254 ASSERT_FALSE(GetNode("in")->dirty_); 00255 ASSERT_TRUE(GetNode("mid")->dirty_); 00256 ASSERT_TRUE(GetNode("out")->dirty_); 00257 } 00258 00259 } // namespace
1.7.5.1