| // Copyright 2015 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include <map> | 
 | #include <string> | 
 |  | 
 | #include "services/files/files_test_base.h" | 
 |  | 
 | namespace mojo { | 
 | namespace files { | 
 | namespace { | 
 |  | 
 | using DirectoryImplTest = FilesTestBase; | 
 |  | 
 | TEST_F(DirectoryImplTest, Read) { | 
 |   SynchronousInterfacePtr<Directory> directory; | 
 |   GetTemporaryRoot(&directory); | 
 |   Error error; | 
 |  | 
 |   // Make some files. | 
 |   const struct { | 
 |     const char* name; | 
 |     uint32_t open_flags; | 
 |   } files_to_create[] = { | 
 |       {"my_file1", kOpenFlagRead | kOpenFlagWrite | kOpenFlagCreate}, | 
 |       {"my_file2", kOpenFlagWrite | kOpenFlagCreate | kOpenFlagExclusive}, | 
 |       {"my_file3", kOpenFlagWrite | kOpenFlagCreate | kOpenFlagAppend}, | 
 |       {"my_file4", kOpenFlagWrite | kOpenFlagCreate | kOpenFlagTruncate}}; | 
 |   for (size_t i = 0; i < arraysize(files_to_create); i++) { | 
 |     error = Error::INTERNAL; | 
 |     ASSERT_TRUE(directory->OpenFile(files_to_create[i].name, nullptr, | 
 |                                     files_to_create[i].open_flags, &error)); | 
 |     EXPECT_EQ(Error::OK, error); | 
 |   } | 
 |   // Make a directory. | 
 |   error = Error::INTERNAL; | 
 |   ASSERT_TRUE(directory->OpenDirectory( | 
 |       "my_dir", nullptr, kOpenFlagRead | kOpenFlagWrite | kOpenFlagCreate, | 
 |       &error)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |  | 
 |   error = Error::INTERNAL; | 
 |   Array<DirectoryEntryPtr> directory_contents; | 
 |   ASSERT_TRUE(directory->Read(&error, &directory_contents)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |  | 
 |   // Expected contents of the directory. | 
 |   std::map<std::string, FileType> expected_contents; | 
 |   expected_contents["my_file1"] = FileType::REGULAR_FILE; | 
 |   expected_contents["my_file2"] = FileType::REGULAR_FILE; | 
 |   expected_contents["my_file3"] = FileType::REGULAR_FILE; | 
 |   expected_contents["my_file4"] = FileType::REGULAR_FILE; | 
 |   expected_contents["my_dir"] = FileType::DIRECTORY; | 
 |   expected_contents["."] = FileType::DIRECTORY; | 
 |   expected_contents[".."] = FileType::DIRECTORY; | 
 |  | 
 |   EXPECT_EQ(expected_contents.size(), directory_contents.size()); | 
 |   for (size_t i = 0; i < directory_contents.size(); i++) { | 
 |     ASSERT_TRUE(directory_contents[i]); | 
 |     ASSERT_TRUE(directory_contents[i]->name); | 
 |     auto it = expected_contents.find(directory_contents[i]->name.get()); | 
 |     ASSERT_TRUE(it != expected_contents.end()); | 
 |     EXPECT_EQ(it->second, directory_contents[i]->type); | 
 |     expected_contents.erase(it); | 
 |   } | 
 | } | 
 |  | 
 | // Note: Ignore nanoseconds, since it may not always be supported. We expect at | 
 | // least second-resolution support though. | 
 | // TODO(vtl): Maybe share this with |FileImplTest.StatTouch| ... but then it'd | 
 | // be harder to split this file. | 
 | TEST_F(DirectoryImplTest, StatTouch) { | 
 |   SynchronousInterfacePtr<Directory> directory; | 
 |   GetTemporaryRoot(&directory); | 
 |   Error error; | 
 |  | 
 |   // Stat it. | 
 |   error = Error::INTERNAL; | 
 |   FileInformationPtr file_info; | 
 |   ASSERT_TRUE(directory->Stat(&error, &file_info)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |   ASSERT_FALSE(file_info.is_null()); | 
 |   EXPECT_EQ(FileType::DIRECTORY, file_info->type); | 
 |   EXPECT_EQ(0, file_info->size); | 
 |   ASSERT_FALSE(file_info->atime.is_null()); | 
 |   EXPECT_GT(file_info->atime->seconds, 0);  // Expect that it's not 1970-01-01. | 
 |   ASSERT_FALSE(file_info->mtime.is_null()); | 
 |   EXPECT_GT(file_info->mtime->seconds, 0); | 
 |   int64_t first_mtime = file_info->mtime->seconds; | 
 |  | 
 |   // Touch only the atime. | 
 |   error = Error::INTERNAL; | 
 |   TimespecOrNowPtr t(TimespecOrNow::New()); | 
 |   t->now = false; | 
 |   t->timespec = Timespec::New(); | 
 |   const int64_t kPartyTime1 = 1234567890;  // Party like it's 2009-02-13. | 
 |   t->timespec->seconds = kPartyTime1; | 
 |   ASSERT_TRUE(directory->Touch(t.Pass(), nullptr, &error)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |  | 
 |   // Stat again. | 
 |   error = Error::INTERNAL; | 
 |   file_info.reset(); | 
 |   ASSERT_TRUE(directory->Stat(&error, &file_info)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |   ASSERT_FALSE(file_info.is_null()); | 
 |   ASSERT_FALSE(file_info->atime.is_null()); | 
 |   EXPECT_EQ(kPartyTime1, file_info->atime->seconds); | 
 |   ASSERT_FALSE(file_info->mtime.is_null()); | 
 |   EXPECT_EQ(first_mtime, file_info->mtime->seconds); | 
 |  | 
 |   // Touch only the mtime. | 
 |   t = TimespecOrNow::New(); | 
 |   t->now = false; | 
 |   t->timespec = Timespec::New(); | 
 |   const int64_t kPartyTime2 = 1425059525;  // No time like the present. | 
 |   t->timespec->seconds = kPartyTime2; | 
 |   ASSERT_TRUE(directory->Touch(nullptr, t.Pass(), &error)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |  | 
 |   // Stat again. | 
 |   error = Error::INTERNAL; | 
 |   file_info.reset(); | 
 |   ASSERT_TRUE(directory->Stat(&error, &file_info)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |   ASSERT_FALSE(file_info.is_null()); | 
 |   ASSERT_FALSE(file_info->atime.is_null()); | 
 |   EXPECT_EQ(kPartyTime1, file_info->atime->seconds); | 
 |   ASSERT_FALSE(file_info->mtime.is_null()); | 
 |   EXPECT_EQ(kPartyTime2, file_info->mtime->seconds); | 
 |  | 
 |   // TODO(vtl): Also test Touch() "now" options. | 
 |   // TODO(vtl): Also test touching both atime and mtime. | 
 | } | 
 |  | 
 | // TODO(vtl): Properly test OpenFile() and OpenDirectory() (including flags). | 
 |  | 
 | TEST_F(DirectoryImplTest, BasicRenameDelete) { | 
 |   SynchronousInterfacePtr<Directory> directory; | 
 |   GetTemporaryRoot(&directory); | 
 |   Error error; | 
 |  | 
 |   // Create my_file. | 
 |   error = Error::INTERNAL; | 
 |   ASSERT_TRUE(directory->OpenFile("my_file", nullptr, | 
 |                                   kOpenFlagWrite | kOpenFlagCreate, &error)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |  | 
 |   // Opening my_file should succeed. | 
 |   error = Error::INTERNAL; | 
 |   ASSERT_TRUE(directory->OpenFile("my_file", nullptr, kOpenFlagRead, &error)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |  | 
 |   // Rename my_file to my_new_file. | 
 |   error = Error::INTERNAL; | 
 |   ASSERT_TRUE(directory->Rename("my_file", "my_new_file", &error)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |  | 
 |   // Opening my_file should fail. | 
 |   error = Error::INTERNAL; | 
 |   ASSERT_TRUE(directory->OpenFile("my_file", nullptr, kOpenFlagRead, &error)); | 
 |   EXPECT_EQ(Error::UNKNOWN, error); | 
 |  | 
 |   // Opening my_new_file should succeed. | 
 |   error = Error::INTERNAL; | 
 |   ASSERT_TRUE( | 
 |       directory->OpenFile("my_new_file", nullptr, kOpenFlagRead, &error)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |  | 
 |   // Delete my_new_file (no flags). | 
 |   error = Error::INTERNAL; | 
 |   ASSERT_TRUE(directory->Delete("my_new_file", 0u, &error)); | 
 |   EXPECT_EQ(Error::OK, error); | 
 |  | 
 |   // Opening my_new_file should fail. | 
 |   error = Error::INTERNAL; | 
 |   ASSERT_TRUE( | 
 |       directory->OpenFile("my_new_file", nullptr, kOpenFlagRead, &error)); | 
 |   EXPECT_EQ(Error::UNKNOWN, error); | 
 | } | 
 |  | 
 | // TODO(vtl): Test that an open file can be moved (by someone else) without | 
 | // operations on it being affected. | 
 | // TODO(vtl): Test delete flags. | 
 |  | 
 | // TODO(vtl): Strictly speaking, this is a test of |FilesImpl|, but we need | 
 | // |Directory| to do anything, so it lives here. | 
 | TEST_F(DirectoryImplTest, AppPersistentCache) { | 
 |   { | 
 |     SynchronousInterfacePtr<Directory> directory; | 
 |     GetAppPersistentCacheRoot(&directory); | 
 |  | 
 |     // Create my_file. | 
 |     Error error = Error::INTERNAL; | 
 |     ASSERT_TRUE(directory->OpenFile("my_file", nullptr, | 
 |                                     kOpenFlagCreate | kOpenFlagWrite, &error)); | 
 |     EXPECT_EQ(Error::OK, error); | 
 |   } | 
 |  | 
 |   { | 
 |     SynchronousInterfacePtr<Directory> directory; | 
 |     GetAppPersistentCacheRoot(&directory); | 
 |  | 
 |     // We should be in the same directory and my_file should still exist, so we | 
 |     // should be able to delete it. | 
 |     Error error = Error::INTERNAL; | 
 |     ASSERT_TRUE(directory->Delete("my_file", kDeleteFlagFileOnly, &error)); | 
 |     EXPECT_EQ(Error::OK, error); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace files | 
 | }  // namespace mojo |