Support FD.io Multiple Loss Ratio search (MLRsearch) 55/65255/11
authortreyad <treyad@viosoft.com>
Tue, 20 Nov 2018 09:54:08 +0000 (01:54 -0800)
committertreyad <treyad@viosoft.com>
Wed, 27 Mar 2019 12:17:13 +0000 (05:17 -0700)
Optimized binary search algorithm for finding NDR and PDR bounds

JIRA: YARDSTICK-1486

Change-Id: Iba0e6c7065b61f7b7f3f65cceca509d1aaff8366
Signed-off-by: treyad <treyad@viosoft.com>
14 files changed:
samples/vnf_samples/traffic_profiles/ipv4_throughput_latency_vpp.yaml
yardstick/network_services/helpers/vpp_helpers/__init__.py [new file with mode: 0644]
yardstick/network_services/helpers/vpp_helpers/abstract_search_algorithm.py [new file with mode: 0644]
yardstick/network_services/helpers/vpp_helpers/multiple_loss_ratio_search.py [new file with mode: 0644]
yardstick/network_services/helpers/vpp_helpers/ndr_pdr_result.py [new file with mode: 0644]
yardstick/network_services/helpers/vpp_helpers/receive_rate_interval.py [new file with mode: 0644]
yardstick/network_services/helpers/vpp_helpers/receive_rate_measurement.py [new file with mode: 0644]
yardstick/network_services/traffic_profile/vpp_rfc2544.py
yardstick/tests/unit/network_services/helpers/vpp_helpers/__init__.py [new file with mode: 0644]
yardstick/tests/unit/network_services/helpers/vpp_helpers/test_multiple_loss_ratio_search.py [new file with mode: 0644]
yardstick/tests/unit/network_services/helpers/vpp_helpers/test_ndr_pdr_result.py [new file with mode: 0644]
yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_interval.py [new file with mode: 0644]
yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_measurement.py [new file with mode: 0644]
yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py

index 1add9bf..abbad67 100644 (file)
@@ -20,6 +20,7 @@ description:     Traffic profile to run RFC2544 latency
 traffic_profile:
   traffic_type: VppRFC2544Profile # defines traffic behavior - constant or look for highest possible throughput
   enable_latency: true
+  intermediate_phases: 2
   test_precision: 0.1
   duration: 30
   lower_bound: 1.0
diff --git a/yardstick/network_services/helpers/vpp_helpers/__init__.py b/yardstick/network_services/helpers/vpp_helpers/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/yardstick/network_services/helpers/vpp_helpers/abstract_search_algorithm.py b/yardstick/network_services/helpers/vpp_helpers/abstract_search_algorithm.py
new file mode 100644 (file)
index 0000000..fced058
--- /dev/null
@@ -0,0 +1,53 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# 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.
+#
+# This is a modified copy of
+# https://gerrit.fd.io/r/gitweb?p=csit.git;a=blob_plain;f=resources/libraries/python/MLRsearch/AbstractSearchAlgorithm.py;hb=HEAD
+
+
+from abc import ABCMeta, abstractmethod
+
+
+class AbstractSearchAlgorithm(object):
+    """Abstract class defining common API for search algorithms."""
+
+    __metaclass__ = ABCMeta
+
+    def __init__(self, measurer):
+        """Store the rate provider.
+
+        :param measurer: Object able to perform trial or composite measurements.
+        :type measurer: AbstractMeasurer.AbstractMeasurer
+        """
+        # TODO: Type check for AbstractMeasurer?
+        self.measurer = measurer
+
+    @abstractmethod
+    def narrow_down_ndr_and_pdr(
+            self, fail_rate, line_rate, packet_loss_ratio):
+        """Perform measurements to narrow down intervals, return them.
+
+        This will be renamed when custom loss ratio lists are supported.
+
+        :param fail_rate: Minimal target transmit rate [pps].
+        :param line_rate: Maximal target transmit rate [pps].
+        :param packet_loss_ratio: Fraction of packets lost, for PDR [1].
+        :type fail_rate: float
+        :type line_rate: float
+        :type packet_loss_ratio: float
+        :returns: Structure containing narrowed down intervals
+            and their measurements.
+        :rtype: NdrPdrResult.NdrPdrResult
+        """
+        # TODO: Do we agree on arguments related to precision or trial duration?
diff --git a/yardstick/network_services/helpers/vpp_helpers/multiple_loss_ratio_search.py b/yardstick/network_services/helpers/vpp_helpers/multiple_loss_ratio_search.py
new file mode 100644 (file)
index 0000000..582e3dc
--- /dev/null
@@ -0,0 +1,688 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# 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.
+#
+# This is a modified copy of
+# https://gerrit.fd.io/r/gitweb?p=csit.git;a=blob_plain;f=resources/libraries/python/MLRsearch/MultipleLossRatioSearch.py;hb=HEAD
+
+import datetime
+import logging
+import math
+import time
+
+from yardstick.network_services.helpers.vpp_helpers.abstract_search_algorithm import \
+    AbstractSearchAlgorithm
+from yardstick.network_services.helpers.vpp_helpers.ndr_pdr_result import \
+    NdrPdrResult
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \
+    ReceiveRateInterval
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \
+    ReceiveRateMeasurement
+
+LOGGING = logging.getLogger(__name__)
+
+
+class MultipleLossRatioSearch(AbstractSearchAlgorithm):
+    """Optimized binary search algorithm for finding NDR and PDR bounds.
+
+    Traditional binary search algorithm needs initial interval
+    (lower and upper bound), and returns final interval after bisecting
+    (until some exit condition is met).
+    The exit condition is usually related to the interval width,
+    (upper bound value minus lower bound value).
+
+    The optimized algorithm contains several improvements
+    aimed to reduce overall search time.
+
+    One improvement is searching for two intervals at once.
+    The intervals are for NDR (No Drop Rate) and PDR (Partial Drop Rate).
+
+    Next improvement is that the initial interval does not need to be valid.
+    Imagine initial interval (10, 11) where 11 is smaller
+    than the searched value.
+    The algorithm will try (11, 13) interval next, and if 13 is still smaller,
+    (13, 17) and so on, doubling width until the upper bound is valid.
+    The part when interval expands is called external search,
+    the part when interval is bisected is called internal search.
+
+    Next improvement is that trial measurements at small trial duration
+    can be used to find a reasonable interval for full trial duration search.
+    This results in more trials performed, but smaller overall duration
+    in general.
+
+    Next improvement is bisecting in logarithmic quantities,
+    so that exit criteria can be independent of measurement units.
+
+    Next improvement is basing the initial interval on receive rates.
+
+    Final improvement is exiting early if the minimal value
+    is not a valid lower bound.
+
+    The complete search consist of several phases,
+    each phase performing several trial measurements.
+    Initial phase creates initial interval based on receive rates
+    at maximum rate and at maximum receive rate (MRR).
+    Final phase and preceding intermediate phases are performing
+    external and internal search steps,
+    each resulting interval is the starting point for the next phase.
+    The resulting interval of final phase is the result of the whole algorithm.
+
+    Each non-initial phase uses its own trial duration and width goal.
+    Any non-initial phase stops searching (for NDR or PDR independently)
+    when minimum is not a valid lower bound (at current duration),
+    or all of the following is true:
+    Both bounds are valid, bound bounds are measured at the current phase
+    trial duration, interval width is less than the width goal
+    for current phase.
+
+    TODO: Review and update this docstring according to rst docs.
+    TODO: Support configurable number of Packet Loss Ratios.
+    """
+
+    class ProgressState(object):
+        """Structure containing data to be passed around in recursion."""
+
+        def __init__(
+                self, result, phases, duration, width_goal, packet_loss_ratio,
+                minimum_transmit_rate, maximum_transmit_rate):
+            """Convert and store the argument values.
+
+            :param result: Current measured NDR and PDR intervals.
+            :param phases: How many intermediate phases to perform
+                before the current one.
+            :param duration: Trial duration to use in the current phase [s].
+            :param width_goal: The goal relative width for the curreent phase.
+            :param packet_loss_ratio: PDR fraction for the current search.
+            :param minimum_transmit_rate: Minimum target transmit rate
+                for the current search [pps].
+            :param maximum_transmit_rate: Maximum target transmit rate
+                for the current search [pps].
+            :type result: NdrPdrResult.NdrPdrResult
+            :type phases: int
+            :type duration: float
+            :type width_goal: float
+            :type packet_loss_ratio: float
+            :type minimum_transmit_rate: float
+            :type maximum_transmit_rate: float
+            """
+            self.result = result
+            self.phases = int(phases)
+            self.duration = float(duration)
+            self.width_goal = float(width_goal)
+            self.packet_loss_ratio = float(packet_loss_ratio)
+            self.minimum_transmit_rate = float(minimum_transmit_rate)
+            self.maximum_transmit_rate = float(maximum_transmit_rate)
+
+    def __init__(self, measurer, latency=False, pkt_size=64,
+                 final_relative_width=0.005,
+                 final_trial_duration=30.0, initial_trial_duration=1.0,
+                 number_of_intermediate_phases=2, timeout=600.0, doublings=1):
+        """Store the measurer object and additional arguments.
+
+        :param measurer: Rate provider to use by this search object.
+        :param final_relative_width: Final lower bound transmit rate
+            cannot be more distant that this multiple of upper bound [1].
+        :param final_trial_duration: Trial duration for the final phase [s].
+        :param initial_trial_duration: Trial duration for the initial phase
+            and also for the first intermediate phase [s].
+        :param number_of_intermediate_phases: Number of intermediate phases
+            to perform before the final phase [1].
+        :param timeout: The search will fail itself when not finished
+            before this overall time [s].
+        :param doublings: How many doublings to do in external search step.
+            Default 1 is suitable for fairly stable tests,
+            less stable tests might get better overal duration with 2 or more.
+        :type measurer: AbstractMeasurer.AbstractMeasurer
+        :type final_relative_width: float
+        :type final_trial_duration: float
+        :type initial_trial_duration: int
+        :type number_of_intermediate_phases: int
+        :type timeout: float
+        :type doublings: int
+        """
+        super(MultipleLossRatioSearch, self).__init__(measurer)
+        self.latency = latency
+        self.pkt_size = int(pkt_size)
+        self.final_trial_duration = float(final_trial_duration)
+        self.final_relative_width = float(final_relative_width)
+        self.number_of_intermediate_phases = int(number_of_intermediate_phases)
+        self.initial_trial_duration = float(initial_trial_duration)
+        self.timeout = float(timeout)
+        self.doublings = int(doublings)
+
+        self.queue = None
+        self.port_pg_id = None
+        self.ports = []
+        self.test_data = {}
+        self.profiles = {}
+
+    @staticmethod
+    def double_relative_width(relative_width):
+        """Return relative width corresponding to double logarithmic width.
+
+        :param relative_width: The base relative width to double.
+        :type relative_width: float
+        :returns: The relative width of double logarithmic size.
+        :rtype: float
+        """
+        return 1.999 * relative_width - relative_width * relative_width
+        # The number should be 2.0, but we want to avoid rounding errors,
+        # and ensure half of double is not larger than the original value.
+
+    @staticmethod
+    def double_step_down(relative_width, current_bound):
+        """Return rate of double logarithmic width below.
+
+        :param relative_width: The base relative width to double.
+        :param current_bound: The current target transmit rate to move [pps].
+        :type relative_width: float
+        :type current_bound: float
+        :returns: Transmit rate smaller by logarithmically double width [pps].
+        :rtype: float
+        """
+        return current_bound * (
+                1.0 - MultipleLossRatioSearch.double_relative_width(
+            relative_width))
+
+    @staticmethod
+    def expand_down(relative_width, doublings, current_bound):
+        """Return rate of expanded logarithmic width below.
+
+        :param relative_width: The base relative width to double.
+        :param doublings: How many doublings to do for expansion.
+        :param current_bound: The current target transmit rate to move [pps].
+        :type relative_width: float
+        :type doublings: int
+        :type current_bound: float
+        :returns: Transmit rate smaller by logarithmically double width [pps].
+        :rtype: float
+        """
+        for _ in range(doublings):
+            relative_width = MultipleLossRatioSearch.double_relative_width(
+                relative_width)
+        return current_bound * (1.0 - relative_width)
+
+    @staticmethod
+    def double_step_up(relative_width, current_bound):
+        """Return rate of double logarithmic width above.
+
+        :param relative_width: The base relative width to double.
+        :param current_bound: The current target transmit rate to move [pps].
+        :type relative_width: float
+        :type current_bound: float
+        :returns: Transmit rate larger by logarithmically double width [pps].
+        :rtype: float
+        """
+        return current_bound / (
+                1.0 - MultipleLossRatioSearch.double_relative_width(
+            relative_width))
+
+    @staticmethod
+    def expand_up(relative_width, doublings, current_bound):
+        """Return rate of expanded logarithmic width above.
+
+        :param relative_width: The base relative width to double.
+        :param doublings: How many doublings to do for expansion.
+        :param current_bound: The current target transmit rate to move [pps].
+        :type relative_width: float
+        :type doublings: int
+        :type current_bound: float
+        :returns: Transmit rate smaller by logarithmically double width [pps].
+        :rtype: float
+        """
+        for _ in range(doublings):
+            relative_width = MultipleLossRatioSearch.double_relative_width(
+                relative_width)
+        return current_bound / (1.0 - relative_width)
+
+    @staticmethod
+    def half_relative_width(relative_width):
+        """Return relative width corresponding to half logarithmic width.
+
+        :param relative_width: The base relative width to halve.
+        :type relative_width: float
+        :returns: The relative width of half logarithmic size.
+        :rtype: float
+        """
+        return 1.0 - math.sqrt(1.0 - relative_width)
+
+    @staticmethod
+    def half_step_up(relative_width, current_bound):
+        """Return rate of half logarithmic width above.
+
+        :param relative_width: The base relative width to halve.
+        :param current_bound: The current target transmit rate to move [pps].
+        :type relative_width: float
+        :type current_bound: float
+        :returns: Transmit rate larger by logarithmically half width [pps].
+        :rtype: float
+        """
+        return current_bound / (
+                1.0 - MultipleLossRatioSearch.half_relative_width(
+            relative_width))
+
+    def init_generator(self, ports, port_pg_id, profiles, test_data, queue):
+        self.ports = ports
+        self.port_pg_id = port_pg_id
+        self.profiles = profiles
+        self.test_data = test_data
+        self.queue = queue
+        self.queue.cancel_join_thread()
+
+    def collect_kpi(self, stats, test_value):
+        samples = self.measurer.generate_samples(stats, self.ports,
+                                                 self.port_pg_id, self.latency)
+        samples.update(self.test_data)
+        LOGGING.info("Collect TG KPIs %s %s %s", datetime.datetime.now(),
+                     test_value, samples)
+        self.queue.put(samples)
+
+    def narrow_down_ndr_and_pdr(
+            self, minimum_transmit_rate, maximum_transmit_rate,
+            packet_loss_ratio):
+        """Perform initial phase, create state object, proceed with next phases.
+
+        :param minimum_transmit_rate: Minimal target transmit rate [pps].
+        :param maximum_transmit_rate: Maximal target transmit rate [pps].
+        :param packet_loss_ratio: Fraction of packets lost, for PDR [1].
+        :type minimum_transmit_rate: float
+        :type maximum_transmit_rate: float
+        :type packet_loss_ratio: float
+        :returns: Structure containing narrowed down intervals
+            and their measurements.
+        :rtype: NdrPdrResult.NdrPdrResult
+        :raises RuntimeError: If total duration is larger than timeout.
+        """
+        minimum_transmit_rate = float(minimum_transmit_rate)
+        maximum_transmit_rate = float(maximum_transmit_rate)
+        packet_loss_ratio = float(packet_loss_ratio)
+        line_measurement = self.measure(
+            self.initial_trial_duration, maximum_transmit_rate, self.latency)
+        initial_width_goal = self.final_relative_width
+        for _ in range(self.number_of_intermediate_phases):
+            initial_width_goal = self.double_relative_width(initial_width_goal)
+        max_lo = maximum_transmit_rate * (1.0 - initial_width_goal)
+        mrr = max(
+            minimum_transmit_rate,
+            min(max_lo, line_measurement.receive_rate))
+        mrr_measurement = self.measure(
+            self.initial_trial_duration, mrr, self.latency)
+        # Attempt to get narrower width.
+        if mrr_measurement.loss_fraction > 0.0:
+            max2_lo = mrr * (1.0 - initial_width_goal)
+            mrr2 = min(max2_lo, mrr_measurement.receive_rate)
+        else:
+            mrr2 = mrr / (1.0 - initial_width_goal)
+        if mrr2 > minimum_transmit_rate and mrr2 < maximum_transmit_rate:
+            line_measurement = mrr_measurement
+            mrr_measurement = self.measure(
+                self.initial_trial_duration, mrr2, self.latency)
+            if mrr2 > mrr:
+                buf = line_measurement
+                line_measurement = mrr_measurement
+                mrr_measurement = buf
+        starting_interval = ReceiveRateInterval(
+            mrr_measurement, line_measurement)
+        starting_result = NdrPdrResult(starting_interval, starting_interval)
+        state = self.ProgressState(
+            starting_result, self.number_of_intermediate_phases,
+            self.final_trial_duration, self.final_relative_width,
+            packet_loss_ratio, minimum_transmit_rate, maximum_transmit_rate)
+        state = self.ndrpdr(state)
+        result = state.result
+        # theor_max_thruput = 0
+        result_samples = {}
+
+        MultipleLossRatioSearch.display_single_bound(result_samples,
+                'NDR_LOWER', result.ndr_interval.measured_low.transmit_rate,
+                self.pkt_size, result.ndr_interval.measured_low.latency)
+        MultipleLossRatioSearch.display_single_bound(result_samples,
+                'NDR_UPPER', result.ndr_interval.measured_high.transmit_rate,
+                self.pkt_size)
+        MultipleLossRatioSearch.display_single_bound(result_samples,
+                'PDR_LOWER', result.pdr_interval.measured_low.transmit_rate,
+                self.pkt_size, result.pdr_interval.measured_low.latency)
+        MultipleLossRatioSearch.display_single_bound(result_samples,
+                'PDR_UPPER', result.pdr_interval.measured_high.transmit_rate,
+                self.pkt_size)
+        pdr_msg = self.check_ndrpdr_interval_validity(result_samples, "PDR",
+                                                      result.pdr_interval,
+                                                      packet_loss_ratio)
+        ndr_msg = self.check_ndrpdr_interval_validity(result_samples, "NDR",
+                                                      result.ndr_interval)
+        self.queue.put(result_samples)
+
+        LOGGING.debug("result_samples: %s", result_samples)
+        LOGGING.info(pdr_msg)
+        LOGGING.info(ndr_msg)
+
+        self.perform_additional_measurements_based_on_ndrpdr_result(result)
+
+        return result_samples
+
+    def _measure_and_update_state(self, state, transmit_rate):
+        """Perform trial measurement, update bounds, return new state.
+
+        :param state: State before this measurement.
+        :param transmit_rate: Target transmit rate for this measurement [pps].
+        :type state: ProgressState
+        :type transmit_rate: float
+        :returns: State after the measurement.
+        :rtype: ProgressState
+        """
+        # TODO: Implement https://stackoverflow.com/a/24683360
+        # to avoid the string manipulation if log verbosity is too low.
+        LOGGING.info("result before update: %s", state.result)
+        LOGGING.debug(
+            "relative widths in goals: %s", state.result.width_in_goals(
+                self.final_relative_width))
+        measurement = self.measure(state.duration, transmit_rate, self.latency)
+        ndr_interval = self._new_interval(
+            state.result.ndr_interval, measurement, 0.0)
+        pdr_interval = self._new_interval(
+            state.result.pdr_interval, measurement, state.packet_loss_ratio)
+        state.result = NdrPdrResult(ndr_interval, pdr_interval)
+        return state
+
+    @staticmethod
+    def _new_interval(old_interval, measurement, packet_loss_ratio):
+        """Return new interval with bounds updated according to the measurement.
+
+        :param old_interval: The current interval before the measurement.
+        :param measurement: The new meaqsurement to take into account.
+        :param packet_loss_ratio: Fraction for PDR (or zero for NDR).
+        :type old_interval: ReceiveRateInterval.ReceiveRateInterval
+        :type measurement: ReceiveRateMeasurement.ReceiveRateMeasurement
+        :type packet_loss_ratio: float
+        :returns: The updated interval.
+        :rtype: ReceiveRateInterval.ReceiveRateInterval
+        """
+        old_lo, old_hi = old_interval.measured_low, old_interval.measured_high
+        # Priority zero: direct replace if the target Tr is the same.
+        if measurement.target_tr in (old_lo.target_tr, old_hi.target_tr):
+            if measurement.target_tr == old_lo.target_tr:
+                return ReceiveRateInterval(measurement, old_hi)
+            else:
+                return ReceiveRateInterval(old_lo, measurement)
+        # Priority one: invalid lower bound allows only one type of update.
+        if old_lo.loss_fraction > packet_loss_ratio:
+            # We can only expand down, old bound becomes valid upper one.
+            if measurement.target_tr < old_lo.target_tr:
+                return ReceiveRateInterval(measurement, old_lo)
+            else:
+                return old_interval
+        # Lower bound is now valid.
+        # Next priorities depend on target Tr.
+        if measurement.target_tr < old_lo.target_tr:
+            # Lower external measurement, relevant only
+            # if the new measurement has high loss rate.
+            if measurement.loss_fraction > packet_loss_ratio:
+                # Returning the broader interval as old_lo
+                # would be invalid upper bound.
+                return ReceiveRateInterval(measurement, old_hi)
+        elif measurement.target_tr > old_hi.target_tr:
+            # Upper external measurement, only relevant for invalid upper bound.
+            if old_hi.loss_fraction <= packet_loss_ratio:
+                # Old upper bound becomes valid new lower bound.
+                return ReceiveRateInterval(old_hi, measurement)
+        else:
+            # Internal measurement, replaced boundary
+            # depends on measured loss fraction.
+            if measurement.loss_fraction > packet_loss_ratio:
+                # We have found a narrow valid interval,
+                # regardless of whether old upper bound was valid.
+                return ReceiveRateInterval(old_lo, measurement)
+            else:
+                # In ideal world, we would not want to shrink interval
+                # if upper bound is not valid.
+                # In the real world, we want to shrink it for
+                # "invalid upper bound at maximal rate" case.
+                return ReceiveRateInterval(measurement, old_hi)
+        # Fallback, the interval is unchanged by the measurement.
+        return old_interval
+
+    def ndrpdr(self, state):
+        """Pefrom trials for this phase. Return the new state when done.
+
+        :param state: State before this phase.
+        :type state: ProgressState
+        :returns: The updated state.
+        :rtype: ProgressState
+        :raises RuntimeError: If total duration is larger than timeout.
+        """
+        start_time = time.time()
+        if state.phases > 0:
+            # We need to finish preceding intermediate phases first.
+            saved_phases = state.phases
+            state.phases -= 1
+            # Preceding phases have shorter duration.
+            saved_duration = state.duration
+            duration_multiplier = state.duration / self.initial_trial_duration
+            phase_exponent = float(state.phases) / saved_phases
+            state.duration = self.initial_trial_duration * math.pow(
+                duration_multiplier, phase_exponent)
+            # Shorter durations do not need that narrow widths.
+            saved_width = state.width_goal
+            state.width_goal = self.double_relative_width(state.width_goal)
+            # Recurse.
+            state = self.ndrpdr(state)
+            # Restore the state for current phase.
+            state.duration = saved_duration
+            state.width_goal = saved_width
+            state.phases = saved_phases  # Not needed, but just in case.
+        LOGGING.info(
+            "starting iterations with duration %s and relative width goal %s",
+            state.duration, state.width_goal)
+        while 1:
+            if time.time() > start_time + self.timeout:
+                raise RuntimeError("Optimized search takes too long.")
+            # Order of priorities: invalid bounds (nl, pl, nh, ph),
+            # then narrowing relative Tr widths.
+            # Durations are not priorities yet,
+            # they will settle on their own hopefully.
+            ndr_lo = state.result.ndr_interval.measured_low
+            ndr_hi = state.result.ndr_interval.measured_high
+            pdr_lo = state.result.pdr_interval.measured_low
+            pdr_hi = state.result.pdr_interval.measured_high
+            ndr_rel_width = max(
+                state.width_goal, state.result.ndr_interval.rel_tr_width)
+            pdr_rel_width = max(
+                state.width_goal, state.result.pdr_interval.rel_tr_width)
+            # If we are hitting maximal or minimal rate, we cannot shift,
+            # but we can re-measure.
+            if ndr_lo.loss_fraction > 0.0:
+                if ndr_lo.target_tr > state.minimum_transmit_rate:
+                    new_tr = max(
+                        state.minimum_transmit_rate,
+                        self.expand_down(
+                            ndr_rel_width, self.doublings, ndr_lo.target_tr))
+                    LOGGING.info("ndr lo external %s", new_tr)
+                    state = self._measure_and_update_state(state, new_tr)
+                    continue
+                elif ndr_lo.duration < state.duration:
+                    LOGGING.info("ndr lo minimal re-measure")
+                    state = self._measure_and_update_state(
+                        state, state.minimum_transmit_rate)
+                    continue
+            if pdr_lo.loss_fraction > state.packet_loss_ratio:
+                if pdr_lo.target_tr > state.minimum_transmit_rate:
+                    new_tr = max(
+                        state.minimum_transmit_rate,
+                        self.expand_down(
+                            pdr_rel_width, self.doublings, pdr_lo.target_tr))
+                    LOGGING.info("pdr lo external %s", new_tr)
+                    state = self._measure_and_update_state(state, new_tr)
+                    continue
+                elif pdr_lo.duration < state.duration:
+                    LOGGING.info("pdr lo minimal re-measure")
+                    state = self._measure_and_update_state(
+                        state, state.minimum_transmit_rate)
+                    continue
+            if ndr_hi.loss_fraction <= 0.0:
+                if ndr_hi.target_tr < state.maximum_transmit_rate:
+                    new_tr = min(
+                        state.maximum_transmit_rate,
+                        self.expand_up(
+                            ndr_rel_width, self.doublings, ndr_hi.target_tr))
+                    LOGGING.info("ndr hi external %s", new_tr)
+                    state = self._measure_and_update_state(state, new_tr)
+                    continue
+                elif ndr_hi.duration < state.duration:
+                    LOGGING.info("ndr hi maximal re-measure")
+                    state = self._measure_and_update_state(
+                        state, state.maximum_transmit_rate)
+                    continue
+            if pdr_hi.loss_fraction <= state.packet_loss_ratio:
+                if pdr_hi.target_tr < state.maximum_transmit_rate:
+                    new_tr = min(
+                        state.maximum_transmit_rate,
+                        self.expand_up(
+                            pdr_rel_width, self.doublings, pdr_hi.target_tr))
+                    LOGGING.info("pdr hi external %s", new_tr)
+                    state = self._measure_and_update_state(state, new_tr)
+                    continue
+                elif pdr_hi.duration < state.duration:
+                    LOGGING.info("ndr hi maximal re-measure")
+                    state = self._measure_and_update_state(
+                        state, state.maximum_transmit_rate)
+                    continue
+            # If we are hitting maximum_transmit_rate,
+            # it is still worth narrowing width,
+            # hoping large enough loss fraction will happen.
+            # But if we are hitting the minimal rate (at current duration),
+            # no additional measurement will help with that,
+            # so we can stop narrowing in this phase.
+            if (ndr_lo.target_tr <= state.minimum_transmit_rate
+                    and ndr_lo.loss_fraction > 0.0):
+                ndr_rel_width = 0.0
+            if (pdr_lo.target_tr <= state.minimum_transmit_rate
+                    and pdr_lo.loss_fraction > state.packet_loss_ratio):
+                pdr_rel_width = 0.0
+            if ndr_rel_width > state.width_goal:
+                # We have to narrow NDR width first, as NDR internal search
+                # can invalidate PDR (but not vice versa).
+                new_tr = self.half_step_up(ndr_rel_width, ndr_lo.target_tr)
+                LOGGING.info("Bisecting for NDR at %s", new_tr)
+                state = self._measure_and_update_state(state, new_tr)
+                continue
+            if pdr_rel_width > state.width_goal:
+                # PDR iternal search.
+                new_tr = self.half_step_up(pdr_rel_width, pdr_lo.target_tr)
+                LOGGING.info("Bisecting for PDR at %s", new_tr)
+                state = self._measure_and_update_state(state, new_tr)
+                continue
+            # We do not need to improve width, but there still might be
+            # some measurements with smaller duration.
+            # We need to re-measure with full duration, possibly
+            # creating invalid bounds to resolve (thus broadening width).
+            if ndr_lo.duration < state.duration:
+                LOGGING.info("re-measuring NDR lower bound")
+                state = self._measure_and_update_state(state, ndr_lo.target_tr)
+                continue
+            if pdr_lo.duration < state.duration:
+                LOGGING.info("re-measuring PDR lower bound")
+                state = self._measure_and_update_state(state, pdr_lo.target_tr)
+                continue
+            # Except when lower bounds have high loss fraction, in that case
+            # we do not need to re-measure _upper_ bounds.
+            if ndr_hi.duration < state.duration and ndr_rel_width > 0.0:
+                LOGGING.info("re-measuring NDR upper bound")
+                state = self._measure_and_update_state(state, ndr_hi.target_tr)
+                continue
+            if pdr_hi.duration < state.duration and pdr_rel_width > 0.0:
+                LOGGING.info("re-measuring PDR upper bound")
+                state = self._measure_and_update_state(state, pdr_hi.target_tr)
+                continue
+            # Widths are narrow (or lower bound minimal), bound measurements
+            # are long enough, we can return.
+            LOGGING.info("phase done")
+            break
+        return state
+
+    def measure(self, duration, transmit_rate, latency):
+        duration = float(duration)
+        transmit_rate = float(transmit_rate)
+        # Trex needs target Tr per stream, but reports aggregate Tx and Dx.
+        unit_rate = str(transmit_rate / 2.0) + "pps"
+        stats = self.measurer.send_traffic_on_tg(self.ports, self.port_pg_id,
+                                                 duration, unit_rate,
+                                                 latency=latency)
+        self.measurer.client.reset(ports=self.ports)
+        self.measurer.client.clear_stats(ports=self.ports)
+        self.measurer.client.remove_all_streams(ports=self.ports)
+        for port, profile in self.profiles.items():
+            self.measurer.client.add_streams(profile, ports=[port])
+        self.collect_kpi(stats, unit_rate)
+        transmit_count = int(self.measurer.sent)
+        loss_count = int(self.measurer.loss)
+        measurement = ReceiveRateMeasurement(
+            duration, transmit_rate, transmit_count, loss_count)
+        measurement.latency = self.measurer.latency
+        return measurement
+
+    def perform_additional_measurements_based_on_ndrpdr_result(self, result):
+        duration = 5.0
+        rate = "{}{}".format(result.ndr_interval.measured_low.target_tr / 2.0,
+                             'pps')
+        for _ in range(0, 1):
+            stats = self.measurer.send_traffic_on_tg(self.ports,
+                                                     self.port_pg_id, duration,
+                                                     rate)
+            self.collect_kpi(stats, rate)
+            LOGGING.info('Traffic loss occurred: %s', self.measurer.loss)
+
+    @staticmethod
+    def display_single_bound(result_samples, result_type, rate_total, pkt_size,
+                             latency=None):
+        bandwidth_total = float(rate_total) * (pkt_size + 20) * 8 / (10 ** 9)
+
+        result_samples["Result_{}".format(result_type)] = {
+            "rate_total_pps": float(rate_total),
+            "bandwidth_total_Gbps": float(bandwidth_total),
+        }
+
+        if latency:
+            for item in latency:
+                if latency.index(item) == 0:
+                    name = "Result_{}_{}".format("stream0", result_type)
+                else:
+                    name = "Result_{}_{}".format("stream1", result_type)
+                lat_min, lat_avg, lat_max = item.split('/')
+                result_samples[name] = {
+                    "min_latency": float(lat_min),
+                    "avg_latency": float(lat_avg),
+                    "max_latency": float(lat_max),
+                }
+
+    @staticmethod
+    def check_ndrpdr_interval_validity(result_samples, result_type, interval,
+                                       packet_loss_ratio=0.0):
+        lower_bound = interval.measured_low
+        lower_bound_lf = lower_bound.loss_fraction
+
+        result_samples["Result_{}_packets_lost".format(result_type)] = {
+            "packet_loss_ratio": float(lower_bound_lf),
+            "packets_lost": float(lower_bound.loss_count),
+        }
+
+        if lower_bound_lf <= packet_loss_ratio:
+            return "Minimal rate loss fraction {} reach target {}".format(
+                lower_bound_lf, packet_loss_ratio)
+        else:
+            message = "Minimal rate loss fraction {} does not reach target {}".format(
+                lower_bound_lf, packet_loss_ratio)
+            if lower_bound_lf >= 1.0:
+                return '{}\nZero packets forwarded!'.format(message)
+            else:
+                return '{}\n{} packets lost.'.format(message,
+                                                     lower_bound.loss_count)
diff --git a/yardstick/network_services/helpers/vpp_helpers/ndr_pdr_result.py b/yardstick/network_services/helpers/vpp_helpers/ndr_pdr_result.py
new file mode 100644 (file)
index 0000000..34a97f9
--- /dev/null
@@ -0,0 +1,68 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# 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.
+#
+# This is a modified copy of
+# https://gerrit.fd.io/r/gitweb?p=csit.git;a=blob_plain;f=resources/libraries/python/MLRsearch/NdrPdrResult.py;hb=HEAD
+
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \
+    ReceiveRateInterval
+
+
+class NdrPdrResult(object):
+    """Two measurement intervals, return value of search algorithms.
+
+    Partial fraction is NOT part of the result. Pdr interval should be valid
+    for all partial fractions implied by the interval."""
+
+    def __init__(self, ndr_interval, pdr_interval):
+        """Store the measured intervals after checking argument types.
+
+        :param ndr_interval: Object containing data for NDR part of the result.
+        :param pdr_interval: Object containing data for PDR part of the result.
+        :type ndr_interval: ReceiveRateInterval.ReceiveRateInterval
+        :type pdr_interval: ReceiveRateInterval.ReceiveRateInterval
+        """
+        # TODO: Type checking is not very pythonic,
+        # perhaps users can fix wrong usage without it?
+        if not isinstance(ndr_interval, ReceiveRateInterval):
+            raise TypeError("ndr_interval, is not a ReceiveRateInterval: "
+                            "{ndr!r}".format(ndr=ndr_interval))
+        if not isinstance(pdr_interval, ReceiveRateInterval):
+            raise TypeError("pdr_interval, is not a ReceiveRateInterval: "
+                            "{pdr!r}".format(pdr=pdr_interval))
+        self.ndr_interval = ndr_interval
+        self.pdr_interval = pdr_interval
+
+    def width_in_goals(self, relative_width_goal):
+        """Return a debug string related to current widths in logarithmic scale.
+
+        :param relative_width_goal: Upper bound times this is the goal
+            difference between upper bound and lower bound.
+        :type relative_width_goal: float
+        :returns: Message containing NDR and PDR widths in goals.
+        :rtype: str
+        """
+        return "ndr {ndr_in_goals}; pdr {pdr_in_goals}".format(
+            ndr_in_goals=self.ndr_interval.width_in_goals(relative_width_goal),
+            pdr_in_goals=self.pdr_interval.width_in_goals(relative_width_goal))
+
+    def __str__(self):
+        """Return string as tuple of named values."""
+        return "NDR={ndr!s};PDR={pdr!s}".format(
+            ndr=self.ndr_interval, pdr=self.pdr_interval)
+
+    def __repr__(self):
+        """Return string evaluable as a constructor call."""
+        return "NdrPdrResult(ndr_interval={ndr!r},pdr_interval={pdr!r})".format(
+            ndr=self.ndr_interval, pdr=self.pdr_interval)
diff --git a/yardstick/network_services/helpers/vpp_helpers/receive_rate_interval.py b/yardstick/network_services/helpers/vpp_helpers/receive_rate_interval.py
new file mode 100644 (file)
index 0000000..517a99c
--- /dev/null
@@ -0,0 +1,88 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# 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.
+#
+# This is a modified copy of
+# https://gerrit.fd.io/r/gitweb?p=csit.git;a=blob_plain;f=resources/libraries/python/MLRsearch/ReceiveRateInterval.py;hb=HEAD
+
+import math
+
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \
+    ReceiveRateMeasurement
+
+
+class ReceiveRateInterval(object):
+    """Structure defining two Rr measurements, and their relation."""
+
+    def __init__(self, measured_low, measured_high):
+        """Store the bound measurements after checking argument types.
+
+        :param measured_low: Measurement for the lower bound.
+        :param measured_high: Measurement for the upper bound.
+        :type measured_low: ReceiveRateMeasurement.ReceiveRateMeasurement
+        :type measured_high: ReceiveRateMeasurement.ReceiveRateMeasurement
+        """
+        # TODO: Type checking is not very pythonic,
+        # perhaps users can fix wrong usage without it?
+        if not isinstance(measured_low, ReceiveRateMeasurement):
+            raise TypeError("measured_low is not a ReceiveRateMeasurement: "
+                            "{low!r}".format(low=measured_low))
+        if not isinstance(measured_high, ReceiveRateMeasurement):
+            raise TypeError("measured_high is not a ReceiveRateMeasurement: "
+                            "{high!r}".format(high=measured_high))
+        self.measured_low = measured_low
+        self.measured_high = measured_high
+        # Declare secondary quantities to appease pylint.
+        self.abs_tr_width = None
+        """Absolute width of target transmit rate. Upper minus lower."""
+        self.rel_tr_width = None
+        """Relative width of target transmit rate. Absolute divided by upper."""
+        self.sort()
+
+    def sort(self):
+        """Sort bounds by target Tr, compute secondary quantities."""
+        if self.measured_low.target_tr > self.measured_high.target_tr:
+            self.measured_low, self.measured_high = (
+                self.measured_high, self.measured_low)
+        self.abs_tr_width = (
+                self.measured_high.target_tr - self.measured_low.target_tr)
+        self.rel_tr_width = round(
+            self.abs_tr_width / self.measured_high.target_tr, 5)
+
+    def width_in_goals(self, relative_width_goal):
+        """Return float value.
+
+        Relative width goal is some (negative) value on logarithmic scale.
+        Current relative width is another logarithmic value.
+        Return the latter divided by the former.
+        This is useful when investigating how did surprising widths come to be.
+
+        :param relative_width_goal: Upper bound times this is the goal
+            difference between upper bound and lower bound.
+        :type relative_width_goal: float
+        :returns: Current width as logarithmic multiple of goal width [1].
+        :rtype: float
+        """
+        return round(math.log(1.0 - self.rel_tr_width) / math.log(
+            1.0 - relative_width_goal), 5)
+
+    def __str__(self):
+        """Return string as half-open interval."""
+        return "[{low!s};{high!s})".format(
+            low=self.measured_low, high=self.measured_high)
+
+    def __repr__(self):
+        """Return string evaluable as a constructor call."""
+        return ("ReceiveRateInterval(measured_low={low!r}"
+                ",measured_high={high!r})".format(low=self.measured_low,
+                                                  high=self.measured_high))
diff --git a/yardstick/network_services/helpers/vpp_helpers/receive_rate_measurement.py b/yardstick/network_services/helpers/vpp_helpers/receive_rate_measurement.py
new file mode 100644 (file)
index 0000000..2c59ea1
--- /dev/null
@@ -0,0 +1,58 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# 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.
+#
+# This is a modified copy of
+# https://gerrit.fd.io/r/gitweb?p=csit.git;a=blob_plain;f=resources/libraries/python/MLRsearch/ReceiveRateMeasurement.py;hb=HEAD
+
+
+class ReceiveRateMeasurement(object):
+    """Structure defining the result of single Rr measurement."""
+
+    def __init__(self, duration, target_tr, transmit_count, loss_count):
+        """Constructor, normalize primary and compute secondary quantities.
+
+        :param duration: Measurement duration [s].
+        :param target_tr: Target transmit rate [pps].
+            If bidirectional traffic is measured, this is bidirectional rate.
+        :param transmit_count: Number of packets transmitted [1].
+        :param loss_count: Number of packets transmitted but not received [1].
+        :type duration: float
+        :type target_tr: float
+        :type transmit_count: int
+        :type loss_count: int
+        """
+        self.duration = float(duration)
+        self.target_tr = float(target_tr)
+        self.transmit_count = int(transmit_count)
+        self.loss_count = int(loss_count)
+        self.receive_count = round(transmit_count - loss_count, 5)
+        self.transmit_rate = round(transmit_count / self.duration, 5)
+        self.loss_rate = round(loss_count / self.duration, 5)
+        self.receive_rate = round(self.receive_count / self.duration, 5)
+        self.loss_fraction = round(
+            float(self.loss_count) / self.transmit_count, 5)
+        # TODO: Do we want to store also the real time (duration + overhead)?
+
+    def __str__(self):
+        """Return string reporting input and loss fraction."""
+        return "d={dur!s},Tr={rate!s},Df={frac!s}".format(
+            dur=self.duration, rate=self.target_tr, frac=self.loss_fraction)
+
+    def __repr__(self):
+        """Return string evaluable as a constructor call."""
+        return ("ReceiveRateMeasurement(duration={dur!r},target_tr={rate!r}"
+                ",transmit_count={trans!r},loss_count={loss!r})".format(
+            dur=self.duration, rate=self.target_tr,
+            trans=self.transmit_count,
+            loss=self.loss_count))
index 0f8185f..412e4e6 100644 (file)
 # limitations under the License.
 
 import datetime
+import ipaddress
 import logging
-from random import choice
-from string import ascii_letters
+import random
+import string
 
-from ipaddress import ip_address
 from trex_stl_lib import api as Pkt
 from trex_stl_lib import trex_stl_client
 from trex_stl_lib import trex_stl_packet_builder_scapy
 from trex_stl_lib import trex_stl_streams
 
 from yardstick.common import constants
+from yardstick.network_services.helpers.vpp_helpers.multiple_loss_ratio_search import \
+    MultipleLossRatioSearch
 from yardstick.network_services.traffic_profile.rfc2544 import RFC2544Profile, \
     PortPgIDMap
 from yardstick.network_services.traffic_profile.trex_traffic_profile import IP, \
@@ -37,6 +39,10 @@ class VppRFC2544Profile(RFC2544Profile):
     def __init__(self, traffic_generator):
         super(VppRFC2544Profile, self).__init__(traffic_generator)
 
+        tp_cfg = traffic_generator["traffic_profile"]
+        self.number_of_intermediate_phases = tp_cfg.get("intermediate_phases",
+                                                        2)
+
         self.duration = self.config.duration
         self.precision = self.config.test_precision
         self.lower_bound = self.config.lower_bound
@@ -85,7 +91,7 @@ class VppRFC2544Profile(RFC2544Profile):
     def _gen_payload(length):
         payload = ""
         for _ in range(length):
-            payload += choice(ascii_letters)
+            payload += random.choice(string.ascii_letters)
 
         return payload
 
@@ -153,8 +159,9 @@ class VppRFC2544Profile(RFC2544Profile):
                                 dst=dst_start_ip,
                                 proto=outer_l3v4['proto'])
         if self.flow > 1:
-            dst_start_int = int(ip_address(str(dst_start_ip)))
-            dst_end_ip_new = ip_address(dst_start_int + self.flow - 1)
+            dst_start_int = int(ipaddress.ip_address(str(dst_start_ip)))
+            dst_end_ip_new = ipaddress.ip_address(
+                dst_start_int + self.flow - 1)
             # self._set_proto_addr(IP, SRC, outer_l3v4['srcip4'], outer_l3v4['count'])
             self._set_proto_addr(IP, DST,
                                  "{start_ip}-{end_ip}".format(
@@ -248,8 +255,19 @@ class VppRFC2544Profile(RFC2544Profile):
 
     def binary_search_with_optimized(self, traffic_generator, duration,
                                      timeout, test_data):
-        # TODO Support FD.io Multiple Loss Ratio search (MLRsearch)
-        pass
+        self.queue.cancel_join_thread()
+        algorithm = MultipleLossRatioSearch(
+            measurer=traffic_generator, latency=self.enable_latency,
+            pkt_size=self.pkt_size,
+            final_trial_duration=duration,
+            final_relative_width=self.step_interval / 100,
+            number_of_intermediate_phases=self.number_of_intermediate_phases,
+            initial_trial_duration=1,
+            timeout=timeout)
+        algorithm.init_generator(self.ports, self.port_pg_id, self.profiles,
+                                 test_data, self.queue)
+        return algorithm.narrow_down_ndr_and_pdr(10000, self.max_rate,
+                                                 self.tolerance_high)
 
     def binary_search(self, traffic_generator, duration, tolerance_value,
                       test_data):
diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/__init__.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_multiple_loss_ratio_search.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_multiple_loss_ratio_search.py
new file mode 100644 (file)
index 0000000..d314554
--- /dev/null
@@ -0,0 +1,2164 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# 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 unittest
+
+import mock
+
+from yardstick.network_services.helpers.vpp_helpers.multiple_loss_ratio_search import \
+    MultipleLossRatioSearch
+from yardstick.network_services.helpers.vpp_helpers.ndr_pdr_result import \
+    NdrPdrResult
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \
+    ReceiveRateInterval
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \
+    ReceiveRateMeasurement
+from yardstick.network_services.traffic_profile.rfc2544 import PortPgIDMap
+
+
+class TestMultipleLossRatioSearch(unittest.TestCase):
+
+    def test___init__(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        self.assertEqual(True, algorithm.latency)
+        self.assertEqual(64, algorithm.pkt_size)
+        self.assertEqual(30, algorithm.final_trial_duration)
+        self.assertEqual(0.005, algorithm.final_relative_width)
+        self.assertEqual(2, algorithm.number_of_intermediate_phases)
+        self.assertEqual(1, algorithm.initial_trial_duration)
+        self.assertEqual(720, algorithm.timeout)
+        self.assertEqual(1, algorithm.doublings)
+
+    def test_double_relative_width(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        self.assertEqual(0.00997, algorithm.double_relative_width(0.005))
+
+    def test_double_step_down(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        self.assertEqual(99003.0, algorithm.double_step_down(0.005, 100000))
+
+    def test_expand_down(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        self.assertEqual(99003.0, algorithm.expand_down(0.005, 1, 100000))
+
+    def test_double_step_up(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        self.assertEqual(101007.0401907013,
+                         algorithm.double_step_up(0.005, 100000))
+
+    def test_expand_up(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        self.assertEqual(101007.0401907013,
+                         algorithm.expand_up(0.005, 1, 100000))
+
+    def test_half_relative_width(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        self.assertEqual(0.0025031328369998773,
+                         algorithm.half_relative_width(0.005))
+
+    def test_half_step_up(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        self.assertEqual(100250.94142341711,
+                         algorithm.half_step_up(0.005, 100000))
+
+    def test_init_generator(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(),
+                                     mock.Mock(), mock.Mock()))
+        self.assertEqual(ports, algorithm.ports)
+        self.assertEqual(port_pg_id, algorithm.port_pg_id)
+
+    def test_collect_kpi(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        algorithm.init_generator(ports, port_pg_id, mock.Mock, mock.Mock,
+                                 mock.Mock())
+        self.assertIsNone(algorithm.collect_kpi({}, 100000))
+
+    def test_narrow_down_ndr_and_pdr(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, 'ndrpdr') as \
+                        mock_ndrpdr:
+            ndr_measured_low = ReceiveRateMeasurement(10, 13880000, 13879927,
+                                                      0)
+            ndr_measured_high = ReceiveRateMeasurement(10, 14880000, 14879927,
+                                                       0)
+            ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_low = ReceiveRateMeasurement(10, 11880000, 11879927,
+                                                      0)
+            pdr_measured_high = ReceiveRateMeasurement(10, 12880000, 12879927,
+                                                       0)
+            pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_interval = ReceiveRateInterval(ndr_measured_low,
+                                               ndr_measured_high)
+            pdr_interval = ReceiveRateInterval(pdr_measured_low,
+                                               pdr_measured_high)
+            starting_result = NdrPdrResult(ndr_interval, pdr_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock_ndrpdr.return_value = MultipleLossRatioSearch.ProgressState(
+                starting_result, 2, 30, 0.005, 0.0,
+                4857361, 4977343)
+            self.assertEqual(
+                {'Result_NDR_LOWER': {'bandwidth_total_Gbps': 0.9327310944,
+                                      'rate_total_pps': 1387992.7},
+                 'Result_NDR_UPPER': {
+                     'bandwidth_total_Gbps': 0.9999310943999999,
+                     'rate_total_pps': 1487992.7},
+                 'Result_NDR_packets_lost': {'packet_loss_ratio': 0.0,
+                                             'packets_lost': 0.0},
+                 'Result_PDR_LOWER': {
+                     'bandwidth_total_Gbps': 0.7983310943999999,
+                     'rate_total_pps': 1187992.7},
+                 'Result_PDR_UPPER': {'bandwidth_total_Gbps': 0.8655310944,
+                                      'rate_total_pps': 1287992.7},
+                 'Result_PDR_packets_lost': {'packet_loss_ratio': 0.0,
+                                             'packets_lost': 0.0},
+                 'Result_stream0_NDR_LOWER': {'avg_latency': 3081.0,
+                                              'max_latency': 3962.0,
+                                              'min_latency': 1000.0},
+                 'Result_stream0_PDR_LOWER': {'avg_latency': 3081.0,
+                                              'max_latency': 3962.0,
+                                              'min_latency': 1000.0},
+                 'Result_stream1_NDR_LOWER': {'avg_latency': 3149.0,
+                                              'max_latency': 3730.0,
+                                              'min_latency': 500.0},
+                 'Result_stream1_PDR_LOWER': {'avg_latency': 3149.0,
+                                              'max_latency': 3730.0,
+                                              'min_latency': 500.0}},
+                algorithm.narrow_down_ndr_and_pdr(12880000, 15880000, 0.0))
+
+    def test__measure_and_update_state(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        starting_interval = ReceiveRateInterval(measured_low, measured_high)
+        starting_result = NdrPdrResult(starting_interval, starting_interval)
+        previous_state = MultipleLossRatioSearch.ProgressState(starting_result,
+                                                               2, 30, 0.005,
+                                                               0.0, 4857361,
+                                                               4977343)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure:
+            mock_measure.return_value = ReceiveRateMeasurement(1,
+                                                               4626121.09635,
+                                                               4626100, 13074)
+            state = algorithm._measure_and_update_state(previous_state,
+                                                        4626121.09635)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(1, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(4626121.09635,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(4626100,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(13074,
+                         state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(4613026,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(4626100,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(13074.0,
+                         state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(4613026.0,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.00283,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(1, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(4857361,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(4857339,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(84965,
+                         state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(4772374,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(4857339,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(84965.0,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(4772374.0,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(0.01749,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(1, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(4626121.09635,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(4626100,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(13074,
+                         state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(4613026,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(4626100,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(13074.0,
+                         state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(4613026.0,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.00283,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(1, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(4857361,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(4857339,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(84965,
+                         state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(4772374,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(4857339,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(84965.0,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(4772374.0,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(0.01749,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(2, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.005, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(4857361, state.minimum_transmit_rate)
+        self.assertEqual(4977343, state.maximum_transmit_rate)
+
+    def test_new_interval(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        measured = ReceiveRateMeasurement(1, 3972540.4108, 21758482, 0)
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        result = algorithm._new_interval(receive_rate_interval, measured, 0.0)
+        self.assertIsInstance(result, ReceiveRateInterval)
+        self.assertEqual(1, result.measured_low.duration)
+        self.assertEqual(3972540.4108, result.measured_low.target_tr)
+        self.assertEqual(21758482, result.measured_low.transmit_count)
+        self.assertEqual(0, result.measured_low.loss_count)
+        self.assertEqual(21758482, result.measured_low.receive_count)
+        self.assertEqual(21758482, result.measured_low.transmit_rate)
+        self.assertEqual(0.0, result.measured_low.loss_rate)
+        self.assertEqual(21758482.0, result.measured_low.receive_rate)
+        self.assertEqual(0.0, result.measured_low.loss_fraction)
+        self.assertEqual(1, result.measured_high.duration)
+        self.assertEqual(4857361, result.measured_high.target_tr)
+        self.assertEqual(4857339, result.measured_high.transmit_count)
+        self.assertEqual(84965, result.measured_high.loss_count)
+        self.assertEqual(4772374, result.measured_high.receive_count)
+        self.assertEqual(4857339, result.measured_high.transmit_rate)
+        self.assertEqual(84965.0, result.measured_high.loss_rate)
+        self.assertEqual(4772374.0, result.measured_high.receive_rate)
+        self.assertEqual(0.01749, result.measured_high.loss_fraction)
+
+    def test_new_interval_zero(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        measured = ReceiveRateMeasurement(1, 4977343, 21758482, 0)
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        result = algorithm._new_interval(receive_rate_interval, measured, 0.0)
+        self.assertIsInstance(result, ReceiveRateInterval)
+        self.assertEqual(1, result.measured_low.duration)
+        self.assertEqual(4857361.0, result.measured_low.target_tr)
+        self.assertEqual(4857339, result.measured_low.transmit_count)
+        self.assertEqual(84965, result.measured_low.loss_count)
+        self.assertEqual(4772374, result.measured_low.receive_count)
+        self.assertEqual(4857339.0, result.measured_low.transmit_rate)
+        self.assertEqual(84965.0, result.measured_low.loss_rate)
+        self.assertEqual(4772374.0, result.measured_low.receive_rate)
+        self.assertEqual(0.01749, result.measured_low.loss_fraction)
+        self.assertEqual(1, result.measured_high.duration)
+        self.assertEqual(4977343.0, result.measured_high.target_tr)
+        self.assertEqual(21758482, result.measured_high.transmit_count)
+        self.assertEqual(0, result.measured_high.loss_count)
+        self.assertEqual(21758482, result.measured_high.receive_count)
+        self.assertEqual(21758482.0, result.measured_high.transmit_rate)
+        self.assertEqual(0.0, result.measured_high.loss_rate)
+        self.assertEqual(21758482.0, result.measured_high.receive_rate)
+        self.assertEqual(0.0, result.measured_high.loss_fraction)
+
+    def test_new_interval_one(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        measured = ReceiveRateMeasurement(1, 5000000, 2175848, 0)
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        result = algorithm._new_interval(receive_rate_interval, measured, 0.0)
+        self.assertIsInstance(result, ReceiveRateInterval)
+        self.assertEqual(1, result.measured_low.duration)
+        self.assertEqual(4857361.0, result.measured_low.target_tr)
+        self.assertEqual(4857339, result.measured_low.transmit_count)
+        self.assertEqual(84965, result.measured_low.loss_count)
+        self.assertEqual(4772374, result.measured_low.receive_count)
+        self.assertEqual(4857339.0, result.measured_low.transmit_rate)
+        self.assertEqual(84965.0, result.measured_low.loss_rate)
+        self.assertEqual(4772374.0, result.measured_low.receive_rate)
+        self.assertEqual(0.01749, result.measured_low.loss_fraction)
+        self.assertEqual(1, result.measured_high.duration)
+        self.assertEqual(4977343.0, result.measured_high.target_tr)
+        self.assertEqual(4977320, result.measured_high.transmit_count)
+        self.assertEqual(119959, result.measured_high.loss_count)
+        self.assertEqual(4857361, result.measured_high.receive_count)
+        self.assertEqual(4977320.0, result.measured_high.transmit_rate)
+        self.assertEqual(119959.0, result.measured_high.loss_rate)
+        self.assertEqual(4857361.0, result.measured_high.receive_rate)
+        self.assertEqual(0.0241, result.measured_high.loss_fraction)
+
+    def test_new_interval_valid_1st(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        measured = ReceiveRateMeasurement(1, 4000000, 2175848, 0)
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        result = algorithm._new_interval(receive_rate_interval, measured, 0.5)
+        self.assertIsInstance(result, ReceiveRateInterval)
+        self.assertEqual(1, result.measured_low.duration)
+        self.assertEqual(4857361.0, result.measured_low.target_tr)
+        self.assertEqual(4857339, result.measured_low.transmit_count)
+        self.assertEqual(84965, result.measured_low.loss_count)
+        self.assertEqual(4772374, result.measured_low.receive_count)
+        self.assertEqual(4857339.0, result.measured_low.transmit_rate)
+        self.assertEqual(84965.0, result.measured_low.loss_rate)
+        self.assertEqual(4772374.0, result.measured_low.receive_rate)
+        self.assertEqual(0.01749, result.measured_low.loss_fraction)
+        self.assertEqual(1, result.measured_high.duration)
+        self.assertEqual(4977343.0, result.measured_high.target_tr)
+        self.assertEqual(4977320, result.measured_high.transmit_count)
+        self.assertEqual(119959, result.measured_high.loss_count)
+        self.assertEqual(4857361, result.measured_high.receive_count)
+        self.assertEqual(4977320.0, result.measured_high.transmit_rate)
+        self.assertEqual(119959.0, result.measured_high.loss_rate)
+        self.assertEqual(4857361.0, result.measured_high.receive_rate)
+        self.assertEqual(0.0241, result.measured_high.loss_fraction)
+
+    def test_new_interval_valid_1st_loss(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        measured = ReceiveRateMeasurement(1, 4000000, 2175848, 1000000)
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        result = algorithm._new_interval(receive_rate_interval, measured, 0.02)
+        self.assertIsInstance(result, ReceiveRateInterval)
+        self.assertEqual(1, result.measured_low.duration)
+        self.assertEqual(4000000.0, result.measured_low.target_tr)
+        self.assertEqual(2175848, result.measured_low.transmit_count)
+        self.assertEqual(1000000, result.measured_low.loss_count)
+        self.assertEqual(1175848, result.measured_low.receive_count)
+        self.assertEqual(2175848.0, result.measured_low.transmit_rate)
+        self.assertEqual(1000000.0, result.measured_low.loss_rate)
+        self.assertEqual(1175848.0, result.measured_low.receive_rate)
+        self.assertEqual(0.45959, result.measured_low.loss_fraction)
+        self.assertEqual(1, result.measured_high.duration)
+        self.assertEqual(4977343.0, result.measured_high.target_tr)
+        self.assertEqual(4977320, result.measured_high.transmit_count)
+        self.assertEqual(119959, result.measured_high.loss_count)
+        self.assertEqual(4857361, result.measured_high.receive_count)
+        self.assertEqual(4977320.0, result.measured_high.transmit_rate)
+        self.assertEqual(119959.0, result.measured_high.loss_rate)
+        self.assertEqual(4857361.0, result.measured_high.receive_rate)
+        self.assertEqual(0.0241, result.measured_high.loss_fraction)
+
+    def test_new_interval_valid_2nd(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        measured = ReceiveRateMeasurement(1, 5000000, 2175848, 0)
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        result = algorithm._new_interval(receive_rate_interval, measured, 0.5)
+        self.assertIsInstance(result, ReceiveRateInterval)
+        self.assertEqual(1, result.measured_low.duration)
+        self.assertEqual(4977343.0, result.measured_low.target_tr)
+        self.assertEqual(4977320, result.measured_low.transmit_count)
+        self.assertEqual(119959, result.measured_low.loss_count)
+        self.assertEqual(4857361, result.measured_low.receive_count)
+        self.assertEqual(4977320.0, result.measured_low.transmit_rate)
+        self.assertEqual(119959.0, result.measured_low.loss_rate)
+        self.assertEqual(4857361.0, result.measured_low.receive_rate)
+        self.assertEqual(0.0241, result.measured_low.loss_fraction)
+        self.assertEqual(1, result.measured_high.duration)
+        self.assertEqual(5000000.0, result.measured_high.target_tr)
+        self.assertEqual(2175848, result.measured_high.transmit_count)
+        self.assertEqual(0, result.measured_high.loss_count)
+        self.assertEqual(2175848, result.measured_high.receive_count)
+        self.assertEqual(2175848.0, result.measured_high.transmit_rate)
+        self.assertEqual(0.0, result.measured_high.loss_rate)
+        self.assertEqual(2175848.0, result.measured_high.receive_rate)
+        self.assertEqual(0.0, result.measured_high.loss_fraction)
+
+    def test_new_interval_valid_3rd(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        measured = ReceiveRateMeasurement(1, 4867361, 2175848, 0)
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        result = algorithm._new_interval(receive_rate_interval, measured, 0.5)
+        self.assertIsInstance(result, ReceiveRateInterval)
+        self.assertEqual(1, result.measured_low.duration)
+        self.assertEqual(4867361.0, result.measured_low.target_tr)
+        self.assertEqual(2175848, result.measured_low.transmit_count)
+        self.assertEqual(0, result.measured_low.loss_count)
+        self.assertEqual(2175848, result.measured_low.receive_count)
+        self.assertEqual(2175848.0, result.measured_low.transmit_rate)
+        self.assertEqual(0.0, result.measured_low.loss_rate)
+        self.assertEqual(2175848.0, result.measured_low.receive_rate)
+        self.assertEqual(0.0, result.measured_low.loss_fraction)
+        self.assertEqual(1, result.measured_high.duration)
+        self.assertEqual(4977343.0, result.measured_high.target_tr)
+        self.assertEqual(4977320, result.measured_high.transmit_count)
+        self.assertEqual(119959, result.measured_high.loss_count)
+        self.assertEqual(4857361, result.measured_high.receive_count)
+        self.assertEqual(4977320.0, result.measured_high.transmit_rate)
+        self.assertEqual(119959.0, result.measured_high.loss_rate)
+        self.assertEqual(4857361.0, result.measured_high.receive_rate)
+        self.assertEqual(0.0241, result.measured_high.loss_fraction)
+
+    def test_new_interval_valid_3rd_loss(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        measured = ReceiveRateMeasurement(1, 4867361, 2175848, 1000000)
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        result = algorithm._new_interval(receive_rate_interval, measured, 0.2)
+        self.assertIsInstance(result, ReceiveRateInterval)
+        self.assertEqual(1, result.measured_low.duration)
+        self.assertEqual(4857361.0, result.measured_low.target_tr)
+        self.assertEqual(4857339, result.measured_low.transmit_count)
+        self.assertEqual(84965, result.measured_low.loss_count)
+        self.assertEqual(4772374, result.measured_low.receive_count)
+        self.assertEqual(4857339.0, result.measured_low.transmit_rate)
+        self.assertEqual(84965.0, result.measured_low.loss_rate)
+        self.assertEqual(4772374.0, result.measured_low.receive_rate)
+        self.assertEqual(0.01749, result.measured_low.loss_fraction)
+        self.assertEqual(1, result.measured_high.duration)
+        self.assertEqual(4867361.0, result.measured_high.target_tr)
+        self.assertEqual(2175848, result.measured_high.transmit_count)
+        self.assertEqual(1000000, result.measured_high.loss_count)
+        self.assertEqual(1175848, result.measured_high.receive_count)
+        self.assertEqual(2175848.0, result.measured_high.transmit_rate)
+        self.assertEqual(1000000.0, result.measured_high.loss_rate)
+        self.assertEqual(1175848.0, result.measured_high.receive_rate)
+        self.assertEqual(0.45959, result.measured_high.loss_fraction)
+
+    def test_ndrpdr(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure:
+            measured_low = ReceiveRateMeasurement(30, 14880000, 14879927, 0)
+            measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 0)
+            measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            starting_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+            starting_result = NdrPdrResult(starting_interval,
+                                           starting_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 30, 0.005, 0.0, 14880000,
+                14880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(30, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(14880000,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.005, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_ndrpdr_ndr_rel_width(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, '_measure_and_update_state') as \
+                        mock__measure_and_update_state:
+            measured_low = ReceiveRateMeasurement(30, 880000, 879927, 0)
+            measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 0)
+            measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            starting_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+            ending_interval = ReceiveRateInterval(measured_high, measured_high)
+            starting_result = NdrPdrResult(starting_interval,
+                                           starting_interval)
+            ending_result = NdrPdrResult(ending_interval, ending_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock__measure_and_update_state.return_value = \
+                MultipleLossRatioSearch.ProgressState(ending_result, -1, 30,
+                                                      0.005, 0.0, 14880000,
+                                                      14880000)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 30, 0.005, 0.0, 14880000,
+                14880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(30, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(14880000,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.005, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_ndrpdr_pdr_rel_width(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, '_measure_and_update_state') as \
+                        mock__measure_and_update_state:
+            ndr_measured_low = ReceiveRateMeasurement(30, 14880000, 14879927,
+                                                      0)
+            ndr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927,
+                                                       0)
+            ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_low = ReceiveRateMeasurement(30, 880000, 879927, 0)
+            pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927,
+                                                       0)
+            pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_interval = ReceiveRateInterval(ndr_measured_low,
+                                               ndr_measured_high)
+            pdr_interval = ReceiveRateInterval(pdr_measured_low,
+                                               pdr_measured_high)
+            starting_result = NdrPdrResult(ndr_interval, pdr_interval)
+            ending_result = NdrPdrResult(ndr_interval, ndr_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock__measure_and_update_state.return_value = \
+                MultipleLossRatioSearch.ProgressState(ending_result, -1, 30,
+                                                      0.005, 0.0, 14880000,
+                                                      14880000)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 30, 0.005, 0.0, 14880000,
+                14880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(30, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(14880000,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.005, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_ndrpdr_ndr_lo_duration(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, '_measure_and_update_state') as \
+                        mock__measure_and_update_state:
+            measured_low = ReceiveRateMeasurement(30, 14880000, 14879927, 0)
+            measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 100)
+            measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            starting_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+            starting_result = NdrPdrResult(starting_interval,
+                                           starting_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock__measure_and_update_state.return_value = \
+                MultipleLossRatioSearch.ProgressState(starting_result, -1, 30,
+                                                      0.005, 0.0, 14880000,
+                                                      14880000)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 50, 0.005, 0.0, 14880000,
+                14880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(30, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(14880000,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(100,
+                         state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(14879827,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(3.33333,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(495994.23333,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(1e-05,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(100,
+                         state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(14879827,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(3.33333,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(495994.23333,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(1e-05,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.005, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_ndrpdr_ndr_hi_duration(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, '_measure_and_update_state') as \
+                        mock__measure_and_update_state:
+            measured_low = ReceiveRateMeasurement(60, 14880000, 14879927, 0)
+            measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 100)
+            measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            starting_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+            starting_result = NdrPdrResult(starting_interval,
+                                           starting_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock__measure_and_update_state.return_value = \
+                MultipleLossRatioSearch.ProgressState(starting_result, -1, 30,
+                                                      0.005, 0.0, 14880000,
+                                                      14880000)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 50, 0.005, 0.0, 14880000,
+                14880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(60.0, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(14880000,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(247998.78333,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(247998.78333,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(100,
+                         state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(14879827,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(3.33333,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(495994.23333,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(1e-05,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(60.0, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(247998.78333,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(247998.78333,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(100,
+                         state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(14879827,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(3.33333,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(495994.23333,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(1e-05,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.005, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_ndrpdr_error(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=0)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure:
+            measured_low = ReceiveRateMeasurement(30, 14880000, 14879927, 0)
+            measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 0)
+            measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            starting_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+            starting_result = NdrPdrResult(starting_interval,
+                                           starting_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 30, 0.005, 0.0, 14880000,
+                14880000)
+        with self.assertRaises(RuntimeError) as raised:
+            algorithm.ndrpdr(previous_state)
+
+        self.assertIn('Optimized search takes too long.',
+                      str(raised.exception))
+
+    def test_ndrpdr_update_state_ndr_hi(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, '_measure_and_update_state') as \
+                        mock__measure_and_update_state:
+            ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927,
+                                                      0)
+            ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                       0)
+            ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                      0)
+            pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927,
+                                                       0)
+            pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_interval = ReceiveRateInterval(ndr_measured_low,
+                                               ndr_measured_high)
+            pdr_interval = ReceiveRateInterval(pdr_measured_low,
+                                               pdr_measured_high)
+            starting_result = NdrPdrResult(ndr_interval, pdr_interval)
+            ending_result = NdrPdrResult(pdr_interval, pdr_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock__measure_and_update_state.return_value = \
+                MultipleLossRatioSearch.ProgressState(ending_result, -1, 30,
+                                                      0.2, 0.0, 14880000,
+                                                      14880000)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 30, 0.005, 0.0, 14880000,
+                14880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(30, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(12880000.0,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(12879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(12879927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(429330.9,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(14880000.0,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(12880000.0,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(12879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(12879927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(429330.9,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.2, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_ndrpdr_update_state_ndr_hi_duration(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, '_measure_and_update_state') as \
+                        mock__measure_and_update_state:
+            ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927,
+                                                      0)
+            ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                       0)
+            ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                      0)
+            pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927,
+                                                       0)
+            pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_interval = ReceiveRateInterval(ndr_measured_low,
+                                               ndr_measured_high)
+            pdr_interval = ReceiveRateInterval(pdr_measured_low,
+                                               pdr_measured_high)
+            starting_result = NdrPdrResult(ndr_interval, pdr_interval)
+            ending_result = NdrPdrResult(pdr_interval, pdr_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock__measure_and_update_state.return_value = \
+                MultipleLossRatioSearch.ProgressState(ending_result, -1, 30,
+                                                      0.2, 0.0, 14880000,
+                                                      14880000)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 50, 0.005, 0.0, 4880000,
+                10880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(30, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(12880000.0,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(12879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(12879927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(429330.9,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(14880000.0,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(12880000.0,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(12879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(12879927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(429330.9,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.2, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_ndrpdr_update_state_ndr_lo(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, '_measure_and_update_state') as \
+                        mock__measure_and_update_state:
+            ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927,
+                                                      100000)
+            ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                       100000)
+            ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                      100000)
+            pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927,
+                                                       100000)
+            pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_interval = ReceiveRateInterval(ndr_measured_low,
+                                               ndr_measured_high)
+            pdr_interval = ReceiveRateInterval(pdr_measured_low,
+                                               pdr_measured_high)
+            starting_result = NdrPdrResult(ndr_interval, pdr_interval)
+            ending_result = NdrPdrResult(pdr_interval, pdr_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock__measure_and_update_state.return_value = \
+                MultipleLossRatioSearch.ProgressState(ending_result, -1, 30,
+                                                      0.2, 0.0, 14880000,
+                                                      14880000)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 30, 0.005, 0.0, 100000,
+                14880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(30, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(12880000.0,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(12879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(100000,
+                         state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(12779927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(425997.56667,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.00776,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(14880000.0,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(100000,
+                         state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(14779927,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(492664.23333,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(0.00672,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(12880000.0,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(12879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(100000,
+                         state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(12779927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(425997.56667,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.00776,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(100000,
+                         state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(14779927,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(492664.23333,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(0.00672,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.2, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_ndrpdr_update_state_pdr_lo(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, '_measure_and_update_state') as \
+                        mock__measure_and_update_state:
+            ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927,
+                                                      0)
+            ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                       0)
+            ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                      100000)
+            pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927,
+                                                       100000)
+            pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_interval = ReceiveRateInterval(ndr_measured_low,
+                                               ndr_measured_high)
+            pdr_interval = ReceiveRateInterval(pdr_measured_low,
+                                               pdr_measured_high)
+            starting_result = NdrPdrResult(ndr_interval, pdr_interval)
+            ending_result = NdrPdrResult(pdr_interval, pdr_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock__measure_and_update_state.return_value = \
+                MultipleLossRatioSearch.ProgressState(ending_result, -1, 30,
+                                                      0.2, 0.0, 14880000,
+                                                      14880000)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 30, 0.005, 0.0, 100000,
+                14880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(30, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(12880000.0,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(12879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(100000,
+                         state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(12779927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(425997.56667,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.00776,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(14880000.0,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(100000,
+                         state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(14779927,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(492664.23333,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(0.00672,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(12880000.0,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(12879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(100000,
+                         state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(12779927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(425997.56667,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.00776,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(100000,
+                         state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(14779927,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(492664.23333,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(0.00672,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.2, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_ndrpdr_update_state_pdr_lo_duration(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, '_measure_and_update_state') as \
+                        mock__measure_and_update_state:
+            ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927,
+                                                      0)
+            ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                       0)
+            ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                      100000)
+            pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927,
+                                                       100000)
+            pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_interval = ReceiveRateInterval(ndr_measured_low,
+                                               ndr_measured_high)
+            pdr_interval = ReceiveRateInterval(pdr_measured_low,
+                                               pdr_measured_high)
+            starting_result = NdrPdrResult(ndr_interval, pdr_interval)
+            ending_result = NdrPdrResult(pdr_interval, pdr_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock__measure_and_update_state.return_value = \
+                MultipleLossRatioSearch.ProgressState(ending_result, -1, 30,
+                                                      0.2, 0.0, 14880000,
+                                                      14880000)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 50, 0.005, 0.0, 14880000,
+                14880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(30, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(12880000.0,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(12879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(100000,
+                         state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(12779927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(425997.56667,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.00776,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(14880000.0,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(100000,
+                         state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(14779927,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(492664.23333,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(0.00672,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(12880000.0,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(12879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(100000,
+                         state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(12779927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(425997.56667,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.00776,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(14880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(14879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(100000,
+                         state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(14779927,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(495997.56667,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(492664.23333,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(0.00672,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.2, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_ndrpdr_update_state_pdr_hi(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, '_measure_and_update_state') as \
+                        mock__measure_and_update_state:
+            ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927,
+                                                      0)
+            ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                       100000)
+            ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                      0)
+            pdr_measured_high = ReceiveRateMeasurement(30, 13880000, 14879927,
+                                                       0)
+            pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_interval = ReceiveRateInterval(ndr_measured_low,
+                                               ndr_measured_high)
+            pdr_interval = ReceiveRateInterval(pdr_measured_low,
+                                               pdr_measured_high)
+            starting_result = NdrPdrResult(ndr_interval, pdr_interval)
+            ending_result = NdrPdrResult(ndr_interval, ndr_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock__measure_and_update_state.return_value = \
+                MultipleLossRatioSearch.ProgressState(ending_result, -1, 30,
+                                                      0.2, 0.0, 14880000,
+                                                      14880000)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 30, 0.005, 0.0, 100000,
+                14880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(30, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(10880000.0,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(10879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(0,
+                         state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(10879927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(362664.23333,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(362664.23333,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(12880000.0,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(12879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(100000,
+                         state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(12779927,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(425997.56667,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(0.00776,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(10880000.0,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(10879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(0,
+                         state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(10879927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(362664.23333,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(362664.23333,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(12880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(12879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(100000,
+                         state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(12779927,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(425997.56667,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(0.00776,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.2, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_ndrpdr_update_state_pdr_hi_duration(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock,
+                                     mock.Mock()))
+        with mock.patch.object(algorithm, 'measure') as \
+                mock_measure, \
+                mock.patch.object(algorithm, '_measure_and_update_state') as \
+                        mock__measure_and_update_state:
+            ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927,
+                                                      0)
+            ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                       100000)
+            ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927,
+                                                      0)
+            pdr_measured_high = ReceiveRateMeasurement(30, 13880000, 14879927,
+                                                       0)
+            pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            ndr_interval = ReceiveRateInterval(ndr_measured_low,
+                                               ndr_measured_high)
+            pdr_interval = ReceiveRateInterval(pdr_measured_low,
+                                               pdr_measured_high)
+            starting_result = NdrPdrResult(ndr_interval, pdr_interval)
+            ending_result = NdrPdrResult(ndr_interval, ndr_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock__measure_and_update_state.return_value = \
+                MultipleLossRatioSearch.ProgressState(ending_result, -1, 30,
+                                                      0.2, 0.0, 14880000,
+                                                      14880000)
+            previous_state = MultipleLossRatioSearch.ProgressState(
+                starting_result, -1, 50, 0.005, 0.0, 100000,
+                10880000)
+            state = algorithm.ndrpdr(previous_state)
+            self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState)
+        self.assertEqual(30, state.result.ndr_interval.measured_low.duration)
+        self.assertEqual(10880000.0,
+                         state.result.ndr_interval.measured_low.target_tr)
+        self.assertEqual(10879927,
+                         state.result.ndr_interval.measured_low.transmit_count)
+        self.assertEqual(0,
+                         state.result.ndr_interval.measured_low.loss_count)
+        self.assertEqual(10879927,
+                         state.result.ndr_interval.measured_low.receive_count)
+        self.assertEqual(362664.23333,
+                         state.result.ndr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_low.loss_rate)
+        self.assertEqual(362664.23333,
+                         state.result.ndr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.ndr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.ndr_interval.measured_high.duration)
+        self.assertEqual(12880000.0,
+                         state.result.ndr_interval.measured_high.target_tr)
+        self.assertEqual(12879927,
+                         state.result.ndr_interval.measured_high.transmit_count)
+        self.assertEqual(100000,
+                         state.result.ndr_interval.measured_high.loss_count)
+        self.assertEqual(12779927,
+                         state.result.ndr_interval.measured_high.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.ndr_interval.measured_high.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.ndr_interval.measured_high.loss_rate)
+        self.assertEqual(425997.56667,
+                         state.result.ndr_interval.measured_high.receive_rate)
+        self.assertEqual(0.00776,
+                         state.result.ndr_interval.measured_high.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_low.duration)
+        self.assertEqual(10880000.0,
+                         state.result.pdr_interval.measured_low.target_tr)
+        self.assertEqual(10879927,
+                         state.result.pdr_interval.measured_low.transmit_count)
+        self.assertEqual(0,
+                         state.result.pdr_interval.measured_low.loss_count)
+        self.assertEqual(10879927,
+                         state.result.pdr_interval.measured_low.receive_count)
+        self.assertEqual(362664.23333,
+                         state.result.pdr_interval.measured_low.transmit_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_low.loss_rate)
+        self.assertEqual(362664.23333,
+                         state.result.pdr_interval.measured_low.receive_rate)
+        self.assertEqual(0.0,
+                         state.result.pdr_interval.measured_low.loss_fraction)
+        self.assertEqual(30, state.result.pdr_interval.measured_high.duration)
+        self.assertEqual(12880000,
+                         state.result.pdr_interval.measured_high.target_tr)
+        self.assertEqual(12879927,
+                         state.result.pdr_interval.measured_high.transmit_count)
+        self.assertEqual(100000,
+                         state.result.pdr_interval.measured_high.loss_count)
+        self.assertEqual(12779927,
+                         state.result.pdr_interval.measured_high.receive_count)
+        self.assertEqual(429330.9,
+                         state.result.pdr_interval.measured_high.transmit_rate)
+        self.assertEqual(3333.33333,
+                         state.result.pdr_interval.measured_high.loss_rate)
+        self.assertEqual(425997.56667,
+                         state.result.pdr_interval.measured_high.receive_rate)
+        self.assertEqual(0.00776,
+                         state.result.pdr_interval.measured_high.loss_fraction)
+        self.assertEqual(-1, state.phases)
+        self.assertEqual(30, state.duration)
+        self.assertEqual(0.2, state.width_goal)
+        self.assertEqual(0.0, state.packet_loss_ratio)
+        self.assertEqual(14880000, state.minimum_transmit_rate)
+        self.assertEqual(14880000, state.maximum_transmit_rate)
+
+    def test_measure(self):
+        measurer = mock.MagicMock()
+        measurer.sent = 102563094
+        measurer.loss = 30502
+        algorithm = MultipleLossRatioSearch(measurer=measurer, latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.MagicMock(),
+                                     mock.Mock, mock.Mock()))
+        measurement = algorithm.measure(30, 3418770.3425, True)
+        self.assertIsInstance(measurement, ReceiveRateMeasurement)
+        self.assertEqual(30, measurement.duration)
+        self.assertEqual(3418770.3425, measurement.target_tr)
+        self.assertEqual(102563094, measurement.transmit_count)
+        self.assertEqual(30502, measurement.loss_count)
+        self.assertEqual(102532592, measurement.receive_count)
+        self.assertEqual(3418769.8, measurement.transmit_rate)
+        self.assertEqual(1016.73333, measurement.loss_rate)
+        self.assertEqual(3417753.06667, measurement.receive_rate)
+        self.assertEqual(0.0003, measurement.loss_fraction)
+
+    def test_perform_additional_measurements_based_on_ndrpdr_result(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        ports = [0, 1]
+        port_pg_id = PortPgIDMap()
+        port_pg_id.add_port(0)
+        port_pg_id.add_port(1)
+        self.assertIsNone(
+            algorithm.init_generator(ports, port_pg_id, mock.Mock, mock.Mock,
+                                     mock.Mock()))
+        result = mock.MagicMock()
+        result.ndr_interval.measured_low.target_tr.return_result = 100000
+        self.assertIsNone(
+            algorithm.perform_additional_measurements_based_on_ndrpdr_result(
+                result))
+
+    def test_display_single_bound(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        result_samples = {}
+        self.assertIsNone(
+            algorithm.display_single_bound(result_samples, 'NDR_LOWER',
+                                           4857361, 64,
+                                           ['20/849/1069', '40/69/183']))
+        self.assertEqual(
+            {'Result_NDR_LOWER': {'bandwidth_total_Gbps': 3.264146592,
+                                  'rate_total_pps': 4857361.0},
+             'Result_stream0_NDR_LOWER': {'avg_latency': 849.0,
+                                          'max_latency': 1069.0,
+                                          'min_latency': 20.0},
+             'Result_stream1_NDR_LOWER': {'avg_latency': 69.0,
+                                          'max_latency': 183.0,
+                                          'min_latency': 40.0}},
+            result_samples)
+
+    def test_check_ndrpdr_interval_validity(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        result_samples = {}
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 0)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 0)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        self.assertEqual('Minimal rate loss fraction 0.0 reach target 0.0',
+                         algorithm.check_ndrpdr_interval_validity(
+                             result_samples, 'NDR_LOWER',
+                             receive_rate_interval))
+        self.assertEqual(
+            {'Result_NDR_LOWER_packets_lost': {'packet_loss_ratio': 0.0,
+                                               'packets_lost': 0.0}},
+            result_samples)
+
+    def test_check_ndrpdr_interval_validity_fail(self):
+        algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True,
+                                            pkt_size=64,
+                                            final_trial_duration=30,
+                                            final_relative_width=0.005,
+                                            number_of_intermediate_phases=2,
+                                            initial_trial_duration=1,
+                                            timeout=720)
+        result_samples = {}
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        self.assertEqual(
+            'Minimal rate loss fraction 0.01749 does not reach target 0.005\n84965 packets lost.',
+            algorithm.check_ndrpdr_interval_validity(result_samples,
+                                                     'NDR_LOWER',
+                                                     receive_rate_interval,
+                                                     0.005))
+        self.assertEqual({'Result_NDR_LOWER_packets_lost': {
+            'packet_loss_ratio': 0.01749,
+            'packets_lost': 84965.0}}, result_samples)
diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_ndr_pdr_result.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_ndr_pdr_result.py
new file mode 100644 (file)
index 0000000..ea9c39a
--- /dev/null
@@ -0,0 +1,91 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# 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 unittest
+
+import mock
+
+from yardstick.network_services.helpers.vpp_helpers.ndr_pdr_result import \
+    NdrPdrResult
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \
+    ReceiveRateInterval
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \
+    ReceiveRateMeasurement
+
+
+class TestNdrPdrResult(unittest.TestCase):
+
+    def test___init__(self):
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        starting_interval = ReceiveRateInterval(measured_low, measured_high)
+        ndrpdr_result = NdrPdrResult(starting_interval, starting_interval)
+        self.assertIsInstance(ndrpdr_result.ndr_interval, ReceiveRateInterval)
+        self.assertIsInstance(ndrpdr_result.pdr_interval, ReceiveRateInterval)
+
+    def test___init__ndr_error(self):
+        starting_interval = mock.MagicMock()
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        end_interval = ReceiveRateInterval(measured_low, measured_high)
+        with self.assertRaises(TypeError) as raised:
+            NdrPdrResult(starting_interval, end_interval)
+        self.assertIn('ndr_interval, is not a ReceiveRateInterval: ',
+                      str(raised.exception))
+
+    def test___init__pdr_error(self):
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        starting_interval = ReceiveRateInterval(measured_low, measured_high)
+        end_interval = mock.MagicMock()
+        with self.assertRaises(TypeError) as raised:
+            NdrPdrResult(starting_interval, end_interval)
+        self.assertIn('pdr_interval, is not a ReceiveRateInterval: ',
+                      str(raised.exception))
+
+    def test_width_in_goals(self):
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        starting_interval = ReceiveRateInterval(measured_low, measured_high)
+        ndrpdr_result = NdrPdrResult(starting_interval, starting_interval)
+        self.assertEqual('ndr 4.86887; pdr 4.86887',
+                         ndrpdr_result.width_in_goals(0.005))
+
+    def test___str__(self):
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        starting_interval = ReceiveRateInterval(measured_low, measured_high)
+        ndrpdr_result = NdrPdrResult(starting_interval, starting_interval)
+        self.assertEqual(
+            'NDR=[d=1.0,Tr=4857361.0,Df=0.01749;d=1.0,Tr=4977343.0,Df=0.0241);'
+            'PDR=[d=1.0,Tr=4857361.0,Df=0.01749;d=1.0,Tr=4977343.0,Df=0.0241)',
+            ndrpdr_result.__str__())
+
+    def test___repr__(self):
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        starting_interval = ReceiveRateInterval(measured_low, measured_high)
+        ndrpdr_result = NdrPdrResult(starting_interval, starting_interval)
+        self.assertEqual(
+            'NdrPdrResult(ndr_interval=ReceiveRateInterval(measured_low=' \
+            'ReceiveRateMeasurement(duration=1.0,target_tr=4857361.0,' \
+            'transmit_count=4857339,loss_count=84965),measured_high=' \
+            'ReceiveRateMeasurement(duration=1.0,target_tr=4977343.0,' \
+            'transmit_count=4977320,loss_count=119959)),pdr_interval=' \
+            'ReceiveRateInterval(measured_low=ReceiveRateMeasurement' \
+            '(duration=1.0,target_tr=4857361.0,transmit_count=4857339,' \
+            'loss_count=84965),measured_high=ReceiveRateMeasurement' \
+            '(duration=1.0,target_tr=4977343.0,transmit_count=4977320,' \
+            'loss_count=119959)))',
+            ndrpdr_result.__repr__())
diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_interval.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_interval.py
new file mode 100644 (file)
index 0000000..bbf2416
--- /dev/null
@@ -0,0 +1,100 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# 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 unittest
+
+import mock
+
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \
+    ReceiveRateInterval
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \
+    ReceiveRateMeasurement
+
+
+class TestReceiveRateInterval(unittest.TestCase):
+
+    def test__init__(self):
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        self.assertIsInstance(receive_rate_interval.measured_low,
+                              ReceiveRateMeasurement)
+        self.assertIsInstance(receive_rate_interval.measured_high,
+                              ReceiveRateMeasurement)
+
+    def test__init__measured_low_error(self):
+        measured_low = mock.MagicMock()
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        with self.assertRaises(TypeError) as raised:
+            ReceiveRateInterval(measured_low, measured_high)
+        self.assertIn('measured_low is not a ReceiveRateMeasurement: ',
+                      str(raised.exception))
+
+    def test__init__measured_high_error(self):
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = mock.MagicMock()
+        with self.assertRaises(TypeError) as raised:
+            ReceiveRateInterval(measured_low, measured_high)
+        self.assertIn('measured_high is not a ReceiveRateMeasurement: ',
+                      str(raised.exception))
+
+    def test_sort(self):
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        self.assertIsNone(receive_rate_interval.sort())
+        self.assertEqual(119982.0, receive_rate_interval.abs_tr_width)
+        self.assertEqual(0.02411,
+                         receive_rate_interval.rel_tr_width)
+
+    def test_sort_swap(self):
+        measured_low = ReceiveRateMeasurement(1, 14857361, 14857339, 184965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        self.assertIsNone(receive_rate_interval.sort())
+        self.assertEqual(9880018.0, receive_rate_interval.abs_tr_width)
+        self.assertEqual(0.66499,
+                         receive_rate_interval.rel_tr_width)
+
+    def test_width_in_goals(self):
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        self.assertEqual(4.86887,
+                         receive_rate_interval.width_in_goals(0.005))
+
+    def test___str__(self):
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        self.assertEqual(
+            '[d=1.0,Tr=4857361.0,Df=0.01749;d=1.0,Tr=4977343.0,Df=0.0241)',
+            receive_rate_interval.__str__())
+
+    def test___repr__(self):
+        measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959)
+        receive_rate_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+        self.assertEqual('ReceiveRateInterval(measured_low=' \
+                         'ReceiveRateMeasurement(duration=1.0,target_tr=4857361.0,' \
+                         'transmit_count=4857339,loss_count=84965),measured_high=' \
+                         'ReceiveRateMeasurement(duration=1.0,target_tr=4977343.0,' \
+                         'transmit_count=4977320,loss_count=119959))',
+                         receive_rate_interval.__repr__())
diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_measurement.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_measurement.py
new file mode 100644 (file)
index 0000000..d4e2d79
--- /dev/null
@@ -0,0 +1,44 @@
+# Copyright (c) 2019 Viosoft Corporation
+#
+# 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 unittest
+
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \
+    ReceiveRateMeasurement
+
+
+class TestReceiveRateMeasurement(unittest.TestCase):
+
+    def test__init__(self):
+        measured = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        self.assertEqual(1, measured.duration)
+        self.assertEqual(4857361, measured.target_tr)
+        self.assertEqual(4857339, measured.transmit_count)
+        self.assertEqual(84965, measured.loss_count)
+        self.assertEqual(4772374, measured.receive_count)
+        self.assertEqual(4857339, measured.transmit_rate)
+        self.assertEqual(84965.0, measured.loss_rate)
+        self.assertEqual(4772374.0, measured.receive_rate)
+        self.assertEqual(0.01749, measured.loss_fraction)
+
+    def test___str__(self):
+        measured = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        self.assertEqual('d=1.0,Tr=4857361.0,Df=0.01749',
+                         measured.__str__())
+
+    def test___repr__(self):
+        measured = ReceiveRateMeasurement(1, 4857361, 4857339, 84965)
+        self.assertEqual('ReceiveRateMeasurement(duration=1.0,' \
+                         'target_tr=4857361.0,transmit_count=4857339,loss_count=84965)',
+                         measured.__repr__())
index 4e5a0f7..8ad17b5 100644 (file)
@@ -18,8 +18,17 @@ from trex_stl_lib import trex_stl_packet_builder_scapy
 from trex_stl_lib import trex_stl_streams
 
 from yardstick.common import constants
+from yardstick.network_services.helpers.vpp_helpers.multiple_loss_ratio_search import \
+    MultipleLossRatioSearch
+from yardstick.network_services.helpers.vpp_helpers.ndr_pdr_result import \
+    NdrPdrResult
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \
+    ReceiveRateInterval
+from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \
+    ReceiveRateMeasurement
 from yardstick.network_services.traffic_profile import base as tp_base
 from yardstick.network_services.traffic_profile import rfc2544, vpp_rfc2544
+from yardstick.network_services.traffic_profile.rfc2544 import PortPgIDMap
 from yardstick.tests.unit import base
 
 
@@ -129,6 +138,7 @@ class TestVppRFC2544Profile(base.BaseUnitTestCase):
         self.assertEqual(vpp_rfc2544_profile.max_rate,
                          vpp_rfc2544_profile.rate)
         self.assertEqual(0, vpp_rfc2544_profile.min_rate)
+        self.assertEqual(2, vpp_rfc2544_profile.number_of_intermediate_phases)
         self.assertEqual(30, vpp_rfc2544_profile.duration)
         self.assertEqual(0.1, vpp_rfc2544_profile.precision)
         self.assertEqual(1.0, vpp_rfc2544_profile.lower_bound)
@@ -287,6 +297,7 @@ class TestVppRFC2544Profile(base.BaseUnitTestCase):
         vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
             self.TRAFFIC_PROFILE_MAX_RATE)
         vpp_rfc2544_profile.init_queue(mock.MagicMock())
+        vpp_rfc2544_profile.pkt_size = 64
         vpp_rfc2544_profile.params = {
             'downlink_0': 'profile1',
             'uplink_0': 'profile2'}
@@ -480,6 +491,7 @@ class TestVppRFC2544Profile(base.BaseUnitTestCase):
         mock_latency.side_effect = ['latency1', 'latency2']
         vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
             self.TRAFFIC_PROFILE_MAX_RATE)
+        vpp_rfc2544_profile.pkt_size = 64
         vpp_rfc2544_profile.port_pg_id = rfc2544.PortPgIDMap()
         vpp_rfc2544_profile.port_pg_id.add_port(1)
         with mock.patch.object(vpp_rfc2544_profile, '_create_single_packet') as \
@@ -523,10 +535,69 @@ class TestVppRFC2544Profile(base.BaseUnitTestCase):
     def test_binary_search_with_optimized(self):
         vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(
             self.TRAFFIC_PROFILE)
+        vpp_rfc2544_profile.pkt_size = 64
+        vpp_rfc2544_profile.init_queue(mock.MagicMock())
         mock_generator = mock.MagicMock()
-        self.assertIsNone(
-            vpp_rfc2544_profile.binary_search_with_optimized(mock_generator,
-                                                             30, 720, ''))
+        mock_generator.vnfd_helper.interfaces = [
+            {"name": "xe0"}, {"name": "xe0"}
+        ]
+
+        vpp_rfc2544_profile.ports = [0, 1]
+        vpp_rfc2544_profile.port_pg_id = PortPgIDMap()
+        vpp_rfc2544_profile.port_pg_id.add_port(0)
+        vpp_rfc2544_profile.port_pg_id.add_port(1)
+        vpp_rfc2544_profile.profiles = mock.MagicMock()
+        vpp_rfc2544_profile.test_data = mock.MagicMock()
+        vpp_rfc2544_profile.queue = mock.MagicMock()
+
+        with mock.patch.object(MultipleLossRatioSearch, 'measure') as \
+                mock_measure, \
+                mock.patch.object(MultipleLossRatioSearch, 'ndrpdr') as \
+                        mock_ndrpdr:
+            measured_low = ReceiveRateMeasurement(1, 14880000, 14879927, 0)
+            measured_high = ReceiveRateMeasurement(1, 14880000, 14879927, 0)
+            measured_low.latency = ['1000/3081/3962', '500/3149/3730']
+            measured_high.latency = ['1000/3081/3962', '500/3149/3730']
+            starting_interval = ReceiveRateInterval(measured_low,
+                                                    measured_high)
+            starting_result = NdrPdrResult(starting_interval,
+                                           starting_interval)
+            mock_measure.return_value = ReceiveRateMeasurement(1, 14880000,
+                                                               14879927, 0)
+            mock_ndrpdr.return_value = MultipleLossRatioSearch.ProgressState(
+                starting_result, 2, 30, 0.005, 0.0,
+                4857361, 4977343)
+
+            result_samples = vpp_rfc2544_profile.binary_search_with_optimized(
+                traffic_generator=mock_generator, duration=30,
+                timeout=720,
+                test_data={})
+
+        expected = {'Result_NDR_LOWER': {'bandwidth_total_Gbps': 9.999310944,
+                                         'rate_total_pps': 14879927.0},
+                    'Result_NDR_UPPER': {'bandwidth_total_Gbps': 9.999310944,
+                                         'rate_total_pps': 14879927.0},
+                    'Result_NDR_packets_lost': {'packet_loss_ratio': 0.0,
+                                                'packets_lost': 0.0},
+                    'Result_PDR_LOWER': {'bandwidth_total_Gbps': 9.999310944,
+                                         'rate_total_pps': 14879927.0},
+                    'Result_PDR_UPPER': {'bandwidth_total_Gbps': 9.999310944,
+                                         'rate_total_pps': 14879927.0},
+                    'Result_PDR_packets_lost': {'packet_loss_ratio': 0.0,
+                                                'packets_lost': 0.0},
+                    'Result_stream0_NDR_LOWER': {'avg_latency': 3081.0,
+                                                 'max_latency': 3962.0,
+                                                 'min_latency': 1000.0},
+                    'Result_stream0_PDR_LOWER': {'avg_latency': 3081.0,
+                                                 'max_latency': 3962.0,
+                                                 'min_latency': 1000.0},
+                    'Result_stream1_NDR_LOWER': {'avg_latency': 3149.0,
+                                                 'max_latency': 3730.0,
+                                                 'min_latency': 500.0},
+                    'Result_stream1_PDR_LOWER': {'avg_latency': 3149.0,
+                                                 'max_latency': 3730.0,
+                                                 'min_latency': 500.0}}
+        self.assertEqual(expected, result_samples)
 
     def test_binary_search(self):
         vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(