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