• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3.4
2#
3#   Copyright 2018 - The Android Open Source Project
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.
16import time
17
18import acts.test_utils.power.PowerBaseTest as PBT
19from acts.controllers.anritsu_lib._anritsu_utils import AnritsuError
20from acts.controllers.anritsu_lib.md8475a import MD8475A
21from acts.test_utils.power.tel_simulations.GsmSimulation import GsmSimulation
22from acts.test_utils.power.tel_simulations.LteSimulation import LteSimulation
23from acts.test_utils.power.tel_simulations.UmtsSimulation import UmtsSimulation
24from acts.test_utils.power.tel_simulations.LteCaSimulation import LteCaSimulation
25
26
27class PowerCellularLabBaseTest(PBT.PowerBaseTest):
28    """ Base class for Cellular power related tests.
29
30    Inherits from PowerBaseTest so it has methods to collect power measurements.
31    Provides methods to setup and control the Anritsu simulation.
32
33    """
34
35    # List of test name keywords that indicate the RAT to be used
36
37    PARAM_SIM_TYPE_LTE = "lte"
38    PARAM_SIM_TYPE_LTE_CA = "lteca"
39    PARAM_SIM_TYPE_UMTS = "umts"
40    PARAM_SIM_TYPE_GSM = "gsm"
41
42    # User param keywords
43    KEY_CALIBRATION_TABLE = "calibration_table"
44
45    def __init__(self, controllers):
46        """ Class initialization.
47
48        Sets class attributes to None.
49        """
50
51        super().__init__(controllers)
52
53        self.simulation = None
54        self.anritsu = None
55        self.calibration_table = {}
56
57        # If callbox version was not specified in the config files,
58        # set a default value
59        if not hasattr(self, "md8475_version"):
60            self.md8475_version = "A"
61
62    def setup_class(self):
63        """ Executed before any test case is started.
64
65        Sets the device to rockbottom and connects to the anritsu callbox.
66
67        Returns:
68            False if connecting to the callbox fails.
69        """
70
71        super().setup_class()
72
73        # Gets the name of the interface from which packets are sent
74        if hasattr(self, 'packet_senders'):
75            self.pkt_sender = self.packet_senders[0]
76
77        # Load calibration tables
78        # Load calibration tables
79        if self.KEY_CALIBRATION_TABLE in self.user_params:
80            self.calibration_table = self.unpack_custom_file(
81                self.user_params[self.KEY_CALIBRATION_TABLE], False)
82
83        # Store the value of the key to access the test config in the
84        # user_params dictionary.
85        self.PARAMS_KEY = self.TAG + "_params"
86
87        # Set DUT to rockbottom
88        self.dut_rockbottom()
89
90        # Establish connection to Anritsu Callbox
91        return self.connect_to_anritsu()
92
93    def connect_to_anritsu(self):
94        """ Connects to Anritsu Callbox and gets handle object.
95
96        Returns:
97            False if a connection with the callbox could not be started
98        """
99
100        try:
101
102            self.anritsu = MD8475A(
103                self.md8475a_ip_address,
104                self.log,
105                self.wlan_option,
106                md8475_version=self.md8475_version)
107            return True
108        except AnritsuError:
109            self.log.error('Error in connecting to Anritsu Callbox')
110            return False
111
112    def setup_test(self):
113        """ Executed before every test case.
114
115        Parses parameters from the test name and sets a simulation up according
116        to those values. Also takes care of attaching the phone to the base
117        station. Because starting new simulations and recalibrating takes some
118        time, the same simulation object is kept between tests and is only
119        destroyed and re instantiated in case the RAT is different from the
120        previous tests.
121
122        Children classes need to call the parent method first. This method will
123        create the list self.parameters with the keywords separated by
124        underscores in the test name and will remove the ones that were consumed
125        for the simulation config. The setup_test methods in the children
126        classes can then consume the remaining values.
127        """
128
129        # Get list of parameters from the test name
130        self.parameters = self.current_test_name.split('_')
131
132        # Remove the 'test' keyword
133        self.parameters.remove('test')
134
135        # Decide what type of simulation and instantiate it if needed
136        if self.consume_parameter(self.PARAM_SIM_TYPE_LTE):
137            self.init_simulation(self.PARAM_SIM_TYPE_LTE)
138        elif self.consume_parameter(self.PARAM_SIM_TYPE_LTE_CA):
139            self.init_simulation(self.PARAM_SIM_TYPE_LTE_CA)
140        elif self.consume_parameter(self.PARAM_SIM_TYPE_UMTS):
141            self.init_simulation(self.PARAM_SIM_TYPE_UMTS)
142        elif self.consume_parameter(self.PARAM_SIM_TYPE_GSM):
143            self.init_simulation(self.PARAM_SIM_TYPE_GSM)
144        else:
145            self.log.error(
146                "Simulation type needs to be indicated in the test name.")
147            return False
148
149        # Changing cell parameters requires the phone to be detached
150        self.simulation.detach()
151
152        # Parse simulation parameters.
153        # This may throw a ValueError exception if incorrect values are passed
154        # or if required arguments are omitted.
155        try:
156            self.simulation.parse_parameters(self.parameters)
157        except ValueError as error:
158            self.log.error(str(error))
159            return False
160
161        # Wait for new params to settle
162        time.sleep(5)
163
164        # Attach the phone to the basestation
165        if not self.simulation.attach():
166            return False
167
168        self.simulation.start_test_case()
169
170        # Make the device go to sleep
171        self.dut.droid.goToSleepNow()
172
173        return True
174
175    def consume_parameter(self, parameter_name, num_values=0):
176        """ Parses a parameter from the test name.
177
178        Allows the test to get parameters from its name. Deletes parameters from
179        the list after consuming them to ensure that they are not used twice.
180
181        Args:
182            parameter_name: keyword to look up in the test name
183            num_values: number of arguments following the parameter name in the
184                test name
185        Returns:
186            A list containing the parameter name and the following num_values
187            arguments.
188        """
189
190        try:
191            i = self.parameters.index(parameter_name)
192        except ValueError:
193            # parameter_name is not set
194            return []
195
196        return_list = []
197
198        try:
199            for j in range(num_values + 1):
200                return_list.append(self.parameters.pop(i))
201        except IndexError:
202            self.log.error(
203                "Parameter {} has to be followed by {} values.".format(
204                    parameter_name, num_values))
205            raise ValueError()
206
207        return return_list
208
209    def teardown_class(self):
210        """Clean up the test class after tests finish running.
211
212        Stop the simulation and then disconnect from the Anritsu Callbox.
213
214        """
215        super().teardown_class()
216
217        if self.anritsu:
218            self.anritsu.stop_simulation()
219            self.anritsu.disconnect()
220
221    def init_simulation(self, sim_type):
222        """ Starts a new simulation only if needed.
223
224        Only starts a new simulation if type is different from the one running
225        before.
226
227        Args:
228            type: defines the type of simulation to be started.
229        """
230
231        simulation_dictionary = {
232            self.PARAM_SIM_TYPE_LTE: LteSimulation,
233            self.PARAM_SIM_TYPE_UMTS: UmtsSimulation,
234            self.PARAM_SIM_TYPE_GSM: GsmSimulation,
235            self.PARAM_SIM_TYPE_LTE_CA: LteCaSimulation
236        }
237
238        if not sim_type in simulation_dictionary:
239            raise ValueError("The provided simulation type is invalid.")
240
241        simulation_class = simulation_dictionary[sim_type]
242
243        if isinstance(self.simulation, simulation_class):
244            # The simulation object we already have is enough.
245            return
246
247        if self.simulation:
248            # Make sure the simulation is stopped before loading a new one
249            self.simulation.stop()
250
251        # If the calibration table doesn't have an entry for this simulation
252        # type add an empty one
253        if sim_type not in self.calibration_table:
254            self.calibration_table[sim_type] = {}
255
256        # Instantiate a new simulation
257        self.simulation = simulation_class(self.anritsu, self.log, self.dut,
258                                           self.user_params[self.PARAMS_KEY],
259                                           self.calibration_table[sim_type])
260
261        # Start the simulation
262        self.simulation.start()
263