1/** \file
2 * \brief Resource file abstraction to be used in tests. Resources
3 * are compiled into binary format and can be accessed with the
4 * classes provided by this file.
5 *
6 * \author Tilo Wiedera, Jöran Schierbaum
7 *
8 * \par License:
9 * This file is part of the Open Graph Drawing Framework (OGDF).
10 *
11 * \par
12 * Copyright (C)<br>
13 * See README.md in the OGDF root directory for details.
14 *
15 * \par
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * Version 2 or 3 as published by the Free Software Foundation;
19 * see the file LICENSE.txt included in the packaging of this file
20 * for details.
21 *
22 * \par
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * \par
29 * You should have received a copy of the GNU General Public
30 * License along with this program; if not, see
31 * http://www.gnu.org/copyleft/gpl.html
32 */
33
34#pragma once
35
36#include <string>
37#include <sstream>
38#include <unordered_map>
39#include <vector>
40#include <ogdf/fileformats/GraphIO.h>
41#include <testing.h>
42
43namespace resources {
44
45//! Abstract base class for resources
46class AbstractResource {
47protected:
48 string m_path; //!< The relative path in the resource directory
49 string m_name; //!< The name of this resource
50
51public:
52 AbstractResource(const string& path, const string& name) : m_path(path), m_name(name) { }
53
54 inline const string& path() const { return m_path; }
55 inline const string& name() const { return m_name; }
56 inline string fullPath() const { return (m_path.empty() ? m_name : m_path + '/' + m_name); }
57};
58
59//! A resource file whose contents can be retrieved
60/**
61 * Use the ResourceFile::get() methods to get const pointers for test cases.
62 */
63class ResourceFile : public AbstractResource {
64protected:
65 string m_data; //!< File contents
66
67public:
68 ResourceFile() : AbstractResource("", "") { }
69 ResourceFile(const string& path, const string& name, const string& data) : AbstractResource(path, name), m_data(data) { }
70
71 inline const string& data() const { return m_data; }
72
73 //! Retrieves a resource with given path
74 /**
75 * @param path file path and name, relative to original resources folder
76 * @return pointer to file if found, \c nullptr otherwise
77 */
78 static const ResourceFile* get(const string& path);
79
80 //! Retrieves a resource with given path and name
81 /**
82 * @param dir a directory relative to original resources folder
83 * @param file filename of the requested file
84 * @return pointer to file if found, \c nullptr otherwise
85 */
86 static const ResourceFile* get(const string& dir, const string& file);
87};
88
89//! A resource folder, holding subfolders and files
90/**
91 * Use the ResourceDirectory::get() methods to get const pointers for test cases.
92 */
93class ResourceDirectory : public AbstractResource {
94protected:
95 std::unordered_map<string, ResourceDirectory*> m_directories;
96 std::unordered_map<string, ResourceFile*> m_files;
97
98public:
99 ResourceDirectory() : AbstractResource("", "") { }
100 ResourceDirectory(const string& path, const string& name) : AbstractResource(path, name) { }
101
102 //! Registers a new file in this directory
103 void addFile(ResourceFile* file);
104
105 //! Returns a file in this directory
106 /**
107 * @param name the filename
108 * @return pointer to file if found, \c nullptr otherwise
109 */
110 const ResourceFile* getFile(const string& name) const;
111
112 //! Recursively look for a file
113 /**
114 * @param path the file's path relative to this directory
115 * @return pointer to file if found, \c nullptr otherwise
116 */
117 const ResourceFile* getFileByPath(const string& path) const;
118
119 //! Registers a new directory as a subdirectory of the current object
120 void addDirectory(ResourceDirectory*);
121
122 //! Registers a new directory as a subdirectory of the current object
123 ResourceDirectory* addDirectory(const string& name);
124
125 //! Returns a subdirectory
126 /**
127 * @param name name of the subdirectory
128 * @return pointer to subdirectory if found, \c nullptr otherwise
129 */
130 const ResourceDirectory* getDirectory(const string& name) const;
131
132 //! Returns a subdirectory
133 /**
134 * @param name name of the subdirectory
135 * @param create if \c true, create directory if not found
136 * @return pointer to subdirectory if found, \c nullptr otherwise
137 */
138 ResourceDirectory* getDirectory(const string& name, bool create = false);
139
140 //! Recursively looks for a directory with the given path
141 /**
142 * @param path the directory's path relative to this directory
143 * @return pointer to directory if found, \c nullptr otherwise
144 */
145 const ResourceDirectory* getDirectoryByPath(const string& path) const;
146
147 //! Recursively looks for a directory with the given path
148 /**
149 * @param path the directory's path relative to this directory
150 * @param createPath whether empty directories should be created when a
151 * non-existant path gets queried
152 * @return pointer to directory if found, \c nullptr otherwise
153 */
154 ResourceDirectory* getDirectoryByPath(const string& path, bool createPath = false);
155
156 //! Returns all files in this directory
157 std::vector<const ResourceFile*> getFiles() const;
158
159 //! Returns all subdirectories
160 std::vector<const ResourceDirectory*> getDirectories() const;
161
162 //! Iterates over each file contained in this directory.
163 /**
164 * \param callback A function that will be called for each file in the directory.
165 * \param recurse Whether to include sub directories.
166 */
167 void forEachFile(std::function<void(const ResourceFile*)> callback, bool recurse = false) const;
168
169 //! Retrieves a resource directory with given path
170 /**
171 * @param path directory path, relative to original resources folder
172 * @return pointer to directory if found, \c nullptr otherwise
173 */
174 static const ResourceDirectory* get(const string& path);
175
176 //! Retrieves a resource directory with given path and name
177 /**
178 * @param dir a directory relative to original resources folder
179 * @param name filename of the requested directory
180 * @return pointer to directory if found, \c nullptr otherwise
181 */
182 static const ResourceDirectory* get(const string& dir, const string& name);
183};
184
185namespace internal {
186
187/* Internal global variables holding the folder structure */
188extern std::unordered_map<string, ResourceDirectory> g_directories;
189extern std::unordered_map<string, ResourceFile> g_resources;
190extern ResourceDirectory g_resources_root;
191
192//! Registers a resource and stores it in global file structure
193/**
194 * This function is called by the generated resource source file to register all loaded
195 * resource files for use in other areas.
196 *
197 * @note You should not call this manually from tests as it changes the global state of
198 * the virtual file tree and thus directly affects other test cases.
199 *
200 * @param directory the file's path
201 * @param filename the file's name
202 * @param contents the contents to store
203 */
204void registerResource(const string& directory, const string& filename, const string& contents);
205
206} // namespace internal
207
208//! Loads the generated resource files into the global data structure
209void load_resources();
210
211} // namespace resources
212
213// Export the two important classes for easier access
214using resources::ResourceFile;
215using resources::ResourceDirectory;
216
217//! Iterates over each file contained in the specified directory.
218/**
219 * \param directory The path of the directory.
220 * \param callback A function that will be called for each file in the directory.
221 * \param recurse Whether to include sub directories.
222 */
223inline void for_each_file(const string &directory, std::function<void(const ResourceFile*)> callback, bool recurse = false) {
224 const ResourceDirectory* dir = ResourceDirectory::get(directory);
225 if (dir != nullptr) {
226 dir->forEachFile(callback, recurse);
227 }
228}
229
230//! Reads the specified files and creates a test for each graph.
231/**
232 * \param title The base title for the test cases.
233 * \param filenames The names of the files to be read.
234 * \param testFunc The actual test to be performed.
235 * \param reader The function used to parse the files, defaults to GraphIO::readGML.
236 */
237inline void for_each_graph_it(const string &title, const std::vector<string> &filenames, std::function<void(Graph&)> testFunc, GraphIO::ReaderFunc reader = GraphIO::readGML) {
238 for(const string filename : filenames) {
239 bandit::it(title + " [" + filename.c_str() + "]", [&] {
240 Graph graph;
241 const ResourceFile* file = ResourceFile::get(filename);
242 AssertThat(file == nullptr, IsFalse());
243 std::stringstream ss{file->data()};
244 AssertThat(reader(graph, ss), IsTrue());
245 testFunc(graph);
246 });
247 }
248}
249