filesystem_test.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "common/filesystem.h"
  5. #include <gmock/gmock.h>
  6. #include <gtest/gtest.h>
  7. #include <concepts>
  8. #include <string>
  9. #include <utility>
  10. #include "common/error_test_helpers.h"
  11. namespace Carbon::Filesystem {
  12. namespace {
  13. using ::testing::_;
  14. using ::testing::Eq;
  15. using ::testing::HasSubstr;
  16. using Testing::IsError;
  17. using Testing::IsSuccess;
  18. class FilesystemTest : public ::testing::Test {
  19. public:
  20. explicit FilesystemTest() {
  21. auto result = MakeTmpDir();
  22. CARBON_CHECK(result.ok(), "{0}", result.error());
  23. dir_ = std::move(*result);
  24. }
  25. ~FilesystemTest() override {
  26. auto result = std::move(dir_).Remove();
  27. CARBON_CHECK(result.ok(), "{0}", result.error());
  28. }
  29. auto path() const -> const std::filesystem::path& { return dir_.abs_path(); }
  30. // The test's temp directory, deleted on destruction.
  31. RemovingDir dir_;
  32. };
  33. TEST_F(FilesystemTest, CreateOpenCloseAndUnlink) {
  34. auto unlink_result = dir_.Unlink("test");
  35. ASSERT_FALSE(unlink_result.ok());
  36. EXPECT_TRUE(unlink_result.error().no_entity());
  37. #if defined(_GNU_SOURCE) && \
  38. (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32))
  39. EXPECT_THAT(unlink_result, IsError(HasSubstr("ENOENT")));
  40. #endif
  41. EXPECT_THAT(unlink_result, IsError(HasSubstr("No such file")));
  42. auto f = dir_.OpenWriteOnly("test", CreationOptions::CreateNew);
  43. ASSERT_THAT(f, IsSuccess(_));
  44. auto result = (*std::move(f)).Close();
  45. EXPECT_THAT(result, IsSuccess(_));
  46. f = dir_.OpenWriteOnly("test", CreationOptions::CreateNew);
  47. ASSERT_FALSE(f.ok());
  48. EXPECT_TRUE(f.error().already_exists());
  49. #if defined(_GNU_SOURCE) && \
  50. (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32))
  51. EXPECT_THAT(f, IsError(HasSubstr("EEXIST")));
  52. #endif
  53. EXPECT_THAT(f, IsError(HasSubstr("File exists")));
  54. f = dir_.OpenWriteOnly("test");
  55. ASSERT_THAT(f, IsSuccess(_));
  56. result = std::move(*f).Close();
  57. EXPECT_THAT(result, IsSuccess(_));
  58. f = dir_.OpenWriteOnly("test");
  59. ASSERT_THAT(f, IsSuccess(_));
  60. result = std::move(*f).Close();
  61. EXPECT_THAT(result, IsSuccess(_));
  62. unlink_result = dir_.Unlink("test");
  63. EXPECT_THAT(unlink_result, IsSuccess(_));
  64. f = dir_.OpenWriteOnly("test");
  65. EXPECT_FALSE(f.ok());
  66. EXPECT_TRUE(f.error().no_entity());
  67. #if defined(_GNU_SOURCE) && \
  68. (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 32))
  69. EXPECT_THAT(f, IsError(HasSubstr("ENOENT")));
  70. #endif
  71. EXPECT_THAT(f, IsError(HasSubstr("No such file")));
  72. f = dir_.OpenWriteOnly("test", CreationOptions::OpenAlways);
  73. ASSERT_THAT(f, IsSuccess(_));
  74. result = std::move(*f).Close();
  75. EXPECT_THAT(result, IsSuccess(_));
  76. unlink_result = dir_.Unlink("test");
  77. EXPECT_THAT(unlink_result, IsSuccess(_));
  78. }
  79. TEST_F(FilesystemTest, BasicWriteAndRead) {
  80. std::string content_str = "0123456789";
  81. {
  82. auto f = dir_.OpenWriteOnly("test", CreationOptions::CreateNew);
  83. ASSERT_THAT(f, IsSuccess(_));
  84. auto write_result = f->WriteFromString(content_str);
  85. EXPECT_THAT(write_result, IsSuccess(_));
  86. (*std::move(f)).Close().Check();
  87. }
  88. {
  89. auto f = dir_.OpenReadOnly("test");
  90. ASSERT_THAT(f, IsSuccess(_));
  91. auto read_result = f->ReadToString();
  92. EXPECT_THAT(read_result, IsSuccess(Eq(content_str)));
  93. }
  94. auto unlink_result = dir_.Unlink("test");
  95. EXPECT_THAT(unlink_result, IsSuccess(_));
  96. }
  97. TEST_F(FilesystemTest, CreateAndRemoveDirecotries) {
  98. auto d1 = Cwd().CreateDirectories(path() / "a" / "b" / "c" / "test1");
  99. ASSERT_THAT(d1, IsSuccess(_));
  100. auto d2 = Cwd().CreateDirectories(path() / "a" / "b" / "c" / "test2");
  101. ASSERT_THAT(d2, IsSuccess(_));
  102. auto d3 = Cwd().CreateDirectories(path() / "a" / "b" / "c" / "test3");
  103. ASSERT_THAT(d3, IsSuccess(_));
  104. // Get a directory object to use, this shouldn't cover much new.
  105. auto d4 = Cwd().CreateDirectories(path());
  106. EXPECT_THAT(d4, IsSuccess(_));
  107. // Single, present, relative component.
  108. auto d5 = d4->CreateDirectories("a");
  109. EXPECT_THAT(d5, IsSuccess(_));
  110. // Multiple, present, but relative components.
  111. auto d6 = d5->CreateDirectories(std::filesystem::path("b") / "c");
  112. EXPECT_THAT(d6, IsSuccess(_));
  113. // Single new component.
  114. auto d7 = d6->CreateDirectories("test4");
  115. ASSERT_THAT(d7, IsSuccess(_));
  116. // Two new relative components.
  117. auto d8 = d6->CreateDirectories(std::filesystem::path("test5") / "d");
  118. EXPECT_THAT(d8, IsSuccess(_));
  119. // Mixed relative components.
  120. auto d9 = d5->CreateDirectories(std::filesystem::path("b") / "test6");
  121. EXPECT_THAT(d9, IsSuccess(_));
  122. {
  123. auto f1 = d1->OpenWriteOnly("file1", CreateNew);
  124. ASSERT_THAT(f1, IsSuccess(_));
  125. auto f2 = d2->OpenWriteOnly("file2", CreateNew);
  126. ASSERT_THAT(f2, IsSuccess(_));
  127. auto f3 = d3->OpenWriteOnly("file3", CreateNew);
  128. ASSERT_THAT(f3, IsSuccess(_));
  129. auto f4 = d7->OpenWriteOnly("file4", CreateNew);
  130. ASSERT_THAT(f4, IsSuccess(_));
  131. (*std::move(f1)).Close().Check();
  132. (*std::move(f2)).Close().Check();
  133. (*std::move(f3)).Close().Check();
  134. (*std::move(f4)).Close().Check();
  135. }
  136. auto rm_result = Cwd().Rmtree(path() / "a");
  137. ASSERT_THAT(rm_result, IsSuccess(_));
  138. }
  139. TEST_F(FilesystemTest, StatAndAccess) {
  140. auto access_result = dir_.Access("test");
  141. ASSERT_FALSE(access_result.ok());
  142. EXPECT_TRUE(access_result.error().no_entity());
  143. // Make sure the flags and bit-or-ing them works in the boring case.
  144. access_result =
  145. dir_.Access("test", AccessCheckFlags::Read | AccessCheckFlags::Write |
  146. AccessCheckFlags::Execute);
  147. ASSERT_FALSE(access_result.ok());
  148. EXPECT_TRUE(access_result.error().no_entity());
  149. auto stat_result = dir_.Stat("test");
  150. ASSERT_FALSE(access_result.ok());
  151. EXPECT_TRUE(access_result.error().no_entity());
  152. // Create a file for testing, using very unusual and minimal permissions to
  153. // help us test. Hopefully this isn't modified on the usual `umask` tests run
  154. // under.
  155. std::string content_str = "0123456789";
  156. ModeType permissions = 0450;
  157. auto f = dir_.OpenWriteOnly("test", CreationOptions::CreateNew, permissions);
  158. ASSERT_THAT(f, IsSuccess(_));
  159. auto write_result = f->WriteFromString(content_str);
  160. EXPECT_THAT(write_result, IsSuccess(_));
  161. access_result = dir_.Access("test");
  162. EXPECT_THAT(access_result, IsSuccess(_));
  163. access_result = dir_.Access("test", AccessCheckFlags::Read);
  164. EXPECT_THAT(access_result, IsSuccess(_));
  165. // Neither write nor execute permission should be present though.
  166. access_result = dir_.Access("test", AccessCheckFlags::Write);
  167. ASSERT_FALSE(access_result.ok());
  168. EXPECT_TRUE(access_result.error().access_denied());
  169. access_result =
  170. dir_.Access("test", AccessCheckFlags::Read | AccessCheckFlags::Write |
  171. AccessCheckFlags::Execute);
  172. ASSERT_FALSE(access_result.ok());
  173. EXPECT_TRUE(access_result.error().access_denied());
  174. stat_result = dir_.Stat("test");
  175. ASSERT_THAT(stat_result, IsSuccess(_));
  176. EXPECT_TRUE(stat_result->is_file());
  177. EXPECT_FALSE(stat_result->is_dir());
  178. EXPECT_FALSE(stat_result->is_symlink());
  179. EXPECT_THAT(stat_result->size(), Eq(content_str.size()));
  180. EXPECT_THAT(stat_result->permissions(), Eq(permissions));
  181. // Directory instead of file.
  182. access_result =
  183. dir_.Access(".", AccessCheckFlags::Read | AccessCheckFlags::Write |
  184. AccessCheckFlags::Execute);
  185. EXPECT_THAT(access_result, IsSuccess(_));
  186. stat_result = dir_.Stat(".");
  187. ASSERT_THAT(stat_result, IsSuccess(_));
  188. EXPECT_FALSE(stat_result->is_file());
  189. EXPECT_TRUE(stat_result->is_dir());
  190. EXPECT_FALSE(stat_result->is_symlink());
  191. // Can remove file but still stat through the file.
  192. auto unlink_result = dir_.Unlink("test");
  193. ASSERT_THAT(unlink_result, IsSuccess(_));
  194. auto file_stat_result = f->Stat();
  195. ASSERT_THAT(file_stat_result, IsSuccess(_));
  196. EXPECT_TRUE(file_stat_result->is_file());
  197. EXPECT_FALSE(file_stat_result->is_dir());
  198. EXPECT_FALSE(file_stat_result->is_symlink());
  199. EXPECT_THAT(file_stat_result->size(), Eq(content_str.size()));
  200. EXPECT_THAT(file_stat_result->permissions(), Eq(permissions));
  201. (*std::move(f)).Close().Check();
  202. }
  203. TEST_F(FilesystemTest, Symlinks) {
  204. auto readlink_result = dir_.Readlink("test");
  205. ASSERT_FALSE(readlink_result.ok());
  206. EXPECT_TRUE(readlink_result.error().no_entity());
  207. auto lstat_result = dir_.Lstat("test");
  208. ASSERT_FALSE(lstat_result.ok());
  209. EXPECT_TRUE(lstat_result.error().no_entity());
  210. auto symlink_result = dir_.Symlink("test", "abc");
  211. EXPECT_THAT(symlink_result, IsSuccess(_));
  212. readlink_result = dir_.Readlink("test");
  213. EXPECT_THAT(readlink_result, IsSuccess(Eq("abc")));
  214. symlink_result = dir_.Symlink("test", "def");
  215. ASSERT_FALSE(symlink_result.ok());
  216. EXPECT_TRUE(symlink_result.error().already_exists());
  217. lstat_result = dir_.Lstat("test");
  218. ASSERT_THAT(lstat_result, IsSuccess(_));
  219. EXPECT_FALSE(lstat_result->is_file());
  220. EXPECT_FALSE(lstat_result->is_dir());
  221. EXPECT_TRUE(lstat_result->is_symlink());
  222. EXPECT_THAT(lstat_result->size(), Eq(strlen("abc")));
  223. auto unlink_result = dir_.Unlink("test");
  224. EXPECT_THAT(unlink_result, IsSuccess(_));
  225. readlink_result = dir_.Readlink("test");
  226. ASSERT_FALSE(readlink_result.ok());
  227. EXPECT_TRUE(readlink_result.error().no_entity());
  228. // Try a symlink with null bytes for fun. This demonstrates that the symlink
  229. // syscall only uses the leading C-string.
  230. symlink_result = dir_.Symlink("test", std::string("a\0b\0c", 5));
  231. EXPECT_THAT(symlink_result, IsSuccess(_));
  232. readlink_result = dir_.Readlink("test");
  233. EXPECT_THAT(readlink_result, IsSuccess(Eq("a")));
  234. }
  235. TEST_F(FilesystemTest, Chdir) {
  236. auto current_result = Cwd().OpenDir(".");
  237. ASSERT_THAT(current_result, IsSuccess(_));
  238. auto symlink_result = dir_.Symlink("test", "abc");
  239. EXPECT_THAT(symlink_result, IsSuccess(_));
  240. auto chdir_result = dir_.Chdir();
  241. EXPECT_THAT(chdir_result, IsSuccess(_));
  242. auto readlink_result = Cwd().Readlink("test");
  243. EXPECT_THAT(readlink_result, IsSuccess(Eq("abc")));
  244. auto chdir_path_result = dir_.Chdir("missing");
  245. ASSERT_FALSE(chdir_path_result.ok());
  246. EXPECT_TRUE(chdir_path_result.error().no_entity());
  247. // Dangling symlink.
  248. chdir_path_result = dir_.Chdir("test");
  249. ASSERT_FALSE(chdir_path_result.ok());
  250. EXPECT_TRUE(chdir_path_result.error().no_entity());
  251. // Create a regular file and try to chdir to that.
  252. auto f = dir_.OpenWriteOnly("test2", CreationOptions::CreateNew);
  253. ASSERT_THAT(f, IsSuccess(_));
  254. auto write_result = f->WriteFromString("test2");
  255. EXPECT_THAT(write_result, IsSuccess(_));
  256. chdir_path_result = dir_.Chdir("test2");
  257. ASSERT_FALSE(chdir_path_result.ok());
  258. EXPECT_TRUE(chdir_path_result.error().not_dir());
  259. auto d2_result = Cwd().OpenDir("test_d2", CreationOptions::CreateNew);
  260. ASSERT_THAT(d2_result, IsSuccess(_));
  261. symlink_result = d2_result->Symlink("test2", "def");
  262. EXPECT_THAT(symlink_result, IsSuccess(_));
  263. chdir_path_result = dir_.Chdir("test_d2");
  264. ASSERT_THAT(chdir_path_result, IsSuccess(_));
  265. readlink_result = Cwd().Readlink("test2");
  266. EXPECT_THAT(readlink_result, IsSuccess(Eq("def")));
  267. readlink_result = Cwd().Readlink("../test");
  268. EXPECT_THAT(readlink_result, IsSuccess(Eq("abc")));
  269. chdir_result = current_result->Chdir();
  270. ASSERT_THAT(chdir_result, IsSuccess(_));
  271. readlink_result = Cwd().Readlink("test");
  272. ASSERT_FALSE(readlink_result.ok());
  273. EXPECT_TRUE(readlink_result.error().no_entity());
  274. (*std::move(f)).Close().Check();
  275. }
  276. } // namespace
  277. } // namespace Carbon::Filesystem