1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stdint.h>
6
7 #include "base/files/file.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "gn/functions.h"
11 #include "gn/scheduler.h"
12 #include "gn/test_with_scheduler.h"
13 #include "gn/test_with_scope.h"
14 #include "util/test/test.h"
15
16 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_HAIKU) || defined(OS_MSYS)
17 #include <sys/time.h>
18 #elif defined(OS_ZOS)
19 #include <utime.h>
20 #endif
21
22 #if defined(OS_WIN)
23 #include <windows.h>
24 #endif
25
26 namespace {
27
28 // Returns true on success, false if write_file signaled an error.
CallWriteFile(Scope * scope,const std::string & filename,const Value & data)29 bool CallWriteFile(Scope* scope,
30 const std::string& filename,
31 const Value& data) {
32 Err err;
33
34 std::vector<Value> args;
35 args.push_back(Value(nullptr, filename));
36 args.push_back(data);
37
38 FunctionCallNode function_call;
39 Value result = functions::RunWriteFile(scope, &function_call, args, &err);
40 EXPECT_EQ(Value::NONE, result.type()); // Should always return none.
41
42 return !err.has_error();
43 }
44
45 } // namespace
46
47 using WriteFileTest = TestWithScheduler;
48
TEST_F(WriteFileTest,WithData)49 TEST_F(WriteFileTest, WithData) {
50 TestWithScope setup;
51
52 // Make a real directory for writing the files.
53 base::ScopedTempDir temp_dir;
54 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
55 setup.build_settings()->SetRootPath(temp_dir.GetPath());
56 setup.build_settings()->SetBuildDir(SourceDir("//out/"));
57
58 Value some_string(nullptr, "some string contents");
59
60 // Should refuse to write files outside of the output dir.
61 EXPECT_FALSE(CallWriteFile(setup.scope(), "//in_root.txt", some_string));
62 EXPECT_FALSE(
63 CallWriteFile(setup.scope(), "//other_dir/foo.txt", some_string));
64
65 // Should be able to write to a new dir inside the out dir.
66 EXPECT_TRUE(CallWriteFile(setup.scope(), "//out/foo.txt", some_string));
67 base::FilePath foo_name = temp_dir.GetPath()
68 .Append(FILE_PATH_LITERAL("out"))
69 .Append(FILE_PATH_LITERAL("foo.txt"));
70 std::string result_contents;
71 EXPECT_TRUE(base::ReadFileToString(foo_name, &result_contents));
72 EXPECT_EQ(some_string.string_value(), result_contents);
73
74 // Update the contents with a list of a string and a number.
75 Value some_list(nullptr, Value::LIST);
76 some_list.list_value().push_back(Value(nullptr, "line 1"));
77 some_list.list_value().push_back(Value(nullptr, static_cast<int64_t>(2)));
78 EXPECT_TRUE(CallWriteFile(setup.scope(), "//out/foo.txt", some_list));
79 EXPECT_TRUE(base::ReadFileToString(foo_name, &result_contents));
80 EXPECT_EQ("line 1\n2\n", result_contents);
81
82 // Test that the file is not rewritten if the contents are not changed.
83 base::File foo_file(foo_name, base::File::FLAG_OPEN | base::File::FLAG_READ |
84 base::File::FLAG_WRITE);
85 ASSERT_TRUE(foo_file.IsValid());
86
87 // Start by setting the modified time to something old to avoid clock
88 // resolution issues.
89 #if defined(OS_WIN)
90 FILETIME last_access_filetime = {};
91 FILETIME last_modified_filetime = {};
92 ASSERT_TRUE(::SetFileTime(foo_file.GetPlatformFile(), nullptr,
93 &last_access_filetime, &last_modified_filetime));
94 #elif defined(OS_AIX) || defined(OS_HAIKU) || defined(OS_SOLARIS)
95 struct timeval times[2] = {};
96 ASSERT_EQ(utimes(foo_name.value().c_str(), times), 0);
97 #elif defined(OS_ZOS)
98 ASSERT_EQ(utime(foo_name.value().c_str(), NULL), 0);
99 #else
100 struct timeval times[2] = {};
101 ASSERT_EQ(futimes(foo_file.GetPlatformFile(), times), 0);
102 #endif
103
104 // Read the current time to avoid timer resolution issues when comparing
105 // below.
106 base::File::Info original_info;
107 foo_file.GetInfo(&original_info);
108
109 EXPECT_TRUE(CallWriteFile(setup.scope(), "//out/foo.txt", some_list));
110
111 // Verify that the last modified time is the same as before.
112 base::File::Info new_info;
113 foo_file.GetInfo(&new_info);
114 EXPECT_EQ(original_info.last_modified, new_info.last_modified);
115 }
116