Merge "Refactoring parse_parameters methods"
diff --git a/acts/framework/acts/test_utils/power/PowerCellularLabBaseTest.py b/acts/framework/acts/test_utils/power/PowerCellularLabBaseTest.py
index 676dafb..e3495aa 100644
--- a/acts/framework/acts/test_utils/power/PowerCellularLabBaseTest.py
+++ b/acts/framework/acts/test_utils/power/PowerCellularLabBaseTest.py
@@ -150,8 +150,12 @@
         self.simulation.detach()
 
         # Parse simulation parameters.
-        # This may return false if incorrect values are passed.
-        if not self.simulation.parse_parameters(self.parameters):
+        # This may throw a ValueError exception if incorrect values are passed
+        # or if required arguments are omitted.
+        try:
+            self.simulation.parse_parameters(self.parameters)
+        except ValueError as error:
+            self.log.error(str(error))
             return False
 
         # Wait for new params to settle
@@ -161,8 +165,7 @@
         if not self.simulation.attach():
             return False
 
-        if not self.simulation.start_test_case():
-            return False
+        self.simulation.start_test_case()
 
         # Make the device go to sleep
         self.dut.droid.goToSleepNow()
diff --git a/acts/framework/acts/test_utils/power/tel_simulations/BaseSimulation.py b/acts/framework/acts/test_utils/power/tel_simulations/BaseSimulation.py
index f30c8a4..bb191a7 100644
--- a/acts/framework/acts/test_utils/power/tel_simulations/BaseSimulation.py
+++ b/acts/framework/acts/test_utils/power/tel_simulations/BaseSimulation.py
@@ -237,11 +237,9 @@
 
         Args:
             parameters: list of parameters
-        Returns:
-            False if there was an error while parsing parameters
         """
 
-        return True
+        pass
 
     def consume_parameter(self, parameters, parameter_name, num_values=0):
         """ Parses a parameter from a list.
@@ -271,10 +269,9 @@
             for j in range(num_values + 1):
                 return_list.append(parameters.pop(i))
         except IndexError:
-            self.log.error(
+            raise ValueError(
                 "Parameter {} has to be followed by {} values.".format(
                     parameter_name, num_values))
-            raise ValueError()
 
         return return_list
 
@@ -657,10 +654,6 @@
         """ Starts a test case in the current simulation.
 
         Requires the phone to be attached.
-
-        Returns:
-            True if the case was successfully started.
-
         """
 
-        return True
+        pass
diff --git a/acts/framework/acts/test_utils/power/tel_simulations/GsmSimulation.py b/acts/framework/acts/test_utils/power/tel_simulations/GsmSimulation.py
index 67fb981..16bfa8a 100644
--- a/acts/framework/acts/test_utils/power/tel_simulations/GsmSimulation.py
+++ b/acts/framework/acts/test_utils/power/tel_simulations/GsmSimulation.py
@@ -87,22 +87,18 @@
 
         Args:
             parameters: list of parameters
-        Returns:
-            False if there was an error while parsing the config
         """
 
-        if not super().parse_parameters(parameters):
-            return False
+        super().parse_parameters(parameters)
 
         # Setup band
 
         values = self.consume_parameter(parameters, self.PARAM_BAND, 1)
 
         if not values:
-            self.log.error(
+            raise ValueError(
                 "The test name needs to include parameter '{}' followed by "
                 "the required band number.".format(self.PARAM_BAND))
-            return False
 
         self.set_band(self.bts1, values[1])
 
@@ -115,24 +111,19 @@
         elif self.consume_parameter(parameters, self.PARAM_NO_GPRS):
             self.bts1.gsm_gprs_mode = BtsGprsMode.NO_GPRS
         else:
-            self.log.error(
+            raise ValueError(
                 "GPRS mode needs to be indicated in the test name with either "
                 "{}, {} or {}.".format(self.PARAM_GPRS, self.PARAM_EGPRS,
                                        self.PARAM_NO_GPRS))
-            return False
 
         # Setup slot allocation
 
         values = self.consume_parameter(parameters, self.PARAM_SLOTS, 2)
 
         if not values:
-            self.log.error(
+            raise ValueError(
                 "The test name needs to include parameter {} followed by two "
                 "int values indicating DL and UL slots.".format(
                     self.PARAM_SLOTS))
-            return False
 
         self.bts1.gsm_slots = (int(values[1]), int(values[2]))
-
-        # No errors were found
-        return True
diff --git a/acts/framework/acts/test_utils/power/tel_simulations/LteCaSimulation.py b/acts/framework/acts/test_utils/power/tel_simulations/LteCaSimulation.py
index 0032eb4..f401465 100644
--- a/acts/framework/acts/test_utils/power/tel_simulations/LteCaSimulation.py
+++ b/acts/framework/acts/test_utils/power/tel_simulations/LteCaSimulation.py
@@ -68,23 +68,19 @@
 
         Args:
             parameters: list of parameters
-        Returns:
-            False if there was an error while parsing the config
         """
 
-        if not super(LteSimulation, self).parse_parameters(parameters):
-            return False
+        super(LteSimulation, self).parse_parameters(parameters)
 
         # Get the CA band configuration
 
         values = self.consume_parameter(parameters, self.PARAM_CA, 1)
 
         if not values:
-            self.log.error(
+            raise ValueError(
                 "The test name needs to include parameter '{}' followed by "
                 "the CA configuration. For example: ca_3c7c28a".format(
                     self.PARAM_CA))
-            return False
 
         # Carrier aggregation configurations are indicated with the band numbers
         # followed by the CA classes in a single string. For example, for 5 CA
@@ -92,10 +88,9 @@
         ca_configs = re.findall(r'(\d+[abcABC])', values[1])
 
         if not ca_configs:
-            self.log.error(
+            raise ValueError(
                 "The CA configuration has to be indicated with one string as "
                 "in the following example: ca_3c7c28a".format(self.PARAM_CA))
-            return False
 
         carriers = []
         bts_index = 0
@@ -109,20 +104,19 @@
             ca_class = ca[-1]
 
             if ca_class.upper() == 'B':
-                self.log.error("Class B carrier aggregation is not supported.")
-                return False
+                raise ValueError(
+                    "Class B carrier aggregation is not supported.")
 
             if band in carriers:
-                self.log.error("Intra-band non contiguous carrier aggregation "
-                               "is not supported.")
-                return False
+                raise ValueError(
+                    "Intra-band non contiguous carrier aggregation "
+                    "is not supported.")
 
             if ca_class.upper() == 'A':
 
                 if bts_index >= len(self.bts):
-                    self.log.error("This callbox model doesn't allow the "
-                                   "requested CA configuration")
-                    return False
+                    raise ValueError("This callbox model doesn't allow the "
+                                     "requested CA configuration")
 
                 self.set_band_with_defaults(
                     self.bts[bts_index],
@@ -134,9 +128,8 @@
             elif ca_class.upper() == 'C':
 
                 if bts_index + 1 >= len(self.bts):
-                    self.log.error("This callbox model doesn't allow the "
-                                   "requested CA configuration")
-                    return False
+                    raise ValueError("This callbox model doesn't allow the "
+                                     "requested CA configuration")
 
                 self.set_band_with_defaults(
                     self.bts[bts_index],
@@ -150,18 +143,16 @@
                 bts_index += 2
 
             else:
-                self.log.error("Invalid carrier aggregation configuration: "
-                               "{}{}.".format(band, ca_class))
-                return False
+                raise ValueError("Invalid carrier aggregation configuration: "
+                                 "{}{}.".format(band, ca_class))
 
             carriers.append(band)
 
         # Ensure there are at least two carriers being used
         self.num_carriers = bts_index
         if self.num_carriers < 2:
-            self.log.error("At least two carriers need to be indicated for the"
-                           " carrier aggregation sim.")
-            return False
+            raise ValueError("At least two carriers need to be indicated for "
+                             "the carrier aggregation sim.")
 
         # Get the bw for each carrier
         # This is an optional parameter, by default the maximum bandwidth for
@@ -214,17 +205,16 @@
                                              self.num_carriers)
 
         if not mimo_values:
-            self.log.error("The test parameter '{}' has to be included in the "
-                           "test name followed by the MIMO mode for each "
-                           "carrier separated by underscores.".format(
-                               self.PARAM_MIMO))
-            return False
+            raise ValueError(
+                "The test parameter '{}' has to be included in the "
+                "test name followed by the MIMO mode for each "
+                "carrier separated by underscores.".format(self.PARAM_MIMO))
 
         if len(mimo_values) != self.num_carriers + 1:
-            self.log.error("The test parameter '{}' has to be followed by "
-                           "a number of MIMO mode values equal to the number "
-                           "of carriers being used.".format(self.PARAM_MIMO))
-            return False
+            raise ValueError(
+                "The test parameter '{}' has to be followed by "
+                "a number of MIMO mode values equal to the number "
+                "of carriers being used.".format(self.PARAM_MIMO))
 
         for bts_index in range(self.num_carriers):
 
@@ -235,16 +225,15 @@
                     requested_mimo = mimo_mode
                     break
             else:
-                self.log.error("The mimo mode must be one of %s." %
-                               {elem.value
-                                for elem in LteSimulation.MimoMode})
-                return False
+                raise ValueError(
+                    "The mimo mode must be one of %s." %
+                    {elem.value
+                     for elem in LteSimulation.MimoMode})
 
             if (requested_mimo == LteSimulation.MimoMode.MIMO_4x4
                     and self.anritsu._md8475_version == 'A'):
-                self.log.error("The test requires 4x4 MIMO, but that is not "
-                               "supported by the MD8475A callbox.")
-                return False
+                raise ValueError("The test requires 4x4 MIMO, but that is not "
+                                 "supported by the MD8475A callbox.")
 
             self.set_mimo_mode(self.bts[bts_index], requested_mimo)
 
@@ -256,11 +245,10 @@
                         requested_tm = tm
                         break
                 else:
-                    self.log.error(
+                    raise ValueError(
                         "The TM must be one of %s." %
                         {elem.value
                          for elem in LteSimulation.MimoMode})
-                    return False
             else:
                 # Provide default values if the TM parameter is not set
                 if requested_mimo == LteSimulation.MimoMode.MIMO_1x1:
@@ -277,9 +265,6 @@
 
         ul_power = self.get_uplink_power_from_parameters(parameters)
 
-        if not ul_power:
-            return False
-
         # Power is not set on the callbox until after the simulation is
         # started. Saving this value in a variable for later
         self.sim_ul_power = ul_power
@@ -288,9 +273,6 @@
 
         dl_power = self.get_downlink_power_from_parameters(parameters)
 
-        if not dl_power:
-            return False
-
         # Power is not set on the callbox until after the simulation is
         # started. Saving this value in a variable for later
         self.sim_dl_power = dl_power
@@ -311,13 +293,12 @@
                     scheduling = scheduling_mode
                     break
             else:
-                self.log.error(
+                raise ValueError(
                     "The test name parameter '{}' has to be followed by one of "
                     "{}.".format(
                         self.PARAM_SCHEDULING,
                         {elem.value
                          for elem in LteSimulation.SchedulingMode}))
-                return False
 
         if scheduling == LteSimulation.SchedulingMode.STATIC:
 
@@ -338,11 +319,10 @@
 
             if (dl_pattern, ul_pattern) not in [(0, 100), (100, 0), (100,
                                                                      100)]:
-                self.log.error(
+                raise ValueError(
                     "Only full RB allocation for DL or UL is supported in CA "
                     "sims. The allowed combinations are 100/0, 0/100 and "
                     "100/100.")
-                return False
 
             if self.dl_256_qam and bw == 1.4:
                 mcs_dl = 26
@@ -377,9 +357,6 @@
                 self.set_scheduling_mode(self.bts[bts_index],
                                          LteSimulation.SchedulingMode.DYNAMIC)
 
-        # No errors were found
-        return True
-
     def set_band_with_defaults(self, bts, band, calibrate_if_necessary=True):
         """ Switches to the given band restoring default values
 
@@ -442,12 +419,12 @@
         while self.anritsu.get_testcase_status() == "0":
             retry_counter += 1
             if retry_counter == 3:
-                self.log.error("The test case failed to start.")
-                return False
+                raise RuntimeError("The test case failed to start after {} "
+                                   "retries. The connection between the phone "
+                                   "and the basestation might be unstable."
+                                   .format(retry_counter))
             time.sleep(10)
 
-        return True
-
     def maximum_downlink_throughput(self):
         """ Calculates maximum downlink throughput as the sum of all the active
         carriers.
diff --git a/acts/framework/acts/test_utils/power/tel_simulations/LteSimulation.py b/acts/framework/acts/test_utils/power/tel_simulations/LteSimulation.py
index bf3040e..73a5cd0 100644
--- a/acts/framework/acts/test_utils/power/tel_simulations/LteSimulation.py
+++ b/acts/framework/acts/test_utils/power/tel_simulations/LteSimulation.py
@@ -301,22 +301,18 @@
 
         Args:
             parameters: list of parameters
-        Returns:
-            False if there was an error while parsing the config
         """
 
-        if not super().parse_parameters(parameters):
-            return False
+        super().parse_parameters(parameters)
 
         # Setup band
 
         values = self.consume_parameter(parameters, self.PARAM_BAND, 1)
 
         if not values:
-            self.log.error(
+            raise ValueError(
                 "The test name needs to include parameter '{}' followed by "
                 "the required band number.".format(self.PARAM_BAND))
-            return False
 
         band = values[1]
 
@@ -324,16 +320,16 @@
 
         # Set DL/UL frame configuration
         if self.get_duplex_mode(band) == self.DuplexMode.TDD:
-            try:
-                values = self.consume_parameter(parameters,
-                                                self.PARAM_FRAME_CONFIG, 1)
-                frame_config = int(values[1])
-            except:
-                self.log.error("When a TDD band is selected the frame "
-                               "structure has to be indicated with the '{}' "
-                               "parameter followed by a number from 0 to 6."
-                               .format(self.PARAM_FRAME_CONFIG))
-                return False
+
+            values = self.consume_parameter(parameters,
+                                            self.PARAM_FRAME_CONFIG, 1)
+            if not values:
+                raise ValueError("When a TDD band is selected the frame "
+                                 "structure has to be indicated with the '{}' "
+                                 "parameter followed by a number from 0 to 6."
+                                 .format(self.PARAM_FRAME_CONFIG))
+
+            frame_config = int(values[1])
 
             self.set_dlul_configuration(self.bts1, frame_config)
 
@@ -342,10 +338,10 @@
         values = self.consume_parameter(parameters, self.PARAM_BW, 1)
 
         if not values:
-            self.log.error(
-                "The test name needs to include parameter {} followed by an int"
-                " value (to indicate 1.4 MHz use 14).".format(self.PARAM_BW))
-            return False
+            raise ValueError(
+                "The test name needs to include parameter {} followed by an "
+                "int value (to indicate 1.4 MHz use 14).".format(
+                    self.PARAM_BW))
 
         bw = float(values[1])
 
@@ -359,25 +355,22 @@
         values = self.consume_parameter(parameters, self.PARAM_MIMO, 1)
 
         if not values:
-            self.log.error(
-                "The test name needs to include parameter '{}' followed by the"
-                " mimo mode.".format(self.PARAM_MIMO))
-            return False
+            raise ValueError(
+                "The test name needs to include parameter '{}' followed by the "
+                "mimo mode.".format(self.PARAM_MIMO))
 
         for mimo_mode in LteSimulation.MimoMode:
             if values[1] == mimo_mode.value:
                 self.set_mimo_mode(self.bts1, mimo_mode)
                 break
         else:
-            self.log.error("The {} parameter needs to be followed by either "
-                           "1x1, 2x2 or 4x4.".format(self.PARAM_MIMO))
-            return False
+            raise ValueError("The {} parameter needs to be followed by either "
+                             "1x1, 2x2 or 4x4.".format(self.PARAM_MIMO))
 
         if (mimo_mode == LteSimulation.MimoMode.MIMO_4x4
                 and self.anritsu._md8475_version == 'A'):
-            self.log.error("The test requires 4x4 MIMO, but that is not "
-                           "supported by the MD8475A callbox.")
-            return False
+            raise ValueError("The test requires 4x4 MIMO, but that is not "
+                             "supported by the MD8475A callbox.")
 
         self.set_mimo_mode(self.bts1, mimo_mode)
 
@@ -386,21 +379,19 @@
         values = self.consume_parameter(parameters, self.PARAM_TM, 1)
 
         if not values:
-            self.log.error(
-                "The test name needs to include parameter {} followed by an int"
-                " value from 1 to 4 indicating transmission mode.".format(
+            raise ValueError(
+                "The test name needs to include parameter {} followed by an "
+                "int value from 1 to 4 indicating transmission mode.".format(
                     self.PARAM_TM))
-            return False
 
         for tm in LteSimulation.TransmissionMode:
             if values[1] == tm.value[2:]:
                 self.set_transmission_mode(self.bts1, tm)
                 break
         else:
-            self.log.error("The {} parameter needs to be followed by either "
-                           "TM1, TM2, TM3, TM4, TM7, TM8 or TM9.".format(
-                               self.PARAM_MIMO))
-            return False
+            raise ValueError("The {} parameter needs to be followed by either "
+                             "TM1, TM2, TM3, TM4, TM7, TM8 or TM9.".format(
+                                 self.PARAM_MIMO))
 
         # Setup scheduling mode
 
@@ -416,10 +407,9 @@
         elif values[1] == self.PARAM_SCHEDULING_STATIC:
             scheduling = LteSimulation.SchedulingMode.STATIC
         else:
-            self.log.error(
+            raise ValueError(
                 "The test name parameter '{}' has to be followed by either "
                 "'dynamic' or 'static'.".format(self.PARAM_SCHEDULING))
-            return False
 
         if scheduling == LteSimulation.SchedulingMode.STATIC:
 
@@ -439,10 +429,9 @@
                 ul_pattern = int(values[2])
 
             if not (0 <= dl_pattern <= 100 and 0 <= ul_pattern <= 100):
-                self.log.error(
+                raise ValueError(
                     "The scheduling pattern parameters need to be two "
                     "positive numbers between 0 and 100.")
-                return False
 
             dl_rbs, ul_rbs = self.allocation_percentages_to_rbs(
                 self.bts1, dl_pattern, ul_pattern)
@@ -477,9 +466,6 @@
 
         ul_power = self.get_uplink_power_from_parameters(parameters)
 
-        if not ul_power:
-            return False
-
         # Power is not set on the callbox until after the simulation is
         # started. Saving this value in a variable for later
         self.sim_ul_power = ul_power
@@ -488,29 +474,21 @@
 
         dl_power = self.get_downlink_power_from_parameters(parameters)
 
-        if not dl_power:
-            return False
-
         # Power is not set on the callbox until after the simulation is
         # started. Saving this value in a variable for later
         self.sim_dl_power = dl_power
 
-        # No errors were found
-        return True
-
     def get_uplink_power_from_parameters(self, parameters):
         """ Reads uplink power from a list of parameters. """
 
         values = self.consume_parameter(parameters, self.PARAM_UL_PW, 1)
 
         if not values or values[1] not in self.uplink_signal_level_dictionary:
-            self.log.error(
+            raise ValueError(
                 "The test name needs to include parameter {} followed by one "
                 "the following values: {}.".format(self.PARAM_UL_PW, [
-                    "\n" + val
-                    for val in self.uplink_signal_level_dictionary.keys()
+                    val for val in self.uplink_signal_level_dictionary.keys()
                 ]))
-            return None
 
         return self.uplink_signal_level_dictionary[values[1]]
 
@@ -521,17 +499,16 @@
 
         if values:
             if values[1] not in self.downlink_rsrp_dictionary:
-                self.log.error("Invalid signal level value {}.".format(
+                raise ValueError("Invalid signal level value {}.".format(
                     values[1]))
-                return None
             else:
                 return self.downlink_rsrp_dictionary[values[1]]
         else:
             # Use default value
             power = self.downlink_rsrp_dictionary['excellent']
-            self.log.error(
-                "No DL signal level value was indicated in the test parameters."
-                " Using default value of {} RSRP.".format(power))
+            self.log.info(
+                "No DL signal level value was indicated in the test "
+                "parameters. Using default value of {} RSRP.".format(power))
             return power
 
     def set_downlink_rx_power(self, bts, rsrp):
diff --git a/acts/framework/acts/test_utils/power/tel_simulations/UmtsSimulation.py b/acts/framework/acts/test_utils/power/tel_simulations/UmtsSimulation.py
index a2cb7ac..aa89d01 100644
--- a/acts/framework/acts/test_utils/power/tel_simulations/UmtsSimulation.py
+++ b/acts/framework/acts/test_utils/power/tel_simulations/UmtsSimulation.py
@@ -107,22 +107,18 @@
 
         Args:
             parameters: list of parameters
-        Returns:
-            False if there was an error while parsing the config
         """
 
-        if not super().parse_parameters(parameters):
-            return False
+        super().parse_parameters(parameters)
 
         # Setup band
 
         values = self.consume_parameter(parameters, self.PARAM_BAND, 1)
 
         if not values:
-            self.log.error(
+            raise ValueError(
                 "The test name needs to include parameter '{}' followed by "
                 "the required band number.".format(self.PARAM_BAND))
-            return False
 
         self.set_band(self.bts1, values[1])
 
@@ -135,10 +131,9 @@
                 self.PARAM_RELEASE_VERSION_7, self.PARAM_RELEASE_VERSION_8,
                 self.PARAM_RELEASE_VERSION_99
         ]:
-            self.log.error(
+            raise ValueError(
                 "The test name needs to include the parameter {} followed by a "
                 "valid release version.".format(self.PARAM_RELEASE_VERSION))
-            return False
 
         self.set_release_version(self.bts1, values[1])
 
@@ -146,13 +141,12 @@
 
         values = self.consume_parameter(parameters, self.PARAM_UL_PW, 1)
         if not values or values[1] not in self.uplink_signal_level_dictionary:
-            self.log.error(
+            raise ValueError(
                 "The test name needs to include parameter {} followed by "
                 "one the following values: {}.".format(self.PARAM_UL_PW, [
                     "\n" + val
                     for val in self.uplink_signal_level_dictionary.keys()
                 ]))
-            return False
 
         # Power is not set on the callbox until after the simulation is
         # started. Will save this value in a variable and use it later
@@ -163,22 +157,16 @@
         values = self.consume_parameter(parameters, self.PARAM_DL_PW, 1)
 
         if not values or values[1] not in self.downlink_rscp_dictionary:
-            self.log.error(
+            raise ValueError(
                 "The test name needs to include parameter {} followed by "
                 "one of the following values: {}.".format(
-                    self.PARAM_DL_PW, [
-                        "\n" + val
-                        for val in self.downlink_rscp_dictionary.keys()
-                    ]))
-            return False
+                    self.PARAM_DL_PW,
+                    [val for val in self.downlink_rscp_dictionary.keys()]))
 
         # Power is not set on the callbox until after the simulation is
         # started. Will save this value in a variable and use it later
         self.sim_dl_power = self.downlink_rscp_dictionary[values[1]]
 
-        # No errors were found
-        return True
-
     def set_release_version(self, bts, release_version):
         """ Sets the release version.