The Battle for Wesnoth  1.19.12+dev
test_filesystem.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2015 - 2025
3  by Iris Morelle <shadowm2006@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include <boost/test/unit_test.hpp>
17 
18 #include "config_cache.hpp"
19 #include "filesystem.hpp"
20 #include "game_config.hpp"
21 #include "log.hpp"
23 
24 #if 0
25 namespace {
26 
27 template<typename T>
28 void dump(const T& v)
29 {
30  for(typename T::const_iterator k = v.begin(); k != v.end(); ++k) {
31  PLAIN_LOG << " * " << *k;
32  }
33 }
34 
35 }
36 #endif
37 
38 BOOST_AUTO_TEST_SUITE( filesystem ); // implicit namespace filesystem
39 
40 const std::string& gamedata = game_config::path;
41 
42 BOOST_AUTO_TEST_CASE( test_fs_game_path_reverse_engineering )
43 {
44  const std::string maincfg = "_main.cfg";
45 
46  std::string gamedata_rev = get_wml_location("_main.cfg").value();
47 
48  const std::size_t strip_len = (maincfg + "/data/").length();
49  BOOST_REQUIRE(gamedata_rev.length() > strip_len);
50  gamedata_rev.resize(gamedata_rev.length() - strip_len);
51 
52  BOOST_CHECK_EQUAL( gamedata_rev, gamedata );
53 }
54 
55 BOOST_AUTO_TEST_CASE( test_fs_base )
56 {
57  BOOST_CHECK( is_root("/") );
58  BOOST_CHECK( is_root("////") );
59  BOOST_CHECK( is_root("/../") );
60  BOOST_CHECK( is_root("/../.././") );
61  BOOST_CHECK( is_root("/.././../.") );
62  BOOST_CHECK( is_root("/.") );
63 
64  BOOST_CHECK( is_relative(".") );
65  BOOST_CHECK( is_relative("..") );
66  BOOST_CHECK( is_relative("../foo") );
67  BOOST_CHECK( is_relative("foo") );
68  BOOST_CHECK( !is_relative("/./../foo/..") );
69  BOOST_CHECK( !is_relative("/foo/..") );
70  BOOST_CHECK( !is_relative("/foo") );
71  BOOST_CHECK( !is_relative("///foo") );
72 
73  BOOST_CHECK( is_directory("/") );
74  BOOST_CHECK( is_directory("/.") );
75  BOOST_CHECK( is_directory("/./././.") );
76  BOOST_CHECK( is_directory("/..") );
77 
78  BOOST_CHECK( is_directory(".") );
79  BOOST_CHECK( is_directory("..") );
80  BOOST_CHECK( is_directory("./././.") );
81 
82  BOOST_CHECK( is_directory(gamedata + "/data/core/../../data/././../data/././core") );
83 
84  BOOST_CHECK( file_exists("/") );
85  BOOST_CHECK( file_exists("/.") );
86  BOOST_CHECK( file_exists("/./././.") );
87  BOOST_CHECK( file_exists("/..") );
88 
89  BOOST_CHECK_EQUAL( base_name("foo/bar/baz.cfg"), "baz.cfg" );
90  // FIXME: BFS gives "." for this, Unix basename gives "bar"!
91  //BOOST_CHECK_EQUAL( base_name("foo/bar/"), "bar" );
92  BOOST_CHECK_EQUAL( base_name("foo/bar"), "bar" );
93  BOOST_CHECK_EQUAL( base_name("/"), "/" );
94  BOOST_CHECK_EQUAL( base_name(""), "" );
95 
96  BOOST_CHECK_EQUAL( directory_name("foo/bar/baz.cfg"), "foo/bar" );
97  BOOST_CHECK_EQUAL( directory_name("foo/bar/"), "foo/bar" );
98  BOOST_CHECK_EQUAL( directory_name("foo/bar"), "foo" );
99  BOOST_CHECK_EQUAL( directory_name("/"), "" );
100  BOOST_CHECK_EQUAL( directory_name(""), "" );
101 
102  // TODO normalize_path
103 
104  //BOOST_CHECK_EQUAL( normalize_path(gamedata + "/data/core/../../data/././../data/././core"),
105  // gamedata + "/data/core" );
106 }
107 
108 BOOST_AUTO_TEST_CASE( test_fs_enum )
109 {
110  const std::string path = "data/test/test/filesystem/enum";
111 
112  const std::vector<std::string> expected_filenames {
113  "_initial.cfg",
114  "A1.cfg",
115  "A2.cfg",
116  "A3.cfg",
117  "B1.cfg",
118  "B2.cfg",
119  "B3.cfg",
120  "_final.cfg"};
121  const std::vector<std::string> expected_dirnames {
122  "D1",
123  "D2",
124  "D3"};
125 
126  std::vector<std::string> files, dirs;
127  std::vector<std::string> expected_filepaths, expected_dirpaths;
128 
129  for(const std::string& n : expected_filenames) {
130  expected_filepaths.push_back(gamedata + "/" + path + "/" + n);
131  }
132 
133  for(const std::string& n : expected_dirnames) {
134  expected_dirpaths.push_back(gamedata + "/" + path + "/" + n);
135  }
136 
137  // FIXME: get_files_in_dir with mode == FILE_NAME_ONLY will fail to reorder
138  // entries because the sorting code looks for forward slashes.
139  // This affects both the BFS-based and legacy implementations.
140  get_files_in_dir(path, &files, &dirs, name_mode::ENTIRE_FILE_PATH, filter_mode::NO_FILTER, reorder_mode::DO_REORDER);
141 
142  BOOST_CHECK( files == expected_filepaths );
143  BOOST_CHECK( dirs == expected_dirpaths );
144 }
145 
146 BOOST_AUTO_TEST_CASE( test_fs_binary_path )
147 {
148  config main_config;
149  game_config_view game_config_view_ = game_config_view::wrap(main_config);
151 
152  cache.clear_defines();
153  cache.add_define("EDITOR");
154  cache.add_define("MULTIPLAYER");
155  cache.get_config(game_config::path +"/data", main_config);
156 
157  const filesystem::binary_paths_manager bin_paths_manager(game_config_view_);
158 
159  //load_language_list();
160  game_config::load_config(main_config.mandatory_child("game_config"));
161 
162  BOOST_CHECK_EQUAL( get_binary_dir_location("images", ".").value(), gamedata + "/images/." );
163 
164  BOOST_CHECK_EQUAL( get_binary_file_location("images", "wesnoth-icon.png").value(),
165  gamedata + "/data/core/images/wesnoth-icon.png" );
166 
167  BOOST_CHECK_EQUAL( get_binary_file_location("music", "silence.ogg").value(),
168  gamedata + "/data/core/music/silence.ogg" );
169 
170  BOOST_CHECK_EQUAL( get_binary_file_location("sounds", "explosion.ogg").value(),
171  gamedata + "/data/core/sounds/explosion.ogg" );
172 
173  BOOST_CHECK_EQUAL( get_independent_binary_file_path("images", "wesnoth-icon.png").value(),
174  "data/core/images/wesnoth-icon.png" );
175 
176  // Inexistent paths are resolved empty.
177  BOOST_CHECK( !get_binary_dir_location("images", "").has_value() );
178  BOOST_CHECK( !get_binary_dir_location("inexistent_resource_type", "").has_value() );
179  BOOST_CHECK( !get_binary_file_location("image", "wesnoth-icon.png").has_value() );
180  BOOST_CHECK( !get_binary_file_location("images", "bunnies_and_ponies_and_rainbows_oh_em_gee.psd").has_value() );
181  BOOST_CHECK( !get_binary_file_location("music", "this_track_does_not_exist.aiff").has_value() );
182  BOOST_CHECK( !get_binary_file_location("sounds", "rude_noises.aiff").has_value() );
183  BOOST_CHECK( !get_independent_binary_file_path("images", "dopefish.txt").has_value() );
184 
185  // to_asset_path checks
186  std::string path = gamedata + "/data/core/images/wesnoth-icon.png";
187  utils::optional<std::string> outpath = to_asset_path(path, "", "images");
188  BOOST_CHECK( outpath.has_value() );
189  BOOST_CHECK_EQUAL( outpath.value(), "wesnoth-icon.png" );
190 
191  path = gamedata + "/images/icons/action/modern/language_25-active.png";
192  outpath = to_asset_path(path, "", "images");
193  BOOST_CHECK( outpath.has_value() );
194  BOOST_CHECK_EQUAL( outpath.value(), "icons/action/modern/language_25-active.png" );
195 
196  path = gamedata + "/data/core/sounds/ambient/campfire.ogg";
197  outpath = to_asset_path(path, "", "sounds");
198  BOOST_CHECK( outpath.has_value() );
199  BOOST_CHECK_EQUAL( outpath.value(), "ambient/campfire.ogg" );
200 
201  path = gamedata + "/images/this/path/doesn't/exist/campfire.ogg";
202  BOOST_CHECK( !to_asset_path(path, "", "images").has_value() );
203 }
204 
205 BOOST_AUTO_TEST_CASE( test_fs_wml_path )
206 {
207  const std::string& userdata = get_user_data_dir();
208 
209  BOOST_CHECK_EQUAL( get_wml_location("").value_or(""), "" );
210 
211  BOOST_CHECK_EQUAL( get_wml_location("_main.cfg").value_or(""), gamedata + "/data/_main.cfg" );
212  BOOST_CHECK_EQUAL( get_wml_location("core/_main.cfg").value_or(""), gamedata + "/data/core/_main.cfg" );
213  BOOST_CHECK_EQUAL( get_wml_location(".", std::string("")).value_or(""), "." );
214 
215  BOOST_CHECK_EQUAL( get_wml_location("~/").value_or(""), userdata + "/data/" );
216 
217  // Inexistent paths are resolved empty.
218  BOOST_CHECK( !get_wml_location("why_would_anyone_ever_name_a_file_like_this").has_value() );
219 }
220 
221 BOOST_AUTO_TEST_CASE( test_fs_search )
222 {
223  const std::string& userdata = get_user_data_dir();
224 
225  BOOST_CHECK_EQUAL( nearest_extant_parent(userdata + "/THIS_DOES_NOT_EXIST/foo/bar"), userdata );
226 
227  BOOST_CHECK_EQUAL( nearest_extant_parent(gamedata + "/THIS_DOES_NOT_EXIST_EITHER/foo"), gamedata );
228  BOOST_CHECK_EQUAL( nearest_extant_parent(gamedata + "/data/_main.cfg"), gamedata + "/data" );
229  BOOST_CHECK_EQUAL( nearest_extant_parent(gamedata + "/data/core/THIS_DOES_NOT_EXIST/test"), gamedata + "/data/core" );
230 
231  BOOST_CHECK_EQUAL( nearest_extant_parent("/THIS_HOPEFULLY_DOES_NOT_EXIST"), "/" );
232  BOOST_CHECK_EQUAL( nearest_extant_parent("/THIS_HOPEFULLY_DOES_NOT_EXIST/foo/bar"), "/" );
233  BOOST_CHECK_EQUAL( nearest_extant_parent("/THIS_HOPEFULLY_DOES_NOT_EXIST/foo/bar/.."), "/" );
234 }
235 
236 BOOST_AUTO_TEST_CASE( test_fs_fluff )
237 {
238  BOOST_CHECK( looks_like_pbl("foo.pbl") );
239  BOOST_CHECK( looks_like_pbl("FOO.PBL") );
240  BOOST_CHECK( looks_like_pbl("Foo.Pbl") );
241  BOOST_CHECK( !looks_like_pbl("foo.pbl.cfg") );
242 
243  BOOST_CHECK( is_gzip_file("foo.gz") );
244  BOOST_CHECK( !is_gzip_file("foo.gz.bz2") );
245  BOOST_CHECK( is_bzip2_file("foo.bz2") );
246  BOOST_CHECK( !is_bzip2_file("foo.bz2.gz") );
247 
248  BOOST_CHECK( is_compressed_file("foo.gz") );
249  BOOST_CHECK( is_compressed_file("foo.bz2") );
250  BOOST_CHECK( !is_compressed_file("foo.txt") );
251 
252  // FIXME: Is this even intended?
253  BOOST_CHECK( !is_gzip_file("foo.GZ") );
254  BOOST_CHECK( !is_bzip2_file("foo.BZ2") );
255  BOOST_CHECK( !is_compressed_file("foo.GZ") );
256  BOOST_CHECK( !is_compressed_file("foo.BZ2") );
257 }
258 
259 BOOST_AUTO_TEST_SUITE_END()
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:362
Singleton class to manage game config file caching.
void add_define(const std::string &define)
Add a entry to preproc defines map.
static config_cache & instance()
Get reference to the singleton object.
void clear_defines()
Clear stored defines map to default values.
void get_config(const std::string &path, config &cfg, abstract_validator *validator=nullptr)
Gets a config object from given path.
A class grating read only view to a vector of config objects, viewed as one config with all children ...
static game_config_view wrap(const config &cfg)
Declarations for File-IO.
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:296
bool is_bzip2_file(const std::string &filename)
Returns true if the file ends with '.bz2'.
bool is_relative(const std::string &path)
Returns whether the path seems to be relative.
bool is_root(const std::string &path)
Returns whether the path is the root of the file hierarchy.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Get a list of all files and/or directories in a given directory.
Definition: filesystem.cpp:459
std::string get_user_data_dir()
Definition: filesystem.cpp:865
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
bool is_gzip_file(const std::string &filename)
Returns true if the file ends with '.gz'.
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:337
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > &current_dir)
Returns a translated path to the actual file or directory, if it exists.
bool is_compressed_file(const std::string &filename)
Definition: filesystem.hpp:294
utils::optional< std::string > get_binary_file_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual file of a given type, if it exists.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
utils::optional< std::string > get_binary_dir_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual directory of a given type, if it exists.
std::string nearest_extant_parent(const std::string &file)
Finds the nearest parent in existence for a file or directory.
bool looks_like_pbl(const std::string &file)
utils::optional< std::string > get_independent_binary_file_path(const std::string &type, const std::string &filename)
Returns an asset path to filename for binary path-independent use in saved games.
utils::optional< std::string > to_asset_path(const std::string &path, const std::string &addon_id, const std::string &asset_type)
Helper function to convert absolute path to wesnoth relative path.
std::string path
Definition: filesystem.cpp:102
void load_config(const config &v)
The paths manager is responsible for recording the various paths that binary files may be located at.
Definition: filesystem.hpp:439
BOOST_AUTO_TEST_SUITE(filesystem)
const std::string & gamedata
BOOST_AUTO_TEST_CASE(test_fs_game_path_reverse_engineering)
static map_location::direction n