Ninja
disk_interface_test.cc
Go to the documentation of this file.
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 namespace {
00027 
00028 struct DiskInterfaceTest : public testing::Test {
00029   virtual void SetUp() {
00030     // These tests do real disk accesses, so create a temp dir.
00031     temp_dir_.CreateAndEnter("Ninja-DiskInterfaceTest");
00032   }
00033 
00034   virtual void TearDown() {
00035     temp_dir_.Cleanup();
00036   }
00037 
00038   bool Touch(const char* path) {
00039     FILE *f = fopen(path, "w");
00040     if (!f)
00041       return false;
00042     return fclose(f) == 0;
00043   }
00044 
00045   ScopedTempDir temp_dir_;
00046   RealDiskInterface disk_;
00047 };
00048 
00049 TEST_F(DiskInterfaceTest, StatMissingFile) {
00050   EXPECT_EQ(0, disk_.Stat("nosuchfile"));
00051 
00052   // On Windows, the errno for a file in a nonexistent directory
00053   // is different.
00054   EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile"));
00055 
00056   // On POSIX systems, the errno is different if a component of the
00057   // path prefix is not a directory.
00058   ASSERT_TRUE(Touch("notadir"));
00059   EXPECT_EQ(0, disk_.Stat("notadir/nosuchfile"));
00060 }
00061 
00062 TEST_F(DiskInterfaceTest, StatBadPath) {
00063   disk_.quiet_ = true;
00064 #ifdef _WIN32
00065   string bad_path("cc:\\foo");
00066   EXPECT_EQ(-1, disk_.Stat(bad_path));
00067 #else
00068   string too_long_name(512, 'x');
00069   EXPECT_EQ(-1, disk_.Stat(too_long_name));
00070 #endif
00071   disk_.quiet_ = false;
00072 }
00073 
00074 TEST_F(DiskInterfaceTest, StatExistingFile) {
00075   ASSERT_TRUE(Touch("file"));
00076   EXPECT_GT(disk_.Stat("file"), 1);
00077 }
00078 
00079 TEST_F(DiskInterfaceTest, StatExistingDir) {
00080   ASSERT_TRUE(disk_.MakeDir("subdir"));
00081   ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir"));
00082   EXPECT_GT(disk_.Stat("."), 1);
00083   EXPECT_GT(disk_.Stat("subdir"), 1);
00084   EXPECT_GT(disk_.Stat("subdir/subsubdir"), 1);
00085 
00086   EXPECT_EQ(disk_.Stat("subdir"), disk_.Stat("subdir/."));
00087   EXPECT_EQ(disk_.Stat("subdir"), disk_.Stat("subdir/subsubdir/.."));
00088   EXPECT_EQ(disk_.Stat("subdir/subsubdir"), disk_.Stat("subdir/subsubdir/."));
00089 }
00090 
00091 #ifdef _WIN32
00092 TEST_F(DiskInterfaceTest, StatCache) {
00093   disk_.AllowStatCache(true);
00094 
00095   ASSERT_TRUE(Touch("file1"));
00096   ASSERT_TRUE(Touch("fiLE2"));
00097   ASSERT_TRUE(disk_.MakeDir("subdir"));
00098   ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir"));
00099   ASSERT_TRUE(Touch("subdir\\subfile1"));
00100   ASSERT_TRUE(Touch("subdir\\SUBFILE2"));
00101   ASSERT_TRUE(Touch("subdir\\SUBFILE3"));
00102 
00103   EXPECT_GT(disk_.Stat("FIle1"), 1);
00104   EXPECT_GT(disk_.Stat("file1"), 1);
00105 
00106   EXPECT_GT(disk_.Stat("subdir/subfile2"), 1);
00107   EXPECT_GT(disk_.Stat("sUbdir\\suBFile1"), 1);
00108 
00109   EXPECT_GT(disk_.Stat("."), 1);
00110   EXPECT_GT(disk_.Stat("subdir"), 1);
00111   EXPECT_GT(disk_.Stat("subdir/subsubdir"), 1);
00112 
00113   EXPECT_EQ(disk_.Stat("subdir"), disk_.Stat("subdir/."));
00114   EXPECT_EQ(disk_.Stat("subdir"), disk_.Stat("subdir/subsubdir/.."));
00115   EXPECT_EQ(disk_.Stat("subdir/subsubdir"), disk_.Stat("subdir/subsubdir/."));
00116 
00117   // Test error cases.
00118   disk_.quiet_ = true;
00119   string bad_path("cc:\\foo");
00120   EXPECT_EQ(-1, disk_.Stat(bad_path));
00121   EXPECT_EQ(-1, disk_.Stat(bad_path));
00122   EXPECT_EQ(0, disk_.Stat("nosuchfile"));
00123   EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile"));
00124 }
00125 #endif
00126 
00127 TEST_F(DiskInterfaceTest, ReadFile) {
00128   string err;
00129   EXPECT_EQ("", disk_.ReadFile("foobar", &err));
00130   EXPECT_EQ("", err);
00131 
00132   const char* kTestFile = "testfile";
00133   FILE* f = fopen(kTestFile, "wb");
00134   ASSERT_TRUE(f);
00135   const char* kTestContent = "test content\nok";
00136   fprintf(f, "%s", kTestContent);
00137   ASSERT_EQ(0, fclose(f));
00138 
00139   EXPECT_EQ(kTestContent, disk_.ReadFile(kTestFile, &err));
00140   EXPECT_EQ("", err);
00141 }
00142 
00143 TEST_F(DiskInterfaceTest, MakeDirs) {
00144   string path = "path/with/double//slash/";
00145   EXPECT_TRUE(disk_.MakeDirs(path.c_str()));
00146   FILE* f = fopen((path + "a_file").c_str(), "w");
00147   EXPECT_TRUE(f);
00148   EXPECT_EQ(0, fclose(f));
00149 #ifdef _WIN32
00150   string path2 = "another\\with\\back\\\\slashes\\";
00151   EXPECT_TRUE(disk_.MakeDirs(path2.c_str()));
00152   FILE* f2 = fopen((path2 + "a_file").c_str(), "w");
00153   EXPECT_TRUE(f2);
00154   EXPECT_EQ(0, fclose(f2));
00155 #endif
00156 }
00157 
00158 TEST_F(DiskInterfaceTest, RemoveFile) {
00159   const char* kFileName = "file-to-remove";
00160   ASSERT_TRUE(Touch(kFileName));
00161   EXPECT_EQ(0, disk_.RemoveFile(kFileName));
00162   EXPECT_EQ(1, disk_.RemoveFile(kFileName));
00163   EXPECT_EQ(1, disk_.RemoveFile("does not exist"));
00164 }
00165 
00166 struct StatTest : public StateTestWithBuiltinRules,
00167                   public DiskInterface {
00168   StatTest() : scan_(&state_, NULL, NULL, this) {}
00169 
00170   // DiskInterface implementation.
00171   virtual TimeStamp Stat(const string& path) const;
00172   virtual bool WriteFile(const string& path, const string& contents) {
00173     assert(false);
00174     return true;
00175   }
00176   virtual bool MakeDir(const string& path) {
00177     assert(false);
00178     return false;
00179   }
00180   virtual string ReadFile(const string& path, string* err) {
00181     assert(false);
00182     return "";
00183   }
00184   virtual int RemoveFile(const string& path) {
00185     assert(false);
00186     return 0;
00187   }
00188 
00189   DependencyScan scan_;
00190   map<string, TimeStamp> mtimes_;
00191   mutable vector<string> stats_;
00192 };
00193 
00194 TimeStamp StatTest::Stat(const string& path) const {
00195   stats_.push_back(path);
00196   map<string, TimeStamp>::const_iterator i = mtimes_.find(path);
00197   if (i == mtimes_.end())
00198     return 0;  // File not found.
00199   return i->second;
00200 }
00201 
00202 TEST_F(StatTest, Simple) {
00203   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
00204 "build out: cat in\n"));
00205 
00206   Node* out = GetNode("out");
00207   out->Stat(this);
00208   ASSERT_EQ(1u, stats_.size());
00209   scan_.RecomputeDirty(out->in_edge(), NULL);
00210   ASSERT_EQ(2u, stats_.size());
00211   ASSERT_EQ("out", stats_[0]);
00212   ASSERT_EQ("in",  stats_[1]);
00213 }
00214 
00215 TEST_F(StatTest, TwoStep) {
00216   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
00217 "build out: cat mid\n"
00218 "build mid: cat in\n"));
00219 
00220   Node* out = GetNode("out");
00221   out->Stat(this);
00222   ASSERT_EQ(1u, stats_.size());
00223   scan_.RecomputeDirty(out->in_edge(), NULL);
00224   ASSERT_EQ(3u, stats_.size());
00225   ASSERT_EQ("out", stats_[0]);
00226   ASSERT_TRUE(GetNode("out")->dirty());
00227   ASSERT_EQ("mid",  stats_[1]);
00228   ASSERT_TRUE(GetNode("mid")->dirty());
00229   ASSERT_EQ("in",  stats_[2]);
00230 }
00231 
00232 TEST_F(StatTest, Tree) {
00233   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
00234 "build out: cat mid1 mid2\n"
00235 "build mid1: cat in11 in12\n"
00236 "build mid2: cat in21 in22\n"));
00237 
00238   Node* out = GetNode("out");
00239   out->Stat(this);
00240   ASSERT_EQ(1u, stats_.size());
00241   scan_.RecomputeDirty(out->in_edge(), NULL);
00242   ASSERT_EQ(1u + 6u, stats_.size());
00243   ASSERT_EQ("mid1", stats_[1]);
00244   ASSERT_TRUE(GetNode("mid1")->dirty());
00245   ASSERT_EQ("in11", stats_[2]);
00246 }
00247 
00248 TEST_F(StatTest, Middle) {
00249   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
00250 "build out: cat mid\n"
00251 "build mid: cat in\n"));
00252 
00253   mtimes_["in"] = 1;
00254   mtimes_["mid"] = 0;  // missing
00255   mtimes_["out"] = 1;
00256 
00257   Node* out = GetNode("out");
00258   out->Stat(this);
00259   ASSERT_EQ(1u, stats_.size());
00260   scan_.RecomputeDirty(out->in_edge(), NULL);
00261   ASSERT_FALSE(GetNode("in")->dirty());
00262   ASSERT_TRUE(GetNode("mid")->dirty());
00263   ASSERT_TRUE(GetNode("out")->dirty());
00264 }
00265 
00266 }  // namespace