-| | Clear and show runtime counters with running traffic | ${duration}
-| | ... | ${rate} | ${framesize} | ${topology_type}
-| | ${ret}= | Is DPDK performance test
-| | Run Keyword If | ${ret}==${FALSE} | Clear all counters on all DUTs
-| | Send traffic on tg | ${duration} | ${rate} | ${framesize}
-| | ... | ${topology_type} | warmup_time=0
-| | Run Keyword If | ${ret}==${FALSE} | Show statistics on all DUTs | ${nodes}
-| | Run Keyword If | ${fail_on_loss} | Partial traffic loss accepted
-| | ... | ${loss_acceptance} | ${loss_acceptance_type}
+| | ${results} = | Send traffic at specified rate | ${trial_duration}
+| | ... | ${max_rate}pps | ${frame_size} | ${traffic_profile} | ${subsamples}
+| | ... | ${unidirection} | ${tx_port} | ${rx_port}
+| | Set Test Message | ${\n}Maximum Receive Rate trial results
+| | Set Test Message | in packets per second: ${results}
+| | ... | append=yes
+| | # TODO: Should we also report the percentage relative to transmit rate,
+| | # so that people looking at console can decide how close to 100% it is?
+| | Run Keyword If | ${fail_no_traffic} | Fail if no traffic forwarded
+
+| Send traffic at specified rate
+| | [Documentation]
+| | ... | Send traffic at specified rate.
+| | ... | Return list of measured receive rates.
+| | ... | The rate argument should be TRex friendly, so it should include "pps".
+| | ...
+| | ... | *Arguments:*
+| | ... | - trial_duration - Duration of single trial [s]. Type: float
+| | ... | - rate - Rate for sending packets. Type: string
+| | ... | - frame_size - L2 Frame Size [B]. Type: integer/string
+| | ... | - traffic_profile - Topology type. Type: string
+| | ... | - subsamples - How many trials in this measurement. Type: int
+| | ... | - unidirection - False if traffic is bidirectional. Type: boolean
+| | ... | - tx_port - TX port of TG, default 0. Type: integer
+| | ... | - rx_port - RX port of TG, default 1. Type: integer
+| | ...
+| | ... | *Example:*
+| | ...
+| | ... | \| Send traffic at specified rate \| ${1.0} \| 4.0mpps \| 64 \
+| | ... | \| 3-node-IPv4 \| ${10} \| ${False} \| ${0} | ${1} \|
+| | ...
+| | [Arguments] | ${trial_duration} | ${rate} | ${frame_size}
+| | ... | ${traffic_profile} | ${subsamples}=${1} | ${unidirection}=${False}
+| | ... | ${tx_port}=${0} | ${rx_port}=${1}
+| | ...
+| | Clear and show runtime counters with running traffic | ${trial_duration}
+| | ... | ${rate} | ${frame_size} | ${traffic_profile}
+| | ... | ${unidirection} | ${tx_port} | ${rx_port}
+| | Run Keyword If | ${dut_stats}==${True} | Clear all counters on all DUTs
+| | Run Keyword If | ${dut_stats}==${True} and ${pkt_trace}==${True}
+| | ... | VPP Enable Traces On All DUTs | ${nodes}
+| | Run Keyword If | ${dut_stats}==${True}
+| | ... | VPP enable elog traces on all DUTs | ${nodes}
+| | ${results} = | Create List
+| | :FOR | ${i} | IN RANGE | ${subsamples}
+| | | # The following line is skipping some default arguments,
+| | | # that is why subsequent arguments have to be named.
+| | | Send traffic on tg | ${trial_duration} | ${rate} | ${frame_size}
+| | | ... | ${traffic_profile} | warmup_time=${0} | unidirection=${unidirection}
+| | | ... | tx_port=${tx_port} | rx_port=${rx_port}
+| | | ${rx} = | Get Received
+| | | ${rr} = | Evaluate | ${rx} / ${trial_duration}
+| | | Append To List | ${results} | ${rr}
+| | Run Keyword If | ${dut_stats}==${True} | Show event logger on all DUTs
+| | ... | ${nodes}
+| | Run Keyword If | ${dut_stats}==${True} | Show statistics on all DUTs
+| | ... | ${nodes}
+| | Run Keyword If | ${dut_stats}==${True} and ${pkt_trace}==${True}
+| | ... | Show Packet Trace On All Duts | ${nodes} | maximum=${100}
+| | Return From Keyword | ${results}