1 /******************************************************************************
2 * Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following
8 * conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided
15 * with the distribution.
16 * * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
17 * names of its contributors may be used to endorse or promote
18 * products derived from this software without specific prior
19 * written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32 * OF SUCH DAMAGE.
33 ****************************************************************************/
34
35 #include <pdal/pdal_test_main.hpp>
36
37 #include <pdal/util/FileUtils.hpp>
38 #include <pdal/util/Utils.hpp>
39
40 #include "Support.hpp"
41
42 #include <iostream>
43
44 using namespace pdal;
45
TEST(FileUtilsTest,test_file_ops)46 TEST(FileUtilsTest, test_file_ops)
47 {
48 std::string tmp1(Support::temppath("unittest1.tmp"));
49 std::string tmp2(Support::temppath("unittest2.tmp"));
50
51 // first, clean up from any previous test run
52 FileUtils::deleteFile(tmp1);
53 FileUtils::deleteFile(tmp2);
54 EXPECT_TRUE(FileUtils::fileExists(tmp1)==false);
55 EXPECT_TRUE(FileUtils::fileExists(tmp2)==false);
56
57 // write test
58 std::ostream* ostr = FileUtils::createFile(tmp1);
59 *ostr << "yow";
60 FileUtils::closeFile(ostr);
61
62 EXPECT_EQ(FileUtils::fileExists(tmp1), true);
63 EXPECT_EQ(FileUtils::fileSize(tmp1), 3U);
64
65 // rename test
66 FileUtils::renameFile(tmp2,tmp1);
67 EXPECT_TRUE(FileUtils::fileExists(tmp1)==false);
68 EXPECT_TRUE(FileUtils::fileExists(tmp2)==true);
69
70 // read test
71 std::istream* istr = FileUtils::openFile(tmp2);
72 std::string yow;
73 *istr >> yow;
74 FileUtils::closeFile(istr);
75 EXPECT_TRUE(yow=="yow");
76
77 // delete test
78 FileUtils::deleteFile(tmp2);
79 EXPECT_TRUE(FileUtils::fileExists(tmp2)==false);
80
81 EXPECT_THROW(FileUtils::openFile("~foo1.glob"), pdal::pdal_error);
82 EXPECT_NO_THROW(FileUtils::openFile("foo~1.glob"));
83 }
84
TEST(FileUtilsTest,test_readFileIntoString)85 TEST(FileUtilsTest, test_readFileIntoString)
86 {
87 const std::string filename = Support::datapath("text/text.txt");
88 EXPECT_TRUE(FileUtils::fileExists(filename));
89
90 std::string source = FileUtils::readFileIntoString(filename);
91
92 std::string ref = "This is a file that allows us to test that we "
93 "can read a text file into a string through the file utils.\n";
94
95 EXPECT_TRUE(source == ref);
96 }
97
98 #ifdef _WIN32
99 static const std::string drive = "A:";
100 #else
101 static const std::string drive = "";
102 #endif
103
normalize(const std::string p)104 static std::string normalize(const std::string p)
105 {
106 return Utils::replaceAll(p, "\\", "/");
107 }
108
compare_paths(const std::string a,const std::string b)109 static void compare_paths(const std::string a, const std::string b)
110 {
111 EXPECT_EQ(normalize(a), normalize(b));
112 }
113
TEST(FileUtilsTest,test_toAbsolutePath)114 TEST(FileUtilsTest, test_toAbsolutePath)
115 {
116 using namespace std;
117
118 const string root = FileUtils::getcwd();
119
120 // check 1-arg version: make absolute when file is relative,
121 // via current working dir
122 const string a = FileUtils::toAbsolutePath("foo.txt");
123 compare_paths(a, root + "foo.txt");
124
125 // check 1-arg version: make absolute when file is already absolute
126 const string b = FileUtils::toAbsolutePath(drive + "/baz/foo.txt");
127 compare_paths(b, drive + "/baz/foo.txt");
128
129 // check 2-arg version: make absolute when file relative, via given base
130 const string c = FileUtils::toAbsolutePath("foo.txt", drive + "/a/b/c/d");
131 compare_paths(c, drive + "/a/b/c/d/foo.txt");
132
133 // check 2-arg version: make absolute when file is relative, via given base (which isn't absolute)
134 const string d = FileUtils::toAbsolutePath("foo.txt", "x/y/z");
135 compare_paths(d, root + "x/y/z/" + "foo.txt");
136
137 // check 1-arg version: make absolute when file is already absolute
138 const string e = FileUtils::toAbsolutePath(drive+"/baz/foo.txt", drive+"/a/b/c/d");
139 compare_paths(e, drive + "/baz/foo.txt");
140 }
141
TEST(FileUtilsTest,test_getDirectory)142 TEST(FileUtilsTest, test_getDirectory)
143 {
144 // test absolute case
145 const std::string a = FileUtils::getDirectory(drive + "/a/b/foo.txt");
146 compare_paths(a, drive + "/a/b/");
147
148 // test relative case
149 const std::string b = FileUtils::getDirectory("a/b/foo.txt");
150 compare_paths(b, "a/b/");
151 }
152
TEST(FileUtilsTest,test_isAbsolute)153 TEST(FileUtilsTest, test_isAbsolute)
154 {
155 // test absolute case
156 const bool a = FileUtils::isAbsolutePath(drive + "/a/b/foo.txt");
157 EXPECT_TRUE(a);
158
159 // test relative case
160 const bool b = FileUtils::isAbsolutePath("a/b/foo.txt");
161 EXPECT_TRUE(!b);
162 }
163
TEST(FileUtilsTest,filename)164 TEST(FileUtilsTest, filename)
165 {
166 std::string filename = "";
167 EXPECT_EQ(FileUtils::getFilename(filename), "");
168
169 filename = "/";
170 EXPECT_EQ(FileUtils::getFilename(filename), "");
171
172 filename = "/foo/bar/";
173 EXPECT_EQ(FileUtils::getFilename(filename), "");
174
175 filename = "/foo//bar//baz.c";
176 EXPECT_EQ(FileUtils::getFilename(filename), "baz.c");
177
178 #ifdef _WIN32
179 filename = "C:/foo/bar/baz.c";
180 EXPECT_EQ(FileUtils::getFilename(filename), "baz.c");
181
182 filename = "C:\\foo\\bar\\baz.c";
183 EXPECT_EQ(FileUtils::getFilename(filename), "baz.c");
184
185 filename = "C:\\foo/bar\\meaw/baz.c";
186 EXPECT_EQ(FileUtils::getFilename(filename), "baz.c");
187 #else
188 filename = "C:\\foo\\bar\\baz.c";
189 EXPECT_EQ(FileUtils::getFilename(filename), filename);
190 #endif
191 }
192
TEST(FileUtilsTest,extension)193 TEST(FileUtilsTest, extension)
194 {
195 EXPECT_EQ(FileUtils::extension("/foo//bar//baz.c"), ".c");
196 EXPECT_EQ(FileUtils::extension("foobar"), "");
197 EXPECT_EQ(FileUtils::extension("/foo/bar"), "");
198 EXPECT_EQ(FileUtils::extension("/fo.o/b.ar.baz23"), ".baz23");
199 }
200
TEST(FileUtilsTest,stem)201 TEST(FileUtilsTest, stem)
202 {
203 EXPECT_EQ(FileUtils::stem("/foo//bar//baz.c"), "baz");
204 EXPECT_EQ(FileUtils::stem("foobar"), "foobar");
205 EXPECT_EQ(FileUtils::stem("/foo/bar"), "bar");
206 EXPECT_EQ(FileUtils::stem("/fo.o/b.ar.baz23"), "b.ar");
207 EXPECT_EQ(FileUtils::stem("."), ".");
208 EXPECT_EQ(FileUtils::stem(".."), "..");
209 }
210
TEST(FileUtilsTest,glob)211 TEST(FileUtilsTest, glob)
212 {
213 auto TP = [](const std::string s)
214 {
215 return Support::temppath(s);
216 };
217
218 auto filenames = [&TP]()
219 {
220 std::vector<std::string> names;
221 std::string name;
222 for (int i = 0; i < 5; ++i)
223 {
224 std::string name;
225 name = TP(std::string("foo") + std::to_string(i) + ".glob");
226 names.push_back(name);
227 name = TP(std::string("bar") + std::to_string(i) + ".glob");
228 names.push_back(name);
229 }
230 return names;
231 };
232
233 for (std::string& file : filenames())
234 FileUtils::deleteFile(file);
235
236 for (std::string& file : filenames()) {
237 auto f = FileUtils::createFile(file);
238 FileUtils::closeFile(f);
239 }
240
241 EXPECT_EQ(FileUtils::glob(TP("*.glob")).size(), 10u);
242 EXPECT_EQ(FileUtils::glob(TP("foo1.glob")).size(), 1u);
243
244 for (std::string& file : filenames())
245 FileUtils::deleteFile(file);
246
247 EXPECT_EQ(FileUtils::glob(TP("*.glob")).size(), 0u);
248 EXPECT_EQ(FileUtils::glob(TP("foo1.glob")).size(), 0u);
249
250 #ifdef _WIN32
251 EXPECT_THROW(FileUtils::glob("~foo1.glob"), pdal::pdal_error);
252 EXPECT_NO_THROW(FileUtils::glob(TP("foo1~.glob")));
253 #endif
254
255 std::string temp_filename = Support::temppath("temp.glob");
256 FileUtils::deleteFile(temp_filename);
257 FileUtils::closeFile(FileUtils::createFile(temp_filename));
258 EXPECT_EQ(FileUtils::glob(temp_filename).size(), 1u);
259 FileUtils::deleteFile(temp_filename);
260 }
261
TEST(FileUtilsTest,test_file_ops_with_unicode_paths)262 TEST(FileUtilsTest, test_file_ops_with_unicode_paths)
263 {
264 // 1. Read Unicode encoded word, ie. Japanese, from .txt file.
265 // 2. Create temporary directory named using the word.
266 // 3. Create a file in the directory.
267 // 4. Exercise the FileUtils using the Unicode-based path.
268
269 for (std::string japanese_txt: {"japanese-pr2135.txt", "japanese-pr2227.txt"})
270 {
271 japanese_txt = Support::datapath("unicode/" + japanese_txt);
272 EXPECT_TRUE(FileUtils::fileExists(japanese_txt));
273 auto const japanese = FileUtils::readFileIntoString(japanese_txt);
274 EXPECT_FALSE(japanese.empty());
275
276 auto const japanese_dir = Support::temppath(japanese);
277 EXPECT_TRUE(FileUtils::createDirectories(japanese_dir));
278 std::string tmp1(japanese_dir + "/06LC743.unicode");
279 std::string tmp2(Support::temppath("nonunicode.tmp"));
280
281 // first, clean up from any previous test run
282 FileUtils::deleteFile(tmp1);
283 FileUtils::deleteFile(tmp2);
284 EXPECT_FALSE(FileUtils::fileExists(tmp1));
285 EXPECT_FALSE(FileUtils::fileExists(tmp2));
286
287 // write test
288 std::ostream *ostr = FileUtils::createFile(tmp1);
289 EXPECT_TRUE(ostr != nullptr);
290 *ostr << "yow";
291 FileUtils::closeFile(ostr);
292
293 EXPECT_EQ(FileUtils::fileExists(tmp1), true);
294 EXPECT_EQ(FileUtils::fileSize(tmp1), 3U);
295
296 // glob for files with Unicode path
297 auto const filenames = FileUtils::glob(japanese_dir + "/*");
298 EXPECT_GE(filenames.size(), 1U);
299 auto const tmp1count = std::count_if(filenames.cbegin(), filenames.cend(),
300 [&tmp1](std::string const& f) { return normalize(f) == normalize(tmp1); });
301 EXPECT_EQ(tmp1count, 1);
302
303 // rename test
304 FileUtils::renameFile(tmp2, tmp1);
305 EXPECT_FALSE(FileUtils::fileExists(tmp1));
306 EXPECT_TRUE(FileUtils::fileExists(tmp2));
307
308 // read test
309 std::istream *istr = FileUtils::openFile(tmp2);
310 std::string yow;
311 *istr >> yow;
312 FileUtils::closeFile(istr);
313 EXPECT_TRUE(yow == "yow");
314
315 // delete test
316 FileUtils::deleteFile(tmp2);
317 EXPECT_FALSE(FileUtils::fileExists(tmp2));
318 FileUtils::deleteDirectory(japanese_dir);
319 EXPECT_FALSE(FileUtils::directoryExists(japanese_dir));
320 }
321 }
322