WebKit Bugzilla
Attachment 347422 Details for
Bug 186214
: Have WebKitTestRunner check for abandoned documents
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-186214-20180817191059.patch (text/plain), 70.76 KB, created by
Simon Fraser (smfr)
on 2018-08-17 19:11:00 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Simon Fraser (smfr)
Created:
2018-08-17 19:11:00 PDT
Size:
70.76 KB
patch
obsolete
>Subversion Revision: 234993 >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 2c936c39e8d3a671398adc80edf731a8cb1976f5..f09c5f354ac7fbed2df72481088f8f8ff8f9ea8e 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,22 @@ >+2018-08-17 Simon Fraser <simon.fraser@apple.com> >+ >+ Have WebKitTestRunner check for abandoned documents >+ https://bugs.webkit.org/show_bug.cgi?id=186214 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add C SPI to get the list of live documents, and to clear the page and memory >+ caches, for use by WebKitTestRunner. >+ >+ * WebProcess/InjectedBundle/API/c/WKBundle.cpp: >+ (WKBundleGetLiveDocumentURLs): >+ (WKBundleClearPageCache): >+ (WKBundleClearMemoryCache): >+ * WebProcess/InjectedBundle/API/c/WKBundlePrivate.h: >+ * WebProcess/InjectedBundle/InjectedBundle.cpp: >+ (WebKit::InjectedBundle::liveDocumentURLs): >+ * WebProcess/InjectedBundle/InjectedBundle.h: >+ > 2018-08-17 Alex Christensen <achristensen@webkit.org> > > Pass webPageID and webFrameID to NetworkLoad for speculative loads >diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundle.cpp b/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundle.cpp >index d1c7b0fe92e2c6e0576b89a8ba8a1c8035dacf8e..aa8ba19b7c07265853556f95f5d5f3fb27c4145b 100644 >--- a/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundle.cpp >+++ b/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundle.cpp >@@ -34,11 +34,19 @@ > #include "WKAPICast.h" > #include "WKBundleAPICast.h" > #include "WKBundlePrivate.h" >+#include "WKMutableArray.h" >+#include "WKMutableDictionary.h" >+#include "WKNumber.h" >+#include "WKRetainPtr.h" >+#include "WKString.h" > #include "WebConnection.h" > #include "WebFrame.h" > #include "WebPage.h" > #include "WebPageGroupProxy.h" >+ > #include <WebCore/DatabaseTracker.h> >+#include <WebCore/MemoryCache.h> >+#include <WebCore/PageCache.h> > #include <WebCore/ResourceLoadObserver.h> > #include <WebCore/ServiceWorkerThreadProxy.h> > >@@ -213,6 +221,30 @@ void WKBundleSetAsynchronousSpellCheckingEnabled(WKBundleRef bundleRef, WKBundle > toImpl(bundleRef)->setAsynchronousSpellCheckingEnabled(toImpl(pageGroupRef), enabled); > } > >+WKArrayRef WKBundleGetLiveDocumentURLs(WKBundleRef bundleRef, WKBundlePageGroupRef pageGroupRef, bool excludeDocumentsInPageGroupPages) >+{ >+ auto liveDocuments = toImpl(bundleRef)->liveDocumentURLs(toImpl(pageGroupRef), excludeDocumentsInPageGroupPages); >+ >+ auto liveURLs = adoptWK(WKMutableArrayCreate()); >+ >+ for (const auto& it : liveDocuments) { >+ auto urlInfo = adoptWK(WKMutableDictionaryCreate()); >+ >+ auto documentIDKey = adoptWK(WKStringCreateWithUTF8CString("id")); >+ auto documentURLKey = adoptWK(WKStringCreateWithUTF8CString("url")); >+ >+ auto documentIDValue = adoptWK(WKUInt64Create(it.key)); >+ auto documentURLValue = adoptWK(toCopiedAPI(it.value)); >+ >+ WKDictionarySetItem(urlInfo.get(), documentIDKey.get(), documentIDValue.get()); >+ WKDictionarySetItem(urlInfo.get(), documentURLKey.get(), documentURLValue.get()); >+ >+ WKArrayAppendItem(liveURLs.get(), urlInfo.get()); >+ } >+ >+ return liveURLs.leakRef(); >+} >+ > void WKBundleReportException(JSContextRef context, JSValueRef exception) > { > InjectedBundle::reportException(context, exception); >@@ -229,6 +261,16 @@ void WKBundleSetDatabaseQuota(WKBundleRef bundleRef, uint64_t quota) > DatabaseTracker::singleton().setQuota(*SecurityOriginData::fromDatabaseIdentifier("file__0"), quota); > } > >+void WKBundleClearPageCache(WKBundleRef bundle) >+{ >+ PageCache::singleton().pruneToSizeNow(0, PruningReason::MemoryPressure); >+} >+ >+void WKBundleClearMemoryCache(WKBundleRef bundle) >+{ >+ MemoryCache::singleton().evictResources(); >+} >+ > WKDataRef WKBundleCreateWKDataFromUInt8Array(WKBundleRef bundle, JSContextRef context, JSValueRef data) > { > return toAPI(&toImpl(bundle)->createWebDataFromUint8Array(context, data).leakRef()); >diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePrivate.h b/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePrivate.h >index f4b0943ac6bea35d1d215d98704225c83f4b4fbb..0f8ed595818feb5155db74be28d56119e3ebf093 100644 >--- a/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePrivate.h >+++ b/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePrivate.h >@@ -68,6 +68,8 @@ WK_EXPORT void WKBundleRemoveAllWebNotificationPermissions(WKBundleRef bundle, W > WK_EXPORT uint64_t WKBundleGetWebNotificationID(WKBundleRef bundle, JSContextRef context, JSValueRef notification); > WK_EXPORT WKDataRef WKBundleCreateWKDataFromUInt8Array(WKBundleRef bundle, JSContextRef context, JSValueRef data); > WK_EXPORT void WKBundleSetAsynchronousSpellCheckingEnabled(WKBundleRef bundleRef, WKBundlePageGroupRef pageGroupRef, bool enabled); >+// Dictionary keys are document identifiers, values are document URLs. >+WK_EXPORT WKArrayRef WKBundleGetLiveDocumentURLs(WKBundleRef bundle, WKBundlePageGroupRef pageGroup, bool excludeDocumentsInPageGroupPages); > > // UserContent API > WK_EXPORT void WKBundleAddUserScript(WKBundleRef bundle, WKBundlePageGroupRef pageGroup, WKBundleScriptWorldRef scriptWorld, WKStringRef source, WKURLRef url, WKArrayRef whitelist, WKArrayRef blacklist, _WKUserScriptInjectionTime injectionTime, WKUserContentInjectedFrames injectedFrames); >@@ -96,6 +98,9 @@ WK_EXPORT void WKBundleResourceLoadStatisticsNotifyObserver(WKBundleRef); > > WK_EXPORT void WKBundleExtendClassesForParameterCoder(WKBundleRef bundle, WKArrayRef classes); > >+WK_EXPORT void WKBundleClearPageCache(WKBundleRef bundle); >+WK_EXPORT void WKBundleClearMemoryCache(WKBundleRef bundle); >+ > #ifdef __cplusplus > } > #endif >diff --git a/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.cpp b/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.cpp >index 0f3b9b7386d33c4587c496583fff0c63dd6a5b33..af102bb64180b772e4ef61b88a60ee8931753c06 100644 >--- a/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.cpp >+++ b/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.cpp >@@ -55,6 +55,7 @@ > #include <WebCore/ApplicationCache.h> > #include <WebCore/ApplicationCacheStorage.h> > #include <WebCore/CommonVM.h> >+#include <WebCore/Document.h> > #include <WebCore/Frame.h> > #include <WebCore/FrameLoader.h> > #include <WebCore/FrameView.h> >@@ -605,6 +606,26 @@ Ref<API::Data> InjectedBundle::createWebDataFromUint8Array(JSContextRef context, > return API::Data::create(static_cast<unsigned char*>(arrayData->baseAddress()), arrayData->byteLength()); > } > >+InjectedBundle::DocumentIDToURLMap InjectedBundle::liveDocumentURLs(WebPageGroupProxy* pageGroup, bool excludeDocumentsInPageGroupPages) >+{ >+ DocumentIDToURLMap result; >+ >+ for (const auto* document : Document::allDocuments()) >+ result.add(document->identifier().toUInt64(), document->url().string()); >+ >+ if (excludeDocumentsInPageGroupPages) { >+ for (const auto* page : PageGroup::pageGroup(pageGroup->identifier())->pages()) { >+ for (const auto* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) { >+ if (!frame->document()) >+ continue; >+ result.remove(frame->document()->identifier().toUInt64()); >+ } >+ } >+ } >+ >+ return result; >+} >+ > void InjectedBundle::setTabKeyCyclesThroughElements(WebPage* page, bool enabled) > { > page->corePage()->setTabKeyCyclesThroughElements(enabled); >diff --git a/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.h b/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.h >index 16c7c2f2bd2ca867260ede0634ec812595daa17b..257ebb782b02240bba14225348d9d69eb44eab07 100644 >--- a/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.h >+++ b/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.h >@@ -122,6 +122,9 @@ public: > void removeAllWebNotificationPermissions(WebPage*); > uint64_t webNotificationID(JSContextRef, JSValueRef); > Ref<API::Data> createWebDataFromUint8Array(JSContextRef, JSValueRef); >+ >+ typedef HashMap<uint64_t, String> DocumentIDToURLMap; >+ DocumentIDToURLMap liveDocumentURLs(WebPageGroupProxy*, bool excludeDocumentsInPageGroupPages); > > // UserContent API > void addUserScript(WebPageGroupProxy*, InjectedBundleScriptWorld*, String&& source, String&& url, API::Array* whitelist, API::Array* blacklist, WebCore::UserScriptInjectionTime, WebCore::UserContentInjectedFrames); >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index b86c749ec151d1b03f387e2df1c879121f70e94c..6ea359dc452535e394a4ded40ceacfeff354a2d8 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,112 @@ >+2018-08-17 Simon Fraser <simon.fraser@apple.com> >+ >+ Have WebKitTestRunner check for abandoned documents >+ https://bugs.webkit.org/show_bug.cgi?id=186214 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Teach webkitpy about the notion of "post-hoc data". This is data collected after >+ the test has finished, but data that indicates that something bad happened during >+ the test (like a memory leak). For now, the only kind of post-hoc data supported >+ is data about abandoned documents (those in the live documents list, but not loaded >+ in any frame). >+ >+ This new data is collected when passing "--check-for-abandoned-documents" to run-webkit-tests, >+ put into full_results.json, and shown in results.html, and in the results summary that prints >+ to stdout. >+ >+ WebKitTestRunner checks for abandoned documents in two cases: first, when test options >+ require that a new WKWebView is created (we need to test for abandoned documents in the old >+ web process before it goes away), and second at the end of a worker. In each case, >+ findAndDumpAbandondedDocuments() runs, which calls into the InjectedBundle to clear the page >+ and memory caches, run a GC and then get the list of live documents (excluding any documents >+ loaded in frames). >+ >+ At the end of a worker, driver.py triggers findAndDumpAbandondedDocuments() by sending >+ '#CHECK FOR ABANDONED DOCUMENTS' to WTR, which is handled as a "command" by >+ TestController::runTestingServerLoop(). >+ >+ WTR also fetches the list of live documents after every test, so that it can identify >+ which test an abandoned document first appeared in. >+ >+ Migrate some of the runUntil(m_doneResetting...) things in WTR to use a simple new AsyncTask >+ class, so that not everything waits on m_doneResetting, and migrate some timing data to use >+ WTF::Seconds. >+ >+ * Scripts/webkitpy/common/net/resultsjsonparser_unittest.py: >+ (ParsedJSONResultsTest): >+ * Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py: >+ (LayoutTestRunner): >+ (LayoutTestRunner._annotate_results_with_post_hoc_data): >+ (LayoutTestRunner._handle_finished_test_group): >+ (Worker.handle): >+ (Worker._finished_test_group): >+ * Scripts/webkitpy/layout_tests/models/test_expectations.py: >+ (TestExpectationParser): >+ (TestExpectations): >+ * Scripts/webkitpy/layout_tests/models/test_results.py: >+ (TestResult.__init__): >+ * Scripts/webkitpy/layout_tests/models/test_run_results.py: >+ (_interpret_test_failures): >+ (summarize_results): >+ * Scripts/webkitpy/layout_tests/run_webkit_tests.py: >+ (parse_args): >+ * Scripts/webkitpy/layout_tests/views/buildbot_results.py: >+ (BuildBotPrinter.print_unexpected_results): >+ (BuildBotPrinter.print_unexpected_results.add_result): >+ (BuildBotPrinter): >+ * Scripts/webkitpy/port/driver.py: >+ (DriverPostHocOutput): >+ (DriverPostHocOutput.__init__): >+ (Driver.do_post_tests_work): >+ (Driver.cmd_line): >+ (DriverProxy.do_post_tests_work): >+ * WebKitTestRunner/InjectedBundle/InjectedBundle.cpp: >+ (WTR::InjectedBundle::didReceiveMessageToPage): >+ (WTR::InjectedBundle::InjectedBundle): Deleted. >+ * WebKitTestRunner/InjectedBundle/InjectedBundle.h: >+ * WebKitTestRunner/Options.cpp: >+ (WTR::handleOptionCheckForAbandonedDocuments): >+ (WTR::OptionsHandler::OptionsHandler): >+ * WebKitTestRunner/Options.h: >+ * WebKitTestRunner/TestController.cpp: >+ (WTR::AsyncTask::run): >+ (WTR::AsyncTask::currentTask): >+ (WTR::TestController::TestController): >+ (WTR::TestController::initialize): >+ (WTR::TestController::ensureViewSupportsOptionsForTest): >+ (WTR::TestController::resetStateToConsistentValues): >+ (WTR::TestController::updateLiveDocumentsAfterTest): >+ (WTR::TestController::checkForAbandonedDocuments): >+ (WTR::TestController::findAndDumpAbandondedDocuments): >+ (WTR::TestController::willDestroyWebView): >+ (WTR::parseInputLine): >+ (WTR::TestController::runTest): >+ (WTR::TestController::waitForCompletion): >+ (WTR::TestController::handleControlCommand): >+ (WTR::TestController::runTestingServerLoop): >+ (WTR::TestController::runUntil): >+ (WTR::TestController::didReceiveLiveDocumentsList): >+ (WTR::TestController::didReceiveMessageFromInjectedBundle): >+ (WTR::TestController::didFinishNavigation): >+ * WebKitTestRunner/TestController.h: >+ (WTR::AsyncTask::AsyncTask): >+ (WTR::AsyncTask::taskComplete): >+ (WTR::TestController::AbandonedDocumentInfo::AbandonedDocumentInfo): >+ * WebKitTestRunner/TestInvocation.cpp: >+ (WTR::TestInvocation::shortTimeout const): >+ (WTR::TestInvocation::createTestSettingsDictionary): >+ (WTR::TestInvocation::invoke): >+ * WebKitTestRunner/TestInvocation.h: >+ * WebKitTestRunner/TestOptions.h: >+ (WTR::TestOptions::hasSameInitializationOptions const): >+ * WebKitTestRunner/cocoa/TestControllerCocoa.mm: >+ (WTR::TestController::platformRunUntil): >+ * WebKitTestRunner/gtk/TestControllerGtk.cpp: >+ (WTR::TestController::platformRunUntil): >+ * WebKitTestRunner/wpe/TestControllerWPE.cpp: >+ (WTR::TestController::platformRunUntil): >+ > 2018-08-17 Jer Noble <jer.noble@apple.com> > > REGRESSION (234743) Timeouts in TestWebKitAPI.PreferredAudioBufferSize.AudioWithWebAudio and TestWebKitAPI.PreferredAudioBufferSize.WebAudio >diff --git a/Tools/Scripts/webkitpy/common/net/resultsjsonparser_unittest.py b/Tools/Scripts/webkitpy/common/net/resultsjsonparser_unittest.py >index 2f21f44b9189f040b570c243a152e1328623a01e..19fb0a49b9dd44b0f069c29b2a4d9aefd07abc7f 100644 >--- a/Tools/Scripts/webkitpy/common/net/resultsjsonparser_unittest.py >+++ b/Tools/Scripts/webkitpy/common/net/resultsjsonparser_unittest.py >@@ -58,7 +58,13 @@ class ParsedJSONResultsTest(unittest.TestCase): > }, > "prototype-strawberry.html": { > "expected": "PASS", >- "actual": "FAIL PASS" >+ "actual": "FAIL PASS", >+ "post_hoc_data": { >+ "abandoned_documents": [ >+ "file:///Volumes/Data/slave/webkit/build/LayoutTests/fast/dom/prototype-strawberry.html", >+ "about:blank" >+ ] >+ } > } > } > }, >@@ -106,7 +112,13 @@ class ParsedJSONResultsTest(unittest.TestCase): > }, > "prototype-strawberry.html": { > "expected": "PASS", >- "actual": "FAIL PASS" >+ "actual": "FAIL PASS", >+ "post_hoc_data": { >+ "abandoned_documents": [ >+ "file:///Volumes/Data/slave/webkit/build/LayoutTests/fast/dom/prototype-strawberry.html", >+ "about:blank" >+ ] >+ } > } > } > }, >diff --git a/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py b/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py >index 3e52e614aa89a62d8e5ee3fefafcdcca28b803d0..e0b0e43ec47544a619534cd14d34a69b513d5a70 100644 >--- a/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py >+++ b/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py >@@ -62,6 +62,8 @@ class TestRunInterruptedException(Exception): > > > class LayoutTestRunner(object): >+ (ABANDONED_DOCUMENTS) = ('abandoned_documents') >+ > def __init__(self, options, port, printer, results_directory, test_is_slow_fn, needs_http=False, needs_websockets=False, needs_web_platform_test_server=False): > self._options = options > self._port = port >@@ -192,6 +194,14 @@ class LayoutTestRunner(object): > > self._interrupt_if_at_failure_limits(run_results) > >+ def _annotate_results_with_post_hoc_data(self, run_results, post_hoc_data): >+ for test_name, test_data in post_hoc_data.items(): >+ existing_result = run_results.results_by_name.get(test_name) >+ # We might find a document abandoned by an -expected file, so existing_result can be None. >+ if existing_result: >+ existing_result.post_hoc_data = test_data >+ >+ > def start_servers(self): > if self._needs_http and not self._did_start_http_server and not self._port.is_http_server_running(): > self._printer.write_update('Starting HTTP server ...') >@@ -232,6 +242,9 @@ class LayoutTestRunner(object): > def _handle_finished_test(self, worker_name, result, log_messages=[]): > self._update_summary_with_result(self._current_run_results, result) > >+ def _handle_finished_test_group(self, worker_name, post_hoc_test_data): >+ self._annotate_results_with_post_hoc_data(self._current_run_results, post_hoc_test_data) >+ > > class Worker(object): > def __init__(self, caller, results_directory, options): >@@ -269,6 +282,8 @@ class Worker(object): > for test_input in test_inputs: > self._run_test(test_input, test_list_name) > >+ self._finished_test_group(test_inputs) >+ > def _update_test_input(self, test_input): > if test_input.reference_files is None: > # Lazy initialization. >@@ -302,6 +317,23 @@ class Worker(object): > > self._clean_up_after_test(test_input, result) > >+ def _finished_test_group(self, test_inputs): >+ _log.debug("%s finished test group" % self._name) >+ >+ if self._driver and self._driver.has_crashed(): >+ self._kill_driver() >+ >+ post_hoc_test_data = {} >+ >+ if self._driver: >+ post_test_output = self._driver.do_post_tests_work(test_inputs) >+ if post_test_output: >+ for test_name, doc_list in post_test_output.abandoned_documents_dict.iteritems(): >+ per_test_data = {LayoutTestRunner.ABANDONED_DOCUMENTS: doc_list} >+ post_hoc_test_data[test_name] = per_test_data >+ >+ self._caller.post('finished_test_group', post_hoc_test_data) >+ > def stop(self): > _log.debug("%s cleaning up" % self._name) > self._kill_driver() >diff --git a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py >index e54ac94125f9dfacd7c6760788bd771b22fb43ff..a2f97d9e8054ba3e34c0ebd013cf32bc6953b045 100644 >--- a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py >+++ b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py >@@ -43,7 +43,7 @@ _log = logging.getLogger(__name__) > # FIXME: range() starts with 0 which makes if expectation checks harder > # as PASS is 0. > (PASS, FAIL, TEXT, IMAGE, IMAGE_PLUS_TEXT, AUDIO, TIMEOUT, CRASH, SKIP, WONTFIX, >- SLOW, DUMPJSCONSOLELOGINSTDERR, REBASELINE, MISSING, FLAKY, NOW, NONE) = range(17) >+ SLOW, ABANDONED_DOCUMENT, DUMPJSCONSOLELOGINSTDERR, REBASELINE, MISSING, FLAKY, NOW, NONE) = range(18) > > # FIXME: Perhas these two routines should be part of the Port instead? > BASELINE_SUFFIX_LIST = ('png', 'wav', 'txt') >@@ -244,6 +244,7 @@ class TestExpectationParser(object): > 'Failure': 'FAIL', > 'ImageOnlyFailure': 'IMAGE', > 'Missing': 'MISSING', >+ 'AbandonedDocument': 'ABANDONED', > 'Pass': 'PASS', > 'Rebaseline': 'REBASELINE', > 'Skip': 'SKIP', >@@ -794,6 +795,7 @@ class TestExpectations(object): > 'timeout': TIMEOUT, > 'crash': CRASH, > 'missing': MISSING, >+ 'abandoned': ABANDONED_DOCUMENT, > 'skip': SKIP} > > # (aggregated by category, pass/fail/skip, type) >@@ -806,9 +808,10 @@ class TestExpectations(object): > AUDIO: 'audio failures', > CRASH: 'crashes', > TIMEOUT: 'timeouts', >- MISSING: 'missing results'} >+ MISSING: 'missing results', >+ ABANDONED_DOCUMENT: 'abandoned document'} > >- EXPECTATION_ORDER = (PASS, CRASH, TIMEOUT, MISSING, FAIL, IMAGE, SKIP) >+ EXPECTATION_ORDER = (PASS, CRASH, TIMEOUT, ABANDONED_DOCUMENT, MISSING, FAIL, IMAGE, SKIP) > > BUILD_TYPES = ('debug', 'release') > >diff --git a/Tools/Scripts/webkitpy/layout_tests/models/test_results.py b/Tools/Scripts/webkitpy/layout_tests/models/test_results.py >index 82e1fb21171f3a2e6515fc710ad47b97f14a923d..894fcea07900c6adbe7b99e2ad335d8d0210e3f2 100644 >--- a/Tools/Scripts/webkitpy/layout_tests/models/test_results.py >+++ b/Tools/Scripts/webkitpy/layout_tests/models/test_results.py >@@ -56,6 +56,7 @@ class TestResult(object): > self.total_run_time = 0 # The time taken to run the test plus any references, compute diffs, etc. > self.test_number = None > self.is_other_crash = False >+ self.post_hoc_data = {} # Data collected after the test runs, e.g. abandoned documents. > > def __eq__(self, other): > return (self.test_name == other.test_name and >diff --git a/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py b/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py >index 5021a2ff3e848a698fa3dbaaecbc272f05d56124..9557c9777a44cf5fcc268cd37b51149152219f96 100644 >--- a/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py >+++ b/Tools/Scripts/webkitpy/layout_tests/models/test_run_results.py >@@ -132,6 +132,7 @@ class RunDetails(object): > > def _interpret_test_failures(failures): > test_dict = {} >+ > failure_types = [type(failure) for failure in failures] > # FIXME: get rid of all this is_* values once there is a 1:1 map between > # TestFailure type and test_expectations.EXPECTATION. >@@ -206,6 +207,9 @@ def summarize_results(port_obj, expectations, initial_results, retry_results, en > if result.has_stderr: > test_dict['has_stderr'] = True > >+ if result.post_hoc_data: >+ test_dict['post_hoc_data'] = result.post_hoc_data >+ > if result.reftest_type: > test_dict.update(reftest_type=list(result.reftest_type)) > >@@ -215,7 +219,7 @@ def summarize_results(port_obj, expectations, initial_results, retry_results, en > if result_type == test_expectations.PASS: > num_passes += 1 > # FIXME: include passing tests that have stderr output. >- if expected == 'PASS' and not include_passes: >+ if expected == 'PASS' and not include_passes and not result.post_hoc_data: > continue > elif result_type == test_expectations.CRASH: > if test_name in initial_results.unexpected_results_by_name: >diff --git a/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py >index ced6b9db88b39d206b9a88f1a22bf8b49cbb2250..b215bd2dc977d767c0fdaa93fcbb04307dc7fe6d 100755 >--- a/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py >+++ b/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py >@@ -290,6 +290,8 @@ def parse_args(args): > optparse.make_option('--display-server', choices=['xvfb', 'xorg', 'weston', 'wayland'], default='xvfb', > help='"xvfb": Use a virtualized X11 server. "xorg": Use the current X11 session. ' > '"weston": Use a virtualized Weston server. "wayland": Use the current wayland session.'), >+ # Defaulting to True just for EWS testing. >+ optparse.make_option("--check-for-abandoned-documents", action="store_true", default=True, help="Check for abandoned (\"leaked\") documents during testing."), > ])) > > option_group_definitions.append(("iOS Options", [ >diff --git a/Tools/Scripts/webkitpy/layout_tests/views/buildbot_results.py b/Tools/Scripts/webkitpy/layout_tests/views/buildbot_results.py >old mode 100644 >new mode 100755 >index 9b0a971cb0b1d5122787d889b596c0d0b108d942..0cb9581248b35716ce447768982e45b2d1128e14 >--- a/Tools/Scripts/webkitpy/layout_tests/views/buildbot_results.py >+++ b/Tools/Scripts/webkitpy/layout_tests/views/buildbot_results.py >@@ -93,11 +93,12 @@ class BuildBotPrinter(object): > passes = {} > flaky = {} > regressions = {} >+ tests_with_post_hoc_data = {} > > def add_to_dict_of_lists(dict, key, value): > dict.setdefault(key, []).append(value) > >- def add_result(test, results, passes=passes, flaky=flaky, regressions=regressions): >+ def add_result(test, results, passes=passes, flaky=flaky, regressions=regressions, tests_with_post_hoc_data=tests_with_post_hoc_data): > actual = results['actual'].split(" ") > expected = results['expected'].split(" ") > >@@ -123,6 +124,10 @@ class BuildBotPrinter(object): > else: > add_to_dict_of_lists(regressions, results['actual'], test) > >+ if 'post_hoc_data' in results: >+ for data_type in results['post_hoc_data']: >+ add_to_dict_of_lists(tests_with_post_hoc_data, data_type, test) >+ > resultsjsonparser.for_each_test(summarized_results['tests'], add_result) > > if len(passes) or len(flaky) or len(regressions): >@@ -166,3 +171,15 @@ class BuildBotPrinter(object): > > if len(summarized_results['tests']) and self.debug_logging: > self._print("%s" % ("-" * 78)) >+ >+ if len(tests_with_post_hoc_data): >+ post_hoc_data_readable_types = { >+ 'abandoned_documents': 'abandoned documents' >+ } >+ for key, tests in tests_with_post_hoc_data.iteritems(): >+ self._print("Tests with %s: (%d)" % (post_hoc_data_readable_types[key], len(tests))) >+ tests.sort() >+ for test in tests: >+ self._print(" %s" % test) >+ self._print("") >+ self._print("") >diff --git a/Tools/Scripts/webkitpy/port/driver.py b/Tools/Scripts/webkitpy/port/driver.py >index ea287fe000b2775d10d279237424d3ae3e989fbd..27f3d8751667ec27b34e9740460fd339b4a07bdf 100644 >--- a/Tools/Scripts/webkitpy/port/driver.py >+++ b/Tools/Scripts/webkitpy/port/driver.py >@@ -34,6 +34,7 @@ import shlex > import sys > import time > import os >+from collections import defaultdict > > from os.path import normpath > from webkitpy.common.system import path >@@ -119,6 +120,13 @@ class DriverOutput(object): > self.error = re.sub(pattern[0], pattern[1], self.error) > > >+class DriverPostHocOutput(object): >+ """Groups data collected for a set of tests, collected after all those testse have run >+ (for example, data about leaked objects)""" >+ def __init__(self, abandoned_documents_dict): >+ self.abandoned_documents_dict = abandoned_documents_dict >+ >+ > class Driver(object): > """object for running test(s) using DumpRenderTree/WebKitTestRunner.""" > >@@ -242,6 +250,30 @@ class Driver(object): > crashed_process_name=self._crashed_process_name, > crashed_pid=self._crashed_pid, crash_log=crash_log, pid=pid) > >+ def do_post_tests_work(self, test_inputs): >+ if not self._port.get_option('check_for_abandoned_documents'): >+ return None >+ >+ _log.debug('Checking for abandoned documents...') >+ self._server_process.write('#CHECK FOR ABANDONED DOCUMENTS\n') >+ deadline = time.time() + 20 >+ block = self._read_block(deadline, '', wait_for_stderr_eof=True) >+ >+ tests_with_abandoned_documents = defaultdict(list) >+ >+ last_test = None >+ for line in block.decoded_content.splitlines(): >+ m = re.match('^TEST: (.+)$', line) >+ if m: >+ last_test = self.uri_to_test(m.group(1)) >+ m = re.match('^ABANDONED DOCUMENT: (.+)$', line) >+ if m: >+ abandoned_document_url = m.group(1) >+ if last_test: >+ tests_with_abandoned_documents[last_test].append(abandoned_document_url) >+ >+ return DriverPostHocOutput(tests_with_abandoned_documents) >+ > def _get_crash_log(self, stdout, stderr, newer_than): > return self._port._get_crash_log(self._crashed_process_name, self._crashed_pid, stdout, stderr, newer_than, target_host=self._target_host) > >@@ -432,6 +464,8 @@ class Driver(object): > cmd.append('--accelerated-drawing') > if self._port.get_option('remote_layer_tree'): > cmd.append('--remote-layer-tree') >+ if self._port.get_option('check_for_abandoned_documents'): >+ cmd.append('--check-for-abandoned-documents') > if self._port.get_option('threaded'): > cmd.append('--threaded') > if self._no_timeout: >@@ -705,6 +739,9 @@ class DriverProxy(object): > > return self._driver.run_test(driver_input, stop_when_done) > >+ def do_post_tests_work(self, test_inputs): >+ return self._driver.do_post_tests_work(test_inputs) >+ > def has_crashed(self): > return self._driver.has_crashed() > >diff --git a/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp >index e6b55e58deb713465dbc5887ceeb6f5888684669..d774e54ad7f49e3db7dec7a9e78c01efe6865843 100644 >--- a/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp >+++ b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp >@@ -64,17 +64,6 @@ InjectedBundle& InjectedBundle::singleton() > return shared; > } > >-InjectedBundle::InjectedBundle() >- : m_bundle(0) >- , m_topLoadingFrame(0) >- , m_state(Idle) >- , m_dumpPixels(false) >- , m_useWaitToDumpWatchdogTimer(true) >- , m_useWorkQueue(false) >- , m_timeout(0) >-{ >-} >- > void InjectedBundle::didCreatePage(WKBundleRef bundle, WKBundlePageRef page, const void* clientInfo) > { > static_cast<InjectedBundle*>(const_cast<void*>(clientInfo))->didCreatePage(page); >@@ -231,7 +220,6 @@ void InjectedBundle::didReceiveMessageToPage(WKBundlePageRef page, WKStringRef m > > WKRetainPtr<WKStringRef> shouldGCKey(AdoptWK, WKStringCreateWithUTF8CString("ShouldGC")); > bool shouldGC = WKBooleanGetValue(static_cast<WKBooleanRef>(WKDictionaryGetItemForKey(messageBodyDictionary, shouldGCKey.get()))); >- > if (shouldGC) > WKBundleGarbageCollectJavaScriptObjects(m_bundle); > >@@ -260,6 +248,26 @@ void InjectedBundle::didReceiveMessageToPage(WKBundlePageRef page, WKStringRef m > return; > } > >+ if (WKStringIsEqualToUTF8CString(messageName, "GetLiveDocuments")) { >+ const bool excludeDocumentsInPageGroup = false; >+ auto documentURLs = adoptWK(WKBundleGetLiveDocumentURLs(m_bundle, m_pageGroup, excludeDocumentsInPageGroup)); >+ auto ackMessageName = adoptWK(WKStringCreateWithUTF8CString("LiveDocuments")); >+ WKBundlePagePostMessage(page, ackMessageName.get(), documentURLs.get()); >+ return; >+ } >+ >+ if (WKStringIsEqualToUTF8CString(messageName, "CheckForAbandonedDocuments")) { >+ WKBundleClearPageCache(m_bundle); >+ WKBundleClearMemoryCache(m_bundle); >+ WKBundleGarbageCollectJavaScriptObjects(m_bundle); >+ >+ const bool excludeDocumentsInPageGroup = true; >+ auto documentURLs = adoptWK(WKBundleGetLiveDocumentURLs(m_bundle, m_pageGroup, excludeDocumentsInPageGroup)); >+ auto ackMessageName = adoptWK(WKStringCreateWithUTF8CString("LiveDocuments")); >+ WKBundlePagePostMessage(page, ackMessageName.get(), documentURLs.get()); >+ return; >+ } >+ > if (WKStringIsEqualToUTF8CString(messageName, "CallAddChromeInputFieldCallback")) { > m_testRunner->callAddChromeInputFieldCallback(); > return; >diff --git a/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h >index 6d4ded8e8a57aa0788f1b707a062de50f1ec0ed2..1197b842068d3d7aa9036694a2abbd64b8a09b53 100644 >--- a/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h >+++ b/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h >@@ -145,7 +145,7 @@ public: > size_t userScriptInjectedCount() const { return m_userScriptInjectedCount; } > > private: >- InjectedBundle(); >+ InjectedBundle() = default; > ~InjectedBundle(); > > static void didCreatePage(WKBundleRef, WKBundlePageRef, const void* clientInfo); >@@ -170,8 +170,8 @@ private: > > bool booleanForKey(WKDictionaryRef, const char* key); > >- WKBundleRef m_bundle; >- WKBundlePageGroupRef m_pageGroup; >+ WKBundleRef m_bundle { nullptr }; >+ WKBundlePageGroupRef m_pageGroup { nullptr }; > Vector<std::unique_ptr<InjectedBundlePage>> m_pages; > > #if HAVE(ACCESSIBILITY) >@@ -182,19 +182,19 @@ private: > RefPtr<EventSendingController> m_eventSendingController; > RefPtr<TextInputController> m_textInputController; > >- WKBundleFrameRef m_topLoadingFrame; >+ WKBundleFrameRef m_topLoadingFrame { nullptr }; > > enum State { > Idle, > Testing, > Stopping > }; >- State m_state; >+ State m_state { Idle }; > >- bool m_dumpPixels; >- bool m_useWaitToDumpWatchdogTimer; >- bool m_useWorkQueue; >- int m_timeout; >+ bool m_dumpPixels { false }; >+ bool m_useWaitToDumpWatchdogTimer { true }; >+ bool m_useWorkQueue { false }; >+ int m_timeout { 0 }; > bool m_pixelResultIsPending { false }; > bool m_dumpJSConsoleLogInStdErr { false }; > >diff --git a/Tools/WebKitTestRunner/Options.cpp b/Tools/WebKitTestRunner/Options.cpp >index a89e0db4e2495aae50e33a44d39d70402345917b..c64ed3eb7e64204a67deecbac4bde4cf24071858 100644 >--- a/Tools/WebKitTestRunner/Options.cpp >+++ b/Tools/WebKitTestRunner/Options.cpp >@@ -93,6 +93,12 @@ static bool handleOptionShowTouches(Options& options, const char*, const char*) > return true; > } > >+static bool handleOptionCheckForAbandonedDocuments(Options& options, const char*, const char*) >+{ >+ options.checkForAbandonedDocuments = true; >+ return true; >+} >+ > static bool handleOptionAllowAnyHTTPSCertificateForAllowedHosts(Options& options, const char*, const char*) > { > options.allowAnyHTTPSCertificateForAllowedHosts = true; >@@ -129,6 +135,7 @@ OptionsHandler::OptionsHandler(Options& o) > optionList.append(Option("--allow-any-certificate-for-allowed-hosts", "Allows any HTTPS certificate for an allowed host.", handleOptionAllowAnyHTTPSCertificateForAllowedHosts)); > optionList.append(Option("--show-webview", "Show the WebView during test runs (for debugging)", handleOptionShowWebView)); > optionList.append(Option("--show-touches", "Show the touches during test runs (for debugging)", handleOptionShowTouches)); >+ optionList.append(Option("--check-for-abandoned-documents", "Check for abandoned (\"leaked\") documents", handleOptionCheckForAbandonedDocuments)); > > optionList.append(Option(0, 0, handleOptionUnmatched)); > } >diff --git a/Tools/WebKitTestRunner/Options.h b/Tools/WebKitTestRunner/Options.h >index f30c495d9930766e3083fd84ccf0567681510792..ed263a6858dd83cfeee0e0a180aefd68b8f2b3ab 100644 >--- a/Tools/WebKitTestRunner/Options.h >+++ b/Tools/WebKitTestRunner/Options.h >@@ -48,6 +48,7 @@ struct Options { > bool shouldUseRemoteLayerTree { false }; > bool shouldShowWebView { false }; > bool shouldShowTouches { false }; >+ bool checkForAbandonedDocuments { false }; > bool allowAnyHTTPSCertificateForAllowedHosts { false }; > std::vector<std::string> paths; > std::set<std::string> allowedHosts; >diff --git a/Tools/WebKitTestRunner/TestController.cpp b/Tools/WebKitTestRunner/TestController.cpp >index e3a62eaf0c9016974f6cd42405ca2c4fb4c536a1..155b19ea49c89dcd509439749748e5bf00938daa 100644 >--- a/Tools/WebKitTestRunner/TestController.cpp >+++ b/Tools/WebKitTestRunner/TestController.cpp >@@ -93,9 +93,8 @@ const unsigned TestController::viewHeight = 600; > const unsigned TestController::w3cSVGViewWidth = 480; > const unsigned TestController::w3cSVGViewHeight = 360; > >-const double TestController::defaultShortTimeout = 5.0; >- >-const double TestController::noTimeout = -1; >+const WTF::Seconds TestController::defaultShortTimeout = 5_s; >+const WTF::Seconds TestController::noTimeout = -1_s; > > static WKURLRef blankURL() > { >@@ -115,6 +114,22 @@ static WKStringRef copySignedPublicKeyAndChallengeString(WKPageRef, const void*) > return WKStringCreateWithUTF8CString("MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue%2BPtwBRE6XfV%0AWtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID%0AAQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n%2FS%0Ar%2F7iJNroWlSzSMtTiQTEB%2BADWHGj9u1xrUrOilq%2Fo2cuQxIfZcNZkYAkWP4DubqW%0Ai0%2F%2FrgBvmco%3D"); > } > >+AsyncTask* AsyncTask::m_currentTask; >+ >+bool AsyncTask::run() >+{ >+ m_currentTask = this; >+ m_task(); >+ TestController::singleton().runUntil(m_taskDone, m_timeout); >+ m_currentTask = nullptr; >+ return m_taskDone; >+} >+ >+AsyncTask* AsyncTask::currentTask() >+{ >+ return m_currentTask; >+} >+ > static TestController* controller; > > TestController& TestController::singleton() >@@ -128,7 +143,7 @@ TestController::TestController(int argc, const char* argv[]) > initialize(argc, argv); > controller = this; > run(); >- controller = 0; >+ controller = nullptr; > } > > TestController::~TestController() >@@ -384,6 +399,7 @@ void TestController::initialize(int argc, const char* argv[]) > m_allowedHosts = options.allowedHosts; > m_shouldShowWebView = options.shouldShowWebView; > m_shouldShowTouches = options.shouldShowTouches; >+ m_checkForAbandonedDocuments = options.checkForAbandonedDocuments; > m_allowAnyHTTPSCertificateForAllowedHosts = options.allowAnyHTTPSCertificateForAllowedHosts; > > if (options.printSupportedFeatures) { >@@ -660,6 +676,8 @@ void TestController::ensureViewSupportsOptionsForTest(const TestInvocation& test > if (m_mainWebView->viewSupportsOptions(options)) > return; > >+ willDestroyWebView(); >+ > WKPageSetPageUIClient(m_mainWebView->page(), nullptr); > WKPageSetPageNavigationClient(m_mainWebView->page(), nullptr); > WKPageClose(m_mainWebView->page()); >@@ -669,7 +687,7 @@ void TestController::ensureViewSupportsOptionsForTest(const TestInvocation& test > > createWebViewWithOptions(options); > >- if (!resetStateToConsistentValues(options)) >+ if (!resetStateToConsistentValues(options, ResetStage::BeforeTest)) > TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n"); > } > >@@ -783,7 +801,7 @@ void TestController::resetPreferencesToConsistentValues(const TestOptions& optio > platformResetPreferencesToConsistentValues(); > } > >-bool TestController::resetStateToConsistentValues(const TestOptions& options) >+bool TestController::resetStateToConsistentValues(const TestOptions& options, ResetStage resetStage) > { > SetForScope<State> changeState(m_state, Resetting); > m_beforeUnloadReturnValue = true; >@@ -909,11 +927,73 @@ bool TestController::resetStateToConsistentValues(const TestOptions& options) > > // Reset main page back to about:blank > m_doneResetting = false; >+ // FIXME: Move this to the AsyncTask() model. > WKPageLoadURL(m_mainWebView->page(), blankURL()); > runUntil(m_doneResetting, m_currentInvocation->shortTimeout()); >+ if (!m_doneResetting) >+ return false; >+ >+ if (resetStage == ResetStage::AfterTest && m_checkForAbandonedDocuments) >+ updateLiveDocumentsAfterTest(); >+ > return m_doneResetting; > } > >+void TestController::updateLiveDocumentsAfterTest() >+{ >+ AsyncTask([]() { >+ // After each test, we update the list of live documents so that we can detect when an abandoned document first showed up. >+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("GetLiveDocuments")); >+ WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr); >+ }, 5_s).run(); >+} >+ >+void TestController::checkForAbandonedDocuments() >+{ >+ AsyncTask([]() { >+ // This runs at the end of a series of tests. It clears caches, runs a GC and then fetches the list of documents. >+ WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CheckForAbandonedDocuments")); >+ WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr); >+ }, 20_s).run(); >+} >+ >+void TestController::findAndDumpAbandondedDocuments() >+{ >+ checkForAbandonedDocuments(); >+ >+ StringBuilder builder; >+ >+ if (m_abandonedDocumentInfo.size()) { >+ for (const auto& it : m_abandonedDocumentInfo) { >+ auto documentURL = it.value.abandonedDocumentURL; >+ if (documentURL.isEmpty()) >+ documentURL = "(no url)"; >+ builder.append("TEST: "); >+ builder.append(it.value.testURL); >+ builder.append('\n'); >+ builder.append("ABANDONED DOCUMENT: "); >+ builder.append(documentURL); >+ builder.append('\n'); >+ } >+ } else >+ builder.append("no abandoned documents"); >+ >+ String result = builder.toString(); >+ printf("Content-Type: text/plain\n"); >+ printf("Content-Length: %u\n", result.length()); >+ fwrite(result.utf8().data(), 1, result.length(), stdout); >+ printf("#EOF\n"); >+ fprintf(stderr, "#EOF\n"); >+ fflush(stdout); >+ fflush(stderr); >+} >+ >+void TestController::willDestroyWebView() >+{ >+ // Before we kill the web view, look for abandoned documents before that web process goes away. >+ checkForAbandonedDocuments(); >+} >+ > void TestController::terminateWebContentProcess() > { > WKPageTerminate(m_mainWebView->page()); >@@ -1238,7 +1318,7 @@ NO_RETURN static void die(const std::string& inputLine) > exit(1); > } > >-TestCommand parseInputLine(const std::string& inputLine) >+static TestCommand parseInputLine(const std::string& inputLine) > { > TestCommand result; > CommandTokenizer tokenizer(inputLine); >@@ -1251,7 +1331,7 @@ TestCommand parseInputLine(const std::string& inputLine) > arg = tokenizer.next(); > if (arg == std::string("--timeout")) { > std::string timeoutToken = tokenizer.next(); >- result.timeout = atoi(timeoutToken.c_str()); >+ result.timeout = Seconds(atoi(timeoutToken.c_str())); > } else if (arg == std::string("-p") || arg == std::string("--pixel-test")) { > result.shouldDumpPixels = true; > if (tokenizer.hasNext()) >@@ -1282,8 +1362,10 @@ bool TestController::runTest(const char* inputLine) > > if (command.shouldDumpPixels || m_shouldDumpPixelsForAllTests) > m_currentInvocation->setIsPixelTest(command.expectedPixelHash); >- if (command.timeout > 0) >+ >+ if (command.timeout > 0_s) > m_currentInvocation->setCustomTimeout(command.timeout); >+ > m_currentInvocation->setDumpJSConsoleLogInStdErr(command.dumpJSConsoleLogInStdErr || options.dumpJSConsoleLogInStdErr); > > platformWillRunTest(*m_currentInvocation); >@@ -1294,6 +1376,23 @@ bool TestController::runTest(const char* inputLine) > return true; > } > >+bool TestController::waitForCompletion(const WTF::Function<void ()>& function, WTF::Seconds timeout) >+{ >+ m_doneResetting = false; >+ function(); >+ runUntil(m_doneResetting, timeout); >+ return !m_doneResetting; >+} >+ >+bool TestController::handleControlCommand(const char* command) >+{ >+ if (!strcmp("#CHECK FOR ABANDONED DOCUMENTS", command)) { >+ findAndDumpAbandondedDocuments(); >+ return true; >+ } >+ return false; >+} >+ > void TestController::runTestingServerLoop() > { > char filenameBuffer[2048]; >@@ -1305,6 +1404,9 @@ void TestController::runTestingServerLoop() > if (strlen(filenameBuffer) == 0) > continue; > >+ if (handleControlCommand(filenameBuffer)) >+ continue; >+ > if (!runTest(filenameBuffer)) > break; > } >@@ -1322,7 +1424,7 @@ void TestController::run() > } > } > >-void TestController::runUntil(bool& done, double timeout) >+void TestController::runUntil(bool& done, WTF::Seconds timeout) > { > if (m_forceNoTimeout) > timeout = noTimeout; >@@ -1383,8 +1485,51 @@ void TestController::didReceiveKeyDownMessageFromInjectedBundle(WKDictionaryRef > m_eventSenderProxy->keyDown(key, modifiers, location); > } > >+void TestController::didReceiveLiveDocumentsList(WKArrayRef liveDocumentList) >+{ >+ auto numDocuments = WKArrayGetSize(liveDocumentList); >+ >+ HashMap<uint64_t, String> documentInfo; >+ for (size_t i = 0; i < numDocuments; ++i) { >+ WKTypeRef item = WKArrayGetItemAtIndex(liveDocumentList, i); >+ if (item && WKGetTypeID(item) == WKDictionaryGetTypeID()) { >+ WKDictionaryRef liveDocumentItem = static_cast<WKDictionaryRef>(item); >+ >+ WKRetainPtr<WKStringRef> idKey(AdoptWK, WKStringCreateWithUTF8CString("id")); >+ WKUInt64Ref documentID = static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(liveDocumentItem, idKey.get())); >+ >+ WKRetainPtr<WKStringRef> urlKey(AdoptWK, WKStringCreateWithUTF8CString("url")); >+ WKStringRef documentURL = static_cast<WKStringRef>(WKDictionaryGetItemForKey(liveDocumentItem, urlKey.get())); >+ >+ documentInfo.add(WKUInt64GetValue(documentID), toWTFString(documentURL)); >+ } >+ } >+ >+ if (!documentInfo.size()) { >+ m_abandonedDocumentInfo.clear(); >+ return; >+ } >+ >+ // Remove any documents which are no longer live. >+ m_abandonedDocumentInfo.removeIf([&](auto& keyAndValue) { >+ return !documentInfo.contains(keyAndValue.key); >+ }); >+ >+ // Add newly abandoned documents. >+ String currentTestURL = m_currentInvocation ? toWTFString(adoptWK(WKURLCopyString(m_currentInvocation->url()))) : "no test"; >+ for (const auto& it : documentInfo) >+ m_abandonedDocumentInfo.add(it.key, AbandonedDocumentInfo(currentTestURL, it.value)); >+} >+ > void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody) > { >+ if (WKStringIsEqualToUTF8CString(messageName, "LiveDocuments")) { >+ ASSERT(WKGetTypeID(messageBody) == WKArrayGetTypeID()); >+ didReceiveLiveDocumentsList(static_cast<WKArrayRef>(messageBody)); >+ AsyncTask::currentTask()->taskComplete(); >+ return; >+ } >+ > if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) { > if (m_state != RunningTest) > return; >@@ -1413,7 +1558,6 @@ void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName > > if (WKStringIsEqualToUTF8CString(subMessageName, "KeyDown")) { > didReceiveKeyDownMessageFromInjectedBundle(messageBodyDictionary, false); >- > return; > } > >@@ -1860,6 +2004,7 @@ void TestController::didFinishNavigation(WKPageRef page, WKNavigationRef navigat > return; > > m_doneResetting = true; >+ > singleton().notifyDone(); > } > >diff --git a/Tools/WebKitTestRunner/TestController.h b/Tools/WebKitTestRunner/TestController.h >index 42a2b6f74227b0dec77743c72c94ba34ce25f76b..3983b3a59ad865deffd78add3e7fdaafd2e20b79 100644 >--- a/Tools/WebKitTestRunner/TestController.h >+++ b/Tools/WebKitTestRunner/TestController.h >@@ -23,8 +23,7 @@ > * THE POSSIBILITY OF SUCH DAMAGE. > */ > >-#ifndef TestController_h >-#define TestController_h >+#pragma once > > #include "GeolocationProviderMock.h" > #include "WebNotificationProvider.h" >@@ -34,6 +33,7 @@ > #include <string> > #include <vector> > #include <wtf/HashMap.h> >+#include <wtf/Seconds.h> > #include <wtf/Vector.h> > #include <wtf/text/StringHash.h> > >@@ -48,6 +48,33 @@ class EventSenderProxy; > struct TestCommand; > struct TestOptions; > >+class AsyncTask { >+public: >+ AsyncTask(WTF::Function<void ()>&& task, WTF::Seconds timeout) >+ : m_task(WTFMove(task)) >+ , m_timeout(timeout) >+ { >+ ASSERT(!currentTask()); >+ } >+ >+ // Returns false on timeout. >+ bool run(); >+ >+ void taskComplete() >+ { >+ m_taskDone = true; >+ } >+ >+ static AsyncTask* currentTask(); >+ >+private: >+ static AsyncTask* m_currentTask; >+ >+ WTF::Function<void ()> m_task; >+ WTF::Seconds m_timeout; >+ bool m_taskDone { false }; >+}; >+ > // FIXME: Rename this TestRunner? > class TestController { > public: >@@ -59,8 +86,8 @@ public: > static const unsigned w3cSVGViewWidth; > static const unsigned w3cSVGViewHeight; > >- static const double defaultShortTimeout; >- static const double noTimeout; >+ static const WTF::Seconds defaultShortTimeout; >+ static const WTF::Seconds noTimeout; > > TestController(int argc, const char* argv[]); > ~TestController(); >@@ -79,7 +106,7 @@ public: > > // Runs the run loop until `done` is true or the timeout elapses. > bool useWaitToDumpWatchdogTimer() { return m_useWaitToDumpWatchdogTimer; } >- void runUntil(bool& done, double timeoutSeconds); >+ void runUntil(bool& done, WTF::Seconds timeoutSeconds); > void notifyDone(); > > bool shouldShowWebView() const { return m_shouldShowWebView; } >@@ -121,9 +148,12 @@ public: > > unsigned imageCountInGeneralPasteboard() const; > >- bool resetStateToConsistentValues(const TestOptions&); >+ enum class ResetStage { BeforeTest, AfterTest }; >+ bool resetStateToConsistentValues(const TestOptions&, ResetStage); > void resetPreferencesToConsistentValues(const TestOptions&); > >+ void willDestroyWebView(); >+ > void terminateWebContentProcess(); > void reattachPageToWebProcess(); > >@@ -228,6 +258,11 @@ private: > > void runTestingServerLoop(); > bool runTest(const char* pathOrURL); >+ >+ // Returns false if timed out. >+ bool waitForCompletion(const WTF::Function<void ()>&, WTF::Seconds timeout); >+ >+ bool handleControlCommand(const char* command); > > void platformInitialize(); > void platformDestroy(); >@@ -244,7 +279,7 @@ private: > #endif > void platformConfigureViewForTest(const TestInvocation&); > void platformWillRunTest(const TestInvocation&); >- void platformRunUntil(bool& done, double timeout); >+ void platformRunUntil(bool& done, WTF::Seconds timeout); > void platformDidCommitLoadForFrame(WKPageRef, WKFrameRef); > WKContextRef platformContext(); > WKPreferencesRef platformPreferences(); >@@ -258,6 +293,12 @@ private: > void updateWebViewSizeForTest(const TestInvocation&); > void updateWindowScaleForTest(PlatformWebView*, const TestInvocation&); > >+ void updateLiveDocumentsAfterTest(); >+ void checkForAbandonedDocuments(); >+ >+ void didReceiveLiveDocumentsList(WKArrayRef); >+ void findAndDumpAbandondedDocuments(); >+ > void decidePolicyForGeolocationPermissionRequestIfPossible(); > void decidePolicyForUserMediaPermissionRequestIfPossible(); > >@@ -288,7 +329,6 @@ private: > static void didFinishNavigation(WKPageRef, WKNavigationRef, WKTypeRef userData, const void*); > void didFinishNavigation(WKPageRef, WKNavigationRef); > >- > // WKContextDownloadClient > static void downloadDidStart(WKContextRef, WKDownloadRef, const void*); > void downloadDidStart(WKContextRef, WKDownloadRef); >@@ -429,6 +469,7 @@ private: > bool m_shouldShowWebView { false }; > > bool m_shouldShowTouches { false }; >+ bool m_checkForAbandonedDocuments { false }; > > bool m_allowAnyHTTPSCertificateForAllowedHosts { false }; > >@@ -442,17 +483,28 @@ private: > std::unique_ptr<EventSenderProxy> m_eventSenderProxy; > > WorkQueueManager m_workQueueManager; >+ >+ struct AbandonedDocumentInfo { >+ String testURL; >+ String abandonedDocumentURL; >+ >+ AbandonedDocumentInfo() = default; >+ AbandonedDocumentInfo(String inTestURL, String inAbandonedDocumentURL) >+ : testURL(inTestURL) >+ , abandonedDocumentURL(inAbandonedDocumentURL) >+ { } >+ }; >+ HashMap<uint64_t, AbandonedDocumentInfo> m_abandonedDocumentInfo; >+ > }; > > struct TestCommand { > std::string pathOrURL; > std::string absolutePath; >- bool shouldDumpPixels { false }; > std::string expectedPixelHash; >- int timeout { 0 }; >+ WTF::Seconds timeout; >+ bool shouldDumpPixels { false }; > bool dumpJSConsoleLogInStdErr { false }; > }; > > } // namespace WTR >- >-#endif // TestController_h >diff --git a/Tools/WebKitTestRunner/TestInvocation.cpp b/Tools/WebKitTestRunner/TestInvocation.cpp >index 7d4fdac1806adc75a9c07499101357956cf71a81..ee7e5cb0692caf693080df0ad6124ef0652663af 100644 >--- a/Tools/WebKitTestRunner/TestInvocation.cpp >+++ b/Tools/WebKitTestRunner/TestInvocation.cpp >@@ -101,7 +101,7 @@ void TestInvocation::setIsPixelTest(const std::string& expectedPixelHash) > m_expectedPixelHash = expectedPixelHash; > } > >-double TestInvocation::shortTimeout() const >+WTF::Seconds TestInvocation::shortTimeout() const > { > if (!m_timeout) { > // Running WKTR directly, without webkitpy. >@@ -137,7 +137,7 @@ WKRetainPtr<WKMutableDictionaryRef> TestInvocation::createTestSettingsDictionary > WKDictionarySetItem(beginTestMessageBody.get(), useWaitToDumpWatchdogTimerKey.get(), useWaitToDumpWatchdogTimerValue.get()); > > WKRetainPtr<WKStringRef> timeoutKey = adoptWK(WKStringCreateWithUTF8CString("Timeout")); >- WKRetainPtr<WKUInt64Ref> timeoutValue = adoptWK(WKUInt64Create(m_timeout)); >+ WKRetainPtr<WKUInt64Ref> timeoutValue = adoptWK(WKUInt64Create(m_timeout.value())); > WKDictionarySetItem(beginTestMessageBody.get(), timeoutKey.get(), timeoutValue.get()); > > WKRetainPtr<WKStringRef> dumpJSConsoleLogInStdErrKey = adoptWK(WKStringCreateWithUTF8CString("DumpJSConsoleLogInStdErr")); >@@ -187,7 +187,7 @@ end: > WKInspectorClose(WKPageGetInspector(TestController::singleton().mainWebView()->page())); > #endif // !PLATFORM(IOS) > >- if (TestController::singleton().resetStateToConsistentValues(m_options)) >+ if (TestController::singleton().resetStateToConsistentValues(m_options, TestController::ResetStage::AfterTest)) > return; > > // The process is unresponsive, so let's start a new one. >diff --git a/Tools/WebKitTestRunner/TestInvocation.h b/Tools/WebKitTestRunner/TestInvocation.h >index 72173b7ebdd5a068211aab9f23a178b7ec72938b..36f606188cb6857d6382cea66e25ec2e10ce808f 100644 >--- a/Tools/WebKitTestRunner/TestInvocation.h >+++ b/Tools/WebKitTestRunner/TestInvocation.h >@@ -33,6 +33,7 @@ > #include <WebKit/WKRetainPtr.h> > #include <string> > #include <wtf/Noncopyable.h> >+#include <wtf/Seconds.h> > #include <wtf/text/StringBuilder.h> > > namespace WTR { >@@ -51,11 +52,10 @@ public: > void setIsPixelTest(const std::string& expectedPixelHash); > > // Milliseconds >- void setCustomTimeout(int duration) { m_timeout = duration; } >+ void setCustomTimeout(WTF::Seconds duration) { m_timeout = duration; } > void setDumpJSConsoleLogInStdErr(bool value) { m_dumpJSConsoleLogInStdErr = value; } > >- // Seconds >- double shortTimeout() const; >+ WTF::Seconds shortTimeout() const; > > void invoke(); > void didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody); >@@ -116,7 +116,7 @@ private: > > std::string m_expectedPixelHash; > >- int m_timeout { 0 }; >+ WTF::Seconds m_timeout; > bool m_dumpJSConsoleLogInStdErr { false }; > > // Invocation state >diff --git a/Tools/WebKitTestRunner/TestOptions.h b/Tools/WebKitTestRunner/TestOptions.h >index 6068abf8b35574d471d4349557f93020b8ef3541..286017f31a751e38a2dd4fa40d46e68e1bc5d098 100644 >--- a/Tools/WebKitTestRunner/TestOptions.h >+++ b/Tools/WebKitTestRunner/TestOptions.h >@@ -23,8 +23,7 @@ > * THE POSSIBILITY OF SUCH DAMAGE. > */ > >-#ifndef TestOptions_h >-#define TestOptions_h >+#pragma once > > #include <wtf/Vector.h> > #include <wtf/text/WTFString.h> >@@ -61,6 +60,7 @@ struct TestOptions { > bool enableColorFilter { false }; > bool punchOutWhiteBackgroundsInDarkMode { false }; > bool runSingly { false }; >+ bool checkForAbandonedDocuments { false }; > > float deviceScaleFactor { 1 }; > Vector<String> overrideLanguages; >@@ -98,7 +98,8 @@ struct TestOptions { > || enableColorFilter != options.enableColorFilter > || punchOutWhiteBackgroundsInDarkMode != options.punchOutWhiteBackgroundsInDarkMode > || jscOptions != options.jscOptions >- || runSingly != options.runSingly) >+ || runSingly != options.runSingly >+ || checkForAbandonedDocuments != options.checkForAbandonedDocuments) > return false; > > return true; >@@ -111,5 +112,3 @@ struct TestOptions { > }; > > } >- >-#endif // TestOptions_h >diff --git a/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm b/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm >index 5d495eb736e5af76634fb9b3d062ff434790db15..e9caeb44ee8e0c664a6ec86725317e07134dd5ca 100644 >--- a/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm >+++ b/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm >@@ -196,9 +196,9 @@ WKContextRef TestController::platformAdjustContext(WKContextRef context, WKConte > #endif > } > >-void TestController::platformRunUntil(bool& done, double timeout) >+void TestController::platformRunUntil(bool& done, WTF::Seconds timeout) > { >- NSDate *endDate = (timeout > 0) ? [NSDate dateWithTimeIntervalSinceNow:timeout] : [NSDate distantFuture]; >+ NSDate *endDate = (timeout > 0_s) ? [NSDate dateWithTimeIntervalSinceNow:timeout.value()] : [NSDate distantFuture]; > > while (!done && [endDate compare:[NSDate date]] == NSOrderedDescending) > [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:endDate]; >diff --git a/Tools/WebKitTestRunner/gtk/TestControllerGtk.cpp b/Tools/WebKitTestRunner/gtk/TestControllerGtk.cpp >index aa5885b6480188d1c6f3bdbd908c009439fa2a3e..b431a1516e24335e51f16c828e196b82cc28f926 100644 >--- a/Tools/WebKitTestRunner/gtk/TestControllerGtk.cpp >+++ b/Tools/WebKitTestRunner/gtk/TestControllerGtk.cpp >@@ -74,13 +74,12 @@ void TestController::platformDestroy() > { > } > >-void TestController::platformRunUntil(bool&, double timeout) >+void TestController::platformRunUntil(bool&, WTF::Seconds timeout) > { > if (timeout > 0) { > // FIXME: This conversion is now repeated in several places, it should be moved to a common place in WTF and used everywhere. >- auto timeoutDuration = Seconds { timeout }; > gint64 currentTime = g_get_monotonic_time(); >- gint64 targetTime = currentTime + std::min<gint64>(G_MAXINT64 - currentTime, timeoutDuration.microsecondsAs<int64_t>()); >+ gint64 targetTime = currentTime + std::min<gint64>(G_MAXINT64 - currentTime, timeout.microsecondsAs<int64_t>()); > ASSERT(targetTime >= currentTime); > g_source_set_ready_time(timeoutSource(), targetTime); > } else >diff --git a/Tools/WebKitTestRunner/wpe/TestControllerWPE.cpp b/Tools/WebKitTestRunner/wpe/TestControllerWPE.cpp >index b7b1e4d706c363cc69e2a3fef0e4f7d8ee005621..fe30d3de8552575a86fee6a4587b45354b18ed5c 100644 >--- a/Tools/WebKitTestRunner/wpe/TestControllerWPE.cpp >+++ b/Tools/WebKitTestRunner/wpe/TestControllerWPE.cpp >@@ -58,7 +58,7 @@ void TestController::platformInitializeContext() > { > } > >-void TestController::platformRunUntil(bool& condition, double timeout) >+void TestController::platformRunUntil(bool& condition, WTF::Seconds timeout) > { > struct TimeoutTimer { > TimeoutTimer() >@@ -70,8 +70,8 @@ void TestController::platformRunUntil(bool& condition, double timeout) > } timeoutTimer; > > timeoutTimer.timer.setPriority(G_PRIORITY_DEFAULT_IDLE); >- if (timeout >= 0) >- timeoutTimer.timer.startOneShot(Seconds(timeout)); >+ if (timeout >= 0_s) >+ timeoutTimer.timer.startOneShot(timeout); > > RunLoop::main().run(); > >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index c13ffd403f3e02ee997fd865c7434a628e9d9357..52aee244864d3454f3c0df24cf52afcd4fc5b6a6 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,19 @@ >+2018-08-17 Simon Fraser <simon.fraser@apple.com> >+ >+ Have WebKitTestRunner check for abandoned documents >+ https://bugs.webkit.org/show_bug.cgi?id=186214 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add to results.html a table at the bottom which lists tests with post-hoc data, >+ showing the list of abandoned documents for each test that has one or more. >+ Also fix an issue where tests with stderr and test with abandoned documents would >+ disappear if you checked "Unexpected results only". >+ >+ * fast/harness/full_results.json: >+ * fast/harness/results-expected.txt: >+ * fast/harness/results.html: >+ > 2018-08-17 Simon Fraser <simon.fraser@apple.com> > > Modernize results.html >diff --git a/LayoutTests/fast/harness/full_results.json b/LayoutTests/fast/harness/full_results.json >index ed235cec2bbdc9224dd924220133a9e6c9aee828..6db0a039155c1bb4a2bac89f4e4baada39fa3925 100644 >--- a/LayoutTests/fast/harness/full_results.json >+++ b/LayoutTests/fast/harness/full_results.json >@@ -40,8 +40,15 @@ ADD_RESULTS({ > "video-loop.html": { > "report": "FLAKY", > "expected": "PASS TIMEOUT", >- "actual": "TEXT PASS TIMEOUT" >- }, >+ "actual": "TEXT PASS TIMEOUT", >+ "post_hoc_data": { >+ "abandoned_documents": [ >+ "file:///Volumes/Data/slave/highsierra-release-tests-wk2/build/LayoutTests/fast/css/user-drag-none.html", >+ "file:///Volumes/Data/slave/highsierra-release-tests-wk2/build/LayoutTests/fast/css/user-drag-test.html", >+ "about:blank" >+ ] >+ } >+ } > }, > "canvas": { > "philip": { >diff --git a/LayoutTests/fast/harness/results-expected.txt b/LayoutTests/fast/harness/results-expected.txt >index 798066443506c6928172bfbd2be464a44d53fe50..707fb79e207e0df1f98fc1a908818171213677b6 100644 >--- a/LayoutTests/fast/harness/results-expected.txt >+++ b/LayoutTests/fast/harness/results-expected.txt >@@ -4,23 +4,24 @@ Only unexpected results > Use newlines in flagged list > Tests that crashed (1): flag all > >-+http/tests/contentextensions/top-url.html crash log sample history >+http/tests/contentextensions/top-url.html crash log sample history > Other crashes (2): flag all > > +DumpRenderTree-54888 crash log > +DumpRenderTree-56804 crash log >-Tests that failed text/pixel/audio diff (2): flag all >+Tests that failed text/pixel/audio diff (3): flag all > > test results actual failure expected failure history > +http/tests/storageAccess/request-storage-access-top-frame.html expected actual diff pretty diff text pass timeout history > +http/wpt/cache-storage/cache-put-keys.https.any.worker.html expected actual diff pretty diff text pass history >++webarchive/test-css-url-resources-in-stylesheets.html expected actual diff pretty diff text fail history > Tests that had no expected results (probably new) (1): flag all > > test results image results actual failure expected failure history > +svg/batik/smallFonts.svg missing history > Tests that timed out (1): flag all > >-+platform/mac/media/audio-session-category-video-paused.html expected actual diff pretty diff history >+platform/mac/media/audio-session-category-video-paused.html expected actual diff pretty diff history > Tests that had stderr output (2): flag all > > +http/tests/contentextensions/top-url.html stderr history >@@ -29,12 +30,22 @@ Flaky tests (failed the first run and passed on retry) (1): flag all > > test results image results actual failure expected failure history failures > +media/video-loop.html expected actual diff pretty diff text pass timeout pass timeout history >-Tests expected to fail but passed (4): flag all >+Tests expected to fail but passed (7): flag all > > test expected failure history > canvas/philip/tests/2d.gradient.interpolate.solid.html fail history > editing/spelling/spelling-marker-includes-hyphen.html image history > editing/spelling/spelling-markers-in-overlapping-lines.html image history >+http/tests/contentextensions/async-xhr-onerror.html pass timeout history >+http/wpt/cache-storage/cache-put-stream.https.any.html pass fail history >+webarchive/loading/object.html pass crash history > webarchive/loading/test-loading-archive-subresource-null-mimetype.html crash history >+Tests that have post-hoc data (1): flag all >+ >+media/video-loop.html >+Abandoned documents?: >+fast/css/user-drag-none.html >+fast/css/user-drag-test.html >+about:blank > httpd access log: access_log.txt > httpd error log: error_log.txt >diff --git a/LayoutTests/fast/harness/results.html b/LayoutTests/fast/harness/results.html >index 4a514069b8a3449e525f065974d016b7dafd3377..0f45a92e6790a479f9ae0d644b37e184dc25edf9 100644 >--- a/LayoutTests/fast/harness/results.html >+++ b/LayoutTests/fast/harness/results.html >@@ -34,12 +34,17 @@ tr:not(.results-row) td:first-of-type { > white-space: normal; > } > >-td:not(:first-of-type) { >+td:not(:first-of-type, .post-hoc-data) { > text-transform: lowercase; > } > > th, td { > padding: 1px 4px; >+ vertical-align: top; >+} >+ >+td:nth-child(1) { >+ min-width: 35em; > } > > th:empty, td:empty { >@@ -51,6 +56,15 @@ th { > -moz-user-select: none; > } > >+dt > sup { >+ vertical-align:text-top; >+ font-size:75%; >+} >+ >+sup > a { >+ text-decoration: none; >+} >+ > .content-container { > min-height: 0; > } >@@ -408,6 +422,11 @@ class TestResult > { > return this.info.has_stderr; > } >+ >+ hasPostHocData() >+ { >+ return 'post_hoc_data' in this.info; >+ } > }; > > class TestResults >@@ -424,6 +443,7 @@ class TestResults > this.timeoutTests = []; > this.unexpectedPassTests = []; > this.flakyPassTests = []; >+ this.testsWithPostHocData = []; > > this.hasHttpTests = false; > this.hasImageFailures = false; >@@ -484,6 +504,9 @@ class TestResults > > if (testResult.isImageFailure()) > this.hasImageFailures = true; >+ >+ if (testResult.hasPostHocData()) >+ this.testsWithPostHocData.push(testResult); > > if (testResult.isMissing()) { > // FIXME: make sure that new-run-webkit-tests spits out an -actual.txt file for tests with MISSING results. >@@ -597,6 +620,9 @@ class TestResultsController > if (this.testResults.usesExpectationsFile() && this.testResults.unexpectedPassTests.length) > this.containerElement.appendChild(this.buildOneSection(this.testResults.unexpectedPassTests, UnexpectedPassTestsSectionBuilder)); > >+ if (this.testResults.testsWithPostHocData.length) >+ this.containerElement.appendChild(this.buildOneSection(this.testResults.testsWithPostHocData, TestsWithPostHocDataSectionBuilder)); >+ > if (this.testResults.hasHttpTests) { > let httpdAccessLogLink = document.createElement('p'); > httpdAccessLogLink.innerHTML = 'httpd access log: <a href="access_log.txt">access_log.txt</a>'; >@@ -746,6 +772,17 @@ class TestResultsController > > return basePath; > } >+ >+ convertToLayoutTestBaseRelativeURL(fullURL) >+ { >+ if (fullURL.startsWith('file://')) { >+ let urlPrefix = 'file://' + this.layoutTestsBasePath(); >+ if (fullURL.startsWith(urlPrefix)) >+ return fullURL.substring(urlPrefix.length); >+ } >+ >+ return fullURL; >+ } > > shouldUseTracLinks() > { >@@ -1091,7 +1128,7 @@ class SectionBuilder { > createTableRow(testResult) > { > let tbody = document.createElement('tbody'); >- if (testResult.isExpected) >+ if (this.hideWhenShowingUnexpectedResultsOnly() && testResult.isExpected) > tbody.classList.add('expected'); > > let row = document.createElement('tr'); >@@ -1123,7 +1160,7 @@ class SectionBuilder { > > fillTestCell(testResult, cell) > { >- cell.innerHTML = '<span class=expand-button onclick="controller.toggleExpectations(this)"><span class=expand-button-text>+</span></span>' + this._resultsController.testLink(testResult); >+ cell.innerHTML = '<a class=test-link onclick="controller.checkServerIsRunning(event)" href="' + this._resultsController.layoutTestURL(testResult) + '">' + testResult.name + '</a><span class=flag onclick="controller.unflag(this)"> \u2691</span>'; > } > > fillTestResultCell(testResult, cell) >@@ -1163,7 +1200,7 @@ class FailuresSectionBuilder extends SectionBuilder { > createTableRow(testResult) > { > let tbody = document.createElement('tbody'); >- if (testResult.isExpected) >+ if (this.hideWhenShowingUnexpectedResultsOnly() && testResult.isExpected) > tbody.classList.add('expected'); > > if (testResult.isMismatchRefTest()) >@@ -1219,6 +1256,11 @@ class FailuresSectionBuilder extends SectionBuilder { > return tbody; > } > >+ fillTestCell(testResult, cell) >+ { >+ cell.innerHTML = '<span class=expand-button onclick="controller.toggleExpectations(this)"><span class=expand-button-text>+</span></span>' + this._resultsController.testLink(testResult); >+ } >+ > appendTextFailureLinks(testResult, cell) > { > cell.innerHTML += this._resultsController.textResultLinks(Utils.stripExtension(testResult.name)); >@@ -1310,18 +1352,52 @@ class UnexpectedPassTestsSectionBuilder extends SectionBuilder { > } > }; > >- > class TestsWithStdErrSectionBuilder extends SectionBuilder { > tableID() { return 'stderr-table'; } > sectionTitle() { return 'Tests that had stderr output'; } > hideWhenShowingUnexpectedResultsOnly() { return false; } > >+ fillTestCell(testResult, cell) >+ { >+ cell.innerHTML = '<span class=expand-button onclick="controller.toggleExpectations(this)"><span class=expand-button-text>+</span></span>' + this._resultsController.testLink(testResult); >+ } >+ > fillTestResultCell(testResult, cell) > { > cell.innerHTML = TestResultsController.resultLink(Utils.stripExtension(testResult.name), '-stderr.txt', 'stderr'); > } > }; > >+class TestsWithPostHocDataSectionBuilder extends SectionBuilder { >+ tableID() { return 'posthoc-data-table'; } >+ sectionTitle() { return 'Tests that have post-hoc data'; } >+ hideWhenShowingUnexpectedResultsOnly() { return false; } >+ >+ fillTestResultCell(testResult, cell) >+ { >+ cell.classList.add('post-hoc-data'); >+ // For now, the only kind of post-hoc data we have is abandoned documents. >+ let abandoned_documents = testResult.info.post_hoc_data['abandoned_documents']; >+ if (!abandoned_documents) >+ return; >+ >+ let abandonedDocsDT = document.createElement('dt'); >+ abandonedDocsDT.innerHTML = 'Abandoned documents<sup><a href="https://trac.webkit.org/wiki/Abandoned%20documents">?</a></sup>:'; >+ cell.appendChild(abandonedDocsDT); >+ >+ for (let url of abandoned_documents) { >+ let dd = document.createElement('dd'); >+ dd.innerHTML = `<a href="${url}">${this._resultsController.convertToLayoutTestBaseRelativeURL(url)}</a>`; >+ cell.appendChild(dd); >+ } >+ } >+ >+ createHistoryCell(testResult) >+ { >+ return null; >+ } >+}; >+ > class TimedOutTestsSectionBuilder extends SectionBuilder { > tableID() { return 'timeout-tests-table'; } > sectionTitle() { return 'Tests that timed out'; }
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 186214
:
341812
|
347422
|
347424
|
347425
|
347429
|
347431
|
347433
|
347445