cisco-open-source / qtwebdriver

WebDriver implementation for Qt
https://github.com/cisco-open-source/qtwebdriver/wiki
197 stars 59 forks source link

memory leak? #4

Open ebiancarelli opened 9 years ago

ebiancarelli commented 9 years ago

Is anyone else experience high memory usage and leaks when exercising this code?

I'm using qt5.2.1 and this code in conjunction with a sample python selenium script that loads Yahoo and does a search. If I run this test code a few times, I can see the allocated memory in 'htop' is climbing and not recovering. I have an advanced test suite that can drive the browser to exhaust all available memory. :-(

hekra01 commented 9 years ago

Also run Qt5.2 test suites (java/python) but have not observed such issue yet. Could you please provide your sample script?

ebiancarelli commented 9 years ago

I'll note that I'm running qt5.2.1 with several patches applied for performance improvements in WebKit. I'm not running stock qt5.2.1...I do have a basic WebView application that I am driving.

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

import unittest

class SeleniumTest(unittest.TestCase):

    def setUp(self):
        print('In setUp()')
        #self.browser = webdriver.Firefox()
        #self.browser = webdriver.Remote(command_executor='http://10.143.32.80:9517',
        self.browser = webdriver.Remote(command_executor='http://192.168.0.36:9517',
                                desired_capabilities={'browserName': 'chrome', 'browserStartWindow': '*'})
        self.browser.get('http://cnn.com')

    def tearDown(self):
        print('In tearDown()')
        self.browser.quit()

    def testPageTitle(self):
        self.browser.get('http://www.google.com')
        self.assertIn('Google', self.browser.title)

        #self.browser.get('http://www.yahoo.com')
        #assert 'Yahoo' in self.browser.title
        self.browser.get('https://msb-eng.timewarnercable.com/hnavc/hnavclient-stable/?profile=dev&noHam=true&noHydra=true&oauth.forceUserNamePwd=true')
        assert 'TWC TV' in self.browser.title

        print self.browser.title
        print self.browser.current_url
        print self.browser.current_window_handle

        classes = {}

        elems = self.browser.find_elements_by_tag_name('div')
        for elem in elems:
            class_name = elem.get_attribute('class')
            if class_name in classes:
                classes[class_name] += 1
            else:
                classes[class_name] = 1

        for class_name, count in classes.iteritems():
            print class_name, count

        elem = self.browser.find_element_by_name('p')  # Find the search box
        elem.send_keys('seleniumhq' + Keys.RETURN)

        print elem.text, elem.tag_name

if __name__ == '__main__':
    unittest.main()

WebView application

#include <QGraphicsView>
#include <QGLWidget>

#include <QtDebug>
#include <QtWebKitWidgets>

#include <iostream>

void setWindowTitle(QWindow *window)
{
    QString title =
            "QT-" QT_VERSION_STR
#ifdef QT_OPENGL_ES_2
            "-EGL"
#else
            "-GLX"
#endif
#ifdef QT_NO_DEBUG
            "-RELEASE"
#else
            "-DEBUG"
#endif
            ;

    window->setTitle(title);
}

QGraphicsView *viewForItem(QGraphicsItem *item, bool openGL = true)
{
    QGraphicsView *view = new QGraphicsView;
    view->setScene(new QGraphicsScene);
    if (openGL) {
        QGLWidget *glWidget = new QGLWidget(QGLFormat(QGL::SampleBuffers));
        view->setViewport(glWidget);
    }

    view->scene()->addItem(item);
    view->resize(1282, 722);
    view->show();

    setWindowTitle(view->windowHandle());

    return view;
}

extern void startWebDriver(int argc, char **argv);

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    QWebSettings::globalSettings()->setAttribute(QWebSettings::AcceleratedCompositingEnabled, true);

#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
    QWebSettings::globalSettings()->setAttribute(QWebSettings::Accelerated2dCanvasEnabled, true);
#endif
    QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
    QWebSettings::globalSettings()->setAttribute(QWebSettings::LocalStorageEnabled, true);

    int capacity = 20 * 1024 * 1024;
    QWebSettings::setObjectCacheCapacities(0, capacity, capacity);

    QWebSettings::enablePersistentStorage(QFileInfo(".").absoluteFilePath());

    QUrl url;

    if (argc > 1) {
        url = QUrl(argv[1]);
    } else {
        url = QUrl("https://msb-eng.timewarnercable.com/hnavc/hnavclient-stable/?profile=dev&noHam=true&noHydra=true&oauth.forceUserNamePwd=true");
        url.setUserName("hnav-vendor");
        url.setPassword("SUspuC%259");
    }

    qDebug() << url;

    QGraphicsWebView *web = new QGraphicsWebView;
    QWebPage *page = new QWebPage(web);
    page->mainFrame()->load(url);
    web->setPage(page);
    web->resize(1280, 720);

    web->setFocusPolicy(Qt::StrongFocus);

    viewForItem(web);

    startWebDriver(argc, argv);

    return app.exec();
}

webdriver.cpp

#include "webdriver_server.h"
#include "webdriver_view_transitions.h"

#include "extension_qt/q_view_runner.h"
#include "extension_qt/q_session_lifecycle_actions.h"

#include "extension_qt/qwebviewext.h"

#include "extension_qt/web_view_creator.h"
#include "extension_qt/web_view_executor.h"
#include "extension_qt/web_view_enumerator.h"

#include "extension_qt/graphics_web_view_executor.h"
#include "extension_qt/graphics_web_view_enumerator.h"

#include <iostream>

void startWebDriver(int argc, char **argv)
{
    CommandLine commandLine(CommandLine::NO_PROGRAM);
    commandLine.InitFromArgv(argc, argv);

    webdriver::ViewRunner::RegisterCustomRunner<webdriver::QViewRunner>();

    webdriver::SessionLifeCycleActions::RegisterCustomLifeCycleActions<webdriver::QSessionLifeCycleActions>();

    //webdriver::ViewEnumerator::AddViewEnumeratorImpl(new webdriver::WebViewEnumeratorImpl());
    //webdriver::ViewCmdExecutorFactory::GetInstance()->AddViewCmdExecutorCreator(new webdriver::QWebViewCmdExecutorCreator());

    webdriver::ViewEnumerator::AddViewEnumeratorImpl(new webdriver::GraphicsWebViewEnumeratorImpl());
    webdriver::ViewCmdExecutorFactory::GetInstance()->AddViewCmdExecutorCreator(new webdriver::GraphicsWebViewCmdExecutorCreator());

    webdriver::Server* server = webdriver::Server::GetInstance();
    int configureError = server->Configure(commandLine);
    if (configureError) {
        std::cout << "Error while configuring webdriver server, errorCode " << configureError << std::endl;
        return;
    }

    int startError = server->Start();
    if (startError) {
        std::cout << "Error while starting webdriver server, errorCode " << startError << std::endl;
        return;
    }
}

webview.pro

######################################################################
# Automatically generated by qmake (3.0) søn. okt. 26 15:04:50 2014
######################################################################

TEMPLATE = app
TARGET = webview
INCLUDEPATH += .

QT += widgets opengl webkitwidgets

# Input
SOURCES += main.cpp
hekra01 commented 9 years ago

Hi

WebDriver should be started in Qt main loop I suggest you start the WebDriver in the same thread as you app

int main(int argc, char **argv) {
    // etc...

    // Start WebDriver
    int startError = wd_server->Start();
    if (startError){
        std::cout << "Error while starting server, errorCode " << startError << std::endl;
        return startError;
    }

    QGraphicsWebView *web = new QGraphicsWebView;
    QWebPage *page = new QWebPage(web);
    // etc....

    return app.exec();

An example test app is available here https://github.com/cisco-open-source/qtwebdriver/blob/WD_1.X_dev/src/Test/main.cc

ebiancarelli commented 9 years ago

I updated my previous comment and included the code we use to start the web driver. We do start it from the main thread (sorry, I added that in too). Maybe I need to use different options to start the server and in my 'capabilities' set in the test?

hekra01 commented 9 years ago

Python test is fine.

    if (openGL) {
        QGLWidget *glWidget = new QGLWidget(QGLFormat(QGL::SampleBuffers));
        view->setViewport(glWidget);
    }
ebiancarelli commented 9 years ago

Have tried with and without OpenGl. I can get the memory issue to happen. I've asked another developer to reproduce my findings, and he also can see the memory leak.

We've done research into why there is a memory leak, but have not made headway due to the large WebKit codebase.

hekra01 commented 9 years ago

We reproduced the leak, even launching pages without WD. Looking into it