Skip to content
Permalink
Browse files
[GTK] Add support for running unit tests in the web process
https://bugs.webkit.org/show_bug.cgi?id=118427

Reviewed by Gustavo Noronha Silva.

Some tests, like GObject DOM bindings API tests, run entirely in
the WebProcess, so we just need to start the test from the UI
process and wait until the test finishes running in the
WebProcess. Tests are split in two files, one containing the
actual test that runs in the WebProcess and another one to add the
tests to the glib test system that works as a
proxy. WebProcessTestRunner class starts a private DBus session
bus and starts the tests sending a message to the WebExtension
waiting until it finishes or fails. WebProcess tests are created
by defining a class derived from WebProcessTest class and
implementing the static create method and the virtual runTest
method. The macro REGISTER_TEST is used by the web process tests
to register their test cases. This patch includes the migration
of the WebKitDOMNode test, all other GObject DOM bindings tests
will be migrated in the same way in follow up patches.

* UIProcess/API/gtk/tests/DOMNodeTest.cpp: Added.
(WebKitDOMNodeTest::create): Create a new WebKitDOMNodeTest.
(WebKitDOMNodeTest::webPageFromArgs): Get the pageID parameter
from the arguments dictionary.
(WebKitDOMNodeTest::testHierarchyNavigation):
(WebKitDOMNodeTest::testInsertion):
(WebKitDOMNodeTest::runTest): Run the given test.
(registerTests): Register test cases.
* UIProcess/API/gtk/tests/GNUmakefile.am: Add new files to
compilation.
* UIProcess/API/gtk/tests/TestDOMNode.cpp: Added.
(testWebKitDOMNodeHierarchyNavigation):
(testWebKitDOMNodeInsertion):
(beforeAll):
(afterAll):
* UIProcess/API/gtk/tests/TestMain.cpp:
(main): Unset DBUS_SESSION_BUS_ADDRESS environment variable to
make sure that the GLib bus singleton is initialized by the
private DBus session bus created by the tests.
* UIProcess/API/gtk/tests/WebProcessTest.cpp: Added.
(testsMap): Initialize and get the global map of tests.
(WebProcessTest::add): Add a new test to the map, keeping a
function to create the test.
(WebProcessTest::create): Create a test for the given name.
(methodCallCallback): Handle RunTest DBus method. It creates and
runs the given test.
(webkit_web_extension_initialize):Register the DBus service for
this WebExtension.
* UIProcess/API/gtk/tests/WebProcessTest.h: Added.
* UIProcess/API/gtk/tests/WebProcessTestRunner.cpp: Added.
(WebProcessTestRunner::WebProcessTestRunner): Start a private DBus
session bus and get a connection to it.
(WebProcessTestRunner::~WebProcessTestRunner): Stop the private
DBus session bus.
(WebProcessTestRunner::proxyCreatedCallback):
(WebProcessTestRunner::proxy): Create a new proxy to send messages
to the WebExtension if it doesn't exists.
(WebProcessTestRunner::onNameAppeared): Called when the DBus
service has been registered in the WebExtension and it's safe to
create a proxy.
(WebProcessTestRunner::onNameVanished): Called when the DBus
service is unregistered. This happens when the web process crash,
so we just exit here, because the g_asserts in the web process
have already registered the error message.
(WebProcessTestRunner::testFinishedCallback): Called when the
WebProcess tests has finished.
(WebProcessTestRunner::runTest): Send a message to the
WebExtension to start the given test and monitor the service.
(WebProcessTestRunner::finishTest): Save the test result and
finish the main loop.
* UIProcess/API/gtk/tests/WebProcessTestRunner.h: Added.

Canonical link: https://commits.webkit.org/137111@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@153327 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
carlosgcampos committed Jul 25, 2013
1 parent 3408458 commit 459b9442a40ed3ab1dae566273754529038fc316
@@ -1,3 +1,78 @@
2013-07-25 Carlos Garcia Campos <cgarcia@igalia.com>

[GTK] Add support for running unit tests in the web process
https://bugs.webkit.org/show_bug.cgi?id=118427

Reviewed by Gustavo Noronha Silva.

Some tests, like GObject DOM bindings API tests, run entirely in
the WebProcess, so we just need to start the test from the UI
process and wait until the test finishes running in the
WebProcess. Tests are split in two files, one containing the
actual test that runs in the WebProcess and another one to add the
tests to the glib test system that works as a
proxy. WebProcessTestRunner class starts a private DBus session
bus and starts the tests sending a message to the WebExtension
waiting until it finishes or fails. WebProcess tests are created
by defining a class derived from WebProcessTest class and
implementing the static create method and the virtual runTest
method. The macro REGISTER_TEST is used by the web process tests
to register their test cases. This patch includes the migration
of the WebKitDOMNode test, all other GObject DOM bindings tests
will be migrated in the same way in follow up patches.

* UIProcess/API/gtk/tests/DOMNodeTest.cpp: Added.
(WebKitDOMNodeTest::create): Create a new WebKitDOMNodeTest.
(WebKitDOMNodeTest::webPageFromArgs): Get the pageID parameter
from the arguments dictionary.
(WebKitDOMNodeTest::testHierarchyNavigation):
(WebKitDOMNodeTest::testInsertion):
(WebKitDOMNodeTest::runTest): Run the given test.
(registerTests): Register test cases.
* UIProcess/API/gtk/tests/GNUmakefile.am: Add new files to
compilation.
* UIProcess/API/gtk/tests/TestDOMNode.cpp: Added.
(testWebKitDOMNodeHierarchyNavigation):
(testWebKitDOMNodeInsertion):
(beforeAll):
(afterAll):
* UIProcess/API/gtk/tests/TestMain.cpp:
(main): Unset DBUS_SESSION_BUS_ADDRESS environment variable to
make sure that the GLib bus singleton is initialized by the
private DBus session bus created by the tests.
* UIProcess/API/gtk/tests/WebProcessTest.cpp: Added.
(testsMap): Initialize and get the global map of tests.
(WebProcessTest::add): Add a new test to the map, keeping a
function to create the test.
(WebProcessTest::create): Create a test for the given name.
(methodCallCallback): Handle RunTest DBus method. It creates and
runs the given test.
(webkit_web_extension_initialize):Register the DBus service for
this WebExtension.
* UIProcess/API/gtk/tests/WebProcessTest.h: Added.
* UIProcess/API/gtk/tests/WebProcessTestRunner.cpp: Added.
(WebProcessTestRunner::WebProcessTestRunner): Start a private DBus
session bus and get a connection to it.
(WebProcessTestRunner::~WebProcessTestRunner): Stop the private
DBus session bus.
(WebProcessTestRunner::proxyCreatedCallback):
(WebProcessTestRunner::proxy): Create a new proxy to send messages
to the WebExtension if it doesn't exists.
(WebProcessTestRunner::onNameAppeared): Called when the DBus
service has been registered in the WebExtension and it's safe to
create a proxy.
(WebProcessTestRunner::onNameVanished): Called when the DBus
service is unregistered. This happens when the web process crash,
so we just exit here, because the g_asserts in the web process
have already registered the error message.
(WebProcessTestRunner::testFinishedCallback): Called when the
WebProcess tests has finished.
(WebProcessTestRunner::runTest): Send a message to the
WebExtension to start the given test and monitor the service.
(WebProcessTestRunner::finishTest): Save the test result and
finish the main loop.
* UIProcess/API/gtk/tests/WebProcessTestRunner.h: Added.

2013-05-05 Geoffrey Garen <ggaren@apple.com>

Rolled back in r149527 with crash fixed.
@@ -0,0 +1,190 @@
/*
* Copyright (C) 2013 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/

#include "config.h"

#include "WebProcessTest.h"
#include <gio/gio.h>
#include <webkit2/webkit-web-extension.h>

class WebKitDOMNodeTest : public WebProcessTest {
public:
static PassOwnPtr<WebProcessTest> create() { return adoptPtr(new WebKitDOMNodeTest()); }

private:
guint64 webPageFromArgs(GVariant* args)
{
GVariantIter iter;
g_variant_iter_init(&iter, args);

const char* key;
GVariant* value;
while (g_variant_iter_loop(&iter, "{&sv}", &key, &value)) {
if (!strcmp(key, "pageID") && g_variant_classify(value) == G_VARIANT_CLASS_UINT64)
return g_variant_get_uint64(value);
}

g_assert_not_reached();
return 0;
}

bool testHierarchyNavigation(WebKitWebExtension* extension, GVariant* args)
{
WebKitWebPage* page = webkit_web_extension_get_page(extension, webPageFromArgs(args));
g_assert(WEBKIT_IS_WEB_PAGE(page));
WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
g_assert(WEBKIT_DOM_IS_DOCUMENT(document));

WebKitDOMHTMLHeadElement* head = webkit_dom_document_get_head(document);
g_assert(WEBKIT_DOM_IS_HTML_HEAD_ELEMENT(head));

// Title, head's child.
g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(head)));
GRefPtr<WebKitDOMNodeList> list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(head)));
g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), 0);
g_assert(WEBKIT_DOM_IS_HTML_TITLE_ELEMENT(node));

// Body, Head sibling.
node = webkit_dom_node_get_next_sibling(WEBKIT_DOM_NODE(head));
g_assert(WEBKIT_DOM_IS_HTML_BODY_ELEMENT(node));
WebKitDOMHTMLBodyElement* body = WEBKIT_DOM_HTML_BODY_ELEMENT(node);

// There is no third sibling
g_assert(!webkit_dom_node_get_next_sibling(node));

// Body's previous sibling is Head.
node = webkit_dom_node_get_previous_sibling(WEBKIT_DOM_NODE(body));
g_assert(WEBKIT_DOM_IS_HTML_HEAD_ELEMENT(node));

// Body has 3 children.
g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
unsigned long length = webkit_dom_node_list_get_length(list.get());
g_assert_cmpint(length, ==, 3);

// The three of them are P tags.
for (unsigned long i = 0; i < length; i++) {
node = webkit_dom_node_list_item(list.get(), i);
g_assert(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(node));
}

// Go backwards
unsigned i;
for (i = 0; node; node = webkit_dom_node_get_previous_sibling(node), i++) { }
g_assert_cmpint(i, ==, 3);

return true;
}

bool testInsertion(WebKitWebExtension* extension, GVariant* args)
{
WebKitWebPage* page = webkit_web_extension_get_page(extension, webPageFromArgs(args));
g_assert(WEBKIT_IS_WEB_PAGE(page));
WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
g_assert(WEBKIT_DOM_IS_DOCUMENT(document));

WebKitDOMHTMLElement* body = webkit_dom_document_get_body(document);
g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(body));

// Body shouldn't have any children at this point.
g_assert(!webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));

// Insert one P element.
WebKitDOMElement* p = webkit_dom_document_create_element(document, "P", 0);
g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(p));
webkit_dom_node_append_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(p), 0);

// Now it should have one, the same that we inserted.
g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
GRefPtr<WebKitDOMNodeList> list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), 0);
g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(p), node));

// Replace the P tag with a DIV tag.
WebKitDOMElement* div = webkit_dom_document_create_element(document, "DIV", 0);
g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(div));
webkit_dom_node_replace_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(p), 0);
g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
node = webkit_dom_node_list_item(list.get(), 0);
g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));

// Now remove the tag.
webkit_dom_node_remove_child(WEBKIT_DOM_NODE(body), node, 0);
list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 0);

// Test insert before. If refChild is null, insert newChild as last element of parent.
div = webkit_dom_document_create_element(document, "DIV", 0);
g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(div));
webkit_dom_node_insert_before(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(div), 0, 0);
g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
node = webkit_dom_node_list_item(list.get(), 0);
g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));

// Now insert a 'p' before 'div'.
p = webkit_dom_document_create_element(document, "P", 0);
g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(p));
webkit_dom_node_insert_before(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(p), WEBKIT_DOM_NODE(div), 0);
g_assert(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
g_assert(WEBKIT_DOM_IS_NODE_LIST(list.get()));
g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 2);
node = webkit_dom_node_list_item(list.get(), 0);
g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(p), node));
node = webkit_dom_node_list_item(list.get(), 1);
g_assert(WEBKIT_DOM_IS_HTML_ELEMENT(node));
g_assert(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));

return true;
}

virtual bool runTest(const char* testName, WebKitWebExtension* extension, GVariant* args)
{
if (!strcmp(testName, "hierarchy-navigation"))
return testHierarchyNavigation(extension, args);
if (!strcmp(testName, "insertion"))
return testInsertion(extension, args);

g_assert_not_reached();
return false;
}
};

static void __attribute__((constructor)) registerTests()
{
REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/hierarchy-navigation");
REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/insertion");
}


@@ -5,6 +5,7 @@ TEST_PROGS += \
Programs/WebKit2APITests/TestBackForwardList \
Programs/WebKit2APITests/TestContextMenu \
Programs/WebKit2APITests/TestCookieManager \
Programs/WebKit2APITests/TestDOMNode \
Programs/WebKit2APITests/TestDownloads \
Programs/WebKit2APITests/TestInspector \
Programs/WebKit2APITests/TestInspectorServer \
@@ -90,6 +91,8 @@ Libraries_libWebKit2APITestCore_la_SOURCES = \
Source/WebKit2/UIProcess/API/gtk/tests/WebKitTestBus.h \
Source/WebKit2/UIProcess/API/gtk/tests/WebKitTestServer.cpp \
Source/WebKit2/UIProcess/API/gtk/tests/WebKitTestServer.h \
Source/WebKit2/UIProcess/API/gtk/tests/WebProcessTestRunner.cpp \
Source/WebKit2/UIProcess/API/gtk/tests/WebProcessTestRunner.h \
Source/WebKit2/UIProcess/API/gtk/tests/TestMain.cpp \
Source/WebKit2/UIProcess/API/gtk/tests/TestMain.h \
Source/WebKit2/UIProcess/API/gtk/tests/WebViewTest.cpp \
@@ -120,6 +123,32 @@ Libraries_WebExtensions_libWebExtensionTest_la_CFLAGS = \
$(global_cflags)


noinst_LTLIBRARIES += Libraries/WebExtensions/libWebProcessTest.la
Libraries_WebExtensions_libWebProcessTest_la_SOURCES = \
Source/WebKit2/UIProcess/API/gtk/tests/DOMNodeTest.cpp \
Source/WebKit2/UIProcess/API/gtk/tests/WebProcessTest.cpp \
Source/WebKit2/UIProcess/API/gtk/tests/WebProcessTest.h

Libraries_WebExtensions_libWebProcessTest_la_LDFLAGS = \
-rpath ${shell pwd}/$(top_builddir)/Libraries/WebExtensions/.libs \
$(no_undefined) \
-avoid-version \
-module

Libraries_WebExtensions_libWebProcessTest_la_CPPFLAGS = \
-I$(srcdir)/Source/WebKit2/WebProcess/InjectedBundle/API/gtk \
-I$(top_builddir)/DerivedSources \
-I$(top_builddir)/DerivedSources/WebKit2/webkit2extension/include \
-DWEBKIT2_COMPILATION \
$(webkit2_tests_cppflags)

Libraries_WebExtensions_libWebProcessTest_la_CXXFLAGS = \
$(global_cxxflags)

Libraries_WebExtensions_libWebProcessTest_la_CFLAGS = \
$(global_cflags)


EXTRA_DIST += \
Source/WebKit2/UIProcess/API/gtk/tests/resources/test-cert.pem \
Source/WebKit2/UIProcess/API/gtk/tests/resources/test-key.pem \
@@ -268,4 +297,10 @@ Programs_WebKit2APITests_TestWebKitWebViewGroup_CPPFLAGS = $(webkit2_tests_cppfl
Programs_WebKit2APITests_TestWebKitWebViewGroup_LDADD = $(webkit2_tests_ldadd)
Programs_WebKit2APITests_TestWebKitWebViewGroup_LDFLAGS = $(webkit2_tests_ldflags)

Programs_WebKit2APITests_TestDOMNode_SOURCES = \
Source/WebKit2/UIProcess/API/gtk/tests/TestDOMNode.cpp
Programs_WebKit2APITests_TestDOMNode_CPPFLAGS = $(webkit2_tests_cppflags)
Programs_WebKit2APITests_TestDOMNode_LDADD = $(webkit2_tests_ldadd)
Programs_WebKit2APITests_TestDOMNode_LDFLAGS = $(webkit2_tests_ldflags)

endif # ENABLE_WEBKIT2
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2013 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2,1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/

#include "config.h"

#include "WebProcessTestRunner.h"
#include "WebViewTest.h"
#include <gtk/gtk.h>
#include <webkit2/webkit2.h>

static WebProcessTestRunner* testRunner;

static void testWebKitDOMNodeHierarchyNavigation(WebViewTest* test, gconstpointer)
{
static const char* testHTML = "<html><head><title>This is the title</title></head><body><p>1</p><p>2</p><p>3</p></body></html>";
test->loadHtml(testHTML, 0);
test->waitUntilLoadFinished();

GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add(&builder, "{sv}", "pageID", g_variant_new_uint64(webkit_web_view_get_page_id(test->m_webView)));
g_assert(testRunner->runTest("WebKitDOMNode", "hierarchy-navigation", g_variant_builder_end(&builder)));
}

static void testWebKitDOMNodeInsertion(WebViewTest* test, gconstpointer)
{
static const char* testHTML = "<html><body></body></html>";
test->loadHtml(testHTML, 0);
test->waitUntilLoadFinished();

GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add(&builder, "{sv}", "pageID", g_variant_new_uint64(webkit_web_view_get_page_id(test->m_webView)));
g_assert(testRunner->runTest("WebKitDOMNode", "insertion", g_variant_builder_end(&builder)));
}

void beforeAll()
{
testRunner = new WebProcessTestRunner();
webkit_web_context_set_web_extensions_directory(webkit_web_context_get_default(), WEBKIT_TEST_WEB_EXTENSIONS_DIR);

WebViewTest::add("WebKitDOMNode", "hierarchy-navigation", testWebKitDOMNodeHierarchyNavigation);
WebViewTest::add("WebKitDOMNode", "insertion", testWebKitDOMNodeInsertion);
}

void afterAll()
{
delete testRunner;
}

0 comments on commit 459b944

Please sign in to comment.