1695b41eeSopenharmony_ci// Copyright 2011 Google Inc. All Rights Reserved. 2695b41eeSopenharmony_ci// 3695b41eeSopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License"); 4695b41eeSopenharmony_ci// you may not use this file except in compliance with the License. 5695b41eeSopenharmony_ci// You may obtain a copy of the License at 6695b41eeSopenharmony_ci// 7695b41eeSopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0 8695b41eeSopenharmony_ci// 9695b41eeSopenharmony_ci// Unless required by applicable law or agreed to in writing, software 10695b41eeSopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS, 11695b41eeSopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12695b41eeSopenharmony_ci// See the License for the specific language governing permissions and 13695b41eeSopenharmony_ci// limitations under the License. 14695b41eeSopenharmony_ci 15695b41eeSopenharmony_ci#include <assert.h> 16695b41eeSopenharmony_ci#include <stdio.h> 17695b41eeSopenharmony_ci#ifdef _WIN32 18695b41eeSopenharmony_ci#include <io.h> 19695b41eeSopenharmony_ci#include <windows.h> 20695b41eeSopenharmony_ci#include <direct.h> 21695b41eeSopenharmony_ci#endif 22695b41eeSopenharmony_ci 23695b41eeSopenharmony_ci#include "disk_interface.h" 24695b41eeSopenharmony_ci#include "graph.h" 25695b41eeSopenharmony_ci#include "test.h" 26695b41eeSopenharmony_ci 27695b41eeSopenharmony_ciusing namespace std; 28695b41eeSopenharmony_ci 29695b41eeSopenharmony_cinamespace { 30695b41eeSopenharmony_ci 31695b41eeSopenharmony_cistruct DiskInterfaceTest : public testing::Test { 32695b41eeSopenharmony_ci virtual void SetUp() { 33695b41eeSopenharmony_ci // These tests do real disk accesses, so create a temp dir. 34695b41eeSopenharmony_ci temp_dir_.CreateAndEnter("Ninja-DiskInterfaceTest"); 35695b41eeSopenharmony_ci } 36695b41eeSopenharmony_ci 37695b41eeSopenharmony_ci virtual void TearDown() { 38695b41eeSopenharmony_ci temp_dir_.Cleanup(); 39695b41eeSopenharmony_ci } 40695b41eeSopenharmony_ci 41695b41eeSopenharmony_ci bool Touch(const char* path) { 42695b41eeSopenharmony_ci FILE *f = fopen(path, "w"); 43695b41eeSopenharmony_ci if (!f) 44695b41eeSopenharmony_ci return false; 45695b41eeSopenharmony_ci return fclose(f) == 0; 46695b41eeSopenharmony_ci } 47695b41eeSopenharmony_ci 48695b41eeSopenharmony_ci ScopedTempDir temp_dir_; 49695b41eeSopenharmony_ci RealDiskInterface disk_; 50695b41eeSopenharmony_ci}; 51695b41eeSopenharmony_ci 52695b41eeSopenharmony_ciTEST_F(DiskInterfaceTest, StatMissingFile) { 53695b41eeSopenharmony_ci string err; 54695b41eeSopenharmony_ci EXPECT_EQ(0, disk_.Stat("nosuchfile", &err)); 55695b41eeSopenharmony_ci EXPECT_EQ("", err); 56695b41eeSopenharmony_ci 57695b41eeSopenharmony_ci // On Windows, the errno for a file in a nonexistent directory 58695b41eeSopenharmony_ci // is different. 59695b41eeSopenharmony_ci EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err)); 60695b41eeSopenharmony_ci EXPECT_EQ("", err); 61695b41eeSopenharmony_ci 62695b41eeSopenharmony_ci // On POSIX systems, the errno is different if a component of the 63695b41eeSopenharmony_ci // path prefix is not a directory. 64695b41eeSopenharmony_ci ASSERT_TRUE(Touch("notadir")); 65695b41eeSopenharmony_ci EXPECT_EQ(0, disk_.Stat("notadir/nosuchfile", &err)); 66695b41eeSopenharmony_ci EXPECT_EQ("", err); 67695b41eeSopenharmony_ci} 68695b41eeSopenharmony_ci 69695b41eeSopenharmony_ciTEST_F(DiskInterfaceTest, StatMissingFileWithCache) { 70695b41eeSopenharmony_ci disk_.AllowStatCache(true); 71695b41eeSopenharmony_ci string err; 72695b41eeSopenharmony_ci 73695b41eeSopenharmony_ci // On Windows, the errno for FindFirstFileExA, which is used when the stat 74695b41eeSopenharmony_ci // cache is enabled, is different when the directory name is not a directory. 75695b41eeSopenharmony_ci ASSERT_TRUE(Touch("notadir")); 76695b41eeSopenharmony_ci EXPECT_EQ(0, disk_.Stat("notadir/nosuchfile", &err)); 77695b41eeSopenharmony_ci EXPECT_EQ("", err); 78695b41eeSopenharmony_ci} 79695b41eeSopenharmony_ci 80695b41eeSopenharmony_ciTEST_F(DiskInterfaceTest, StatBadPath) { 81695b41eeSopenharmony_ci string err; 82695b41eeSopenharmony_ci#ifdef _WIN32 83695b41eeSopenharmony_ci string bad_path("cc:\\foo"); 84695b41eeSopenharmony_ci EXPECT_EQ(-1, disk_.Stat(bad_path, &err)); 85695b41eeSopenharmony_ci EXPECT_NE("", err); 86695b41eeSopenharmony_ci#else 87695b41eeSopenharmony_ci string too_long_name(512, 'x'); 88695b41eeSopenharmony_ci EXPECT_EQ(-1, disk_.Stat(too_long_name, &err)); 89695b41eeSopenharmony_ci EXPECT_NE("", err); 90695b41eeSopenharmony_ci#endif 91695b41eeSopenharmony_ci} 92695b41eeSopenharmony_ci 93695b41eeSopenharmony_ciTEST_F(DiskInterfaceTest, StatExistingFile) { 94695b41eeSopenharmony_ci string err; 95695b41eeSopenharmony_ci ASSERT_TRUE(Touch("file")); 96695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat("file", &err), 1); 97695b41eeSopenharmony_ci EXPECT_EQ("", err); 98695b41eeSopenharmony_ci} 99695b41eeSopenharmony_ci 100695b41eeSopenharmony_ci#ifdef _WIN32 101695b41eeSopenharmony_ciTEST_F(DiskInterfaceTest, StatExistingFileWithLongPath) { 102695b41eeSopenharmony_ci string err; 103695b41eeSopenharmony_ci char currentdir[32767]; 104695b41eeSopenharmony_ci _getcwd(currentdir, sizeof(currentdir)); 105695b41eeSopenharmony_ci const string filename = string(currentdir) + 106695b41eeSopenharmony_ci"\\filename_with_256_characters_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ 107695b41eeSopenharmony_cixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ 108695b41eeSopenharmony_cixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ 109695b41eeSopenharmony_cixxxxxxxxxxxxxxxxxxxxx"; 110695b41eeSopenharmony_ci const string prefixed = "\\\\?\\" + filename; 111695b41eeSopenharmony_ci ASSERT_TRUE(Touch(prefixed.c_str())); 112695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat(disk_.AreLongPathsEnabled() ? 113695b41eeSopenharmony_ci filename : prefixed, &err), 1); 114695b41eeSopenharmony_ci EXPECT_EQ("", err); 115695b41eeSopenharmony_ci} 116695b41eeSopenharmony_ci#endif 117695b41eeSopenharmony_ci 118695b41eeSopenharmony_ciTEST_F(DiskInterfaceTest, StatExistingDir) { 119695b41eeSopenharmony_ci string err; 120695b41eeSopenharmony_ci ASSERT_TRUE(disk_.MakeDir("subdir")); 121695b41eeSopenharmony_ci ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir")); 122695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat("..", &err), 1); 123695b41eeSopenharmony_ci EXPECT_EQ("", err); 124695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat(".", &err), 1); 125695b41eeSopenharmony_ci EXPECT_EQ("", err); 126695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat("subdir", &err), 1); 127695b41eeSopenharmony_ci EXPECT_EQ("", err); 128695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1); 129695b41eeSopenharmony_ci EXPECT_EQ("", err); 130695b41eeSopenharmony_ci 131695b41eeSopenharmony_ci EXPECT_EQ(disk_.Stat("subdir", &err), 132695b41eeSopenharmony_ci disk_.Stat("subdir/.", &err)); 133695b41eeSopenharmony_ci EXPECT_EQ(disk_.Stat("subdir", &err), 134695b41eeSopenharmony_ci disk_.Stat("subdir/subsubdir/..", &err)); 135695b41eeSopenharmony_ci EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err), 136695b41eeSopenharmony_ci disk_.Stat("subdir/subsubdir/.", &err)); 137695b41eeSopenharmony_ci} 138695b41eeSopenharmony_ci 139695b41eeSopenharmony_ci#ifdef _WIN32 140695b41eeSopenharmony_ciTEST_F(DiskInterfaceTest, StatCache) { 141695b41eeSopenharmony_ci string err; 142695b41eeSopenharmony_ci 143695b41eeSopenharmony_ci ASSERT_TRUE(Touch("file1")); 144695b41eeSopenharmony_ci ASSERT_TRUE(Touch("fiLE2")); 145695b41eeSopenharmony_ci ASSERT_TRUE(disk_.MakeDir("subdir")); 146695b41eeSopenharmony_ci ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir")); 147695b41eeSopenharmony_ci ASSERT_TRUE(Touch("subdir\\subfile1")); 148695b41eeSopenharmony_ci ASSERT_TRUE(Touch("subdir\\SUBFILE2")); 149695b41eeSopenharmony_ci ASSERT_TRUE(Touch("subdir\\SUBFILE3")); 150695b41eeSopenharmony_ci 151695b41eeSopenharmony_ci disk_.AllowStatCache(false); 152695b41eeSopenharmony_ci TimeStamp parent_stat_uncached = disk_.Stat("..", &err); 153695b41eeSopenharmony_ci disk_.AllowStatCache(true); 154695b41eeSopenharmony_ci 155695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat("FIle1", &err), 1); 156695b41eeSopenharmony_ci EXPECT_EQ("", err); 157695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat("file1", &err), 1); 158695b41eeSopenharmony_ci EXPECT_EQ("", err); 159695b41eeSopenharmony_ci 160695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat("subdir/subfile2", &err), 1); 161695b41eeSopenharmony_ci EXPECT_EQ("", err); 162695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat("sUbdir\\suBFile1", &err), 1); 163695b41eeSopenharmony_ci EXPECT_EQ("", err); 164695b41eeSopenharmony_ci 165695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat("..", &err), 1); 166695b41eeSopenharmony_ci EXPECT_EQ("", err); 167695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat(".", &err), 1); 168695b41eeSopenharmony_ci EXPECT_EQ("", err); 169695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat("subdir", &err), 1); 170695b41eeSopenharmony_ci EXPECT_EQ("", err); 171695b41eeSopenharmony_ci EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1); 172695b41eeSopenharmony_ci EXPECT_EQ("", err); 173695b41eeSopenharmony_ci 174695b41eeSopenharmony_ci#ifndef _MSC_VER // TODO: Investigate why. Also see https://github.com/ninja-build/ninja/pull/1423 175695b41eeSopenharmony_ci EXPECT_EQ(disk_.Stat("subdir", &err), 176695b41eeSopenharmony_ci disk_.Stat("subdir/.", &err)); 177695b41eeSopenharmony_ci EXPECT_EQ("", err); 178695b41eeSopenharmony_ci EXPECT_EQ(disk_.Stat("subdir", &err), 179695b41eeSopenharmony_ci disk_.Stat("subdir/subsubdir/..", &err)); 180695b41eeSopenharmony_ci#endif 181695b41eeSopenharmony_ci EXPECT_EQ("", err); 182695b41eeSopenharmony_ci EXPECT_EQ(disk_.Stat("..", &err), parent_stat_uncached); 183695b41eeSopenharmony_ci EXPECT_EQ("", err); 184695b41eeSopenharmony_ci EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err), 185695b41eeSopenharmony_ci disk_.Stat("subdir/subsubdir/.", &err)); 186695b41eeSopenharmony_ci EXPECT_EQ("", err); 187695b41eeSopenharmony_ci 188695b41eeSopenharmony_ci // Test error cases. 189695b41eeSopenharmony_ci string bad_path("cc:\\foo"); 190695b41eeSopenharmony_ci EXPECT_EQ(-1, disk_.Stat(bad_path, &err)); 191695b41eeSopenharmony_ci EXPECT_NE("", err); err.clear(); 192695b41eeSopenharmony_ci EXPECT_EQ(-1, disk_.Stat(bad_path, &err)); 193695b41eeSopenharmony_ci EXPECT_NE("", err); err.clear(); 194695b41eeSopenharmony_ci EXPECT_EQ(0, disk_.Stat("nosuchfile", &err)); 195695b41eeSopenharmony_ci EXPECT_EQ("", err); 196695b41eeSopenharmony_ci EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err)); 197695b41eeSopenharmony_ci EXPECT_EQ("", err); 198695b41eeSopenharmony_ci} 199695b41eeSopenharmony_ci#endif 200695b41eeSopenharmony_ci 201695b41eeSopenharmony_ciTEST_F(DiskInterfaceTest, ReadFile) { 202695b41eeSopenharmony_ci string err; 203695b41eeSopenharmony_ci std::string content; 204695b41eeSopenharmony_ci ASSERT_EQ(DiskInterface::NotFound, 205695b41eeSopenharmony_ci disk_.ReadFile("foobar", &content, &err)); 206695b41eeSopenharmony_ci EXPECT_EQ("", content); 207695b41eeSopenharmony_ci EXPECT_NE("", err); // actual value is platform-specific 208695b41eeSopenharmony_ci err.clear(); 209695b41eeSopenharmony_ci 210695b41eeSopenharmony_ci const char* kTestFile = "testfile"; 211695b41eeSopenharmony_ci FILE* f = fopen(kTestFile, "wb"); 212695b41eeSopenharmony_ci ASSERT_TRUE(f); 213695b41eeSopenharmony_ci const char* kTestContent = "test content\nok"; 214695b41eeSopenharmony_ci fprintf(f, "%s", kTestContent); 215695b41eeSopenharmony_ci ASSERT_EQ(0, fclose(f)); 216695b41eeSopenharmony_ci 217695b41eeSopenharmony_ci ASSERT_EQ(DiskInterface::Okay, 218695b41eeSopenharmony_ci disk_.ReadFile(kTestFile, &content, &err)); 219695b41eeSopenharmony_ci EXPECT_EQ(kTestContent, content); 220695b41eeSopenharmony_ci EXPECT_EQ("", err); 221695b41eeSopenharmony_ci} 222695b41eeSopenharmony_ci 223695b41eeSopenharmony_ciTEST_F(DiskInterfaceTest, MakeDirs) { 224695b41eeSopenharmony_ci string path = "path/with/double//slash/"; 225695b41eeSopenharmony_ci EXPECT_TRUE(disk_.MakeDirs(path)); 226695b41eeSopenharmony_ci FILE* f = fopen((path + "a_file").c_str(), "w"); 227695b41eeSopenharmony_ci EXPECT_TRUE(f); 228695b41eeSopenharmony_ci EXPECT_EQ(0, fclose(f)); 229695b41eeSopenharmony_ci#ifdef _WIN32 230695b41eeSopenharmony_ci string path2 = "another\\with\\back\\\\slashes\\"; 231695b41eeSopenharmony_ci EXPECT_TRUE(disk_.MakeDirs(path2)); 232695b41eeSopenharmony_ci FILE* f2 = fopen((path2 + "a_file").c_str(), "w"); 233695b41eeSopenharmony_ci EXPECT_TRUE(f2); 234695b41eeSopenharmony_ci EXPECT_EQ(0, fclose(f2)); 235695b41eeSopenharmony_ci#endif 236695b41eeSopenharmony_ci} 237695b41eeSopenharmony_ci 238695b41eeSopenharmony_ciTEST_F(DiskInterfaceTest, RemoveFile) { 239695b41eeSopenharmony_ci const char* kFileName = "file-to-remove"; 240695b41eeSopenharmony_ci ASSERT_TRUE(Touch(kFileName)); 241695b41eeSopenharmony_ci EXPECT_EQ(0, disk_.RemoveFile(kFileName)); 242695b41eeSopenharmony_ci EXPECT_EQ(1, disk_.RemoveFile(kFileName)); 243695b41eeSopenharmony_ci EXPECT_EQ(1, disk_.RemoveFile("does not exist")); 244695b41eeSopenharmony_ci#ifdef _WIN32 245695b41eeSopenharmony_ci ASSERT_TRUE(Touch(kFileName)); 246695b41eeSopenharmony_ci EXPECT_EQ(0, system((std::string("attrib +R ") + kFileName).c_str())); 247695b41eeSopenharmony_ci EXPECT_EQ(0, disk_.RemoveFile(kFileName)); 248695b41eeSopenharmony_ci EXPECT_EQ(1, disk_.RemoveFile(kFileName)); 249695b41eeSopenharmony_ci#endif 250695b41eeSopenharmony_ci} 251695b41eeSopenharmony_ci 252695b41eeSopenharmony_ciTEST_F(DiskInterfaceTest, RemoveDirectory) { 253695b41eeSopenharmony_ci const char* kDirectoryName = "directory-to-remove"; 254695b41eeSopenharmony_ci EXPECT_TRUE(disk_.MakeDir(kDirectoryName)); 255695b41eeSopenharmony_ci EXPECT_EQ(0, disk_.RemoveFile(kDirectoryName)); 256695b41eeSopenharmony_ci EXPECT_EQ(1, disk_.RemoveFile(kDirectoryName)); 257695b41eeSopenharmony_ci EXPECT_EQ(1, disk_.RemoveFile("does not exist")); 258695b41eeSopenharmony_ci} 259695b41eeSopenharmony_ci 260695b41eeSopenharmony_cistruct StatTest : public StateTestWithBuiltinRules, 261695b41eeSopenharmony_ci public DiskInterface { 262695b41eeSopenharmony_ci StatTest() : scan_(&state_, NULL, NULL, this, NULL) {} 263695b41eeSopenharmony_ci 264695b41eeSopenharmony_ci // DiskInterface implementation. 265695b41eeSopenharmony_ci virtual TimeStamp Stat(const string& path, string* err) const; 266695b41eeSopenharmony_ci virtual bool WriteFile(const string& path, const string& contents) { 267695b41eeSopenharmony_ci assert(false); 268695b41eeSopenharmony_ci return true; 269695b41eeSopenharmony_ci } 270695b41eeSopenharmony_ci virtual bool MakeDir(const string& path) { 271695b41eeSopenharmony_ci assert(false); 272695b41eeSopenharmony_ci return false; 273695b41eeSopenharmony_ci } 274695b41eeSopenharmony_ci virtual Status ReadFile(const string& path, string* contents, string* err) { 275695b41eeSopenharmony_ci assert(false); 276695b41eeSopenharmony_ci return NotFound; 277695b41eeSopenharmony_ci } 278695b41eeSopenharmony_ci virtual int RemoveFile(const string& path) { 279695b41eeSopenharmony_ci assert(false); 280695b41eeSopenharmony_ci return 0; 281695b41eeSopenharmony_ci } 282695b41eeSopenharmony_ci 283695b41eeSopenharmony_ci DependencyScan scan_; 284695b41eeSopenharmony_ci map<string, TimeStamp> mtimes_; 285695b41eeSopenharmony_ci mutable vector<string> stats_; 286695b41eeSopenharmony_ci}; 287695b41eeSopenharmony_ci 288695b41eeSopenharmony_ciTimeStamp StatTest::Stat(const string& path, string* err) const { 289695b41eeSopenharmony_ci stats_.push_back(path); 290695b41eeSopenharmony_ci map<string, TimeStamp>::const_iterator i = mtimes_.find(path); 291695b41eeSopenharmony_ci if (i == mtimes_.end()) 292695b41eeSopenharmony_ci return 0; // File not found. 293695b41eeSopenharmony_ci return i->second; 294695b41eeSopenharmony_ci} 295695b41eeSopenharmony_ci 296695b41eeSopenharmony_ciTEST_F(StatTest, Simple) { 297695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 298695b41eeSopenharmony_ci"build out: cat in\n")); 299695b41eeSopenharmony_ci 300695b41eeSopenharmony_ci Node* out = GetNode("out"); 301695b41eeSopenharmony_ci string err; 302695b41eeSopenharmony_ci EXPECT_TRUE(out->Stat(this, &err)); 303695b41eeSopenharmony_ci EXPECT_EQ("", err); 304695b41eeSopenharmony_ci ASSERT_EQ(1u, stats_.size()); 305695b41eeSopenharmony_ci scan_.RecomputeDirty(out, NULL, NULL); 306695b41eeSopenharmony_ci ASSERT_EQ(2u, stats_.size()); 307695b41eeSopenharmony_ci ASSERT_EQ("out", stats_[0]); 308695b41eeSopenharmony_ci ASSERT_EQ("in", stats_[1]); 309695b41eeSopenharmony_ci} 310695b41eeSopenharmony_ci 311695b41eeSopenharmony_ciTEST_F(StatTest, TwoStep) { 312695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 313695b41eeSopenharmony_ci"build out: cat mid\n" 314695b41eeSopenharmony_ci"build mid: cat in\n")); 315695b41eeSopenharmony_ci 316695b41eeSopenharmony_ci Node* out = GetNode("out"); 317695b41eeSopenharmony_ci string err; 318695b41eeSopenharmony_ci EXPECT_TRUE(out->Stat(this, &err)); 319695b41eeSopenharmony_ci EXPECT_EQ("", err); 320695b41eeSopenharmony_ci ASSERT_EQ(1u, stats_.size()); 321695b41eeSopenharmony_ci scan_.RecomputeDirty(out, NULL, NULL); 322695b41eeSopenharmony_ci ASSERT_EQ(3u, stats_.size()); 323695b41eeSopenharmony_ci ASSERT_EQ("out", stats_[0]); 324695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("out")->dirty()); 325695b41eeSopenharmony_ci ASSERT_EQ("mid", stats_[1]); 326695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("mid")->dirty()); 327695b41eeSopenharmony_ci ASSERT_EQ("in", stats_[2]); 328695b41eeSopenharmony_ci} 329695b41eeSopenharmony_ci 330695b41eeSopenharmony_ciTEST_F(StatTest, Tree) { 331695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 332695b41eeSopenharmony_ci"build out: cat mid1 mid2\n" 333695b41eeSopenharmony_ci"build mid1: cat in11 in12\n" 334695b41eeSopenharmony_ci"build mid2: cat in21 in22\n")); 335695b41eeSopenharmony_ci 336695b41eeSopenharmony_ci Node* out = GetNode("out"); 337695b41eeSopenharmony_ci string err; 338695b41eeSopenharmony_ci EXPECT_TRUE(out->Stat(this, &err)); 339695b41eeSopenharmony_ci EXPECT_EQ("", err); 340695b41eeSopenharmony_ci ASSERT_EQ(1u, stats_.size()); 341695b41eeSopenharmony_ci scan_.RecomputeDirty(out, NULL, NULL); 342695b41eeSopenharmony_ci ASSERT_EQ(1u + 6u, stats_.size()); 343695b41eeSopenharmony_ci ASSERT_EQ("mid1", stats_[1]); 344695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("mid1")->dirty()); 345695b41eeSopenharmony_ci ASSERT_EQ("in11", stats_[2]); 346695b41eeSopenharmony_ci} 347695b41eeSopenharmony_ci 348695b41eeSopenharmony_ciTEST_F(StatTest, Middle) { 349695b41eeSopenharmony_ci ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, 350695b41eeSopenharmony_ci"build out: cat mid\n" 351695b41eeSopenharmony_ci"build mid: cat in\n")); 352695b41eeSopenharmony_ci 353695b41eeSopenharmony_ci mtimes_["in"] = 1; 354695b41eeSopenharmony_ci mtimes_["mid"] = 0; // missing 355695b41eeSopenharmony_ci mtimes_["out"] = 1; 356695b41eeSopenharmony_ci 357695b41eeSopenharmony_ci Node* out = GetNode("out"); 358695b41eeSopenharmony_ci string err; 359695b41eeSopenharmony_ci EXPECT_TRUE(out->Stat(this, &err)); 360695b41eeSopenharmony_ci EXPECT_EQ("", err); 361695b41eeSopenharmony_ci ASSERT_EQ(1u, stats_.size()); 362695b41eeSopenharmony_ci scan_.RecomputeDirty(out, NULL, NULL); 363695b41eeSopenharmony_ci ASSERT_FALSE(GetNode("in")->dirty()); 364695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("mid")->dirty()); 365695b41eeSopenharmony_ci ASSERT_TRUE(GetNode("out")->dirty()); 366695b41eeSopenharmony_ci} 367695b41eeSopenharmony_ci 368695b41eeSopenharmony_ci} // namespace 369