650 lines
28 KiB
Plaintext
650 lines
28 KiB
Plaintext
#import <Mapbox.h>
|
|
|
|
#import "MLNStyle_Private.h"
|
|
#import "MLNVectorTileSource_Private.h"
|
|
|
|
#include <mln/style/layers/particle_validation_layer.hpp>
|
|
#include <mln/style/style.hpp>
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#import "NSBundle+MLNAdditions.h"
|
|
|
|
#import <XCTest/XCTest.h>
|
|
#if TARGET_OS_IPHONE
|
|
#import <UIKit/UIKit.h>
|
|
#else
|
|
#import <Cocoa/Cocoa.h>
|
|
#endif
|
|
#import <objc/runtime.h>
|
|
|
|
@interface MLNStyleTests : XCTestCase <MLNMapViewDelegate>
|
|
|
|
@property (nonatomic) MLNMapView *mapView;
|
|
@property (nonatomic) MLNStyle *style;
|
|
|
|
@end
|
|
|
|
@implementation MLNStyleTests {
|
|
XCTestExpectation *_styleLoadingExpectation;
|
|
}
|
|
|
|
- (void)setUp {
|
|
[super setUp];
|
|
|
|
[MLNSettings useWellKnownTileServer:MLNMapTiler];
|
|
[MLNSettings setApiKey:@"pk.feedcafedeadbeefbadebede"];
|
|
|
|
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner"
|
|
withExtension:@"json"];
|
|
self.mapView = [[MLNMapView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) styleURL:styleURL];
|
|
self.mapView.delegate = self;
|
|
if (!self.mapView.style) {
|
|
_styleLoadingExpectation =
|
|
[self expectationWithDescription:@"Map view should finish loading style."];
|
|
[self waitForExpectationsWithTimeout:10 handler:nil];
|
|
}
|
|
}
|
|
|
|
- (void)mapView:(MLNMapView *)mapView didFinishLoadingStyle:(MLNStyle *)style {
|
|
XCTAssertNotNil(mapView.style);
|
|
XCTAssertEqual(mapView.style, style);
|
|
|
|
[_styleLoadingExpectation fulfill];
|
|
}
|
|
|
|
- (void)tearDown {
|
|
_styleLoadingExpectation = nil;
|
|
self.mapView = nil;
|
|
|
|
[super tearDown];
|
|
}
|
|
|
|
// TODO: remove backed property _style
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wunused-property-ivar"
|
|
- (MLNStyle *)style {
|
|
return self.mapView.style;
|
|
}
|
|
#pragma clang diagnostic pop
|
|
|
|
- (void)testName {
|
|
XCTAssertNil(self.style.name);
|
|
}
|
|
|
|
- (void)testSources {
|
|
NSSet<MLNSource *> *initialSources = self.style.sources;
|
|
if ([initialSources.anyObject.identifier isEqualToString:@"org.maplibre.annotations"]) {
|
|
XCTAssertEqual(self.style.sources.count, 1UL);
|
|
} else {
|
|
XCTAssertEqual(self.style.sources.count, 0UL);
|
|
}
|
|
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource"
|
|
shape:nil
|
|
options:nil];
|
|
[self.style addSource:shapeSource];
|
|
XCTAssertEqual(self.style.sources.count, initialSources.count + 1);
|
|
XCTAssertEqual(shapeSource, [self.style sourceWithIdentifier:@"shapeSource"]);
|
|
[self.style removeSource:shapeSource];
|
|
XCTAssertEqual(self.style.sources.count, initialSources.count);
|
|
}
|
|
|
|
- (void)testAddingSourcesTwice {
|
|
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource"
|
|
shape:nil
|
|
options:nil];
|
|
[self.style addSource:shapeSource];
|
|
XCTAssertThrowsSpecificNamed([self.style addSource:shapeSource], NSException,
|
|
MLNRedundantSourceException);
|
|
|
|
MLNRasterTileSource *rasterTileSource =
|
|
[[MLNRasterTileSource alloc] initWithIdentifier:@"rasterTileSource"
|
|
configurationURL:[NSURL URLWithString:@".json"]
|
|
tileSize:42];
|
|
[self.style addSource:rasterTileSource];
|
|
XCTAssertThrowsSpecificNamed([self.style addSource:rasterTileSource], NSException,
|
|
MLNRedundantSourceException);
|
|
|
|
MLNVectorTileSource *vectorTileSource =
|
|
[[MLNVectorTileSource alloc] initWithIdentifier:@"vectorTileSource"
|
|
configurationURL:[NSURL URLWithString:@".json"]];
|
|
[self.style addSource:vectorTileSource];
|
|
XCTAssertThrowsSpecificNamed([self.style addSource:vectorTileSource], NSException,
|
|
MLNRedundantSourceException);
|
|
}
|
|
|
|
- (void)testAddingSourcesWithDuplicateIdentifiers {
|
|
MLNVectorTileSource *source1 = [[MLNVectorTileSource alloc]
|
|
initWithIdentifier:@"my-source"
|
|
configurationURL:[NSURL URLWithString:@"maptiler://sources/hillshades"]];
|
|
MLNVectorTileSource *source2 = [[MLNVectorTileSource alloc]
|
|
initWithIdentifier:@"my-source"
|
|
configurationURL:[NSURL URLWithString:@"maptiler://sources/hillshades"]];
|
|
|
|
[self.style addSource:source1];
|
|
XCTAssertThrowsSpecificNamed([self.style addSource:source2], NSException,
|
|
MLNRedundantSourceIdentifierException);
|
|
}
|
|
|
|
- (void)testRemovingSourcesBeforeAddingThem {
|
|
MLNRasterTileSource *rasterTileSource =
|
|
[[MLNRasterTileSource alloc] initWithIdentifier:@"raster-tile-source"
|
|
tileURLTemplates:@[]
|
|
options:nil];
|
|
[self.style removeSource:rasterTileSource];
|
|
[self.style addSource:rasterTileSource];
|
|
XCTAssertNotNil([self.style sourceWithIdentifier:rasterTileSource.identifier]);
|
|
|
|
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shape-source"
|
|
shape:nil
|
|
options:nil];
|
|
[self.style removeSource:shapeSource];
|
|
[self.style addSource:shapeSource];
|
|
XCTAssertNotNil([self.style sourceWithIdentifier:shapeSource.identifier]);
|
|
|
|
MLNVectorTileSource *vectorTileSource =
|
|
[[MLNVectorTileSource alloc] initWithIdentifier:@"vector-tile-source"
|
|
tileURLTemplates:@[]
|
|
options:nil];
|
|
[self.style removeSource:vectorTileSource];
|
|
[self.style addSource:vectorTileSource];
|
|
XCTAssertNotNil([self.style sourceWithIdentifier:vectorTileSource.identifier]);
|
|
}
|
|
|
|
- (void)testAddingSourceOfTypeABeforeSourceOfTypeBWithSameIdentifier {
|
|
// Add a raster tile source
|
|
MLNRasterTileSource *rasterTileSource =
|
|
[[MLNRasterTileSource alloc] initWithIdentifier:@"some-identifier"
|
|
tileURLTemplates:@[]
|
|
options:nil];
|
|
[self.style addSource:rasterTileSource];
|
|
|
|
// Attempt to remove an image source with the same identifier as the raster tile source
|
|
MLNImageSource *imageSource =
|
|
[[MLNImageSource alloc] initWithIdentifier:@"some-identifier"
|
|
coordinateQuad:{}
|
|
URL:[NSURL URLWithString:@"http://host/image.png"]];
|
|
[self.style removeSource:imageSource];
|
|
// The raster tile source should still be added
|
|
XCTAssertTrue([[self.style sourceWithIdentifier:rasterTileSource.identifier]
|
|
isMemberOfClass:[MLNRasterTileSource class]]);
|
|
|
|
// Remove the raster tile source
|
|
[self.style removeSource:rasterTileSource];
|
|
|
|
// Add the shape source
|
|
[self.style addSource:imageSource];
|
|
|
|
// Attempt to remove a vector tile source with the same identifer as the shape source
|
|
MLNVectorTileSource *vectorTileSource =
|
|
[[MLNVectorTileSource alloc] initWithIdentifier:@"some-identifier"
|
|
tileURLTemplates:@[]
|
|
options:nil];
|
|
[self.style removeSource:vectorTileSource];
|
|
// The image source should still be added
|
|
XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier]
|
|
isMemberOfClass:[MLNImageSource class]]);
|
|
|
|
// Remove the image source
|
|
[self.style removeSource:imageSource];
|
|
|
|
// Add the vector tile source
|
|
[self.style addSource:vectorTileSource];
|
|
|
|
// Attempt to remove the previously created raster tile source that has the same identifer as the
|
|
// shape source
|
|
[self.style removeSource:rasterTileSource];
|
|
// The vector tile source should still be added
|
|
XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier]
|
|
isMemberOfClass:[MLNVectorTileSource class]]);
|
|
}
|
|
|
|
- (void)testRemovingSourceInUse {
|
|
// Add a raster tile source
|
|
MLNVectorTileSource *vectorTileSource =
|
|
[[MLNVectorTileSource alloc] initWithIdentifier:@"some-identifier"
|
|
tileURLTemplates:@[]
|
|
options:nil];
|
|
[self.style addSource:vectorTileSource];
|
|
|
|
// Add a layer using it
|
|
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fillLayer"
|
|
source:vectorTileSource];
|
|
[self.style addLayer:fillLayer];
|
|
|
|
// Attempt to remove the raster tile source
|
|
NSError *error;
|
|
BOOL result = [self.style removeSource:vectorTileSource error:&error];
|
|
|
|
XCTAssertFalse(result);
|
|
XCTAssertEqualObjects(error.domain, MLNErrorDomain);
|
|
XCTAssertEqual(error.code, MLNErrorCodeSourceIsInUseCannotRemove);
|
|
|
|
// Ensure it is still there
|
|
XCTAssertTrue([[self.style sourceWithIdentifier:vectorTileSource.identifier]
|
|
isMemberOfClass:[MLNVectorTileSource class]]);
|
|
}
|
|
|
|
- (void)testLayers {
|
|
NSArray<MLNStyleLayer *> *initialLayers = self.style.layers;
|
|
if ([initialLayers.firstObject.identifier isEqualToString:@"org.maplibre.annotations.points"]) {
|
|
XCTAssertEqual(self.style.layers.count, 1UL);
|
|
} else {
|
|
XCTAssertEqual(self.style.layers.count, 0UL);
|
|
}
|
|
MLNShapeSource *shapeSource = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource"
|
|
shape:nil
|
|
options:nil];
|
|
[self.style addSource:shapeSource];
|
|
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fillLayer"
|
|
source:shapeSource];
|
|
[self.style addLayer:fillLayer];
|
|
XCTAssertEqual(self.style.layers.count, initialLayers.count + 1);
|
|
XCTAssertEqual(fillLayer, [self.style layerWithIdentifier:@"fillLayer"]);
|
|
[self.style removeLayer:fillLayer];
|
|
XCTAssertEqual(self.style.layers.count, initialLayers.count);
|
|
}
|
|
|
|
// Native-only layers must remain renderable while ObjC/KVC accessors expose
|
|
// only layers for which Darwin can construct a valid MLNStyleLayer peer.
|
|
- (void)testCoreOnlyLayersPreserveObjCCollectionAndOrdering {
|
|
NSUInteger initialCount = self.style.layers.count;
|
|
NSUInteger initialNativeCount = self.style.rawStyle->getLayers().size();
|
|
|
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"core-only-test-source"
|
|
shape:nil
|
|
options:nil];
|
|
[self.style addSource:source];
|
|
|
|
MLNCircleStyleLayer *lower = [[MLNCircleStyleLayer alloc]
|
|
initWithIdentifier:@"core-only-visible-lower" source:source];
|
|
MLNCircleStyleLayer *upper = [[MLNCircleStyleLayer alloc]
|
|
initWithIdentifier:@"core-only-visible-upper" source:source];
|
|
[self.style addLayer:lower];
|
|
[self.style addLayer:upper];
|
|
|
|
// Mix hidden core layers between and above the public ObjC layers.
|
|
self.style.rawStyle->addLayer(
|
|
std::make_unique<mln::style::ParticleValidationLayer>("core-only-middle"),
|
|
std::string("core-only-visible-upper"));
|
|
self.style.rawStyle->addLayer(
|
|
std::make_unique<mln::style::ParticleValidationLayer>("core-only-top"));
|
|
|
|
NSArray<MLNStyleLayer *> *visible = self.style.layers;
|
|
XCTAssertEqual(visible.count, initialCount + 2);
|
|
XCTAssertEqual([self.style countOfLayers], initialCount + 2);
|
|
XCTAssertEqual(self.style.rawStyle->getLayers().size(), initialNativeCount + 4);
|
|
XCTAssertEqualObjects(visible[initialCount].identifier, lower.identifier);
|
|
XCTAssertEqualObjects(visible[initialCount + 1].identifier, upper.identifier);
|
|
XCTAssertNil([self.style layerWithIdentifier:@"core-only-middle"]);
|
|
XCTAssertNotNil(self.style.rawStyle->getLayer("core-only-middle"));
|
|
XCTAssertNotNil(self.style.rawStyle->getLayer("core-only-top"));
|
|
|
|
id kvcStyle = self.style;
|
|
XCTAssertEqualObjects([kvcStyle objectInLayersAtIndex:initialCount], lower);
|
|
MLNStyleLayer *__unsafe_unretained rangeLayers[2] = {nil, nil};
|
|
[kvcStyle getLayers:rangeLayers range:NSMakeRange(initialCount, 2)];
|
|
XCTAssertEqualObjects(rangeLayers[0], lower);
|
|
XCTAssertEqualObjects(rangeLayers[1], upper);
|
|
XCTAssertThrowsSpecificNamed([kvcStyle objectInLayersAtIndex:visible.count], NSException,
|
|
NSRangeException);
|
|
|
|
// The native layer immediately above lower lacks an ObjC peer. Inserting
|
|
// above lower must still work and retain the relative visible order.
|
|
MLNCircleStyleLayer *above = [[MLNCircleStyleLayer alloc]
|
|
initWithIdentifier:@"core-only-visible-above" source:source];
|
|
[self.style insertLayer:above aboveLayer:lower];
|
|
XCTAssertEqualObjects(self.style.layers[initialCount + 1], above);
|
|
XCTAssertEqualObjects(self.style.layers[initialCount + 2], upper);
|
|
|
|
// KVC's index applies to public peers, not the internal native stack.
|
|
MLNCircleStyleLayer *inserted = [[MLNCircleStyleLayer alloc]
|
|
initWithIdentifier:@"core-only-visible-inserted" source:source];
|
|
[self.style insertLayer:inserted atIndex:initialCount + 1];
|
|
XCTAssertEqualObjects(self.style.layers[initialCount + 1], inserted);
|
|
[kvcStyle removeObjectFromLayersAtIndex:initialCount + 1];
|
|
XCTAssertNil([self.style layerWithIdentifier:inserted.identifier]);
|
|
XCTAssertEqualObjects(self.style.layers[initialCount + 1], above);
|
|
XCTAssertNotNil(self.style.rawStyle->getLayer("core-only-middle"));
|
|
XCTAssertNotNil(self.style.rawStyle->getLayer("core-only-top"));
|
|
}
|
|
|
|
- (void)testAddingLayersTwice {
|
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"shapeSource"
|
|
shape:nil
|
|
options:nil];
|
|
|
|
MLNBackgroundStyleLayer *backgroundLayer =
|
|
[[MLNBackgroundStyleLayer alloc] initWithIdentifier:@"backgroundLayer"];
|
|
[self.style addLayer:backgroundLayer];
|
|
XCTAssertThrowsSpecificNamed([self.style addLayer:backgroundLayer], NSException,
|
|
MLNRedundantLayerException);
|
|
|
|
MLNCircleStyleLayer *circleLayer = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"circleLayer"
|
|
source:source];
|
|
[self.style addLayer:circleLayer];
|
|
XCTAssertThrowsSpecificNamed([self.style addLayer:circleLayer], NSException,
|
|
MLNRedundantLayerException);
|
|
|
|
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fillLayer"
|
|
source:source];
|
|
[self.style addLayer:fillLayer];
|
|
XCTAssertThrowsSpecificNamed([self.style addLayer:fillLayer], NSException,
|
|
MLNRedundantLayerException);
|
|
|
|
MLNLineStyleLayer *lineLayer = [[MLNLineStyleLayer alloc] initWithIdentifier:@"lineLayer"
|
|
source:source];
|
|
[self.style addLayer:lineLayer];
|
|
XCTAssertThrowsSpecificNamed([self.style addLayer:lineLayer], NSException,
|
|
MLNRedundantLayerException);
|
|
|
|
MLNRasterStyleLayer *rasterLayer = [[MLNRasterStyleLayer alloc] initWithIdentifier:@"rasterLayer"
|
|
source:source];
|
|
[self.style addLayer:rasterLayer];
|
|
XCTAssertThrowsSpecificNamed([self.style addLayer:rasterLayer], NSException,
|
|
MLNRedundantLayerException);
|
|
|
|
MLNSymbolStyleLayer *symbolLayer = [[MLNSymbolStyleLayer alloc] initWithIdentifier:@"symbolLayer"
|
|
source:source];
|
|
[self.style addLayer:symbolLayer];
|
|
XCTAssertThrowsSpecificNamed([self.style addLayer:symbolLayer], NSException,
|
|
MLNRedundantLayerException);
|
|
}
|
|
|
|
- (void)testAddingLayersWithDuplicateIdentifiers {
|
|
// Just some source
|
|
MLNVectorTileSource *source = [[MLNVectorTileSource alloc]
|
|
initWithIdentifier:@"my-source"
|
|
configurationURL:[NSURL URLWithString:@"maptiler://sources/hillshades"]];
|
|
[self.style addSource:source];
|
|
|
|
// Add initial layer
|
|
MLNFillStyleLayer *initial = [[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer"
|
|
source:source];
|
|
[self.style addLayer:initial];
|
|
|
|
// Try to add the duplicate
|
|
XCTAssertThrowsSpecificNamed(
|
|
[self.style addLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer"
|
|
source:source]],
|
|
NSException, @"MLNRedundantLayerIdentifierException");
|
|
XCTAssertThrowsSpecificNamed(
|
|
[self.style insertLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer"
|
|
source:source]
|
|
belowLayer:initial],
|
|
NSException, @"MLNRedundantLayerIdentifierException");
|
|
XCTAssertThrowsSpecificNamed(
|
|
[self.style insertLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer"
|
|
source:source]
|
|
aboveLayer:initial],
|
|
NSException, @"MLNRedundantLayerIdentifierException");
|
|
XCTAssertThrowsSpecificNamed(
|
|
[self.style insertLayer:[[MLNFillStyleLayer alloc] initWithIdentifier:@"my-layer"
|
|
source:source]
|
|
atIndex:0],
|
|
NSException, @"MLNRedundantLayerIdentifierException");
|
|
XCTAssertThrowsSpecificNamed(
|
|
[self.style insertLayer:[[MLNCustomStyleLayer alloc] initWithIdentifier:@"my-layer"]
|
|
atIndex:0],
|
|
NSException, @"MLNRedundantLayerIdentifierException");
|
|
}
|
|
|
|
- (void)testSymbolLayerIconImageNameWithMultiCaseImageExpression {
|
|
MLNPointFeature *feature = [[MLNPointFeature alloc] init];
|
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"sourceID"
|
|
shape:feature
|
|
options:nil];
|
|
MLNSymbolStyleLayer *layer = [[MLNSymbolStyleLayer alloc] initWithIdentifier:@"layerID"
|
|
source:source];
|
|
// Reproduces https://github.com/maplibre/maplibre-native/issues/3477.
|
|
NSArray *jsonExpression = @[
|
|
@"case", @[ @"==", @[ @"get", @"icon" ], @"1" ], @[ @"image", @"icon1" ],
|
|
@[ @"==", @[ @"get", @"icon" ], @"2" ], @[ @"image", @"icon2" ], @[ @"image", @"icon3" ]
|
|
];
|
|
NSExpression *expression = [NSExpression expressionWithMLNJSONObject:jsonExpression];
|
|
|
|
XCTAssertNoThrow(layer.iconImageName = expression);
|
|
}
|
|
|
|
- (void)testRemovingLayerBeforeAddingSameLayer {
|
|
{
|
|
MLNShapeSource *source =
|
|
[[MLNShapeSource alloc] initWithIdentifier:@"shape-source-removing-before-adding"
|
|
shape:nil
|
|
options:nil];
|
|
|
|
// Attempting to find a layer with identifier will trigger an exception if the source associated
|
|
// with the layer is not added
|
|
[self.style addSource:source];
|
|
|
|
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"fill-layer"
|
|
source:source];
|
|
[self.style removeLayer:fillLayer];
|
|
[self.style addLayer:fillLayer];
|
|
XCTAssertNotNil([self.style layerWithIdentifier:fillLayer.identifier]);
|
|
|
|
MLNSymbolStyleLayer *symbolLayer =
|
|
[[MLNSymbolStyleLayer alloc] initWithIdentifier:@"symbol-layer" source:source];
|
|
[self.style removeLayer:symbolLayer];
|
|
[self.style addLayer:symbolLayer];
|
|
XCTAssertNotNil([self.style layerWithIdentifier:symbolLayer.identifier]);
|
|
|
|
MLNLineStyleLayer *lineLayer = [[MLNLineStyleLayer alloc] initWithIdentifier:@"line-layer"
|
|
source:source];
|
|
[self.style removeLayer:lineLayer];
|
|
[self.style addLayer:lineLayer];
|
|
XCTAssertNotNil([self.style layerWithIdentifier:lineLayer.identifier]);
|
|
|
|
MLNCircleStyleLayer *circleLayer =
|
|
[[MLNCircleStyleLayer alloc] initWithIdentifier:@"circle-layer" source:source];
|
|
[self.style removeLayer:circleLayer];
|
|
[self.style addLayer:circleLayer];
|
|
XCTAssertNotNil([self.style layerWithIdentifier:circleLayer.identifier]);
|
|
|
|
MLNBackgroundStyleLayer *backgroundLayer =
|
|
[[MLNBackgroundStyleLayer alloc] initWithIdentifier:@"background-layer"];
|
|
[self.style removeLayer:backgroundLayer];
|
|
[self.style addLayer:backgroundLayer];
|
|
XCTAssertNotNil([self.style layerWithIdentifier:backgroundLayer.identifier]);
|
|
}
|
|
|
|
{
|
|
MLNRasterTileSource *rasterSource =
|
|
[[MLNRasterTileSource alloc] initWithIdentifier:@"raster-tile-source"
|
|
tileURLTemplates:@[]
|
|
options:nil];
|
|
[self.style addSource:rasterSource];
|
|
|
|
MLNRasterStyleLayer *rasterLayer =
|
|
[[MLNRasterStyleLayer alloc] initWithIdentifier:@"raster-layer" source:rasterSource];
|
|
[self.style removeLayer:rasterLayer];
|
|
[self.style addLayer:rasterLayer];
|
|
XCTAssertNotNil([self.style layerWithIdentifier:rasterLayer.identifier]);
|
|
}
|
|
}
|
|
|
|
- (void)testAddingLayerOfTypeABeforeRemovingLayerOfTypeBWithSameIdentifier {
|
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"shape-source-identifier"
|
|
shape:nil
|
|
options:nil];
|
|
[self.style addSource:source];
|
|
|
|
// Add a fill layer
|
|
MLNFillStyleLayer *fillLayer = [[MLNFillStyleLayer alloc] initWithIdentifier:@"some-identifier"
|
|
source:source];
|
|
[self.style addLayer:fillLayer];
|
|
|
|
// Attempt to remove a line layer with the same identifier as the fill layer
|
|
MLNLineStyleLayer *lineLayer = [[MLNLineStyleLayer alloc] initWithIdentifier:fillLayer.identifier
|
|
source:source];
|
|
[self.style removeLayer:lineLayer];
|
|
|
|
XCTAssertTrue([[self.style layerWithIdentifier:fillLayer.identifier]
|
|
isMemberOfClass:[MLNFillStyleLayer class]]);
|
|
}
|
|
|
|
- (NSString *)stringWithContentsOfStyleHeader {
|
|
NSURL *styleHeaderURL = [[[NSBundle mgl_frameworkBundle].bundleURL
|
|
URLByAppendingPathComponent:@"Headers"
|
|
isDirectory:YES] URLByAppendingPathComponent:@"MLNStyle.h"];
|
|
NSError *styleHeaderError;
|
|
NSString *styleHeader = [NSString stringWithContentsOfURL:styleHeaderURL
|
|
usedEncoding:nil
|
|
error:&styleHeaderError];
|
|
XCTAssertNil(styleHeaderError, @"Error getting contents of MLNStyle.h.");
|
|
return styleHeader;
|
|
}
|
|
|
|
- (void)testImages {
|
|
NSString *imageName = @"TrackingLocationMask";
|
|
#if TARGET_OS_IPHONE
|
|
MLNImage *image = [MLNImage imageNamed:imageName
|
|
inBundle:[NSBundle bundleForClass:[self class]]
|
|
compatibleWithTraitCollection:nil];
|
|
#else
|
|
MLNImage *image = [[NSBundle bundleForClass:[self class]] imageForResource:imageName];
|
|
#endif
|
|
XCTAssertNotNil(image);
|
|
|
|
[self.style setImage:image forName:imageName];
|
|
MLNImage *styleImage = [self.style imageForName:imageName];
|
|
|
|
XCTAssertNotNil(styleImage);
|
|
XCTAssertEqual(image.size.width, styleImage.size.width);
|
|
XCTAssertEqual(image.size.height, styleImage.size.height);
|
|
}
|
|
|
|
- (void)testLayersOrder {
|
|
NSString *filePath = [[NSBundle bundleForClass:self.class] pathForResource:@"amsterdam"
|
|
ofType:@"geojson"];
|
|
NSURL *url = [NSURL fileURLWithPath:filePath];
|
|
MLNShapeSource *source = [[MLNShapeSource alloc] initWithIdentifier:@"sourceID"
|
|
URL:url
|
|
options:nil];
|
|
[self.style addSource:source];
|
|
|
|
MLNCircleStyleLayer *layer1 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer1"
|
|
source:source];
|
|
[self.style addLayer:layer1];
|
|
|
|
MLNCircleStyleLayer *layer3 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer3"
|
|
source:source];
|
|
[self.style addLayer:layer3];
|
|
|
|
MLNCircleStyleLayer *layer2 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer2"
|
|
source:source];
|
|
[self.style insertLayer:layer2 aboveLayer:layer1];
|
|
|
|
MLNCircleStyleLayer *layer4 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer4"
|
|
source:source];
|
|
[self.style insertLayer:layer4 aboveLayer:layer3];
|
|
|
|
MLNCircleStyleLayer *layer0 = [[MLNCircleStyleLayer alloc] initWithIdentifier:@"layer0"
|
|
source:source];
|
|
[self.style insertLayer:layer0 belowLayer:layer1];
|
|
|
|
NSArray<MLNStyleLayer *> *layers = [self.style layers];
|
|
NSUInteger startIndex = 0;
|
|
if ([layers.firstObject.identifier isEqualToString:@"org.maplibre.annotations.points"]) {
|
|
startIndex++;
|
|
}
|
|
|
|
XCTAssertEqualObjects(layers[startIndex++].identifier, layer0.identifier);
|
|
XCTAssertEqualObjects(layers[startIndex++].identifier, layer1.identifier);
|
|
XCTAssertEqualObjects(layers[startIndex++].identifier, layer2.identifier);
|
|
XCTAssertEqualObjects(layers[startIndex++].identifier, layer3.identifier);
|
|
XCTAssertEqualObjects(layers[startIndex++].identifier, layer4.identifier);
|
|
}
|
|
|
|
// MARK: Localization tests
|
|
|
|
- (void)testLanguageMatching {
|
|
{
|
|
NSArray *preferences = @[ @"en" ];
|
|
XCTAssertEqualObjects(
|
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
|
}
|
|
{
|
|
NSArray *preferences = @[ @"en-US" ];
|
|
XCTAssertEqualObjects(
|
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
|
}
|
|
{
|
|
NSArray *preferences = @[ @"fr" ];
|
|
XCTAssertEqualObjects(
|
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"fr");
|
|
}
|
|
{
|
|
NSArray *preferences = @[ @"zh-Hans" ];
|
|
XCTAssertEqualObjects(
|
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
|
|
}
|
|
{
|
|
NSArray *preferences = @[ @"zh-Hans", @"en" ];
|
|
XCTAssertEqualObjects(
|
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
|
|
}
|
|
{
|
|
NSArray *preferences = @[ @"zh-Hant" ];
|
|
XCTAssertEqualObjects(
|
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hant");
|
|
}
|
|
{
|
|
NSArray *preferences = @[ @"en", @"fr", @"el" ];
|
|
XCTAssertEqualObjects(
|
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
|
}
|
|
{
|
|
NSArray *preferences = @[ @"tlh" ];
|
|
XCTAssertNil([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
|
|
}
|
|
{
|
|
NSArray *preferences = @[ @"tlh", @"en" ];
|
|
XCTAssertEqualObjects(
|
|
[MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
|
|
}
|
|
{
|
|
NSArray *preferences = @[ @"mul" ];
|
|
XCTAssertNil([MLNVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
|
|
}
|
|
}
|
|
|
|
// MARK: Transition tests
|
|
|
|
- (void)testTransition {
|
|
MLNTransition transitionTest = MLNTransitionMake(5, 4);
|
|
|
|
self.style.transition = transitionTest;
|
|
|
|
XCTAssert(self.style.transition.delay == transitionTest.delay);
|
|
XCTAssert(self.style.transition.duration == transitionTest.duration);
|
|
}
|
|
|
|
- (void)testPerformsPlacementTransitions {
|
|
XCTAssertTrue(self.style.performsPlacementTransitions,
|
|
@"The default value for enabling placement transitions should be YES.");
|
|
|
|
self.style.performsPlacementTransitions = NO;
|
|
XCTAssertFalse(self.style.performsPlacementTransitions,
|
|
@"Enabling placement transitions should be NO.");
|
|
}
|
|
|
|
- (void)testStyleJSON {
|
|
// Test getting style JSON
|
|
NSString *styleJSON = self.style.styleJSON;
|
|
XCTAssertNotNil(styleJSON, @"Style JSON should not be nil");
|
|
|
|
// Verify the JSON is valid
|
|
NSError *error = nil;
|
|
id jsonObject =
|
|
[NSJSONSerialization JSONObjectWithData:[styleJSON dataUsingEncoding:NSUTF8StringEncoding]
|
|
options:0
|
|
error:&error];
|
|
XCTAssertNil(error, @"Style JSON should be valid JSON");
|
|
XCTAssertNotNil(jsonObject, @"Style JSON should parse to a valid object");
|
|
XCTAssertTrue([jsonObject isKindOfClass:[NSDictionary class]],
|
|
@"Style JSON should represent a dictionary");
|
|
}
|
|
|
|
@end
|