diff options
Diffstat (limited to 'platform/node')
-rw-r--r-- | platform/node/CHANGELOG.md | 19 | ||||
-rw-r--r-- | platform/node/README.md | 6 | ||||
-rw-r--r-- | platform/node/bitrise.yml | 2 | ||||
-rw-r--r-- | platform/node/index.js | 27 | ||||
-rwxr-xr-x | platform/node/scripts/after_success.sh | 9 | ||||
-rw-r--r-- | platform/node/src/node_conversion.hpp | 8 | ||||
-rw-r--r-- | platform/node/src/node_geojson.hpp | 8 | ||||
-rw-r--r-- | platform/node/src/node_map.cpp | 208 | ||||
-rw-r--r-- | platform/node/src/node_map.hpp | 20 | ||||
-rw-r--r-- | platform/node/src/node_request.cpp | 10 | ||||
-rw-r--r-- | platform/node/src/node_request.hpp | 6 | ||||
-rw-r--r-- | platform/node/test/ignores.json | 59 | ||||
-rw-r--r-- | platform/node/test/js/map.test.js | 2 | ||||
-rw-r--r-- | platform/node/test/js/request.test.js | 167 | ||||
-rw-r--r-- | platform/node/test/js/request_fail.test.js | 59 | ||||
-rw-r--r-- | platform/node/test/js/request_notfound.test.js | 74 | ||||
-rw-r--r-- | platform/node/test/mockfs.js | 6 | ||||
-rw-r--r-- | platform/node/test/query.test.js | 9 | ||||
-rw-r--r-- | platform/node/test/render.test.js | 13 | ||||
-rw-r--r-- | platform/node/test/suite_implementation.js | 71 |
20 files changed, 459 insertions, 324 deletions
diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md index e77ceff31f..e75b1687f1 100644 --- a/platform/node/CHANGELOG.md +++ b/platform/node/CHANGELOG.md @@ -1,3 +1,22 @@ +# master +* Increased the default maximum zoom level from 20 to 22 ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835)) + +# 3.5.5 - July 14, 2017 +- Provide debuggable release builds for node packages [#9497](https://github.com/mapbox/mapbox-gl-native/pull/9497) + +# 3.5.4 - June 6, 2017 +- Add support for ImageSource [#8968](https://github.com/mapbox/mapbox-gl-native/pull/8968) +- Fixed an issue with `map.addImage()` which would cause added images to randomly be replaced with images found the style's sprite sheet ([#9119](https://github.com/mapbox/mapbox-gl-native/pull/9119)) + +# 3.5.3 - May 30, 2017 + +- Fixed a regression around `line-dasharrary` and `fill-pattern` that caused these properties to sometimes not render correctly ([#9130](https://github.com/mapbox/mapbox-gl-native/pull/9130)) + +# 3.5.2 - May 18, 2017 + +- Fixed a memory leak ([#8884](https://github.com/mapbox/mapbox-gl-native/pull/9035)) + + # 3.5.1 - May 8, 2017 - Adds Node v6 binaries. **Note, Node v4 binaries will be removed on August 1st.** ([#8884](https://github.com/mapbox/mapbox-gl-native/pull/8884)) diff --git a/platform/node/README.md b/platform/node/README.md index 545d87861f..d19b2a9343 100644 --- a/platform/node/README.md +++ b/platform/node/README.md @@ -1,6 +1,6 @@ # node-mapbox-gl-native -[![NPM](https://nodei.co/npm/mapbox-gl-native.png)](https://npmjs.org/package/mapbox-gl-native) +[![NPM](https://nodei.co/npm/@mapbox/mapbox-gl-native.png)](https://npmjs.org/package/@mapbox/mapbox-gl-native) ## Installing @@ -14,7 +14,7 @@ By default, installs binaries. On these platforms no additional dependencies are Run: ``` -npm install mapbox-gl-native +npm install @mapbox/mapbox-gl-native ``` Other platforms will fall back to a source compile with `make node`; see INSTALL.md in the repository root directory for prequisites. @@ -31,7 +31,7 @@ npm run test-suite ```js var fs = require('fs'); var path = require('path'); -var mbgl = require('mapbox-gl-native'); +var mbgl = require('@mapbox/mapbox-gl-native'); var sharp = require('sharp'); var options = { diff --git a/platform/node/bitrise.yml b/platform/node/bitrise.yml index 00005f0f36..fab3093d6e 100644 --- a/platform/node/bitrise.yml +++ b/platform/node/bitrise.yml @@ -64,7 +64,7 @@ workflows: brew install cmake awscli node@4 node@6 brew link node@4 --force gem install xcpretty --no-rdoc --no-ri - export BUILDTYPE=Release + export BUILDTYPE=RelWithDebInfo export PUBLISH=true make test-node && ./platform/node/scripts/after_success.sh brew unlink node@4 diff --git a/platform/node/index.js b/platform/node/index.js index 54ba5c0dc6..5944a0a27d 100644 --- a/platform/node/index.js +++ b/platform/node/index.js @@ -4,6 +4,7 @@ var mbgl = require('../../lib/mapbox_gl_native.node'); var constructor = mbgl.Map.prototype.constructor; +var process = require('process'); var Map = function(options) { if (!(options instanceof Object)) { @@ -18,9 +19,29 @@ var Map = function(options) { return new constructor(Object.assign(options, { request: function(req) { - request(req, function() { - req.respond.apply(req, arguments); - }); + // Protect against `request` implementations that call the callback synchronously, + // call it multiple times, or throw exceptions. + // http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony + + var responded = false; + var callback = function() { + var args = arguments; + if (!responded) { + responded = true; + process.nextTick(function() { + req.respond.apply(req, args); + }); + } else { + console.warn('request function responded multiple times; it should call the callback only once'); + } + }; + + try { + request(req, callback); + } catch (e) { + console.warn('request function threw an exception; it should call the callback with an error instead'); + callback(e); + } } })); }; diff --git a/platform/node/scripts/after_success.sh b/platform/node/scripts/after_success.sh index ae34446927..18e00a4f0d 100755 --- a/platform/node/scripts/after_success.sh +++ b/platform/node/scripts/after_success.sh @@ -3,10 +3,13 @@ set -e set -o pipefail -if [[ -n ${PUBLISH:-} ]]; then - if [[ "${BUILDTYPE}" == "Release" ]]; then +if [[ "${PUBLISH:-}" == true ]]; then + if [[ "${BUILDTYPE}" == "RelWithDebInfo" ]]; then ./node_modules/.bin/node-pre-gyp package publish info - else + elif [[ "${BUILDTYPE}" == "Debug" ]]; then ./node_modules/.bin/node-pre-gyp package publish info --debug + else + echo "error: must provide either Debug or RelWithDebInfo for BUILDTYPE" + exit 1 fi fi diff --git a/platform/node/src/node_conversion.hpp b/platform/node/src/node_conversion.hpp index 22daedef6a..d266745548 100644 --- a/platform/node/src/node_conversion.hpp +++ b/platform/node/src/node_conversion.hpp @@ -82,6 +82,14 @@ inline optional<float> toNumber(v8::Local<v8::Value> value) { return value->NumberValue(); } +inline optional<double> toDouble(v8::Local<v8::Value> value) { + Nan::HandleScope scope; + if (!value->IsNumber()) { + return {}; + } + return value->NumberValue(); +} + inline optional<std::string> toString(v8::Local<v8::Value> value) { Nan::HandleScope scope; if (!value->IsString()) { diff --git a/platform/node/src/node_geojson.hpp b/platform/node/src/node_geojson.hpp index 9bae86b76a..8a3927e2cf 100644 --- a/platform/node/src/node_geojson.hpp +++ b/platform/node/src/node_geojson.hpp @@ -1,13 +1,15 @@ #include <mbgl/style/conversion/geojson.hpp> +#include <string> namespace mbgl { namespace style { namespace conversion { template <> -optional<GeoJSON> Converter<GeoJSON>::operator()(const v8::Local<v8::Value>&, Error& error) const { - error = { "not implemented" }; - return {}; +optional<GeoJSON> Converter<GeoJSON>::operator()(const v8::Local<v8::Value>& value, Error& error) const { + Nan::JSON JSON; + std::string string = *Nan::Utf8String(JSON.Stringify(value->ToObject()).ToLocalChecked()); + return convert<GeoJSON>(string, error); } } // namespace conversion diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index 1c8bf6d1d2..9a7ebce445 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -4,14 +4,15 @@ #include "node_conversion.hpp" #include "node_geojson.hpp" -#include <mbgl/gl/headless_display.hpp> #include <mbgl/util/exception.hpp> +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/gl/headless_frontend.hpp> #include <mbgl/style/conversion/source.hpp> #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/conversion/filter.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/image.hpp> -#include <mbgl/map/backend_scope.hpp> -#include <mbgl/map/query.hpp> +#include <mbgl/map/map_observer.hpp> #include <mbgl/util/premultiply.hpp> #include <unistd.h> @@ -24,27 +25,18 @@ struct NodeMap::RenderOptions { double pitch = 0; double latitude = 0; double longitude = 0; - unsigned int width = 512; - unsigned int height = 512; + mbgl::Size size = { 512, 512 }; std::vector<std::string> classes; mbgl::MapDebugOptions debugOptions = mbgl::MapDebugOptions::NoDebug; }; Nan::Persistent<v8::Function> NodeMap::constructor; -static std::shared_ptr<mbgl::HeadlessDisplay> sharedDisplay() { - static auto display = std::make_shared<mbgl::HeadlessDisplay>(); - return display; -} - static const char* releasedMessage() { return "Map resources have already been released"; } -NodeBackend::NodeBackend() - : HeadlessBackend(sharedDisplay()) {} - -void NodeBackend::onDidFailLoadingMap(std::exception_ptr error) { +void NodeMapObserver::onDidFailLoadingMap(std::exception_ptr error) { std::rethrow_exception(error); } @@ -60,8 +52,8 @@ void NodeMap::Init(v8::Local<v8::Object> target) { Nan::SetPrototypeMethod(tpl, "release", Release); Nan::SetPrototypeMethod(tpl, "cancel", Cancel); - Nan::SetPrototypeMethod(tpl, "addClass", AddClass); Nan::SetPrototypeMethod(tpl, "addSource", AddSource); + Nan::SetPrototypeMethod(tpl, "removeSource", RemoveSource); Nan::SetPrototypeMethod(tpl, "addLayer", AddLayer); Nan::SetPrototypeMethod(tpl, "removeLayer", RemoveLayer); Nan::SetPrototypeMethod(tpl, "addImage", AddImage); @@ -79,9 +71,6 @@ void NodeMap::Init(v8::Local<v8::Object> target) { constructor.Reset(tpl->GetFunction()); Nan::Set(target, Nan::New("Map").ToLocalChecked(), tpl->GetFunction()); - - // Initialize display connection on module load. - sharedDisplay(); } /** @@ -163,25 +152,15 @@ void NodeMap::New(const Nan::FunctionCallbackInfo<v8::Value>& info) { info.GetReturnValue().Set(info.This()); } -std::string StringifyStyle(v8::Local<v8::Value> styleHandle) { - Nan::HandleScope scope; - - v8::Local<v8::Object> JSON = Nan::To<v8::Object>( - Nan::Get( - Nan::GetCurrentContext()->Global(), - Nan::New("JSON").ToLocalChecked() - ).ToLocalChecked() - ).ToLocalChecked(); - - return *Nan::Utf8String(Nan::MakeCallback(JSON, "stringify", 1, &styleHandle)); -} - /** * Load a stylesheet * * @function * @name load * @param {string|Object} stylesheet either an object or a JSON representation + * @param {Object} options + * @param {boolean} options.defaultStyleCamera if true, sets the default style + * camera * @returns {undefined} loads stylesheet into map * @throws {Error} if stylesheet is missing or invalid * @example @@ -206,7 +185,8 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) { std::string style; if (info[0]->IsObject()) { - style = StringifyStyle(info[0]); + Nan::JSON JSON; + style = *Nan::Utf8String(JSON.Stringify(info[0]->ToObject()).ToLocalChecked()); } else if (info[0]->IsString()) { style = *Nan::Utf8String(info[0]); } else { @@ -214,11 +194,26 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) { } try { - nodeMap->map->setStyleJSON(style); + nodeMap->map->getStyle().loadJSON(style); } catch (const std::exception &ex) { return Nan::ThrowError(ex.what()); } + if (info.Length() == 2) { + if (!info[1]->IsObject()) { + return Nan::ThrowTypeError("Second argument must be an options object"); + } + auto options = Nan::To<v8::Object>(info[1]).ToLocalChecked(); + if (Nan::Has(options, Nan::New("defaultStyleCamera").ToLocalChecked()).FromJust()) { + if (!Nan::Get(options, Nan::New("defaultStyleCamera").ToLocalChecked()).ToLocalChecked()->IsBoolean()) { + return Nan::ThrowError("Options object 'defaultStyleCamera' property must be a boolean"); + } + if (Nan::Get(options, Nan::New("cameraMutated").ToLocalChecked()).ToLocalChecked()->BooleanValue()) { + nodeMap->map->jumpTo(nodeMap->map->getStyle().getDefaultCamera()); + } + } + } + nodeMap->loaded = true; info.GetReturnValue().SetUndefined(); @@ -266,11 +261,11 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) { } if (Nan::Has(obj, Nan::New("width").ToLocalChecked()).FromJust()) { - options.width = Nan::Get(obj, Nan::New("width").ToLocalChecked()).ToLocalChecked()->IntegerValue(); + options.size.width = Nan::Get(obj, Nan::New("width").ToLocalChecked()).ToLocalChecked()->IntegerValue(); } if (Nan::Has(obj, Nan::New("height").ToLocalChecked()).FromJust()) { - options.height = Nan::Get(obj, Nan::New("height").ToLocalChecked()).ToLocalChecked()->IntegerValue(); + options.size.height = Nan::Get(obj, Nan::New("height").ToLocalChecked()).ToLocalChecked()->IntegerValue(); } if (Nan::Has(obj, Nan::New("classes").ToLocalChecked()).FromJust()) { @@ -278,7 +273,7 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) { const int length = classes->Length(); options.classes.reserve(length); for (int i = 0; i < length; i++) { - options.classes.push_back(std::string { *Nan::Utf8String(Nan::To<v8::String>(Nan::Get(classes, i).ToLocalChecked()).ToLocalChecked()) }); + options.classes.emplace_back(std::string { *Nan::Utf8String(Nan::To<v8::String>(Nan::Get(classes, i).ToLocalChecked()).ToLocalChecked()) }); } } @@ -366,48 +361,22 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) { } void NodeMap::startRender(NodeMap::RenderOptions options) { - map->setSize({ options.width, options.height }); + frontend->setSize(options.size); + map->setSize(options.size); - const mbgl::Size fbSize{ static_cast<uint32_t>(options.width * pixelRatio), - static_cast<uint32_t>(options.height * pixelRatio) }; - if (!view || view->getSize() != fbSize) { - view.reset(); - mbgl::BackendScope scope { backend }; - view = std::make_unique<mbgl::OffscreenView>(backend.getContext(), fbSize); - } - - if (map->getClasses() != options.classes) { - map->setClasses(options.classes); - } - - if (map->getZoom() != options.zoom) { - map->setZoom(options.zoom); - } - - mbgl::LatLng latLng(options.latitude, options.longitude); - if (map->getLatLng() != latLng) { - map->setLatLng(latLng); - } - - if (map->getBearing() != options.bearing) { - map->setBearing(options.bearing); - } + mbgl::CameraOptions camera; + camera.center = mbgl::LatLng { options.latitude, options.longitude }; + camera.zoom = options.zoom; + camera.angle = -options.bearing * mbgl::util::DEG2RAD; + camera.pitch = options.pitch * mbgl::util::DEG2RAD; - if (map->getPitch() != options.pitch) { - map->setPitch(options.pitch); - } - - if (map->getDebug() != options.debugOptions) { - map->setDebug(options.debugOptions); - } - - map->renderStill(*view, [this](const std::exception_ptr eptr) { + map->renderStill(camera, options.debugOptions, [this](const std::exception_ptr eptr) { if (eptr) { error = std::move(eptr); uv_async_send(async); } else { assert(!image.data); - image = view->readStillImage(); + image = frontend->readStillImage(); uv_async_send(async); } }); @@ -506,7 +475,7 @@ void NodeMap::release() { uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *h) { delete reinterpret_cast<uv_async_t *>(h); }); - + map.reset(); } @@ -532,37 +501,24 @@ void NodeMap::Cancel(const Nan::FunctionCallbackInfo<v8::Value>& info) { } void NodeMap::cancel() { - auto style = map->getStyleJSON(); + auto style = map->getStyle().getJSON(); + + // Reset map explicitly as it resets the renderer frontend + map.reset(); - map = std::make_unique<mbgl::Map>(backend, mbgl::Size{ 256, 256 }, - pixelRatio, *this, threadpool, mbgl::MapMode::Still); + frontend = std::make_unique<mbgl::HeadlessFrontend>(mbgl::Size{ 256, 256 }, pixelRatio, *this, threadpool); + map = std::make_unique<mbgl::Map>(*frontend, mapObserver, frontend->getSize(), pixelRatio, + *this, threadpool, mbgl::MapMode::Still); // FIXME: Reload the style after recreating the map. We need to find // a better way of canceling an ongoing rendering on the core level // without resetting the map, which is way too expensive. - map->setStyleJSON(style); + map->getStyle().loadJSON(style); error = std::make_exception_ptr(std::runtime_error("Canceled")); renderFinished(); } -void NodeMap::AddClass(const Nan::FunctionCallbackInfo<v8::Value>& info) { - auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); - if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); - - if (info.Length() <= 0 || !info[0]->IsString()) { - return Nan::ThrowTypeError("First argument must be a string"); - } - - try { - nodeMap->map->addClass(*Nan::Utf8String(info[0])); - } catch (const std::exception &ex) { - return Nan::ThrowError(ex.what()); - } - - info.GetReturnValue().SetUndefined(); -} - void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) { using namespace mbgl::style; using namespace mbgl::style::conversion; @@ -578,6 +534,10 @@ void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) { return Nan::ThrowTypeError("First argument must be a string"); } + if (!info[1]->IsObject()) { + return Nan::ThrowTypeError("Second argument must be an object"); + } + Error error; mbgl::optional<std::unique_ptr<Source>> source = convert<std::unique_ptr<Source>>(info[1], error, *Nan::Utf8String(info[0])); if (!source) { @@ -585,7 +545,25 @@ void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) { return; } - nodeMap->map->addSource(std::move(*source)); + nodeMap->map->getStyle().addSource(std::move(*source)); +} + +void NodeMap::RemoveSource(const Nan::FunctionCallbackInfo<v8::Value>& info) { + using namespace mbgl::style; + using namespace mbgl::style::conversion; + + auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); + if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); + + if (info.Length() != 1) { + return Nan::ThrowTypeError("One argument required"); + } + + if (!info[0]->IsString()) { + return Nan::ThrowTypeError("First argument must be a string"); + } + + nodeMap->map->getStyle().removeSource(*Nan::Utf8String(info[0])); } void NodeMap::AddLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) { @@ -606,7 +584,7 @@ void NodeMap::AddLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) { return; } - nodeMap->map->addLayer(std::move(*layer)); + nodeMap->map->getStyle().addLayer(std::move(*layer)); } void NodeMap::RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) { @@ -624,7 +602,7 @@ void NodeMap::RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) { return Nan::ThrowTypeError("First argument must be a string"); } - nodeMap->map->removeLayer(*Nan::Utf8String(info[0])); + nodeMap->map->getStyle().removeLayer(*Nan::Utf8String(info[0])); } void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) { @@ -660,7 +638,7 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) { return Nan::ThrowTypeError("width parameter required"); } - if (!Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->IsUint32()) { + if (!Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->IsNumber()) { return Nan::ThrowTypeError("pixelRatio parameter required"); } @@ -671,7 +649,7 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) { return Nan::ThrowTypeError("Max height and width is 1024"); } - uint32_t pixelRatio = Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->Uint32Value(); + float pixelRatio = Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->NumberValue(); auto imageBuffer = Nan::To<v8::Object>(info[1]).ToLocalChecked()->ToObject(); char * imageDataBuffer = node::Buffer::Data(imageBuffer); @@ -686,7 +664,7 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) { mbgl::UnassociatedImage cImage({ imageWidth, imageHeight}, std::move(data)); mbgl::PremultipliedImage cPremultipliedImage = mbgl::util::premultiply(std::move(cImage)); - nodeMap->map->addImage(*Nan::Utf8String(info[0]), std::make_unique<mbgl::style::Image>(std::move(cPremultipliedImage), pixelRatio)); + nodeMap->map->getStyle().addImage(std::make_unique<mbgl::style::Image>(*Nan::Utf8String(info[0]), std::move(cPremultipliedImage), pixelRatio)); } void NodeMap::RemoveImage(const Nan::FunctionCallbackInfo<v8::Value>& info) { @@ -704,7 +682,7 @@ void NodeMap::RemoveImage(const Nan::FunctionCallbackInfo<v8::Value>& info) { return Nan::ThrowTypeError("First argument must be a string"); } - nodeMap->map->removeImage(*Nan::Utf8String(info[0])); + nodeMap->map->getStyle().removeImage(*Nan::Utf8String(info[0])); } void NodeMap::SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>& info) { @@ -722,7 +700,7 @@ void NodeMap::SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>& info return Nan::ThrowTypeError("First argument must be a string"); } - mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0])); + mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0])); if (!layer) { return Nan::ThrowTypeError("layer not found"); } @@ -754,7 +732,7 @@ void NodeMap::SetPaintProperty(const Nan::FunctionCallbackInfo<v8::Value>& info) return Nan::ThrowTypeError("First argument must be a string"); } - mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0])); + mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0])); if (!layer) { return Nan::ThrowTypeError("layer not found"); } @@ -763,12 +741,7 @@ void NodeMap::SetPaintProperty(const Nan::FunctionCallbackInfo<v8::Value>& info) return Nan::ThrowTypeError("Second argument must be a string"); } - mbgl::optional<std::string> klass; - if (info.Length() == 4 && info[3]->IsString()) { - klass = std::string(*Nan::Utf8String(info[3])); - } - - mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), info[2], klass); + mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), info[2]); if (error) { return Nan::ThrowTypeError(error->message.c_str()); } @@ -812,7 +785,7 @@ void NodeMap::SetFilter(const Nan::FunctionCallbackInfo<v8::Value>& info) { return Nan::ThrowTypeError("First argument must be a string"); } - mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0])); + mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0])); if (!layer) { return Nan::ThrowTypeError("layer not found"); } @@ -911,6 +884,8 @@ void NodeMap::DumpDebugLogs(const Nan::FunctionCallbackInfo<v8::Value>& info) { if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); nodeMap->map->dumpDebugLogs(); + nodeMap->frontend->getRenderer()->dumpDebugLogs(); + info.GetReturnValue().SetUndefined(); } @@ -947,7 +922,7 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>& auto layers = layersOption.As<v8::Array>(); std::vector<std::string> layersVec; for (uint32_t i=0; i < layers->Length(); i++) { - layersVec.push_back(*Nan::Utf8String(Nan::Get(layers,i).ToLocalChecked())); + layersVec.emplace_back(*Nan::Utf8String(Nan::Get(layers,i).ToLocalChecked())); } queryOptions.layerIDs = layersVec; } @@ -972,7 +947,7 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>& auto pos0 = Nan::Get(posOrBox, 0).ToLocalChecked().As<v8::Array>(); auto pos1 = Nan::Get(posOrBox, 1).ToLocalChecked().As<v8::Array>(); - optional = nodeMap->map->queryRenderedFeatures(mbgl::ScreenBox { + optional = nodeMap->frontend->getRenderer()->queryRenderedFeatures(mbgl::ScreenBox { { Nan::Get(pos0, 0).ToLocalChecked()->NumberValue(), Nan::Get(pos0, 1).ToLocalChecked()->NumberValue() @@ -983,7 +958,7 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>& }, queryOptions); } else { - optional = nodeMap->map->queryRenderedFeatures(mbgl::ScreenCoordinate { + optional = nodeMap->frontend->getRenderer()->queryRenderedFeatures(mbgl::ScreenCoordinate { Nan::Get(posOrBox, 0).ToLocalChecked()->NumberValue(), Nan::Get(posOrBox, 1).ToLocalChecked()->NumberValue() }, queryOptions); @@ -1007,9 +982,12 @@ NodeMap::NodeMap(v8::Local<v8::Object> options) .ToLocalChecked() ->NumberValue() : 1.0; - }()), - map(std::make_unique<mbgl::Map>(backend, - mbgl::Size{ 256, 256 }, + }()) + , mapObserver(NodeMapObserver()) + , frontend(std::make_unique<mbgl::HeadlessFrontend>(mbgl::Size { 256, 256 }, pixelRatio, *this, threadpool)) + , map(std::make_unique<mbgl::Map>(*frontend, + mapObserver, + frontend->getSize(), pixelRatio, *this, threadpool, diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp index cdc8f1e51f..c8e33bb347 100644 --- a/platform/node/src/node_map.hpp +++ b/platform/node/src/node_map.hpp @@ -4,8 +4,7 @@ #include <mbgl/map/map.hpp> #include <mbgl/storage/file_source.hpp> -#include <mbgl/gl/headless_backend.hpp> -#include <mbgl/gl/offscreen_view.hpp> +#include <mbgl/util/image.hpp> #include <exception> @@ -15,12 +14,15 @@ #include <nan.h> #pragma GCC diagnostic pop +namespace mbgl { +class Map; +class HeadlessFrontend; +} // namespace mbgl + namespace node_mbgl { -class NodeBackend : public mbgl::HeadlessBackend { -public: - NodeBackend(); - void onDidFailLoadingMap(std::exception_ptr) final; +class NodeMapObserver : public mbgl::MapObserver { + void onDidFailLoadingMap(std::exception_ptr) override; }; class NodeMap : public Nan::ObjectWrap, @@ -42,8 +44,8 @@ public: static void Render(const Nan::FunctionCallbackInfo<v8::Value>&); static void Release(const Nan::FunctionCallbackInfo<v8::Value>&); static void Cancel(const Nan::FunctionCallbackInfo<v8::Value>&); - static void AddClass(const Nan::FunctionCallbackInfo<v8::Value>&); static void AddSource(const Nan::FunctionCallbackInfo<v8::Value>&); + static void RemoveSource(const Nan::FunctionCallbackInfo<v8::Value>&); static void AddLayer(const Nan::FunctionCallbackInfo<v8::Value>&); static void RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>&); static void AddImage(const Nan::FunctionCallbackInfo<v8::Value>&); @@ -69,9 +71,9 @@ public: std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback); const float pixelRatio; - NodeBackend backend; - std::unique_ptr<mbgl::OffscreenView> view; NodeThreadPool threadpool; + NodeMapObserver mapObserver; + std::unique_ptr<mbgl::HeadlessFrontend> frontend; std::unique_ptr<mbgl::Map> map; std::exception_ptr error; diff --git a/platform/node/src/node_request.cpp b/platform/node/src/node_request.cpp index 09373b1779..de16710f78 100644 --- a/platform/node/src/node_request.cpp +++ b/platform/node/src/node_request.cpp @@ -122,19 +122,9 @@ void NodeRequest::HandleCallback(const Nan::FunctionCallbackInfo<v8::Value>& inf } void NodeRequest::Execute() { - asyncExecute = std::make_unique<mbgl::util::AsyncTask>([this] { doExecute(); Unref(); }); - asyncExecute->send(); - - Ref(); -} - -void NodeRequest::doExecute() { - Nan::HandleScope scope; - v8::Local<v8::Value> argv[] = { handle() }; Nan::MakeCallback(Nan::To<v8::Object>(target->handle()->GetInternalField(1)).ToLocalChecked(), "request", 1, argv); - asyncExecute.reset(); } NodeRequest::NodeAsyncRequest::NodeAsyncRequest(NodeRequest* request_) : request(request_) { diff --git a/platform/node/src/node_request.hpp b/platform/node/src/node_request.hpp index 356566132b..7d7679a3c7 100644 --- a/platform/node/src/node_request.hpp +++ b/platform/node/src/node_request.hpp @@ -8,9 +8,6 @@ #include <mbgl/storage/resource.hpp> #include <mbgl/storage/file_source.hpp> -#include <mbgl/util/async_task.hpp> - -#include <memory> namespace node_mbgl { @@ -38,12 +35,9 @@ public: void Execute(); private: - void doExecute(); - NodeMap* target; mbgl::FileSource::Callback callback; NodeAsyncRequest* asyncRequest = nullptr; - std::unique_ptr<mbgl::util::AsyncTask> asyncExecute; }; } diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json new file mode 100644 index 0000000000..1084a5c923 --- /dev/null +++ b/platform/node/test/ignores.json @@ -0,0 +1,59 @@ +{ + "query-tests/geometry/multilinestring": "needs investigation", + "query-tests/geometry/multipolygon": "needs investigation", + "query-tests/geometry/polygon": "needs investigation", + "query-tests/regressions/mapbox-gl-js#3534": "https://github.com/mapbox/mapbox-gl-native/issues/8193", + "query-tests/regressions/mapbox-gl-js#4417": "https://github.com/mapbox/mapbox-gl-native/issues/8007", + "query-tests/symbol-features-in/pitched-screen": "https://github.com/mapbox/mapbox-gl-native/issues/6817", + "query-tests/symbol-features-in/tilted-inside": "https://github.com/mapbox/mapbox-gl-native/issues/5056", + "query-tests/symbol-features-in/tilted-outside": "https://github.com/mapbox/mapbox-gl-native/issues/9435", + "query-tests/world-wrapping/box": "skip - needs issue", + "query-tests/world-wrapping/point": "skip - needs issue", + "render-tests/debug/collision-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/3841", + "render-tests/debug/collision-pitched-wrapped": "https://github.com/mapbox/mapbox-gl-native/issues/3841", + "render-tests/debug/collision-pitched": "https://github.com/mapbox/mapbox-gl-native/issues/3841", + "render-tests/debug/collision": "https://github.com/mapbox/mapbox-gl-native/issues/3841", + "render-tests/debug/tile-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/3841", + "render-tests/debug/tile": "https://github.com/mapbox/mapbox-gl-native/issues/3841", + "render-tests/extent/1024-circle": "needs investigation", + "render-tests/extent/1024-symbol": "needs investigation", + "render-tests/fill-extrusion-pattern/@2x": "https://github.com/mapbox/mapbox-gl-js/issues/3327", + "render-tests/fill-extrusion-pattern/function-2": "https://github.com/mapbox/mapbox-gl-js/issues/3327", + "render-tests/fill-extrusion-pattern/function": "https://github.com/mapbox/mapbox-gl-js/issues/3327", + "render-tests/fill-extrusion-pattern/literal": "https://github.com/mapbox/mapbox-gl-js/issues/3327", + "render-tests/fill-extrusion-pattern/missing": "https://github.com/mapbox/mapbox-gl-js/issues/3327", + "render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327", + "render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary", + "render-tests/geojson/inline-polygon-symbol": "behavior needs reconciliation with gl-js", + "render-tests/icon-size/composite-function-high-base-plain": "https://github.com/mapbox/mapbox-gl-native/issues/8654", + "render-tests/icon-size/composite-function-high-base-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/8654", + "render-tests/icon-text-fit/both-padding": "https://github.com/mapbox/mapbox-gl-native/issues/5602", + "render-tests/icon-text-fit/both": "https://github.com/mapbox/mapbox-gl-native/issues/5602", + "render-tests/icon-text-fit/height": "https://github.com/mapbox/mapbox-gl-native/issues/5602", + "render-tests/icon-text-fit/width-padding": "https://github.com/mapbox/mapbox-gl-native/issues/5602", + "render-tests/icon-text-fit/width": "https://github.com/mapbox/mapbox-gl-native/issues/5602", + "render-tests/line-width/property-function": "https://github.com/mapbox/mapbox-gl-js/issues/3682#issuecomment-264348200", + "render-tests/line-join/property-function": "https://github.com/mapbox/mapbox-gl-js/pull/5020", + "render-tests/line-join/property-function-dasharray": "https://github.com/mapbox/mapbox-gl-js/pull/5020", + "render-tests/line-opacity/step-curve": "https://github.com/mapbox/mapbox-gl-native/pull/9439", + "render-tests/regressions/mapbox-gl-js#2305": "https://github.com/mapbox/mapbox-gl-native/issues/6927", + "render-tests/regressions/mapbox-gl-js#3682": "https://github.com/mapbox/mapbox-gl-js/issues/3682", + "render-tests/regressions/mapbox-gl-native#7357": "https://github.com/mapbox/mapbox-gl-native/issues/7357", + "render-tests/runtime-styling/image-add-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/9847", + "render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745", + "render-tests/runtime-styling/set-style-paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745", + "render-tests/symbol-placement/line": "needs issue", + "render-tests/text-font/camera-function": "https://github.com/mapbox/mapbox-gl-native/pull/9439", + "render-tests/text-keep-upright/line-placement-true-offset": "https://github.com/mapbox/mapbox-gl-native/issues/9271", + "render-tests/text-pitch-alignment/auto-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732", + "render-tests/text-pitch-alignment/auto-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732", + "render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732", + "render-tests/text-pitch-alignment/map-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732", + "render-tests/text-pitch-alignment/viewport-overzoomed": "https://github.com/mapbox/mapbox-gl-js/issues/5095", + "render-tests/text-pitch-alignment/viewport-overzoomed-single-glyph": "https://github.com/mapbox/mapbox-gl-js/issues/5095", + "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732", + "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732", + "render-tests/text-pitch-scaling/line-half": "https://github.com/mapbox/mapbox-gl-native/issues/9732", + "render-tests/text-size/composite-expression": "https://github.com/mapbox/mapbox-gl-native/pull/9439", + "render-tests/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601" +} diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js index 4ab76b937a..39665e41ef 100644 --- a/platform/node/test/js/map.test.js +++ b/platform/node/test/js/map.test.js @@ -108,8 +108,8 @@ test('Map', function(t) { 'render', 'release', 'cancel', - 'addClass', 'addSource', + 'removeSource', 'addLayer', 'removeLayer', 'addImage', diff --git a/platform/node/test/js/request.test.js b/platform/node/test/js/request.test.js new file mode 100644 index 0000000000..4f8d1cabb0 --- /dev/null +++ b/platform/node/test/js/request.test.js @@ -0,0 +1,167 @@ +'use strict'; + +var mockfs = require('../mockfs'); +var mbgl = require('../../index'); +var test = require('tape'); + +[ 'sprite_png', 'sprite_json', 'source_vector', 'glyph' ].forEach(function (resource) { + test(`render reports an error when the request function responds with an error (${resource})`, function(t) { + var map = new mbgl.Map({ + request: function(req, callback) { + var data = mockfs.dataForRequest(req); + if (mockfs[resource] === data) { + callback(new Error('message')); + } else { + callback(null, { data: data }); + } + } + }); + map.load(mockfs.style_vector); + map.render({ zoom: 16 }, function(err, pixels) { + t.assert(err); + t.assert(/message/.test(err.message)); + t.assert(!pixels); + t.end(); + }); + }); +}); + +[ 'vector', 'raster' ].forEach(function (type) { + test(`render does not report an error when the request function responds with no data for a tile (${type})`, function(t) { + var map = new mbgl.Map({ + request: function(req, callback) { + var data = mockfs.dataForRequest(req); + if (mockfs[`tile_${type}`] === data) { + callback(); + } else { + callback(null, { data: data }); + } + } + }); + map.load(mockfs[`style_${type}`]); + map.render({ zoom: 16 }, function(err, pixels) { + t.error(err); + t.assert(pixels); + t.end(); + }); + }); + + test(`render reports an error when the request function responds with an error for a tile (${type})`, function(t) { + var map = new mbgl.Map({ + request: function(req, callback) { + var data = mockfs.dataForRequest(req); + if (mockfs[`tile_${type}`] === data) { + callback(new Error('message')); + } else { + callback(null, { data: data }); + } + } + }); + map.load(mockfs[`style_${type}`]); + map.render({ zoom: 16 }, function(err, pixels) { + t.assert(err); + t.assert(/message/.test(err.message)); + t.assert(!pixels); + t.end(); + }); + }); +}); + +test(`render reports an error if the request function throws an exception`, function(t) { + var map = new mbgl.Map({ + request: function() { + throw new Error('message'); + } + }); + map.load(mockfs.style_vector); + map.render({ zoom: 16 }, function(err, pixels) { + t.assert(err); + t.assert(/message/.test(err.message)); + t.assert(!pixels); + t.end(); + }); +}); + +test(`render ignores request functions throwing an exception after calling the callback`, function(t) { + var map = new mbgl.Map({ + request: function(req, callback) { + var data = mockfs.dataForRequest(req); + callback(null, { data: data }); + throw new Error('message'); + } + }); + map.load(mockfs.style_vector); + map.render({ zoom: 16 }, function(err, pixels) { + t.error(err); + t.assert(pixels); + t.end(); + }); +}); + +test(`render ignores request functions calling the callback a second time`, function(t) { + var map = new mbgl.Map({ + request: function(req, callback) { + var data = mockfs.dataForRequest(req); + callback(null, { data: data }); + callback(null, { data: data }); + } + }); + map.load(mockfs.style_vector); + map.render({ zoom: 16 }, function(err, pixels) { + t.error(err); + t.assert(pixels); + t.end(); + }); +}); + +test(`render reports an error from loading the current style`, function(t) { + var map = new mbgl.Map({ + request: function(req, callback) { + var data = mockfs.dataForRequest(req); + if (mockfs.source_vector === data) { + callback(new Error('message')); + } else { + callback(null, { data: data }); + } + } + }); + map.load(mockfs.style_vector); + map.render({ zoom: 16 }, function(err, pixels) { + t.assert(err); + t.assert(/message/.test(err.message)); + t.assert(!pixels); + + map.render({ zoom: 16 }, function(err, pixels) { + t.assert(err); + t.assert(/message/.test(err.message)); + t.assert(!pixels); + t.end(); + }); + }); +}); + +test(`render does not report an error from rendering a previous style`, function(t) { + var map = new mbgl.Map({ + request: function(req, callback) { + var data = mockfs.dataForRequest(req); + if (mockfs.source_vector === data) { + callback(new Error('message')); + } else { + callback(null, { data: data }); + } + } + }); + map.load(mockfs.style_vector); + map.render({ zoom: 16 }, function(err, pixels) { + t.assert(err); + t.assert(/message/.test(err.message)); + t.assert(!pixels); + + map.load(mockfs.style_raster); + map.render({ zoom: 16 }, function(err, pixels) { + t.error(err); + t.assert(pixels); + t.end(); + }); + }); +}); diff --git a/platform/node/test/js/request_fail.test.js b/platform/node/test/js/request_fail.test.js deleted file mode 100644 index fad116a2b8..0000000000 --- a/platform/node/test/js/request_fail.test.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; - -var mockfs = require('../mockfs'); -var mbgl = require('../../index'); -var test = require('tape'); - -function asyncReply(callback, data) { - setTimeout(function() { callback(null, { data: data }); }, 0); -}; - -function asyncFail(callback) { - setTimeout(function() { callback(new Error('not found')); }, 0); -}; - -function failRequest(t, style, failedResource) { - var options = { - request: function(req, callback) { - var data = mockfs.dataForRequest(req); - - if (failedResource != data) { - asyncReply(callback, data); - } else { - asyncFail(callback); - } - }, - ratio: 2, - }; - - var map = new mbgl.Map(options); - map.load(style); - - map.render({ zoom: 16 }, function(err, pixels) { - if (err) { - t.pass("pass"); - map.release(); - } - }); -}; - -test('Vector', function(t) { - t.plan(5); - - failRequest(t, mockfs.style_vector, null); - failRequest(t, mockfs.style_vector, mockfs.sprite_png); - failRequest(t, mockfs.style_vector, mockfs.sprite_json); - failRequest(t, mockfs.style_vector, mockfs.source_vector); - failRequest(t, mockfs.style_vector, mockfs.tile_vector); - failRequest(t, mockfs.style_vector, mockfs.glyph); -}); - -test('Raster', function(t) { - t.plan(4); - - failRequest(t, mockfs.style_raster, null); - failRequest(t, mockfs.style_raster, mockfs.sprite_png); - failRequest(t, mockfs.style_raster, mockfs.sprite_json); - failRequest(t, mockfs.style_raster, mockfs.source_raster); - failRequest(t, mockfs.style_raster, mockfs.tile_raster); -}); diff --git a/platform/node/test/js/request_notfound.test.js b/platform/node/test/js/request_notfound.test.js deleted file mode 100644 index d2d2812784..0000000000 --- a/platform/node/test/js/request_notfound.test.js +++ /dev/null @@ -1,74 +0,0 @@ -'use strict'; - -var mockfs = require('../mockfs'); -var mbgl = require('../../index'); -var test = require('tape'); - -function isTile(data) { - return data == mockfs.tile_vector || data == mockfs.tile_raster; -} - -function asyncReply(callback, data) { - setTimeout(function() { callback(null, { data: data }); }, 0); -}; - -function asyncFail(callback, data) { - // Do not set an error for tile when not found. A not found - // tile is a valid tile. - if (isTile(data)) { - setTimeout(function() { callback(); }, 0); - } else { - setTimeout(function() { callback(new Error('not found')); }, 0); - } -}; - -function notfoundRequest(t, style, notfoundResource) { - var options = { - request: function(req, callback) { - var data = mockfs.dataForRequest(req); - - if (notfoundResource != data) { - asyncReply(callback, data); - } else { - asyncFail(callback, data); - } - }, - ratio: 2, - }; - - var map = new mbgl.Map(options); - map.load(style); - - map.render({ zoom: 16 }, function(err, pixels) { - if (err && !isTile(notfoundResource)) { - t.pass("pass"); - return; - } - - if (!err && isTile(notfoundResource)) { - t.pass("pass"); - return; - } - - t.fail("fail"); - }); -}; - -test('Vector', function(t) { - t.plan(5); - - notfoundRequest(t, mockfs.style_vector, mockfs.sprite_png); - notfoundRequest(t, mockfs.style_vector, mockfs.sprite_json); - notfoundRequest(t, mockfs.style_vector, mockfs.source_vector); - notfoundRequest(t, mockfs.style_vector, mockfs.tile_vector); - notfoundRequest(t, mockfs.style_vector, mockfs.glyph); -}); - -test('Raster', function(t) { - t.plan(4); - - notfoundRequest(t, mockfs.style_raster, mockfs.sprite_png); - notfoundRequest(t, mockfs.style_raster, mockfs.sprite_json); - notfoundRequest(t, mockfs.style_raster, mockfs.source_raster); - notfoundRequest(t, mockfs.style_raster, mockfs.tile_raster); -}); diff --git a/platform/node/test/mockfs.js b/platform/node/test/mockfs.js index dfa5a425e3..2d27f3bbbe 100644 --- a/platform/node/test/mockfs.js +++ b/platform/node/test/mockfs.js @@ -5,7 +5,7 @@ var path = require('path'); function readFixture(file) { return fs.readFileSync(path.join('test/fixtures/resources', file)); -}; +} var style_raster = readFixture('style_raster.json').toString('utf8'); var style_vector = readFixture('style_vector.json').toString('utf8'); @@ -18,7 +18,7 @@ var tile_raster = readFixture('raster.tile'); var tile_vector = readFixture('vector.tile'); function dataForRequest(req) { - if (req.url == null) { + if (req.url === null) { return null; } else if (req.url.indexOf('sprite') > -1 && req.url.endsWith('json')) { return sprite_json; @@ -37,7 +37,7 @@ function dataForRequest(req) { } else { return null; } -}; +} module.exports = { dataForRequest: dataForRequest, diff --git a/platform/node/test/query.test.js b/platform/node/test/query.test.js index 8125f1c7cd..02602d3f5a 100644 --- a/platform/node/test/query.test.js +++ b/platform/node/test/query.test.js @@ -1,12 +1,13 @@ 'use strict'; -var suite = require('../../../mapbox-gl-js/test/integration').query; -var suiteImplementation = require('./suite_implementation'); +const suite = require('../../../mapbox-gl-js/test/integration').query; +const suiteImplementation = require('./suite_implementation'); +const ignores = require('./ignores.json'); -var tests; +let tests; if (process.argv[1] === __filename && process.argv.length > 2) { tests = process.argv.slice(2); } -suite.run('native', {tests: tests}, suiteImplementation); +suite.run('native', {tests: tests, ignores: ignores}, suiteImplementation); diff --git a/platform/node/test/render.test.js b/platform/node/test/render.test.js index e2e13534c9..812a531f20 100644 --- a/platform/node/test/render.test.js +++ b/platform/node/test/render.test.js @@ -1,12 +1,7 @@ 'use strict'; -var suite = require('../../../mapbox-gl-js/test/integration').render; -var suiteImplementation = require('./suite_implementation'); +const suite = require('../../../mapbox-gl-js/test/integration').render; +const suiteImplementation = require('./suite_implementation'); +const ignores = require('./ignores.json'); -var tests; - -if (process.argv[1] === __filename && process.argv.length > 2) { - tests = process.argv.slice(2); -} - -suite.run('native', {tests: tests}, suiteImplementation); +suite.run('native', ignores, suiteImplementation); diff --git a/platform/node/test/suite_implementation.js b/platform/node/test/suite_implementation.js index 8ac372b7c3..323f429bed 100644 --- a/platform/node/test/suite_implementation.js +++ b/platform/node/test/suite_implementation.js @@ -10,23 +10,27 @@ mbgl.on('message', function(msg) { console.log('%s (%s): %s', msg.severity, msg.class, msg.text); }); +// Map of map objects by pixel ratio +var maps = new Map(); + module.exports = function (style, options, callback) { - var map = new mbgl.Map({ - ratio: options.pixelRatio, - request: function(req, callback) { - request(req.url, {encoding: null}, function (err, response, body) { - if (err) { - callback(err); - } else if (response.statusCode == 404) { - callback(); - } else if (response.statusCode != 200) { - callback(new Error(response.statusMessage)); - } else { - callback(null, {data: body}); - } - }); + if (options.recycleMap) { + if (maps.has(options.pixelRatio)) { + var map = maps.get(options.pixelRatio); + map.request = mapRequest; + } else { + maps.set(options.pixelRatio, new mbgl.Map({ + ratio: options.pixelRatio, + request: mapRequest + })); + var map = maps.get(options.pixelRatio); } - }); + } else { + var map = new mbgl.Map({ + ratio: options.pixelRatio, + request: mapRequest + }); + } var timedOut = false; var watchdog = setTimeout(function () { @@ -46,14 +50,30 @@ module.exports = function (style, options, callback) { options.bearing = style.bearing || 0; options.pitch = style.pitch || 0; - map.load(style); + map.load(style, { defaultStyleCamera: true }); + + function mapRequest(req, callback) { + request(req.url, {encoding: null}, function (err, response, body) { + if (err) { + callback(err); + } else if (response.statusCode == 404) { + callback(); + } else if (response.statusCode != 200) { + callback(new Error(response.statusMessage)); + } else { + callback(null, {data: body}); + } + }); + }; applyOperations(options.operations, function() { map.render(options, function (err, pixels) { var results = options.queryGeometry ? map.queryRenderedFeatures(options.queryGeometry, options.queryOptions || {}) : []; - map.release(); + if (!options.recycleMap) { + map.release(); + } if (timedOut) return; clearTimeout(watchdog); callback(err, pixels, results.map(prepareFeatures)); @@ -70,16 +90,25 @@ module.exports = function (style, options, callback) { applyOperations(operations.slice(1), callback); }); - } else if (operation[0] === 'addImage') { + } else if (operation[0] === 'addImage' || operation[0] === 'updateImage') { var img = PNG.sync.read(fs.readFileSync(path.join(__dirname, '../../../mapbox-gl-js/test/integration', operation[2]))); + const testOpts = (operation.length > 3) ? operation[3] : {}; - map.addImage(operation[1], img.data, { + const options = { height: img.height, width: img.width, - pixelRatio: operation[3] || 1 - }); + pixelRatio: testOpts.pixelRatio || 1, + sdf: testOpts.sdf || false + } + map.addImage(operation[1], img.data, options); + + applyOperations(operations.slice(1), callback); + + } else if (operation[0] === 'setStyle') { + map.load(operation[1]); applyOperations(operations.slice(1), callback); + } else { // Ensure that the next `map.render(options)` does not overwrite this change. if (operation[0] === 'setCenter') { |