Index: experimental/telemetry_mini/android_go_stories.py |
diff --git a/experimental/telemetry_mini/android_go_stories.py b/experimental/telemetry_mini/android_go_stories.py |
index 7d9c57b929c93a3c30713396c359973e7a029078..07a17ed8ebd971752e7af457226feb8f0dc1da95 100755 |
--- a/experimental/telemetry_mini/android_go_stories.py |
+++ b/experimental/telemetry_mini/android_go_stories.py |
@@ -4,6 +4,7 @@ |
# found in the LICENSE file. |
import argparse |
+import fnmatch |
import logging |
import os |
import sys |
@@ -31,81 +32,6 @@ BROWSERS = { |
} |
-class ProcessWatcher(object): |
- def __init__(self, device): |
- self.device = device |
- self._process_pid = {} |
- |
- def StartWatching(self, process_name): |
- """Register a process or android app to keep track of its PID.""" |
- if isinstance(process_name, telemetry_mini.AndroidApp): |
- process_name = process_name.PACKAGE_NAME |
- |
- @telemetry_mini.RetryOn(returns_falsy=True) |
- def GetPids(): |
- # Returns an empty list if the process name is not found. |
- return self.device.ProcessStatus()[process_name] |
- |
- assert process_name not in self._process_pid |
- pids = GetPids() |
- assert pids, 'PID for %s not found' % process_name |
- assert len(pids) == 1, 'Single PID for %s expected, but found: %s' % ( |
- process_name, pids) |
- logging.info('Started watching %s (PID=%d)', process_name, pids[0]) |
- self._process_pid[process_name] = pids[0] |
- |
- def AssertAllAlive(self): |
- """Check that all watched processes remain alive and were not restarted.""" |
- status = self.device.ProcessStatus() |
- all_alive = True |
- for process_name, old_pid in sorted(self._process_pid.iteritems()): |
- new_pids = status[process_name] |
- if not new_pids: |
- all_alive = False |
- logging.error('Process %s died (PID=%d).', process_name, old_pid) |
- elif new_pids != [old_pid]: |
- all_alive = False |
- logging.error( |
- 'Process %s restarted (PID=%d -> %s).', process_name, |
- old_pid, new_pids) |
- else: |
- logging.info('Process %s still alive (PID=%d)', process_name, old_pid) |
- assert all_alive, 'Some watched processes died or got restarted' |
- |
- |
-def EnsureSingleBrowser(device, browser_name, force_install=False): |
- """Ensure a single Chrome browser is installed and available on the device. |
- |
- Having more than one Chrome browser available may produce results which are |
- confusing or unreliable (e.g. unclear which browser will respond by default |
- to intents triggered by other apps). |
- |
- This function ensures only the selected browser is available, installing it |
- if necessary, and uninstalling/disabling others. |
- """ |
- browser = BROWSERS[browser_name](device) |
- available_browsers = set(device.ListPackages('chrome', only_enabled=True)) |
- |
- # Install or enable if needed. |
- if force_install or browser.PACKAGE_NAME not in available_browsers: |
- browser.Install() |
- |
- # Uninstall disable other browser apps. |
- for other_browser in BROWSERS.itervalues(): |
- if (other_browser.PACKAGE_NAME != browser.PACKAGE_NAME and |
- other_browser.PACKAGE_NAME in available_browsers): |
- other_browser(device).Uninstall() |
- |
- # Finally check that only the selected browser is actually available. |
- available_browsers = device.ListPackages('chrome', only_enabled=True) |
- assert browser.PACKAGE_NAME in available_browsers, ( |
- 'Unable to make %s available' % browser.PACKAGE_NAME) |
- available_browsers.remove(browser.PACKAGE_NAME) |
- assert not available_browsers, ( |
- 'Other browsers may intefere with the test: %s' % available_browsers) |
- return browser |
- |
- |
class TwitterApp(telemetry_mini.AndroidApp): |
PACKAGE_NAME = 'com.twitter.android' |
@@ -115,6 +41,14 @@ class InstagramApp(telemetry_mini.AndroidApp): |
class TwitterFlipkartStory(telemetry_mini.UserStory): |
+ """Load Chrome Custom Tab from another application. |
+ |
+ The flow of the story is: |
+ - Start Twitter app to view the @flipkart profile. |
+ - Tap on a link to open Flipkart in a Chrome Custom Tab. |
+ - Return to Twitter app. |
+ """ |
+ NAME = 'twitter_flipkart' |
FLIPKART_TWITTER_LINK = [ |
('package', 'com.twitter.android'), |
('class', 'android.widget.TextView'), |
@@ -149,6 +83,16 @@ class TwitterFlipkartStory(telemetry_mini.UserStory): |
class FlipkartInstagramStory(telemetry_mini.UserStory): |
+ """Interaction between Chrome, PWAs and a WebView-based app. |
+ |
+ The flow of the story is: |
+ - Launch the Flipkart PWA. |
+ - Go back home and launch the Instagram app. |
+ - Use the app switcher to return to Flipkart. |
+ - Go back home and launch Cricbuzz from a shortcut. |
+ """ |
+ NAME = 'flipkart_instagram' |
+ |
def __init__(self, *args, **kwargs): |
super(FlipkartInstagramStory, self).__init__(*args, **kwargs) |
self.watcher = ProcessWatcher(self.device) |
@@ -187,6 +131,87 @@ class FlipkartInstagramStory(telemetry_mini.UserStory): |
self.instagram.ForceStop() |
+STORIES = ( |
+ TwitterFlipkartStory, |
+ FlipkartInstagramStory |
+) |
+ |
+ |
+class ProcessWatcher(object): |
+ def __init__(self, device): |
+ self.device = device |
+ self._process_pid = {} |
+ |
+ def StartWatching(self, process_name): |
+ """Register a process or android app to keep track of its PID.""" |
+ if isinstance(process_name, telemetry_mini.AndroidApp): |
+ process_name = process_name.PACKAGE_NAME |
+ |
+ @telemetry_mini.RetryOn(returns_falsy=True) |
+ def GetPids(): |
+ # Returns an empty list if the process name is not found. |
+ return self.device.ProcessStatus()[process_name] |
+ |
+ assert process_name not in self._process_pid |
+ pids = GetPids() |
+ assert pids, 'PID for %s not found' % process_name |
+ assert len(pids) == 1, 'Single PID for %s expected, but found: %s' % ( |
+ process_name, pids) |
+ logging.info('Started watching %s (PID=%d)', process_name, pids[0]) |
+ self._process_pid[process_name] = pids[0] |
+ |
+ def AssertAllAlive(self): |
+ """Check that all watched processes remain alive and were not restarted.""" |
+ status = self.device.ProcessStatus() |
+ all_alive = True |
+ for process_name, old_pid in sorted(self._process_pid.iteritems()): |
+ new_pids = status[process_name] |
+ if not new_pids: |
+ all_alive = False |
+ logging.error('Process %s died (PID=%d).', process_name, old_pid) |
+ elif new_pids != [old_pid]: |
+ all_alive = False |
+ logging.error( |
+ 'Process %s restarted (PID=%d -> %s).', process_name, |
+ old_pid, new_pids) |
+ else: |
+ logging.info('Process %s still alive (PID=%d)', process_name, old_pid) |
+ assert all_alive, 'Some watched processes died or got restarted' |
+ |
+ |
+def EnsureSingleBrowser(device, browser_name, force_install=False): |
+ """Ensure a single Chrome browser is installed and available on the device. |
+ |
+ Having more than one Chrome browser available may produce results which are |
+ confusing or unreliable (e.g. unclear which browser will respond by default |
+ to intents triggered by other apps). |
+ |
+ This function ensures only the selected browser is available, installing it |
+ if necessary, and uninstalling/disabling others. |
+ """ |
+ browser = BROWSERS[browser_name](device) |
+ available_browsers = set(device.ListPackages('chrome', only_enabled=True)) |
+ |
+ # Install or enable if needed. |
+ if force_install or browser.PACKAGE_NAME not in available_browsers: |
+ browser.Install() |
+ |
+ # Uninstall disable other browser apps. |
+ for other_browser in BROWSERS.itervalues(): |
+ if (other_browser.PACKAGE_NAME != browser.PACKAGE_NAME and |
+ other_browser.PACKAGE_NAME in available_browsers): |
+ other_browser(device).Uninstall() |
+ |
+ # Finally check that only the selected browser is actually available. |
+ available_browsers = device.ListPackages('chrome', only_enabled=True) |
+ assert browser.PACKAGE_NAME in available_browsers, ( |
+ 'Unable to make %s available' % browser.PACKAGE_NAME) |
+ available_browsers.remove(browser.PACKAGE_NAME) |
+ assert not available_browsers, ( |
+ 'Other browsers may intefere with the test: %s' % available_browsers) |
+ return browser |
+ |
+ |
def main(): |
browser_names = sorted(BROWSERS) |
default_browser = 'android-chrome' |
@@ -201,6 +226,15 @@ def main(): |
help='one of: %s' % ', '.join( |
'%s (default)' % b if b == default_browser else b |
for b in browser_names)) |
+ parser.add_argument('--story-filter', metavar='PATTERN', default='*', |
+ help='run the matching stories only (allows Unix' |
+ ' shell-style wildcards)') |
+ parser.add_argument('--repeat', metavar='NUM', type=int, default=1, |
+ help='repeat the story set a number of times' |
+ ' (default: %(default)d)') |
+ parser.add_argument('--output-dir', metavar='PATH', |
+ help='path to directory for placing output trace files' |
+ ' (defaults to current directory)') |
parser.add_argument('--force-install', action='store_true', |
help='install APK even if browser is already available') |
parser.add_argument('--apks-dir', metavar='PATH', |
@@ -215,6 +249,17 @@ def main(): |
if args.verbose: |
logging.getLogger().setLevel(logging.INFO) |
+ stories = [s for s in STORIES if fnmatch.fnmatch(s.NAME, args.story_filter)] |
+ if not stories: |
+ return 'No matching stories' |
+ |
+ if args.output_dir is None: |
+ args.output_dir = os.getcwd() |
+ else: |
+ args.output_dir = os.path.realpath(args.output_dir) |
+ if not os.path.isdir(args.output_dir): |
+ return 'Output directory does not exit' |
+ |
if args.apks_dir is None: |
args.apks_dir = os.path.realpath(os.path.join( |
os.path.dirname(__file__), '..', '..', '..', '..', |
@@ -233,10 +278,10 @@ def main(): |
device.RunCommand('wait-for-device') |
browser = EnsureSingleBrowser(device, args.browser, args.force_install) |
+ browser.SetBrowserFlags(BROWSER_FLAGS) |
+ browser.SetTraceConfig(TRACE_CONFIG) |
browser.SetDevToolsLocalPort(args.port) |
- |
- story = FlipkartInstagramStory(browser) |
- story.Run(BROWSER_FLAGS, TRACE_CONFIG, 'trace.json') |
+ telemetry_mini.RunStories(browser, stories, args.repeat, args.output_dir) |
if __name__ == '__main__': |