directory_impl_unittest.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Copyright 2015 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. #include <stddef.h>
  5. #include <stdint.h>
  6. #include <map>
  7. #include <string>
  8. #include "base/test/task_environment.h"
  9. #include "components/services/filesystem/directory_test_helper.h"
  10. #include "components/services/filesystem/public/mojom/directory.mojom.h"
  11. #include "components/services/filesystem/public/mojom/types.mojom.h"
  12. #include "mojo/public/cpp/bindings/pending_receiver.h"
  13. #include "mojo/public/cpp/bindings/remote.h"
  14. #include "testing/gtest/include/gtest/gtest.h"
  15. namespace filesystem {
  16. namespace {
  17. class DirectoryImplTest : public testing::Test {
  18. public:
  19. DirectoryImplTest() = default;
  20. DirectoryImplTest(const DirectoryImplTest&) = delete;
  21. DirectoryImplTest& operator=(const DirectoryImplTest&) = delete;
  22. mojo::Remote<mojom::Directory> CreateTempDir() {
  23. return test_helper_.CreateTempDir();
  24. }
  25. private:
  26. base::test::TaskEnvironment task_environment_;
  27. DirectoryTestHelper test_helper_;
  28. };
  29. constexpr char kData[] = "one two three";
  30. TEST_F(DirectoryImplTest, Read) {
  31. mojo::Remote<mojom::Directory> directory = CreateTempDir();
  32. base::File::Error error;
  33. // Make some files.
  34. const struct {
  35. const char* name;
  36. uint32_t open_flags;
  37. } files_to_create[] = {
  38. {"my_file1", mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate},
  39. {"my_file2", mojom::kFlagWrite | mojom::kFlagCreate},
  40. {"my_file3", mojom::kFlagAppend | mojom::kFlagCreate}};
  41. for (size_t i = 0; i < std::size(files_to_create); i++) {
  42. error = base::File::Error::FILE_ERROR_FAILED;
  43. base::File tmp_base_file;
  44. bool handled = directory->OpenFileHandle(files_to_create[i].name,
  45. files_to_create[i].open_flags,
  46. &error, &tmp_base_file);
  47. ASSERT_TRUE(handled);
  48. EXPECT_EQ(base::File::Error::FILE_OK, error);
  49. tmp_base_file.Close();
  50. }
  51. // Make a directory.
  52. error = base::File::Error::FILE_ERROR_FAILED;
  53. bool handled = directory->OpenDirectory(
  54. "my_dir", mojo::NullReceiver(),
  55. mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
  56. ASSERT_TRUE(handled);
  57. EXPECT_EQ(base::File::Error::FILE_OK, error);
  58. error = base::File::Error::FILE_ERROR_FAILED;
  59. absl::optional<std::vector<mojom::DirectoryEntryPtr>> directory_contents;
  60. handled = directory->Read(&error, &directory_contents);
  61. ASSERT_TRUE(handled);
  62. EXPECT_EQ(base::File::Error::FILE_OK, error);
  63. ASSERT_TRUE(directory_contents.has_value());
  64. // Expected contents of the directory.
  65. std::map<std::string, mojom::FsFileType> expected_contents;
  66. expected_contents["my_file1"] = mojom::FsFileType::REGULAR_FILE;
  67. expected_contents["my_file2"] = mojom::FsFileType::REGULAR_FILE;
  68. expected_contents["my_file3"] = mojom::FsFileType::REGULAR_FILE;
  69. expected_contents["my_dir"] = mojom::FsFileType::DIRECTORY;
  70. // Note: We don't expose ".." or ".".
  71. EXPECT_EQ(expected_contents.size(), directory_contents->size());
  72. for (size_t i = 0; i < directory_contents->size(); i++) {
  73. auto& item = directory_contents.value()[i];
  74. ASSERT_TRUE(item);
  75. auto it = expected_contents.find(item->name.AsUTF8Unsafe());
  76. ASSERT_TRUE(it != expected_contents.end());
  77. EXPECT_EQ(it->second, item->type);
  78. expected_contents.erase(it);
  79. }
  80. }
  81. // TODO(vtl): Properly test OpenDirectory() (including flags).
  82. TEST_F(DirectoryImplTest, BasicRenameDelete) {
  83. mojo::Remote<mojom::Directory> directory = CreateTempDir();
  84. base::File::Error error;
  85. // Create my_file.
  86. error = base::File::Error::FILE_ERROR_FAILED;
  87. base::File tmp_base_file;
  88. bool handled = directory->OpenFileHandle(
  89. "my_file", mojom::kFlagWrite | mojom::kFlagCreate, &error,
  90. &tmp_base_file);
  91. ASSERT_TRUE(handled);
  92. EXPECT_EQ(base::File::Error::FILE_OK, error);
  93. // Opening my_file should succeed.
  94. error = base::File::Error::FILE_ERROR_FAILED;
  95. handled = directory->OpenFileHandle(
  96. "my_file", mojom::kFlagRead | mojom::kFlagOpen, &error, &tmp_base_file);
  97. ASSERT_TRUE(handled);
  98. EXPECT_EQ(base::File::Error::FILE_OK, error);
  99. tmp_base_file.Close();
  100. // Rename my_file to my_new_file.
  101. handled = directory->Rename("my_file", "my_new_file", &error);
  102. ASSERT_TRUE(handled);
  103. EXPECT_EQ(base::File::Error::FILE_OK, error);
  104. // Opening my_file should fail.
  105. error = base::File::Error::FILE_ERROR_FAILED;
  106. handled = directory->OpenFileHandle(
  107. "my_file", mojom::kFlagRead | mojom::kFlagOpen, &error, &tmp_base_file);
  108. ASSERT_TRUE(handled);
  109. EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_FOUND, error);
  110. tmp_base_file.Close();
  111. // Opening my_new_file should succeed.
  112. error = base::File::Error::FILE_ERROR_FAILED;
  113. handled = directory->OpenFileHandle("my_new_file",
  114. mojom::kFlagRead | mojom::kFlagOpen,
  115. &error, &tmp_base_file);
  116. ASSERT_TRUE(handled);
  117. EXPECT_EQ(base::File::Error::FILE_OK, error);
  118. tmp_base_file.Close();
  119. // Delete my_new_file (no flags).
  120. handled = directory->Delete("my_new_file", 0, &error);
  121. ASSERT_TRUE(handled);
  122. EXPECT_EQ(base::File::Error::FILE_OK, error);
  123. tmp_base_file.Close();
  124. // Opening my_new_file should fail.
  125. error = base::File::Error::FILE_ERROR_FAILED;
  126. handled = directory->OpenFileHandle("my_new_file",
  127. mojom::kFlagRead | mojom::kFlagOpen,
  128. &error, &tmp_base_file);
  129. ASSERT_TRUE(handled);
  130. EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_FOUND, error);
  131. tmp_base_file.Close();
  132. }
  133. TEST_F(DirectoryImplTest, CantOpenDirectoriesAsFiles) {
  134. mojo::Remote<mojom::Directory> directory = CreateTempDir();
  135. base::File::Error error;
  136. {
  137. // Create a directory called 'my_file'
  138. mojo::Remote<mojom::Directory> my_file_directory;
  139. error = base::File::Error::FILE_ERROR_FAILED;
  140. bool handled = directory->OpenDirectory(
  141. "my_file", my_file_directory.BindNewPipeAndPassReceiver(),
  142. mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
  143. ASSERT_TRUE(handled);
  144. EXPECT_EQ(base::File::Error::FILE_OK, error);
  145. }
  146. {
  147. // Attempt to open that directory as a file. This must fail!
  148. base::File tmp_file_handle;
  149. error = base::File::Error::FILE_ERROR_FAILED;
  150. bool handled = directory->OpenFileHandle(
  151. "my_file", mojom::kFlagRead | mojom::kFlagOpen, &error,
  152. &tmp_file_handle);
  153. ASSERT_TRUE(handled);
  154. EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_A_FILE, error);
  155. }
  156. }
  157. TEST_F(DirectoryImplTest, Clone) {
  158. mojo::Remote<mojom::Directory> clone_one;
  159. mojo::Remote<mojom::Directory> clone_two;
  160. base::File::Error error;
  161. {
  162. mojo::Remote<mojom::Directory> directory = CreateTempDir();
  163. directory->Clone(clone_one.BindNewPipeAndPassReceiver());
  164. directory->Clone(clone_two.BindNewPipeAndPassReceiver());
  165. // Original temporary directory goes out of scope here; shouldn't be
  166. // deleted since it has clones.
  167. }
  168. std::vector<uint8_t> data(kData, kData + strlen(kData));
  169. {
  170. bool handled = clone_one->WriteFile("data", data, &error);
  171. ASSERT_TRUE(handled);
  172. EXPECT_EQ(base::File::Error::FILE_OK, error);
  173. }
  174. {
  175. std::vector<uint8_t> file_contents;
  176. bool handled = clone_two->ReadEntireFile("data", &error, &file_contents);
  177. ASSERT_TRUE(handled);
  178. EXPECT_EQ(base::File::Error::FILE_OK, error);
  179. EXPECT_EQ(data, file_contents);
  180. }
  181. }
  182. TEST_F(DirectoryImplTest, WriteFileReadFile) {
  183. mojo::Remote<mojom::Directory> directory = CreateTempDir();
  184. base::File::Error error;
  185. std::vector<uint8_t> data(kData, kData + strlen(kData));
  186. {
  187. bool handled = directory->WriteFile("data", data, &error);
  188. ASSERT_TRUE(handled);
  189. EXPECT_EQ(base::File::Error::FILE_OK, error);
  190. }
  191. {
  192. std::vector<uint8_t> file_contents;
  193. bool handled = directory->ReadEntireFile("data", &error, &file_contents);
  194. ASSERT_TRUE(handled);
  195. EXPECT_EQ(base::File::Error::FILE_OK, error);
  196. EXPECT_EQ(data, file_contents);
  197. }
  198. }
  199. TEST_F(DirectoryImplTest, ReadEmptyFileIsNotFoundError) {
  200. mojo::Remote<mojom::Directory> directory = CreateTempDir();
  201. base::File::Error error;
  202. {
  203. std::vector<uint8_t> file_contents;
  204. bool handled =
  205. directory->ReadEntireFile("doesnt_exist", &error, &file_contents);
  206. ASSERT_TRUE(handled);
  207. EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_FOUND, error);
  208. }
  209. }
  210. TEST_F(DirectoryImplTest, CantReadEntireFileOnADirectory) {
  211. mojo::Remote<mojom::Directory> directory = CreateTempDir();
  212. base::File::Error error;
  213. // Create a directory
  214. {
  215. mojo::Remote<mojom::Directory> my_file_directory;
  216. error = base::File::Error::FILE_ERROR_FAILED;
  217. bool handled = directory->OpenDirectory(
  218. "my_dir", my_file_directory.BindNewPipeAndPassReceiver(),
  219. mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
  220. ASSERT_TRUE(handled);
  221. EXPECT_EQ(base::File::Error::FILE_OK, error);
  222. }
  223. // Try to read it as a file
  224. {
  225. std::vector<uint8_t> file_contents;
  226. bool handled = directory->ReadEntireFile("my_dir", &error, &file_contents);
  227. ASSERT_TRUE(handled);
  228. EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_A_FILE, error);
  229. }
  230. }
  231. TEST_F(DirectoryImplTest, CantWriteFileOnADirectory) {
  232. mojo::Remote<mojom::Directory> directory = CreateTempDir();
  233. base::File::Error error;
  234. // Create a directory
  235. {
  236. mojo::Remote<mojom::Directory> my_file_directory;
  237. error = base::File::Error::FILE_ERROR_FAILED;
  238. bool handled = directory->OpenDirectory(
  239. "my_dir", my_file_directory.BindNewPipeAndPassReceiver(),
  240. mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
  241. ASSERT_TRUE(handled);
  242. EXPECT_EQ(base::File::Error::FILE_OK, error);
  243. }
  244. {
  245. std::vector<uint8_t> data(kData, kData + strlen(kData));
  246. bool handled = directory->WriteFile("my_dir", data, &error);
  247. ASSERT_TRUE(handled);
  248. EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_A_FILE, error);
  249. }
  250. }
  251. TEST_F(DirectoryImplTest, Flush) {
  252. mojo::Remote<mojom::Directory> directory = CreateTempDir();
  253. base::File::Error error;
  254. {
  255. bool handled = directory->Flush(&error);
  256. ASSERT_TRUE(handled);
  257. EXPECT_EQ(base::File::Error::FILE_OK, error);
  258. }
  259. }
  260. } // namespace
  261. } // namespace filesystem