diff --git a/_infrastructure/tests/runner.js b/_infrastructure/tests/runner.js index e3eca657f..c6ab61c8a 100644 --- a/_infrastructure/tests/runner.js +++ b/_infrastructure/tests/runner.js @@ -52,7 +52,6 @@ var DT; this.ext = path.extname(this.filePathWithName); this.file = path.basename(this.filePathWithName, this.ext); this.dir = path.dirname(this.filePathWithName); - this.formatName = path.join(this.dir, this.file + this.ext); this.fullPath = path.join(this.baseDir, this.dir, this.file + this.ext); // lock it (shallow) (needs `use strict` in each file to work) // Object.freeze(this); @@ -202,6 +201,7 @@ var DT; var fs = require('fs'); var path = require('path'); + var glob = require('glob'); var Lazy = require('lazy.js'); var Promise = require('bluebird'); @@ -211,7 +211,8 @@ var DT; // Track all files in the repo: map full path to File objects ///////////////////////////////// var FileIndex = (function () { - function FileIndex(options) { + function FileIndex(runner, options) { + this.runner = runner; this.options = options; } FileIndex.prototype.hasFile = function (target) { @@ -225,14 +226,76 @@ var DT; return null; }; - FileIndex.prototype.parseFiles = function (files) { + FileIndex.prototype.setFile = function (file) { + if (file.fullPath in this.fileMap) { + throw new Error('cannot overwrite file'); + } + this.fileMap[file.fullPath] = file; + }; + + FileIndex.prototype.readIndex = function () { + var _this = this; + this.fileMap = Object.create(null); + + return Promise.promisify(glob).call(glob, '**/*.ts', { + cwd: this.runner.dtPath + }).then(function (filesNames) { + _this.files = Lazy(filesNames).filter(function (fileName) { + return _this.runner.checkAcceptFile(fileName); + }).map(function (fileName) { + var file = new DT.File(_this.runner.dtPath, fileName); + _this.fileMap[file.fullPath] = file; + return file; + }).toArray(); + }); + }; + + FileIndex.prototype.collectDiff = function (changes) { + var _this = this; + return new Promise(function (resolve) { + // filter changes and bake map for easy lookup + _this.changed = Object.create(null); + _this.removed = Object.create(null); + + Lazy(changes).filter(function (full) { + return _this.runner.checkAcceptFile(full); + }).uniq().each(function (local) { + var full = path.resolve(_this.runner.dtPath, local); + var file = _this.getFile(full); + if (!file) { + // TODO figure out what to do here + // what does it mean? deleted?ss + file = new DT.File(_this.runner.dtPath, local); + _this.setFile(file); + _this.removed[full] = file; + // console.log('not in index? %', file.fullPath); + } else { + _this.changed[full] = file; + } + }); + + // console.log('changed:\n' + Object.keys(this.changed).join('\n')); + // console.log('removed:\n' + Object.keys(this.removed).join('\n')); + resolve(); + }); + }; + + FileIndex.prototype.parseFiles = function () { + var _this = this; + return this.loadReferences(this.files).then(function () { + return _this.getMissingReferences(); + }); + }; + + FileIndex.prototype.getMissingReferences = function () { var _this = this; return Promise.attempt(function () { - _this.fileMap = Object.create(null); - files.forEach(function (file) { - _this.fileMap[file.fullPath] = file; + _this.missing = Object.create(null); + Lazy(_this.removed).keys().each(function (removed) { + if (removed in _this.refMap) { + _this.missing[removed] = _this.refMap[removed]; + } }); - return _this.loadReferences(files); }); }; @@ -262,6 +325,19 @@ var DT; } }; next(); + }).then(function () { + // bake reverse reference map (referenced to referrers) + _this.refMap = Object.create(null); + + Lazy(files).each(function (file) { + Lazy(file.references).each(function (ref) { + if (ref.fullPath in _this.refMap) { + _this.refMap[ref.fullPath].push(file); + } else { + _this.refMap[ref.fullPath] = [file]; + } + }); + }); }); }; @@ -287,11 +363,43 @@ var DT; return file; }); }; + + FileIndex.prototype.collectTargets = function () { + var _this = this; + return new Promise(function (resolve) { + // map out files linked to changes + // - queue holds files touched by a change + // - pre-fill with actually changed files + // - loop queue, if current not seen: + // - add to result + // - from refMap queue all files referring to current + var result = Object.create(null); + var queue = Lazy(_this.changed).values().toArray(); + + while (queue.length > 0) { + var next = queue.shift(); + var fp = next.fullPath; + if (result[fp]) { + continue; + } + result[fp] = next; + if (fp in _this.refMap) { + var arr = _this.refMap[fp]; + for (var i = 0, ii = arr.length; i < ii; i++) { + // just add it and skip expensive checks + queue.push(arr[i]); + } + } + } + resolve(Lazy(result).values().toArray()); + }); + }; return FileIndex; })(); DT.FileIndex = FileIndex; })(DT || (DT = {})); /// +/// var DT; (function (DT) { 'use strict'; @@ -302,11 +410,10 @@ var DT; var Promise = require('bluebird'); var GitChanges = (function () { - function GitChanges(baseDir) { - this.baseDir = baseDir; + function GitChanges(runner) { + this.runner = runner; this.options = {}; - this.paths = []; - var dir = path.join(baseDir, '.git'); + var dir = path.join(this.runner.dtPath, '.git'); if (!fs.existsSync(dir)) { throw new Error('cannot locate git-dir: ' + dir); } @@ -315,12 +422,11 @@ var DT; this.git = new Git(this.options); this.git.exec = Promise.promisify(this.git.exec); } - GitChanges.prototype.getChanges = function () { - var _this = this; + GitChanges.prototype.readChanges = function () { var opts = {}; var args = ['--name-only HEAD~1']; return this.git.exec('diff', opts, args).then(function (msg) { - _this.paths = msg.replace(/^\s+/, '').replace(/\s+$/, '').split(/\r?\n/g); + return msg.replace(/^\s+/, '').replace(/\s+$/, '').split(/\r?\n/g); }); }; return GitChanges; @@ -393,7 +499,7 @@ var DT; }; Print.prototype.printErrorsForFile = function (testResult) { - this.out('----------------- For file:' + testResult.targetFile.formatName); + this.out('----------------- For file:' + testResult.targetFile.filePathWithName); this.printBreak().out(testResult.stderr).printBreak(); }; @@ -471,6 +577,101 @@ var DT; }); } }; + + Print.prototype.printTestComplete = function (testResult, index) { + var reporter = testResult.hostedBy.testReporter; + if (testResult.success) { + reporter.printPositiveCharacter(index, testResult); + } else { + reporter.printNegativeCharacter(index, testResult); + } + }; + + Print.prototype.printSuiteComplete = function (suite) { + this.printBreak(); + + this.printDiv(); + this.printElapsedTime(suite.timer.asString, suite.timer.time); + this.printSuccessCount(suite.okTests.length, suite.testResults.length); + this.printFailedCount(suite.ngTests.length, suite.testResults.length); + }; + + Print.prototype.printTests = function (adding) { + var _this = this; + this.printDiv(); + this.printSubHeader('Testing'); + this.printDiv(); + + Object.keys(adding).sort().map(function (src) { + _this.printLine(adding[src].filePathWithName); + return adding[src]; + }); + }; + + Print.prototype.printFiles = function (files) { + var _this = this; + this.printDiv(); + this.printSubHeader('Files:'); + this.printDiv(); + + files.forEach(function (file) { + _this.printLine(file.filePathWithName); + file.references.forEach(function (file) { + _this.printElement(file.filePathWithName); + }); + }); + }; + + Print.prototype.printMissing = function (index, refMap) { + var _this = this; + this.printDiv(); + this.printSubHeader('Missing references'); + this.printDiv(); + + Object.keys(refMap).sort().forEach(function (src) { + var ref = index.getFile(src); + _this.printLine('\33[31m\33[1m' + ref.filePathWithName + '\33[0m'); + refMap[src].forEach(function (file) { + _this.printElement(file.filePathWithName); + }); + }); + }; + + Print.prototype.printAllChanges = function (paths) { + var _this = this; + this.printSubHeader('All changes'); + this.printDiv(); + + paths.sort().forEach(function (line) { + _this.printLine(line); + }); + }; + + Print.prototype.printRelChanges = function (changeMap) { + var _this = this; + this.printDiv(); + this.printSubHeader('Relevant changes'); + this.printDiv(); + + Object.keys(changeMap).sort().forEach(function (src) { + _this.printLine(changeMap[src].filePathWithName); + }); + }; + + Print.prototype.printRefMap = function (index, refMap) { + var _this = this; + this.printDiv(); + this.printSubHeader('Referring'); + this.printDiv(); + + Object.keys(refMap).sort().forEach(function (src) { + var ref = index.getFile(src); + _this.printLine(ref.filePathWithName); + refMap[src].forEach(function (file) { + _this.printLine(' - ' + file.filePathWithName); + }); + }); + }; return Print; })(); DT.Print = Print; @@ -607,7 +808,7 @@ var DT; } SyntaxChecking.prototype.filterTargetFiles = function (files) { return Promise.cast(files.filter(function (file) { - return endDts.test(file.formatName); + return endDts.test(file.filePathWithName); })); }; return SyntaxChecking; @@ -634,7 +835,7 @@ var DT; } TestEval.prototype.filterTargetFiles = function (files) { return Promise.cast(files.filter(function (file) { - return endTestDts.test(file.formatName); + return endTestDts.test(file.filePathWithName); })); }; return TestEval; @@ -664,7 +865,7 @@ var DT; this.testReporter = { printPositiveCharacter: function (index, testResult) { - _this.print.clearCurrentLine().printTypingsWithoutTestName(testResult.targetFile.formatName); + _this.print.clearCurrentLine().printTypingsWithoutTestName(testResult.targetFile.filePathWithName); }, printNegativeCharacter: function (index, testResult) { } @@ -680,7 +881,7 @@ var DT; FindNotRequiredTscparams.prototype.runTest = function (targetFile) { var _this = this; - this.print.clearCurrentLine().out(targetFile.formatName); + this.print.clearCurrentLine().out(targetFile.filePathWithName); return new DT.Test(this, targetFile, { tscVersion: this.options.tscVersion, @@ -729,7 +930,6 @@ var DT; var fs = require('fs'); var path = require('path'); - var glob = require('glob'); var assert = require('assert'); var tsExp = /\.ts$/; @@ -792,8 +992,9 @@ var DT; this.suites = []; this.options.findNotRequiredTscparams = !!this.options.findNotRequiredTscparams; - this.index = new DT.FileIndex(this.options); - this.changes = new DT.GitChanges(this.dtPath); + this.index = new DT.FileIndex(this, this.options); + this.changes = new DT.GitChanges(this); + this.print = new DT.Print(this.options.tscVersion); } TestRunner.prototype.addSuite = function (suite) { @@ -813,102 +1014,37 @@ var DT; this.timer = new DT.Timer(); this.timer.start(); + this.print.printChangeHeader(); + // only includes .d.ts or -tests.ts or -test.ts or .ts - return Promise.promisify(glob).call(glob, '**/*.ts', { - cwd: dtPath - }).then(function (filesNames) { - _this.files = Lazy(filesNames).filter(function (fileName) { - return _this.checkAcceptFile(fileName); - }).map(function (fileName) { - return new DT.File(dtPath, fileName); - }).toArray(); - - _this.print.printChangeHeader(); - - return _this.changes.getChanges().then(function () { - _this.printAllChanges(_this.changes.paths); - }); + return this.index.readIndex().then(function () { + return _this.changes.readChanges(); + }).then(function (changes) { + _this.print.printAllChanges(changes); + return _this.index.collectDiff(changes); }).then(function () { - return _this.index.parseFiles(_this.files).then(function () { - // this.printFiles(this.files); - }); + return _this.index.parseFiles(); }).then(function () { - return _this.collectTargets(_this.files, _this.changes.paths); - }).then(function (files) { - return _this.runTests(files); - }).then(function () { - return !_this.suites.some(function (suite) { - return suite.ngTests.length !== 0; - }); - }); - }; + // this.print.printRefMap(this.index, this.index.refMap); + if (Lazy(_this.index.missing).some(function (arr) { + return arr.length > 0; + })) { + _this.print.printMissing(_this.index, _this.index.missing); + _this.print.printBoldDiv(); - TestRunner.prototype.collectTargets = function (files, changes) { - var _this = this; - return new Promise(function (resolve) { - // filter changes and bake map for easy lookup - var changeMap = Object.create(null); - - Lazy(changes).filter(function (full) { - return _this.checkAcceptFile(full); - }).map(function (local) { - return path.resolve(_this.dtPath, local); - }).each(function (full) { - var file = _this.index.getFile(full); - if (!file) { - // TODO figure out what to do here - // what does it mean? deleted? - console.log('not in index: ' + full); - return; - } - changeMap[full] = file; - }); - - _this.printRelChanges(changeMap); - - // bake reverse reference map (referenced to referrers) - var refMap = Object.create(null); - - Lazy(files).each(function (file) { - Lazy(file.references).each(function (ref) { - if (ref.fullPath in refMap) { - refMap[ref.fullPath].push(file); - } else { - refMap[ref.fullPath] = [file]; - } - }); - }); - - // this.printRefMap(refMap); - // map out files linked to changes - // - queue holds files touched by a change - // - pre-fill with actually changed files - // - loop queue, if current not seen: - // - add to result - // - from refMap queue all files referring to current - var adding = Object.create(null); - var queue = Lazy(changeMap).values().toArray(); - - while (queue.length > 0) { - var next = queue.shift(); - var fp = next.fullPath; - if (adding[fp]) { - continue; - } - adding[fp] = next; - if (fp in refMap) { - var arr = refMap[fp]; - for (var i = 0, ii = arr.length; i < ii; i++) { - // just add it and skip expensive checks - queue.push(arr[i]); - } - } + // bail + return Promise.delay(500).return(false); } - _this.printTests(adding); - - var result = Lazy(adding).values().toArray(); - resolve(result); + // this.print.printFiles(this.files); + return _this.index.collectTargets().then(function (files) { + // this.print.printTests(files); + return _this.runTests(files); + }).then(function () { + return !_this.suites.some(function (suite) { + return suite.ngTests.length !== 0; + }); + }); }); }; @@ -944,10 +1080,10 @@ var DT; return suite.filterTargetFiles(files).then(function (targetFiles) { return suite.start(targetFiles, function (testResult, index) { - _this.printTestComplete(testResult, index); + _this.print.printTestComplete(testResult, index); }); }).then(function (suite) { - _this.printSuiteComplete(suite); + _this.print.printSuiteComplete(suite); return count++; }); }, 0); @@ -957,85 +1093,6 @@ var DT; }); }; - TestRunner.prototype.printTests = function (adding) { - var _this = this; - this.print.printDiv(); - this.print.printSubHeader('Testing'); - this.print.printDiv(); - - Object.keys(adding).sort().map(function (src) { - _this.print.printLine(adding[src].formatName); - return adding[src]; - }); - }; - TestRunner.prototype.printFiles = function (files) { - var _this = this; - this.print.printDiv(); - this.print.printSubHeader('Files:'); - this.print.printDiv(); - - files.forEach(function (file) { - _this.print.printLine(file.filePathWithName); - file.references.forEach(function (file) { - _this.print.printElement(file.filePathWithName); - }); - }); - }; - - TestRunner.prototype.printAllChanges = function (paths) { - var _this = this; - this.print.printSubHeader('All changes'); - this.print.printDiv(); - - paths.sort().forEach(function (line) { - _this.print.printLine(line); - }); - }; - - TestRunner.prototype.printRelChanges = function (changeMap) { - var _this = this; - this.print.printDiv(); - this.print.printSubHeader('Relevant changes'); - this.print.printDiv(); - - Object.keys(changeMap).sort().forEach(function (src) { - _this.print.printLine(changeMap[src].formatName); - }); - }; - - TestRunner.prototype.printRefMap = function (refMap) { - var _this = this; - this.print.printDiv(); - this.print.printSubHeader('Referring'); - this.print.printDiv(); - - Object.keys(refMap).sort().forEach(function (src) { - var ref = _this.index.getFile(src); - _this.print.printLine(ref.formatName); - refMap[src].forEach(function (file) { - _this.print.printLine(' - ' + file.formatName); - }); - }); - }; - - TestRunner.prototype.printTestComplete = function (testResult, index) { - var reporter = testResult.hostedBy.testReporter; - if (testResult.success) { - reporter.printPositiveCharacter(index, testResult); - } else { - reporter.printNegativeCharacter(index, testResult); - } - }; - - TestRunner.prototype.printSuiteComplete = function (suite) { - this.print.printBreak(); - - this.print.printDiv(); - this.print.printElapsedTime(suite.timer.asString, suite.timer.time); - this.print.printSuccessCount(suite.okTests.length, suite.testResults.length); - this.print.printFailedCount(suite.ngTests.length, suite.testResults.length); - }; - TestRunner.prototype.finaliseTests = function (files) { var _this = this; var testEval = Lazy(this.suites).filter(function (suite) { diff --git a/_infrastructure/tests/runner.ts b/_infrastructure/tests/runner.ts index 8dfcc7b4c..126b824b2 100644 --- a/_infrastructure/tests/runner.ts +++ b/_infrastructure/tests/runner.ts @@ -27,7 +27,6 @@ module DT { var fs = require('fs'); var path = require('path'); - var glob = require('glob'); var assert = require('assert'); var tsExp = /\.ts$/; @@ -79,31 +78,23 @@ module DT { findNotRequiredTscparams?:boolean; } - export interface FileDict { - [fullPath:string]: File; - } - - export interface FileArrDict { - [fullPath:string]: File[]; - } - ///////////////////////////////// // The main class to kick things off ///////////////////////////////// export class TestRunner { - private files: File[]; private timer: Timer; private suites: ITestSuite[] = []; - private index: FileIndex; - private changes: GitChanges; - private print: Print; + public changes: GitChanges; + public index: FileIndex; + public print: Print; constructor(public dtPath: string, public options: ITestRunnerOptions = {tscVersion: DT.DEFAULT_TSC_VERSION}) { this.options.findNotRequiredTscparams = !!this.options.findNotRequiredTscparams; - this.index = new FileIndex(this.options); - this.changes = new GitChanges(this.dtPath); + this.index = new FileIndex(this, this.options); + this.changes = new GitChanges(this); + this.print = new Print(this.options.tscVersion); } @@ -111,7 +102,7 @@ module DT { this.suites.push(suite); } - private checkAcceptFile(fileName: string): boolean { + public checkAcceptFile(fileName: string): boolean { var ok = tsExp.test(fileName); ok = ok && fileName.indexOf('_infrastructure') < 0; ok = ok && fileName.indexOf('node_modules/') < 0; @@ -123,104 +114,35 @@ module DT { this.timer = new Timer(); this.timer.start(); + this.print.printChangeHeader(); + // only includes .d.ts or -tests.ts or -test.ts or .ts - return Promise.promisify(glob).call(glob, '**/*.ts', { - cwd: dtPath - }).then((filesNames: string[]) => { - this.files = Lazy(filesNames).filter((fileName) => { - return this.checkAcceptFile(fileName); - }).map((fileName: string) => { - return new File(dtPath, fileName); - }).toArray(); - - this.print.printChangeHeader(); - - return this.changes.getChanges().then(() => { - this.printAllChanges(this.changes.paths); - }); + return this.index.readIndex().then(() => { + return this.changes.readChanges(); + }).then((changes: string[]) => { + this.print.printAllChanges(changes); + return this.index.collectDiff(changes); }).then(() => { - return this.index.parseFiles(this.files).then(() => { - // this.printFiles(this.files); - }); + return this.index.parseFiles(); }).then(() => { - return this.collectTargets(this.files, this.changes.paths); - }).then((files) => { - return this.runTests(files); - }).then(() => { - return !this.suites.some((suite) => { - return suite.ngTests.length !== 0 - }); - }); - } + // this.print.printRefMap(this.index, this.index.refMap); - private collectTargets(files: File[], changes: string[]): Promise { - return new Promise((resolve) => { + if (Lazy(this.index.missing).some((arr: any[]) => arr.length > 0)) { + this.print.printMissing(this.index, this.index.missing); + this.print.printBoldDiv(); + // bail + return Promise.delay(500).return(false); + } + // this.print.printFiles(this.files); + return this.index.collectTargets().then((files) => { + // this.print.printTests(files); - // filter changes and bake map for easy lookup - var changeMap: FileDict = Object.create(null); - - Lazy(changes).filter((full) => { - return this.checkAcceptFile(full); - }).map((local) => { - return path.resolve(this.dtPath, local); - }).each((full) => { - var file = this.index.getFile(full); - if (!file) { - // TODO figure out what to do here - // what does it mean? deleted? - console.log('not in index: ' + full); - return; - } - changeMap[full] = file; - }); - - this.printRelChanges(changeMap); - - // bake reverse reference map (referenced to referrers) - var refMap: FileArrDict = Object.create(null); - - Lazy(files).each((file) => { - Lazy(file.references).each((ref) => { - if (ref.fullPath in refMap) { - refMap[ref.fullPath].push(file); - } - else { - refMap[ref.fullPath] = [file]; - } + return this.runTests(files); + }).then(() => { + return !this.suites.some((suite) => { + return suite.ngTests.length !== 0 }); }); - - // this.printRefMap(refMap); - - // map out files linked to changes - // - queue holds files touched by a change - // - pre-fill with actually changed files - // - loop queue, if current not seen: - // - add to result - // - from refMap queue all files referring to current - var adding: FileDict = Object.create(null); - var queue = Lazy(changeMap).values().toArray(); - - while (queue.length > 0) { - var next = queue.shift(); - var fp = next.fullPath; - if (adding[fp]) { - continue; - } - adding[fp] = next; - if (fp in refMap) { - var arr = refMap[fp]; - for (var i = 0, ii = arr.length; i < ii; i++) { - // just add it and skip expensive checks - queue.push(arr[i]); - } - } - } - - this.printTests(adding); - - var result: File[] = Lazy(adding).values().toArray(); - resolve(result); }); } @@ -255,10 +177,10 @@ module DT { return suite.filterTargetFiles(files).then((targetFiles) => { return suite.start(targetFiles, (testResult, index) => { - this.printTestComplete(testResult, index); + this.print.printTestComplete(testResult, index); }); }).then((suite) => { - this.printSuiteComplete(suite); + this.print.printSuiteComplete(suite); return count++; }); }, 0); @@ -268,81 +190,6 @@ module DT { }); } - private printTests(adding: FileDict): void { - this.print.printDiv(); - this.print.printSubHeader('Testing'); - this.print.printDiv(); - - Object.keys(adding).sort().map((src) => { - this.print.printLine(adding[src].formatName); - return adding[src]; - }); - } - private printFiles(files: File[]): void { - this.print.printDiv(); - this.print.printSubHeader('Files:'); - this.print.printDiv(); - - files.forEach((file) => { - this.print.printLine(file.filePathWithName); - file.references.forEach((file) => { - this.print.printElement(file.filePathWithName); - }); - }); - } - - private printAllChanges(paths: string[]): void { - this.print.printSubHeader('All changes'); - this.print.printDiv(); - - paths.sort().forEach((line) => { - this.print.printLine(line); - }); - } - - private printRelChanges(changeMap: FileDict): void { - this.print.printDiv(); - this.print.printSubHeader('Relevant changes'); - this.print.printDiv(); - - Object.keys(changeMap).sort().forEach((src) => { - this.print.printLine(changeMap[src].formatName); - }); - } - - private printRefMap(refMap: FileArrDict): void { - this.print.printDiv(); - this.print.printSubHeader('Referring'); - this.print.printDiv(); - - Object.keys(refMap).sort().forEach((src) => { - var ref = this.index.getFile(src); - this.print.printLine(ref.formatName); - refMap[src].forEach((file) => { - this.print.printLine(' - ' + file.formatName); - }); - }); - } - - private printTestComplete(testResult: TestResult, index: number): void { - var reporter = testResult.hostedBy.testReporter; - if (testResult.success) { - reporter.printPositiveCharacter(index, testResult); - } - else { - reporter.printNegativeCharacter(index, testResult); - } - } - - private printSuiteComplete(suite: ITestSuite): void { - this.print.printBreak(); - - this.print.printDiv(); - this.print.printElapsedTime(suite.timer.asString, suite.timer.time); - this.print.printSuccessCount(suite.okTests.length, suite.testResults.length); - this.print.printFailedCount(suite.ngTests.length, suite.testResults.length); - } - private finaliseTests(files: File[]): void { var testEval: TestEval = Lazy(this.suites).filter((suite) => { return suite instanceof TestEval; diff --git a/_infrastructure/tests/src/changes.ts b/_infrastructure/tests/src/changes.ts index f3ec26208..a56c4029e 100644 --- a/_infrastructure/tests/src/changes.ts +++ b/_infrastructure/tests/src/changes.ts @@ -1,4 +1,5 @@ /// +/// module DT { 'use strict'; @@ -12,10 +13,9 @@ module DT { git; options = {}; - paths: string[] = []; - constructor(public baseDir: string) { - var dir = path.join(baseDir, '.git'); + constructor(private runner: TestRunner) { + var dir = path.join(this.runner.dtPath, '.git'); if (!fs.existsSync(dir)) { throw new Error('cannot locate git-dir: ' + dir); } @@ -25,11 +25,11 @@ module DT { this.git.exec = Promise.promisify(this.git.exec); } - getChanges(): Promise { + public readChanges(): Promise { var opts = {}; var args = ['--name-only HEAD~1']; return this.git.exec('diff', opts, args).then((msg: string) => { - this.paths = msg.replace(/^\s+/, '').replace(/\s+$/, '').split(/\r?\n/g); + return msg.replace(/^\s+/, '').replace(/\s+$/, '').split(/\r?\n/g); }); } } diff --git a/_infrastructure/tests/src/file.ts b/_infrastructure/tests/src/file.ts index 389936b49..2c69f8f88 100644 --- a/_infrastructure/tests/src/file.ts +++ b/_infrastructure/tests/src/file.ts @@ -5,6 +5,14 @@ module DT { var path = require('path'); + export interface FileDict { + [fullPath:string]: File; + } + + export interface FileArrDict { + [fullPath:string]: File[]; + } + ///////////////////////////////// // Given a document root + ts file pattern this class returns: // all the TS files OR just tests OR just definition files @@ -15,7 +23,6 @@ module DT { dir: string; file: string; ext: string; - formatName: string; fullPath: string; references: File[] = []; @@ -26,14 +33,13 @@ module DT { this.ext = path.extname(this.filePathWithName); this.file = path.basename(this.filePathWithName, this.ext); this.dir = path.dirname(this.filePathWithName); - this.formatName = path.join(this.dir, this.file + this.ext); this.fullPath = path.join(this.baseDir, this.dir, this.file + this.ext); // lock it (shallow) (needs `use strict` in each file to work) // Object.freeze(this); } - toString() { + toString(): string { return '[File ' + this.filePathWithName + ']'; } } diff --git a/_infrastructure/tests/src/index.ts b/_infrastructure/tests/src/index.ts index 183c1e64d..7f5878dcb 100644 --- a/_infrastructure/tests/src/index.ts +++ b/_infrastructure/tests/src/index.ts @@ -7,7 +7,8 @@ module DT { var fs = require('fs'); var path = require('path'); - var Lazy = require('lazy.js'); + var glob = require('glob'); + var Lazy: LazyJS.LazyStatic = require('lazy.js'); var Promise: typeof Promise = require('bluebird'); var readFile = Promise.promisify(fs.readFile); @@ -17,31 +18,95 @@ module DT { ///////////////////////////////// export class FileIndex { - fileMap: {[fullPath:string]:File}; + files: File[]; + fileMap: FileDict; + refMap: FileArrDict; options: ITestRunnerOptions; + changed: FileDict; + removed: FileDict; + missing: FileArrDict; - constructor(options: ITestRunnerOptions) { + constructor(private runner: TestRunner, options: ITestRunnerOptions) { this.options = options; } - hasFile(target: string): boolean { + public hasFile(target: string): boolean { return target in this.fileMap; } - getFile(target: string): File { + public getFile(target: string): File { if (target in this.fileMap) { return this.fileMap[target]; } return null; } - parseFiles(files: File[]): Promise { - return Promise.attempt(() => { - this.fileMap = Object.create(null); - files.forEach((file) => { + public setFile(file: File): void { + if (file.fullPath in this.fileMap) { + throw new Error('cannot overwrite file'); + } + this.fileMap[file.fullPath] = file; + } + + public readIndex(): Promise { + this.fileMap = Object.create(null); + + return Promise.promisify(glob).call(glob, '**/*.ts', { + cwd: this.runner.dtPath + }).then((filesNames: string[]) => { + this.files = Lazy(filesNames).filter((fileName) => { + return this.runner.checkAcceptFile(fileName); + }).map((fileName: string) => { + var file = new File(this.runner.dtPath, fileName); this.fileMap[file.fullPath] = file; + return file; + }).toArray(); + }); + } + + public collectDiff(changes: string[]): Promise { + return new Promise((resolve) => { + // filter changes and bake map for easy lookup + this.changed = Object.create(null); + this.removed = Object.create(null); + + Lazy(changes).filter((full) => { + return this.runner.checkAcceptFile(full); + }).uniq().each((local) => { + var full = path.resolve(this.runner.dtPath, local); + var file = this.getFile(full); + if (!file) { + // TODO figure out what to do here + // what does it mean? deleted?ss + file = new File(this.runner.dtPath, local); + this.setFile(file); + this.removed[full] = file; + // console.log('not in index? %', file.fullPath); + } + else { + this.changed[full] = file; + } + }); + // console.log('changed:\n' + Object.keys(this.changed).join('\n')); + // console.log('removed:\n' + Object.keys(this.removed).join('\n')); + resolve(); + }); + } + + public parseFiles(): Promise { + return this.loadReferences(this.files).then(() => { + return this.getMissingReferences(); + }); + } + + private getMissingReferences(): Promise { + return Promise.attempt(() => { + this.missing = Object.create(null); + Lazy(this.removed).keys().each((removed) => { + if (removed in this.refMap) { + this.missing[removed] = this.refMap[removed]; + } }); - return this.loadReferences(files); }); } @@ -70,6 +135,20 @@ module DT { } }; next(); + }).then(() => { + // bake reverse reference map (referenced to referrers) + this.refMap = Object.create(null); + + Lazy(files).each((file) => { + Lazy(file.references).each((ref) => { + if (ref.fullPath in this.refMap) { + this.refMap[ref.fullPath].push(file); + } + else { + this.refMap[ref.fullPath] = [file]; + } + }); + }); }); } @@ -94,5 +173,36 @@ module DT { return file; }); } + + public collectTargets(): Promise { + return new Promise((resolve) => { + // map out files linked to changes + // - queue holds files touched by a change + // - pre-fill with actually changed files + // - loop queue, if current not seen: + // - add to result + // - from refMap queue all files referring to current + + var result: FileDict = Object.create(null); + var queue = Lazy(this.changed).values().toArray(); + + while (queue.length > 0) { + var next = queue.shift(); + var fp = next.fullPath; + if (result[fp]) { + continue; + } + result[fp] = next; + if (fp in this.refMap) { + var arr = this.refMap[fp]; + for (var i = 0, ii = arr.length; i < ii; i++) { + // just add it and skip expensive checks + queue.push(arr[i]); + } + } + } + resolve(Lazy(result).values().toArray()); + }); + } } } diff --git a/_infrastructure/tests/src/printer.ts b/_infrastructure/tests/src/printer.ts index 613251b82..9be9a79aa 100644 --- a/_infrastructure/tests/src/printer.ts +++ b/_infrastructure/tests/src/printer.ts @@ -72,7 +72,7 @@ module DT { } public printErrorsForFile(testResult: TestResult) { - this.out('----------------- For file:' + testResult.targetFile.formatName); + this.out('----------------- For file:' + testResult.targetFile.filePathWithName); this.printBreak().out(testResult.stderr).printBreak(); } @@ -149,5 +149,95 @@ module DT { }); } } + + public printTestComplete(testResult: TestResult, index: number): void { + var reporter = testResult.hostedBy.testReporter; + if (testResult.success) { + reporter.printPositiveCharacter(index, testResult); + } + else { + reporter.printNegativeCharacter(index, testResult); + } + } + + public printSuiteComplete(suite: ITestSuite): void { + this.printBreak(); + + this.printDiv(); + this.printElapsedTime(suite.timer.asString, suite.timer.time); + this.printSuccessCount(suite.okTests.length, suite.testResults.length); + this.printFailedCount(suite.ngTests.length, suite.testResults.length); + } + + public printTests(adding: FileDict): void { + this.printDiv(); + this.printSubHeader('Testing'); + this.printDiv(); + + Object.keys(adding).sort().map((src) => { + this.printLine(adding[src].filePathWithName); + return adding[src]; + }); + } + + public printFiles(files: File[]): void { + this.printDiv(); + this.printSubHeader('Files:'); + this.printDiv(); + + files.forEach((file) => { + this.printLine(file.filePathWithName); + file.references.forEach((file) => { + this.printElement(file.filePathWithName); + }); + }); + } + + public printMissing(index: FileIndex, refMap: FileArrDict): void { + this.printDiv(); + this.printSubHeader('Missing references'); + this.printDiv(); + + Object.keys(refMap).sort().forEach((src) => { + var ref = index.getFile(src); + this.printLine('\33[31m\33[1m' + ref.filePathWithName + '\33[0m'); + refMap[src].forEach((file) => { + this.printElement(file.filePathWithName); + }); + }); + } + + public printAllChanges(paths: string[]): void { + this.printSubHeader('All changes'); + this.printDiv(); + + paths.sort().forEach((line) => { + this.printLine(line); + }); + } + + public printRelChanges(changeMap: FileDict): void { + this.printDiv(); + this.printSubHeader('Relevant changes'); + this.printDiv(); + + Object.keys(changeMap).sort().forEach((src) => { + this.printLine(changeMap[src].filePathWithName); + }); + } + + public printRefMap(index: FileIndex, refMap: FileArrDict): void { + this.printDiv(); + this.printSubHeader('Referring'); + this.printDiv(); + + Object.keys(refMap).sort().forEach((src) => { + var ref = index.getFile(src); + this.printLine(ref.filePathWithName); + refMap[src].forEach((file) => { + this.printLine(' - ' + file.filePathWithName); + }); + }); + } } } diff --git a/_infrastructure/tests/src/suite/syntax.ts b/_infrastructure/tests/src/suite/syntax.ts index 41808b2a2..2e0d0b71a 100644 --- a/_infrastructure/tests/src/suite/syntax.ts +++ b/_infrastructure/tests/src/suite/syntax.ts @@ -19,7 +19,7 @@ module DT { public filterTargetFiles(files: File[]): Promise { return Promise.cast(files.filter((file) => { - return endDts.test(file.formatName); + return endDts.test(file.filePathWithName); })); } } diff --git a/_infrastructure/tests/src/suite/testEval.ts b/_infrastructure/tests/src/suite/testEval.ts index bc842549f..95044daa7 100644 --- a/_infrastructure/tests/src/suite/testEval.ts +++ b/_infrastructure/tests/src/suite/testEval.ts @@ -19,7 +19,7 @@ module DT { public filterTargetFiles(files: File[]): Promise { return Promise.cast(files.filter((file) => { - return endTestDts.test(file.formatName); + return endTestDts.test(file.filePathWithName); })); } } diff --git a/_infrastructure/tests/src/suite/tscParams.ts b/_infrastructure/tests/src/suite/tscParams.ts index cef23d622..bb064dd81 100644 --- a/_infrastructure/tests/src/suite/tscParams.ts +++ b/_infrastructure/tests/src/suite/tscParams.ts @@ -22,7 +22,7 @@ module DT { printPositiveCharacter: (index: number, testResult: TestResult) => { this.print .clearCurrentLine() - .printTypingsWithoutTestName(testResult.targetFile.formatName); + .printTypingsWithoutTestName(testResult.targetFile.filePathWithName); }, printNegativeCharacter: (index: number, testResult: TestResult) => { } @@ -38,7 +38,7 @@ module DT { } public runTest(targetFile: File): Promise { - this.print.clearCurrentLine().out(targetFile.formatName); + this.print.clearCurrentLine().out(targetFile.filePathWithName); return new Test(this, targetFile, { tscVersion: this.options.tscVersion, diff --git a/_infrastructure/tests/typings/lazy.js/lazy.js.d.ts b/_infrastructure/tests/typings/lazy.js/lazy.js.d.ts index b9367b73b..b2909dfce 100644 --- a/_infrastructure/tests/typings/lazy.js/lazy.js.d.ts +++ b/_infrastructure/tests/typings/lazy.js/lazy.js.d.ts @@ -6,12 +6,12 @@ declare module LazyJS { interface LazyStatic { - (value: string):StringLikeSequence; (value: T[]):ArrayLikeSequence; (value: any[]):ArrayLikeSequence; (value: Object):ObjectLikeSequence; (value: Object):ObjectLikeSequence; + (value: string):StringLikeSequence; strict():LazyStatic; @@ -200,7 +200,7 @@ declare module LazyJS { functions(): Sequence; get(property: string): ObjectLikeSequence; invert(): ObjectLikeSequence; - keys(): StringLikeSequence; + keys(): Sequence; omit(properties: string[]): ObjectLikeSequence; pairs(): Sequence; pick(properties: string[]): ObjectLikeSequence;