import XCTest import Mapbox extension MGLStyleValueTests { func testConstantValues() { let shapeSource = MGLShapeSource(identifier: "source", shape: nil, options: nil) let symbolStyleLayer = MGLSymbolStyleLayer(identifier: "symbolLayer", source: shapeSource) let circleStyleLayer = MGLCircleStyleLayer(identifier: "circleLayer", source: shapeSource) // Boolean symbolStyleLayer.iconAllowsOverlap = MGLStyleConstantValue(rawValue: true) XCTAssertEqual((symbolStyleLayer.iconAllowsOverlap as! MGLStyleConstantValue).rawValue, true) // Number symbolStyleLayer.iconHaloWidth = MGLStyleConstantValue(rawValue: 3) XCTAssertEqual((symbolStyleLayer.iconHaloWidth as! MGLStyleConstantValue).rawValue, 3) // String symbolStyleLayer.text = MGLStyleConstantValue(rawValue: "{name}") XCTAssertEqual((symbolStyleLayer.text as! MGLStyleConstantValue).rawValue, "{name}") var circleTranslationOne = CGVector(dx: 100, dy: 0) let circleTranslationValueOne = NSValue(bytes: &circleTranslationOne, objCType: "{CGVector=dd}") // non-data-driven (interpolatable property value), set to constant style value let expectedCircleTranslationValue = MGLStyleValue(rawValue: circleTranslationValueOne) circleStyleLayer.circleTranslation = expectedCircleTranslationValue XCTAssertEqual(circleStyleLayer.circleTranslation, expectedCircleTranslationValue) // non-data-driven (enumeration property value), set to constant style value let expectedCircleScaleAlignmentValue = MGLStyleValue(rawValue: NSValue(mglCircleScaleAlignment: .map)) circleStyleLayer.circleScaleAlignment = expectedCircleScaleAlignmentValue XCTAssertEqual(circleStyleLayer.circleScaleAlignment, expectedCircleScaleAlignmentValue) } func testDeprecatedFunctions() { let shapeSource = MGLShapeSource(identifier: "test", shape: nil, options: nil) let symbolStyleLayer = MGLSymbolStyleLayer(identifier: "symbolLayer", source: shapeSource) // deprecated function, stops with float values let iconHaloBlurStyleValue = MGLStyleValue(interpolationBase: 1.0, stops: [1: MGLStyleValue(rawValue: 0), 2: MGLStyleValue(rawValue: 1), 3: MGLStyleValue(rawValue: 2), 4: MGLStyleValue(rawValue: 3)]) symbolStyleLayer.iconHaloBlur = iconHaloBlurStyleValue XCTAssertEqual(symbolStyleLayer.iconHaloBlur!, iconHaloBlurStyleValue) // deprecated function, stops with boolean values let stops: [NSNumber: MGLStyleValue] = [ 1: MGLStyleValue(rawValue: true), 2: MGLStyleValue(rawValue: false), 3: MGLStyleValue(rawValue: true), 4: MGLStyleValue(rawValue: false), ] let iconAllowsOverlapStyleValue = MGLStyleValue(interpolationBase: 1, stops: stops) symbolStyleLayer.iconAllowsOverlap = iconAllowsOverlapStyleValue // iconAllowsOverlap is boolean so mgl and mbgl conversions will coerce the developers stops into interval stops let expectedIconAllowsOverlapStyleValue = MGLStyleValue( interpolationMode: .interval, cameraStops: stops, options: nil ) XCTAssertEqual(symbolStyleLayer.iconAllowsOverlap, expectedIconAllowsOverlapStyleValue) } func testFunctionsWithNonDataDrivenProperties() { let shapeSource = MGLShapeSource(identifier: "test", shape: nil, options: nil) let circleStyleLayer = MGLCircleStyleLayer(identifier: "circleLayer", source: shapeSource) var circleTranslationOne = CGVector(dx: 100, dy: 0) let circleTranslationValueOne = NSValue(bytes: &circleTranslationOne, objCType: "{CGVector=dd}") var circleTranslationTwo = CGVector(dx: 0, dy: 0) let circleTranslationValueTwo = NSValue(bytes: &circleTranslationTwo, objCType: "{CGVector=dd}") let circleTranslationStops = [ 0: MGLStyleValue(rawValue: circleTranslationValueOne), 10: MGLStyleValue(rawValue: circleTranslationValueTwo) ] // non-data-driven (interpolatable property value), camera function with CGVector (NSValue) stop values let expectedCircleTranslationValue = MGLStyleValue( interpolationMode: .interval, cameraStops: circleTranslationStops, options: nil ) circleStyleLayer.circleTranslation = expectedCircleTranslationValue XCTAssertEqual(circleStyleLayer.circleTranslation, expectedCircleTranslationValue) // non-data-driven (enumeration property value), camera function with MGLCircleScaleAlignment enum (NSValue) stop values let scaleAlignmentStops = [ 0: MGLStyleValue(rawValue: NSValue(mglCircleScaleAlignment: .map)), 10: MGLStyleValue(rawValue: NSValue(mglCircleScaleAlignment: .viewport)) ] let expectedCircleScaleAlignmentValue = MGLStyleValue( interpolationMode: .interval, cameraStops: scaleAlignmentStops, options: nil ) circleStyleLayer.circleScaleAlignment = expectedCircleScaleAlignmentValue XCTAssertEqual(circleStyleLayer.circleScaleAlignment, expectedCircleScaleAlignmentValue) } func testFunctionsWithDataDrivenProperties() { let shapeSource = MGLShapeSource(identifier: "test", shape: nil, options: nil) let circleStyleLayer = MGLCircleStyleLayer(identifier: "circleLayer", source: shapeSource) #if os(iOS) || os(watchOS) || os(tvOS) // data-driven, camera function with exponential color stop values let redGreenStops = [ 0: MGLStyleValue(rawValue: .red), 10: MGLStyleValue(rawValue: .red), 15: MGLStyleValue(rawValue: .green) ] let expectedCircleColorValue = MGLStyleValue( interpolationMode: .exponential, cameraStops: redGreenStops, options: [.interpolationBase: 10.0] ) circleStyleLayer.circleColor = expectedCircleColorValue XCTAssertEqual(circleStyleLayer.circleColor, expectedCircleColorValue) // data-driven, source function with categorical color stop values with string attribute keys let redOnlyStops = [ "red": MGLStyleValue(rawValue: .red) ] let expectedRedCategoricalValue = MGLStyleValue( interpolationMode: .categorical, sourceStops: redOnlyStops, attributeName: "red", options: [.defaultValue: MGLStyleValue(rawValue: .cyan)] ) circleStyleLayer.circleColor = expectedRedCategoricalValue XCTAssertEqual(circleStyleLayer.circleColor, expectedRedCategoricalValue) // data-driven, source function with categorical color stop values with integer attribute keys let greenOrangeStops = [ 0: MGLStyleValue(rawValue: .green), 100: MGLStyleValue(rawValue: .orange) ] let expectedGreenOrangeCategoricalValue = MGLStyleValue( interpolationMode: .categorical, sourceStops: greenOrangeStops, attributeName: "temp", options: [.defaultValue: MGLStyleValue(rawValue: .red)] ) circleStyleLayer.circleColor = expectedGreenOrangeCategoricalValue XCTAssertEqual(circleStyleLayer.circleColor, expectedGreenOrangeCategoricalValue) // data-driven, source function with exponential color stop values let expectedRedGreenSourceExponentialValue = MGLStyleValue( interpolationMode: .exponential, sourceStops: redGreenStops, attributeName: "temp", options: nil ) circleStyleLayer.circleColor = expectedRedGreenSourceExponentialValue XCTAssertEqual(circleStyleLayer.circleColor, expectedRedGreenSourceExponentialValue) // data-driven, identity source function let expectedSourceIdentityValue = MGLStyleValue( interpolationMode: .identity, sourceStops: nil, attributeName: "size", options: [.defaultValue: MGLStyleValue(rawValue: .green)] ) circleStyleLayer.circleColor = expectedSourceIdentityValue XCTAssertEqual(circleStyleLayer.circleColor, expectedSourceIdentityValue) #elseif os(macOS) // data-driven, camera function with exponential color stop values let redGreenStops = [ 0: MGLStyleValue(rawValue: .red), 10: MGLStyleValue(rawValue: .red), 15: MGLStyleValue(rawValue: .green) ] let expectedCircleColorValue = MGLStyleValue( interpolationMode: .exponential, cameraStops: redGreenStops, options: [.interpolationBase: 10.0] ) circleStyleLayer.circleColor = expectedCircleColorValue XCTAssertEqual(circleStyleLayer.circleColor, expectedCircleColorValue) // data-driven, source function with categorical color stop values with string typed keys let redOnlyStops = [ "red": MGLStyleValue(rawValue: .red) ] let expectedRedCategoricalValue = MGLStyleValue( interpolationMode: .categorical, sourceStops: redOnlyStops, attributeName: "red", options: [.defaultValue: MGLStyleValue(rawValue: .cyan)] ) circleStyleLayer.circleColor = expectedRedCategoricalValue XCTAssertEqual(circleStyleLayer.circleColor, expectedRedCategoricalValue) // data-driven, source function with categorical color stop values with integer attribute keys let greenOrangeStops = [ 0: MGLStyleValue(rawValue: .green), 100: MGLStyleValue(rawValue: .orange) ] let expectedGreenOrangeCategoricalValue = MGLStyleValue( interpolationMode: .categorical, sourceStops: greenOrangeStops, attributeName: "temp", options: [.defaultValue: MGLStyleValue(rawValue: .red)] ) circleStyleLayer.circleColor = expectedGreenOrangeCategoricalValue XCTAssertEqual(circleStyleLayer.circleColor, expectedGreenOrangeCategoricalValue) // data-driven, source function with exponential color stop values let expectedRedGreenSourceExponentialValue = MGLStyleValue( interpolationMode: .exponential, sourceStops: redGreenStops, attributeName: "temp", options: nil ) circleStyleLayer.circleColor = expectedRedGreenSourceExponentialValue XCTAssertEqual(circleStyleLayer.circleColor, expectedRedGreenSourceExponentialValue) // data-driven, identity source function let expectedSourceIdentityValue = MGLStyleValue( interpolationMode: .identity, sourceStops: nil, attributeName: "size", options: nil ) circleStyleLayer.circleColor = expectedSourceIdentityValue XCTAssertEqual(circleStyleLayer.circleColor, expectedSourceIdentityValue) #endif // data-driven, source function with categorical color stop values with boolean attribute keys let booleanCategoricalStops = [ false: MGLStyleValue(rawValue: 0), true: MGLStyleValue(rawValue: 2) ] let expectedCircleBlurCategoricalValue = MGLStyleValue( interpolationMode: .categorical, sourceStops: booleanCategoricalStops, attributeName: "fuzzy", options: [.defaultValue: MGLStyleValue(rawValue: 42)] ) circleStyleLayer.circleBlur = expectedCircleBlurCategoricalValue XCTAssertEqual(circleStyleLayer.circleBlur, expectedCircleBlurCategoricalValue) // data-driven, composite function with inner categorical color stop values with string attribute keys nested in outer camera stops let smallRadius = MGLStyleValue(rawValue: 5) let mediumRadius = MGLStyleValue(rawValue: 10) let largeRadius = MGLStyleValue(rawValue: 20) let radiusCompositeCategoricalStops: [NSNumber: [String: MGLStyleValue]] = [ 0: ["green": smallRadius], 10: ["green": smallRadius], 15: ["green": largeRadius], 20: ["green": largeRadius] ] let defaultRadius = MGLStyleValue(rawValue: 2) let expectedCompositeCategoricalValue = MGLStyleValue( interpolationMode: .categorical, compositeStops: radiusCompositeCategoricalStops, attributeName: "color", options: [.defaultValue: defaultRadius] ) circleStyleLayer.circleRadius = expectedCompositeCategoricalValue XCTAssertEqual(circleStyleLayer.circleRadius, expectedCompositeCategoricalValue) // data-driven, composite function with inner exponential color stop values nested in outer camera stops let radiusCompositeExponentialOrIntervalStops: [NSNumber: [NSNumber: MGLStyleValue]] = [ 0: [0: smallRadius], 10: [200: smallRadius], 20: [200: largeRadius] ] let expectedCompositeExponentialValue = MGLStyleValue( interpolationMode: .exponential, compositeStops: radiusCompositeExponentialOrIntervalStops, attributeName: "temp", options: [.defaultValue: mediumRadius] ) circleStyleLayer.circleRadius = expectedCompositeExponentialValue XCTAssertEqual(circleStyleLayer.circleRadius, expectedCompositeExponentialValue) // get a value back if let returnedCircleRadius = circleStyleLayer.circleRadius as? MGLCompositeStyleFunction { if let returnedStops = returnedCircleRadius.stops as NSDictionary? as? [NSNumber: [NSNumber: MGLStyleValue]] { let lhs: MGLStyleValue = returnedStops[0]!.values.first! let rhs: MGLStyleValue = radiusCompositeExponentialOrIntervalStops[0]!.values.first! XCTAssertEqual(lhs, rhs) } } // data-driven, composite function with inner interval color stop values nested in outer camera stops let expectedCompositeIntervalValue = MGLStyleValue( interpolationMode: .interval, compositeStops: radiusCompositeExponentialOrIntervalStops, attributeName: "temp", options: nil ) circleStyleLayer.circleRadius = expectedCompositeIntervalValue XCTAssertEqual(circleStyleLayer.circleRadius, expectedCompositeIntervalValue) } }