mapnik/test/standalone/map_xml_test.cpp

218 lines
7.3 KiB
C++
Raw Normal View History

#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include <mapnik/mapnik.hpp>
#include <mapnik/map.hpp>
#include <mapnik/load_map.hpp>
#include <mapnik/save_map.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/datasource_cache.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <iostream>
#include <random>
namespace {
static std::random_device entropy;
std::string unique_mapnik_name()
{
std::mt19937 gen(entropy());
std::uniform_int_distribution<> distrib(0, 65535);
auto fmt = boost::format("mapnik-test-%1$04x-%2$04x-%3$04x-%4$04x") % distrib(gen) % distrib(gen) % distrib(gen) %
distrib(gen);
return fmt.str();
}
2022-01-26 23:25:53 +01:00
class tmp_dir
{
private:
mapnik::fs::path m_path;
2022-01-26 23:25:53 +01:00
public:
2017-11-06 09:47:01 +01:00
tmp_dir()
: m_path(mapnik::fs::temp_directory_path() / unique_mapnik_name())
2022-01-26 23:25:53 +01:00
{
mapnik::fs::create_directories(m_path);
2017-11-06 09:47:01 +01:00
}
2022-01-26 23:25:53 +01:00
~tmp_dir()
{
2017-11-06 09:47:01 +01:00
// files might be deleted by other things while the test is
// running, which isn't necessarily an error as far as this
// code is concerned - it just wants to delete everything
// underneath the temporary directory.
mapnik::error_code err;
2017-11-06 09:47:01 +01:00
// catch all errors - we don't want to throw in the destructor
2022-01-26 23:25:53 +01:00
try
{
2017-11-06 09:47:01 +01:00
// but loop while the path exists and the errors are
// ignorable.
while (mapnik::fs::exists(m_path))
2022-01-26 23:25:53 +01:00
{
mapnik::fs::remove_all(m_path, err);
2017-11-06 09:47:01 +01:00
// for any non-ignorable error, there's not much we can
// do from the destructor. it's in /tmp anyway, so it'll
// get reclaimed next boot.
if (err && (err != std::errc::no_such_file_or_directory))
2022-01-26 23:25:53 +01:00
{
2017-11-06 09:47:01 +01:00
break;
}
}
}
catch (const std::exception& e)
2022-01-26 23:25:53 +01:00
{
2017-11-06 09:47:01 +01:00
std::cerr << "Exception caught while trying to remove "
2022-01-26 23:25:53 +01:00
<< "temporary directory " << m_path << ": " << e.what() << "\n";
}
catch (...)
2022-01-26 23:25:53 +01:00
{
2017-11-06 09:47:01 +01:00
std::cerr << "Unknown exception caught while trying to "
2022-01-26 23:25:53 +01:00
<< "remove temporary directory " << m_path << "\n";
2017-11-06 09:47:01 +01:00
}
}
mapnik::fs::path path() const { return m_path; }
};
void compare_map(mapnik::fs::path xml)
2022-01-26 23:25:53 +01:00
{
2017-11-06 09:47:01 +01:00
tmp_dir dir;
mapnik::Map m(256, 256);
REQUIRE(m.register_fonts("fonts", true));
mapnik::fs::path abs_base = xml.parent_path();
2017-11-06 09:47:01 +01:00
// first, load the XML into a map object and save it. this
// is a normalisation step to ensure that the file is in
// whatever the current version of mapnik uses as the
// standard indentation, quote style, etc...
2020-11-21 19:03:55 +01:00
REQUIRE_NOTHROW(mapnik::load_map(m, xml.generic_string(), false, abs_base.generic_string()));
mapnik::fs::path test_map1 = dir.path() / "mapnik-temp-map1.xml";
2020-11-21 19:03:55 +01:00
REQUIRE_NOTHROW(mapnik::save_map(m, test_map1.generic_string()));
2017-11-06 09:47:01 +01:00
// create a new map, load the one saved in the previous
// step, and write it out again.
mapnik::Map new_map(256, 256);
REQUIRE(new_map.register_fonts("fonts", true));
2020-11-21 19:03:55 +01:00
REQUIRE_NOTHROW(mapnik::load_map(new_map, test_map1.generic_string(), false, abs_base.generic_string()));
mapnik::fs::path test_map2 = dir.path() / "mapnik-temp-map2.xml";
2020-11-21 19:03:55 +01:00
REQUIRE_NOTHROW(mapnik::save_map(new_map, test_map2.generic_string()));
2017-11-06 09:47:01 +01:00
// if all the information survived the load/save round-trip
// then the two files ought to be identical.
REQUIRE(mapnik::fs::is_regular_file(test_map1));
REQUIRE(mapnik::fs::is_regular_file(test_map2));
REQUIRE(mapnik::fs::file_size(test_map1) == mapnik::fs::file_size(test_map2));
2017-11-06 09:47:01 +01:00
std::ifstream in_map1(test_map1.native()), in_map2(test_map2.native());
2022-01-26 23:25:53 +01:00
REQUIRE(std::equal(std::istream_iterator<char>(in_map1),
std::istream_iterator<char>(),
2017-11-06 09:47:01 +01:00
std::istream_iterator<char>(in_map2)));
}
void add_xml_files(mapnik::fs::path dir, std::vector<mapnik::fs::path>& xml_files)
2022-01-26 23:25:53 +01:00
{
for (auto const& entry :
boost::make_iterator_range(mapnik::fs::directory_iterator(dir), mapnik::fs::directory_iterator()))
2022-01-26 23:25:53 +01:00
{
2017-11-06 09:47:01 +01:00
auto path = entry.path();
2022-01-26 23:25:53 +01:00
if (path.extension().generic_string() == ".xml")
{
2017-11-06 09:47:01 +01:00
xml_files.emplace_back(path);
}
}
}
void load_map(mapnik::Map& m, mapnik::fs::path const& path)
2022-01-26 23:25:53 +01:00
{
2017-11-06 09:47:01 +01:00
try
{
2020-11-21 19:03:55 +01:00
mapnik::load_map(m, path.generic_string());
}
catch (std::exception const& ex)
2017-11-06 09:47:01 +01:00
{
// errors which come from the datasource not being loaded or
// database not being set up aren't really useful - they're
// more likely to be spurious than meaningful, so ignore them.
std::string what = ex.what();
if ((what.find("Could not create datasource") == std::string::npos) &&
2022-01-26 23:25:53 +01:00
(what.find("Postgis Plugin: could not connect to server") == std::string::npos))
{
2017-11-06 09:47:01 +01:00
throw;
}
}
}
} // anonymous namespace
2022-11-28 08:31:25 +01:00
#ifndef MAPNIK_STATIC_PLUGINS
2022-01-26 23:25:53 +01:00
const bool registered =
mapnik::datasource_cache::instance().register_datasources((mapnik::fs::path("plugins") / "input").generic_string());
2022-11-28 08:31:25 +01:00
#endif
2022-01-26 23:25:53 +01:00
TEST_CASE("map xml I/O")
{
mapnik::setup();
2022-11-28 08:31:25 +01:00
#ifndef MAPNIK_STATIC_PLUGINS
2017-11-06 09:47:01 +01:00
// make sure plugins are loaded
REQUIRE(registered);
2022-11-28 08:31:25 +01:00
#endif
2017-11-06 09:47:01 +01:00
// make the tests silent since we intentially test error conditions that are noisy
auto const severity = mapnik::logger::instance().get_severity();
mapnik::logger::instance().set_severity(mapnik::logger::none);
2022-01-26 23:25:53 +01:00
SECTION("good maps")
{
std::vector<mapnik::fs::path> good_maps;
add_xml_files(mapnik::fs::path("test") / "data" / "good_maps", good_maps);
2022-01-26 23:25:53 +01:00
for (auto const& path : good_maps)
{
2020-11-21 19:16:47 +01:00
CAPTURE(path.generic_string());
2017-11-06 09:47:01 +01:00
// check that it can load
mapnik::Map m(256, 256);
REQUIRE(m.register_fonts("fonts", true));
REQUIRE_NOTHROW(load_map(m, path));
2017-11-06 09:47:01 +01:00
// and, if it can, that it saves the same way
compare_map(path);
}
} // END SECTION
2022-01-26 23:25:53 +01:00
SECTION("duplicate styles only throw in strict mode")
{
std::string duplicate_stylename(
(mapnik::fs::path("test") / "data" / "broken_maps" / "duplicate_stylename.xml").generic_string());
CAPTURE(duplicate_stylename);
mapnik::Map m(256, 256);
REQUIRE(m.register_fonts("fonts", true));
REQUIRE_NOTHROW(mapnik::load_map(m, duplicate_stylename, false));
mapnik::Map m2(256, 256);
REQUIRE(m2.register_fonts("fonts", true));
REQUIRE_THROWS(mapnik::load_map(m2, duplicate_stylename, true));
} // END SECTION
2022-01-26 23:25:53 +01:00
SECTION("broken maps")
{
std::vector<mapnik::fs::path> broken_maps;
add_xml_files(mapnik::fs::path("test") / "data" / "broken_maps", broken_maps);
broken_maps.emplace_back(mapnik::fs::path("test") / "data" / "broken_maps" / "does_not_exist.xml");
2022-01-26 23:25:53 +01:00
for (auto const& path : broken_maps)
{
2020-11-21 19:03:55 +01:00
CAPTURE(path.generic_string());
2017-11-06 09:47:01 +01:00
mapnik::Map m(256, 256);
REQUIRE(m.register_fonts("fonts", true));
2020-11-21 19:03:55 +01:00
REQUIRE_THROWS(mapnik::load_map(m, path.generic_string(), true));
2017-11-06 09:47:01 +01:00
}
} // END SECTION
2017-11-06 09:47:01 +01:00
mapnik::logger::instance().set_severity(severity);
} // END TEST CASE