+def _tpc_modify_test_name(test_name, ignore_nic=False):
+ """Modify a test name by replacing its parts.
+
+ :param test_name: Test name to be modified.
+ :param ignore_nic: If True, NIC is removed from TC name.
+ :type test_name: str
+ :type ignore_nic: bool
+ :returns: Modified test name.
+ :rtype: str
+ """
+ test_name_mod = test_name.\
+ replace(u"-ndrpdr", u"").\
+ replace(u"1t1c", u"1c").\
+ replace(u"2t1c", u"1c"). \
+ replace(u"2t2c", u"2c").\
+ replace(u"4t2c", u"2c"). \
+ replace(u"4t4c", u"4c").\
+ replace(u"8t4c", u"4c")
+
+ if ignore_nic:
+ return re.sub(REGEX_NIC, u"", test_name_mod)
+ return test_name_mod
+
+
+def _tpc_modify_displayed_test_name(test_name):
+ """Modify a test name which is displayed in a table by replacing its parts.
+
+ :param test_name: Test name to be modified.
+ :type test_name: str
+ :returns: Modified test name.
+ :rtype: str
+ """
+ return test_name.\
+ replace(u"1t1c", u"1c").\
+ replace(u"2t1c", u"1c"). \
+ replace(u"2t2c", u"2c").\
+ replace(u"4t2c", u"2c"). \
+ replace(u"4t4c", u"4c").\
+ replace(u"8t4c", u"4c")
+
+
+def _tpc_insert_data(target, src, include_tests):
+ """Insert src data to the target structure.
+
+ :param target: Target structure where the data is placed.
+ :param src: Source data to be placed into the target structure.
+ :param include_tests: Which results will be included (MRR, NDR, PDR).
+ :type target: list
+ :type src: dict
+ :type include_tests: str
+ """
+ try:
+ if include_tests == u"MRR":
+ target[u"mean"] = src[u"result"][u"receive-rate"]
+ target[u"stdev"] = src[u"result"][u"receive-stdev"]
+ elif include_tests == u"PDR":
+ target[u"data"].append(src[u"throughput"][u"PDR"][u"LOWER"])
+ elif include_tests == u"NDR":
+ target[u"data"].append(src[u"throughput"][u"NDR"][u"LOWER"])
+ except (KeyError, TypeError):
+ pass
+
+
+def _tpc_generate_html_table(header, data, out_file_name, legend=u"",
+ footnote=u"", sort_data=True, title=u"",
+ generate_rst=True):
+ """Generate html table from input data with simple sorting possibility.
+
+ :param header: Table header.
+ :param data: Input data to be included in the table. It is a list of lists.
+ Inner lists are rows in the table. All inner lists must be of the same
+ length. The length of these lists must be the same as the length of the
+ header.
+ :param out_file_name: The name (relative or full path) where the
+ generated html table is written.
+ :param legend: The legend to display below the table.
+ :param footnote: The footnote to display below the table (and legend).
+ :param sort_data: If True the data sorting is enabled.
+ :param title: The table (and file) title.
+ :param generate_rst: If True, wrapping rst file is generated.
+ :type header: list
+ :type data: list of lists
+ :type out_file_name: str
+ :type legend: str
+ :type footnote: str
+ :type sort_data: bool
+ :type title: str
+ :type generate_rst: bool
+ """
+
+ try:
+ idx = header.index(u"Test Case")
+ except ValueError:
+ idx = 0
+ params = {
+ u"align-hdr": (
+ [u"left", u"right"],
+ [u"left", u"left", u"right"],
+ [u"left", u"left", u"left", u"right"]
+ ),
+ u"align-itm": (
+ [u"left", u"right"],
+ [u"left", u"left", u"right"],
+ [u"left", u"left", u"left", u"right"]
+ ),
+ u"width": ([15, 9], [4, 24, 10], [4, 4, 32, 10])
+ }
+
+ df_data = pd.DataFrame(data, columns=header)
+
+ if sort_data:
+ df_sorted = [df_data.sort_values(
+ by=[key, header[idx]], ascending=[True, True]
+ if key != header[idx] else [False, True]) for key in header]
+ df_sorted_rev = [df_data.sort_values(
+ by=[key, header[idx]], ascending=[False, True]
+ if key != header[idx] else [True, True]) for key in header]
+ df_sorted.extend(df_sorted_rev)
+ else:
+ df_sorted = df_data
+
+ fill_color = [[u"#d4e4f7" if idx % 2 else u"#e9f1fb"
+ for idx in range(len(df_data))]]
+ table_header = dict(
+ values=[f"<b>{item.replace(u',', u',<br>')}</b>" for item in header],
+ fill_color=u"#7eade7",
+ align=params[u"align-hdr"][idx],
+ font=dict(
+ family=u"Courier New",
+ size=12
+ )
+ )
+
+ fig = go.Figure()
+
+ if sort_data:
+ for table in df_sorted:
+ columns = [table.get(col) for col in header]
+ fig.add_trace(
+ go.Table(
+ columnwidth=params[u"width"][idx],
+ header=table_header,
+ cells=dict(
+ values=columns,
+ fill_color=fill_color,
+ align=params[u"align-itm"][idx],
+ font=dict(
+ family=u"Courier New",
+ size=12
+ )
+ )
+ )
+ )
+
+ buttons = list()
+ menu_items = [f"<b>{itm}</b> (ascending)" for itm in header]
+ menu_items.extend([f"<b>{itm}</b> (descending)" for itm in header])
+ for idx, hdr in enumerate(menu_items):
+ visible = [False, ] * len(menu_items)
+ visible[idx] = True
+ buttons.append(
+ dict(
+ label=hdr.replace(u" [Mpps]", u""),
+ method=u"update",
+ args=[{u"visible": visible}],
+ )
+ )
+
+ fig.update_layout(
+ updatemenus=[
+ go.layout.Updatemenu(
+ type=u"dropdown",
+ direction=u"down",
+ x=0.0,
+ xanchor=u"left",
+ y=1.002,
+ yanchor=u"bottom",
+ active=len(menu_items) - 1,
+ buttons=list(buttons)
+ )
+ ],
+ )
+ else:
+ fig.add_trace(
+ go.Table(
+ columnwidth=params[u"width"][idx],
+ header=table_header,
+ cells=dict(
+ values=[df_sorted.get(col) for col in header],
+ fill_color=fill_color,
+ align=params[u"align-itm"][idx],
+ font=dict(
+ family=u"Courier New",
+ size=12
+ )
+ )
+ )
+ )
+
+ ploff.plot(
+ fig,
+ show_link=False,
+ auto_open=False,
+ filename=f"{out_file_name}_in.html"
+ )
+
+ if not generate_rst:
+ return
+
+ file_name = out_file_name.split(u"/")[-1]
+ if u"vpp" in out_file_name:
+ path = u"_tmp/src/vpp_performance_tests/comparisons/"
+ else:
+ path = u"_tmp/src/dpdk_performance_tests/comparisons/"
+ logging.info(f" Writing the HTML file to {path}{file_name}.rst")
+ with open(f"{path}{file_name}.rst", u"wt") as rst_file:
+ rst_file.write(
+ u"\n"
+ u".. |br| raw:: html\n\n <br />\n\n\n"
+ u".. |prein| raw:: html\n\n <pre>\n\n\n"
+ u".. |preout| raw:: html\n\n </pre>\n\n"
+ )
+ if title:
+ rst_file.write(f"{title}\n")
+ rst_file.write(f"{u'`' * len(title)}\n\n")
+ rst_file.write(
+ u".. raw:: html\n\n"
+ f' <iframe frameborder="0" scrolling="no" '
+ f'width="1600" height="1200" '
+ f'src="../..{out_file_name.replace(u"_build", u"")}_in.html">'
+ f'</iframe>\n\n'
+ )
+
+ if legend:
+ try:
+ itm_lst = legend[1:-2].split(u"\n")
+ rst_file.write(
+ f"{itm_lst[0]}\n\n- " + u'\n- '.join(itm_lst[1:]) + u"\n\n"
+ )
+ except IndexError as err:
+ logging.error(f"Legend cannot be written to html file\n{err}")
+ if footnote:
+ try:
+ itm_lst = footnote[1:].split(u"\n")
+ rst_file.write(
+ f"{itm_lst[0]}\n\n- " + u'\n- '.join(itm_lst[1:]) + u"\n\n"
+ )
+ except IndexError as err:
+ logging.error(f"Footnote cannot be written to html file\n{err}")
+
+
+def table_soak_vs_ndr(table, input_data):
+ """Generate the table(s) with algorithm: table_soak_vs_ndr