1"""
2Copyright (C) 2023 The Android Open Source Project
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15"""
16
17load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
18load("//build/bazel/rules/cc:cc_prebuilt_library_static.bzl", "cc_prebuilt_library_static")
19load("//build/bazel/rules/test_common:paths.bzl", "get_output_and_package_dir_based_path")
20
21_fake_expected_lib = "{[()]}"
22
23def _cc_prebuilt_library_static_alwayslink_test_impl(ctx):
24    env = analysistest.begin(ctx)
25    target = analysistest.target_under_test(env)
26    expected_lib = ctx.attr.expected_lib
27    alwayslink = ctx.attr.alwayslink
28    cc_info = target[CcInfo]
29    linker_inputs = cc_info.linking_context.linker_inputs.to_list()
30    libs_to_link = []
31    for l in linker_inputs:
32        libs_to_link += l.libraries
33
34    has_lib = False
35    has_alwayslink = False
36    libs = {}
37    for lib_to_link in libs_to_link:
38        lib = lib_to_link.static_library.basename
39        libs[lib_to_link.static_library] = lib_to_link.alwayslink
40        if lib == expected_lib:
41            has_lib = True
42            has_alwayslink = lib_to_link.alwayslink
43        if has_alwayslink:
44            break
45    asserts.true(
46        env,
47        has_lib,
48        "\nExpected to find the static library `%s` in the linker_input:\n\t%s" % (expected_lib, str(libs)),
49    )
50    asserts.equals(
51        env,
52        has_alwayslink,
53        alwayslink,
54        "\nExpected to find the static library `%s` unconditionally in the linker_input, with alwayslink set to %s:\n\t%s" % (expected_lib, alwayslink, str(libs)),
55    )
56
57    return analysistest.end(env)
58
59_cc_prebuilt_library_static_alwayslink_test = analysistest.make(
60    _cc_prebuilt_library_static_alwayslink_test_impl,
61    attrs = {
62        "expected_lib": attr.string(),
63        "alwayslink": attr.bool(),
64    },
65)
66
67def _cc_prebuilt_library_static_alwayslink_lib(alwayslink):
68    name = "_cc_prebuilt_library_static_alwayslink_lib_" + str(alwayslink)
69    test_name = name + "_test"
70    lib = "libfoo.a"
71
72    cc_prebuilt_library_static(
73        name = name,
74        static_library = lib,
75        alwayslink = alwayslink,
76        tags = ["manual"],
77    )
78    _cc_prebuilt_library_static_alwayslink_test(
79        name = test_name,
80        target_under_test = name,
81        expected_lib = lib,
82        alwayslink = alwayslink,
83    )
84
85    return test_name
86
87def _cc_prebuilt_library_static_test_impl(ctx):
88    env = analysistest.begin(ctx)
89    target = analysistest.target_under_test(env)
90    expected_lib = ctx.attr.expected_lib
91    cc_info = target[CcInfo]
92    compilation_context = cc_info.compilation_context
93    linker_inputs = cc_info.linking_context.linker_inputs.to_list()
94    libs_to_link = []
95    for lib in linker_inputs:
96        libs_to_link += lib.libraries
97
98    if expected_lib == _fake_expected_lib:
99        asserts.true(
100            env,
101            len(libs_to_link) == 0,
102            "\nExpected the static library to be empty, but instead got:\n\t%s\n" % str(libs_to_link),
103        )
104    else:
105        asserts.true(
106            env,
107            expected_lib in [lib.static_library.basename for lib in libs_to_link],
108            "\nExpected the target to include the static library %s; but instead got:\n\t%s\n" % (expected_lib, libs_to_link),
109        )
110
111    # Checking for the expected {,system_}includes
112    assert_template = "\nExpected the %s for " + expected_lib + " to be:\n\t%s\n, but instead got:\n\t%s\n"
113    expand_paths = lambda paths: [get_output_and_package_dir_based_path(env, p) for p in paths]
114    expected_includes = expand_paths(ctx.attr.expected_includes)
115    expected_system_includes = expand_paths(ctx.attr.expected_system_includes)
116
117    includes = compilation_context.includes.to_list()
118    for include in expected_includes:
119        asserts.true(env, include in includes, assert_template % ("includes", expected_includes, includes))
120
121    system_includes = compilation_context.system_includes.to_list()
122    for include in expected_system_includes:
123        asserts.true(env, include in system_includes, assert_template % ("system_includes", expected_system_includes, system_includes))
124
125    return analysistest.end(env)
126
127_cc_prebuilt_library_static_test = analysistest.make(
128    _cc_prebuilt_library_static_test_impl,
129    attrs = dict(
130        expected_lib = attr.string(default = _fake_expected_lib),
131        expected_includes = attr.string_list(),
132        expected_system_includes = attr.string_list(),
133    ),
134)
135
136def _cc_prebuilt_library_static_simple():
137    name = "_cc_prebuilt_library_static_simple"
138    test_name = name + "_test"
139    lib = "libfoo.a"
140
141    cc_prebuilt_library_static(
142        name = name,
143        static_library = lib,
144        tags = ["manual"],
145    )
146    _cc_prebuilt_library_static_test(
147        name = test_name,
148        target_under_test = name,
149        expected_lib = lib,
150    )
151
152    return test_name
153
154def _cc_prebuilt_library_static_None():
155    name = "_cc_prebuilt_library_static_None"
156    test_name = name + "_test"
157    lib = None
158
159    cc_prebuilt_library_static(
160        name = name,
161        static_library = lib,
162        tags = ["manual"],
163    )
164    _cc_prebuilt_library_static_test(
165        name = test_name,
166        target_under_test = name,
167        expected_lib = lib,  # We expect the default of _fake_expected_lib
168    )
169
170    return test_name
171
172def _cc_prebuilt_library_static_has_all_includes():
173    name = "_cc_prebuilt_library_static_has_all_includes"
174    test_name = name + "_test"
175    lib = "libfoo.a"
176    includes = ["includes"]
177    system_includes = ["system_includes"]
178
179    cc_prebuilt_library_static(
180        name = name,
181        static_library = lib,
182        export_includes = includes,
183        export_system_includes = system_includes,
184        tags = ["manual"],
185    )
186    _cc_prebuilt_library_static_test(
187        name = test_name,
188        target_under_test = name,
189        expected_lib = lib,
190        expected_includes = includes,
191        expected_system_includes = system_includes,
192    )
193
194    return test_name
195
196# TODO: Test that is alwayslink = alse
197
198def cc_prebuilt_library_static_test_suite(name):
199    native.test_suite(
200        name = name,
201        tests = [
202            _cc_prebuilt_library_static_simple(),
203            _cc_prebuilt_library_static_None(),
204            _cc_prebuilt_library_static_alwayslink_lib(True),
205            _cc_prebuilt_library_static_alwayslink_lib(False),
206            _cc_prebuilt_library_static_has_all_includes(),
207        ],
208    )
209