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, ReadFile) {
00080   string err;
00081   EXPECT_EQ("", disk_.ReadFile("foobar", &err));
00082   EXPECT_EQ("", err);
00083 
00084   const char* kTestFile = "testfile";
00085   FILE* f = fopen(kTestFile, "wb");
00086   ASSERT_TRUE(f);
00087   const char* kTestContent = "test content\nok";
00088   fprintf(f, "%s", kTestContent);
00089   ASSERT_EQ(0, fclose(f));
00090 
00091   EXPECT_EQ(kTestContent, disk_.ReadFile(kTestFile, &err));
00092   EXPECT_EQ("", err);
00093 }
00094 
00095 TEST_F(DiskInterfaceTest, MakeDirs) {
00096   EXPECT_TRUE(disk_.MakeDirs("path/with/double//slash/"));
00097 }
00098 
00099 TEST_F(DiskInterfaceTest, RemoveFile) {
00100   const char* kFileName = "file-to-remove";
00101   ASSERT_TRUE(Touch(kFileName));
00102   EXPECT_EQ(0, disk_.RemoveFile(kFileName));
00103   EXPECT_EQ(1, disk_.RemoveFile(kFileName));
00104   EXPECT_EQ(1, disk_.RemoveFile("does not exist"));
00105 }
00106 
00107 struct StatTest : public StateTestWithBuiltinRules,
00108                   public DiskInterface {
00109   StatTest() : scan_(&state_, NULL, NULL, this) {}
00110 
00111   // DiskInterface implementation.
00112   virtual TimeStamp Stat(const string& path);
00113   virtual bool WriteFile(const string& path, const string& contents) {
00114     assert(false);
00115     return true;
00116   }
00117   virtual bool MakeDir(const string& path) {
00118     assert(false);
00119     return false;
00120   }
00121   virtual string ReadFile(const string& path, string* err) {
00122     assert(false);
00123     return "";
00124   }
00125   virtual int RemoveFile(const string& path) {
00126     assert(false);
00127     return 0;
00128   }
00129 
00130   DependencyScan scan_;
00131   map<string, TimeStamp> mtimes_;
00132   vector<string> stats_;
00133 };
00134 
00135 TimeStamp StatTest::Stat(const string& path) {
00136   stats_.push_back(path);
00137   map<string, TimeStamp>::iterator i = mtimes_.find(path);
00138   if (i == mtimes_.end())
00139     return 0;  // File not found.
00140   return i->second;
00141 }
00142 
00143 TEST_F(StatTest, Simple) {
00144   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
00145 "build out: cat in\n"));
00146 
00147   Node* out = GetNode("out");
00148   out->Stat(this);
00149   ASSERT_EQ(1u, stats_.size());
00150   scan_.RecomputeDirty(out->in_edge(), NULL);
00151   ASSERT_EQ(2u, stats_.size());
00152   ASSERT_EQ("out", stats_[0]);
00153   ASSERT_EQ("in",  stats_[1]);
00154 }
00155 
00156 TEST_F(StatTest, TwoStep) {
00157   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
00158 "build out: cat mid\n"
00159 "build mid: cat in\n"));
00160 
00161   Node* out = GetNode("out");
00162   out->Stat(this);
00163   ASSERT_EQ(1u, stats_.size());
00164   scan_.RecomputeDirty(out->in_edge(), NULL);
00165   ASSERT_EQ(3u, stats_.size());
00166   ASSERT_EQ("out", stats_[0]);
00167   ASSERT_TRUE(GetNode("out")->dirty());
00168   ASSERT_EQ("mid",  stats_[1]);
00169   ASSERT_TRUE(GetNode("mid")->dirty());
00170   ASSERT_EQ("in",  stats_[2]);
00171 }
00172 
00173 TEST_F(StatTest, Tree) {
00174   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
00175 "build out: cat mid1 mid2\n"
00176 "build mid1: cat in11 in12\n"
00177 "build mid2: cat in21 in22\n"));
00178 
00179   Node* out = GetNode("out");
00180   out->Stat(this);
00181   ASSERT_EQ(1u, stats_.size());
00182   scan_.RecomputeDirty(out->in_edge(), NULL);
00183   ASSERT_EQ(1u + 6u, stats_.size());
00184   ASSERT_EQ("mid1", stats_[1]);
00185   ASSERT_TRUE(GetNode("mid1")->dirty());
00186   ASSERT_EQ("in11", stats_[2]);
00187 }
00188 
00189 TEST_F(StatTest, Middle) {
00190   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
00191 "build out: cat mid\n"
00192 "build mid: cat in\n"));
00193 
00194   mtimes_["in"] = 1;
00195   mtimes_["mid"] = 0;  // missing
00196   mtimes_["out"] = 1;
00197 
00198   Node* out = GetNode("out");
00199   out->Stat(this);
00200   ASSERT_EQ(1u, stats_.size());
00201   scan_.RecomputeDirty(out->in_edge(), NULL);
00202   ASSERT_FALSE(GetNode("in")->dirty());
00203   ASSERT_TRUE(GetNode("mid")->dirty());
00204   ASSERT_TRUE(GetNode("out")->dirty());
00205 }
00206 
00207 }  // namespace