From cf4f0a9168b2ff2c0818f3a42f0119ceaf3d0d45 Mon Sep 17 00:00:00 2001 From: anaslari23 Date: Sat, 10 Jan 2026 03:25:29 +0530 Subject: [PATCH 1/2] Support value-dependent hyperlinks in table generator Implement support for ${value} placeholder in the href attribute of table columns. This allows dynamic hyperlinks that reference the content of the current table cell. - Added support for ${value} substitution in _create_link. - Updated prepare_value and clean_up_results to pass cell values. - Maintained backward compatibility for static strings and other variables. - Added unit test to verify substitution logic. --- benchexec/tablegenerator/htmltable.py | 55 +++++++++------- .../tablegenerator/test_href_substitution.py | 63 +++++++++++++++++++ 2 files changed, 97 insertions(+), 21 deletions(-) create mode 100644 benchexec/tablegenerator/test_href_substitution.py diff --git a/benchexec/tablegenerator/htmltable.py b/benchexec/tablegenerator/htmltable.py index b53e2beb0..0b1a7878a 100644 --- a/benchexec/tablegenerator/htmltable.py +++ b/benchexec/tablegenerator/htmltable.py @@ -426,7 +426,14 @@ def prepare_value(column, value, run_result): formatted_value = column.format_value(value, "html_cell") result = {} if column.href: - result["href"] = _create_link(column.href, base_dir, run_result, href_base) + # We use raw_value for the ${value} placeholder. + # If the cell is empty, we fall back to the column pattern if it exists. + subst_value = raw_value + if not subst_value and not formatted_value: + subst_value = column.pattern + result["href"] = _create_link( + column.href, base_dir, run_result, href_base, value=subst_value + ) if not raw_value and not formatted_value: raw_value = column.pattern if raw_value is not None and not raw_value == "": @@ -450,7 +457,9 @@ def clean_up_results(res): if getattr(res, k) is not None } if toolHref: - result["href"] = _create_link(toolHref, base_dir, res, href_base) + result["href"] = _create_link( + toolHref, base_dir, res, href_base, value=res.status + ) result["values"] = values return result @@ -474,31 +483,35 @@ def clean_up_row(row): return [clean_up_row(row) for row in rows] -def _create_link(href, base_dir, runResult=None, href_base=None): +def _create_link(href, base_dir, runResult=None, href_base=None, value=None): def get_replacements(task_file): var_prefix = "taskdef_" if task_file.endswith(".yml") else "inputfile_" - return ( + replacements = [ (var_prefix + "name", os.path.basename(task_file)), (var_prefix + "path", os.path.dirname(task_file) or "."), (var_prefix + "path_abs", os.path.dirname(os.path.abspath(task_file))), - ) + ( - ( - ("logfile_name", os.path.basename(runResult.log_file)), - ( - "logfile_path", - os.path.dirname( - os.path.relpath(runResult.log_file, href_base or ".") - ) - or ".", - ), - ( - "logfile_path_abs", - os.path.dirname(os.path.abspath(runResult.log_file)), - ), + ] + if value is not None: + replacements.append(("value", str(value))) + + if runResult and runResult.log_file: + replacements.extend( + [ + ("logfile_name", os.path.basename(runResult.log_file)), + ( + "logfile_path", + os.path.dirname( + os.path.relpath(runResult.log_file, href_base or ".") + ) + or ".", + ), + ( + "logfile_path_abs", + os.path.dirname(os.path.abspath(runResult.log_file)), + ), + ] ) - if runResult.log_file - else () - ) + return tuple(replacements) source_file = ( # os.path.relpath creates os-dependant paths, so standardize the output between OSs diff --git a/benchexec/tablegenerator/test_href_substitution.py b/benchexec/tablegenerator/test_href_substitution.py new file mode 100644 index 000000000..71a5b51ea --- /dev/null +++ b/benchexec/tablegenerator/test_href_substitution.py @@ -0,0 +1,63 @@ +import os +import unittest +from benchexec.tablegenerator import htmltable +from benchexec.tablegenerator.columns import Column + + +class TestHrefSubstitution(unittest.TestCase): + def test_create_link_with_value_substitution(self): + href = "http://example.com/${value}" + base_dir = "." + value = "test-value" + + # We need a dummy runResult for _create_link to work if it tries to get source_file + # or we can pass None if we handle it. + # Looking at _create_link, it uses runResult to get source_file. + + class DummyTaskId: + def __init__(self, name): + self.name = name + + class DummyRunResult: + def __init__(self, task_name, log_file=None): + self.task_id = DummyTaskId(task_name) + self.log_file = log_file + + def __getitem__(self, key): + return getattr(self, key) + + run_result = DummyRunResult("task1") + + # Test basic substitution + link = htmltable._create_link(href, base_dir, runResult=run_result, value=value) + self.assertEqual(link, "http://example.com/test-value") + + def test_create_link_with_value_substitution_and_other_vars(self): + href = "http://example.com/${inputfile_name}?v=${value}" + base_dir = "." + value = "123" + + class DummyTaskId: + def __init__(self, name): + self.name = name + + class DummyRunResult: + def __init__(self, task_name, log_file=None): + self.task_id = DummyTaskId(task_name) + self.log_file = log_file + + run_result = DummyRunResult("dir/task1.c") + + link = htmltable._create_link(href, base_dir, runResult=run_result, value=value) + self.assertEqual(link, "http://example.com/task1.c?v=123") + + def test_create_link_backward_compatibility(self): + href = "http://example.com/static" + base_dir = "." + + link = htmltable._create_link(href, base_dir) + self.assertEqual(link, "http://example.com/static") + + +if __name__ == "__main__": + unittest.main() From d5cdfd7ca2156f170a564dd979b187a4377360fd Mon Sep 17 00:00:00 2001 From: anaslari23 Date: Sat, 10 Jan 2026 04:35:01 +0530 Subject: [PATCH 2/2] Fix REUSE headers and lint issues in new test --- benchexec/tablegenerator/test_href_substitution.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/benchexec/tablegenerator/test_href_substitution.py b/benchexec/tablegenerator/test_href_substitution.py index 71a5b51ea..017df7795 100644 --- a/benchexec/tablegenerator/test_href_substitution.py +++ b/benchexec/tablegenerator/test_href_substitution.py @@ -1,7 +1,12 @@ -import os +# This file is part of BenchExec, a framework for reliable benchmarking: +# https://github.com/sosy-lab/benchexec +# +# SPDX-FileCopyrightText: 2007-2026 Dirk Beyer +# +# SPDX-License-Identifier: Apache-2.0 + import unittest from benchexec.tablegenerator import htmltable -from benchexec.tablegenerator.columns import Column class TestHrefSubstitution(unittest.TestCase):