jones-test/lib/LintTest.js (169 lines of code) (raw):

/* Copyright (c) 2013, 2016 Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ "use strict"; var path = require("path"), fs = require("fs"), util = require("util"), assert = require("assert"), unified_debug = require("unified_debug"), udebug = unified_debug.getLogger("LintTest.js"), Test = require("./Test"); var skipTests = false; var haveJsLint = false; var ignoredErrors = {}; // File-scope, for all tests var workingIgnoredErrors = {}; // working copy of ignored errors var lintModule, linter; var lintOptions = { "vars" : true, // allow multiple var declarations "plusplus" : true, // allow ++ and -- operators "white" : true, // misc. white space "stupid" : true, // sync methods "node" : true, // node.js globals "nomen" : true, // allow dangling underscore "eqeq" : true, // allow == "bitwise" : true, // allow bitwise operators "ass" : true, // allow assignment expressions "todo" : true, // allow TODO comments "regexp" : true, // allow . and [^ ...] in regular expressions "unparam" : true, // allow unused parameters, "debug" : true // allows empty blocks }; var jslintLoaderError; try { lintModule = require("jslint/lib/linter"); linter = lintModule.lint; assert(typeof linter === 'function'); haveJsLint = true; // older node-jslint } catch(e1) { jslintLoaderError = e1.message; try { lintModule = require("jslint"); linter = lintModule.load("es5"); assert(typeof linter === 'function'); haveJsLint = true; // newer node-jslint } catch(e2) { jslintLoaderError = e2.message; } } if(! haveJsLint) { skipTests = true; } // LintTest function LintTest(basePath, sourceFile) { this.sourceFileName = path.basename(sourceFile); this.sourceFile = path.resolve(basePath, sourceFile); this.name = path.basename(basePath) + "/" + path.basename(sourceFile); } LintTest.prototype = new Test.Test(); LintTest.prototype.fullName = function() { return this.suite.name + " " + this.name; }; // LintSmokeTest function LintSmokeTest() { this.phase = 0; this.name = "LintSmokeTest"; } LintSmokeTest.prototype = new Test.Test(); LintSmokeTest.prototype.run = function() { if(skipTests) { this.fail("linter is not available: " + jslintLoaderError); } else { // there is one copy of workingIgnoredErrors for all lint tests // so before each series of lint tests, // deep copy ignoredErrors to workingIgnoredErrors workingIgnoredErrors = JSON.parse(JSON.stringify(ignoredErrors)); this.pass(); } }; function isIgnored(file, pos, msg) { var list; var ignoreAlways = "Expected \'{\' and instead saw"; list = workingIgnoredErrors[file]; if(list && list[0] && (list[0].pos === pos) && (msg.search(list[0].msg) > -1)) { list.shift(); return true; } if(msg.indexOf(ignoreAlways) === 0) { return true; } return false; } /// run() method for LintTest LintTest.prototype.run = function() { if(skipTests) { return this.skip("jslint not avaliable"); } var e, i, n=0; var ok, errors, msg = ""; var data = fs.readFileSync(this.sourceFile, "utf8"); var result = linter(data, lintOptions); var nIgnored = 0; /* Adapt to differing APIs of jslint and jshint */ if(typeof result === 'boolean') { /* We are using jshint */ ok = result; errors = linter.errors; } else { /* jslint */ ok = result.ok; errors = result.errors; } try { nIgnored = ignoredErrors[this.sourceFileName].length; } catch(ignore) { } if(! ok) { udebug.log(this.sourceFileName, "errors:", errors.length, "ignored:", nIgnored); for (i = 0; i < errors.length; i += 1) { e = errors[i]; if(e && ! isIgnored(this.sourceFileName, e.character, e.reason)) { n += 1; msg += util.format('\n * Line %d[%d]: %s', e.line, e.character, e.reason); } } msg = util.format("%d lint error%s", n, n===1 ? '':'s') + msg; if (n > 0) { this.appendErrorMessage(msg); } } return true; }; function ignore(file, pos, msg, count) { var i; var list = ignoredErrors[file]; if(! list) { list = []; ignoredErrors[file] = list; workingIgnoredErrors[file] = list; } if(count === undefined) { list.push({ 'pos': pos, 'msg': msg}); } else { for(i = 0 ; i < count ; i++) { list.push({ 'pos': pos, 'msg': msg}); } } udebug.log("ignore:", file, pos, msg, count ? "x"+count : ""); } function predefine(keywordArray) { lintOptions.predef = keywordArray; } var skipFilePatterns = [ /^\./, // skip files starting with . /~[1-9]~$/ // bzr leaves these around ]; function getLintTestsForDirectory(directory) { var tests, files, file, i, useFile; tests = []; for(i = 1 ; i < arguments.length ; i++) { directory = path.resolve(directory, arguments[i]); } /* Add the individual file lint tests */ files = fs.readdirSync(directory); while(file = files.pop()) { useFile = false; for(i = 0 ; i < skipFilePatterns.length ; i++) { if( (file.match(/\.js$/) && (! file.match(skipFilePatterns[i])))) { useFile = true; } } if(useFile) { tests.push(new LintTest(directory, file)); } } return tests; } exports.forDirectory = getLintTestsForDirectory; exports.LintTest = LintTest; exports.LintSmokeTest = LintSmokeTest; exports.ignore = ignore; exports.predefine = predefine; exports.isAvailable = haveJsLint;