[igt-dev] [PATCH RFC 2/3] scripts/igt_doc.py: add a logic to generate Intel CI testlists

Mauro Carvalho Chehab mauro.chehab at linux.intel.com
Tue Nov 28 16:43:02 UTC 2023


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

Testlist for Intel CI requires parsing not only IGT testlists,
but also block and permit lists. This is currently handled
internally, but not from the documentation.

Now that we have everything set in place, add a method for
generating it at IGT. The logic there is somewhat generic,
but it expects some fields with a namespace as defined on
tests/intel/*.json files.

So, instead of placing at the generic code (test_list.py),
add them to igt_doc.py, where IGT-specific glue can easily
be added while keeping test_list.py generic enought to be
used on other projects.

Signed-off-by: Mauro Carvalho Chehab <mchehab at kernel.org>
---
 scripts/igt_doc.py   | 148 ++++++++++++++++++++++++++++++++++++++++++-
 scripts/test_list.py |   3 +
 2 files changed, 150 insertions(+), 1 deletion(-)

diff --git a/scripts/igt_doc.py b/scripts/igt_doc.py
index ab6179366831..a807d2bb373d 100755
--- a/scripts/igt_doc.py
+++ b/scripts/igt_doc.py
@@ -11,10 +11,150 @@
 """Maintain test plan and test implementation documentation on IGT."""
 
 import argparse
+import os
+import re
 import sys
 
 from test_list import TestList
 
+class IgtTestList(TestList):
+    """
+        This class implements testlist generation as expected by Intel CI.
+        It does that by handling test lists split by "Run type" and
+        using GPU (or configuration) specific fields, being "GPU" for a
+        permit list of tests, and "GPU excluded platform" for a block
+        list of tests.
+
+        The logic below has "priority zero" rules, which are:
+
+        - if the test is not on any block lists nor it contains
+          "GPU" or "GPU excluded platform", it won't be blocked;
+        - if the test is in "all" block list, it will be blocked for all
+          GPUs. Values from "GPU" and "GPU excluded platform" will be ignored.
+
+        If none of the above rules apply, it will handle GPU positive
+        and negative rules:
+
+        - if "GPU" field is present on such test, the default is
+          is to block the test (default_gpu_value = False). If not
+          present, the default is to not block (default_gpu_value = True).
+
+        Now, it will check for "GPU" and "GPU excluded platform":
+
+        - it sets the default according to default_gpu_value.
+
+        Then:
+
+        - if "GPU" exists, for each GPU listed on the list, it will
+          unblock the test;
+        - if "GPU excluded platform" exists, for each GPU listed on
+          the list, it will block the test.
+    """
+    def gen_intelci_testlist(self): #pylint: disable=R0912
+        """Return a list of gpu configs and testlists."""
+
+        subtest_dict = self.expand_dictionary(True)
+
+        # Create a tests_per_list dict
+        gpus = set()
+        tests_per_list = {}
+        split_regex = re.compile(r",\s*")
+
+        for subname, subtest in subtest_dict.items():
+            run_type = subtest.get("Run type", "other")
+
+            run_type_set = set(split_regex.split(run_type))
+            for run_type in run_type_set:
+                if run_type not in tests_per_list:
+                    tests_per_list[run_type] = {}
+
+                if subname not in tests_per_list[run_type]:
+                    tests_per_list[run_type][subname] = {}
+
+                if "GPU" in subtest:
+                    for gpu_list in split_regex.split(subtest["GPU"]):
+                        gpus.add(gpu_list)
+                        tests_per_list[run_type][subname][gpu_list] = True
+
+                if "GPU excluded platform" in subtest:
+                    for gpu_list in split_regex.split(subtest["GPU excluded platform"]):
+                        gpus.add(gpu_list)
+                        tests_per_list[run_type][subname][gpu_list] = False
+
+        # Handle block and permit lists
+
+        for run_type in tests_per_list.keys(): # pylint: disable=C0201,C0206
+            for subname, gpu in tests_per_list[run_type].items():
+
+                # Trivial case: fields not defined
+                if not gpus:
+                    tests_per_list[run_type][subname]["all"] = True
+                    continue
+
+                if not gpu:
+                    tests_per_list[run_type][subname] = {}
+                    for gpu in gpus:
+                        tests_per_list[run_type][subname][gpu] = True
+                    continue
+
+                default_gpu_value = True
+                for gpu, value in tests_per_list[run_type][subname].items():
+                    if value:
+                        default_gpu_value = False
+                        break
+                    if not gpu in tests_per_list[run_type][subname]:
+                        for gpu in gpus:
+                            tests_per_list[run_type][subname][gpu] = default_gpu_value
+
+                if "all" in tests_per_list[run_type][subname]:
+                    if not tests_per_list[run_type][subname]["all"]:
+                        for gpu in gpus:
+                            tests_per_list[run_type][subname][gpu] = False
+
+        return (gpus, tests_per_list)
+
+    def write_intelci_testlist(self, directory):
+        '''Create testlist directory (if needed) and files'''
+
+        if not os.path.exists(directory):
+            os.makedirs(directory)
+
+        (gpus, tests_per_list)  = self.gen_intelci_testlist()
+        testlists = {}
+
+        for run_type in tests_per_list.keys(): # pylint: disable=C0201,C0206
+            for subname, gpu_dict in tests_per_list[run_type].items():
+                for gpu, value in gpu_dict.items():
+                    run_name = re.sub(r"[\W_]+", "-", run_type)
+                    gpu = re.sub(r"[\W_]+", "-", gpu)
+
+                    if gpus:
+                        name = f"{run_name}_{gpu}".lower()
+                    else:
+                        name = run_name.lower()
+
+                    if not name.startswith(self.driver_name.lower()):
+                        name = f"{self.driver_name.lower()}_{name}"
+
+                    name = re.sub(r"_+", "_", name)
+
+                    if name not in testlists:
+                        testlists[name] = set()
+
+                    if value:
+                        testlists[name].add(subname)
+
+        for testlist, subtests in testlists.items():
+            if testlist == "":
+                testlist = "other"
+
+
+            fname = os.path.join(directory, testlist) + ".testlist"
+            with open(fname, 'w', encoding='utf8') as handler:
+                for sub in sorted(subtests):
+                    handler.write (f"{sub}\n")
+                print(f"{fname} created.")
+
 def main():
     """
     Main logic
@@ -48,12 +188,14 @@ def main():
                         default=igt_build_path)
     parser.add_argument("--gen-testlist",
                         help="Generate documentation at the GEN_TESTLIST directory, using SORT_FIELD to split the tests. Requires --sort-field.")
+    parser.add_argument("--intelci-testlist",
+                        help="Generate testlists for Intel CI integration at the INTELCI_TESTLIST directory.")
     parser.add_argument('--files', nargs='+',
                         help="File name(s) to be processed")
 
     parse_args = parser.parse_args()
 
-    tests = TestList(config_fname = parse_args.config,
+    tests = IgtTestList(config_fname = parse_args.config,
                         include_plan = parse_args.include_plan,
                         file_list = parse_args.files,
                         igt_build_path = parse_args.igt_build_path)
@@ -77,6 +219,10 @@ def main():
             sys.exit("Need a field to split the testlists")
         tests.gen_testlist(parse_args.gen_testlist, parse_args.sort_field)
 
+    if parse_args.intelci_testlist:
+        run = True
+        tests.write_intelci_testlist(parse_args.intelci_testlist)
+
     if parse_args.to_json:
         run = True
         tests.print_json(parse_args.to_json)
diff --git a/scripts/test_list.py b/scripts/test_list.py
index 3954e883ada3..36eea5fb1c76 100644
--- a/scripts/test_list.py
+++ b/scripts/test_list.py
@@ -263,6 +263,7 @@ class TestList:
         self.filters = {}
         self.subtest_separator = subtest_separator
         self.main_name = main_name
+        self.driver_name = ""
 
         self.internal_fields =  [ '_summary_', '_arg_', '_subtest_line_' ]
 
@@ -289,6 +290,8 @@ class TestList:
             cfg_path = "./"
             driver_name = main_name
 
+        self.driver_name = driver_name
+
         if sources_path:
             cfg_path = os.path.realpath(sources_path) + "/"
 
-- 
2.42.0



More information about the igt-dev mailing list