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