summaryrefslogtreecommitdiff
path: root/test/util
diff options
context:
space:
mode:
Diffstat (limited to 'test/util')
-rw-r--r--test/util/http_timeout.test.cpp15
-rw-r--r--test/util/image.test.cpp29
-rw-r--r--test/util/mapbox.test.cpp3
-rw-r--r--test/util/memory.test.cpp54
-rw-r--r--test/util/merge_lines.test.cpp136
-rw-r--r--test/util/offscreen_texture.test.cpp88
-rw-r--r--test/util/thread.test.cpp78
-rw-r--r--test/util/url.test.cpp20
8 files changed, 291 insertions, 132 deletions
diff --git a/test/util/http_timeout.test.cpp b/test/util/http_timeout.test.cpp
index e99c703159..c9373d955d 100644
--- a/test/util/http_timeout.test.cpp
+++ b/test/util/http_timeout.test.cpp
@@ -2,22 +2,25 @@
#include <mbgl/util/logging.hpp>
#include <mbgl/util/http_timeout.hpp>
-#include <regex>
-#include <iostream>
using namespace mbgl;
using namespace mbgl::http;
TEST(HttpRetry, OtherError) {
- //Non-retryable
+ // Non-retryable
ASSERT_EQ(Duration::max(), errorRetryTimeout(Response::Error::Reason::Other, 1));
}
+TEST(HttpRetry, NotFound) {
+ // Non-retryable
+ ASSERT_EQ(Duration::max(), errorRetryTimeout(Response::Error::Reason::NotFound, 1));
+}
+
TEST(HttpRetry, ServerError) {
// 1-3 failures -> 1 sec
ASSERT_EQ(Seconds(1), errorRetryTimeout(Response::Error::Reason::Server, 1));
ASSERT_EQ(Seconds(1), errorRetryTimeout(Response::Error::Reason::Server, 3));
-
+
// After 3, exponential backoff
ASSERT_EQ(Seconds(2), errorRetryTimeout(Response::Error::Reason::Server, 4));
ASSERT_EQ(Seconds(1u << 31), errorRetryTimeout(Response::Error::Reason::Server, 50));
@@ -32,8 +35,8 @@ TEST(HttpRetry, ConnectionError) {
TEST(HttpRetry, RateLimit) {
// Pre-set value from header
ASSERT_EQ(Seconds(1), errorRetryTimeout(Response::Error::Reason::Server, 1, { util::now() + Seconds(1) }));
-
- //Default
+
+ // Default
ASSERT_EQ(Seconds(5), errorRetryTimeout(Response::Error::Reason::RateLimit, 1, {}));
}
diff --git a/test/util/image.test.cpp b/test/util/image.test.cpp
index b15ddc1b3f..0cd4a7d8af 100644
--- a/test/util/image.test.cpp
+++ b/test/util/image.test.cpp
@@ -86,6 +86,35 @@ TEST(Image, WebPTile) {
}
#endif // !defined(__ANDROID__) && !defined(__APPLE__) && !defined(QT_IMAGE_DECODERS)
+TEST(Image, Copy) {
+ PremultipliedImage src5({5, 5});
+ PremultipliedImage dst5({5, 5});
+ PremultipliedImage src10({10, 10});
+ PremultipliedImage dst10({10, 10});
+
+ EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {6, 0}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {0, 6}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {5, 0}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {0, 5}), std::out_of_range);
+
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {6, 0}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {0, 6}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {5, 0}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {0, 5}), std::out_of_range);
+
+ const uint32_t max = std::numeric_limits<uint32_t>::max();
+
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {max, 0}, {0, 0}, {1, 0}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, max}, {0, 0}, {0, 1}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {max, 0}, {1, 0}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, max}, {0, 1}), std::out_of_range);
+
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {1, 0}, {0, 0}, {max, 0}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 1}, {0, 0}, {0, max}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {1, 0}, {max, 0}), std::out_of_range);
+ EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, 1}, {0, max}), std::out_of_range);
+}
+
TEST(Image, Premultiply) {
UnassociatedImage rgba({ 1, 1 });
rgba.data[0] = 255;
diff --git a/test/util/mapbox.test.cpp b/test/util/mapbox.test.cpp
index 452106d6e6..299f0df833 100644
--- a/test/util/mapbox.test.cpp
+++ b/test/util/mapbox.test.cpp
@@ -3,8 +3,7 @@
#include <mbgl/util/logging.hpp>
#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/constants.hpp>
-#include <regex>
-#include <iostream>
+#include <stdexcept>
using namespace mbgl;
diff --git a/test/util/memory.test.cpp b/test/util/memory.test.cpp
index 984e7a3e24..d49c49018f 100644
--- a/test/util/memory.test.cpp
+++ b/test/util/memory.test.cpp
@@ -1,4 +1,5 @@
#include <mbgl/test/stub_file_source.hpp>
+#include <mbgl/test/getrss.hpp>
#include <mbgl/test/util.hpp>
#include <mbgl/map/map.hpp>
@@ -21,29 +22,6 @@
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() {
@@ -109,16 +87,31 @@ TEST(Memory, Raster) {
test::render(map, test.view);
}
+/**
+On CI, we only run the memory footprint test in the Qt build, because it uses
+jemalloc, which yields more consistent memory usage results. To force it to
+run locally, use `DO_MEMORY_FOOTPRINT=1 make run-test-Memory.Footprint.
+*/
+bool shouldRunFootprint() {
+ const char* preload = getenv("LD_PRELOAD");
+
+ if (preload) {
+ return std::string(preload).find("libjemalloc.so") != std::string::npos;
+ } else {
+ return getenv("DO_MEMORY_FOOTPRINT");
+ }
+}
+
// 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()) {
+ if (!shouldRunFootprint()) {
return;
}
-
+
MemoryTest test;
auto renderMap = [&](Map& map, const char* style){
@@ -141,7 +134,7 @@ TEST(Memory, Footprint) {
std::vector<std::unique_ptr<Map>> maps;
unsigned runs = 15;
- long vectorInitialRSS = getRSS();
+ long vectorInitialRSS = mbgl::test::getCurrentRSS();
for (unsigned i = 0; i < runs; ++i) {
auto vector = std::make_unique<Map>(test.backend, Size{ 256, 256 }, 2, test.fileSource,
test.threadPool, MapMode::Still);
@@ -149,9 +142,9 @@ TEST(Memory, Footprint) {
maps.push_back(std::move(vector));
};
- double vectorFootprint = (getRSS() - vectorInitialRSS) / double(runs);
+ double vectorFootprint = (mbgl::test::getCurrentRSS() - vectorInitialRSS) / double(runs);
- long rasterInitialRSS = getRSS();
+ long rasterInitialRSS = mbgl::test::getCurrentRSS();
for (unsigned i = 0; i < runs; ++i) {
auto raster = std::make_unique<Map>(test.backend, Size{ 256, 256 }, 2, test.fileSource,
test.threadPool, MapMode::Still);
@@ -159,7 +152,10 @@ TEST(Memory, Footprint) {
maps.push_back(std::move(raster));
};
- double rasterFootprint = (getRSS() - rasterInitialRSS) / double(runs);
+ double rasterFootprint = (mbgl::test::getCurrentRSS() - rasterInitialRSS) / double(runs);
+
+ RecordProperty("vectorFootprint", vectorFootprint);
+ RecordProperty("rasterFootprint", rasterFootprint);
ASSERT_LT(vectorFootprint, 65 * 1024 * 1024) << "\
mbgl::Map footprint over 65MB for vector styles.";
diff --git a/test/util/merge_lines.test.cpp b/test/util/merge_lines.test.cpp
index 9a8f01ef35..8a3a400887 100644
--- a/test/util/merge_lines.test.cpp
+++ b/test/util/merge_lines.test.cpp
@@ -8,95 +8,131 @@ const std::u16string bbb = u"b";
using namespace mbgl;
-TEST(MergeLines, SameText) {
- // merges lines with the same text
- std::vector<mbgl::SymbolFeature> input1 = {
- { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, 0 },
- { FeatureType::LineString, {{{8, 0}, {9, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{{6, 0}, {7, 0}, {8, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{{5, 0}, {6, 0}}}, aaa, {}, 0 }
+class GeometryTileFeatureStub : public GeometryTileFeature {
+public:
+ GeometryTileFeatureStub(optional<FeatureIdentifier> id_, FeatureType type_, GeometryCollection geometry_,
+ std::unordered_map<std::string, Value> properties_) :
+ id(id_),
+ type(type_),
+ geometry(geometry_),
+ properties(properties_)
+ {}
+
+ FeatureType getType() const override { return type; }
+ optional<Value> getValue(const std::string& key) const override {
+ auto it = properties.find(key);
+ if (it != properties.end()) {
+ return it->second;
+ }
+ return {};
};
+ std::unordered_map<std::string,Value> getProperties() const override { return properties; };
+ optional<FeatureIdentifier> getID() const override { return id; };
+ GeometryCollection getGeometries() const override { return geometry; };
+
+ optional<FeatureIdentifier> id;
+ FeatureType type;
+ GeometryCollection geometry;
+ std::unordered_map<std::string,Value> properties;
+};
+
+class SymbolFeatureStub : public SymbolFeature {
+public:
+ SymbolFeatureStub(optional<FeatureIdentifier> id_, FeatureType type_, GeometryCollection geometry_,
+ std::unordered_map<std::string, Value> properties_, optional<std::u16string> text_,
+ optional<std::string> icon_, std::size_t index_) :
+ SymbolFeature(std::make_unique<GeometryTileFeatureStub>(id_, type_, geometry_, properties_))
+ {
+ text = text_;
+ icon = icon_;
+ index = index_;
+ }
+};
- const std::vector<mbgl::SymbolFeature> expected1 = {
- { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, 0 },
- { FeatureType::LineString, {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{}}, aaa, {}, 0 },
- { FeatureType::LineString, {{}}, aaa, {}, 0 },
- { FeatureType::LineString, {{}}, aaa, {}, 0 }
+TEST(MergeLines, SameText) {
+ // merges lines with the same text
+ std::vector<mbgl::SymbolFeature> input1;
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0));
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {}, bbb, {}, 0));
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{8, 0}, {9, 0}}}, {}, aaa, {}, 0));
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0));
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{6, 0}, {7, 0}, {8, 0}}}, {}, aaa, {}, 0));
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{5, 0}, {6, 0}}}, {}, aaa, {}, 0));
+
+ const std::vector<GeometryTileFeatureStub> expected1 = {
+ { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, {} },
+ { {}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {} },
+ { {}, FeatureType::LineString, {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, {} },
+ { {}, FeatureType::LineString, {{}}, {} },
+ { {}, FeatureType::LineString, {{}}, {} },
+ { {}, FeatureType::LineString, {{}}, {} }
};
-
+
mbgl::util::mergeLines(input1);
for (int i = 0; i < 6; i++) {
- EXPECT_TRUE(input1[i].geometry == expected1[i].geometry);
+ EXPECT_TRUE(input1[i].geometry == expected1[i].getGeometries());
}
}
TEST(MergeLines, BothEnds) {
// mergeLines handles merge from both ends
- std::vector<mbgl::SymbolFeature> input2 = {
- { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 }
- };
-
- const std::vector<mbgl::SymbolFeature> expected2 = {
- { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{}}, aaa, {}, 0 },
- { FeatureType::LineString, {{}}, aaa, {}, 0 }
+ std::vector<mbgl::SymbolFeature> input2;
+ input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0 });
+ input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {}, aaa, {}, 0 });
+ input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0 });
+
+ const std::vector<GeometryTileFeatureStub> expected2 = {
+ { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, {} },
+ { {}, FeatureType::LineString, {{}}, {} },
+ { {}, FeatureType::LineString, {{}}, {} }
};
mbgl::util::mergeLines(input2);
for (int i = 0; i < 3; i++) {
- EXPECT_TRUE(input2[i].geometry == expected2[i].geometry);
+ EXPECT_TRUE(input2[i].geometry == expected2[i].getGeometries());
}
}
TEST(MergeLines, CircularLines) {
// mergeLines handles circular lines
- std::vector<mbgl::SymbolFeature> input3 = {
- { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{{4, 0}, {0, 0}}}, aaa, {}, 0 }
- };
-
- const std::vector<mbgl::SymbolFeature> expected3 = {
- { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, aaa, {}, 0 },
- { FeatureType::LineString, {{}}, aaa, {}, 0 },
- { FeatureType::LineString, {{}}, aaa, {}, 0 }
+ std::vector<mbgl::SymbolFeature> input3;
+ input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0 });
+ input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0 });
+ input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{4, 0}, {0, 0}}}, {}, aaa, {}, 0 });
+
+ const std::vector<GeometryTileFeatureStub> expected3 = {
+ { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, {} },
+ { {}, FeatureType::LineString, {{}}, {} },
+ { {}, FeatureType::LineString, {{}}, {} }
};
mbgl::util::mergeLines(input3);
for (int i = 0; i < 3; i++) {
- EXPECT_TRUE(input3[i].geometry == expected3[i].geometry);
+ EXPECT_TRUE(input3[i].geometry == expected3[i].getGeometries());
}
}
TEST(MergeLines, EmptyOuterGeometry) {
- std::vector<mbgl::SymbolFeature> input = {
- { FeatureType::LineString, {}, aaa, {}, 0 },
- };
+ std::vector<mbgl::SymbolFeature> input;
+ input.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {}, {}, aaa, {}, 0 });
- const std::vector<mbgl::SymbolFeature> expected = input;
+ const std::vector<GeometryTileFeatureStub> expected = { { {}, FeatureType::LineString, {}, {} } };
mbgl::util::mergeLines(input);
- EXPECT_EQ(expected[0].geometry, input[0].geometry);
+ EXPECT_EQ(input[0].geometry, expected[0].getGeometries());
}
TEST(MergeLines, EmptyInnerGeometry) {
- std::vector<mbgl::SymbolFeature> input = {
- { FeatureType::LineString, {{}}, aaa, {}, 0 },
- };
+ std::vector<mbgl::SymbolFeature> input;
+ input.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{}}, {}, aaa, {}, 0 });
- const std::vector<mbgl::SymbolFeature> expected = input;
+ const std::vector<GeometryTileFeatureStub> expected = { { {}, FeatureType::LineString, {{}}, {} } };
mbgl::util::mergeLines(input);
- EXPECT_EQ(expected[0].geometry, input[0].geometry);
+ EXPECT_EQ(input[0].geometry, expected[0].getGeometries());
}
diff --git a/test/util/offscreen_texture.test.cpp b/test/util/offscreen_texture.test.cpp
index 31fb985394..feaabf2630 100644
--- a/test/util/offscreen_texture.test.cpp
+++ b/test/util/offscreen_texture.test.cpp
@@ -4,6 +4,7 @@
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/headless_backend.hpp>
#include <mbgl/gl/offscreen_view.hpp>
+#include <mbgl/map/backend_scope.hpp>
#include <mbgl/util/offscreen_texture.hpp>
@@ -11,6 +12,7 @@ using namespace mbgl;
TEST(OffscreenTexture, EmptyRed) {
HeadlessBackend backend { test::sharedDisplay() };
+ BackendScope scope { backend };
OffscreenView view(backend.getContext(), { 512, 256 });
view.bind();
@@ -68,6 +70,7 @@ struct Buffer {
TEST(OffscreenTexture, RenderToTexture) {
HeadlessBackend backend { test::sharedDisplay() };
+ BackendScope scope { backend };
auto& context = backend.getContext();
MBGL_CHECK_ERROR(glEnable(GL_BLEND));
@@ -116,50 +119,45 @@ void main() {
Buffer triangleBuffer({ 0, 0.5, 0.5, -0.5, -0.5, -0.5 });
Buffer viewportBuffer({ -1, -1, 1, -1, -1, 1, 1, 1 });
- // Make sure the texture gets destructed before we call context.reset();
- {
- OffscreenView view(context, { 512, 256 });
- view.bind();
-
- // First, draw red to the bound FBO.
- context.clear(Color::red(), {}, {});
-
- // Then, create a texture, bind it, and render yellow to that texture. This should not
- // affect the originally bound FBO.
- OffscreenTexture texture(context, { 128, 128 });
- texture.bind();
-
- context.clear(Color(), {}, {});
-
- MBGL_CHECK_ERROR(glUseProgram(paintShader.program));
- MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, triangleBuffer.buffer));
- MBGL_CHECK_ERROR(glEnableVertexAttribArray(paintShader.a_pos));
- MBGL_CHECK_ERROR(
- glVertexAttribPointer(paintShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
- MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 3));
-
- auto image = texture.readStillImage();
- test::checkImage("test/fixtures/offscreen_texture/render-to-texture", image, 0, 0);
-
- // Now reset the FBO back to normal and retrieve the original (restored) framebuffer.
- view.bind();
-
- image = view.readStillImage();
- test::checkImage("test/fixtures/offscreen_texture/render-to-fbo", image, 0, 0);
-
- // Now, composite the Framebuffer texture we've rendered to onto the main FBO.
- context.bindTexture(texture.getTexture(), 0, gl::TextureFilter::Linear);
- MBGL_CHECK_ERROR(glUseProgram(compositeShader.program));
- MBGL_CHECK_ERROR(glUniform1i(u_texture, 0));
- MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, viewportBuffer.buffer));
- MBGL_CHECK_ERROR(glEnableVertexAttribArray(compositeShader.a_pos));
- MBGL_CHECK_ERROR(
- glVertexAttribPointer(compositeShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
- MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
-
- image = view.readStillImage();
- test::checkImage("test/fixtures/offscreen_texture/render-to-fbo-composited", image, 0, 0.1);
- }
+ OffscreenView view(context, { 512, 256 });
+ view.bind();
+
+ // First, draw red to the bound FBO.
+ context.clear(Color::red(), {}, {});
+
+ // Then, create a texture, bind it, and render yellow to that texture. This should not
+ // affect the originally bound FBO.
+ OffscreenTexture texture(context, { 128, 128 });
+ texture.bind();
+
+ context.clear(Color(), {}, {});
+
+ MBGL_CHECK_ERROR(glUseProgram(paintShader.program));
+ MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, triangleBuffer.buffer));
+ MBGL_CHECK_ERROR(glEnableVertexAttribArray(paintShader.a_pos));
+ MBGL_CHECK_ERROR(
+ glVertexAttribPointer(paintShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
+ MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 3));
+
+ auto image = texture.readStillImage();
+ test::checkImage("test/fixtures/offscreen_texture/render-to-texture", image, 0, 0);
+
+ // Now reset the FBO back to normal and retrieve the original (restored) framebuffer.
+ view.bind();
- context.reset();
+ image = view.readStillImage();
+ test::checkImage("test/fixtures/offscreen_texture/render-to-fbo", image, 0, 0);
+
+ // Now, composite the Framebuffer texture we've rendered to onto the main FBO.
+ context.bindTexture(texture.getTexture(), 0, gl::TextureFilter::Linear);
+ MBGL_CHECK_ERROR(glUseProgram(compositeShader.program));
+ MBGL_CHECK_ERROR(glUniform1i(u_texture, 0));
+ MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, viewportBuffer.buffer));
+ MBGL_CHECK_ERROR(glEnableVertexAttribArray(compositeShader.a_pos));
+ MBGL_CHECK_ERROR(
+ glVertexAttribPointer(compositeShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
+ MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
+
+ image = view.readStillImage();
+ test::checkImage("test/fixtures/offscreen_texture/render-to-fbo-composited", image, 0, 0.1);
}
diff --git a/test/util/thread.test.cpp b/test/util/thread.test.cpp
index fc41fd4b78..972bddf383 100644
--- a/test/util/thread.test.cpp
+++ b/test/util/thread.test.cpp
@@ -3,6 +3,8 @@
#include <mbgl/test/util.hpp>
+#include <atomic>
+
using namespace mbgl::util;
class TestObject {
@@ -216,3 +218,79 @@ TEST(Thread, WorkRequestDeletionCancelsImmediately) {
started.get_future().get();
request1.reset();
}
+
+TEST(Thread, DeletePausedThread) {
+ RunLoop loop;
+
+ std::atomic_bool flag(false);
+
+ auto thread = std::make_unique<Thread<TestWorker>>(ThreadContext{"Test"});
+ thread->pause();
+ thread->invoke(&TestWorker::send, [&] { flag = true; }, [] {});
+
+ // Should not hang.
+ thread.reset();
+
+ // Should process the queue before destruction.
+ ASSERT_TRUE(flag);
+}
+
+TEST(Thread, Pause) {
+ RunLoop loop;
+
+ std::atomic_bool flag(false);
+
+ Thread<TestWorker> thread1({"Test1"});
+ thread1.pause();
+
+ Thread<TestWorker> thread2({"Test2"});
+
+ for (unsigned i = 0; i < 100; ++i) {
+ thread1.invoke(&TestWorker::send, [&] { flag = true; }, [] {});
+ thread2.invoke(&TestWorker::send, [&] { ASSERT_FALSE(flag); }, [] {});
+ }
+
+ // Queue a message at the end of thread2 queue.
+ thread2.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {});
+ loop.run();
+}
+
+TEST(Thread, Resume) {
+ RunLoop loop;
+
+ std::atomic_bool flag(false);
+
+ Thread<TestWorker> thread({"Test"});
+ thread.pause();
+
+ for (unsigned i = 0; i < 100; ++i) {
+ thread.invoke(&TestWorker::send, [&] { flag = true; }, [] {});
+ }
+
+ // Thread messages are ondered, when we resume, this is going
+ // to me the last thing to run on the message queue.
+ thread.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {});
+
+ // This test will be flaky if the thread doesn't get paused.
+ ASSERT_FALSE(flag);
+
+ thread.resume();
+ loop.run();
+
+ ASSERT_TRUE(flag);
+}
+
+TEST(Thread, PauseResume) {
+ RunLoop loop;
+
+ Thread<TestWorker> thread({"Test"});
+
+ // Test if multiple pause/resume work.
+ for (unsigned i = 0; i < 100; ++i) {
+ thread.pause();
+ thread.resume();
+ }
+
+ thread.invoke(&TestWorker::send, [&] { loop.stop(); }, [] {});
+ loop.run();
+}
diff --git a/test/util/url.test.cpp b/test/util/url.test.cpp
index 2acf3cb0db..ca24a5949a 100644
--- a/test/util/url.test.cpp
+++ b/test/util/url.test.cpp
@@ -26,6 +26,9 @@ TEST(URL, isURL) {
TEST(URL, Scheme) {
EXPECT_EQ(URL::Segment({ 0, 4 }), URL("http://example.com/test?query=foo").scheme);
+ EXPECT_EQ(URL::Segment({ 0, 4 }), URL("http://127.0.0.1:8080/test?query=foo").scheme);
+ EXPECT_EQ(URL::Segment({ 0, 4 }), URL("http://[2a01:4f8:c17:3680::386a:6f3d]:8080/test?query=foo").scheme);
+ EXPECT_EQ(URL::Segment({ 0, 4 }), URL("http://user:password@example.com.:80/test?query=foo").scheme);
EXPECT_EQ(URL::Segment({ 0, 0 }), URL("htt").scheme);
EXPECT_EQ(URL::Segment({ 0, 6 }), URL("mapbox://").scheme);
EXPECT_EQ(URL::Segment({ 0, 6 }), URL("mapbox:/#").scheme);
@@ -33,12 +36,17 @@ TEST(URL, Scheme) {
EXPECT_EQ(URL::Segment({ 0, 0 }), URL("").scheme);
EXPECT_EQ(URL::Segment({ 0, 0 }), URL("http?query://baz").scheme);
EXPECT_EQ(URL::Segment({ 0, 0 }), URL(":::").scheme);
+ EXPECT_EQ(URL::Segment({ 0, 0 }), URL("127.0.0.1:8080/test?query=foo").scheme);
+ EXPECT_EQ(URL::Segment({ 0, 0 }), URL("[2a01:4f8:c17:3680::386a:6f3d]:8080/test?query=foo").scheme);
EXPECT_EQ(URL::Segment({ 0, 4 }), URL("data:,Hello%2C%20World!").scheme);
EXPECT_EQ(URL::Segment({ 0, 4 }), URL("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D").scheme);
}
TEST(URL, Query) {
EXPECT_EQ(URL::Segment({ 23, 10 }), URL("http://example.com/test?query=foo").query);
+ EXPECT_EQ(URL::Segment({ 26, 10 }), URL("http://127.0.0.1:8080/test?query=foo").query);
+ EXPECT_EQ(URL::Segment({ 47, 10 }), URL("http://[2a01:4f8:c17:3680::386a:6f3d]:8080/test?query=foo").query);
+ EXPECT_EQ(URL::Segment({ 41, 10 }), URL("http://user:password@example.com.:80/test?query=foo").query);
EXPECT_EQ(URL::Segment({ 23, 10 }), URL("http://example.com/test?query=foo#page-2").query);
EXPECT_EQ(URL::Segment({ 23, 0 }), URL("http://example.com/test#query=foo?page-2").query);
EXPECT_EQ(URL::Segment({ 0, 10 }), URL("?query=foo").query);
@@ -49,12 +57,17 @@ TEST(URL, Query) {
EXPECT_EQ(URL::Segment({ 12, 0 }), URL("mapbox://bar").query);
EXPECT_EQ(URL::Segment({ 0, 0 }), URL("").query);
EXPECT_EQ(URL::Segment({ 3, 0 }), URL(":::").query);
+ EXPECT_EQ(URL::Segment({ 19, 10 }), URL("127.0.0.1:8080/test?query=foo").query);
+ EXPECT_EQ(URL::Segment({ 40, 10 }), URL("[2a01:4f8:c17:3680::386a:6f3d]:8080/test?query=foo").query);
EXPECT_EQ(URL::Segment({ 23, 0 }), URL("data:,Hello%2C%20World!").query);
EXPECT_EQ(URL::Segment({ 47, 0 }), URL("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D").query);
}
TEST(URL, Domain) {
EXPECT_EQ(URL::Segment({ 7, 11 }), URL("http://example.com/test?query=foo").domain);
+ EXPECT_EQ(URL::Segment({ 7, 14 }), URL("http://127.0.0.1:8080/test?query=foo").domain);
+ EXPECT_EQ(URL::Segment({ 7, 35 }), URL("http://[2a01:4f8:c17:3680::386a:6f3d]:8080/test?query=foo").domain);
+ EXPECT_EQ(URL::Segment({ 7, 29 }), URL("http://user:password@example.com.:80/test?query=foo").domain);
EXPECT_EQ(URL::Segment({ 5, 11 }), URL("http:example.com/test?query=foo").domain);
EXPECT_EQ(URL::Segment({ 0, 3 }), URL("htt").domain);
EXPECT_EQ(URL::Segment({ 0, 4 }), URL("http?query://baz").domain);
@@ -71,18 +84,25 @@ TEST(URL, Domain) {
EXPECT_EQ(URL::Segment({ 7, 6 }), URL("http://domain?").domain);
EXPECT_EQ(URL::Segment({ 7, 6 }), URL("http://domain?foo").domain);
EXPECT_EQ(URL::Segment({ 3, 0 }), URL(":::").domain);
+ EXPECT_EQ(URL::Segment({ 0, 14 }), URL("127.0.0.1:8080/test?query=foo").domain);
+ EXPECT_EQ(URL::Segment({ 0, 35 }), URL("[2a01:4f8:c17:3680::386a:6f3d]:8080/test?query=foo").domain);
EXPECT_EQ(URL::Segment({ 5, 0 }), URL("data:,Hello%2C%20World!").domain);
EXPECT_EQ(URL::Segment({ 5, 17 }), URL("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D").domain);
}
TEST(URL, Path) {
EXPECT_EQ(URL::Segment({ 18, 5 }), URL("http://example.com/test?query=foo").path);
+ EXPECT_EQ(URL::Segment({ 21, 5 }), URL("http://127.0.0.1:8080/test?query=foo").path);
+ EXPECT_EQ(URL::Segment({ 42, 5 }), URL("http://[2a01:4f8:c17:3680::386a:6f3d]:8080/test?query=foo").path);
+ EXPECT_EQ(URL::Segment({ 36, 5 }), URL("http://user:password@example.com.:80/test?query=foo").path);
EXPECT_EQ(URL::Segment({ 18, 5 }), URL("http://example.com/test?query=foo#bar").path);
EXPECT_EQ(URL::Segment({ 18, 5 }), URL("http://example.com/test#bar").path);
EXPECT_EQ(URL::Segment({ 18, 0 }), URL("http://example.com?query=foo").path);
EXPECT_EQ(URL::Segment({ 18, 0 }), URL("http://example.com#?query=foo").path);
EXPECT_EQ(URL::Segment({ 18, 1 }), URL("http://example.com/?query=foo").path);
EXPECT_EQ(URL::Segment({ 3, 0 }), URL(":::").path);
+ EXPECT_EQ(URL::Segment({ 14, 5 }), URL("127.0.0.1:8080/test?query=foo").path);
+ EXPECT_EQ(URL::Segment({ 35, 5 }), URL("[2a01:4f8:c17:3680::386a:6f3d]:8080/test?query=foo").path);
EXPECT_EQ(URL::Segment({ 13, 0 }), URL("http://domain").path);
EXPECT_EQ(URL::Segment({ 6, 4 }), URL("domain/foo?bar").path);
EXPECT_EQ(URL::Segment({ 6, 17 }), URL("data:,Hello%2C%20World!").path);