Merge "Adding LTE hotspot test suite for baselines"
diff --git a/acts/framework/acts/base_test.py b/acts/framework/acts/base_test.py
index d8a08e1..8df1a23 100755
--- a/acts/framework/acts/base_test.py
+++ b/acts/framework/acts/base_test.py
@@ -13,7 +13,7 @@
# 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.
-import copy
+
import fnmatch
import importlib
import logging
@@ -38,6 +38,7 @@
from acts.event.event import TestClassEndEvent
from acts.event.subscription_bundle import SubscriptionBundle
+from mobly import controller_manager
# Macro strings for test result reporting
TEST_CASE_TOKEN = "[Test Case]"
@@ -59,7 +60,7 @@
event.test_case_name))
except error.ActsError as e:
- test_instance.results.errors.append(e)
+ test_instance.results.error.append(e)
test_instance.log.error('BaseTest setup_test error: %s' % e.message)
except Exception as e:
@@ -80,7 +81,7 @@
event.test_case_name))
except error.ActsError as e:
- test_instance.results.errors.append(e)
+ test_instance.results.error.append(e)
test_instance.log.error('BaseTest teardown_test error: %s' % e.message)
except Exception as e:
@@ -123,8 +124,6 @@
current_test_name: A string that's the name of the test case currently
being executed. If no test is executing, this should
be None.
- controller_registry: A dictionary that holds the controller objects used
- in a test run.
"""
TAG = None
@@ -150,7 +149,11 @@
'consecutive_failure_limit', -1)
self.size_limit_reached = False
- self.controller_registry = {}
+ # Initialize a controller manager (Mobly)
+ self._controller_manager = controller_manager.ControllerManager(
+ class_name=self.__class__.__name__,
+ controller_configs=self.testbed_configs)
+
# Import and register the built-in controller modules specified
# in testbed config.
for module in self._import_builtin_controllers():
@@ -238,32 +241,6 @@
return builtin_controllers
@staticmethod
- def verify_controller_module(module):
- """Verifies a module object follows the required interface for
- controllers.
-
- Args:
- module: An object that is a controller module. This is usually
- imported with import statements or loaded by importlib.
-
- Raises:
- ControllerError is raised if the module does not match the ACTS
- controller interface, or one of the required members is null.
- """
- required_attributes = ("create", "destroy",
- "ACTS_CONTROLLER_CONFIG_NAME")
- for attr in required_attributes:
- if not hasattr(module, attr):
- raise signals.ControllerError(
- ("Module %s missing required "
- "controller module attribute %s.") % (module.__name__,
- attr))
- if not getattr(module, attr):
- raise signals.ControllerError(
- "Controller interface %s in %s cannot be null." %
- (attr, module.__name__))
-
- @staticmethod
def get_module_reference_name(a_module):
"""Returns the module's reference name.
@@ -285,7 +262,8 @@
controller_module,
required=True,
builtin=False):
- """Registers an ACTS controller module for a test run.
+ """Registers an ACTS controller module for a test class. Invokes Mobly's
+ implementation of register_controller.
An ACTS controller module is a Python lib that can be used to control
a device, service, or equipment. To be ACTS compatible, a controller
@@ -353,41 +331,18 @@
the controller module has already been registered or any other error
occurred in the registration process.
"""
- BaseTestClass.verify_controller_module(controller_module)
module_ref_name = self.get_module_reference_name(controller_module)
- if controller_module in self.controller_registry:
- raise signals.ControllerError(
- "Controller module %s has already been registered. It can not "
- "be registered again." % module_ref_name)
- # Create controller objects.
+ # Substitute Mobly controller's module config name with the ACTS one
module_config_name = controller_module.ACTS_CONTROLLER_CONFIG_NAME
- if module_config_name not in self.testbed_configs:
- if required:
- raise signals.ControllerError(
- "No corresponding config found for %s" %
- module_config_name)
- else:
- self.log.warning(
- "No corresponding config found for optional controller %s",
- module_config_name)
+ controller_module.MOBLY_CONTROLLER_CONFIG_NAME = module_config_name
+
+ # Get controller objects from Mobly's register_controller
+ controllers = self._controller_manager.register_controller(
+ controller_module, required=required)
+ if not controllers:
return None
- try:
- # Make a deep copy of the config to pass to the controller module,
- # in case the controller module modifies the config internally.
- original_config = self.testbed_configs[module_config_name]
- controller_config = copy.deepcopy(original_config)
- controllers = controller_module.create(controller_config)
- except:
- self.log.exception(
- "Failed to initialize objects for controller %s, abort!",
- module_config_name)
- raise
- if not isinstance(controllers, list):
- raise signals.ControllerError(
- "Controller module %s did not return a list of objects, abort."
- % module_ref_name)
- self.controller_registry[controller_module] = controllers
+
# Collect controller information and write to test result.
# Implementation of "get_info" is optional for a controller module.
if hasattr(controller_module, "get_info"):
@@ -402,31 +357,35 @@
if builtin:
setattr(self, module_ref_name, controllers)
- self.log.debug("Found %d objects for controller %s", len(controllers),
- module_config_name)
return controllers
def unregister_controllers(self):
- """Destroy controller objects and clear internal registry.
+ """Destroy controller objects and clear internal registry. Invokes
+ Mobly's controller manager's unregister_controllers.
- This will be called at the end of each TestRunner.run call.
+ This will be called upon test class teardown.
"""
- for controller_module, controllers in self.controller_registry.items():
- name = self.get_module_reference_name(controller_module)
+ controller_modules = self._controller_manager._controller_modules
+ controller_objects = self._controller_manager._controller_objects
+ # Record post job info for the controller
+ for name, controller_module in controller_modules.items():
if hasattr(controller_module, 'get_post_job_info'):
self.log.debug('Getting post job info for %s', name)
try:
name, value = controller_module.get_post_job_info(
- controllers)
+ controller_objects[name])
self.results.set_extra_data(name, value)
+ self.summary_writer.dump(
+ {name: value}, records.TestSummaryEntryType.USER_DATA)
except:
self.log.error("Fail to get post job info for %s", name)
- try:
- self.log.debug('Destroying %s.', name)
- controller_module.destroy(controllers)
- except:
- self.log.exception("Exception occurred destroying %s.", name)
- self.controller_registry = {}
+ self._controller_manager.unregister_controllers()
+
+ def _record_controller_info(self):
+ """Collect controller information and write to summary file."""
+ for record in self._controller_manager.get_controller_info_records():
+ self.summary_writer.dump(
+ record.to_dict(), records.TestSummaryEntryType.CONTROLLER_INFO)
def _setup_class(self):
"""Proxy function to guarantee the base implementation of setup_class
@@ -451,6 +410,7 @@
is called.
"""
self.teardown_class()
+ self._record_controller_info()
self.unregister_controllers()
event_bus.post(TestClassEndEvent(self, self.results))
@@ -467,9 +427,9 @@
"""
self.current_test_name = test_name
- # Block the test if the consecutive test case failure limit is reached.
+ # Skip the test if the consecutive test case failure limit is reached.
if self.consecutive_failures == self.consecutive_failure_limit:
- raise signals.TestBlocked('Consecutive test failure')
+ raise signals.TestError('Consecutive test failure')
return self.setup_test()
@@ -570,26 +530,6 @@
begin_time: Logline format timestamp taken when the test started.
"""
- def _on_blocked(self, record):
- """Proxy function to guarantee the base implementation of on_blocked
- is called.
-
- Args:
- record: The records.TestResultRecord object for the blocked test
- case.
- """
- self.log.info(RESULT_LINE_TEMPLATE, record.test_name, record.result)
- self.log.info("Reason to block: %s", record.details)
- self.on_blocked(record.test_name, record.begin_time)
-
- def on_blocked(self, test_name, begin_time):
- """A function that is executed upon a test begin skipped.
-
- Args:
- test_name: Name of the test that triggered this function.
- begin_time: Logline format timestamp taken when the test started.
- """
-
def _on_exception(self, record):
"""Proxy function to guarantee the base implementation of on_exception
is called.
@@ -691,11 +631,6 @@
self.log.exception(e)
tr_record.test_fail(e)
self._exec_procedure_func(self._on_fail, tr_record)
- except signals.TestBlocked as e:
- # Test blocked.
- test_signal = e
- tr_record.test_blocked(e)
- self._exec_procedure_func(self._on_blocked, tr_record)
except signals.TestSkip as e:
# Test skipped.
test_signal = e
@@ -714,14 +649,14 @@
self._exec_procedure_func(self._on_pass, tr_record)
except error.ActsError as e:
test_signal = e
- self.results.errors.append(e)
+ tr_record.test_error(e)
self.log.error(
'BaseTest execute_one_test_case error: %s' % e.message)
except Exception as e:
test_signal = e
self.log.error(traceback.format_exc())
# Exception happened during test.
- tr_record.test_unknown(e)
+ tr_record.test_error(e)
self._exec_procedure_func(self._on_exception, tr_record)
self._exec_procedure_func(self._on_fail, tr_record)
else:
@@ -734,6 +669,8 @@
self._exec_procedure_func(self._on_fail, tr_record)
finally:
self.results.add_record(tr_record)
+ self.summary_writer.dump(
+ tr_record.to_dict(), records.TestSummaryEntryType.RECORD)
self.current_test_name = None
event_bus.post(TestCaseEndEvent(self, self.test_name, test_signal))
@@ -932,14 +869,16 @@
Default is 'Failed class setup'
"""
for test_name, test_func in tests:
- signal = signals.TestBlocked(reason)
+ signal = signals.TestError(reason)
record = records.TestResultRecord(test_name, self.TAG)
record.test_begin()
if hasattr(test_func, 'gather'):
signal.extras = test_func.gather()
- record.test_blocked(signal)
+ record.test_error(signal)
self.results.add_record(record)
- self._on_blocked(record)
+ self.summary_writer.dump(
+ record.to_dict(), records.TestSummaryEntryType.RECORD)
+ self._on_skip(record)
def run(self, test_names=None, test_case_iterations=1):
"""Runs test cases within a test class by the order they appear in the
@@ -980,8 +919,10 @@
else:
matches = valid_tests
self.results.requested = matches
+ self.summary_writer.dump(self.results.requested_test_names_dict(),
+ records.TestSummaryEntryType.TEST_NAME_LIST)
tests = self._get_test_funcs(matches)
- # A TestResultRecord used for when setup_class fails.
+
# Setup for the class.
setup_fail = False
try:
diff --git a/acts/framework/acts/controllers/android_device.py b/acts/framework/acts/controllers/android_device.py
index 96798c7..a774de4 100755
--- a/acts/framework/acts/controllers/android_device.py
+++ b/acts/framework/acts/controllers/android_device.py
@@ -709,8 +709,7 @@
end_time: Epoch time of the ending of the time period, default None
dest_path: Destination path of the excerpt file.
"""
- log_begin_time = acts_logger.normalize_log_line_timestamp(
- acts_logger.epoch_to_log_line_timestamp(begin_time))
+ log_begin_time = acts_logger.epoch_to_log_line_timestamp(begin_time)
if end_time is None:
log_end_time = acts_logger.get_log_line_timestamp()
else:
@@ -723,7 +722,9 @@
return
adb_excerpt_dir = os.path.join(self.log_path, dest_path)
utils.create_dir(adb_excerpt_dir)
- out_name = '%s,%s.txt' % (log_begin_time, self.serial)
+ out_name = '%s,%s.txt' % (
+ acts_logger.normalize_log_line_timestamp(log_begin_time),
+ self.serial)
tag_len = utils.MAX_FILENAME_LEN - len(out_name)
out_name = '%s,%s' % (tag[:tag_len], out_name)
adb_excerpt_path = os.path.join(adb_excerpt_dir, out_name)
diff --git a/acts/framework/acts/keys.py b/acts/framework/acts/keys.py
index 6ae23c7..2c3c6ff 100644
--- a/acts/framework/acts/keys.py
+++ b/acts/framework/acts/keys.py
@@ -60,6 +60,7 @@
ikey_testbed_name = 'testbed_name'
ikey_logger = 'log'
ikey_logpath = 'log_path'
+ ikey_summary_writer = 'summary_writer'
ikey_cli_args = 'cli_args'
# module name of controllers packaged in ACTS.
m_key_monsoon = 'monsoon'
diff --git a/acts/framework/acts/libs/yaml_writer.py b/acts/framework/acts/libs/yaml_writer.py
new file mode 100644
index 0000000..f1f0831
--- /dev/null
+++ b/acts/framework/acts/libs/yaml_writer.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+#
+# Copyright 2019 - The Android Open Source Project
+#
+# 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.
+
+import collections
+import yaml
+
+# Allow yaml to dump OrderedDict
+yaml.add_representer(collections.OrderedDict,
+ lambda dumper, data: dumper.represent_dict(data),
+ Dumper=yaml.SafeDumper)
+
+_DUMP_KWARGS = dict(explicit_start=True, allow_unicode=True, indent=4)
+if yaml.__version__ >= '5.1':
+ _DUMP_KWARGS.update(sort_keys=False)
+
+
+def safe_dump(content, file):
+ """Calls yaml.safe_dump to write content to the file, with additional
+ parameters from _DUMP_KWARGS."""
+ yaml.safe_dump(content, file, **_DUMP_KWARGS)
diff --git a/acts/framework/acts/records.py b/acts/framework/acts/records.py
index a3795b8..575ee60 100644
--- a/acts/framework/acts/records.py
+++ b/acts/framework/acts/records.py
@@ -18,40 +18,62 @@
import collections
import copy
+import io
import json
import logging
from acts import logger
-from acts import utils
+from acts.libs import yaml_writer
+
+from mobly.records import ExceptionRecord
+from mobly.records import OUTPUT_FILE_SUMMARY
+from mobly.records import TestResultEnums as MoblyTestResultEnums
+from mobly.records import TestResultRecord as MoblyTestResultRecord
+from mobly.records import TestResult as MoblyTestResult
+from mobly.records import TestSummaryEntryType
+from mobly.records import TestSummaryWriter as MoblyTestSummaryWriter
-class TestResultEnums(object):
- """Enums used for TestResultRecord class.
+class TestSummaryWriter(MoblyTestSummaryWriter):
+ """Writes test results to a summary file in real time. Inherits from Mobly's
+ TestSummaryWriter.
+ """
+
+ def dump(self, content, entry_type):
+ """Update Mobly's implementation of dump to work on OrderedDict.
+
+ See MoblyTestSummaryWriter.dump for documentation.
+ """
+ new_content = collections.OrderedDict(copy.deepcopy(content))
+ new_content['Type'] = entry_type.value
+ new_content.move_to_end('Type', last=False)
+ # Both user code and Mobly code can trigger this dump, hence the lock.
+ with self._lock:
+ # For Python3, setting the encoding on yaml.safe_dump does not work
+ # because Python3 file descriptors set an encoding by default, which
+ # PyYAML uses instead of the encoding on yaml.safe_dump. So, the
+ # encoding has to be set on the open call instead.
+ with io.open(self._path, 'a', encoding='utf-8') as f:
+ # Use safe_dump here to avoid language-specific tags in final
+ # output.
+ yaml_writer.safe_dump(new_content, f)
+
+
+class TestResultEnums(MoblyTestResultEnums):
+ """Enums used for TestResultRecord class. Inherits from Mobly's
+ TestResultEnums.
Includes the tokens to mark test result with, and the string names for each
field in TestResultRecord.
"""
- RECORD_NAME = "Test Name"
- RECORD_CLASS = "Test Class"
- RECORD_BEGIN_TIME = "Begin Time"
- RECORD_END_TIME = "End Time"
RECORD_LOG_BEGIN_TIME = "Log Begin Time"
RECORD_LOG_END_TIME = "Log End Time"
- RECORD_RESULT = "Result"
- RECORD_UID = "UID"
- RECORD_EXTRAS = "Extras"
- RECORD_ADDITIONAL_ERRORS = "Extra Errors"
- RECORD_DETAILS = "Details"
- TEST_RESULT_PASS = "PASS"
- TEST_RESULT_FAIL = "FAIL"
- TEST_RESULT_SKIP = "SKIP"
- TEST_RESULT_BLOCKED = "BLOCKED"
- TEST_RESULT_UNKNOWN = "UNKNOWN"
-class TestResultRecord(object):
- """A record that holds the information of a test case execution.
+class TestResultRecord(MoblyTestResultRecord):
+ """A record that holds the information of a test case execution. This class
+ inherits from Mobly's TestResultRecord class.
Attributes:
test_name: A string representing the name of the test case.
@@ -64,24 +86,16 @@
"""
def __init__(self, t_name, t_class=None):
- self.test_name = t_name
- self.test_class = t_class
- self.begin_time = None
- self.end_time = None
+ super().__init__(t_name, t_class)
self.log_begin_time = None
self.log_end_time = None
- self.uid = None
- self.result = None
- self.extras = None
- self.details = None
- self.additional_errors = {}
def test_begin(self):
"""Call this when the test case it records begins execution.
Sets the begin_time of this record.
"""
- self.begin_time = utils.get_current_epoch_time()
+ super().test_begin()
self.log_begin_time = logger.epoch_to_log_line_timestamp(
self.begin_time)
@@ -94,86 +108,10 @@
be any exception instance or of any subclass of
acts.signals.TestSignal.
"""
- self.end_time = utils.get_current_epoch_time()
- self.log_end_time = logger.epoch_to_log_line_timestamp(self.end_time)
- self.result = result
- if self.additional_errors:
- self.result = TestResultEnums.TEST_RESULT_UNKNOWN
- if hasattr(e, 'details'):
- self.details = e.details
- elif e:
- self.details = str(e)
- if hasattr(e, 'extras'):
- self.extras = e.extras
-
- def test_pass(self, e=None):
- """To mark the test as passed in this record.
-
- Args:
- e: An instance of acts.signals.TestPass.
- """
- self._test_end(TestResultEnums.TEST_RESULT_PASS, e)
-
- def test_fail(self, e=None):
- """To mark the test as failed in this record.
-
- Only test_fail does instance check because we want "assert xxx" to also
- fail the test same way assert_true does.
-
- Args:
- e: An exception object. It can be an instance of AssertionError or
- acts.base_test.TestFailure.
- """
- self._test_end(TestResultEnums.TEST_RESULT_FAIL, e)
-
- def test_skip(self, e=None):
- """To mark the test as skipped in this record.
-
- Args:
- e: An instance of acts.signals.TestSkip.
- """
- self._test_end(TestResultEnums.TEST_RESULT_SKIP, e)
-
- def test_blocked(self, e=None):
- """To mark the test as blocked in this record.
-
- Args:
- e: An instance of acts.signals.Test
- """
- self._test_end(TestResultEnums.TEST_RESULT_BLOCKED, e)
-
- def test_unknown(self, e=None):
- """To mark the test as unknown in this record.
-
- Args:
- e: An exception object.
- """
- self._test_end(TestResultEnums.TEST_RESULT_UNKNOWN, e)
-
- def add_error(self, tag, e):
- """Add extra error happened during a test mark the test result as
- UNKNOWN.
-
- If an error is added the test record, the record's result is equivalent
- to the case where an uncaught exception happened.
-
- Args:
- tag: A string describing where this error came from, e.g. 'on_pass'.
- e: An exception object.
- """
- self.result = TestResultEnums.TEST_RESULT_UNKNOWN
- self.additional_errors[tag] = str(e)
-
- def __str__(self):
- d = self.to_dict()
- l = ["%s = %s" % (k, v) for k, v in d.items()]
- s = ', '.join(l)
- return s
-
- def __repr__(self):
- """This returns a short string representation of the test record."""
- t = utils.epoch_to_human_time(self.begin_time)
- return "%s %s %s" % (t, self.test_name, self.result)
+ super()._test_end(result, e)
+ if self.end_time:
+ self.log_end_time = logger.epoch_to_log_line_timestamp(
+ self.end_time)
def to_dict(self):
"""Gets a dictionary representing the content of this class.
@@ -192,7 +130,11 @@
d[TestResultEnums.RECORD_UID] = self.uid
d[TestResultEnums.RECORD_EXTRAS] = self.extras
d[TestResultEnums.RECORD_DETAILS] = self.details
- d[TestResultEnums.RECORD_ADDITIONAL_ERRORS] = self.additional_errors
+ d[TestResultEnums.RECORD_EXTRA_ERRORS] = {
+ key: value.to_dict()
+ for (key, value) in self.extra_errors.items()
+ }
+ d[TestResultEnums.RECORD_STACKTRACE] = self.stacktrace
return d
def json_str(self):
@@ -212,8 +154,9 @@
return json.dumps(self.to_dict())
-class TestResult(object):
- """A class that contains metrics of a test run.
+class TestResult(MoblyTestResult):
+ """A class that contains metrics of a test run. This class inherits from
+ Mobly's TestResult class.
This class is essentially a container of TestResultRecord objects.
@@ -224,21 +167,12 @@
self.executed: A list of records for tests that were actually executed.
self.passed: A list of records for tests passed.
self.skipped: A list of records for tests skipped.
- self.unknown: A list of records for tests with unknown result token.
"""
def __init__(self):
- self.requested = []
- self.failed = []
- self.executed = []
- self.passed = []
- self.skipped = []
- self.blocked = []
- self.unknown = []
+ super().__init__()
self.controller_info = {}
- self.post_run_data = {}
self.extras = {}
- self.errors = []
def __add__(self, r):
"""Overrides '+' operator for TestResult class.
@@ -286,36 +220,6 @@
info = str(info)
self.extras[name] = info
- def add_record(self, record):
- """Adds a test record to test result.
-
- A record is considered executed once it's added to the test result.
-
- Args:
- record: A test record object to add.
- """
- if record.result == TestResultEnums.TEST_RESULT_FAIL:
- self.executed.append(record)
- self.failed.append(record)
- elif record.result == TestResultEnums.TEST_RESULT_SKIP:
- self.skipped.append(record)
- elif record.result == TestResultEnums.TEST_RESULT_PASS:
- self.executed.append(record)
- self.passed.append(record)
- elif record.result == TestResultEnums.TEST_RESULT_BLOCKED:
- self.blocked.append(record)
- else:
- self.executed.append(record)
- self.unknown.append(record)
-
- @property
- def is_all_pass(self):
- """True if no tests failed or threw errors, False otherwise."""
- num_of_failures = (len(self.failed) +
- len(self.unknown) +
- len(self.blocked))
- return num_of_failures == 0
-
def json_str(self):
"""Converts this test result to a string in json format.
@@ -337,7 +241,7 @@
d["Results"] = [record.to_dict() for record in self.executed]
d["Summary"] = self.summary_dict()
d["Extras"] = self.extras
- d["Errors"] = self.errors_list()
+ d["Error"] = self.errors_list()
json_str = json.dumps(d, indent=4)
return json_str
@@ -357,29 +261,12 @@
msg = ", ".join(l)
return msg
- def summary_dict(self):
- """Gets a dictionary that summarizes the stats of this test result.
-
- The summary provides the counts of how many test cases fall into each
- category, like "Passed", "Failed" etc.
-
- Returns:
- A dictionary with the stats of this test result.
- """
- d = collections.OrderedDict()
- d["ControllerInfo"] = self.controller_info
- d["Requested"] = len(self.requested)
- d["Executed"] = len(self.executed)
- d["Passed"] = len(self.passed)
- d["Failed"] = len(self.failed)
- d["Skipped"] = len(self.skipped)
- d["Blocked"] = len(self.blocked)
- d["Unknown"] = len(self.unknown)
- d["Errors"] = len(self.errors)
- return d
-
def errors_list(self):
l = list()
- for e in self.errors:
- l.append({"Error Code": e.error_code, "Message": e.message})
+ for record in self.error:
+ record_dict = record.to_dict()
+ l.append({k: record_dict[k] for k in [
+ TestResultEnums.RECORD_NAME,
+ TestResultEnums.RECORD_DETAILS,
+ TestResultEnums.RECORD_EXTRA_ERRORS]})
return l
diff --git a/acts/framework/acts/signals.py b/acts/framework/acts/signals.py
index e116984..67c22cd 100644
--- a/acts/framework/acts/signals.py
+++ b/acts/framework/acts/signals.py
@@ -17,7 +17,3 @@
"""
from mobly.signals import *
-
-
-class TestBlocked(TestSkip):
- """Raised when a test has been blocked from running."""
diff --git a/acts/framework/acts/test_runner.py b/acts/framework/acts/test_runner.py
index 1f127c8..1adfc91 100644
--- a/acts/framework/acts/test_runner.py
+++ b/acts/framework/acts/test_runner.py
@@ -106,6 +106,8 @@
self.log_path: A string representing the path of the dir under which
all logs from this test run should be written.
self.log: The logger object used throughout this test run.
+ self.summary_writer: The TestSummaryWriter object used to stream test
+ results to a file.
self.test_classes: A dictionary where we can look up the test classes
by name to instantiate. Supports unix shell style
wildcards.
@@ -131,6 +133,8 @@
self.log_path = os.path.abspath(l_path)
logger.setup_test_logger(self.log_path, self.testbed_name)
self.log = logging.getLogger()
+ self.summary_writer = records.TestSummaryWriter(
+ os.path.join(self.log_path, records.OUTPUT_FILE_SUMMARY))
if self.test_configs.get(keys.Config.key_random.value):
test_case_iterations = self.test_configs.get(
keys.Config.key_test_case_iterations.value, 10)
@@ -214,6 +218,8 @@
# Unpack other params.
self.test_run_info[keys.Config.ikey_logpath.value] = self.log_path
self.test_run_info[keys.Config.ikey_logger.value] = self.log
+ self.test_run_info[
+ keys.Config.ikey_summary_writer.value] = self.summary_writer
cli_args = test_configs.get(keys.Config.ikey_cli_args.value)
self.test_run_info[keys.Config.ikey_cli_args.value] = cli_args
user_param_pairs = []
@@ -290,7 +296,7 @@
cls_result = test_cls_instance.run(test_cases,
test_case_iterations)
self.results += cls_result
- self._write_results_json_str()
+ self._write_results_to_file()
except signals.TestAbortAll as e:
self.results += e.results
raise e
@@ -350,19 +356,20 @@
if self.running:
msg = "\nSummary for test run %s: %s\n" % (
self.id, self.results.summary_str())
- self._write_results_json_str()
+ self._write_results_to_file()
self.log.info(msg.strip())
logger.kill_test_logger(self.log)
self.running = False
- def _write_results_json_str(self):
- """Writes out a json file with the test result info for easy parsing.
-
- TODO(angli): This should be replaced by standard log record mechanism.
- """
+ def _write_results_to_file(self):
+ """Writes test results to file(s) in a serializable format."""
+ # Old JSON format
path = os.path.join(self.log_path, "test_run_summary.json")
with open(path, 'w') as f:
f.write(self.results.json_str())
+ # New YAML format
+ self.summary_writer.dump(
+ self.results.summary_dict(), records.TestSummaryEntryType.SUMMARY)
def dump_config(self):
"""Writes the test config to a JSON file under self.log_path"""
diff --git a/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py b/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
index 0746d8b..5ee4721 100644
--- a/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
+++ b/acts/framework/acts/test_utils/tel/TelephonyBaseTest.py
@@ -443,9 +443,6 @@
def on_fail(self, test_name, begin_time):
self._take_bug_report(test_name, begin_time)
- def on_blocked(self, test_name, begin_time):
- self.on_fail(test_name, begin_time)
-
def _ad_take_extra_logs(self, ad, test_name, begin_time):
ad.adb.wait_for_device()
result = True
@@ -509,7 +506,7 @@
def _block_all_test_cases(self, tests, reason='Failed class setup'):
"""Over-write _block_all_test_cases in BaseTestClass."""
for (i, (test_name, test_func)) in enumerate(tests):
- signal = signals.TestBlocked(reason)
+ signal = signals.TestFailure(reason)
record = records.TestResultRecord(test_name, self.TAG)
record.test_begin()
# mark all test cases as FAIL
diff --git a/acts/framework/acts/test_utils/tel/tel_defines.py b/acts/framework/acts/test_utils/tel/tel_defines.py
index 222d77b..3aefe98 100644
--- a/acts/framework/acts/test_utils/tel/tel_defines.py
+++ b/acts/framework/acts/test_utils/tel/tel_defines.py
@@ -143,6 +143,9 @@
# has sufficient time to reconfigure based on new network
WAIT_TIME_BETWEEN_REG_AND_CALL = 5
+# Wait time for data pdn to be up on CBRS
+WAIT_TIME_FOR_CBRS_DATA_SWITCH = 30
+
# Time to wait for 1xrtt voice attach check
# After DUT voice network type report 1xrtt (from unknown), it need to wait for
# several seconds before the DUT can receive incoming call.
diff --git a/acts/framework/setup.py b/acts/framework/setup.py
index 936f0a7..0619706 100755
--- a/acts/framework/setup.py
+++ b/acts/framework/setup.py
@@ -30,6 +30,7 @@
'mock<=1.0.1',
'numpy',
'pyserial',
+ 'pyyaml>=5.1',
'shellescape>=3.4.1',
'protobuf',
'requests',
diff --git a/acts/framework/tests/acts_base_class_test.py b/acts/framework/tests/acts_base_class_test.py
index 37b5c53..b402a47 100755
--- a/acts/framework/tests/acts_base_class_test.py
+++ b/acts/framework/tests/acts_base_class_test.py
@@ -52,6 +52,7 @@
'reporter': mock.MagicMock(),
'log': mock.MagicMock(),
'log_path': self.tmp_dir,
+ 'summary_writer': mock.MagicMock(),
'cli_args': None,
'user_params': {
'some_param': 'hahaha'
@@ -80,7 +81,7 @@
class MockBaseTest(base_test.BaseTestClass):
def __init__(self, controllers):
super(MockBaseTest, self).__init__(controllers)
- self.tests = ('test_something', )
+ self.tests = ('test_something',)
def test_something(self):
pass
@@ -98,7 +99,7 @@
class MockBaseTest(base_test.BaseTestClass):
def __init__(self, controllers):
super(MockBaseTest, self).__init__(controllers)
- self.tests = ('not_a_test_something', )
+ self.tests = ('not_a_test_something',)
def not_a_test_something(self):
pass
@@ -166,7 +167,7 @@
class MockBaseTest(base_test.BaseTestClass):
def __init__(self, controllers):
super(MockBaseTest, self).__init__(controllers)
- self.tests = ('test_something', )
+ self.tests = ('test_something',)
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
@@ -184,16 +185,16 @@
# This should not execute because setup_class failed.
never_call()
- def on_blocked(self, test_name, begin_time):
+ def on_skip(self, test_name, begin_time):
call_check('haha')
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.blocked[0]
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, 'test_something')
expected_summary = {
- 'Blocked': 1, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 0,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 0
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
call_check.assert_called_once_with('haha')
@@ -209,13 +210,13 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run(test_names=['test_something'])
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, self.mock_test_name)
self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -235,8 +236,8 @@
self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 1, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 0
+ 'Error': 0, 'Executed': 1,
+ 'Failed': 1, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -257,8 +258,8 @@
self.assertEqual(actual_record.details, expected_msg)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 1, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 0
+ 'Error': 0, 'Executed': 1,
+ 'Failed': 1, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -273,8 +274,8 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run(test_names=['test_something'])
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 1, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -288,13 +289,13 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, self.mock_test_name)
- self.assertIsNone(actual_record.details)
+ self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -308,15 +309,13 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, self.mock_test_name)
- self.assertIsNone(actual_record.details)
+ self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
- expected_extra_error = {'teardown_test': MSG_EXPECTED_EXCEPTION}
- self.assertEqual(actual_record.additional_errors, expected_extra_error)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -338,8 +337,8 @@
self.assertIsNone(actual_record.details)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 1, 'Requested': 1, 'Skipped': 0, 'Unknown': 0
+ 'Error': 0, 'Executed': 1,
+ 'Failed': 0, 'Passed': 1, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -358,14 +357,14 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
my_mock.assert_called_once_with('teardown_test')
self.assertEqual(actual_record.test_name, self.mock_test_name)
self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -381,14 +380,14 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
my_mock.assert_called_once_with('teardown_test')
self.assertEqual(actual_record.test_name, self.mock_test_name)
self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -408,13 +407,13 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
my_mock.assert_called_once_with('on_exception')
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, self.mock_test_name)
- self.assertIsNone(actual_record.details)
+ self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -436,8 +435,8 @@
self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 1, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 0
+ 'Error': 0, 'Executed': 1,
+ 'Failed': 1, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -457,13 +456,13 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
my_mock.assert_called_once_with('on_fail')
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, self.mock_test_name)
self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -489,29 +488,30 @@
'Setup for test_something failed.')
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 1, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 0
+ 'Error': 0, 'Executed': 1,
+ 'Failed': 1, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
def test_failure_to_call_procedure_function_is_recorded(self):
class MockBaseTest(base_test.BaseTestClass):
- def on_fail(self):
+ # Wrong method signature; will raise exception
+ def on_pass(self):
pass
def test_something(self):
- asserts.assert_true(False, MSG_EXPECTED_EXCEPTION)
+ asserts.explicit_pass(MSG_EXPECTED_EXCEPTION)
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.unknown[0]
- self.assertIn('_on_fail', actual_record.additional_errors)
+ actual_record = bt_cls.results.error[0]
+ self.assertIn('_on_pass', actual_record.extra_errors)
self.assertEqual(actual_record.test_name, self.mock_test_name)
self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -527,15 +527,13 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.unknown[0]
- expected_extra_error = {'_on_pass': expected_msg}
- self.assertEqual(actual_record.additional_errors, expected_extra_error)
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, self.mock_test_name)
self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -549,15 +547,15 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, self.mock_test_name)
self.assertEqual(actual_record.details, 'Test Body Exception.')
self.assertIsNone(actual_record.extras)
- self.assertEqual(actual_record.additional_errors['teardown_test'],
- 'Details=This is an expected exception., Extras=None')
+ self.assertEqual(actual_record.extra_errors['teardown_test'].details,
+ 'This is an expected exception.')
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -574,15 +572,15 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, self.mock_test_name)
self.assertEqual(actual_record.details, 'Test Passed!')
self.assertIsNone(actual_record.extras)
- self.assertEqual(actual_record.additional_errors['teardown_test'],
- 'Details=This is an expected exception., Extras=None')
+ self.assertEqual(actual_record.extra_errors['teardown_test'].details,
+ 'This is an expected exception.')
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -597,16 +595,13 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, self.mock_test_name)
self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertEqual(actual_record.extras, MOCK_EXTRA)
- self.assertEqual(actual_record.additional_errors, {
- '_on_pass': MSG_EXPECTED_EXCEPTION
- })
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 1, 'Executed': 1,
+ 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -620,17 +615,14 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.unknown[0]
- self.assertEqual(bt_cls.results.failed, [])
+ actual_record = bt_cls.results.failed[0]
+ self.assertEqual(bt_cls.results.error, [])
self.assertEqual(actual_record.test_name, self.mock_test_name)
self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertEqual(actual_record.extras, MOCK_EXTRA)
- self.assertEqual(actual_record.additional_errors, {
- '_on_fail': MSG_EXPECTED_EXCEPTION
- })
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 1,
- 'Failed': 0, 'Passed': 0, 'Requested': 1, 'Skipped': 0, 'Unknown': 1
+ 'Error': 0, 'Executed': 1,
+ 'Failed': 1, 'Passed': 0, 'Requested': 1, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -652,8 +644,8 @@
self.assertEqual(bt_cls.results.failed[0].details,
MSG_EXPECTED_EXCEPTION)
expected_summary = {
- 'Blocked': 0, 'ControllerInfo': {}, 'Errors': 0, 'Executed': 2,
- 'Failed': 1, 'Passed': 1, 'Requested': 3, 'Skipped': 0, 'Unknown': 0
+ 'Error': 0, 'Executed': 2,
+ 'Failed': 1, 'Passed': 1, 'Requested': 3, 'Skipped': 0
}
self.assertEqual(bt_cls.results.summary_dict(), expected_summary)
@@ -665,7 +657,7 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run(test_names=['test_func'])
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, 'test_func')
self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
@@ -811,7 +803,7 @@
bt_cls = MockBaseTest(self.mock_test_cls_configs)
bt_cls.run()
- actual_record = bt_cls.results.unknown[0]
+ actual_record = bt_cls.results.error[0]
self.assertEqual(actual_record.test_name, 'test_func')
self.assertEqual(actual_record.details, MSG_UNEXPECTED_EXCEPTION)
self.assertIsNone(actual_record.extras)
@@ -986,8 +978,9 @@
base_cls = base_test.BaseTestClass(self.mock_test_cls_configs)
base_cls.register_controller(mock_controller)
registered_name = 'mock_controller'
- self.assertTrue(mock_controller in base_cls.controller_registry)
- mock_ctrlrs = base_cls.controller_registry[mock_controller]
+ controller_objects = base_cls._controller_manager._controller_objects
+ self.assertTrue(registered_name in controller_objects)
+ mock_ctrlrs = controller_objects[registered_name]
self.assertEqual(mock_ctrlrs[0].magic, 'magic1')
self.assertEqual(mock_ctrlrs[1].magic, 'magic2')
expected_msg = 'Controller module .* has already been registered.'
@@ -1027,7 +1020,8 @@
base_cls = base_test.BaseTestClass(self.mock_test_cls_configs)
base_cls.register_controller(mock_controller, builtin=True)
self.assertTrue(hasattr(base_cls, mock_ref_name))
- self.assertTrue(mock_controller in base_cls.controller_registry)
+ self.assertTrue(mock_controller.__name__ in
+ base_cls._controller_manager._controller_objects)
mock_ctrlrs = getattr(base_cls, mock_ctrlr_ref_name)
self.assertEqual(mock_ctrlrs[0].magic, 'magic1')
self.assertEqual(mock_ctrlrs[1].magic, 'magic2')
@@ -1065,31 +1059,6 @@
self.assertEqual(magic_devices[0].magic, 'magic1')
self.assertEqual(magic_devices[1].magic, 'magic2')
- def test_verify_controller_module(self):
- base_test.BaseTestClass.verify_controller_module(mock_controller)
-
- def test_verify_controller_module_null_attr(self):
- tmp = mock_controller.ACTS_CONTROLLER_CONFIG_NAME
- mock_controller.ACTS_CONTROLLER_CONFIG_NAME = None
- msg = 'Controller interface .* in .* cannot be null.'
- try:
- with self.assertRaisesRegexp(signals.ControllerError, msg):
- base_test.BaseTestClass.verify_controller_module(
- mock_controller)
- finally:
- mock_controller.ACTS_CONTROLLER_CONFIG_NAME = tmp
-
- def test_verify_controller_module_missing_attr(self):
- tmp = mock_controller.ACTS_CONTROLLER_CONFIG_NAME
- delattr(mock_controller, 'ACTS_CONTROLLER_CONFIG_NAME')
- msg = 'Module .* missing required controller module attribute'
- try:
- with self.assertRaisesRegexp(signals.ControllerError, msg):
- base_test.BaseTestClass.verify_controller_module(
- mock_controller)
- finally:
- setattr(mock_controller, 'ACTS_CONTROLLER_CONFIG_NAME', tmp)
-
if __name__ == '__main__':
unittest.main()
diff --git a/acts/framework/tests/acts_records_test.py b/acts/framework/tests/acts_records_test.py
index ee59258..a744e1a 100755
--- a/acts/framework/tests/acts_records_test.py
+++ b/acts/framework/tests/acts_records_test.py
@@ -53,7 +53,8 @@
d[records.TestResultEnums.RECORD_LOG_END_TIME] = record.log_end_time
d[records.TestResultEnums.RECORD_UID] = None
d[records.TestResultEnums.RECORD_CLASS] = None
- d[records.TestResultEnums.RECORD_ADDITIONAL_ERRORS] = {}
+ d[records.TestResultEnums.RECORD_EXTRA_ERRORS] = {}
+ d[records.TestResultEnums.RECORD_STACKTRACE] = record.stacktrace
actual_d = record.to_dict()
self.assertDictEqual(actual_d, d)
# Verify that these code paths do not cause crashes and yield non-empty
@@ -214,7 +215,7 @@
record1.test_fail(s)
record2 = records.TestResultRecord(self.tn)
record2.test_begin()
- record2.test_unknown(s)
+ record2.test_error(s)
tr = records.TestResult()
tr.add_record(record1)
tr.add_record(record2)
diff --git a/acts/framework/tests/libs/version_selector_test.py b/acts/framework/tests/libs/version_selector_test.py
index 54aa78b..53c6352 100755
--- a/acts/framework/tests/libs/version_selector_test.py
+++ b/acts/framework/tests/libs/version_selector_test.py
@@ -21,6 +21,7 @@
sys.path[0] = os.path.join(sys.path[0], '../')
import unittest
import logging
+import mock
from acts import base_test
from acts.libs import version_selector
@@ -101,6 +102,7 @@
"""Tests that VersionedTestClass (above) can be called with
test_tracker_info."""
test_class = VersionedTestClass({'log': logging.getLogger(),
+ 'summary_writer': mock.MagicMock(),
'cli_args': []})
test_class.run(['test_1', 'test_2'], 1)
diff --git a/acts/tests/google/fuchsia/wlan/WlanPhyCompliance11NTest.py b/acts/tests/google/fuchsia/wlan/WlanPhyCompliance11NTest.py
index 2a595b3..0af980f 100644
--- a/acts/tests/google/fuchsia/wlan/WlanPhyCompliance11NTest.py
+++ b/acts/tests/google/fuchsia/wlan/WlanPhyCompliance11NTest.py
@@ -116,6 +116,8 @@
Args:
ap_settings: A dictionary of hostapd constant n_capabilities.
"""
+ security_profile = None
+ password = None
temp_n_capabilities = list(ap_settings['n_capabilities'])
n_capabilities = []
for n_capability in temp_n_capabilities:
@@ -155,8 +157,7 @@
password=rand_ascii_str(20),
wpa_cipher='CCMP',
wpa2_cipher='CCMP')
- else:
- security_profile = None
+ password = security_profile.password
validate_setup_ap_and_associate(
access_point=self.access_point,
@@ -169,7 +170,7 @@
force_wmm=True,
ssid=utils.rand_ascii_str(20),
security=security_profile,
- password=security_profile.password
+ password=password
)
diff --git a/acts/tests/google/tel/live/TelLiveCBRSTest.py b/acts/tests/google/tel/live/TelLiveCBRSTest.py
new file mode 100644
index 0000000..6454db0
--- /dev/null
+++ b/acts/tests/google/tel/live/TelLiveCBRSTest.py
@@ -0,0 +1,540 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2019 - Google
+#
+# 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.
+"""
+ Test Script for CBRS devices
+"""
+
+import time
+import collections
+
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
+from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED
+from acts.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
+from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
+from acts.test_utils.tel.tel_defines import WAIT_TIME_FOR_CBRS_DATA_SWITCH
+from acts.test_utils.tel.tel_test_utils import get_phone_number
+from acts.test_utils.tel.tel_test_utils import hangup_call
+from acts.test_utils.tel.tel_test_utils import hangup_call_by_adb
+from acts.test_utils.tel.tel_test_utils import initiate_call
+from acts.test_utils.tel.tel_test_utils import is_phone_not_in_call
+from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
+from acts.test_utils.tel.tel_test_utils import is_phone_in_call
+from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
+from acts.test_utils.tel.tel_test_utils import test_data_browsing_success_using_sl4a
+from acts.test_utils.tel.tel_test_utils import test_data_browsing_failure_using_sl4a
+from acts.test_utils.tel.tel_test_utils import get_operator_name
+from acts.test_utils.tel.tel_test_utils import is_current_data_on_cbrs
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_not_iwlan
+from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte
+from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts.test_utils.tel.tel_voice_utils import phone_setup_volte_for_subscription
+from acts.test_utils.tel.tel_voice_utils import phone_setup_cdma
+from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_2g
+from acts.test_utils.tel.tel_voice_utils import phone_idle_3g
+from acts.test_utils.tel.tel_voice_utils import phone_idle_2g
+from acts.test_utils.tel.tel_voice_utils import phone_idle_csfb
+from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
+from acts.test_utils.tel.tel_voice_utils import phone_idle_not_iwlan
+from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
+from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
+from acts.test_utils.tel.tel_subscription_utils import get_operatorname_from_slot_index
+from acts.utils import get_current_epoch_time
+
+
+class TelLiveCBRSTest(TelephonyBaseTest):
+ def __init__(self, controllers):
+ TelephonyBaseTest.__init__(self, controllers)
+ self.number_of_devices = 2
+ self.stress_test_number = self.get_stress_test_number()
+ self.message_lengths = (50, 160, 180)
+ self.long_message_lengths = (800, 1600)
+ self.cbrs_subid = None
+ self.default_data_subid = None
+
+
+ def on_fail(self, test_name, begin_time):
+ if test_name.startswith('test_stress'):
+ return
+ super().on_fail(test_name, begin_time)
+
+
+ def _cbrs_call_sequence(self, ads, mo_mt,
+ cbrs_phone_setup_func,
+ verify_cbrs_initial_idle_func,
+ verify_data_initial_func,
+ verify_cbrs_in_call_state_func,
+ verify_data_in_call_func,
+ incall_cbrs_setting_check_func,
+ verify_data_final_func,
+ verify_cbrs_final_func,
+ expected_result):
+ """_cbrs_call_sequence
+
+ Args:
+ ads: list of android devices. This list should have 2 ad.
+ mo_mt: indicating this call sequence is MO or MT.
+ Valid input: DIRECTION_MOBILE_ORIGINATED and
+ DIRECTION_MOBILE_TERMINATED.
+
+ Returns:
+ if expected_result is True,
+ Return True if call sequence finish without exception.
+ if expected_result is string,
+ Return True if expected exception happened. Otherwise False.
+
+ """
+
+ class _CBRSCallSequenceException(Exception):
+ pass
+
+ if (len(ads) != 2) or (mo_mt not in [
+ DIRECTION_MOBILE_ORIGINATED, DIRECTION_MOBILE_TERMINATED
+ ]):
+ self.log.error("Invalid parameters.")
+ return False
+
+ # Fetch CBRS sub_id and default
+ slot_dict = {0: {}, 1: {}}
+ for slot in (0, 1):
+ slot_dict[slot]['sub_id'] = get_subid_from_slot_index(
+ ads[0].log, ads[0], slot)
+ slot_dict[slot]['operator'] = get_operatorname_from_slot_index(
+ ads[0], slot)
+ if "Google" in slot_dict[slot]['operator']:
+ self.cbrs_subid = slot_dict[slot]['sub_id']
+ else:
+ self.default_data_subid = slot_dict[slot]['sub_id']
+ ads[0].log.info("Slot %d - Sub %s - %s", slot,
+ slot_dict[slot]['sub_id'],
+ slot_dict[slot]['operator'])
+
+ if mo_mt == DIRECTION_MOBILE_ORIGINATED:
+ ad_caller = ads[0]
+ ad_callee = ads[1]
+ caller_number = get_phone_number(self.log, ad_caller)
+ callee_number = get_phone_number(self.log, ad_callee)
+ mo_operator = get_operator_name(ads[0].log, ads[0])
+ mt_operator = get_operator_name(ads[1].log, ads[1])
+ else:
+ ad_caller = ads[1]
+ ad_callee = ads[0]
+ caller_number = get_phone_number(self.log, ad_caller)
+ callee_number = get_phone_number(self.log, ad_callee)
+ mt_operator = get_operator_name(ads[0].log, ads[0])
+ mo_operator = get_operator_name(ads[1].log, ads[1])
+
+ self.log.info("-->Begin cbrs_call_sequence: %s to %s<--",
+ caller_number, callee_number)
+ self.log.info("--> %s to %s <--", mo_operator, mt_operator)
+
+ try:
+ # Setup
+ if cbrs_phone_setup_func and not cbrs_phone_setup_func():
+ raise _CBRSCallSequenceException("cbrs_phone_setup_func fail.")
+ if not phone_setup_voice_general(self.log, ads[1]):
+ raise _CBRSCallSequenceException(
+ "phone_setup_voice_general fail.")
+ time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
+
+ # Ensure idle status correct
+ if verify_cbrs_initial_idle_func and not \
+ verify_cbrs_initial_idle_func():
+ raise _CBRSCallSequenceException(
+ "verify_cbrs_initial_idle_func fail.")
+
+ # Ensure data checks are performed
+ if verify_data_initial_func and not \
+ verify_data_initial_func():
+ raise _CBRSCallSequenceException(
+ "verify_data_initial_func fail.")
+
+ # Make MO/MT call.
+ if not initiate_call(self.log, ad_caller, callee_number):
+ raise _CBRSCallSequenceException("initiate_call fail.")
+ if not wait_and_answer_call(self.log, ad_callee, caller_number):
+ raise _CBRSCallSequenceException("wait_and_answer_call fail.")
+ time.sleep(WAIT_TIME_FOR_CBRS_DATA_SWITCH)
+
+ # Check state, wait 30 seconds, check again.
+ if (verify_cbrs_in_call_state_func and not
+ verify_cbrs_in_call_state_func()):
+ raise _CBRSCallSequenceException(
+ "verify_cbrs_in_call_state_func fail.")
+
+ if is_phone_not_in_call(self.log, ads[1]):
+ raise _CBRSCallSequenceException("PhoneB not in call.")
+
+ # Ensure data checks are performed
+ if verify_data_in_call_func and not \
+ verify_data_in_call_func():
+ raise _CBRSCallSequenceException(
+ "verify_data_in_call_func fail.")
+
+ time.sleep(WAIT_TIME_IN_CALL)
+
+ if (verify_cbrs_in_call_state_func and not
+ verify_cbrs_in_call_state_func()):
+ raise _CBRSCallSequenceException(
+ "verify_cbrs_in_call_state_func fail after 30 seconds.")
+ if is_phone_not_in_call(self.log, ads[1]):
+ raise _CBRSCallSequenceException(
+ "PhoneB not in call after 30 seconds.")
+
+ # in call change setting and check
+ if (incall_cbrs_setting_check_func and not
+ incall_cbrs_setting_check_func()):
+ raise _CBRSCallSequenceException(
+ "incall_cbrs_setting_check_func fail.")
+
+ # Hangup call
+ if is_phone_in_call(self.log, ads[0]):
+ if not hangup_call(self.log, ads[0]):
+ raise _CBRSCallSequenceException("hangup_call fail.")
+ else:
+ if incall_cbrs_setting_check_func is None:
+ raise _CBRSCallSequenceException("Unexpected call drop.")
+
+ time.sleep(WAIT_TIME_FOR_CBRS_DATA_SWITCH)
+
+ # Ensure data checks are performed
+ if verify_data_final_func and not \
+ verify_data_final_func():
+ raise _CBRSCallSequenceException(
+ "verify_data_final_func fail.")
+
+ # Ensure data checks are performed
+ if verify_cbrs_final_func and not \
+ verify_cbrs_final_func():
+ raise _CBRSCallSequenceException(
+ "verify_cbrs_final_func fail.")
+
+ except _CBRSCallSequenceException as e:
+ if str(e) == expected_result:
+ self.log.info("Expected exception: <%s>, return True.", e)
+ return True
+ else:
+ self.log.info("Unexpected exception: <%s>, return False.", e)
+ return False
+ finally:
+ for ad in ads:
+ if ad.droid.telecomIsInCall():
+ hangup_call_by_adb(ad)
+ self.log.info("cbrs_call_sequence finished, return %s",
+ expected_result is True)
+ return (expected_result is True)
+
+
+ def _phone_idle_iwlan(self):
+ return phone_idle_iwlan(self.log, self.android_devices[0])
+
+ def _phone_idle_not_iwlan(self):
+ return phone_idle_not_iwlan(self.log, self.android_devices[0])
+
+ def _phone_idle_volte(self):
+ return phone_idle_volte(self.log, self.android_devices[0])
+
+ def _phone_idle_csfb(self):
+ return phone_idle_csfb(self.log, self.android_devices[0])
+
+ def _phone_idle_3g(self):
+ return phone_idle_3g(self.log, self.android_devices[0])
+
+ def _phone_idle_2g(self):
+ return phone_idle_2g(self.log, self.android_devices[0])
+
+ def _is_phone_in_call_iwlan(self):
+ return is_phone_in_call_iwlan(self.log, self.android_devices[0])
+
+ def _is_phone_in_call_not_iwlan(self):
+ return is_phone_in_call_not_iwlan(self.log, self.android_devices[0])
+
+ def _is_phone_not_in_call(self):
+ if is_phone_in_call(self.log, self.android_devices[0]):
+ self.log.info("{} in call.".format(self.android_devices[0].serial))
+ return False
+ self.log.info("{} not in call.".format(self.android_devices[0].serial))
+ return True
+
+ def _is_phone_in_call_volte(self):
+ return is_phone_in_call_volte(self.log, self.android_devices[0])
+
+ def _is_phone_in_call_3g(self):
+ return is_phone_in_call_3g(self.log, self.android_devices[0])
+
+ def _is_phone_in_call_2g(self):
+ return is_phone_in_call_2g(self.log, self.android_devices[0])
+
+ def _is_phone_in_call_csfb(self):
+ return is_phone_in_call_csfb(self.log, self.android_devices[0])
+
+ def _is_phone_in_call(self):
+ return is_phone_in_call(self.log, self.android_devices[0])
+
+ def _phone_setup_voice_general(self):
+ return phone_setup_voice_general(self.log, self.android_devices[0])
+
+ def _phone_setup_volte(self):
+ return phone_setup_volte_for_subscription(self.log,
+ self.android_devices[0],
+ self.default_data_subid)
+
+ def _phone_setup_1x(self):
+ return phone_setup_cdma(self.log, self.android_devices[0])
+
+ def _phone_setup_2g(self):
+ return phone_setup_voice_2g(self.log, self.android_devices[0])
+
+
+ def _test_data_browsing_success_using_sl4a(self):
+ return test_data_browsing_success_using_sl4a(self.log,
+ self.android_devices[0])
+
+ def _test_data_browsing_failure_using_sl4a(self):
+ return test_data_browsing_failure_using_sl4a(self.log,
+ self.android_devices[0])
+
+ def _is_current_data_on_cbrs(self):
+ return is_current_data_on_cbrs(self.android_devices[0],
+ self.cbrs_subid)
+
+ def _is_current_data_on_default(self):
+ return not is_current_data_on_cbrs(self.android_devices[0],
+ self.cbrs_subid)
+
+
+ """ Tests Begin """
+
+
+ @test_tracker_info(uuid="f7a3db92-2f1b-4131-99bc-b323dbce812c")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_cbrs_mo_voice_data_concurrency_lte(self):
+ """ Test CBRS Data with MO Voice Call on LTE
+
+ PhoneA should be on LTE with VoLTE enabled
+ Verify Data Browsing works fine on cbrs before call
+ Call from PhoneA to PhoneB, call should succeed
+ Verify Data Browsing works fine on pSIM during call
+ Terminate call
+ Verify Data Browsing works fine on cbrs after call
+
+ Returns:
+ True if pass; False if fail.
+ """
+ ads = [self.android_devices[0], self.android_devices[1]]
+ result = self._cbrs_call_sequence(
+ ads, DIRECTION_MOBILE_ORIGINATED,
+ self._phone_setup_volte, self._is_current_data_on_cbrs,
+ self._test_data_browsing_success_using_sl4a,
+ self._is_phone_in_call_volte,
+ self._is_current_data_on_default,
+ self._test_data_browsing_success_using_sl4a,
+ self._test_data_browsing_success_using_sl4a,
+ self._is_current_data_on_cbrs, True)
+
+ self.log.info("CBRS MO Result: %s", result)
+ return result
+
+
+ @test_tracker_info(uuid="17acce7a-de9c-4540-b2d3-2c98367a0b4e")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_cbrs_mt_voice_data_concurrency_lte(self):
+ """ Test CBRS Data with MT Voice Call on LTE
+
+ PhoneA should be on LTE with VoLTE enabled
+ Verify Data Browsing works fine on cbrs before call
+ Call from PhoneB to PhoneA, call should succeed
+ Verify Data Browsing works fine on pSIM during call
+ Terminate call
+ Verify Data Browsing works fine on cbrs after call
+
+ Returns:
+ True if pass; False if fail.
+ """
+ ads = [self.android_devices[0], self.android_devices[1]]
+ result = self._cbrs_call_sequence(
+ ads, DIRECTION_MOBILE_TERMINATED,
+ self._phone_setup_volte, self._is_current_data_on_cbrs,
+ self._test_data_browsing_success_using_sl4a,
+ self._is_phone_in_call_volte,
+ self._is_current_data_on_default,
+ self._test_data_browsing_success_using_sl4a,
+ self._test_data_browsing_success_using_sl4a,
+ self._is_current_data_on_cbrs, True)
+
+ self.log.info("CBRS MT Result: %s", result)
+ return result
+
+ @test_tracker_info(uuid="dc2608fc-b99d-419b-8989-e1f8cdeb04da")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_cbrs_mo_voice_data_concurrency_1x(self):
+ """ Test CBRS Data with MO Voice Call on 3G
+
+ PhoneA should be on UMTS
+ Verify Data Browsing works fine on cbrs before call
+ Call from PhoneA to PhoneB, call should succeed
+ Verify Data Browsing works fine on pSIM during call
+ Terminate call
+ Verify Data Browsing works fine on cbrs after call
+
+ Returns:
+ True if pass; False if fail.
+ """
+ ads = [self.android_devices[0], self.android_devices[1]]
+ result = self._cbrs_call_sequence(
+ ads, DIRECTION_MOBILE_ORIGINATED,
+ self._phone_setup_1x, self._is_current_data_on_cbrs,
+ self._test_data_browsing_success_using_sl4a,
+ self._is_phone_in_call_3g,
+ self._is_current_data_on_default,
+ self._test_data_browsing_failure_using_sl4a,
+ self._test_data_browsing_success_using_sl4a,
+ self._is_current_data_on_cbrs, True)
+
+ self.log.info("CBRS MO Result: %s", result)
+ return result
+
+
+ @test_tracker_info(uuid="cd3a6613-ca37-43c7-8364-7e4e627ca558")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_cbrs_mt_voice_data_concurrency_1x(self):
+ """ Test CBRS Data with MT Voice Call on 3G
+
+ PhoneA should be on UMTS
+ Verify Data Browsing works fine on cbrs before call
+ Call from PhoneA to PhoneA, call should succeed
+ Verify Data Browsing works fine on pSIM during call
+ Terminate call
+ Verify Data Browsing works fine on cbrs after call
+
+ Returns:
+ True if pass; False if fail.
+ """
+ ads = [self.android_devices[0], self.android_devices[1]]
+ result = self._cbrs_call_sequence(
+ ads, DIRECTION_MOBILE_TERMINATED,
+ self._phone_setup_1x, self._is_current_data_on_cbrs,
+ self._test_data_browsing_success_using_sl4a,
+ self._is_phone_in_call_3g,
+ self._is_current_data_on_default,
+ self._test_data_browsing_failure_using_sl4a,
+ self._test_data_browsing_success_using_sl4a,
+ self._is_current_data_on_cbrs, True)
+
+ self.log.info("CBRS MT Result: %s", result)
+ return result
+
+
+ def _test_stress_cbrs(self, mo_mt):
+ """ Test CBRS/SSIM VoLTE Stress
+
+ mo_mt: indicating this call sequence is MO or MT.
+ Valid input: DIRECTION_MOBILE_ORIGINATED and
+ DIRECTION_MOBILE_TERMINATED.
+
+ Returns:
+ True if pass; False if fail.
+ """
+ if (mo_mt not in [DIRECTION_MOBILE_ORIGINATED,
+ DIRECTION_MOBILE_TERMINATED]):
+ self.log.error("Invalid parameters.")
+ return False
+ ads = [self.android_devices[0], self.android_devices[1]]
+ total_iteration = self.stress_test_number
+ fail_count = collections.defaultdict(int)
+ slot_dict = {0: {}, 1: {}}
+ for slot in (0, 1):
+ slot_dict[slot]['sub_id'] = get_subid_from_slot_index(
+ ads[0].log, ads[0], slot)
+ slot_dict[slot]['operator'] = get_operatorname_from_slot_index(
+ ads[0], slot)
+ if "Google" in slot_dict[slot]['operator']:
+ self.cbrs_subid = slot_dict[slot]['sub_id']
+ else:
+ self.default_data_subid = slot_dict[slot]['sub_id']
+ ads[0].log.info("Slot %d - Sub %s - %s", slot,
+ slot_dict[slot]['sub_id'],
+ slot_dict[slot]['operator'])
+ self.log.info("Total iteration = %d.", total_iteration)
+ current_iteration = 1
+ for i in range(1, total_iteration + 1):
+ msg = "Stress Call Test Iteration: <%s> / <%s>" % (
+ i, total_iteration)
+ begin_time = get_current_epoch_time()
+ self.log.info(msg)
+ start_qxdm_loggers(self.log, self.android_devices, begin_time)
+ iteration_result = self._cbrs_call_sequence(
+ ads, mo_mt,
+ self._phone_setup_volte,
+ self._is_current_data_on_cbrs,
+ self._test_data_browsing_success_using_sl4a,
+ self._is_phone_in_call_volte,
+ self._is_current_data_on_default,
+ self._test_data_browsing_success_using_sl4a,
+ self._test_data_browsing_success_using_sl4a,
+ self._is_current_data_on_cbrs, True)
+ self.log.info("Result: %s", iteration_result)
+ if iteration_result:
+ self.log.info(">----Iteration : %d/%d succeed.----<",
+ i, total_iteration)
+ else:
+ fail_count["cbrs_fail"] += 1
+ self.log.error(">----Iteration : %d/%d failed.----<",
+ i, total_iteration)
+ self._take_bug_report("%s_IterNo_%s" % (self.test_name, i),
+ begin_time)
+ current_iteration += 1
+ test_result = True
+ for failure, count in fail_count.items():
+ if count:
+ self.log.error("%s: %s %s failures in %s iterations",
+ self.test_name, count, failure,
+ total_iteration)
+ test_result = False
+ return test_result
+
+ @test_tracker_info(uuid="860dc00d-5be5-4cdd-aeb1-a89edfa83342")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_stress_cbrs_mt_calls_lte(self):
+ """ Test SSIM to CBRS stress
+
+ Call from PhoneB to PhoneA
+ Perform CBRS Data checks
+ Repeat above steps
+
+ Returns:
+ True if pass; False if fail.
+ """
+ return self._test_stress_cbrs(DIRECTION_MOBILE_TERMINATED)
+
+ @test_tracker_info(uuid="54366c70-c9cb-4eed-bd1c-a37c83d5c0ae")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_stress_cbrs_mo_calls_lte(self):
+ """ Test CBRS to SSIM stress
+
+ Call from PhoneA to PhoneB
+ Perform CBRS Data checks
+ Repeat above steps
+
+ Returns:
+ True if pass; False if fail.
+ """
+ return self._test_stress_cbrs(DIRECTION_MOBILE_ORIGINATED)
\ No newline at end of file
diff --git a/acts/tests/google/tel/live/TelLiveStressTest.py b/acts/tests/google/tel/live/TelLiveStressTest.py
index 77007e6..afd6e34 100644
--- a/acts/tests/google/tel/live/TelLiveStressTest.py
+++ b/acts/tests/google/tel/live/TelLiveStressTest.py
@@ -40,8 +40,6 @@
from acts.test_utils.tel.tel_defines import NETWORK_MODE_CDMA
from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY
from acts.test_utils.tel.tel_defines import NETWORK_MODE_TDSCDMA_GSM_WCDMA
-from acts.test_utils.tel.tel_defines import RAT_LTE
-from acts.test_utils.tel.tel_defines import RAT_UNKNOWN
from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_MODE_CHANGE
from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
@@ -52,7 +50,6 @@
from acts.test_utils.tel.tel_test_utils import active_file_download_test
from acts.test_utils.tel.tel_test_utils import is_phone_in_call
from acts.test_utils.tel.tel_test_utils import call_setup_teardown
-from acts.test_utils.tel.tel_test_utils import check_is_wifi_connected
from acts.test_utils.tel.tel_test_utils import ensure_network_generation_for_subscription
from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
from acts.test_utils.tel.tel_test_utils import extract_test_log
@@ -77,7 +74,7 @@
from acts.test_utils.tel.tel_test_utils import wait_for_call_id_clearing
from acts.test_utils.tel.tel_test_utils import wait_for_data_connection
from acts.test_utils.tel.tel_test_utils import wait_for_in_call_active
-from acts.test_utils.tel.tel_test_utils import wifi_toggle_state
+from acts.test_utils.tel.tel_test_utils import check_call_state_idle_by_adb
from acts.test_utils.tel.tel_test_utils import is_current_data_on_cbrs
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g
from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_2g
@@ -92,11 +89,6 @@
from acts.test_utils.tel.tel_voice_utils import phone_idle_iwlan
from acts.test_utils.tel.tel_voice_utils import phone_idle_volte
from acts.test_utils.tel.tel_voice_utils import get_current_voice_rat
-from acts.test_utils.tel.tel_subscription_utils import get_default_data_sub_id
-from acts.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id
-from acts.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
-from acts.test_utils.tel.tel_subscription_utils import get_incoming_message_sub_id
from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
from acts.test_utils.tel.tel_subscription_utils import get_operatorname_from_slot_index
from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data
@@ -698,12 +690,20 @@
self.result_info["CBRS-Data-Pass"] += 1
cbrs_fail = False
else:
- self.result_info["CBRS-Data-Fail"] += 1
- cbrs_fail = True
+ if not check_call_state_idle_by_adb(ad):
+ self.result_info["CBRS-InCall-Pass"] += 1
+ cbrs_fail = False
+ else:
+ self.result_info["CBRS-Data-Fail"] += 1
+ cbrs_fail = True
else:
if is_current_data_on_cbrs(ad, ad.cbrs):
- self.result_info["CBRS-InCall-Fail"] += 1
- cbrs_fail = True
+ if check_call_state_idle_by_adb(ad):
+ self.result_info["CBRS-Data-Pass"] += 1
+ cbrs_fail = False
+ else:
+ self.result_info["CBRS-InCall-Fail"] += 1
+ cbrs_fail = True
else:
self.result_info["CBRS-InCall-Pass"] += 1
cbrs_fail = False