1# Copyright 2015-2017 the openage authors. See copying.md for legal info.
2
3"""
4Generates code for C++ testing, mostly the table to look up symbols from test
5names.
6"""
7
8import collections
9
10
11class Namespace:
12    """
13    Represents a C++ namespace, which contains other namespaces and functions.
14
15    gen_prototypes() generates the code for the namespace.
16    """
17    def __init__(self):
18        self.namespaces = collections.defaultdict(self.__class__)
19        self.functions = []
20
21    def add_functionname(self, path):
22        """
23        Adds a function to the namespace.
24
25        Path is the qualified function "path" (e.g., openage::test::foo)
26        has the path ["openage", "test", "foo"].
27
28        Descends recursively, creating subnamespaces as required.
29        """
30        if len(path) == 1:
31            self.functions.append(path[0])
32        else:
33            subnamespace = self.namespaces[path[0]]
34            subnamespace.add_functionname(path[1:])
35
36    def gen_prototypes(self):
37        """
38        Generates the actual C++ code for this namespace,
39        including all sub-namespaces and function prototypes.
40        """
41        for name in self.functions:
42            yield "void %s();\n" % name
43
44        for namespacename, namespace in sorted(self.namespaces.items()):
45            yield "namespace %s {\n" % namespacename
46            for line in namespace.gen_prototypes():
47                yield line
48            yield "} // %s\n\n" % namespacename
49
50    def get_functionnames(self):
51        """
52        Yields all function names in this namespace,
53        as well as all subnamespaces.
54        """
55        for name in self.functions:
56            yield name
57
58        for namespacename, namespace in sorted(self.namespaces.items()):
59            for name in namespace.get_functionnames():
60                yield namespacename + "::" + name
61
62
63def generate_testlist(projectdir):
64    """
65    Generates the test/demo method symbol lookup file from tests_cpp.
66
67    projectdir is a util.fslike.path.Path.
68    """
69    root_namespace = Namespace()
70
71    from ..testing.list_processor import list_targets_cpp
72
73    for testname, _, _, _ in list_targets_cpp():
74        root_namespace.add_functionname(testname.split('::'))
75
76    func_prototypes = list(root_namespace.gen_prototypes())
77
78    method_mappings = [
79        "{\"%s\", ::%s}" % (functionname, functionname)
80        for functionname in root_namespace.get_functionnames()]
81
82    tmpl_path = projectdir.joinpath("libopenage/testing/testlist.cpp.template")
83    with tmpl_path.open() as tmpl:
84        content = tmpl.read()
85
86    content = content.replace('FUNCTION_PROTOTYPES', "".join(func_prototypes))
87    content = content.replace('METHOD_MAPPINGS', ",\n\t".join(method_mappings))
88
89    gen_path = projectdir.joinpath("libopenage/testing/testlist.gen.cpp")
90    with gen_path.open("w") as gen:
91        gen.write(content)
92