github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/src/test.cc (about)

     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  #include <io.h>
    28  #else
    29  #include <unistd.h>
    30  #endif
    31  
    32  #include "build_log.h"
    33  #include "graph.h"
    34  #include "manifest_parser.h"
    35  #include "util.h"
    36  
    37  #ifdef _AIX
    38  extern "C" {
    39          // GCC "helpfully" strips the definition of mkdtemp out on AIX.
    40          // The function is still present, so if we define it ourselves
    41          // it will work perfectly fine.
    42          extern char* mkdtemp(char* name_template);
    43  }
    44  #endif
    45  
    46  using namespace std;
    47  
    48  namespace {
    49  
    50  #ifdef _WIN32
    51  /// Windows has no mkdtemp.  Implement it in terms of _mktemp_s.
    52  char* mkdtemp(char* name_template) {
    53    int err = _mktemp_s(name_template, strlen(name_template) + 1);
    54    if (err < 0) {
    55      perror("_mktemp_s");
    56      return NULL;
    57    }
    58  
    59    err = _mkdir(name_template);
    60    if (err < 0) {
    61      perror("mkdir");
    62      return NULL;
    63    }
    64  
    65    return name_template;
    66  }
    67  #endif  // _WIN32
    68  
    69  string GetSystemTempDir() {
    70  #ifdef _WIN32
    71    char buf[1024];
    72    if (!GetTempPath(sizeof(buf), buf))
    73      return "";
    74    return buf;
    75  #else
    76    const char* tempdir = getenv("TMPDIR");
    77    if (tempdir)
    78      return tempdir;
    79    return "/tmp";
    80  #endif
    81  }
    82  
    83  }  // anonymous namespace
    84  
    85  StateTestWithBuiltinRules::StateTestWithBuiltinRules() {
    86    AddCatRule(&state_);
    87  }
    88  
    89  void StateTestWithBuiltinRules::AddCatRule(State* state) {
    90    AssertParse(state,
    91  "rule cat\n"
    92  "  command = cat $in > $out\n");
    93  }
    94  
    95  Node* StateTestWithBuiltinRules::GetNode(const string& path) {
    96    EXPECT_FALSE(strpbrk(path.c_str(), "/\\"));
    97    return state_.GetNode(path, 0);
    98  }
    99  
   100  void AssertParse(State* state, const char* input,
   101                   ManifestParserOptions opts) {
   102    ManifestParser parser(state, NULL, opts);
   103    string err;
   104    EXPECT_TRUE(parser.ParseTest(input, &err));
   105    ASSERT_EQ("", err);
   106    VerifyGraph(*state);
   107  }
   108  
   109  void AssertHash(const char* expected, uint64_t actual) {
   110    ASSERT_EQ(BuildLog::LogEntry::HashCommand(expected), actual);
   111  }
   112  
   113  void VerifyGraph(const State& state) {
   114    for (vector<Edge*>::const_iterator e = state.edges_.begin();
   115         e != state.edges_.end(); ++e) {
   116      // All edges need at least one output.
   117      EXPECT_FALSE((*e)->outputs_.empty());
   118      // Check that the edge's inputs have the edge as out-edge.
   119      for (vector<Node*>::const_iterator in_node = (*e)->inputs_.begin();
   120           in_node != (*e)->inputs_.end(); ++in_node) {
   121        const vector<Edge*>& out_edges = (*in_node)->out_edges();
   122        EXPECT_NE(find(out_edges.begin(), out_edges.end(), *e),
   123                  out_edges.end());
   124      }
   125      // Check that the edge's outputs have the edge as in-edge.
   126      for (vector<Node*>::const_iterator out_node = (*e)->outputs_.begin();
   127           out_node != (*e)->outputs_.end(); ++out_node) {
   128        EXPECT_EQ((*out_node)->in_edge(), *e);
   129      }
   130    }
   131  
   132    // The union of all in- and out-edges of each nodes should be exactly edges_.
   133    set<const Edge*> node_edge_set;
   134    for (State::Paths::const_iterator p = state.paths_.begin();
   135         p != state.paths_.end(); ++p) {
   136      const Node* n = p->second;
   137      if (n->in_edge())
   138        node_edge_set.insert(n->in_edge());
   139      node_edge_set.insert(n->out_edges().begin(), n->out_edges().end());
   140    }
   141    set<const Edge*> edge_set(state.edges_.begin(), state.edges_.end());
   142    EXPECT_EQ(node_edge_set, edge_set);
   143  }
   144  
   145  void VirtualFileSystem::Create(const string& path,
   146                                 const string& contents) {
   147    files_[path].mtime = now_;
   148    files_[path].contents = contents;
   149    files_created_.insert(path);
   150  }
   151  
   152  TimeStamp VirtualFileSystem::Stat(const string& path, string* err) const {
   153    FileMap::const_iterator i = files_.find(path);
   154    if (i != files_.end()) {
   155      *err = i->second.stat_error;
   156      return i->second.mtime;
   157    }
   158    return 0;
   159  }
   160  
   161  bool VirtualFileSystem::WriteFile(const string& path, const string& contents) {
   162    Create(path, contents);
   163    return true;
   164  }
   165  
   166  bool VirtualFileSystem::MakeDir(const string& path) {
   167    directories_made_.push_back(path);
   168    return true;  // success
   169  }
   170  
   171  FileReader::Status VirtualFileSystem::ReadFile(const string& path,
   172                                                 string* contents,
   173                                                 string* err) {
   174    files_read_.push_back(path);
   175    FileMap::iterator i = files_.find(path);
   176    if (i != files_.end()) {
   177      *contents = i->second.contents;
   178      return Okay;
   179    }
   180    *err = strerror(ENOENT);
   181    return NotFound;
   182  }
   183  
   184  int VirtualFileSystem::RemoveFile(const string& path) {
   185    if (find(directories_made_.begin(), directories_made_.end(), path)
   186        != directories_made_.end())
   187      return -1;
   188    FileMap::iterator i = files_.find(path);
   189    if (i != files_.end()) {
   190      files_.erase(i);
   191      files_removed_.insert(path);
   192      return 0;
   193    } else {
   194      return 1;
   195    }
   196  }
   197  
   198  void ScopedTempDir::CreateAndEnter(const string& name) {
   199    // First change into the system temp dir and save it for cleanup.
   200    start_dir_ = GetSystemTempDir();
   201    if (start_dir_.empty())
   202      Fatal("couldn't get system temp dir");
   203    if (chdir(start_dir_.c_str()) < 0)
   204      Fatal("chdir: %s", strerror(errno));
   205  
   206    // Create a temporary subdirectory of that.
   207    char name_template[1024];
   208    strcpy(name_template, name.c_str());
   209    strcat(name_template, "-XXXXXX");
   210    char* tempname = mkdtemp(name_template);
   211    if (!tempname)
   212      Fatal("mkdtemp: %s", strerror(errno));
   213    temp_dir_name_ = tempname;
   214  
   215    // chdir into the new temporary directory.
   216    if (chdir(temp_dir_name_.c_str()) < 0)
   217      Fatal("chdir: %s", strerror(errno));
   218  }
   219  
   220  void ScopedTempDir::Cleanup() {
   221    if (temp_dir_name_.empty())
   222      return;  // Something went wrong earlier.
   223  
   224    // Move out of the directory we're about to clobber.
   225    if (chdir(start_dir_.c_str()) < 0)
   226      Fatal("chdir: %s", strerror(errno));
   227  
   228  #ifdef _WIN32
   229    string command = "rmdir /s /q " + temp_dir_name_;
   230  #else
   231    string command = "rm -rf " + temp_dir_name_;
   232  #endif
   233    if (system(command.c_str()) < 0)
   234      Fatal("system: %s", strerror(errno));
   235  
   236    temp_dir_name_.clear();
   237  }