[PATCH i-g-t 10/10] scripts/xls_to_doc.py: add a logic to simplify tags

Mauro Carvalho Chehab mauro.chehab at linux.intel.com
Thu Mar 14 10:29:19 UTC 2024


From: Mauro Carvalho Chehab <mchehab at kernel.org>

When there are common fields at the test and they were updated,
reflect the changes at the test level if all subtests have equal
values at the same field(s).

Signed-off-by: Mauro Carvalho Chehab <mchehab at kernel.org>
---
 scripts/xls_to_doc.py | 133 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 121 insertions(+), 12 deletions(-)

diff --git a/scripts/xls_to_doc.py b/scripts/xls_to_doc.py
index 60f7536c8839..6492c5d8337a 100755
--- a/scripts/xls_to_doc.py
+++ b/scripts/xls_to_doc.py
@@ -8,6 +8,7 @@
 """Import contents of a XLS file into testplan documentation."""
 
 import argparse
+import copy
 import json
 import os
 import re
@@ -38,7 +39,7 @@ class FillTests(TestList):
         # Read current documentation
         TestList.__init__(self, config_path)
 
-        self.orig_doc = self.doc.copy()
+        self.orig_doc = copy.deepcopy(self.doc)
 
         self.testname_regex = re.compile(r'^\s*(igt@[^\n\@]+)\@?(\S*)\s*')
         self.key_has_wildcard = re.compile(r'\%?arg\[(\d+)\]')
@@ -152,11 +153,26 @@ class FillTests(TestList):
 
         return self.spreadsheet_data
 
-    def change_value(self, content, subtest, line, field, value):
+    def change_value(self, content, test, subtest, line, field, value):
         """
         Change the contents of a source file to update its documentation.
         """
 
+        if test:
+            line = 0
+            while True:
+                line += 1
+                if line >= len(content):
+                    break
+
+                file_line = content[line]
+
+                match = re.match(r'^\s*\* ?TEST:\s*(.*)', file_line)
+                if match:
+                    break
+
+            line += 1
+
         current_field = None
         found_line = None
         i = line
@@ -173,11 +189,14 @@ class FillTests(TestList):
             file_line = re.sub(r'^\s*\* ?', '', file_line)
 
             match = re.match(r'^SUBTESTS?:\s*(.*)', file_line)
-            if match and match.group(1) != subtest:
-                break
+            if match:
+                if subtest and match.group(1) != subtest:
+                    break
+                if test:
+                    break
 
             match = re.match(r'^TEST:\s*(.*)', file_line)
-            if match and match.group(1) != subtest:
+            if match:
                 break
 
             match = re.match(r'arg\[(\d+)\]:\s*(.*)', file_line)
@@ -208,11 +227,22 @@ class FillTests(TestList):
 
                     content[i] = ""
 
-        if value != "":
-            if found_line:
+        if i > len(content):
+            if test:
+                print(f"Warning: coun't find doc string for test {subtest}, field {field}")
+            else:
+                print(f"Warning: coun't find doc string for subtest {subtest}, field {field} on line {line}")
+
+            return
+
+        if found_line:
+            if value != "":
                 content[found_line] = f' * {field}: {value}\n'
-                return
+            else:
+                content[found_line] = ""
+            return
 
+        if value != "":
             if i > 2 and re.match(r'\s*\*\s*$', content[i - 1]):
                 i -= 1
 
@@ -261,6 +291,78 @@ class FillTests(TestList):
             for key, value in row.items():
                 self.tests[testname]["subtests"][subtest][key] = value
 
+    def simplify_doc_strings(self):
+        """
+        Group common properties at TEST level if all subtests have identical
+        values for such property.
+        """
+
+        print("Handling common information on test from subtests")
+
+        for testname in self.tests:
+            # Get common properties
+            common = {}
+            test_nr = self.tests[testname].get("Test")
+            for key, value in self.doc[test_nr].items():
+                # Ignore description when checking common fields
+                if key.lower() == "description":
+                    continue
+
+                if key in self.update_fields:
+                    common[key] = value
+
+            # Step 1: verify common items
+            first = True
+            for subtest in sorted(list(self.tests[testname]["subtests"].keys())):
+                if subtest.count("@") > 1:
+                    print(f"Warning: don't simplify dynamic subtest: {testname}{subtest}")
+                    continue
+
+                # Store common items
+                subtest_fields = sorted(self.tests[testname]["subtests"][subtest].keys())
+                for k in subtest_fields:
+                    # Ignore description when checking common fields
+                    if k.lower() == "description":
+                        continue
+
+                    # Ignore internal fields
+                    if k not in self.update_fields:
+                        continue
+
+                    val = self.tests[testname]["subtests"][subtest][k]
+                    val = val.strip()
+                    val = re.sub(r" +", " ", val)
+                    val = re.sub(r"([^\.])\s?\n\s*([A-Z])", r"\1.\n\2", val)
+                    self.tests[testname]["subtests"][subtest][k] = val
+
+                    if first and val != '':
+                        common[k] = val
+                    else:
+                        if k not in common:
+                            continue
+                        if common[k] != val:
+                            del common[k]
+
+                first = False
+
+                # Remove keys that aren't common to all subtests
+                for k in list(common.keys()):
+                    if k not in subtest_fields:
+                        del common[k]
+
+            # Step 2: drop common items from subtests
+            for subtest, fields in list(self.tests[testname]["subtests"].items()):
+                for k, val in list(fields.items()):
+                    if k not in common:
+                        continue
+
+                    if val == common[k]:
+                        self.tests[testname]["subtests"][subtest][k] = ""
+
+            # Step 3: store the modified common properties
+            for k, val in common.items():
+                self.doc[test_nr][k] = val
+
     def update_test_file(self, testname, args):
         """
         Update a C source file using the contents of self.tests as
@@ -293,6 +395,7 @@ class FillTests(TestList):
                 if doc_value == value:
                     continue
 
+                self.change_value(content, True, testname, 0, field, value)
 
             # Update subtest fields
             for subtest, subtest_content in sorted(self.tests[testname]["subtests"].items()):
@@ -300,7 +403,6 @@ class FillTests(TestList):
                     print(f"Warning: didn't find where {subtest} is documented.")
                     continue
 
-                line = subtest_content['line']
                 subtest_nr = subtest_content['subtest_nr']
 
                 if subtest_nr not in self.doc[test_nr]["subtest"]:
@@ -310,6 +412,7 @@ class FillTests(TestList):
                         print(f"Warning: test {testname}, subtest {subtest} is not documented.")
                     continue
 
+                line = self.doc[test_nr]["_subtest_line_"][subtest_nr]
                 doc_content = self.orig_doc[test_nr]["subtest"][subtest_nr]
 
                 fields = set(subtest_content.keys()) | set(doc_content.keys())
@@ -342,7 +445,7 @@ class FillTests(TestList):
                     # Just in case, handle continuation lines
                     value = re.sub(r"\n", "\n *   ", value)
 
-                    self.change_value(content, subtest, line, field, value)
+                    self.change_value(content, False, subtest, line, field, value)
 
                     # Update line numbers after insert
                     skip = True
@@ -391,7 +494,7 @@ def main():
     parser = argparse.ArgumentParser(description=__doc__)
     parser.add_argument("--config", required=True,
                         help="JSON file describing the test plan template")
-    parser.add_argument("--xls", required=True,
+    parser.add_argument("--xls",
                         help="Input XLS file.")
     parser.add_argument("--sheets", nargs="*",
                         help="Input only some specific sheets from the XLS file.")
@@ -399,13 +502,19 @@ def main():
                         help='Ignore fields that are updated via test lists')
     parser.add_argument("--store-json", action="store_true",
                         help="Generate JSON files with documentation. Useful for debugging purposes.")
+    parser.add_argument("--simplify", action="store_true",
+                        help="Run a simplify logic to cleanup TEST common fields.")
     parser.add_argument('-v', '--verbose', action='count', default=0)
 
     parse_args = parser.parse_args()
 
     fill_test = FillTests(parse_args.config, parse_args.verbose)
 
-    fill_test.parse_spreadsheet(parse_args.xls, parse_args.sheets)
+    if parse_args.xls:
+        fill_test.parse_spreadsheet(parse_args.xls, parse_args.sheets)
+
+    if parse_args.simplify:
+        fill_test.simplify_doc_strings()
 
     if "store_json" in parse_args:
         print("Generating fill_test.json debug file")
-- 
2.43.2



More information about the igt-dev mailing list