• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3.4
2#
3#   Copyright 2016 - Google
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16"""
17    Base Class for Defining Common Telephony Test Functionality
18"""
19
20import os
21import time
22import inspect
23import traceback
24
25import acts.controllers.diag_logger
26
27from acts.base_test import BaseTestClass
28from acts.keys import Config
29from acts.signals import TestSignal
30from acts import utils
31
32from acts.test_utils.tel.tel_subscription_utils import \
33    get_subid_from_slot_index
34from acts.test_utils.tel.tel_subscription_utils import \
35    initial_set_up_for_subid_infomation
36from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data
37from acts.test_utils.tel.tel_subscription_utils import \
38    set_subid_for_message
39from acts.test_utils.tel.tel_subscription_utils import \
40    set_subid_for_outgoing_call
41from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode
42from acts.test_utils.tel.tel_test_utils import ensure_phones_default_state
43from acts.test_utils.tel.tel_test_utils import \
44    reset_preferred_network_type_to_allowable_range
45from acts.test_utils.tel.tel_test_utils import set_phone_screen_on
46from acts.test_utils.tel.tel_test_utils import set_phone_silent_mode
47from acts.test_utils.tel.tel_test_utils import setup_droid_properties
48from acts.test_utils.tel.tel_test_utils import refresh_droid_config
49from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND
50from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING
51from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND
52from acts.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_ENABLED
53from acts.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_DISABLED
54from acts.utils import force_airplane_mode
55
56
57class _TelephonyTraceLogger():
58    def __init__(self, logger):
59        self._logger = logger
60
61    @staticmethod
62    def _get_trace_info():
63        # we want the stack frame above this and above the error/warning/info
64        stack_frames = inspect.stack()[2]
65        info = inspect.getframeinfo(stack_frames[0])
66
67        return "{}:{}:{}".format(
68            os.path.basename(info.filename), info.function, info.lineno)
69
70    def error(self, msg, *args, **kwargs):
71        trace_info = _TelephonyTraceLogger._get_trace_info()
72        self._logger.error("{} - {}".format(trace_info, msg), *args, **kwargs)
73
74    def warning(self, msg, *args, **kwargs):
75        trace_info = _TelephonyTraceLogger._get_trace_info()
76        self._logger.error("{} - {}".format(trace_info, msg), *args, **kwargs)
77
78    def __getattr__(self, name):
79        return getattr(self._logger, name)
80
81
82class TelephonyBaseTest(BaseTestClass):
83    def __init__(self, controllers):
84
85        BaseTestClass.__init__(self, controllers)
86        self.logger_sessions = []
87
88        self.log = _TelephonyTraceLogger(self.log)
89
90    # Use for logging in the test cases to facilitate
91    # faster log lookup and reduce ambiguity in logging.
92    def tel_test_wrap(fn):
93        def _safe_wrap_test_case(self, *args, **kwargs):
94            test_id = "{}:{}:{}".format(self.__class__.__name__, fn.__name__,
95                                        time.time())
96            log_string = "[Test ID] {}".format(test_id)
97            self.log.info(log_string)
98            try:
99                for ad in self.android_devices:
100                    ad.droid.logI("Started " + log_string)
101                # TODO: b/19002120 start QXDM Logging
102                result = fn(self, *args, **kwargs)
103                if result is not True and "telephony_auto_rerun" in self.user_params:
104                    self.teardown_test()
105                    # re-run only once, if re-run pass, mark as pass
106                    log_string = "[Rerun Test ID] {}. 1st run failed.".format(
107                        test_id)
108                    self.log.info(log_string)
109                    self.setup_test()
110                    for ad in self.android_devices:
111                        ad.droid.logI("Rerun Started " + log_string)
112                    result = fn(self, *args, **kwargs)
113                    if result is True:
114                        self.log.info("Rerun passed.")
115                    elif result is False:
116                        self.log.info("Rerun failed.")
117                    else:
118                        # In the event that we have a non-bool or null
119                        # retval, we want to clearly distinguish this in the
120                        # logs from an explicit failure, though the test will
121                        # still be considered a failure for reporting purposes.
122                        self.log.info("Rerun indeterminate.")
123                        result = False
124                return result
125            except TestSignal:
126                raise
127            except Exception as e:
128                self.log.error(traceback.format_exc())
129                self.log.error(str(e))
130                return False
131            finally:
132                # TODO: b/19002120 stop QXDM Logging
133                for ad in self.android_devices:
134                    try:
135                        ad.adb.wait_for_device()
136                        ad.droid.logI("Finished " + log_string)
137                    except Exception as e:
138                        self.log.error(str(e))
139
140        return _safe_wrap_test_case
141
142    def setup_class(self):
143
144        if not "sim_conf_file" in self.user_params.keys():
145            self.log.error("Missing mandatory user config \"sim_conf_file\"!")
146            return False
147
148        sim_conf_file = self.user_params["sim_conf_file"]
149        # If the sim_conf_file is not a full path, attempt to find it
150        # relative to the config file.
151        if not os.path.isfile(sim_conf_file):
152            sim_conf_file = os.path.join(
153                self.user_params[Config.key_config_path], sim_conf_file)
154            if not os.path.isfile(sim_conf_file):
155                self.log.error("Unable to load user config " + sim_conf_file +
156                               "from test config file.")
157                return False
158
159        setattr(self,
160                "diag_logger",
161                self.register_controller(acts.controllers.diag_logger,
162                                         required=False))
163        for ad in self.android_devices:
164            setup_droid_properties(self.log, ad, sim_conf_file)
165
166            # Ensure that a test class starts from a consistent state that
167            # improves chances of valid network selection and facilitates
168            # logging.
169            toggle_airplane_mode(self.log, ad, True)
170            if not set_phone_screen_on(self.log, ad):
171                self.log.error("Failed to set phone screen-on time.")
172                return False
173            if not set_phone_silent_mode(self.log, ad):
174                self.log.error("Failed to set phone silent mode.")
175                return False
176
177            ad.droid.telephonyAdjustPreciseCallStateListenLevel(
178                PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND, True)
179            ad.droid.telephonyAdjustPreciseCallStateListenLevel(
180                PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING, True)
181            ad.droid.telephonyAdjustPreciseCallStateListenLevel(
182                PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND, True)
183
184            if "enable_wifi_verbose_logging" in self.user_params:
185                ad.droid.wifiEnableVerboseLogging(WIFI_VERBOSE_LOGGING_ENABLED)
186
187            # Reset preferred network type.
188            reset_preferred_network_type_to_allowable_range(self.log, ad)
189
190        # Sub ID setup
191        for ad in self.android_devices:
192            initial_set_up_for_subid_infomation(self.log, ad)
193        return True
194
195    def teardown_class(self):
196        try:
197            ensure_phones_default_state(self.log, self.android_devices)
198
199            for ad in self.android_devices:
200                ad.droid.telephonyAdjustPreciseCallStateListenLevel(
201                    PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND, False)
202                ad.droid.telephonyAdjustPreciseCallStateListenLevel(
203                    PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING, False)
204                ad.droid.telephonyAdjustPreciseCallStateListenLevel(
205                    PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND, False)
206                if "enable_wifi_verbose_logging" in self.user_params:
207                    ad.droid.wifiEnableVerboseLogging(
208                        WIFI_VERBOSE_LOGGING_DISABLED)
209        finally:
210            for ad in self.android_devices:
211                try:
212                    toggle_airplane_mode(self.log, ad, True)
213                except BrokenPipeError:
214                    # Broken Pipe, can not call SL4A API to turn on Airplane Mode.
215                    # Use adb command to turn on Airplane Mode.
216                    if not force_airplane_mode(ad, True):
217                        self.log.error(
218                            "Can not turn on airplane mode on:{}".format(
219                                ad.serial))
220        return True
221
222    def setup_test(self):
223        for ad in self.android_devices:
224            refresh_droid_config(self.log, ad)
225
226        if getattr(self, "diag_logger", None):
227            for logger in self.diag_logger:
228                self.log.info("Starting a diagnostic session {}".format(
229                    logger))
230                self.logger_sessions.append((logger, logger.start()))
231
232        return ensure_phones_default_state(self.log, self.android_devices)
233
234    def teardown_test(self):
235        return True
236
237    def _cleanup_logger_sessions(self):
238        for (logger, session) in self.logger_sessions:
239            self.log.info("Resetting a diagnostic session {},{}".format(
240                logger, session))
241            logger.reset()
242        self.logger_sessions = []
243
244    def on_exception(self, test_name, begin_time):
245        self._pull_diag_logs(test_name, begin_time)
246        self._take_bug_report(test_name, begin_time)
247        self._cleanup_logger_sessions()
248
249    def on_fail(self, test_name, begin_time):
250        self._pull_diag_logs(test_name, begin_time)
251        self._take_bug_report(test_name, begin_time)
252        self._cleanup_logger_sessions()
253
254    def on_pass(self, test_name, begin_time):
255        self._cleanup_logger_sessions()
256
257    def _pull_diag_logs(self, test_name, begin_time):
258        for (logger, session) in self.logger_sessions:
259            self.log.info("Pulling diagnostic session {}".format(logger))
260            logger.stop(session)
261            diag_path = os.path.join(self.log_path, begin_time)
262            utils.create_dir(diag_path)
263            logger.pull(session, diag_path)
264
265    def _take_bug_report(self, test_name, begin_time):
266        if "no_bug_report_on_fail" in self.user_params:
267            return
268
269        # magical sleep to ensure the runtime restart or reboot begins
270        time.sleep(1)
271        for ad in self.android_devices:
272            try:
273                ad.adb.wait_for_device()
274                ad.take_bug_report(test_name, begin_time)
275                tombstone_path = os.path.join(
276                    ad.log_path, "BugReports",
277                    "{},{}".format(begin_time, ad.serial).replace(' ', '_'))
278                utils.create_dir(tombstone_path)
279                ad.adb.pull('/data/tombstones/', tombstone_path)
280            except:
281                ad.log.error("Failed to take a bug report for {}, {}"
282                             .format(ad.serial, test_name))
283