• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Copyright (C) 2022 The Android Open Source Project
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7     http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14"""
15
16load("@bazel_skylib//lib:paths.bzl", "paths")
17load("@bazel_skylib//lib:sets.bzl", "sets")
18load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
19load(":cc_proto.bzl", "PROTO_GEN_NAME_SUFFIX", "cc_proto_library")
20
21PROTO_GEN = "external/protobuf/aprotoc"
22VIRTUAL_IMPORT = "_virtual_imports"
23RUNFILES = "_middlemen/external_Sprotobuf_Saprotoc-runfiles"
24
25GEN_SUFFIX = [
26    ".pb.h",
27    ".pb.cc",
28]
29
30def _get_search_paths(action):
31    cmd = action.argv
32    search_paths = sets.make()
33    cmd_len = len(cmd)
34    for i in range(cmd_len):
35        if cmd[i].startswith("-I"):
36            sets.insert(search_paths, cmd[i].lstrip("- I"))
37
38    return search_paths
39
40def _proto_code_gen_test_impl(ctx):
41    env = analysistest.begin(ctx)
42    target_under_test = analysistest.target_under_test(env)
43    actions = analysistest.target_actions(env)
44    package_root = ctx.label.package
45    local_file_output_path = paths.join(
46        package_root,
47        target_under_test.label.name,
48        package_root,
49    )
50
51    input_files = [
52        ctx.attr.local_file_path,
53        ctx.attr.external_file_path,
54        ctx.attr.deps_file_path,
55    ]
56
57    output_files = [
58        ctx.attr.local_file_path,
59        ctx.attr.external_file_path,
60    ]
61
62    asserts.true(
63        env,
64        len(actions) == 1,
65        "Proto gen action not found: %s" % actions,
66    )
67
68    action = actions[0]
69
70    asserts.set_equals(
71        env,
72        expected = sets.make(
73            [paths.join(package_root, file) for file in input_files] + [
74                PROTO_GEN,
75                RUNFILES,
76            ],
77        ),
78        actual = sets.make([
79            file.short_path
80            for file in action.inputs.to_list()
81        ]),
82    )
83
84    asserts.set_equals(
85        env,
86        expected = sets.make(
87            [
88                paths.join(
89                    local_file_output_path,
90                    paths.replace_extension(file, ext),
91                )
92                for ext in GEN_SUFFIX
93                for file in output_files
94            ],
95        ),
96        actual = sets.make([
97            file.short_path
98            for file in action.outputs.to_list()
99        ]),
100    )
101
102    search_paths = _get_search_paths(action)
103
104    asserts.equals(
105        env,
106        expected = sets.make(
107            ["."] +
108            [paths.join(package_root, f) + "=" + paths.join(package_root, f) for f in input_files],
109        ),
110        actual = search_paths,
111    )
112
113    return analysistest.end(env)
114
115proto_code_gen_test = analysistest.make(
116    _proto_code_gen_test_impl,
117    attrs = {
118        "local_file_path": attr.string(),
119        "deps_file_path": attr.string(),
120        "external_file_path": attr.string(),
121    },
122)
123
124def _test_proto_code_gen():
125    test_name = "proto_code_gen_test"
126    local_file_path = "local/proto_local.proto"
127    external_file_path = "external/proto_external.proto"
128    deps_file_path = "deps/proto_deps.proto"
129    external_proto_name = test_name + "_external_proto"
130    deps_proto_name = test_name + "_deps_proto"
131    local_proto_name = test_name + "_proto"
132    cc_name = test_name + "_cc_proto"
133
134    native.proto_library(
135        name = external_proto_name,
136        srcs = [external_file_path],
137        tags = ["manual"],
138    )
139
140    native.proto_library(
141        name = deps_proto_name,
142        srcs = [deps_file_path],
143        tags = ["manual"],
144    )
145
146    native.proto_library(
147        name = local_proto_name,
148        srcs = [local_file_path],
149        deps = [":" + deps_proto_name],
150        tags = ["manual"],
151    )
152
153    cc_proto_library(
154        name = cc_name,
155        deps = [
156            ":" + local_proto_name,
157            ":" + external_proto_name,
158        ],
159        tags = ["manual"],
160    )
161
162    proto_code_gen_test(
163        name = test_name,
164        target_under_test = cc_name + PROTO_GEN_NAME_SUFFIX,
165        local_file_path = local_file_path,
166        deps_file_path = deps_file_path,
167        external_file_path = external_file_path,
168    )
169
170    return test_name
171
172def _proto_strip_import_prefix_test_impl(ctx):
173    env = analysistest.begin(ctx)
174    target_under_test = analysistest.target_under_test(env)
175    actions = analysistest.target_actions(env)
176    package_root = ctx.label.package
177
178    # strip the proto file path, src/stripped/stripped.proto -> stripped/stripped.proto
179    stripped_file_name = paths.relativize(ctx.attr.stripped_file_name, ctx.attr.strip_import_prefix)
180    stripped_file_input_path = paths.join(
181        package_root,
182        VIRTUAL_IMPORT,
183        ctx.attr.stripped_proto_name,
184    )
185    stripped_file_input_full_path = paths.join(
186        stripped_file_input_path,
187        stripped_file_name,
188    )
189    unstripped_file_output_path = paths.join(
190        package_root,
191        target_under_test.label.name,
192        package_root,
193    )
194    stripped_file_output_path = paths.join(
195        unstripped_file_output_path,
196        VIRTUAL_IMPORT,
197        ctx.attr.stripped_proto_name,
198    )
199
200    asserts.true(
201        env,
202        len(actions) == 1,
203        "Proto gen action not found: %s" % actions,
204    )
205
206    action = actions[0]
207
208    asserts.set_equals(
209        env,
210        expected = sets.make(
211            [
212                paths.join(package_root, ctx.attr.unstripped_file_name),
213                stripped_file_input_full_path,
214                PROTO_GEN,
215                RUNFILES,
216            ],
217        ),
218        actual = sets.make([
219            file.short_path
220            for file in action.inputs.to_list()
221        ]),
222    )
223
224    asserts.set_equals(
225        env,
226        expected = sets.make(
227            [
228                paths.join(
229                    unstripped_file_output_path,
230                    paths.replace_extension(ctx.attr.unstripped_file_name, ext),
231                )
232                for ext in GEN_SUFFIX
233            ] +
234            [
235                paths.join(
236                    stripped_file_output_path,
237                    paths.replace_extension(stripped_file_name, ext),
238                )
239                for ext in GEN_SUFFIX
240            ],
241        ),
242        actual = sets.make([
243            file.short_path
244            for file in action.outputs.to_list()
245        ]),
246    )
247
248    search_paths = _get_search_paths(action)
249
250    asserts.equals(
251        env,
252        expected = sets.make([
253            ".",
254            paths.join(package_root, ctx.attr.unstripped_file_name) + "=" + paths.join(package_root, ctx.attr.unstripped_file_name),
255            stripped_file_input_full_path + "=" +
256            paths.join(
257                ctx.genfiles_dir.path,
258                stripped_file_input_full_path,
259            ),
260            paths.join(
261                ctx.genfiles_dir.path,
262                stripped_file_input_path,
263            ),
264        ]),
265        actual = search_paths,
266    )
267
268    return analysistest.end(env)
269
270proto_strip_import_prefix_test = analysistest.make(
271    _proto_strip_import_prefix_test_impl,
272    attrs = {
273        "stripped_proto_name": attr.string(),
274        "stripped_file_name": attr.string(),
275        "unstripped_file_name": attr.string(),
276        "strip_import_prefix": attr.string(),
277    },
278)
279
280def _test_proto_strip_import_prefix():
281    test_name = "proto_strip_import_prefix_test"
282    unstripped_proto_name = test_name + "_unstripped_proto"
283    stripped_proto_name = test_name + "_stripped_proto"
284    unstripped_file_name = "unstripped/unstripped.proto"
285    stripped_file_name = "src/stripped/stripped.proto"
286    cc_name = test_name + "_cc_proto"
287    strip_import_prefix = "src"
288
289    native.proto_library(
290        name = unstripped_proto_name,
291        srcs = [unstripped_file_name],
292        tags = ["manual"],
293    )
294
295    native.proto_library(
296        name = stripped_proto_name,
297        srcs = [stripped_file_name],
298        strip_import_prefix = strip_import_prefix,
299        tags = ["manual"],
300    )
301
302    cc_proto_library(
303        name = cc_name,
304        deps = [
305            ":" + stripped_proto_name,
306            ":" + unstripped_proto_name,
307        ],
308        tags = ["manual"],
309    )
310
311    proto_strip_import_prefix_test(
312        name = test_name,
313        target_under_test = cc_name + PROTO_GEN_NAME_SUFFIX,
314        stripped_proto_name = stripped_proto_name,
315        stripped_file_name = stripped_file_name,
316        unstripped_file_name = unstripped_file_name,
317        strip_import_prefix = strip_import_prefix,
318    )
319
320    return test_name
321
322def _proto_with_external_packages_test_impl(ctx):
323    env = analysistest.begin(ctx)
324    target_under_test = analysistest.target_under_test(env)
325    actions = analysistest.target_actions(env)
326    package_root = ctx.label.package
327    deps_file_path = ctx.attr.deps_file_path
328    external_file_path = ctx.attr.external_file_path
329    local_file_path = ctx.attr.local_file_path
330
331    asserts.true(
332        env,
333        len(actions) == 1,
334        "Proto gen action not found: %s" % actions,
335    )
336
337    action = actions[0]
338
339    asserts.set_equals(
340        env,
341        expected = sets.make(
342            [
343                paths.join(package_root, local_file_path),
344                deps_file_path,
345                external_file_path,
346                PROTO_GEN,
347                RUNFILES,
348            ],
349        ),
350        actual = sets.make([
351            file.short_path
352            for file in action.inputs.to_list()
353        ]),
354    )
355
356    asserts.set_equals(
357        env,
358        expected = sets.make(
359            [
360                paths.join(
361                    package_root,
362                    target_under_test.label.name,
363                    package_root,
364                    paths.replace_extension(local_file_path, ext),
365                )
366                for ext in GEN_SUFFIX
367            ] +
368            [
369                paths.join(
370                    package_root,
371                    target_under_test.label.name,
372                    paths.replace_extension(external_file_path, ext),
373                )
374                for ext in GEN_SUFFIX
375            ],
376        ),
377        actual = sets.make([
378            file.short_path
379            for file in action.outputs.to_list()
380        ]),
381    )
382
383    search_paths = _get_search_paths(action)
384
385    asserts.equals(
386        env,
387        expected = sets.make([
388            ".",
389            paths.join(package_root, local_file_path) + "=" + paths.join(package_root, local_file_path),
390            deps_file_path + "=" + deps_file_path,
391            external_file_path + "=" + external_file_path,
392        ]),
393        actual = search_paths,
394    )
395
396    return analysistest.end(env)
397
398proto_with_external_packages_test = analysistest.make(
399    _proto_with_external_packages_test_impl,
400    attrs = {
401        "local_file_path": attr.string(),
402        "deps_file_path": attr.string(),
403        "external_file_path": attr.string(),
404    },
405)
406
407def _test_proto_with_external_packages():
408    test_name = "proto_with_external_packages_test"
409    proto_name = test_name + "_proto"
410    cc_name = test_name + "_cc_proto"
411    local_file_path = "local/proto_local.proto"
412    deps_file_path = "build/bazel/examples/cc/proto/deps/src/enums/proto_deps.proto"
413    external_file_path = "build/bazel/examples/cc/proto/external/src/enums/proto_external.proto"
414
415    native.proto_library(
416        name = proto_name,
417        srcs = [local_file_path],
418        deps = ["//build/bazel/examples/cc/proto/deps:deps_proto"],
419        tags = ["manual"],
420    )
421
422    cc_proto_library(
423        name = cc_name,
424        deps = [
425            ":" + proto_name,
426            "//build/bazel/examples/cc/proto/external:external_proto",
427        ],
428        tags = ["manual"],
429    )
430
431    proto_with_external_packages_test(
432        name = test_name,
433        target_under_test = cc_name + PROTO_GEN_NAME_SUFFIX,
434        local_file_path = local_file_path,
435        deps_file_path = deps_file_path,
436        external_file_path = external_file_path,
437    )
438
439    return test_name
440
441def cc_proto_test_suite(name):
442    native.test_suite(
443        name = name,
444        tests = [
445            _test_proto_code_gen(),
446            _test_proto_strip_import_prefix(),
447            _test_proto_with_external_packages(),
448        ],
449    )
450