<commit_before>(function (root, factory) {
    'use strict';
    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers.

    /* istanbul ignore next */
    if (typeof define === 'function' && define.amd) {
        define('stacktrace-gps', ['source-map', 'stackframe'], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(require('source-map/lib/source-map-consumer'), require('stackframe'));
    } else {
        root.StackTraceGPS = factory(root.SourceMap || root.sourceMap, root.StackFrame);
    }
}(this, function (SourceMap, StackFrame) {
    'use strict';

    /**
     * Make a X-Domain request to url and callback.
     *
     * @param url [String]
     * @return Promise with response text if fulfilled
     */
    function _xdr(url) {
        return new Promise(function (resolve, reject) {
            var req = new XMLHttpRequest();
            req.open('get', url);
            req.onerror = reject;
            req.onreadystatechange = function onreadystatechange() {
                if (req.readyState === 4) {
                    if (req.status >= 200 && req.status < 300) {
                        resolve(req.responseText);
                    } else {
                        reject(new Error('HTTP status: ' + req.status + ' retrieving ' + url));
                    }
                }
            };
            req.send();
        });

    }

    function _findFunctionName(source, lineNumber, columnNumber) {
        // function {name}({args}) m[1]=name m[2]=args
        var reFunctionDeclaration = /function\s+([^(]*?)\s*\(([^)]*)\)/;
        // {name} = function ({args}) TODO args capture
        var reFunctionExpression = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/;
        // {name} = eval()
        var reFunctionEvaluation = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/;
        var lines = source.split('\n');

        // Walk backwards in the source lines until we find the line which matches one of the patterns above
        var code = '', line, maxLines = Math.min(lineNumber, 20), m, commentPos;
        for (var i = 0; i < maxLines; ++i) {
            // lineNo is 1-based, source[] is 0-based
            line = lines[lineNumber - i - 1];
            commentPos = line.indexOf('//');
            if (commentPos >= 0) {
                line = line.substr(0, commentPos);
            }

            if (line) {
                code = line + code;
                m = reFunctionExpression.exec(code);
                if (m && m[1]) {
                    return m[1];
                }
                m = reFunctionDeclaration.exec(code);
                if (m && m[1]) {
                    return m[1];
                }
                m = reFunctionEvaluation.exec(code);
                if (m && m[1]) {
                    return m[1];
                }
            }
        }
        return undefined;
    }

    function _ensureSupportedEnvironment() {
        if (typeof Object.defineProperty !== 'function' || typeof Object.create !== 'function') {
            throw new Error('Unable to consume source maps in older browsers');
        }
    }

    function _ensureStackFrameIsLegit(stackframe) {
        if (typeof stackframe !== 'object') {
            throw new TypeError('Given StackFrame is not an object');
        } else if (typeof stackframe.fileName !== 'string') {
            throw new TypeError('Given file name is not a String');
        } else if (typeof stackframe.lineNumber !== 'number' || stackframe.lineNumber % 1 !== 0 || stackframe.lineNumber < 1) {
            throw new TypeError('Given line number must be a positive integer');
        } else if (typeof stackframe.columnNumber !== 'number' || stackframe.columnNumber % 1 !== 0 || stackframe.columnNumber < 0) {
            throw new TypeError('Given column number must be a non-negative integer');
        }
        return true;
    }

    function _findSourceMappingURL(source) {
        var m = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/.exec(source);
        if (m && m[1]) {
            return m[1];
        } else {
            throw new Error('sourceMappingURL not found');
        }
    }

    function _extractLocationInfoFromSourceMap(rawSourceMap, args, lineNumber, columnNumber, sourceCache) {
        var mapConsumer = new SourceMap.SourceMapConsumer(rawSourceMap);

        var loc = mapConsumer.originalPositionFor({
            line: lineNumber,
            column: columnNumber
        });

        var mappedSource = mapConsumer.sourceContentFor(loc.source);
        if (mappedSource) {
            sourceCache[loc.source] = mappedSource;
        }

        return new StackFrame(loc.name, args, loc.source, loc.line, loc.column);
    }

    /**
     * @param opts: [Object] options.
     *      opts.sourceCache = {url: "Source String"} => preload source cache
     *      opts.offline = True to prevent network requests.
     *              Best effort without sources or source maps.
     *      opts.ajax = Promise returning function to make X-Domain requests
     */
    return function StackTraceGPS(opts) {
        if (!(this instanceof StackTraceGPS)) {
            return new StackTraceGPS(opts);
        }
        opts = opts || {};

        this.sourceCache = opts.sourceCache || {};

        this.ajax = opts.ajax || _xdr;

        this._get = function _get(location) {
            return new Promise(function (resolve, reject) {
                var isDataUrl = location.substr(0, 5) === 'data:';
                if (this.sourceCache[location]) {
                    resolve(this.sourceCache[location]);
                } else if (opts.offline && !isDataUrl) {
                    reject(new Error('Cannot make network requests in offline mode'));
                } else {
                    if (isDataUrl) {
                        // data URLs can have parameters.
                        // see http://tools.ietf.org/html/rfc2397
                        var supportedEncodingRegexp =
                            /^data:application\/json;([\w=:"-]+;)*base64,/;
                        var match = location.match(supportedEncodingRegexp);
                        if (match) {
                            var sourceMapStart = match[0].length;
                            var encodedSource = location.substr(sourceMapStart);
                            var source = window.atob(encodedSource);
                            this.sourceCache[location] = source;
                            resolve(source);
                        } else {
                            reject(new Error('The encoding of the inline sourcemap is not supported'));
                        }
                    } else {
                        var xhrPromise = this.ajax(location, {method: 'get'});
                        // Cache the Promise to prevent duplicate in-flight requests
                        this.sourceCache[location] = xhrPromise;
                        xhrPromise.then(resolve, reject);
                    }
                }
            }.bind(this));
        };

        /**
         * Given a StackFrame, enhance function name and use source maps for a
         * better StackFrame.
         *
         * @param stackframe - {StackFrame}-like object
         *      {fileName: 'path/to/file.js', lineNumber: 100, columnNumber: 5}
         * @return StackFrame with source-mapped location
         */
        this.pinpoint = function StackTraceGPS$$pinpoint(stackframe) {
            return new Promise(function (resolve, reject) {
                this.getMappedLocation(stackframe).then(function (mappedStackFrame) {
                    function resolveMappedStackFrame() {
                        resolve(mappedStackFrame);
                    }

                    this.findFunctionName(mappedStackFrame)
                        .then(resolve, resolveMappedStackFrame)
                        ['catch'](resolveMappedStackFrame);
                }.bind(this), reject);
            }.bind(this));
        };

        /**
         * Given a StackFrame, guess function name from location information.
         *
         * @param stackframe - {StackFrame}-like object
         *      {fileName: 'path/to/file.js', lineNumber: 100, columnNumber: 5}
         * @return StackFrame with guessed function name
         */
        this.findFunctionName = function StackTraceGPS$$findFunctionName(stackframe) {
            return new Promise(function (resolve, reject) {
                _ensureStackFrameIsLegit(stackframe);
                this._get(stackframe.fileName).then(function getSourceCallback(source) {
                    var guessedFunctionName = _findFunctionName(source, stackframe.lineNumber, stackframe.columnNumber);
                    resolve(new StackFrame(guessedFunctionName, stackframe.args, stackframe.fileName, stackframe.lineNumber, stackframe.columnNumber));
                }, reject)['catch'](reject);
            }.bind(this));
        };

        /**
         * Given a StackFrame, seek source-mapped location and return new enhanced StackFrame.
         *
         * @param stackframe - {StackFrame}-like object
         *      {fileName: 'path/to/file.js', lineNumber: 100, columnNumber: 5}
         * @return StackFrame with source-mapped location
         */
        this.getMappedLocation = function StackTraceGPS$$getMappedLocation(stackframe) {
            return new Promise(function (resolve, reject) {
                _ensureSupportedEnvironment();
                _ensureStackFrameIsLegit(stackframe);

                var sourceCache = this.sourceCache;
                var fileName = stackframe.fileName;
                this._get(fileName).then(function (source) {
                    var sourceMappingURL = _findSourceMappingURL(source);
                    var isDataUrl = sourceMappingURL.substr(0, 5) === 'data:';

                    if (sourceMappingURL[0] !== '/' && !isDataUrl) {
                        sourceMappingURL = fileName.substring(0, fileName.lastIndexOf('/') + 1) + sourceMappingURL;
                    }

                    this._get(sourceMappingURL).then(function (map) {
                        var lineNumber = stackframe.lineNumber;
                        var columnNumber = stackframe.columnNumber;
                        resolve(_extractLocationInfoFromSourceMap(map, stackframe.args, lineNumber, columnNumber, sourceCache));
                    }, reject)['catch'](reject);
                }.bind(this), reject)['catch'](reject);
            }.bind(this));
        };
    };
}));
<commit_msg>test for absolute urls before making urls absolute<commit_after>(function (root, factory) {
    'use strict';
    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers.

    /* istanbul ignore next */
    if (typeof define === 'function' && define.amd) {
        define('stacktrace-gps', ['source-map', 'stackframe'], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(require('source-map/lib/source-map-consumer'), require('stackframe'));
    } else {
        root.StackTraceGPS = factory(root.SourceMap || root.sourceMap, root.StackFrame);
    }
}(this, function (SourceMap, StackFrame) {
    'use strict';

    /**
     * Make a X-Domain request to url and callback.
     *
     * @param url [String]
     * @return Promise with response text if fulfilled
     */
    function _xdr(url) {
        return new Promise(function (resolve, reject) {
            var req = new XMLHttpRequest();
            req.open('get', url);
            req.onerror = reject;
            req.onreadystatechange = function onreadystatechange() {
                if (req.readyState === 4) {
                    if (req.status >= 200 && req.status < 300) {
                        resolve(req.responseText);
                    } else {
                        reject(new Error('HTTP status: ' + req.status + ' retrieving ' + url));
                    }
                }
            };
            req.send();
        });

    }

    function _findFunctionName(source, lineNumber, columnNumber) {
        // function {name}({args}) m[1]=name m[2]=args
        var reFunctionDeclaration = /function\s+([^(]*?)\s*\(([^)]*)\)/;
        // {name} = function ({args}) TODO args capture
        var reFunctionExpression = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/;
        // {name} = eval()
        var reFunctionEvaluation = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/;
        var lines = source.split('\n');

        // Walk backwards in the source lines until we find the line which matches one of the patterns above
        var code = '', line, maxLines = Math.min(lineNumber, 20), m, commentPos;
        for (var i = 0; i < maxLines; ++i) {
            // lineNo is 1-based, source[] is 0-based
            line = lines[lineNumber - i - 1];
            commentPos = line.indexOf('//');
            if (commentPos >= 0) {
                line = line.substr(0, commentPos);
            }

            if (line) {
                code = line + code;
                m = reFunctionExpression.exec(code);
                if (m && m[1]) {
                    return m[1];
                }
                m = reFunctionDeclaration.exec(code);
                if (m && m[1]) {
                    return m[1];
                }
                m = reFunctionEvaluation.exec(code);
                if (m && m[1]) {
                    return m[1];
                }
            }
        }
        return undefined;
    }

    function _ensureSupportedEnvironment() {
        if (typeof Object.defineProperty !== 'function' || typeof Object.create !== 'function') {
            throw new Error('Unable to consume source maps in older browsers');
        }
    }

    function _ensureStackFrameIsLegit(stackframe) {
        if (typeof stackframe !== 'object') {
            throw new TypeError('Given StackFrame is not an object');
        } else if (typeof stackframe.fileName !== 'string') {
            throw new TypeError('Given file name is not a String');
        } else if (typeof stackframe.lineNumber !== 'number' || stackframe.lineNumber % 1 !== 0 || stackframe.lineNumber < 1) {
            throw new TypeError('Given line number must be a positive integer');
        } else if (typeof stackframe.columnNumber !== 'number' || stackframe.columnNumber % 1 !== 0 || stackframe.columnNumber < 0) {
            throw new TypeError('Given column number must be a non-negative integer');
        }
        return true;
    }

    function _findSourceMappingURL(source) {
        var m = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/.exec(source);
        if (m && m[1]) {
            return m[1];
        } else {
            throw new Error('sourceMappingURL not found');
        }
    }

    function _extractLocationInfoFromSourceMap(rawSourceMap, args, lineNumber, columnNumber, sourceCache) {
        var mapConsumer = new SourceMap.SourceMapConsumer(rawSourceMap);

        var loc = mapConsumer.originalPositionFor({
            line: lineNumber,
            column: columnNumber
        });

        var mappedSource = mapConsumer.sourceContentFor(loc.source);
        if (mappedSource) {
            sourceCache[loc.source] = mappedSource;
        }

        return new StackFrame(loc.name, args, loc.source, loc.line, loc.column);
    }

    /**
     * @param opts: [Object] options.
     *      opts.sourceCache = {url: "Source String"} => preload source cache
     *      opts.offline = True to prevent network requests.
     *              Best effort without sources or source maps.
     *      opts.ajax = Promise returning function to make X-Domain requests
     */
    return function StackTraceGPS(opts) {
        if (!(this instanceof StackTraceGPS)) {
            return new StackTraceGPS(opts);
        }
        opts = opts || {};

        this.sourceCache = opts.sourceCache || {};

        this.ajax = opts.ajax || _xdr;

        this._get = function _get(location) {
            return new Promise(function (resolve, reject) {
                var isDataUrl = location.substr(0, 5) === 'data:';
                if (this.sourceCache[location]) {
                    resolve(this.sourceCache[location]);
                } else if (opts.offline && !isDataUrl) {
                    reject(new Error('Cannot make network requests in offline mode'));
                } else {
                    if (isDataUrl) {
                        // data URLs can have parameters.
                        // see http://tools.ietf.org/html/rfc2397
                        var supportedEncodingRegexp =
                            /^data:application\/json;([\w=:"-]+;)*base64,/;
                        var match = location.match(supportedEncodingRegexp);
                        if (match) {
                            var sourceMapStart = match[0].length;
                            var encodedSource = location.substr(sourceMapStart);
                            var source = window.atob(encodedSource);
                            this.sourceCache[location] = source;
                            resolve(source);
                        } else {
                            reject(new Error('The encoding of the inline sourcemap is not supported'));
                        }
                    } else {
                        var xhrPromise = this.ajax(location, {method: 'get'});
                        // Cache the Promise to prevent duplicate in-flight requests
                        this.sourceCache[location] = xhrPromise;
                        xhrPromise.then(resolve, reject);
                    }
                }
            }.bind(this));
        };

        /**
         * Given a StackFrame, enhance function name and use source maps for a
         * better StackFrame.
         *
         * @param stackframe - {StackFrame}-like object
         *      {fileName: 'path/to/file.js', lineNumber: 100, columnNumber: 5}
         * @return StackFrame with source-mapped location
         */
        this.pinpoint = function StackTraceGPS$$pinpoint(stackframe) {
            return new Promise(function (resolve, reject) {
                this.getMappedLocation(stackframe).then(function (mappedStackFrame) {
                    function resolveMappedStackFrame() {
                        resolve(mappedStackFrame);
                    }

                    this.findFunctionName(mappedStackFrame)
                        .then(resolve, resolveMappedStackFrame)
                        ['catch'](resolveMappedStackFrame);
                }.bind(this), reject);
            }.bind(this));
        };

        /**
         * Given a StackFrame, guess function name from location information.
         *
         * @param stackframe - {StackFrame}-like object
         *      {fileName: 'path/to/file.js', lineNumber: 100, columnNumber: 5}
         * @return StackFrame with guessed function name
         */
        this.findFunctionName = function StackTraceGPS$$findFunctionName(stackframe) {
            return new Promise(function (resolve, reject) {
                _ensureStackFrameIsLegit(stackframe);
                this._get(stackframe.fileName).then(function getSourceCallback(source) {
                    var guessedFunctionName = _findFunctionName(source, stackframe.lineNumber, stackframe.columnNumber);
                    resolve(new StackFrame(guessedFunctionName, stackframe.args, stackframe.fileName, stackframe.lineNumber, stackframe.columnNumber));
                }, reject)['catch'](reject);
            }.bind(this));
        };

        /**
         * Given a StackFrame, seek source-mapped location and return new enhanced StackFrame.
         *
         * @param stackframe - {StackFrame}-like object
         *      {fileName: 'path/to/file.js', lineNumber: 100, columnNumber: 5}
         * @return StackFrame with source-mapped location
         */
        this.getMappedLocation = function StackTraceGPS$$getMappedLocation(stackframe) {
            return new Promise(function (resolve, reject) {
                _ensureSupportedEnvironment();
                _ensureStackFrameIsLegit(stackframe);

                var sourceCache = this.sourceCache;
                var fileName = stackframe.fileName;
                this._get(fileName).then(function (source) {
                    var sourceMappingURL = _findSourceMappingURL(source);
                    var isDataUrl = sourceMappingURL.substr(0, 5) === 'data:';

                    if (sourceMappingURL[0] !== '/' && !isDataUrl && !(/^https?:\/\/|^\/\//i).test(sourceMappingURL)) {
                        sourceMappingURL = fileName.substring(0, fileName.lastIndexOf('/') + 1) + sourceMappingURL;
                    }

                    this._get(sourceMappingURL).then(function (map) {
                        var lineNumber = stackframe.lineNumber;
                        var columnNumber = stackframe.columnNumber;
                        resolve(_extractLocationInfoFromSourceMap(map, stackframe.args, lineNumber, columnNumber, sourceCache));
                    }, reject)['catch'](reject);
                }.bind(this), reject)['catch'](reject);
            }.bind(this));
        };
    };
}));
<|endoftext|>


<commit_before>const Timer = require('../../stats/timer');
const debug = require('debug')('windshaft:renderer:pg_mvt');
const SubstitutionTokens = require('../../utils/substitution_tokens');

function shouldUseLayer(layer, zoom) {
    const minZoom = Number.isFinite(layer.options.minzoom) ? layer.options.minzoom : 0;
    const maxZoom = Number.isFinite(layer.options.maxzoom) ? layer.options.maxzoom : 30;
    return zoom >= minZoom && zoom <= maxZoom;
}

function extractMVT(data) {
    if (data &&
        data.rows &&
        data.rows.length > 0 &&
        data.rows[0].st_asmvt &&
        data.rows[0].st_asmvt.length > 0) {
        return data.rows[0].st_asmvt;
    }

    return null;
}

/// CLASS: pg_mvt Renderer
//
/// A renderer for a given MapConfig layer
///
module.exports = class PostgresVectorRenderer {
    constructor (layers, psql, attrs, options) {
        options = options || {};

        this.psql = psql;
        this.attrs = attrs;
        this.layers = layers;

        this.tile_size = options.tileSize || 256;
        this.tile_max_geosize = options.maxGeosize || 40075017; // earth circumference in webmercator 3857
        this.buffer_size = options.bufferSize || 0; // Same as Mapnik::bufferSize
        this.mvt_extent = options.mvt_extent || 4096;
    }

    /// API: renders a tile with the Renderer configuration
    /// @param x tile x coordinate
    /// @param y tile y coordinate
    /// @param z tile zoom
    /// callback: will be called when done using nodejs protocol (err, data)
    getTile (z, x, y, callback) {
        const subqueriesPromises = this._getLayerQueries({ z, x, y });
        const empty_tile_msg = 'Tile does not exist';
        Promise.all(subqueriesPromises)
        .then(subqueries =>  {
            if (subqueries.length === 0) {
                return callback(new Error(empty_tile_msg));
            }

            const query = `SELECT ((${subqueries.join(') || (')})) AS st_asmvt`;
            const timer = new Timer();

            timer.start('query');
            this.psql.query(query, function (err, data) {
                timer.end('query');
                if (err) {
                    debug("Error running pg-mvt query " + query + ": " + err);
                    if (err.message) {
                        err.message = "PgMvtRenderer: " + err.message;
                    }
                    return callback(err);
                }

                const mvt = extractMVT(data);
                if (!mvt) {
                    return callback(new Error(empty_tile_msg));
                }

                const headers = { 'Content-Type': 'application/x-protobuf' };
                const stats = timer.getTimes();

                return callback(null, mvt, headers, stats);
            });
        }, err => { return callback(err); })
        .catch(() => { }); // Avoids UnhandledPromiseRejectionWarning
    }

    _getLayerColumns (layer) {
        return new Promise((resolve, reject) => {
            if (layer._metadata_columns) {
                resolve(layer._metadata_columns);
            }
            const layerSQL = this._replaceTokens(layer.options.sql, 0, 0, 0);
            const limitedQuery = `SELECT * FROM (${layerSQL}) __windshaft_mvt_schema LIMIT 0;`;

            this.psql.query(limitedQuery, function (err, data) {
                if (err) {
                    if (err.message) {
                        err.message = "PgMvtRenderer: " + err.message;
                    }
                    return reject(err);
                }
                else {
                    // We filter here by type id to avoid the implicit casts that ST_AsMVT does
                    // from any unknown type to string
                    // These are the list of compatible type oids as declared in
                    // mapnik/plugins/input/postgis/postgis_datasource.cpp
                    const COMPATIBLE_MVT_TYPEIDS = [
                        16,     // bool
                        20,     // int8
                        21,     // int2
                        23,     // int4
                        700,    // float4
                        701,    // float8
                        1700,   // numeric
                        1042,   // bpchar
                        1043,   // varchar
                        25,     // text
                        705,    // literal
                    ];

                    let _metadata_columns = [];
                    Object.keys(data.fields).forEach(function(key){
                        const val = data.fields[key];
                        if (COMPATIBLE_MVT_TYPEIDS.includes(val.dataTypeID)) {
                            _metadata_columns.push(val.name);
                        }
                    });
                    resolve(_metadata_columns);
                }
            });
        });
    }

    _getLayerQueries({ z, x, y }) {
        return this.layers.filter(l => shouldUseLayer(l, z)).map(layer => {
            return this._getLayerColumns(layer)
                .then( layerColumns => {
                    const geomColumn = this._geomColumn(layer, { z, x, y });
                    const columns = [geomColumn].concat(layerColumns);
                    const query = this._replaceTokens(layer.options.sql, { z, x, y });
                    const finalQuery = this._vectorLayerQuery(layer, columns, query, { z, x, y });
                    return finalQuery;
                });
        });
    }

    _geomColumn (layer, { z, x, y }) {
        // Geometry column
        const subpixelBufferSize = Math.round(this.buffer_size * this.mvt_extent / this.tile_size);
        const geomColumn = layer.options.geom_column || 'the_geom_webmercator';
        return `
        ST_AsMVTGeom(
            ${geomColumn},
            CDB_XYZ_Extent(${x},${y},${z}),
            ${this.mvt_extent},
            ${subpixelBufferSize},
            true
        ) as the_geom_webmercator
        `;
    }

    _replaceTokens (sql, { z, x, y }) {
        return SubstitutionTokens.replace(sql, {
            bbox: `CDB_XYZ_Extent(${x},${y},${z})`,
            // See https://github.com/mapnik/mapnik/wiki/ScaleAndPpi#scale-denominator
            scale_denominator: `(cdb_XYZ_Resolution(${z})::numeric *${256 / this.tile_size / 0.00028})`,
            pixel_width: `cdb_XYZ_Resolution(${z})*${256 / this.tile_size}`,
            pixel_height: `cdb_XYZ_Resolution(${z})*${256 / this.tile_size}`,
            var_zoom: z,
            var_x: x,
            var_y: y
        });
    }

    _vectorLayerQuery (layer, columns, query, { z, x, y }) {
        const layerId = layer.id;
        const geomColumn = layer.options.geom_column || 'the_geom_webmercator';

        return `
SELECT ST_AsMVT(geom, '${layerId}')
FROM (
    SELECT ${columns.join(', ')}
    FROM (${query}) AS cdbq
    WHERE
        ${geomColumn} &&
        ST_Expand(
            CDB_XYZ_Extent(${x},${y},${z}),
            CDB_XYZ_Resolution(${z}) * ${Math.round(256 * this.buffer_size / this.tile_size)}
        )
) AS geom
        `;
    }
};
<commit_msg>pg-mvt: Improve promise error handling<commit_after>const Timer = require('../../stats/timer');
const debug = require('debug')('windshaft:renderer:pg_mvt');
const SubstitutionTokens = require('../../utils/substitution_tokens');

function shouldUseLayer(layer, zoom) {
    const minZoom = Number.isFinite(layer.options.minzoom) ? layer.options.minzoom : 0;
    const maxZoom = Number.isFinite(layer.options.maxzoom) ? layer.options.maxzoom : 30;
    return zoom >= minZoom && zoom <= maxZoom;
}

function extractMVT(data) {
    if (data &&
        data.rows &&
        data.rows.length > 0 &&
        data.rows[0].st_asmvt &&
        data.rows[0].st_asmvt.length > 0) {
        return data.rows[0].st_asmvt;
    }

    return null;
}

/// CLASS: pg_mvt Renderer
//
/// A renderer for a given MapConfig layer
///
module.exports = class PostgresVectorRenderer {
    constructor (layers, psql, attrs, options) {
        options = options || {};

        this.psql = psql;
        this.attrs = attrs;
        this.layers = layers;

        this.tile_size = options.tileSize || 256;
        this.tile_max_geosize = options.maxGeosize || 40075017; // earth circumference in webmercator 3857
        this.buffer_size = options.bufferSize || 0; // Same as Mapnik::bufferSize
        this.mvt_extent = options.mvt_extent || 4096;
    }

    /// API: renders a tile with the Renderer configuration
    /// @param x tile x coordinate
    /// @param y tile y coordinate
    /// @param z tile zoom
    /// callback: will be called when done using nodejs protocol (err, data)
    getTile (z, x, y, callback) {
        const subqueriesPromises = this._getLayerQueries({ z, x, y });
        const empty_tile_msg = 'Tile does not exist';
        Promise.all(subqueriesPromises)
        .then(subqueries =>  {
            if (subqueries.length === 0) {
                return callback(new Error(empty_tile_msg));
            }

            const query = `SELECT ((${subqueries.join(') || (')})) AS st_asmvt`;
            const timer = new Timer();

            timer.start('query');
            this.psql.query(query, function (err, data) {
                timer.end('query');
                if (err) {
                    debug("Error running pg-mvt query " + query + ": " + err);
                    if (err.message) {
                        err.message = "PgMvtRenderer: " + err.message;
                    }
                    return callback(err);
                }

                const mvt = extractMVT(data);
                if (!mvt) {
                    return callback(new Error(empty_tile_msg));
                }

                const headers = { 'Content-Type': 'application/x-protobuf' };
                const stats = timer.getTimes();

                return callback(null, mvt, headers, stats);
            });
        })
        .catch(err => { return callback(err); });
    }

    _getLayerColumns (layer) {
        return new Promise((resolve, reject) => {
            if (layer._metadata_columns) {
                resolve(layer._metadata_columns);
            }
            const layerSQL = this._replaceTokens(layer.options.sql, 0, 0, 0);
            const limitedQuery = `SELECT * FROM (${layerSQL}) __windshaft_mvt_schema LIMIT 0;`;

            this.psql.query(limitedQuery, function (err, data) {
                if (err) {
                    if (err.message) {
                        err.message = "PgMvtRenderer: " + err.message;
                    }
                    return reject(err);
                }
                else {
                    // We filter here by type id to avoid the implicit casts that ST_AsMVT does
                    // from any unknown type to string
                    // These are the list of compatible type oids as declared in
                    // mapnik/plugins/input/postgis/postgis_datasource.cpp
                    const COMPATIBLE_MVT_TYPEIDS = [
                        16,     // bool
                        20,     // int8
                        21,     // int2
                        23,     // int4
                        700,    // float4
                        701,    // float8
                        1700,   // numeric
                        1042,   // bpchar
                        1043,   // varchar
                        25,     // text
                        705,    // literal
                    ];

                    let _metadata_columns = [];
                    Object.keys(data.fields).forEach(function(key){
                        const val = data.fields[key];
                        if (COMPATIBLE_MVT_TYPEIDS.includes(val.dataTypeID)) {
                            _metadata_columns.push(val.name);
                        }
                    });
                    resolve(_metadata_columns);
                }
            });
        });
    }

    _getLayerQueries({ z, x, y }) {
        return this.layers.filter(l => shouldUseLayer(l, z)).map(layer => {
            return this._getLayerColumns(layer)
                .then( layerColumns => {
                    const geomColumn = this._geomColumn(layer, { z, x, y });
                    const columns = [geomColumn].concat(layerColumns);
                    const query = this._replaceTokens(layer.options.sql, { z, x, y });
                    const finalQuery = this._vectorLayerQuery(layer, columns, query, { z, x, y });
                    return finalQuery;
                });
        });
    }

    _geomColumn (layer, { z, x, y }) {
        // Geometry column
        const subpixelBufferSize = Math.round(this.buffer_size * this.mvt_extent / this.tile_size);
        const geomColumn = layer.options.geom_column || 'the_geom_webmercator';
        return `
        ST_AsMVTGeom(
            ${geomColumn},
            CDB_XYZ_Extent(${x},${y},${z}),
            ${this.mvt_extent},
            ${subpixelBufferSize},
            true
        ) as the_geom_webmercator
        `;
    }

    _replaceTokens (sql, { z, x, y }) {
        return SubstitutionTokens.replace(sql, {
            bbox: `CDB_XYZ_Extent(${x},${y},${z})`,
            // See https://github.com/mapnik/mapnik/wiki/ScaleAndPpi#scale-denominator
            scale_denominator: `(cdb_XYZ_Resolution(${z})::numeric *${256 / this.tile_size / 0.00028})`,
            pixel_width: `cdb_XYZ_Resolution(${z})*${256 / this.tile_size}`,
            pixel_height: `cdb_XYZ_Resolution(${z})*${256 / this.tile_size}`,
            var_zoom: z,
            var_x: x,
            var_y: y
        });
    }

    _vectorLayerQuery (layer, columns, query, { z, x, y }) {
        const layerId = layer.id;
        const geomColumn = layer.options.geom_column || 'the_geom_webmercator';

        return `
SELECT ST_AsMVT(geom, '${layerId}')
FROM (
    SELECT ${columns.join(', ')}
    FROM (${query}) AS cdbq
    WHERE
        ${geomColumn} &&
        ST_Expand(
            CDB_XYZ_Extent(${x},${y},${z}),
            CDB_XYZ_Resolution(${z}) * ${Math.round(256 * this.buffer_size / this.tile_size)}
        )
) AS geom
        `;
    }
};
<|endoftext|>


<commit_before>const translate = require('../lib/translate');
const assert = require('assert');
const _ = require('lodash');

describe('argument parsing', () => {
  it('should throw with no arguments ()', () => {
    assert.throws(translate, /invalid/i);
  });
  it('should throw with just ("text")', () => {
    assert.throws(() => translate('text'), /need.*target/i);
  });
  it('should throw with just ({})', () => {
    assert.throws(() => translate({}), /need.*text/i);
  });
  it('should throw with just ({text})', () => {
    assert.throws(() => translate({ text: 'text' }), /need.*target/i);
  });
  it('should be ok with ("text", "fr")', () => {
    assert.ok(translate('text', 'fr'));
  });
  it('should be ok with ({text, target})', () => {
    assert.ok(translate({ text: 'text', target: 'fr' }));
  });
  it('should be ok with ("text", "fr", cb)', () => {
    assert.ok(translate('text', 'fr', () => {}));
  });
  it('should be ok with ({text, target}, cb)', () => {
    assert.ok(translate({ text: 'text', target: 'fr' }, () => {}));
  });
});

describe('callback', () => {
  it('should callback with ("text", "fr", cb)', (done) => {
    assert.ok(translate('text', 'fr', () => done()));
  });
  it('should callback with ({text, target}, cb)', (done) => {
    assert.ok(translate({ text: 'text', target: 'fr' }, () => done()));
  });
});

describe('promise', () => {
  const promise = translate('text', 'fr');
  it('should return a promise', () => {
    assert.equal(typeof promise.then, 'function');
    assert.equal(typeof promise.catch, 'function');
  });
  it('should call the promise\'s then/catch', (done) => {
    assert.ok(promise.then(() => done()).catch(() => done()));
  });
});

describe('translation', () => {
  it('should translate a word "text" =(fr)> "texte"', (done) => {
    translate('text', 'fr').then(result => {
      assert.equal(result.toString(), 'texte');
      done();
    }).catch(done);
  });
  it('should translate ("texte", "fr", "en") => "text"', (done) => {
    translate('texte', 'fr', 'en').then(result => {
      assert.equal(result.toString(), 'text');
      done();
    }).catch(done);
  });
});
<commit_msg>Update tests<commit_after>const translate = require('../lib/translate');
const assert = require('assert');
const _ = require('lodash');

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

describe('argument parsing', function () {
  it('should throw with no arguments ()', function () {
    assert.throws(translate, /invalid/i);
  });
  it('should throw with just ("text")', function () {
    assert.throws(function () {
      return translate('text');
    }, /need.*target/i);
  });
  it('should throw with just ({})', function () {
    assert.throws(function () {
      return translate({});
    }, /need.*text/i);
  });
  it('should throw with just ({text})', function () {
    assert.throws(function () {
      return translate({ text: 'text' });
    }, /need.*target/i);
  });
  it('should be ok with ("text", "fr")', function () {
    assert.ok(translate('text', 'fr'));
  });
  it('should be ok with ({text, target})', function () {
    assert.ok(translate({ text: 'text', target: 'fr' }));
  });
  it('should be ok with ("text", "fr", cb)', function () {
    assert.ok(translate('text', 'fr', function () {}));
  });
  it('should be ok with ({text, target}, cb)', function () {
    assert.ok(translate({ text: 'text', target: 'fr' }, function () {}));
  });
});

describe('callback', function () {
  it('should callback with ("text", "fr", cb)', function (done) {
    assert.ok(translate('text', 'fr', function () {
      return done();
    }));
  });
  it('should callback with ({text, target}, cb)', function (done) {
    assert.ok(translate({ text: 'text', target: 'fr' }, function () {
      return done();
    }));
  });
});

describe('promise', function () {
  var promise = translate('text', 'fr');
  it('should return a promise', function () {
    assert.equal(_typeof(promise.then), 'function');
    assert.equal(_typeof(promise.catch), 'function');
  });
  it('should call the promise\'s then/catch', function (done) {
    assert.ok(promise.then(function () {
      return done();
    }).catch(function () {
      return done();
    }));
  });
});

describe('translation', function () {
  it('should translate a word "text" =(fr)> "texte"', function (done) {
    translate('text', 'fr').then(function (result) {
      assert.equal(result.toString(), 'texte');
      done();
    }).catch(done);
  });
  it('should translate ("texte", "fr", "en") => "text"', function (done) {
    translate('texte', 'fr', 'en').then(function (result) {
      assert.equal(result.toString(), 'text');
      done();
    }).catch(done);
  });
});
<|endoftext|>


<commit_before><commit_msg>Fix links<commit_after><|endoftext|>


<commit_before>(function (root, factory) {
  if (typeof module !== 'undefined' && module.exports) {
    global.Promise = require('es6-promise').Promise;

    var
      assert = require('assert'),
      fs = require('fs'),
      path = require('path'),
      rdf = require('rdf-interfaces'),
      utils = require('rdf-test-utils')(rdf);

    require('../rdf-ext.js')(rdf);

    var readFile = function (filename) {
      return new Promise(function (resolve) {
        resolve(fs.readFileSync(path.join(__dirname, filename)).toString());
      });
    };

    module.exports = factory(assert, jsonld, rdf, readFile, utils);
  } else {
    var readFile = function (filename) {
      return new Promise(function (resolve) {
        root.rdf.defaultRequest('GET', filename, {}, null, function (status, headers, content) {
          resolve(content);
        });
      });
    };

    factory(root.assert, root.jsonld, root.rdf, readFile, root.rdf.testUtils(root.rdf));
  }
})(this, function (assert, jsonld, rdf, readFile, utils) {

  describe('rdf-ext', function() {
    var
      cardGraph;

    var buildCardGraph = function() {
      var graph = rdf.createGraph();

      var cardNode = rdf.createNamedNode('https://www.example.com/john/card#me');

      graph.add(rdf.createTriple(
        cardNode,
        rdf.createNamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
        rdf.createNamedNode('http://xmlns.com/foaf/0.1/Person')));

      graph.add(rdf.createTriple(
        cardNode,
        rdf.createNamedNode('http://xmlns.com/foaf/0.1/name'),
        rdf.createLiteral('John Smith', 'en')));

      var keyNode = rdf.createBlankNode();

      graph.add(rdf.createTriple(
        cardNode,
        rdf.createNamedNode('http://www.w3.org/ns/auth/cert#key'),
        keyNode));

      graph.add(rdf.createTriple(
        keyNode,
        rdf.createNamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
        rdf.createNamedNode('http://www.w3.org/ns/auth/cert#RSAPublicKey')));

      graph.add(rdf.createTriple(
        keyNode,
        rdf.createNamedNode('http://www.w3.org/ns/auth/cert#exponent'),
        rdf.createLiteral('65537', null, rdf.createNamedNode('http://www.w3.org/2001/XMLSchema#integer'))));

      graph.add(rdf.createTriple(
        keyNode,
        rdf.createNamedNode('http://www.w3.org/ns/auth/cert#modulus'),
        rdf.createLiteral('abcdef', null, rdf.createNamedNode('http://www.w3.org/2001/XMLSchema#hexBinary'))));

      return graph;
    };

    before(function(done) {
      cardGraph = buildCardGraph();

      done();
    });

    describe('parsers', function() {
      it('Turtle parser should parse card.ttl', function(done) {
        var parser = new rdf.promise.Parser(new rdf.TurtleParser());

        readFile('support/card.ttl')
          .then(function (card) { return parser.parse(card, 'https://www.example.com/john/card') })
          .then(function (graph) { return  utils.p.assertGraphEqual(graph, cardGraph); })
          .then(function () { done() })
          .catch(function (error) { done(error); });
      });

      it('JSON-LD parser should parse card.json', function(done) {
        var parser = new rdf.promise.Parser(new rdf.JsonLdParser());

        readFile('support/card.json')
          .then(function (card) { return parser.parse(card, 'https://www.example.com/john/card') })
          .then(function (graph) { return  utils.p.assertGraphEqual(graph, cardGraph); })
          .then(function () { done() })
          .catch(function (error) { done(error); });
      });

      it('RDF/XML parser should parse card.xml', function(done) {
        var parser = new rdf.promise.Parser(new rdf.RdfXmlParser());

        readFile('support/card.xml')
          .then(function (card) { return parser.parse(card, 'https://www.example.com/john/card') })
          .then(function (graph) { return  utils.p.assertGraphEqual(graph, cardGraph); })
          .then(function () { done() })
          .catch(function (error) { done(error); });
      });
    });

    describe('serializers', function () {
      it('JSON-LD serializer should generate parseable card', function(done) {
        var
          serializer = new rdf.promise.Serializer(new rdf.JsonLdSerializer())
          parser = new rdf.promise.Parser(new rdf.JsonLdParser());

        serializer.serialize(cardGraph)
          .then(function (card) { return parser.parse(card, 'https://www.example.com/john/card'); })
          .then(function (graph) { return  utils.p.assertGraphEqual(graph, cardGraph); })
          .then(function () { done() })
          .catch(function (error) { done(error); });
      });
    });

    describe('microdata-rdf test suite', function () {
      var
        tests = [
          '0001', '0002', '0003', '0004', '0005', '0006', '0007', '0008', '0009', '0010', '0011', '0012', '0013',
          '0014', '0015', '0046', '0047', '0048', '0049', '0050', '0051', '0052', '0053', '0054', '0055', '0056',
          '0057', '0058', '0059', '0060', '0061', '0062', '0063', '0064', '0065', '0066', '0067', '0068', '0069',
          '0070', '0073', '0074', '0075', '0076', '0077', '0078', '0079', '0080', '0081', '0082', '0083', '0084' ],
        microdataParser = new rdf.promise.Parser(new rdf.MicrodataParser()),
        turtleParser = new rdf.promise.Parser(new rdf.TurtleParser());

      var runTest = function (number) {
        it('should pass test ' + number, function (done) {

          Promise.all([
            readFile('support/microdata-rdf/' + number + '.html'),
            readFile('support/microdata-rdf/' + number + '.ttl')
          ]).then(function (contents) {
            return Promise.all([
              microdataParser.parse(contents[0], 'http://example.com/'),
              turtleParser.parse(contents[1], 'http://example.com/')
            ])
          }).then(function (graphs) {
            return utils.p.assertGraphEqual(graphs[0], graphs[1]);
          }).then(function () {
            done()
          }).catch(function (error) {
            done(error);
          })
        });
      };

      tests.forEach(function (number) {
        runTest(number);
      })
    });
  });
});<commit_msg>fixed wrong base name<commit_after>(function (root, factory) {
  if (typeof module !== 'undefined' && module.exports) {
    global.Promise = require('es6-promise').Promise;

    var
      assert = require('assert'),
      fs = require('fs'),
      path = require('path'),
      rdf = require('rdf-interfaces'),
      utils = require('rdf-test-utils')(rdf);

    require('../rdf-ext.js')(rdf);

    var readFile = function (filename) {
      return new Promise(function (resolve) {
        resolve(fs.readFileSync(path.join(__dirname, filename)).toString());
      });
    };

    module.exports = factory(assert, jsonld, rdf, readFile, utils);
  } else {
    var readFile = function (filename) {
      return new Promise(function (resolve) {
        root.rdf.defaultRequest('GET', filename, {}, null, function (status, headers, content) {
          resolve(content);
        });
      });
    };

    factory(root.assert, root.jsonld, root.rdf, readFile, root.rdf.testUtils(root.rdf));
  }
})(this, function (assert, jsonld, rdf, readFile, utils) {

  describe('rdf-ext', function() {
    var
      cardGraph;

    var buildCardGraph = function() {
      var graph = rdf.createGraph();

      var cardNode = rdf.createNamedNode('https://www.example.com/john/card#me');

      graph.add(rdf.createTriple(
        cardNode,
        rdf.createNamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
        rdf.createNamedNode('http://xmlns.com/foaf/0.1/Person')));

      graph.add(rdf.createTriple(
        cardNode,
        rdf.createNamedNode('http://xmlns.com/foaf/0.1/name'),
        rdf.createLiteral('John Smith', 'en')));

      var keyNode = rdf.createBlankNode();

      graph.add(rdf.createTriple(
        cardNode,
        rdf.createNamedNode('http://www.w3.org/ns/auth/cert#key'),
        keyNode));

      graph.add(rdf.createTriple(
        keyNode,
        rdf.createNamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
        rdf.createNamedNode('http://www.w3.org/ns/auth/cert#RSAPublicKey')));

      graph.add(rdf.createTriple(
        keyNode,
        rdf.createNamedNode('http://www.w3.org/ns/auth/cert#exponent'),
        rdf.createLiteral('65537', null, rdf.createNamedNode('http://www.w3.org/2001/XMLSchema#integer'))));

      graph.add(rdf.createTriple(
        keyNode,
        rdf.createNamedNode('http://www.w3.org/ns/auth/cert#modulus'),
        rdf.createLiteral('abcdef', null, rdf.createNamedNode('http://www.w3.org/2001/XMLSchema#hexBinary'))));

      return graph;
    };

    before(function(done) {
      cardGraph = buildCardGraph();

      done();
    });

    describe('parsers', function() {
      it('Turtle parser should parse card.ttl', function(done) {
        var parser = new rdf.promise.Parser(new rdf.TurtleParser());

        readFile('support/card.ttl')
          .then(function (card) { return parser.parse(card, 'https://www.example.com/john/card') })
          .then(function (graph) { return  utils.p.assertGraphEqual(graph, cardGraph); })
          .then(function () { done() })
          .catch(function (error) { done(error); });
      });

      it('JSON-LD parser should parse card.json', function(done) {
        var parser = new rdf.promise.Parser(new rdf.JsonLdParser());

        readFile('support/card.json')
          .then(function (card) { return parser.parse(card, 'https://www.example.com/john/card') })
          .then(function (graph) { return  utils.p.assertGraphEqual(graph, cardGraph); })
          .then(function () { done() })
          .catch(function (error) { done(error); });
      });

      it('RDF/XML parser should parse card.xml', function(done) {
        var parser = new rdf.promise.Parser(new rdf.RdfXmlParser());

        readFile('support/card.xml')
          .then(function (card) { return parser.parse(card, 'https://www.example.com/john/card') })
          .then(function (graph) { return  utils.p.assertGraphEqual(graph, cardGraph); })
          .then(function () { done() })
          .catch(function (error) { done(error); });
      });
    });

    describe('serializers', function () {
      it('JSON-LD serializer should generate parseable card', function(done) {
        var
          serializer = new rdf.promise.Serializer(new rdf.JsonLdSerializer()),
          parser = new rdf.promise.Parser(new rdf.JsonLdParser());

        serializer.serialize(cardGraph)
          .then(function (card) { return parser.parse(card, 'https://www.example.com/john/card'); })
          .then(function (graph) { return  utils.p.assertGraphEqual(graph, cardGraph); })
          .then(function () { done() })
          .catch(function (error) { done(error); });
      });
    });

    describe('microdata-rdf test suite', function () {
      var
        tests = [
          '0001', '0002', '0003', '0004', '0005', '0006', '0007', '0008', '0009', '0010', '0011', '0012', '0013',
          '0014', '0015', '0046', '0047', '0048', '0049', '0050', '0051', '0052', '0053', '0054', '0055', '0056',
          '0057', '0058', '0059', '0060', '0061', '0062', '0063', '0064', '0065', '0066', '0067', '0068', '0069',
          '0070', '0073', '0074', '0075', '0076', '0077', '0078', '0079', '0080', '0081', '0082', '0083', '0084' ],
        microdataParser = new rdf.promise.Parser(new rdf.MicrodataParser()),
        turtleParser = new rdf.promise.Parser(new rdf.TurtleParser());

      var runTest = function (number) {
        it('should pass test ' + number, function (done) {

          Promise.all([
            readFile('support/microdata-rdf/' + number + '.html'),
            readFile('support/microdata-rdf/' + number + '.ttl')
          ]).then(function (contents) {
            return Promise.all([
              microdataParser.parse(contents[0], 'http://example.com/' + number + '.html'),
              turtleParser.parse(contents[1], 'http://example.com/' + number + '.html')
            ])
          }).then(function (graphs) {
            return utils.p.assertGraphEqual(graphs[0], graphs[1]);
          }).then(function () {
            done()
          }).catch(function (error) {
            done(error);
          })
        });
      };

      tests.forEach(function (number) {
        runTest(number);
      })
    });
  });
});<|endoftext|>


<commit_before>var assert = require('assert')
var should = require('should')
var dvbtee = require('bindings')('dvbtee.node')

describe('node-dvbtee', function() {

  describe('#libVersion()', function() {

    var version = dvbtee.libVersion()

    it('should return an Array of 3 Numbers to represent libdvbtee library version in the form [ MAJOR, MINOR, PATCH ]', function() {
      assert.equal(3, version.length)

      version.forEach(function (x) {
        assert.equal("number", typeof x)
      })
    })
  })

  describe('#logLevel()', function() {

    it('should return the dvbtee object (for method chaining)', function() {
      assert.equal(dvbtee, dvbtee.logLevel(0,0))
    })
  })

  describe('#getTableDecoderIds()', function() {

    var tableIds = dvbtee.getTableDecoderIds()

    it('should return an Array of Numbers that indicate the Table IDs supported by the built-in PSIP table decoders', function() {
      assert.equal(11, tableIds.length)

      tableIds.forEach(function (x) {
        assert.equal("number", typeof x)
      })
    })

    it('should support decoding PAT tables', function() {
      tableIds.should.containEql(0x00)
    })

    it('should support decoding PMT tables', function() {
      tableIds.should.containEql(0x02)
    })

    it('should support decoding NIT tables', function() {
      tableIds.should.containEql(0x40)
    })

    it('should support decoding SDT tables', function() {
      tableIds.should.containEql(0x42)
    })

    it('should support decoding EIT (dvb) tables', function() {
      tableIds.should.containEql(0x4e)
    })

    it('should support decoding TDT/TOT tables', function() {
      tableIds.should.containEql(0x70)
    })

    it('should support decoding MGT tables', function() {
      tableIds.should.containEql(0xc7)
    })

    it('should support decoding VCT tables', function() {
      tableIds.should.containEql(0xc8)
    })

    it('should support decoding EIT (atsc) tables', function() {
      tableIds.should.containEql(0xcb)
    })

    it('should support decoding ETT tables', function() {
      tableIds.should.containEql(0xcc)
    })

    it('should support decoding STT tables', function() {
      tableIds.should.containEql(0xcd)
    })
  })

  describe('#getDescriptorDecoderIds()', function() {

    var descIds = dvbtee.getDescriptorDecoderIds()

    it('should return an Array of Numbers that indicate the Descriptor IDs supported by the built-in PSIP descriptor decoders', function() {
      assert.equal(9, descIds.length)

      descIds.forEach(function (x) {
        assert.equal("number", typeof x)
      })
    })

    it('should support decoding ISO639 language descriptors', function() {
      descIds.should.containEql(0x0a)
    })

    it('should support decoding service descriptors', function() {
      descIds.should.containEql(0x48)
    })

    it('should support decoding short event descriptors', function() {
      descIds.should.containEql(0x4d)
    })

    it('should support decoding frequency list descriptors', function() {
      descIds.should.containEql(0x62)
    })

    it('should support decoding AC-3 audio descriptors', function() {
      descIds.should.containEql(0x81)
    })

    it('should support decoding logical channel number descriptors', function() {
      descIds.should.containEql(0x83)
    })

    it('should support decoding caption service descriptors', function() {
      descIds.should.containEql(0x86)
    })

    it('should support decoding extended channel name descriptors', function() {
      descIds.should.containEql(0xa0)
    })

    it('should support decoding service location descriptors', function() {
      descIds.should.containEql(0xa1)
    })
  })
})

describe('dvbtee-parser', function() {

  describe('#New', function() {
    it('should return an object when called without operator new', function() {
      assert.equal("object", typeof dvbtee.Parser())
    })

    it('should return an object when called with operator new', function() {
      assert.equal("object", typeof new dvbtee.Parser())
    })
  })
})
<commit_msg>test: separate the 'number of supported decoders' test from the 'returns array of numbers' test<commit_after>var assert = require('assert')
var should = require('should')
var dvbtee = require('bindings')('dvbtee.node')

describe('node-dvbtee', function() {

  describe('#libVersion()', function() {

    var version = dvbtee.libVersion()

    it('should return an Array of 3 Numbers to represent libdvbtee library version in the form [ MAJOR, MINOR, PATCH ]', function() {
      assert.equal(3, version.length)

      version.forEach(function (x) {
        assert.equal("number", typeof x)
      })
    })
  })

  describe('#logLevel()', function() {

    it('should return the dvbtee object (for method chaining)', function() {
      assert.equal(dvbtee, dvbtee.logLevel(0,0))
    })
  })

  describe('#getTableDecoderIds()', function() {

    var tableIds = dvbtee.getTableDecoderIds()

    it('should return an Array of Numbers that indicate the Table IDs supported by the built-in PSIP table decoders', function() {
      tableIds.forEach(function (x) {
        assert.equal("number", typeof x)
      })
    })

    it('should include 11 PSIP table decoders', function() {
      assert.equal(11, tableIds.length)
    })

    it('should support decoding PAT tables', function() {
      tableIds.should.containEql(0x00)
    })

    it('should support decoding PMT tables', function() {
      tableIds.should.containEql(0x02)
    })

    it('should support decoding NIT tables', function() {
      tableIds.should.containEql(0x40)
    })

    it('should support decoding SDT tables', function() {
      tableIds.should.containEql(0x42)
    })

    it('should support decoding EIT (dvb) tables', function() {
      tableIds.should.containEql(0x4e)
    })

    it('should support decoding TDT/TOT tables', function() {
      tableIds.should.containEql(0x70)
    })

    it('should support decoding MGT tables', function() {
      tableIds.should.containEql(0xc7)
    })

    it('should support decoding VCT tables', function() {
      tableIds.should.containEql(0xc8)
    })

    it('should support decoding EIT (atsc) tables', function() {
      tableIds.should.containEql(0xcb)
    })

    it('should support decoding ETT tables', function() {
      tableIds.should.containEql(0xcc)
    })

    it('should support decoding STT tables', function() {
      tableIds.should.containEql(0xcd)
    })
  })

  describe('#getDescriptorDecoderIds()', function() {

    var descIds = dvbtee.getDescriptorDecoderIds()

    it('should return an Array of Numbers that indicate the Descriptor IDs supported by the built-in PSIP descriptor decoders', function() {
      descIds.forEach(function (x) {
        assert.equal("number", typeof x)
      })
    })

    it('should include 9 PSIP descriptor decoders', function() {
      assert.equal(9, descIds.length)
    })

    it('should support decoding ISO639 language descriptors', function() {
      descIds.should.containEql(0x0a)
    })

    it('should support decoding service descriptors', function() {
      descIds.should.containEql(0x48)
    })

    it('should support decoding short event descriptors', function() {
      descIds.should.containEql(0x4d)
    })

    it('should support decoding frequency list descriptors', function() {
      descIds.should.containEql(0x62)
    })

    it('should support decoding AC-3 audio descriptors', function() {
      descIds.should.containEql(0x81)
    })

    it('should support decoding logical channel number descriptors', function() {
      descIds.should.containEql(0x83)
    })

    it('should support decoding caption service descriptors', function() {
      descIds.should.containEql(0x86)
    })

    it('should support decoding extended channel name descriptors', function() {
      descIds.should.containEql(0xa0)
    })

    it('should support decoding service location descriptors', function() {
      descIds.should.containEql(0xa1)
    })
  })
})

describe('dvbtee-parser', function() {

  describe('#New', function() {
    it('should return an object when called without operator new', function() {
      assert.equal("object", typeof dvbtee.Parser())
    })

    it('should return an object when called with operator new', function() {
      assert.equal("object", typeof new dvbtee.Parser())
    })
  })
})
<|endoftext|>


<commit_before>// Just to show that higher-order features like undo support can be
// implemented easily in OO style.  For real applications you might
// nevertheless prefer redux-undo, which has quite a few bells and
// whistles, which are not implemented here since they're unrelated to
// the question of using an OO style.

import {State} from "../utils";

const UNDO = "@@undoable/UNDO";
const REDO = "@@undoable/REDO";

export class Undoable extends State {
  // action creators
  undoAction() { return this.wrapAction({ type: UNDO }); }
  redoAction() { return this.wrapAction({ type: REDO }); }

  // utility methods for the UI (for enabling/disabling the buttons)
  undoable() { return this.past != null; }
  redoable() { return this.future != null; }

  // reducer helpers
  undo() {
    if (this.undoable()) {
      const {past, present, future} = this;
      return this.withProps({
        past: past.rest,
        present: past.first,
        future: {first: present, rest: future}
      });
    }
    else
      return this;
  }

  redo() {
    if (this.redoable()) {
      const {past, present, future} = this;
      return this.withProps({
        past: {first: present, rest: past},
        present: future.first,
        future: future.rest
      });
    }
    else
      return this;
  }

  doIt(action) {
    const {past, present} = this;
    return this.withProps({
      past: {first: present, rest: past},
      present: present.reduce(action),
      future: null
    });
  }

  // main reducer
  reduce(action) {
    switch (action.type) {
      case UNDO: return this.undo();
      case REDO: return this.redo();
      // It's ugly that we have to treat redux INIT actions explicitly:
      // (Not competely sure if ignoring is the right thing to do.  Or
      // should we still send INIT actions to this.present, which will
      // probably ignore them nevertheless?)
      case "@@redux/INIT": return this;
      case "@@INIT": return this;
      default: return this.doIt(action);
    }
  }
}

Undoable.defaults = {
  past: null,
  present: undefined,
  future: null
}
<commit_msg>Comment on open questions regarding "@@..." actions in undo code.<commit_after>// Just to show that higher-order features like undo support can be
// implemented easily in OO style.  For real applications you might
// nevertheless prefer redux-undo, which has quite a few bells and
// whistles, which are not implemented here since they're unrelated to
// the question of using an OO style.

import {State} from "../utils";

const UNDO = "@@undoable/UNDO";
const REDO = "@@undoable/REDO";

export class Undoable extends State {
  // action creators
  undoAction() { return this.wrapAction({ type: UNDO }); }
  redoAction() { return this.wrapAction({ type: REDO }); }

  // utility methods for the UI (for enabling/disabling the buttons)
  undoable() { return this.past != null; }
  redoable() { return this.future != null; }

  // reducer helpers
  undo() {
    if (this.undoable()) {
      const {past, present, future} = this;
      return this.withProps({
        past: past.rest,
        present: past.first,
        future: {first: present, rest: future}
      });
    }
    else
      return this;
  }

  redo() {
    if (this.redoable()) {
      const {past, present, future} = this;
      return this.withProps({
        past: {first: present, rest: past},
        present: future.first,
        future: future.rest
      });
    }
    else
      return this;
  }

  doIt(action) {
    const {past, present} = this;
    return this.withProps({
      past: {first: present, rest: past},
      present: present.reduce(action),
      future: null
    });
  }

  // main reducer
  reduce(action) {
    switch (action.type) {
      case UNDO: return this.undo();
      case REDO: return this.redo();

      // It's ugly that we have to treat redux INIT actions explicitly:
      // (Not competely sure if ignoring is the right thing to do.  Or
      // should we still send INIT actions to this.present, which will
      // probably ignore them nevertheless?)
      //
      // What about other actions starting with "@@"?  And is it ok that
      // our own action names UNDO and REDO start with "@@"?  (See also
      // https://github.com/rackt/redux/issues/186.)
      case "@@redux/INIT": return this;
      case "@@INIT": return this;

      default: return this.doIt(action);
    }
  }
}

Undoable.defaults = {
  past: null,
  present: undefined,
  future: null
}
<|endoftext|>