<commit_before>/*eslint strict: [2, "function"]*/ // IIFE form
/*eslint-disable func-style*/ // So that everything is grouped in one var
(function () {
    "use strict";

    /*global run, exit:true*/ // From bdd.js
    /*global __coverage__*/ // Coverage data

    var safeFunc = Function,
        sources,
        context = (function () {
            /*global global*/ // NodeJS global
            if (typeof global === "object") {
                return global;
            }
            /*global window*/ // Browser global
            if (typeof window !== "undefined") {
                return window;
            }
            return safeFunc("return this;")();
        }()),
        noTimeout = typeof setTimeout === "undefined",

        _resolvePath = function (configuration, relativePath) {
            return configuration.gpfPath
                .split(configuration.pathSeparator)
                .concat(relativePath.split("/"))
                .join(configuration.pathSeparator);
        },

        _processParameters = function (configuration, options) {
            var len = configuration.parameters.length,
                idx,
                value,
                option;
            for (idx = 0; idx < len; ++idx) {
                value = configuration.parameters[idx];
                if (value.charAt(0) === "-") {
                    value = value.substring(1);
                    option = options[value];
                    if (typeof option === "boolean") {
                        options[value] = !option; // Simple switch
                    } else if (typeof option === "function") {
                        option.call(options);
                    }
                } else {
                    options.tests.push(value);
                }
            }
            if (options.perfInfinite) {
                options.perf = true;
            }
        },

        _eval = function (configuration, path, content) {
            try {
                /*jslint evil: true*/
                eval(content); //eslint-disable-line no-eval
                /*jslint evil: false*/
            } catch (e) {
                configuration.log("An error occurred while evaluating: " + path + "\r\n" + e.message);
                configuration.log(e);
                configuration.exit(-1);
            }
        },

        _load = function (configuration, path) {
            _eval(configuration, path, configuration.read(path));
        },

        _requireGpf = function (configuration, path) {
            if (configuration.require) {
                context.gpf = configuration.require(path);
            } else {
                _load(configuration, path);
                if (typeof gpf === "undefined") {
                    configuration.log("GPF was not loaded");
                    configuration.exit(-1);
                }
            }
        },

        _loadGpf = function (configuration, options, verbose) {
            verbose("Using " + options.label + " version");
            if (options.build) {
                _requireGpf(configuration, _resolvePath(configuration, "build/" + options.build + ".js"));
            } else {
                if (!context.gpfSourcesPath) {
                    context.gpfSourcesPath = _resolvePath(configuration, "src/");
                }
                _load(configuration, gpfSourcesPath + "boot.js");
            }
            // Load the list of modules
            var sourcesJson = configuration.read("src/sources.json");
            sources = safeFunc("return " + sourcesJson + ";")();
        },

        _loadBDD = function (configuration, verbose) {
            if (undefined === configuration.run) {
                verbose("Loading BDD...");
                _load(configuration, _resolvePath(configuration, "test/host/bdd.js"));
                verbose("BDD loaded.");
            }
        },

        _loadTest = function (configuration, source) {
            var path = _resolvePath(configuration, "test/" + source + ".js");
            if (configuration.loadTest) {
                configuration.loadTest(path);
            } else {
                var testFileContent = configuration.read(path),
                    modifiedContent = testFileContent.replace(/[^."]assert\((.*)\);/g, function (match, test, offset) {
                        var lines = testFileContent.substring(0, offset).split("\n"),
                            lineNumber = lines.length,
                            pos = lines.pop().length + 1,
                            message = source + ".js@" + lineNumber + ":" + pos + ": " + test
                                .split("\\").join("\\\\")
                                .split("\"").join("\\\"");
                        return match.substring(0, match.length - 2) + ", \"" + message + "\");";
                    });
                _eval(configuration, path, modifiedContent);
            }
        },

        _patchLegacy = function (configuration, verbose, version) {
            // gpf is loaded
            var legacy = JSON.parse(configuration.read(_resolvePath(configuration, "test/legacy/legacy.json")))
                    .filter(function (item) {
                        return item.before > version;
                    }),
                _describes = [],
                _describe = context.describe,
                _it = context.it;
            context.describe = function (label) {
                var issue;
                legacy.every(function (item) {
                    if (item.ignore.some(function (ignore) {
                        return ignore.describe === label && !ignore.it;

                    })) {
                        issue = item.issue;
                        return false;
                    }
                    return true;
                });
                if (issue) {
                    verbose("describe(\"" + label + "\", ...) ignored, see issue #" + issue);
                } else {
                    _describes.unshift(label);
                    _describe.apply(this, arguments);
                    _describes.shift();
                }
            };
            context.it = function (label) {
                var issue;
                legacy.every(function (item) {
                    if (item.ignore.some(function (ignore) {
                        return ignore.describe === _describes[0] && ignore.it === label;

                    })) {
                        issue = item.issue;
                        return false;
                    }
                    return true;
                });
                if (issue) {
                    verbose("it(\"" + label + "\", ...) ignored, see issue #" + issue);
                } else {
                    _it.apply(this, arguments);
                }
            };
        },

        _setupInclude = function (configuration) {
            context.include = function (source) {
                _loadTest(configuration, source);
            };
        },

        _loadTestsFromNames = function (configuration, names, verbose) {
            var len = names.length,
                sourceIdx,
                source;
            for (sourceIdx = 0; sourceIdx < len; ++sourceIdx) {
                source = names[sourceIdx];
                if ((/^legacy(\\|\/)/).exec(source)) {
                    verbose("Legacy detected");
                    _patchLegacy(configuration, verbose, source.substring(7));
                    delete gpf.internals;
                }
                _loadTest(configuration, source);
            }
        },

        _loadTestsFromModules = function (configuration) {
            var len = sources.length,
                sourceIdx,
                source;
            for (sourceIdx = 0; sourceIdx < len; ++sourceIdx) {
                source = sources[sourceIdx];
                if (source.load !== false && source.test !== false) {
                    _loadTest(configuration, source.name);
                }
            }
        },

        _loadTests = function (configuration, options, verbose) {
            try {
                if (options.tests.length) {
                    verbose("Loading specified test files...");
                    _loadTestsFromNames(configuration, options.tests, verbose);
                } else {
                    verbose("Loading all test files...");
                    _loadTestsFromModules(configuration);
                }
            } catch (e) {
                configuration.log("An error occurred while loading tests: " + e.message);
                configuration.log(e);
                configuration.exit(-1);
            }
        },

        _setupConfig = function (configuration) {
            var configFile = JSON.parse(configuration.read(_resolvePath(configuration, "tmp/config.json")));
            context.config = Object.assign({
                httpPort: configFile.serve.httpPort
            }, configuration.config);
            _load(configuration, _resolvePath(configuration, "test/host/features.js"));
            _setupInclude(configuration);
        },

        _mean = function (values) {
            var total = 0,
                len = values.length,
                idx;
            for (idx = 0; idx < len; ++idx) {
                total += values[idx];
            }
            return Math.floor(total / len);
        },

        _stdDeviation = function (values) {
            var mean = _mean(values),
                total = 0,
                len = values.length,
                idx,
                diff;
            for (idx = 0; idx < len; ++idx) {
                diff = values[idx] - mean;
                total += diff * diff;
            }
            return Math.floor(Math.sqrt(total / len));
        },

        _pad = function (value, size) {
            var pad = size - value.toString().length;
            if (pad > 0) {
                return new Array(pad).join(" ") + value;
            }
            return value;
        },

        _perfError = function (configuration, data) {
            configuration.log("ERROR during performance test:");
            var property,
                value;
            for (property in data) {
                if (data.hasOwnProperty(property)) {
                    value = data[property];
                    if (property === "exception" && value instanceof Error) {
                        configuration.log("\t" + _pad(property, 10) + ": " + value.message);
                    } else {
                        configuration.log("\t" + _pad(property, 10) + ": " + value.toString());
                    }
                }
            }
        },

        _runBDDPerf = function (configuration, options) {
            var loop = 1,
                maxLoop,
                runWithCallback,
                measures = [];
            if (options.perfInfinite) {
                maxLoop = Math.MAX_SAFE_INTEGER;
            } else {
                maxLoop = 10;
            }
            function callback (type, data) {
                var statistics;
                if (type === "it") {
                    if (!data.result) {
                        _perfError(configuration, data);
                        maxLoop = 0; // Stop
                    }
                } else if (type === "results") {
                    statistics = ["Round ", _pad(loop, 9), ": ", _pad(data.timeSpent, 5), "ms "];
                    if (loop === 1) {
                        statistics.push("(ignored)");
                    } else {
                        measures.push(data.timeSpent);
                        statistics.push("mean: ", _pad(_mean(measures), 5), "ms ",
                            "deviation: ", _pad(_stdDeviation(measures), 5));
                    }
                    configuration.log(statistics.join(""));
                    if (maxLoop < loop) {
                        configuration.log("Mean time: " + _mean(measures) + "ms");
                    } else {
                        ++loop;
                        setTimeout(runWithCallback, 0);
                    }
                }
            }
            runWithCallback = function () {
                run(callback);
            };
            runWithCallback();
        },

        _updloadCoverage = function () {
            var coverageData = JSON.stringify(__coverage__),
                pos = 0,
                length = coverageData.length,
                chunkSize = 4096,
                method = gpf.http.methods.post,
                url = "http://localhost:" + config.httpPort + "/fs/tmp/coverage/reports/coverage."
                    + gpf.host() + ".json";
            function upload () {
                return gpf.http.request({
                    method: method,
                    url: url,
                    data: coverageData.substring(pos, pos + chunkSize)
                })
                    .then(function () {
                        method = gpf.http.methods.put;
                        pos += chunkSize;
                        if (pos < length) {
                            return upload();
                        }
                    });
            }
            return upload();
        },

        _runBDDForCoverage = function (configuration, options, verbose) {
            verbose("Running BDD - coverage");
            run(function (type, data) {
                if (type === "it" && !data.result) {
                    configuration.log("KO: " + data.context.slice(1) + " " + data.label);
                }
                if (type !== "results") {
                    return;
                }
                if (data.fail) {
                    configuration.log("Failed: " + data.fail + ".");
                    configuration.exit(-1);
                }
                verbose("Uploading coverage results...");
                _updloadCoverage()
                    .then(function () {
                        verbose("Coverage results uploaded.");
                        configuration.exit(0);
                    }, function (reason) {
                        configuration.log("Upload failed: " + reason.toString());
                        configuration.exit(-1);
                    });
            });
        },

        _runBDD = function (configuration, options, verbose) {
            exit = configuration.exit; // used by BDD.js
            if (options.perf) {
                verbose("Running BDD - performances");
                _runBDDPerf(configuration, options);
            } else if (options.runCoverage) {
                _runBDDForCoverage(configuration, options, verbose);
            } else {
                verbose("Running BDD");
                run();
            }
        },

        _safeRunBDD = function (configuration, options, verbose) {
            try {
                if (undefined === configuration.run) {
                    _runBDD(configuration, options, verbose);
                } else {
                    configuration.run();
                }
                if (noTimeout) {
                    gpf.handleTimeout();
                }
            } catch (e) {
                configuration.log("An error occurred while running the tests:\r\n" + e.message);
                configuration.exit(-1);
            }
        },

        _loadFlavor = function (configuration, options) {
            // Load the list of modules
            var
                flavorName = options.tests[0].substring(7),
                flavorJson = configuration.read("make/flavor/" + flavorName + ".json"),
                flavor = safeFunc("return " + flavorJson + ";")();
            options.build = "gpf-" + flavorName;
            options.label = "\"" + options.flavor + "\" flavor";
            options.tests = flavor.tests;
        },

        _checkFlavor = function (configuration, options) {
            if (options.tests.length === 1 && options.tests[0].indexOf("flavor:") === 0) {
                _loadFlavor(configuration, options);
            }
        };

    /*
     * Expected members on configuration:
     * - {String[]} parameters command line parameters
     * - {String} gpfPath GPF base path
     * - {String} pathSeparator
     * - {Function} log
     * - {Function} exit
     * - (Function) require
     * - (Function) read  function (filePath) {} reads a file
     * - (Function) [loadTest=undefined] loadTest function (filePath) {} reads a test file
     * - {Function} run When defined, this method is triggered to execute the tests
     */
    context.loadGpfAndTests = function (configuration) {
        var options = {
                build: "",
                label: "source",
                release: function () {
                    this.build = "gpf";
                    this.label = "release";
                },
                debug: function () {
                    this.build = "gpf-debug";
                    this.label = "debug";
                },
                coverage: function () {
                    this.ignoreConsole = true;
                    this.label = "*instrumented* source";
                    this.runCoverage = true;
                    context.global = context;
                    context.gpfSourcesPath = _resolvePath(configuration, "tmp/coverage/instrument/src/");
                },
                verbose: false,
                ignoreConsole: false,
                perf: false,
                perfInfinite: false,
                tests: []
            },
            verbose;
        _processParameters(configuration, options);
        // Define a debug function that outputs when verbose is set
        if (options.verbose) {
            verbose = configuration.log;
        } else {
            verbose = function () {};
        }
        _checkFlavor(configuration, options);
        _loadGpf(configuration, options, verbose);
        _loadBDD(configuration, verbose);
        if (!options.ignoreConsole) {
            verbose("Loading console...");
            _load(configuration, _resolvePath(configuration, "test/host/console.js"));
            verbose("Console loaded.");
        }
        _setupConfig(configuration);
        _loadTests(configuration, options, verbose);
        _safeRunBDD(configuration, options, verbose);
    };

}());
<commit_msg>Remove the load flag (#294)<commit_after>/*eslint strict: [2, "function"]*/ // IIFE form
/*eslint-disable func-style*/ // So that everything is grouped in one var
(function () {
    "use strict";

    /*global run, exit:true*/ // From bdd.js
    /*global __coverage__*/ // Coverage data

    var safeFunc = Function,
        sources,
        context = (function () {
            /*global global*/ // NodeJS global
            if (typeof global === "object") {
                return global;
            }
            /*global window*/ // Browser global
            if (typeof window !== "undefined") {
                return window;
            }
            return safeFunc("return this;")();
        }()),
        noTimeout = typeof setTimeout === "undefined",

        _resolvePath = function (configuration, relativePath) {
            return configuration.gpfPath
                .split(configuration.pathSeparator)
                .concat(relativePath.split("/"))
                .join(configuration.pathSeparator);
        },

        _processParameters = function (configuration, options) {
            var len = configuration.parameters.length,
                idx,
                value,
                option;
            for (idx = 0; idx < len; ++idx) {
                value = configuration.parameters[idx];
                if (value.charAt(0) === "-") {
                    value = value.substring(1);
                    option = options[value];
                    if (typeof option === "boolean") {
                        options[value] = !option; // Simple switch
                    } else if (typeof option === "function") {
                        option.call(options);
                    }
                } else {
                    options.tests.push(value);
                }
            }
            if (options.perfInfinite) {
                options.perf = true;
            }
        },

        _eval = function (configuration, path, content) {
            try {
                /*jslint evil: true*/
                eval(content); //eslint-disable-line no-eval
                /*jslint evil: false*/
            } catch (e) {
                configuration.log("An error occurred while evaluating: " + path + "\r\n" + e.message);
                configuration.log(e);
                configuration.exit(-1);
            }
        },

        _load = function (configuration, path) {
            _eval(configuration, path, configuration.read(path));
        },

        _requireGpf = function (configuration, path) {
            if (configuration.require) {
                context.gpf = configuration.require(path);
            } else {
                _load(configuration, path);
                if (typeof gpf === "undefined") {
                    configuration.log("GPF was not loaded");
                    configuration.exit(-1);
                }
            }
        },

        _loadGpf = function (configuration, options, verbose) {
            verbose("Using " + options.label + " version");
            if (options.build) {
                _requireGpf(configuration, _resolvePath(configuration, "build/" + options.build + ".js"));
            } else {
                if (!context.gpfSourcesPath) {
                    context.gpfSourcesPath = _resolvePath(configuration, "src/");
                }
                _load(configuration, gpfSourcesPath + "boot.js");
            }
            // Load the list of modules
            var sourcesJson = configuration.read("src/sources.json");
            sources = safeFunc("return " + sourcesJson + ";")();
        },

        _loadBDD = function (configuration, verbose) {
            if (undefined === configuration.run) {
                verbose("Loading BDD...");
                _load(configuration, _resolvePath(configuration, "test/host/bdd.js"));
                verbose("BDD loaded.");
            }
        },

        _loadTest = function (configuration, source) {
            var path = _resolvePath(configuration, "test/" + source + ".js");
            if (configuration.loadTest) {
                configuration.loadTest(path);
            } else {
                var testFileContent = configuration.read(path),
                    modifiedContent = testFileContent.replace(/[^."]assert\((.*)\);/g, function (match, test, offset) {
                        var lines = testFileContent.substring(0, offset).split("\n"),
                            lineNumber = lines.length,
                            pos = lines.pop().length + 1,
                            message = source + ".js@" + lineNumber + ":" + pos + ": " + test
                                .split("\\").join("\\\\")
                                .split("\"").join("\\\"");
                        return match.substring(0, match.length - 2) + ", \"" + message + "\");";
                    });
                _eval(configuration, path, modifiedContent);
            }
        },

        _patchLegacy = function (configuration, verbose, version) {
            // gpf is loaded
            var legacy = JSON.parse(configuration.read(_resolvePath(configuration, "test/legacy/legacy.json")))
                    .filter(function (item) {
                        return item.before > version;
                    }),
                _describes = [],
                _describe = context.describe,
                _it = context.it;
            context.describe = function (label) {
                var issue;
                legacy.every(function (item) {
                    if (item.ignore.some(function (ignore) {
                        return ignore.describe === label && !ignore.it;

                    })) {
                        issue = item.issue;
                        return false;
                    }
                    return true;
                });
                if (issue) {
                    verbose("describe(\"" + label + "\", ...) ignored, see issue #" + issue);
                } else {
                    _describes.unshift(label);
                    _describe.apply(this, arguments);
                    _describes.shift();
                }
            };
            context.it = function (label) {
                var issue;
                legacy.every(function (item) {
                    if (item.ignore.some(function (ignore) {
                        return ignore.describe === _describes[0] && ignore.it === label;

                    })) {
                        issue = item.issue;
                        return false;
                    }
                    return true;
                });
                if (issue) {
                    verbose("it(\"" + label + "\", ...) ignored, see issue #" + issue);
                } else {
                    _it.apply(this, arguments);
                }
            };
        },

        _setupInclude = function (configuration) {
            context.include = function (source) {
                _loadTest(configuration, source);
            };
        },

        _loadTestsFromNames = function (configuration, names, verbose) {
            var len = names.length,
                sourceIdx,
                source;
            for (sourceIdx = 0; sourceIdx < len; ++sourceIdx) {
                source = names[sourceIdx];
                if ((/^legacy(\\|\/)/).exec(source)) {
                    verbose("Legacy detected");
                    _patchLegacy(configuration, verbose, source.substring(7));
                    delete gpf.internals;
                }
                _loadTest(configuration, source);
            }
        },

        _loadTestsFromModules = function (configuration) {
            var len = sources.length,
                sourceIdx,
                source;
            for (sourceIdx = 0; sourceIdx < len; ++sourceIdx) {
                source = sources[sourceIdx];
                if (source.test !== false) {
                    _loadTest(configuration, source.name);
                }
            }
        },

        _loadTests = function (configuration, options, verbose) {
            try {
                if (options.tests.length) {
                    verbose("Loading specified test files...");
                    _loadTestsFromNames(configuration, options.tests, verbose);
                } else {
                    verbose("Loading all test files...");
                    _loadTestsFromModules(configuration);
                }
            } catch (e) {
                configuration.log("An error occurred while loading tests: " + e.message);
                configuration.log(e);
                configuration.exit(-1);
            }
        },

        _setupConfig = function (configuration) {
            var configFile = JSON.parse(configuration.read(_resolvePath(configuration, "tmp/config.json")));
            context.config = Object.assign({
                httpPort: configFile.serve.httpPort
            }, configuration.config);
            _load(configuration, _resolvePath(configuration, "test/host/features.js"));
            _setupInclude(configuration);
        },

        _mean = function (values) {
            var total = 0,
                len = values.length,
                idx;
            for (idx = 0; idx < len; ++idx) {
                total += values[idx];
            }
            return Math.floor(total / len);
        },

        _stdDeviation = function (values) {
            var mean = _mean(values),
                total = 0,
                len = values.length,
                idx,
                diff;
            for (idx = 0; idx < len; ++idx) {
                diff = values[idx] - mean;
                total += diff * diff;
            }
            return Math.floor(Math.sqrt(total / len));
        },

        _pad = function (value, size) {
            var pad = size - value.toString().length;
            if (pad > 0) {
                return new Array(pad).join(" ") + value;
            }
            return value;
        },

        _perfError = function (configuration, data) {
            configuration.log("ERROR during performance test:");
            var property,
                value;
            for (property in data) {
                if (data.hasOwnProperty(property)) {
                    value = data[property];
                    if (property === "exception" && value instanceof Error) {
                        configuration.log("\t" + _pad(property, 10) + ": " + value.message);
                    } else {
                        configuration.log("\t" + _pad(property, 10) + ": " + value.toString());
                    }
                }
            }
        },

        _runBDDPerf = function (configuration, options) {
            var loop = 1,
                maxLoop,
                runWithCallback,
                measures = [];
            if (options.perfInfinite) {
                maxLoop = Math.MAX_SAFE_INTEGER;
            } else {
                maxLoop = 10;
            }
            function callback (type, data) {
                var statistics;
                if (type === "it") {
                    if (!data.result) {
                        _perfError(configuration, data);
                        maxLoop = 0; // Stop
                    }
                } else if (type === "results") {
                    statistics = ["Round ", _pad(loop, 9), ": ", _pad(data.timeSpent, 5), "ms "];
                    if (loop === 1) {
                        statistics.push("(ignored)");
                    } else {
                        measures.push(data.timeSpent);
                        statistics.push("mean: ", _pad(_mean(measures), 5), "ms ",
                            "deviation: ", _pad(_stdDeviation(measures), 5));
                    }
                    configuration.log(statistics.join(""));
                    if (maxLoop < loop) {
                        configuration.log("Mean time: " + _mean(measures) + "ms");
                    } else {
                        ++loop;
                        setTimeout(runWithCallback, 0);
                    }
                }
            }
            runWithCallback = function () {
                run(callback);
            };
            runWithCallback();
        },

        _updloadCoverage = function () {
            var coverageData = JSON.stringify(__coverage__),
                pos = 0,
                length = coverageData.length,
                chunkSize = 4096,
                method = gpf.http.methods.post,
                url = "http://localhost:" + config.httpPort + "/fs/tmp/coverage/reports/coverage."
                    + gpf.host() + ".json";
            function upload () {
                return gpf.http.request({
                    method: method,
                    url: url,
                    data: coverageData.substring(pos, pos + chunkSize)
                })
                    .then(function () {
                        method = gpf.http.methods.put;
                        pos += chunkSize;
                        if (pos < length) {
                            return upload();
                        }
                    });
            }
            return upload();
        },

        _runBDDForCoverage = function (configuration, options, verbose) {
            verbose("Running BDD - coverage");
            run(function (type, data) {
                if (type === "it" && !data.result) {
                    configuration.log("KO: " + data.context.slice(1) + " " + data.label);
                }
                if (type !== "results") {
                    return;
                }
                if (data.fail) {
                    configuration.log("Failed: " + data.fail + ".");
                    configuration.exit(-1);
                }
                verbose("Uploading coverage results...");
                _updloadCoverage()
                    .then(function () {
                        verbose("Coverage results uploaded.");
                        configuration.exit(0);
                    }, function (reason) {
                        configuration.log("Upload failed: " + reason.toString());
                        configuration.exit(-1);
                    });
            });
        },

        _runBDD = function (configuration, options, verbose) {
            exit = configuration.exit; // used by BDD.js
            if (options.perf) {
                verbose("Running BDD - performances");
                _runBDDPerf(configuration, options);
            } else if (options.runCoverage) {
                _runBDDForCoverage(configuration, options, verbose);
            } else {
                verbose("Running BDD");
                run();
            }
        },

        _safeRunBDD = function (configuration, options, verbose) {
            try {
                if (undefined === configuration.run) {
                    _runBDD(configuration, options, verbose);
                } else {
                    configuration.run();
                }
                if (noTimeout) {
                    gpf.handleTimeout();
                }
            } catch (e) {
                configuration.log("An error occurred while running the tests:\r\n" + e.message);
                configuration.exit(-1);
            }
        },

        _loadFlavor = function (configuration, options) {
            // Load the list of modules
            var
                flavorName = options.tests[0].substring(7),
                flavorJson = configuration.read("make/flavor/" + flavorName + ".json"),
                flavor = safeFunc("return " + flavorJson + ";")();
            options.build = "gpf-" + flavorName;
            options.label = "\"" + options.flavor + "\" flavor";
            options.tests = flavor.tests;
        },

        _checkFlavor = function (configuration, options) {
            if (options.tests.length === 1 && options.tests[0].indexOf("flavor:") === 0) {
                _loadFlavor(configuration, options);
            }
        };

    /*
     * Expected members on configuration:
     * - {String[]} parameters command line parameters
     * - {String} gpfPath GPF base path
     * - {String} pathSeparator
     * - {Function} log
     * - {Function} exit
     * - (Function) require
     * - (Function) read  function (filePath) {} reads a file
     * - (Function) [loadTest=undefined] loadTest function (filePath) {} reads a test file
     * - {Function} run When defined, this method is triggered to execute the tests
     */
    context.loadGpfAndTests = function (configuration) {
        var options = {
                build: "",
                label: "source",
                release: function () {
                    this.build = "gpf";
                    this.label = "release";
                },
                debug: function () {
                    this.build = "gpf-debug";
                    this.label = "debug";
                },
                coverage: function () {
                    this.ignoreConsole = true;
                    this.label = "*instrumented* source";
                    this.runCoverage = true;
                    context.global = context;
                    context.gpfSourcesPath = _resolvePath(configuration, "tmp/coverage/instrument/src/");
                },
                verbose: false,
                ignoreConsole: false,
                perf: false,
                perfInfinite: false,
                tests: []
            },
            verbose;
        _processParameters(configuration, options);
        // Define a debug function that outputs when verbose is set
        if (options.verbose) {
            verbose = configuration.log;
        } else {
            verbose = function () {};
        }
        _checkFlavor(configuration, options);
        _loadGpf(configuration, options, verbose);
        _loadBDD(configuration, verbose);
        if (!options.ignoreConsole) {
            verbose("Loading console...");
            _load(configuration, _resolvePath(configuration, "test/host/console.js"));
            verbose("Console loaded.");
        }
        _setupConfig(configuration);
        _loadTests(configuration, options, verbose);
        _safeRunBDD(configuration, options, verbose);
    };

}());
<|endoftext|>


<commit_before>var BaseTexture = require('./BaseTexture'),
    CONST = require('../const');

/**
 * A BaseRenderTexture is a special texture that allows any Pixi display object to be rendered to it.
 *
 * __Hint__: All DisplayObjects (i.e. Sprites) that render to a BaseRenderTexture should be preloaded
 * otherwise black rectangles will be drawn instead.
 *
 * A BaseRenderTexture takes a snapshot of any Display Object given to its render method. The position
 * and rotation of the given Display Objects is ignored. For example:
 *
 * ```js
 * var renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 });
 * var BaserenderTexture = new PIXI.BaseRenderTexture(renderer, 800, 600);
 * var sprite = PIXI.Sprite.fromImage("spinObj_01.png");
 *
 * sprite.position.x = 800/2;
 * sprite.position.y = 600/2;
 * sprite.anchor.x = 0.5;
 * sprite.anchor.y = 0.5;
 *
 * BaserenderTexture.render(sprite);
 * ```
 *
 * The Sprite in this case will be rendered to a position of 0,0. To render this sprite at its actual
 * position a Container should be used:
 *
 * ```js
 * var doc = new PIXI.Container();
 *
 * doc.addChild(sprite);
 *
 * var baseRenderTexture = new PIXI.BaserenderTexture(100, 100);
 * var renderTexture = new PIXI.RenderTexture(baseRenderTexture);
 *
 * renderer.render(doc, renderTexture);  // Renders to center of RenderTexture
 * ```
 *
 * @class
 * @extends PIXI.BaseTexture
 * @memberof PIXI
 * @param [width=100] {number} The width of the base render texture
 * @param [height=100] {number} The height of the base render texture
 * @param [scaleMode=PIXI.SCALE_MODES.DEFAULT] {number} See {@link PIXI.SCALE_MODES} for possible values
 * @param [resolution=1] {number} The resolution / device pixel ratio of the texture being generated
 */
function BaseRenderTexture(width, height, scaleMode, resolution)
{
    BaseTexture.call(this, null, scaleMode);

    this.width = width || 100;
    this.height = height || 100;

    this.resolution = resolution || CONST.RESOLUTION;
    this.scaleMode = scaleMode || CONST.SCALE_MODES.DEFAULT;
    this.hasLoaded = true;

    /**
     * A map of renderer IDs to webgl renderTargets
     *
     * @member {object<number, WebGLTexture>}
     * @private
     */
    this._glRenderTargets = [];

    /**
     * A reference to the canvas render target (we only need one as this can be shared accross renderers)
     *
     * @member {object<number, WebGLTexture>}
     * @private
     */
    this._canvasRenderTarget = null;

    /**
     * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered.
     *
     * @member {boolean}
     */
    this.valid = false;
}

BaseRenderTexture.prototype = Object.create(BaseTexture.prototype);
BaseRenderTexture.prototype.constructor = BaseRenderTexture;
module.exports = BaseRenderTexture;

/**
 * Resizes the BaseRenderTexture.
 *
 * @param width {number} The width to resize to.
 * @param height {number} The height to resize to.
 */
BaseRenderTexture.prototype.resize = function (width, height)
{

    if (width === this.width && height === this.height)
    {
        return;
    }

    this.valid = (width > 0 && height > 0);

    this.width = width;
    this.height = height;

    if (!this.valid)
    {
        return;
    }

    this.emit('update', this);

};

/**
 * Destroys this texture
 *
 */
BaseRenderTexture.prototype.destroy = function ()
{
    BaseTexture.prototype.destroy.call(this, true);
    this.renderer = null;
};

<commit_msg>Able to find out the real width and height of a base render texture, accounting for the resolution applied to it (#2741)<commit_after>var BaseTexture = require('./BaseTexture'),
    CONST = require('../const');

/**
 * A BaseRenderTexture is a special texture that allows any Pixi display object to be rendered to it.
 *
 * __Hint__: All DisplayObjects (i.e. Sprites) that render to a BaseRenderTexture should be preloaded
 * otherwise black rectangles will be drawn instead.
 *
 * A BaseRenderTexture takes a snapshot of any Display Object given to its render method. The position
 * and rotation of the given Display Objects is ignored. For example:
 *
 * ```js
 * var renderer = PIXI.autoDetectRenderer(1024, 1024, { view: canvas, ratio: 1 });
 * var BaserenderTexture = new PIXI.BaseRenderTexture(renderer, 800, 600);
 * var sprite = PIXI.Sprite.fromImage("spinObj_01.png");
 *
 * sprite.position.x = 800/2;
 * sprite.position.y = 600/2;
 * sprite.anchor.x = 0.5;
 * sprite.anchor.y = 0.5;
 *
 * BaserenderTexture.render(sprite);
 * ```
 *
 * The Sprite in this case will be rendered to a position of 0,0. To render this sprite at its actual
 * position a Container should be used:
 *
 * ```js
 * var doc = new PIXI.Container();
 *
 * doc.addChild(sprite);
 *
 * var baseRenderTexture = new PIXI.BaserenderTexture(100, 100);
 * var renderTexture = new PIXI.RenderTexture(baseRenderTexture);
 *
 * renderer.render(doc, renderTexture);  // Renders to center of RenderTexture
 * ```
 *
 * @class
 * @extends PIXI.BaseTexture
 * @memberof PIXI
 * @param [width=100] {number} The width of the base render texture
 * @param [height=100] {number} The height of the base render texture
 * @param [scaleMode=PIXI.SCALE_MODES.DEFAULT] {number} See {@link PIXI.SCALE_MODES} for possible values
 * @param [resolution=1] {number} The resolution / device pixel ratio of the texture being generated
 */
function BaseRenderTexture(width, height, scaleMode, resolution)
{
    BaseTexture.call(this, null, scaleMode);

    this.width = width || 100;
    this.height = height || 100;

    this.realWidth = this.width * resolution;
    this.realHeight = this.height * resolution;

    this.resolution = resolution || CONST.RESOLUTION;
    this.scaleMode = scaleMode || CONST.SCALE_MODES.DEFAULT;
    this.hasLoaded = true;

    /**
     * A map of renderer IDs to webgl renderTargets
     *
     * @member {object<number, WebGLTexture>}
     * @private
     */
    this._glRenderTargets = [];

    /**
     * A reference to the canvas render target (we only need one as this can be shared accross renderers)
     *
     * @member {object<number, WebGLTexture>}
     * @private
     */
    this._canvasRenderTarget = null;

    /**
     * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered.
     *
     * @member {boolean}
     */
    this.valid = false;
}

BaseRenderTexture.prototype = Object.create(BaseTexture.prototype);
BaseRenderTexture.prototype.constructor = BaseRenderTexture;
module.exports = BaseRenderTexture;

/**
 * Resizes the BaseRenderTexture.
 *
 * @param width {number} The width to resize to.
 * @param height {number} The height to resize to.
 */
BaseRenderTexture.prototype.resize = function (width, height)
{

    if (width === this.width && height === this.height)
    {
        return;
    }

    this.valid = (width > 0 && height > 0);

    this.width = width;
    this.height = height;

    this.realWidth = this.width * this.resolution;
    this.realHeight = this.height * this.resolution;

    if (!this.valid)
    {
        return;
    }

    this.emit('update', this);

};

/**
 * Destroys this texture
 *
 */
BaseRenderTexture.prototype.destroy = function ()
{
    BaseTexture.prototype.destroy.call(this, true);
    this.renderer = null;
};

<|endoftext|>


<commit_before>// @flow weak

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import dataUpdate from '../core/dataUpdate';
import withTransitions from '../core/withTransitions';
import { getDisplayName, getRemoveUDID } from '../core/helpers';

const keyAccessor = (d, i) => `tick-${i}`;

export const propTypes = {
  scale: PropTypes.func.isRequired,
  className: PropTypes.string,
  tickCount: PropTypes.number,
};

export default function createTickGroup(tickComponent, wrapperComponent) {
  return class TickGroup extends PureComponent {
    static propTypes = propTypes;

    static defaultProps = {
      className: 'tick-group',
      tickCount: 10,
    };

    static displayName = `TickGroup(${getDisplayName(tickComponent)})`

    constructor(props) {
      super(props);

      (this:any).removeUDID = this.removeUDID.bind(this);
      (this:any).lazyRemoveUDID = this.lazyRemoveUDID.bind(this);
      this.WrappedComponent = withTransitions(tickComponent);
    }

    state = {
      nodes: [],
      udids: {},
      removed: {},
      prevScale: () => {},
      currScale: () => {},
    }

    componentDidMount() {
      this.updateTicks(this.props, this.props);
    }

    componentWillReceiveProps(next) {
      if (this.props.scale !== next.scale) {
        this.updateTicks(this.props, next);
      }
    }

    WrappedComponent = null;

    updateTicks(prev, next) {
      const { tickCount, scale } = next;
      const ticks = scale.ticks ? scale.ticks(tickCount) : [];

      this.setState((prevState) => {
        const mapped = ticks.map((tick) => ({ val: tick }));
        const update = dataUpdate(mapped, prevState, keyAccessor);
        return { ...update, prevScale: prev.scale, currScale: scale };
      });
    }

    removeUDID = getRemoveUDID.call(this, keyAccessor);

    lazyRemoveUDID(udid) {
      this.setState((prevState) => ({
        removed: Object.assign({}, prevState.removed, { [udid]: true }),
      }));
    }

    render() {
      const { props, WrappedComponent, state } = this;
      const childProps = Object.assign({}, props);
      delete childProps.scale;
      delete childProps.className;
      delete childProps.tickCount;

      return React.createElement(
        wrapperComponent,
        { className: props.className },
        state.nodes.map((node, index) => {
          const udid = keyAccessor(node);
          const type = state.udids[udid];

          return (
            <WrappedComponent
              key={udid}
              udid={udid}
              type={type}
              node={node}
              data={props.scale}
              index={index}
              prevScale={state.prevScale}
              currScale={state.currScale}
              removeUDID={this.removeUDID}
              lazyRemoveUDID={this.lazyRemoveUDID}
              {...childProps}
            />
          );
        }),
      );
    }
  };
}
<commit_msg>use tick.val for keys<commit_after>// @flow weak

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import dataUpdate from '../core/dataUpdate';
import withTransitions from '../core/withTransitions';
import { getDisplayName, getRemoveUDID } from '../core/helpers';

const keyAccessor = (d) => `tick-${d.val}`;

export const propTypes = {
  scale: PropTypes.func.isRequired,
  className: PropTypes.string,
  tickCount: PropTypes.number,
};

export default function createTickGroup(tickComponent, wrapperComponent) {
  return class TickGroup extends PureComponent {
    static propTypes = propTypes;

    static defaultProps = {
      className: 'tick-group',
      tickCount: 10,
    };

    static displayName = `TickGroup(${getDisplayName(tickComponent)})`

    constructor(props) {
      super(props);

      (this:any).removeUDID = this.removeUDID.bind(this);
      (this:any).lazyRemoveUDID = this.lazyRemoveUDID.bind(this);
      this.WrappedComponent = withTransitions(tickComponent);
    }

    state = {
      nodes: [],
      udids: {},
      removed: {},
      prevScale: () => {},
      currScale: () => {},
    }

    componentDidMount() {
      this.updateTicks(this.props, this.props);
    }

    componentWillReceiveProps(next) {
      if (this.props.scale !== next.scale) {
        this.updateTicks(this.props, next);
      }
    }

    WrappedComponent = null;

    updateTicks(prev, next) {
      const { tickCount, scale } = next;
      const ticks = scale.ticks ? scale.ticks(tickCount) : [];

      this.setState((prevState) => {
        const mapped = ticks.map((tick) => ({ val: tick }));
        const update = dataUpdate(mapped, prevState, keyAccessor);
        return { ...update, prevScale: prev.scale, currScale: scale };
      });
    }

    removeUDID = getRemoveUDID.call(this, keyAccessor);

    lazyRemoveUDID(udid) {
      this.setState((prevState) => ({
        removed: Object.assign({}, prevState.removed, { [udid]: true }),
      }));
    }

    render() {
      const { props, WrappedComponent, state } = this;
      const childProps = Object.assign({}, props);
      delete childProps.scale;
      delete childProps.className;
      delete childProps.tickCount;

      return React.createElement(
        wrapperComponent,
        { className: props.className },
        state.nodes.map((node, index) => {
          const udid = keyAccessor(node);
          const type = state.udids[udid];

          return (
            <WrappedComponent
              key={udid}
              udid={udid}
              type={type}
              node={node}
              data={props.scale}
              index={index}
              prevScale={state.prevScale}
              currScale={state.currScale}
              removeUDID={this.removeUDID}
              lazyRemoveUDID={this.lazyRemoveUDID}
              {...childProps}
            />
          );
        }),
      );
    }
  };
}
<|endoftext|>


<commit_before>/* eslint-disable no-unused-expressions */
/* eslint-disable camelcase */
require('./gently-preamble')
const chai = require('chai')
var chaiAsPromised = require('chai-as-promised')
const TransloaditClient = require('../src/TransloaditClient')
const localtunnel = require('localtunnel')
const http = require('http')
const querystring = require('querystring')
const temp = require('temp')
const fs = require('fs')
const _ = require('lodash')
const { join } = require('path')
const { promisify } = require('util')
const { pipeline: streamPipeline } = require('stream')
const got = require('got')
const pipeline = promisify(streamPipeline)

const { expect } = chai
chai.use(chaiAsPromised)

const authKey = process.env.TRANSLOADIT_KEY
const authSecret = process.env.TRANSLOADIT_SECRET
if (authKey == null || authSecret == null) {
  let msg = 'specify environment variables TRANSLOADIT_KEY and TRANSLOADIT_SECRET'
  msg += ' to enable integration tests.'
  console.warn(msg)
} else {
  const startServerAsync = async (handler) => new Promise((resolve, reject) => {
    const server = http.createServer(handler)

    // Find a port to use
    let port = 8000
    server.on('error', err => {
      if (err.code === 'EADDRINUSE') {
        if (++port >= 65535) {
          server.close()
          reject(new Error('Failed to bind to port'))
        }
        return server.listen(port, '127.0.0.1')
      } else {
        return reject(err)
      }
    })

    server.listen(port, '127.0.0.1')

    // Once a port has been found and the server is ready, setup the
    // localtunnel
    return server.on('listening', async () => {
      try {
        const tunnel = await localtunnel(port)
        // console.log('localtunnel', tunnel.url)

        tunnel.on('error', console.error)
        tunnel.on('close', () => {
          // console.log('tunnel closed')
          server.close()
        })

        return resolve({
          url: tunnel.url,
          close () {
            tunnel.close()
          },
        })
      } catch (err) {
        if (err != null) {
          server.close()
          return reject(err)
        }
      }
    })
  })

  const startServer = (handler, cb) => startServerAsync(handler).then((server) => cb(null, server)).catch(cb)

  // https://transloadit.com/demos/importing-files/import-a-file-over-http
  const genericImg = 'https://demos.transloadit.com/inputs/chameleon.jpg'
  const genericParams = {
    params: {
      steps: {
        import: {
          robot: '/http/import',
          url  : genericImg,
        },
        resize: {
          robot : '/image/resize',
          use   : 'import',
          result: true,
          width : 130,
          height: 130,
        },
      },
    },
    waitForCompletion: true,
  }

  describe('API integration', function () {
    this.timeout(100000)
    describe('assembly creation', () => {
      it('should create a retrievable assembly on the server', done => {
        const client = new TransloaditClient({ authKey, authSecret })

        return client.createAssembly(genericParams, (err, result) => {
          expect(err).to.not.exist
          expect(result).to.not.have.property('error')
          expect(result).to.have.property('ok')
          expect(result).to.have.property('assembly_id') // Since we're using it

          const id = result.assembly_id

          return client.getAssembly(id, (err, result) => {
            expect(err).to.not.exist
            expect(result).to.not.have.property('error')
            expect(result).to.have.property('ok')
            expect(result.assembly_id).to.equal(id)
            return done()
          })
        })
      })

      it('should get a full assembly status reliably', function (done) {
        this.timeout(0)

        const client = new TransloaditClient({ authKey, authSecret })

        const nbranches = 5
        let ndone = 0
        const branchDone = () => {
          if (++ndone === nbranches) {
            return done()
          }
        }

        const reproduce = nattempts => {
          if (nattempts === 0) {
            return branchDone()
          }

          client.createAssembly(genericParams, (err, { error, assembly_id } = {}) => {
            if (err != null || error != null) {
              return reproduce(nattempts - 1)
            }

            client.getAssembly(assembly_id, (err, result) => {
              if (err != null || result.error != null) {
                reproduce(nattempts - 1)
              }

              expect(result).to.have.property('assembly_url').that.exist
              reproduce(nattempts - 1)
            })
          })
        }

        // attempt to reproduce the incomplete status response 100 times
        for (let i = 0; i < nbranches; i++) reproduce(100 / nbranches)
      })

      it("should signal an error if a file selected for upload doesn't exist", async () => {
        const client = new TransloaditClient({ authKey, authSecret })

        const params = {
          params: {
            steps: {
              resize: {
                robot : '/image/resize',
                use   : ':original',
                result: true,
                width : 130,
                height: 130,
              },
            },
          },
        }

        client.addFile('original', temp.path({ suffix: '.transloadit.jpg' })) // Non-existing path

        const promise = client.createAssemblyAsync(params)
        await expect(promise).to.eventually.be.rejectedWith(Error).and.have.property('code').that.equals('ENOENT')
      })

      it('should allow uploading files that do exist', async () => {
        const client = new TransloaditClient({ authKey, authSecret })

        const params = {
          params: {
            steps: {
              resize: {
                robot : '/image/resize',
                use   : ':original',
                result: true,
                width : 130,
                height: 130,
              },
            },
          },
        }

        const { path } = await temp.open('transloadit')

        await pipeline(got.stream(genericImg), fs.createWriteStream(path))
            client.addFile('original', path)

        await client.createAssemblyAsync(params)
      })

      async function testUploadProgress (isResumable) {
        const client = new TransloaditClient({ authKey, authSecret })

        const params = {
          isResumable,
          params: {
            steps: {
              resize: {
                robot : '/image/resize',
                use   : ':original',
                result: true,
                width : 130,
                height: 130,
              },
            },
          },
        }

        const { path } = await temp.open('transloadit')

        await pipeline(got.stream(genericImg), fs.createWriteStream(path))
        client.addFile('original', path)
        let progressCalled = false
        function onProgress (progress) {
          // console.log(progress)
          expect(progress.uploadProgress.uploadedBytes).to.exist
          progressCalled = true
        }
        await client.createAssemblyAsync(params, onProgress)
        expect(progressCalled).to.be.true
      }

      it('should trigger progress callbacks when uploading files, resumable', async () => {
        await testUploadProgress(true)
      })

      it('should trigger progress callbacks when uploading files, nonresumable', async () => {
        await testUploadProgress(false)
      })

      it('should trigger the callback when waitForComplete is false', done => {
        const client = new TransloaditClient({ authKey, authSecret })
        const params = Object.assign({}, genericParams, { waitForCompletion: false })

        return client.createAssembly(params, (err, result) => {
          expect(err).to.not.exist
          expect(result).to.not.have.property('error')
          expect(result).to.have.property('ok')
          return done()
        })
      })

      it('should exit fast when assembly has failed', async () => {
        // An old bug caused it to continuously retry until timeout when errors such as INVALID_FILE_META_DATA
        const client = new TransloaditClient({ authKey, authSecret })
        const opts = {
          params: {
            steps: {
              resize: {
                use   : ':original',
                robot : '/image/resize',
                result: true,
                width : 130,
                height: 130,
              },
            },
          },
          waitForCompletion: true,
        }
        client.addFile('file', join(__dirname, './fixtures/zerobytes.jpg'))

        const promise = client.createAssemblyAsync(opts)
        await promise.catch((err) => {
          expect(err.error).to.eq('INVALID_FILE_META_DATA')
          expect(err.assembly_id).to.exist
        })
        await expect(promise).to.eventually.be.rejectedWith(Error)
      }).timeout(7000)
    })

    describe('assembly cancelation', () => {
      it('should stop the assembly from reaching completion', async () => {
        const client = new TransloaditClient({ authKey, authSecret })
        // const opts = {
        //   params: {
        //     steps: {
        //       resize: {
        //         robot : '/image/resize',
        //         use   : ':original',
        //         result: true,
        //         width : 130,
        //         height: 130,
        //       },
        //     },
        //   },
        // }

        // We need to ensure that the assembly doesn't complete before it can be
        // canceled, so we start an http server for the assembly to import from,
        // and delay transmission of data until we've already sent the cancel
        // request

        // Async book-keeping for delaying the response
        let sendServerResponse

        const promise = new Promise((resolve) => {
          sendServerResponse = resolve
        })

        const handler = async (req, res) => {
          // console.log('handler', req.url)

          expect(req.url).to.equal('/')

          await promise

          res.setHeader('Content-type', 'image/jpeg')
          res.writeHead(200)
          got.stream(genericImg).pipe(res)
        }

        const server = await startServerAsync(handler)

        try {
          const params = {
            params: {
              steps: {
                import: {
                  robot: '/http/import',
                  url  : server.url,
                },
                resize: {
                  robot : '/image/resize',
                  use   : 'import',
                  result: true,
                  width : 130,
                  height: 130,
                },
              },
            },
          }

          // Finally send the createAssembly request
          const { assembly_id: id } = await client.createAssemblyAsync(params)

          // Now delete it
          const resp = await client.deleteAssemblyAsync(id)

          // Allow the upload to finish
          sendServerResponse()

          expect(resp.ok).to.equal('ASSEMBLY_CANCELED')

          // Successful cancel requests get ASSEMBLY_CANCELED even when it
          // completed, so we now request the assembly status to check the
          // *actual* status.
          const resp2 = await client.getAssemblyAsync(id)
          expect(resp2.ok).to.equal('ASSEMBLY_CANCELED')
        } finally {
          server.close()
        }
      })
    })

    describe('replaying assemblies', () => {
      it('should replay an assembly after it has completed', done => {
        const client = new TransloaditClient({ authKey, authSecret })

        client.createAssembly(genericParams, (err, { assembly_id } = {}) => {
          expect(err).to.not.exist

          const originalId = assembly_id // eslint-disable-line camelcase

          // ensure that the assembly has completed
          const ensureCompletion = cb =>
            client.getAssembly(originalId, (err, result) => {
              expect(err).to.not.exist
              const ok = result.ok

              if (ok === 'ASSEMBLY_UPLOADING' || ok === 'ASSEMBLY_EXECUTING') {
                setTimeout(() => ensureCompletion(cb), 1000)
              } else {
                cb()
              }
            })

          // Start an asynchonous loop
          ensureCompletion(() =>
            client.replayAssembly({ assembly_id: originalId }, (err, { ok } = {}) => {
              expect(err).to.not.exist
              expect(ok).to.equal('ASSEMBLY_REPLAYING')
              done()
            }),
          )
        })
      })
    })

    describe('assembly list retrieval', () => {
      it('should retrieve a list of assemblies', done => {
        const client = new TransloaditClient({ authKey, authSecret })

        client.listAssemblies({}, (err, result) => {
          expect(err).to.not.exist
          expect(result).to.have.property('count')
          expect(result)
            .to.have.property('items')
            .that.is.instanceof(Array)
          done()
        })
      })

      it('should be able to handle pagination with a stream', done => {
        const client = new TransloaditClient({ authKey, authSecret })
        const assemblies = client.streamAssemblies({ pagesize: 2 })
        let n = 0
        let isDone = false

        assemblies.on('readable', () => {
          const assembly = assemblies.read()

          if (isDone) return

          if (assembly == null) {
            return done()
          }

          if (n === 5) {
            isDone = true
            return done()
          }

          expect(assembly).to.have.property('id')
          n++
        })
      })
    })

    describe('assembly notification', () => {
      // helper function
      const streamToString = (stream, cb) => {
        const chunks = []
        stream.on('data', chunk => chunks.push(chunk))
        stream.on('error', err => cb(err))
        stream.on('end', () => cb(null, chunks.join('')))
      }

      const testCase = (desc, endBehavior) =>
        it(desc, done => {
          const client = new TransloaditClient({ authKey, authSecret })

          // listens for notifications
          const handler = (req, res) => {
            expect(req.url).to.equal('/')

            expect(req.method).to.equal('POST')
            streamToString(req, (err, body) => {
              if (err) {
                console.error(err)
              }
              const result = JSON.parse(querystring.parse(body).transloadit)
              expect(result).to.have.property('ok')
              res.writeHead(200)
              res.end()
              if (result.ok !== 'ASSEMBLY_COMPLETED') return
              endBehavior(client, result.assembly_id, done)
            })
          }

          startServer(handler, (err, server) => {
            expect(err).to.not.exist

            const params = { params: _.extend({}, genericParams.params, { notify_url: server.url }) }

            client.createAssembly(params, (err, result) => expect(err).to.not.exist)
          })
        })

      testCase('should send a notification upon assembly completion', (client, id, done) => done())

      let notificationsRecvd = 0
      testCase('should replay the notification when requested', (client, id, done) => {
        if (notificationsRecvd++ === 0) {
          setTimeout(() => {
            client.replayAssemblyNotification({ assembly_id: id }, err => expect(err).to.not.exist)
          }, 2000)
        } else {
          done()
        }
      })
    })

    describe('template methods', () => {
      // can contain only lowercase latin letters, numbers, and dashes.
      const templName = `node-sdk-test-${new Date().toISOString().toLocaleLowerCase('en-US').replace(/[^0-9a-z-]/g, '-')}`
      let templId = null
      const client = new TransloaditClient({ authKey, authSecret })

      it('should allow creating a template', async () => {
        const { id } = await client.createTemplateAsync({ name: templName, template: genericParams.params })
        templId = id
      })

      it("should be able to fetch a template's definition", async () => {
        expect(templId).to.exist

        const { name, content } = await client.getTemplateAsync(templId)
        expect(name).to.equal(templName)
        expect(content).to.deep.equal(genericParams.params)
      })

      it('should delete the template successfully', done => {
        expect(templId).to.exist

        client.deleteTemplate(templId, (err, { ok } = {}) => {
          expect(err).to.not.exist
          expect(ok).to.equal('TEMPLATE_DELETED')
          client.getTemplate(templId, (err, result) => {
            expect(result).to.not.exist
            expect(err).to.exist
            expect(err.error).to.equal('TEMPLATE_NOT_FOUND')
            done()
          })
        })
      })
    })
  })
}
<commit_msg>improve tests<commit_after>/* eslint-disable no-unused-expressions */
/* eslint-disable camelcase */
require('./gently-preamble')
const chai = require('chai')
var chaiAsPromised = require('chai-as-promised')
const TransloaditClient = require('../src/TransloaditClient')
const localtunnel = require('localtunnel')
const http = require('http')
const querystring = require('querystring')
const temp = require('temp')
const fs = require('fs')
const _ = require('lodash')
const { join } = require('path')
const { promisify } = require('util')
const { pipeline: streamPipeline } = require('stream')
const got = require('got')
const pipeline = promisify(streamPipeline)

const { expect } = chai
chai.use(chaiAsPromised)

async function downloadTmpFile (url) {
  const { path } = await temp.open('transloadit')
  await pipeline(got.stream(url), fs.createWriteStream(path))
  return path
}

const authKey = process.env.TRANSLOADIT_KEY
const authSecret = process.env.TRANSLOADIT_SECRET
if (authKey == null || authSecret == null) {
  let msg = 'specify environment variables TRANSLOADIT_KEY and TRANSLOADIT_SECRET'
  msg += ' to enable integration tests.'
  console.warn(msg)
} else {
  const startServerAsync = async (handler) => new Promise((resolve, reject) => {
    const server = http.createServer(handler)

    // Find a port to use
    let port = 8000
    server.on('error', err => {
      if (err.code === 'EADDRINUSE') {
        if (++port >= 65535) {
          server.close()
          reject(new Error('Failed to bind to port'))
        }
        return server.listen(port, '127.0.0.1')
      } else {
        return reject(err)
      }
    })

    server.listen(port, '127.0.0.1')

    // Once a port has been found and the server is ready, setup the
    // localtunnel
    return server.on('listening', async () => {
      try {
        const tunnel = await localtunnel(port)
        // console.log('localtunnel', tunnel.url)

        tunnel.on('error', console.error)
        tunnel.on('close', () => {
          // console.log('tunnel closed')
          server.close()
        })

        return resolve({
          url: tunnel.url,
          close () {
            tunnel.close()
          },
        })
      } catch (err) {
        if (err != null) {
          server.close()
          return reject(err)
        }
      }
    })
  })

  const startServer = (handler, cb) => startServerAsync(handler).then((server) => cb(null, server)).catch(cb)

  // https://transloadit.com/demos/importing-files/import-a-file-over-http
  const genericImg = 'https://demos.transloadit.com/inputs/chameleon.jpg'
  const resizeOriginalStep = {
    robot : '/image/resize',
    use   : ':original',
    result: true,
    width : 130,
    height: 130,
  }
  const genericParams = {
    params: {
      steps: {
        import: {
          robot: '/http/import',
          url  : genericImg,
        },
        resize: {
          robot : '/image/resize',
          use   : 'import',
          result: true,
          width : 130,
          height: 130,
        },
      },
    },
    waitForCompletion: true,
  }

  describe('API integration', function () {
    this.timeout(100000)
    describe('assembly creation', () => {
      it('should create a retrievable assembly on the server', done => {
        const client = new TransloaditClient({ authKey, authSecret })

        return client.createAssembly(genericParams, (err, result) => {
          expect(err).to.not.exist
          expect(result).to.not.have.property('error')
          expect(result).to.have.property('ok')
          expect(result).to.have.property('assembly_id') // Since we're using it

          const id = result.assembly_id

          return client.getAssembly(id, (err, result) => {
            expect(err).to.not.exist
            expect(result).to.not.have.property('error')
            expect(result).to.have.property('ok')
            expect(result.assembly_id).to.equal(id)
            return done()
          })
        })
      })

      it('should get a full assembly status reliably', function (done) {
        this.timeout(0)

        const client = new TransloaditClient({ authKey, authSecret })

        const nbranches = 5
        let ndone = 0
        const branchDone = () => {
          if (++ndone === nbranches) {
            return done()
          }
        }

        const reproduce = nattempts => {
          if (nattempts === 0) {
            return branchDone()
          }

          client.createAssembly(genericParams, (err, { error, assembly_id } = {}) => {
            if (err != null || error != null) {
              return reproduce(nattempts - 1)
            }

            client.getAssembly(assembly_id, (err, result) => {
              if (err != null || result.error != null) {
                reproduce(nattempts - 1)
              }

              expect(result).to.have.property('assembly_url').that.exist
              reproduce(nattempts - 1)
            })
          })
        }

        // attempt to reproduce the incomplete status response 100 times
        for (let i = 0; i < nbranches; i++) reproduce(100 / nbranches)
      })

      it("should signal an error if a file selected for upload doesn't exist", async () => {
        const client = new TransloaditClient({ authKey, authSecret })

        const params = {
          params: {
            steps: {
              resize: resizeOriginalStep,
            },
          },
        }

        client.addFile('original', temp.path({ suffix: '.transloadit.jpg' })) // Non-existing path

        const promise = client.createAssemblyAsync(params)
        await expect(promise).to.eventually.be.rejectedWith(Error).and.have.property('code').that.equals('ENOENT')
      })

      it('should allow uploading files that do exist', async () => {
        const client = new TransloaditClient({ authKey, authSecret })

        const params = {
          params: {
            steps: {
              resize: resizeOriginalStep,
            },
          },
        }

        const path = await downloadTmpFile(genericImg)
            client.addFile('original', path)

        await client.createAssemblyAsync(params)
      })

      async function testUploadProgress (isResumable) {
        const client = new TransloaditClient({ authKey, authSecret })

        const params = {
          isResumable,
          params: {
            steps: {
              resize: resizeOriginalStep,
            },
          },
        }

        const path = await downloadTmpFile(genericImg)
        client.addFile('original', path)

        let progressCalled = false
        function onProgress (progress) {
          // console.log(progress)
          expect(progress.uploadProgress.uploadedBytes).to.exist
          progressCalled = true
        }
        await client.createAssemblyAsync(params, onProgress)
        expect(progressCalled).to.be.true
      }

      it('should trigger progress callbacks when uploading files, resumable', async () => {
        await testUploadProgress(true)
      })

      it('should trigger progress callbacks when uploading files, nonresumable', async () => {
        await testUploadProgress(false)
      })

      it('should trigger the callback when waitForComplete is false', done => {
        const client = new TransloaditClient({ authKey, authSecret })
        const params = Object.assign({}, genericParams, { waitForCompletion: false })

        return client.createAssembly(params, (err, result) => {
          expect(err).to.not.exist
          expect(result).to.not.have.property('error')
          expect(result).to.have.property('ok')
          return done()
        })
      })

      it('should exit fast when assembly has failed', async () => {
        // An old bug caused it to continuously retry until timeout when errors such as INVALID_FILE_META_DATA
        const client = new TransloaditClient({ authKey, authSecret })
        const opts = {
          params: {
            steps: {
              resize: resizeOriginalStep,
            },
          },
          waitForCompletion: true,
        }
        client.addFile('file', join(__dirname, './fixtures/zerobytes.jpg'))

        const promise = client.createAssemblyAsync(opts)
        await promise.catch((err) => {
          expect(err.error).to.eq('INVALID_FILE_META_DATA')
          expect(err.assembly_id).to.exist
        })
        await expect(promise).to.eventually.be.rejectedWith(Error)
      }).timeout(7000)
    })

    describe('assembly cancelation', () => {
      it('should stop the assembly from reaching completion', async () => {
        const client = new TransloaditClient({ authKey, authSecret })

        // We need to ensure that the assembly doesn't complete before it can be
        // canceled, so we start an http server for the assembly to import from,
        // and delay transmission of data until we've already sent the cancel
        // request

        // Async book-keeping for delaying the response
        let sendServerResponse

        const promise = new Promise((resolve) => {
          sendServerResponse = resolve
        })

        const handler = async (req, res) => {
          // console.log('handler', req.url)

          expect(req.url).to.equal('/')

          await promise

          res.setHeader('Content-type', 'image/jpeg')
          res.writeHead(200)
          got.stream(genericImg).pipe(res)
        }

        const server = await startServerAsync(handler)

        try {
          const params = {
            params: {
              steps: {
                import: {
                  robot: '/http/import',
                  url  : server.url,
                },
                resize: {
                  robot : '/image/resize',
                  use   : 'import',
                  result: true,
                  width : 130,
                  height: 130,
                },
              },
            },
          }

          // Finally send the createAssembly request
          const { assembly_id: id } = await client.createAssemblyAsync(params)

          // Now delete it
          const resp = await client.deleteAssemblyAsync(id)

          // Allow the upload to finish
          sendServerResponse()

          expect(resp.ok).to.equal('ASSEMBLY_CANCELED')

          // Successful cancel requests get ASSEMBLY_CANCELED even when it
          // completed, so we now request the assembly status to check the
          // *actual* status.
          const resp2 = await client.getAssemblyAsync(id)
          expect(resp2.ok).to.equal('ASSEMBLY_CANCELED')
        } finally {
          server.close()
        }
      })
    })

    describe('replaying assemblies', () => {
      it('should replay an assembly after it has completed', done => {
        const client = new TransloaditClient({ authKey, authSecret })

        client.createAssembly(genericParams, (err, { assembly_id } = {}) => {
          expect(err).to.not.exist

          const originalId = assembly_id // eslint-disable-line camelcase

          // ensure that the assembly has completed
          const ensureCompletion = cb =>
            client.getAssembly(originalId, (err, result) => {
              expect(err).to.not.exist
              const ok = result.ok

              if (ok === 'ASSEMBLY_UPLOADING' || ok === 'ASSEMBLY_EXECUTING') {
                setTimeout(() => ensureCompletion(cb), 1000)
              } else {
                cb()
              }
            })

          // Start an asynchonous loop
          ensureCompletion(() =>
            client.replayAssembly({ assembly_id: originalId }, (err, { ok } = {}) => {
              expect(err).to.not.exist
              expect(ok).to.equal('ASSEMBLY_REPLAYING')
              done()
            }),
          )
        })
      })
    })

    describe('assembly list retrieval', () => {
      it('should retrieve a list of assemblies', done => {
        const client = new TransloaditClient({ authKey, authSecret })

        client.listAssemblies({}, (err, result) => {
          expect(err).to.not.exist
          expect(result).to.have.property('count')
          expect(result)
            .to.have.property('items')
            .that.is.instanceof(Array)
          done()
        })
      })

      it('should be able to handle pagination with a stream', done => {
        const client = new TransloaditClient({ authKey, authSecret })
        const assemblies = client.streamAssemblies({ pagesize: 2 })
        let n = 0
        let isDone = false

        assemblies.on('readable', () => {
          const assembly = assemblies.read()

          if (isDone) return

          if (assembly == null) {
            return done()
          }

          if (n === 5) {
            isDone = true
            return done()
          }

          expect(assembly).to.have.property('id')
          n++
        })
      })
    })

    describe('assembly notification', () => {
      // helper function
      const streamToString = (stream, cb) => {
        const chunks = []
        stream.on('data', chunk => chunks.push(chunk))
        stream.on('error', err => cb(err))
        stream.on('end', () => cb(null, chunks.join('')))
      }

      const testCase = (desc, endBehavior) =>
        it(desc, done => {
          const client = new TransloaditClient({ authKey, authSecret })

          // listens for notifications
          const handler = (req, res) => {
            expect(req.url).to.equal('/')

            expect(req.method).to.equal('POST')
            streamToString(req, (err, body) => {
              if (err) {
                console.error(err)
              }
              const result = JSON.parse(querystring.parse(body).transloadit)
              expect(result).to.have.property('ok')
              res.writeHead(200)
              res.end()
              if (result.ok !== 'ASSEMBLY_COMPLETED') return
              endBehavior(client, result.assembly_id, done)
            })
          }

          startServer(handler, (err, server) => {
            expect(err).to.not.exist

            const params = { params: _.extend({}, genericParams.params, { notify_url: server.url }) }

            client.createAssembly(params, (err, result) => expect(err).to.not.exist)
          })
        })

      testCase('should send a notification upon assembly completion', (client, id, done) => done())

      let notificationsRecvd = 0
      testCase('should replay the notification when requested', (client, id, done) => {
        if (notificationsRecvd++ === 0) {
          setTimeout(() => {
            client.replayAssemblyNotification({ assembly_id: id }, err => expect(err).to.not.exist)
          }, 2000)
        } else {
          done()
        }
      })
    })

    describe('template methods', () => {
      // can contain only lowercase latin letters, numbers, and dashes.
      const templName = `node-sdk-test-${new Date().toISOString().toLocaleLowerCase('en-US').replace(/[^0-9a-z-]/g, '-')}`
      let templId = null
      const client = new TransloaditClient({ authKey, authSecret })

      it('should allow creating a template', async () => {
        const { id } = await client.createTemplateAsync({ name: templName, template: genericParams.params })
        templId = id
      })

      it("should be able to fetch a template's definition", async () => {
        expect(templId).to.exist

        const { name, content } = await client.getTemplateAsync(templId)
        expect(name).to.equal(templName)
        expect(content).to.deep.equal(genericParams.params)
      })

      it('should delete the template successfully', done => {
        expect(templId).to.exist

        client.deleteTemplate(templId, (err, { ok } = {}) => {
          expect(err).to.not.exist
          expect(ok).to.equal('TEMPLATE_DELETED')
          client.getTemplate(templId, (err, result) => {
            expect(result).to.not.exist
            expect(err).to.exist
            expect(err.error).to.equal('TEMPLATE_NOT_FOUND')
            done()
          })
        })
      })
    })
  })
}
<|endoftext|>


<commit_before>function dumpModernizr(){
  var str = '';
  dumpModernizr.old = dumpModernizr.old || {};

    for (var prop in Modernizr) {

      // skip previously done ones.
      if (dumpModernizr.old[prop]) continue;
      else dumpModernizr.old[prop] = true;

      if (typeof Modernizr[prop] === 'function') continue;
      // skip unit test items
      if (/^test/.test(prop)) continue;

      if (~TEST.inputs.indexOf(prop)) {
        str += '
'+prop+'{}
';
        for (var field in Modernizr[prop]) {
          str += '
' + field + ': ' + Modernizr[prop][field] + '
';
        }
        str += '

';
      } else {
        str += '
' + prop + ': ' + Modernizr[prop] + '
';
      }
  }
  return str;
}

function grabFeatDetects(){
  // thx github.js
  gh.object("Modernizr", "Modernizr").blobAll('master', function (data) {

    var filenames = [];

    for (var file in data.blobs){
      var match = file.match(/^feature-detects\/(.*)/);
      if (!match) continue;

      var relpath = location.host == "modernizr.github.com" ?
                      'https://raw.github.com/Modernizr/Modernizr/master/' : '../';

      filenames.push(relpath + match[0]);
    }

    var jqxhrs = filenames.map(function(filename){
      return jQuery.getScript(filename);
    });

    jQuery.when.apply(jQuery, jqxhrs).done(resultsToDOM);

  });
}

function resultsToDOM(){

  var modOutput = document.createElement('div'),
      ref = document.getElementById('qunit-testresult') || document.getElementById('qunit-tests');

  modOutput.className = 'output';
  modOutput.innerHTML = dumpModernizr();
  if (Modernizr.csscolumns) ref.parentNode.insertBefore(modOutput, ref);

  // Modernizr object as text
  document.getElementsByTagName('textarea')[0].innerHTML = JSON.stringify(Modernizr);

}

/* uno  */ resultsToDOM();
/* dos  */ grabFeatDetects();
/* tres */ setTimeout(resultsToDOM, 5e3);

<commit_msg>test suite has some laggy tests. yeahhh.<commit_after>function dumpModernizr(){
  var str = '';
  dumpModernizr.old = dumpModernizr.old || {};

    for (var prop in Modernizr) {

      // skip previously done ones.
      if (dumpModernizr.old[prop]) continue;
      else dumpModernizr.old[prop] = true;

      if (typeof Modernizr[prop] === 'function') continue;
      // skip unit test items
      if (/^test/.test(prop)) continue;

      if (~TEST.inputs.indexOf(prop)) {
        str += '
'+prop+'{}
';
        for (var field in Modernizr[prop]) {
          str += '
' + field + ': ' + Modernizr[prop][field] + '
';
        }
        str += '

';
      } else {
        str += '
' + prop + ': ' + Modernizr[prop] + '
';
      }
  }
  return str;
}

function grabFeatDetects(){
  // thx github.js
  gh.object("Modernizr", "Modernizr").blobAll('master', function (data) {

    var filenames = [];

    for (var file in data.blobs){
      var match = file.match(/^feature-detects\/(.*)/);
      if (!match) continue;

      var relpath = location.host == "modernizr.github.com" ?
                      'https://raw.github.com/Modernizr/Modernizr/master/' : '../';

      filenames.push(relpath + match[0]);
    }

    var jqxhrs = filenames.map(function(filename){
      return jQuery.getScript(filename);
    });

    jQuery.when.apply(jQuery, jqxhrs).done(resultsToDOM);

  });
}

function resultsToDOM(){

  var modOutput = document.createElement('div'),
      ref = document.getElementById('qunit-testresult') || document.getElementById('qunit-tests');

  modOutput.className = 'output';
  modOutput.innerHTML = dumpModernizr();
  if (Modernizr.csscolumns) ref.parentNode.insertBefore(modOutput, ref);

  // Modernizr object as text
  document.getElementsByTagName('textarea')[0].innerHTML = JSON.stringify(Modernizr);

}

/* uno    */ resultsToDOM();
/* dos    */ grabFeatDetects();
/* tres   */ setTimeout(resultsToDOM,  5e3);
/* quatro */ setTimeout(resultsToDOM, 15e3);
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// jshinting (syntax checking) of the source

const vows = require("vows"),
      should = require("should"),
      fs = require('fs'),
      path = require('path'),
      jshint = require('jshint').JSHINT,
      walk = require('walk'),
      util = require('util');

var suite = vows.describe('jshint');

function jshintFormatter(errors) {
  return errors.map(function(e) {
    return e.error.reason + ' ' + e.file + ':' + e.error.line;
  });
}

var jshintrc = null;

suite.addBatch({
  '.jshintrc': {
    topic: function() {
      fs.readFile(path.join(__dirname, '../.jshintrc'), this.callback);
    },
    "should be readable": function(err, string) {
      should.not.exist(err);
      (string).should.be.an.instanceOf(Buffer);
    },
    "should parse": {
      topic: function(string) {
        this.callback(null, JSON.parse(string));
      },
      "ok": function(err, json) {
        jshintrc = json;
      }
    }
  }
});

suite.addBatch({
  'code syntax': {
    topic: function() {
      var filesToLint = [
        path.join(__dirname, '../index.js')
      ];

      var walker = walk.walkSync(path.join(__dirname, '../lib'), {});

      walker.on("file", function(root, fStat, next) {
        var f = path.join(root, fStat.name);
        if (/\.js$/.test(f)) {
          filesToLint.push(f);
        }
        next();
      });
      var self = this;
      walker.on("end", function() {
        self.callback(null, filesToLint);
      });
    },
    "for all implementation files": {
      topic: function(filesToLint) {
        var errors = [];
        var self = this;

        function checkNext() {
          if (!filesToLint.length) {
            if (errors.length) {
              var buf = util.format("%d jshint errors", errors.length);
              self.callback(buf);
            } else {
              self.callback(null);
            }
            return;
          }
          var f = filesToLint.shift();
          fs.readFile(f.toString(), function(err, data) {
            // now
            f = path.relative(process.cwd(), f);
            if (!jshint(data.toString(), jshintrc)) {
              jshint.errors.forEach(function(e) {
                if (e) {
                  var msg = util.format("%s %s:%d - %s", e.id, f, e.line, e.reason);
                  console.error(msg);
                  errors.push(msg);
                }
              });
            }
            checkNext();
          });
        }
        checkNext();
      },
      "should lint cleanly": function(err) { }
    }
  }
});

// run or export the suite.
if (process.argv[1] === __filename) suite.run();
else suite.export(module);
<commit_msg>remove dep on should, no need to introduce another assertion library dep<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// jshinting (syntax checking) of the source

const vows = require("vows"),
      fs = require('fs'),
      path = require('path'),
      jshint = require('jshint').JSHINT,
      walk = require('walk'),
      util = require('util'),
      assert = require('assert');

var suite = vows.describe('jshint');

function jshintFormatter(errors) {
  return errors.map(function(e) {
    return e.error.reason + ' ' + e.file + ':' + e.error.line;
  });
}

var jshintrc = null;

suite.addBatch({
  '.jshintrc': {
    topic: function() {
      fs.readFile(path.join(__dirname, '../.jshintrc'), this.callback);
    },
    "should be readable": function(err, string) {
      assert(!err);
      assert(string);
    },
    "should parse": {
      topic: function(string) {
        this.callback(null, JSON.parse(string));
      },
      "ok": function(err, json) {
        jshintrc = json;
      }
    }
  }
});

suite.addBatch({
  'code syntax': {
    topic: function() {
      var filesToLint = [
        path.join(__dirname, '../index.js')
      ];

      var walker = walk.walkSync(path.join(__dirname, '../lib'), {});

      walker.on("file", function(root, fStat, next) {
        var f = path.join(root, fStat.name);
        if (/\.js$/.test(f)) {
          filesToLint.push(f);
        }
        next();
      });
      var self = this;
      walker.on("end", function() {
        self.callback(null, filesToLint);
      });
    },
    "for all implementation files": {
      topic: function(filesToLint) {
        var errors = [];
        var self = this;

        function checkNext() {
          if (!filesToLint.length) {
            if (errors.length) {
              var buf = util.format("%d jshint errors", errors.length);
              self.callback(buf);
            } else {
              self.callback(null);
            }
            return;
          }
          var f = filesToLint.shift();
          fs.readFile(f.toString(), function(err, data) {
            // now
            f = path.relative(process.cwd(), f);
            if (!jshint(data.toString(), jshintrc)) {
              jshint.errors.forEach(function(e) {
                if (e) {
                  var msg = util.format("%s %s:%d - %s", e.id, f, e.line, e.reason);
                  console.error(msg);
                  errors.push(msg);
                }
              });
            }
            checkNext();
          });
        }
        checkNext();
      },
      "should lint cleanly": function(err) { }
    }
  }
});

// run or export the suite.
if (process.argv[1] === __filename) suite.run();
else suite.export(module);
<|endoftext|>