summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/fixtures/resources/raster.tilebin32058 -> 95464 bytes
-rw-r--r--test/test.gypi1
-rw-r--r--test/util/memory.cpp228
3 files changed, 229 insertions, 0 deletions
diff --git a/test/fixtures/resources/raster.tile b/test/fixtures/resources/raster.tile
index 28ecadbb5e..43cc9a0c77 100644
--- a/test/fixtures/resources/raster.tile
+++ b/test/fixtures/resources/raster.tile
Binary files differ
diff --git a/test/test.gypi b/test/test.gypi
index e72718d865..304e0e9730 100644
--- a/test/test.gypi
+++ b/test/test.gypi
@@ -22,6 +22,7 @@
'util/geo.cpp',
'util/image.cpp',
'util/mapbox.cpp',
+ 'util/memory.cpp',
'util/merge_lines.cpp',
'util/run_loop.cpp',
'util/number_conversions.cpp',
diff --git a/test/util/memory.cpp b/test/util/memory.cpp
new file mode 100644
index 0000000000..bc20200916
--- /dev/null
+++ b/test/util/memory.cpp
@@ -0,0 +1,228 @@
+#include <mbgl/test/stub_file_source.hpp>
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/map/map.hpp>
+#include <mbgl/platform/default/headless_display.hpp>
+#include <mbgl/platform/default/headless_view.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace mbgl;
+using namespace std::literals::string_literals;
+
+long getRSS() {
+ auto statm = util::read_file("/proc/self/statm");
+
+ std::vector<std::string> stats;
+ std::istringstream stream(statm);
+
+ std::copy(std::istream_iterator<std::string>(stream),
+ std::istream_iterator<std::string>(),
+ std::back_inserter(stats));
+
+ return std::stol(stats[1]) * getpagesize();
+}
+
+bool isUsingJemalloc() {
+ const char* preload = getenv("LD_PRELOAD");
+
+ if (preload) {
+ return std::string(preload).find("libjemalloc.so") != std::string::npos;
+ } else {
+ return false;
+ }
+}
+
+class MemoryTest {
+public:
+ MemoryTest() {
+ fileSource.styleResponse = [&](const Resource& res) { return response("style_" + getType(res) + ".json");};
+ fileSource.tileResponse = [&](const Resource& res) { return response(getType(res) + ".tile"); };
+ fileSource.sourceResponse = [&](const Resource& res) { return response("source_" + getType(res) + ".json"); };
+ fileSource.glyphsResponse = [&](const Resource&) { return response("glyphs.pbf"); };
+ fileSource.spriteJSONResponse = [&](const Resource&) { return response("sprite.json"); };
+ fileSource.spriteImageResponse = [&](const Resource&) { return response("sprite.png"); };
+ }
+
+ util::RunLoop runLoop;
+ std::shared_ptr<HeadlessDisplay> display { std::make_shared<mbgl::HeadlessDisplay>() };
+ HeadlessView view { display, 2 };
+ StubFileSource fileSource;
+
+private:
+ Response response(const std::string& path) {
+ Response result;
+
+ auto it = cache.find(path);
+ if (it != cache.end()) {
+ result.data = it->second;
+ } else {
+ auto data = std::make_shared<std::string>(
+ util::read_file("test/fixtures/resources/"s + path));
+
+ cache.insert(it, std::make_pair(path, data));
+ result.data = data;
+ }
+
+ return result;
+ }
+
+ std::string getType(const Resource& res) {
+ if (res.url.find("satellite") != std::string::npos) {
+ return "raster";
+ } else {
+ return "vector";
+ }
+ };
+
+ std::unordered_map<std::string, std::shared_ptr<std::string>> cache;
+};
+
+TEST(Memory, Vector) {
+ MemoryTest test;
+
+ Map map(test.view, test.fileSource, MapMode::Still);
+ map.setZoom(16); // more map features
+ map.setStyleURL("mapbox://streets");
+
+ test::render(map);
+}
+
+TEST(Memory, Raster) {
+ MemoryTest test;
+
+ Map map(test.view, test.fileSource, MapMode::Still);
+ map.setStyleURL("mapbox://satellite");
+
+ test::render(map);
+}
+
+// This test will render 3 map objects alternating
+// between a raster and a vector style. Memory is
+// expected to grow between the renderings due to
+// fragmentation. A good allocator for Mapbox GL
+// Native will keep memory growth within acceptable
+// levels and stable in the long run.
+TEST(Memory, Fragmentation) {
+ if (!isUsingJemalloc()) {
+ return;
+ }
+
+ MemoryTest test;
+
+ Map map1(test.view, test.fileSource, MapMode::Still);
+ Map map2(test.view, test.fileSource, MapMode::Still);
+ Map map3(test.view, test.fileSource, MapMode::Still);
+
+ map1.setZoom(16);
+ map2.setZoom(16);
+ map3.setZoom(16);
+
+ auto renderMap = [&] {
+ map1.setStyleURL("mapbox://satellite");
+ test::render(map1);
+
+ map2.setStyleURL("mapbox://satellite");
+ test::render(map2);
+
+ map3.setStyleURL("mapbox://satellite");
+ test::render(map3);
+
+ map1.setStyleURL("mapbox://streets");
+ test::render(map1);
+
+ map2.setStyleURL("mapbox://streets");
+ test::render(map2);
+
+ map3.setStyleURL("mapbox://streets");
+ test::render(map3);
+ };
+
+ // Warm up buffers and cache.
+ for (unsigned i = 0; i < 5; ++i) {
+ renderMap();
+ }
+
+ long lastRSS = getRSS();
+ long memoryFragments = 0;
+
+ for (unsigned i = 0; i < 20; ++i) {
+ renderMap();
+
+ long currentRSS = getRSS();
+
+ memoryFragments += currentRSS - lastRSS;
+ lastRSS = currentRSS;
+ }
+
+ ASSERT_LT(memoryFragments, 10 * 1024 * 1024) << "\
+ Abnormal memory growth detected.";
+}
+
+// This test will measure the size of a Map object
+// after rendering a raster and a vector style. The
+// idea is to try to keep the memory footprint within
+// reasonable limits, so this test acts more like a
+// safeguard.
+TEST(Memory, Footprint) {
+ if (!isUsingJemalloc()) {
+ return;
+ }
+
+ MemoryTest test;
+
+ auto renderMap = [](Map* map, const char* style){
+ map->setZoom(16);
+
+ map->setStyleURL(style);
+ test::render(*map);
+ };
+
+ // Warm up buffers and cache.
+ for (unsigned i = 0; i < 10; ++i) {
+ Map map(test.view, test.fileSource, MapMode::Still);
+ renderMap(&map, "mapbox://streets");
+ renderMap(&map, "mapbox://satellite");
+ };
+
+ // Process close callbacks, mostly needed by
+ // libuv runloop.
+ test.runLoop.runOnce();
+
+ std::vector<std::unique_ptr<Map>> maps;
+ unsigned runs = 15;
+
+ long vectorInitialRSS = getRSS();
+ for (unsigned i = 0; i < runs; ++i) {
+ auto vector = std::make_unique<Map>(test.view, test.fileSource, MapMode::Still);
+ renderMap(vector.get(), "mapbox://streets");
+ maps.push_back(std::move(vector));
+ };
+
+ double vectorFootprint = (getRSS() - vectorInitialRSS) / double(runs);
+
+ long rasterInitialRSS = getRSS();
+ for (unsigned i = 0; i < runs; ++i) {
+ auto raster = std::make_unique<Map>(test.view, test.fileSource, MapMode::Still);
+ renderMap(raster.get(), "mapbox://satellite");
+ maps.push_back(std::move(raster));
+ };
+
+ double rasterFootprint = (getRSS() - rasterInitialRSS) / double(runs);
+
+ ASSERT_LT(vectorFootprint, 65 * 1024 * 1024) << "\
+ mbgl::Map footprint over 65MB for vector styles.";
+
+ ASSERT_LT(rasterFootprint, 25 * 1024 * 1024) << "\
+ mbgl::Map footprint over 25MB for raster styles.";
+}