1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "base/file_util.h"
31 
32 #ifdef OS_WIN
33 #include <Windows.h>
34 #endif  // OS_WIN
35 
36 #include <fstream>
37 #include <string>
38 
39 #include "base/file_stream.h"
40 #include "base/logging.h"
41 #include "base/util.h"
42 #include "testing/base/public/gunit.h"
43 
44 // Ad-hoc workadound against macro problem on Windows.
45 // On Windows, following macros, defined when you include <Windows.h>,
46 // should be removed here because they affects the method name definition of
47 // Util class.
48 // TODO(yukawa): Use different method name if applicable.
49 #ifdef CreateDirectory
50 #undef CreateDirectory
51 #endif  // CreateDirectory
52 #ifdef RemoveDirectory
53 #undef RemoveDirectory
54 #endif  // RemoveDirectory
55 #ifdef CopyFile
56 #undef CopyFile
57 #endif  // CopyFile
58 
59 DECLARE_string(test_srcdir);
60 DECLARE_string(test_tmpdir);
61 
62 namespace mozc {
63 
64 class FileUtilTest : public testing::Test {
65 };
66 
67 namespace {
CreateTestFile(const string & filename,const string & data)68 void CreateTestFile(const string &filename, const string &data) {
69   OutputFileStream ofs(filename.c_str(), std::ios::binary | std::ios::trunc);
70   ofs << data;
71   EXPECT_TRUE(ofs.good());
72 }
73 }  // namespace
74 
TEST_F(FileUtilTest,CreateDirectory)75 TEST_F(FileUtilTest, CreateDirectory) {
76   EXPECT_TRUE(FileUtil::DirectoryExists(FLAGS_test_tmpdir));
77   // dirpath = FLAGS_test_tmpdir/testdir
78   const string dirpath = FileUtil::JoinPath(FLAGS_test_tmpdir, "testdir");
79 
80   // Delete dirpath, if it exists.
81   if (FileUtil::FileExists(dirpath)) {
82     FileUtil::RemoveDirectory(dirpath);
83   }
84   ASSERT_FALSE(FileUtil::FileExists(dirpath));
85 
86   // Create the directory.
87   EXPECT_TRUE(FileUtil::CreateDirectory(dirpath));
88   EXPECT_TRUE(FileUtil::DirectoryExists(dirpath));
89 
90   // Delete the directory.
91   ASSERT_TRUE(FileUtil::RemoveDirectory(dirpath));
92   ASSERT_FALSE(FileUtil::FileExists(dirpath));
93 }
94 
TEST_F(FileUtilTest,DirectoryExists)95 TEST_F(FileUtilTest, DirectoryExists) {
96   EXPECT_TRUE(FileUtil::DirectoryExists(FLAGS_test_tmpdir));
97   const string filepath = FileUtil::JoinPath(FLAGS_test_tmpdir, "testfile");
98 
99   // Delete filepath, if it exists.
100   if (FileUtil::FileExists(filepath)) {
101     FileUtil::Unlink(filepath);
102   }
103   ASSERT_FALSE(FileUtil::FileExists(filepath));
104 
105   // Create a file.
106   CreateTestFile(filepath, "test data");
107   EXPECT_TRUE(FileUtil::FileExists(filepath));
108   EXPECT_FALSE(FileUtil::DirectoryExists(filepath));
109 
110   // Delete the file.
111   FileUtil::Unlink(filepath);
112   ASSERT_FALSE(FileUtil::FileExists(filepath));
113 }
114 
TEST_F(FileUtilTest,Unlink)115 TEST_F(FileUtilTest, Unlink) {
116   const string filepath = FileUtil::JoinPath(FLAGS_test_tmpdir, "testfile");
117   FileUtil::Unlink(filepath);
118   EXPECT_FALSE(FileUtil::FileExists(filepath));
119 
120   CreateTestFile(filepath, "simple test");
121   EXPECT_TRUE(FileUtil::FileExists(filepath));
122   EXPECT_TRUE(FileUtil::Unlink(filepath));
123   EXPECT_FALSE(FileUtil::FileExists(filepath));
124 
125 #ifdef OS_WIN
126   const DWORD kTestAttributeList[] = {
127     FILE_ATTRIBUTE_ARCHIVE,
128     FILE_ATTRIBUTE_HIDDEN,
129     FILE_ATTRIBUTE_NORMAL,
130     FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
131     FILE_ATTRIBUTE_OFFLINE,
132     FILE_ATTRIBUTE_READONLY,
133     FILE_ATTRIBUTE_SYSTEM,
134     FILE_ATTRIBUTE_TEMPORARY,
135   };
136 
137   std::wstring wfilepath;
138   Util::UTF8ToWide(filepath, &wfilepath);
139   for (size_t i = 0; i < arraysize(kTestAttributeList); ++i) {
140     SCOPED_TRACE(Util::StringPrintf("AttributeTest %zd", i));
141     CreateTestFile(filepath, "attribute_test");
142     EXPECT_NE(FALSE,
143               ::SetFileAttributesW(wfilepath.c_str(), kTestAttributeList[i]));
144     EXPECT_TRUE(FileUtil::FileExists(filepath));
145     EXPECT_TRUE(FileUtil::Unlink(filepath));
146     EXPECT_FALSE(FileUtil::FileExists(filepath));
147   }
148 #endif  // OS_WIN
149 
150   FileUtil::Unlink(filepath);
151 }
152 
153 #ifdef OS_WIN
TEST_F(FileUtilTest,HideFile)154 TEST_F(FileUtilTest, HideFile) {
155   const string filename = FileUtil::JoinPath(FLAGS_test_tmpdir, "testfile");
156   FileUtil::Unlink(filename);
157 
158   EXPECT_FALSE(FileUtil::HideFile(filename));
159 
160   std::wstring wfilename;
161   Util::UTF8ToWide(filename.c_str(), &wfilename);
162 
163   CreateTestFile(filename, "test data");
164   EXPECT_TRUE(FileUtil::FileExists(filename));
165 
166   EXPECT_NE(FALSE,
167             ::SetFileAttributesW(wfilename.c_str(), FILE_ATTRIBUTE_NORMAL));
168   EXPECT_TRUE(FileUtil::HideFile(filename));
169   EXPECT_EQ(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM |
170             FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
171             ::GetFileAttributesW(wfilename.c_str()));
172 
173   EXPECT_NE(FALSE,
174             ::SetFileAttributesW(wfilename.c_str(), FILE_ATTRIBUTE_ARCHIVE));
175   EXPECT_TRUE(FileUtil::HideFile(filename));
176   EXPECT_EQ(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM |
177             FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_ARCHIVE,
178             ::GetFileAttributesW(wfilename.c_str()));
179 
180   EXPECT_NE(FALSE,
181             ::SetFileAttributesW(wfilename.c_str(), FILE_ATTRIBUTE_NORMAL));
182   EXPECT_TRUE(FileUtil::HideFileWithExtraAttributes(filename,
183                                                     FILE_ATTRIBUTE_TEMPORARY));
184   EXPECT_EQ(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM |
185             FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_TEMPORARY,
186             ::GetFileAttributesW(wfilename.c_str()));
187 
188   EXPECT_NE(FALSE,
189             ::SetFileAttributesW(wfilename.c_str(), FILE_ATTRIBUTE_ARCHIVE));
190   EXPECT_TRUE(FileUtil::HideFileWithExtraAttributes(filename,
191                                                     FILE_ATTRIBUTE_TEMPORARY));
192   EXPECT_EQ(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM |
193             FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_ARCHIVE |
194             FILE_ATTRIBUTE_TEMPORARY,
195             ::GetFileAttributesW(wfilename.c_str()));
196 
197   FileUtil::Unlink(filename);
198 }
199 #endif  // OS_WIN
200 
TEST_F(FileUtilTest,IsEqualFile)201 TEST_F(FileUtilTest, IsEqualFile) {
202   const string filename1 = FileUtil::JoinPath(FLAGS_test_tmpdir, "test1");
203   const string filename2 = FileUtil::JoinPath(FLAGS_test_tmpdir, "test2");
204   FileUtil::Unlink(filename1);
205   FileUtil::Unlink(filename2);
206   EXPECT_FALSE(FileUtil::IsEqualFile(filename1, filename2));
207 
208   CreateTestFile(filename1, "test data1");
209   EXPECT_FALSE(FileUtil::IsEqualFile(filename1, filename2));
210 
211   CreateTestFile(filename2, "test data1");
212   EXPECT_TRUE(FileUtil::IsEqualFile(filename1, filename2));
213 
214   CreateTestFile(filename2, "test data1 test data1");
215   EXPECT_FALSE(FileUtil::IsEqualFile(filename1, filename2));
216 
217   CreateTestFile(filename2, "test data2");
218   EXPECT_FALSE(FileUtil::IsEqualFile(filename1, filename2));
219 
220   FileUtil::Unlink(filename1);
221   FileUtil::Unlink(filename2);
222 }
223 
TEST_F(FileUtilTest,CopyFile)224 TEST_F(FileUtilTest, CopyFile) {
225   // just test rename operation works as intended
226   const string from = FileUtil::JoinPath(FLAGS_test_tmpdir, "copy_from");
227   const string to = FileUtil::JoinPath(FLAGS_test_tmpdir, "copy_to");
228   FileUtil::Unlink(from);
229   FileUtil::Unlink(to);
230 
231   CreateTestFile(from, "simple test");
232   EXPECT_TRUE(FileUtil::CopyFile(from, to));
233   EXPECT_TRUE(FileUtil::IsEqualFile(from, to));
234 
235   CreateTestFile(from, "overwrite test");
236   EXPECT_TRUE(FileUtil::CopyFile(from, to));
237   EXPECT_TRUE(FileUtil::IsEqualFile(from, to));
238 
239 #ifdef OS_WIN
240   struct TestData {
241     TestData(DWORD from, DWORD to)
242         : from_attributes(from), to_attributes(to) {}
243     const DWORD from_attributes;
244     const DWORD to_attributes;
245   };
246   const TestData kTestDataList[] = {
247     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE),
248     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_HIDDEN),
249     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL),
250     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED),
251     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_OFFLINE),
252     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_READONLY),
253     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_SYSTEM),
254     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_TEMPORARY),
255 
256     TestData(FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_NORMAL),
257     TestData(FILE_ATTRIBUTE_NORMAL,
258              FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY),
259     TestData(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
260              FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM),
261   };
262 
263   for (size_t i = 0; i < arraysize(kTestDataList); ++i) {
264     const string test_label =
265         "overwrite test with attributes " + std::to_string(i);
266     SCOPED_TRACE(test_label);
267     CreateTestFile(from, test_label);
268 
269     const TestData &kData = kTestDataList[i];
270     std::wstring wfrom, wto;
271     Util::UTF8ToWide(from.c_str(), &wfrom);
272     Util::UTF8ToWide(to.c_str(), &wto);
273     EXPECT_NE(FALSE,
274               ::SetFileAttributesW(wfrom.c_str(), kData.from_attributes));
275     EXPECT_NE(FALSE, ::SetFileAttributesW(wto.c_str(), kData.to_attributes));
276 
277     EXPECT_TRUE(FileUtil::CopyFile(from, to));
278     EXPECT_TRUE(FileUtil::IsEqualFile(from, to));
279     EXPECT_EQ(kData.from_attributes, ::GetFileAttributesW(wfrom.c_str()));
280     EXPECT_EQ(kData.from_attributes, ::GetFileAttributesW(wto.c_str()));
281 
282     EXPECT_NE(FALSE,
283               ::SetFileAttributesW(wfrom.c_str(), FILE_ATTRIBUTE_NORMAL));
284     EXPECT_NE(FALSE, ::SetFileAttributesW(wto.c_str(), FILE_ATTRIBUTE_NORMAL));
285   }
286 #endif  // OS_WIN
287 
288   FileUtil::Unlink(from);
289   FileUtil::Unlink(to);
290 }
291 
TEST_F(FileUtilTest,AtomicRename)292 TEST_F(FileUtilTest, AtomicRename) {
293   // just test rename operation works as intended
294   const string from = FileUtil::JoinPath(FLAGS_test_tmpdir,
295                                          "atomic_rename_test_from");
296   const string to = FileUtil::JoinPath(FLAGS_test_tmpdir,
297                                        "atomic_rename_test_to");
298   FileUtil::Unlink(from);
299   FileUtil::Unlink(to);
300 
301   // |from| is not found
302   EXPECT_FALSE(FileUtil::AtomicRename(from, to));
303   CreateTestFile(from, "test");
304   EXPECT_TRUE(FileUtil::AtomicRename(from, to));
305 
306   // from is deleted
307   EXPECT_FALSE(FileUtil::FileExists(from));
308   EXPECT_TRUE(FileUtil::FileExists(to));
309 
310   {
311     InputFileStream ifs(to.c_str());
312     EXPECT_TRUE(ifs.good());
313     string line;
314     getline(ifs, line);
315     EXPECT_EQ("test", line);
316   }
317 
318   EXPECT_FALSE(FileUtil::AtomicRename(from, to));
319 
320   FileUtil::Unlink(from);
321   FileUtil::Unlink(to);
322 
323   // overwrite the file
324   CreateTestFile(from, "test");
325   CreateTestFile(to, "test");
326   EXPECT_TRUE(FileUtil::AtomicRename(from, to));
327 
328 #ifdef OS_WIN
329   struct TestData {
330     TestData(DWORD from, DWORD to)
331         : from_attributes(from), to_attributes(to) {}
332     const DWORD from_attributes;
333     const DWORD to_attributes;
334   };
335   const TestData kTestDataList[] = {
336     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_ARCHIVE),
337     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_HIDDEN),
338     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL),
339     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED),
340     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_OFFLINE),
341     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_READONLY),
342     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_SYSTEM),
343     TestData(FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_TEMPORARY),
344 
345     TestData(FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_NORMAL),
346     TestData(FILE_ATTRIBUTE_NORMAL,
347              FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY),
348     TestData(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
349              FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM),
350   };
351 
352   for (size_t i = 0; i < arraysize(kTestDataList); ++i) {
353     const string test_label =
354         "overwrite file with attributes " + std::to_string(i);
355     SCOPED_TRACE(test_label);
356     CreateTestFile(from, test_label);
357 
358     const TestData &kData = kTestDataList[i];
359     std::wstring wfrom, wto;
360     Util::UTF8ToWide(from.c_str(), &wfrom);
361     Util::UTF8ToWide(to.c_str(), &wto);
362     EXPECT_NE(FALSE,
363               ::SetFileAttributesW(wfrom.c_str(), kData.from_attributes));
364     EXPECT_NE(FALSE, ::SetFileAttributesW(wto.c_str(), kData.to_attributes));
365 
366     EXPECT_TRUE(FileUtil::AtomicRename(from, to));
367     EXPECT_EQ(kData.from_attributes, ::GetFileAttributesW(wto.c_str()));
368     EXPECT_FALSE(FileUtil::FileExists(from));
369     EXPECT_TRUE(FileUtil::FileExists(to));
370 
371     ::SetFileAttributesW(wfrom.c_str(), FILE_ATTRIBUTE_NORMAL);
372     ::SetFileAttributesW(wto.c_str(), FILE_ATTRIBUTE_NORMAL);
373   }
374 #endif  // OS_WIN
375 
376   FileUtil::Unlink(from);
377   FileUtil::Unlink(to);
378 }
379 
380 #ifdef OS_WIN
381 #define SP "\\"
382 #else
383 #define SP "/"
384 #endif  // OS_WIN
385 
TEST_F(FileUtilTest,JoinPath)386 TEST_F(FileUtilTest, JoinPath) {
387   EXPECT_TRUE(FileUtil::JoinPath({}).empty());
388   EXPECT_EQ("foo", FileUtil::JoinPath({"foo"}));
389   EXPECT_EQ("foo" SP "bar", FileUtil::JoinPath({"foo", "bar"}));
390   EXPECT_EQ("foo" SP "bar" SP "baz", FileUtil::JoinPath({"foo", "bar", "baz"}));
391 
392   // Some path components end with delimiter.
393   EXPECT_EQ("foo" SP "bar" SP "baz",
394             FileUtil::JoinPath({"foo" SP, "bar", "baz"}));
395   EXPECT_EQ("foo" SP "bar" SP "baz",
396             FileUtil::JoinPath({"foo", "bar" SP, "baz"}));
397   EXPECT_EQ("foo" SP "bar" SP "baz" SP,
398             FileUtil::JoinPath({"foo", "bar", "baz" SP}));
399 
400   // Containing empty strings.
401   EXPECT_TRUE(FileUtil::JoinPath({"", "", ""}).empty());
402   EXPECT_EQ("foo" SP "bar", FileUtil::JoinPath({"", "foo", "bar"}));
403   EXPECT_EQ("foo" SP "bar", FileUtil::JoinPath({"foo", "", "bar"}));
404   EXPECT_EQ("foo" SP "bar", FileUtil::JoinPath({"foo", "bar", ""}));
405 }
406 
TEST_F(FileUtilTest,Dirname)407 TEST_F(FileUtilTest, Dirname) {
408   EXPECT_EQ(SP "foo", FileUtil::Dirname(SP "foo" SP "bar"));
409   EXPECT_EQ(SP "foo" SP "bar",
410             FileUtil::Dirname(SP "foo" SP "bar" SP "foo.txt"));
411   EXPECT_EQ("", FileUtil::Dirname("foo.txt"));
412   EXPECT_EQ("", FileUtil::Dirname(SP));
413 }
414 
TEST_F(FileUtilTest,Basename)415 TEST_F(FileUtilTest, Basename) {
416   EXPECT_EQ("bar", FileUtil::Basename(SP "foo" SP "bar"));
417   EXPECT_EQ("foo.txt", FileUtil::Basename(SP "foo" SP "bar" SP "foo.txt"));
418   EXPECT_EQ("foo.txt", FileUtil::Basename("foo.txt"));
419   EXPECT_EQ("foo.txt", FileUtil::Basename("." SP "foo.txt"));
420   EXPECT_EQ(".foo.txt", FileUtil::Basename("." SP ".foo.txt"));
421   EXPECT_EQ("", FileUtil::Basename(SP));
422   EXPECT_EQ("", FileUtil::Basename("foo" SP "bar" SP "buz" SP));
423 }
424 
425 #undef SP
426 
TEST_F(FileUtilTest,NormalizeDirectorySeparator)427 TEST_F(FileUtilTest, NormalizeDirectorySeparator) {
428 #ifdef OS_WIN
429   EXPECT_EQ("\\foo\\bar", FileUtil::NormalizeDirectorySeparator("\\foo\\bar"));
430   EXPECT_EQ("\\foo\\bar", FileUtil::NormalizeDirectorySeparator("/foo\\bar"));
431   EXPECT_EQ("\\foo\\bar", FileUtil::NormalizeDirectorySeparator("\\foo/bar"));
432   EXPECT_EQ("\\foo\\bar", FileUtil::NormalizeDirectorySeparator("/foo/bar"));
433   EXPECT_EQ("\\foo\\bar\\",
434             FileUtil::NormalizeDirectorySeparator("\\foo\\bar\\"));
435   EXPECT_EQ("\\foo\\bar\\", FileUtil::NormalizeDirectorySeparator("/foo/bar/"));
436   EXPECT_EQ("", FileUtil::NormalizeDirectorySeparator(""));
437   EXPECT_EQ("\\", FileUtil::NormalizeDirectorySeparator("/"));
438   EXPECT_EQ("\\", FileUtil::NormalizeDirectorySeparator("\\"));
439 #else
440   EXPECT_EQ("\\foo\\bar", FileUtil::NormalizeDirectorySeparator("\\foo\\bar"));
441   EXPECT_EQ("/foo\\bar", FileUtil::NormalizeDirectorySeparator("/foo\\bar"));
442   EXPECT_EQ("\\foo/bar", FileUtil::NormalizeDirectorySeparator("\\foo/bar"));
443   EXPECT_EQ("/foo/bar", FileUtil::NormalizeDirectorySeparator("/foo/bar"));
444   EXPECT_EQ("\\foo\\bar\\",
445             FileUtil::NormalizeDirectorySeparator("\\foo\\bar\\"));
446   EXPECT_EQ("/foo/bar/", FileUtil::NormalizeDirectorySeparator("/foo/bar/"));
447   EXPECT_EQ("", FileUtil::NormalizeDirectorySeparator(""));
448   EXPECT_EQ("/", FileUtil::NormalizeDirectorySeparator("/"));
449   EXPECT_EQ("\\", FileUtil::NormalizeDirectorySeparator("\\"));
450 #endif  // OS_WIN
451 }
452 
TEST_F(FileUtilTest,GetModificationTime)453 TEST_F(FileUtilTest, GetModificationTime) {
454   FileTimeStamp time_stamp = 0;
455   EXPECT_FALSE(FileUtil::GetModificationTime("not_existent_file", &time_stamp));
456 
457   const string &path = FileUtil::JoinPath(FLAGS_test_tmpdir, "testfile");
458   CreateTestFile(path, "content");
459   EXPECT_TRUE(FileUtil::GetModificationTime(path, &time_stamp));
460   EXPECT_NE(0, time_stamp);
461 
462   FileTimeStamp time_stamp2 = 0;
463   EXPECT_TRUE(FileUtil::GetModificationTime(path, &time_stamp2));
464   EXPECT_EQ(time_stamp, time_stamp2);
465 
466   // Cleanup
467   FileUtil::Unlink(path);
468 }
469 
470 }  // namespace mozc
471