Update from https://crrev.com/304121
Includes DEPS updates and port of
https://codereview.chromium.org/665223004 to accomodate skia API change
on android.
Review URL: https://codereview.chromium.org/723343002
diff --git a/build/android/findbugs_filter/findbugs_known_bugs.txt b/build/android/findbugs_filter/findbugs_known_bugs.txt
index f641e15..ee27544 100644
--- a/build/android/findbugs_filter/findbugs_known_bugs.txt
+++ b/build/android/findbugs_filter/findbugs_known_bugs.txt
@@ -24,3 +24,4 @@
M V EI: org.chromium.content_public.browser.LoadUrlParams.getPostData() may expose internal representation by returning LoadUrlParams.mPostData At LoadUrlParams.java
M D NP: Read of unwritten public or protected field data in org.chromium.components.devtools_bridge.SessionDependencyFactory$DataChannelObserverAdapter.onMessage(DataChannel$Buffer) At SessionDependencyFactory.java
M D NP: Read of unwritten public or protected field mandatory in org.chromium.components.devtools_bridge.SessionDependencyFactory.createPeerConnection(RTCConfiguration, AbstractPeerConnection$Observer) At SessionDependencyFactory.java
+M V EI2: org.chromium.net.ChromiumUrlRequest.setUploadData(String, byte[]) may expose internal representation by storing an externally mutable object into ChromiumUrlRequest.mUploadData At ChromiumUrlRequest.java
diff --git a/build/android/gyp/java_cpp_enum.py b/build/android/gyp/java_cpp_enum.py
index 8ae5f36..b10e7c5 100755
--- a/build/android/gyp/java_cpp_enum.py
+++ b/build/android/gyp/java_cpp_enum.py
@@ -13,14 +13,21 @@
from util import build_utils
+# List of C++ types that are compatible with the Java code generated by this
+# script.
+ENUM_FIXED_TYPE_WHITELIST = ['char', 'unsigned char',
+ 'short', 'unsigned short',
+ 'int', 'int8_t', 'int16_t', 'int32_t', 'uint8_t', 'uint16_t']
+
class EnumDefinition(object):
def __init__(self, original_enum_name=None, class_name_override=None,
- enum_package=None, entries=None):
+ enum_package=None, entries=None, fixed_type=None):
self.original_enum_name = original_enum_name
self.class_name_override = class_name_override
self.enum_package = enum_package
self.entries = collections.OrderedDict(entries or [])
self.prefix_to_strip = None
+ self.fixed_type = fixed_type
def AppendEntry(self, key, value):
if key in self.entries:
@@ -40,6 +47,9 @@
assert self.class_name
assert self.enum_package
assert self.entries
+ if self.fixed_type and self.fixed_type not in ENUM_FIXED_TYPE_WHITELIST:
+ raise Exception('Fixed type %s for enum %s not whitelisted.' %
+ (self.fixed_type, self.class_name))
def _AssignEntryIndices(self):
# Enums, if given no value, are given the value of the previous enum + 1.
@@ -110,12 +120,17 @@
class HeaderParser(object):
single_line_comment_re = re.compile(r'\s*//')
multi_line_comment_start_re = re.compile(r'\s*/\*')
- enum_start_re = re.compile(r'^\s*enum\s+(\w+)\s+{\s*$')
enum_line_re = re.compile(r'^\s*(\w+)(\s*\=\s*([^,\n]+))?,?')
enum_end_re = re.compile(r'^\s*}\s*;\.*$')
generator_directive_re = re.compile(
r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*([\.\w]+)$')
+ optional_class_or_struct_re = r'(class|struct)?'
+ enum_name_re = r'(\w+)'
+ optional_fixed_type_re = r'(\:\s*(\w+\s*\w+?))?'
+ enum_start_re = re.compile(r'^\s*enum\s+' + optional_class_or_struct_re +
+ '\s*' + enum_name_re + '\s*' + optional_fixed_type_re + '\s*{\s*$')
+
def __init__(self, lines):
self._lines = lines
self._enum_definitions = []
@@ -162,7 +177,8 @@
if self._generator_directives.empty:
return
self._current_definition = EnumDefinition(
- original_enum_name=enum_start.groups()[0])
+ original_enum_name=enum_start.groups()[1],
+ fixed_type=enum_start.groups()[3])
self._in_enum = True
elif generator_directive:
directive_name = generator_directive.groups()[0]
diff --git a/build/android/gyp/java_cpp_enum_tests.py b/build/android/gyp/java_cpp_enum_tests.py
index bb8150d..3aa386e 100755
--- a/build/android/gyp/java_cpp_enum_tests.py
+++ b/build/android/gyp/java_cpp_enum_tests.py
@@ -14,7 +14,8 @@
import sys
import unittest
-from java_cpp_enum import EnumDefinition, GenerateOutput, HeaderParser
+from java_cpp_enum import EnumDefinition, GenerateOutput, GetScriptName
+from java_cpp_enum import HeaderParser
sys.path.append(os.path.join(os.path.dirname(__file__), "gyp"))
from util import build_utils
@@ -31,7 +32,7 @@
// found in the LICENSE file.
// This file is autogenerated by
-// build/android/gyp/java_cpp_enum_tests.py
+// %s
// From
// path/to/file
@@ -42,7 +43,7 @@
public static final int E2 = 2 << 2;
}
"""
- self.assertEqual(expected, output)
+ self.assertEqual(expected % GetScriptName(), output)
def testParseSimpleEnum(self):
test_data = """
@@ -150,6 +151,78 @@
with self.assertRaises(Exception):
HeaderParser(test_data).ParseDefinitions()
+ def testParseEnumClass(self):
+ test_data = """
+ // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
+ enum class Foo {
+ FOO_A,
+ };
+ """.split('\n')
+ definitions = HeaderParser(test_data).ParseDefinitions()
+ self.assertEqual(1, len(definitions))
+ definition = definitions[0]
+ self.assertEqual('Foo', definition.class_name)
+ self.assertEqual('test.namespace', definition.enum_package)
+ self.assertEqual(collections.OrderedDict([('A', 0)]),
+ definition.entries)
+
+ def testParseEnumStruct(self):
+ test_data = """
+ // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
+ enum struct Foo {
+ FOO_A,
+ };
+ """.split('\n')
+ definitions = HeaderParser(test_data).ParseDefinitions()
+ self.assertEqual(1, len(definitions))
+ definition = definitions[0]
+ self.assertEqual('Foo', definition.class_name)
+ self.assertEqual('test.namespace', definition.enum_package)
+ self.assertEqual(collections.OrderedDict([('A', 0)]),
+ definition.entries)
+
+ def testParseFixedTypeEnum(self):
+ test_data = """
+ // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
+ enum Foo : int {
+ FOO_A,
+ };
+ """.split('\n')
+ definitions = HeaderParser(test_data).ParseDefinitions()
+ self.assertEqual(1, len(definitions))
+ definition = definitions[0]
+ self.assertEqual('Foo', definition.class_name)
+ self.assertEqual('test.namespace', definition.enum_package)
+ self.assertEqual('int', definition.fixed_type)
+ self.assertEqual(collections.OrderedDict([('A', 0)]),
+ definition.entries)
+
+ def testParseFixedTypeEnumClass(self):
+ test_data = """
+ // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
+ enum class Foo: unsigned short {
+ FOO_A,
+ };
+ """.split('\n')
+ definitions = HeaderParser(test_data).ParseDefinitions()
+ self.assertEqual(1, len(definitions))
+ definition = definitions[0]
+ self.assertEqual('Foo', definition.class_name)
+ self.assertEqual('test.namespace', definition.enum_package)
+ self.assertEqual('unsigned short', definition.fixed_type)
+ self.assertEqual(collections.OrderedDict([('A', 0)]),
+ definition.entries)
+
+ def testParseUnknownFixedTypeRaises(self):
+ test_data = """
+ // GENERATED_JAVA_ENUM_PACKAGE: test.namespace
+ enum class Foo: foo_type {
+ FOO_A,
+ };
+ """.split('\n')
+ with self.assertRaises(Exception):
+ HeaderParser(test_data).ParseDefinitions()
+
def testEnumValueAssignmentNoneDefined(self):
definition = EnumDefinition(original_enum_name='c', enum_package='p')
definition.AppendEntry('A', None)
diff --git a/build/android/gyp/package_resources.py b/build/android/gyp/package_resources.py
index 6444ed9..9b6ec68 100755
--- a/build/android/gyp/package_resources.py
+++ b/build/android/gyp/package_resources.py
@@ -37,6 +37,11 @@
parser.add_option('--android-manifest', help='AndroidManifest.xml path')
parser.add_option('--version-code', help='Version code for apk.')
parser.add_option('--version-name', help='Version name for apk.')
+ parser.add_option(
+ '--shared-resources',
+ action='store_true',
+ help='Make a resource package that can be loaded by a different'
+ 'application at runtime to access the package\'s resources.')
parser.add_option('--resource-zips',
help='zip files containing resources to be packaged')
parser.add_option('--asset-dir',
@@ -132,6 +137,8 @@
if options.no_compress:
for ext in options.no_compress.split(','):
package_command += ['-0', ext]
+ if options.shared_resources:
+ package_command.append('--shared-lib')
if os.path.exists(options.asset_dir):
package_command += ['-A', options.asset_dir]
diff --git a/build/android/gyp/process_resources.py b/build/android/gyp/process_resources.py
index 6bf71f3..45b0947 100755
--- a/build/android/gyp/process_resources.py
+++ b/build/android/gyp/process_resources.py
@@ -38,6 +38,11 @@
parser.add_option('--android-manifest', help='AndroidManifest.xml path')
parser.add_option('--custom-package', help='Java package for R.java')
+ parser.add_option(
+ '--shared-resources',
+ action='store_true',
+ help='Make a resource package that can be loaded by a different'
+ 'application at runtime to access the package\'s resources.')
parser.add_option('--resource-dirs',
help='Directories containing resources of this target.')
@@ -236,6 +241,8 @@
package_command += ['--custom-package', options.custom_package]
if options.proguard_file:
package_command += ['-G', options.proguard_file]
+ if options.shared_resources:
+ package_command.append('--shared-lib')
build_utils.CheckOutput(package_command, print_stderr=False)
if options.extra_res_packages:
diff --git a/build/android/pylib/base/base_test_result.py b/build/android/pylib/base/base_test_result.py
index 1f45214..f9e61a9 100644
--- a/build/android/pylib/base/base_test_result.py
+++ b/build/android/pylib/base/base_test_result.py
@@ -23,18 +23,20 @@
class BaseTestResult(object):
"""Base class for a single test result."""
- def __init__(self, name, test_type, log=''):
+ def __init__(self, name, test_type, duration=0, log=''):
"""Construct a BaseTestResult.
Args:
name: Name of the test which defines uniqueness.
test_type: Type of the test result as defined in ResultType.
+ duration: Time it took for the test to run in milliseconds.
log: An optional string listing any errors.
"""
assert name
assert test_type in ResultType.GetTypes()
self._name = name
self._test_type = test_type
+ self._duration = duration
self._log = log
def __str__(self):
@@ -66,6 +68,10 @@
"""Get the test result type."""
return self._test_type
+ def GetDuration(self):
+ """Get the test duration."""
+ return self._duration
+
def GetLog(self):
"""Get the test log."""
return self._log
diff --git a/build/android/pylib/base/base_test_runner.py b/build/android/pylib/base/base_test_runner.py
index 1f76149..6e51b43 100644
--- a/build/android/pylib/base/base_test_runner.py
+++ b/build/android/pylib/base/base_test_runner.py
@@ -9,10 +9,8 @@
# model.
import logging
-import time
from pylib import ports
-from pylib.chrome_test_server_spawner import SpawningServer
from pylib.device import device_utils
from pylib.forwarder import Forwarder
from pylib.valgrind_tools import CreateTool
@@ -42,7 +40,6 @@
self._forwarder_device_port = 8000
self.forwarder_base_url = ('http://localhost:%d' %
self._forwarder_device_port)
- self._spawning_server = None
# We will allocate port for test server spawner when calling method
# LaunchChromeTestServerSpawner and allocate port for test server when
# starting it in TestServerThread.
@@ -146,45 +143,4 @@
if self._http_server:
self._UnmapPorts([(self._forwarder_device_port, self._http_server.port)])
self._http_server.ShutdownHttpServer()
- if self._spawning_server:
- self._spawning_server.Stop()
- def CleanupSpawningServerState(self):
- """Tells the spawning server to clean up any state.
-
- If the spawning server is reused for multiple tests, this should be called
- after each test to prevent tests affecting each other.
- """
- if self._spawning_server:
- self._spawning_server.CleanupState()
-
- def LaunchChromeTestServerSpawner(self):
- """Launches test server spawner."""
- server_ready = False
- error_msgs = []
- # TODO(pliard): deflake this function. The for loop should be removed as
- # well as IsHttpServerConnectable(). spawning_server.Start() should also
- # block until the server is ready.
- # Try 3 times to launch test spawner server.
- for _ in xrange(0, 3):
- self.test_server_spawner_port = ports.AllocateTestServerPort()
- self._ForwardPorts(
- [(self.test_server_spawner_port, self.test_server_spawner_port)])
- self._spawning_server = SpawningServer(self.test_server_spawner_port,
- self.device,
- self.tool)
- self._spawning_server.Start()
- server_ready, error_msg = ports.IsHttpServerConnectable(
- '127.0.0.1', self.test_server_spawner_port, path='/ping',
- expected_read='ready')
- if server_ready:
- break
- else:
- error_msgs.append(error_msg)
- self._spawning_server.Stop()
- # Wait for 2 seconds then restart.
- time.sleep(2)
- if not server_ready:
- logging.error(';'.join(error_msgs))
- raise Exception('Can not start the test spawner server.')
- self._PushTestServerPortInfoToDevice()
diff --git a/build/android/pylib/base/test_collection.py b/build/android/pylib/base/test_collection.py
new file mode 100644
index 0000000..e914652
--- /dev/null
+++ b/build/android/pylib/base/test_collection.py
@@ -0,0 +1,80 @@
+# Copyright 2013 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 threading
+
+class TestCollection(object):
+ """A threadsafe collection of tests.
+
+ Args:
+ tests: List of tests to put in the collection.
+ """
+
+ def __init__(self, tests=None):
+ if not tests:
+ tests = []
+ self._lock = threading.Lock()
+ self._tests = []
+ self._tests_in_progress = 0
+ # Used to signal that an item is available or all items have been handled.
+ self._item_available_or_all_done = threading.Event()
+ for t in tests:
+ self.add(t)
+
+ def _pop(self):
+ """Pop a test from the collection.
+
+ Waits until a test is available or all tests have been handled.
+
+ Returns:
+ A test or None if all tests have been handled.
+ """
+ while True:
+ # Wait for a test to be available or all tests to have been handled.
+ self._item_available_or_all_done.wait()
+ with self._lock:
+ # Check which of the two conditions triggered the signal.
+ if self._tests_in_progress == 0:
+ return None
+ try:
+ return self._tests.pop(0)
+ except IndexError:
+ # Another thread beat us to the available test, wait again.
+ self._item_available_or_all_done.clear()
+
+ def add(self, test):
+ """Add an test to the collection.
+
+ Args:
+ test: A test to add.
+ """
+ with self._lock:
+ self._tests.append(test)
+ self._item_available_or_all_done.set()
+ self._tests_in_progress += 1
+
+ def test_completed(self):
+ """Indicate that a test has been fully handled."""
+ with self._lock:
+ self._tests_in_progress -= 1
+ if self._tests_in_progress == 0:
+ # All tests have been handled, signal all waiting threads.
+ self._item_available_or_all_done.set()
+
+ def __iter__(self):
+ """Iterate through tests in the collection until all have been handled."""
+ while True:
+ r = self._pop()
+ if r is None:
+ break
+ yield r
+
+ def __len__(self):
+ """Return the number of tests currently in the collection."""
+ return len(self._tests)
+
+ def test_names(self):
+ """Return a list of the names of the tests currently in the collection."""
+ with self._lock:
+ return list(t.test for t in self._tests)
diff --git a/build/android/pylib/base/test_dispatcher.py b/build/android/pylib/base/test_dispatcher.py
index 7b00ccd..929c408 100644
--- a/build/android/pylib/base/test_dispatcher.py
+++ b/build/android/pylib/base/test_dispatcher.py
@@ -24,6 +24,7 @@
from pylib import android_commands
from pylib import constants
from pylib.base import base_test_result
+from pylib.base import test_collection
from pylib.device import device_errors
from pylib.utils import reraiser_thread
from pylib.utils import watchdog_timer
@@ -65,92 +66,16 @@
self.tries = tries
-class _TestCollection(object):
- """A threadsafe collection of tests.
-
- Args:
- tests: List of tests to put in the collection.
- """
-
- def __init__(self, tests=None):
- if not tests:
- tests = []
- self._lock = threading.Lock()
- self._tests = []
- self._tests_in_progress = 0
- # Used to signal that an item is available or all items have been handled.
- self._item_available_or_all_done = threading.Event()
- for t in tests:
- self.add(t)
-
- def _pop(self):
- """Pop a test from the collection.
-
- Waits until a test is available or all tests have been handled.
-
- Returns:
- A test or None if all tests have been handled.
- """
- while True:
- # Wait for a test to be available or all tests to have been handled.
- self._item_available_or_all_done.wait()
- with self._lock:
- # Check which of the two conditions triggered the signal.
- if self._tests_in_progress == 0:
- return None
- try:
- return self._tests.pop(0)
- except IndexError:
- # Another thread beat us to the available test, wait again.
- self._item_available_or_all_done.clear()
-
- def add(self, test):
- """Add an test to the collection.
-
- Args:
- test: A test to add.
- """
- with self._lock:
- self._tests.append(test)
- self._item_available_or_all_done.set()
- self._tests_in_progress += 1
-
- def test_completed(self):
- """Indicate that a test has been fully handled."""
- with self._lock:
- self._tests_in_progress -= 1
- if self._tests_in_progress == 0:
- # All tests have been handled, signal all waiting threads.
- self._item_available_or_all_done.set()
-
- def __iter__(self):
- """Iterate through tests in the collection until all have been handled."""
- while True:
- r = self._pop()
- if r is None:
- break
- yield r
-
- def __len__(self):
- """Return the number of tests currently in the collection."""
- return len(self._tests)
-
- def test_names(self):
- """Return a list of the names of the tests currently in the collection."""
- with self._lock:
- return list(t.test for t in self._tests)
-
-
-def _RunTestsFromQueue(runner, test_collection, out_results, watcher,
+def _RunTestsFromQueue(runner, collection, out_results, watcher,
num_retries, tag_results_with_device=False):
- """Runs tests from the test_collection until empty using the given runner.
+ """Runs tests from the collection until empty using the given runner.
Adds TestRunResults objects to the out_results list and may add tests to the
out_retry list.
Args:
runner: A TestRunner object used to run the tests.
- test_collection: A _TestCollection from which to get _Test objects to run.
+ collection: A TestCollection from which to get _Test objects to run.
out_results: A list to add TestRunResults to.
watcher: A watchdog_timer.WatchdogTimer object, used as a shared timeout.
num_retries: Number of retries for a test.
@@ -174,7 +99,7 @@
new_test_run_results.AddResult(test_result)
return new_test_run_results
- for test in test_collection:
+ for test in collection:
watcher.Reset()
try:
if runner.device_serial not in android_commands.GetAttachedDevices():
@@ -192,18 +117,18 @@
pass_results.AddResults(result.GetPass())
out_results.append(pass_results)
logging.warning('Will retry test, try #%s.' % test.tries)
- test_collection.add(_Test(test=retry, tries=test.tries))
+ collection.add(_Test(test=retry, tries=test.tries))
else:
# All tests passed or retry limit reached. Either way, record results.
out_results.append(result)
except:
# An unhandleable exception, ensure tests get run by another device and
# reraise this exception on the main thread.
- test_collection.add(test)
+ collection.add(test)
raise
finally:
# Retries count as separate tasks so always mark the popped test as done.
- test_collection.test_completed()
+ collection.test_completed()
def _SetUp(runner_factory, device, out_runners, threadsafe_counter):
@@ -238,7 +163,7 @@
Args:
runners: A list of TestRunner objects.
- test_collection_factory: A callable to generate a _TestCollection object for
+ test_collection_factory: A callable to generate a TestCollection object for
each test runner.
num_retries: Number of retries for a test.
timeout: Watchdog timeout in seconds.
@@ -384,16 +309,17 @@
tests_expanded = ApplyMaxPerRun(tests, max_per_run)
if shard:
- # Generate a shared _TestCollection object for all test runners, so they
+ # Generate a shared TestCollection object for all test runners, so they
# draw from a common pool of tests.
- shared_test_collection = _TestCollection([_Test(t) for t in tests_expanded])
+ shared_test_collection = test_collection.TestCollection(
+ [_Test(t) for t in tests_expanded])
test_collection_factory = lambda: shared_test_collection
tag_results_with_device = False
log_string = 'sharded across devices'
else:
- # Generate a unique _TestCollection object for each test runner, but use
+ # Generate a unique TestCollection object for each test runner, but use
# the same set of tests.
- test_collection_factory = lambda: _TestCollection(
+ test_collection_factory = lambda: test_collection.TestCollection(
[_Test(t) for t in tests_expanded])
tag_results_with_device = True
log_string = 'replicated on each device'
diff --git a/build/android/pylib/base/test_dispatcher_unittest.py b/build/android/pylib/base/test_dispatcher_unittest.py
index d349f32..b57cca9 100644
--- a/build/android/pylib/base/test_dispatcher_unittest.py
+++ b/build/android/pylib/base/test_dispatcher_unittest.py
@@ -18,6 +18,7 @@
android_commands.GetAttachedDevices = lambda: ['0', '1']
from pylib import constants
from pylib.base import base_test_result
+from pylib.base import test_collection
from pylib.base import test_dispatcher
from pylib.utils import watchdog_timer
@@ -83,7 +84,7 @@
@staticmethod
def _RunTests(mock_runner, tests):
results = []
- tests = test_dispatcher._TestCollection(
+ tests = test_collection.TestCollection(
[test_dispatcher._Test(t) for t in tests])
test_dispatcher._RunTestsFromQueue(mock_runner, tests, results,
watchdog_timer.WatchdogTimer(None), 2)
@@ -129,7 +130,7 @@
"""Tests test_dispatcher._RunAllTests and test_dispatcher._CreateRunners."""
def setUp(self):
self.tests = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
- shared_test_collection = test_dispatcher._TestCollection(
+ shared_test_collection = test_collection.TestCollection(
[test_dispatcher._Test(t) for t in self.tests])
self.test_collection_factory = lambda: shared_test_collection
diff --git a/build/android/pylib/base/test_server.py b/build/android/pylib/base/test_server.py
new file mode 100644
index 0000000..085a51e
--- /dev/null
+++ b/build/android/pylib/base/test_server.py
@@ -0,0 +1,19 @@
+# Copyright 2014 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.
+
+class TestServer(object):
+ """Base class for any server that needs to be set up for the tests."""
+
+ def __init__(self, *args, **kwargs):
+ pass
+
+ def SetUp(self):
+ raise NotImplementedError
+
+ def Reset(self):
+ raise NotImplementedError
+
+ def TearDown(self):
+ raise NotImplementedError
+
diff --git a/build/android/pylib/constants.py b/build/android/pylib/constants.py
index cef098f..29da601 100644
--- a/build/android/pylib/constants.py
+++ b/build/android/pylib/constants.py
@@ -189,13 +189,12 @@
'pylib.device.device_utils_test',
]
},
-# TODO(mkosiba) Enable after fixing these tests.
-# 'gyp_py_unittests': {
-# 'path': os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android', 'gyp'),
-# 'test_modules': [
-# 'java_cpp_enum_tests'
-# ]
-# },
+ 'gyp_py_unittests': {
+ 'path': os.path.join(DIR_SOURCE_ROOT, 'build', 'android', 'gyp'),
+ 'test_modules': [
+ 'java_cpp_enum_tests',
+ ]
+ },
}
LOCAL_MACHINE_TESTS = ['junit', 'python']
@@ -217,6 +216,10 @@
os.environ['CHROMIUM_OUT_DIR'] = build_directory
+def SetOutputDirectort(output_directory):
+ os.environ['CHROMIUM_OUTPUT_DIR'] = output_directory
+
+
def GetOutDirectory(build_type=None):
"""Returns the out directory where the output binaries are built.
@@ -224,6 +227,10 @@
build_type: Build type, generally 'Debug' or 'Release'. Defaults to the
globally set build type environment variable BUILDTYPE.
"""
+ if 'CHROMIUM_OUTPUT_DIR' in os.environ:
+ return os.path.abspath(os.path.join(
+ DIR_SOURCE_ROOT, os.environ.get('CHROMIUM_OUTPUT_DIR')))
+
return os.path.abspath(os.path.join(
DIR_SOURCE_ROOT, os.environ.get('CHROMIUM_OUT_DIR', 'out'),
GetBuildType() if build_type is None else build_type))
diff --git a/build/android/pylib/device/adb_wrapper.py b/build/android/pylib/device/adb_wrapper.py
index 97f387a..e7a8418 100644
--- a/build/android/pylib/device/adb_wrapper.py
+++ b/build/android/pylib/device/adb_wrapper.py
@@ -152,14 +152,14 @@
self._DeviceAdbCmd(['pull', remote, local], timeout, retries)
_VerifyLocalFileExists(local)
- def Shell(self, command, expect_rc=None, timeout=_DEFAULT_TIMEOUT,
+ def Shell(self, command, expect_rc=0, timeout=_DEFAULT_TIMEOUT,
retries=_DEFAULT_RETRIES):
"""Runs a shell command on the device.
Args:
command: The shell command to run.
- expect_rc: (optional) If set checks that the command's return code matches
- this value.
+ expect_rc: (optional) Check that the command's return code matches this
+ value. Default is 0. If set to None the test is skipped.
timeout: (optional) Timeout per try in seconds.
retries: (optional) Number of retries to attempt.
diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py
index f4bce41..f6bebce 100644
--- a/build/android/pylib/device/device_utils.py
+++ b/build/android/pylib/device/device_utils.py
@@ -215,9 +215,6 @@
CommandTimeoutError on timeout.
DeviceUnreachableError on missing device.
"""
- return self._GetExternalStoragePathImpl()
-
- def _GetExternalStoragePathImpl(self):
if 'external_storage' in self._cache:
return self._cache['external_storage']
@@ -285,7 +282,8 @@
return self.GetProp('sys.boot_completed') == '1'
def wifi_enabled():
- return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'])
+ return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
+ check_return=False)
self.adb.WaitForDevice()
timeout_retry.WaitFor(sd_card_ready)
@@ -442,7 +440,7 @@
timeout = self._default_timeout
try:
- output = self.adb.Shell(cmd, expect_rc=0)
+ output = self.adb.Shell(cmd)
except device_errors.AdbShellCommandFailedError as e:
if check_return:
raise
@@ -531,6 +529,22 @@
raise device_errors.CommandFailedError(l, device=str(self))
@decorators.WithTimeoutAndRetriesFromInstance()
+ def StartInstrumentation(self, component, finish=True, raw=False,
+ extras=None, timeout=None, retries=None):
+ if extras is None:
+ extras = {}
+
+ cmd = ['am', 'instrument']
+ if finish:
+ cmd.append('-w')
+ if raw:
+ cmd.append('-r')
+ for k, v in extras.iteritems():
+ cmd.extend(['-e', k, v])
+ cmd.append(component)
+ return self.RunShellCommand(cmd, check_return=True)
+
+ @decorators.WithTimeoutAndRetriesFromInstance()
def BroadcastIntent(self, intent, timeout=None, retries=None):
"""Send a broadcast intent.
@@ -768,7 +782,7 @@
zip_proc.start()
zip_proc.join()
- zip_on_device = '%s/tmp.zip' % self._GetExternalStoragePathImpl()
+ zip_on_device = '%s/tmp.zip' % self.GetExternalStoragePath()
try:
self.adb.Push(zip_file.name, zip_on_device)
self.RunShellCommand(
diff --git a/build/android/pylib/device/device_utils_test.py b/build/android/pylib/device/device_utils_test.py
index fa4ffaa..65547d4 100755
--- a/build/android/pylib/device/device_utils_test.py
+++ b/build/android/pylib/device/device_utils_test.py
@@ -26,6 +26,7 @@
from pylib.device import device_errors
from pylib.device import device_utils
from pylib.device import intent
+from pylib.utils import mock_calls
# RunCommand from third_party/android_testrunner/run_command.py is mocked
# below, so its path needs to be in sys.path.
@@ -218,82 +219,7 @@
'0123456789abcdef', default_timeout=1, default_retries=0)
-class Args:
- def __init__(self, *args, **kwargs):
- self.args = args
- self.kwargs = kwargs
-
- def __eq__(self, other):
- return (self.args, self.kwargs) == (other.args, other.kwargs)
-
- def __repr__(self):
- return '%s(%s)' % (type(self).__name__, str(self))
-
- def __str__(self):
- toks = (['%r' % v for v in self.args] +
- ['%s=%r' % (k, self.kwargs[k]) for k in sorted(self.kwargs)])
- return ', '.join(toks)
-
-
-class MockCallSequence(object):
- def __init__(self, test_case, obj, method, calls):
- def assert_and_return(*args, **kwargs):
- received_args = Args(*args, **kwargs)
- test_case.assertTrue(
- self._calls,
- msg=('Unexpected call\n'
- ' received: %s(%s)\n' % (self._method, received_args)))
- expected_args, return_value = self._calls.pop(0)
- test_case.assertTrue(
- received_args == expected_args,
- msg=('Call does not match expected args\n'
- ' received: %s(%s)\n'
- ' expected: %s(%s)\n'
- % (self._method, received_args,
- self._method, expected_args)))
- if isinstance(return_value, Exception):
- raise return_value
- else:
- return return_value
-
- self._calls = list(calls)
- self._test_case = test_case
- self._method = method
- self._patched = mock.patch.object(obj, self._method,
- side_effect=assert_and_return)
-
- def __enter__(self):
- return self._patched.__enter__()
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- self._patched.__exit__(exc_type, exc_val, exc_tb)
- if exc_type is None:
- missing = ''.join(' expected: %s(%s)\n'
- % (self._method, expected_args)
- for expected_args, _ in self._calls)
- self._test_case.assertTrue(
- not missing,
- msg=('Expected calls not found\n' + missing))
-
-
-class _ShellError:
- def __init__(self, output=None, return_code=1):
- if output is None:
- self.output = 'Permission denied\r\n'
- else:
- self.output = output
- self.return_code = return_code
-
-
-class _CmdTimeout:
- def __init__(self, msg=None):
- if msg is None:
- self.msg = 'Operation timed out'
- else:
- self.msg = msg
-
-
-class DeviceUtilsNewImplTest(unittest.TestCase):
+class DeviceUtilsNewImplTest(mock_calls.TestCase):
def setUp(self):
test_serial = '0123456789abcdef'
@@ -302,66 +228,52 @@
self.adb.GetDeviceSerial.return_value = test_serial
self.device = device_utils.DeviceUtils(
self.adb, default_timeout=10, default_retries=0)
+ self.watchMethodCalls(self.call.adb)
- def assertShellCallSequence(self, calls):
- '''Assert that we expect a sequence of calls to adb.Shell.
+ def ShellError(self, output=None, exit_code=1):
+ def action(cmd, *args, **kwargs):
+ raise device_errors.AdbShellCommandFailedError(
+ cmd, exit_code, output, str(self.device))
+ if output is None:
+ output = 'Permission denied\n'
+ return action
- Args:
- calls: a sequence of (cmd, return_value) pairs, where |cmd| is the
- expected shell command to run on the device (with any quoting already
- applied), and |return_value| is either a string to give as mock output
- or a _ShellError object to raise an AdbShellCommandFailedError.
- '''
- def mk_expected_call(cmd, return_value):
- expected_args = Args(cmd, expect_rc=0)
- if isinstance(return_value, _ShellError):
- return_value = device_errors.AdbShellCommandFailedError(cmd,
- return_value.return_code, return_value.output, str(self.device))
- elif isinstance(return_value, _CmdTimeout):
- return_value = device_errors.CommandTimeoutError(return_value.msg,
- str(self.device))
- return (expected_args, return_value)
+ def TimeoutError(self, msg=None):
+ if msg is None:
+ msg = 'Operation timed out'
+ return mock.Mock(side_effect=device_errors.CommandTimeoutError(
+ msg, str(self.device)))
- expected_calls = (mk_expected_call(a, r) for a, r in calls)
- return MockCallSequence(self, self.adb, 'Shell', expected_calls)
-
- def assertShellCall(self, cmd, return_value=''):
- return self.assertShellCallSequence([(cmd, return_value)])
-
-
-class DeviceUtilsHybridImplTest(DeviceUtilsOldImplTest):
-
- def setUp(self):
- super(DeviceUtilsHybridImplTest, self).setUp()
- self.device.adb = self.adb = mock.Mock(spec=adb_wrapper.AdbWrapper)
+ def CommandError(self, msg=None):
+ if msg is None:
+ msg = 'Command failed'
+ return mock.Mock(side_effect=device_errors.CommandFailedError(
+ msg, str(self.device)))
class DeviceUtilsIsOnlineTest(DeviceUtilsNewImplTest):
def testIsOnline_true(self):
- self.adb.GetState = mock.Mock(return_value='device')
- self.assertTrue(self.device.IsOnline())
- self.adb.GetState.assert_called_once_with()
+ with self.assertCall(self.call.adb.GetState(), 'device'):
+ self.assertTrue(self.device.IsOnline())
def testIsOnline_false(self):
- self.adb.GetState = mock.Mock(return_value='offline')
- self.assertFalse(self.device.IsOnline())
- self.adb.GetState.assert_called_once_with()
+ with self.assertCall(self.call.adb.GetState(), 'offline'):
+ self.assertFalse(self.device.IsOnline())
def testIsOnline_error(self):
- self.adb.GetState = mock.Mock(
- side_effect=device_errors.CommandFailedError('falied'))
- self.assertFalse(self.device.IsOnline())
- self.adb.GetState.assert_called_once_with()
+ with self.assertCall(self.call.adb.GetState(), self.CommandError()):
+ self.assertFalse(self.device.IsOnline())
+
class DeviceUtilsHasRootTest(DeviceUtilsNewImplTest):
def testHasRoot_true(self):
- with self.assertShellCall('ls /root', 'foo\r\n'):
+ with self.assertCall(self.call.adb.Shell('ls /root'), 'foo\n'):
self.assertTrue(self.device.HasRoot())
def testHasRoot_false(self):
- with self.assertShellCall('ls /root', _ShellError()):
+ with self.assertCall(self.call.adb.Shell('ls /root'), self.ShellError()):
self.assertFalse(self.device.HasRoot())
@@ -395,25 +307,26 @@
class DeviceUtilsIsUserBuildTest(DeviceUtilsNewImplTest):
def testIsUserBuild_yes(self):
- with self.assertShellCall('getprop ro.build.type', 'user\r\n'):
+ with self.assertCall(
+ self.call.device.GetProp('ro.build.type', cache=True), 'user'):
self.assertTrue(self.device.IsUserBuild())
def testIsUserBuild_no(self):
- with self.assertShellCall('getprop ro.build.type', 'userdebug\r\n'):
+ with self.assertCall(
+ self.call.device.GetProp('ro.build.type', cache=True), 'userdebug'):
self.assertFalse(self.device.IsUserBuild())
class DeviceUtilsGetExternalStoragePathTest(DeviceUtilsNewImplTest):
def testGetExternalStoragePath_succeeds(self):
- fakeStoragePath = '/fake/storage/path'
- with self.assertShellCall('echo $EXTERNAL_STORAGE',
- '%s\r\n' % fakeStoragePath):
- self.assertEquals(fakeStoragePath,
+ with self.assertCall(
+ self.call.adb.Shell('echo $EXTERNAL_STORAGE'), '/fake/storage/path\n'):
+ self.assertEquals('/fake/storage/path',
self.device.GetExternalStoragePath())
def testGetExternalStoragePath_fails(self):
- with self.assertShellCall('echo $EXTERNAL_STORAGE', '\r\n'):
+ with self.assertCall(self.call.adb.Shell('echo $EXTERNAL_STORAGE'), '\n'):
with self.assertRaises(device_errors.CommandFailedError):
self.device.GetExternalStoragePath()
@@ -421,148 +334,150 @@
class DeviceUtilsGetApplicationPathTest(DeviceUtilsNewImplTest):
def testGetApplicationPath_exists(self):
- with self.assertShellCall('pm path android',
- 'package:/path/to/android.apk\n'):
+ with self.assertCall(self.call.adb.Shell('pm path android'),
+ 'package:/path/to/android.apk\n'):
self.assertEquals('/path/to/android.apk',
self.device.GetApplicationPath('android'))
def testGetApplicationPath_notExists(self):
- with self.assertShellCall('pm path not.installed.app',
- ''):
+ with self.assertCall(self.call.adb.Shell('pm path not.installed.app'), ''):
self.assertEquals(None,
self.device.GetApplicationPath('not.installed.app'))
def testGetApplicationPath_fails(self):
- with self.assertShellCall('pm path android',
- 'ERROR. Is package manager running?\n'):
+ with self.assertCall(self.call.adb.Shell('pm path android'),
+ self.CommandError('ERROR. Is package manager running?\n')):
with self.assertRaises(device_errors.CommandFailedError):
self.device.GetApplicationPath('android')
+@mock.patch('time.sleep', mock.Mock())
class DeviceUtilsWaitUntilFullyBootedTest(DeviceUtilsNewImplTest):
def testWaitUntilFullyBooted_succeedsNoWifi(self):
- with self.assertShellCallSequence([
- # sc_card_ready
- ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'),
- ('test -d /fake/storage/path', ''),
+ with self.assertCalls(
+ self.call.adb.WaitForDevice(),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
+ (self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- ('pm path android', 'package:this.is.a.test.package\r\n'),
+ (self.call.device.GetApplicationPath('android'),
+ 'package:/some/fake/path'),
# boot_completed
- ('getprop sys.boot_completed', '1\r\n')]):
+ (self.call.device.GetProp('sys.boot_completed'), '1')):
self.device.WaitUntilFullyBooted(wifi=False)
- self.adb.WaitForDevice.assert_called_once_with()
def testWaitUntilFullyBooted_succeedsWithWifi(self):
- with self.assertShellCallSequence([
- # sc_card_ready
- ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'),
- ('test -d /fake/storage/path', ''),
+ with self.assertCalls(
+ self.call.adb.WaitForDevice(),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
+ (self.call.adb.Shell('test -d /fake/storage/path'), ''),
# pm_ready
- ('pm path android', 'package:this.is.a.test.package\r\n'),
+ (self.call.device.GetApplicationPath('android'),
+ 'package:/some/fake/path'),
# boot_completed
- ('getprop sys.boot_completed', '1\r\n'),
+ (self.call.device.GetProp('sys.boot_completed'), '1'),
# wifi_enabled
- ('dumpsys wifi', 'stuff\r\nWi-Fi is enabled\r\nmore stuff\r\n')]):
+ (self.call.adb.Shell('dumpsys wifi'),
+ 'stuff\nWi-Fi is enabled\nmore stuff\n')):
self.device.WaitUntilFullyBooted(wifi=True)
- self.adb.WaitForDevice.assert_called_once_with()
def testWaitUntilFullyBooted_sdCardReadyFails_noPath(self):
- with self.assertShellCallSequence([
- # sc_card_ready
- ('echo $EXTERNAL_STORAGE', '\r\n')]):
+ with self.assertCalls(
+ self.call.adb.WaitForDevice(),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), self.CommandError())):
with self.assertRaises(device_errors.CommandFailedError):
self.device.WaitUntilFullyBooted(wifi=False)
- def testWaitUntilFullyBooted_sdCardReadyFails_emptyPath(self):
- with mock.patch('time.sleep'):
- with self.assertShellCallSequence([
- # sc_card_ready
- ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'),
- ('test -d /fake/storage/path', _ShellError()),
- # sc_card_ready
- ('test -d /fake/storage/path', _ShellError()),
- # sc_card_ready
- ('test -d /fake/storage/path', _CmdTimeout())]):
- with self.assertRaises(device_errors.CommandTimeoutError):
- self.device.WaitUntilFullyBooted(wifi=False)
+ def testWaitUntilFullyBooted_sdCardReadyFails_notExists(self):
+ with self.assertCalls(
+ self.call.adb.WaitForDevice(),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
+ (self.call.adb.Shell('test -d /fake/storage/path'), self.ShellError()),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
+ (self.call.adb.Shell('test -d /fake/storage/path'), self.ShellError()),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
+ (self.call.adb.Shell('test -d /fake/storage/path'),
+ self.TimeoutError())):
+ with self.assertRaises(device_errors.CommandTimeoutError):
+ self.device.WaitUntilFullyBooted(wifi=False)
def testWaitUntilFullyBooted_devicePmFails(self):
- with mock.patch('time.sleep'):
- with self.assertShellCallSequence([
- # sc_card_ready
- ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'),
- ('test -d /fake/storage/path', ''),
- # pm_ready
- ('pm path android', 'Error. Is package manager running?\r\n'),
- # pm_ready
- ('pm path android', 'Error. Is package manager running?\r\n'),
- # pm_ready
- ('pm path android', _CmdTimeout())]):
- with self.assertRaises(device_errors.CommandTimeoutError):
- self.device.WaitUntilFullyBooted(wifi=False)
+ with self.assertCalls(
+ self.call.adb.WaitForDevice(),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
+ (self.call.adb.Shell('test -d /fake/storage/path'), ''),
+ # pm_ready
+ (self.call.device.GetApplicationPath('android'), self.CommandError()),
+ # pm_ready
+ (self.call.device.GetApplicationPath('android'), self.CommandError()),
+ # pm_ready
+ (self.call.device.GetApplicationPath('android'), self.TimeoutError())):
+ with self.assertRaises(device_errors.CommandTimeoutError):
+ self.device.WaitUntilFullyBooted(wifi=False)
def testWaitUntilFullyBooted_bootFails(self):
- with mock.patch('time.sleep'):
- with self.assertShellCallSequence([
- # sc_card_ready
- ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'),
- ('test -d /fake/storage/path', ''),
- # pm_ready
- ('pm path android', 'package:this.is.a.test.package\r\n'),
- # boot_completed
- ('getprop sys.boot_completed', '0\r\n'),
- # boot_completed
- ('getprop sys.boot_completed', '0\r\n'),
- # boot_completed
- ('getprop sys.boot_completed', _CmdTimeout())]):
- with self.assertRaises(device_errors.CommandTimeoutError):
- self.device.WaitUntilFullyBooted(wifi=False)
+ with self.assertCalls(
+ self.call.adb.WaitForDevice(),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
+ (self.call.adb.Shell('test -d /fake/storage/path'), ''),
+ # pm_ready
+ (self.call.device.GetApplicationPath('android'),
+ 'package:/some/fake/path'),
+ # boot_completed
+ (self.call.device.GetProp('sys.boot_completed'), '0'),
+ # boot_completed
+ (self.call.device.GetProp('sys.boot_completed'), '0'),
+ # boot_completed
+ (self.call.device.GetProp('sys.boot_completed'), self.TimeoutError())):
+ with self.assertRaises(device_errors.CommandTimeoutError):
+ self.device.WaitUntilFullyBooted(wifi=False)
def testWaitUntilFullyBooted_wifiFails(self):
- with mock.patch('time.sleep'):
- with self.assertShellCallSequence([
- # sc_card_ready
- ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'),
- ('test -d /fake/storage/path', ''),
- # pm_ready
- ('pm path android', 'package:this.is.a.test.package\r\n'),
- # boot_completed
- ('getprop sys.boot_completed', '1\r\n'),
- # wifi_enabled
- ('dumpsys wifi', 'stuff\r\nmore stuff\r\n'),
- # wifi_enabled
- ('dumpsys wifi', 'stuff\r\nmore stuff\r\n'),
- # wifi_enabled
- ('dumpsys wifi', _CmdTimeout())]):
- with self.assertRaises(device_errors.CommandTimeoutError):
- self.device.WaitUntilFullyBooted(wifi=True)
+ with self.assertCalls(
+ self.call.adb.WaitForDevice(),
+ # sd_card_ready
+ (self.call.device.GetExternalStoragePath(), '/fake/storage/path'),
+ (self.call.adb.Shell('test -d /fake/storage/path'), ''),
+ # pm_ready
+ (self.call.device.GetApplicationPath('android'),
+ 'package:/some/fake/path'),
+ # boot_completed
+ (self.call.device.GetProp('sys.boot_completed'), '1'),
+ # wifi_enabled
+ (self.call.adb.Shell('dumpsys wifi'), 'stuff\nmore stuff\n'),
+ # wifi_enabled
+ (self.call.adb.Shell('dumpsys wifi'), 'stuff\nmore stuff\n'),
+ # wifi_enabled
+ (self.call.adb.Shell('dumpsys wifi'), self.TimeoutError())):
+ with self.assertRaises(device_errors.CommandTimeoutError):
+ self.device.WaitUntilFullyBooted(wifi=True)
+@mock.patch('time.sleep', mock.Mock())
class DeviceUtilsRebootTest(DeviceUtilsNewImplTest):
def testReboot_nonBlocking(self):
- self.adb.Reboot = mock.Mock()
- self.device.IsOnline = mock.Mock(return_value=False)
- self.device.Reboot(block=False)
- self.adb.Reboot.assert_called_once_with()
- self.device.IsOnline.assert_called_once_with()
+ with self.assertCalls(
+ self.call.adb.Reboot(),
+ (self.call.device.IsOnline(), True),
+ (self.call.device.IsOnline(), False)):
+ self.device.Reboot(block=False)
def testReboot_blocking(self):
- self.adb.Reboot = mock.Mock()
- self.device.IsOnline = mock.Mock(return_value=False)
- with self.assertShellCallSequence([
- # sc_card_ready
- ('echo $EXTERNAL_STORAGE', '/fake/storage/path\r\n'),
- ('test -d /fake/storage/path', ''),
- # pm_ready
- ('pm path android', 'package:this.is.a.test.package\r\n'),
- # boot_completed
- ('getprop sys.boot_completed', '1\r\n')]):
+ with self.assertCalls(
+ self.call.adb.Reboot(),
+ (self.call.device.IsOnline(), True),
+ (self.call.device.IsOnline(), False),
+ self.call.device.WaitUntilFullyBooted()):
self.device.Reboot(block=True)
- self.adb.Reboot.assert_called_once_with()
- self.device.IsOnline.assert_called_once_with()
- self.adb.WaitForDevice.assert_called_once_with()
class DeviceUtilsInstallTest(DeviceUtilsOldImplTest):
@@ -656,24 +571,31 @@
class DeviceUtilsRunShellCommandTest(DeviceUtilsNewImplTest):
+
+ def setUp(self):
+ super(DeviceUtilsRunShellCommandTest, self).setUp()
+ self.device.NeedsSU = mock.Mock(return_value=False)
+
def testRunShellCommand_commandAsList(self):
- with self.assertShellCall('pm list packages'):
+ with self.assertCall(self.call.adb.Shell('pm list packages'), ''):
self.device.RunShellCommand(['pm', 'list', 'packages'])
def testRunShellCommand_commandAsListQuoted(self):
- with self.assertShellCall("echo 'hello world' '$10'"):
+ with self.assertCall(self.call.adb.Shell("echo 'hello world' '$10'"), ''):
self.device.RunShellCommand(['echo', 'hello world', '$10'])
def testRunShellCommand_commandAsString(self):
- with self.assertShellCall('echo "$VAR"'):
+ with self.assertCall(self.call.adb.Shell('echo "$VAR"'), ''):
self.device.RunShellCommand('echo "$VAR"')
def testNewRunShellImpl_withEnv(self):
- with self.assertShellCall('VAR=some_string echo "$VAR"'):
+ with self.assertCall(
+ self.call.adb.Shell('VAR=some_string echo "$VAR"'), ''):
self.device.RunShellCommand('echo "$VAR"', env={'VAR': 'some_string'})
def testNewRunShellImpl_withEnvQuoted(self):
- with self.assertShellCall('PATH="$PATH:/other/path" run_this'):
+ with self.assertCall(
+ self.call.adb.Shell('PATH="$PATH:/other/path" run_this'), ''):
self.device.RunShellCommand('run_this', env={'PATH': '$PATH:/other/path'})
def testNewRunShellImpl_withEnv_failure(self):
@@ -681,132 +603,129 @@
self.device.RunShellCommand('some_cmd', env={'INVALID NAME': 'value'})
def testNewRunShellImpl_withCwd(self):
- with self.assertShellCall('cd /some/test/path && ls'):
+ with self.assertCall(self.call.adb.Shell('cd /some/test/path && ls'), ''):
self.device.RunShellCommand('ls', cwd='/some/test/path')
def testNewRunShellImpl_withCwdQuoted(self):
- with self.assertShellCall("cd '/some test/path with/spaces' && ls"):
+ with self.assertCall(
+ self.call.adb.Shell("cd '/some test/path with/spaces' && ls"), ''):
self.device.RunShellCommand('ls', cwd='/some test/path with/spaces')
def testRunShellCommand_withSu(self):
- with self.assertShellCallSequence([
- ('su -c ls /root && ! ls /root', ''),
- ('su -c setprop service.adb.root 0', '')]):
- self.device.RunShellCommand('setprop service.adb.root 0', as_root=True)
-
- def testRunShellCommand_withRoot(self):
- with self.assertShellCallSequence([
- ('su -c ls /root && ! ls /root', _ShellError()),
- ('setprop service.adb.root 0', '')]):
+ with self.assertCalls(
+ (self.call.device.NeedsSU(), True),
+ (self.call.adb.Shell('su -c setprop service.adb.root 0'), '')):
self.device.RunShellCommand('setprop service.adb.root 0', as_root=True)
def testRunShellCommand_manyLines(self):
cmd = 'ls /some/path'
- with self.assertShellCall(cmd, 'file1\r\nfile2\r\nfile3\r\n'):
+ with self.assertCall(self.call.adb.Shell(cmd), 'file1\nfile2\nfile3\n'):
self.assertEquals(['file1', 'file2', 'file3'],
self.device.RunShellCommand(cmd))
def testRunShellCommand_singleLine_success(self):
cmd = 'echo $VALUE'
- with self.assertShellCall(cmd, 'some value\r\n'):
+ with self.assertCall(self.call.adb.Shell(cmd), 'some value\n'):
self.assertEquals('some value',
self.device.RunShellCommand(cmd, single_line=True))
def testRunShellCommand_singleLine_successEmptyLine(self):
cmd = 'echo $VALUE'
- with self.assertShellCall(cmd, '\r\n'):
+ with self.assertCall(self.call.adb.Shell(cmd), '\n'):
self.assertEquals('',
self.device.RunShellCommand(cmd, single_line=True))
def testRunShellCommand_singleLine_successWithoutEndLine(self):
cmd = 'echo -n $VALUE'
- with self.assertShellCall(cmd, 'some value'):
+ with self.assertCall(self.call.adb.Shell(cmd), 'some value'):
self.assertEquals('some value',
self.device.RunShellCommand(cmd, single_line=True))
def testRunShellCommand_singleLine_successNoOutput(self):
cmd = 'echo -n $VALUE'
- with self.assertShellCall(cmd, ''):
+ with self.assertCall(self.call.adb.Shell(cmd), ''):
self.assertEquals('',
self.device.RunShellCommand(cmd, single_line=True))
def testRunShellCommand_singleLine_failTooManyLines(self):
cmd = 'echo $VALUE'
- with self.assertShellCall(cmd, 'some value\r\nanother value\r\n'):
+ with self.assertCall(self.call.adb.Shell(cmd),
+ 'some value\nanother value\n'):
with self.assertRaises(device_errors.CommandFailedError):
self.device.RunShellCommand(cmd, single_line=True)
def testRunShellCommand_checkReturn_success(self):
cmd = 'echo $ANDROID_DATA'
- output = '/data\r\n'
- with self.assertShellCall(cmd, output):
+ output = '/data\n'
+ with self.assertCall(self.call.adb.Shell(cmd), output):
self.assertEquals([output.rstrip()],
self.device.RunShellCommand(cmd, check_return=True))
def testRunShellCommand_checkReturn_failure(self):
cmd = 'ls /root'
- output = 'opendir failed, Permission denied\r\n'
- with self.assertShellCall(cmd, _ShellError(output)):
+ output = 'opendir failed, Permission denied\n'
+ with self.assertCall(self.call.adb.Shell(cmd), self.ShellError(output)):
with self.assertRaises(device_errors.AdbShellCommandFailedError):
self.device.RunShellCommand(cmd, check_return=True)
def testRunShellCommand_checkReturn_disabled(self):
cmd = 'ls /root'
- output = 'opendir failed, Permission denied\r\n'
- with self.assertShellCall(cmd, _ShellError(output)):
+ output = 'opendir failed, Permission denied\n'
+ with self.assertCall(self.call.adb.Shell(cmd), self.ShellError(output)):
self.assertEquals([output.rstrip()],
self.device.RunShellCommand(cmd, check_return=False))
+@mock.patch('time.sleep', mock.Mock())
class DeviceUtilsKillAllTest(DeviceUtilsNewImplTest):
def testKillAll_noMatchingProcesses(self):
- output = 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n'
- with self.assertShellCallSequence([('ps', output)]):
+ with self.assertCall(self.call.adb.Shell('ps'),
+ 'USER PID PPID VSIZE RSS WCHAN PC NAME\n'):
with self.assertRaises(device_errors.CommandFailedError):
self.device.KillAll('test_process')
def testKillAll_nonblocking(self):
- with self.assertShellCallSequence([
- ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n'
- 'u0_a1 1234 174 123456 54321 ffffffff 456789ab '
- 'this.is.a.test.process\r\n'),
- ('kill -9 1234', '')]):
+ with self.assertCalls(
+ (self.call.adb.Shell('ps'),
+ 'USER PID PPID VSIZE RSS WCHAN PC NAME\n'
+ 'u0_a1 1234 174 123456 54321 ffffffff 456789ab some.process\n'),
+ (self.call.adb.Shell('kill -9 1234'), '')):
self.assertEquals(1,
- self.device.KillAll('this.is.a.test.process', blocking=False))
+ self.device.KillAll('some.process', blocking=False))
def testKillAll_blocking(self):
- with mock.patch('time.sleep'):
- with self.assertShellCallSequence([
- ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n'
- 'u0_a1 1234 174 123456 54321 ffffffff 456789ab '
- 'this.is.a.test.process\r\n'),
- ('kill -9 1234', ''),
- ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n'
- 'u0_a1 1234 174 123456 54321 ffffffff 456789ab '
- 'this.is.a.test.process\r\n'),
- ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n')]):
- self.assertEquals(1,
- self.device.KillAll('this.is.a.test.process', blocking=True))
+ with self.assertCalls(
+ (self.call.adb.Shell('ps'),
+ 'USER PID PPID VSIZE RSS WCHAN PC NAME\n'
+ 'u0_a1 1234 174 123456 54321 ffffffff 456789ab some.process\n'),
+ (self.call.adb.Shell('kill -9 1234'), ''),
+ (self.call.adb.Shell('ps'),
+ 'USER PID PPID VSIZE RSS WCHAN PC NAME\n'
+ 'u0_a1 1234 174 123456 54321 ffffffff 456789ab some.process\n'),
+ (self.call.adb.Shell('ps'),
+ 'USER PID PPID VSIZE RSS WCHAN PC NAME\n')):
+ self.assertEquals(1,
+ self.device.KillAll('some.process', blocking=True))
def testKillAll_root(self):
- with self.assertShellCallSequence([
- ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n'
- 'u0_a1 1234 174 123456 54321 ffffffff 456789ab '
- 'this.is.a.test.process\r\n'),
- ('su -c ls /root && ! ls /root', ''),
- ('su -c kill -9 1234', '')]):
+ with self.assertCalls(
+ (self.call.adb.Shell('ps'),
+ 'USER PID PPID VSIZE RSS WCHAN PC NAME\n'
+ 'u0_a1 1234 174 123456 54321 ffffffff 456789ab some.process\n'),
+ (self.call.device.NeedsSU(), True),
+ (self.call.adb.Shell('su -c kill -9 1234'), '')):
self.assertEquals(1,
- self.device.KillAll('this.is.a.test.process', as_root=True))
+ self.device.KillAll('some.process', as_root=True))
def testKillAll_sigterm(self):
- with self.assertShellCallSequence([
- ('ps', 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n'
- 'u0_a1 1234 174 123456 54321 ffffffff 456789ab '
- 'this.is.a.test.process\r\n'),
- ('kill -15 1234', '')]):
+ with self.assertCalls(
+ (self.call.adb.Shell('ps'),
+ 'USER PID PPID VSIZE RSS WCHAN PC NAME\n'
+ 'u0_a1 1234 174 123456 54321 ffffffff 456789ab some.process\n'),
+ (self.call.adb.Shell('kill -15 1234'), '')):
self.assertEquals(1,
- self.device.KillAll('this.is.a.test.process', signum=signal.SIGTERM))
+ self.device.KillAll('some.process', signum=signal.SIGTERM))
class DeviceUtilsStartActivityTest(DeviceUtilsOldImplTest):
@@ -974,6 +893,48 @@
self.device.StartActivity(test_intent)
+class DeviceUtilsStartInstrumentationTest(DeviceUtilsNewImplTest):
+
+ def testStartInstrumentation_nothing(self):
+ with self.assertCalls(
+ self.call.device.RunShellCommand(
+ ['am', 'instrument', 'test.package/.TestInstrumentation'],
+ check_return=True)):
+ self.device.StartInstrumentation(
+ 'test.package/.TestInstrumentation',
+ finish=False, raw=False, extras=None)
+
+ def testStartInstrumentation_finish(self):
+ with self.assertCalls(
+ (self.call.device.RunShellCommand(
+ ['am', 'instrument', '-w', 'test.package/.TestInstrumentation'],
+ check_return=True),
+ ['OK (1 test)'])):
+ output = self.device.StartInstrumentation(
+ 'test.package/.TestInstrumentation',
+ finish=True, raw=False, extras=None)
+ self.assertEquals(['OK (1 test)'], output)
+
+ def testStartInstrumentation_raw(self):
+ with self.assertCalls(
+ self.call.device.RunShellCommand(
+ ['am', 'instrument', '-r', 'test.package/.TestInstrumentation'],
+ check_return=True)):
+ self.device.StartInstrumentation(
+ 'test.package/.TestInstrumentation',
+ finish=False, raw=True, extras=None)
+
+ def testStartInstrumentation_extras(self):
+ with self.assertCalls(
+ self.call.device.RunShellCommand(
+ ['am', 'instrument', '-e', 'foo', 'Foo', '-e', 'bar', 'Bar',
+ 'test.package/.TestInstrumentation'],
+ check_return=True)):
+ self.device.StartInstrumentation(
+ 'test.package/.TestInstrumentation',
+ finish=False, raw=False, extras={'foo': 'Foo', 'bar': 'Bar'})
+
+
class DeviceUtilsBroadcastIntentTest(DeviceUtilsOldImplTest):
def testBroadcastIntent_noExtras(self):
@@ -1056,93 +1017,61 @@
def testPushChangedFilesIndividually_empty(self):
test_files = []
- self.device._PushChangedFilesIndividually(test_files)
- self.assertEqual(0, self.adb.Push.call_count)
+ with self.assertCalls():
+ self.device._PushChangedFilesIndividually(test_files)
def testPushChangedFilesIndividually_single(self):
test_files = [('/test/host/path', '/test/device/path')]
- self.device._PushChangedFilesIndividually(test_files)
- self.adb.Push.assert_called_once_with(
- '/test/host/path', '/test/device/path')
+ with self.assertCalls(self.call.adb.Push(*test_files[0])):
+ self.device._PushChangedFilesIndividually(test_files)
def testPushChangedFilesIndividually_multiple(self):
test_files = [
('/test/host/path/file1', '/test/device/path/file1'),
('/test/host/path/file2', '/test/device/path/file2')]
- self.device._PushChangedFilesIndividually(test_files)
- self.assertEqual(2, self.adb.Push.call_count)
- self.adb.Push.assert_any_call(
- '/test/host/path/file1', '/test/device/path/file1')
- self.adb.Push.assert_any_call(
- '/test/host/path/file2', '/test/device/path/file2')
+ with self.assertCalls(
+ self.call.adb.Push(*test_files[0]),
+ self.call.adb.Push(*test_files[1])):
+ self.device._PushChangedFilesIndividually(test_files)
-@mock.patch('pylib.device.commands.install_commands.Installed', new=None)
-@mock.patch('pylib.device.commands.install_commands.InstallCommands', new=None)
-class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsHybridImplTest):
-
- def setUp(self):
- super(DeviceUtilsPushChangedFilesZippedTest, self).setUp()
+class DeviceUtilsPushChangedFilesZippedTest(DeviceUtilsNewImplTest):
def testPushChangedFilesZipped_empty(self):
test_files = []
- self.device._PushChangedFilesZipped(test_files)
- self.assertEqual(0, self.adb.Push.call_count)
+ with self.assertCalls():
+ self.device._PushChangedFilesZipped(test_files)
+
+ def _testPushChangedFilesZipped_spec(self, test_files):
+ mock_zip_temp = mock.mock_open()
+ mock_zip_temp.return_value.name = '/test/temp/file/tmp.zip'
+ with self.assertCalls(
+ (mock.call.tempfile.NamedTemporaryFile(suffix='.zip'), mock_zip_temp),
+ (mock.call.multiprocessing.Process(
+ target=device_utils.DeviceUtils._CreateDeviceZip,
+ args=('/test/temp/file/tmp.zip', test_files)), mock.Mock()),
+ (self.call.device.GetExternalStoragePath(),
+ '/test/device/external_dir'),
+ self.call.adb.Push(
+ '/test/temp/file/tmp.zip', '/test/device/external_dir/tmp.zip'),
+ self.call.device.RunShellCommand(
+ ['unzip', '/test/device/external_dir/tmp.zip'],
+ as_root=True,
+ env={'PATH': '$PATH:/data/local/tmp/bin'},
+ check_return=True),
+ (self.call.device.IsOnline(), True),
+ self.call.device.RunShellCommand(
+ ['rm', '/test/device/external_dir/tmp.zip'], check_return=True)):
+ self.device._PushChangedFilesZipped(test_files)
def testPushChangedFilesZipped_single(self):
- test_files = [('/test/host/path/file1', '/test/device/path/file1')]
-
- self.device._GetExternalStoragePathImpl = mock.Mock(
- return_value='/test/device/external_dir')
- self.device.IsOnline = mock.Mock(return_value=True)
- self.device.RunShellCommand = mock.Mock()
- mock_zip_temp = mock.mock_open()
- mock_zip_temp.return_value.name = '/test/temp/file/tmp.zip'
- with mock.patch('multiprocessing.Process') as mock_zip_proc, (
- mock.patch('tempfile.NamedTemporaryFile', mock_zip_temp)):
- self.device._PushChangedFilesZipped(test_files)
-
- mock_zip_proc.assert_called_once_with(
- target=device_utils.DeviceUtils._CreateDeviceZip,
- args=('/test/temp/file/tmp.zip', test_files))
- self.adb.Push.assert_called_once_with(
- '/test/temp/file/tmp.zip', '/test/device/external_dir/tmp.zip')
- self.assertEqual(2, self.device.RunShellCommand.call_count)
- self.device.RunShellCommand.assert_any_call(
- ['unzip', '/test/device/external_dir/tmp.zip'],
- as_root=True,
- env={'PATH': '$PATH:/data/local/tmp/bin'},
- check_return=True)
- self.device.RunShellCommand.assert_any_call(
- ['rm', '/test/device/external_dir/tmp.zip'], check_return=True)
+ self._testPushChangedFilesZipped_spec(
+ [('/test/host/path/file1', '/test/device/path/file1')])
def testPushChangedFilesZipped_multiple(self):
- test_files = [('/test/host/path/file1', '/test/device/path/file1'),
- ('/test/host/path/file2', '/test/device/path/file2')]
-
- self.device._GetExternalStoragePathImpl = mock.Mock(
- return_value='/test/device/external_dir')
- self.device.IsOnline = mock.Mock(return_value=True)
- self.device.RunShellCommand = mock.Mock()
- mock_zip_temp = mock.mock_open()
- mock_zip_temp.return_value.name = '/test/temp/file/tmp.zip'
- with mock.patch('multiprocessing.Process') as mock_zip_proc, (
- mock.patch('tempfile.NamedTemporaryFile', mock_zip_temp)):
- self.device._PushChangedFilesZipped(test_files)
-
- mock_zip_proc.assert_called_once_with(
- target=device_utils.DeviceUtils._CreateDeviceZip,
- args=('/test/temp/file/tmp.zip', test_files))
- self.adb.Push.assert_called_once_with(
- '/test/temp/file/tmp.zip', '/test/device/external_dir/tmp.zip')
- self.assertEqual(2, self.device.RunShellCommand.call_count)
- self.device.RunShellCommand.assert_any_call(
- ['unzip', '/test/device/external_dir/tmp.zip'],
- as_root=True,
- env={'PATH': '$PATH:/data/local/tmp/bin'},
- check_return=True)
- self.device.RunShellCommand.assert_any_call(
- ['rm', '/test/device/external_dir/tmp.zip'], check_return=True)
+ self._testPushChangedFilesZipped_spec(
+ [('/test/host/path/file1', '/test/device/path/file1'),
+ ('/test/host/path/file2', '/test/device/path/file2')])
class DeviceUtilsFileExistsTest(DeviceUtilsOldImplTest):
@@ -1370,23 +1299,27 @@
self.device.WriteFile('/test/file/no.permissions.to.write',
'new test file contents', as_root=True)
+
class DeviceUtilsWriteTextFileTest(DeviceUtilsNewImplTest):
def testWriteTextFileTest_basic(self):
- with self.assertShellCall('echo some.string > /test/file/to.write'):
+ with self.assertCall(
+ self.call.adb.Shell('echo some.string > /test/file/to.write'), ''):
self.device.WriteTextFile('/test/file/to.write', 'some.string')
def testWriteTextFileTest_quoted(self):
- with self.assertShellCall(
- "echo 'some other string' > '/test/file/to write'"):
+ with self.assertCall(
+ self.call.adb.Shell("echo 'some other string' > '/test/file/to write'"),
+ ''):
self.device.WriteTextFile('/test/file/to write', 'some other string')
- def testWriteTextFileTest_asRoot(self):
- with self.assertShellCallSequence([
- ('su -c ls /root && ! ls /root', ''),
- ('su -c echo string > /test/file', '')]):
+ def testWriteTextFileTest_withSU(self):
+ with self.assertCalls(
+ (self.call.device.NeedsSU(), True),
+ (self.call.adb.Shell('su -c echo string > /test/file'), '')):
self.device.WriteTextFile('/test/file', 'string', as_root=True)
+
class DeviceUtilsLsTest(DeviceUtilsOldImplTest):
def testLs_nothing(self):
@@ -1512,29 +1445,29 @@
class DeviceUtilsGetPropTest(DeviceUtilsNewImplTest):
def testGetProp_exists(self):
- with self.assertShellCall('getprop this.is.a.test.property',
- 'test_property_value\r\n'):
- self.assertEqual('test_property_value',
- self.device.GetProp('this.is.a.test.property'))
+ with self.assertCall(
+ self.call.adb.Shell('getprop test.property'), 'property_value\n'):
+ self.assertEqual('property_value',
+ self.device.GetProp('test.property'))
def testGetProp_doesNotExist(self):
- with self.assertShellCall('getprop this.property.does.not.exist',
- '\r\n'):
- self.assertEqual('', self.device.GetProp('this.property.does.not.exist'))
+ with self.assertCall(
+ self.call.adb.Shell('getprop property.does.not.exist'), '\n'):
+ self.assertEqual('', self.device.GetProp('property.does.not.exist'))
def testGetProp_cachedRoProp(self):
- with self.assertShellCall('getprop ro.build.type',
- 'userdebug\r\n'):
+ with self.assertCall(
+ self.call.adb.Shell('getprop ro.build.type'), 'userdebug\n'):
self.assertEqual('userdebug',
self.device.GetProp('ro.build.type', cache=True))
self.assertEqual('userdebug',
self.device.GetProp('ro.build.type', cache=True))
def testGetProp_retryAndCache(self):
- with self.assertShellCallSequence([
- ('getprop ro.build.type', _ShellError()),
- ('getprop ro.build.type', _ShellError()),
- ('getprop ro.build.type', 'userdebug\r\n')]):
+ with self.assertCalls(
+ (self.call.adb.Shell('getprop ro.build.type'), self.ShellError()),
+ (self.call.adb.Shell('getprop ro.build.type'), self.ShellError()),
+ (self.call.adb.Shell('getprop ro.build.type'), 'userdebug\n')):
self.assertEqual('userdebug',
self.device.GetProp('ro.build.type',
cache=True, retries=3))
@@ -1546,46 +1479,55 @@
class DeviceUtilsSetPropTest(DeviceUtilsNewImplTest):
def testSetProp(self):
- with self.assertShellCall(
- "setprop this.is.a.test.property 'test property value'"):
- self.device.SetProp('this.is.a.test.property', 'test property value')
+ with self.assertCall(
+ self.call.adb.Shell("setprop test.property 'test value'"), ''):
+ self.device.SetProp('test.property', 'test value')
+
+ def testSetProp_check_succeeds(self):
+ with self.assertCalls(
+ (self.call.adb.Shell('setprop test.property new_value'), ''),
+ (self.call.adb.Shell('getprop test.property'), 'new_value')):
+ self.device.SetProp('test.property', 'new_value', check=True)
+
+ def testSetProp_check_fails(self):
+ with self.assertCalls(
+ (self.call.adb.Shell('setprop test.property new_value'), ''),
+ (self.call.adb.Shell('getprop test.property'), 'old_value')):
+ with self.assertRaises(device_errors.CommandFailedError):
+ self.device.SetProp('test.property', 'new_value', check=True)
class DeviceUtilsGetPidsTest(DeviceUtilsNewImplTest):
def testGetPids_noMatches(self):
- with self.assertShellCall(
- 'ps',
- 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n'
- 'user 1000 100 1024 1024 ffffffff 00000000 no.match\r\n'):
+ with self.assertCall(self.call.adb.Shell('ps'),
+ 'USER PID PPID VSIZE RSS WCHAN PC NAME\n'
+ 'user 1000 100 1024 1024 ffffffff 00000000 no.match\n'):
self.assertEqual({}, self.device.GetPids('does.not.match'))
def testGetPids_oneMatch(self):
- with self.assertShellCall(
- 'ps',
- 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n'
- 'user 1000 100 1024 1024 ffffffff 00000000 not.a.match\r\n'
- 'user 1001 100 1024 1024 ffffffff 00000000 one.match\r\n'):
+ with self.assertCall(self.call.adb.Shell('ps'),
+ 'USER PID PPID VSIZE RSS WCHAN PC NAME\n'
+ 'user 1000 100 1024 1024 ffffffff 00000000 not.a.match\n'
+ 'user 1001 100 1024 1024 ffffffff 00000000 one.match\n'):
self.assertEqual({'one.match': '1001'}, self.device.GetPids('one.match'))
def testGetPids_mutlipleMatches(self):
- with self.assertShellCall(
- 'ps',
- 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n'
- 'user 1000 100 1024 1024 ffffffff 00000000 not\r\n'
- 'user 1001 100 1024 1024 ffffffff 00000000 one.match\r\n'
- 'user 1002 100 1024 1024 ffffffff 00000000 two.match\r\n'
- 'user 1003 100 1024 1024 ffffffff 00000000 three.match\r\n'):
+ with self.assertCall(self.call.adb.Shell('ps'),
+ 'USER PID PPID VSIZE RSS WCHAN PC NAME\n'
+ 'user 1000 100 1024 1024 ffffffff 00000000 not\n'
+ 'user 1001 100 1024 1024 ffffffff 00000000 one.match\n'
+ 'user 1002 100 1024 1024 ffffffff 00000000 two.match\n'
+ 'user 1003 100 1024 1024 ffffffff 00000000 three.match\n'):
self.assertEqual(
{'one.match': '1001', 'two.match': '1002', 'three.match': '1003'},
self.device.GetPids('match'))
def testGetPids_exactMatch(self):
- with self.assertShellCall(
- 'ps',
- 'USER PID PPID VSIZE RSS WCHAN PC NAME\r\n'
- 'user 1000 100 1024 1024 ffffffff 00000000 not.exact.match\r\n'
- 'user 1234 100 1024 1024 ffffffff 00000000 exact.match\r\n'):
+ with self.assertCall(self.call.adb.Shell('ps'),
+ 'USER PID PPID VSIZE RSS WCHAN PC NAME\n'
+ 'user 1000 100 1024 1024 ffffffff 00000000 not.exact.match\n'
+ 'user 1234 100 1024 1024 ffffffff 00000000 exact.match\n'):
self.assertEqual(
{'not.exact.match': '1000', 'exact.match': '1234'},
self.device.GetPids('exact.match'))
@@ -1668,6 +1610,7 @@
class DeviceUtilsStrTest(DeviceUtilsOldImplTest):
+
def testStr_noAdbCalls(self):
with self.assertNoAdbCalls():
self.assertEqual('0123456789abcdef', str(self.device))
diff --git a/build/android/pylib/forwarder.py b/build/android/pylib/forwarder.py
index 5e45043..5f40a94 100644
--- a/build/android/pylib/forwarder.py
+++ b/build/android/pylib/forwarder.py
@@ -270,6 +270,7 @@
pid_file.seek(0)
pid_file.write(
'%s:%s' % (pid_for_lock, str(_GetProcessStartTime(pid_for_lock))))
+ pid_file.truncate()
def _InitDeviceLocked(self, device, tool):
"""Initializes the device_forwarder daemon for a specific device (once).
diff --git a/build/android/pylib/gtest/test_runner.py b/build/android/pylib/gtest/test_runner.py
index 75892ee..56e7449 100644
--- a/build/android/pylib/gtest/test_runner.py
+++ b/build/android/pylib/gtest/test_runner.py
@@ -7,9 +7,11 @@
import re
from pylib import pexpect
+from pylib import ports
from pylib.base import base_test_result
from pylib.base import base_test_runner
from pylib.device import device_errors
+from pylib.local import local_test_server_spawner
from pylib.perf import perf_control
@@ -53,6 +55,13 @@
if _TestSuiteRequiresHighPerfMode(self.test_package.suite_name):
self._perf_controller = perf_control.PerfControl(self.device)
+ if _TestSuiteRequiresMockTestServer(self.test_package.suite_name):
+ self._servers = [
+ local_test_server_spawner.LocalTestServerSpawner(
+ ports.AllocateTestServerPort(), self.device, self.tool)]
+ else:
+ self._servers = []
+
#override
def InstallTestPackage(self):
self.test_package.Install(self.device)
@@ -149,7 +158,8 @@
test_results = self._ParseTestOutput(
self.test_package.SpawnTestProcess(self.device))
finally:
- self.CleanupSpawningServerState()
+ for s in self._servers:
+ s.Reset()
# Calculate unknown test results.
all_tests = set(test.split(':'))
all_tests_ran = set([t.GetName() for t in test_results.GetAll()])
@@ -164,8 +174,8 @@
def SetUp(self):
"""Sets up necessary test enviroment for the test suite."""
super(TestRunner, self).SetUp()
- if _TestSuiteRequiresMockTestServer(self.test_package.suite_name):
- self.LaunchChromeTestServerSpawner()
+ for s in self._servers:
+ s.SetUp()
if _TestSuiteRequiresHighPerfMode(self.test_package.suite_name):
self._perf_controller.SetHighPerfMode()
self.tool.SetupEnvironment()
@@ -173,6 +183,8 @@
#override
def TearDown(self):
"""Cleans up the test enviroment for the test suite."""
+ for s in self._servers:
+ s.TearDown()
if _TestSuiteRequiresHighPerfMode(self.test_package.suite_name):
self._perf_controller.SetDefaultPerfMode()
self.test_package.ClearApplicationState(self.device)
diff --git a/build/android/pylib/instrumentation/test_jar.py b/build/android/pylib/instrumentation/test_jar.py
index c81258f..d83d00a 100644
--- a/build/android/pylib/instrumentation/test_jar.py
+++ b/build/android/pylib/instrumentation/test_jar.py
@@ -23,7 +23,7 @@
import unittest_util # pylint: disable=F0401
# If you change the cached output of proguard, increment this number
-PICKLE_FORMAT_VERSION = 2
+PICKLE_FORMAT_VERSION = 3
class TestJar(object):
@@ -55,6 +55,16 @@
if not self._GetCachedProguardData():
self._GetProguardData()
+ @staticmethod
+ def _CalculateMd5(path):
+ # TODO(jbudorick): Move MD5sum calculations out of here and
+ # AndroidCommands to their own module.
+ out = cmd_helper.GetCmdOutput(
+ [os.path.join(constants.GetOutDirectory(),
+ 'md5sum_bin_host'),
+ path])
+ return out
+
def _GetCachedProguardData(self):
if (os.path.exists(self._pickled_proguard_name) and
(os.path.getmtime(self._pickled_proguard_name) >
@@ -64,7 +74,9 @@
try:
with open(self._pickled_proguard_name, 'r') as r:
d = pickle.loads(r.read())
- if d['VERSION'] == PICKLE_FORMAT_VERSION:
+ jar_md5 = self._CalculateMd5(self._jar_path)
+ if (d['JAR_MD5SUM'] == jar_md5 and
+ d['VERSION'] == PICKLE_FORMAT_VERSION):
self._test_methods = d['TEST_METHODS']
return True
except:
@@ -182,7 +194,8 @@
logging.info('Storing proguard output to %s', self._pickled_proguard_name)
d = {'VERSION': PICKLE_FORMAT_VERSION,
- 'TEST_METHODS': self._test_methods}
+ 'TEST_METHODS': self._test_methods,
+ 'JAR_MD5SUM': self._CalculateMd5(self._jar_path)}
with open(self._pickled_proguard_name, 'w') as f:
f.write(pickle.dumps(d))
diff --git a/build/android/pylib/instrumentation/test_result.py b/build/android/pylib/instrumentation/test_result.py
index a0eea65..24e80a8 100644
--- a/build/android/pylib/instrumentation/test_result.py
+++ b/build/android/pylib/instrumentation/test_result.py
@@ -18,7 +18,8 @@
dur: Duration of the test run in milliseconds.
log: A string listing any errors.
"""
- super(InstrumentationTestResult, self).__init__(full_name, test_type, log)
+ super(InstrumentationTestResult, self).__init__(
+ full_name, test_type, dur, log)
name_pieces = full_name.rsplit('#')
if len(name_pieces) > 1:
self._test_name = name_pieces[1]
@@ -27,8 +28,3 @@
self._class_name = full_name
self._test_name = full_name
self._start_date = start_date
- self._dur = dur
-
- def GetDur(self):
- """Get the test duration."""
- return self._dur
diff --git a/build/android/pylib/instrumentation/test_runner.py b/build/android/pylib/instrumentation/test_runner.py
index 3bac503..60e00f3 100644
--- a/build/android/pylib/instrumentation/test_runner.py
+++ b/build/android/pylib/instrumentation/test_runner.py
@@ -369,16 +369,11 @@
Returns:
The raw output of am instrument as a list of lines.
"""
- # Build the 'am instrument' command
- instrumentation_path = (
- '%s/%s' % (self.test_pkg.GetPackageName(), self.options.test_runner))
-
- cmd = ['am', 'instrument', '-r']
- for k, v in self._GetInstrumentationArgs().iteritems():
- cmd.extend(['-e', k, v])
- cmd.extend(['-e', 'class', test])
- cmd.extend(['-w', instrumentation_path])
- return self.device.RunShellCommand(cmd, timeout=timeout, retries=0)
+ extras = self._GetInstrumentationArgs()
+ extras['class'] = test
+ return self.device.StartInstrumentation(
+ '%s/%s' % (self.test_pkg.GetPackageName(), self.options.test_runner),
+ raw=True, extras=extras, timeout=timeout, retries=0)
@staticmethod
def _ParseAmInstrumentRawOutput(raw_output):
@@ -506,7 +501,7 @@
self.tool.GetTimeoutScale())
if (self.device.GetProp('ro.build.version.sdk')
< constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN):
- timeout *= 4
+ timeout *= 10
start_ms = 0
duration_ms = 0
diff --git a/build/android/pylib/instrumentation/test_runner_test.py b/build/android/pylib/instrumentation/test_runner_test.py
index 039bb03..0f2845e 100755
--- a/build/android/pylib/instrumentation/test_runner_test.py
+++ b/build/android/pylib/instrumentation/test_runner_test.py
@@ -144,7 +144,7 @@
self.assertEqual('test.package.TestClass#testMethod', result.GetName())
self.assertEqual(base_test_result.ResultType.UNKNOWN, result.GetType())
self.assertEqual('', result.GetLog())
- self.assertEqual(1000, result.GetDur())
+ self.assertEqual(1000, result.GetDuration())
def testGenerateTestResult_testPassed(self):
statuses = [
@@ -253,17 +253,18 @@
def test_RunTest_verifyAdbShellCommand(self):
self.instance.options.test_runner = 'MyTestRunner'
- self.instance.device.RunShellCommand = mock.Mock()
+ self.instance.device.StartInstrumentation = mock.Mock()
self.instance.test_pkg.GetPackageName = mock.Mock(
return_value='test.package')
self.instance._GetInstrumentationArgs = mock.Mock(
return_value={'test_arg_key': 'test_arg_value'})
self.instance._RunTest('test.package.TestClass#testMethod', 100)
- self.instance.device.RunShellCommand.assert_called_with(
- ['am', 'instrument', '-r',
- '-e', 'test_arg_key', 'test_arg_value',
- '-e', 'class', 'test.package.TestClass#testMethod',
- '-w', 'test.package/MyTestRunner'],
+ self.instance.device.StartInstrumentation.assert_called_with(
+ 'test.package/MyTestRunner', raw=True,
+ extras={
+ 'test_arg_key': 'test_arg_value',
+ 'class': 'test.package.TestClass#testMethod'
+ },
timeout=100, retries=0)
if __name__ == '__main__':
diff --git a/build/android/pylib/linker/test_case.py b/build/android/pylib/linker/test_case.py
index 446bc84..f64a58b 100644
--- a/build/android/pylib/linker/test_case.py
+++ b/build/android/pylib/linker/test_case.py
@@ -340,7 +340,7 @@
base_test_result.BaseTestResult(
self.tagged_name,
status,
- logs))
+ log=logs))
return results
diff --git a/build/android/pylib/local/__init__.py b/build/android/pylib/local/__init__.py
new file mode 100644
index 0000000..4d6aabb
--- /dev/null
+++ b/build/android/pylib/local/__init__.py
@@ -0,0 +1,3 @@
+# Copyright 2014 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.
diff --git a/build/android/pylib/local/local_test_server_spawner.py b/build/android/pylib/local/local_test_server_spawner.py
new file mode 100644
index 0000000..77f552e
--- /dev/null
+++ b/build/android/pylib/local/local_test_server_spawner.py
@@ -0,0 +1,45 @@
+# Copyright 2014 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.
+
+from pylib import chrome_test_server_spawner
+from pylib import forwarder
+from pylib.base import test_server
+
+
+class LocalTestServerSpawner(test_server.TestServer):
+
+ def __init__(self, port, device, tool):
+ super(LocalTestServerSpawner, self).__init__()
+ self._device = device
+ self._spawning_server = chrome_test_server_spawner.SpawningServer(
+ port, device, tool)
+ self._tool = tool
+
+ @property
+ def server_address(self):
+ return self._spawning_server.server.server_address
+
+ @property
+ def port(self):
+ return self.server_address[1]
+
+ #override
+ def SetUp(self):
+ self._device.WriteFile(
+ '%s/net-test-server-ports' % self._device.GetExternalStoragePath(),
+ '%s:0' % str(self.port))
+ forwarder.Forwarder.Map(
+ [(self.port, self.port)], self._device, self._tool)
+ self._spawning_server.Start()
+
+ #override
+ def Reset(self):
+ self._spawning_server.CleanupState()
+
+ #override
+ def TearDown(self):
+ self.Reset()
+ self._spawning_server.Stop()
+ forwarder.Forwarder.UnmapDevicePort(self.port, self._device)
+
diff --git a/build/android/pylib/uiautomator/test_package.py b/build/android/pylib/uiautomator/test_package.py
index fd9120e..cb51fdf 100644
--- a/build/android/pylib/uiautomator/test_package.py
+++ b/build/android/pylib/uiautomator/test_package.py
@@ -11,6 +11,11 @@
class TestPackage(test_jar.TestJar):
+
+ UIAUTOMATOR_PATH = 'uiautomator/'
+ UIAUTOMATOR_DEVICE_DIR = os.path.join(constants.TEST_EXECUTABLE_DIR,
+ UIAUTOMATOR_PATH)
+
def __init__(self, jar_path, jar_info_path):
test_jar.TestJar.__init__(self, jar_info_path)
@@ -24,4 +29,5 @@
# Override.
def Install(self, device):
- device.PushChangedFiles([(self._jar_path, constants.TEST_EXECUTABLE_DIR)])
+ device.PushChangedFiles([(self._jar_path, self.UIAUTOMATOR_DEVICE_DIR +
+ self.GetPackageName())])
diff --git a/build/android/pylib/uiautomator/test_runner.py b/build/android/pylib/uiautomator/test_runner.py
index c0f8f15..f4e5730 100644
--- a/build/android/pylib/uiautomator/test_runner.py
+++ b/build/android/pylib/uiautomator/test_runner.py
@@ -73,7 +73,8 @@
package=self._package),
blocking=True,
force_stop=True)
- cmd = ['uiautomator', 'runtest', self.test_pkg.GetPackageName(),
+ cmd = ['uiautomator', 'runtest',
+ self.test_pkg.UIAUTOMATOR_PATH + self.test_pkg.GetPackageName(),
'-e', 'class', test]
return self.device.RunShellCommand(cmd, timeout=timeout, retries=0)
diff --git a/build/android/pylib/utils/flakiness_dashboard_results_uploader.py b/build/android/pylib/utils/flakiness_dashboard_results_uploader.py
index 246c83b..ff286b6 100644
--- a/build/android/pylib/utils/flakiness_dashboard_results_uploader.py
+++ b/build/android/pylib/utils/flakiness_dashboard_results_uploader.py
@@ -131,7 +131,7 @@
test_result = json_results_generator.TestResult(
test=single_test_result.GetName(),
failed=failed,
- elapsed_time=single_test_result.GetDur() / 1000)
+ elapsed_time=single_test_result.GetDuration() / 1000)
# The WebKit TestResult object sets the modifier it based on test name.
# Since we don't use the same test naming convention as WebKit the
# modifier will be wrong, so we need to overwrite it.
diff --git a/build/android/pylib/utils/isolator.py b/build/android/pylib/utils/isolator.py
index 9ee5671..afbee2a 100644
--- a/build/android/pylib/utils/isolator.py
+++ b/build/android/pylib/utils/isolator.py
@@ -32,7 +32,6 @@
'component': 'static_library',
'fastbuild': '0',
'icu_use_data_file_flag': '1',
- 'libpeer_target_type': 'static_library',
'lsan': '0',
# TODO(maruel): This may not always be true.
'target_arch': 'arm',
diff --git a/build/android/pylib/utils/mock_calls.py b/build/android/pylib/utils/mock_calls.py
new file mode 100755
index 0000000..1f9d8e3
--- /dev/null
+++ b/build/android/pylib/utils/mock_calls.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+"""
+A test facility to assert call sequences while mocking their behavior.
+"""
+
+import os
+import sys
+import unittest
+
+from pylib import constants
+
+sys.path.append(os.path.join(
+ constants.DIR_SOURCE_ROOT, 'third_party', 'pymock'))
+import mock # pylint: disable=F0401
+
+
+class TestCase(unittest.TestCase):
+ """Adds assertCalls to TestCase objects."""
+ class _AssertCalls(object):
+ def __init__(self, test_case, expected_calls, watched):
+ def call_action(pair):
+ if isinstance(pair, type(mock.call)):
+ return (pair, None)
+ else:
+ return pair
+
+ def do_check(call):
+ def side_effect(*args, **kwargs):
+ received_call = call(*args, **kwargs)
+ self._test_case.assertTrue(
+ self._expected_calls,
+ msg=('Unexpected call: %s' % str(received_call)))
+ expected_call, action = self._expected_calls.pop(0)
+ self._test_case.assertTrue(
+ received_call == expected_call,
+ msg=('Expected call missmatch:\n'
+ ' expected: %s\n'
+ ' received: %s\n'
+ % (str(expected_call), str(received_call))))
+ if callable(action):
+ return action(*args, **kwargs)
+ else:
+ return action
+ return side_effect
+
+ self._test_case = test_case
+ self._expected_calls = [call_action(pair) for pair in expected_calls]
+ watched = watched.copy() # do not pollute the caller's dict
+ watched.update((call.parent.name, call.parent)
+ for call, _ in self._expected_calls)
+ self._patched = [test_case.patch_call(call, side_effect=do_check(call))
+ for call in watched.itervalues()]
+
+ def __enter__(self):
+ for patch in self._patched:
+ patch.__enter__()
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ for patch in self._patched:
+ patch.__exit__(exc_type, exc_val, exc_tb)
+ if exc_type is None:
+ missing = ''.join(' expected: %s\n' % str(call)
+ for call, _ in self._expected_calls)
+ self._test_case.assertFalse(
+ missing,
+ msg='Expected calls not found:\n' + missing)
+
+ def __init__(self, *args, **kwargs):
+ super(TestCase, self).__init__(*args, **kwargs)
+ self.call = mock.call.self
+ self._watched = {}
+
+ def call_target(self, call):
+ """Resolve a self.call instance to the target it represents.
+
+ Args:
+ call: a self.call instance, e.g. self.call.adb.Shell
+
+ Returns:
+ The target object represented by the call, e.g. self.adb.Shell
+
+ Raises:
+ ValueError if the path of the call does not start with "self", i.e. the
+ target of the call is external to the self object.
+ AttributeError if the path of the call does not specify a valid
+ chain of attributes (without any calls) starting from "self".
+ """
+ path = call.name.split('.')
+ if path.pop(0) != 'self':
+ raise ValueError("Target %r outside of 'self' object" % call.name)
+ target = self
+ for attr in path:
+ target = getattr(target, attr)
+ return target
+
+ def patch_call(self, call, **kwargs):
+ """Patch the target of a mock.call instance.
+
+ Args:
+ call: a mock.call instance identifying a target to patch
+ Extra keyword arguments are processed by mock.patch
+
+ Returns:
+ A context manager to mock/unmock the target of the call
+ """
+ if call.name.startswith('self.'):
+ target = self.call_target(call.parent)
+ _, attribute = call.name.rsplit('.', 1)
+ return mock.patch.object(target, attribute, **kwargs)
+ else:
+ return mock.patch(call.name, **kwargs)
+
+ def watchCalls(self, calls):
+ """Add calls to the set of watched calls.
+
+ Args:
+ calls: a sequence of mock.call instances identifying targets to watch
+ """
+ self._watched.update((call.name, call) for call in calls)
+
+ def watchMethodCalls(self, call):
+ """Watch all public methods of the target identified by a self.call.
+
+ Args:
+ call: a self.call instance indetifying an object
+ """
+ target = self.call_target(call)
+ self.watchCalls(getattr(call, method)
+ for method in dir(target.__class__)
+ if not method.startswith('_'))
+
+ def clearWatched(self):
+ """Clear the set of watched calls."""
+ self._watched = {}
+
+ def assertCalls(self, *calls):
+ """A context manager to assert that a sequence of calls is made.
+
+ During the assertion, a number of functions and methods will be "watched",
+ and any calls made to them is expected to appear---in the exact same order,
+ and with the exact same arguments---as specified by the argument |calls|.
+
+ By default, the targets of all expected calls are watched. Further targets
+ to watch may be added using watchCalls and watchMethodCalls.
+
+ Optionaly, each call may be accompanied by an action. If the action is a
+ (non-callable) value, this value will be used as the return value given to
+ the caller when the matching call is found. Alternatively, if the action is
+ a callable, the action will be then called with the same arguments as the
+ intercepted call, so that it can provide a return value or perform other
+ side effects. If the action is missing, a return value of None is assumed.
+
+ Note that mock.Mock objects are often convenient to use as a callable
+ action, e.g. to raise exceptions or return other objects which are
+ themselves callable.
+
+ Args:
+ calls: each argument is either a pair (expected_call, action) or just an
+ expected_call, where expected_call is a mock.call instance.
+
+ Raises:
+ AssertionError if the watched targets do not receive the exact sequence
+ of calls specified. Missing calls, extra calls, and calls with
+ mismatching arguments, all cause the assertion to fail.
+ """
+ return self._AssertCalls(self, calls, self._watched)
+
+ def assertCall(self, call, action=None):
+ return self.assertCalls((call, action))
+
diff --git a/build/android/pylib/utils/mock_calls_test.py b/build/android/pylib/utils/mock_calls_test.py
new file mode 100755
index 0000000..1b474af
--- /dev/null
+++ b/build/android/pylib/utils/mock_calls_test.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+"""
+Unit tests for the contents of mock_calls.py.
+"""
+
+import logging
+import os
+import sys
+import unittest
+
+from pylib import constants
+from pylib.utils import mock_calls
+
+sys.path.append(os.path.join(
+ constants.DIR_SOURCE_ROOT, 'third_party', 'pymock'))
+import mock # pylint: disable=F0401
+
+
+class _DummyAdb(object):
+ def __str__(self):
+ return '0123456789abcdef'
+
+ def Push(self, host_path, device_path):
+ logging.debug('(device %s) pushing %r to %r', self, host_path, device_path)
+
+ def IsOnline(self):
+ logging.debug('(device %s) checking device online', self)
+ return True
+
+ def Shell(self, cmd):
+ logging.debug('(device %s) running command %r', self, cmd)
+ return "nice output\n"
+
+ def Reboot(self):
+ logging.debug('(device %s) rebooted!', self)
+
+
+class TestCaseWithAssertCallsTest(mock_calls.TestCase):
+ def setUp(self):
+ self.adb = _DummyAdb()
+
+ def ShellError(self):
+ def action(cmd):
+ raise ValueError('(device %s) command %r is not nice' % (self.adb, cmd))
+ return action
+
+ def get_answer(self):
+ logging.debug("called 'get_answer' of %r object", self)
+ return 42
+
+ def echo(self, thing):
+ logging.debug("called 'echo' of %r object", self)
+ return thing
+
+ def testCallTarget_succeds(self):
+ self.assertEquals(self.adb.Shell,
+ self.call_target(self.call.adb.Shell))
+
+ def testCallTarget_failsExternal(self):
+ with self.assertRaises(ValueError):
+ self.call_target(mock.call.sys.getcwd)
+
+ def testCallTarget_failsUnknownAttribute(self):
+ with self.assertRaises(AttributeError):
+ self.call_target(self.call.adb.Run)
+
+ def testCallTarget_failsIntermediateCalls(self):
+ with self.assertRaises(AttributeError):
+ self.call_target(self.call.adb.RunShell('cmd').append)
+
+ def testPatchCall_method(self):
+ self.assertEquals(42, self.get_answer())
+ with self.patch_call(self.call.get_answer, return_value=123):
+ self.assertEquals(123, self.get_answer())
+ self.assertEquals(42, self.get_answer())
+
+ def testPatchCall_attribute_method(self):
+ with self.patch_call(self.call.adb.Shell, return_value='hello'):
+ self.assertEquals('hello', self.adb.Shell('echo hello'))
+
+ def testPatchCall_global(self):
+ with self.patch_call(mock.call.os.getcwd, return_value='/some/path'):
+ self.assertEquals('/some/path', os.getcwd())
+
+ def testPatchCall_withSideEffect(self):
+ with self.patch_call(self.call.adb.Shell, side_effect=ValueError):
+ with self.assertRaises(ValueError):
+ self.adb.Shell('echo hello')
+
+ def testAssertCalls_succeeds_simple(self):
+ self.assertEquals(42, self.get_answer())
+ with self.assertCall(self.call.get_answer(), 123):
+ self.assertEquals(123, self.get_answer())
+ self.assertEquals(42, self.get_answer())
+
+ def testAssertCalls_succeeds_multiple(self):
+ with self.assertCalls(
+ (mock.call.os.getcwd(), '/some/path'),
+ (self.call.echo('hello'), 'hello'),
+ (self.call.get_answer(), 11),
+ self.call.adb.Push('this_file', 'that_file'),
+ (self.call.get_answer(), 12)):
+ self.assertEquals(os.getcwd(), '/some/path')
+ self.assertEquals('hello', self.echo('hello'))
+ self.assertEquals(11, self.get_answer())
+ self.adb.Push('this_file', 'that_file')
+ self.assertEquals(12, self.get_answer())
+
+ def testAsserCalls_succeeds_withAction(self):
+ with self.assertCall(
+ self.call.adb.Shell('echo hello'), self.ShellError()):
+ with self.assertRaises(ValueError):
+ self.adb.Shell('echo hello')
+
+ def testAssertCalls_fails_tooManyCalls(self):
+ with self.assertRaises(AssertionError):
+ with self.assertCalls(self.call.adb.IsOnline()):
+ self.adb.IsOnline()
+ self.adb.IsOnline()
+
+ def testAssertCalls_fails_tooFewCalls(self):
+ with self.assertRaises(AssertionError):
+ with self.assertCalls(self.call.adb.IsOnline()):
+ pass
+
+ def testAssertCalls_succeeds_extraCalls(self):
+ # we are not watching Reboot, so the assertion succeeds
+ with self.assertCalls(self.call.adb.IsOnline()):
+ self.adb.IsOnline()
+ self.adb.Reboot()
+
+ def testAssertCalls_fails_extraCalls(self):
+ self.watchCalls([self.call.adb.Reboot])
+ # this time we are also watching Reboot, so the assertion fails
+ with self.assertRaises(AssertionError):
+ with self.assertCalls(self.call.adb.IsOnline()):
+ self.adb.IsOnline()
+ self.adb.Reboot()
+
+ def testAssertCalls_succeeds_NoCalls(self):
+ self.watchMethodCalls(self.call.adb) # we are watching all adb methods
+ with self.assertCalls():
+ pass
+
+ def testAssertCalls_fails_NoCalls(self):
+ self.watchMethodCalls(self.call.adb)
+ with self.assertRaises(AssertionError):
+ with self.assertCalls():
+ self.adb.IsOnline()
+
+
+if __name__ == '__main__':
+ logging.getLogger().setLevel(logging.DEBUG)
+ unittest.main(verbosity=2)
+
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index 2e39c3c..d5c9b35 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -64,6 +64,11 @@
group.add_option('--build-directory', dest='build_directory',
help=('Path to the directory in which build files are'
' located (should not include build type)'))
+ group.add_option('--output-directory', dest='output_directory',
+ help=('Path to the directory in which build files are'
+ ' located (must include build type). This will take'
+ ' precedence over --debug, --release and'
+ ' --build-directory'))
group.add_option('--num_retries', dest='num_retries', type='int',
default=2,
help=('Number of retries for a test before '
@@ -95,6 +100,8 @@
constants.SetBuildType(options.build_type)
if options.build_directory:
constants.SetBuildDirectory(options.build_directory)
+ if options.output_directory:
+ constants.SetOutputDirectort(options.output_directory)
if options.environment not in constants.VALID_ENVIRONMENTS:
error_func('--environment must be one of: %s' %
', '.join(constants.VALID_ENVIRONMENTS))