./ 0000755 0000156 0000165 00000000000 12703462032 011074 5 ustar jenkins jenkins ./doc/ 0000755 0000156 0000165 00000000000 12703462032 011641 5 ustar jenkins jenkins ./doc/WebView.qdoc 0000644 0000156 0000165 00000017736 12703462031 014076 0 ustar jenkins jenkins /*
* Copyright 2014-2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*!
\qmltype WebView
\inqmlmodule Ubuntu.Web 0.2
\ingroup ubuntu
\brief A webview that can be used to render web content in an application.
Here is a very simple example of how to use a WebView to render a web page:
\qml
import QtQuick 2.4
import Ubuntu.Web 0.2
WebView {
url: "http://ubuntu.com"
}
\endqml
The \c WebView component defaults to using a \l {SharedWebContext}
{shared \c WebContext} that is shared across all \c WebView instances
in a given application.
\sa SharedWebContext
*/
/*!
\qmlproperty url WebView::url
The URL of the current page.
*/
/*!
\qmlproperty string WebView::title
The title of the current page.
*/
/*!
\qmlproperty url WebView::icon
The URL of the favicon of the current page.
*/
/*!
\qmlproperty bool WebView::canGoBack
Whether the navigation history has a previous entry to navigate back.
\sa goBack, canGoForward
*/
/*!
\qmlproperty bool WebView::canGoForward
Whether the navigation history has a next entry to navigate forward.
\sa goForward, canGoBack
*/
/*!
\qmlproperty bool WebView::incognito
Whether the WebView is being used in private browsing mode,
where no data is persisted across sessions.
*/
/*!
\qmlproperty bool WebView::loading
Whether the current page is loading.
\sa loadProgress, stop, reload
*/
/*!
\qmlproperty bool WebView::fullscreen
Whether the current page requested fullscreen display.
*/
/*!
\qmlproperty int WebView::loadProgress
The load progress of the current page
(as a integer value between 0 and 100).
\sa loading
*/
/*!
\qmlproperty component WebView::alertDialog
The QML component that will be instantiated to display
a JavaScript alert dialog.
\sa confirmDialog, promptDialog, beforeUnloadDialog
*/
/*!
\qmlproperty component WebView::confirmDialog
The QML component that will be instantiated to display
a JavaScript confirmation dialog.
\sa alertDialog, promptDialog, beforeUnloadDialog
*/
/*!
\qmlproperty component WebView::promptDialog
The QML component that will be instantiated to display
a JavaScript prompt dialog.
\sa alertDialog, confirmDialog, beforeUnloadDialog
*/
/*!
\qmlproperty component WebView::beforeUnloadDialog
The QML component that will be instantiated to display
a JavaScript confirmation when the user initiates a navigation away from
the current page, if the page has defined an \c onBeforeUnload handler.
\sa alertDialog, confirmDialog, promptDialog
*/
/*!
\qmlproperty component WebView::filePicker
The QML component that will be instantiated to let the user select files
when the user clicks an \c {} element
on the current page.
*/
/*!
\qmlproperty WebContext WebView::context
The web context associated to this WebView.
By default a \l {SharedWebContext} {shared context} is used which should
fit most use cases, do not override unless you really need a finer control
over the context.
*/
/*!
\qmlproperty list WebView::navigationHistory
The navigation history (back/forward entries) stored as a list model
with a \c currentIndex property. Each entry exposes the URL and title of
the corresponding page, as well as a timestamp of when it was visited.
*/
/*!
\qmlproperty ActionList WebView::contextualActions
A list of actions that the user will be presented with when invoking a
context menu (by way of a right click on desktop, or a long press on a
touch-enabled device, on an image or a hyperlink).
By default the list is empty, and no menu is shown.
User-defined actions can access the \l {contextModel} {context model}.
Example of user-defined actions:
\code
import Ubuntu.Components 1.3
import Ubuntu.Web 0.2
WebView {
contextualActions: ActionList {
Action {
text: i18n.tr("Open link in browser")
enabled: contextModel && contextModel.linkUrl.toString()
onTriggered: Qt.openUrlExternally(contextModel.linkUrl)
}
}
}
\endcode
\sa contextModel
*/
/*!
\deprecated
\qmlproperty QtObject WebView::contextualData
This property is deprecated, use the \l {contextModel} property instead.
An object that holds the contextual data associated with the current context
menu. User-defined \l {contextualActions} {contextual actions} can use this
data to process it when triggered.
It has the following properties:
\list
\li href (url): the full URI of the hyperlink, if any
\li title (string): the title of the hyperlink, if any
\li img (url): the full URI of the image
\endlist
Note that in the case of an image enclosed inside a hyperlink, both \c href
and \c img will be available, allowing a user-defined contextual action to
operate on both elements.
\sa contextualActions, contextModel
*/
/*!
\qmlproperty QtObject WebView::contextModel
An object that holds the contextual data associated with the current context
menu, as well as methods to interact with this data. User-defined
\l {contextualActions} {contextual actions} can use this data to process it
when triggered.
It has the following properties:
\list
\li linkUrl (url): the full URI of the hyperlink, if any
\li srcUrl (url): the full URI of the image/media, if any
\li mediaType (int): the type of media (one of Oxide.WebView.MediaTypeNone,
Oxide.WebView.MediaTypeImage, Oxide.WebView.MediaTypeCanvas,
Oxide.WebView.MediaTypeAudio, Oxide.WebView.MediaTypeVideo)
\li isEditable (bool): whether the current element is editable
\li editFlags (int): for editable elements, an OR-combined list of flags that
define the current editing capabilities (Oxide.WebView.UndoCapability,
Oxide.WebView.RedoCapability, Oxide.WebView.CutCapability,
Oxide.WebView.CopyCapability, Oxide.WebView.PasteCapability,
Oxide.WebView.EraseCapability, Oxide.WebView.SelectAllCapability)
\endlist
It has the following methods:
\list
\li saveLink(): initiates a download request for the resource pointed
to by the hyperlink, if any
\li saveMedia(): initiates a download request for the media
(image, canvas, audio, video), if any
\endlist
When there is no active context menu, \c contextModel is null.
\sa contextualActions
*/
/*!
\qmlmethod void WebView::goBack()
Go back one entry in the navigation history.
\sa canGoBack, goForward
*/
/*!
\qmlmethod void WebView::goForward()
Go forward one entry in the navigation history.
\sa canGoForward, goBack
*/
/*!
\qmlmethod void WebView::stop()
Stop loading the current page.
Does nothing if there is no page currently loading.
\sa reload, loading
*/
/*!
\qmlmethod void WebView::reload()
Reload the current page.
\sa stop
*/
/*!
\qmlmethod void WebView::loadHtml(string html, url baseUrl)
Load HTML content from memory instead of loading it from a URL.
The \c baseUrl argument is used to resolve relative URLs in the provided
content.
*/
./doc/WebContext.qdoc 0000644 0000156 0000165 00000004501 12703462031 014572 0 ustar jenkins jenkins /*
* Copyright 2014 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*!
\qmltype WebContext
\inqmlmodule Ubuntu.Web 0.2
\ingroup ubuntu
\brief A default context implementation for use with \l WebView.
This default WebContext implementation has the default user agent string
used by the Ubuntu browser as well as the UA override mechanism that allows
sending an overridden user agent for given domains/websites, based on the
form factor.
It has its data path (for cache and cookie database) set to the standard
writable data location for the current application, which is based on the
application name (see \c Qt.application.name), for example
\e ~/.local/share/myApp/.
Note that the \l WebView component already uses this default context, so
there is no need to explicitly instantiate a \c WebContext unless you
want to override some of its default properties.
\sa SharedWebContext
*/
// Note: only a subset of the properties of Oxide’s WebContext are documented
// here. This is intentional, typical applications are not expected to be
// needing the full set of properties and methods exposed by Oxide.
/*!
\qmlproperty string WebContext::userAgent
The default user agent string that will be sent with each HTTP request.
*/
/*!
\qmlproperty url WebContext::dataPath
The local path where persistent data (such as cookies) will be stored.
*/
/*!
\qmlproperty url WebContext::cachePath
The local path where network data will be cached.
If not set, it defaults to \l dataPath.
*/
/*!
\qmlproperty string WebContext::acceptLangs
The list of accepted languages (the Accept-Language HTTP header),
as a comma-separated list of language codes.
*/
./doc/ubuntu-web.qdocconf 0000644 0000156 0000165 00000001152 12703462031 015452 0 ustar jenkins jenkins project = UbuntuWeb
description = Ubuntu Web module documentation
sourcedirs = ./
sources.fileextensions = "*.qdoc"
outputdir = html
outputformats = HTML
version = 0.2
syntaxhightlighting = true
sourceencoding = UTF-8
outputencoding = UTF-8
HTML.nonavigationbar = "true"
HTML.stylesheets = \
css/base.css \
css/custom.css \
css/qtquick.css
HTML.headerstyles = \
"\n" \
"\n" \
"\n"
./doc/ubuntu-web.qdoc 0000644 0000156 0000165 00000003126 12703462031 014607 0 ustar jenkins jenkins /*
* Copyright 2014 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*!
\qmlmodule Ubuntu.Web 0.2
\title Ubuntu Web module
\brief WebView and related components
This QML module exposes a WebView component that can be used to render
web content in an application, as well as related components.
This is the preferred way of rendering web content in an Ubuntu application.
The rendering is performed by \l {https://launchpad.net/oxide} {Oxide}.
The use of QtWebKit is deprecated and unsupported.
This is version 0.2 of the module.
Note that the module was previously named
\c Ubuntu.Components.Extras.Browser, the old namespace has been kept around
for backwards compatibility but its use is discouraged, please update your
applications to import \c {Ubuntu.Web 0.2}.
Also note that importing both \c com.canonical.Oxide (or \c QtWebKit) and
\c Ubuntu.Web in the same QML file results in an undefined behaviour,
as both expose a \c WebView type.
*/
./doc/SharedWebContext.qdoc 0000644 0000156 0000165 00000002345 12703462031 015725 0 ustar jenkins jenkins /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*!
\qmltype SharedWebContext
\inqmlmodule Ubuntu.Web 0.2
\ingroup ubuntu
\brief A singleton that exposes a shared \l WebContext that can be used
by several \l WebView instances in the same application.
Note that the \l WebView component already uses the shared context.
*/
/*!
\qmlproperty WebContext SharedWebContext::sharedContext
The \l WebContext instance that several \l WebView instances can share.
*/
/*!
\qmlproperty string SharedWebContext::customUA
An alias to the shared context’s default user agent string.
*/
./doc/css/ 0000755 0000156 0000165 00000000000 12703462032 012431 5 ustar jenkins jenkins ./doc/css/base.css 0000644 0000156 0000165 00000027067 12703462031 014070 0 ustar jenkins jenkins /**
* Ubuntu Developer base stylesheet
*
* A base stylesheet containing site-wide styles
*
* @project Ubuntu Developer
* @version 1.0
* @author Canonical Web Team: Steve Edwards
* @copyright 2011 Canonical Ltd.
*/
/**
* @section Global
*/
body {
font-family: 'Ubuntu', 'Ubuntu Beta', UbuntuBeta, Ubuntu, 'Bitstream Vera Sans', 'DejaVu Sans', Tahoma, sans-serif;
font-size: 13px;
line-height: 1.4;
color: #333;
}
a {
color: #dd4814;
text-decoration: none;
outline: 0;
}
p, dl {
margin-bottom: 10px;
}
strong {
font-weight: bold;
}
em {
font-style: italic;
}
code{
padding: 10px;
font-family: 'Ubuntu Mono', 'Consolas', 'Monaco', 'DejaVu Sans Mono', Courier, monospace;
background-color: #fdf6f2;
display: block;
margin-bottom: 10px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
h1 {
font-size: 36px;
line-height: 1.1;
margin-bottom: 20px;
}
article h1,
h2 {
font-size: 24px;
line-height: 1.2;
margin-bottom: 14px;
}
h3 {
font-size: 16px;
line-height: 1.3;
margin-bottom: 8px;
}
h4 {
font-weight: bold;
}
time {
color:#999;
}
/**
* @section Structure
*/
.header-login,
.header-navigation div,
.header-content div {
margin: 0 auto;
width: 940px;
}
.header-content h1{
background-color:#ffffff;
display:inline-block;
}
.header-content h2{
background-color:#ffffff;
display:table;
}
.header-login ul {
margin: 4px 0;
float: right;
}
.header-login li {
margin-right: 10px;
float: left;
}
.header-login a {
color: #333;
}
.header-navigation {
border-top: 2px solid #dd4814;
border-bottom: 2px solid #dd4814;
background-color: #fff;
height: 54px;
clear: right;
overflow: hidden;
}
.header-navigation nav ul {
border-right: 1px solid #dd4814;
float: right;
}
.header-navigation nav li {
border-left: 1px solid #dd4814;
float: left;
height: 54px;
}
.header-navigation nav a {
padding: 18px 14px 0;
font-size: 14px;
display: block;
height: 36px;
}
.header-navigation nav a:hover {
background-color: #fcece7;
}
.header-navigation nav .current_page_item a,
.header-navigation nav .current_page_parent a,
.header-navigation nav .current_page_ancestor a {
background-color: #dd4814;
color: #fff;
}
.header-navigation input {
margin: 12px 10px 0 10px;
padding: 5px;
border-top: 1px solid #a1a1a1;
border-right: 1px solid #e0e0e0;
border-bottom: 1px solid #fff;
border-left: 1px solid #e0e0e0;
width: 90px;
font-style: italic;
color: #ccc;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
-moz-box-shadow: inset 0 1px 1px #e0e0e0;
-webkit-box-shadow: inset 0 1px 1px #e0e0e0;
box-shadow: inset 0 1px 1px #e0e0e0;
}
.header-navigation h2 {
margin: 18px 0 0 6px;
text-transform: lowercase;
font-size: 22px;
color: #dd4814;
float: left;
}
.header-navigation .logo-ubuntu {
margin-top: 12px;
float: left;
}
.header-content .header-navigation-secondary {
margin-bottom: 40px;
padding: 0;
position: relative;
z-index: 2;
}
.header-navigation-secondary div {
padding: 0;
border: 2px solid #dd4814;
-moz-border-radius: 0px 0px 4px 4px;
-webkit-border-radius: 0px 0px 4px 4px;
border-radius: 0px 0px 4px 4px;
background: #fff;
border-top: 0px;
width: 936px;
}
.header-navigation-secondary nav li {
float: left;
}
.header-navigation-secondary nav li a {
color: #333;
display: block;
height: 25px;
padding: 8px 8px 0;
}
.header-navigation-secondary nav li:hover,
.header-navigation-secondary nav .current_page_item a {
background: url("../img/sec-nav-hover.gif");
}
.header-content {
padding-bottom: 30px;
border-bottom: 1px solid #e0e0e0;
-moz-box-shadow: 0 1px 3px #e0e0e0;
-webkit-box-shadow: 0 1px 3px #e0e0e0;
box-shadow: 0 1px 3px #e0e0e0;
margin-bottom: 3px;
position: relative;
overflow: hidden;
}
footer {
padding: 10px 10px 40px 10px;
position: relative;
-moz-border-radius: 0 0 4px 4px;
-webkit-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
font-size: 12px;
background: url("../img/background-footer.png") repeat scroll 0 0 #f7f6f5;
}
footer div {
margin: 0 auto;
padding: 0 10px;
width: 940px;
}
footer a {
color: #000;
}
footer nav ul {
margin: 10px 17px 30px 0;
width: 172px;
display: inline-block;
vertical-align: top;
height: auto;
zoom: 1;
*display: inline;
}
footer nav ul.last {
margin-right: 0;
}
footer nav li {
margin-bottom: 8px;
}
footer nav li:first-child {
font-weight: bold;
}
footer p {
margin-bottom: 0;
}
#content {
padding-top: 35px;
}
.arrow-nav {
display: none;
position: absolute;
top: -1px;
z-index: 3;
}
.shadow {
margin: 30px 0 3px 0;
border-bottom: 1px solid #e0e0e0;
-moz-box-shadow: 0 2px 3px #e0e0e0;
-webkit-box-shadow: 0 2px 3px #e0e0e0;
box-shadow: 0 2px 3px #e0e0e0;
height: 3px;
}
/**
* @section Site-wide
*/
#content h2{
font-size:24px;
}
.box-orange {
padding: 10px;
border: 3px solid #dd4814;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
.box-orange .link-action-small {
float: right;
margin: 0 0 0 20px;
}
.link-bug {
margin-left: 10px;
color: #999;
}
.link-action {
float: left;
margin-bottom: 20px;
padding: 8px 12px;
display: block;
background-color: #dd4814;
color: #fff;
-moz-border-radius: 20px;
-webkit-border-radius: 20px;
border-radius: 20px;
font-size: 16px;
line-height: 1.3;
border-top: 3px solid #e6633a;
border-bottom: 3px solid #c03d14;
}
.link-action2 {
float: left;
display: block;
color: #fff;
font-size: 16px;
line-height: 1.3;
}
.link-action2 span{
display:block;
float:left;
}
.link-action2 .cta-left{
background:url(../img/button-cta-left.png) no-repeat;
width:22px;
height:48px;
}
.link-action2 .cta-center{
background:url(../img/button-cta-slice.png) repeat-x;
line-height:45px;
height:48px;
}
.link-action2 .cta-right{
background:url(../img/button-cta-right.png) no-repeat;
width:22px;
height:48px;
}
.link-action-small {
float: left;
display: block;
color: #fff;
font-size: 16px;
}
.link-action-small span{
display:block;
float:left;
height:42px;
}
.link-action-small .cta-left{
background:url(../img/button-cta-left-small.png) no-repeat;
width:19px;
}
.link-action-small .cta-center{
background:url(../img/button-cta-slice-small.png) repeat-x;
line-height:42px;
}
.link-action-small .cta-right{
background:url(../img/button-cta-right-small.png) no-repeat;
width:19px;
}
.link-action:active {
position: relative;
top: 1px;
}
.link-action2:active {
position: relative;
top: 1px;
}
.link-action-small:active {
position: relative;
top: 1px;
}
.list-bullets li {
margin-bottom: 10px;
list-style: disc;
list-style-position: inside;
}
.box {
margin-bottom: 30px;
padding: 15px;
border: 1px solid #aea79f;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
.box-padded {
margin-bottom: 30px;
padding: 5px;
border: 2px solid #aea79f;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
background: url("../img/pattern-featured.gif") repeat scroll 0 0 #ebe9e7;
overflow: hidden;
}
.box-padded h3 {
margin: 5px 0 10px 5px;
}
.box-padded div {
padding: 10px;
border: 1px solid #aea79f;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
background-color: #fff;
overflow: hidden;
}
.box-padded li {
padding: 0 10px;
float: left;
width: 211px;
border-right: 1px dotted #aea79f;
}
.box-padded li.first {
padding: 0;
margin-bottom: 0;
}
.box-padded li.last {
border: 0;
width: 217px;
}
.box-padded img {
margin: 0 10px 50px 0;
float: left;
-moz-border-radius: 8px;
-webkit-border-radius: 8px;
border-radius: 8px;
}
.box-clear {
margin-bottom: 40px;
}
.box-clear .grid-4.first {
margin-right: 15px;
padding-right: 15px;
}
.box-clear .grid-4 {
margin-left: 0;
margin-right: 10px;
padding-right: 10px;
width: 298px;
}
.box-clear time {
display: block;
border-bottom: 1px dotted #aea79f;
padding-bottom: 10px;
margin-bottom: 10px;
}
.box-clear div.first {
border-right: 1px dotted #aea79f;
}
.box-clear a {
display: block;
}
.box-clear .rss {
background: url("../img/rss.jpg") no-repeat scroll 0 center;
padding-left: 20px;
}
.box-clear .location {
display: block;
margin-bottom: 1px;
}
.box-clear .last {
margin: 0;
padding-right: 0;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
width: 293px;
}
/* Widgets */
.ui-state-focus {
outline: none;
}
.ui-accordion {
border-bottom: 1px dotted #aea79f;
}
.ui-accordion a {
display: block;
}
.ui-accordion h3 {
margin-bottom: 0;
border-top: 1px dotted #aea79f;
position: relative;
font-size: 13px;
font-weight: bold;
}
.ui-accordion h3 a {
padding: 10px 0;
color: #333;
}
.ui-accordion h4 {
margin-bottom: 5px;
}
.ui-accordion div fieldset {
padding-bottom: 5px;
}
.ui-accordion div li,
.ui-accordion div input {
margin-bottom: 10px;
}
.ui-accordion .ui-icon {
position: absolute;
top: 15px;
right: 0;
display: block;
width: 8px;
height: 8px;
background: url("../img/icon-accordion-inactive.png") 0 0 no-repeat transparent;
}
.ui-accordion .ui-state-active .ui-icon {
background-image: url("../img/icon-accordion-active.png");
}
.ui-accordion .current_page_item a {
color: #333;
}
.container-tweet {
-moz-border-radius: 4px 4px 4px 4px;
-webkit-border-radius: 4px 4px 4px 4px;
border-radius: 4px 4px 4px 4px;
padding: 10px 10px 10px;
background-color: #f7f7f7;
}
.container-tweet .tweet-follow {
margin-top: 10px;
margin-bottom: -10px;
padding-left: 55px;
padding-bottom: 6px;
background: url("../img/tweet-follow.png") 0 5px no-repeat;
display: block;
}
.container-tweet .tweet-follow span {
font-size: 16px;
font-weight: bold;
line-height: 1.2;
display: block;
}
.tweet a {
display: inline;
}
.tweet .tweet_text {
padding: 10px;
background-color: #fff;
-moz-border-radius: 4px 4px 4px 4px;
-webkit-border-radius: 4px 4px 4px 4px;
border-radius: 4px 4px 4px 4px;
border: 1px solid #dd4814;
font-size: 16px;
display: block;
clear: both;
}
.tweet.tweet-small .tweet_text {
font-size: inherit;
}
.tweet .tweet_text a {
color: #333;
}
.tweet .tweet_time,
.tweet .tweet_user_and_time {
padding: 15px 0 10px 0;
position: relative;
top: -2px;
background: url("../img/tweet-arrow.png") no-repeat;
display: block;
}
.tweet .tweet_odd .tweet_time,
.tweet .tweet_odd .tweet_user_and_time {
background-position: right 0;
float: right;
}
.tweet .tweet_even .tweet_time,
.tweet .tweet_even .tweet_user_and_time {
background-position: left 0;
float: left;
}
/* Search */
#content .list-search li {
list-style-type:none;
border:0px;
margin-bottom: 15px;
padding-top: 15px;
}
/* Blog */
.blog-article #nav-single {
margin-top: 30px;
margin-bottom: 30px;
}
.blog-article #nav-single .nav-next {
float: right;
}
.blog-article article header .entry-meta {
margin-bottom: 20px;
}
.blog-article article .entry-meta {
color: #999;
}
.blog-article #respond form input[type="submit"] {
float: left;
cursor: pointer;
margin-bottom: 20px;
padding: 8px 12px;
display: block;
background-color: #dd4814;
color: #fff;
-moz-border-radius: 20px;
-webkit-border-radius: 20px;
border-radius: 20px;
font-size: 16px;
line-height: 1.3;
border-top: 3px solid #e6633a;
border-left: 3px solid #e6633a;
border-right: 3px solid #e6633a;
border-bottom: 3px solid #c03d14;
}
.blog-article #respond form input[type="submit"]:active {
position: relative;
top: 1px;
}
.alignnone{
float:left;
margin:10px 20px 10px 0;
}
.alignleft{
float:left;
margin:10px 20px 10px 0;
}
.alignright{
float:right;
margin:10px 0 10px 20px;
}
.aligncenter{
float:left;
margin:10px 20px 10px 0;
}
.entry-content h2, .entry-content h3{
margin-top:20px;
}
.entry-content ul li{
list-style-type: circle;
margin-left:16px;
}
.entry-content hr{
border:none;
border-top: 1px dotted #AEA79F;
}
./doc/css/custom.css 0000644 0000156 0000165 00000000164 12703462031 014455 0 ustar jenkins jenkins li#buildversion {
display: none;
}
div.toc {
display: none;
}
span.type a:visited {
color: #dd4814;
}
./doc/css/qtquick.css 0000644 0000156 0000165 00000033160 12703462031 014626 0 ustar jenkins jenkins /*
* Copyright 2013 Canonical Ltd.
*
* This program 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; version 3.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
@media screen
{
/* basic elements */
html
{
color: #000000;
background: #FFFFFF;
}
table
{
border-collapse: collapse;
border-spacing: 0;
}
fieldset, img
{
border: 0;
max-width:100%;
}
address, caption, cite, code, dfn, em, strong, th, var, optgroup
{
font-style: inherit;
font-weight: inherit;
}
del, ins
{
text-decoration: none;
}
ol li
{
list-style: decimal;
}
ul li
{
list-style: none;
}
caption, th
{
text-align: left;
}
h1.title
{
font-weight: bold;
font-size: 150%;
}
h0
{
font-weight: bold;
font-size: 130%;
}
h1, h2, h3, h4, h5, h6
{
font-size: 100%;
}
q:before, q:after
{
content: '';
}
abbr, acronym
{
border: 0;
font-variant: normal;
}
sup, sub
{
vertical-align: baseline;
}
tt, .qmlreadonly span, .qmldefault span
{
word-spacing:0.5em;
}
legend
{
color: #000000;
}
strong
{
font-weight: bold;
}
em
{
font-style: italic;
}
body
{
margin: 0 1.5em 0 1.5em;
font-family: ubuntu;
line-height: normal
}
a
{
color: #00732F;
text-decoration: none;
}
hr
{
background-color: #E6E6E6;
border: 1px solid #E6E6E6;
height: 1px;
width: 100%;
text-align: left;
margin: 1.5em 0 1.5em 0;
}
pre
{
border: 1px solid #DDDDDD;
-moz-border-radius: 0.7em 0.7em 0.7em 0.7em;
-webkit-border-radius: 0.7em 0.7em 0.7em 0.7em;
border-radius: 0.7em 0.7em 0.7em 0.7em;
padding: 1em 1em 1em 1em;
overflow-x: auto;
}
table, pre
{
-moz-border-radius: 0.7em 0.7em 0.7em 0.7em;
-webkit-border-radius: 0.7em 0.7em 0.7em 0.7em;
border-radius: 0.7em 0.7em 0.7em 0.7em;
background-color: #F6F6F6;
border: 1px solid #E6E6E6;
border-collapse: separate;
margin-bottom: 2.5em;
}
pre {
font-size: 90%;
display: block;
overflow:hidden;
}
thead
{
margin-top: 0.5em;
font-weight: bold
}
th
{
padding: 0.5em 1.5em 0.5em 1em;
background-color: #E1E1E1;
border-left: 1px solid #E6E6E6;
}
td
{
padding: 0.25em 1.5em 0.25em 1em;
}
td.rightAlign
{
padding: 0.25em 0.5em 0.25em 1em;
}
table tr.odd
{
border-left: 1px solid #E6E6E6;
background-color: #F6F6F6;
color: black;
}
table tr.even
{
border-left: 1px solid #E6E6E6;
background-color: #ffffff;
color: #202020;
}
div.float-left
{
float: left; margin-right: 2em
}
div.float-right
{
float: right; margin-left: 2em
}
span.comment
{
color: #008B00;
}
span.string, span.char
{
color: #000084;
}
span.number
{
color: #a46200;
}
span.operator
{
color: #202020;
}
span.keyword
{
color: #840000;
}
span.name
{
color: black
}
span.type
{
font-weight: bold
}
span.type a:visited
{
color: #0F5300;
}
span.preprocessor
{
color: #404040
}
/* end basic elements */
/* font style elements */
.heading
{
font-weight: bold;
font-size: 125%;
}
.subtitle
{
font-size: 110%
}
.small-subtitle
{
font-size: 100%
}
.red
{
color:red;
}
/* end font style elements */
/* global settings*/
.header, .footer
{
display: block;
clear: both;
overflow: hidden;
}
/* end global settings*/
/* header elements */
.header .qtref
{
color: #00732F;
font-weight: bold;
font-size: 130%;
}
.header .content
{
margin-left: 5px;
margin-top: 5px;
margin-bottom: 0.5em;
}
.header .breadcrumb
{
font-size: 90%;
padding: 0.5em 0 0.5em 1em;
margin: 0;
background-color: #fafafa;
height: 1.35em;
border-bottom: 1px solid #d1d1d1;
}
.header .breadcrumb ul
{
margin: 0;
padding: 0;
}
.header .content
{
word-wrap: break-word;
}
.header .breadcrumb ul li
{
float: left;
background: url(../images/breadcrumb.png) no-repeat 0 3px;
padding-left: 1.5em;
margin-left: 1.5em;
}
.header .breadcrumb ul li.last
{
font-weight: normal;
}
.header .breadcrumb ul li a
{
color: #00732F;
}
.header .breadcrumb ul li.first
{
background-image: none;
padding-left: 0;
margin-left: 0;
}
.header .content ol li {
background: none;
margin-bottom: 1.0em;
margin-left: 1.2em;
padding-left: 0
}
.header .content li
{
background: url(../images/bullet_sq.png) no-repeat 0 5px;
margin-bottom: 1em;
padding-left: 1.2em;
}
/* end header elements */
/* content elements */
.content h1
{
font-weight: bold;
font-size: 130%
}
.content h2
{
font-weight: bold;
font-size: 120%;
width: 100%;
}
.content h3
{
font-weight: bold;
font-size: 110%;
width: 100%;
}
.content table p
{
margin: 0
}
.content ul
{
padding-left: 2.5em;
}
.content li
{
padding-top: 0.25em;
padding-bottom: 0.25em;
}
.content ul img {
vertical-align: middle;
}
.content a:visited
{
color: #4c0033;
text-decoration: none;
}
.content a:visited:hover
{
color: #4c0033;
text-decoration: underline;
}
a:hover
{
color: #4c0033;
text-decoration: underline;
}
descr p a
{
text-decoration: underline;
}
.descr p a:visited
{
text-decoration: underline;
}
.alphaChar{
width:95%;
background-color:#F6F6F6;
border:1px solid #E6E6E6;
-moz-border-radius: 7px 7px 7px 7px;
border-radius: 7px 7px 7px 7px;
-webkit-border-radius: 7px 7px 7px 7px;
font-size:12pt;
padding-left:10px;
margin-top:10px;
margin-bottom:10px;
}
.flowList{
/*vertical-align:top;*/
/*margin:20px auto;*/
column-count:3;
-webkit-column-count:3;
-moz-column-count:3;
/*
column-width:100%;
-webkit-column-width:200px;
-col-column-width:200px;
*/
column-gap:41px;
-webkit-column-gap:41px;
-moz-column-gap:41px;
column-rule: 1px dashed #ccc;
-webkit-column-rule: 1px dashed #ccc;
-moz-column-rule: 1px dashed #ccc;
}
.flowList dl{
}
.flowList dd{
/*display:inline-block;*/
margin-left:10px;
min-width:250px;
line-height: 1.5;
min-width:100%;
min-height:15px;
}
.flowList dd a{
}
.mainContent
{
padding-left:5px;
}
.content .flowList p{
padding:0px;
}
.content .alignedsummary
{
margin: 15px;
}
.qmltype
{
text-align: center;
font-size: 120%;
}
.qmlreadonly
{
padding-left: 5px;
float: right;
color: #254117;
}
.qmldefault
{
padding-left: 5px;
float: right;
color: red;
}
.qmldoc
{
}
.generic .alphaChar{
margin-top:5px;
}
.generic .odd .alphaChar{
background-color: #F6F6F6;
}
.generic .even .alphaChar{
background-color: #FFFFFF;
}
.memItemRight{
padding: 0.25em 1.5em 0.25em 0;
}
.highlightedCode
{
margin: 1.0em;
}
.annotated td {
padding: 0.25em 0.5em 0.25em 0.5em;
}
.toc
{
font-size: 80%
}
.header .content .toc ul
{
padding-left: 0px;
}
.content .toc h3 {
border-bottom: 0px;
margin-top: 0px;
}
.content .toc h3 a:hover {
color: #00732F;
text-decoration: none;
}
.content .toc .level2
{
margin-left: 1.5em;
}
.content .toc .level3
{
margin-left: 3.0em;
}
.content ul li
{
background: url(../images/bullet_sq.png) no-repeat 0 0.7em;
padding-left: 1em
}
.content .toc li
{
background: url(../images/bullet_dn.png) no-repeat 0 5px;
padding-left: 1em
}
.relpage
{
-moz-border-radius: 7px 7px 7px 7px;
-webkit-border-radius: 7px 7px 7px 7px;
border-radius: 7px 7px 7px 7px;
border: 1px solid #DDDDDD;
padding: 25px 25px;
clear: both;
}
.relpage ul
{
float: none;
padding: 1.5em;
}
h3.fn, span.fn
{
-moz-border-radius:7px 7px 7px 7px;
-webkit-border-radius:7px 7px 7px 7px;
border-radius:7px 7px 7px 7px;
background-color: #F6F6F6;
border-width: 1px;
border-style: solid;
border-color: #E6E6E6;
font-weight: bold;
word-spacing:3px;
padding:3px 5px;
}
.functionIndex {
font-size:12pt;
word-spacing:10px;
margin-bottom:10px;
background-color: #F6F6F6;
border-width: 1px;
border-style: solid;
border-color: #E6E6E6;
-moz-border-radius: 7px 7px 7px 7px;
-webkit-border-radius: 7px 7px 7px 7px;
border-radius: 7px 7px 7px 7px;
width:100%;
}
.centerAlign
{
text-align:center;
}
.rightAlign
{
text-align:right;
}
.leftAlign
{
text-align:left;
}
.topAlign{
vertical-align:top
}
.functionIndex a{
display:inline-block;
}
/* end content elements */
/* footer elements */
.footer
{
color: #393735;
font-size: 0.75em;
text-align: center;
padding-top: 1.5em;
padding-bottom: 1em;
background-color: #E6E7E8;
margin: 0;
}
.footer p
{
margin: 0.25em
}
.small
{
font-size: 0.5em;
}
/* end footer elements */
.item {
float: left;
position: relative;
width: 100%;
overflow: hidden;
}
.item .primary {
margin-right: 220px;
position: relative;
}
.item hr {
margin-left: -220px;
}
.item .secondary {
float: right;
width: 200px;
position: relative;
}
.item .cols {
clear: both;
display: block;
}
.item .cols .col {
float: left;
margin-left: 1.5%;
}
.item .cols .col.first {
margin-left: 0;
}
.item .cols.two .col {
width: 45%;
}
.item .box {
margin: 0 0 10px 0;
}
.item .box h3 {
margin: 0 0 10px 0;
}
.cols.unclear {
clear:none;
}
}
/* end of screen media */
/* start of print media */
@media print
{
input, textarea, .header, .footer, .toolbar, .feedback, .wrapper .hd, .wrapper .bd .sidebar, .wrapper .ft, #feedbackBox, #blurpage, .toc, .breadcrumb, .toolbar, .floatingResult
{
display: none;
background: none;
}
.content
{
background: none;
display: block;
width: 100%; margin: 0; float: none;
}
}
/* end of print media */
/* modify the TOC layouts */
div.toc ul {
padding-left: 20px;
}
div.toc li {
padding-left: 4px;
}
/* Remove the border around images*/
a img
{
border:none;
}
/*Add styling to the front pages*/
.threecolumn_area
{
padding-top: 20px;
padding-bottom: 20px;
}
.threecolumn_piece
{
display: inline-block;
margin-left: 78px;
margin-top: 8px;
padding: 0;
vertical-align: top;
width: 25.5%;
}
div.threecolumn_piece ul {
list-style-type: none;
padding-left: 0px;
margin-top: 2px;
}
div.threecolumn_piece p {
margin-bottom: 7px;
color: #5C626E;
text-decoration: none;
font-weight: bold;
}
div.threecolumn_piece li {
padding-left: 0px;
margin-bottom: 5px;
}
div.threecolumn_piece a {
font-weight: normal;
}
/* Add style to guide page*/
.fourcolumn_area
{
padding-top: 20px;
padding-bottom: 20px;
}
.fourcolumn_piece
{
display: inline-block;
margin-left: 35px;
margin-top: 8px;
padding: 0;
vertical-align: top;
width: 21.3%;
}
div.fourcolumn_piece ul {
list-style-type: none;
padding-left: 0px;
margin-top: 2px;
}
div.fourcolumn_piece p {
margin-bottom: 7px;
color: #40444D;
text-decoration: none;
font-weight: bold;
}
div.fourcolumn_piece li {
padding-left: 0px;
margin-bottom: 5px;
}
div.fourcolumn_piece a {
font-weight: normal;
}
./doc/CMakeLists.txt 0000644 0000156 0000165 00000001030 12703462031 014372 0 ustar jenkins jenkins project(ubuntu-web-doc)
find_program(QDOC_EXECUTABLE qdoc)
if(QDOC_EXECUTABLE STREQUAL "QDOC_EXECUTABLE-NOTFOUND")
message(WARNING "qdoc not found, documentation cannot be built")
else()
add_custom_target(doc ALL
COMMAND ${QDOC_EXECUTABLE}
-outputdir ${CMAKE_CURRENT_BINARY_DIR}/html
${CMAKE_CURRENT_SOURCE_DIR}/ubuntu-web.qdocconf)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
DESTINATION ${CMAKE_INSTALL_DATADIR}/doc/ubuntu-web)
endif()
./cmake/ 0000755 0000156 0000165 00000000000 12703462032 012154 5 ustar jenkins jenkins ./cmake/EnableCoverageReport.cmake 0000644 0000156 0000165 00000015350 12703462031 017217 0 ustar jenkins jenkins # - Creates a special coverage build type and target on GCC.
#
# Defines a function ENABLE_COVERAGE_REPORT which generates the coverage target
# for selected targets. Optional arguments to this function are used to filter
# unwanted results using globbing expressions. Moreover targets with tests for
# the source code can be specified to trigger regenerating the report if the
# test has changed
#
# ENABLE_COVERAGE_REPORT(TARGETS target... [FILTER filter...] [TESTS test targets...])
#
# To generate a coverage report first build the project with
# CMAKE_BUILD_TYPE=coverage, then call make test and afterwards make coverage.
#
# The coverage report is based on gcov. Depending on the availability of lcov
# a HTML report will be generated and/or an XML report of gcovr is found.
# The generated coverage target executes all found solutions. Special targets
# exist to create e.g. only the xml report: coverage-xml.
#
# Copyright (C) 2010 by Johannes Wienke
#
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General
# Public License as published by the Free Software Foundation;
# either version 2, or (at your option)
# any later version.
#
# This program 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 General Public License for more details.
#
INCLUDE(ParseArguments)
FIND_PACKAGE(Lcov)
FIND_PACKAGE(gcovr)
FUNCTION(ENABLE_COVERAGE_REPORT)
# argument parsing
PARSE_ARGUMENTS(ARG "FILTER;TARGETS;TESTS;EXCLUDES" "" ${ARGN})
SET(COVERAGE_RAW_FILE "${CMAKE_BINARY_DIR}/coverage.raw.info")
SET(COVERAGE_FILTERED_FILE "${CMAKE_BINARY_DIR}/coverage.info")
SET(COVERAGE_REPORT_DIR "${CMAKE_BINARY_DIR}/coveragereport")
SET(COVERAGE_XML_FILE "${CMAKE_BINARY_DIR}/coverage.xml")
SET(COVERAGE_XML_COMMAND_FILE "${CMAKE_BINARY_DIR}/coverage-xml.cmake")
# decide if there is any tool to create coverage data
SET(TOOL_FOUND FALSE)
IF(LCOV_FOUND OR GCOVR_FOUND)
SET(TOOL_FOUND TRUE)
ENDIF()
IF(NOT TOOL_FOUND)
MESSAGE(STATUS "Cannot enable coverage targets because neither lcov nor gcovr are found.")
ENDIF()
STRING(TOLOWER "${CMAKE_BUILD_TYPE}" COVERAGE_BUILD_TYPE)
IF(CMAKE_COMPILER_IS_GNUCXX AND TOOL_FOUND AND "${COVERAGE_BUILD_TYPE}" MATCHES "coverage")
MESSAGE(STATUS "Coverage support enabled for targets: ${ARG_TARGETS}")
# create coverage build type
SET(CMAKE_CXX_FLAGS_COVERAGE ${CMAKE_CXX_FLAGS_DEBUG} PARENT_SCOPE)
SET(CMAKE_C_FLAGS_COVERAGE ${CMAKE_C_FLAGS_DEBUG} PARENT_SCOPE)
SET(CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES} coverage PARENT_SCOPE)
# instrument targets
SET_TARGET_PROPERTIES(${ARG_TARGETS} PROPERTIES COMPILE_FLAGS --coverage
LINK_FLAGS --coverage)
# html report
IF (LCOV_FOUND)
MESSAGE(STATUS "Enabling HTML coverage report")
# set up coverage target
ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_RAW_FILE}
COMMAND ${LCOV_EXECUTABLE} -c -d ${CMAKE_BINARY_DIR} -o ${COVERAGE_RAW_FILE}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Collecting coverage data"
DEPENDS ${ARG_TARGETS} ${ARG_TESTS}
VERBATIM)
# filter unwanted stuff
LIST(LENGTH ARG_FILTER FILTER_LENGTH)
IF(${FILTER_LENGTH} GREATER 0)
SET(FILTER COMMAND ${LCOV_EXECUTABLE})
FOREACH(F ${ARG_FILTER})
SET(FILTER ${FILTER} -r ${COVERAGE_FILTERED_FILE} ${F})
ENDFOREACH()
SET(FILTER ${FILTER} -o ${COVERAGE_FILTERED_FILE})
ELSE()
SET(FILTER "")
ENDIF()
ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_FILTERED_FILE}
COMMAND ${LCOV_EXECUTABLE} -e ${COVERAGE_RAW_FILE} "${CMAKE_SOURCE_DIR}*" -o ${COVERAGE_FILTERED_FILE}
${FILTER}
DEPENDS ${COVERAGE_RAW_FILE}
COMMENT "Filtering recorded coverage data for project-relevant entries"
VERBATIM)
ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_REPORT_DIR}
COMMAND ${CMAKE_COMMAND} -E make_directory ${COVERAGE_REPORT_DIR}
COMMAND ${GENHTML_EXECUTABLE} --legend --show-details -t "${PROJECT_NAME} test coverage" -o ${COVERAGE_REPORT_DIR} ${COVERAGE_FILTERED_FILE}
DEPENDS ${COVERAGE_FILTERED_FILE}
COMMENT "Generating HTML coverage report in ${COVERAGE_REPORT_DIR}"
VERBATIM)
ADD_CUSTOM_TARGET(coverage-html
DEPENDS ${COVERAGE_REPORT_DIR})
ENDIF()
# xml coverage report
IF(GCOVR_FOUND)
MESSAGE(STATUS "Enabling XML coverage report")
# gcovr cannot write directly to a file so the execution needs to
# be wrapped in a cmake file that generates the file output
FILE(WRITE ${COVERAGE_XML_COMMAND_FILE}
"SET(ENV{LANG} en)\n")
FILE(APPEND ${COVERAGE_XML_COMMAND_FILE}
"EXECUTE_PROCESS(COMMAND \"${GCOVR_EXECUTABLE}\" -x -e \"${ARG_EXCLUDES}\" -r \"${CMAKE_SOURCE_DIR}\" OUTPUT_FILE \"${COVERAGE_XML_FILE}\" WORKING_DIRECTORY \"${CMAKE_BINARY_DIR}\")\n")
ADD_CUSTOM_COMMAND(OUTPUT ${COVERAGE_XML_FILE}
COMMAND ${CMAKE_COMMAND} ARGS -P ${COVERAGE_XML_COMMAND_FILE}
COMMENT "Generating coverage XML report"
VERBATIM)
ADD_CUSTOM_TARGET(coverage-xml
DEPENDS ${COVERAGE_XML_FILE})
ENDIF()
# provide a global coverage target executing both steps if available
SET(GLOBAL_DEPENDS "")
IF(LCOV_FOUND)
LIST(APPEND GLOBAL_DEPENDS ${COVERAGE_REPORT_DIR})
ENDIF()
IF(GCOVR_FOUND)
LIST(APPEND GLOBAL_DEPENDS ${COVERAGE_XML_FILE})
ENDIF()
IF(LCOV_FOUND OR GCOVR_FOUND)
ADD_CUSTOM_TARGET(coverage
DEPENDS ${GLOBAL_DEPENDS})
ENDIF()
ENDIF()
ENDFUNCTION()
./cmake/Findgcovr.cmake 0000644 0000156 0000165 00000001702 12703462031 015076 0 ustar jenkins jenkins # - Find gcovr scrip
# Will define:
#
# GCOVR_EXECUTABLE - the gcovr script
#
# Uses:
#
# GCOVR_ROOT - root to search for the script
#
# Copyright (C) 2011 by Johannes Wienke
#
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General
# Public License as published by the Free Software Foundation;
# either version 2, or (at your option)
# any later version.
#
# This program 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 General Public License for more details.
#
INCLUDE(FindPackageHandleStandardArgs)
FIND_PROGRAM(GCOVR_EXECUTABLE gcovr HINTS ${GCOVR_ROOT} "${GCOVR_ROOT}/bin")
FIND_PACKAGE_HANDLE_STANDARD_ARGS(gcovr DEFAULT_MSG GCOVR_EXECUTABLE)
# only visible in advanced view
MARK_AS_ADVANCED(GCOVR_EXECUTABLE)
./cmake/ubuntu-arm-linux-gnueabihf.cmake 0000644 0000156 0000165 00000001501 12703462031 020334 0 ustar jenkins jenkins # shamelessly copied over from oxide’s build system
# to enable ARM cross compilation
set(CMAKE_SYSTEM_NAME Linux CACHE INTERNAL "")
find_program(_DPKG_ARCH_EXECUTABLE dpkg-architecture)
if(_DPKG_ARCH_EXECUTABLE STREQUAL "DPKG_ARCHITECTURE_EXECUTABLE-NOTFOUND")
message(FATAL_ERROR "dpkg-architecture not found")
endif()
execute_process(COMMAND ${_DPKG_ARCH_EXECUTABLE} -qDEB_BUILD_GNU_TYPE
RESULT_VARIABLE _RESULT
OUTPUT_VARIABLE HOST_ARCHITECTURE
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT _RESULT EQUAL 0)
message(FATAL_ERROR "Failed to determine host architecture")
endif()
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc CACHE INTERNAL "")
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++ CACHE INTERNAL "")
set(CMAKE_LIBRARY_ARCHITECTURE arm-linux-gnueabihf CACHE INTERNAL "")
./cmake/qt5.cmake 0000644 0000156 0000165 00000002772 12703462031 013676 0 ustar jenkins jenkins # shamelessly copied over from oxide’s build system
# to enable ARM cross compilation
if(CMAKE_CROSSCOMPILING)
# QT_MOC_EXECUTABLE is set by Qt5CoreConfigExtras, but it sets it to
# the target executable rather than the host executable, which is no
# use for cross-compiling. For cross-compiling, we have a guess and
# override it ourselves
if(NOT TARGET Qt5::moc)
find_program(
QT_MOC_EXECUTABLE moc
PATHS /usr/lib/qt5/bin /usr/lib/${HOST_ARCHITECTURE}/qt5/bin
NO_DEFAULT_PATH)
if(QT_MOC_EXECUTABLE STREQUAL "QT_MOC_EXECUTABLE-NOTFOUND")
message(FATAL_ERROR "Can't find a moc executable for the host arch")
endif()
add_executable(Qt5::moc IMPORTED)
set_target_properties(Qt5::moc PROPERTIES
IMPORTED_LOCATION "${QT_MOC_EXECUTABLE}")
endif()
# Dummy targets - not used anywhere, but this stops Qt5CoreConfigExtras.cmake
# from creating them and checking if the binary exists, which is broken when
# cross-building because it checks for the target system binary. We need the
# host system binaries installed, because they are in the same package as the
# moc in Ubuntu (qtbase5-dev-tools), which is not currently multi-arch
if(NOT TARGET Qt5::qmake)
add_executable(Qt5::qmake IMPORTED)
endif()
if(NOT TARGET Qt5::rcc)
add_executable(Qt5::rcc IMPORTED)
endif()
if(NOT TARGET Qt5::uic)
add_executable(Qt5::uic IMPORTED)
endif()
else()
# This should be enough to initialize QT_MOC_EXECUTABLE
find_package(Qt5Core)
endif()
./cmake/ParseArguments.cmake 0000644 0000156 0000165 00000003406 12703462031 016120 0 ustar jenkins jenkins # Parse arguments passed to a function into several lists separated by
# upper-case identifiers and options that do not have an associated list e.g.:
#
# SET(arguments
# hello OPTION3 world
# LIST3 foo bar
# OPTION2
# LIST1 fuz baz
# )
# PARSE_ARGUMENTS(ARG "LIST1;LIST2;LIST3" "OPTION1;OPTION2;OPTION3" ${arguments})
#
# results in 7 distinct variables:
# * ARG_DEFAULT_ARGS: hello;world
# * ARG_LIST1: fuz;baz
# * ARG_LIST2:
# * ARG_LIST3: foo;bar
# * ARG_OPTION1: FALSE
# * ARG_OPTION2: TRUE
# * ARG_OPTION3: TRUE
#
# taken from http://www.cmake.org/Wiki/CMakeMacroParseArguments
MACRO(PARSE_ARGUMENTS prefix arg_names option_names)
SET(DEFAULT_ARGS)
FOREACH(arg_name ${arg_names})
SET(${prefix}_${arg_name})
ENDFOREACH(arg_name)
FOREACH(option ${option_names})
SET(${prefix}_${option} FALSE)
ENDFOREACH(option)
SET(current_arg_name DEFAULT_ARGS)
SET(current_arg_list)
FOREACH(arg ${ARGN})
SET(larg_names ${arg_names})
LIST(FIND larg_names "${arg}" is_arg_name)
IF (is_arg_name GREATER -1)
SET(${prefix}_${current_arg_name} ${current_arg_list})
SET(current_arg_name ${arg})
SET(current_arg_list)
ELSE (is_arg_name GREATER -1)
SET(loption_names ${option_names})
LIST(FIND loption_names "${arg}" is_option)
IF (is_option GREATER -1)
SET(${prefix}_${arg} TRUE)
ELSE (is_option GREATER -1)
SET(current_arg_list ${current_arg_list} ${arg})
ENDIF (is_option GREATER -1)
ENDIF (is_arg_name GREATER -1)
ENDFOREACH(arg)
SET(${prefix}_${current_arg_name} ${current_arg_list})
ENDMACRO(PARSE_ARGUMENTS)
./cmake/FindLcov.cmake 0000644 0000156 0000165 00000001720 12703462031 014661 0 ustar jenkins jenkins # - Find lcov
# Will define:
#
# LCOV_EXECUTABLE - the lcov binary
# GENHTML_EXECUTABLE - the genhtml executable
#
# Copyright (C) 2010 by Johannes Wienke
#
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General
# Public License as published by the Free Software Foundation;
# either version 2, or (at your option)
# any later version.
#
# This program 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 General Public License for more details.
#
INCLUDE(FindPackageHandleStandardArgs)
FIND_PROGRAM(LCOV_EXECUTABLE lcov)
FIND_PROGRAM(GENHTML_EXECUTABLE genhtml)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lcov DEFAULT_MSG LCOV_EXECUTABLE GENHTML_EXECUTABLE)
# only visible in advanced view
MARK_AS_ADVANCED(LCOV_EXECUTABLE GENHTML_EXECUTABLE)
./tests/ 0000755 0000156 0000165 00000000000 12703462031 012235 5 ustar jenkins jenkins ./tests/unittests/ 0000755 0000156 0000165 00000000000 12703462031 014277 5 ustar jenkins jenkins ./tests/unittests/limit-proxy-model/ 0000755 0000156 0000165 00000000000 12703462032 017673 5 ustar jenkins jenkins ./tests/unittests/limit-proxy-model/tst_LimitProxyModelTests.cpp 0000644 0000156 0000165 00000012625 12703462031 025422 0 ustar jenkins jenkins /*
* Copyright 2014-2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
#include
// local
#include "limit-proxy-model.h"
class SimpleListModel : public QAbstractListModel {
Q_OBJECT
public:
enum Roles {
Index = Qt::UserRole + 1,
String
};
QHash roleNames() const
{
static QHash roles;
if (roles.isEmpty()) {
roles[Index] = "index";
roles[String] = "string";
}
return roles;
}
int rowCount(const QModelIndex& parent=QModelIndex()) const
{
return m_strings.count();
}
QVariant data(const QModelIndex& index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
switch (role) {
case Index:
return index.row();
case String:
return m_strings.at(index.row());
default:
return QVariant();
}
}
void append(const QStringList& strings)
{
int index = m_strings.count();
beginInsertRows(QModelIndex(), index, index + strings.count() - 1);
m_strings << strings;
endInsertRows();
}
void remove(int index)
{
beginRemoveRows(QModelIndex(), index, index);
m_strings.removeAt(index);
endRemoveRows();
}
private:
QStringList m_strings;
};
class LimitProxyModelTests : public QObject
{
Q_OBJECT
private:
SimpleListModel* strings;
LimitProxyModel* model;
private Q_SLOTS:
void init()
{
strings = new SimpleListModel;
model = new LimitProxyModel;
model->setSourceModel(strings);
}
void cleanup()
{
delete model;
delete strings;
}
void shouldBeInitiallyEmpty()
{
QCOMPARE(model->rowCount(), 0);
}
void shouldLimitBeInitiallyMinusOne()
{
QCOMPARE(model->limit(), -1);
}
void shouldNotifyWhenChangingSourceModel()
{
QSignalSpy spy(model, SIGNAL(sourceModelChanged()));
model->setSourceModel(strings);
QVERIFY(spy.isEmpty());
QStringListModel strings2;
model->setSourceModel(&strings2);
QCOMPARE(spy.count(), 1);
QCOMPARE(model->sourceModel(), &strings2);
model->setSourceModel(0);
QCOMPARE(spy.count(), 2);
QCOMPARE(model->sourceModel(), (QAbstractItemModel*) 0);
}
void shouldLimitEntriesWithLimitSetBeforePopulating()
{
model->setLimit(2);
strings->append({"a", "b", "c"});
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->unlimitedRowCount(), 3);
}
void shouldLimitEntriesWithLimitSetAfterPopulating()
{
strings->append({"a", "b", "c"});
model->setLimit(2);
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->unlimitedRowCount(), 3);
}
void shouldNotLimitEntriesIfLimitIsMinusOne()
{
model->setLimit(-1);
strings->append({"a", "b", "c"});
QCOMPARE(model->unlimitedRowCount(), 3);
QCOMPARE(model->rowCount(), model->unlimitedRowCount());
}
void shouldNotLimitEntriesIfLimitIsGreaterThanRowCount()
{
model->setLimit(4);
strings->append({"a", "b", "c"});
QCOMPARE(model->unlimitedRowCount(), 3);
QCOMPARE(model->rowCount(), model->unlimitedRowCount());
}
void shouldUpdateRowCountAndNotifyAfterAnEntryIsRemoved()
{
model->setLimit(2);
strings->append({"a", "b", "c", "d"});
QSignalSpy spyChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
QSignalSpy spyRemoved(model, SIGNAL(rowsRemoved(QModelIndex, int, int)));
strings->remove(0);
QCOMPARE(spyChanged.count(), 1);
QVERIFY(spyRemoved.isEmpty());
QCOMPARE(model->data(model->index(0, 0), SimpleListModel::String).toString(), QString("b"));
QCOMPARE(model->data(model->index(1, 0), SimpleListModel::String).toString(), QString("c"));
QCOMPARE(model->unlimitedRowCount(), 3);
QCOMPARE(model->rowCount(), 2);
}
void shouldGetItemWithCorrectValues()
{
strings->append({"a"});
QVariantMap item = model->get(0);
QHash roles = model->roleNames();
QCOMPARE(roles.count(), item.count());
Q_FOREACH(int role, roles.keys()) {
QString roleName = QString::fromUtf8(roles.value(role));
QCOMPARE(model->data(model->index(0, 0), role), item.value(roleName));
}
}
void shouldReturnEmptyItemIfGetOutOfBounds()
{
QVariantMap item = model->get(1);
QVERIFY(item.isEmpty());
}
};
QTEST_MAIN(LimitProxyModelTests)
#include "tst_LimitProxyModelTests.moc"
./tests/unittests/limit-proxy-model/CMakeLists.txt 0000644 0000156 0000165 00000000630 12703462031 022431 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Sql REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_LimitProxyModelTests)
add_executable(${TEST} tst_LimitProxyModelTests.cpp)
include_directories(${webbrowser-app_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Sql
Qt5::Test
webbrowser-app-models
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/cookie-store/ 0000755 0000156 0000165 00000000000 12703462032 016703 5 ustar jenkins jenkins ./tests/unittests/cookie-store/tst_CookieStore.cpp 0000644 0000156 0000165 00000003740 12703462031 022532 0 ustar jenkins jenkins /*
* Copyright 2014 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
#include
#include
#include
// local
#include "chrome-cookie-store.h"
#include "online-accounts-cookie-store.h"
uint qHash(const QNetworkCookie &cookie, uint seed)
{
return qHash(cookie.toRawForm(), seed);
}
class CookieStoreTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testChromeProperties();
};
void CookieStoreTest::testChromeProperties()
{
QTemporaryDir tmpDir;
QVERIFY(tmpDir.isValid());
QDir testDir(tmpDir.path());
QTemporaryDir tmpDir2;
QVERIFY(tmpDir2.isValid());
QDir testDir2(tmpDir2.path());
ChromeCookieStore store;
QSignalSpy dbPathChanged(&store, SIGNAL(dbPathChanged()));
QString path = testDir.filePath("cookies.db");
store.setProperty("dbPath", path);
QCOMPARE(dbPathChanged.count(), 1);
QCOMPARE(store.property("dbPath").toString(), path);
dbPathChanged.clear();
QString path2 = testDir2.filePath("cookies.db");
store.setProperty("dbPath", "file://" + path2);
QCOMPARE(dbPathChanged.count(), 1);
QCOMPARE(store.property("dbPath").toString(), path2);
QVERIFY(store.property("cookies").value().isEmpty());
}
QTEST_MAIN(CookieStoreTest)
#include "tst_CookieStore.moc"
./tests/unittests/cookie-store/CMakeLists.txt 0000644 0000156 0000165 00000001155 12703462031 021444 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Sql REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_CookieStoreTests)
set(SOURCES
${webapp-container_SOURCE_DIR}/chrome-cookie-store.cpp
${webapp-container_SOURCE_DIR}/cookie-store.cpp
${webapp-container_SOURCE_DIR}/oxide-cookie-helper.cpp
tst_CookieStore.cpp
)
add_executable(${TEST} ${SOURCES})
include_directories(${webapp-container_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Network
Qt5::Sql
Qt5::Test
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/tabs-model/ 0000755 0000156 0000165 00000000000 12703462032 016327 5 ustar jenkins jenkins ./tests/unittests/tabs-model/tst_TabsModelTests.cpp 0000644 0000156 0000165 00000041720 12703462031 022625 0 ustar jenkins jenkins /*
* Copyright 2013-2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
#include
#include
#include
#include
// local
#include "tabs-model.h"
class TabsModelTests : public QObject
{
Q_OBJECT
private:
QQmlEngine engine;
TabsModel* model;
QQuickItem* createTab()
{
QQmlComponent component(&engine);
QByteArray data("import QtQuick 2.4\nItem {\nproperty url url\n"
"property string title\nproperty url icon\n}");
component.setData(data, QUrl());
QObject* object = component.create();
object->setParent(this);
QQuickItem* item = qobject_cast(object);
return item;
}
QQuickItem* createTabWithTitle(const QString& title) {
QQuickItem* tab = createTab();
tab->setProperty("title", title);
return tab;
}
void verifyTabsOrder(QStringList orderedTitles) {
QCOMPARE(model->rowCount(), orderedTitles.count());
int i = 0;
Q_FOREACH(QString title, orderedTitles) {
QCOMPARE(model->get(i++)->property("title").toString(), title);
}
}
private Q_SLOTS:
void init()
{
model = new TabsModel;
}
void cleanup()
{
while (model->rowCount() > 0) {
delete model->remove(0);
}
delete model;
}
void shouldBeInitiallyEmpty()
{
QCOMPARE(model->rowCount(), 0);
QCOMPARE(model->currentTab(), (QObject*) nullptr);
}
void shouldExposeRoleNames()
{
QList roleNames = model->roleNames().values();
QVERIFY(roleNames.contains("url"));
QVERIFY(roleNames.contains("title"));
QVERIFY(roleNames.contains("icon"));
QVERIFY(roleNames.contains("tab"));
}
void shouldNotAllowSettingTheIndexToAnInvalidValue_data()
{
QTest::addColumn("index");
QTest::newRow("zero") << 0;
QTest::newRow("too high") << 2;
QTest::newRow("too low") << -2;
}
void shouldNotAllowSettingTheIndexToAnInvalidValue()
{
QFETCH(int, index);
model->setCurrentIndex(index);
QCOMPARE(model->currentIndex(), -1);
QCOMPARE(model->currentTab(), (QObject*) nullptr);
}
void shouldNotAddNullTab()
{
QCOMPARE(model->add(0), -1);
QCOMPARE(model->rowCount(), 0);
}
void shouldReturnIndexWhenAddingTab()
{
for(int i = 0; i < 3; ++i) {
QCOMPARE(model->add(createTab()), i);
}
}
void shouldUpdateCountWhenAddingTab()
{
QSignalSpy spy(model, SIGNAL(countChanged()));
model->add(createTab());
QCOMPARE(spy.count(), 1);
QCOMPARE(model->rowCount(), 1);
}
void shouldNotInsertNullTab()
{
QCOMPARE(model->insert(0, 0), -1);
QCOMPARE(model->rowCount(), 0);
}
void shouldReturnIndexWhenInsertingTab()
{
for(int i = 0; i < 3; ++i) {
model->add(createTab());
}
for(int i = 2; i >= 0; --i) {
QCOMPARE(model->insert(createTab(), i), i);
}
}
void shouldUpdateCountWhenInsertingTab()
{
QSignalSpy spy(model, SIGNAL(countChanged()));
model->insert(createTab(), 0);
QCOMPARE(spy.count(), 1);
QCOMPARE(model->rowCount(), 1);
}
void shouldInsertAtCorrectIndex()
{
model->insert(createTabWithTitle("B"), 0);
model->insert(createTabWithTitle("A"), 0);
verifyTabsOrder(QStringList({"A", "B"}));
model->insert(createTabWithTitle("X"), 1);
verifyTabsOrder(QStringList({"A", "X", "B"}));
model->insert(createTabWithTitle("C"), 3);
verifyTabsOrder(QStringList({"A", "X", "B", "C"}));
}
void shouldClampIndexWhenInsertingTabOutOfBounds()
{
model->add(createTabWithTitle("A"));
model->add(createTabWithTitle("B"));
model->insert(createTabWithTitle("C"), 3);
verifyTabsOrder(QStringList({"A", "B", "C"}));
model->insert(createTabWithTitle("X"), -1);
verifyTabsOrder(QStringList({"X", "A", "B", "C"}));
}
void shouldUpdateCountWhenRemovingTab()
{
model->add(createTab());
QSignalSpy spy(model, SIGNAL(countChanged()));
delete model->remove(0);
QCOMPARE(spy.count(), 1);
QCOMPARE(model->rowCount(), 0);
}
void shouldNotAllowRemovingAtInvalidIndex()
{
QCOMPARE(model->remove(0), (QObject*) nullptr);
QCOMPARE(model->remove(2), (QObject*) nullptr);
QCOMPARE(model->remove(-2), (QObject*) nullptr);
}
void shouldReturnTabWhenRemoving()
{
QQuickItem* tab = createTab();
model->add(tab);
QObject* removed = model->remove(0);
QCOMPARE(removed, tab);
delete removed;
}
void shouldNotDeleteTabWhenRemoving()
{
QQuickItem* tab = createTab();
model->add(tab);
model->remove(0);
QCOMPARE(tab->parent(), this);
delete tab;
}
void shouldNotifyWhenAddingTab()
{
QSignalSpy spy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
for(int i = 0; i < 3; ++i) {
model->add(createTab());
QCOMPARE(spy.count(), 1);
QList args = spy.takeFirst();
QCOMPARE(args.at(1).toInt(), i);
QCOMPARE(args.at(2).toInt(), i);
}
}
void shouldNotifyWhenRemovingTab()
{
QSignalSpy spy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
for(int i = 0; i < 5; ++i) {
model->add(createTab());
}
delete model->remove(3);
QCOMPARE(spy.count(), 1);
QList args = spy.takeFirst();
QCOMPARE(args.at(1).toInt(), 3);
QCOMPARE(args.at(2).toInt(), 3);
for(int i = 3; i >= 0; --i) {
delete model->remove(i);
QCOMPARE(spy.count(), 1);
args = spy.takeFirst();
QCOMPARE(args.at(1).toInt(), i);
QCOMPARE(args.at(2).toInt(), i);
}
}
void shouldNotifyWhenTabPropertiesChange()
{
qRegisterMetaType >();
QSignalSpy spy(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
QQuickItem* tab = createTab();
model->add(tab);
QQmlProperty(tab, "url").write(QUrl("http://ubuntu.com"));
QCOMPARE(spy.count(), 1);
QList args = spy.takeFirst();
QCOMPARE(args.at(0).toModelIndex().row(), 0);
QCOMPARE(args.at(1).toModelIndex().row(), 0);
QVector roles = args.at(2).value >();
QCOMPARE(roles.size(), 1);
QVERIFY(roles.contains(TabsModel::Url));
QQmlProperty(tab, "title").write(QString("Lorem Ipsum"));
QCOMPARE(spy.count(), 1);
args = spy.takeFirst();
QCOMPARE(args.at(0).toModelIndex().row(), 0);
QCOMPARE(args.at(1).toModelIndex().row(), 0);
roles = args.at(2).value >();
QCOMPARE(roles.size(), 1);
QVERIFY(roles.contains(TabsModel::Title));
QQmlProperty(tab, "icon").write(QUrl("image://webicon/123"));
QCOMPARE(spy.count(), 1);
args = spy.takeFirst();
QCOMPARE(args.at(0).toModelIndex().row(), 0);
QCOMPARE(args.at(1).toModelIndex().row(), 0);
roles = args.at(2).value >();
QCOMPARE(roles.size(), 1);
QVERIFY(roles.contains(TabsModel::Icon));
}
void shouldUpdateCurrentTabWhenSettingCurrentIndex()
{
QQuickItem* tab1 = createTab();
model->add(tab1);
QSignalSpy spy(model, SIGNAL(currentTabChanged()));
model->setCurrentIndex(0);
QCOMPARE(model->currentIndex(), 0);
QVERIFY(spy.isEmpty());
QCOMPARE(model->currentTab(), tab1);
QQuickItem* tab2 = createTab();
model->add(tab2);
model->setCurrentIndex(1);
QCOMPARE(model->currentIndex(), 1);
QCOMPARE(spy.count(), 1);
QCOMPARE(model->currentTab(), tab2);
}
void shouldSetCurrentTabWhenAddingFirstTab()
{
// Adding a tab to an empty model should update the current tab
// to that tab
QSignalSpy spytab(model, SIGNAL(currentTabChanged()));
QSignalSpy spyindex(model, SIGNAL(currentIndexChanged()));
QCOMPARE(model->currentIndex(), -1);
QCOMPARE(model->currentTab(), (QObject*) nullptr);
QQuickItem* tab1 = createTab();
model->add(tab1);
QCOMPARE(spytab.count(), 1);
QCOMPARE(spyindex.count(), 1);
QCOMPARE(model->currentIndex(), 0);
QCOMPARE(model->currentTab(), tab1);
// But adding further items should keep the index where it was
model->add(createTab());
model->add(createTab());
QCOMPARE(spytab.count(), 1);
QCOMPARE(spyindex.count(), 1);
QCOMPARE(model->currentIndex(), 0);
QCOMPARE(model->currentTab(), tab1);
}
void shouldSetCurrentTabWhenInsertingFirstTab()
{
// Inserting a tab to an empty model should update the current tab
// to that tab
QSignalSpy spytab(model, SIGNAL(currentTabChanged()));
QSignalSpy spyindex(model, SIGNAL(currentIndexChanged()));
QCOMPARE(model->currentIndex(), -1);
QCOMPARE(model->currentTab(), (QObject*) nullptr);
QQuickItem* tab1 = createTab();
model->insert(tab1, 0);
QCOMPARE(spytab.count(), 1);
QCOMPARE(spyindex.count(), 1);
QCOMPARE(model->currentIndex(), 0);
QCOMPARE(model->currentTab(), tab1);
}
void shouldUpdateCurrentTabWhenInsertingAtCurrentIndex()
{
QQuickItem* tab1 = createTab();
model->insert(tab1, 0);
QCOMPARE(model->currentIndex(), 0);
QCOMPARE(model->currentTab(), tab1);
QSignalSpy spytab(model, SIGNAL(currentTabChanged()));
QSignalSpy spyindex(model, SIGNAL(currentIndexChanged()));
QQuickItem* tab2 = createTab();
model->insert(tab2, 0);
QVERIFY(spyindex.isEmpty());
QCOMPARE(spytab.count(), 1);
QCOMPARE(model->currentTab(), tab2);
}
void shouldUpdateCurrentIndexWhenInsertingBeforeCurrentIndex()
{
model->insert(createTab(), 0);
model->insert(createTab(), 0);
model->setCurrentIndex(1);
QObject* current = model->currentTab();
QSignalSpy spytab(model, SIGNAL(currentTabChanged()));
QSignalSpy spyindex(model, SIGNAL(currentIndexChanged()));
model->insert(createTab(), 0);
QVERIFY(spytab.isEmpty());
QCOMPARE(spyindex.count(), 1);
QCOMPARE(model->currentTab(), current);
QCOMPARE(model->currentIndex(), 2);
}
void shouldSetInvalidIndexWhenRemovingLastTab()
{
// Removing the last item should also set the current index to -1
// and the current tab to null
model->add(createTab());
QSignalSpy spytab(model, SIGNAL(currentTabChanged()));
QSignalSpy spyindex(model, SIGNAL(currentIndexChanged()));
delete model->remove(0);
QCOMPARE(spytab.count(), 1);
QCOMPARE(spyindex.count(), 1);
QCOMPARE(model->currentIndex(), -1);
QCOMPARE(model->currentTab(), (QObject*) nullptr);
}
void shouldNotChangeIndexWhenRemovingAfterCurrent()
{
// When removing a tab after the current one,
// the current tab shouldn’t change.
QQuickItem* tab1 = createTab();
model->add(tab1);
model->add(createTab());
QSignalSpy spytab(model, SIGNAL(currentTabChanged()));
QSignalSpy spyindex(model, SIGNAL(currentIndexChanged()));
delete model->remove(1);
QCOMPARE(model->currentTab(), tab1);
QVERIFY(spytab.isEmpty());
QVERIFY(spyindex.isEmpty());
}
void shouldUpdateIndexWhenRemovingCurrent()
{
// When removing the current tab, if there is a tab after it,
// it becomes the current one.
QQuickItem* tab1 = createTab();
QQuickItem* tab2 = createTab();
QQuickItem* tab3 = createTab();
model->add(tab1);
model->add(tab2);
model->add(tab3);
model->setCurrentIndex(1);
QCOMPARE(model->currentIndex(), 1);
QCOMPARE(model->currentTab(), tab2);
QSignalSpy spytab(model, SIGNAL(currentTabChanged()));
QSignalSpy spyindex(model, SIGNAL(currentIndexChanged()));
delete model->remove(1);
QCOMPARE(spyindex.count(), 0);
QCOMPARE(spytab.count(), 1);
QCOMPARE(model->currentTab(), tab3);
// If there is no tab after it but one before, that one becomes current
delete model->remove(1);
QCOMPARE(spyindex.count(), 1);
QCOMPARE(spytab.count(), 2);
QCOMPARE(model->currentIndex(), 0);
QCOMPARE(model->currentTab(), tab1);
}
void shouldDecreaseIndexWhenRemovingBeforeCurrent()
{
// When removing a tab before the current tab, the current index
// should decrease to match.
model->add(createTab());
model->add(createTab());
QQuickItem* tab = createTab();
model->add(tab);
model->setCurrentIndex(2);
QSignalSpy spytab(model, SIGNAL(currentTabChanged()));
QSignalSpy spyindex(model, SIGNAL(currentIndexChanged()));
delete model->remove(1);
QVERIFY(spytab.isEmpty());
QCOMPARE(spyindex.count(), 1);
QCOMPARE(model->currentIndex(), 1);
QCOMPARE(model->currentTab(), tab);
}
void shouldReturnData()
{
QQuickItem* tab = createTab();
QQmlProperty(tab, "url").write(QUrl("http://ubuntu.com/"));
QQmlProperty(tab, "title").write(QString("Lorem Ipsum"));
QQmlProperty(tab, "icon").write(QUrl("image://webicon/123"));
model->add(tab);
QVERIFY(!model->data(QModelIndex(), TabsModel::Url).isValid());
QVERIFY(!model->data(model->index(-1, 0), TabsModel::Url).isValid());
QVERIFY(!model->data(model->index(3, 0), TabsModel::Url).isValid());
QCOMPARE(model->data(model->index(0, 0), TabsModel::Url).toUrl(), QUrl("http://ubuntu.com/"));
QCOMPARE(model->data(model->index(0, 0), TabsModel::Title).toString(), QString("Lorem Ipsum"));
QCOMPARE(model->data(model->index(0, 0), TabsModel::Icon).toUrl(), QUrl("image://webicon/123"));
QCOMPARE(model->data(model->index(0, 0), TabsModel::Tab).value(), tab);
QVERIFY(!model->data(model->index(0, 0), TabsModel::Tab + 3).isValid());
}
void shouldReturnTabAtIndex()
{
// valid indexes
QQuickItem* tab1 = createTab();
model->add(tab1);
QQuickItem* tab2 = createTab();
model->add(tab2);
QQuickItem* tab3 = createTab();
model->add(tab3);
QCOMPARE(model->get(0), tab1);
QCOMPARE(model->get(1), tab2);
QCOMPARE(model->get(2), tab3);
// invalid indexes
QCOMPARE(model->get(-1), (QObject*) nullptr);
QCOMPARE(model->get(3), (QObject*) nullptr);
}
private:
void moveTabs(int from, int to, bool moved, bool indexChanged, int newIndex)
{
QSignalSpy spyMoved(model, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
QSignalSpy spyIndex(model, SIGNAL(currentIndexChanged()));
QSignalSpy spyTab(model, SIGNAL(currentTabChanged()));
model->move(from, to);
QCOMPARE(spyMoved.count(), moved ? 1 : 0);
if (moved) {
QList args = spyMoved.takeFirst();
QCOMPARE(args.at(1).toInt(), from);
QCOMPARE(args.at(2).toInt(), from);
QCOMPARE(args.at(4).toInt(), to);
}
QCOMPARE(spyIndex.count(), indexChanged ? 1 : 0);
QCOMPARE(model->currentIndex(), newIndex);
QVERIFY(spyTab.isEmpty());
}
private Q_SLOTS:
void shouldNotifyWhenMovingTabs()
{
for (int i = 0; i < 3; ++i) {
model->add(createTab());
}
moveTabs(1, 1, false, false, 0);
moveTabs(4, 1, false, false, 0);
moveTabs(1, 4, false, false, 0);
moveTabs(0, 2, true, true, 2);
moveTabs(1, 0, true, false, 2);
moveTabs(2, 1, true, true, 1);
moveTabs(2, 0, true, true, 2);
moveTabs(2, 1, true, true, 1);
moveTabs(0, 2, true, true, 0);
}
};
QTEST_MAIN(TabsModelTests)
#include "tst_TabsModelTests.moc"
./tests/unittests/tabs-model/CMakeLists.txt 0000644 0000156 0000165 00000001065 12703462031 021070 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Qml REQUIRED)
find_package(Qt5Quick REQUIRED)
find_package(Qt5Sql REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_TabsModelTests)
add_executable(${TEST} tst_TabsModelTests.cpp)
include_directories(${webbrowser-app_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Qml
Qt5::Quick
Qt5::Sql
Qt5::Test
webbrowser-app-models
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
set_tests_properties(${TEST} PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
./tests/unittests/bookmarks-model/ 0000755 0000156 0000165 00000000000 12703462032 017366 5 ustar jenkins jenkins ./tests/unittests/bookmarks-model/tst_BookmarksModelTests.cpp 0000644 0000156 0000165 00000022626 12703462031 024727 0 ustar jenkins jenkins /*
* Copyright 2013-2014 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
#include
// local
#include "bookmarks-model.h"
class BookmarksModelTests : public QObject
{
Q_OBJECT
private:
BookmarksModel* model;
private Q_SLOTS:
void init()
{
model = new BookmarksModel;
model->setDatabasePath(":memory:");
}
void cleanup()
{
delete model;
}
void shouldBeInitiallyEmpty()
{
QCOMPARE(model->rowCount(), 0);
}
void shouldExposeRoleNames()
{
QList roleNames = model->roleNames().values();
QVERIFY(roleNames.contains("url"));
QVERIFY(roleNames.contains("title"));
QVERIFY(roleNames.contains("icon"));
QVERIFY(roleNames.contains("created"));
QVERIFY(roleNames.contains("folder"));
}
void shouldAddNewEntries()
{
QSignalSpy spy(model, SIGNAL(rowsInserted(QModelIndex, int, int)));
model->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "");
QCOMPARE(model->rowCount(), 1);
QCOMPARE(spy.count(), 1);
QVariantList args = spy.takeFirst();
QCOMPARE(args.at(1).toInt(), 0);
QCOMPARE(args.at(2).toInt(), 0);
model->add(QUrl("http://wikipedia.org/"), "Wikipedia", QUrl(), "");
QCOMPARE(model->rowCount(), 2);
QCOMPARE(spy.count(), 1);
args = spy.takeFirst();
QCOMPARE(args.at(1).toInt(), 0);
QCOMPARE(args.at(2).toInt(), 0);
model->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl(), "");
QCOMPARE(model->rowCount(), 3);
QCOMPARE(spy.count(), 1);
args = spy.takeFirst();
QCOMPARE(args.at(1).toInt(), 0);
QCOMPARE(args.at(2).toInt(), 0);
model->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "");
QCOMPARE(model->rowCount(), 3);
QVERIFY(spy.isEmpty());
}
void shouldRemoveEntries()
{
QSignalSpy spy(model, SIGNAL(rowsRemoved(QModelIndex, int, int)));
model->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "");
model->add(QUrl("http://wikipedia.org/"), "Wikipedia", QUrl(), "");
model->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl(), "");
QCOMPARE(model->rowCount(), 3);
QVERIFY(spy.isEmpty());
model->remove(QUrl("http://ubuntu.com/"));
QCOMPARE(model->rowCount(), 2);
QCOMPARE(spy.count(), 1);
QVariantList args = spy.takeFirst();
// Model is chronologically sorted so deleting the last entry added
// actually deletes the first item in the model
QCOMPARE(args.at(1).toInt(), 0);
QCOMPARE(args.at(2).toInt(), 0);
model->remove(QUrl("http://ubuntu.com/"));
QCOMPARE(model->rowCount(), 2);
QVERIFY(spy.isEmpty());
}
void shouldUpdateEntries()
{
QSignalSpy spy(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
model->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "");
model->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl(), "");
QCOMPARE(model->rowCount(), 2);
QVERIFY(spy.isEmpty());
model->update(QUrl("http://example.org/"), "New Domain Title", "SampleFolder");
QCOMPARE(model->rowCount(), 2);
QCOMPARE(spy.count(), 1);
QList args = spy.takeFirst();
QCOMPARE(args.at(0).toModelIndex().row(), 1);
QCOMPARE(args.at(1).toModelIndex().row(), 1);
QVector roles = args.at(2).value >();
QVERIFY(roles.size() >= 2);
QVERIFY(roles.contains(BookmarksModel::Title));
QVERIFY(roles.contains(BookmarksModel::Folder));
QCOMPARE(model->data(model->index(1, 0), BookmarksModel::Url).toUrl(), QUrl("http://example.org/"));
QCOMPARE(model->data(model->index(1, 0), BookmarksModel::Title).toString(), QString("New Domain Title"));
QCOMPARE(model->data(model->index(1, 0), BookmarksModel::Icon).toUrl(), QUrl(""));
QCOMPARE(model->data(model->index(1, 0), BookmarksModel::Folder).toString(), QString("SampleFolder"));
QCOMPARE(model->data(model->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://ubuntu.com/"));
}
void shouldContainEntries()
{
model->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "");
model->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl(), "");
QVERIFY(model->contains(QUrl("http://ubuntu.com/")));
QVERIFY(!model->contains(QUrl("http://wikipedia.org/")));
}
void shouldKeepEntriesSortedChronologically()
{
model->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl(), "");
model->add(QUrl("http://wikipedia.org/"), "Wikipedia", QUrl(), "");
model->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "");
QCOMPARE(model->data(model->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://example.org/"));
QCOMPARE(model->data(model->index(1, 0), BookmarksModel::Url).toUrl(), QUrl("http://wikipedia.org/"));
QCOMPARE(model->data(model->index(2, 0), BookmarksModel::Url).toUrl(), QUrl("http://ubuntu.com/"));
}
void shouldReturnData()
{
model->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl("image://webicon/123"), "SampleFolder");
QVERIFY(!model->data(QModelIndex(), BookmarksModel::Url).isValid());
QVERIFY(!model->data(model->index(-1, 0), BookmarksModel::Url).isValid());
QVERIFY(!model->data(model->index(3, 0), BookmarksModel::Url).isValid());
QCOMPARE(model->data(model->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://ubuntu.com/"));
QCOMPARE(model->data(model->index(0, 0), BookmarksModel::Title).toString(), QString("Ubuntu"));
QCOMPARE(model->data(model->index(0, 0), BookmarksModel::Icon).toUrl(), QUrl("image://webicon/123"));
QVERIFY(model->data(model->index(0, 0), BookmarksModel::Created).toDateTime() <= QDateTime::currentDateTime());
QCOMPARE(model->data(model->index(0, 0), BookmarksModel::Folder).toString(), QString("SampleFolder"));
QVERIFY(!model->data(model->index(0, 0), BookmarksModel::Folder + 1).isValid());
}
void shouldReturnDatabasePath()
{
QCOMPARE(model->databasePath(), QString(":memory:"));
}
void shouldNotifyWhenSettingDatabasePath()
{
QSignalSpy spyPath(model, SIGNAL(databasePathChanged()));
QSignalSpy spyReset(model, SIGNAL(modelReset()));
model->setDatabasePath(":memory:");
QVERIFY(spyPath.isEmpty());
QVERIFY(spyReset.isEmpty());
model->setDatabasePath("");
QCOMPARE(spyPath.count(), 1);
QCOMPARE(spyReset.count(), 1);
QCOMPARE(model->databasePath(), QString(":memory:"));
}
void shouldSerializeOnDisk()
{
QTemporaryFile tempFile;
tempFile.open();
QString fileName = tempFile.fileName();
delete model;
model = new BookmarksModel;
model->setDatabasePath(fileName);
model->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "");
model->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl(), "");
delete model;
model = new BookmarksModel;
model->setDatabasePath(fileName);
QCOMPARE(model->rowCount(), 2);
}
void shouldCountNumberOfEntries()
{
QSignalSpy spyCount(model, SIGNAL(rowCountChanged()));
QCOMPARE(model->property("count").toInt(), 0);
model->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "");
QCOMPARE(model->property("count").toInt(), 1);
QCOMPARE(spyCount.count(), 1);
model->add(QUrl("http://example.com/"), "Example Domain", QUrl(), "");
QCOMPARE(model->property("count").toInt(), 2);
QCOMPARE(spyCount.count(), 2);
model->remove(QUrl("http://example.com/"));
QCOMPARE(model->property("count").toInt(), 1);
QCOMPARE(spyCount.count(), 3);
}
void shouldPopulateModelWithExistingFolders()
{
QTemporaryFile tempFile;
tempFile.open();
QString fileName = tempFile.fileName();
delete model;
model = new BookmarksModel;
QSignalSpy spy(model, SIGNAL(folderAdded(QString)));
model->setDatabasePath(fileName);
model->addFolder("SampleFolder");
model->addFolder("AnotherFolder");
// The empty folder is added by default
QCOMPARE(spy.count(), 3);
QCOMPARE(model->folders().count(), 3);
delete model;
model = new BookmarksModel;
QSignalSpy spyPopulate(model, SIGNAL(folderAdded(QString)));
model->setDatabasePath(fileName);
QCOMPARE(spyPopulate.count(), 3);
QCOMPARE(model->folders().count(), 3);
}
};
QTEST_MAIN(BookmarksModelTests)
#include "tst_BookmarksModelTests.moc"
./tests/unittests/bookmarks-model/CMakeLists.txt 0000644 0000156 0000165 00000000626 12703462031 022131 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Sql REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_BookmarksModelTests)
add_executable(${TEST} tst_BookmarksModelTests.cpp)
include_directories(${webbrowser-app_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Sql
Qt5::Test
webbrowser-app-models
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/session-storage/ 0000755 0000156 0000165 00000000000 12703462032 017425 5 ustar jenkins jenkins ./tests/unittests/session-storage/tst_SessionStorageTests.cpp 0000644 0000156 0000165 00000007352 12703462031 025024 0 ustar jenkins jenkins /*
* Copyright 2014 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
// local
#include "session-storage.h"
class SessionStorageTests : public QObject
{
Q_OBJECT
private:
SessionStorage* session;
private Q_SLOTS:
void init()
{
session = new SessionStorage;
}
void cleanup()
{
delete session;
}
void shouldNotDoAnythingWithEmptyDataFile()
{
QVERIFY(session->dataFile().isEmpty());
QVERIFY(!session->isLocked());
session->store(QString("foobar")); // should be a no-op
QVERIFY(session->retrieve().isEmpty());
}
void shouldSetDataFile()
{
QSignalSpy dataFileSpy(session, SIGNAL(dataFileChanged()));
QSignalSpy lockedSpy(session, SIGNAL(lockedChanged()));
// Set an invalid session file
QString invalidDataFile("/f00/bAr/session.json");
session->setDataFile(invalidDataFile);
QCOMPARE(session->dataFile(), invalidDataFile);
QCOMPARE(dataFileSpy.count(), 1);
QCOMPARE(lockedSpy.count(), 0);
QVERIFY(!session->isLocked());
dataFileSpy.clear();
// Set a valid session file
QTemporaryFile file;
QVERIFY(file.open());
file.close();
session->setDataFile(file.fileName());
QCOMPARE(session->dataFile(), file.fileName());
QCOMPARE(dataFileSpy.count(), 1);
QCOMPARE(lockedSpy.count(), 1);
QVERIFY(session->isLocked());
dataFileSpy.clear();
lockedSpy.clear();
// Reset the session file
session->setDataFile("");
QCOMPARE(session->dataFile(), QString(""));
QCOMPARE(dataFileSpy.count(), 1);
QCOMPARE(lockedSpy.count(), 1);
QVERIFY(!session->isLocked());
dataFileSpy.clear();
lockedSpy.clear();
}
void shouldStoreAndRetrieveCorrectlyAfterwards()
{
QTemporaryFile file;
QVERIFY(file.open());
file.close();
session->setDataFile(file.fileName());
QVERIFY(session->isLocked());
QString data("f00bAr");
session->store(data);
QCOMPARE(session->retrieve(), data);
delete session;
session = new SessionStorage;
session->setDataFile(file.fileName());
QVERIFY(session->isLocked());
QCOMPARE(session->retrieve(), data);
}
void shouldLockOutSecondInstance()
{
QTemporaryFile file;
QVERIFY(file.open());
file.close();
session->setDataFile(file.fileName());
QVERIFY(session->isLocked());
SessionStorage session2;
session2.setDataFile(session->dataFile());
QVERIFY(!session2.isLocked());
// Verify that the session that held the lock going away
// doesn’t automagically make the next one acquire it
// (this would be undesirable as it would allow the second
// instance to overwrite the first session’s data).
delete session;
session = NULL;
QVERIFY(!session2.isLocked());
}
};
QTEST_MAIN(SessionStorageTests)
#include "tst_SessionStorageTests.moc"
./tests/unittests/session-storage/CMakeLists.txt 0000644 0000156 0000165 00000000651 12703462031 022166 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_SessionStorageTests)
set(SOURCES
${webbrowser-common_SOURCE_DIR}/session-storage.cpp
tst_SessionStorageTests.cpp
)
add_executable(${TEST} ${SOURCES})
include_directories(${webbrowser-common_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Test
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/history-domain-model/ 0000755 0000156 0000165 00000000000 12703462032 020344 5 ustar jenkins jenkins ./tests/unittests/history-domain-model/tst_HistoryDomainModelTests.cpp 0000644 0000156 0000165 00000006174 12703462031 026546 0 ustar jenkins jenkins /*
* Copyright 2013-2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
// local
#include "history-model.h"
#include "history-domain-model.h"
class HistoryDomainModelTests : public QObject
{
Q_OBJECT
private:
HistoryModel* history;
HistoryDomainModel* model;
private Q_SLOTS:
void init()
{
history = new HistoryModel;
history->setDatabasePath(":memory:");
model = new HistoryDomainModel;
model->setSourceModel(history);
}
void cleanup()
{
delete model;
delete history;
}
void shouldBeInitiallyEmpty()
{
QCOMPARE(model->rowCount(), 0);
}
void shouldNotifyWhenChangingSourceModel()
{
QSignalSpy spy(model, SIGNAL(sourceModelChanged()));
model->setSourceModel(history);
QVERIFY(spy.isEmpty());
HistoryModel history2;
model->setSourceModel(&history2);
QCOMPARE(spy.count(), 1);
QCOMPARE(model->sourceModel(), &history2);
model->setSourceModel(nullptr);
QCOMPARE(spy.count(), 2);
QCOMPARE(model->sourceModel(), (HistoryModel*) nullptr);
}
void shouldNotifyWhenChangingDomain()
{
QSignalSpy spy(model, SIGNAL(domainChanged()));
model->setDomain(QString());
QVERIFY(spy.isEmpty());
model->setDomain(QString(""));
QVERIFY(spy.isEmpty());
model->setDomain("example.org");
QCOMPARE(spy.count(), 1);
}
void shouldMatchAllWhenNoDomainSet()
{
history->add(QUrl("http://example.org"), "Example Domain", QUrl());
history->add(QUrl("http://example.com"), "Example Domain", QUrl());
QCOMPARE(model->rowCount(), 2);
}
void shouldFilterOutNonMatchingDomains()
{
history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
history->add(QUrl("http://example.com/"), "Example Domain", QUrl());
model->setDomain("example.org");
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Url).toUrl(), QUrl("http://example.org/"));
model->setDomain("example.com");
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Url).toUrl(), QUrl("http://example.com/"));
model->setDomain("ubuntu.com");
QCOMPARE(model->rowCount(), 0);
model->setDomain("");
QCOMPARE(model->rowCount(), 2);
}
};
QTEST_MAIN(HistoryDomainModelTests)
#include "tst_HistoryDomainModelTests.moc"
./tests/unittests/history-domain-model/CMakeLists.txt 0000644 0000156 0000165 00000000636 12703462031 023110 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Sql REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_HistoryDomainModelTests)
add_executable(${TEST} tst_HistoryDomainModelTests.cpp)
include_directories(${webbrowser-app_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Sql
Qt5::Test
webbrowser-app-models
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/history-domainlist-model/ 0000755 0000156 0000165 00000000000 12703462032 021240 5 ustar jenkins jenkins ./tests/unittests/history-domainlist-model/tst_HistoryDomainListModelTests.cpp 0000644 0000156 0000165 00000025024 12703462031 030271 0 ustar jenkins jenkins /*
* Copyright 2013-2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
// local
#include "domain-utils.h"
#include "history-model.h"
#include "history-domain-model.h"
#include "history-domainlist-model.h"
class HistoryDomainListModelTests : public QObject
{
Q_OBJECT
private:
HistoryModel* history;
HistoryDomainListModel* model;
void verifyDataChanged(QSignalSpy& spy, int row)
{
QList args;
bool changed = false;
while(!changed && !spy.isEmpty()) {
args = spy.takeFirst();
int start = args.at(0).toModelIndex().row();
int end = args.at(1).toModelIndex().row();
changed = (start <= row) && (row <= end);
}
QVERIFY(changed);
}
private Q_SLOTS:
void init()
{
history = new HistoryModel;
history->setDatabasePath(":memory:");
model = new HistoryDomainListModel;
model->setSourceModel(history);
}
void cleanup()
{
delete model;
delete history;
}
void shouldBeInitiallyEmpty()
{
QCOMPARE(model->rowCount(), 0);
}
void shouldUpdateDomainListWhenInsertingEntries()
{
QSignalSpy spyRowsInserted(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
qRegisterMetaType >();
QSignalSpy spyDataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
QVERIFY(spyDataChanged.isEmpty());
QCOMPARE(spyRowsInserted.count(), 1);
QList args = spyRowsInserted.takeFirst();
QCOMPARE(args.at(1).toInt(), 0);
QCOMPARE(args.at(2).toInt(), 0);
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0, 0), HistoryDomainListModel::Domain).toString(), QString("example.org"));
history->add(QUrl("http://example.com/"), "Example Domain", QUrl());
QVERIFY(spyDataChanged.isEmpty());
QCOMPARE(spyRowsInserted.count(), 1);
args = spyRowsInserted.takeFirst();
QCOMPARE(args.at(1).toInt(), 0);
QCOMPARE(args.at(2).toInt(), 0);
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->data(model->index(0, 0), HistoryDomainListModel::Domain).toString(), QString("example.com"));
history->add(QUrl("http://example.org/test.html"), "Test page", QUrl());
QVERIFY(spyRowsInserted.isEmpty());
QVERIFY(!spyDataChanged.isEmpty());
args = spyDataChanged.takeFirst();
QCOMPARE(args.at(0).toModelIndex().row(), 1);
QCOMPARE(args.at(1).toModelIndex().row(), 1);
QCOMPARE(model->rowCount(), 2);
}
void shouldUpdateDomainListWhenRemovingEntries()
{
history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
history->add(QUrl("http://example.com/"), "Example Domain", QUrl());
history->add(QUrl("http://example.org/test"), "Example Domain", QUrl());
QCOMPARE(model->rowCount(), 2);
history->removeEntryByUrl(QUrl("http://example.org/test"));
QCOMPARE(model->rowCount(), 2);
history->removeEntryByUrl(QUrl("http://example.org/"));
QCOMPARE(model->rowCount(), 1);
}
void shouldUpdateDataWhenMovingEntries()
{
history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
history->add(QUrl("http://example.com/"), "Example Domain", QUrl());
QTest::qWait(100);
QSignalSpy spyRowsMoved(model, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
qRegisterMetaType >();
QSignalSpy spyDataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
QVERIFY(spyRowsMoved.isEmpty());
}
void shouldUpdateDataWhenDataChanges()
{
history->add(QUrl("http://example.com/"), "Example Domain", QUrl());
history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
QSignalSpy spyRowsMoved(model, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
qRegisterMetaType >();
QSignalSpy spyDataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
history->add(QUrl("http://example.org/foobar"), "Example Domain", QUrl());
QVERIFY(spyRowsMoved.isEmpty());
QVERIFY(!spyDataChanged.isEmpty());
verifyDataChanged(spyDataChanged, 1);
spyDataChanged.clear();
history->add(QUrl("http://example.org/foobar"), "Example Domain 2", QUrl());
QVERIFY(spyRowsMoved.isEmpty());
QVERIFY(!spyDataChanged.isEmpty());
verifyDataChanged(spyDataChanged, 1);
}
void shouldUpdateWhenChangingSourceModel()
{
QSignalSpy spy(model, SIGNAL(sourceModelChanged()));
history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
history->add(QUrl("http://example.com/"), "Example Domain", QUrl());
history->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl());
QCOMPARE(model->rowCount(), 3);
model->setSourceModel(history);
QVERIFY(spy.isEmpty());
QCOMPARE(model->rowCount(), 3);
model->setSourceModel(nullptr);
QCOMPARE(spy.count(), 1);
QCOMPARE(model->sourceModel(), (HistoryModel*) nullptr);
QCOMPARE(model->rowCount(), 0);
HistoryModel history2;
model->setSourceModel(&history2);
QCOMPARE(spy.count(), 2);
QCOMPARE(model->sourceModel(), &history2);
QCOMPARE(model->rowCount(), 0);
}
void shouldKeepDomainsSorted()
{
history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
history->add(QUrl("http://www.gogle.com/lawnmower"), "Gogle Lawn Mower", QUrl());
history->add(QUrl("http://example.com/"), "Example Domain", QUrl());
history->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl());
history->add(QUrl("file:///tmp/test.html"), "test", QUrl());
history->add(QUrl("http://www.gogle.com/mail"), "Gogle Mail", QUrl());
history->add(QUrl("https://mail.gogle.com/"), "Gogle Mail", QUrl());
history->add(QUrl("https://es.wikipedia.org/wiki/Wikipedia:Portada"), "Wikipedia, la enciclopedia libre", QUrl());
QCOMPARE(model->rowCount(), 6);
QStringList domains;
domains << DomainUtils::TOKEN_LOCAL << "example.com" << "example.org"
<< "gogle.com" << "ubuntu.com" << "wikipedia.org";
for (int i = 0; i < domains.count(); ++i) {
QModelIndex index = model->index(i, 0);
QString domain = model->data(index, HistoryDomainListModel::Domain).toString();
HistoryDomainModel* entries = model->data(index, HistoryDomainListModel::Entries).value();
QVERIFY(!domain.isNull());
QVERIFY(!entries->domain().isNull());
QCOMPARE(domain, domains.at(i));
QCOMPARE(entries->domain(), domain);
}
}
void shouldExposeDomainModels()
{
history->add(QUrl("http://example.com/"), "Example Domain", QUrl());
history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
QTest::qWait(100);
history->add(QUrl("http://example.org/test.html"), "Test Page", QUrl());
history->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl());
QCOMPARE(model->rowCount(), 3);
QModelIndex index = model->index(0, 0);
QString domain = model->data(index, HistoryDomainListModel::Domain).toString();
QCOMPARE(domain, QString("example.com"));
HistoryDomainModel* entries = model->data(index, HistoryDomainListModel::Entries).value();
QCOMPARE(entries->rowCount(), 1);
QCOMPARE(entries->data(entries->index(0, 0), HistoryModel::Url).toUrl(), QUrl("http://example.com/"));
index = model->index(1, 0);
domain = model->data(index, HistoryDomainListModel::Domain).toString();
QCOMPARE(domain, QString("example.org"));
entries = model->data(index, HistoryDomainListModel::Entries).value();
QCOMPARE(entries->rowCount(), 2);
QCOMPARE(entries->data(entries->index(0, 0), HistoryModel::Url).toUrl(), QUrl("http://example.org/test.html"));
QCOMPARE(entries->data(entries->index(1, 0), HistoryModel::Url).toUrl(), QUrl("http://example.org/"));
index = model->index(2, 0);
domain = model->data(index, HistoryDomainListModel::Domain).toString();
QCOMPARE(domain, QString("ubuntu.com"));
entries = model->data(index, HistoryDomainListModel::Entries).value();
QCOMPARE(entries->rowCount(), 1);
QCOMPARE(entries->data(entries->index(0, 0), HistoryModel::Url).toUrl(), QUrl("http://ubuntu.com/"));
}
void shouldReturnData()
{
QDateTime now = QDateTime::currentDateTimeUtc();
history->add(QUrl("http://example.org/"), "Example Domain", QUrl());
QVERIFY(!model->data(QModelIndex(), HistoryDomainListModel::Domain).isValid());
QVERIFY(!model->data(model->index(-1, 0), HistoryDomainListModel::Domain).isValid());
QVERIFY(!model->data(model->index(3, 0), HistoryDomainListModel::Domain).isValid());
QCOMPARE(model->data(model->index(0, 0), HistoryDomainListModel::Domain).toString(), QString("example.org"));
QVERIFY(model->data(model->index(0, 0), HistoryDomainListModel::LastVisit).toDateTime() >= now);
HistoryDomainModel* entries = model->data(model->index(0, 0), HistoryDomainListModel::Entries).value();
QVERIFY(entries != 0);
QCOMPARE(entries->rowCount(), 1);
QVERIFY(!model->data(model->index(0, 0), HistoryDomainListModel::Entries + 3).isValid());
}
};
QTEST_MAIN(HistoryDomainListModelTests)
#include "tst_HistoryDomainListModelTests.moc"
./tests/unittests/history-domainlist-model/CMakeLists.txt 0000644 0000156 0000165 00000000646 12703462031 024005 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Sql REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_HistoryDomainListModelTests)
add_executable(${TEST} tst_HistoryDomainListModelTests.cpp)
include_directories(${webbrowser-app_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Sql
Qt5::Test
webbrowser-app-models
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/qml/ 0000755 0000156 0000165 00000000000 12703462032 015071 5 ustar jenkins jenkins ./tests/unittests/qml/tst_BookmarksFoldersViewWide.qml 0000644 0000156 0000165 00000017333 12703462031 023417 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import "../../../src/app/webbrowser"
import webbrowserapp.private 0.1
Item {
id: root
width: 800
height: 600
property BookmarksFoldersViewWide view
property string homepage: "http://example.com/homepage"
Component {
id: viewComponent
BookmarksFoldersViewWide {
anchors.fill: parent
homeBookmarkUrl: homepage
}
}
SignalSpy {
id: bookmarkClickedSpy
signalName: "bookmarkClicked"
}
SignalSpy {
id: bookmarkRemovedSpy
signalName: "bookmarkRemoved"
}
WebbrowserTestCase {
name: "BookmarksFoldersViewWide"
when: windowShown
function init() {
BookmarksModel.databasePath = ":memory:"
view = viewComponent.createObject(root)
populate()
view.focus = true
bookmarkClickedSpy.target = view
bookmarkClickedSpy.clear()
bookmarkRemovedSpy.target = view
bookmarkRemovedSpy.clear()
}
function populate() {
BookmarksModel.add("http://example.com", "Example Com", "", "")
BookmarksModel.add("http://example.org/bar", "Example Org Bar", "", "Folder B")
BookmarksModel.add("http://example.org/foo", "Example Org Foo", "", "Folder B")
BookmarksModel.add("http://example.net/a", "Example Net A", "", "Folder A")
BookmarksModel.add("http://example.net/b", "Example Net B", "", "Folder A")
}
function cleanup() {
BookmarksModel.databasePath = ""
view.destroy()
view = null
}
function test_folder_list() {
var items = getListItems(findChild(view, "foldersList"), "folderItem")
compare(items.length, 3)
verify(items[0].isAllBookmarksFolder)
compare(items[0].model.folder, "")
// named folder items should appear alphabetically sorted
compare(items[1].model.folder, "Folder A")
compare(items[2].model.folder, "Folder B")
}
function test_all_bookmarks_list() {
var items = getListItems(findChild(view, "bookmarksList"), "bookmarkItem")
compare(items.length, 2)
compare(items[0].url, homepage)
compare(items[1].title, "Example Com")
}
function test_navigate_folders_by_keyboard() {
var folders = getListItems(findChild(view, "foldersList"), "folderItem")
verify(folders[0].isActiveFolder)
keyClick(Qt.Key_Down)
verify(!folders[0].isActiveFolder)
verify(folders[1].isActiveFolder)
// bookmarks within a folder are sorted with the first bookmarked appearing last
var bookmarksListView = findChild(view, "bookmarksList")
var items = getListItems(bookmarksListView, "bookmarkItem")
compare(items[0].title, "Example Net B")
compare(items[1].title, "Example Net A")
compare(items.length, 2)
keyClick(Qt.Key_Down)
verify(folders[2].isActiveFolder)
items = getListItems(bookmarksListView, "bookmarkItem")
compare(items[0].title, "Example Org Foo")
compare(items[1].title, "Example Org Bar")
compare(items.length, 2)
// verify scrolling beyond bottom of list is not allowed
keyClick(Qt.Key_Down)
verify(folders[2].isActiveFolder)
keyClick(Qt.Key_Up)
verify(folders[1].isActiveFolder)
keyClick(Qt.Key_Up)
verify(folders[0].isActiveFolder)
keyClick(Qt.Key_Up)
}
function test_switch_between_folder_and_bookmarks_by_keyboard() {
var foldersList = findChild(view, "foldersList")
var bookmarks = findChild(view, "bookmarksList")
var folders = getListItems(foldersList, "folderItem")
verify(folders[0].isActiveFolder)
keyClick(Qt.Key_Right)
verify(bookmarks.activeFocus)
keyClick(Qt.Key_Right)
verify(bookmarks.activeFocus) // verify no circular scrolling
keyClick(Qt.Key_Left)
verify(foldersList.activeFocus)
keyClick(Qt.Key_Left)
verify(foldersList.activeFocus) // verify no circular scrolling
keyClick(Qt.Key_Down)
verify(!folders[0].isActiveFolder)
verify(folders[1].isActiveFolder)
keyClick(Qt.Key_Right)
verify(bookmarks.activeFocus)
keyClick(Qt.Key_Right)
verify(bookmarks.activeFocus) // verify no circular scrolling
keyClick(Qt.Key_Left)
verify(foldersList.activeFocus)
keyClick(Qt.Key_Left)
verify(foldersList.activeFocus) // verify no circular scrolling
}
function test_activate_bookmarks_by_keyboard() {
keyClick(Qt.Key_Right)
var items = getListItems(findChild(view, "bookmarksList"), "bookmarkItem")
keyClick(Qt.Key_Return)
compare(bookmarkClickedSpy.count, 1)
compare(bookmarkClickedSpy.signalArguments[0][0], homepage)
keyClick(Qt.Key_Down)
keyClick(Qt.Key_Return)
compare(bookmarkClickedSpy.count, 2)
compare(bookmarkClickedSpy.signalArguments[1][0], "http://example.com")
}
function test_activate_bookmarks_by_mouse() {
var items = getListItems(findChild(view, "bookmarksList"), "bookmarkItem")
clickItem(items[0])
compare(bookmarkClickedSpy.count, 1)
compare(bookmarkClickedSpy.signalArguments[0][0], homepage)
clickItem(items[1])
compare(bookmarkClickedSpy.count, 2)
compare(bookmarkClickedSpy.signalArguments[1][0], "http://example.com")
}
function test_switch_folders_by_mouse() {
var folders = getListItems(findChild(view, "foldersList"), "folderItem")
clickItem(folders[1])
var bookmarksListView = findChild(view, "bookmarksList")
var items = getListItems(bookmarksListView, "bookmarkItem")
compare(items[0].title, "Example Net B")
compare(items[1].title, "Example Net A")
compare(items.length, 2)
clickItem(folders[0])
items = getListItems(bookmarksListView, "bookmarkItem")
compare(items[0].url, homepage)
compare(items[1].title, "Example Com")
compare(items.length, 2)
}
function test_remove_bookmarks_by_keyboard() {
keyClick(Qt.Key_Right)
var items = getListItems(findChild(view, "bookmarksList"), "bookmarkItem")
// verify that trying to delete the homepage bookmark does not work
keyClick(Qt.Key_Delete)
compare(bookmarkRemovedSpy.count, 0)
keyClick(Qt.Key_Down)
keyClick(Qt.Key_Delete)
compare(bookmarkRemovedSpy.count, 1)
compare(bookmarkRemovedSpy.signalArguments[0][0], items[1].url)
}
}
}
./tests/unittests/qml/tst_SettingsPage.qml 0000644 0000156 0000165 00000004606 12703462031 021100 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import webbrowserapp.private 0.1
import webbrowsertest.private 0.1
import "../../../src/app/webbrowser"
Item {
width: 400
height: 600
property var settingsPage: settingsPageLoader.item
Loader {
id: settingsPageLoader
anchors.fill: parent
active: false
sourceComponent: SettingsPage {
anchors.fill: parent
// NOTE: the following properties are not necessary for the tests
// currently in this file, but if we don't provide them a lot of
// warnings will be generated.
// Ideally either more tests that use them will be added or the code
// in SettingsPage will be refactored to cope with the missing
// settings.
settingsObject: QtObject {
property url homepage
property string searchEngine
property int newTabDefaultSection: 0
}
}
}
WebbrowserTestCase {
name: "TestSettingsPage"
when: windowShown
function init() {
settingsPageLoader.active = true
waitForRendering(settingsPageLoader.item)
}
function cleanup() {
settingsPageLoader.active = false
}
function activateSettingsItem(itemName, pageName) {
var item = findChild(settingsPage, itemName)
clickItem(item)
var page = findChild(settingsPage, pageName)
waitForRendering(page)
return page
}
function test_goToMediaAccessPage() {
activateSettingsItem("privacy", "privacySettings")
return activateSettingsItem("privacy.mediaAccess", "mediaAccessSettings")
}
}
}
./tests/unittests/qml/tst_UbuntuWebView02.qml 0000644 0000156 0000165 00000006645 12703462031 021425 0 ustar jenkins jenkins /*
* Copyright 2013-2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import Ubuntu.Test 1.0
import Ubuntu.Components 1.3
import Ubuntu.Web 0.2
Item {
id: root
width: 200
height: 200
Component {
id: webviewComponent
WebView {
anchors.fill: parent
}
}
ActionList {
id: actionList
Action {
id: action1
}
Action {
id: action2
}
}
readonly property string htmlWithHyperlink: ''
UbuntuTestCase {
name: "UbuntuWebView02"
when: windowShown
property var webview: null
function init() {
webview = webviewComponent.createObject(root)
}
function cleanup() {
webview.destroy()
}
function test_context_singleton() {
var other = webviewComponent.createObject(root)
compare(other.context, webview.context)
other.destroy()
}
function rightClickWebview() {
var center = centerOf(webview)
mouseClick(webview, center.x, center.y, Qt.RightButton)
// give the context menu a chance to appear before carrying on
wait(500)
}
function getContextMenu() {
return findChild(webview, "contextMenu")
}
function dismissContextMenu() {
var center = centerOf(webview)
mouseClick(webview, center.x, center.y)
wait(500)
compare(getContextMenu(), null)
}
function test_no_contextual_actions() {
webview.loadHtml(root.htmlWithHyperlink, "file:///")
tryCompare(webview, "loading", false)
rightClickWebview()
compare(getContextMenu(), null)
}
function test_contextual_actions() {
webview.contextualActions = actionList
webview.loadHtml(root.htmlWithHyperlink, "file:///")
tryCompare(webview, "loading", false)
rightClickWebview()
compare(getContextMenu().actions, actionList)
compare(webview.contextualData.href, "http://example.org/")
dismissContextMenu()
compare(webview.contextualData.href, "")
}
function test_contextual_actions_all_disabled() {
webview.contextualActions = actionList
action1.enabled = false
action2.enabled = false
webview.loadHtml(root.htmlWithHyperlink, "file:///")
tryCompare(webview, "loading", false)
rightClickWebview()
compare(getContextMenu(), null)
action1.enabled = true
action2.enabled = true
}
}
}
./tests/unittests/qml/tst_BookmarksViewWide.qml 0000644 0000156 0000165 00000006272 12703462031 022100 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import "../../../src/app/webbrowser"
import webbrowserapp.private 0.1
Item {
id: root
width: 800
height: 600
BookmarksViewWide {
id: view
anchors.fill: parent
homepageUrl: "http://example.com/homepage"
}
SignalSpy {
id: bookmarkEntryClickedSpy
target: view
signalName: "bookmarkEntryClicked"
}
SignalSpy {
id: doneSpy
target: view
signalName: "done"
}
SignalSpy {
id: newTabClickedSpy
target: view
signalName: "newTabClicked"
}
WebbrowserTestCase {
name: "BookmarksViewWide"
when: windowShown
function init() {
BookmarksModel.databasePath = ":memory:"
populate()
view.forceActiveFocus()
compare(bookmarkEntryClickedSpy.count, 0)
compare(doneSpy.count, 0)
compare(newTabClickedSpy.count, 0)
}
function populate() {
BookmarksModel.add("http://example.com", "Example", "", "")
BookmarksModel.add("http://example.org/a", "Example a", "", "FolderA")
BookmarksModel.add("http://example.org/b", "Example b", "", "FolderB")
compare(BookmarksModel.count, 3)
}
function cleanup() {
BookmarksModel.databasePath = ""
bookmarkEntryClickedSpy.clear()
doneSpy.clear()
newTabClickedSpy.clear()
}
function test_done() {
var button = findChild(view, "doneButton")
clickItem(button)
compare(doneSpy.count, 1)
}
function test_new_tab() {
var action = findChild(view, "newTabAction")
clickItem(action)
compare(newTabClickedSpy.count, 1)
}
function test_click_bookmark() {
var items = getListItems(findChild(view, "bookmarksList"), "bookmarkItem")
clickItem(items[0])
compare(bookmarkEntryClickedSpy.count, 1)
compare(bookmarkEntryClickedSpy.signalArguments[0][0], view.homepageUrl)
clickItem(items[1])
compare(bookmarkEntryClickedSpy.count, 2)
compare(bookmarkEntryClickedSpy.signalArguments[1][0], "http://example.com")
}
function test_delete_bookmark() {
var bookmark = getListItems(findChild(view, "bookmarksList"), "bookmarkItem")[1]
swipeToDeleteAndConfirm(bookmark)
tryCompare(BookmarksModel, "count", 2)
}
}
}
./tests/unittests/qml/tst_UrlUtils.qml 0000644 0000156 0000165 00000007356 12703462031 020273 0 ustar jenkins jenkins /*
* Copyright 2014 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtTest 1.0
import "../../../src/app/UrlUtils.js" as UrlUtils
TestCase {
name: "UrlUtils"
function test_extractAuthority_data() {
return [
{url: "", authority: ""},
{url: "http://example.org/", authority: "example.org"},
{url: "http://www.example.org/", authority: "www.example.org"},
{url: "http://www.example.org/foo/bar", authority: "www.example.org"},
{url: "http://example.org:2442/foo", authority: "example.org:2442"},
{url: "http://user:pwd@example.org/", authority: "user:pwd@example.org"},
{url: "http://user:pwd@example.org:2442/", authority: "user:pwd@example.org:2442"},
{url: "ftp://user:pwd@example.org:21/foo/bar", authority: "user:pwd@example.org:21"}
]
}
function test_extractAuthority(data) {
compare(UrlUtils.extractAuthority(data.url), data.authority)
}
function test_extractHost_data() {
return [
{url: "http://example.org/", host: "example.org"},
{url: "http://www.example.org/", host: "www.example.org"},
{url: "http://www.example.org/foo/bar", host: "www.example.org"},
{url: "http://example.org:2442/foo", host: "example.org"},
{url: "http://user:pwd@example.org/", host: "example.org"},
{url: "http://user:pwd@example.org:2442/", host: "example.org"},
{url: "ftp://user:pwd@example.org:21/foo/bar", host: "example.org"}
]
}
function test_extractHost(data) {
compare(UrlUtils.extractHost(data.url), data.host)
}
function test_removeScheme_data() {
return [
{url: "http://example.org/", removed: "example.org/"},
{url: "file://user:pwd@example.org:2442/", removed: "user:pwd@example.org:2442/"},
{url: "file:///home/foo/bar.txt", removed: "/home/foo/bar.txt"},
{url: "ht+tp://www.example.org/", removed: "www.example.org/"},
{url: "www.example.org", removed: "www.example.org"},
]
}
function test_removeScheme(data) {
compare(UrlUtils.removeScheme(data.url), data.removed)
}
function test_looksLikeAUrl_data() {
return [
{url: "", looksLike: false},
{url: "http://example.org/", looksLike: true},
{url: "example.org", looksLike: true},
{url: "http://www.example.org?q=foo bar", looksLike: false},
{url: "about:blank", looksLike: true},
{url: "file:///usr/foo/bar", looksLike: true},
{url: "hello://my/name/is/", looksLike: true},
{url: "192.168.1.0", looksLike: true}
]
}
function test_looksLikeAUrl(data) {
compare(UrlUtils.looksLikeAUrl(data.url), data.looksLike)
}
function test_fixUrl_data() {
return [
{url: "About:BLANK", fixed: "about:blank"},
{url: "/usr/bin/", fixed: "file:///usr/bin/"},
{url: "example.org", fixed: "http://example.org"}
]
}
function test_fixUrl(data) {
compare(UrlUtils.fixUrl(data.url), data.fixed)
}
}
./tests/unittests/qml/tst_BookmarksView.qml 0000644 0000156 0000165 00000014212 12703462031 021260 0 ustar jenkins jenkins /*
* Copyright 2015-2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtQuick.Window 2.2
import QtTest 1.0
import "../../../src/app/webbrowser"
import webbrowserapp.private 0.1
FocusScope {
id: root
focus: true
width: 300
height: 500
BookmarksView {
id: view
anchors.fill: parent
focus: true
homepageUrl: "http://example.com/homepage"
}
SignalSpy {
id: bookmarkEntryClickedSpy
target: view
signalName: "bookmarkEntryClicked"
}
SignalSpy {
id: doneSpy
target: view
signalName: "done"
}
SignalSpy {
id: newTabClickedSpy
target: view
signalName: "newTabClicked"
}
WebbrowserTestCase {
name: "BookmarksView"
when: windowShown
function init() {
BookmarksModel.databasePath = ":memory:"
populate()
verify(view.activeFocus)
compare(bookmarkEntryClickedSpy.count, 0)
compare(doneSpy.count, 0)
compare(newTabClickedSpy.count, 0)
}
function populate() {
BookmarksModel.add("http://example.com", "Example", "", "")
BookmarksModel.add("http://example.org/a", "Example a", "", "FolderA")
BookmarksModel.add("http://example.org/b", "Example b", "", "FolderB")
compare(BookmarksModel.count, 3)
}
function cleanup() {
BookmarksModel.databasePath = ""
bookmarkEntryClickedSpy.clear()
doneSpy.clear()
newTabClickedSpy.clear()
}
function test_done() {
var button = findChild(view, "doneButton")
clickItem(button)
compare(doneSpy.count, 1)
}
function test_new_tab() {
var action = findChild(view, "newTabAction")
clickItem(action)
compare(newTabClickedSpy.count, 1)
}
function test_click_bookmark() {
var items = getListItems(findChild(view, "bookmarksFolderListView"),
"bookmarkFolderDelegateLoader")
clickItem(findChild(items[0], "urlDelegate_0"))
compare(bookmarkEntryClickedSpy.count, 1)
compare(bookmarkEntryClickedSpy.signalArguments[0][0], view.homepageUrl)
clickItem(findChild(items[0], "urlDelegate_1"))
compare(bookmarkEntryClickedSpy.count, 2)
compare(bookmarkEntryClickedSpy.signalArguments[1][0], "http://example.com")
}
function test_delete_bookmark() {
var items = getListItems(findChild(view, "bookmarksFolderListView"),
"bookmarkFolderDelegateLoader")
var bookmark = findChild(items[0], "urlDelegate_1")
swipeToDeleteAndConfirm(bookmark)
tryCompare(BookmarksModel, "count", 2)
}
function test_keyboard_navigation() {
var listview = findChild(view, "bookmarksFolderListView")
waitForRendering(listview)
verify(listview.activeFocus)
var firstHeader = findChild(listview, "bookmarkFolderHeader")
verify(firstHeader.activeFocus)
var firstFolder = firstHeader.parent
compare(firstFolder.folderName, "")
verify(firstFolder.expanded)
keyClick(Qt.Key_Up)
verify(firstHeader.activeFocus)
keyClick(Qt.Key_Space)
verify(!firstFolder.expanded)
keyClick(Qt.Key_Space)
verify(firstFolder.expanded)
keyClick(Qt.Key_Up)
verify(firstHeader.activeFocus)
keyClick(Qt.Key_Down)
verify(findChild(firstFolder, "urlDelegate_0").activeFocus)
keyClick(Qt.Key_Enter)
compare(bookmarkEntryClickedSpy.count, 1)
compare(bookmarkEntryClickedSpy.signalArguments[0][0], "http://example.com/homepage")
keyClick(Qt.Key_Down)
verify(findChild(firstFolder, "urlDelegate_1").activeFocus)
keyClick(Qt.Key_Return)
compare(bookmarkEntryClickedSpy.count, 2)
compare(bookmarkEntryClickedSpy.signalArguments[1][0], "http://example.com")
keyClick(Qt.Key_Delete)
compare(BookmarksModel.count, 2)
verify(findChild(firstFolder, "urlDelegate_0").activeFocus)
keyClick(Qt.Key_Down)
var secondHeader = root.Window.activeFocusItem
compare(secondHeader.objectName, "bookmarkFolderHeader")
var secondFolder = secondHeader.parent
compare(secondFolder.folderName, "FolderA")
verify(!secondFolder.expanded)
keyClick(Qt.Key_Down)
var thirdHeader = root.Window.activeFocusItem
compare(thirdHeader.objectName, "bookmarkFolderHeader")
var thirdFolder = thirdHeader.parent
compare(thirdFolder.folderName, "FolderB")
verify(!thirdFolder.expanded)
keyClick(Qt.Key_Down)
verify(thirdHeader.activeFocus)
keyClick(Qt.Key_Delete)
verify(thirdHeader.activeFocus)
keyClick(Qt.Key_Space)
verify(thirdFolder.expanded)
keyClick(Qt.Key_Down)
verify(findChild(thirdFolder, "urlDelegate_0").activeFocus)
keyClick(Qt.Key_Delete)
compare(BookmarksModel.count, 1)
verify(!thirdFolder.active)
verify(secondFolder.activeFocus)
keyClick(Qt.Key_Space)
verify(secondFolder.expanded)
}
}
}
./tests/unittests/qml/WebbrowserTestCase.qml 0000644 0000156 0000165 00000004272 12703462031 021365 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import Ubuntu.Test 1.0
UbuntuTestCase {
function clickItem(item, button) {
if (button === undefined) button = Qt.LeftButton
var center = centerOf(item)
mouseClick(item, center.x, center.y, button)
}
function longPressItem(item, button) {
if (button === undefined) button = Qt.LeftButton
var center = centerOf(item)
mouseLongPress(item, center.x, center.y, button)
mouseRelease(item, center.x, center.y, button)
}
function getListItems(listview, itemName) {
waitForRendering(listview)
var items = []
if (listview) {
// ensure all the delegates are created
listview.cacheBuffer = listview.count * 1000
// In some cases the ListView might add other children to the
// contentItem, so we filter the list of children to include
// only actual delegates (names for delegates in this case
// follow the pattern "name_index")
var children = listview.contentItem.children
for (var i = 0; i < children.length; i++) {
if (children[i].objectName.indexOf(itemName) == 0) {
items.push(children[i])
}
}
}
return items
}
function swipeToDeleteAndConfirm(listitem) {
flick(listitem, listitem.width / 10, listitem.height / 2, listitem.width / 2, 0)
var confirm = findChild(listitem, "actionbutton_leadingAction.delete")
clickItem(confirm)
}
}
./tests/unittests/qml/tst_QmlTests.cpp 0000644 0000156 0000165 00000015474 12703462031 020255 0 ustar jenkins jenkins /*
* Copyright 2013-2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
#include
#include
#include
// local
#include "bookmarks-model.h"
#include "bookmarks-folderlist-model.h"
#include "favicon-fetcher.h"
#include "file-operations.h"
#include "history-domain-model.h"
#include "history-domainlist-model.h"
#include "history-model.h"
#include "history-lastvisitdatelist-model.h"
#include "limit-proxy-model.h"
#include "searchengine.h"
#include "tabs-model.h"
#include "text-search-filter-model.h"
#include "Unity/InputInfo/qdeclarativeinputdevicemodel_p.h"
class TestContext : public QObject
{
Q_OBJECT
Q_PROPERTY(QString testDir1 READ testDir1 CONSTANT)
Q_PROPERTY(QString testDir2 READ testDir2 CONSTANT)
public:
explicit TestContext(QObject* parent=0)
: QObject(parent)
{}
QString testDir1() const
{
return m_testDir1.path();
}
QString testDir2() const
{
return m_testDir2.path();
}
Q_INVOKABLE bool writeSearchEngineDescription(
const QString& path, const QString& filename, const QString& name,
const QString& description, const QString& urlTemplate)
{
QFile file(QDir(path).absoluteFilePath(QString("%1.xml").arg(filename)));
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
out << "";
out << "" << name << "";
out << "" << description << "";
out << "";
out << "";
file.close();
return true;
} else {
return false;
}
}
Q_INVOKABLE bool writeInvalidSearchEngineDescription(const QString& path, const QString& filename)
{
QFile file(QDir(path).absoluteFilePath(QString("%1.xml").arg(filename)));
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
out << "invalid";
file.close();
return true;
} else {
return false;
}
}
Q_INVOKABLE bool deleteSearchEngineDescription(const QString& path, const QString& filename)
{
return QFile(QDir(path).absoluteFilePath(QString("%1.xml").arg(filename))).remove();
}
Q_INVOKABLE bool createFile(const QString& filePath) {
// create all the directories necessary for the file to be created
QFileInfo fileInfo(filePath);
if (!QFileInfo::exists(fileInfo.path())) {
QDir::root().mkpath(fileInfo.path());
}
QFile file(fileInfo.absoluteFilePath());
return file.open(QIODevice::WriteOnly | QIODevice::Text);
}
Q_INVOKABLE bool removeDirectory(const QString& path) {
QDir dir(path);
return dir.removeRecursively();
}
private:
QTemporaryDir m_testDir1;
QTemporaryDir m_testDir2;
};
class HistoryModelMock : public HistoryModel {
Q_OBJECT
public:
static bool compareHistoryEntries(const HistoryEntry& a, const HistoryEntry& b) {
return a.lastVisit < b.lastVisit;
}
Q_INVOKABLE int addByDate(const QUrl& url, const QString& title, const QDateTime& date)
{
int index = getEntryIndex(url);
int visitsToAdd = 1;
if (index == -1) {
add(url, title, QString());
index = getEntryIndex(url);
visitsToAdd = 0;
}
// Since this is useful only for testing and efficiency is not critical
// we reorder the model and reset it every time we add a new item by date
// to keep things simple.
beginResetModel();
HistoryEntry entry = m_entries.takeAt(index);
entry.lastVisit = date;
entry.visits = entry.visits + visitsToAdd;
m_entries.append(entry);
std::sort(m_entries.begin(), m_entries.end(), compareHistoryEntries);
endResetModel();
updateExistingEntryInDatabase(entry);
return entry.visits;
}
};
#define MAKE_SINGLETON_FACTORY(type) \
static QObject* type##_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine) { \
Q_UNUSED(engine); \
Q_UNUSED(scriptEngine); \
return new type(); \
}
MAKE_SINGLETON_FACTORY(FileOperations)
MAKE_SINGLETON_FACTORY(BookmarksModel)
MAKE_SINGLETON_FACTORY(HistoryModelMock)
MAKE_SINGLETON_FACTORY(TestContext)
int main(int argc, char** argv)
{
const char* commonUri = "webbrowsercommon.private";
qmlRegisterType(commonUri, 0, 1, "FaviconFetcher");
const char* browserUri = "webbrowserapp.private";
qmlRegisterType(browserUri, 0, 1, "SearchEngine");
qmlRegisterType(browserUri, 0, 1, "TabsModel");
qmlRegisterSingletonType(browserUri, 0, 1, "BookmarksModel", BookmarksModel_singleton_factory);
qmlRegisterType(browserUri, 0, 1, "BookmarksFolderListModel");
qmlRegisterSingletonType(browserUri, 0, 1, "HistoryModel", HistoryModelMock_singleton_factory);
qmlRegisterType(browserUri, 0, 1, "HistoryDomainModel");
qmlRegisterType(browserUri, 0, 1, "HistoryDomainListModel");
qmlRegisterType(browserUri, 0, 1, "HistoryLastVisitDateListModel");
qmlRegisterType(browserUri, 0, 1, "LimitProxyModel");
qmlRegisterType(browserUri, 0, 1, "TextSearchFilterModel");
qmlRegisterSingletonType(browserUri, 0, 1, "FileOperations", FileOperations_singleton_factory);
const char* testUri = "webbrowsertest.private";
qmlRegisterSingletonType(testUri, 0, 1, "TestContext", TestContext_singleton_factory);
const char* inputInfoUri = "Unity.InputInfo";
qmlRegisterType(inputInfoUri, 0, 1, "InputDeviceModel");
qmlRegisterType(inputInfoUri, 0, 1, "InputInfo");
return quick_test_main(argc, argv, "QmlTests", nullptr);
}
#include "tst_QmlTests.moc"
./tests/unittests/qml/tst_BrowserTab.qml 0000644 0000156 0000165 00000012532 12703462031 020552 0 ustar jenkins jenkins /*
* Copyright 2014-2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import "../../../src/app/webbrowser"
import webbrowserapp.private 0.1
Item {
id: root
width: 200
height: 200
Component {
id: tabComponent
BrowserTab {
id: tab
anchors.fill: parent
webviewComponent: Item {
anchors.fill: parent
property url url
property string title
property url icon
property var request
property string currentState
property bool incognito: tab.incognito
property int reloaded: 0
function reload() { reloaded++ }
}
readonly property bool webviewPresent: webview
}
}
SignalSpy {
id: previewSavedSpy
target: PreviewManager
signalName: "previewSaved"
}
TestCase {
name: "BrowserTab"
when: windowShown
function init() {
previewSavedSpy.clear()
}
function test_unique_ids() {
var tab = tabComponent.createObject(root)
var tab2 = tabComponent.createObject(root)
verify(tab.uniqueId)
verify(tab2.uniqueId)
verify(tab.uniqueId !== tab2.uniqueId)
tab.destroy()
tab2.destroy()
}
function test_load_unload() {
var tab = tabComponent.createObject(root)
verify(!tab.webviewPresent)
tab.initialUrl = "http://example.org"
tab.load()
tryCompare(tab, 'webviewPresent', true)
compare(tab.webview.url, "http://example.org")
tab.webview.url = "http://ubuntu.com"
tab.webview.title = "Ubuntu"
tab.webview.currentState = "foobar"
tab.unload()
tryCompare(tab, 'webviewPresent', false)
compare(tab.initialUrl, "http://ubuntu.com")
compare(tab.initialTitle, "Ubuntu")
compare(tab.restoreState, "foobar")
tab.destroy()
}
function test_reload() {
var tab = tabComponent.createObject(root)
verify(!tab.webviewPresent)
tab.initialUrl = "http://example.org"
tab.reload()
tryCompare(tab, 'webviewPresent', true)
compare(tab.webview.reloaded, 0)
tab.reload()
verify(tab.webviewPresent)
compare(tab.webview.reloaded, 1)
tab.destroy()
}
function test_create_with_request() {
var tab = tabComponent.createObject(root, {'request': "foobar"})
tryCompare(tab, 'webviewPresent', true)
verify(tab.webviewPresent)
compare(tab.webview.request, "foobar")
tab.destroy()
}
function test_save_preview() {
var tab = tabComponent.createObject(root)
tab.initialUrl = "http://example.org"
tab.load()
tryCompare(tab, 'webviewPresent', true)
tab.current = true
tab.current = false
tryCompare(previewSavedSpy, "count", 1)
verify(!tab.visible)
compare(previewSavedSpy.signalArguments[0][0], tab.initialUrl)
compare(previewSavedSpy.signalArguments[0][1], Qt.resolvedUrl(PreviewManager.previewPathFromUrl(tab.initialUrl)))
compare(tab.preview, Qt.resolvedUrl(PreviewManager.previewPathFromUrl(tab.initialUrl)))
tab.destroy()
}
function test_no_save_preview_when_incognito() {
var tab = tabComponent.createObject(root)
tab.incognito = true
tab.initialUrl = "http://example.org"
tab.load()
tryCompare(tab, 'webviewPresent', true)
tab.current = true
tab.current = false
// this does not fully guarantee the event won't be emitted later,
// but it is a reasonable delay and certainly better than nothing
wait(250)
compare(previewSavedSpy.count, 0)
compare(tab.preview, "")
tab.destroy()
}
function test_delete_preview_on_close() {
var url = "http://example.org"
var path = Qt.resolvedUrl(PreviewManager.previewPathFromUrl(url))
var tab = tabComponent.createObject(root)
tab.initialUrl = url
tab.load()
tryCompare(tab, 'webviewPresent', true)
tab.current = true
tab.current = false
tryCompare(previewSavedSpy, "count", 1)
verify(FileOperations.exists(path))
tab.close()
verify(!FileOperations.exists(path))
tab.destroy()
}
}
}
./tests/unittests/qml/tst_PreviewManager.qml 0000644 0000156 0000165 00000007127 12703462031 021420 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import Ubuntu.Test 1.0
import "../../../src/app/webbrowser"
import webbrowserapp.private 0.1
import webbrowsertest.private 0.1
Item {
id: root
width: 800
height: 600
SignalSpy {
id: previewSavedSpy
target: PreviewManager
signalName: "previewSaved"
}
QtObject {
id: grabResultMock
function saveToFile(path) {
TestContext.createFile(path);
return true
}
}
QtObject {
id: grabResultFailMock
function saveToFile(path) { return false }
}
UbuntuTestCase {
name: "PreviewManager"
when: windowShown
property string baseUrl: "http://example.com/"
function initTestCase() {
HistoryModel.databasePath = ":memory:"
}
function init() {
previewSavedSpy.clear()
verify(TestContext.removeDirectory(PreviewManager.capturesDir))
}
function populate(count, createPreviewFiles) {
for (var i = 0; i < 11; i++) {
var url = baseUrl + i
HistoryModel.add(url, "Example Com" + i, "")
if (createPreviewFiles) {
var path = PreviewManager.previewPathFromUrl(url)
TestContext.createFile(path)
}
}
}
function cleanup() {
HistoryModel.clearAll()
}
function test_topsites_not_deleted() {
populate(11, true)
for (var i = 0; i < 11; i++) {
var url = baseUrl + i
PreviewManager.checkDelete(url)
var path = Qt.resolvedUrl(PreviewManager.previewPathFromUrl(url))
// verify that only the item that is outside of the top 10 list
// gets deleted
if (i < 10) verify(FileOperations.exists(path))
else verify(!FileOperations.exists(path))
}
}
function test_save_preview() {
var file = Qt.resolvedUrl(PreviewManager.previewPathFromUrl(baseUrl))
PreviewManager.saveToDisk(grabResultMock, baseUrl)
verify(FileOperations.exists(file))
compare(previewSavedSpy.count, 1)
compare(previewSavedSpy.signalArguments[0][0], baseUrl)
compare(previewSavedSpy.signalArguments[0][1], file)
}
function test_save_preview_fail() {
var path = PreviewManager.previewPathFromUrl(baseUrl)
var file = Qt.resolvedUrl(path)
ignoreWarning("Failed to save preview to disk for %1 (path is %2)".arg(baseUrl).arg(path))
PreviewManager.saveToDisk(grabResultFailMock, baseUrl)
verify(!FileOperations.exists(file))
compare(previewSavedSpy.count, 1)
compare(previewSavedSpy.signalArguments[0][0], baseUrl)
compare(previewSavedSpy.signalArguments[0][1], "")
}
}
}
./tests/unittests/qml/tst_HistoryViewWide.qml 0000644 0000156 0000165 00000041263 12703462031 021610 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import Ubuntu.Components 1.3
import webbrowserapp.private 0.1
import webbrowsertest.private 0.1
import "../../../src/app/webbrowser"
Item {
id: root
width: 700
height: 500
property var historyViewWide: historyViewWideLoader.item
property int ctrlFCaptured: 0
Keys.onPressed: {
if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_F)
ctrlFCaptured++
}
Loader {
id: historyViewWideLoader
anchors.fill: parent
active: false
focus: true
sourceComponent: HistoryViewWide {
focus: true
}
}
SignalSpy {
id: doneSpy
target: historyViewWide
signalName: "done"
}
SignalSpy {
id: newTabRequestedSpy
target: historyViewWide
signalName: "newTabRequested"
}
SignalSpy {
id: historyEntryClickedSpy
target: historyViewWide
signalName: "historyEntryClicked"
}
WebbrowserTestCase {
name: "HistoryViewWide"
when: windowShown
function longPressItem(item) {
var center = centerOf(item)
mousePress(item, center.x, center.y)
mouseRelease(item, center.x, center.y, Qt.LeftButton, Qt.NoModifier, 2000)
}
function initTestCase() {
HistoryModel.databasePath = ":memory:"
}
function init() {
historyViewWideLoader.active = true
waitForRendering(historyViewWideLoader.item)
for (var i = 0; i < 3; ++i) {
HistoryModel.add("http://example.org/" + i, "Example Domain " + i, "")
}
historyViewWide.loadModel()
var urlsList = findChild(historyViewWide, "urlsListView")
waitForRendering(urlsList)
tryCompare(urlsList, "count", 3)
}
function cleanup() {
HistoryModel.clearAll()
historyViewWideLoader.active = false
ctrlFCaptured = 0
}
function test_done_button() {
var doneButton = findChild(historyViewWide, "doneButton")
verify(doneButton != null)
doneSpy.clear()
clickItem(doneButton)
compare(doneSpy.count, 1)
}
function test_new_tab_button() {
var newTabButton = findChild(historyViewWide, "newTabButton")
verify(newTabButton != null)
doneSpy.clear()
newTabRequestedSpy.clear()
clickItem(newTabButton)
compare(newTabRequestedSpy.count, 1)
compare(doneSpy.count, 1)
}
function test_history_entry_clicked() {
var urlsList = findChild(historyViewWide, "urlsListView")
compare(urlsList.count, 3)
historyEntryClickedSpy.clear()
clickItem(urlsList.children[0])
compare(historyEntryClickedSpy.count, 1)
var args = historyEntryClickedSpy.signalArguments[0]
var entry = urlsList.model.get(0)
compare(args[0], entry.url)
}
function test_selection_mode() {
var urlsList = findChild(historyViewWide, "urlsListView")
compare(urlsList.count, 3)
var backButton = findChild(historyViewWide, "backButton")
var selectButton = findChild(historyViewWide, "selectButton")
var deleteButton = findChild(historyViewWide, "deleteButton")
verify(!backButton.visible)
verify(!selectButton.visible)
verify(!deleteButton.visible)
longPressItem(urlsList.children[0])
verify(backButton.visible)
verify(selectButton.visible)
verify(deleteButton.visible)
clickItem(backButton)
verify(!backButton.visible)
verify(!selectButton.visible)
verify(!deleteButton.visible)
}
function test_toggle_select_button() {
var urlsList = findChild(historyViewWide, "urlsListView")
compare(urlsList.count, 3)
longPressItem(urlsList.children[0])
var selectedIndices = urlsList.ViewItems.selectedIndices
compare(selectedIndices.length, 1)
var selectButton = findChild(historyViewWide, "selectButton")
clickItem(selectButton)
compare(selectedIndices.length, urlsList.count)
clickItem(selectButton)
var backButton = findChild(historyViewWide, "backButton")
clickItem(backButton)
}
function test_delete_button() {
var urlsList = findChild(historyViewWide, "urlsListView")
compare(urlsList.count, 3)
var deletedUrl = urlsList.model.get(0).url
longPressItem(urlsList.children[0])
var deleteButton = findChild(historyViewWide, "deleteButton")
clickItem(deleteButton)
compare(urlsList.count, 2)
for (var i = 0; i < urlsList.count; ++i) {
verify(urlsList.model.get(i).url != deletedUrl)
}
}
function test_keyboard_navigation_between_lists() {
var lastVisitDateList = findChild(historyViewWide, "lastVisitDateListView")
var urlsList = findChild(historyViewWide, "urlsListView")
verify(!lastVisitDateList.activeFocus)
verify(urlsList.activeFocus)
keyClick(Qt.Key_Left)
verify(lastVisitDateList.activeFocus)
verify(!urlsList.activeFocus)
keyClick(Qt.Key_Right)
verify(urlsList.activeFocus)
}
function test_search_button() {
var searchQuery = findChild(historyViewWide, "searchQuery")
verify(!searchQuery.visible)
var searchButton = findChild(historyViewWide, "searchButton")
verify(searchButton.visible)
clickItem(searchButton)
verify(!searchButton.visible)
verify(searchQuery.visible)
verify(searchQuery.activeFocus)
compare(searchQuery.text, "")
var urlsList = findChild(historyViewWide, "urlsListView")
compare(urlsList.count, 3)
typeString("2")
compare(urlsList.count, 1)
var backButton = findChild(historyViewWide, "backButton")
verify(backButton.visible)
clickItem(backButton)
verify(!backButton.visible)
verify(!searchQuery.visible)
verify(searchButton.visible)
compare(urlsList.count, 3)
clickItem(searchButton)
compare(searchQuery.text, "")
}
function test_keyboard_navigation_for_search() {
var urlsList = findChild(historyViewWide, "urlsListView")
verify(urlsList.activeFocus)
keyClick(Qt.Key_F, Qt.ControlModifier)
var searchQuery = findChild(historyViewWide, "searchQuery")
verify(searchQuery.activeFocus)
keyClick(Qt.Key_Escape)
verify(urlsList.activeFocus)
keyClick(Qt.Key_F, Qt.ControlModifier)
keyClick(Qt.Key_Down)
verify(urlsList.activeFocus)
keyClick(Qt.Key_Up)
verify(searchQuery.activeFocus)
keyClick(Qt.Key_Down)
keyClick(Qt.Key_Left)
keyClick(Qt.Key_Up)
verify(searchQuery.activeFocus)
}
function test_ctrl_f_during_search_returns_to_query() {
var urlsList = findChild(historyViewWide, "urlsListView")
var datesList = findChild(historyViewWide, "lastVisitDateListView")
var searchQuery = findChild(historyViewWide, "searchQuery")
verify(urlsList.activeFocus)
verify(!historyViewWide.searchMode)
keyClick(Qt.Key_F, Qt.ControlModifier)
verify(searchQuery.activeFocus)
verify(historyViewWide.searchMode)
// CTRL+F jumps back to the search box from the urls list...
keyClick(Qt.Key_Down)
verify(urlsList.activeFocus)
keyClick(Qt.Key_F, Qt.ControlModifier)
verify(searchQuery.activeFocus)
// ...and from the dates list
keyClick(Qt.Key_Down)
keyClick(Qt.Key_Left)
verify(datesList.activeFocus)
keyClick(Qt.Key_F, Qt.ControlModifier)
verify(searchQuery.activeFocus)
}
function test_ctrl_f_during_select_is_swallowed() {
var urlsList = findChild(historyViewWide, "urlsListView")
longPressItem(urlsList.children[0])
verify(historyViewWide.selectMode)
keyClick(Qt.Key_F, Qt.ControlModifier)
wait(50) // make sure event loop has processed
compare(ctrlFCaptured, 0)
verify(historyViewWide.selectMode)
}
function test_history_entry_activated_by_keyboard() {
var urlsList = findChild(historyViewWide, "urlsListView")
compare(urlsList.count, 3)
historyEntryClickedSpy.clear()
keyClick(Qt.Key_Enter)
compare(historyEntryClickedSpy.count, 1)
var args = historyEntryClickedSpy.signalArguments[0]
var entry = urlsList.model.get(0)
compare(String(args[0]), String(entry.url))
// now try the same during a search
historyEntryClickedSpy.clear()
keyClick(Qt.Key_F, Qt.ControlModifier)
typeString("dom")
keyClick(Qt.Key_Down)
keyClick(Qt.Key_Enter)
compare(historyEntryClickedSpy.count, 1)
args = historyEntryClickedSpy.signalArguments[0]
entry = urlsList.model.get(0)
compare(String(args[0]), String(entry.url))
}
function test_search_highlight() {
function wraphtml(text) { return "%1".arg(text) }
function highlight(term) {
return "%2".arg("#752571").arg(term)
}
var searchButton = findChild(historyViewWide, "searchButton")
var searchQuery = findChild(historyViewWide, "searchQuery")
var urlsList = findChild(historyViewWide, "urlsListView")
clickItem(searchButton)
var term = "2"
typeString(term)
var items = getListItems(urlsList, "historyDelegate")
compare(items.length, 1)
compare(items[0].title, wraphtml("Example Domain " + highlight(term)))
var backButton = findChild(historyViewWide, "backButton")
clickItem(backButton)
clickItem(searchButton)
var terms = ["1", "Example"]
typeString(terms.join(" "))
items = getListItems(urlsList, "historyDelegate")
compare(items.length, 1)
compare(items[0].title, wraphtml("%1 Domain %0"
.arg(highlight(terms[0]))
.arg(highlight(terms[1]))))
}
function test_search_updates_dates_list() {
function getDateItem(date) {
var lastVisitDateList = findChild(historyViewWide, "lastVisitDateListView")
var dates = getListItems(lastVisitDateList, "lastVisitDateDelegate")
var items = dates.filter(function(item) {
return item.lastVisitDate.valueOf() === date.valueOf()
})
if (items.length > 0) return items.pop()
else return null
}
function returnToDatesList() {
keyClick(Qt.Key_Down)
keyClick(Qt.Key_Left)
}
var searchQuery = findChild(historyViewWide, "searchQuery")
var today = new Date()
today = new Date(today.getFullYear(), today.getMonth(), today.getDate())
var youngest = new Date(1912, 6, 23)
var model = HistoryModel
model.addByDate("https://en.wikipedia.org/wiki/Alan_Turing", "Alan Turing", youngest)
model.addByDate("https://en.wikipedia.org/wiki/Alonzo_Church", "Alonzo Church", new Date(1903, 6, 14))
var lastVisitDateList = findChild(historyViewWide, "lastVisitDateListView")
var dates = getListItems(lastVisitDateList, "lastVisitDateDelegate")
var urlsListView = findChild(historyViewWide, "urlsListView")
var urls = getListItems(urlsListView, "historyDelegate")
compare(dates.length, 4)
compare(urls.length, 5)
// select a date that has search results in it and verify it is
// still the currently selected one after the search.
var testItem = getDateItem(youngest)
clickItem(testItem)
verify(testItem.activeFocus)
keyClick(Qt.Key_F, Qt.ControlModifier)
typeString("Alan")
urls = getListItems(urlsListView, "historyDelegate")
compare(urls.length, 1)
returnToDatesList()
verify(testItem.activeFocus)
// change the search terms so that it will display more items, but
// since we have a selected date, we will see only one
keyClick(Qt.Key_F, Qt.ControlModifier)
keyClick(Qt.Key_Backspace)
keyClick(Qt.Key_Backspace)
compare(searchQuery.text, "Al")
returnToDatesList()
verify(testItem.activeFocus)
urls = getListItems(urlsListView, "historyDelegate")
compare(urls.length, 1)
// change the search terms so that the current date will not be
// included in the results
keyClick(Qt.Key_F, Qt.ControlModifier)
typeString("onzo")
compare(searchQuery.text, "Alonzo")
returnToDatesList()
testItem = getDateItem(youngest)
compare(testItem, null)
urls = getListItems(urlsListView, "historyDelegate")
compare(urls.length, 1)
// verify that the current item has reverted to the first in the
// dates list ("all dates")
compare(lastVisitDateList.currentIndex, 0)
// if widen the search again now, we should see both results again
keyClick(Qt.Key_F, Qt.ControlModifier)
keyClick(Qt.Key_Backspace)
keyClick(Qt.Key_Backspace)
keyClick(Qt.Key_Backspace)
keyClick(Qt.Key_Backspace)
compare(searchQuery.text, "Al")
urls = getListItems(urlsListView, "historyDelegate")
compare(urls.length, 2)
}
function test_delete_key_at_urls_list_view() {
var urlsList = findChild(historyViewWide, "urlsListView")
keyClick(Qt.Key_Right)
verify(urlsList.activeFocus)
compare(urlsList.count, 3)
keyClick(Qt.Key_Delete)
compare(urlsList.count, 2)
// now try the same while in a search
keyClick(Qt.Key_F, Qt.ControlModifier)
typeString("dom")
keyClick(Qt.Key_Down)
keyClick(Qt.Key_Delete)
compare(urlsList.count, 1)
}
function test_delete_key_at_last_visit_date() {
var lastVisitDateList = findChild(historyViewWide, "lastVisitDateListView")
var urlsList = findChild(historyViewWide, "urlsListView")
keyClick(Qt.Key_Left)
verify(lastVisitDateList.activeFocus)
compare(lastVisitDateList.currentIndex, 0)
keyClick(Qt.Key_Down)
compare(lastVisitDateList.currentIndex, 1)
compare(urlsList.count, 3)
keyClick(Qt.Key_Delete)
compare(urlsList.count, 0)
}
function test_delete_key_at_all_history() {
var lastVisitDateList = findChild(historyViewWide, "lastVisitDateListView")
var urlsList = findChild(historyViewWide, "urlsListView")
keyClick(Qt.Key_Left)
verify(lastVisitDateList.activeFocus)
compare(lastVisitDateList.currentIndex, 0)
compare(urlsList.count, 3)
keyClick(Qt.Key_Delete)
compare(urlsList.count, 0)
}
}
}
./tests/unittests/qml/tst_WebProcessMonitor.qml 0000644 0000156 0000165 00000006514 12703462031 022127 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import com.canonical.Oxide 1.8 as Oxide
import "../../../src/app"
WebProcessMonitor {
id: monitor
Item {
id: webviewMock
property int webProcessStatus
property int reloadCalled
function reload() {
webProcessStatus = Oxide.WebView.WebProcessRunning
reloadCalled++
}
}
TestCase {
name: "WebProcessMonitor"
function init() {
webviewMock.webProcessStatus = Oxide.WebView.WebProcessRunning
webviewMock.reloadCalled = 0
}
function test_no_webview() {
monitor.webview = null
compare(monitor.killedRetries, 0)
verify(!monitor.killed)
verify(!monitor.crashed)
}
function test_killed() {
monitor.webview = webviewMock
compare(monitor.killedRetries, 0)
webviewMock.webProcessStatus = Oxide.WebView.WebProcessKilled
verify(monitor.killed)
verify(!monitor.crashed)
tryCompare(monitor, "killedRetries", 1)
tryCompare(webviewMock, "reloadCalled", 1)
verify(!monitor.killed)
verify(!monitor.crashed)
compare(monitor.killedRetries, 1)
webviewMock.webProcessStatus = Oxide.WebView.WebProcessKilled
verify(monitor.killed)
verify(!monitor.crashed)
compare(monitor.killedRetries, 1)
compare(webviewMock.reloadCalled, 1)
}
function test_crashed() {
monitor.webview = webviewMock
compare(monitor.killedRetries, 0)
webviewMock.webProcessStatus = Oxide.WebView.WebProcessCrashed
verify(!monitor.killed)
verify(monitor.crashed)
compare(monitor.killedRetries, 0)
compare(webviewMock.reloadCalled, 0)
webviewMock.webProcessStatus = Oxide.WebView.WebProcessRunning
verify(!monitor.killed)
verify(!monitor.crashed)
compare(monitor.killedRetries, 0)
compare(webviewMock.reloadCalled, 0)
}
function test_change_webview() {
monitor.webview = webviewMock
compare(monitor.killedRetries, 0)
verify(!monitor.killed)
verify(!monitor.crashed)
webviewMock.webProcessStatus = Oxide.WebView.WebProcessKilled
verify(monitor.killed)
verify(!monitor.crashed)
tryCompare(monitor, "killedRetries", 1)
monitor.webview = null
compare(monitor.killedRetries, 0)
verify(!monitor.killed)
verify(!monitor.crashed)
}
}
}
./tests/unittests/qml/tst_NewTabViewWide.qml 0000644 0000156 0000165 00000015175 12703462031 021332 0 ustar jenkins jenkins /*
* Copyright 2015-2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import "../../../src/app/webbrowser"
import webbrowserapp.private 0.1
Item {
id: root
width: 800
height: 600
property NewTabViewWide view
property var bookmarks
property string homepage: "http://example.com/homepage"
Component {
id: viewComponent
NewTabViewWide {
anchors.fill: parent
settingsObject: QtObject {
property url homepage: root.homepage
property int newTabDefaultSection: 0
}
}
}
SignalSpy {
id: historyEntryClickedSpy
signalName: "historyEntryClicked"
}
SignalSpy {
id: bookmarkClickedSpy
signalName: "bookmarkClicked"
}
SignalSpy {
id: bookmarkRemovedSpy
signalName: "bookmarkRemoved"
}
WebbrowserTestCase {
name: "NewTabViewWide"
when: windowShown
function init()
{
BookmarksModel.databasePath = ":memory:"
HistoryModel.databasePath = ":memory:"
view = viewComponent.createObject(root)
populate()
view.focus = true
historyEntryClickedSpy.target = view
historyEntryClickedSpy.clear()
bookmarkClickedSpy.target = view
bookmarkClickedSpy.clear()
bookmarkRemovedSpy.target = view
bookmarkRemovedSpy.clear()
}
function populate() {
HistoryModel.add("http://example.com", "Example Com", "")
HistoryModel.add("http://example.org", "Example Org", "")
HistoryModel.add("http://example.net", "Example Net", "")
BookmarksModel.add("http://example.com", "Example Com", "", "")
BookmarksModel.add("http://example.org/bar", "Example Org Bar", "", "Folder B")
BookmarksModel.add("http://example.org/foo", "Example Org Foo", "", "Folder B")
BookmarksModel.add("http://example.net/a", "Example Net A", "", "Folder A")
BookmarksModel.add("http://example.net/b", "Example Net B", "", "Folder A")
}
function cleanup() {
BookmarksModel.databasePath = ""
HistoryModel.databasePath = ""
view.destroy()
view = null
}
function goToBookmarks() {
findChild(view, "sections").selectedIndex = 1
}
function test_topsites_list() {
// add 8 more top sites so that we are beyond the limit of 10
for (var i = 0; i < 8; i++) {
HistoryModel.add("http://example.com/" + i, "Example Com " + i, "")
}
var items = getListItems(findChild(view, "topSitesList"), "topSiteItem")
compare(items.length, 10)
compare(items[0].title, "Example Com")
compare(items[1].title, "Example Org")
compare(items[2].title, "Example Net")
for (var i = 0; i < 7; i++) {
compare(items[i + 3].title, "Example Com " + i)
}
}
function test_switch_sections_by_keyboard() {
skip("Would fail due to UITK bug: http://pad.lv/1481233")
var sections = findChild(view, "sections")
var folders = findChild(view, "foldersList")
var bookmarks = findChild(view, "bookmarksList")
var topSites = findChild(view, "topSitesList")
compare(sections.selectedIndex, 0)
verify(topSites.visible)
verify(!folders.visible)
verify(!bookmarks.visible)
keyClick(Qt.Key_Tab)
compare(sections.selectedIndex, 1)
verify(!topSites.visible)
verify(folders.visible)
verify(bookmarks.visible)
keyClick(Qt.Key_Backtab)
compare(sections.selectedIndex, 0)
}
function test_navigate_topsites_by_keyboard() {
var items = getListItems(findChild(view, "topSitesList"), "topSiteItem")
var list = findChild(view, "topSitesList")
list.currentIndex = 0
keyClick(Qt.Key_Right)
compare(list.currentIndex, 1)
keyClick(Qt.Key_Right)
compare(list.currentIndex, 2)
keyClick(Qt.Key_Right) // ensure list does not wrap around
compare(list.currentIndex, 2)
keyClick(Qt.Key_Left)
compare(list.currentIndex, 1)
keyClick(Qt.Key_Left)
compare(list.currentIndex, 0)
keyClick(Qt.Key_Up)
compare(list.currentIndex, 0)
keyClick(Qt.Key_Left)
compare(list.currentIndex, 0)
}
function test_activate_topsites_by_keyboard() {
var items = getListItems(findChild(view, "topSitesList"), "topSiteItem")
keyClick(Qt.Key_Return)
compare(historyEntryClickedSpy.count, 1)
compare(historyEntryClickedSpy.signalArguments[0][0], "http://example.com")
keyClick(Qt.Key_Right)
keyClick(Qt.Key_Return)
compare(historyEntryClickedSpy.count, 2)
compare(historyEntryClickedSpy.signalArguments[1][0], "http://example.org")
}
function test_activate_topsites_by_mouse() {
var items = getListItems(findChild(view, "topSitesList"), "topSiteItem")
clickItem(items[0])
compare(historyEntryClickedSpy.count, 1)
compare(historyEntryClickedSpy.signalArguments[0][0], "http://example.com")
clickItem(items[1])
compare(historyEntryClickedSpy.count, 2)
compare(historyEntryClickedSpy.signalArguments[1][0], "http://example.org")
}
function test_remove_top_sites_by_keyboard() {
var topSitesListView = findChild(view, "topSitesList")
var previous = getListItems(topSitesListView, "topSiteItem")
keyClick(Qt.Key_Delete)
var items = getListItems(topSitesListView, "topSiteItem")
compare(previous.length - 1, items.length)
}
}
}
./tests/unittests/qml/tst_ChromeController.qml 0000644 0000156 0000165 00000030275 12703462031 021765 0 ustar jenkins jenkins /*
* Copyright 2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import com.canonical.Oxide 1.7 as Oxide
import "../../../src/app"
ChromeController {
id: controller
Item {
id: webviewMock
property bool loading: false
readonly property bool loadingState: loading
property bool fullscreen: false
property var locationBarController: QtObject {
property bool animated: false
property int mode: controller.defaultMode
signal show(bool animate)
}
signal loadEvent(var event)
}
SignalSpy {
id: showSpy
target: webviewMock.locationBarController
signalName: "show"
}
webview: webviewMock
TestCase {
name: "ChromeController"
readonly property int modeAuto: Oxide.LocationBarController.ModeAuto
readonly property int modeShown: Oxide.LocationBarController.ModeShown
readonly property int modeHidden: Oxide.LocationBarController.ModeHidden
function init() {
controller.forceHide = false
controller.forceShow = false
controller.defaultMode = modeAuto
webviewMock.loading = false
webviewMock.fullscreen = false
webviewMock.locationBarController.animated = false
webviewMock.locationBarController.mode = controller.defaultMode
showSpy.clear()
}
function test_change_webview_data() {
return [
{forceHide: false, forceShow: false, fullscreen: false,
mode: modeAuto, shown: true},
{forceHide: false, forceShow: true, fullscreen: false,
mode: modeShown, shown: false},
{forceHide: false, forceShow: false, fullscreen: true,
mode: modeAuto, shown: false},
{forceHide: false, forceShow: true, fullscreen: true,
mode: modeShown, shown: false},
{forceHide: true, forceShow: false, fullscreen: true,
mode: modeHidden, shown: false},
{forceHide: true, forceShow: true, fullscreen: false,
mode: modeHidden, shown: false},
{forceHide: true, forceShow: true, fullscreen: true,
mode: modeHidden, shown: false},
]
}
function test_change_webview(data) {
controller.webview = null
controller.forceHide = data.forceHide
controller.forceShow = data.forceShow
webviewMock.fullscreen = data.fullscreen
showSpy.clear()
controller.webview = webviewMock
compare(webviewMock.locationBarController.mode, data.mode)
compare(showSpy.count, data.shown ? 1 : 0)
}
function test_change_forceHide_data() {
return [
{forceShow: false, fullscreen: false,
modes: [modeAuto, modeHidden, modeAuto], shown: 1},
{forceShow: true, fullscreen: false,
modes: [modeShown, modeHidden, modeShown], shown: 0},
{forceShow: false, fullscreen: true,
modes: [modeHidden, modeHidden, modeHidden], shown: 0},
{forceShow: true, fullscreen: true,
modes: [modeHidden, modeHidden, modeShown], shown: 0},
]
}
function test_change_forceHide(data) {
controller.forceShow = data.forceShow
webviewMock.fullscreen = data.fullscreen
showSpy.clear()
controller.forceHide = false
compare(webviewMock.locationBarController.mode, data.modes[0])
controller.forceHide = true
compare(webviewMock.locationBarController.mode, data.modes[1])
controller.forceHide = false
compare(webviewMock.locationBarController.mode, data.modes[2])
compare(showSpy.count, data.shown)
}
function test_change_forceShow_data() {
return [
{forceHide: false, fullscreen: false,
modes: [modeAuto, modeShown, modeAuto], shown: 1},
{forceHide: true, fullscreen: false,
modes: [modeHidden, modeHidden, modeHidden], shown: 0},
{forceHide: false, fullscreen: true,
modes: [modeHidden, modeShown, modeShown], shown: 0},
{forceHide: true, fullscreen: true,
modes: [modeHidden, modeHidden, modeHidden], shown: 0},
]
}
function test_change_forceShow(data) {
controller.forceHide = data.forceHide
webviewMock.fullscreen = data.fullscreen
showSpy.clear()
controller.forceShow = false
compare(webviewMock.locationBarController.mode, data.modes[0])
controller.forceShow = true
compare(webviewMock.locationBarController.mode, data.modes[1])
controller.forceShow = false
compare(webviewMock.locationBarController.mode, data.modes[2])
compare(showSpy.count, data.shown)
}
function test_change_fullscreen_data() {
return [
{forceHide: false, forceShow: false, defaultMode: modeAuto,
mode: modeAuto, shown: true},
{forceHide: false, forceShow: false, defaultMode: modeShown,
mode: modeShown, shown: false},
{forceHide: false, forceShow: false, defaultMode: modeHidden,
mode: modeHidden, shown: false},
{forceHide: true, forceShow: false, defaultMode: modeAuto,
mode: modeHidden, shown: false},
{forceHide: true, forceShow: false, defaultMode: modeShown,
mode: modeHidden, shown: false},
{forceHide: true, forceShow: false, defaultMode: modeHidden,
mode: modeHidden, shown: false},
{forceHide: false, forceShow: true, defaultMode: modeAuto,
mode: modeShown, shown: false},
{forceHide: false, forceShow: true, defaultMode: modeShown,
mode: modeShown, shown: false},
{forceHide: false, forceShow: true, defaultMode: modeHidden,
mode: modeShown, shown: false},
{forceHide: true, forceShow: true, defaultMode: modeAuto,
mode: modeHidden, shown: false},
{forceHide: true, forceShow: true, defaultMode: modeShown,
mode: modeHidden, shown: false},
{forceHide: true, forceShow: true, defaultMode: modeHidden,
mode: modeHidden, shown: false},
]
}
function test_change_fullscreen(data) {
webviewMock.fullscreen = false
controller.forceHide = data.forceHide
controller.forceShow = data.forceShow
controller.defaultMode = data.defaultMode
showSpy.clear()
webviewMock.fullscreen = true
compare(webviewMock.locationBarController.mode, modeHidden)
compare(showSpy.count, 0)
webviewMock.fullscreen = false
compare(webviewMock.locationBarController.mode, data.mode)
compare(showSpy.count, data.shown ? 1 : 0)
}
function test_loading_state_changed_data() {
return [
{forceHide: false, forceShow: false, fullscreen: false,
mode: modeAuto, shown: true},
{forceHide: false, forceShow: false, fullscreen: false,
mode: modeShown, shown: false},
{forceHide: false, forceShow: false, fullscreen: false,
mode: modeHidden, shown: false},
{forceHide: true, forceShow: false, fullscreen: false,
mode: modeHidden, shown: false},
{forceHide: false, forceShow: true, fullscreen: false,
mode: modeShown, shown: false},
{forceHide: false, forceShow: false, fullscreen: true,
mode: modeHidden, shown: false},
{forceHide: true, forceShow: true, fullscreen: false,
mode: modeHidden, shown: false},
{forceHide: true, forceShow: false, fullscreen: true,
mode: modeHidden, shown: false},
{forceHide: false, forceShow: true, fullscreen: true,
mode: modeShown, shown: false},
{forceHide: true, forceShow: true, fullscreen: true,
mode: modeHidden, shown: false},
]
}
function test_loading_state_changed(data) {
controller.forceHide = data.forceHide
controller.forceShow = data.forceShow
webviewMock.fullscreen = data.fullscreen
webviewMock.locationBarController.mode = data.mode
showSpy.clear()
webviewMock.loading = true
compare(showSpy.count, data.shown ? 1 : 0)
compare(webviewMock.locationBarController.mode, data.mode)
showSpy.clear()
webviewMock.loading = false
compare(showSpy.count, 0)
compare(webviewMock.locationBarController.mode, data.mode)
}
function test_load_event_data() {
var data = []
var booleanValues = [false, true]
var modeValues = [modeAuto, modeHidden, modeShown]
for (var i in booleanValues) {
for (var j in booleanValues) {
for (var k in modeValues) {
for (var l in modeValues) {
data.push({forceHide: booleanValues[i], forceShow: booleanValues[j],
initialMode: modeValues[k], defaultMode: modeValues[l]})
}
}
}
}
return data
}
function test_load_event(data) {
// event types
var started = Oxide.LoadEvent.TypeStarted
var committed = Oxide.LoadEvent.TypeCommitted
var succeeded = Oxide.LoadEvent.TypeSucceeded
var stopped = Oxide.LoadEvent.TypeStopped
var failed = Oxide.LoadEvent.TypeFailed
var redirected = Oxide.LoadEvent.TypeRedirected
controller.forceHide = data.forceHide
controller.forceShow = data.forceShow
controller.defaultMode = data.defaultMode
webviewMock.locationBarController.mode = data.initialMode
showSpy.clear()
function test_sequence(sequence, modes) {
for (var i in sequence) {
webviewMock.loadEvent({type: sequence[i]})
if (data.forceHide || data.forceShow) {
compare(webviewMock.locationBarController.mode, data.initialMode)
} else {
compare(webviewMock.locationBarController.mode, modes[i])
}
compare(showSpy.count, 0)
}
}
var sequence = [started, committed, succeeded]
var modes = [modeShown, data.defaultMode, data.defaultMode]
test_sequence(sequence, modes)
sequence = [started, stopped]
modes = [modeShown, data.defaultMode]
test_sequence(sequence, modes)
sequence = [started, failed, committed]
modes = [modeShown, modeShown, data.defaultMode]
test_sequence(sequence, modes)
sequence = [started, redirected, committed, succeeded]
modes = [modeShown, modeShown, data.defaultMode, data.defaultMode]
test_sequence(sequence, modes)
}
}
}
./tests/unittests/qml/tst_AddressBar.qml 0000644 0000156 0000165 00000033617 12703462031 020521 0 ustar jenkins jenkins /*
* Copyright 2013-2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import "../../../src/app/webbrowser"
Item {
width: 300
height: 60
FocusScope {
anchors.fill: parent
AddressBar {
id: addressBar
anchors {
top: parent.top
left: parent.left
right: parent.right
}
height: parent.height / 2
searchUrl: "http://www.ubuntu.com/search?q={searchTerms}"
editing: activeFocus
canSimplifyText: true
findController: QtObject {
property int current
property int count
}
}
// only exists to steal focus from the address bar
TextInput {
id: textInput
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
height: parent.height / 2
}
}
SignalSpy {
id: validatedSpy
target: addressBar
signalName: "validated"
}
WebbrowserTestCase {
name: "AddressBar"
when: windowShown
function init() {
addressBar.actualUrl = ""
validatedSpy.clear()
// Ensure the address bar has active focus
clickItem(addressBar)
verify(addressBar.activeFocus)
// Clear it
var clearButton = findChild(addressBar, "clear_button")
verify(clearButton != null)
clickItem(clearButton)
compare(addressBar.text, "")
// Ensure it still has active focus
verify(addressBar.activeFocus)
}
function test_validUrlShouldNotBeRewritten_data() {
return [
{url: "file:///usr/share/doc/ubuntu-online-tour/index.html"},
{url: "http://ubuntu.com"},
{url: "https://google.com"},
{url: "ftp://ubuntu.com"},
{url: "about:blank"},
{url: "data:,A brief note"},
{url: "http://com.google"}
]
}
function test_validUrlShouldNotBeRewritten(data) {
typeString(data.url)
compare(addressBar.text, data.url)
keyClick(Qt.Key_Return)
validatedSpy.wait()
compare(addressBar.requestedUrl, data.url)
}
function test_urlWithoutSchemeShouldBeRewritten_data() {
return [
{text: "ubuntu.com", requestedUrl: "http://ubuntu.com"},
{text: "192.168.1.1", requestedUrl: "http://192.168.1.1"},
{text: "192.168.1.1:8000", requestedUrl: "http://192.168.1.1:8000"},
{text: "192.168.1.1:8000/dummy.html", requestedUrl: "http://192.168.1.1:8000/dummy.html"},
{text: "/usr/share/doc/ubuntu-online-tour/index.html", requestedUrl: "file:///usr/share/doc/ubuntu-online-tour/index.html"},
]
}
function test_urlWithoutSchemeShouldBeRewritten(data) {
typeString(data.text)
compare(addressBar.text, data.text)
keyClick(Qt.Key_Return)
validatedSpy.wait()
compare(addressBar.requestedUrl, data.requestedUrl)
}
function test_leadingAndTrailingWhitespacesShouldBeTrimmed_data() {
return [
{text: " http://ubuntu.com", requestedUrl: "http://ubuntu.com"},
{text: "http://ubuntu.com ", requestedUrl: "http://ubuntu.com"},
{text: " http://ubuntu.com ", requestedUrl: "http://ubuntu.com"},
]
}
function test_leadingAndTrailingWhitespacesShouldBeTrimmed(data) {
typeString(data.text)
compare(addressBar.text, data.text)
keyClick(Qt.Key_Return)
validatedSpy.wait()
compare(addressBar.requestedUrl, data.requestedUrl)
}
function test_searchQueryShouldBeRewritten_data() {
return [
{text: "lorem ipsum dolor sit amet", start: "http://www.ubuntu.com", query: "lorem+ipsum+dolor+sit+amet"},
{text: "ubuntu", start: "http://www.ubuntu.com", query: "ubuntu"},
]
}
function test_searchQueryShouldBeRewritten(data) {
typeString(data.text)
compare(addressBar.text, data.text)
keyClick(Qt.Key_Return)
validatedSpy.wait()
compare(addressBar.requestedUrl.toString().indexOf(data.start), 0)
verify(addressBar.requestedUrl.toString().indexOf("q=" + data.query) > 0)
}
function test_htmlEntitiesShouldBeEscapedInSearchQueries_data() {
return [
{text: "tom & jerry", escaped: "tom+%26+jerry"},
{text: "a+ rating", escaped: "a%2B+rating"},
{text: "\"kung fu\"", escaped: "%22kung+fu%22"},
{text: "surfin' usa", escaped: "surfin'+usa"},
{text: "to be or not to be?", escaped: "to+be+or+not+to+be%3F"},
]
}
function test_htmlEntitiesShouldBeEscapedInSearchQueries(data) {
typeString(data.text)
compare(addressBar.text, data.text)
keyClick(Qt.Key_Return)
validatedSpy.wait()
verify(addressBar.requestedUrl.toString().indexOf("q=" + data.escaped) > 0)
}
function test_uppercaseDomainsShouldBeRewritten_data() {
return [
{text: "WWW.UBUNTU.COM", requestedUrl: "http://www.ubuntu.com"},
{text: "EN.WIKIPEDIA.ORG/wiki/Ubuntu", requestedUrl: "http://en.wikipedia.org/wiki/Ubuntu"},
{text: "EN.WIKIPEDIA.ORG/wiki/UBUNTU", requestedUrl: "http://en.wikipedia.org/wiki/UBUNTU"},
]
}
function test_uppercaseDomainsShouldBeRewritten(data) {
typeString(data.text)
compare(addressBar.text, data.text)
keyClick(Qt.Key_Return)
validatedSpy.wait()
compare(addressBar.requestedUrl, data.requestedUrl)
}
function test_uppercaseSchemeShouldBeRewritten_data() {
return [
{text: "HTTP://WWW.UBUNTU.COM", requestedUrl: "http://www.ubuntu.com"},
{text: "HTTP://www.ubuntu.com", requestedUrl: "http://www.ubuntu.com"},
{text: "HTTPS://www.ubuntu.com", requestedUrl: "https://www.ubuntu.com"},
{text: "FILE:///usr/share/doc/ubuntu-online-tour/index.html", requestedUrl: "file:///usr/share/doc/ubuntu-online-tour/index.html"},
{text: "FTP://ubuntu.com", requestedUrl: "ftp://ubuntu.com"},
{text: "ABOUT:BLANK", requestedUrl: "about:blank"},
{text: "DATA:,A brief note", requestedUrl: "data:,A brief note"},
{text: "HTTP://com.GOOGLE", requestedUrl: "http://com.google"}
]
}
function test_uppercaseSchemeShouldBeRewritten(data) {
typeString(data.text)
compare(addressBar.text, data.text)
keyClick(Qt.Key_Return)
validatedSpy.wait()
compare(addressBar.requestedUrl, data.requestedUrl)
}
function test_urlShouldBeSimplifiedWhenUnfocused_data() {
return [
{input: "http://www.ubuntu.com",
simplified: "ubuntu.com",
actualUrl: "http://www.ubuntu.com"},
{input: "http://www.ubuntu.com/",
simplified: "ubuntu.com",
actualUrl: "http://www.ubuntu.com/"},
{input: "http://www.ubuntu.com:80",
simplified: "ubuntu.com",
actualUrl: "http://www.ubuntu.com:80"},
{input: "http://www.ubuntuwww.com",
simplified: "ubuntuwww.com",
actualUrl: "http://www.ubuntuwww.com"},
{input: "http://www.com",
simplified: "www.com",
actualUrl: "http://www.com"},
{input: "http://user@www.ubuntu.com",
simplified: "ubuntu.com",
actualUrl: "http://user@www.ubuntu.com"},
{input: "http://user:password@www.ubuntu.com",
simplified: "ubuntu.com",
actualUrl: "http://user:password@www.ubuntu.com"},
{input: "http://user:password@www.ubuntu.com:80",
simplified: "ubuntu.com",
actualUrl: "http://user:password@www.ubuntu.com:80"},
{input: "file:///home/phablet/",
simplified: "file:///home/phablet/",
actualUrl: "file:///home/phablet/"},
{input: "http://en.wikipedia.org/wiki/Ubuntu",
simplified: "en.wikipedia.org",
actualUrl: "http://en.wikipedia.org/wiki/Ubuntu"},
{input: "en.wikipedia.org",
simplified: "en.wikipedia.org",
actualUrl: "http://en.wikipedia.org"},
{input: "en.wikipedia.org/wiki/Foo",
simplified: "en.wikipedia.org",
actualUrl: "http://en.wikipedia.org/wiki/Foo"},
{input: "http://com.google",
simplified: "com.google",
actualUrl: "http://com.google"},
]
}
function test_urlShouldBeSimplifiedWhenUnfocused(data) {
typeString(data.input)
compare(addressBar.text, data.input)
keyClick(Qt.Key_Return)
validatedSpy.wait()
addressBar.actualUrl = addressBar.requestedUrl
compare(addressBar.text, data.input)
clickItem(textInput)
compare(addressBar.text, data.simplified)
clickItem(addressBar)
compare(addressBar.text, data.actualUrl)
}
function test_actionButtonShouldBeDisabledWhenEmpty() {
verify(!addressBar.__actionButton.enabled)
keyClick(Qt.Key_U)
verify(addressBar.text != "")
verify(addressBar.__actionButton.enabled)
}
function test_clickingWhenUnfocusedShouldSelectAll() {
var url = "http://example.org/"
typeString(url)
compare(addressBar.text, url)
addressBar.actualUrl = url
clickItem(textInput)
verify(!addressBar.activeFocus)
clickItem(addressBar)
compare(addressBar.__textField.selectedText, url)
}
function test_clickingWhenFocusedShouldDeselectText() {
var url = "http://example.org/"
typeString(url)
compare(addressBar.text, url)
addressBar.actualUrl = url
clickItem(textInput)
verify(!addressBar.activeFocus)
clickItem(addressBar)
compare(addressBar.__textField.selectedText, url)
clickItem(addressBar)
compare(addressBar.__textField.selectedText, "")
verify(addressBar.__textField.cursorPosition > 0)
}
function test_clickingActionButtonWhenUnfocusedShouldNotSelectAll() {
var url = "http://example.org/"
typeString(url)
compare(addressBar.text, url)
clickItem(textInput)
verify(!addressBar.activeFocus)
clickItem(addressBar.__actionButton)
compare(addressBar.__textField.selectedText, "")
}
function test_shouldNotAllowBookmarkingWhenEmpty() {
// focused
var toggle = addressBar.__bookmarkToggle
verify(!toggle.visible)
// and unfocused
clickItem(textInput)
verify(!toggle.visible)
}
function test_shouldNotAllowBookmarkingWhileFocused() {
addressBar.actualUrl = "http://example.org"
var toggle = addressBar.__bookmarkToggle
verify(!toggle.visible)
clickItem(textInput)
verify(toggle.visible)
}
function test_togglingIndicatorShouldBookmark() {
skip("Skipped due to what seems to be a bug in the UITK: https://launchpad.net/bugs/1483279")
addressBar.actualUrl = "http://example.org"
clickItem(textInput)
verify(!addressBar.bookmarked)
var toggle = addressBar.__bookmarkToggle
clickItem(toggle)
verify(addressBar.bookmarked)
clickItem(toggle)
verify(!addressBar.bookmarked)
}
function test_unfocusingWhileEditingShouldResetUrl() {
var url = "http://example.org/"
typeString(url)
compare(addressBar.text, url)
addressBar.actualUrl = url
var clearButton = findChild(addressBar, "clear_button")
verify(clearButton != null)
clickItem(clearButton)
compare(addressBar.text, "")
clickItem(textInput)
compare(addressBar.text, "example.org")
clickItem(addressBar)
compare(addressBar.text, url)
}
function test_exitingFindInPageRestoresUrl() {
addressBar.actualUrl = "http://example.org/"
addressBar.findInPageMode = true
verify(addressBar.activeFocus)
compare(addressBar.text, "")
typeString("hello")
addressBar.findInPageMode = false
compare(addressBar.text, "example.org")
}
}
}
./tests/unittests/qml/tst_TabsBar.qml 0000644 0000156 0000165 00000023020 12703462031 020010 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import "../../../src/app/webbrowser"
import webbrowserapp.private 0.1
Item {
id: root
width: 600
height: 200
signal reload(string url)
TabsModel {
id: tabsModel
}
Component {
id: tabComponent
QtObject {
id: tab
property url url
property string title
property url icon
function close() { destroy() }
function reload() { root.reload(tab.url) }
}
}
TabsBar {
id: tabs
// Make the tabs bar smaller than the window and aligned in the middle
// to leave room for the context menu to pop up and have all its items
// visible within the screen.
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
height: 50
model: tabsModel
onSwitchToTab: model.currentIndex = index
onRequestNewTab: insertTab("", "", "", index)
function appendTab(url, title, icon) {
insertTab(url, title, icon, model.count)
model.currentIndex = model.count - 1
}
function insertTab(url, title, icon, index) {
var tab = tabComponent.createObject(root, {"url": url, "title": title, "icon": icon})
model.insert(tab, index)
}
}
SignalSpy {
id: newTabRequestSpy
target: tabs
signalName: "requestNewTab"
}
SignalSpy {
id: tabClosedSpy
target: tabs
signalName: "tabClosed"
}
SignalSpy {
id: reloadSpy
target: root
signalName: "reload"
}
WebbrowserTestCase {
name: "TabsBar"
when: windowShown
function getMenuItemForAction(menu, actionName) {
return findChild(menu, "tab_action_" + actionName + "_button")
}
function getTabDelegate(index) {
var container = findChild(tabs, "tabsContainer")
for (var i = 0; i < container.children.length; ++i) {
var child = container.children[i]
if ((child.objectName == "tabDelegate") && (child.tabIndex == index)) {
return child
}
}
return null
}
function popupMenuOnTab(index) {
var tab = getTabDelegate(index)
if (tab) {
clickItem(tab, Qt.RightButton)
var menu = findChild(root, "tabContextualActions")
waitForRendering(menu)
return menu
} else return null
}
function cleanup() {
while (tabsModel.count > 0) {
tabsModel.remove(0).destroy()
}
newTabRequestSpy.clear()
reloadSpy.clear()
tabClosedSpy.clear()
}
function populateTabs() {
var count = 3
for (var i = 0; i < count; ++i) {
tabs.appendTab("", "tab " + i, "")
}
compare(tabsModel.currentIndex, count - 1)
return count
}
function test_create_new_tab() {
var newTabButton = findChild(tabs, "newTabButton")
for (var i = 0; i < 3; ++i) {
tryCompare(tabsModel, "count", i)
clickItem(newTabButton)
}
compare(newTabRequestSpy.count, 3)
compare(newTabRequestSpy.signalArguments[0][0], 0)
compare(newTabRequestSpy.signalArguments[1][0], 1)
compare(newTabRequestSpy.signalArguments[2][0], 2)
}
function test_mouse_left_click() {
// Left click makes the tab current
populateTabs()
for (var i = 2; i >= 0; --i) {
clickItem(getTabDelegate(i))
compare(tabsModel.currentIndex, i)
}
}
function test_mouse_middle_click() {
// Middle click closes the tab
var count = populateTabs()
for (var i = 0; i < count; i++) {
var tab = getTabDelegate(i)
clickItem(tab, Qt.MiddleButton)
compare(tabClosedSpy.count, i + 1)
}
}
function test_mouse_right_click() {
// Right click pops up the contextual actions menu
populateTabs()
var menu = popupMenuOnTab(0)
verify(menu)
verify(menu.visible)
}
function test_mouse_wheel() {
// Wheel events cycle through open tabs
populateTabs()
var tab0 = getTabDelegate(0)
var c = centerOf(tab0)
function wheelUp() { mouseWheel(tab0, c.x, c.y, 0, 120) }
function wheelDown() { mouseWheel(tab0, c.x, c.y, 0, -120) }
wheelDown()
compare(tabsModel.currentIndex, 2)
wheelUp()
compare(tabsModel.currentIndex, 1)
wheelUp()
compare(tabsModel.currentIndex, 0)
wheelUp()
compare(tabsModel.currentIndex, 0)
wheelDown()
compare(tabsModel.currentIndex, 1)
}
function test_close_tabs_data() {
return [
{button: Qt.LeftButton},
{button: Qt.MiddleButton}
]
}
function test_close_tabs(data) {
var count = populateTabs()
for (var i = 0; i < count; i++) {
var tab = getTabDelegate(count - (i + 1))
var closeButton = findChild(tab, "closeButton")
clickItem(closeButton, data.button)
compare(tabClosedSpy.count, i + 1)
compare(tabClosedSpy.signalArguments[i][0], count - (i + 1))
}
}
function test_drag_tab() {
populateTabs()
function dragTab(tab, dx, index) {
var c = centerOf(tab)
mouseDrag(tab, c.x, c.y, dx, 0)
compare(getTabDelegate(index), tab)
compare(tabsModel.currentIndex, index)
wait(500)
}
// Move the first tab to the right
var tab = getTabDelegate(0)
dragTab(tab, tab.width * 0.8, 1)
// Start a move to the right and release too early
dragTab(tab, tab.width * 0.3, 1)
// Start a move to the left and release too early
dragTab(tab, -tab.width * 0.4, 1)
// Move the tab all the way to the right and overshoot
dragTab(tab, tab.width * 3, 2)
// Move another tab all the way to the left and overshoot
tab = getTabDelegate(1)
dragTab(tab, -tab.width * 2, 0)
}
function test_menu_states_on_new_tab() {
populateTabs()
var menu = popupMenuOnTab(0)
var item = getMenuItemForAction(menu, "new_tab")
verify(item.enabled)
item = getMenuItemForAction(menu, "reload")
verify(!item.enabled)
item = getMenuItemForAction(menu, "close_tab")
verify(item.enabled)
}
function test_menu_states_on_page() {
tabs.appendTab("http://localhost/", "tab", "")
var menu = popupMenuOnTab(0)
var item = getMenuItemForAction(menu, "new_tab")
verify(item.enabled)
item = getMenuItemForAction(menu, "reload")
verify(item.enabled)
item = getMenuItemForAction(menu, "close_tab")
verify(item.enabled)
}
function test_context_menu_close() {
populateTabs()
var menu = popupMenuOnTab(1)
var item = getMenuItemForAction(menu, "close_tab")
clickItem(item)
compare(tabClosedSpy.count, 1)
compare(tabClosedSpy.signalArguments[0][0], 1)
}
function test_context_menu_reload() {
var baseUrl = "http://localhost/"
tabs.appendTab(baseUrl + "1", "tab 1", "")
tabs.appendTab(baseUrl + "2", "tab 2", "")
var menu = popupMenuOnTab(1)
var item = getMenuItemForAction(menu, "reload")
clickItem(item)
compare(reloadSpy.count, 1)
compare(reloadSpy.signalArguments[0][0], baseUrl + "2")
}
function test_context_menu_new_tab() {
var baseUrl = "http://localhost/"
tabs.appendTab(baseUrl + "1", "tab 1", "")
tabs.appendTab(baseUrl + "2", "tab 2", "")
var menu = popupMenuOnTab(0)
var item = getMenuItemForAction(menu, "new_tab")
clickItem(item)
compare(newTabRequestSpy.count, 1)
compare(newTabRequestSpy.signalArguments[0][0], 1)
compare(tabsModel.count, 3)
compare(tabsModel.get(0).url, baseUrl + "1")
compare(tabsModel.get(1).url, "")
compare(tabsModel.get(2).url, baseUrl + "2")
}
}
}
./tests/unittests/qml/tst_NewTabView.qml 0000644 0000156 0000165 00000023407 12703462031 020516 0 ustar jenkins jenkins /*
* Copyright 2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtQuick.Window 2.2
import QtTest 1.0
import Qt.labs.settings 1.0
import "../../../src/app/webbrowser"
import webbrowserapp.private 0.1
FocusScope {
id: root
focus: true
width: 350
height: 500
Loader {
id: newTabViewLoader
anchors.fill: parent
focus: true
active: false
sourceComponent: NewTabView {
focus: true
anchors.fill: parent
settingsObject: Settings {
property url homepage: "http://example.com/homepage"
}
}
}
readonly property Item view: newTabViewLoader.item
SignalSpy {
id: bookmarkClickedSpy
target: view
signalName: "bookmarkClicked"
}
SignalSpy {
id: bookmarkRemovedSpy
target: view
signalName: "bookmarkRemoved"
}
SignalSpy {
id: historyEntryClickedSpy
target: view
signalName: "historyEntryClicked"
}
SignalSpy {
id: countChangedSpy
signalName: "countChanged"
}
WebbrowserTestCase {
name: "NewTabView"
when: windowShown
function init() {
HistoryModel.databasePath = ":memory:"
BookmarksModel.databasePath = ":memory:"
populate()
newTabViewLoader.active = true
waitForRendering(view)
verify(view.activeFocus)
compare(bookmarkClickedSpy.count, 0)
compare(bookmarkRemovedSpy.count, 0)
compare(historyEntryClickedSpy.count, 0)
}
function populate() {
HistoryModel.add("http://example.com/foo", "foo", "")
HistoryModel.add("http://example.org/bar", "bar", "")
HistoryModel.add("http://example.net/baz", "baz", "")
HistoryModel.add("http://example.net/qux", "qux", "")
HistoryModel.add("http://example.net/norf", "norf", "")
compare(HistoryModel.count, 5)
BookmarksModel.add("http://example.com", "Example", "", "")
BookmarksModel.add("http://example.org/a", "Example a", "", "FolderA")
BookmarksModel.add("http://example.org/b", "Example b", "", "FolderB")
BookmarksModel.add("http://example.org/c", "Example c", "", "FolderC")
BookmarksModel.add("http://example.org/d", "Example d", "", "FolderD")
compare(BookmarksModel.count, 5)
}
function cleanup() {
newTabViewLoader.active = false
BookmarksModel.databasePath = ""
HistoryModel.databasePath = ""
bookmarkClickedSpy.clear()
bookmarkRemovedSpy.clear()
historyEntryClickedSpy.clear()
}
function verify_bookmarks_expanded(expected) {
if (expected) {
compare(findChild(view, "bookmarksList"), null)
compare(findChild(view, "bookmarksFolderListView").objectName,
"bookmarksFolderListView")
} else {
compare(findChild(view, "bookmarksList").objectName,
"bookmarksList")
compare(findChild(view, "bookmarksFolderListView"), null)
}
}
function check_focused_item(objectName) {
var focused = root.Window.activeFocusItem
verify(focused !== null)
compare(focused.objectName, objectName)
return focused
}
function test_click_bookmark() {
var listview = findChild(view, "bookmarksList")
var homepage = findChild(listview, "homepageBookmark")
clickItem(homepage)
compare(bookmarkClickedSpy.count, 1)
compare(bookmarkClickedSpy.signalArguments[0][0],
view.settingsObject.homepage)
var bookmark = findChild(listview, "bookmark_1")
clickItem(bookmark)
compare(bookmarkClickedSpy.count, 2)
compare(bookmarkClickedSpy.signalArguments[1][0], bookmark.url)
}
function test_delete_bookmark() {
var listview = findChild(view, "bookmarksList")
var bookmark = findChild(listview, "bookmark_2")
swipeToDeleteAndConfirm(bookmark)
bookmarkRemovedSpy.wait()
compare(bookmarkRemovedSpy.count, 1)
compare(bookmarkRemovedSpy.signalArguments[0][0], bookmark.url)
}
function test_expand_bookmarks() {
verify_bookmarks_expanded(false)
var button = findChild(view, "bookmarks.moreButton")
clickItem(button)
verify_bookmarks_expanded(true)
clickItem(button)
verify_bookmarks_expanded(false)
}
function test_click_top_site() {
var grid = findChild(view, "topSitesList")
compare(grid.count, 5)
var topsites = getListItems(grid, "topSiteItem")
compare(topsites.length, 5)
var topsite = topsites[3]
clickItem(topsite)
compare(historyEntryClickedSpy.count, 1)
compare(historyEntryClickedSpy.signalArguments[0][0], topsite.url)
}
function test_delete_top_site() {
var grid = findChild(view, "topSitesList")
compare(grid.count, 5)
var topsite = getListItems(grid, "topSiteItem")[1]
clickItem(topsite, Qt.RightButton)
var contextMenu = findChild(root, "urlPreviewDelegate.contextMenu")
var action = findChild(contextMenu, "delete_button")
clickItem(action)
compare(grid.count, 4)
}
function test_keyboard_navigation() {
var bookmarksList = findChild(view, "bookmarksList")
verify(bookmarksList.activeFocus)
check_focused_item("homepageBookmark")
keyClick(Qt.Key_Up)
check_focused_item("bookmarkListHeader")
verify_bookmarks_expanded(false)
keyClick(Qt.Key_Enter)
verify_bookmarks_expanded(true)
keyClick(Qt.Key_Return)
verify_bookmarks_expanded(false)
keyClick(Qt.Key_Space)
verify_bookmarks_expanded(true)
keyClick(Qt.Key_Enter)
verify_bookmarks_expanded(false)
keyClick(Qt.Key_Up)
check_focused_item("bookmarkListHeader")
keyClick(Qt.Key_Down)
check_focused_item("homepageBookmark")
keyClick(Qt.Key_Delete)
compare(bookmarkRemovedSpy.count, 0)
keyClick(Qt.Key_Enter)
compare(bookmarkClickedSpy.count, 1)
compare(bookmarkClickedSpy.signalArguments[0][0],
view.settingsObject.homepage)
keyClick(Qt.Key_Down)
var bookmark = check_focused_item("bookmark_1")
keyClick(Qt.Key_Delete)
compare(bookmarkRemovedSpy.count, 1)
compare(bookmarkRemovedSpy.signalArguments[0][0], bookmark.url)
keyClick(Qt.Key_Down)
keyClick(Qt.Key_Down)
keyClick(Qt.Key_Down)
check_focused_item("bookmark_4")
keyClick(Qt.Key_Down)
var grid = findChild(view, "topSitesList")
verify(grid.activeFocus)
compare(grid.currentIndex, 0)
var topsite = check_focused_item("topSiteItem")
keyClick(Qt.Key_Enter)
compare(historyEntryClickedSpy.count, 1)
compare(historyEntryClickedSpy.signalArguments[0][0], topsite.url)
keyClick(Qt.Key_Return)
compare(historyEntryClickedSpy.count, 2)
compare(historyEntryClickedSpy.signalArguments[1][0], topsite.url)
keyClick(Qt.Key_Right)
compare(grid.currentIndex, 1)
check_focused_item("topSiteItem")
keyClick(Qt.Key_Down)
compare(grid.currentIndex, 3)
check_focused_item("topSiteItem")
keyClick(Qt.Key_Down)
compare(grid.currentIndex, 3)
check_focused_item("topSiteItem")
keyClick(Qt.Key_Left)
compare(grid.currentIndex, 2)
check_focused_item("topSiteItem")
keyClick(Qt.Key_Down)
compare(grid.currentIndex, 4)
check_focused_item("topSiteItem")
keyClick(Qt.Key_Down)
compare(grid.currentIndex, 4)
check_focused_item("topSiteItem")
keyClick(Qt.Key_Left)
compare(grid.currentIndex, 3)
check_focused_item("topSiteItem")
var notopsiteslabel = findChild(view, "notopsites")
verify(!notopsiteslabel.visible)
compare(grid.count, 5)
countChangedSpy.target = grid
for (var i = 4; i >= 0; --i) {
keyClick(Qt.Key_Delete)
countChangedSpy.wait()
compare(grid.count, i)
compare(grid.currentIndex, i - 1)
}
compare(grid.count, 0)
verify(notopsiteslabel.visible)
bookmarksList = findChild(view, "bookmarksList")
verify(bookmarksList.activeFocus)
keyClick(Qt.Key_Down)
verify(bookmarksList.activeFocus)
}
}
}
./tests/unittests/qml/tst_SearchEngines.qml 0000644 0000156 0000165 00000011304 12703462031 021212 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import "../../../src/app/webbrowser"
import webbrowsertest.private 0.1
import webbrowserapp.private 0.1
Item {
id: root
width: 200
height: 200
SearchEngines {
id: searchEngines
readonly property int count: engines.count
}
SearchEngine {
id: testEngine
searchPaths: searchEngines.searchPaths
}
TestCase {
name: "SearchEngines"
function checkEngine(index, filename, name, description, urlTemplate) {
compare(searchEngines.engines.get(index).filename, filename)
testEngine.filename = filename
compare(testEngine.name, name)
compare(testEngine.description, description)
compare(testEngine.urlTemplate, urlTemplate)
testEngine.filename = ""
}
function test_no_engines() {
searchEngines.searchPaths = []
tryCompare(searchEngines, "count", 0)
}
function test_find_engines() {
verify(TestContext.writeSearchEngineDescription(
TestContext.testDir1, "engine1", "engine1", "engine1 search",
"https://example.org/search1?q={searchTerms}"))
verify(TestContext.writeSearchEngineDescription(
TestContext.testDir2, "engine2", "engine2", "engine2 search",
"https://example.org/search2?q={searchTerms}"))
searchEngines.searchPaths = [TestContext.testDir1, TestContext.testDir2]
tryCompare(searchEngines, "count", 2)
checkEngine(0, "engine1", "engine1", "engine1 search",
"https://example.org/search1?q={searchTerms}")
checkEngine(1, "engine2", "engine2", "engine2 search",
"https://example.org/search2?q={searchTerms}")
// override engine2 in dir2 with another description in dir1
verify(TestContext.writeSearchEngineDescription(
TestContext.testDir1, "engine2",
"engine2-overridden", "engine2-overridden search",
"https://example.org/search2-overridden?q={searchTerms}"))
compare(searchEngines.count, 2)
checkEngine(1, "engine2", "engine2-overridden",
"engine2-overridden search",
"https://example.org/search2-overridden?q={searchTerms}")
// reverse the order of search paths to verify that the order
// of precedence is updated
searchEngines.searchPaths = [TestContext.testDir2, TestContext.testDir1]
tryCompare(searchEngines, "count", 2)
checkEngine(0, "engine1", "engine1", "engine1 search",
"https://example.org/search1?q={searchTerms}")
checkEngine(1, "engine2", "engine2", "engine2 search",
"https://example.org/search2?q={searchTerms}")
// override engine2 with an invalid description and verify
// that it is removed from the list
verify(TestContext.deleteSearchEngineDescription(
TestContext.testDir2, "engine2"))
verify(TestContext.writeInvalidSearchEngineDescription(
TestContext.testDir2, "engine2"))
tryCompare(searchEngines, "count", 1)
checkEngine(0, "engine1", "engine1", "engine1 search",
"https://example.org/search1?q={searchTerms}")
// remove the invalid description and verify that the other
// description re-appears
verify(TestContext.deleteSearchEngineDescription(
TestContext.testDir2, "engine2"))
tryCompare(searchEngines, "count", 2)
checkEngine(1, "engine2", "engine2-overridden",
"engine2-overridden search",
"https://example.org/search2-overridden?q={searchTerms}")
// clean up
verify(TestContext.deleteSearchEngineDescription(
TestContext.testDir1, "engine2"))
verify(TestContext.deleteSearchEngineDescription(
TestContext.testDir1, "engine1"))
}
}
}
./tests/unittests/qml/tst_FileExtensionMapper.qml 0000644 0000156 0000165 00000002352 12703462031 022420 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtTest 1.0
import "../../../src/app/FileExtensionMapper.js" as FileExtensionMapper
TestCase {
name: "FileExtensionMapper"
function test_getExtension_data() {
return [
{in: "", ext: ""},
{in: "pdf", ext: ""},
{in: ".vimrc", ext: ""},
{in: "example.pdf", ext: "pdf"},
{in: "http://example.org/path/example.pdf", ext: "pdf"},
{in: "EXAMPLE.PDF", ext: "pdf"}
]
}
function test_getExtension(data) {
compare(FileExtensionMapper.getExtension(data.in), data.ext)
}
}
./tests/unittests/qml/CMakeLists.txt 0000644 0000156 0000165 00000003204 12703462031 017627 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Qml REQUIRED)
find_package(Qt5Quick REQUIRED)
find_package(Qt5QuickTest REQUIRED)
find_package(Qt5Sql REQUIRED)
set(XVFB_COMMAND)
find_program(XVFBRUN xvfb-run)
if(XVFBRUN)
set(XVFB_COMMAND ${XVFBRUN} -s "-screen 0 640x480x24" -a)
else()
message(WARNING "Cannot find xvfb-run.")
endif()
set(TEST tst_QmlTests)
set(SOURCES
${webbrowser-common_SOURCE_DIR}/favicon-fetcher.cpp
${webbrowser-app_SOURCE_DIR}/bookmarks-model.cpp
${webbrowser-app_SOURCE_DIR}/bookmarks-folder-model.cpp
${webbrowser-app_SOURCE_DIR}/bookmarks-folderlist-model.cpp
${webbrowser-app_SOURCE_DIR}/file-operations.cpp
${webbrowser-app_SOURCE_DIR}/history-domain-model.cpp
${webbrowser-app_SOURCE_DIR}/history-domainlist-model.cpp
${webbrowser-app_SOURCE_DIR}/history-model.cpp
${webbrowser-app_SOURCE_DIR}/history-lastvisitdatelist-model.cpp
${webbrowser-app_SOURCE_DIR}/limit-proxy-model.cpp
${webbrowser-app_SOURCE_DIR}/searchengine.cpp
${webbrowser-app_SOURCE_DIR}/tabs-model.cpp
${webbrowser-app_SOURCE_DIR}/text-search-filter-model.cpp
tst_QmlTests.cpp
)
add_executable(${TEST} ${SOURCES})
include_directories(
${webbrowser-common_SOURCE_DIR}
${webbrowser-app_SOURCE_DIR}
${unity8_SOURCE_DIR}/plugins
)
target_link_libraries(${TEST}
Qt5::Core
Qt5::Gui
Qt5::Network
Qt5::Qml
Qt5::Quick
Qt5::QuickTest
Qt5::Sql
InputInfo
)
add_test(${TEST} ${XVFB_COMMAND} ${CMAKE_CURRENT_BINARY_DIR}/${TEST}
-input ${CMAKE_CURRENT_SOURCE_DIR}
-import ${CMAKE_BINARY_DIR}/src)
./tests/unittests/qml/tst_HistoryView.qml 0000644 0000156 0000165 00000017303 12703462031 020775 0 ustar jenkins jenkins /*
* Copyright 2015-2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtQuick.Window 2.2
import QtTest 1.0
import "../../../src/app/webbrowser"
import webbrowserapp.private 0.1
FocusScope {
id: root
focus: true
width: 300
height: 500
readonly property Item historyView: historyViewLoader.item
Loader {
id: historyViewLoader
anchors.fill: parent
active: false
focus: true
sourceComponent: HistoryView {
focus: true
}
}
SignalSpy {
id: seeMoreEntriesClickedSpy
target: historyView
signalName: "seeMoreEntriesClicked"
}
SignalSpy {
id: newTabRequestedSpy
target: historyView
signalName: "newTabRequested"
}
SignalSpy {
id: doneSpy
target: historyView
signalName: "done"
}
WebbrowserTestCase {
name: "HistoryView"
when: windowShown
function init() {
HistoryModel.databasePath = ":memory:"
populate()
historyViewLoader.active = true
historyView.loadModel()
waitForRendering(historyView)
verify(historyView.activeFocus)
var domainsList = findChild(historyView, "domainsListView")
waitForRendering(domainsList)
tryCompare(domainsList, "count", 3)
compare(seeMoreEntriesClickedSpy.count, 0)
compare(doneSpy.count, 0)
}
function populate() {
HistoryModel.add("http://example.com/foo", "foo", "")
HistoryModel.add("http://example.org/bar", "bar", "")
HistoryModel.add("http://example.net/baz", "baz", "")
compare(HistoryModel.count, 3)
}
function cleanup() {
historyViewLoader.active = false
HistoryModel.databasePath = ""
seeMoreEntriesClickedSpy.clear()
doneSpy.clear()
}
function test_done() {
var button = findChild(historyView, "doneButton")
clickItem(button)
compare(doneSpy.count, 1)
}
function test_new_tab() {
var action = findChild(historyView, "newTabAction")
clickItem(action)
compare(newTabRequestedSpy.count, 1)
compare(doneSpy.count, 1)
}
function test_see_more_entries() {
var listview = findChild(historyView, "domainsListView")
var domain = getListItems(listview, "historyViewDomainDelegate")[0]
clickItem(domain)
compare(seeMoreEntriesClickedSpy.count, 1)
compare(seeMoreEntriesClickedSpy.signalArguments[0][0].domain, domain.title)
}
function test_delete_domain() {
var listview = findChild(historyView, "domainsListView")
var domain = getListItems(listview, "historyViewDomainDelegate")[1]
swipeToDeleteAndConfirm(domain)
tryCompare(HistoryModel, "count", 2)
}
function test_exit_select_mode() {
var listview = findChild(historyView, "domainsListView")
var domains = getListItems(listview, "historyViewDomainDelegate")
var first = domains[0]
verify(!first.selectMode)
longPressItem(first)
tryCompare(first, "selectMode", true)
var closeButton = findChild(historyView, "closeButton")
clickItem(closeButton)
tryCompare(first, "selectMode", false)
}
function test_delete_multiple_domains() {
var listview = findChild(historyView, "domainsListView")
var domains = getListItems(listview, "historyViewDomainDelegate")
var first = domains[0]
verify(!first.selectMode)
longPressItem(first)
tryCompare(first, "selectMode", true)
tryCompare(first, "selected", true)
var third = domains[2]
verify(!third.selected)
clickItem(third)
tryCompare(third, "selected", true)
var deleteButton = findChild(historyView, "deleteButton")
clickItem(deleteButton)
tryCompare(first, "selectMode", false)
tryCompare(HistoryModel, "count", 1)
}
function test_select_all() {
var listview = findChild(historyView, "domainsListView")
var domains = getListItems(listview, "historyViewDomainDelegate")
var first = domains[0], second = domains[1], third = domains[2]
verify(!first.selectMode)
longPressItem(first)
tryCompare(first, "selectMode", true)
tryCompare(first, "selected", true)
verify(!second.selected)
verify(!third.selected)
var deleteButton = findChild(historyView, "deleteButton")
verify(deleteButton.enabled)
var selectAllButton = findChild(historyView, "selectAllButton")
clickItem(selectAllButton)
verify(first.selected)
tryCompare(second, "selected", true)
tryCompare(third, "selected", true)
clickItem(selectAllButton)
tryCompare(first, "selected", false)
tryCompare(second, "selected", false)
tryCompare(third, "selected", false)
verify(!deleteButton.enabled)
clickItem(selectAllButton)
tryCompare(first, "selected", true)
tryCompare(second, "selected", true)
tryCompare(third, "selected", true)
verify(deleteButton.enabled)
clickItem(deleteButton)
tryCompare(HistoryModel, "count", 0)
}
function test_keyboard_navigation() {
var listview = findChild(historyView, "domainsListView")
verify(listview.activeFocus)
var domains = getListItems(listview, "historyViewDomainDelegate")
function check_current(index) {
compare(listview.currentIndex, index)
var current = root.Window.activeFocusItem
compare(current.objectName, "historyViewDomainDelegate")
compare(current.modelIndex, index)
compare(current.title, domains[index].title)
verify(current.activeFocus)
return current
}
var current = check_current(0)
keyClick(Qt.Key_Up)
verify(current.activeFocus)
keyClick(Qt.Key_Enter)
compare(seeMoreEntriesClickedSpy.count, 1)
compare(seeMoreEntriesClickedSpy.signalArguments[0][0].domain, domains[0].title)
keyClick(Qt.Key_Down)
current = check_current(1)
keyClick(Qt.Key_Return)
compare(seeMoreEntriesClickedSpy.count, 2)
compare(seeMoreEntriesClickedSpy.signalArguments[1][0].domain, domains[1].title)
keyClick(Qt.Key_Down)
current = check_current(2)
keyClick(Qt.Key_Down)
verify(current.activeFocus)
keyClick(Qt.Key_Delete)
tryCompare(listview, 'currentIndex', 1)
current = check_current(1)
}
}
}
./tests/unittests/qml/tst_Suggestions.qml 0000644 0000156 0000165 00000010542 12703462031 021011 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import QtQuick 2.4
import QtTest 1.0
import Ubuntu.Test 1.0
import "../../../src/app/webbrowser"
Item {
width: 300
height: 300
ListModel {
id: model1
readonly property bool displayUrl: true
readonly property url icon: ""
}
readonly property var model2: []
ListModel {
id: model3
readonly property bool displayUrl: true
readonly property url icon: ""
}
Suggestions {
id: suggestions
focus: true
anchors.fill: parent
models: [model1, model2, model3]
searchTerms: []
}
SignalSpy {
id: activatedSpy
target: suggestions
signalName: "activated"
}
UbuntuTestCase {
name: "Suggestions"
when: windowShown
function init() {
model1.append({"title": "lorem ipsum", "url": "http://model1/item1"})
model1.append({"title": "a+ rating", "url": "http://model1/item2"})
model2.icon = ""
model2.push({"title": "Tom & Jerry", "url": "http://model2/item1"})
model2.push({"title": "$1 in €", "url": "http://model2/item2"})
model2.push({"title": "foo | bar | baz", "url": "http://model2/item3"})
model3.append({"title": "(a+b)^2", "url": "http://model3/item1"})
model3.append({"title": "Çà et là", "url": "http://model3/item2"})
compare(suggestions.count, 7)
activatedSpy.clear()
}
function cleanup() {
activatedSpy.clear()
model3.clear()
model2.splice(0, 3)
model1.clear()
compare(suggestions.count, 0)
}
function test_highlighting_data() {
function highlight(term) {
return "%2".arg("#752571").arg(term)
}
return [
{terms: [], index: 0, title: "lorem ipsum"},
{terms: ["a+"], index: 1, title: "%1 rating".arg(highlight("a+"))},
{terms: ["a+"], index: 5, title: "(%1b)^2".arg(highlight("a+"))},
{terms: ["tom", "jerry"], index: 2, title: "%1 & %2".arg(highlight("Tom")).arg(highlight("Jerry"))},
{terms: ["$"], index: 3, title: "%991 in €".arg(highlight("$"))},
{terms: ["|"], index: 4, title: "foo %1 bar %1 baz".arg(highlight("|"))},
{terms: ["(", ")"], index: 5, title: "%1a+b%2^2".arg(highlight("(")).arg(highlight(")"))},
{terms: ["à", "ET"], index: 6, title: "Ç%1 %2 l%1".arg(highlight("à")).arg(highlight("et"))},
]
}
function test_highlighting(data) {
suggestions.searchTerms = data.terms
var delegate = findChild(suggestions, "suggestionDelegate_" + data.index)
compare(delegate.title, data.title)
}
function test_mouseActivation() {
var delegate = findChild(suggestions, "suggestionDelegate_4")
var center = centerOf(delegate)
mouseClick(delegate, center.x, center.y)
compare(activatedSpy.count, 1)
compare(activatedSpy.signalArguments[0][0], "http://model2/item3")
}
function test_keyboardActivation() {
var listview = findChild(suggestions, "suggestionsList")
compare(listview.currentIndex, 0)
keyClick(Qt.Key_Down)
keyClick(Qt.Key_Down)
compare(listview.currentIndex, 2)
keyClick(Qt.Key_Return)
compare(activatedSpy.count, 1)
compare(activatedSpy.signalArguments[0][0], "http://model2/item1")
}
}
}
./tests/unittests/webapp-container-hook/ 0000755 0000156 0000165 00000000000 12703462032 020474 5 ustar jenkins jenkins ./tests/unittests/webapp-container-hook/tst_WebappContainerHookTests.cpp 0000644 0000156 0000165 00000016500 12703462031 027020 0 ustar jenkins jenkins /*
* Copyright 2014 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
// local
#include "click-hooks/hook-utils.h"
namespace {
void createFileWithContent(const QString& filename, const QString& content)
{
QFile file(filename);
if (!file.open(QIODevice::ReadWrite))
{
return;
}
file.write(content.toUtf8());
file.flush();
}
QString readAll(const QString& filename)
{
QFile file(filename);
if (!file.open(QIODevice::ReadOnly))
{
return QString();
}
return file.readAll();
}
}
class WebappContainerHookTests : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testRemoveVersion_data()
{
QTest::addColumn("appId");
QTest::addColumn("appIdNoVersion");
QTest::newRow("With version") << QString("com.ubuntu.blabla_blabla_0.2") << QString("com.ubuntu.blabla_blabla");
QTest::newRow("With no version") << QString("com.ubuntu.blabla_blabla") << QString("com.ubuntu.blabla_blabla");
}
void testRemoveVersion()
{
QFETCH(QString, appId);
QFETCH(QString, appIdNoVersion);
QCOMPARE(HookUtils::removeVersionFrom(appId), appIdNoVersion);
}
void testClickHookUpdate_data()
{
QTest::addColumn("processedHookFilename");
QTest::addColumn("processedHookFileContent");
QTest::addColumn("installedHookFilename");
QTest::addColumn("installedHookFileContent");
QTest::addColumn("shouldBeUpdated");
QTest::newRow("Valid hook file") << QString("com.ubuntu.blabla_blabla")
<< QString("[{}]")
<< QString("com.ubuntu.blabla_blabla_0.2.webapp")
<< QString("[{\"uninstall\": { \"delete-cookies\": true, \"delete-cache\": true } }]")
<< true;
QTest::newRow("Valid hook file - no update") << QString("com.ubuntu.blabla_blabla")
<< QString("[{}]")
<< QString("com.ubuntu.blabla_blabla_0.2.webapp")
<< QString("[{\"uninstall\": { \"delete-cookies\": true, \"delete-cache\": true } }]")
<< false;
}
void testClickHookUpdate()
{
QFETCH(QString, processedHookFilename);
QFETCH(QString, installedHookFilename);
QFETCH(QString, processedHookFileContent);
QFETCH(QString, installedHookFileContent);
QFETCH(bool, shouldBeUpdated);
QTemporaryDir processedHookFiledTmpDir;
QTemporaryDir installedHookFiledTmpDir;
QVERIFY(!processedHookFilename.isEmpty());
QString processedFilepath =
processedHookFiledTmpDir.path() + "/" + processedHookFilename;
QVERIFY(!installedHookFilename.isEmpty());
QString installedHookFilepath =
installedHookFiledTmpDir.path() + "/" + installedHookFilename;
if (shouldBeUpdated)
{
createFileWithContent(processedFilepath, processedHookFileContent);
// Wait in order to have a meaningful lastModifiedData for comparaison
QTest::qSleep(1000);
createFileWithContent(installedHookFilepath, installedHookFileContent);
}
else
{
createFileWithContent(installedHookFilepath, installedHookFileContent);
// Wait in order to have a meaningful lastModifiedData for comparaison
QTest::qSleep(1000);
createFileWithContent(processedFilepath, processedHookFileContent);
}
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("WEBAPPCONTAINER_PROCESSED_HOOKS_FOLDER", processedHookFiledTmpDir.path());
env.insert("WEBAPPCONTAINER_INSTALLED_HOOKS_FOLDER", installedHookFiledTmpDir.path());
QProcess process;
process.setProcessEnvironment(env);
process.start(CLICK_HOOK_EXEC_BIN, QStringList());
process.waitForFinished();
if (shouldBeUpdated)
{
QCOMPARE(readAll(processedFilepath), readAll(installedHookFilepath));
}
else
{
QCOMPARE(readAll(processedFilepath), processedHookFileContent);
}
}
void testClickHookInstallProcessing()
{
QString processedHookFilename =
"com.ubuntu.blabla_blabla";
QString installedHookFilename =
"com.ubuntu.blabla_blabla_0.2.webapp";
QString installedHookFileContent =
"[{\"uninstall\": { \"delete-cookies\": true, \"delete-cache\": true } }]";
QTemporaryDir processedHookFiledTmpDir;
QTemporaryDir installedHookFiledTmpDir;
QString processedFilepath =
processedHookFiledTmpDir.path() + "/" + processedHookFilename;
QString installedHookFilepath =
installedHookFiledTmpDir.path() + "/" + installedHookFilename;
createFileWithContent(installedHookFilepath, installedHookFileContent);
QVERIFY(!QFile::exists(processedFilepath));
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("WEBAPPCONTAINER_PROCESSED_HOOKS_FOLDER", processedHookFiledTmpDir.path());
env.insert("WEBAPPCONTAINER_INSTALLED_HOOKS_FOLDER", installedHookFiledTmpDir.path());
QProcess process;
process.setProcessEnvironment(env);
process.start(CLICK_HOOK_EXEC_BIN, QStringList());
process.waitForFinished();
QVERIFY(QFile::exists(processedFilepath));
QCOMPARE(readAll(processedFilepath), readAll(installedHookFilepath));
}
void testClickHookUninstall()
{
QString processedHookFilename =
"com.ubuntu.blabla_blabla";
QString processedHookFileContent =
"[{\"uninstall\": { \"delete-cookies\": false, \"delete-cache\": false } }]";
QTemporaryDir processedHookFiledTmpDir;
QTemporaryDir installedHookFiledTmpDir;
QString processedFilepath =
processedHookFiledTmpDir.path() + "/" + processedHookFilename;
createFileWithContent(processedFilepath, processedHookFileContent);
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.insert("WEBAPPCONTAINER_PROCESSED_HOOKS_FOLDER", processedHookFiledTmpDir.path());
env.insert("WEBAPPCONTAINER_INSTALLED_HOOKS_FOLDER", installedHookFiledTmpDir.path());
QProcess process;
process.setProcessEnvironment(env);
process.start(CLICK_HOOK_EXEC_BIN, QStringList());
process.waitForFinished();
QVERIFY(!QFile::exists(processedFilepath));
}
};
QTEST_MAIN(WebappContainerHookTests)
#include "tst_WebappContainerHookTests.moc"
./tests/unittests/webapp-container-hook/CMakeLists.txt 0000644 0000156 0000165 00000001065 12703462031 023235 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_WebappContainerHookTests)
set(SOURCES
${CMAKE_SOURCE_DIR}/click-hooks/hook-utils.cpp
tst_WebappContainerHookTests.cpp
)
set(WEBAPP_CONTAINER_HOOK webapp-container-hook)
include_directories(${CMAKE_SOURCE_DIR})
add_definitions(-DCLICK_HOOK_EXEC_BIN="${CMAKE_BINARY_DIR}/click-hooks/${WEBAPP_CONTAINER_HOOK}")
add_executable(${TEST} ${SOURCES})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Test
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/search-engine/ 0000755 0000156 0000165 00000000000 12703462032 017010 5 ustar jenkins jenkins ./tests/unittests/search-engine/tst_SearchEngineTests.cpp 0000644 0000156 0000165 00000022222 12703462031 023763 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
#include
#include
#include
// local
#include "searchengine.h"
class SearchEngineTests : public QObject
{
Q_OBJECT
private:
QTemporaryDir* dir1;
QTemporaryDir* dir2;
SearchEngine* engine;
QSignalSpy* searchPathsSpy;
QSignalSpy* filenameSpy;
QSignalSpy* nameSpy;
QSignalSpy* descriptionSpy;
QSignalSpy* urlTemplateSpy;
QSignalSpy* suggestionsUrlTemplateSpy;
QSignalSpy* validSpy;
private Q_SLOTS:
void init()
{
dir1 = new QTemporaryDir;
QVERIFY(dir1->isValid());
dir2 = new QTemporaryDir;
QVERIFY(dir2->isValid());
engine = new SearchEngine;
searchPathsSpy = new QSignalSpy(engine, SIGNAL(searchPathsChanged()));
filenameSpy = new QSignalSpy(engine, SIGNAL(filenameChanged()));
nameSpy = new QSignalSpy(engine, SIGNAL(nameChanged()));
descriptionSpy = new QSignalSpy(engine, SIGNAL(descriptionChanged()));
urlTemplateSpy = new QSignalSpy(engine, SIGNAL(urlTemplateChanged()));
suggestionsUrlTemplateSpy = new QSignalSpy(engine, SIGNAL(suggestionsUrlTemplateChanged()));
validSpy = new QSignalSpy(engine, SIGNAL(validChanged()));
QFile file(QDir(dir1->path()).absoluteFilePath("engine1.xml"));
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
QTextStream out(&file);
out << "";
out << "engine1";
out << "engine1 search";
out << "";
out << "";
out << "";
file.close();
file.setFileName(QDir(dir2->path()).absoluteFilePath("engine2.xml"));
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
out.setDevice(&file);
out << "";
out << "engine2";
out << "";
out << "";
file.close();
file.setFileName(QDir(dir2->path()).absoluteFilePath("invalid.xml"));
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
out.setDevice(&file);
out << "invalid";
file.close();
engine->setSearchPaths({dir1->path(), dir2->path()});
QCOMPARE(searchPathsSpy->count(), 1);
searchPathsSpy->clear();
nameSpy->clear();
descriptionSpy->clear();
urlTemplateSpy->clear();
suggestionsUrlTemplateSpy->clear();
validSpy->clear();
QVERIFY(!engine->isValid());
}
void cleanup()
{
delete validSpy;
delete suggestionsUrlTemplateSpy;
delete urlTemplateSpy;
delete descriptionSpy;
delete nameSpy;
delete filenameSpy;
delete searchPathsSpy;
delete engine;
delete dir1;
delete dir2;
}
void shouldChangeSearchPaths()
{
QCOMPARE(engine->searchPaths(), QStringList({dir1->path(), dir2->path()}));
engine->setSearchPaths({dir2->path()});
QCOMPARE(searchPathsSpy->count(), 1);
QCOMPARE(engine->searchPaths(), QStringList({dir2->path()}));
}
void shouldChangeFilename()
{
QVERIFY(engine->filename().isEmpty());
engine->setFilename("engine1");
QCOMPARE(filenameSpy->count(), 1);
QCOMPARE(engine->filename(), QString("engine1"));
engine->setFilename("");
QCOMPARE(filenameSpy->count(), 2);
QVERIFY(engine->filename().isEmpty());
}
void shouldParseValidDescriptionWithDescription()
{
engine->setFilename("engine1");
QCOMPARE(nameSpy->count(), 1);
QCOMPARE(engine->name(), QString("engine1"));
QCOMPARE(descriptionSpy->count(), 1);
QCOMPARE(engine->description(), QString("engine1 search"));
QCOMPARE(urlTemplateSpy->count(), 1);
QCOMPARE(engine->urlTemplate(), QString("https://example.org/search1?q={searchTerms}"));
QCOMPARE(suggestionsUrlTemplateSpy->count(), 1);
QCOMPARE(engine->suggestionsUrlTemplate(), QString("https://example.org/suggest1?q={searchTerms}"));
QCOMPARE(validSpy->count(), 1);
QVERIFY(engine->isValid());
}
void shouldParseValidDescriptionWithoutDescriptionAndSuggestionsTemplate()
{
engine->setFilename("engine2");
QCOMPARE(nameSpy->count(), 1);
QCOMPARE(engine->name(), QString("engine2"));
QVERIFY(descriptionSpy->isEmpty());
QVERIFY(engine->description().isEmpty());
QCOMPARE(urlTemplateSpy->count(), 1);
QCOMPARE(engine->urlTemplate(), QString("https://example.org/search2?q={searchTerms}"));
QVERIFY(suggestionsUrlTemplateSpy->isEmpty());
QVERIFY(engine->suggestionsUrlTemplate().isEmpty());
QCOMPARE(validSpy->count(), 1);
QVERIFY(engine->isValid());
}
void shouldFailToParseInvalidDescription()
{
engine->setFilename("invalid");
QVERIFY(nameSpy->isEmpty());
QVERIFY(engine->name().isEmpty());
QVERIFY(descriptionSpy->isEmpty());
QVERIFY(engine->description().isEmpty());
QVERIFY(urlTemplateSpy->isEmpty());
QVERIFY(engine->urlTemplate().isEmpty());
QVERIFY(suggestionsUrlTemplateSpy->isEmpty());
QVERIFY(engine->suggestionsUrlTemplate().isEmpty());
QVERIFY(validSpy->isEmpty());
QVERIFY(!engine->isValid());
}
void shouldFailToLocateNonexistentDescription()
{
engine->setFilename("nonexistent");
QVERIFY(nameSpy->isEmpty());
QVERIFY(engine->name().isEmpty());
QVERIFY(descriptionSpy->isEmpty());
QVERIFY(engine->description().isEmpty());
QVERIFY(urlTemplateSpy->isEmpty());
QVERIFY(engine->urlTemplate().isEmpty());
QVERIFY(suggestionsUrlTemplateSpy->isEmpty());
QVERIFY(engine->suggestionsUrlTemplate().isEmpty());
QVERIFY(validSpy->isEmpty());
QVERIFY(!engine->isValid());
}
void shouldOverrideExistingDescription()
{
QFile file(QDir(dir1->path()).absoluteFilePath("engine2.xml"));
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
QTextStream out(&file);
out << "";
out << "engine2-overridden";
out << "engine2 overridden search";
out << "";
out << "";
out << "";
file.close();
engine->setFilename("engine2");
QCOMPARE(nameSpy->count(), 1);
QCOMPARE(engine->name(), QString("engine2-overridden"));
QCOMPARE(descriptionSpy->count(), 1);
QCOMPARE(engine->description(), QString("engine2 overridden search"));
QCOMPARE(urlTemplateSpy->count(), 1);
QCOMPARE(engine->urlTemplate(), QString("https://example.org/search2overridden?q={searchTerms}"));
QCOMPARE(suggestionsUrlTemplateSpy->count(), 1);
QCOMPARE(engine->suggestionsUrlTemplate(), QString("https://example.org/suggest2?q={searchTerms}"));
QCOMPARE(validSpy->count(), 1);
QVERIFY(engine->isValid());
}
void shouldOverrideAndInvalidateDescription()
{
QFile file(QDir(dir1->path()).absoluteFilePath("engine2.xml"));
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
file.close();
engine->setFilename("engine2");
QVERIFY(nameSpy->isEmpty());
QVERIFY(engine->name().isEmpty());
QVERIFY(descriptionSpy->isEmpty());
QVERIFY(engine->description().isEmpty());
QVERIFY(urlTemplateSpy->isEmpty());
QVERIFY(engine->urlTemplate().isEmpty());
QVERIFY(suggestionsUrlTemplateSpy->isEmpty());
QVERIFY(engine->suggestionsUrlTemplate().isEmpty());
QVERIFY(validSpy->isEmpty());
QVERIFY(!engine->isValid());
}
};
QTEST_MAIN(SearchEngineTests)
#include "tst_SearchEngineTests.moc"
./tests/unittests/search-engine/CMakeLists.txt 0000644 0000156 0000165 00000000634 12703462031 021552 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_SearchEngineTests)
set(SOURCES
${webbrowser-app_SOURCE_DIR}/searchengine.cpp
tst_SearchEngineTests.cpp
)
add_executable(${TEST} ${SOURCES})
include_directories(${webbrowser-app_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Test
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/oxide-cookie-helper/ 0000755 0000156 0000165 00000000000 12703462032 020134 5 ustar jenkins jenkins ./tests/unittests/oxide-cookie-helper/tst_OxideCookieHelper.cpp 0000644 0000156 0000165 00000035151 12703462031 025100 0 ustar jenkins jenkins /*
* Copyright 2014 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
#include
#include
#include
#include
// local
#include "oxide-cookie-helper.h"
typedef QList Cookies;
/* Fake Oxide backend implementation */
class CookieBackend : public QObject
{
Q_OBJECT
public:
CookieBackend(QObject *parent = 0):
QObject(parent),
m_lastRequestId(0)
{}
void sendReply(int requestId, const QList& failedCookies) {
QVariant failedCookiesVariant =
OxideCookieHelper::variantFromCookies(failedCookies);
Q_EMIT setCookiesResponse(requestId, failedCookiesVariant);
}
void setFailUrls(const QSet& urls) { m_failUrls = urls; }
public Q_SLOTS:
int setNetworkCookies(const QUrl& url, const QList& cookies) {
if (m_failUrls.contains(url)) return -1;
m_lastRequestId++;
/* Handle host cookies */
QList restoredCookies;
Q_FOREACH(const QNetworkCookie& cookie, cookies) {
QNetworkCookie c(cookie);
if (c.domain().isEmpty()) {
c.setDomain(url.host());
}
restoredCookies.append(c);
}
Q_EMIT setNetworkCookiesCalled(m_lastRequestId, url, restoredCookies);
return m_lastRequestId;
}
void onTimerTimeout() {
QObject *timer = sender();
int requestId = timer->property("requestId").toInt();
Cookies failedCookies = timer->property("failedCookies").value();
sendReply(requestId, failedCookies);
}
Q_SIGNALS:
void setCookiesResponse(int requestId, const QVariant& failedCookies);
void setNetworkCookiesCalled(int requestId, const QUrl& url,
const QList& cookies);
private:
int m_lastRequestId;
QSet m_failUrls;
};
class OxideCookieHelperTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testSetCookiesSanity();
void testSetCookies_data();
void testSetCookies();
void testSetCookiesSubDomain();
void testImmediateFailure_data();
void testImmediateFailure();
};
void OxideCookieHelperTest::testSetCookiesSanity()
{
OxideCookieHelper helper;
QSignalSpy cookiesSet(&helper,
SIGNAL(cookiesSet(const QList&)));
QVERIFY(helper.oxideStoreBackend() == 0);
Cookies cookies = QNetworkCookie::parseCookies("a=2\nb=3");
QCOMPARE(cookies.count(), 2);
helper.setCookies(cookies);
QTest::qWait(10);
QCOMPARE(cookiesSet.count(), 0);
CookieBackend backend;
helper.setOxideStoreBackend(&backend);
QCOMPARE(helper.oxideStoreBackend(), &backend);
QCOMPARE(helper.property("oxideStoreBackend").value(),
&backend);
}
void OxideCookieHelperTest::testSetCookies_data()
{
QTest::addColumn("domain1");
QTest::addColumn("cookies1");
QTest::addColumn("timeout1");
QTest::addColumn("failedcookies1");
QTest::addColumn("domain2");
QTest::addColumn("cookies2");
QTest::addColumn("timeout2");
QTest::addColumn("failedcookies2");
QTest::addColumn("domain3");
QTest::addColumn("cookies3");
QTest::addColumn("timeout3");
QTest::addColumn("failedcookies3");
QTest::newRow("empty") <<
QString() << QString() << 0 << QString() <<
QString() << QString() << 0 << QString() <<
QString() << QString() << 0 << QString();
QTest::newRow("one domain, success") <<
"example.org" <<
"a=0; Domain=example.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT\n"
"b=2; Domain=example.org; HttpOnly\n"
"c=something; Domain=example.org; Secure" << 10 << QString() <<
QString() << QString() << 0 << QString() <<
QString() << QString() << 0 << QString();
QTest::newRow("one domain, with one failure") <<
"example.org" <<
"a=0; Domain=example.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT\n"
"b=2; Domain=example.org; HttpOnly\n"
"c=something; Domain=example.org; Secure" << 10 <<
"b=2; Domain=example.org; HttpOnly" <<
QString() << QString() << 0 << QString() <<
QString() << QString() << 0 << QString();
QTest::newRow("three domains, success") <<
"example.org" <<
"a=0; Domain=example.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT\n"
"m=2; Domain=example.org; HttpOnly\n"
"z=something; Domain=example.org; Secure" << 10 << QString() <<
"domain.net" <<
"c=4; Domain=domain.net; HttpOnly\n"
"r=no; Domain=domain.net; Expires=Wed, 13 Jan 2021 22:23:01 GMT" << 20 << QString() <<
"sub.site.org" <<
"d=yes; Domain=sub.site.org; Secure\n"
"e=3; Domain=sub.site.org; HttpOnly\n"
"z=last; Domain=sub.site.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT" << 40 << QString();
QTest::newRow("three domains, some failures") <<
"example.org" <<
"a=0; Domain=example.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT\n"
"m=2; Domain=example.org; HttpOnly\n"
"z=something; Domain=example.org; Secure" << 10 << QString() <<
"domain.net" <<
"c=4; Domain=domain.net; HttpOnly\n"
"r=no; Domain=domain.net; Expires=Wed, 13 Jan 2021 22:23:01 GMT" << 40 <<
"c=4; Domain=domain.net; HttpOnly" <<
"sub.site.org" <<
"d=yes; Domain=sub.site.org; Secure\n"
"e=3; Domain=sub.site.org; HttpOnly\n"
"z=last; Domain=sub.site.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT" << 20 <<
"e=3; Domain=sub.site.org; HttpOnly\n"
"z=last; Domain=sub.site.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT";
}
struct DomainData {
DomainData(): timeout(0) {}
DomainData(const QString& rawCookies, int t, const QString& rawFailedCookies):
cookies(QNetworkCookie::parseCookies(rawCookies.toUtf8())),
timeout(t),
failedCookies(QNetworkCookie::parseCookies(rawFailedCookies.toUtf8()))
{}
Cookies cookies;
int timeout;
Cookies failedCookies;
};
static bool cookieCompare(const QNetworkCookie a, const QNetworkCookie b)
{
return a.name() < b.name();
}
void OxideCookieHelperTest::testSetCookies()
{
QFETCH(QString, domain1);
QFETCH(QString, cookies1);
QFETCH(int, timeout1);
QFETCH(QString, failedcookies1);
QFETCH(QString, domain2);
QFETCH(QString, cookies2);
QFETCH(int, timeout2);
QFETCH(QString, failedcookies2);
QFETCH(QString, domain3);
QFETCH(QString, cookies3);
QFETCH(int, timeout3);
QFETCH(QString, failedcookies3);
/* Build a data structure easier to handle */
QMap domains;
if (!domain1.isEmpty()) domains[domain1] = DomainData(cookies1, timeout1, failedcookies1);
if (!domain2.isEmpty()) domains[domain2] = DomainData(cookies2, timeout2, failedcookies2);
if (!domain3.isEmpty()) domains[domain3] = DomainData(cookies3, timeout3, failedcookies3);
OxideCookieHelper helper;
QSignalSpy cookiesSet(&helper,
SIGNAL(cookiesSet(const QList&)));
CookieBackend backend;
QSignalSpy setNetworkCookiesCalled(&backend,
SIGNAL(setNetworkCookiesCalled(int,const QUrl&,const QList&)));
helper.setOxideStoreBackend(&backend);
/* Build the list of cookies */
Cookies cookies;
Cookies expectedFailedCookies;
Q_FOREACH(const DomainData &domainData, domains) {
cookies.append(domainData.cookies);
expectedFailedCookies.append(domainData.failedCookies);
}
qSort(cookies.begin(), cookies.end(), cookieCompare);
helper.setCookies(cookies);
QCOMPARE(setNetworkCookiesCalled.count(), domains.count());
QList setNetworkCookiesCalls = setNetworkCookiesCalled;
Q_FOREACH(const QVariantList &args, setNetworkCookiesCalls) {
int requestId = args.at(0).toInt();
QUrl url = args.at(1).toUrl();
Cookies domainCookies = args.at(2).value();
QVERIFY(domains.contains(url.host()));
/* Compare the cookies lists; we don't care about the order, so let's
* sort them before comparing */
const DomainData &domainData = domains[url.host()];
qSort(domainCookies.begin(), domainCookies.end(), cookieCompare);
Cookies expectedCookies = domainData.cookies;
qSort(expectedCookies.begin(), expectedCookies.end(), cookieCompare);
QCOMPARE(domainCookies, expectedCookies);
QTimer* timer = new QTimer(&backend);
timer->setSingleShot(true);
timer->setInterval(domainData.timeout);
timer->setProperty("requestId", requestId);
timer->setProperty("failedCookies", QVariant::fromValue(domainData.failedCookies));
QObject::connect(timer, SIGNAL(timeout()),
&backend, SLOT(onTimerTimeout()));
timer->start();
}
QVERIFY(cookiesSet.wait());
QCOMPARE(cookiesSet.count(), 1);
/* Compare the failed cookies */
qSort(expectedFailedCookies.begin(), expectedFailedCookies.end(), cookieCompare);
Cookies failedCookies = cookiesSet.at(0).at(0).value();
qSort(failedCookies.begin(), failedCookies.end(), cookieCompare);
QCOMPARE(failedCookies, expectedFailedCookies);
}
void OxideCookieHelperTest::testSetCookiesSubDomain()
{
OxideCookieHelper helper;
QSignalSpy cookiesSet(&helper,
SIGNAL(cookiesSet(const QList&)));
Cookies cookies = QNetworkCookie::parseCookies("a=2; Domain=.example.org");
QCOMPARE(cookies.count(), 1);
CookieBackend backend;
QSignalSpy setNetworkCookiesCalled(&backend,
SIGNAL(setNetworkCookiesCalled(int,const QUrl&,const QList&)));
helper.setOxideStoreBackend(&backend);
helper.setCookies(cookies);
QCOMPARE(setNetworkCookiesCalled.count(), 1);
QUrl url = setNetworkCookiesCalled.at(0).at(1).toUrl();
QCOMPARE(url.host(), QString("example.org"));
}
void OxideCookieHelperTest::testImmediateFailure_data()
{
QTest::addColumn("rawCookies");
QTest::addColumn >("failUrls");
QTest::addColumn("rawFailedcookies");
QSet failUrls;
failUrls.insert(QUrl("http://example.org"));
QTest::newRow("one domain, one failure") <<
"a=0; Domain=example.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT\n"
"b=2; Domain=example.org; HttpOnly" <<
failUrls <<
"a=0; Domain=example.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT\n"
"b=2; Domain=example.org; HttpOnly";
failUrls.clear();
failUrls.insert(QUrl("http://domain.net"));
QTest::newRow("multiple domains, one failure") <<
"a=0; Domain=example.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT\n"
"b=2; Domain=example.org; HttpOnly\n"
"c=4; Domain=domain.net; HttpOnly\n"
"d=yes; Domain=sub.site.org; Secure\n"
"e=3; Domain=sub.site.org; HttpOnly\n"
"z=last; Domain=sub.site.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT" <<
failUrls <<
"c=4; Domain=domain.net; HttpOnly";
failUrls.clear();
failUrls.insert(QUrl("http://example.org"));
failUrls.insert(QUrl("http://domain.net"));
failUrls.insert(QUrl("http://sub.site.org"));
QTest::newRow("multiple domains, all failures") <<
"a=0; Domain=example.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT\n"
"b=2; Domain=example.org; HttpOnly\n"
"c=4; Domain=domain.net; HttpOnly\n"
"d=yes; Domain=sub.site.org; Secure\n"
"e=3; Domain=sub.site.org; HttpOnly\n"
"z=last; Domain=sub.site.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT" <<
failUrls <<
"a=0; Domain=example.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT\n"
"b=2; Domain=example.org; HttpOnly\n"
"c=4; Domain=domain.net; HttpOnly\n"
"d=yes; Domain=sub.site.org; Secure\n"
"e=3; Domain=sub.site.org; HttpOnly\n"
"z=last; Domain=sub.site.org; Expires=Wed, 13 Jan 2021 22:23:01 GMT";
failUrls.clear();
}
void OxideCookieHelperTest::testImmediateFailure()
{
QFETCH(QString, rawCookies);
QFETCH(QSet, failUrls);
QFETCH(QString, rawFailedcookies);
OxideCookieHelper helper;
QSignalSpy cookiesSet(&helper,
SIGNAL(cookiesSet(const QList&)));
CookieBackend backend;
QSignalSpy setNetworkCookiesCalled(&backend,
SIGNAL(setNetworkCookiesCalled(int,const QUrl&,const QList&)));
helper.setOxideStoreBackend(&backend);
backend.setFailUrls(failUrls);
/* Build the list of cookies */
Cookies cookies = QNetworkCookie::parseCookies(rawCookies.toUtf8());
Cookies expectedFailedCookies =
QNetworkCookie::parseCookies(rawFailedcookies.toUtf8());
helper.setCookies(cookies);
/* If there were valid calls, reply to them */
QList setNetworkCookiesCalls = setNetworkCookiesCalled;
Q_FOREACH(const QVariantList &args, setNetworkCookiesCalls) {
int requestId = args.at(0).toInt();
QUrl url = args.at(1).toUrl();
Cookies domainCookies = args.at(2).value();
QTimer* timer = new QTimer(&backend);
timer->setSingleShot(true);
timer->setInterval(5);
timer->setProperty("requestId", requestId);
QObject::connect(timer, SIGNAL(timeout()),
&backend, SLOT(onTimerTimeout()));
timer->start();
}
QVERIFY(cookiesSet.wait());
QCOMPARE(cookiesSet.count(), 1);
/* Compare the failed cookies */
qSort(expectedFailedCookies.begin(), expectedFailedCookies.end(), cookieCompare);
Cookies failedCookies = cookiesSet.at(0).at(0).value();
qSort(failedCookies.begin(), failedCookies.end(), cookieCompare);
QCOMPARE(failedCookies, expectedFailedCookies);
}
QTEST_MAIN(OxideCookieHelperTest)
#include "tst_OxideCookieHelper.moc"
./tests/unittests/oxide-cookie-helper/CMakeLists.txt 0000644 0000156 0000165 00000000737 12703462031 022702 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_OxideCookieHelperTests)
set(SOURCES
${webapp-container_SOURCE_DIR}/oxide-cookie-helper.cpp
tst_OxideCookieHelper.cpp
)
add_executable(${TEST} ${SOURCES})
include_directories(${webapp-container_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Network
Qt5::Test
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/single-instance-manager/ 0000755 0000156 0000165 00000000000 12703462032 020773 5 ustar jenkins jenkins ./tests/unittests/single-instance-manager/tst_SingleInstanceManagerTests.cpp 0000644 0000156 0000165 00000004041 12703462031 027613 0 ustar jenkins jenkins /*
* Copyright 2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
#include
// local
#include "single-instance-manager.h"
class SingleInstanceManagerTests : public QObject
{
Q_OBJECT
private:
SingleInstanceManager* singleton;
QSignalSpy* newInstanceSpy;
private Q_SLOTS:
void init()
{
QStandardPaths::setTestModeEnabled(true);
singleton = new SingleInstanceManager(this);
newInstanceSpy = new QSignalSpy(singleton, SIGNAL(newInstanceLaunched(const QStringList&)));
}
void cleanup()
{
delete newInstanceSpy;
delete singleton;
QStandardPaths::setTestModeEnabled(false);
}
void test_cannot_run_twice_same_instance()
{
QVERIFY(singleton->run(QStringList()));
QVERIFY(!singleton->run(QStringList()));
QVERIFY(newInstanceSpy->isEmpty());
}
void test_arguments_passed_to_already_running_instance()
{
QVERIFY(singleton->run(QStringList()));
SingleInstanceManager other;
QStringList args;
args << QStringLiteral("foo") << QStringLiteral("bar") << QStringLiteral("baz");
QVERIFY(!other.run(args));
newInstanceSpy->wait();
QCOMPARE(newInstanceSpy->first().at(0).toStringList(), args);
}
};
QTEST_MAIN(SingleInstanceManagerTests)
#include "tst_SingleInstanceManagerTests.moc"
./tests/unittests/single-instance-manager/CMakeLists.txt 0000644 0000156 0000165 00000001101 12703462031 023523 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_SingleInstanceManagerTests)
set(SOURCES
${webbrowser-common_SOURCE_DIR}/single-instance-manager.cpp
tst_SingleInstanceManagerTests.cpp
)
add_executable(${TEST} ${SOURCES})
include_directories(${webbrowser-common_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Network
Qt5::Test
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
set_tests_properties(${TEST} PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
./tests/unittests/sanity/ 0000755 0000156 0000165 00000000000 12703462031 015606 5 ustar jenkins jenkins ./tests/unittests/sanity/CMakeLists.txt 0000644 0000156 0000165 00000000101 12703462031 020336 0 ustar jenkins jenkins add_test(flake8 python3 -m flake8 ${autopilot-tests_SOURCE_DIR})
./tests/unittests/text-search-filter-model/ 0000755 0000156 0000165 00000000000 12703462032 021110 5 ustar jenkins jenkins ./tests/unittests/text-search-filter-model/tst_TextSearchFilterModelTests.cpp 0000644 0000156 0000165 00000015231 12703462031 027733 0 ustar jenkins jenkins /*
* Copyright 2013-2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
// local
#include "history-model.h"
#include "text-search-filter-model.h"
class TextSearchFilterModelTests : public QObject
{
Q_OBJECT
private:
HistoryModel* model;
TextSearchFilterModel* matches;
private Q_SLOTS:
void init()
{
model = new HistoryModel;
model->setDatabasePath(":memory:");
matches = new TextSearchFilterModel;
matches->setSourceModel(QVariant::fromValue(model));
}
void cleanup()
{
delete matches;
delete model;
}
void shouldBeInitiallyEmpty()
{
QCOMPARE(matches->rowCount(), 0);
}
void shouldNotifyWhenChangingSourceModel()
{
QSignalSpy spy(matches, SIGNAL(sourceModelChanged()));
matches->setSourceModel(QVariant::fromValue(model));
QVERIFY(spy.isEmpty());
HistoryModel* model2 = new HistoryModel;
matches->setSourceModel(QVariant::fromValue(model2));
QCOMPARE(spy.count(), 1);
QCOMPARE(matches->sourceModel(), QVariant::fromValue(model2));
matches->setSourceModel(QVariant());
QCOMPARE(spy.count(), 2);
QVERIFY(matches->sourceModel().isNull());
delete model2;
}
void shouldReturnAllWhileTermsAndOrFieldsEmpty()
{
model->add(QUrl("http://example.org"), "Example Domain", QUrl());
model->add(QUrl("http://example.com"), "Example Domain", QUrl());
QCOMPARE(matches->rowCount(), 2);
matches->setTerms(QStringList({"org"}));
QCOMPARE(matches->rowCount(), 2);
matches->setSearchFields(QStringList({"url"}));
QCOMPARE(matches->rowCount(), 1);
matches->setTerms(QStringList());
QCOMPARE(matches->rowCount(), 2);
}
void shouldRecordTerms()
{
QVERIFY(matches->terms().isEmpty());
QStringList terms;
terms.append("example");
matches->setTerms(terms);
QCOMPARE(matches->terms(), terms);
}
void shouldRecordSearchFields()
{
QVERIFY(matches->searchFields().isEmpty());
QStringList fields;
fields.append("url");
matches->setSearchFields(fields);
QCOMPARE(matches->searchFields(), fields);
}
void shouldMatch()
{
model->add(QUrl("http://example.org"), "Example Domain", QUrl());
model->add(QUrl("http://example.com"), "Example Domain", QUrl());
matches->setTerms(QStringList({"example"}));
matches->setSearchFields(QStringList({"url", "title"}));
QCOMPARE(matches->rowCount(), 2);
}
void shouldMatchSecondField()
{
model->add(QUrl("http://example.org"), "Example Domain", QUrl());
model->add(QUrl("http://example.com"), "Example Domain", QUrl());
matches->setTerms(QStringList({"domain"}));
matches->setSearchFields(QStringList({"url", "title"}));
QCOMPARE(matches->rowCount(), 2);
}
void shouldFilterOutNotMatchingEntries()
{
model->add(QUrl("http://example.org"), "Example Domain", QUrl());
model->add(QUrl("http://ubuntu.com"), "Home | Ubuntu", QUrl());
model->add(QUrl("http://example.com"), "Example Domain", QUrl());
model->add(QUrl("http://wikipedia.org"), "Wikipedia", QUrl());
matches->setTerms(QStringList({"domain"}));
matches->setSearchFields(QStringList({"url", "title"}));
QCOMPARE(matches->rowCount(), 2);
}
void shouldUpdateResultsWhenTermsChange()
{
model->add(QUrl("http://example.org"), "Example Domain", QUrl());
model->add(QUrl("http://ubuntu.com"), "Home | Ubuntu", QUrl());
model->add(QUrl("http://wikipedia.org"), "Wikipedia", QUrl());
model->add(QUrl("http://ubuntu.com/download"), "Download Ubuntu | Ubuntu", QUrl());
matches->setSearchFields(QStringList({"url", "title"}));
matches->setTerms(QStringList({"ubuntu"}));
QCOMPARE(matches->rowCount(), 2);
matches->setTerms(QStringList({"wiki"}));
QCOMPARE(matches->rowCount(), 1);
}
void shouldUpdateResultsWhenSourceModelUpdates()
{
model->add(QUrl("http://example.org"), "Example Domain", QUrl());
model->add(QUrl("http://wikipedia.org"), "Wikipedia", QUrl());
matches->setTerms(QStringList({"ubuntu"}));
matches->setSearchFields(QStringList({"url"}));
QCOMPARE(matches->rowCount(), 0);
model->add(QUrl("http://ubuntu.com"), "Home | Ubuntu", QUrl());
QCOMPARE(matches->rowCount(), 1);
}
void shouldMatchAllTerms()
{
model->add(QUrl("http://example.org"), "Example Domain", QUrl());
model->add(QUrl("http://example.com"), "Example Domain", QUrl());
matches->setTerms(QStringList({"example", "org"}));
matches->setSearchFields(QStringList({"url", "title"}));
QCOMPARE(matches->rowCount(), 1);
}
void shouldMatchTermsInDifferentFields()
{
model->add(QUrl("http://example.org"), "Example Domain", QUrl());
model->add(QUrl("http://example.com"), "Example Domain", QUrl());
matches->setTerms(QStringList({"org", "domain"}));
matches->setSearchFields(QStringList({"url", "title"}));
QCOMPARE(matches->rowCount(), 1);
}
void shouldMatchDuplicateTerms()
{
model->add(QUrl("http://example.org"), "Example Domain", QUrl());
model->add(QUrl("http://example.com"), "Example Domain", QUrl());
matches->setTerms(QStringList({"org", "org", "org", "org"}));
matches->setSearchFields(QStringList({"url", "title"}));
QCOMPARE(matches->rowCount(), 1);
}
void shouldWarnOnInvalidFields()
{
QTest::ignoreMessage(QtWarningMsg, "Source model does not have role matching field: \"foo\"");
model->add(QUrl("http://example.org"), "Example Domain", QUrl());
matches->setTerms(QStringList({"org"}));
matches->setSearchFields(QStringList({"url", "foo"}));
QCOMPARE(matches->count(), 1);
}
};
QTEST_MAIN(TextSearchFilterModelTests)
#include "tst_TextSearchFilterModelTests.moc"
./tests/unittests/text-search-filter-model/CMakeLists.txt 0000644 0000156 0000165 00000000644 12703462031 023653 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Sql REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_TextSearchFilterModelTests)
add_executable(${TEST} tst_TextSearchFilterModelTests.cpp)
include_directories(${webbrowser-app_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Sql
Qt5::Test
webbrowser-app-models
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/intent-filter/ 0000755 0000156 0000165 00000000000 12703462032 017064 5 ustar jenkins jenkins ./tests/unittests/intent-filter/tst_IntentFilterTests.cpp 0000644 0000156 0000165 00000015762 12703462031 024126 0 ustar jenkins jenkins /*
* Copyright 2014 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
// local
#include "scheme-filter.h"
#include "intent-parser.h"
class IntentFilterTests : public QObject
{
Q_OBJECT
private Q_SLOTS:
void parseIntentUris_data()
{
QTest::addColumn("intentUris");
QTest::addColumn("scheme");
QTest::addColumn("package");
QTest::addColumn("uri");
QTest::addColumn("host");
QTest::addColumn("action");
QTest::addColumn("component");
QTest::addColumn("category");
QTest::addColumn("isValid");
QTest::newRow("Valid intent - host only")
<< "intent://scan/#Intent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end"
<< "zxing"
<< "com.google.zxing.client.android"
<< "/"
<< "scan"
<< "com"
<< "com"
<< "BROWSABLE"
<< true;
QTest::newRow("Valid intent - no host w/ uri")
<< "intent://scan/?a=1/#Intent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end"
<< "zxing"
<< "com.google.zxing.client.android"
<< "?a=1"
<< "scan"
<< "com"
<< "com"
<< "BROWSABLE"
<< true;
QTest::newRow("Valid intent - w/ host")
<< "intent://host/my/long/path?a=1/#Intent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end"
<< "zxing"
<< "com.google.zxing.client.android"
<< "my/long/path?a=1"
<< "host"
<< "com"
<< "com"
<< "BROWSABLE"
<< true;
QTest::newRow("Valid intent - w/o host & uri-path")
<< "intent://#Intent;scheme=trusper.referrertests;package=trusper.referrertests;end"
<< "trusper.referrertests"
<< "trusper.referrertests"
<< ""
<< ""
<< ""
<< ""
<< ""
<< true;
QTest::newRow("Valid intent - w/o host & uri-path and extra /")
<< "intent:///#Intent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end"
<< "zxing"
<< "com.google.zxing.client.android"
<< "/"
<< ""
<< "com"
<< "com"
<< "BROWSABLE"
<< true;
QTest::newRow("Valid intent - w/o host & uri-path impler syntax")
<< "intent://#Intent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end"
<< "zxing"
<< "com.google.zxing.client.android"
<< ""
<< ""
<< "com"
<< "com"
<< "BROWSABLE"
<< true;
QTest::newRow("Invalid intent")
<< "intent:///#Inttent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end"
<< ""
<< ""
<< ""
<< ""
<< ""
<< ""
<< ""
<< false;
}
void parseIntentUris()
{
QFETCH(QString, intentUris);
QFETCH(QString, scheme);
QFETCH(QString, package);
QFETCH(QString, uri);
QFETCH(QString, host);
QFETCH(QString, action);
QFETCH(QString, component);
QFETCH(QString, category);
QFETCH(bool, isValid);
IntentUriDescription d = parseIntentUri(intentUris);
QCOMPARE(d.scheme, scheme);
QCOMPARE(d.package, package);
QCOMPARE(d.uriPath, uri);
QCOMPARE(d.host, host);
QCOMPARE(d.action, action);
QCOMPARE(d.component, component);
QCOMPARE(d.category, category);
}
void applyFilters_data()
{
QTest::addColumn("intentUris");
QTest::addColumn("filterFunctionSource");
QTest::addColumn("scheme");
QTest::addColumn("uri");
QTest::addColumn("host");
QTest::newRow("Valid intent - default filter function")
<< "intent://scan/#Intent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end"
<< ""
<< "zxing"
<< "/"
<< "scan";
QTest::newRow("Valid intent - default filter function")
<< "intent://scan/#Intent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end"
<< "(function(result) {return {'scheme': result.scheme+'custom', 'path': result.path+'custom', 'host': result.host+'custom'}; })"
<< "zxingcustom"
<< "/custom"
<< "scancustom";
QTest::newRow("Valid intent - no (optional) host in filter result")
<< "intent://host/my/long/path?a=1/#Intent;component=com;scheme=zxing;category=BROWSABLE;action=com;package=com.google.zxing.client.android;end"
<< "(function(result) {return {'scheme': result.scheme+'custom', 'path': result.path+'custom' }; })"
<< "zxingcustom"
<< "my/long/path?a=1custom"
<< "";
}
void applyFilters()
{
QFETCH(QString, intentUris);
QFETCH(QString, filterFunctionSource);
QFETCH(QString, scheme);
QFETCH(QString, uri);
QFETCH(QString, host);
QMap filters;
filters["intent"] = filterFunctionSource;
SchemeFilter sf(filters);
QVariantMap r = sf.applyFilter(intentUris);
QVERIFY(r.contains("scheme"));
QVERIFY(r.contains("path"));
QCOMPARE(r.value("scheme").toString(), scheme);
QCOMPARE(r.value("host").toString(), host);
QCOMPARE(r.value("path").toString(), uri);
}
};
QTEST_MAIN(IntentFilterTests)
#include "tst_IntentFilterTests.moc"
./tests/unittests/intent-filter/CMakeLists.txt 0000644 0000156 0000165 00000001001 12703462031 021613 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Qml REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_IntentFilterTests)
set(SOURCES
${webapp-container_SOURCE_DIR}/intent-parser.cpp
${webapp-container_SOURCE_DIR}/scheme-filter.cpp
tst_IntentFilterTests.cpp
)
include_directories(${webapp-container_SOURCE_DIR})
add_executable(${TEST} ${SOURCES})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Qml
Qt5::Test
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/webapp-container-color-helper/ 0000755 0000156 0000165 00000000000 12703462032 022127 5 ustar jenkins jenkins ./tests/unittests/webapp-container-color-helper/CMakeLists.txt 0000644 0000156 0000165 00000001065 12703462031 024670 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_WebappContainerColorTests)
set(SOURCES
${webapp-container_SOURCE_DIR}/webapp-container-helper.cpp
tst_WebappContainerColorTests.cpp
)
add_executable(${TEST} ${SOURCES})
include_directories(${webapp-container_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Gui
Qt5::Test
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
set_tests_properties(${TEST} PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
./tests/unittests/webapp-container-color-helper/tst_WebappContainerColorTests.cpp 0000644 0000156 0000165 00000004233 12703462031 030631 0 ustar jenkins jenkins /*
* Copyright 2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
// local
#include "webapp-container-helper.h"
class ContainerColorTests : public QObject
{
Q_OBJECT
private Q_SLOTS:
void cssColorToRgbTest_data()
{
QTest::addColumn("csscolor");
QTest::addColumn("rgb");
QTest::newRow("Empty CSS color") << " " << "";
QTest::newRow("Invalid CSS color") << " invalid " << "";
QTest::newRow("Invalid RGB CSS color") << " rgb (1,1," << "";
QTest::newRow("Invalid RGB CSS color 2") << " rgb (1,1 0)" << "";
QTest::newRow("Invalid RGB CSS color 3") << " rgb (1, e, 0)" << "";
QTest::newRow("Valid SVG name CSS color") << " red " << "255,0,0";
QTest::newRow("Valid RGB CSS color") << " rgb (255, 0, 0) " << "255,0,0";
QTest::newRow("Valid plain RGB CSS color") << " #FF0000 " << "255,0,0";
QTest::newRow("Valid short plain RGB CSS color") << " #36699 " << "3,102,153";
QTest::newRow("Valid shortest plain RGB CSS color") << " #366 " << "51,102,102";
QTest::newRow("Valid very short plain RGB CSS color") << " #000 " << "0,0,0";
QTest::newRow("Valid SVG name CSS color") << " #FF000044 " << "0,0,68";
}
void cssColorToRgbTest()
{
QFETCH(QString, csscolor);
QFETCH(QString, rgb);
WebappContainerHelper helper;
QCOMPARE(helper.rgbColorFromCSSColor(csscolor), rgb);
}
};
QTEST_MAIN(ContainerColorTests)
#include "tst_WebappContainerColorTests.moc"
./tests/unittests/downloads-model/ 0000755 0000156 0000165 00000000000 12703462032 017370 5 ustar jenkins jenkins ./tests/unittests/downloads-model/tst_DownloadsModelTests.cpp 0000644 0000156 0000165 00000016267 12703462031 024737 0 ustar jenkins jenkins /*
* Copyright 2015-2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include "downloads-model.h"
class DownloadsModelTests : public QObject
{
Q_OBJECT
private:
DownloadsModel* model;
private Q_SLOTS:
void init()
{
model = new DownloadsModel;
model->setDatabasePath(":memory:");
}
void cleanup()
{
delete model;
}
void shouldBeInitiallyEmpty()
{
QCOMPARE(model->rowCount(), 0);
}
void shouldExposeRoleNames()
{
QList roleNames = model->roleNames().values();
QVERIFY(roleNames.contains("downloadId"));
QVERIFY(roleNames.contains("url"));
QVERIFY(roleNames.contains("path"));
QVERIFY(roleNames.contains("filename"));
QVERIFY(roleNames.contains("mimetype"));
QVERIFY(roleNames.contains("complete"));
QVERIFY(roleNames.contains("paused"));
QVERIFY(roleNames.contains("error"));
QVERIFY(roleNames.contains("created"));
}
void shouldContainAddedEntries()
{
QVERIFY(!model->contains(QStringLiteral("testid")));
model->add(QStringLiteral("testid"), QUrl(QStringLiteral("http://example.org/")), QStringLiteral("text/html"));
QVERIFY(model->contains(QStringLiteral("testid")));
}
void shouldAddNewEntries()
{
QSignalSpy spy(model, SIGNAL(added(QString, QUrl, QString)));
model->add("testid", QUrl("http://example.org/"), "text/plain");
QCOMPARE(model->rowCount(), 1);
QCOMPARE(spy.count(), 1);
QVariantList args = spy.takeFirst();
QCOMPARE(args.at(0).toString(), QString("testid"));
QCOMPARE(args.at(1).toUrl(), QUrl("http://example.org/"));
QCOMPARE(args.at(2).toString(), QString("text/plain"));
model->add("testid2", QUrl("http://example.org/pdf"), "application/pdf");
QCOMPARE(model->rowCount(), 2);
QCOMPARE(spy.count(), 1);
args = spy.takeFirst();
QCOMPARE(args.at(0).toString(), QString("testid2"));
QCOMPARE(args.at(1).toUrl(), QUrl("http://example.org/pdf"));
QCOMPARE(args.at(2).toString(), QString("application/pdf"));
}
void shouldRemoveCancelled()
{
model->add("testid", QUrl("http://example.org/"), "text/plain");
model->add("testid2", QUrl("http://example.org/pdf"), "application/pdf");
model->add("testid3", QUrl("https://example.org/secure.png"), "image/png");
QCOMPARE(model->rowCount(), 3);
model->cancelDownload("testid2");
QCOMPARE(model->rowCount(), 2);
model->cancelDownload("invalid");
QCOMPARE(model->rowCount(), 2);
}
void shouldCompleteDownloads()
{
QSignalSpy spy(model, SIGNAL(completeChanged(QString, bool)));
model->add("testid", QUrl("http://example.org/"), "text/plain");
QVERIFY(!model->data(model->index(0, 0), DownloadsModel::Complete).toBool());
model->setComplete("testid", true);
QCOMPARE(spy.count(), 1);
QVariantList args = spy.takeFirst();
QCOMPARE(args.at(0).toString(), QString("testid"));
QCOMPARE(args.at(1).toBool(), true);
}
void shouldKeepEntriesSortedChronologically()
{
model->add("testid", QUrl("http://example.org/"), "text/plain");
model->add("testid2", QUrl("http://example.org/pdf"), "application/pdf");
model->add("testid3", QUrl("https://example.org/secure.png"), "image/png");
QCOMPARE(model->data(model->index(0, 0), DownloadsModel::DownloadId).toString(), QString("testid3"));
QCOMPARE(model->data(model->index(1, 0), DownloadsModel::DownloadId).toString(), QString("testid2"));
QCOMPARE(model->data(model->index(2, 0), DownloadsModel::DownloadId).toString(), QString("testid"));
}
void shouldReturnData()
{
model->add("testid", QUrl("http://example.org/"), "text/plain");
QVERIFY(!model->data(QModelIndex(), DownloadsModel::DownloadId).isValid());
QVERIFY(!model->data(model->index(-1, 0), DownloadsModel::DownloadId).isValid());
QVERIFY(!model->data(model->index(3, 0), DownloadsModel::DownloadId).isValid());
QCOMPARE(model->data(model->index(0, 0), DownloadsModel::DownloadId).toString(), QString("testid"));
QCOMPARE(model->data(model->index(0, 0), DownloadsModel::Url).toUrl(), QUrl("http://example.org/"));
QCOMPARE(model->data(model->index(0, 0), DownloadsModel::Mimetype).toString(), QString("text/plain"));
QVERIFY(model->data(model->index(0, 0), DownloadsModel::Created).toDateTime() <= QDateTime::currentDateTime());
QVERIFY(!model->data(model->index(0, 0), DownloadsModel::Complete).toBool());
}
void shouldReturnDatabasePath()
{
QCOMPARE(model->databasePath(), QString(":memory:"));
}
void shouldNotifyWhenSettingDatabasePath()
{
QSignalSpy spyPath(model, SIGNAL(databasePathChanged()));
QSignalSpy spyReset(model, SIGNAL(modelReset()));
model->setDatabasePath(":memory:");
QVERIFY(spyPath.isEmpty());
QVERIFY(spyReset.isEmpty());
model->setDatabasePath("");
QCOMPARE(spyPath.count(), 1);
QCOMPARE(spyReset.count(), 1);
QCOMPARE(model->databasePath(), QString(":memory:"));
}
void shouldSerializeOnDisk()
{
QTemporaryFile tempFile;
tempFile.open();
QString fileName = tempFile.fileName();
delete model;
model = new DownloadsModel;
model->setDatabasePath(fileName);
model->add("testid", QUrl("http://example.org/"), "text/plain");
model->add("testid2", QUrl("http://example.org/pdf"), "application/pdf");
delete model;
model = new DownloadsModel;
model->setDatabasePath(fileName);
model->fetchMore();
QCOMPARE(model->rowCount(), 2);
}
void shouldCountNumberOfEntries()
{
QCOMPARE(model->property("count").toInt(), 0);
QCOMPARE(model->rowCount(), 0);
model->add("testid", QUrl("http://example.org/"), "text/plain");
QCOMPARE(model->property("count").toInt(), 1);
QCOMPARE(model->rowCount(), 1);
model->add("testid2", QUrl("http://example.org/pdf"), "application/pdf");
QCOMPARE(model->property("count").toInt(), 2);
QCOMPARE(model->rowCount(), 2);
model->add("testid3", QUrl("https://example.org/secure.png"), "image/png");
QCOMPARE(model->property("count").toInt(), 3);
QCOMPARE(model->rowCount(), 3);
}
};
QTEST_MAIN(DownloadsModelTests)
#include "tst_DownloadsModelTests.moc"
./tests/unittests/downloads-model/CMakeLists.txt 0000644 0000156 0000165 00000000626 12703462031 022133 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Sql REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_DownloadsModelTests)
add_executable(${TEST} tst_DownloadsModelTests.cpp)
include_directories(${webbrowser-app_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Sql
Qt5::Test
webbrowser-app-models
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/favicon-fetcher/ 0000755 0000156 0000165 00000000000 12703462032 017343 5 ustar jenkins jenkins ./tests/unittests/favicon-fetcher/tst_FaviconFetcherTests.cpp 0000644 0000156 0000165 00000024003 12703462031 024650 0 ustar jenkins jenkins /*
* Copyright 2014-2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// system
#include
// Qt
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// local
#include "favicon-fetcher.h"
const unsigned char icon_data[] = {
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x00,
0x01, 0x00, 0x38, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const int icon_data_size = 78;
class TestHTTPServer : public QTcpServer
{
Q_OBJECT
public:
TestHTTPServer(QObject* parent = 0)
: QTcpServer(parent)
{
connect(this, SIGNAL(newConnection()), SLOT(onNewConnection()));
}
QString baseURL() const
{
return "http://" + serverAddress().toString() + ":" + QString::number(serverPort());
}
Q_SIGNALS:
void gotRequest(const QString& path) const;
void gotError() const;
private Q_SLOTS:
void onNewConnection() {
if (hasPendingConnections()) {
QTcpSocket* socket = nextPendingConnection();
connect(socket, SIGNAL(readyRead()), SLOT(readClient()));
connect(socket, SIGNAL(disconnected()), SLOT(discardClient()));
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SIGNAL(gotError()));
}
}
void readClient()
{
QTcpSocket* socket = qobject_cast(sender());
if (!socket) {
return;
}
if (!socket->canReadLine()) {
return;
}
QStringList tokens = QString(socket->readLine()).split(QRegExp("[ \r\n][ \r\n]*"));
if (tokens.isEmpty()) {
return;
}
if (tokens.first() != "GET") {
return;
}
QString path = tokens[1];
Q_EMIT gotRequest(path);
QTextStream response(socket);
response.setAutoDetectUnicode(true);
QRegExp icon("/\\w+\\.ico");
QRegExp redirection("^/redirect/(\\d+)/(.*)");
if (icon.exactMatch(path)) {
response << "HTTP/1.0 200 OK\r\n"
<< "Content-Length: " << icon_data_size << "\r\n"
<< "Content-Type: image/x-icon\r\n\r\n"
<< QString::fromLocal8Bit((const char*) icon_data, icon_data_size) << "\n";
} else if (redirection.exactMatch(path)) {
int n = redirection.cap(1).toInt();
response << "HTTP/1.0 303 See Other\r\n"
<< "Content-Length: 9\r\n"
<< "Content-Type: text/plain\r\n"
<< "Location: " << baseURL();
if (n == 1) {
response << "/" << redirection.cap(2);
} else {
response << "/redirect/" << (n - 1) << "/" << redirection.cap(2);
}
response << "\r\n\r\n"
<< "see other\n";
} else {
response << "HTTP/1.0 404 Not Found\r\n"
<< "Content-Length: 9\r\n"
<< "Content-Type: text/plain\r\n\r\n"
<< "not found\n";
}
response.flush();
socket->waitForBytesWritten();
socket->disconnectFromHost();
}
void discardClient()
{
QTcpSocket* socket = qobject_cast(sender());
if (socket) {
socket->deleteLater();
}
}
};
class FaviconFetcherTests : public QObject
{
Q_OBJECT
private:
FaviconFetcher* fetcher;
QSignalSpy* fetcherSpy;
TestHTTPServer* server;
QSignalSpy* serverSpy;
QSignalSpy* errorSpy;
private Q_SLOTS:
void init()
{
{
FaviconFetcher temp;
QDir(temp.cacheLocation()).removeRecursively();
}
fetcher = new FaviconFetcher;
fetcherSpy = new QSignalSpy(fetcher, SIGNAL(localUrlChanged()));
server = new TestHTTPServer;
server->listen();
serverSpy = new QSignalSpy(server, SIGNAL(gotRequest(const QString&)));
errorSpy = new QSignalSpy(server, SIGNAL(gotError()));
}
void cleanup()
{
delete errorSpy;
delete serverSpy;
delete server;
delete fetcherSpy;
delete fetcher;
}
void shouldCacheIcon()
{
QUrl url(server->baseURL() + "/favicon1.ico");
fetcher->setUrl(url);
QCOMPARE(fetcher->url(), url);
QVERIFY(fetcherSpy->wait());
QCOMPARE(serverSpy->count(), 1);
QString cached = fetcher->localUrl().path();
QVERIFY(cached.startsWith(fetcher->cacheLocation()));
QVERIFY(QFileInfo::exists(cached));
}
void shouldNotCacheLocalIcon()
{
QUrl url("file:///tmp/favicon.ico");
fetcher->setUrl(url);
QCOMPARE(fetcherSpy->count(), 1);
QVERIFY(serverSpy->isEmpty());
QCOMPARE(fetcher->localUrl(), url);
QDir cache(fetcher->cacheLocation(), "", QDir::Unsorted, QDir::Files | QDir::NoDotAndDotDot);
QCOMPARE(cache.count(), (uint) 0);
}
void shouldNotCacheInvalidIcon()
{
// First fetch a valid icon to ensure localUrl is initially not empty
QUrl url(server->baseURL() + "/favicon1.ico");
fetcher->setUrl(url);
QVERIFY(fetcherSpy->wait());
QVERIFY(!fetcher->localUrl().isEmpty());
// Then request an invalid one
url = QUrl(server->baseURL() + "/invalid");
fetcher->setUrl(url);
QVERIFY(serverSpy->wait());
QVERIFY(fetcher->localUrl().isEmpty());
}
void shouldReturnCachedIcon()
{
// First fetch an icon so that it’s cached
QUrl url(server->baseURL() + "/favicon1.ico");
fetcher->setUrl(url);
QVERIFY(fetcherSpy->wait());
QUrl localUrl = fetcher->localUrl();
QVERIFY(!localUrl.isEmpty());
// Then fetch another icon
fetcher->setUrl(QUrl(server->baseURL() + "/favicon2.ico"));
QVERIFY(fetcherSpy->wait());
QVERIFY(!fetcher->localUrl().isEmpty());
// Then fetch the first icon again, and verify it comes from the cache
serverSpy->clear();
fetcher->setUrl(url);
QCOMPARE(fetcher->localUrl(), localUrl);
QVERIFY(serverSpy->isEmpty());
}
void shouldNotCacheIcon()
{
fetcher->setShouldCache(false);
QUrl url(server->baseURL() + "/favicon1.ico");
fetcher->setUrl(url);
QCOMPARE(fetcher->url(), url);
QVERIFY(fetcherSpy->wait());
QCOMPARE(serverSpy->count(), 1);
QUrl icon = fetcher->localUrl();
QVERIFY(!icon.isLocalFile());
QCOMPARE(icon.scheme(), QString("data"));
QVERIFY(icon.path().startsWith("image/png;base64,"));
}
void shouldHandleRedirections()
{
QUrl url(server->baseURL() + "/redirect/3/favicon1.ico");
fetcher->setUrl(url);
QVERIFY(fetcherSpy->wait());
QCOMPARE(serverSpy->count(), 4);
}
void shouldNotHandleTooManyRedirections()
{
QUrl url(server->baseURL() + "/redirect/8/favicon1.ico");
QString msg("Failed to download %1 : too many redirections");
QTest::ignoreMessage(QtWarningMsg, msg.arg(url.toString()).toUtf8());
fetcher->setUrl(url);
QVERIFY(!fetcherSpy->wait(500));
QCOMPARE(serverSpy->count(), 5);
}
void shouldDiscardOldCachedIcons()
{
// First fetch an icon, and touch the cached file on disk to ensure
// it will be considered out of date next time it’s requested
QUrl url(server->baseURL() + "/favicon1.ico");
fetcher->setUrl(url);
QVERIFY(fetcherSpy->wait());
QUrl localUrl = fetcher->localUrl();
struct utimbuf ubuf;
ubuf.modtime = QDateTime::currentDateTime().addYears(-1).toTime_t();
QCOMPARE(utime(localUrl.path().toUtf8().constData(), &ubuf), 0);
// Then fetch another icon
fetcher->setUrl(QUrl(server->baseURL() + "/favicon2.ico"));
QVERIFY(fetcherSpy->wait());
QVERIFY(!fetcher->localUrl().isEmpty());
// Then fetch the first icon again, and verify it is being re-downloaded
serverSpy->clear();
fetcher->setUrl(url);
QVERIFY(fetcherSpy->wait());
QCOMPARE(fetcher->localUrl(), localUrl);
QCOMPARE(serverSpy->count(), 1);
}
void shouldCancelRequests()
{
// Issue several requests rapidly in succession, and verify that
// all the previous ones are discarded. Take into account possible
// errors (see https://launchpad.net/bugs/1498539).
int requests = 100;
int errors = 0;
for (int i = 1; i <= requests; ++i) {
QUrl url(server->baseURL() + "/favicon" + QString::number(i) + ".ico");
fetcher->setUrl(url);
QVERIFY(serverSpy->wait(500) || (errorSpy->count() == ++errors));
}
QVERIFY(fetcherSpy->wait());
QCOMPARE(serverSpy->count() + errorSpy->count(), requests);
QCOMPARE(fetcherSpy->count(), 1);
}
};
QTEST_MAIN(FaviconFetcherTests)
#include "tst_FaviconFetcherTests.moc"
./tests/unittests/favicon-fetcher/CMakeLists.txt 0000644 0000156 0000165 00000001126 12703462031 022102 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_FaviconFetcherTests)
set(SOURCES
${webbrowser-common_SOURCE_DIR}/favicon-fetcher.cpp
tst_FaviconFetcherTests.cpp
)
add_executable(${TEST} ${SOURCES})
include_directories(${webbrowser-common_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Gui
Qt5::Network
Qt5::Test
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
set_tests_properties(${TEST} PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
./tests/unittests/bookmarks-folder-model/ 0000755 0000156 0000165 00000000000 12703462032 020637 5 ustar jenkins jenkins ./tests/unittests/bookmarks-folder-model/tst_BookmarksFolderModelTests.cpp 0000644 0000156 0000165 00000010671 12703462031 027331 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
// local
#include "bookmarks-model.h"
#include "bookmarks-folder-model.h"
class BookmarksFolderModelTests : public QObject
{
Q_OBJECT
private:
BookmarksModel* bookmarks;
BookmarksFolderModel* model;
private Q_SLOTS:
void init()
{
bookmarks = new BookmarksModel;
bookmarks->setDatabasePath(":memory:");
model = new BookmarksFolderModel;
model->setSourceModel(bookmarks);
}
void cleanup()
{
delete model;
delete bookmarks;
}
void shouldBeInitiallyEmpty()
{
QCOMPARE(model->rowCount(), 0);
}
void shouldNotifyWhenChangingSourceModel()
{
QSignalSpy spy(model, SIGNAL(sourceModelChanged()));
model->setSourceModel(bookmarks);
QVERIFY(spy.isEmpty());
BookmarksModel* bookmarks2 = new BookmarksModel;
model->setSourceModel(bookmarks2);
QCOMPARE(spy.count(), 1);
QCOMPARE(model->sourceModel(), bookmarks2);
model->setSourceModel(0);
QCOMPARE(spy.count(), 2);
QCOMPARE(model->sourceModel(), (BookmarksModel*) 0);
}
void shouldNotifyWhenChangingFolder()
{
QSignalSpy spy(model, SIGNAL(folderChanged()));
model->setFolder(QString());
QVERIFY(spy.isEmpty());
model->setFolder(QString(""));
QVERIFY(spy.isEmpty());
model->setFolder("SampleFolder");
QCOMPARE(spy.count(), 1);
}
void shouldMatchAllNotInFoldersWhenNoFolderSet()
{
bookmarks->add(QUrl("http://example.org"), "Example Domain", QUrl(), "");
bookmarks->add(QUrl("http://example.com"), "Example Domain", QUrl(), "SampleFolder");
QCOMPARE(model->rowCount(), 1);
}
void shouldFilterOutNonMatchingFolders()
{
bookmarks->add(QUrl("http://example.org/"), "Example Domain Org", QUrl(), "");
bookmarks->add(QUrl("http://example.com/"), "Example Domain Com", QUrl(), "SampleFolder01");
bookmarks->add(QUrl("http://example.net/"), "Example Domain Net", QUrl("http://example.net/icon.png"), "SampleFolder02");
model->setFolder("");
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://example.org/"));
QCOMPARE(model->get(0).value("url").toUrl(), QUrl("http://example.org/"));
model->setFolder("SampleFolder01");
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://example.com/"));
QCOMPARE(model->get(0).value("title").toString(), QString("Example Domain Com"));
model->setFolder("SampleFolder02");
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://example.net/"));
QCOMPARE(model->get(0).value("icon").toUrl(), QUrl("http://example.net/icon.png"));
model->setFolder("AnotherFolder");
QCOMPARE(model->rowCount(), 0);
}
void shouldMatchCaseSensitiveFolderName()
{
bookmarks->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "SAMPLE");
bookmarks->add(QUrl("http://example.com/"), "Example Domain", QUrl(), "sample");
model->setFolder("SAMPLE");
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://example.org/"));
model->setFolder("sample");
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://example.com/"));
model->setFolder("SaMpLe");
QCOMPARE(model->rowCount(), 0);
}
};
QTEST_MAIN(BookmarksFolderModelTests)
#include "tst_BookmarksFolderModelTests.moc"
./tests/unittests/bookmarks-folder-model/CMakeLists.txt 0000644 0000156 0000165 00000000642 12703462031 023400 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Sql REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_BookmarksFolderModelTests)
add_executable(${TEST} tst_BookmarksFolderModelTests.cpp)
include_directories(${webbrowser-app_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Sql
Qt5::Test
webbrowser-app-models
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/bookmarks-folderlist-model/ 0000755 0000156 0000165 00000000000 12703462032 021533 5 ustar jenkins jenkins ./tests/unittests/bookmarks-folderlist-model/CMakeLists.txt 0000644 0000156 0000165 00000000652 12703462031 024275 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Sql REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_BookmarksFolderListModelTests)
add_executable(${TEST} tst_BookmarksFolderListModelTests.cpp)
include_directories(${webbrowser-app_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Sql
Qt5::Test
webbrowser-app-models
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/bookmarks-folderlist-model/tst_BookmarksFolderListModelTests.cpp 0000644 0000156 0000165 00000030035 12703462031 031055 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
// local
#include "bookmarks-model.h"
#include "bookmarks-folder-model.h"
#include "bookmarks-folderlist-model.h"
class BookmarksFolderListModelTests : public QObject
{
Q_OBJECT
private:
BookmarksModel* bookmarks;
BookmarksFolderListModel* model;
void verifyDataChanged(QSignalSpy& spy, int row)
{
QList args;
bool changed = false;
while(!changed && !spy.isEmpty()) {
args = spy.takeFirst();
int start = args.at(0).toModelIndex().row();
int end = args.at(1).toModelIndex().row();
changed = (start <= row) && (row <= end);
}
QVERIFY(changed);
}
private Q_SLOTS:
void init()
{
bookmarks = new BookmarksModel;
bookmarks->setDatabasePath(":memory:");
model = new BookmarksFolderListModel;
model->setSourceModel(bookmarks);
}
void cleanup()
{
delete model;
delete bookmarks;
}
void shouldHaveInitiallyOnlyDefaultFolder()
{
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0, 0), BookmarksFolderListModel::Folder).toString(), QString(""));
}
void shouldUpdateFolderListWhenInsertingEntries()
{
QSignalSpy spyRowsInserted(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
qRegisterMetaType >();
QSignalSpy spyDataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
bookmarks->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "SampleFolder");
QVERIFY(!spyDataChanged.isEmpty());
QCOMPARE(spyRowsInserted.count(), 1);
QList args = spyRowsInserted.takeFirst();
QCOMPARE(args.at(1).toInt(), 1);
QCOMPARE(args.at(2).toInt(), 1);
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->data(model->index(1, 0), BookmarksFolderListModel::Folder).toString(), QString("SampleFolder"));
bookmarks->add(QUrl("http://example.com/"), "Example Domain", QUrl(), "AnotherFolder");
QVERIFY(!spyDataChanged.isEmpty());
QCOMPARE(spyRowsInserted.count(), 1);
args = spyRowsInserted.takeFirst();
QCOMPARE(args.at(1).toInt(), 1);
QCOMPARE(args.at(2).toInt(), 1);
QCOMPARE(model->rowCount(), 3);
QCOMPARE(model->data(model->index(1, 0), BookmarksFolderListModel::Folder).toString(), QString("AnotherFolder"));
bookmarks->add(QUrl("http://example.org/test.html"), "Test page", QUrl(), "SampleFolder");
QVERIFY(spyRowsInserted.isEmpty());
QVERIFY(!spyDataChanged.isEmpty());
QCOMPARE(model->rowCount(), 3);
}
void shouldCreateNewEmptyFolder()
{
model->createNewFolder("SampleFolder");
QCOMPARE(model->rowCount(), 2);
QModelIndex index = model->index(1, 0);
QCOMPARE(model->data(index, BookmarksFolderListModel::Folder).toString(), QString("SampleFolder"));
BookmarksFolderModel* entries = model->data(index, BookmarksFolderListModel::Entries).value();
QCOMPARE(entries->rowCount(), 0);
}
void shouldNotUpdateFolderListWhenRemovingEntries()
{
bookmarks->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "SampleFolder");
bookmarks->add(QUrl("http://example.com/"), "Example Domain", QUrl(), "AnotherFolder");
bookmarks->add(QUrl("http://example.org/test"), "Example Domain", QUrl(), "SampleFolder");
QCOMPARE(model->rowCount(), 3);
bookmarks->remove(QUrl("http://example.org/test"));
QCOMPARE(model->rowCount(), 3);
bookmarks->remove(QUrl("http://example.org/"));
QCOMPARE(model->rowCount(), 3);
QModelIndex index = model->index(2, 0);
QString folder = model->data(index, BookmarksFolderListModel::Folder).toString();
QCOMPARE(folder, QString("SampleFolder"));
BookmarksFolderModel* entries = model->data(index, BookmarksFolderListModel::Entries).value();
QVERIFY(entries != 0);
QCOMPARE(entries->rowCount(), 0);
}
void shouldUpdateDataWhenMovingEntries()
{
bookmarks->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "SampleFolder");
bookmarks->add(QUrl("http://example.com/"), "Example Domain", QUrl(), "AnotherFolder");
QTest::qWait(100);
QSignalSpy spyRowsMoved(model, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
qRegisterMetaType >();
QSignalSpy spyDataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
bookmarks->add(QUrl("http://example.org/test"), "Example Domain", QUrl(), "SampleFolder");
QVERIFY(spyRowsMoved.isEmpty());
}
void shouldUpdateDataWhenDataChanges()
{
bookmarks->add(QUrl("http://example.com/"), "Example Domain", QUrl(), "SampleFolder");
bookmarks->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "AnotherFolder");
QSignalSpy spyRowsMoved(model, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
qRegisterMetaType >();
QSignalSpy spyDataChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
bookmarks->add(QUrl("http://example.org/foobar"), "Example Domain", QUrl(), "SampleFolder");
QVERIFY(spyRowsMoved.isEmpty());
QVERIFY(!spyDataChanged.isEmpty());
verifyDataChanged(spyDataChanged, 2);
}
void shouldUpdateWhenChangingSourceModel()
{
QSignalSpy spy(model, SIGNAL(sourceModelChanged()));
bookmarks->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "SampleFolder");
bookmarks->add(QUrl("http://example.com/"), "Example Domain", QUrl(), "AnotherFolder");
bookmarks->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl(), "");
QCOMPARE(model->rowCount(), 3);
model->setSourceModel(bookmarks);
QVERIFY(spy.isEmpty());
QCOMPARE(model->rowCount(), 3);
model->setSourceModel(0);
QCOMPARE(spy.count(), 1);
QCOMPARE(model->sourceModel(), (BookmarksModel*) 0);
QCOMPARE(model->rowCount(), 0);
BookmarksModel* bookmarks2 = new BookmarksModel();
model->setSourceModel(bookmarks2);
QCOMPARE(spy.count(), 2);
QCOMPARE(model->sourceModel(), bookmarks2);
QCOMPARE(model->rowCount(), 0);
}
void shouldKeepFolderSorted()
{
bookmarks->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "Folder02");
bookmarks->add(QUrl("http://www.gogle.com/lawnmower"), "Gogle Lawn Mower", QUrl(), "Folder03");
bookmarks->add(QUrl("http://example.com/"), "Example Domain", QUrl(), "Folder01");
bookmarks->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl(), "Folder04");
bookmarks->add(QUrl("http://www.gogle.com/mail"), "Gogle Mail", QUrl(), "Folder03");
bookmarks->add(QUrl("https://mail.gogle.com/"), "Gogle Mail", QUrl(), "Folder03");
QCOMPARE(model->rowCount(), 5);
QStringList folders;
folders << "" << "Folder01" << "Folder02" << "Folder03" << "Folder04";
for (int i = 0; i < folders.count(); ++i) {
QModelIndex index = model->index(i, 0);
QString folder = model->data(index, BookmarksFolderListModel::Folder).toString();
BookmarksFolderModel* entries = model->data(index, BookmarksFolderListModel::Entries).value();
QVERIFY(!folder.isNull());
QCOMPARE(folder, folders.at(i));
QCOMPARE(entries->folder(), folder);
}
}
void shouldExposeFolderModels()
{
bookmarks->add(QUrl("http://example.com/"), "Example Domain", QUrl(), "SampleFolder");
bookmarks->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "AnotherFolder");
QTest::qWait(100);
bookmarks->add(QUrl("http://example.org/test.html"), "Test Page", QUrl(), "AnotherFolder");
bookmarks->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl(), "");
QCOMPARE(model->rowCount(), 3);
QModelIndex index = model->index(0, 0);
QString folder = model->data(index, BookmarksFolderListModel::Folder).toString();
QCOMPARE(folder, QString(""));
BookmarksFolderModel* entries = model->data(index, BookmarksFolderListModel::Entries).value();
QCOMPARE(entries->rowCount(), 1);
QCOMPARE(entries->data(entries->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://ubuntu.com/"));
index = model->index(1, 0);
folder = model->data(index, BookmarksFolderListModel::Folder).toString();
QCOMPARE(folder, QString("AnotherFolder"));
entries = model->data(index, BookmarksFolderListModel::Entries).value();
QCOMPARE(entries->rowCount(), 2);
QCOMPARE(entries->data(entries->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://example.org/test.html"));
QCOMPARE(entries->data(entries->index(1, 0), BookmarksModel::Url).toUrl(), QUrl("http://example.org/"));
index = model->index(2, 0);
folder = model->data(index, BookmarksFolderListModel::Folder).toString();
QCOMPARE(folder, QString("SampleFolder"));
entries = model->data(index, BookmarksFolderListModel::Entries).value();
QCOMPARE(entries->rowCount(), 1);
QCOMPARE(entries->data(entries->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://example.com/"));
}
void shouldReturnData()
{
QDateTime now = QDateTime::currentDateTimeUtc();
bookmarks->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "SampleFolder");
QVERIFY(!model->data(QModelIndex(), BookmarksFolderListModel::Folder).isValid());
QVERIFY(!model->data(model->index(-1, 0), BookmarksFolderListModel::Folder).isValid());
QVERIFY(!model->data(model->index(3, 0), BookmarksFolderListModel::Folder).isValid());
QCOMPARE(model->data(model->index(1, 0), BookmarksFolderListModel::Folder).toString(), QString("SampleFolder"));
BookmarksFolderModel* entries = model->data(model->index(1, 0), BookmarksFolderListModel::Entries).value();
QVERIFY(entries != 0);
QCOMPARE(entries->rowCount(), 1);
QVERIFY(!model->data(model->index(1, 0), BookmarksFolderListModel::Entries + 1).isValid());
}
void shouldReturnDataByIndex()
{
bookmarks->add(QUrl("http://example.com/"), "Example Domain", QUrl(), "SampleFolder");
bookmarks->add(QUrl("http://example.org/"), "Example Domain", QUrl(), "AnotherFolder");
bookmarks->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl(), "");
QCOMPARE(model->rowCount(), 3);
QCOMPARE(model->indexOf("AnotherFolder"), 1);
QVariantMap folderMap = model->get(3);
QVERIFY(folderMap.isEmpty());
folderMap = model->get(1);
QCOMPARE(folderMap.value("folder").toString(), QString("AnotherFolder"));
BookmarksFolderModel* entries = folderMap.value("entries").value();
QCOMPARE(entries->rowCount(), 1);
QCOMPARE(entries->data(entries->index(0, 0), BookmarksModel::Url).toUrl(), QUrl("http://example.org/"));
}
};
QTEST_MAIN(BookmarksFolderListModelTests)
#include "tst_BookmarksFolderListModelTests.moc"
./tests/unittests/container-url-patterns/ 0000755 0000156 0000165 00000000000 12703462032 020720 5 ustar jenkins jenkins ./tests/unittests/container-url-patterns/tst_ContainerUrlPatternsTests.cpp 0000644 0000156 0000165 00000014136 12703462031 027473 0 ustar jenkins jenkins /*
* Copyright 2014 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
// local
#include "url-pattern-utils.h"
class ContainerUrlPatternsTests : public QObject
{
Q_OBJECT
private Q_SLOTS:
void transformedUrlPatterns_data()
{
QTest::addColumn("pattern");
QTest::addColumn("transformedPattern");
QTest::addColumn("doTransformUrlPath");
// regular patterns
QTest::newRow("Valid pattern")
<< "https?://*.mydomain.com/*"
<< "https?://[^\\./]*.mydomain.com/[^\\s]*"
<< true;
QTest::newRow("Valid pattern with no tail replacement")
<< "https?://*.mydomain.com/l.php\\?\\w+=([^&]+).*"
<< "https?://[^\\./]*.mydomain.com/l.php\\?\\w+=([^&]+).*"
<< false;
QTest::newRow("Valid pattern - short url")
<< "https?://mydomain.com/*"
<< "https?://mydomain.com/[^\\s]*" << true;
QTest::newRow("Valid pattern - strict url")
<< "https?://www.mydomain.com/*"
<< "https?://www.mydomain.com/[^\\s]*" << true;
#define WEBAPP_INVALID_URL_PATTERN_TEST(id,invalid_url_pattern) \
QTest::newRow("Invalid pattern " #id) \
<< invalid_url_pattern \
<< QString() << true
WEBAPP_INVALID_URL_PATTERN_TEST(1, "http");
WEBAPP_INVALID_URL_PATTERN_TEST(2, "://");
WEBAPP_INVALID_URL_PATTERN_TEST(3, "file://");
WEBAPP_INVALID_URL_PATTERN_TEST(4, "https?://");
WEBAPP_INVALID_URL_PATTERN_TEST(5, "https?://*");
WEBAPP_INVALID_URL_PATTERN_TEST(6, "https?://foo.*");
WEBAPP_INVALID_URL_PATTERN_TEST(7, "https?://foo.ba*r.com");
WEBAPP_INVALID_URL_PATTERN_TEST(8, "https?://foo.*.com/");
WEBAPP_INVALID_URL_PATTERN_TEST(9, "https?://foo.bar.*/");
WEBAPP_INVALID_URL_PATTERN_TEST(10, "https?://*.bar.*");
WEBAPP_INVALID_URL_PATTERN_TEST(11, "https?://*.bar.*/");
WEBAPP_INVALID_URL_PATTERN_TEST(12, "https?://*.bar.*/");
WEBAPP_INVALID_URL_PATTERN_TEST(13, "httpsfoo?://*.bar.com/");
WEBAPP_INVALID_URL_PATTERN_TEST(14, "httppoo://*.bar.com/");
#undef WEBAPP_INVALID_URL_PATTERN_TEST
// Google patterns
QTest::newRow("Valid Google pattern")
<< "https?://mail.google.*/*"
<< "https?://mail.google.[^\\./]*/[^\\s]*" << true;
QTest::newRow("Valid Google com SLD pattern")
<< "https?://mail.google.com.*/*"
<< "https?://mail.google.com.[^\\./]*/[^\\s]*" << true;
QTest::newRow("Valid Google co SLD pattern")
<< "https?://mail.google.co.*/*"
<< "https?://mail.google.co.[^\\./]*/[^\\s]*" << true;
QTest::newRow("Valid non Google pattern")
<< "https://*.google.com/*"
<< "https://[^\\./]*.google.com/[^\\s]*" << true;
#define WEBAPP_INVALID_GOOGLE_URL_PATTERN_TEST(id,invalid_google_url_pattern) \
QTest::newRow("Invalid Google App pattern " #id) \
<< invalid_google_url_pattern \
<< QString() << true
WEBAPP_INVALID_GOOGLE_URL_PATTERN_TEST(1, "https://*.google.*/*");
WEBAPP_INVALID_GOOGLE_URL_PATTERN_TEST(2, "https://service.gooo*gle.com/*");
WEBAPP_INVALID_GOOGLE_URL_PATTERN_TEST(3, "https://service.gooo?gle.com/*");
WEBAPP_INVALID_GOOGLE_URL_PATTERN_TEST(4, "https://service.goo*gle.com/*");
WEBAPP_INVALID_GOOGLE_URL_PATTERN_TEST(5, "https://serv?ice.goo*gle.com/*");
WEBAPP_INVALID_GOOGLE_URL_PATTERN_TEST(6, "https://se*rv?ice.goo*gle.com/*");
WEBAPP_INVALID_GOOGLE_URL_PATTERN_TEST(7, "https://se*rvice.goo*gle.com/*");
WEBAPP_INVALID_GOOGLE_URL_PATTERN_TEST(8, "https://se*rvice.goo*gle.*/*");
WEBAPP_INVALID_GOOGLE_URL_PATTERN_TEST(8, "https://service.google.kom.*/*");
}
void transformedUrlPatterns()
{
QFETCH(QString, pattern);
QFETCH(QString, transformedPattern);
QFETCH(bool, doTransformUrlPath);
QCOMPARE(UrlPatternUtils::transformWebappSearchPatternToSafePattern(pattern, doTransformUrlPath), transformedPattern);
}
void filteredUrlPatterns_data()
{
QTest::addColumn("patterns");
QTest::addColumn("filteredPattern");
// regular patterns
QTest::newRow("Patterns with empty ones")
<< (QStringList() << QString("https?://*.mydomain.com/*")
<< QString()
<< QString("https?://www.mydomain.com/*")
<< QString())
<< (QStringList() << QString("https?://[^\\./]*.mydomain.com/[^\\s]*")
<< QString("https?://www.mydomain.com/[^\\s]*"));
QTest::newRow("Patterns with invalid ones")
<< (QStringList() << QString("https?://*.mydomain.com/*")
<< QString()
<< QString("https?://*")
<< QString())
<< (QStringList() << QString("https?://[^\\./]*.mydomain.com/[^\\s]*"));
}
void filteredUrlPatterns()
{
QFETCH(QStringList, patterns);
QFETCH(QStringList, filteredPattern);
QCOMPARE(UrlPatternUtils::filterAndTransformUrlPatterns(patterns), filteredPattern);
}
};
QTEST_MAIN(ContainerUrlPatternsTests)
#include "tst_ContainerUrlPatternsTests.moc"
./tests/unittests/container-url-patterns/CMakeLists.txt 0000644 0000156 0000165 00000000665 12703462031 023466 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_ContainerUrlPatternsTests)
set(SOURCES
${webapp-container_SOURCE_DIR}/url-pattern-utils.cpp
tst_ContainerUrlPatternsTests.cpp
)
add_executable(${TEST} ${SOURCES})
include_directories(${webapp-container_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Test
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/meminfo/ 0000755 0000156 0000165 00000000000 12703462032 015732 5 ustar jenkins jenkins ./tests/unittests/meminfo/tst_MemInfoTests.cpp 0000644 0000156 0000165 00000004554 12703462031 021714 0 ustar jenkins jenkins /*
* Copyright 2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
// local
#include "meminfo.h"
class MemInfoTests : public QObject
{
Q_OBJECT
private:
MemInfo* meminfo;
private Q_SLOTS:
void init()
{
meminfo = new MemInfo(this);
}
void cleanup()
{
delete meminfo;
}
void test_active_property()
{
QVERIFY(meminfo->active());
QSignalSpy spy(meminfo, SIGNAL(activeChanged()));
meminfo->setActive(true);
QVERIFY(spy.isEmpty());
meminfo->setActive(false);
QCOMPARE(spy.count(), 1);
QVERIFY(!meminfo->active());
spy.clear();
meminfo->setActive(false);
QVERIFY(spy.isEmpty());
meminfo->setActive(true);
QCOMPARE(spy.count(), 1);
QVERIFY(meminfo->active());
}
void test_interval_property()
{
QCOMPARE(meminfo->interval(), 5000);
QSignalSpy spy(meminfo, SIGNAL(intervalChanged()));
meminfo->setInterval(5000);
QVERIFY(spy.isEmpty());
meminfo->setInterval(1500);
QCOMPARE(spy.count(), 1);
QCOMPARE(meminfo->interval(), 1500);
}
void test_initial_values()
{
QCOMPARE(meminfo->total(), 0);
QCOMPARE(meminfo->free(), 0);
}
void test_update()
{
QSignalSpy totalSpy(meminfo, SIGNAL(totalChanged()));
QSignalSpy freeSpy(meminfo, SIGNAL(freeChanged()));
meminfo->setInterval(100);
totalSpy.wait();
freeSpy.wait();
QVERIFY(meminfo->total() > 0);
QVERIFY(meminfo->free() > 0);
QVERIFY(meminfo->total() > meminfo->free());
}
};
QTEST_MAIN(MemInfoTests)
#include "tst_MemInfoTests.moc"
./tests/unittests/meminfo/CMakeLists.txt 0000644 0000156 0000165 00000000742 12703462031 020474 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_MemInfoTests)
set(SOURCES
${webbrowser-common_SOURCE_DIR}/meminfo.cpp
tst_MemInfoTests.cpp
)
add_executable(${TEST} ${SOURCES})
include_directories(${webbrowser-common_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Test
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
set_tests_properties(${TEST} PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
./tests/unittests/history-model/ 0000755 0000156 0000165 00000000000 12703462032 017077 5 ustar jenkins jenkins ./tests/unittests/history-model/tst_HistoryModelTests.cpp 0000644 0000156 0000165 00000035133 12703462031 024146 0 ustar jenkins jenkins /*
* Copyright 2013-2016 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include
#include
#include
// local
#include "history-model.h"
class HistoryModelTests : public QObject
{
Q_OBJECT
private:
HistoryModel* model;
private Q_SLOTS:
void init()
{
model = new HistoryModel;
model->setDatabasePath(":memory:");
}
void cleanup()
{
delete model;
}
void shouldBeInitiallyEmpty()
{
QCOMPARE(model->rowCount(), 0);
}
void shouldNotAddEmptyUrl()
{
QCOMPARE(model->add(QUrl(), "empty URL", QUrl()), 0);
QCOMPARE(model->rowCount(), 0);
}
void shouldAddNewEntries()
{
QCOMPARE(model->add(QUrl("http://example.org/"), "Example Domain", QUrl()), 1);
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->add(QUrl("http://example.com/"), "Example Domain", QUrl()), 1);
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Url).toString(),
QString("http://example.com/"));
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Visits).toInt(), 1);
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Hidden).toBool(), false);
QCOMPARE(model->data(model->index(1, 0), HistoryModel::Url).toString(),
QString("http://example.org/"));
QCOMPARE(model->data(model->index(1, 0), HistoryModel::Visits).toInt(), 1);
QCOMPARE(model->data(model->index(1, 0), HistoryModel::Hidden).toBool(), false);
}
void shouldNotifyWhenAddingNewEntries()
{
QSignalSpy spy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
model->add(QUrl("http://example.org/"), "Example Domain", QUrl());
QCOMPARE(spy.count(), 1);
QList args = spy.takeFirst();
QCOMPARE(args.at(1).toInt(), 0);
QCOMPARE(args.at(2).toInt(), 0);
model->add(QUrl("http://example.com/"), "Example Domain", QUrl());
QCOMPARE(spy.count(), 1);
args = spy.takeFirst();
QCOMPARE(args.at(1).toInt(), 0);
QCOMPARE(args.at(2).toInt(), 0);
}
void shouldUpdateExistingEntry()
{
QCOMPARE(model->add(QUrl("http://example.org/"), "Example Domain", QUrl()), 1);
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->add(QUrl("http://example.org/"), "Example Domain", QUrl("image://webicon/123")), 2);
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Url).toString(),
QString("http://example.org/"));
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Visits).toInt(), 2);
}
void shouldNotifyWhenUpdatingExistingEntry()
{
QSignalSpy spyMoved(model, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)));
qRegisterMetaType >();
QSignalSpy spyChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
model->add(QUrl("http://example.org/"), "Example Domain", QUrl());
QCOMPARE(spyMoved.count(), 0);
QCOMPARE(spyChanged.count(), 0);
QTest::qWait(100);
model->add(QUrl("http://example.org/"), "Example Domain", QUrl("image://webicon/123"));
QCOMPARE(spyMoved.count(), 0);
QCOMPARE(spyChanged.count(), 1);
QList args = spyChanged.takeFirst();
QCOMPARE(args.at(0).toModelIndex().row(), 0);
QCOMPARE(args.at(1).toModelIndex().row(), 0);
QVector roles = args.at(2).value >();
QVERIFY(roles.size() >= 2);
QVERIFY(roles.contains(HistoryModel::Icon));
QVERIFY(roles.contains(HistoryModel::Visits));
model->add(QUrl("http://example.com/"), "Example Domain", QUrl());
QCOMPARE(spyMoved.count(), 0);
QCOMPARE(spyChanged.count(), 0);
model->add(QUrl("http://example.org/"), "Example D0ma1n", QUrl("image://webicon/456"));
QCOMPARE(spyMoved.count(), 1);
args = spyMoved.takeFirst();
QCOMPARE(args.at(1).toInt(), 1);
QCOMPARE(args.at(2).toInt(), 1);
QCOMPARE(args.at(4).toInt(), 0);
QCOMPARE(spyChanged.count(), 1);
args = spyChanged.takeFirst();
QCOMPARE(args.at(0).toModelIndex().row(), 0);
QCOMPARE(args.at(1).toModelIndex().row(), 0);
roles = args.at(2).value >();
QVERIFY(roles.size() >= 3);
QVERIFY(roles.contains(HistoryModel::Title));
QVERIFY(roles.contains(HistoryModel::Icon));
QVERIFY(roles.contains(HistoryModel::Visits));
}
void shouldUpdateAttributes()
{
qRegisterMetaType >();
QSignalSpy spyChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
QUrl url(QStringLiteral("http://example.org/"));
QCOMPARE(model->add(url, QStringLiteral(""), QUrl()), 1);
QVERIFY(!model->update(QUrl(), QStringLiteral(""), QUrl()));
QVERIFY(spyChanged.isEmpty());
QVERIFY(!model->update(QUrl(QStringLiteral("http://example.com/")), QStringLiteral(""), QUrl()));
QVERIFY(spyChanged.isEmpty());
QVERIFY(!model->update(url, QStringLiteral(""), QUrl()));
QVERIFY(spyChanged.isEmpty());
QVERIFY(model->update(url, QStringLiteral("title update"), QUrl()));
QCOMPARE(spyChanged.count(), 1);
QList args = spyChanged.takeFirst();
QCOMPARE(args.at(0).toModelIndex().row(), 0);
QCOMPARE(args.at(1).toModelIndex().row(), 0);
QVector roles = args.at(2).value >();
QCOMPARE(roles.size(), 1);
QVERIFY(roles.contains(HistoryModel::Title));
QCOMPARE(model->get(0)[QStringLiteral("visits")].toInt(), 1);
QVERIFY(model->update(url, QStringLiteral("title update"), QUrl(QStringLiteral("image://webicon/123"))));
QCOMPARE(spyChanged.count(), 1);
args = spyChanged.takeFirst();
QCOMPARE(args.at(0).toModelIndex().row(), 0);
QCOMPARE(args.at(1).toModelIndex().row(), 0);
roles = args.at(2).value >();
QCOMPARE(roles.size(), 1);
QVERIFY(roles.contains(HistoryModel::Icon));
QCOMPARE(model->get(0)[QStringLiteral("visits")].toInt(), 1);
QVERIFY(model->update(url, QStringLiteral("title update 2"), QUrl(QStringLiteral("image://webicon/1234"))));
QCOMPARE(spyChanged.count(), 1);
args = spyChanged.takeFirst();
QCOMPARE(args.at(0).toModelIndex().row(), 0);
QCOMPARE(args.at(1).toModelIndex().row(), 0);
roles = args.at(2).value >();
QCOMPARE(roles.size(), 2);
QVERIFY(roles.contains(HistoryModel::Title));
QVERIFY(roles.contains(HistoryModel::Icon));
QCOMPARE(model->get(0)[QStringLiteral("visits")].toInt(), 1);
}
void shouldNotifyWhenHidingOrUnHidingExistingEntry()
{
qRegisterMetaType >();
QSignalSpy spyChanged(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)));
model->add(QUrl("http://example.org/"), "Example Domain", QUrl());
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Hidden).toBool(), false);
model->hide(QUrl("http://example.org/"));
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Hidden).toBool(), true);
QCOMPARE(spyChanged.count(), 1);
QList args = spyChanged.takeFirst();
QVector roles = args.at(2).value >();
QVERIFY(roles.size() == 1);
QVERIFY(roles.contains(HistoryModel::Hidden));
model->unHide(QUrl("http://example.org/"));
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Hidden).toBool(), false);
QCOMPARE(spyChanged.count(), 1);
args = spyChanged.takeFirst();
roles = args.at(2).value >();
QVERIFY(roles.size() == 1);
QVERIFY(roles.contains(HistoryModel::Hidden));
}
void shouldUpdateTimestamp()
{
QDateTime now = QDateTime::currentDateTimeUtc();
QTest::qWait(1001);
model->add(QUrl("http://example.org/"), "Example Domain", QUrl());
QTest::qWait(1001);
model->add(QUrl("http://example.com/"), "Example Domain", QUrl());
QDateTime ts0 = model->data(model->index(0, 0), HistoryModel::LastVisit).toDateTime();
QDateTime ts1 = model->data(model->index(1, 0), HistoryModel::LastVisit).toDateTime();
QVERIFY(ts0 > ts1);
QVERIFY(ts1 > now);
}
void shouldReturnData()
{
QDateTime now = QDateTime::currentDateTimeUtc();
model->add(QUrl("http://example.org/"), "Example Domain", QUrl("image://webicon/123"));
QVERIFY(!model->data(QModelIndex(), HistoryModel::Url).isValid());
QVERIFY(!model->data(model->index(-1, 0), HistoryModel::Url).isValid());
QVERIFY(!model->data(model->index(3, 0), HistoryModel::Url).isValid());
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Url).toUrl(), QUrl("http://example.org/"));
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Domain).toString(), QString("example.org"));
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Title).toString(), QString("Example Domain"));
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Icon).toUrl(), QUrl("image://webicon/123"));
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Visits).toInt(), 1);
QVERIFY(model->data(model->index(0, 0), HistoryModel::LastVisit).toDateTime() >= now);
QVERIFY(!model->data(model->index(0, 0), HistoryModel::LastVisit + 4).isValid());
}
void shouldReturnDatabasePath()
{
QCOMPARE(model->databasePath(), QString(":memory:"));
}
void shouldNotifyWhenSettingDatabasePath()
{
QSignalSpy spyPath(model, SIGNAL(databasePathChanged()));
QSignalSpy spyReset(model, SIGNAL(modelReset()));
model->setDatabasePath(":memory:");
QVERIFY(spyPath.isEmpty());
QVERIFY(spyReset.isEmpty());
model->setDatabasePath("");
QCOMPARE(spyPath.count(), 1);
QCOMPARE(spyReset.count(), 1);
QCOMPARE(model->databasePath(), QString(":memory:"));
}
void shouldSerializeOnDisk()
{
QTemporaryFile tempFile;
tempFile.open();
QString fileName = tempFile.fileName();
delete model;
model = new HistoryModel;
model->setDatabasePath(fileName);
model->add(QUrl("http://example.org/"), "Example Domain", QUrl());
QTest::qWait(1001);
model->add(QUrl("http://example.com/"), "Example Domain", QUrl());
model->hide(QUrl("http://example.com/"));
delete model;
model = new HistoryModel;
model->setDatabasePath(fileName);
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Url).toUrl(), QUrl("http://example.com/"));
QCOMPARE(model->data(model->index(0, 0), HistoryModel::Hidden).toBool(), true);
QCOMPARE(model->data(model->index(1, 0), HistoryModel::Url).toUrl(), QUrl("http://example.org/"));
QCOMPARE(model->data(model->index(1, 0), HistoryModel::Hidden).toBool(), false);
}
void shouldClearAll()
{
QSignalSpy spyReset(model, SIGNAL(modelReset()));
model->add(QUrl("http://example.org/"), "Example Domain", QUrl());
model->add(QUrl("http://example.com/"), "Example Domain", QUrl());
QCOMPARE(model->rowCount(), 2);
QVERIFY(spyReset.isEmpty());
model->clearAll();
QCOMPARE(spyReset.count(), 1);
QCOMPARE(model->rowCount(), 0);
model->clearAll();
QCOMPARE(spyReset.count(), 1);
}
void shouldRemoveByUrl()
{
QCOMPARE(model->add(QUrl("http://example.org/"), "Example Domain", QUrl()), 1);
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->add(QUrl("http://example.com/"), "Example Domain", QUrl()), 1);
QCOMPARE(model->rowCount(), 2);
model->removeEntryByUrl(QUrl("http://example.org/"));
QCOMPARE(model->rowCount(), 1);
model->removeEntryByUrl(QUrl("http://example.com/"));
QCOMPARE(model->rowCount(), 0);
}
void shouldRemoveByDate()
{
QCOMPARE(model->add(QUrl("http://example.org/"), "Example Domain", QUrl()), 1);
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->add(QUrl("http://example.com/"), "Example Domain", QUrl()), 1);
QCOMPARE(model->rowCount(), 2);
model->removeEntriesByDate(QDate::currentDate());
QCOMPARE(model->rowCount(), 0);
}
void shouldRemoveByDomain()
{
QCOMPARE(model->add(QUrl("http://example.org/page1"), "Example Domain Page 1", QUrl()), 1);
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->add(QUrl("http://example.org/page2"), "Example Domain Page 2", QUrl()), 1);
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->add(QUrl("http://example.com/page1"), "Example Domain Page 1", QUrl()), 1);
QCOMPARE(model->rowCount(), 3);
QCOMPARE(model->add(QUrl("http://example.com/page2"), "Example Domain Page 2", QUrl()), 1);
QCOMPARE(model->rowCount(), 4);
model->removeEntriesByDomain("example.org");
QCOMPARE(model->rowCount(), 2);
model->removeEntriesByDomain("example.com");
QCOMPARE(model->rowCount(), 0);
}
void shouldCountNumberOfEntries()
{
QSignalSpy spyCount(model, SIGNAL(rowCountChanged()));
QCOMPARE(model->property("count").toInt(), 0);
model->add(QUrl("http://example.org/"), "Example Domain", QUrl());
QCOMPARE(model->property("count").toInt(), 1);
QCOMPARE(spyCount.count(), 1);
model->add(QUrl("http://example.com/"), "Example Domain", QUrl());
QCOMPARE(model->property("count").toInt(), 2);
QCOMPARE(spyCount.count(), 2);
model->clearAll();
QCOMPARE(model->property("count").toInt(), 0);
QCOMPARE(spyCount.count(), 3);
}
};
QTEST_MAIN(HistoryModelTests)
#include "tst_HistoryModelTests.moc"
./tests/unittests/history-model/CMakeLists.txt 0000644 0000156 0000165 00000000622 12703462031 021636 0 ustar jenkins jenkins find_package(Qt5Core REQUIRED)
find_package(Qt5Sql REQUIRED)
find_package(Qt5Test REQUIRED)
set(TEST tst_HistoryModelTests)
add_executable(${TEST} tst_HistoryModelTests.cpp)
include_directories(${webbrowser-app_SOURCE_DIR})
target_link_libraries(${TEST}
Qt5::Core
Qt5::Sql
Qt5::Test
webbrowser-app-models
)
add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml)
./tests/unittests/CMakeLists.txt 0000644 0000156 0000165 00000001647 12703462031 017047 0 ustar jenkins jenkins add_subdirectory(sanity)
add_subdirectory(qml)
add_subdirectory(domain-utils)
add_subdirectory(history-model)
add_subdirectory(history-domain-model)
add_subdirectory(history-domainlist-model)
add_subdirectory(history-lastvisitdatelist-model)
add_subdirectory(session-utils)
add_subdirectory(tabs-model)
add_subdirectory(bookmarks-model)
add_subdirectory(bookmarks-folder-model)
add_subdirectory(bookmarks-folderlist-model)
add_subdirectory(limit-proxy-model)
add_subdirectory(container-url-patterns)
add_subdirectory(cookie-store)
add_subdirectory(oxide-cookie-helper)
add_subdirectory(session-storage)
add_subdirectory(favicon-fetcher)
add_subdirectory(webapp-container-hook)
add_subdirectory(intent-filter)
add_subdirectory(search-engine)
add_subdirectory(text-search-filter-model)
add_subdirectory(downloads-model)
add_subdirectory(single-instance-manager)
add_subdirectory(meminfo)
add_subdirectory(webapp-container-color-helper)
./tests/unittests/history-lastvisitdatelist-model/ 0000755 0000156 0000165 00000000000 12703462032 022651 5 ustar jenkins jenkins ./tests/unittests/history-lastvisitdatelist-model/tst_HistoryLastVisitDateListModelTests.cpp 0000644 0000156 0000165 00000035114 12703462031 033214 0 ustar jenkins jenkins /*
* Copyright 2015 Canonical Ltd.
*
* This file is part of webbrowser-app.
*
* webbrowser-app is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* webbrowser-app 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
// Qt
#include
#include