blob: 5a488c78cf255ac36931cd334e02387574d2ddc3 [file] [log] [blame]
# Copyright 2016 The Bazel Authors. 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.
import os
import tempfile
import textwrap
import unittest
# internal imports
from google.protobuf import text_format
from skydoc import build_pb2
from skydoc import macro_extractor
class MacroExtractorTest(unittest.TestCase):
def check_protos(self, src, expected):
tf = tempfile.NamedTemporaryFile(mode='w+', delete=False)
tf.write(src)
tf.flush()
tf.close()
expected_proto = build_pb2.BuildLanguage()
text_format.Merge(expected, expected_proto)
extractor = macro_extractor.MacroDocExtractor()
extractor.parse_bzl(tf.name)
proto = extractor.proto()
os.remove(tf.name)
self.assertEqual(expected_proto, proto)
def test_multi_line_description(self):
src = textwrap.dedent("""\
def multiline(name, foo=False, visibility=None):
\"\"\"A rule with multiline documentation.
Some more documentation about this rule here.
Args:
name: A unique name for this rule.
foo: A test argument.
Documentation for foo continued here.
visibility: The visibility of this rule.
Documentation for visibility continued here.
\"\"\"
native.genrule(
name = name,
out = ["foo"],
cmd = "touch $@",
visibility = visibility,
)
""")
expected = textwrap.dedent("""\
rule {
name: "multiline"
documentation: "A rule with multiline documentation.\\n\\nSome more documentation about this rule here."
attribute {
name: "name"
type: UNKNOWN
mandatory: true
documentation: "A unique name for this rule."
}
attribute {
name: "foo"
type: BOOLEAN
mandatory: false
default: "False"
documentation: "A test argument.\\n\\nDocumentation for foo continued here."
}
attribute {
name: "visibility"
type: UNKNOWN
mandatory: false
documentation: "The visibility of this rule.\\n\\nDocumentation for visibility continued here."
}
type: MACRO
}
""")
self.check_protos(src, expected)
def test_kwargs(self):
src = textwrap.dedent("""\
def with_keyword_args(name, foo=False, **kwargs):
\"\"\"A rule with keyword args.
Args:
name: A unique name for this rule.
foo: A test argument.
**kwargs: Keyword arguments.
\"\"\"
native.genrule(
name = name,
out = ["foo"],
cmd = "touch $@",
visibility = visibility,
**kwargs
)
""")
expected = textwrap.dedent("""\
rule {
name: "with_keyword_args"
documentation: "A rule with keyword args."
attribute {
name: "name"
type: UNKNOWN
mandatory: true
documentation: "A unique name for this rule."
}
attribute {
name: "foo"
type: BOOLEAN
mandatory: false
default: "False"
documentation: "A test argument."
}
attribute {
name: "**kwargs"
type: UNKNOWN
mandatory: false
documentation: "Keyword arguments."
}
type: MACRO
}
""")
self.check_protos(src, expected)
def test_undocumented(self):
src = textwrap.dedent("""\
def undocumented(name, visibility=None):
native.genrule(
name = name,
out = ["foo"],
cmd = "touch $@",
visibility = visibility,
)
""")
expected = textwrap.dedent("""\
rule {
name: "undocumented"
attribute {
name: "name"
type: UNKNOWN
mandatory: true
}
attribute {
name: "visibility"
type: UNKNOWN
mandatory: false
}
type: MACRO
}
""")
self.check_protos(src, expected)
def test_private_macros_skipped(self):
src = textwrap.dedent("""\
def _private(name, visibility=None):
\"\"\"A private macro that should not appear in docs.
Args:
name: A unique name for this rule.
visibility: The visibility of this rule.
\"\"\"
native.genrule(
name = name,
out = ["foo"],
cmd = "touch $@",
visibility = visibility,
)
def public(name, visibility=None):
\"\"\"A public macro that should appear in docs.
Args:
name: A unique name for this rule.
visibility: The visibility of this rule.
\"\"\"
native.genrule(
name = name,
out = ["foo"],
cmd = "touch $@",
visibility = visibility,
)
""")
expected = textwrap.dedent("""\
rule {
name: "public"
documentation: "A public macro that should appear in docs."
attribute {
name: "name"
type: UNKNOWN
mandatory: true
documentation: "A unique name for this rule."
}
attribute {
name: "visibility"
type: UNKNOWN
mandatory: false
documentation: "The visibility of this rule."
}
type: MACRO
}
""")
self.check_protos(src, expected)
def test_rule_macro_mix(self):
src = textwrap.dedent("""\
def _impl(ctx):
return struct()
example_rule = rule(
implementation = _impl,
attrs = {
"arg_label": attr.label(),
"arg_string": attr.string(),
},
)
\"\"\"An example rule.
Args:
name: A unique name for this rule.
arg_label: A label argument.
arg_string: A string argument.
\"\"\"
def example_macro(name, foo, visibility=None):
\"\"\"An example macro.
Args:
name: A unique name for this rule.
foo: A test argument.
visibility: The visibility of this rule.
\"\"\"
native.genrule(
name = name,
out = ["foo"],
cmd = "touch $@",
visibility = visibility,
)
""")
expected = textwrap.dedent("""\
rule {
name: "example_macro"
documentation: "An example macro."
attribute {
name: "name"
type: UNKNOWN
mandatory: true
documentation: "A unique name for this rule."
}
attribute {
name: "foo"
type: UNKNOWN
mandatory: true
documentation: "A test argument."
}
attribute {
name: "visibility"
type: UNKNOWN
mandatory: false
documentation: "The visibility of this rule."
}
type: MACRO
}
""")
self.check_protos(src, expected)
def test_macro_with_example_doc(self):
src = textwrap.dedent("""\
def macro_with_example(name, foo, visibility=None):
\"\"\"Macro with examples.
Args:
name: A unique name for this rule.
foo: A test argument.
visibility: The visibility of this rule.
Example:
Here is an example of how to use this rule.
\"\"\"
native.genrule(
name = name,
out = ["foo"],
cmd = "touch $@",
visibility = visibility,
)
""")
expected = textwrap.dedent("""\
rule {
name: "macro_with_example"
documentation: "Macro with examples."
example_documentation: "Here is an example of how to use this rule."
attribute {
name: "name"
type: UNKNOWN
mandatory: true
documentation: "A unique name for this rule."
}
attribute {
name: "foo"
type: UNKNOWN
mandatory: true
documentation: "A test argument."
}
attribute {
name: "visibility"
type: UNKNOWN
mandatory: false
documentation: "The visibility of this rule."
}
type: MACRO
}
""")
self.check_protos(src, expected)
def test_macro_with_output_doc(self):
src = textwrap.dedent("""\
def macro_with_outputs(name, foo, visibility=None):
\"\"\"Macro with output documentation.
Outputs:
%{name}.jar: A Java archive.
%{name}_deploy.jar: A Java archive suitable for deployment.
Only built if explicitly requested.
Args:
name: A unique name for this rule.
foo: A test argument.
visibility: The visibility of this rule.
\"\"\"
native.genrule(
name = name,
out = ["foo"],
cmd = "touch $@",
visibility = visibility,
)
""")
expected = textwrap.dedent("""\
rule {
name: "macro_with_outputs"
documentation: "Macro with output documentation."
attribute {
name: "name"
type: UNKNOWN
mandatory: true
documentation: "A unique name for this rule."
}
attribute {
name: "foo"
type: UNKNOWN
mandatory: true
documentation: "A test argument."
}
attribute {
name: "visibility"
type: UNKNOWN
mandatory: false
documentation: "The visibility of this rule."
}
output {
template: "%{name}.jar"
documentation: "A Java archive."
}
output {
template: "%{name}_deploy.jar"
documentation: "A Java archive suitable for deployment.\\n\\nOnly built if explicitly requested."
}
type: MACRO
}
""")
self.check_protos(src, expected)
def test_file_doc_title_only(self):
src = textwrap.dedent("""\
\"\"\"Example rules\"\"\"
""")
tf = tempfile.NamedTemporaryFile(mode='w+', delete=False)
tf.write(src)
tf.flush()
tf.close()
extractor = macro_extractor.MacroDocExtractor()
extractor.parse_bzl(tf.name)
os.remove(tf.name)
self.assertEqual('Example rules', extractor.title)
self.assertEqual('', extractor.description)
def test_file_doc_title_description(self):
src = textwrap.dedent("""\
\"\"\"Example rules
This file contains example Bazel rules.
Documentation continued here.
\"\"\"
""")
tf = tempfile.NamedTemporaryFile(mode='w+', delete=False)
tf.write(src)
tf.flush()
tf.close()
extractor = macro_extractor.MacroDocExtractor()
extractor.parse_bzl(tf.name)
os.remove(tf.name)
self.assertEqual('Example rules', extractor.title)
self.assertEqual('This file contains example Bazel rules.'
'\n\nDocumentation continued here.',
extractor.description)
def test_file_doc_title_multiline(self):
src = textwrap.dedent("""\
\"\"\"Example rules
for Bazel
This file contains example Bazel rules.
Documentation continued here.
\"\"\"
""")
tf = tempfile.NamedTemporaryFile(mode='w+', delete=False)
tf.write(src)
tf.flush()
tf.close()
extractor = macro_extractor.MacroDocExtractor()
extractor.parse_bzl(tf.name)
os.remove(tf.name)
self.assertEqual('Example rules for Bazel', extractor.title)
self.assertEqual('This file contains example Bazel rules.'
'\n\nDocumentation continued here.',
extractor.description)
def test_loads_ignored(self):
src = textwrap.dedent("""\
load("//foo/bar:baz.bzl", "foo_library")
load("//foo/bar:baz.bzl", "foo_test", orig_foo_binary = "foo_binary")
""")
expected = ''
self.check_protos(src, expected)
if __name__ == '__main__':
unittest.main()