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

Mauro Carvalho Chehab mauro.chehab at linux.intel.com
Wed Nov 29 12:25:36 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 | 192 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 190 insertions(+), 2 deletions(-)

diff --git a/scripts/igt_doc.py b/scripts/igt_doc.py
index ab6179366831..0ec6e3e5ec97 100755
--- a/scripts/igt_doc.py
+++ b/scripts/igt_doc.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python3
-# pylint: disable=C0301
+# pylint: disable=C0301,C0201,C0206
 # SPDX-License-Identifier: (GPL-2.0 OR MIT)
 
 ## Copyright (C) 2023    Intel Corporation                 ##
@@ -11,10 +11,192 @@
 """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_types = subtest.get("Run type", "other")
+            run_type_set = set(split_regex.split(run_types))
+
+            run_type_set = set()
+            for run_type in set(split_regex.split(run_types)):
+                run_type = run_type.lower()
+
+                if run_type.startswith(tuple(self.drivers)):
+                    run_type_set.add(run_type)
+                else:
+                    for driver in self.drivers:
+                        run_type = f"{driver.lower()}_{run_type}"
+                        run_type_set.add(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():
+            for subname, gpu in tests_per_list[run_type].items():
+
+                # Trivial case: fields not defined
+                if not gpus:
+                    tests_per_list[run_type][subname]["default"] = 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
+
+                if default_gpu_value:
+                    tests_per_list[run_type][subname]["default"] = True
+
+        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():
+            for subname, gpu_dict in tests_per_list[run_type].items():
+                for gpu, value in gpu_dict.items():
+                    if gpu == "all":
+                        continue
+
+                    gpu = re.sub(r"[\W_]+", "-", gpu).lower()
+                    name = re.sub(r"[\W_]+", "-", run_type).lower()
+                    name = re.sub(r"_+", "_", name)
+
+                    drivers = set(self.drivers)
+
+                    for driver in self.drivers:
+                        driver = driver.lower()
+
+                        if name.startswith(driver):
+                            all_drivers = False
+                            name = re.sub(r"^" + driver + r"[\W_]*", "", name)
+                            drivers = set([driver])
+                            break
+
+                    for driver in drivers:
+                        if driver not in testlists:
+                            testlists[driver] = {}
+
+                        if gpu not in testlists[driver]:
+                            testlists[driver][gpu] = {}
+
+                        if name not in testlists[driver][gpu]:
+                            testlists[driver][gpu][name] = set()
+
+                        testlists[driver][gpu][name].add(subname)
+
+        for driver in testlists.keys():
+            driver_path = os.path.join(directory, driver)
+            try:
+                os.makedirs(driver_path)
+            except FileExistsError:
+                pass
+
+            for gpu, names in testlists[driver].items():
+                dname = os.path.join(driver_path, gpu)
+                try:
+                    os.makedirs(dname)
+                except FileExistsError:
+                    pass
+
+                for testlist, subtests in names.items():
+                    if testlist == "":
+                        testlist = "other"
+
+                    if not subtests:
+                        print(f"Warning: empty testlist: {testlist}")
+                        continue
+
+                    fname = os.path.join(dname, 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 +230,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 +261,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)
-- 
2.42.0



More information about the igt-dev mailing list