2013-11-21 22:03:19 +04:00
/* -*- js-indent-level: 2; tab-width: 2; indent-tabs-mode: nil -*- */
2009-02-26 01:27:32 +03:00
// Test timeout (seconds)
2014-03-24 03:55:34 +04:00
var gTimeoutSeconds = 45 ;
2011-06-08 20:04:14 +04:00
var gConfig ;
2009-02-26 01:27:32 +03:00
2008-02-29 00:36:30 +03:00
if ( Cc === undefined ) {
var Cc = Components . classes ;
2014-12-24 00:17:42 +03:00
}
if ( Ci === undefined ) {
2008-02-29 00:36:30 +03:00
var Ci = Components . interfaces ;
2014-12-24 00:17:42 +03:00
}
if ( Cu === undefined ) {
2011-07-14 02:34:01 +04:00
var Cu = Components . utils ;
2008-02-29 00:36:30 +03:00
}
2012-08-03 14:36:59 +04:00
Cu . import ( "resource://gre/modules/XPCOMUtils.jsm" ) ;
2014-04-27 01:05:46 +04:00
Cu . import ( "resource://gre/modules/Task.jsm" ) ;
2015-04-07 11:09:49 +03:00
Cu . import ( "resource://gre/modules/AppConstants.jsm" ) ;
2012-08-03 14:36:59 +04:00
XPCOMUtils . defineLazyModuleGetter ( this , "Services" ,
"resource://gre/modules/Services.jsm" ) ;
2014-02-28 23:55:37 +04:00
XPCOMUtils . defineLazyModuleGetter ( this , "CustomizationTabPreloader" ,
2014-08-21 13:18:54 +04:00
"resource:///modules/CustomizationTabPreloader.jsm" ) ;
XPCOMUtils . defineLazyModuleGetter ( this , "ContentSearch" ,
"resource:///modules/ContentSearch.jsm" ) ;
2014-02-28 23:55:37 +04:00
2015-01-09 13:18:00 +03:00
XPCOMUtils . defineLazyModuleGetter ( this , "SelfSupportBackend" ,
"resource:///modules/SelfSupportBackend.jsm" ) ;
2014-04-11 19:57:59 +04:00
const SIMPLETEST _OVERRIDES =
2014-09-03 19:25:18 +04:00
[ "ok" , "is" , "isnot" , "ise" , "todo" , "todo_is" , "todo_isnot" , "info" , "expectAssertions" , "requestCompleteLog" ] ;
2014-04-11 19:57:59 +04:00
2013-07-28 04:32:40 +04:00
window . addEventListener ( "load" , function testOnLoad ( ) {
window . removeEventListener ( "load" , testOnLoad ) ;
window . addEventListener ( "MozAfterPaint" , function testOnMozAfterPaint ( ) {
window . removeEventListener ( "MozAfterPaint" , testOnMozAfterPaint ) ;
setTimeout ( testInit , 0 ) ;
} ) ;
} ) ;
2008-09-15 16:38:09 +04:00
2014-12-24 00:17:42 +03:00
function b2gStart ( ) {
let homescreen = document . getElementById ( 'systemapp' ) ;
var webNav = homescreen . contentWindow . QueryInterface ( Ci . nsIInterfaceRequestor )
. getInterface ( Ci . nsIWebNavigation ) ;
var url = "chrome://mochikit/content/harness.xul?manifestFile=tests.json" ;
webNav . loadURI ( url , null , null , null , null ) ;
}
2015-02-27 08:35:26 +03:00
let TabDestroyObserver = {
outstanding : new Set ( ) ,
promiseResolver : null ,
init : function ( ) {
Services . obs . addObserver ( this , "message-manager-close" , false ) ;
Services . obs . addObserver ( this , "message-manager-disconnect" , false ) ;
} ,
destroy : function ( ) {
Services . obs . removeObserver ( this , "message-manager-close" ) ;
Services . obs . removeObserver ( this , "message-manager-disconnect" ) ;
} ,
observe : function ( subject , topic , data ) {
if ( topic == "message-manager-close" ) {
this . outstanding . add ( subject ) ;
} else if ( topic == "message-manager-disconnect" ) {
this . outstanding . delete ( subject ) ;
if ( ! this . outstanding . size && this . promiseResolver ) {
this . promiseResolver ( ) ;
}
}
} ,
wait : function ( ) {
if ( ! this . outstanding . size ) {
return Promise . resolve ( ) ;
}
return new Promise ( ( resolve ) => {
this . promiseResolver = resolve ;
} ) ;
} ,
} ;
2013-07-28 04:32:40 +04:00
function testInit ( ) {
2011-05-17 21:10:37 +04:00
gConfig = readConfig ( ) ;
2013-02-13 00:51:24 +04:00
if ( gConfig . testRoot == "browser" ||
gConfig . testRoot == "metro" ||
gConfig . testRoot == "webapprtChrome" ) {
2011-10-06 18:51:03 +04:00
// Make sure to launch the test harness for the first opened window only
2012-08-03 14:36:59 +04:00
var prefs = Services . prefs ;
2011-10-06 18:51:03 +04:00
if ( prefs . prefHasUserValue ( "testing.browserTestHarness.running" ) )
return ;
prefs . setBoolPref ( "testing.browserTestHarness.running" , true ) ;
2013-02-19 22:27:28 +04:00
if ( prefs . prefHasUserValue ( "testing.browserTestHarness.timeout" ) )
gTimeoutSeconds = prefs . getIntPref ( "testing.browserTestHarness.timeout" ) ;
2011-05-17 21:10:37 +04:00
var sstring = Cc [ "@mozilla.org/supports-string;1" ] .
createInstance ( Ci . nsISupportsString ) ;
sstring . data = location . search ;
2012-08-03 14:36:59 +04:00
Services . ww . openWindow ( window , "chrome://mochikit/content/browser-harness.xul" , "browserTest" ,
"chrome,centerscreen,dialog=no,resizable,titlebar,toolbar=no,width=800,height=600" , sstring ) ;
2011-10-06 18:51:03 +04:00
} else {
// This code allows us to redirect without requiring specialpowers for chrome and a11y tests.
2013-07-19 17:51:34 +04:00
let messageHandler = function ( m ) {
2011-10-06 18:51:03 +04:00
messageManager . removeMessageListener ( "chromeEvent" , messageHandler ) ;
var url = m . json . data ;
2014-07-17 11:02:00 +04:00
// Window is the [ChromeWindow] for messageManager, so we need content.window
2011-10-06 18:51:03 +04:00
// Currently chrome tests are run in a content window instead of a ChromeWindow
var webNav = content . window . QueryInterface ( Components . interfaces . nsIInterfaceRequestor )
. getInterface ( Components . interfaces . nsIWebNavigation ) ;
webNav . loadURI ( url , null , null , null , null ) ;
2013-07-19 17:51:34 +04:00
} ;
2011-10-06 18:51:03 +04:00
2014-03-21 08:00:00 +04:00
var listener = 'data:,function doLoad(e) { var data=e.detail&&e.detail.data;removeEventListener("contentEvent", function (e) { doLoad(e); }, false, true);sendAsyncMessage("chromeEvent", {"data":data}); };addEventListener("contentEvent", function (e) { doLoad(e); }, false, true);' ;
2011-10-06 18:51:03 +04:00
messageManager . loadFrameScript ( listener , true ) ;
messageManager . addMessageListener ( "chromeEvent" , messageHandler ) ;
2011-05-17 21:10:37 +04:00
}
2014-03-03 02:33:30 +04:00
if ( gConfig . e10s ) {
e10s _init ( ) ;
2015-02-25 00:00:54 +03:00
let globalMM = Cc [ "@mozilla.org/globalmessagemanager;1" ]
. getService ( Ci . nsIMessageListenerManager ) ;
globalMM . loadFrameScript ( "chrome://mochikit/content/shutdown-leaks-collector.js" , true ) ;
} else {
// In non-e10s, only run the ShutdownLeaksCollector in the parent process.
Components . utils . import ( "chrome://mochikit/content/ShutdownLeaksCollector.jsm" ) ;
2014-03-03 02:33:30 +04:00
}
2015-04-16 22:38:13 +03:00
let gmm = Cc [ "@mozilla.org/globalmessagemanager;1" ] . getService ( Ci . nsIMessageListenerManager ) ;
gmm . loadFrameScript ( "chrome://mochikit/content/tests/SimpleTest/AsyncUtilsContent.js" , true ) ;
2007-07-09 20:24:15 +04:00
}
2009-06-11 21:47:37 +04:00
function Tester ( aTests , aDumper , aCallback ) {
this . dumper = aDumper ;
2007-07-09 20:24:15 +04:00
this . tests = aTests ;
this . callback = aCallback ;
2012-08-03 14:36:59 +04:00
this . openedWindows = { } ;
this . openedURLs = { } ;
this . _scriptLoader = Services . scriptloader ;
2015-03-18 15:38:25 +03:00
this . EventUtils = { } ;
2010-01-21 15:05:27 +03:00
this . _scriptLoader . loadSubScript ( "chrome://mochikit/content/tests/SimpleTest/EventUtils.js" , this . EventUtils ) ;
2009-09-22 13:16:03 +04:00
var simpleTestScope = { } ;
2011-10-06 18:51:03 +04:00
this . _scriptLoader . loadSubScript ( "chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js" , simpleTestScope ) ;
this . _scriptLoader . loadSubScript ( "chrome://mochikit/content/tests/SimpleTest/SpecialPowersObserverAPI.js" , simpleTestScope ) ;
this . _scriptLoader . loadSubScript ( "chrome://mochikit/content/tests/SimpleTest/ChromePowers.js" , simpleTestScope ) ;
2010-01-21 15:05:27 +03:00
this . _scriptLoader . loadSubScript ( "chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" , simpleTestScope ) ;
2013-11-21 00:08:07 +04:00
this . _scriptLoader . loadSubScript ( "chrome://mochikit/content/tests/SimpleTest/MemoryStats.js" , simpleTestScope ) ;
2010-08-24 21:29:34 +04:00
this . _scriptLoader . loadSubScript ( "chrome://mochikit/content/chrome-harness.js" , simpleTestScope ) ;
2009-09-22 13:16:03 +04:00
this . SimpleTest = simpleTestScope . SimpleTest ;
2014-05-22 23:09:21 +04:00
this . SimpleTest . harnessParameters = gConfig ;
2013-11-21 00:08:07 +04:00
this . MemoryStats = simpleTestScope . MemoryStats ;
2014-04-27 01:05:46 +04:00
this . Task = Task ;
2015-02-12 01:28:44 +03:00
this . ContentTask = Components . utils . import ( "resource://testing-common/ContentTask.jsm" , null ) . ContentTask ;
2015-02-23 07:11:22 +03:00
this . BrowserTestUtils = Components . utils . import ( "resource://testing-common/BrowserTestUtils.jsm" , null ) . BrowserTestUtils ;
this . TestUtils = Components . utils . import ( "resource://testing-common/TestUtils.jsm" , null ) . TestUtils ;
2014-06-09 16:43:00 +04:00
this . Task . Debugging . maintainStack = true ;
2014-04-01 15:45:48 +04:00
this . Promise = Components . utils . import ( "resource://gre/modules/Promise.jsm" , null ) . Promise ;
2013-11-21 14:26:33 +04:00
this . Assert = Components . utils . import ( "resource://testing-common/Assert.jsm" , null ) . Assert ;
2014-04-11 19:57:59 +04:00
this . SimpleTestOriginal = { } ;
SIMPLETEST _OVERRIDES . forEach ( m => {
this . SimpleTestOriginal [ m ] = this . SimpleTest [ m ] ;
} ) ;
2014-04-18 00:23:23 +04:00
2014-10-04 16:11:00 +04:00
this . _toleratedUncaughtRejections = null ;
2014-04-18 00:23:23 +04:00
this . _uncaughtErrorObserver = function ( { message , date , fileName , stack , lineNumber } ) {
2014-05-30 09:48:00 +04:00
let error = message ;
2014-04-18 00:23:23 +04:00
if ( fileName || lineNumber ) {
error = {
fileName : fileName ,
lineNumber : lineNumber ,
2014-05-30 09:48:00 +04:00
message : message ,
2014-04-18 00:23:23 +04:00
toString : function ( ) {
2014-05-30 09:48:00 +04:00
return message ;
2014-04-18 00:23:23 +04:00
}
} ;
}
2014-10-04 16:11:00 +04:00
// We may have a whitelist of rejections we wish to tolerate.
let tolerate = this . _toleratedUncaughtRejections &&
this . _toleratedUncaughtRejections . indexOf ( message ) != - 1 ;
let name = "A promise chain failed to handle a rejection: " ;
if ( tolerate ) {
name = "WARNING: (PLEASE FIX THIS AS PART OF BUG 1077403) " + name ;
}
2014-04-18 00:23:23 +04:00
this . currentTest . addResult (
new testResult (
2014-10-04 16:11:00 +04:00
/*success*/ tolerate ,
/*name*/ name ,
2014-04-18 00:23:23 +04:00
/*error*/ error ,
2014-10-04 16:11:00 +04:00
/*known*/ tolerate ,
2014-04-18 00:23:23 +04:00
/*stack*/ stack ) ) ;
} . bind ( this ) ;
2007-07-09 20:24:15 +04:00
}
Tester . prototype = {
2009-09-22 13:16:03 +04:00
EventUtils : { } ,
SimpleTest : { } ,
2013-06-04 17:35:43 +04:00
Task : null ,
2015-02-12 01:28:44 +03:00
ContentTask : null ,
2013-11-21 14:26:33 +04:00
Assert : null ,
2009-09-22 13:16:03 +04:00
2011-08-30 00:14:33 +04:00
repeat : 0 ,
2013-05-24 23:03:50 +04:00
runUntilFailure : false ,
2007-07-09 20:24:15 +04:00
checker : null ,
currentTestIndex : - 1 ,
2010-08-10 00:54:23 +04:00
lastStartTime : null ,
2012-08-03 14:36:59 +04:00
openedWindows : null ,
2013-09-09 14:21:24 +04:00
lastAssertionCount : 0 ,
2012-08-03 14:36:59 +04:00
2007-07-09 20:24:15 +04:00
get currentTest ( ) {
return this . tests [ this . currentTestIndex ] ;
} ,
get done ( ) {
return this . currentTestIndex == this . tests . length - 1 ;
} ,
2008-07-16 09:43:15 +04:00
2007-07-09 20:24:15 +04:00
start : function Tester _start ( ) {
2015-02-27 08:35:26 +03:00
TabDestroyObserver . init ( ) ;
2011-07-15 01:39:17 +04:00
//if testOnLoad was not called, then gConfig is not defined
2012-02-03 13:10:52 +04:00
if ( ! gConfig )
2011-07-15 01:39:17 +04:00
gConfig = readConfig ( ) ;
2013-05-24 23:03:50 +04:00
if ( gConfig . runUntilFailure )
this . runUntilFailure = true ;
if ( gConfig . repeat )
this . repeat = gConfig . repeat ;
2014-07-17 11:02:00 +04:00
this . dumper . structuredLogger . info ( "*** Start BrowserChrome Test Results ***" ) ;
2012-08-03 14:36:59 +04:00
Services . console . registerListener ( this ) ;
Services . obs . addObserver ( this , "chrome-document-global-created" , false ) ;
Services . obs . addObserver ( this , "content-document-global-created" , false ) ;
2012-02-03 13:10:52 +04:00
this . _globalProperties = Object . keys ( window ) ;
2012-11-02 06:46:56 +04:00
this . _globalPropertyWhitelist = [
"navigator" , "constructor" , "top" ,
"Application" ,
"__SS_tabsToRestore" , "__SSi" ,
"webConsoleCommandController" ,
2012-02-03 20:20:08 +04:00
] ;
2009-06-11 21:47:37 +04:00
2014-04-18 00:23:23 +04:00
this . Promise . Debugging . clearUncaughtErrorObservers ( ) ;
this . Promise . Debugging . addUncaughtErrorObserver ( this . _uncaughtErrorObserver ) ;
2009-01-16 22:19:25 +03:00
if ( this . tests . length )
2010-01-21 15:05:27 +03:00
this . nextTest ( ) ;
2009-01-16 22:19:25 +03:00
else
this . finish ( ) ;
2007-07-09 20:24:15 +04:00
} ,
2010-01-21 15:05:27 +03:00
waitForWindowsState : function Tester _waitForWindowsState ( aCallback ) {
2010-07-16 00:38:03 +04:00
let timedOut = this . currentTest && this . currentTest . timedOut ;
let baseMsg = timedOut ? "Found a {elt} after previous test timed out"
: this . currentTest ? "Found an unexpected {elt} at the end of test run"
: "Found an unexpected {elt}" ;
2014-05-07 17:08:05 +04:00
// Remove stale tabs
2012-08-15 02:27:26 +04:00
if ( this . currentTest && window . gBrowser && gBrowser . tabs . length > 1 ) {
2010-06-22 22:28:32 +04:00
while ( gBrowser . tabs . length > 1 ) {
let lastTab = gBrowser . tabContainer . lastChild ;
2010-07-16 00:38:03 +04:00
let msg = baseMsg . replace ( "{elt}" , "tab" ) +
": " + lastTab . linkedBrowser . currentURI . spec ;
2010-06-22 22:28:32 +04:00
this . currentTest . addResult ( new testResult ( false , msg , "" , false ) ) ;
gBrowser . removeTab ( lastTab ) ;
}
2010-06-14 19:53:43 +04:00
}
2014-05-07 17:08:05 +04:00
// Replace the last tab with a fresh one
if ( window . gBrowser ) {
gBrowser . addTab ( "about:blank" , { skipAnimation : true } ) ;
gBrowser . removeCurrentTab ( ) ;
gBrowser . stop ( ) ;
}
// Remove stale windows
2014-07-17 11:02:00 +04:00
this . dumper . structuredLogger . info ( "checking window state" ) ;
2012-08-03 14:36:59 +04:00
let windowsEnum = Services . wm . getEnumerator ( null ) ;
2010-01-21 15:05:27 +03:00
while ( windowsEnum . hasMoreElements ( ) ) {
let win = windowsEnum . getNext ( ) ;
2011-03-29 00:29:09 +04:00
if ( win != window && ! win . closed &&
win . document . documentElement . getAttribute ( "id" ) != "browserTestHarness" ) {
let type = win . document . documentElement . getAttribute ( "windowtype" ) ;
switch ( type ) {
case "navigator:browser" :
type = "browser window" ;
break ;
case null :
type = "unknown window" ;
break ;
}
let msg = baseMsg . replace ( "{elt}" , type ) ;
2010-07-16 00:38:03 +04:00
if ( this . currentTest )
2010-01-21 15:05:27 +03:00
this . currentTest . addResult ( new testResult ( false , msg , "" , false ) ) ;
else
2014-07-17 11:02:00 +04:00
this . dumper . structuredLogger . testEnd ( "browser-test.js" ,
"FAIL" ,
"PASS" ,
msg ) ;
2010-01-21 15:05:27 +03:00
win . close ( ) ;
}
}
// Make sure the window is raised before each test.
2011-06-10 11:00:45 +04:00
this . SimpleTest . waitForFocus ( aCallback ) ;
2010-01-21 15:05:27 +03:00
} ,
2009-06-11 21:47:37 +04:00
finish : function Tester _finish ( aSkipSummary ) {
2014-04-18 00:23:23 +04:00
this . Promise . Debugging . flushUncaughtErrors ( ) ;
2013-05-24 23:03:50 +04:00
var passCount = this . tests . reduce ( function ( a , f ) a + f . passCount , 0 ) ;
var failCount = this . tests . reduce ( function ( a , f ) a + f . failCount , 0 ) ;
var todoCount = this . tests . reduce ( function ( a , f ) a + f . todoCount , 0 ) ;
2011-08-30 00:14:33 +04:00
if ( this . repeat > 0 ) {
-- this . repeat ;
2011-07-15 01:39:17 +04:00
this . currentTestIndex = - 1 ;
this . nextTest ( ) ;
}
else {
2015-02-27 09:34:03 +03:00
TabDestroyObserver . destroy ( ) ;
2012-08-03 14:36:59 +04:00
Services . console . unregisterListener ( this ) ;
Services . obs . removeObserver ( this , "chrome-document-global-created" ) ;
Services . obs . removeObserver ( this , "content-document-global-created" ) ;
2014-04-18 00:23:23 +04:00
this . Promise . Debugging . clearUncaughtErrorObservers ( ) ;
2014-10-04 16:11:00 +04:00
this . _treatUncaughtRejectionsAsFailures = false ;
2015-03-05 01:00:24 +03:00
// In the main process, we print the ShutdownLeaksCollector message here.
let pid = Cc [ "@mozilla.org/xre/app-info;1" ] . getService ( Ci . nsIXULRuntime ) . processID ;
dump ( "Completed ShutdownLeaks collections in process " + pid + "\n" ) ;
2014-07-17 11:02:00 +04:00
this . dumper . structuredLogger . info ( "TEST-START | Shutdown" ) ;
2014-04-18 00:23:23 +04:00
2011-07-15 01:39:17 +04:00
if ( this . tests . length ) {
2014-07-17 11:02:00 +04:00
this . dumper . structuredLogger . info ( "Browser Chrome Test Summary" ) ;
this . dumper . structuredLogger . info ( "Passed: " + passCount ) ;
this . dumper . structuredLogger . info ( "Failed: " + failCount ) ;
this . dumper . structuredLogger . info ( "Todo: " + todoCount ) ;
2011-07-15 01:39:17 +04:00
} else {
2014-07-17 11:02:00 +04:00
this . dumper . structuredLogger . testEnd ( "browser-test.js" ,
"FAIL" ,
"PASS" ,
"No tests to run. Did you pass an invalid --test-path?" ) ;
2011-07-15 01:39:17 +04:00
}
2014-07-17 11:02:00 +04:00
this . dumper . structuredLogger . info ( "*** End BrowserChrome Test Results ***" ) ;
2011-07-15 01:39:17 +04:00
this . dumper . done ( ) ;
2014-07-17 11:02:00 +04:00
2011-07-15 01:39:17 +04:00
// Tests complete, notify the callback and return
this . callback ( this . tests ) ;
this . callback = null ;
this . tests = null ;
2012-08-03 14:36:59 +04:00
this . openedWindows = null ;
}
} ,
2013-11-05 19:48:36 +04:00
haltTests : function Tester _haltTests ( ) {
// Do not run any further tests
this . currentTestIndex = this . tests . length - 1 ;
this . repeat = 0 ;
} ,
2012-08-03 14:36:59 +04:00
observe : function Tester _observe ( aSubject , aTopic , aData ) {
if ( ! aTopic ) {
this . onConsoleMessage ( aSubject ) ;
} else if ( this . currentTest ) {
this . onDocumentCreated ( aSubject ) ;
2009-06-11 21:47:37 +04:00
}
2007-07-09 20:24:15 +04:00
} ,
2012-08-03 14:36:59 +04:00
onDocumentCreated : function Tester _onDocumentCreated ( aWindow ) {
let utils = aWindow . QueryInterface ( Ci . nsIInterfaceRequestor )
. getInterface ( Ci . nsIDOMWindowUtils ) ;
let outerID = utils . outerWindowID ;
let innerID = utils . currentInnerWindowID ;
if ( ! ( outerID in this . openedWindows ) ) {
this . openedWindows [ outerID ] = this . currentTest ;
}
this . openedWindows [ innerID ] = this . currentTest ;
let url = aWindow . location . href || "about:blank" ;
this . openedURLs [ outerID ] = this . openedURLs [ innerID ] = url ;
} ,
onConsoleMessage : function Tester _onConsoleMessage ( aConsoleMessage ) {
2012-02-15 00:37:45 +04:00
// Ignore empty messages.
if ( ! aConsoleMessage . message )
return ;
2010-08-19 13:35:09 +04:00
try {
var msg = "Console message: " + aConsoleMessage . message ;
if ( this . currentTest )
this . currentTest . addResult ( new testMessage ( msg ) ) ;
else
2011-11-12 14:52:39 +04:00
this . dumper . dump ( "TEST-INFO | (browser-test.js) | " + msg . replace ( /\n$/ , "" ) + "\n" ) ;
2010-08-19 13:35:09 +04:00
} catch ( ex ) {
// Swallow exception so we don't lead to another error being reported,
// throwing us into an infinite loop
}
2009-07-03 11:22:31 +04:00
} ,
2014-04-27 01:05:46 +04:00
nextTest : Task . async ( function * ( ) {
2009-11-24 00:32:27 +03:00
if ( this . currentTest ) {
2014-10-04 16:11:00 +04:00
this . Promise . Debugging . flushUncaughtErrors ( ) ;
2009-11-24 00:32:27 +03:00
// Run cleanup functions for the current test before moving on to the
// next one.
let testScope = this . currentTest . scope ;
while ( testScope . _ _cleanupFunctions . length > 0 ) {
let func = testScope . _ _cleanupFunctions . shift ( ) ;
2010-10-30 22:59:18 +04:00
try {
2014-04-24 18:34:00 +04:00
yield func . apply ( testScope ) ;
2010-10-30 22:59:18 +04:00
}
catch ( ex ) {
this . currentTest . addResult ( new testResult ( false , "Cleanup function threw an exception" , ex , false ) ) ;
}
2014-11-05 19:00:52 +03:00
}
if ( this . currentTest . passCount === 0 &&
this . currentTest . failCount === 0 &&
this . currentTest . todoCount === 0 ) {
this . currentTest . addResult ( new testResult ( false , "This test contains no passes, no fails and no todos. Maybe it threw a silent exception? Make sure you use waitForExplicitFinish() if you need it." , "" , false ) ) ;
}
2010-08-24 02:44:07 +04:00
2014-10-17 18:01:15 +04:00
if ( testScope . _ _expected == 'fail' && testScope . _ _num _failed <= 0 ) {
2015-01-12 17:31:40 +03:00
this . currentTest . addResult ( new testResult ( false , "We expected at least one assertion to fail because this test file was marked as fail-if in the manifest!" , "" , true ) ) ;
2014-10-17 18:01:15 +04:00
}
2014-04-18 00:23:23 +04:00
this . Promise . Debugging . flushUncaughtErrors ( ) ;
2013-03-16 01:40:37 +04:00
let winUtils = window . QueryInterface ( Ci . nsIInterfaceRequestor )
. getInterface ( Ci . nsIDOMWindowUtils ) ;
if ( winUtils . isTestControllingRefreshes ) {
this . currentTest . addResult ( new testResult ( false , "test left refresh driver under test control" , "" , false ) ) ;
winUtils . restoreNormalRefresh ( ) ;
}
2011-12-02 16:25:03 +04:00
if ( this . SimpleTest . isExpectingUncaughtException ( ) ) {
this . currentTest . addResult ( new testResult ( false , "expectUncaughtException was called but no uncaught exception was detected!" , "" , false ) ) ;
}
2012-02-03 13:10:52 +04:00
Object . keys ( window ) . forEach ( function ( prop ) {
2013-01-17 21:30:36 +04:00
if ( parseInt ( prop ) == prop ) {
// This is a string which when parsed as an integer and then
// stringified gives the original string. As in, this is in fact a
// string representation of an integer, so an index into
// window.frames. Skip those.
return ;
}
2012-02-03 13:10:52 +04:00
if ( this . _globalProperties . indexOf ( prop ) == - 1 ) {
this . _globalProperties . push ( prop ) ;
if ( this . _globalPropertyWhitelist . indexOf ( prop ) == - 1 )
this . currentTest . addResult ( new testResult ( false , "leaked window property: " + prop , "" , false ) ) ;
}
} , this ) ;
2011-02-02 19:00:47 +03:00
// Clear document.popupNode. The test could have set it to a custom value
// for its own purposes, nulling it out it will go back to the default
// behavior of returning the last opened popup.
document . popupNode = null ;
2013-07-19 17:51:34 +04:00
// Notify a long running test problem if it didn't end up in a timeout.
if ( this . currentTest . unexpectedTimeouts && ! this . currentTest . timedOut ) {
let msg = "This test exceeded the timeout threshold. It should be " +
"rewritten or split up. If that's not possible, use " +
"requestLongerTimeout(N), but only as a last resort." ;
this . currentTest . addResult ( new testResult ( false , msg , "" , false ) ) ;
}
2013-09-09 14:21:24 +04:00
// If we're in a debug build, check assertion counts. This code
// is similar to the code in TestRunner.testUnloaded in
// TestRunner.js used for all other types of mochitests.
let debugsvc = Cc [ "@mozilla.org/xpcom/debug;1" ] . getService ( Ci . nsIDebug2 ) ;
if ( debugsvc . isDebugBuild ) {
let newAssertionCount = debugsvc . assertionCount ;
let numAsserts = newAssertionCount - this . lastAssertionCount ;
this . lastAssertionCount = newAssertionCount ;
let max = testScope . _ _expectedMaxAsserts ;
let min = testScope . _ _expectedMinAsserts ;
if ( numAsserts > max ) {
let msg = "Assertion count " + numAsserts +
" is greater than expected range " +
min + "-" + max + " assertions." ;
2013-09-09 14:21:24 +04:00
// TEST-UNEXPECTED-FAIL (TEMPORARILY TEST-KNOWN-FAIL)
//this.currentTest.addResult(new testResult(false, msg, "", false));
this . currentTest . addResult ( new testResult ( true , msg , "" , true ) ) ;
2013-09-09 14:21:24 +04:00
} else if ( numAsserts < min ) {
let msg = "Assertion count " + numAsserts +
" is less than expected range " +
min + "-" + max + " assertions." ;
// TEST-UNEXPECTED-PASS
this . currentTest . addResult ( new testResult ( false , msg , "" , true ) ) ;
} else if ( numAsserts > 0 ) {
let msg = "Assertion count " + numAsserts +
" is within expected range " +
min + "-" + max + " assertions." ;
// TEST-KNOWN-FAIL
this . currentTest . addResult ( new testResult ( true , msg , "" , true ) ) ;
}
}
2013-11-21 00:08:07 +04:00
// Dump memory stats for main thread.
if ( Cc [ "@mozilla.org/xre/runtime;1" ]
. getService ( Ci . nsIXULRuntime )
. processType == Ci . nsIXULRuntime . PROCESS _TYPE _DEFAULT )
{
2015-04-03 18:19:00 +03:00
this . MemoryStats . dump ( this . currentTestIndex ,
2013-11-21 22:03:19 +04:00
this . currentTest . path ,
gConfig . dumpOutputDirectory ,
gConfig . dumpAboutMemoryAfterTest ,
gConfig . dumpDMDAfterTest ) ;
2013-11-21 00:08:07 +04:00
}
2010-08-24 02:44:07 +04:00
// Note the test run time
let time = Date . now ( ) - this . lastStartTime ;
2014-07-17 11:02:00 +04:00
this . dumper . structuredLogger . testEnd ( this . currentTest . path ,
"OK" ,
undefined ,
"finished in " + time + "ms" ) ;
2010-10-19 23:20:49 +04:00
this . currentTest . setDuration ( time ) ;
2011-06-10 11:00:45 +04:00
2013-11-05 19:48:36 +04:00
if ( this . runUntilFailure && this . currentTest . failCount > 0 ) {
this . haltTests ( ) ;
}
2014-04-11 19:57:59 +04:00
// Restore original SimpleTest methods to avoid leaks.
SIMPLETEST _OVERRIDES . forEach ( m => {
this . SimpleTest [ m ] = this . SimpleTestOriginal [ m ] ;
} ) ;
2011-06-10 11:00:45 +04:00
testScope . destroy ( ) ;
this . currentTest . scope = null ;
2009-11-24 00:32:27 +03:00
}
2010-01-21 15:05:27 +03:00
// Check the window state for the current test before moving to the next one.
// This also causes us to check before starting any tests, since nextTest()
// is invoked to start the tests.
2011-06-10 11:00:45 +04:00
this . waitForWindowsState ( ( function ( ) {
if ( this . done ) {
2014-08-21 13:18:54 +04:00
2013-11-21 00:53:08 +04:00
// Uninitialize a few things explicitly so that they can clean up
// frames and browser intentionally kept alive until shutdown to
// eliminate false positives.
if ( gConfig . testRoot == "browser" ) {
2015-04-07 11:09:49 +03:00
//Skip if SeaMonkey
if ( AppConstants . MOZ _APP _NAME != "seamonkey" ) {
// Replace the document currently loaded in the browser's sidebar.
// This will prevent false positives for tests that were the last
// to touch the sidebar. They will thus not be blamed for leaking
// a document.
let sidebar = document . getElementById ( "sidebar" ) ;
sidebar . setAttribute ( "src" , "data:text/html;charset=utf-8," ) ;
sidebar . docShell . createAboutBlankContentViewer ( null ) ;
sidebar . setAttribute ( "src" , "about:blank" ) ;
// Do the same for the social sidebar.
let socialSidebar = document . getElementById ( "social-sidebar-browser" ) ;
socialSidebar . setAttribute ( "src" , "data:text/html;charset=utf-8," ) ;
socialSidebar . docShell . createAboutBlankContentViewer ( null ) ;
socialSidebar . setAttribute ( "src" , "about:blank" ) ;
SelfSupportBackend . uninit ( ) ;
CustomizationTabPreloader . uninit ( ) ;
SocialFlyout . unload ( ) ;
SocialShare . uninit ( ) ;
TabView . uninit ( ) ;
}
2013-11-21 00:53:08 +04:00
// Destroy BackgroundPageThumbs resources.
let { BackgroundPageThumbs } =
Cu . import ( "resource://gre/modules/BackgroundPageThumbs.jsm" , { } ) ;
BackgroundPageThumbs . _destroy ( ) ;
2014-11-19 18:23:49 +03:00
// Destroy preloaded browsers.
if ( gBrowser . _preloadedBrowser ) {
let browser = gBrowser . _preloadedBrowser ;
gBrowser . _preloadedBrowser = null ;
gBrowser . getNotificationBox ( browser ) . remove ( ) ;
}
2013-11-21 00:53:08 +04:00
}
2011-07-25 14:11:31 +04:00
// Schedule GC and CC runs before finishing in order to detect
2014-02-11 14:07:24 +04:00
// DOM windows leaked by our tests or the tested code. Note that we
// use a shrinking GC so that the JS engine will discard JIT code and
// JIT caches more aggressively.
2012-08-03 14:36:59 +04:00
2013-04-24 22:05:17 +04:00
let checkForLeakedGlobalWindows = aCallback => {
2014-02-11 14:07:24 +04:00
Cu . schedulePreciseShrinkingGC ( ( ) => {
2013-04-24 22:05:17 +04:00
let analyzer = new CCAnalyzer ( ) ;
analyzer . run ( ( ) => {
let results = [ ] ;
for ( let obj of analyzer . find ( "nsGlobalWindow " ) ) {
let m = obj . name . match ( /^nsGlobalWindow #(\d+)/ ) ;
if ( m && m [ 1 ] in this . openedWindows )
results . push ( { name : obj . name , url : m [ 1 ] } ) ;
}
aCallback ( results ) ;
} ) ;
} ) ;
} ;
let reportLeaks = aResults => {
for ( let result of aResults ) {
let test = this . openedWindows [ result . url ] ;
let msg = "leaked until shutdown [" + result . name +
" " + ( this . openedURLs [ result . url ] || "NULL" ) + "]" ;
test . addResult ( new testResult ( false , msg , "" , false ) ) ;
}
} ;
2014-08-31 14:13:08 +04:00
let { AsyncShutdown } =
Cu . import ( "resource://gre/modules/AsyncShutdown.jsm" , { } ) ;
let barrier = new AsyncShutdown . Barrier (
"ShutdownLeaks: Wait for cleanup to be finished before checking for leaks" ) ;
Services . obs . notifyObservers ( { wrappedJSObject : barrier } ,
"shutdown-leaks-before-check" , null ) ;
2015-02-27 08:35:26 +03:00
barrier . client . addBlocker ( "ShutdownLeaks: Wait for tabs to finish closing" ,
TabDestroyObserver . wait ( ) ) ;
2014-08-31 14:13:08 +04:00
barrier . wait ( ) . then ( ( ) => {
// Simulate memory pressure so that we're forced to free more resources
// and thus get rid of more false leaks like already terminated workers.
Services . obs . notifyObservers ( null , "memory-pressure" , "heap-minimize" ) ;
2015-02-25 00:00:54 +03:00
let ppmm = Cc [ "@mozilla.org/parentprocessmessagemanager;1" ]
. getService ( Ci . nsIMessageBroadcaster ) ;
ppmm . broadcastAsyncMessage ( "browser-test:collect-request" ) ;
2014-08-21 13:18:54 +04:00
checkForLeakedGlobalWindows ( aResults => {
if ( aResults . length == 0 ) {
2013-04-24 22:05:17 +04:00
this . finish ( ) ;
2014-08-21 13:18:54 +04:00
return ;
}
// After the first check, if there are reported leaked windows, sleep
// for a while, to allow off-main-thread work to complete and free up
// main-thread objects. Then check again.
setTimeout ( ( ) => {
checkForLeakedGlobalWindows ( aResults => {
reportLeaks ( aResults ) ;
this . finish ( ) ;
} ) ;
} , 1000 ) ;
} ) ;
2013-04-24 22:05:17 +04:00
} ) ;
2011-06-10 11:00:45 +04:00
return ;
}
2007-07-09 20:24:15 +04:00
2011-06-10 11:00:45 +04:00
this . currentTestIndex ++ ;
this . execTest ( ) ;
} ) . bind ( this ) ) ;
2014-04-24 18:34:00 +04:00
} ) ,
2007-07-09 20:24:15 +04:00
2010-01-21 15:05:27 +03:00
execTest : function Tester _execTest ( ) {
2014-07-17 11:02:00 +04:00
this . dumper . structuredLogger . testStart ( this . currentTest . path ) ;
2009-06-11 21:47:37 +04:00
2011-12-02 16:25:03 +04:00
this . SimpleTest . reset ( ) ;
2007-07-09 20:24:15 +04:00
// Load the tests into a testscope
2014-10-17 18:01:15 +04:00
let currentScope = this . currentTest . scope = new testScope ( this , this . currentTest , this . currentTest . expected ) ;
2013-11-21 14:26:33 +04:00
let currentTest = this . currentTest ;
2007-07-09 20:24:15 +04:00
2009-09-22 13:16:03 +04:00
// Import utils in the test scope.
this . currentTest . scope . EventUtils = this . EventUtils ;
this . currentTest . scope . SimpleTest = this . SimpleTest ;
2010-09-10 21:20:38 +04:00
this . currentTest . scope . gTestPath = this . currentTest . path ;
2013-06-04 17:35:43 +04:00
this . currentTest . scope . Task = this . Task ;
2015-02-12 01:28:44 +03:00
this . currentTest . scope . ContentTask = this . ContentTask ;
2015-02-23 07:11:22 +03:00
this . currentTest . scope . BrowserTestUtils = this . BrowserTestUtils ;
this . currentTest . scope . TestUtils = this . TestUtils ;
2013-11-21 14:26:33 +04:00
// Pass a custom report function for mochitest style reporting.
this . currentTest . scope . Assert = new this . Assert ( function ( err , message , stack ) {
let res ;
if ( err ) {
res = new testResult ( false , err . message , err . stack , false , err . stack ) ;
} else {
res = new testResult ( true , message , "" , false , stack ) ;
}
currentTest . addResult ( res ) ;
} ) ;
// Allow Assert.jsm methods to be tacked to the current scope.
this . currentTest . scope . export _assertions = function ( ) {
for ( let func in this . Assert ) {
this [ func ] = this . Assert [ func ] . bind ( this . Assert ) ;
}
} ;
2010-09-10 21:20:38 +04:00
2010-03-18 23:11:20 +03:00
// Override SimpleTest methods with ours.
2014-04-11 19:57:59 +04:00
SIMPLETEST _OVERRIDES . forEach ( function ( m ) {
2010-03-18 23:11:20 +03:00
this . SimpleTest [ m ] = this [ m ] ;
} , this . currentTest . scope ) ;
2009-09-22 13:16:03 +04:00
2010-09-10 21:20:38 +04:00
//load the tools to work with chrome .jar and remote
try {
this . _scriptLoader . loadSubScript ( "chrome://mochikit/content/chrome-harness.js" , this . currentTest . scope ) ;
} catch ( ex ) { /* no chrome-harness tools */ }
2009-11-24 00:32:27 +03:00
// Import head.js script if it exists.
var currentTestDirPath =
this . currentTest . path . substr ( 0 , this . currentTest . path . lastIndexOf ( "/" ) ) ;
var headPath = currentTestDirPath + "/head.js" ;
try {
this . _scriptLoader . loadSubScript ( headPath , this . currentTest . scope ) ;
2012-06-15 06:08:17 +04:00
} catch ( ex ) {
// Ignore if no head.js exists, but report all other errors. Note this
// will also ignore an existing head.js attempting to import a missing
// module - see bug 755558 for why this strategy is preferred anyway.
2014-11-09 04:08:09 +03:00
if ( ! /^Error opening input stream/ . test ( ex . toString ( ) ) ) {
2012-06-15 06:08:17 +04:00
this . currentTest . addResult ( new testResult ( false , "head.js import threw an exception" , ex , false ) ) ;
}
}
2009-11-24 00:32:27 +03:00
// Import the test script.
2007-07-09 20:24:15 +04:00
try {
2010-01-21 15:05:27 +03:00
this . _scriptLoader . loadSubScript ( this . currentTest . path ,
this . currentTest . scope ) ;
2014-04-18 00:23:23 +04:00
this . Promise . Debugging . flushUncaughtErrors ( ) ;
2007-07-16 04:21:11 +04:00
// Run the test
2010-08-10 00:54:23 +04:00
this . lastStartTime = Date . now ( ) ;
2013-06-04 17:35:43 +04:00
if ( this . currentTest . scope . _ _tasks ) {
// This test consists of tasks, added via the `add_task()` API.
if ( "test" in this . currentTest . scope ) {
throw "Cannot run both a add_task test and a normal test at the same time." ;
}
2014-10-17 18:17:37 +04:00
let Promise = this . Promise ;
this . Task . spawn ( function * ( ) {
2013-06-04 17:35:43 +04:00
let task ;
while ( ( task = this . _ _tasks . shift ( ) ) ) {
this . SimpleTest . info ( "Entering test " + task . name ) ;
try {
yield task ( ) ;
} catch ( ex ) {
let isExpected = ! ! this . SimpleTest . isExpectingUncaughtException ( ) ;
let stack = ( typeof ex == "object" && "stack" in ex ) ? ex . stack : null ;
let name = "Uncaught exception" ;
let result = new testResult ( isExpected , name , ex , false , stack ) ;
currentTest . addResult ( result ) ;
}
2014-10-17 18:17:37 +04:00
Promise . Debugging . flushUncaughtErrors ( ) ;
2013-06-04 17:35:43 +04:00
this . SimpleTest . info ( "Leaving test " + task . name ) ;
}
this . finish ( ) ;
2013-11-21 14:26:33 +04:00
} . bind ( currentScope ) ) ;
2013-06-04 17:35:43 +04:00
} else if ( "generatorTest" in this . currentTest . scope ) {
if ( "test" in this . currentTest . scope ) {
2011-03-30 03:41:29 +04:00
throw "Cannot run both a generator test and a normal test at the same time." ;
2013-06-04 17:35:43 +04:00
}
2011-03-30 03:41:29 +04:00
// This test is a generator. It will not finish immediately.
this . currentTest . scope . waitForExplicitFinish ( ) ;
var result = this . currentTest . scope . generatorTest ( ) ;
this . currentTest . scope . _ _generator = result ;
result . next ( ) ;
} else {
this . currentTest . scope . test ( ) ;
}
2007-07-09 20:24:15 +04:00
} catch ( ex ) {
2013-06-04 17:35:43 +04:00
let isExpected = ! ! this . SimpleTest . isExpectingUncaughtException ( ) ;
2011-12-02 16:25:03 +04:00
if ( ! this . SimpleTest . isIgnoringAllUncaughtExceptions ( ) ) {
this . currentTest . addResult ( new testResult ( isExpected , "Exception thrown" , ex , false ) ) ;
this . SimpleTest . expectUncaughtException ( false ) ;
} else {
this . currentTest . addResult ( new testMessage ( "Exception thrown: " + ex ) ) ;
}
2011-03-30 03:41:29 +04:00
this . currentTest . scope . finish ( ) ;
2007-07-09 20:24:15 +04:00
}
2009-02-26 01:27:32 +03:00
// If the test ran synchronously, move to the next test, otherwise the test
// will trigger the next test when it is done.
2009-06-11 21:47:37 +04:00
if ( this . currentTest . scope . _ _done ) {
2010-01-21 15:05:27 +03:00
this . nextTest ( ) ;
2009-02-26 01:27:32 +03:00
}
else {
2007-07-09 20:24:15 +04:00
var self = this ;
2014-09-30 00:05:23 +04:00
var timeoutExpires = Date . now ( ) + gTimeoutSeconds * 1000 ;
var waitUntilAtLeast = timeoutExpires - 1000 ;
2015-01-06 23:03:35 +03:00
this . currentTest . scope . _ _waitTimer =
this . SimpleTest . _originalSetTimeout . apply ( window , [ function timeoutFn ( ) {
2014-09-30 00:05:23 +04:00
// We sometimes get woken up long before the gTimeoutSeconds
// have elapsed (when running in chaos mode for example). This
// code ensures that we don't wrongly time out in that case.
if ( Date . now ( ) < waitUntilAtLeast ) {
self . currentTest . scope . _ _waitTimer =
setTimeout ( timeoutFn , timeoutExpires - Date . now ( ) ) ;
return ;
}
2009-12-04 17:13:22 +03:00
if ( -- self . currentTest . scope . _ _timeoutFactor > 0 ) {
// We were asked to wait a bit longer.
self . currentTest . scope . info (
"Longer timeout required, waiting longer... Remaining timeouts: " +
self . currentTest . scope . _ _timeoutFactor ) ;
self . currentTest . scope . _ _waitTimer =
2013-07-19 17:51:34 +04:00
setTimeout ( timeoutFn , gTimeoutSeconds * 1000 ) ;
2009-12-04 17:13:22 +03:00
return ;
}
2013-07-19 17:51:34 +04:00
// If the test is taking longer than expected, but it's not hanging,
// mark the fact, but let the test continue. At the end of the test,
// if it didn't timeout, we will notify the problem through an error.
// To figure whether it's an actual hang, compare the time of the last
// result or message to half of the timeout time.
// Though, to protect against infinite loops, limit the number of times
// we allow the test to proceed.
const MAX _UNEXPECTED _TIMEOUTS = 10 ;
if ( Date . now ( ) - self . currentTest . lastOutputTime < ( gTimeoutSeconds / 2 ) * 1000 &&
++ self . currentTest . unexpectedTimeouts <= MAX _UNEXPECTED _TIMEOUTS ) {
self . currentTest . scope . _ _waitTimer =
setTimeout ( timeoutFn , gTimeoutSeconds * 1000 ) ;
return ;
}
2014-08-15 22:42:00 +04:00
self . currentTest . addResult ( new testResult ( false , "Test timed out" , null , false ) ) ;
2010-07-16 00:38:03 +04:00
self . currentTest . timedOut = true ;
2009-06-11 21:47:37 +04:00
self . currentTest . scope . _ _waitTimer = null ;
2010-01-21 15:05:27 +03:00
self . nextTest ( ) ;
2015-01-06 23:03:35 +03:00
} , gTimeoutSeconds * 1000 ] ) ;
2007-07-09 20:24:15 +04:00
}
2009-07-03 11:22:31 +04:00
} ,
QueryInterface : function ( aIID ) {
if ( aIID . equals ( Ci . nsIConsoleListener ) ||
aIID . equals ( Ci . nsISupports ) )
return this ;
throw Components . results . NS _ERROR _NO _INTERFACE ;
2007-07-09 20:24:15 +04:00
}
} ;
2010-09-22 12:58:42 +04:00
function testResult ( aCondition , aName , aDiag , aIsTodo , aStack ) {
2014-08-15 22:42:00 +04:00
this . name = aName ;
this . msg = "" ;
2007-07-09 20:24:15 +04:00
2009-07-03 11:22:31 +04:00
this . info = false ;
2007-07-09 20:24:15 +04:00
this . pass = ! ! aCondition ;
2007-07-12 18:29:33 +04:00
this . todo = aIsTodo ;
2009-07-03 11:22:31 +04:00
2007-07-12 18:29:33 +04:00
if ( this . pass ) {
2014-07-17 11:02:00 +04:00
if ( aIsTodo ) {
this . status = "FAIL" ;
this . expected = "FAIL" ;
} else {
this . status = "PASS" ;
this . expected = "PASS" ;
}
2007-07-12 18:29:33 +04:00
} else {
2009-07-10 23:34:04 +04:00
if ( aDiag ) {
if ( typeof aDiag == "object" && "fileName" in aDiag ) {
// we have an exception - print filename and linenumber information
2014-08-15 22:42:00 +04:00
this . msg += "at " + aDiag . fileName + ":" + aDiag . lineNumber + " - " ;
2009-07-10 23:34:04 +04:00
}
2014-08-15 22:42:00 +04:00
this . msg += String ( aDiag ) ;
2009-07-10 23:34:04 +04:00
}
2010-09-22 12:58:42 +04:00
if ( aStack ) {
this . msg += "\nStack trace:\n" ;
2014-06-20 22:23:00 +04:00
let normalized ;
if ( aStack instanceof Components . interfaces . nsIStackFrame ) {
let frames = [ ] ;
for ( let frame = aStack ; frame ; frame = frame . caller ) {
frames . push ( frame . filename + ":" + frame . name + ":" + frame . lineNumber ) ;
}
normalized = frames . join ( "\n" ) ;
} else {
normalized = "" + aStack ;
2010-09-22 12:58:42 +04:00
}
2014-06-20 22:23:00 +04:00
this . msg += Task . Debugging . generateReadableStack ( normalized , " " ) ;
2010-09-22 12:58:42 +04:00
}
2014-07-17 11:02:00 +04:00
if ( aIsTodo ) {
this . status = "PASS" ;
this . expected = "FAIL" ;
} else {
this . status = "FAIL" ;
this . expected = "PASS" ;
}
2013-11-01 23:23:34 +04:00
if ( gConfig . debugOnFailure ) {
// You've hit this line because you requested to break into the
// debugger upon a testcase failure on your test run.
debugger ;
}
2007-07-09 20:24:15 +04:00
}
}
2009-07-03 11:22:31 +04:00
function testMessage ( aName ) {
this . msg = aName || "" ;
this . info = true ;
}
2009-06-11 21:47:37 +04:00
// Need to be careful adding properties to this object, since its properties
// cannot conflict with global variables used in tests.
2014-10-17 18:01:15 +04:00
function testScope ( aTester , aTest , expected ) {
2009-06-11 21:47:37 +04:00
this . _ _tester = aTester ;
2014-10-17 18:01:15 +04:00
this . _ _expected = expected ;
this . _ _num _failed = 0 ;
2008-01-26 00:29:11 +03:00
var self = this ;
2010-09-22 12:58:42 +04:00
this . ok = function test _ok ( condition , name , diag , stack ) {
2014-10-17 18:01:15 +04:00
if ( this . _ _expected == 'fail' ) {
if ( ! condition ) {
this . _ _num _failed ++ ;
condition = true ;
}
}
2011-12-02 16:25:03 +04:00
aTest . addResult ( new testResult ( condition , name , diag , false ,
stack ? stack : Components . stack . caller ) ) ;
2008-07-16 09:43:15 +04:00
} ;
2008-01-26 00:29:11 +03:00
this . is = function test _is ( a , b , name ) {
2010-09-22 12:58:42 +04:00
self . ok ( a == b , name , "Got " + a + ", expected " + b , false ,
Components . stack . caller ) ;
2008-07-16 09:43:15 +04:00
} ;
2008-01-26 00:29:11 +03:00
this . isnot = function test _isnot ( a , b , name ) {
2010-09-22 12:58:42 +04:00
self . ok ( a != b , name , "Didn't expect " + a + ", but got it" , false ,
Components . stack . caller ) ;
2008-07-16 09:43:15 +04:00
} ;
2012-05-11 01:46:15 +04:00
this . ise = function test _ise ( a , b , name ) {
self . ok ( a === b , name , "Got " + a + ", strictly expected " + b , false ,
Components . stack . caller ) ;
} ;
2010-09-22 12:58:42 +04:00
this . todo = function test _todo ( condition , name , diag , stack ) {
2011-12-02 16:25:03 +04:00
aTest . addResult ( new testResult ( ! condition , name , diag , true ,
stack ? stack : Components . stack . caller ) ) ;
2008-07-16 09:43:15 +04:00
} ;
2008-01-26 02:33:17 +03:00
this . todo _is = function test _todo _is ( a , b , name ) {
2010-09-22 12:58:42 +04:00
self . todo ( a == b , name , "Got " + a + ", expected " + b ,
Components . stack . caller ) ;
2008-07-16 09:43:15 +04:00
} ;
2008-01-26 02:33:17 +03:00
this . todo _isnot = function test _todo _isnot ( a , b , name ) {
2010-09-22 12:58:42 +04:00
self . todo ( a != b , name , "Didn't expect " + a + ", but got it" ,
Components . stack . caller ) ;
2008-07-16 09:43:15 +04:00
} ;
2009-07-03 11:22:31 +04:00
this . info = function test _info ( name ) {
2011-12-02 16:25:03 +04:00
aTest . addResult ( new testMessage ( name ) ) ;
2009-07-03 11:22:31 +04:00
} ;
2008-01-26 02:33:17 +03:00
2008-10-30 20:22:17 +03:00
this . executeSoon = function test _executeSoon ( func ) {
2012-08-03 14:36:59 +04:00
Services . tm . mainThread . dispatch ( {
2008-10-30 20:22:17 +03:00
run : function ( ) {
func ( ) ;
}
} , Ci . nsIThread . DISPATCH _NORMAL ) ;
} ;
2011-03-30 03:41:29 +04:00
this . nextStep = function test _nextStep ( arg ) {
if ( self . _ _done ) {
aTest . addResult ( new testResult ( false , "nextStep was called too many times" , "" , false ) ) ;
return ;
}
if ( ! self . _ _generator ) {
aTest . addResult ( new testResult ( false , "nextStep called with no generator" , "" , false ) ) ;
self . finish ( ) ;
return ;
}
try {
self . _ _generator . send ( arg ) ;
} catch ( ex if ex instanceof StopIteration ) {
// StopIteration means test is finished.
self . finish ( ) ;
} catch ( ex ) {
2011-12-02 16:25:03 +04:00
var isExpected = ! ! self . SimpleTest . isExpectingUncaughtException ( ) ;
if ( ! self . SimpleTest . isIgnoringAllUncaughtExceptions ( ) ) {
aTest . addResult ( new testResult ( isExpected , "Exception thrown" , ex , false ) ) ;
self . SimpleTest . expectUncaughtException ( false ) ;
} else {
aTest . addResult ( new testMessage ( "Exception thrown: " + ex ) ) ;
}
2011-03-30 03:41:29 +04:00
self . finish ( ) ;
}
} ;
2009-11-24 00:32:27 +03:00
this . waitForExplicitFinish = function test _waitForExplicitFinish ( ) {
2009-06-11 21:47:37 +04:00
self . _ _done = false ;
2008-07-16 09:43:15 +04:00
} ;
2009-06-11 21:47:37 +04:00
2010-05-18 17:31:03 +04:00
this . waitForFocus = function test _waitForFocus ( callback , targetWindow , expectBlankPage ) {
self . SimpleTest . waitForFocus ( callback , targetWindow , expectBlankPage ) ;
2009-09-22 13:16:03 +04:00
} ;
2011-04-23 04:04:15 +04:00
this . waitForClipboard = function test _waitForClipboard ( expected , setup , success , failure , flavor ) {
self . SimpleTest . waitForClipboard ( expected , setup , success , failure , flavor ) ;
2010-06-03 00:38:32 +04:00
} ;
2009-11-24 00:32:27 +03:00
this . registerCleanupFunction = function test _registerCleanupFunction ( aFunction ) {
self . _ _cleanupFunctions . push ( aFunction ) ;
} ;
2009-12-04 17:13:22 +03:00
this . requestLongerTimeout = function test _requestLongerTimeout ( aFactor ) {
self . _ _timeoutFactor = aFactor ;
} ;
2010-09-10 21:20:38 +04:00
this . copyToProfile = function test _copyToProfile ( filename ) {
self . SimpleTest . copyToProfile ( filename ) ;
} ;
2011-12-02 16:25:03 +04:00
this . expectUncaughtException = function test _expectUncaughtException ( aExpecting ) {
self . SimpleTest . expectUncaughtException ( aExpecting ) ;
2011-07-15 06:43:09 +04:00
} ;
2011-12-02 16:25:03 +04:00
this . ignoreAllUncaughtExceptions = function test _ignoreAllUncaughtExceptions ( aIgnoring ) {
self . SimpleTest . ignoreAllUncaughtExceptions ( aIgnoring ) ;
2011-11-12 15:06:54 +04:00
} ;
2014-10-04 16:11:00 +04:00
this . thisTestLeaksUncaughtRejectionsAndShouldBeFixed = function ( ... rejections ) {
if ( ! aTester . _toleratedUncaughtRejections ) {
aTester . _toleratedUncaughtRejections = [ ] ;
}
aTester . _toleratedUncaughtRejections . push ( ... rejections ) ;
} ;
2013-09-09 14:21:24 +04:00
this . expectAssertions = function test _expectAssertions ( aMin , aMax ) {
let min = aMin ;
let max = aMax ;
if ( typeof ( max ) == "undefined" ) {
max = min ;
}
if ( typeof ( min ) != "number" || typeof ( max ) != "number" ||
min < 0 || max < min ) {
throw "bad parameter to expectAssertions" ;
}
self . _ _expectedMinAsserts = min ;
self . _ _expectedMaxAsserts = max ;
} ;
2014-10-17 18:01:15 +04:00
this . setExpected = function test _setExpected ( ) {
self . _ _expected = 'fail' ;
} ;
2008-01-26 00:29:11 +03:00
this . finish = function test _finish ( ) {
2009-06-11 21:47:37 +04:00
self . _ _done = true ;
if ( self . _ _waitTimer ) {
2009-02-26 01:27:32 +03:00
self . executeSoon ( function ( ) {
2009-06-11 21:47:37 +04:00
if ( self . _ _done && self . _ _waitTimer ) {
clearTimeout ( self . _ _waitTimer ) ;
self . _ _waitTimer = null ;
2010-01-21 15:05:27 +03:00
self . _ _tester . nextTest ( ) ;
2009-02-26 01:27:32 +03:00
}
} ) ;
}
2008-07-16 09:43:15 +04:00
} ;
2014-09-03 19:25:18 +04:00
this . requestCompleteLog = function test _requestCompleteLog ( ) {
self . _ _tester . dumper . structuredLogger . deactivateBuffering ( ) ;
self . registerCleanupFunction ( function ( ) {
self . _ _tester . dumper . structuredLogger . activateBuffering ( ) ;
} )
} ;
2007-07-09 20:24:15 +04:00
}
testScope . prototype = {
2009-06-11 21:47:37 +04:00
_ _done : true ,
2011-03-30 03:41:29 +04:00
_ _generator : null ,
2013-06-04 17:35:43 +04:00
_ _tasks : null ,
2009-06-11 21:47:37 +04:00
_ _waitTimer : null ,
2009-11-24 00:32:27 +03:00
_ _cleanupFunctions : [ ] ,
2009-12-04 17:13:22 +03:00
_ _timeoutFactor : 1 ,
2013-09-09 14:21:24 +04:00
_ _expectedMinAsserts : 0 ,
_ _expectedMaxAsserts : 0 ,
2014-10-17 18:01:15 +04:00
_ _expected : 'pass' ,
2007-07-09 20:24:15 +04:00
2009-09-22 13:16:03 +04:00
EventUtils : { } ,
2011-06-10 11:00:45 +04:00
SimpleTest : { } ,
2013-06-04 17:35:43 +04:00
Task : null ,
2015-02-12 01:28:44 +03:00
ContentTask : null ,
2015-02-23 07:11:22 +03:00
BrowserTestUtils : null ,
TestUtils : null ,
2013-11-21 14:26:33 +04:00
Assert : null ,
2013-06-04 17:35:43 +04:00
/ * *
* Add a test function which is a Task function .
*
* Task functions are functions fed into Task . jsm ' s Task . spawn ( ) . They are
* generators that emit promises .
*
* If an exception is thrown , an assertion fails , or if a rejected
* promise is yielded , the test function aborts immediately and the test is
* reported as a failure . Execution continues with the next test function .
*
* To trigger premature ( but successful ) termination of the function , simply
* return or throw a Task . Result instance .
*
* Example usage :
*
* add _task ( function test ( ) {
* let result = yield Promise . resolve ( true ) ;
*
* ok ( result ) ;
*
* let secondary = yield someFunctionThatReturnsAPromise ( result ) ;
* is ( secondary , "expected value" ) ;
* } ) ;
*
* add _task ( function test _early _return ( ) {
* let result = yield somethingThatReturnsAPromise ( ) ;
*
* if ( ! result ) {
* // Test is ended immediately, with success.
* return ;
* }
*
* is ( result , "foo" ) ;
* } ) ;
* /
add _task : function ( aFunction ) {
if ( ! this . _ _tasks ) {
this . waitForExplicitFinish ( ) ;
this . _ _tasks = [ ] ;
}
this . _ _tasks . push ( aFunction . bind ( this ) ) ;
} ,
2011-06-10 11:00:45 +04:00
destroy : function test _destroy ( ) {
for ( let prop in this )
delete this [ prop ] ;
}
2007-07-09 20:24:15 +04:00
} ;