Fix event client handling under new startup process. (#222)
* Fix constructor call for event handler
* Refactor important pieces of jsonrpc test to a common base class.
* Add unit tests for starting snippets.
* Fix incorrect usages of assertRaises.
diff --git a/mobly/controllers/android_device_lib/snippet_client.py b/mobly/controllers/android_device_lib/snippet_client.py
index 1470927..f47308a 100644
--- a/mobly/controllers/android_device_lib/snippet_client.py
+++ b/mobly/controllers/android_device_lib/snippet_client.py
@@ -138,10 +138,8 @@
def _start_event_client(self):
"""Overrides superclass."""
event_client = SnippetClient(
- package=self.package,
- host_port=self.host_port,
- adb_proxy=self._adb,
- log=self.log)
+ package=self.package, adb_proxy=self._adb, log=self.log)
+ event_client.host_port = self.host_port
event_client.connect(self.uid,
jsonrpc_client_base.JsonRpcCommand.CONTINUE)
return event_client
diff --git a/tests/lib/jsonrpc_client_test_base.py b/tests/lib/jsonrpc_client_test_base.py
new file mode 100755
index 0000000..c4ca7a8
--- /dev/null
+++ b/tests/lib/jsonrpc_client_test_base.py
@@ -0,0 +1,71 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from builtins import str
+
+import mock
+import unittest
+
+
+class JsonRpcClientTestBase(unittest.TestCase):
+ """Base class for tests of JSONRPC clients.
+
+ Contains infrastructure for mocking responses.
+ """
+
+ MOCK_RESP = (
+ b'{"id": 0, "result": 123, "error": null, "status": 1, "uid": 1, '
+ b'"callback": null}')
+ MOCK_RESP_WITHOUT_CALLBACK = (
+ b'{"id": 0, "result": 123, "error": null, "status": 1, "uid": 1}')
+ MOCK_RESP_TEMPLATE = (
+ '{"id": %d, "result": 123, "error": null, "status": 1, "uid": 1, '
+ '"callback": null}')
+ MOCK_RESP_UNKNOWN_STATUS = (
+ b'{"id": 0, "result": 123, "error": null, "status": 0, '
+ b'"callback": null}')
+ MOCK_RESP_WITH_CALLBACK = (
+ b'{"id": 0, "result": 123, "error": null, "status": 1, "uid": 1, '
+ b'"callback": "1-0"}')
+ MOCK_RESP_WITH_ERROR = b'{"id": 0, "error": 1, "status": 1, "uid": 1}'
+
+ class MockSocketFile(object):
+ def __init__(self, resp):
+ self.resp = resp
+ self.last_write = None
+
+ def write(self, msg):
+ self.last_write = msg
+
+ def readline(self):
+ return self.resp
+
+ def flush(self):
+ pass
+
+ def setup_mock_socket_file(self, mock_create_connection, resp=MOCK_RESP):
+ """Sets up a fake socket file from the mock connection.
+
+ Args:
+ mock_create_connection: The mock method for creating a method.
+ resp: (str) response to give. MOCK_RESP by default.
+
+ Returns:
+ The mock file that will be injected into the code.
+ """
+ fake_file = self.MockSocketFile(resp)
+ fake_conn = mock.MagicMock()
+ fake_conn.makefile.return_value = fake_file
+ mock_create_connection.return_value = fake_conn
+ return fake_file
diff --git a/tests/mobly/base_test_test.py b/tests/mobly/base_test_test.py
index 58ceba1..1a2a759 100755
--- a/tests/mobly/base_test_test.py
+++ b/tests/mobly/base_test_test.py
@@ -127,8 +127,8 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
expected_msg = ('Test method name not_a_test_something does not follow '
- 'naming convention test_*, abort.')
- with self.assertRaises(base_test.Error, msg=expected_msg):
+ 'naming convention test_\*, abort.')
+ with self.assertRaisesRegexp(base_test.Error, expected_msg):
bt_cls.run(test_names=["not_a_test_something"])
def test_default_execution_of_all_tests(self):
@@ -790,9 +790,9 @@
"""Missing a required param should raise an error."""
required = ["something"]
bc = base_test.BaseTestClass(self.mock_test_cls_configs)
- expected_msg = ("Missing required user param '%s' in test "
- "configuration.") % required[0]
- with self.assertRaises(base_test.Error, msg=expected_msg):
+ expected_msg = ('Missing required user param "%s" in test '
+ 'configuration.') % required[0]
+ with self.assertRaisesRegexp(base_test.Error, expected_msg):
bc.unpack_userparams(required)
def test_unpack_userparams_optional(self):
diff --git a/tests/mobly/controllers/android_device_lib/jsonrpc_client_base_test.py b/tests/mobly/controllers/android_device_lib/jsonrpc_client_base_test.py
index 3fbe8cf..7263bdc 100755
--- a/tests/mobly/controllers/android_device_lib/jsonrpc_client_base_test.py
+++ b/tests/mobly/controllers/android_device_lib/jsonrpc_client_base_test.py
@@ -20,33 +20,7 @@
import unittest
from mobly.controllers.android_device_lib import jsonrpc_client_base
-
-MOCK_RESP = (b'{"id": 0, "result": 123, "error": null, "status": 1, "uid": 1,'
- b' "callback": null}')
-MOCK_RESP_WITHOUT_CALLBACK = (b'{"id": 0, "result": 123, "error": null, '
- b'"status": 1, "uid": 1}')
-MOCK_RESP_TEMPLATE = ('{"id": %d, "result": 123, "error": null, "status": 1, '
- '"uid": 1, "callback": null}')
-MOCK_RESP_UNKNOWN_STATUS = (b'{"id": 0, "result": 123, "error": null, '
- b'"status": 0, "callback": null}')
-MOCK_RESP_WITH_CALLBACK = (b'{"id": 0, "result": 123, "error": null, '
- b'"status": 1, "uid": 1, "callback": "1-0"}')
-MOCK_RESP_WITH_ERROR = b'{"id": 0, "error": 1, "status": 1, "uid": 1}'
-
-
-class MockSocketFile(object):
- def __init__(self, resp):
- self.resp = resp
- self.last_write = None
-
- def write(self, msg):
- self.last_write = msg
-
- def readline(self):
- return self.resp
-
- def flush(self):
- pass
+from tests.lib import jsonrpc_client_test_base
class FakeRpcClient(jsonrpc_client_base.JsonRpcClientBase):
@@ -54,25 +28,10 @@
super(FakeRpcClient, self).__init__(app_name='FakeRpcClient')
-class JsonRpcClientBaseTest(unittest.TestCase):
+class JsonRpcClientBaseTest(jsonrpc_client_test_base.JsonRpcClientTestBase):
"""Unit tests for mobly.controllers.android_device_lib.jsonrpc_client_base.
"""
- def setup_mock_socket_file(self, mock_create_connection):
- """Sets up a fake socket file from the mock connection.
-
- Args:
- mock_create_connection: The mock method for creating a method.
-
- Returns:
- The mock file that will be injected into the code.
- """
- fake_file = MockSocketFile(MOCK_RESP)
- fake_conn = mock.MagicMock()
- fake_conn.makefile.return_value = fake_file
- mock_create_connection.return_value = fake_conn
- return fake_file
-
@mock.patch('socket.create_connection')
def test_open_timeout_io_error(self, mock_create_connection):
"""Test socket timeout with io error
@@ -104,11 +63,11 @@
Test that if there is an error in the jsonrpc handshake then a protocol
error will be raised.
"""
- fake_conn = mock.MagicMock()
- fake_conn.makefile.return_value = MockSocketFile(None)
- mock_create_connection.return_value = fake_conn
- with self.assertRaises(jsonrpc_client_base.ProtocolError):
- client = FakeRpcClient()
+ self.setup_mock_socket_file(mock_create_connection, resp=None)
+ client = FakeRpcClient()
+ with self.assertRaisesRegexp(
+ jsonrpc_client_base.ProtocolError,
+ jsonrpc_client_base.ProtocolError.NO_RESPONSE_FROM_HANDSHAKE):
client.connect()
@mock.patch('socket.create_connection')
@@ -118,13 +77,9 @@
Test that at the end of a handshake with no errors the client object
has the correct parameters.
"""
- fake_conn = mock.MagicMock()
- fake_conn.makefile.return_value = MockSocketFile(MOCK_RESP)
- mock_create_connection.return_value = fake_conn
-
+ self.setup_mock_socket_file(mock_create_connection)
client = FakeRpcClient()
client.connect()
-
self.assertEqual(client.uid, 1)
@mock.patch('socket.create_connection')
@@ -134,37 +89,13 @@
Test that when the handshake is given an unknown status then the client
will not be given a uid.
"""
- fake_conn = mock.MagicMock()
- fake_conn.makefile.return_value = MockSocketFile(
- MOCK_RESP_UNKNOWN_STATUS)
- mock_create_connection.return_value = fake_conn
-
+ self.setup_mock_socket_file(
+ mock_create_connection, resp=self.MOCK_RESP_UNKNOWN_STATUS)
client = FakeRpcClient()
client.connect()
-
self.assertEqual(client.uid, jsonrpc_client_base.UNKNOWN_UID)
@mock.patch('socket.create_connection')
- def test_connect_no_response(self, mock_create_connection):
- """Test handshake no response
-
- Test that if a handshake recieves no response then it will give a
- protocol error.
- """
- fake_file = self.setup_mock_socket_file(mock_create_connection)
-
- client = FakeRpcClient()
- client.connect()
-
- fake_file.resp = None
-
- with self.assertRaises(
- jsonrpc_client_base.ProtocolError,
- msg=
- jsonrpc_client_base.ProtocolError.NO_RESPONSE_FROM_HANDSHAKE):
- client.some_rpc(1, 2, 3)
-
- @mock.patch('socket.create_connection')
def test_rpc_error_response(self, mock_create_connection):
"""Test rpc that is given an error response
@@ -176,9 +107,9 @@
client = FakeRpcClient()
client.connect()
- fake_file.resp = MOCK_RESP_WITH_ERROR
+ fake_file.resp = self.MOCK_RESP_WITH_ERROR
- with self.assertRaises(jsonrpc_client_base.ApiError, msg=1):
+ with self.assertRaisesRegexp(jsonrpc_client_base.ApiError, '1'):
client.some_rpc(1, 2, 3)
@mock.patch('socket.create_connection')
@@ -193,7 +124,7 @@
client = FakeRpcClient()
client.connect()
- fake_file.resp = MOCK_RESP_WITH_CALLBACK
+ fake_file.resp = self.MOCK_RESP_WITH_CALLBACK
client._event_client = mock.Mock()
callback = client.some_rpc(1, 2, 3)
@@ -212,11 +143,11 @@
client = FakeRpcClient()
client.connect()
- fake_file.resp = (MOCK_RESP_TEMPLATE % 52).encode('utf8')
+ fake_file.resp = (self.MOCK_RESP_TEMPLATE % 52).encode('utf8')
- with self.assertRaises(
+ with self.assertRaisesRegexp(
jsonrpc_client_base.ProtocolError,
- msg=jsonrpc_client_base.ProtocolError.MISMATCHED_API_ID):
+ jsonrpc_client_base.ProtocolError.MISMATCHED_API_ID):
client.some_rpc(1, 2, 3)
@mock.patch('socket.create_connection')
@@ -233,9 +164,9 @@
fake_file.resp = None
- with self.assertRaises(
+ with self.assertRaisesRegexp(
jsonrpc_client_base.ProtocolError,
- msg=jsonrpc_client_base.ProtocolError.NO_RESPONSE_FROM_SERVER):
+ jsonrpc_client_base.ProtocolError.NO_RESPONSE_FROM_SERVER):
client.some_rpc(1, 2, 3)
@mock.patch('socket.create_connection')
@@ -265,10 +196,8 @@
Logic is the same as test_rpc_send_to_socket.
"""
- fake_file = MockSocketFile(MOCK_RESP_WITHOUT_CALLBACK)
- fake_conn = mock.MagicMock()
- fake_conn.makefile.return_value = fake_file
- mock_create_connection.return_value = fake_conn
+ fake_file = self.setup_mock_socket_file(
+ mock_create_connection, resp=self.MOCK_RESP_WITHOUT_CALLBACK)
client = FakeRpcClient()
client.connect()
@@ -293,11 +222,11 @@
client.connect()
for i in range(0, 10):
- fake_file.resp = (MOCK_RESP_TEMPLATE % i).encode('utf-8')
+ fake_file.resp = (self.MOCK_RESP_TEMPLATE % i).encode('utf-8')
client.some_rpc()
self.assertEquals(next(client._counter), 10)
-if __name__ == "__main__":
+if __name__ == '__main__':
unittest.main()
diff --git a/tests/mobly/controllers/android_device_lib/snippet_client_test.py b/tests/mobly/controllers/android_device_lib/snippet_client_test.py
index c5b7df4..911f53e 100755
--- a/tests/mobly/controllers/android_device_lib/snippet_client_test.py
+++ b/tests/mobly/controllers/android_device_lib/snippet_client_test.py
@@ -20,10 +20,11 @@
from mobly.controllers.android_device_lib import jsonrpc_client_base
from mobly.controllers.android_device_lib import snippet_client
+from tests.lib import jsonrpc_client_test_base
MOCK_PACKAGE_NAME = 'some.package.name'
MOCK_MISSING_PACKAGE_NAME = 'not.installed'
-JSONRPC_BASE_PACKAGE = 'mobly.controllers.android_device_lib.jsonrpc_client_base.JsonRpcClientBase'
+JSONRPC_BASE_CLASS = 'mobly.controllers.android_device_lib.jsonrpc_client_base.JsonRpcClientBase'
class MockAdbProxy(object):
@@ -63,47 +64,62 @@
return adb_call
-class JsonRpcClientBaseTest(unittest.TestCase):
+class SnippetClientTest(jsonrpc_client_test_base.JsonRpcClientTestBase):
"""Unit tests for mobly.controllers.android_device_lib.snippet_client.
"""
- @mock.patch('socket.create_connection')
- @mock.patch(JSONRPC_BASE_PACKAGE)
- def test_check_app_installed_normal(self, mock_create_connection,
- mock_client_base):
+ def test_check_app_installed_normal(self):
sc = self._make_client()
sc._check_app_installed()
- @mock.patch('socket.create_connection')
- @mock.patch(JSONRPC_BASE_PACKAGE)
- def test_check_app_installed_fail_app_not_installed(
- self, mock_create_connection, mock_client_base):
+ def test_check_app_installed_fail_app_not_installed(self):
sc = self._make_client(MockAdbProxy(apk_not_installed=True))
expected_msg = '%s is not installed on .*' % MOCK_PACKAGE_NAME
with self.assertRaisesRegexp(jsonrpc_client_base.AppStartError,
expected_msg):
sc._check_app_installed()
- @mock.patch('socket.create_connection')
- @mock.patch(JSONRPC_BASE_PACKAGE)
- def test_check_app_installed_fail_not_instrumented(
- self, mock_create_connection, mock_client_base):
+ def test_check_app_installed_fail_not_instrumented(self):
sc = self._make_client(MockAdbProxy(apk_not_instrumented=True))
expected_msg = '%s is installed on .*, but it is not instrumented.' % MOCK_PACKAGE_NAME
with self.assertRaisesRegexp(jsonrpc_client_base.AppStartError,
expected_msg):
sc._check_app_installed()
- @mock.patch('socket.create_connection')
- @mock.patch(JSONRPC_BASE_PACKAGE)
- def test_check_app_installed_fail_target_not_installed(
- self, mock_create_connection, mock_client_base):
+ def test_check_app_installed_fail_target_not_installed(self):
sc = self._make_client(MockAdbProxy(target_not_installed=True))
expected_msg = 'Instrumentation target %s is not installed on .*' % MOCK_MISSING_PACKAGE_NAME
with self.assertRaisesRegexp(jsonrpc_client_base.AppStartError,
expected_msg):
sc._check_app_installed()
+ @mock.patch('socket.create_connection')
+ def test_snippet_start(self, mock_create_connection):
+ self.setup_mock_socket_file(mock_create_connection)
+ client = self._make_client()
+ client.connect()
+ result = client.testSnippetCall()
+ self.assertEqual(123, result)
+
+ @mock.patch('socket.create_connection')
+ def test_snippet_start_event_client(self, mock_create_connection):
+ fake_file = self.setup_mock_socket_file(mock_create_connection)
+ client = self._make_client()
+ client.host_port = 123 # normally picked by start_app_and_connect
+ client.connect()
+ fake_file.resp = self.MOCK_RESP_WITH_CALLBACK
+ callback = client.testSnippetCall()
+ self.assertEqual(123, callback.ret_value)
+ self.assertEqual('1-0', callback._id)
+
+ # Check to make sure the event client is using the same port as the
+ # main client.
+ self.assertEqual(123, callback._event_client.host_port)
+
+ fake_file.resp = self.MOCK_RESP_WITH_ERROR
+ with self.assertRaisesRegexp(jsonrpc_client_base.ApiError, '1'):
+ callback.getAll('eventName')
+
def _make_client(self, adb_proxy=MockAdbProxy()):
return snippet_client.SnippetClient(
package=MOCK_PACKAGE_NAME, adb_proxy=adb_proxy)