Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Revamp processing order to enable proper aborting of test runs #1124

Merged
merged 2 commits into from
Mar 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 54 additions & 53 deletions src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,88 +25,89 @@ export const globalSuite = new SuiteReport();
// it since each module has a suiteReport associated with it.
config.currentModule.suiteReport = globalSuite;

const moduleStack = [];
var globalStartCalled = false;
var runStarted = false;

export const internalState = {
autorun: false
};

// Figure out if we're running the tests from a server or not
QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" );

// Expose the current QUnit version
QUnit.version = "@VERSION";

function createModule( name, testEnvironment ) {
const parentModule = moduleStack.length ? moduleStack.slice( -1 )[ 0 ] : null;
const moduleName = parentModule !== null ? [ parentModule.name, name ].join( " > " ) : name;
const parentSuite = parentModule ? parentModule.suiteReport : globalSuite;

const module = {
name: moduleName,
parentModule: parentModule,
tests: [],
moduleId: generateHash( moduleName ),
testsRun: 0,
childModules: [],
suiteReport: new SuiteReport( name, parentSuite )
};

const env = {};
if ( parentModule ) {
parentModule.childModules.push( module );
extend( env, parentModule.testEnvironment );
}
extend( env, testEnvironment );
module.testEnvironment = env;

config.modules.push( module );
return module;
}

extend( QUnit, {
on,

// Call on start of module test to prepend name to all tests
module: function( name, testEnvironment, executeNow ) {
var module, moduleFns;
var currentModule = config.currentModule;

if ( arguments.length === 2 ) {
if ( objectType( testEnvironment ) === "function" ) {
executeNow = testEnvironment;
testEnvironment = undefined;
}
}

module = createModule();
let module = createModule( name, testEnvironment );

// Move any hooks to a 'hooks' object
if ( module.testEnvironment ) {
module.hooks = {
before: module.testEnvironment.before,
beforeEach: module.testEnvironment.beforeEach,
afterEach: module.testEnvironment.afterEach,
after: module.testEnvironment.after
};

delete module.testEnvironment.before;
delete module.testEnvironment.beforeEach;
delete module.testEnvironment.afterEach;
delete module.testEnvironment.after;
}

moduleFns = {
const moduleFns = {
before: setHook( module, "before" ),
beforeEach: setHook( module, "beforeEach" ),
afterEach: setHook( module, "afterEach" ),
after: setHook( module, "after" )
};

const currentModule = config.currentModule;
if ( objectType( executeNow ) === "function" ) {
config.moduleStack.push( module );
setCurrentModule( module );
moduleStack.push( module );
config.currentModule = module;
executeNow.call( module.testEnvironment, moduleFns );
config.moduleStack.pop();
moduleStack.pop();
module = module.parentModule || currentModule;
}

setCurrentModule( module );

function createModule() {
var parentModule = config.moduleStack.length ?
config.moduleStack.slice( -1 )[ 0 ] : null;
var moduleName = parentModule !== null ?
[ parentModule.name, name ].join( " > " ) : name;
var parentSuite = parentModule ? parentModule.suiteReport : globalSuite;

var module = {
name: moduleName,
parentModule: parentModule,
tests: [],
moduleId: generateHash( moduleName ),
testsRun: 0,
childModules: [],
suiteReport: new SuiteReport( name, parentSuite )
};

var env = {};
if ( parentModule ) {
parentModule.childModules.push( module );
extend( env, parentModule.testEnvironment );
delete env.beforeEach;
delete env.afterEach;
}
extend( env, testEnvironment );
module.testEnvironment = env;

config.modules.push( module );
return module;
}

function setCurrentModule( module ) {
config.currentModule = module;
}

config.currentModule = module;
},

test: test,
Expand Down Expand Up @@ -241,16 +242,16 @@ export function begin() {
}

config.blocking = false;
ProcessingQueue.advance( true );
ProcessingQueue.advance();
}

function setHook( module, hookName ) {
if ( module.testEnvironment === undefined ) {
module.testEnvironment = {};
if ( !module.hooks ) {
module.hooks = {};
}

return function( callback ) {
module.testEnvironment[ hookName ] = callback;
module.hooks[ hookName ] = callback;
};
}

Expand Down
3 changes: 0 additions & 3 deletions src/core/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ const config = {
// Set of all modules.
modules: [],

// Stack of nested modules
moduleStack: [],

// The first unnamed module
currentModule: {
name: "",
Expand Down
54 changes: 28 additions & 26 deletions src/core/processing-queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
} from "./logging";

import {
internalState,
globalSuite
} from "../core";
import {
Expand All @@ -27,11 +26,7 @@ let unitSampler;
* Advances the ProcessingQueue to the next item if it is ready.
* @param {Boolean} last
*/
function advance( last ) {
function next() {
advance( last );
}

function advance() {
const start = now();
config.depth = ( config.depth || 0 ) + 1;

Expand All @@ -45,54 +40,57 @@ function advance( last ) {
config.current.usedAsync = false;
}

if ( priorityCount > 0 ) {
priorityCount--;
}

config.queue.shift()();
} else {
setTimeout( next, 13 );
setTimeout( advance, 13 );
break;
}
}

config.depth--;

if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
if ( !config.blocking && !config.queue.length && config.depth === 0 ) {
done();
}
}

/**
* Adds a function to the ProcessingQueue for execution.
* @param {Function|Array} callback
* @param {Boolean} priority
* @param {String} seed
*/
function addToQueue( callback, priority, seed ) {
const last = !priority;

function addToQueueImmediate( callback ) {
if ( objectType( callback ) === "array" ) {
while ( callback.length ) {
addToQueue( callback.shift() );
addToQueueImmediate( callback.pop() );
}

return;
}

if ( priority ) {
config.queue.unshift( callback );
priorityCount++;
}

/**
* Adds a function to the ProcessingQueue for execution.
* @param {Function|Array} callback
* @param {Boolean} priority
* @param {String} seed
*/
function addToQueue( callback, prioritize, seed ) {
if ( prioritize ) {
config.queue.splice( priorityCount++, 0, callback );
} else if ( seed ) {
if ( !unitSampler ) {
unitSampler = unitSamplerGenerator( seed );
}

// Insert into a random position after all priority items
// Insert into a random position after all prioritized items
const index = Math.floor( unitSampler() * ( config.queue.length - priorityCount + 1 ) );
config.queue.splice( priorityCount + index, 0, callback );
} else {
config.queue.push( callback );
}

if ( internalState.autorun && !config.blocking ) {
advance( last );
}
}

/**
Expand Down Expand Up @@ -124,7 +122,7 @@ function unitSamplerGenerator( seed ) {
function done() {
const storage = config.storage;

internalState.autorun = true;
ProcessingQueue.finished = true;

const runtime = now() - config.started;
const passed = config.stats.all - config.stats.bad;
Expand All @@ -149,7 +147,11 @@ function done() {
}
}

export default {
const ProcessingQueue = {
finished: false,
add: addToQueue,
addImmediate: addToQueueImmediate,
advance
};

export default ProcessingQueue;
29 changes: 13 additions & 16 deletions src/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,6 @@ Test.prototype = {

config.current = this;

if ( module.testEnvironment ) {
delete module.testEnvironment.before;
delete module.testEnvironment.beforeEach;
delete module.testEnvironment.afterEach;
delete module.testEnvironment.after;
}
this.testEnvironment = extend( {}, module.testEnvironment );

this.started = now();
Expand Down Expand Up @@ -203,9 +197,8 @@ Test.prototype = {
if ( module.parentModule ) {
processHooks( test, module.parentModule );
}
if ( module.testEnvironment &&
objectType( module.testEnvironment[ handler ] ) === "function" ) {
hooks.push( test.queueHook( module.testEnvironment[ handler ], handler, module ) );
if ( module.hooks && objectType( module.hooks[ handler ] ) === "function" ) {
hooks.push( test.queueHook( module.hooks[ handler ], handler, module ) );
}
}

Expand Down Expand Up @@ -303,9 +296,8 @@ Test.prototype = {
}
},

queue: function() {
var priority, previousFailCount,
test = this;
queue() {
const test = this;

if ( !this.valid() ) {
return;
Expand All @@ -314,7 +306,7 @@ Test.prototype = {
function run() {

// Each of these can by async
ProcessingQueue.add( [
ProcessingQueue.addImmediate( [
function() {
test.before();
},
Expand Down Expand Up @@ -344,15 +336,20 @@ Test.prototype = {
] );
}

previousFailCount = config.storage &&
const previousFailCount = config.storage &&
+config.storage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );

// Prioritize previously failed tests, detected from storage
priority = config.reorder && previousFailCount;
const prioritize = config.reorder && !!previousFailCount;

this.previousFailure = !!previousFailCount;

return ProcessingQueue.add( run, priority, config.seed );
ProcessingQueue.add( run, prioritize, config.seed );

// If the queue has already finished, we manually process the new test
if ( ProcessingQueue.finished ) {
ProcessingQueue.advance();
}
},

pushResult: function( resultInfo ) {
Expand Down