From 899d393d79f9a23a35b30fa494a206d6eac25115 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Tue, 24 Jul 2018 16:46:39 -0700 Subject: [PATCH] Basic tests and validation for "formatted". --- .../expression/definitions/formatted.js | 15 +++ .../expression/definitions/literal.js | 9 +- src/style-spec/reference/v8.json | 2 +- src/style-spec/validate/validate.js | 10 +- src/style-spec/validate/validate_formatted.js | 13 +++ .../concat/formatted/test.json | 88 ++++++++++++++++++ .../text-field/formatted-line/expected.png | Bin 0 -> 612 bytes .../text-field/formatted-line/style.json | 56 +++++++++++ .../text-field/formatted/expected.png | Bin 0 -> 4867 bytes .../text-field/formatted/style.json | 53 +++++++++++ 10 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 src/style-spec/validate/validate_formatted.js create mode 100644 test/integration/expression-tests/concat/formatted/test.json create mode 100644 test/integration/render-tests/text-field/formatted-line/expected.png create mode 100644 test/integration/render-tests/text-field/formatted-line/style.json create mode 100644 test/integration/render-tests/text-field/formatted/expected.png create mode 100644 test/integration/render-tests/text-field/formatted/style.json diff --git a/src/style-spec/expression/definitions/formatted.js b/src/style-spec/expression/definitions/formatted.js index 881aa35309a..fc24419ba48 100644 --- a/src/style-spec/expression/definitions/formatted.js +++ b/src/style-spec/expression/definitions/formatted.js @@ -18,6 +18,13 @@ export class FormattedSection { this.scale = scale; this.fontStack = fontStack; } + + serialize() { + const fontStack = this.fontStack ? + ["literal", this.fontStack.split(',')] : + null; + return ["formatted", this.text, { "text-font": fontStack, "font-scale": this.scale }]; + } } export class Formatted { @@ -30,6 +37,14 @@ export class Formatted { toString(): string { return this.sections.map(section => section.text).join(); } + + serialize() { + if (this.sections.length === 1) { + return this.sections[0].serialize(); + } else { + return ["concat"].concat(this.sections.map(section => section.serialize())); + } + } } export class FormattedExpression implements Expression { diff --git a/src/style-spec/expression/definitions/literal.js b/src/style-spec/expression/definitions/literal.js index d1800400afc..09146fec3d5 100644 --- a/src/style-spec/expression/definitions/literal.js +++ b/src/style-spec/expression/definitions/literal.js @@ -2,6 +2,7 @@ import assert from 'assert'; import { isValue, typeOf, Color } from '../values'; +import { Formatted } from './formatted'; import type { Type } from '../types'; import type { Value } from '../values'; @@ -60,11 +61,17 @@ class Literal implements Expression { // couldn't actually generate with a "literal" expression, // so we have to implement an equivalent serialization here return ["rgba"].concat(this.value.toArray()); + } else if (this.value instanceof Formatted) { + // Constant-folding can generate Literal expressions that you + // couldn't actually generate with a "literal" expression, + // so we have to implement an equivalent serialization here + return this.value.serialize(); } else { assert(this.value === null || typeof this.value === 'string' || typeof this.value === 'number' || - typeof this.value === 'boolean'); + typeof this.value === 'boolean' || + typeof this.value === 'formatted'); return (this.value: any); } } diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index cd3d7d1ec10..9698f1c624d 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -1505,7 +1505,7 @@ "property-type": "data-constant" }, "text-field": { - "type": "string | formatted", + "type": "formatted", "default": "", "tokens": true, "doc": "Value to use for a text label.", diff --git a/src/style-spec/validate/validate.js b/src/style-spec/validate/validate.js index 231d9ff8686..5caa8d75800 100644 --- a/src/style-spec/validate/validate.js +++ b/src/style-spec/validate/validate.js @@ -18,6 +18,7 @@ import validateLayer from './validate_layer'; import validateSource from './validate_source'; import validateLight from './validate_light'; import validateString from './validate_string'; +import validateFormatted from './validate_formatted'; const VALIDATORS = { '*': function() { @@ -35,7 +36,8 @@ const VALIDATORS = { 'object': validateObject, 'source': validateSource, 'light': validateLight, - 'string': validateString + 'string': validateString, + 'formatted': validateFormatted }; @@ -64,8 +66,12 @@ export default function validate(options) { return VALIDATORS[valueSpec.type](options); } else { - return validateObject(extend({}, options, { + const valid = validateObject(extend({}, options, { valueSpec: valueSpec.type ? styleSpec[valueSpec.type] : valueSpec })); + if (!valid) { + console.log("not valid"); + } + return valid; } } diff --git a/src/style-spec/validate/validate_formatted.js b/src/style-spec/validate/validate_formatted.js new file mode 100644 index 00000000000..4cac73e2450 --- /dev/null +++ b/src/style-spec/validate/validate_formatted.js @@ -0,0 +1,13 @@ +// @flow + +import ValidationError from '../error/validation_error'; +import validateExpression from './validate_expression'; +import validateString from './validate_string'; + +export default function validateFormatted(options: any) { + if (validateString(options).length === 0) { + return []; + } + + return validateExpression(options); +} diff --git a/test/integration/expression-tests/concat/formatted/test.json b/test/integration/expression-tests/concat/formatted/test.json new file mode 100644 index 00000000000..dffabf25f70 --- /dev/null +++ b/test/integration/expression-tests/concat/formatted/test.json @@ -0,0 +1,88 @@ +{ + "expression": [ + "concat", + "a", + [ + "formatted", + "b", + { + "font-scale": 2 + } + ], + [ + "formatted", + "c", + { + "text-font": [ + "literal", + [ + "a", + "b" + ] + ] + } + ] + ], + "inputs": [ + [ + {}, + {} + ] + ], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": true, + "isZoomConstant": true, + "type": "string" + }, + "outputs": [ + { + "sections": [ + { + "text": "a", + "scale": null, + "fontStack": null + }, + { + "text": "b", + "scale": 2, + "fontStack": null + }, + { + "text": "c", + "scale": null, + "fontStack": "a,b" + } + ] + } + ], + "serialized": [ + "concat", + [ + "formatted", + "a", + { + "font-scale": null, + "text-font": null + } + ], + [ + "formatted", + "b", + { + "font-scale": 2, + "text-font": null + } + ], + [ + "formatted", + "c", + { + "font-scale": null, + "text-font": ["literal", ["a", "b"]] + } + ] + ] + } +} diff --git a/test/integration/render-tests/text-field/formatted-line/expected.png b/test/integration/render-tests/text-field/formatted-line/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..2e8583d07199dad8c769d409e9668b8886a8952b GIT binary patch literal 612 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 Xr#bW*!Mx^IOY88#*ofXR%;o