aboutsummaryrefslogtreecommitdiff
path: root/kotlin/common/testing/testing_rules.bzl
blob: 35359f701bbb6b4452eb913c7475de2fba9a106e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# Copyright 2022 Google LLC. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""kt_testing_rules"""

load("//:visibility.bzl", "RULES_KOTLIN")
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
load(":analysis.bzl", "kt_analysis")

visibility(RULES_KOTLIN)

# Mark targets that's aren't expected to build, but are needed for analysis test assertions.
_ONLY_FOR_ANALYSIS_TAGS = ["manual", "nobuilder", "notap"]

def _wrap_for_analysis(inner_rule):
    """Wrap an existing rule to make it easier to use in analysis tests.

    Args:
        inner_rule: [rule|macro]

    Returns:
        [macro] Calls inner_rule with appropate tags, returning the target name
    """

    def wrapper(name, tags = [], **kwargs):
        inner_rule(
            name = name,
            tags = tags + _ONLY_FOR_ANALYSIS_TAGS,
            **kwargs
        )
        return name

    return wrapper

_assert_failure_test = analysistest.make(
    impl = lambda ctx: _assert_failure_test_impl(ctx),
    expect_failure = True,
    attrs = dict(
        msg_contains = attr.string(mandatory = True),
    ),
)

def _assert_failure_test_impl(ctx):
    kt_analysis.check_endswith_test(ctx)

    env = analysistest.begin(ctx)
    asserts.expect_failure(env, ctx.attr.msg_contains)
    return analysistest.end(env)

_coverage_instrumentation_test = analysistest.make(
    impl = lambda ctx: _coverage_instrumentation_test_impl(ctx),
    attrs = dict(
        expected_instrumented_file_names = attr.string_list(),
    ),
    config_settings = {
        "//command_line_option:collect_code_coverage": "1",
        "//command_line_option:instrument_test_targets": "1",
        "//command_line_option:instrumentation_filter": "+",
    },
)

def _coverage_instrumentation_test_impl(ctx):
    env = analysistest.begin(ctx)
    target_under_test = analysistest.target_under_test(env)
    instrumented_files_info = target_under_test[InstrumentedFilesInfo]
    instrumented_files = instrumented_files_info.instrumented_files.to_list()
    asserts.equals(
        env,
        ctx.attr.expected_instrumented_file_names,
        [file.basename for file in instrumented_files],
    )
    return analysistest.end(env)

def _create_file(name, content = ""):
    """Declare a generated file with optional content.

    Args:
        name: [string] The relative file path
        content: [string]

    Returns:
        [File] The label of the file
    """

    if content.startswith("\n"):
        content = content[1:-1]

    native.genrule(
        name = "gen_" + name,
        outs = [name],
        cmd = """
cat > $@ <<EOF
%s
EOF
""" % content,
    )

    return name

_create_dir = rule(
    implementation = lambda ctx: _create_dir_impl(ctx),
    attrs = dict(
        subdir = attr.string(),
        srcs = attr.label_list(allow_files = True),
    ),
)

def _create_dir_impl(ctx):
    dir = ctx.actions.declare_directory(ctx.attr.name)

    command = "mkdir -p {0} " + ("&& cp {1} {0}" if ctx.files.srcs else "# {1}")
    ctx.actions.run_shell(
        command = command.format(
            dir.path + "/" + ctx.attr.subdir,
            " ".join([s.path for s in ctx.files.srcs]),
        ),
        inputs = ctx.files.srcs,
        outputs = [dir],
    )

    return [DefaultInfo(files = depset([dir]))]

kt_testing_rules = struct(
    # go/keep-sorted start
    ONLY_FOR_ANALYSIS_TAGS = _ONLY_FOR_ANALYSIS_TAGS,
    assert_failure_test = _assert_failure_test,
    coverage_instrumentation_test = _coverage_instrumentation_test,
    create_dir = _wrap_for_analysis(_create_dir),
    create_file = _create_file,
    wrap_for_analysis = _wrap_for_analysis,
    # go/keep-sorted end
)