summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/test/data/database/database_tester.html161
-rw-r--r--chrome/test/functional/databases.py253
-rw-r--r--chrome/test/functional/notifications.py42
-rw-r--r--chrome/test/pyautolib/pyauto.py34
-rw-r--r--chrome/test/pyautolib/pyautolib.cc6
-rw-r--r--chrome/test/pyautolib/pyautolib.h4
-rw-r--r--chrome/test/pyautolib/pyautolib.i5
7 files changed, 471 insertions, 34 deletions
diff --git a/chrome/test/data/database/database_tester.html b/chrome/test/data/database/database_tester.html
new file mode 100644
index 0000000..e5bbe56
--- /dev/null
+++ b/chrome/test/data/database/database_tester.html
@@ -0,0 +1,161 @@
+<html>
+<!--database_tester.html
+Script with javascript functions for simple database operations. This is used in
+pyauto tests.
+-->
+<script>
+
+// Open a Web SQL database.
+var g_db = null;
+if (typeof window.openDatabase == "undefined") {
+ document.write("Error: Web SQL databases are not supported.");
+}
+try {
+ g_db = openDatabase("test", "1.0", "test database", 1024 * 1024);
+} catch(err) {
+ document.write("Error: cannot open database.");
+}
+
+// Creates a table named "table1" with one text column named "data".
+function createTable() {
+ if (!g_db) {
+ sendErrorToTest("database is not open");
+ }
+ g_db.transaction(function(tx) {
+ tx.executeSql(
+ "CREATE TABLE table1 (data TEXT)",
+ [],
+ function(tx, result) {
+ sendValueToTest("created");
+ },
+ function(tx, error) {
+ sendErrorToTest("cannot create table: " + error);
+ });
+ });
+}
+
+// Inserts a record into the database.
+function insertRecord(text) {
+ g_db.transaction(function(tx) {
+ tx.executeSql(
+ "INSERT INTO table1 VALUES (?)",
+ [text],
+ function(tx, result) {
+ sendValueToTest("inserted");
+ },
+ function(tx, error) {
+ sendErrorToTest("insert error: " + error);
+ });
+ });
+}
+
+// Updates a record at the given index with the given text. The indices are
+// 0-based and are ordered from oldest record, to newest record.
+function updateRecord(index, text) {
+ findId(index, function(rowId) {
+ g_db.transaction(function(tx) {
+ tx.executeSql(
+ "UPDATE table1 SET data=? WHERE ROWID=?",
+ [text, rowId],
+ function(tx, result) {
+ if (result.rowsAffected == 1)
+ sendValueToTest("updated");
+ else if (result.rowsAffected == 0)
+ sendErrorToTest("could not update index: " + index);
+ else
+ sendErrorToTest("multiple rows with index: " + index);
+ },
+ function(tx, error) {
+ sendErrorToTest("update error: " + error);
+ });
+ });
+ });
+}
+
+// Deletes a record at the given index.
+function deleteRecord(index) {
+ findId(index, function (rowId) {
+ g_db.transaction(function(tx) {
+ tx.executeSql(
+ "DELETE FROM table1 WHERE ROWID=?",
+ [rowId],
+ function(tx, result) {
+ sendValueToTest("deleted");
+ },
+ function(tx, error) {
+ sendErrorToTest("delete error: " + error);
+ });
+ });
+ });
+}
+
+// Gets all the records in the database, ordered by their age.
+function getRecords() {
+ g_db.readTransaction(function(tx) {
+ tx.executeSql(
+ "SELECT data FROM table1 ORDER BY ROWID",
+ [],
+ function(tx, result) {
+ items = [];
+ for (var i = 0; i < result.rows.length; i++) {
+ items.push(result.rows.item(i).data);
+ }
+ sendValueToTest(items);
+ },
+ function(tx, error) {
+ sendErrorToTest("getRecords error: " + error);
+ });
+ });
+}
+
+// Helper function that finds the ID for a record based on a given index.
+function findId(index, callback) {
+ g_db.readTransaction(function(tx) {
+ // |ROWID| is a special sqlite column. It is unique and is incremented
+ // automatically when a new record is created.
+ // |LIMIT| is a nonstandard clause supported by sqlite that lets us pick
+ // rows from the database by index. E.g., LIMIT 2,10 will give us 10 records
+ // starting at offset 2.
+ tx.executeSql(
+ "SELECT ROWID AS id FROM table1 ORDER BY ROWID LIMIT ?,1",
+ [index],
+ function(tx, result) {
+ if (result.rows.length >= 1)
+ callback(result.rows.item(0).id);
+ else
+ sendErrorToTest("could not find row with index: " + index);
+ },
+ function(tx, error) {
+ sendErrorToTest("findId error: " + error);
+ });
+ });
+}
+
+// Helper function that sends a message back to the test, which contains a value
+// corresponding to the logical return value of the function, and a boolean
+// indicating success.
+function sendValueToTest(value) {
+ sendHelper(true, "", value);
+}
+
+// Helper function that sends a message back to the test, which contains an
+// error message and a boolean indicating failure.
+function sendErrorToTest(errorMsg) {
+ sendHelper(false, errorMsg, 0);
+}
+
+function sendHelper(success, errorMsg, returnValue) {
+ var result = {
+ "succeeded": success,
+ "errorMsg": errorMsg,
+ "returnValue": returnValue
+ };
+ window.domAutomationController.send(JSON.stringify(result));
+}
+
+</script>
+
+<body>
+This page is used for testing Web SQL databases.
+</body>
+</html>
diff --git a/chrome/test/functional/databases.py b/chrome/test/functional/databases.py
new file mode 100644
index 0000000..7e15d14
--- /dev/null
+++ b/chrome/test/functional/databases.py
@@ -0,0 +1,253 @@
+#!/usr/bin/python
+# Copyright (c) 2010 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import simplejson
+import os
+
+import pyauto_functional
+import pyauto
+
+
+class SQLExecutionError(RuntimeError):
+ """Represents an error that occurs while executing an SQL statement."""
+ pass
+
+
+class DatabasesTest(pyauto.PyUITest):
+ """Test of Web SQL Databases."""
+
+ def __init__(self, methodName='runTest'):
+ super(DatabasesTest, self).__init__(methodName)
+ # HTML page used for database testing.
+ self.TEST_PAGE_URL = self.GetFileURLForDataPath(
+ os.path.join('database', 'database_tester.html'))
+
+ def _ParseAndCheckResultFromTestPage(self, json):
+ """Helper function that parses the message sent from |TEST_PAGE_URL| and
+ checks that it succeeded.
+
+ Args:
+ json: the message, encoded in JSON, that the test page sent to us
+
+ Returns:
+ dictionary representing the result from the test page, with format:
+ {
+ 'succeeded': boolean
+ 'errorMsg': optional string
+ 'returnValue': optional any type
+ }
+
+ Raises:
+ SQLExecutionError if the message contains an error message
+ """
+ result_dict = simplejson.loads(json)
+ if result_dict['succeeded'] == False:
+ raise SQLExecutionError(result_dict['errorMsg'])
+ return result_dict
+
+ def _CreateTable(self, tab_index=0, windex=0):
+ """Creates a table in the database.
+
+ This should only be called once per database. This should be called before
+ attempting to insert, update, delete, or get the records in the database.
+
+ Defaults to first tab in first window.
+
+ Args:
+ tab_index: index of the tab that will create the database
+ windex: index of the window containing the tab that will create the
+ database
+ """
+ json = self.CallJavascriptFunc('createTable', [], tab_index, windex)
+ self._ParseAndCheckResultFromTestPage(json)
+
+ def _InsertRecord(self, record, tab_index=0, windex=0):
+ """Inserts a record, i.e., a row, into the database.
+
+ Defaults to first tab in first window.
+
+ Args:
+ record: string that will be added as a new row in the database
+ tab_index: index of the tab that will insert the record
+ windex: index of the window containing the tab that will insert the record
+ """
+ json = self.CallJavascriptFunc('insertRecord', [record], tab_index, windex)
+ self._ParseAndCheckResultFromTestPage(json)
+
+ def _UpdateRecord(self, index, updated_record, tab_index=0, windex=0):
+ """Updates a record, i.e., a row, in the database.
+
+ Defaults to first tab in first window.
+
+ Args:
+ index: index of the record to update. Index 0 refers to the oldest item in
+ the database
+ updated_record: string that will be used to update the row in the database
+ tab_index: index of the tab that will update the record
+ windex: index of the window containing the tab that will update the record
+ """
+ json = self.CallJavascriptFunc(
+ 'updateRecord', [index, updated_record], tab_index, windex)
+ self._ParseAndCheckResultFromTestPage(json)
+
+ def _DeleteRecord(self, index, tab_index=0, windex=0):
+ """Deletes a record, i.e., a row, from the database.
+
+ Defaults to first tab in first window.
+
+ Args:
+ index: index of the record to be deleted. Index 0 refers to the oldest
+ item in the database
+ tab_index: index of the tab that will delete the record
+ windex: index of the window containing the tab that will delete the record
+ """
+ json = self.CallJavascriptFunc('deleteRecord', [index], tab_index, windex)
+ self._ParseAndCheckResultFromTestPage(json)
+
+ def _GetRecords(self, tab_index=0, windex=0):
+ """Returns all the records, i.e., rows, in the database.
+
+ The records are ordererd from oldest to newest.
+
+ Defaults to first tab in first window.
+
+ Returns:
+ array of all the records in the database
+
+ Args:
+ tab_index: index of the tab that will query the database
+ windex: index of the window containing the tab that will query the
+ database
+ """
+ json = self.CallJavascriptFunc('getRecords', [], tab_index, windex)
+ return self._ParseAndCheckResultFromTestPage(json)['returnValue']
+
+ def testInsertRecord(self):
+ """Insert records to the database."""
+ self.NavigateToURL(self.TEST_PAGE_URL)
+ self._CreateTable()
+ self._InsertRecord('text')
+ self.assertEquals(['text'], self._GetRecords())
+ self._InsertRecord('text2')
+ self.assertEquals(['text', 'text2'], self._GetRecords())
+
+ def testUpdateRecord(self):
+ """Update records in the database."""
+ self.NavigateToURL(self.TEST_PAGE_URL)
+ self._CreateTable()
+
+ # Update the first record.
+ self._InsertRecord('text')
+ self._UpdateRecord(0, '0')
+ records = self._GetRecords()
+ self.assertEquals(1, len(records))
+ self.assertEquals('0', records[0])
+
+ # Update the middle record.
+ self._InsertRecord('1')
+ self._InsertRecord('2')
+ self._UpdateRecord(1, '1000')
+ self.assertEquals(['0', '1000', '2'], self._GetRecords())
+
+ def testDeleteRecord(self):
+ """Delete records in the database."""
+ self.NavigateToURL(self.TEST_PAGE_URL)
+ self._CreateTable()
+
+ # Delete the first and only record.
+ self._InsertRecord('text')
+ self._DeleteRecord(0)
+ self.assertFalse(self._GetRecords())
+
+ # Delete the middle record.
+ self._InsertRecord('0')
+ self._InsertRecord('1')
+ self._InsertRecord('2')
+ self._DeleteRecord(1)
+ self.assertEquals(['0', '2'], self._GetRecords())
+
+ def testDeleteNonexistentRow(self):
+ """Attempts to delete a nonexistent row in the table."""
+ self.NavigateToURL(self.TEST_PAGE_URL)
+ self._CreateTable()
+ self._InsertRecord('text')
+ did_throw_exception = False
+ try:
+ self._DeleteRecord(1)
+ except:
+ did_throw_exception = True
+ self.assertTrue(did_throw_exception)
+ self.assertEquals(['text'], self._GetRecords())
+
+ def testDatabaseOperations(self):
+ """Insert, update, and delete records in the database."""
+ self.NavigateToURL(self.TEST_PAGE_URL)
+ self._CreateTable()
+
+ for i in range(10):
+ self._InsertRecord(str(i))
+ records = self._GetRecords()
+ self.assertEqual([str(i) for i in range(10)], records)
+
+ for i in range(10):
+ self._UpdateRecord(i, str(i * i))
+ records = self._GetRecords()
+ self.assertEqual([str(i * i) for i in range(10)], records)
+
+ for i in range(10):
+ self._DeleteRecord(0)
+ self.assertEqual(0, len(self._GetRecords()))
+
+ def testReloadActiveTab(self):
+ """Create records in the database and verify they persist after reload."""
+ self.NavigateToURL(self.TEST_PAGE_URL)
+ self._CreateTable()
+ self._InsertRecord('text')
+ self.ReloadActiveTab()
+ self.assertEquals(['text'], self._GetRecords())
+
+ def testIncognitoCannotReadRegularDatabase(self):
+ """Attempt to read a database created in a regular browser from an incognito
+ browser.
+ """
+ self.NavigateToURL(self.TEST_PAGE_URL)
+ self._CreateTable()
+ self._InsertRecord('text')
+ self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW)
+ self.NavigateToURL(self.TEST_PAGE_URL, 1, 0)
+ can_read_regular_database = False
+ try:
+ # |_GetRecords| should throw an error because the table does not exist.
+ if len(self._GetRecords(windex=1)) == 1:
+ can_read_regular_database = True
+ except SQLExecutionError:
+ pass
+ self.assertFalse(can_read_regular_database)
+ self._CreateTable(windex=1)
+ self.assertEqual(0, len(self._GetRecords(windex=1)))
+
+ def testRegularCannotReadIncognitoDatabase(self):
+ """Attempt to read a database created in an incognito browser from a regular
+ browser.
+ """
+ self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW)
+ self.NavigateToURL(self.TEST_PAGE_URL, 1, 0)
+ self._CreateTable(windex=1)
+ self._InsertRecord('text', windex=1)
+
+ # Verify a regular browser cannot read the incognito database.
+ self.NavigateToURL(self.TEST_PAGE_URL)
+ can_read_incognito_database = False
+ try:
+ # |_GetRecords| should throw an error because the table does not exist.
+ if len(self._GetRecords()) == 1:
+ can_read_incognito_database = True
+ except SQLExecutionError:
+ pass
+ self.assertFalse(can_read_incognito_database)
+
+
+if __name__ == '__main__':
+ pyauto_functional.Main()
diff --git a/chrome/test/functional/notifications.py b/chrome/test/functional/notifications.py
index 9d19ba0..d6a1b9d 100644
--- a/chrome/test/functional/notifications.py
+++ b/chrome/test/functional/notifications.py
@@ -3,6 +3,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import os
import urllib
import pyauto_functional
@@ -20,8 +21,8 @@ class NotificationsTest(pyauto.PyUITest):
self.ASK_SETTING = 3
# HTML page used for notification testing.
- self.TEST_PAGE_URL = (
- self.GetFileURLForDataPath('notifications/notification_tester.html'))
+ self.TEST_PAGE_URL = self.GetFileURLForDataPath(
+ os.path.join('notifications', 'notification_tester.html'))
def Debug(self):
"""Test method for experimentation.
@@ -132,33 +133,6 @@ class NotificationsTest(pyauto.PyUITest):
self.assertEqual('Allow', infobar['buttons'][0])
self.assertEqual('Deny', infobar['buttons'][1])
- def _CallJavascriptFunc(self, function, args=[], tab_index=0, windex=0):
- """Helper function to execute a script that calls a given function.
-
- Defaults to first tab in first window.
-
- Args:
- function: name of the function
- args: list of all the arguments to pass into the called function. These
- should be able to be converted to a string using the |str| function.
- tab_index: index of the tab within the given window
- windex: index of the window
- """
- # Convert the given arguments for evaluation in a javascript statement.
- converted_args = []
- for arg in args:
- # If it is a string argument, we need to quote and escape it properly.
- if type(arg) == type('string') or type(arg) == type(u'unicode'):
- # We must convert all " in the string to \", so that we don't try
- # to evaluate invalid javascript like ""arg"".
- converted_arg = '"' + arg.replace('"', '\\"') + '"'
- else:
- # Convert it to a string so that we can use |join| later.
- converted_arg = str(arg)
- converted_args += [converted_arg]
- js = '%s(%s)' % (function, ', '.join(converted_args))
- return self.ExecuteJavascript(js, windex, tab_index)
-
def _CreateSimpleNotification(self, img_url, title, text,
replace_id='', tab_index=0, windex=0):
"""Creates a simple notification.
@@ -179,7 +153,7 @@ class NotificationsTest(pyauto.PyUITest):
tab_index: index of the tab within the given window
windex: index of the window
"""
- return self._CallJavascriptFunc('createNotification',
+ return self.CallJavascriptFunc('createNotification',
[img_url, title, text, replace_id],
tab_index,
windex);
@@ -202,7 +176,7 @@ class NotificationsTest(pyauto.PyUITest):
tab_index: index of the tab within the given window
windex: index of the window
"""
- return self._CallJavascriptFunc('createHTMLNotification',
+ return self.CallJavascriptFunc('createHTMLNotification',
[content_url, replace_id],
tab_index,
windex)
@@ -216,7 +190,7 @@ class NotificationsTest(pyauto.PyUITest):
tab_index: index of the tab within the given window
windex: index of the window
"""
- self._CallJavascriptFunc('requestPermission', [], windex, tab_index)
+ self.CallJavascriptFunc('requestPermission', [], windex, tab_index)
def _CancelNotification(self, notification_id, tab_index=0, windex=0):
"""Cancels a notification with the given id.
@@ -236,7 +210,7 @@ class NotificationsTest(pyauto.PyUITest):
notification
windex: index of the window
"""
- msg = self._CallJavascriptFunc(
+ msg = self.CallJavascriptFunc(
'cancelNotification', [notification_id], tab_index, windex)
# '1' signifies success.
self.assertEquals('1', msg)
@@ -293,7 +267,7 @@ class NotificationsTest(pyauto.PyUITest):
def testAllowOnPermissionInfobar(self):
"""Tries to create a notification and clicks allow on the infobar."""
self.NavigateToURL(self.TEST_PAGE_URL)
- # This notification should not be shown because we don't have permission.
+ # This notification should not be shown because we do not have permission.
self._CreateHTMLNotification(self.NO_SUCH_URL)
self.assertFalse(self.GetActiveNotifications())
diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py
index 60b4728..a152c7e 100644
--- a/chrome/test/pyautolib/pyauto.py
+++ b/chrome/test/pyautolib/pyauto.py
@@ -1596,6 +1596,40 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase):
}
return self._GetResultFromJSONRequest(cmd_dict, windex=windex)
+ def CallJavascriptFunc(self, function, args=[], tab_index=0, windex=0):
+ """Executes a script which calls a given javascript function.
+
+ The invoked javascript function must send a result back via the
+ domAutomationController.send function, or this function will never return.
+
+ Defaults to first tab in first window.
+
+ Args:
+ function: name of the function
+ args: list of all the arguments to pass into the called function. These
+ should be able to be converted to a string using the |str| function.
+ tab_index: index of the tab within the given window
+ windex: index of the window
+
+ Returns:
+ a string that was sent back via the domAutomationController.send method
+ """
+ # Convert the given arguments for evaluation in a javascript statement.
+ converted_args = []
+ for arg in args:
+ # If it is a string argument, we need to quote and escape it properly.
+ if type(arg) == type('string') or type(arg) == type(u'unicode'):
+ # We must convert all " in the string to \", so that we don't try
+ # to evaluate invalid javascript like ""arg"".
+ converted_arg = '"' + arg.replace('"', '\\"') + '"'
+ else:
+ # Convert it to a string so that we can use |join| later.
+ converted_arg = str(arg)
+ converted_args += [converted_arg]
+ js = '%s(%s)' % (function, ', '.join(converted_args))
+ logging.debug('Executing javascript: ', js)
+ return self.ExecuteJavascript(js, windex, tab_index)
+
class PyUITestSuite(pyautolib.PyUITestSuiteBase, unittest.TestSuite):
"""Base TestSuite for PyAuto UI tests."""
diff --git a/chrome/test/pyautolib/pyautolib.cc b/chrome/test/pyautolib/pyautolib.cc
index e1007df..6d35548 100644
--- a/chrome/test/pyautolib/pyautolib.cc
+++ b/chrome/test/pyautolib/pyautolib.cc
@@ -80,6 +80,12 @@ void PyUITestBase::NavigateToURL(
UITestBase::NavigateToURL(url, window_index, tab_index);
}
+void PyUITestBase::ReloadActiveTab(int window_index) {
+ scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
+ ASSERT_TRUE(tab_proxy.get());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab_proxy->Reload());
+}
+
bool PyUITestBase::AppendTab(const GURL& tab_url, int window_index) {
scoped_refptr<BrowserProxy> browser_proxy =
automation()->GetBrowserWindow(window_index);
diff --git a/chrome/test/pyautolib/pyautolib.h b/chrome/test/pyautolib/pyautolib.h
index c40b97e..00f1c28 100644
--- a/chrome/test/pyautolib/pyautolib.h
+++ b/chrome/test/pyautolib/pyautolib.h
@@ -58,6 +58,10 @@ class PyUITestBase : public UITestBase {
// Blocks until page loaded.
void NavigateToURL(const char* url_string, int window_index, int tab_index);
+ // Reloads the active tab in the given window.
+ // Blocks until page reloaded.
+ void ReloadActiveTab(int window_index = 0);
+
// Get the URL of the active tab.
GURL GetActiveTabURL(int window_index = 0);
diff --git a/chrome/test/pyautolib/pyautolib.i b/chrome/test/pyautolib/pyautolib.i
index 875d530..1a46ee0 100644
--- a/chrome/test/pyautolib/pyautolib.i
+++ b/chrome/test/pyautolib/pyautolib.i
@@ -221,6 +221,11 @@ class PyUITestBase {
void NavigateToURL(const char* url_string);
void NavigateToURL(const char* url_string, int window_index, int tab_index);
+ %feature("docstring", "Reload the active tab in the given window (or first "
+ "window if index not given). Blocks until page has reloaded.")
+ ReloadActiveTab;
+ void ReloadActiveTab(int window_index = 0);
+
// BrowserProxy methods
%feature("docstring", "Apply the accelerator with given id "
"(IDC_BACK, IDC_NEWTAB ...) to the given or first window. "