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

Kamil Konieczny kamil.konieczny at linux.intel.com
Wed Nov 29 15:46:33 UTC 2023


Hi Mauro,
On 2023-11-29 at 13:25:36 +0100, Mauro Carvalho Chehab wrote:
> 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>

Acked-by: Kamil Konieczny <kamil.konieczny at linux.intel.com>

> ---
>  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