Skip to content

Commit

Permalink
Core: handling callback promises serially
Browse files Browse the repository at this point in the history
  • Loading branch information
step2yeung committed Oct 18, 2018
1 parent 04cfb9f commit f3f43e3
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 59 deletions.
18 changes: 16 additions & 2 deletions src/core/logging.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ export function registerLoggingCallbacks( obj ) {

export function runLoggingCallbacks( key, args ) {
var callbacks = config.callbacks[ key ];
var promises = callbacks.map( callback => Promise.resolve( callback( args ) ) );
return Promise.all( promises );

// Handling 'log' callbacks separately. Unlike the other callbacks,
// the log callback is not controlled by the processing queue,
// but rather used by asserts. Hence to promisfy the 'log' callback
// would mean promisfying each step of a test
if ( key === "log" ) {
callbacks.map( callback => callback( args ) );
return;
}

// ensure that each callback is executed serially
return callbacks.reduce( ( promiseChain, callback ) => {
return promiseChain.then( () => {
return Promise.resolve( callback( args ) );
} );
}, Promise.resolve( [] ) );
}
19 changes: 10 additions & 9 deletions src/core/processing-queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const taskQueue = [];
function advance() {
advanceTaskQueue();

if ( !taskQueue.length ) {
if ( !taskQueue.length && !config.blocking ) {
advanceTestQueue();
}
}
Expand Down Expand Up @@ -192,18 +192,19 @@ function done() {
failed: config.stats.bad,
total: config.stats.all,
runtime
} );
} ).then( () => {

// Clear own storage items if all tests passed
if ( storage && config.stats.bad === 0 ) {
for ( let i = storage.length - 1; i >= 0; i-- ) {
const key = storage.key( i );
// Clear own storage items if all tests passed
if ( storage && config.stats.bad === 0 ) {
for ( let i = storage.length - 1; i >= 0; i-- ) {
const key = storage.key( i );

if ( key.indexOf( "qunit-test-" ) === 0 ) {
storage.removeItem( key );
if ( key.indexOf( "qunit-test-" ) === 0 ) {
storage.removeItem( key );
}
}
}
}
} );
}

const ProcessingQueue = {
Expand Down
54 changes: 28 additions & 26 deletions src/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,27 +105,29 @@ function getNotStartedModules( startModule ) {
module = module.parentModule;
}

return modules;
// The above push modules from the child to the parent
// return a reversed order with the top being the top most parent module
return modules.reverse();
}

Test.prototype = {
before: function() {
var i, startModule,
module = this.module,
promises = [],
var module = this.module,
notStartedModules = getNotStartedModules( module );

for ( i = notStartedModules.length - 1; i >= 0; i-- ) {
startModule = notStartedModules[ i ];
startModule.stats = { all: 0, bad: 0, started: now() };
emit( "suiteStart", startModule.suiteReport.start( true ) );
promises.push( runLoggingCallbacks( "moduleStart", {
name: startModule.name,
tests: startModule.tests
} ) );
}
// ensure the callbacks are executed serially for each module
var callbackPromises = notStartedModules.reduce( ( promiseChain, startModule ) => {
return promiseChain.then( () => {
startModule.stats = { all: 0, bad: 0, started: now() };
emit( "suiteStart", startModule.suiteReport.start( true ) );
return runLoggingCallbacks( "moduleStart", {
name: startModule.name,
tests: startModule.tests
} );
} );
}, Promise.resolve( [] ) );

return Promise.all( promises ).then( () => {
return callbackPromises.then( () => {
config.current = this;

this.testEnvironment = extend( {}, module.testEnvironment );
Expand All @@ -137,11 +139,11 @@ Test.prototype = {
module: module.name,
testId: this.testId,
previousFailure: this.previousFailure
} ).then( () => {
if ( !config.pollution ) {
saveGlobal();
}
} );
} ).then( () => {
if ( !config.pollution ) {
saveGlobal();
}
} );
},

Expand Down Expand Up @@ -334,20 +336,21 @@ Test.prototype = {
source: this.stack
} ).then( function() {
if ( module.testsRun === numberOfTests( module ) ) {
return logSuiteEnd( module );
}
} ).then( function() {
if ( module.testsRun === numberOfTests( module ) ) {
const promises = [];
const completedModules = [ module ];

// Check if the parent modules, iteratively, are done. If that the case,
// we emit the `suiteEnd` event and trigger `moduleDone` callback.
let parent = module.parentModule;
while ( parent && parent.testsRun === numberOfTests( parent ) ) {
promises.push( logSuiteEnd( parent ) );
completedModules.push( parent );
parent = parent.parentModule;
}
return Promise.all( promises );

return completedModules.reduce( ( promiseChain, completedModule ) => {
return promiseChain.then( () => {
return logSuiteEnd( completedModule );
} );
}, Promise.resolve( [] ) );
}
} ).then( function() {
config.current = undefined;
Expand All @@ -368,7 +371,6 @@ Test.prototype = {
total: module.stats.all,
runtime: now() - module.stats.started
} );

}
},

Expand Down
103 changes: 81 additions & 22 deletions test/callbacks-promises.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
var done = false;
var callbackCalled = {};
var invokedHooks = [];

function timeoutPromiseCallback( callback, timeout ) {
return new Promise( function( resolve ) {
Expand All @@ -12,35 +12,44 @@ function timeoutPromiseCallback( callback, timeout ) {

QUnit.begin( function() {
return timeoutPromiseCallback( function() {
callbackCalled.begin = true;
invokedHooks.push( "begin" );
}, 100 );
} );
QUnit.begin( function() {
return timeoutPromiseCallback( function() {

// This is the second begin callback, which should
// be executed only after the first one
if ( invokedHooks.indexOf( "begin" ) !== 0 ) {
return;
}
invokedHooks.push( "begin2" );
}, 10 );
} );
QUnit.moduleStart( function() {
return timeoutPromiseCallback( function() {
callbackCalled.moduleStart = true;
invokedHooks.push( "moduleStart" );
}, 100 );
} );
QUnit.testStart( function() {
QUnit.testStart( function( cb ) {
return timeoutPromiseCallback( function() {
callbackCalled.testStart = true;
invokedHooks.push( "testStart - " + cb.name );
}, 100 );
} );

QUnit.testDone( function() {
QUnit.testDone( function( cb ) {
return timeoutPromiseCallback( function() {
callbackCalled.testStart = false;
callbackCalled.testDone = true;
invokedHooks.push( "testDone - " + cb.name );
}, 100 );
} );
QUnit.moduleDone( function() {
QUnit.moduleDone( function( cb ) {
return timeoutPromiseCallback( function() {
callbackCalled.moduleStart = false;
callbackCalled.moduleDone = true;
invokedHooks.push( "moduleDone - " + cb.name );
}, 100 );
} );
QUnit.done( function() {
return timeoutPromiseCallback( function() {
callbackCalled.done = true;
invokedHooks.push( "done" );
}, 100 );
} );

Expand All @@ -52,19 +61,69 @@ QUnit.done( function() {
done = true;

QUnit.test( "verify callback order", function( assert ) {
assert.ok( callbackCalled.begin );
assert.notOk( callbackCalled.moduleStart );
assert.notOk( callbackCalled.testStart );
assert.ok( callbackCalled.testDone );
assert.ok( callbackCalled.moduleDone );
assert.ok( callbackCalled.done );
assert.deepEqual( invokedHooks, [
"begin",
"begin2",
"moduleStart",
"moduleStart",
"testStart - test1",
"testDone - test1",
"moduleDone - module1 > nestedModule1",
"testStart - test2",
"testDone - test2",
"moduleStart",
"testStart - test3",
"testDone - test3",
"moduleDone - module1 > nestedModule2",
"moduleDone - module1",
"done",
"moduleStart",
"testStart - verify callback order"
] );
} );
} );

QUnit.module( "module1", function() {
QUnit.test( "test1", function( assert ) {
assert.ok( callbackCalled.begin );
assert.ok( callbackCalled.moduleStart );
assert.ok( callbackCalled.testStart );
QUnit.module( "nestedModule1", function() {
QUnit.test( "test1", function( assert ) {
assert.deepEqual( invokedHooks, [
"begin",
"begin2",
"moduleStart",
"moduleStart",
"testStart - test1"
] );
} );
} );

QUnit.test( "test2", function( assert ) {
assert.deepEqual( invokedHooks, [
"begin",
"begin2",
"moduleStart",
"moduleStart",
"testStart - test1",
"testDone - test1",
"moduleDone - module1 > nestedModule1",
"testStart - test2"
] );
} );

QUnit.module( "nestedModule2", function() {
QUnit.test( "test3", function( assert ) {
assert.deepEqual( invokedHooks, [
"begin",
"begin2",
"moduleStart",
"moduleStart",
"testStart - test1",
"testDone - test1",
"moduleDone - module1 > nestedModule1",
"testStart - test2",
"testDone - test2",
"moduleStart",
"testStart - test3"
] );
} );
} );
} );

0 comments on commit f3f43e3

Please sign in to comment.