summaryrefslogtreecommitdiffstats
path: root/chrome/test
diff options
context:
space:
mode:
authornirnimesh@chromium.org <nirnimesh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-09 05:55:53 +0000
committernirnimesh@chromium.org <nirnimesh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-09 05:55:53 +0000
commit40aa07af9babb4073a6e59c05d40b00561ccb091 (patch)
tree1b68d25d493cc2864266930a8b4b1e4de3eda262 /chrome/test
parentbfdf36ec3bd8f644b70e9e60c597b2bf087e5970 (diff)
downloadchromium_src-40aa07af9babb4073a6e59c05d40b00561ccb091.zip
chromium_src-40aa07af9babb4073a6e59c05d40b00561ccb091.tar.gz
chromium_src-40aa07af9babb4073a6e59c05d40b00561ccb091.tar.bz2
Exploratory Tool for long running and adhoc testing.
Sample Test Model Omnibox Test Model BUG=None TEST=None Review URL: http://codereview.chromium.org/4089005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@65503 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test')
-rw-r--r--chrome/test/functional/autotour.py212
-rw-r--r--chrome/test/functional/exploratory.py66
-rw-r--r--chrome/test/functional/omniboxmodel.py135
3 files changed, 413 insertions, 0 deletions
diff --git a/chrome/test/functional/autotour.py b/chrome/test/functional/autotour.py
new file mode 100644
index 0000000..7947550
--- /dev/null
+++ b/chrome/test/functional/autotour.py
@@ -0,0 +1,212 @@
+#!/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 logging
+import random
+import time
+
+
+"""Autotour is a semi automatic exploratory framework for exploring actions
+defined on an object. It uses decorators to mark methods as actions and an
+explorer object to explore.
+"""
+
+def GodelAction(weight=1, requires=''):
+ """Action Decorator
+
+ This function is the key to exploration. In effect, it annotates the wrapping
+ function with attributes such as is_action = True and sets some weights and
+ the requires condition. The explorer can invoke functions based on these
+ attributes.
+
+ Args:
+ weight: Weight for the action, default is set to 1. Can be any number >= 0
+ requires: Precondition for an action to be executed. This usually points
+ to a function which returns a boolean result.
+ """
+ def custom(target):
+ """This is the decorator which sets the attributes for
+ is_action, weight and requires.
+
+ Args:
+ target: Function to be decorated.
+
+ Returns:
+ The wrapped function with correct attributes set.
+ """
+ def wrapper(self, *args, **kwargs):
+ target(self, *args, **kwargs)
+ wrapper.is_action = True
+ wrapper.weight = weight
+ wrapper.requires = requires
+ return wrapper
+ return custom
+
+
+class Godel(object):
+ """Base class for all exploratory objects.
+
+ All objects that wish to be explored must inherit this class.
+ It provides an important method GetActions, which looks at all the functions
+ and returns only those that have the is_action attribute set.
+ """
+
+ def Initialize(self, uniqueid):
+ self._uniqueid = uniqueid
+
+ def GetName(self):
+ return type(self).__name__ + str(self._uniqueid)
+
+ def GetActions(self):
+ """Gets all the actions for this class."""
+ return [method for method in dir(self)
+ if hasattr(getattr(self, method), 'is_action')]
+
+ def GetWeight(self, method):
+ """Returns the weight of a given method.
+
+ Args:
+ method: Name of the Method whose Weight is queried
+ """
+ method_obj = getattr(self, method)
+ return getattr(method_obj, 'weight', 1)
+
+ def SetWeight(self, method, weight):
+ """Sets the weight for a given method."""
+ method_obj = getattr(self, method)
+ method_obj.im_func.weight = weight
+
+
+class Explorer(object):
+ """Explorer class that controls the exploration of the object.
+
+ This class has methods to add the exploration object and
+ initiate exploration on them.
+ """
+
+ def __init__(self):
+ self._seed = time.time()
+ logging.info('#Seeded with %s' % self._seed)
+ random.seed(self._seed)
+ self._actionlimit = -1
+ self._godels = []
+ self._fh = logging.FileHandler(str(self._seed))
+ self._log = logging.getLogger()
+ self._log.addHandler(self._fh)
+ self._log.setLevel(logging.DEBUG)
+ self._uniqueid = 0
+
+ def NextId(self):
+ """Gets the NextId by incrementing a counter."""
+ self._uniqueid = self._uniqueid + 1
+ return self._uniqueid
+
+ def Add(self, obj):
+ """Adds an object which inherits from Godel to be explored.
+
+ Args:
+ obj: Object to be explored which usually inherits from the Godel class.
+ """
+ uniqueid = self.NextId()
+ obj.Initialize(uniqueid)
+ name = type(obj).__name__
+ self._log.info('%s = %s()' % (name + str(uniqueid), name))
+ self._godels.append(obj)
+
+ def MeetsRequirement(self, godel, methodname):
+ """Method that returns true if the method's precondition is satisfied.
+ It does so by using the attribute "Requires" which is set by the decorator
+ and invokes it which must return a boolean value
+ Args:
+ godel: Godel object on which the requirement needs to be tested.
+ methodname: Method name which needs to be called to test.
+ Returns:
+ True if the methodname invoked returned True or methodname was empty,
+ False otherwise
+ """
+ method = getattr(godel, methodname)
+ requires = method.im_func.requires
+ if callable(requires):
+ return requires(godel)
+ else:
+ if len(requires) > 0:
+ precondition = getattr(godel, requires)
+ return precondition()
+ else:
+ return True
+
+ def GetAvailableActions(self):
+ """Returns a list of only those actions that satisfy their preconditions"""
+ action_list = []
+ for godel in self._godels:
+ for action in godel.GetActions():
+ if self.MeetsRequirement(godel, action):
+ action_list.append([godel, action, godel.GetWeight(action)])
+
+ return action_list
+
+ def Choose(self, action_list):
+ """Choosing function which allows to choose a method based on random
+ but weighted scale. So if one method has twice the weight, it is twice as
+ likely to be choosen than the other.
+
+ Args:
+ action_list: A list of Actions from which to choose.
+
+ Returns:
+ Chosen Action or None.
+ """
+ total = sum([action_info[2] for action_info in action_list])
+ # Find a pivot value randomly from he total weight.
+ index = random.randint(0, total)
+ for action_info in action_list:
+ # Decrease the total weight by the current action weight.
+ total = total - action_info[2]
+ # If total has fallen below the pivot, then we select the current action
+ if total <= index:
+ return action_info;
+ return None
+
+ def Execute(self, action_info):
+ """Executes the action and logs to console the action taken.
+
+ Args:
+ action_info: Action Info for the action to execute.
+ action_info[0] is the object on which the action is to be invoked.
+ action_info[1] is the name of the method which is to be invoked.
+ action_info[2] is the weight of the method.
+
+ """
+ action = getattr(action_info[0], action_info[1])
+ self._log.info('%s.%s()' % (action_info[0].GetName(), action_info[1]))
+ action()
+
+ def Explore(self, function=None):
+ """Sets the exploration in progress by repeatedly seeing if
+ any actions are available and if so continues to call them. It times out
+ after specified action limit.
+
+ Args:
+ function: A function which can be called to determine if the execution
+ should continue. This function is invoked after each step and
+ if it returns True, execution stops. This is useful in writing
+ tests which explore until a particular condition is met.
+
+ Returns:
+ True, if given |function| returns True, OR if no more action could be
+ chosen. False, otherwise.
+ """
+ count = 0
+ while(True):
+ if self._actionlimit > 0 and count > self._actionlimit:
+ return False
+ action_list = self.GetAvailableActions()
+ action_info = self.Choose(action_list)
+ if action_info is None:
+ return function is None
+ self.Execute(action_info)
+ count = count + 1
+ if function is not None and function():
+ return True \ No newline at end of file
diff --git a/chrome/test/functional/exploratory.py b/chrome/test/functional/exploratory.py
new file mode 100644
index 0000000..2228166
--- /dev/null
+++ b/chrome/test/functional/exploratory.py
@@ -0,0 +1,66 @@
+#!/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 logging
+
+import pyauto_functional
+from pyauto import PyUITest
+from pyauto import GURL
+
+import autotour
+
+
+class ChromeTest(autotour.Godel, PyUITest):
+ """Test Example class which explores OpenTab, CloseTab and Navigate
+ actions and calls them randomly using the Exploratory framework.
+ Each method defines a weight and a precondition to test
+ using the GodelAction decorator before it can be called. The exploratory
+ framework uses these annotation and automatically calls different actions
+ and drives browser testing.
+ """
+ def __init__(self, methodName='runTest', **kwargs):
+ PyUITest.__init__(self, methodName=methodName, **kwargs)
+ self._tab_count = 0
+
+ def CanOpenTab(self):
+ """Pre condition for opening a tab."""
+ return self._tab_count < 5
+
+ def CanCloseTab(self):
+ """Pre condition for closing a tab."""
+ return self._tab_count > 1
+
+ @autotour.GodelAction(10, CanOpenTab)
+ def OpenTab(self):
+ """Opens a new tab and goes to Google.com in the first window."""
+ logging.info('In Open Tab')
+ self._tab_count = self._tab_count + 1
+ self.AppendTab(GURL('http://www.google.com'))
+
+ @autotour.GodelAction(10, CanCloseTab)
+ def CloseTab(self):
+ """Closes the first tab in the first window."""
+ logging.info('In Close Tab')
+ self._tab_count = self._tab_count - 1
+ self.GetBrowserWindow(0).GetTab(0).Close(True)
+
+ @autotour.GodelAction(10, CanCloseTab)
+ def Navigate(self):
+ """Navigates to yahoo.com in the current window."""
+ logging.info("In Navigate")
+ self.NavigateToURL('http://www.yahoo.com')
+
+ def testExplore(self):
+ e = autotour.Explorer()
+ logging.info('Explorer created')
+ e.Add(self)
+ logging.info('Object added')
+ e.Explore()
+ logging.info('Done')
+
+
+if __name__ == '__main__':
+ pyauto_functional.Main()
+
diff --git a/chrome/test/functional/omniboxmodel.py b/chrome/test/functional/omniboxmodel.py
new file mode 100644
index 0000000..91326b0
--- /dev/null
+++ b/chrome/test/functional/omniboxmodel.py
@@ -0,0 +1,135 @@
+#!/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 logging
+import random
+
+import pyauto_functional
+from pyauto import PyUITest
+from pyauto import GURL
+
+import autotour
+
+
+class OmniboxModelTest(autotour.Godel, PyUITest):
+ """Omnibox Model which Opens tabs, navigates to specific URL's and keeps track
+ of URL's visited which are then verified agains the Omnibox's info.
+ """
+ def __init__(self, methodName='runTest', **kwargs):
+ PyUITest.__init__(self, methodName=methodName, **kwargs)
+ self._tab_count = 1
+ self._url_map = {}
+ self.InitUrls()
+
+ def InitUrls(self):
+ """Setup Url Map which stores the name of the url and a list of url and
+ visited count.
+ """
+ self._url_map = {
+ 'google': ('http://www.google.com', 0),
+ 'yahoo': ('http://www.yahoo.com', 0),
+ 'msn': ('http://www.msn.com', 0),
+ 'facebook': ('http://www.facebook.com', 0),
+ 'twitter': ('http://www.twitter.com', 0),
+ }
+
+ def CanOpenTab(self):
+ return self._tab_count < 5
+
+ def CanCloseTab(self):
+ return self._tab_count > 1
+
+ def CanNavigate(self):
+ # FIX: CanNavigate can be called if there is atleast one tab to be closed.
+ # Currently this condition is incorrect because CanCloseTab leaves atleast
+ # one tab because without that, there is some crash which is under
+ # investigation.
+ if self.CanCloseTab():
+ return False
+ for key in self._url_map:
+ if self._url_map[key][1] == 0:
+ return True
+ return False
+
+ def _GetOmniboxMatchesFor(self, text, windex=0, attr_dict=None):
+ """Fetch omnibox matches with the given attributes for the given query.
+
+ Args:
+ text: the query text to use
+ windex: the window index to work on. Defaults to 0 (first window)
+ attr_dict: the dictionary of properties to be satisfied
+
+ Returns:
+ a list of match items
+ """
+ self.SetOmniboxText(text, windex=windex)
+ self.WaitUntilOmniboxQueryDone(windex=windex)
+ if not attr_dict:
+ matches = self.GetOmniboxInfo(windex=windex).Matches()
+ else:
+ matches = self.GetOmniboxInfo(windex=windex).MatchesWithAttributes(
+ attr_dict=attr_dict)
+ return matches
+
+ @autotour.GodelAction(1, CanOpenTab)
+ def OpenTab(self):
+ """Opens a tab in the first window and navigates to a random site from
+ url map.
+ """
+ logging.info('#In Open Tab')
+ self._tab_count = self._tab_count + 1
+ key = random.choice(self._url_map.keys())
+ logging.info('#Navigating to ' + self._url_map[key][0])
+ self.AppendTab(GURL(self._url_map[key][0]))
+ self._url_map[key][1] = self._url_map[key][1] + 1
+ self.VerifyOmniboxInfo()
+
+ @autotour.GodelAction(10, CanCloseTab)
+ def CloseTab(self):
+ """Closes the first tab from the first window"""
+ self._tab_count = self._tab_count - 1
+ self.GetBrowserWindow(0).GetTab(0).Close(True)
+
+ def VerifyOmniboxInfo(self):
+ for key in self._url_map.keys():
+ """Verify inline autocomplete for a pre-visited url."""
+ search_for = key[:3]
+ matches = self._GetOmniboxMatchesFor(search_for, windex=0)
+ self.assertTrue(matches)
+ # Omnibox should suggest auto completed url as the first item
+ matches_description = matches[0]
+ term_to_find = search_for
+ if self._url_map[key][1] > 0:
+ logging.info('#verifying : ' + key)
+ logging.info('#verifying ' + key + ' text ' + search_for)
+ term_to_find = self._url_map[key][0][7:]
+ self.assertEqual('history-url', matches_description['type'])
+ self.assertTrue(self._url_map[key][0][11:] in
+ self.GetOmniboxInfo().Text())
+ self.assertTrue(term_to_find in matches_description['contents'])
+
+ @autotour.GodelAction(10, CanNavigate)
+ def Navigate(self):
+ """Navigates to a URL by picking a random url from list"""
+ logging.info('#In Navigate')
+ index = random.randint(0, len(self._url_map.keys()) - 1)
+ key = self._url_map.keys()[index]
+ logging.info('#navigating to ' + self._url_map[key][0])
+ self.NavigateToURL(self._url_map[key][0])
+ self._url_map[key][1] = self._url_map[key][1] + 1
+ self.VerifyOmniboxInfo()
+
+ def testExplore(self):
+ e = autotour.Explorer()
+ logging.info('#Explorer created')
+ e.Add(self)
+ logging.info('#Object added')
+ e.Explore(self.CanNavigate)
+ logging.info('#Done')
+
+
+if __name__ == '__main__':
+ pyauto_functional.Main()
+