1 /*
2 Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "mysql/harness/filesystem.h"
26
27 ////////////////////////////////////////
28 // Test system include files
29 #include "test/helpers.h"
30
31 ////////////////////////////////////////
32 // Third-party include files
33 #include "gtest/gtest.h"
34
35 ////////////////////////////////////////
36 // Standard include files
37
38 #include <fstream>
39 #include <iostream>
40 #include <stdexcept>
41 #include <vector>
42
43 using std::back_inserter;
44 using std::cout;
45 using std::endl;
46
47 using mysql_harness::Directory;
48 using mysql_harness::Path;
49
50 Path g_here;
51
TEST(TestFilesystem,TestPath)52 TEST(TestFilesystem, TestPath) {
53 // Testing basic path construction
54 EXPECT_EQ(Path("/data/logger.cfg"), "/data/logger.cfg");
55 EXPECT_EQ(Path("data/logger.cfg"), "data/logger.cfg");
56 EXPECT_EQ(Path("/"), "/");
57 EXPECT_EQ(Path("//"), "/");
58 EXPECT_EQ(Path("////////"), "/");
59 EXPECT_EQ(Path("/data/"), "/data");
60 EXPECT_EQ(Path("data/"), "data");
61 EXPECT_EQ(Path("data////"), "data");
62
63 // Testing dirname function
64 EXPECT_EQ(Path("foo.cfg").dirname(), ".");
65 EXPECT_EQ(Path("foo/bar.cfg").dirname(), "foo");
66 EXPECT_EQ(Path("/foo/bar.cfg").dirname(), "/foo");
67 EXPECT_EQ(Path("/").dirname(), "/");
68
69 // Testing basename function
70 EXPECT_EQ(Path("foo.cfg").basename(), "foo.cfg");
71 EXPECT_EQ(Path("foo/bar.cfg").basename(), "bar.cfg");
72 EXPECT_EQ(Path("/foo/bar.cfg").basename(), "bar.cfg");
73 EXPECT_EQ(Path("/").basename(), "/");
74
75 // Testing join function (and indirectly the append function).
76 Path new_path = Path("data").join("test");
77 EXPECT_EQ(new_path, "data/test");
78
79 std::string test_data_dir = mysql_harness::get_tests_data_dir(g_here.str());
80
81 // Testing file status checking functions
82 EXPECT_EQ(Path(test_data_dir).type(), Path::FileType::DIRECTORY_FILE);
83
84 #ifdef _WIN32
85 EXPECT_EQ(Path("c:").type(), Path::FileType::DIRECTORY_FILE);
86 #endif
87
88 EXPECT_EQ(Path(test_data_dir).join("logger.cfg").type(),
89 Path::FileType::REGULAR_FILE);
90 EXPECT_EQ(Path(test_data_dir).join("does-not-exist.cfg").type(),
91 Path::FileType::FILE_NOT_FOUND);
92
93 EXPECT_TRUE(Path(test_data_dir).is_directory());
94 EXPECT_FALSE(Path(test_data_dir).join("logger.cfg").is_directory());
95 EXPECT_FALSE(Path(test_data_dir).is_regular());
96 EXPECT_TRUE(Path(test_data_dir).join("logger.cfg").is_regular());
97 }
98
TEST(TestFilesystem,EmptyPath)99 TEST(TestFilesystem, EmptyPath) {
100 // Testing error usage
101 EXPECT_THROW({ Path path(""); }, std::invalid_argument);
102
103 // Default-constructed paths should be possible to create, but not
104 // to use.
105 Path path;
106 EXPECT_THROW(path.is_regular(), std::invalid_argument);
107 EXPECT_THROW(path.is_directory(), std::invalid_argument);
108 EXPECT_THROW(path.type(), std::invalid_argument);
109 EXPECT_THROW(path.append(g_here), std::invalid_argument);
110 EXPECT_THROW(path.join(g_here), std::invalid_argument);
111 EXPECT_THROW(path.basename(), std::invalid_argument);
112 EXPECT_THROW(path.dirname(), std::invalid_argument);
113 EXPECT_THROW(g_here.append(path), std::invalid_argument);
114 EXPECT_THROW(g_here.join(path), std::invalid_argument);
115
116 // Once a real path is moved into it, all should be fine.
117 path = g_here;
118 EXPECT_EQ(path, g_here);
119 EXPECT_TRUE(path.is_directory());
120 EXPECT_FALSE(path.is_regular());
121 }
122
TEST(TestFilesystem,TestDirectory)123 TEST(TestFilesystem, TestDirectory) {
124 std::string test_data_dir = mysql_harness::get_tests_data_dir(g_here.str());
125
126 {
127 // These are the files in the "data" directory in the test
128 // directory. Please update it if you add more files.
129 //
130 // TODO(Mats): Do not use the data directory for this but create a
131 // dedicated directory for testing this feature.
132 Directory directory{Path(test_data_dir).join("logger.d")};
133 std::vector<Path> expect{
134 Path(test_data_dir).join("logger.d/one.cfg"),
135 Path(test_data_dir).join("logger.d/magic.cfg"),
136 Path(test_data_dir).join("logger.d/default.cfg"),
137 };
138
139 decltype(expect) result(directory.begin(), directory.end());
140 EXPECT_SETEQ(expect, result);
141 }
142
143 {
144 // These are files in the "data" directory in the test
145 // directory. Please update it if you add more files.
146 Directory directory{Path(test_data_dir)};
147 std::vector<Path> expect{
148 Path(test_data_dir).join("tests-bad-1.cfg"),
149 Path(test_data_dir).join("tests-bad-2.cfg"),
150 Path(test_data_dir).join("tests-bad-3.cfg"),
151 };
152
153 decltype(expect) result(directory.glob("tests-bad*.cfg"), directory.end());
154 EXPECT_SETEQ(expect, result);
155 }
156 }
157
158 // unfortunately it's not (reasonably) possible to make folders read-only on
159 // Windows, therefore we can run the following 2 tests only on Unix
160 // https://support.microsoft.com/en-us/help/326549/you-cannot-view-or-change-the-read-only-or-the-system-attributes-of-fo
TEST(TestFilesystem,IsReadableIfFileCanBeRead)161 TEST(TestFilesystem, IsReadableIfFileCanBeRead) {
162 #ifndef _WIN32
163
164 // create temporary file
165 const std::string directory = mysql_harness::get_tmp_dir("tmp");
166 std::shared_ptr<void> exit_guard(
167 nullptr, [&](void *) { mysql_harness::delete_dir_recursive(directory); });
168
169 mysql_harness::Path path = mysql_harness::Path(directory).join("/tmp_file");
170 std::ofstream file(path.str());
171
172 if (!file.good())
173 throw(std::runtime_error("Could not create file " + path.str()));
174
175 // make file readable
176 chmod(path.c_str(), S_IRUSR);
177 ASSERT_TRUE(path.is_readable());
178 #endif
179 }
180
TEST(TestFilesystem,IsNotReadableIfFileCanNotBeRead)181 TEST(TestFilesystem, IsNotReadableIfFileCanNotBeRead) {
182 #ifndef _WIN32
183
184 // create temporary file
185 const std::string directory = mysql_harness::get_tmp_dir("tmp");
186 std::shared_ptr<void> exit_guard(
187 nullptr, [&](void *) { mysql_harness::delete_dir_recursive(directory); });
188
189 mysql_harness::Path path = mysql_harness::Path(directory).join("/tmp_file");
190 std::ofstream file(path.str());
191
192 if (!file.good())
193 throw(std::runtime_error("Could not create file " + path.str()));
194
195 // make file readable
196 chmod(path.c_str(), S_IWUSR | S_IXUSR);
197 ASSERT_FALSE(path.is_readable());
198 #endif
199 }
200
TEST(TestFilesystem,delete_dir_recursive)201 TEST(TestFilesystem, delete_dir_recursive) {
202 using mysql_harness::mkdir;
203 std::ofstream ofs;
204 mkdir("testdir", 0700);
205 mkdir("testdir/a", 0700);
206 mkdir("testdir/a/b", 0700);
207 mkdir("testdir/a/a", 0700);
208 std::ofstream().open("testdir/f");
209 std::ofstream().open("testdir/f2");
210 std::ofstream().open("testdir/a/f");
211 std::ofstream().open("testdir/a/b/f");
212 EXPECT_EQ(0, mysql_harness::delete_dir_recursive("testdir"));
213 }
214
215 /*
216 * Tests mysql_harness::mkdir()
217 */
218
TEST(TestFilesystem,Mkdir)219 TEST(TestFilesystem, Mkdir) {
220 constexpr auto kMode = 0700;
221
222 auto tmp_dir = mysql_harness::get_tmp_dir("test");
223 std::shared_ptr<void> exit_guard(
224 nullptr, [&](void *) { mysql_harness::delete_dir_recursive(tmp_dir); });
225
226 // non-recursive should fail
227 EXPECT_NE(0, mysql_harness::mkdir(tmp_dir + "/a/b/c/d", kMode));
228
229 // recursive should be fine
230 EXPECT_EQ(0, mysql_harness::mkdir(tmp_dir + "/a/b/c/d", kMode, true));
231
232 // make sure it really exists
233 mysql_harness::Path my_path(tmp_dir + "/a/b/c/d");
234 EXPECT_TRUE(my_path.exists());
235
236 // we just created one, trying to recursively create it once more
237 // should succeed as 'mkdir -p' does
238 EXPECT_EQ(0, mysql_harness::mkdir(tmp_dir + "/a/b/c/d", kMode, true));
239
240 // create a regular file and try to create a directory with the same name,
241 // that should fail
242 {
243 mysql_harness::Path file_path(tmp_dir + "/a/b/c/regular_file");
244 std::fstream f;
245 f.open(file_path.str(), std::ios::out);
246 }
247 EXPECT_NE(0,
248 mysql_harness::mkdir(tmp_dir + "/a/b/c/regular_file", kMode, true));
249
250 // empty path should throw
251 EXPECT_THROW(mysql_harness::mkdir("", kMode, true), std::invalid_argument);
252 }
253
main(int argc,char * argv[])254 int main(int argc, char *argv[]) {
255 g_here = Path(argv[0]).dirname();
256
257 ::testing::InitGoogleTest(&argc, argv);
258 return RUN_ALL_TESTS();
259 }
260