1# Copyright (C) 2022 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15load("@bazel_skylib//lib:paths.bzl", "paths")
16load("@bazel_skylib//lib:sets.bzl", "sets")
17load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
18
19def _assert_flags_present_in_action(env, action, expected_flags):
20    if action.argv == None:
21        asserts.true(
22            env,
23            False,
24            "expected %s action to have arguments, but argv was None" % (
25                action.mnemonic,
26            ),
27        )
28        return
29    for flag in expected_flags:
30        asserts.true(
31            env,
32            flag in action.argv,
33            "%s action did not contain flag %s; argv: %s" % (
34                action.mnemonic,
35                flag,
36                action.argv,
37            ),
38        )
39
40# Checks for the presence of a set of given flags in a set of given actions
41# non-exclusively. In other words, it confirms that the specified actions
42# contain the given flags, but does not confirm that other actions do not
43# contain them.
44def _action_flags_present_for_mnemonic_nonexclusive_test_impl(ctx):
45    env = analysistest.begin(ctx)
46
47    for action in analysistest.target_actions(env):
48        if action.mnemonic in ctx.attr.mnemonics:
49            _assert_flags_present_in_action(
50                env,
51                action,
52                ctx.attr.expected_flags,
53            )
54
55    return analysistest.end(env)
56
57action_flags_present_for_mnemonic_nonexclusive_test = analysistest.make(
58    _action_flags_present_for_mnemonic_nonexclusive_test_impl,
59    attrs = {
60        "mnemonics": attr.string_list(
61            doc = """
62            Actions with these mnemonics will be expected to have the flags
63            specified in expected_flags
64            """,
65        ),
66        "expected_flags": attr.string_list(doc = "The flags to be checked for"),
67    },
68)
69
70# Checks for the presence of a set of given flags in a set of given actions
71# exclusively. In other words, it confirms that *only* the specified actions
72# contain the specified flags.
73def _action_flags_present_only_for_mnemonic_test_impl(ctx):
74    env = analysistest.begin(ctx)
75
76    actions = analysistest.target_actions(env)
77    found_at_least_one_action = False
78    for action in actions:
79        if action.mnemonic in ctx.attr.mnemonics:
80            found_at_least_one_action = True
81            _assert_flags_present_in_action(
82                env,
83                action,
84                ctx.attr.expected_flags,
85            )
86        elif action.argv != None:
87            for flag in ctx.attr.expected_flags:
88                asserts.false(
89                    env,
90                    flag in action.argv,
91                    "%s action unexpectedly contained flag %s; argv: %s" % (
92                        action.mnemonic,
93                        flag,
94                        action.argv,
95                    ),
96                )
97    asserts.true(
98        env,
99        found_at_least_one_action,
100        "did not find any actions with mnemonic %s, found: %s" % (
101            ctx.attr.mnemonics,
102            [a.mnemonic for a in actions],
103        ),
104    )
105    return analysistest.end(env)
106
107def action_flags_present_only_for_mnemonic_test_with_config_settings(config_settings = {}):
108    return analysistest.make(
109        _action_flags_present_only_for_mnemonic_test_impl,
110        attrs = {
111            "mnemonics": attr.string_list(
112                doc = """
113                Actions with these mnemonics will be expected to have the flags
114                specified in expected_flags
115                """,
116            ),
117            "expected_flags": attr.string_list(doc = "The flags to be checked for"),
118        },
119        config_settings = config_settings,
120    )
121
122action_flags_present_only_for_mnemonic_test = action_flags_present_only_for_mnemonic_test_with_config_settings()
123
124action_flags_present_only_for_mnemonic_aosp_arm64_test = action_flags_present_only_for_mnemonic_test_with_config_settings({
125    "//command_line_option:platforms": "@//build/bazel/tests/products:aosp_arm64_for_testing",
126})
127
128action_flags_present_only_for_mnemonic_aosp_arm64_host_test = action_flags_present_only_for_mnemonic_test_with_config_settings({
129    "//command_line_option:platforms": "@//build/bazel/tests/products:aosp_arm64_for_testing_linux_x86_64",
130})
131
132# Checks that a given set of flags are NOT present in a given set of actions.
133# Unlike the above test, this test does NOT confirm the absence of flags
134# *exclusively*. It does not confirm that the flags are present in actions
135# other than those specified
136def _action_flags_absent_for_mnemonic_test_impl(ctx):
137    env = analysistest.begin(ctx)
138
139    actions = analysistest.target_actions(env)
140    for action in actions:
141        if action.mnemonic in ctx.attr.mnemonics and action.argv != None:
142            for flag in ctx.attr.expected_absent_flags:
143                asserts.false(
144                    env,
145                    flag in action.argv,
146                    "%s action unexpectedly contained flag %s; argv: %s" % (
147                        action.mnemonic,
148                        flag,
149                        action.argv,
150                    ),
151                )
152
153    return analysistest.end(env)
154
155def action_flags_absent_for_mnemonic_test_with_config_settings(config_settings = {}):
156    return analysistest.make(
157        _action_flags_absent_for_mnemonic_test_impl,
158        attrs = {
159            "mnemonics": attr.string_list(
160                doc = """
161                Actions with these mnemonics will be expected NOT to have the flags
162                specificed in expected_flags
163                """,
164            ),
165            "expected_absent_flags": attr.string_list(
166                doc = """
167                The flags to be confirmed are absent from the actions in mnemonics
168                """,
169            ),
170        },
171        config_settings = config_settings,
172    )
173
174action_flags_absent_for_mnemonic_test = action_flags_absent_for_mnemonic_test_with_config_settings()
175
176action_flags_absent_for_mnemonic_aosp_arm64_test = action_flags_absent_for_mnemonic_test_with_config_settings({
177    "//command_line_option:platforms": "@//build/bazel/tests/products:aosp_arm64_for_testing",
178})
179
180action_flags_absent_for_mnemonic_aosp_arm64_host_test = action_flags_absent_for_mnemonic_test_with_config_settings({
181    "//command_line_option:platforms": "@//build/bazel/tests/products:aosp_arm64_for_testing_linux_x86_64",
182})
183
184def _input_output_verification_test_impl(ctx):
185    env = analysistest.begin(ctx)
186    actions = [a for a in analysistest.target_actions(env) if a.mnemonic == ctx.attr.mnemonic]
187    asserts.true(
188        env,
189        len(actions) == 1,
190        "Action not found: %s" % actions,
191    )
192    package_root = ctx.label.package
193
194    input_files = [paths.join(package_root, a) for a in ctx.attr.input_files]
195    output_files = [paths.join(package_root, a) for a in ctx.attr.output_files]
196
197    action = actions[0]
198
199    if len(input_files) > 0:
200        expected = sets.make(
201            input_files,
202        )
203        actual = sets.make([
204            file.short_path
205            for file in action.inputs.to_list()
206        ])
207        asserts.true(
208            env,
209            sets.is_subset(expected, actual),
210            "Not all input files are present %s %s" % (expected, actual),
211        )
212
213    if len(output_files) > 0:
214        expected = sets.make(
215            output_files,
216        )
217        actual = sets.make([
218            file.short_path
219            for file in action.outputs.to_list()
220        ])
221        asserts.true(
222            env,
223            sets.is_equal(expected, actual),
224            "Not all output files are present %s %s" % (expected, actual),
225        )
226
227    return analysistest.end(env)
228
229input_output_verification_test = analysistest.make(
230    _input_output_verification_test_impl,
231    attrs = {
232        "mnemonic": attr.string(),
233        "input_files": attr.string_list(),
234        "output_files": attr.string_list(),
235    },
236)
237