Line data Source code
1 : /*
2 : Zipios -- a small C++ library that provides easy access to .zip files.
3 :
4 : Copyright (C) 2000-2007 Thomas Sondergaard
5 : Copyright (C) 2015-2019 Made to Order Software Corporation
6 :
7 : This library is free software; you can redistribute it and/or
8 : modify it under the terms of the GNU Lesser General Public
9 : License as published by the Free Software Foundation; either
10 : version 2.1 of the License, or (at your option) any later version.
11 :
12 : This library is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public
18 : License along with this library; if not, write to the Free Software
19 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 : */
21 :
22 : /** \file
23 : *
24 : * Zipios unit tests for the DirectoryCollection class.
25 : */
26 :
27 : #include "tests.hpp"
28 :
29 : #include <fstream>
30 : //
31 : #include <unistd.h>
32 : #include <sys/stat.h>
33 :
34 :
35 : namespace zipios_test
36 : {
37 :
38 :
39 : namespace
40 : {
41 :
42 :
43 : char const g_letters[66]{
44 : '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
45 : 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
46 : 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
47 : '.', '-', '_', '+'
48 : };
49 :
50 :
51 : } // no name namespace
52 :
53 :
54 : /** \class file_t
55 : * \brief Class used to build a regular file.
56 : *
57 : * This file class creates a regular file in a directory.
58 : */
59 :
60 :
61 : /** \brief Create a file.
62 : *
63 : * This function creates a file. If the creation fails, then an error
64 : * is thrown and the process attempts to clean up all the files
65 : * created so far.
66 : *
67 : * The children parameter is used whenever a DIRECTORY is created.
68 : * It determines the number of children that get created inside
69 : * that directory. Sub-directories (since the creation is recursive)
70 : * are given 1/3rd of that number. Also the function creates a new
71 : * sub-directory in about 1 in 10 files it creates.
72 : *
73 : * \param[in] t The type of file (REGULAR or DIRECTORY).
74 : * \param[in] children_count The number of children to create.
75 : */
76 10523 : file_t::file_t(type_t t, int children_count, std::string const& new_filename)
77 : : m_filename(new_filename) // see below also
78 : , m_children() // see below
79 10523 : , m_type(t)
80 : {
81 : // generate a random filename
82 10523 : if(m_filename.empty())
83 : {
84 2 : for(;;)
85 : {
86 10495 : size_t const l(rand() % 100 + 1);
87 544041 : for(size_t idx(0); idx < l; ++idx)
88 : {
89 533546 : m_filename += g_letters[rand() % sizeof(g_letters)];
90 : }
91 : struct stat buf;
92 20990 : if(m_filename != "inexistant" // very unlikely, but just in case...
93 10495 : && stat(m_filename.c_str(), &buf) != 0)
94 : {
95 : // file does not exist, return safely
96 10493 : break;
97 : }
98 : } // LCOV_EXCL_LINE
99 : }
100 :
101 : // This is only to test the validity of the exception handling
102 : //if(children_count < 20)
103 : //{
104 : // throw std::logic_error("Ooops!");
105 : //}
106 :
107 10523 : if(t == type_t::REGULAR)
108 : {
109 : // create a regular file
110 : // (the STL is expected to throw if the create fails from the constructor)
111 18958 : std::ofstream os(m_filename, std::ios::out | std::ios::binary);
112 9479 : size_t count(rand() % (100 * 1024 + 1)); // 0 to 100Kb
113 486602464 : for(size_t sz(0); sz < count; ++sz)
114 : {
115 486592985 : os << static_cast<unsigned char>(rand());
116 : }
117 9479 : if(!os)
118 : {
119 : unlink(m_filename.c_str()); // LCOV_EXCL_LINE
120 : throw std::runtime_error("failed creating regular file"); // LCOV_EXCL_LINE
121 : }
122 : }
123 1044 : else if(t == type_t::DIRECTORY)
124 : {
125 1044 : if(mkdir(m_filename.c_str(), 0777) != 0)
126 : {
127 : throw std::runtime_error("failed creating directory"); // LCOV_EXCL_LINE
128 : }
129 1044 : chdir(m_filename.c_str());
130 11537 : for(int i(0); i < children_count; ++i)
131 : {
132 : try
133 : {
134 10493 : m_children.push_back(pointer_t(new file_t(rand() % 10 == 0 ? type_t::DIRECTORY : type_t::REGULAR, children_count / 3)));
135 : }
136 0 : catch(...)
137 : {
138 0 : m_children.clear();
139 0 : chdir("..");
140 0 : rmdir(m_filename.c_str());
141 0 : throw;
142 : }
143 : }
144 1044 : chdir("..");
145 : }
146 : else
147 : {
148 : throw std::logic_error("unknown type of file"); // LCOV_EXCL_LINE
149 : }
150 10523 : }
151 :
152 : /** \brief Clean up the file.
153 : *
154 : * This function ensures that this file or directory gets deleted
155 : * before deleting the object from memory.
156 : *
157 : * This function is recursive. When deleting a directory, all of
158 : * its children get deleted first.
159 : */
160 21046 : file_t::~file_t()
161 : {
162 : // use this return to keep the tree to check files before they get deleted
163 : //return;
164 10523 : if(m_type == type_t::REGULAR)
165 : {
166 9479 : unlink(m_filename.c_str());
167 : }
168 1044 : else if(m_type == type_t::DIRECTORY)
169 : {
170 : // make sure to delete all the children first
171 1044 : chdir(m_filename.c_str());
172 1044 : m_children.clear();
173 1044 : chdir("..");
174 1044 : rmdir(m_filename.c_str());
175 : }
176 : else
177 : {
178 : // we cannot throw in a destructor... (g++ 7.0+ detects that now!)
179 : //throw std::logic_error("unknown type of file"); // LCOV_EXCL_LINE
180 : std::cerr << "fatal error: unknown type of file" << std::endl; // LCOV_EXCL_LINE
181 : std::terminate(); // LCOV_EXCL_LINE
182 : }
183 10523 : }
184 :
185 : /** \brief Retrieve the type of this file_t object.
186 : *
187 : * This function tells you whether this file_t object is a regular
188 : * file or a directory.
189 : *
190 : * \return REGULAR or DIRECTORY.
191 : */
192 133407 : file_t::type_t file_t::type() const
193 : {
194 133407 : return m_type;
195 : }
196 :
197 : /** \brief Return the filename.
198 : *
199 : * This function returns the filename of the file_t object.
200 : *
201 : * Since most filenames are generated, it is imperative to have a
202 : * way to retrieve the filename of a file_t.
203 : *
204 : * \note
205 : * Filenames are ASCII only (0-9, a-z, A-Z, and a few other characters.)
206 : *
207 : * \return The filename as a standard string.
208 : *
209 : * \sa g_letters
210 : */
211 8516 : std::string const& file_t::filename() const
212 : {
213 8516 : return m_filename;
214 : }
215 :
216 : /** \brief Retrieve the children of this file_t object.
217 : *
218 : * This function retrieves a vector of children. If the file is
219 : * a REGULAR file, then the list of children is always empty.
220 : *
221 : * To get the size of a certain directory, use children().size().
222 : */
223 30 : file_t::vector_t const& file_t::children() const
224 : {
225 30 : return m_children;
226 : }
227 :
228 : /** \brief Calculate the size of the tree starting at this file.
229 : *
230 : * This function is the total number of files this item represents,
231 : * including itself.
232 : *
233 : * The zip includes the directories since these are expected to
234 : * appear in the final Zip archive.
235 : *
236 : * \warning
237 : * This function returns a count that includes the root directory.
238 : * In other words, you have to use the result minus one to compare
239 : * with the total count of a DirectoryCollection.
240 : *
241 : * \return The total size.
242 : */
243 132094 : size_t file_t::size()
244 : {
245 132094 : size_t sz(1); // start with self
246 264025 : for(size_t idx(0); idx < m_children.size(); ++idx)
247 : {
248 131931 : sz += m_children[idx]->size();
249 : }
250 132094 : return sz;
251 : }
252 :
253 : /** \brief Search a file in the tree.
254 : *
255 : * This function is used to search for a file in the tree. It is
256 : * rather slow, that being said, it is used to verify that we get
257 : * exactly the same list in the DirectoryCollection.
258 : *
259 : * \param[in] name The fullname of the file to search.
260 : *
261 : * \return true if the file is found, false otherwise.
262 : */
263 10479004 : file_t::type_t file_t::find(std::string const& name)
264 : {
265 10479004 : std::string::size_type const pos(name.find('/'));
266 :
267 20958008 : std::string const segment(pos == std::string::npos ? name : name.substr(0, pos));
268 :
269 : //std::cerr << "segment = [" << segment << "] vs filename [" << m_filename << "]\n";
270 :
271 10479004 : if(segment != m_filename)
272 : {
273 : // not a match...
274 10005545 : return type_t::UNKNOWN;
275 : }
276 :
277 473459 : if(pos == std::string::npos)
278 : {
279 : // end of 'name' so we got a match
280 132846 : return type();
281 : }
282 :
283 681226 : std::string const remainder(name.substr(pos + 1));
284 :
285 : // this was a folder name, search for child
286 10346158 : for(auto it(m_children.begin()); it != m_children.end(); ++it)
287 : {
288 10346158 : type_t t((*it)->find(remainder));
289 10346158 : if(t != type_t::UNKNOWN)
290 : {
291 340613 : return t;
292 : }
293 : }
294 :
295 0 : return type_t::UNKNOWN;
296 : }
297 :
298 : /** \brief Retrieve all the filenames.
299 : *
300 : * This function builds a vector of all the filenames defined in this
301 : * tree. The sub-folders get their path added as expected.
302 : *
303 : * \return An array with all the filenames defined in this tree.
304 : */
305 20 : file_t::filenames_t file_t::get_all_filenames() const
306 : {
307 20 : filenames_t names;
308 20 : get_filenames(names, m_filename);
309 20 : return names;
310 : }
311 :
312 6027 : void file_t::get_filenames(filenames_t& names, std::string const& parent) const
313 : {
314 6027 : if(m_type == type_t::DIRECTORY)
315 : {
316 : // mark directories as such
317 626 : names.push_back(parent + "/");
318 : }
319 : else
320 : {
321 5401 : names.push_back(parent);
322 : }
323 12034 : for(auto it(m_children.begin()); it != m_children.end(); ++it)
324 : {
325 12014 : file_t::pointer_t f(*it);
326 12014 : std::string p(parent + "/" + f->filename());
327 6007 : f->get_filenames(names, p);
328 : }
329 6027 : }
330 :
331 :
332 3 : } // zipios_tests namespace
333 :
334 : // Local Variables:
335 : // mode: cpp
336 : // indent-tabs-mode: nil
337 : // c-basic-offset: 4
338 : // tab-width: 4
339 : // End:
340 :
341 : // vim: ts=4 sw=4 et
|