File: map_xml_test.cpp

package info (click to toggle)
mapnik 4.2.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 18,548 kB
  • sloc: cpp: 163,861; python: 1,190; sh: 690; xml: 161; makefile: 123; perl: 28; lisp: 13
file content (216 lines) | stat: -rw-r--r-- 7,436 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#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();
}

class tmp_dir
{
  private:
    mapnik::fs::path m_path;

  public:
    tmp_dir()
        : m_path(mapnik::fs::temp_directory_path() / unique_mapnik_name())
    {
        mapnik::fs::create_directories(m_path);
    }

    ~tmp_dir()
    {
        // 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;

        // catch all errors - we don't want to throw in the destructor
        try
        {
            // but loop while the path exists and the errors are
            // ignorable.
            while (mapnik::fs::exists(m_path))
            {
                mapnik::fs::remove_all(m_path, err);

                // 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))
                {
                    break;
                }
            }
        }
        catch (std::exception const& e)
        {
            std::cerr << "Exception caught while trying to remove " << "temporary directory " << m_path << ": "
                      << e.what() << "\n";
        }
        catch (...)
        {
            std::cerr << "Unknown exception caught while trying to " << "remove temporary directory " << m_path << "\n";
        }
    }

    mapnik::fs::path path() const { return m_path; }
};

void compare_map(mapnik::fs::path xml)
{
    tmp_dir dir;
    mapnik::Map m(256, 256);
    REQUIRE(m.register_fonts("fonts", true));
    mapnik::fs::path abs_base = xml.parent_path();

    // 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...
    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";
    REQUIRE_NOTHROW(mapnik::save_map(m, test_map1.generic_string()));

    // 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));
    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";
    REQUIRE_NOTHROW(mapnik::save_map(new_map, test_map2.generic_string()));

    // 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));
    std::ifstream in_map1(test_map1.native()), in_map2(test_map2.native());
    REQUIRE(std::equal(std::istream_iterator<char>(in_map1),
                       std::istream_iterator<char>(),
                       std::istream_iterator<char>(in_map2)));
}

void add_xml_files(mapnik::fs::path dir, std::vector<mapnik::fs::path>& xml_files)
{
    for (auto const& entry :
         boost::make_iterator_range(mapnik::fs::directory_iterator(dir), mapnik::fs::directory_iterator()))
    {
        auto path = entry.path();
        if (path.extension().generic_string() == ".xml")
        {
            xml_files.emplace_back(path);
        }
    }
}

void load_map(mapnik::Map& m, mapnik::fs::path const& path)
{
    try
    {
        mapnik::load_map(m, path.generic_string());
    }
    catch (std::exception const& ex)
    {
        // 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) &&
            (what.find("Postgis Plugin: could not connect to server") == std::string::npos))
        {
            throw;
        }
    }
}

} // anonymous namespace
#ifndef MAPNIK_STATIC_PLUGINS
bool const registered =
  mapnik::datasource_cache::instance().register_datasources((mapnik::fs::path("plugins") / "input").generic_string());
#endif
TEST_CASE("map xml I/O")
{
    mapnik::setup();
#ifndef MAPNIK_STATIC_PLUGINS
    // make sure plugins are loaded
    REQUIRE(registered);
#endif

    // 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);

    SECTION("good maps")
    {
        std::vector<mapnik::fs::path> good_maps;
        add_xml_files(mapnik::fs::path("test") / "data" / "good_maps", good_maps);

        for (auto const& path : good_maps)
        {
            CAPTURE(path.generic_string());

            // check that it can load
            mapnik::Map m(256, 256);
            REQUIRE(m.register_fonts("fonts", true));
            REQUIRE_NOTHROW(load_map(m, path));

            // and, if it can, that it saves the same way
            compare_map(path);
        }
    } // END SECTION

    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

    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");

        for (auto const& path : broken_maps)
        {
            CAPTURE(path.generic_string());

            mapnik::Map m(256, 256);
            REQUIRE(m.register_fonts("fonts", true));
            REQUIRE_THROWS(mapnik::load_map(m, path.generic_string(), true));
        }
    } // END SECTION

    mapnik::logger::instance().set_severity(severity);
} // END TEST CASE