Update release jars and introduce e2e tests (#230)

]
diff --git a/WORKSPACE b/WORKSPACE
index 648965e..a1b0395 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -33,7 +33,7 @@
 # Needed for generating the Stardoc release binary.
 git_repository(
     name = "io_bazel",
-    commit = "858412781d76b7d27c39dc58b7f24cf645bdf035",  # Aug 7, 2019
+    commit = "4ba404f7ed0473df3f0effa016c107ef677464f6",  # Aug 23, 2019
     remote = "https://github.com/bazelbuild/bazel.git",
 )
 
diff --git a/stardoc/renderer_binary.jar b/stardoc/renderer_binary.jar
index 40ecbe3..448a908 100755
--- a/stardoc/renderer_binary.jar
+++ b/stardoc/renderer_binary.jar
Binary files differ
diff --git a/stardoc/stardoc_binary.jar b/stardoc/stardoc_binary.jar
index 44d8148..6a042cf 100755
--- a/stardoc/stardoc_binary.jar
+++ b/stardoc/stardoc_binary.jar
Binary files differ
diff --git a/test/BUILD b/test/BUILD
index 7102640..c76f0ba 100644
--- a/test/BUILD
+++ b/test/BUILD
@@ -1,3 +1,5 @@
+load(":stardoc_test.bzl", "stardoc_test")
+
 licenses(["notice"])  # Apache 2.0
 
 sh_test(
@@ -12,3 +14,235 @@
         "//stardoc:stardoc_doc.md",
     ],
 )
+
+stardoc_test(
+    name = "input_template_test",
+    aspect_template = "testdata/input_template_test/aspect.vm",
+    func_template = "testdata/input_template_test/func.vm",
+    golden_file = "testdata/input_template_test/golden.txt",
+    header_template = "testdata/input_template_test/header.vm",
+    input_file = "testdata/input_template_test/input.bzl",
+    provider_template = "testdata/input_template_test/provider.vm",
+    rule_template = "testdata/input_template_test/rule.vm",
+)
+
+stardoc_test(
+    name = "angle_bracket_test",
+    golden_file = "testdata/angle_bracket_test/golden.txt",
+    input_file = "testdata/angle_bracket_test/input.bzl",
+)
+
+stardoc_test(
+    name = "proto_format_test",
+    format = "proto",
+    golden_file = "testdata/proto_format_test/golden.raw",
+    input_file = "testdata/proto_format_test/input.bzl",
+)
+
+stardoc_test(
+    name = "cc_api_test",
+    golden_file = "testdata/cc_api_test/golden.txt",
+    input_file = "testdata/cc_api_test/input.bzl",
+)
+
+stardoc_test(
+    name = "simple_test",
+    golden_file = "testdata/simple_test/golden.txt",
+    input_file = "testdata/simple_test/input.bzl",
+    symbol_names = ["my_rule"],
+)
+
+stardoc_test(
+    name = "repo_rule_test",
+    golden_file = "testdata/repo_rules_test/golden.txt",
+    input_file = "testdata/repo_rules_test/input.bzl",
+)
+
+stardoc_test(
+    name = "unknown_name",
+    golden_file = "testdata/unknown_name_test/golden.txt",
+    input_file = "testdata/unknown_name_test/input.bzl",
+)
+
+stardoc_test(
+    name = "multiple_rules_test",
+    golden_file = "testdata/multiple_rules_test/golden.txt",
+    input_file = "testdata/multiple_rules_test/input.bzl",
+)
+
+stardoc_test(
+    name = "android_basic_test",
+    golden_file = "testdata/android_basic_test/golden.txt",
+    input_file = "testdata/android_basic_test/input.bzl",
+    semantic_flags = [
+        "--experimental_google_legacy_api",
+    ],
+    symbol_names = ["android_related_rule"],
+)
+
+stardoc_test(
+    name = "apple_basic_test",
+    golden_file = "testdata/apple_basic_test/golden.txt",
+    input_file = "testdata/apple_basic_test/input.bzl",
+    symbol_names = ["apple_related_rule"],
+)
+
+stardoc_test(
+    name = "cpp_basic_test",
+    golden_file = "testdata/cpp_basic_test/golden.txt",
+    input_file = "testdata/cpp_basic_test/input.bzl",
+    symbol_names = ["cpp_related_rule"],
+)
+
+stardoc_test(
+    name = "java_basic_test",
+    golden_file = "testdata/java_basic_test/golden.txt",
+    input_file = "testdata/java_basic_test/input.bzl",
+    symbol_names = ["java_related_rule"],
+)
+
+stardoc_test(
+    name = "multiple_files_test",
+    golden_file = "testdata/multiple_files_test/golden.txt",
+    input_file = "testdata/multiple_files_test/input.bzl",
+    deps = [
+        "testdata/multiple_files_test/dep.bzl",
+        "testdata/multiple_files_test/inner_dep.bzl",
+    ],
+)
+
+stardoc_test(
+    name = "same_level_file_test",
+    golden_file = "//test/testdata/same_level_file_test:golden.txt",
+    input_file = "//test/testdata/same_level_file_test:input.bzl",
+    symbol_names = ["my_rule"],
+    deps = [
+        "//test/testdata/same_level_file_test:dep.bzl",
+    ],
+)
+
+stardoc_test(
+    name = "misc_apis_test",
+    golden_file = "testdata/misc_apis_test/golden.txt",
+    input_file = "testdata/misc_apis_test/input.bzl",
+)
+
+stardoc_test(
+    name = "attribute_types_test",
+    golden_file = "testdata/attribute_types_test/golden.txt",
+    input_file = "testdata/attribute_types_test/input.bzl",
+    symbol_names = ["my_rule"],
+)
+
+stardoc_test(
+    name = "filter_rules_test",
+    golden_file = "testdata/filter_rules_test/golden.txt",
+    input_file = "testdata/filter_rules_test/input.bzl",
+    symbol_names = [
+        "my_rule",
+        "whitelisted_dep_rule",
+    ],
+    deps = [
+        "testdata/filter_rules_test/dep.bzl",
+    ],
+)
+
+stardoc_test(
+    name = "provider_basic_test",
+    golden_file = "testdata/provider_basic_test/golden.txt",
+    input_file = "testdata/provider_basic_test/input.bzl",
+)
+
+stardoc_test(
+    name = "function_basic_test",
+    golden_file = "testdata/function_basic_test/golden.txt",
+    input_file = "testdata/function_basic_test/input.bzl",
+)
+
+stardoc_test(
+    name = "namespace_test",
+    golden_file = "testdata/namespace_test/golden.txt",
+    input_file = "testdata/namespace_test/input.bzl",
+)
+
+stardoc_test(
+    name = "namespace_test_with_whitelist",
+    golden_file = "testdata/namespace_test/golden.txt",
+    input_file = "testdata/namespace_test/input.bzl",
+    symbol_names = [
+        "my_namespace",
+    ],
+)
+
+stardoc_test(
+    name = "multi_level_namespace_test",
+    golden_file = "testdata/multi_level_namespace_test/golden.txt",
+    input_file = "testdata/multi_level_namespace_test/input.bzl",
+)
+
+stardoc_test(
+    name = "multi_level_namespace_test_with_whitelist",
+    golden_file = "testdata/multi_level_namespace_test_with_whitelist/golden.txt",
+    input_file = "testdata/multi_level_namespace_test_with_whitelist/input.bzl",
+    symbol_names = [
+        "my_namespace",
+        "other_namespace.foo.nothing",
+    ],
+)
+
+stardoc_test(
+    name = "macro_kwargs_test",
+    golden_file = "testdata/macro_kwargs_test/golden.txt",
+    input_file = "testdata/macro_kwargs_test/input.bzl",
+)
+
+stardoc_test(
+    name = "py_rule_test",
+    golden_file = "testdata/py_rule_test/golden.txt",
+    input_file = "testdata/py_rule_test/input.bzl",
+    symbol_names = ["py_related_rule"],
+)
+
+stardoc_test(
+    name = "struct_default_value_test",
+    golden_file = "testdata/struct_default_value_test/golden.txt",
+    input_file = "testdata/struct_default_value_test/input.bzl",
+)
+
+stardoc_test(
+    name = "aspect_test",
+    golden_file = "testdata/aspect_test/golden.txt",
+    input_file = "testdata/aspect_test/input.bzl",
+)
+
+stardoc_test(
+    name = "providers_for_attributes_test",
+    golden_file = "testdata/providers_for_attributes_test/golden.txt",
+    input_file = "testdata/providers_for_attributes_test/input.bzl",
+    deps = [
+        "testdata/providers_for_attributes_test/dep.bzl",
+    ],
+)
+
+stardoc_test(
+    name = "pure_markdown_template_test",
+    golden_file = "testdata/pure_markdown_template_test/golden.txt",
+    input_file = "testdata/pure_markdown_template_test/input.bzl",
+    test = "pure_markdown",
+)
+
+genrule(
+    name = "generate_bzl_test_dep",
+    srcs = ["testdata/generated_bzl_test/dep.bzl.tpl"],
+    outs = ["testdata/generated_bzl_test/dep.bzl"],
+    cmd = "cp $< $@",
+)
+
+stardoc_test(
+    name = "generated_bzl_test",
+    golden_file = "testdata/generated_bzl_test/golden.txt",
+    input_file = "testdata/generated_bzl_test/input.bzl",
+    deps = [
+        "testdata/generated_bzl_test/dep.bzl",
+    ],
+)
diff --git a/test/stardoc_test.bzl b/test/stardoc_test.bzl
new file mode 100644
index 0000000..5cfda3b
--- /dev/null
+++ b/test/stardoc_test.bzl
@@ -0,0 +1,121 @@
+# Copyright 2019 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.
+"""Convenience macro for stardoc e2e tests."""
+
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+load("//stardoc:pure_markdown_stardoc.bzl", "pure_markdown_stardoc")
+load("//stardoc:stardoc.bzl", "stardoc")
+
+def stardoc_test(
+        name,
+        input_file,
+        golden_file,
+        deps = [],
+        test = "default",
+        **kwargs):
+    """Convenience macro for stardoc e2e test suites.
+
+    Each invocation creates four targets:
+
+    1. A sh_test target which verifies that stardoc-built-from-source, when run on an input file,
+       creates output matching the contents of a golden file, named "{name}_e2e_test".
+    2. A `stardoc` target which will generate a new golden file given an input file
+       with stardoc-built-from-source. This target should be used to regenerate
+       the golden file when updating stardoc, named "regenerate_{name}_golden".
+    3 & 4. Targets identical to (1) and (2) except they use the prebuilt-stardoc jar, and
+       are named "{name}_e2e_jar_test" and "regenerate_with_jar_{name}_golden".
+    5. A bzl_library target for convenient wrapping of input bzl files, named "{name}_lib".
+
+    Args:
+      name: A unique name to qualify the created targets.
+      input_file: The label string of the Starlark input file for which documentation is generated
+          in this test.
+      golden_file: The label string of the golden file containing the documentation when stardoc
+          is run on the input file.
+      deps: A list of label strings of starlark file dependencies of the input_file.
+      test: The type of test (default or pure_markdown).
+      **kwargs: A dictionary of input template names mapped to template file path for which documentation is generated.
+      """
+
+    bzl_library(
+        name = "%s_lib" % name,
+        srcs = [input_file],
+        deps = deps,
+    )
+    _create_test_targets(test_name = "%s_e2e_test" % name,
+        genrule_name = "regenerate_%s_golden" % name,
+        lib_name = "%s_lib" % name,
+        input_file = input_file,
+        golden_file = golden_file,
+        stardoc_bin = "@io_bazel//src/main/java/com/google/devtools/build/skydoc",
+        renderer_bin = "@io_bazel//src/main/java/com/google/devtools/build/skydoc/renderer",
+        test = test,
+        **kwargs)
+    _create_test_targets(test_name = "%s_e2e_jar_test" % name,
+        genrule_name = "regenerate_with_jar_%s_golden" % name,
+        lib_name = "%s_lib" % name,
+        input_file = input_file,
+        golden_file = golden_file,
+        stardoc_bin = "@io_bazel//src/main/java/com/google/devtools/build/skydoc",
+        renderer_bin = "@io_bazel//src/main/java/com/google/devtools/build/skydoc/renderer",
+        test = test,
+        **kwargs)
+
+def _create_test_targets(test_name,
+                         genrule_name,
+                         lib_name,
+                         input_file,
+                         golden_file,
+                         stardoc_bin,
+                         renderer_bin,
+                         test,
+                         **kwargs):
+    actual_generated_doc = "%s.out" % genrule_name
+
+    native.sh_test(
+        name = test_name,
+        srcs = ["diff_test_runner.sh"],
+        args = [
+            "$(location %s)" % actual_generated_doc,
+            "$(location %s)" % golden_file,
+        ],
+        data = [
+            actual_generated_doc,
+            golden_file,
+        ],
+    )
+
+    if test == "default":
+        stardoc(
+            name = genrule_name,
+            out = actual_generated_doc,
+            input = input_file,
+            deps = [lib_name],
+            renderer = renderer_bin,
+            stardoc = stardoc_bin,
+            **kwargs
+        )
+    elif test == "pure_markdown":
+        pure_markdown_stardoc(
+            name = genrule_name,
+            out = actual_generated_doc,
+            input = input_file,
+            deps = [lib_name],
+            renderer = renderer_bin,
+            stardoc = stardoc_bin,
+            **kwargs
+        )
+    else:
+        fail("parameter 'test' must either be 'default' or 'pure_markdown', but was " + test)
+
diff --git a/test/testdata/android_basic_test/golden.txt b/test/testdata/android_basic_test/golden.txt
new file mode 100644
index 0000000..2bcabb0
--- /dev/null
+++ b/test/testdata/android_basic_test/golden.txt
@@ -0,0 +1,57 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#android_related_rule"></a>
+
+## android_related_rule
+
+<pre>
+android_related_rule(<a href="#android_related_rule-name">name</a>, <a href="#android_related_rule-first">first</a>, <a href="#android_related_rule-fourth">fourth</a>, <a href="#android_related_rule-second">second</a>, <a href="#android_related_rule-third">third</a>)
+</pre>
+
+This rule does android-related things.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="android_related_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="android_related_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+    <tr id="android_related_rule-fourth">
+      <td><code>fourth</code></td>
+      <td>
+        Boolean; optional
+      </td>
+    </tr>
+    <tr id="android_related_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+    <tr id="android_related_rule-third">
+      <td><code>third</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/android_basic_test/input.bzl b/test/testdata/android_basic_test/input.bzl
new file mode 100644
index 0000000..11feec1
--- /dev/null
+++ b/test/testdata/android_basic_test/input.bzl
@@ -0,0 +1,27 @@
+# buildifier: disable=module-docstring
+# buildifier: disable=function-docstring
+def exercise_the_api():
+    _var1 = android_common.create_device_broker_info("")
+    _var2 = ApkInfo
+    _var3 = AndroidInstrumentationInfo
+    _var4 = AndroidDeviceBrokerInfo
+    _var5 = AndroidResourcesInfo
+    _var6 = AndroidNativeLibsInfo
+    _var7 = AndroidSdkInfo
+    _var8 = android_data
+
+exercise_the_api()
+
+def my_rule_impl(ctx):
+    return []
+
+android_related_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This rule does android-related things.",
+    attrs = {
+        "first": attr.label(mandatory = True, allow_single_file = True),
+        "second": attr.string_dict(mandatory = True),
+        "third": attr.output(mandatory = True),
+        "fourth": attr.bool(default = False, mandatory = False),
+    },
+)
diff --git a/test/testdata/angle_bracket_test/golden.txt b/test/testdata/angle_bracket_test/golden.txt
new file mode 100644
index 0000000..88daa40
--- /dev/null
+++ b/test/testdata/angle_bracket_test/golden.txt
@@ -0,0 +1,116 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_anglebrac"></a>
+
+## my_anglebrac
+
+<pre>
+my_anglebrac(<a href="#my_anglebrac-name">name</a>, <a href="#my_anglebrac-useless">useless</a>)
+</pre>
+
+Rule with &lt;brackets&gt;
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_anglebrac-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_anglebrac-useless">
+      <td><code>useless</code></td>
+      <td>
+        String; optional
+        <p>
+          Args with some <b>formatting</b>
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#bracketuse"></a>
+
+## bracketuse
+
+<pre>
+bracketuse(<a href="#bracketuse-foo">foo</a>, <a href="#bracketuse-bar">bar</a>, <a href="#bracketuse-baz">baz</a>)
+</pre>
+
+Information with &lt;brackets&gt;
+
+### Fields
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="bracketuse-foo">
+      <td><code>foo</code></td>
+      <td>
+        <p>A string representing foo</p>
+      </td>
+    </tr>
+    <tr id="bracketuse-bar">
+      <td><code>bar</code></td>
+      <td>
+        <p>A string representing bar</p>
+      </td>
+    </tr>
+    <tr id="bracketuse-baz">
+      <td><code>baz</code></td>
+      <td>
+        <p>A string representing baz</p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#bracket_function"></a>
+
+## bracket_function
+
+<pre>
+bracket_function(<a href="#bracket_function-name">name</a>)
+</pre>
+
+Dummy docstring with &lt;brackets&gt;.
+
+This rule runs checks on &lt;angle brackets&gt;.
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="bracket_function-name">
+      <td><code>name</code></td>
+      <td>
+        required.
+        <p>
+          an arg with <b>formatted</b> docstring.
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/angle_bracket_test/input.bzl b/test/testdata/angle_bracket_test/input.bzl
new file mode 100644
index 0000000..ac9736a
--- /dev/null
+++ b/test/testdata/angle_bracket_test/input.bzl
@@ -0,0 +1,38 @@
+"""Input file to test angle bracket bug (https://github.com/bazelbuild/skydoc/issues/186)"""
+
+def bracket_function(name):
+    """Dummy docstring with <brackets>.
+
+    This rule runs checks on <angle brackets>.
+
+    Args:
+        name: an arg with <b>formatted</b> docstring.
+
+    Returns:
+        some <angled> brackets
+
+    """
+    pass
+
+bracketuse = provider(
+    doc = "Information with <brackets>",
+    fields = {
+        "foo": "A string representing foo",
+        "bar": "A string representing bar",
+        "baz": "A string representing baz",
+    },
+)
+
+def _rule_impl(ctx):
+    return []
+
+my_anglebrac = rule(
+    implementation = _rule_impl,
+    doc = "Rule with <brackets>",
+    attrs = {
+        "useless": attr.string(
+            doc = "Args with some <b>formatting</b>",
+            default = "Find brackets",
+        ),
+    },
+)
diff --git a/test/testdata/apple_basic_test/golden.txt b/test/testdata/apple_basic_test/golden.txt
new file mode 100644
index 0000000..c236b56
--- /dev/null
+++ b/test/testdata/apple_basic_test/golden.txt
@@ -0,0 +1,57 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#apple_related_rule"></a>
+
+## apple_related_rule
+
+<pre>
+apple_related_rule(<a href="#apple_related_rule-name">name</a>, <a href="#apple_related_rule-first">first</a>, <a href="#apple_related_rule-fourth">fourth</a>, <a href="#apple_related_rule-second">second</a>, <a href="#apple_related_rule-third">third</a>)
+</pre>
+
+This rule does apple-related things.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="apple_related_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="apple_related_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+    <tr id="apple_related_rule-fourth">
+      <td><code>fourth</code></td>
+      <td>
+        Boolean; optional
+      </td>
+    </tr>
+    <tr id="apple_related_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+    <tr id="apple_related_rule-third">
+      <td><code>third</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/apple_basic_test/input.bzl b/test/testdata/apple_basic_test/input.bzl
new file mode 100644
index 0000000..c5801ba
--- /dev/null
+++ b/test/testdata/apple_basic_test/input.bzl
@@ -0,0 +1,22 @@
+# buildifier: disable=module-docstring
+# buildifier: disable=function-docstring
+def exercise_the_api():
+    var1 = apple_common.platform_type
+    var2 = apple_common.AppleDynamicFramework
+
+exercise_the_api()
+
+# buildifier: disable=rule-impl-return
+def my_rule_impl(ctx):
+    return struct()
+
+apple_related_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This rule does apple-related things.",
+    attrs = {
+        "first": attr.label(mandatory = True, allow_single_file = True),
+        "second": attr.string_dict(mandatory = True),
+        "third": attr.output(mandatory = True),
+        "fourth": attr.bool(default = False, mandatory = False),
+    },
+)
diff --git a/test/testdata/aspect_test/golden.txt b/test/testdata/aspect_test/golden.txt
new file mode 100644
index 0000000..a4d2093
--- /dev/null
+++ b/test/testdata/aspect_test/golden.txt
@@ -0,0 +1,148 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_aspect_impl"></a>
+
+## my_aspect_impl
+
+<pre>
+my_aspect_impl(<a href="#my_aspect_impl-ctx">ctx</a>)
+</pre>
+
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_aspect_impl-ctx">
+      <td><code>ctx</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#my_aspect"></a>
+
+## my_aspect
+
+<pre>
+my_aspect(<a href="#my_aspect-name">name</a>, <a href="#my_aspect-first">first</a>, <a href="#my_aspect-second">second</a>)
+</pre>
+
+This is my aspect. It does stuff.
+
+### Aspect Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_aspect-deps">
+      <td><code>deps</code></td>
+      <td>
+        String; required.
+    <tr id="my_aspect-attr_aspect">
+      <td><code>attr_aspect</code></td>
+      <td>
+        String; required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_aspect-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_aspect-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+    <tr id="my_aspect-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#other_aspect"></a>
+
+## other_aspect
+
+<pre>
+other_aspect(<a href="#other_aspect-name">name</a>, <a href="#other_aspect-third">third</a>)
+</pre>
+
+This is another aspect.
+
+### Aspect Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="other_aspect-*">
+      <td><code>*</code></td>
+      <td>
+        String; required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="other_aspect-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="other_aspect-third">
+      <td><code>third</code></td>
+      <td>
+        Integer; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/aspect_test/input.bzl b/test/testdata/aspect_test/input.bzl
new file mode 100644
index 0000000..1ffedb4
--- /dev/null
+++ b/test/testdata/aspect_test/input.bzl
@@ -0,0 +1,24 @@
+"""The input file for the aspect test"""
+
+def my_aspect_impl(ctx):
+    return []
+
+my_aspect = aspect(
+    implementation = my_aspect_impl,
+    doc = "This is my aspect. It does stuff.",
+    attr_aspects = ["deps", "attr_aspect"],
+    attrs = {
+        "first": attr.label(mandatory = True, allow_single_file = True),
+        "second": attr.string_dict(mandatory = True),
+    },
+)
+
+other_aspect = aspect(
+    implementation = my_aspect_impl,
+    doc = "This is another aspect.",
+    attr_aspects = ["*"],
+    attrs = {
+        "_hidden": attr.string(),
+        "third": attr.int(mandatory = True),
+    },
+)
diff --git a/test/testdata/attribute_types_test/golden.txt b/test/testdata/attribute_types_test/golden.txt
new file mode 100644
index 0000000..221c872
--- /dev/null
+++ b/test/testdata/attribute_types_test/golden.txt
@@ -0,0 +1,141 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-a">a</a>, <a href="#my_rule-b">b</a>, <a href="#my_rule-c">c</a>, <a href="#my_rule-d">d</a>, <a href="#my_rule-e">e</a>, <a href="#my_rule-f">f</a>, <a href="#my_rule-g">g</a>, <a href="#my_rule-h">h</a>, <a href="#my_rule-i">i</a>, <a href="#my_rule-j">j</a>, <a href="#my_rule-k">k</a>, <a href="#my_rule-l">l</a>)
+</pre>
+
+This is my rule. It does stuff.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-a">
+      <td><code>a</code></td>
+      <td>
+        Boolean; required
+        <p>
+          Some bool
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-b">
+      <td><code>b</code></td>
+      <td>
+        Integer; required
+        <p>
+          Some int
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-c">
+      <td><code>c</code></td>
+      <td>
+        List of integers; required
+        <p>
+          Some int_list
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-d">
+      <td><code>d</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+        <p>
+          Some label
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-e">
+      <td><code>e</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: Label -> String</a>; required
+        <p>
+          Some label_keyed_string_dict
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-f">
+      <td><code>f</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a>; required
+        <p>
+          Some label_list
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-g">
+      <td><code>g</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; optional
+        <p>
+          Some output
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-h">
+      <td><code>h</code></td>
+      <td>
+        List of labels; optional
+        <p>
+          Some output_list
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-i">
+      <td><code>i</code></td>
+      <td>
+        String; required
+        <p>
+          Some string
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-j">
+      <td><code>j</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+        <p>
+          Some string_dict
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-k">
+      <td><code>k</code></td>
+      <td>
+        List of strings; required
+        <p>
+          Some string_list
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-l">
+      <td><code>l</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> List of strings</a>; optional
+        <p>
+          Some string_list_dict
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/attribute_types_test/input.bzl b/test/testdata/attribute_types_test/input.bzl
new file mode 100644
index 0000000..adbe695
--- /dev/null
+++ b/test/testdata/attribute_types_test/input.bzl
@@ -0,0 +1,23 @@
+# buildifier: disable=module-docstring
+# buildifier: disable=function-docstring
+def my_rule_impl(ctx):
+    return []
+
+my_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is my rule. It does stuff.",
+    attrs = {
+        "a": attr.bool(mandatory = True, doc = "Some bool"),
+        "b": attr.int(mandatory = True, doc = "Some int"),
+        "c": attr.int_list(mandatory = True, doc = "Some int_list"),
+        "d": attr.label(mandatory = True, doc = "Some label"),
+        "e": attr.label_keyed_string_dict(mandatory = True, doc = "Some label_keyed_string_dict"),
+        "f": attr.label_list(mandatory = True, doc = "Some label_list"),
+        "g": attr.output(mandatory = False, doc = "Some output"),
+        "h": attr.output_list(mandatory = False, doc = "Some output_list"),
+        "i": attr.string(mandatory = True, doc = "Some string"),
+        "j": attr.string_dict(mandatory = True, doc = "Some string_dict"),
+        "k": attr.string_list(mandatory = True, doc = "Some string_list"),
+        "l": attr.string_list_dict(mandatory = False, doc = "Some string_list_dict"),
+    },
+)
diff --git a/test/testdata/cc_api_test/golden.txt b/test/testdata/cc_api_test/golden.txt
new file mode 100644
index 0000000..29bcab5
--- /dev/null
+++ b/test/testdata/cc_api_test/golden.txt
@@ -0,0 +1,97 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#cpp_related_rule"></a>
+
+## cpp_related_rule
+
+<pre>
+cpp_related_rule(<a href="#cpp_related_rule-name">name</a>, <a href="#cpp_related_rule-first">first</a>, <a href="#cpp_related_rule-fourth">fourth</a>, <a href="#cpp_related_rule-second">second</a>, <a href="#cpp_related_rule-third">third</a>)
+</pre>
+
+This rule does C++-related things.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="cpp_related_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="cpp_related_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+    <tr id="cpp_related_rule-fourth">
+      <td><code>fourth</code></td>
+      <td>
+        Boolean; optional
+      </td>
+    </tr>
+    <tr id="cpp_related_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+    <tr id="cpp_related_rule-third">
+      <td><code>third</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#exercise_the_api"></a>
+
+## exercise_the_api
+
+<pre>
+exercise_the_api()
+</pre>
+
+
+
+
+
+<a name="#my_rule_impl"></a>
+
+## my_rule_impl
+
+<pre>
+my_rule_impl(<a href="#my_rule_impl-ctx">ctx</a>)
+</pre>
+
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule_impl-ctx">
+      <td><code>ctx</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/cc_api_test/input.bzl b/test/testdata/cc_api_test/input.bzl
new file mode 100644
index 0000000..17462fd
--- /dev/null
+++ b/test/testdata/cc_api_test/input.bzl
@@ -0,0 +1,20 @@
+"""Input file for C++ api test """
+
+def exercise_the_api():
+    var1 = CcInfo
+
+exercise_the_api()
+
+def my_rule_impl(ctx):
+    return []
+
+cpp_related_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This rule does C++-related things.",
+    attrs = {
+        "first": attr.label(mandatory = True, allow_single_file = True),
+        "second": attr.string_dict(mandatory = True),
+        "third": attr.output(mandatory = True),
+        "fourth": attr.bool(default = False, mandatory = False),
+    },
+)
diff --git a/test/testdata/config_apis_test/golden.txt b/test/testdata/config_apis_test/golden.txt
new file mode 100644
index 0000000..11e0829
--- /dev/null
+++ b/test/testdata/config_apis_test/golden.txt
@@ -0,0 +1,104 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#int_setting"></a>
+
+## int_setting
+
+<pre>
+int_setting(<a href="#int_setting-name">name</a>)
+</pre>
+
+
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="int_setting-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#string_flag"></a>
+
+## string_flag
+
+<pre>
+string_flag(<a href="#string_flag-name">name</a>)
+</pre>
+
+
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="string_flag-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#exercise_the_api"></a>
+
+## exercise_the_api
+
+<pre>
+exercise_the_api()
+</pre>
+
+
+
+
+
+<a name="#transition_func"></a>
+
+## transition_func
+
+<pre>
+transition_func(<a href="#transition_func-settings">settings</a>)
+</pre>
+
+A no-op transition function.
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="transition_func-settings">
+      <td><code>settings</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/config_apis_test/input.bzl b/test/testdata/config_apis_test/input.bzl
new file mode 100644
index 0000000..5519711
--- /dev/null
+++ b/test/testdata/config_apis_test/input.bzl
@@ -0,0 +1,25 @@
+# buildifier: disable=module-docstring
+# buildifier: disable=function-docstring
+def exercise_the_api():
+    _var6 = configuration_field("foo", "bar")
+
+exercise_the_api()
+
+def transition_func(settings):
+    """A no-op transition function."""
+    return settings
+
+my_transition = transition(implementation = transition_func, inputs = [], outputs = [])
+
+def _build_setting_impl(ctx):
+    return []
+
+string_flag = rule(
+    implementation = _build_setting_impl,
+    build_setting = config.string(flag = True),
+)
+
+int_setting = rule(
+    implementation = _build_setting_impl,
+    build_setting = config.int(flag = False),
+)
diff --git a/test/testdata/cpp_basic_test/golden.txt b/test/testdata/cpp_basic_test/golden.txt
new file mode 100644
index 0000000..79f10b6
--- /dev/null
+++ b/test/testdata/cpp_basic_test/golden.txt
@@ -0,0 +1,57 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#cpp_related_rule"></a>
+
+## cpp_related_rule
+
+<pre>
+cpp_related_rule(<a href="#cpp_related_rule-name">name</a>, <a href="#cpp_related_rule-first">first</a>, <a href="#cpp_related_rule-fourth">fourth</a>, <a href="#cpp_related_rule-second">second</a>, <a href="#cpp_related_rule-third">third</a>)
+</pre>
+
+This rule does cpp-related things.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="cpp_related_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="cpp_related_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+    <tr id="cpp_related_rule-fourth">
+      <td><code>fourth</code></td>
+      <td>
+        Boolean; optional
+      </td>
+    </tr>
+    <tr id="cpp_related_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+    <tr id="cpp_related_rule-third">
+      <td><code>third</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/cpp_basic_test/input.bzl b/test/testdata/cpp_basic_test/input.bzl
new file mode 100644
index 0000000..b927e42
--- /dev/null
+++ b/test/testdata/cpp_basic_test/input.bzl
@@ -0,0 +1,20 @@
+# buildifier: disable=module-docstring
+# buildifier: disable=function-docstring
+def exercise_the_api():
+    var1 = cc_common.CcToolchainInfo
+
+exercise_the_api()
+
+def my_rule_impl(ctx):
+    return []
+
+cpp_related_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This rule does cpp-related things.",
+    attrs = {
+        "first": attr.label(mandatory = True, allow_single_file = True),
+        "second": attr.string_dict(mandatory = True),
+        "third": attr.output(mandatory = True),
+        "fourth": attr.bool(default = False, mandatory = False),
+    },
+)
diff --git a/test/testdata/filter_rules_test/dep.bzl b/test/testdata/filter_rules_test/dep.bzl
new file mode 100644
index 0000000..5c5da10
--- /dev/null
+++ b/test/testdata/filter_rules_test/dep.bzl
@@ -0,0 +1,16 @@
+# buildifier: disable=module-docstring
+def my_rule_impl(ctx):
+  return []
+
+my_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is the dep rule. It does stuff.",
+    attrs = {
+        "first": attr.label(
+            mandatory = True,
+            doc = "dep's my_rule doc string",
+            allow_single_file = True,
+        ),
+        "second": attr.string_dict(mandatory = True),
+    },
+)
diff --git a/test/testdata/filter_rules_test/golden.txt b/test/testdata/filter_rules_test/golden.txt
new file mode 100644
index 0000000..eadcdb8
--- /dev/null
+++ b/test/testdata/filter_rules_test/golden.txt
@@ -0,0 +1,94 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-first">first</a>, <a href="#my_rule-second">second</a>)
+</pre>
+
+This is my rule. It does stuff.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+        <p>
+          first my_rule doc string
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#whitelisted_dep_rule"></a>
+
+## whitelisted_dep_rule
+
+<pre>
+whitelisted_dep_rule(<a href="#whitelisted_dep_rule-name">name</a>, <a href="#whitelisted_dep_rule-first">first</a>, <a href="#whitelisted_dep_rule-second">second</a>)
+</pre>
+
+This is the dep rule. It does stuff.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="whitelisted_dep_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="whitelisted_dep_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+        <p>
+          dep's my_rule doc string
+        </p>
+      </td>
+    </tr>
+    <tr id="whitelisted_dep_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/filter_rules_test/input.bzl b/test/testdata/filter_rules_test/input.bzl
new file mode 100644
index 0000000..5b8679c
--- /dev/null
+++ b/test/testdata/filter_rules_test/input.bzl
@@ -0,0 +1,40 @@
+# buildifier: disable=module-docstring
+load(
+    ":testdata/filter_rules_test/dep.bzl",
+    "my_rule_impl",
+    dep_rule = "my_rule",
+)
+
+def my_rule_impl(ctx):
+    return []
+
+my_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is my rule. It does stuff.",
+    attrs = {
+        "first": attr.label(
+            mandatory = True,
+            doc = "first my_rule doc string",
+            allow_single_file = True,
+        ),
+        "second": attr.string_dict(mandatory = True),
+    },
+)
+
+other_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is another rule.",
+    attrs = {
+        "test": attr.string_dict(mandatory = True),
+    },
+)
+
+whitelisted_dep_rule = dep_rule
+
+yet_another_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is yet another rule",
+    attrs = {
+        "test": attr.string_dict(mandatory = True),
+    },
+)
diff --git a/test/testdata/function_basic_test/golden.txt b/test/testdata/function_basic_test/golden.txt
new file mode 100644
index 0000000..5719b1b
--- /dev/null
+++ b/test/testdata/function_basic_test/golden.txt
@@ -0,0 +1,128 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#check_sources"></a>
+
+## check_sources
+
+<pre>
+check_sources(<a href="#check_sources-name">name</a>, <a href="#check_sources-required_param">required_param</a>, <a href="#check_sources-bool_param">bool_param</a>, <a href="#check_sources-srcs">srcs</a>, <a href="#check_sources-string_param">string_param</a>, <a href="#check_sources-int_param">int_param</a>, <a href="#check_sources-dict_param">dict_param</a>, <a href="#check_sources-struct_param">struct_param</a>)
+</pre>
+
+Runs some checks on the given source files.
+
+This rule runs checks on a given set of source files.
+Use `bazel build` to run the check.
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="check_sources-name">
+      <td><code>name</code></td>
+      <td>
+        required.
+        <p>
+          A unique name for this rule.
+        </p>
+      </td>
+    </tr>
+    <tr id="check_sources-required_param">
+      <td><code>required_param</code></td>
+      <td>
+        required.
+        <p>
+          Use your imagination.
+        </p>
+      </td>
+    </tr>
+    <tr id="check_sources-bool_param">
+      <td><code>bool_param</code></td>
+      <td>
+        optional. default is <code>True</code>
+      </td>
+    </tr>
+    <tr id="check_sources-srcs">
+      <td><code>srcs</code></td>
+      <td>
+        optional. default is <code>[]</code>
+        <p>
+          Source files to run the checks against.
+        </p>
+      </td>
+    </tr>
+    <tr id="check_sources-string_param">
+      <td><code>string_param</code></td>
+      <td>
+        optional. default is <code>""</code>
+      </td>
+    </tr>
+    <tr id="check_sources-int_param">
+      <td><code>int_param</code></td>
+      <td>
+        optional. default is <code>2</code>
+        <p>
+          Your favorite number.
+        </p>
+      </td>
+    </tr>
+    <tr id="check_sources-dict_param">
+      <td><code>dict_param</code></td>
+      <td>
+        optional. default is <code>{}</code>
+      </td>
+    </tr>
+    <tr id="check_sources-struct_param">
+      <td><code>struct_param</code></td>
+      <td>
+        optional. default is <code>struct(foo = "bar")</code>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#undocumented_function"></a>
+
+## undocumented_function
+
+<pre>
+undocumented_function(<a href="#undocumented_function-a">a</a>, <a href="#undocumented_function-b">b</a>, <a href="#undocumented_function-c">c</a>)
+</pre>
+
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="undocumented_function-a">
+      <td><code>a</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+    <tr id="undocumented_function-b">
+      <td><code>b</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+    <tr id="undocumented_function-c">
+      <td><code>c</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/function_basic_test/input.bzl b/test/testdata/function_basic_test/input.bzl
new file mode 100644
index 0000000..721e1d3
--- /dev/null
+++ b/test/testdata/function_basic_test/input.bzl
@@ -0,0 +1,38 @@
+"""A test that verifies basic user function documentation."""
+
+def check_sources(
+        name,
+        required_param,
+        bool_param = True,
+        srcs = [],
+        string_param = "",
+        int_param = 2,
+        dict_param = {},
+        struct_param = struct(foo = "bar")):
+    # buildifier: disable=function-docstring-args
+    """Runs some checks on the given source files.
+
+    This rule runs checks on a given set of source files.
+    Use `bazel build` to run the check.
+
+    Args:
+        name: A unique name for this rule.
+        required_param: Use your imagination.
+        srcs: Source files to run the checks against.
+        doesnt_exist: A param that doesn't exist (lets hope we still get *some* documentation)
+        int_param: Your favorite number.
+    """
+    _ignore = [
+        name,
+        required_param,
+        bool_param,
+        srcs,
+        string_param,
+        int_param,
+        dict_param,
+        struct_param,
+    ]
+    x = ("Hah. All that documentation but nothing really to see here")
+
+def undocumented_function(a, b, c):
+    pass
diff --git a/test/testdata/generated_bzl_test/dep.bzl.tpl b/test/testdata/generated_bzl_test/dep.bzl.tpl
new file mode 100644
index 0000000..4c8bd78
--- /dev/null
+++ b/test/testdata/generated_bzl_test/dep.bzl.tpl
@@ -0,0 +1,4 @@
+"""Used to generate dep.bzl"""
+
+def my_rule_impl(ctx):
+    return []
diff --git a/test/testdata/generated_bzl_test/golden.txt b/test/testdata/generated_bzl_test/golden.txt
new file mode 100644
index 0000000..cf02e4c
--- /dev/null
+++ b/test/testdata/generated_bzl_test/golden.txt
@@ -0,0 +1,48 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-first">first</a>, <a href="#my_rule-second">second</a>)
+</pre>
+
+This is my rule. It does stuff.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+        <p>
+          first my_rule doc string
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/generated_bzl_test/input.bzl b/test/testdata/generated_bzl_test/input.bzl
new file mode 100644
index 0000000..bad15f6
--- /dev/null
+++ b/test/testdata/generated_bzl_test/input.bzl
@@ -0,0 +1,16 @@
+"""A direct dependency file of the input file."""
+
+load(":testdata/generated_bzl_test/dep.bzl", "my_rule_impl")
+
+my_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is my rule. It does stuff.",
+    attrs = {
+        "first": attr.label(
+            mandatory = True,
+            doc = "first my_rule doc string",
+            allow_single_file = True,
+        ),
+        "second": attr.string_dict(mandatory = True),
+    },
+)
diff --git a/test/testdata/input_template_test/aspect.vm b/test/testdata/input_template_test/aspect.vm
new file mode 100644
index 0000000..41b08e5
--- /dev/null
+++ b/test/testdata/input_template_test/aspect.vm
@@ -0,0 +1,32 @@
+<a name="#${aspectName}"></a>
+
+#[[##]]# ${aspectName}
+
+<pre>
+${util.aspectSummary($aspectName, $aspectInfo)}
+</pre>
+
+$aspectInfo.getDocString()
+
+#[[###]]# Aspect Attributes
+
+#if (!$aspectInfo.getAspectAttributeList().isEmpty())
+#foreach ($aspectAttribute in $aspectInfo.getAspectAttributeList())
+        $aspectAttribute
+        String; required.
+#end
+#end
+
+#[[###]]# Attributes
+
+#foreach ($attribute in $aspectInfo.getAttributeList())
+<b>
+      <code>${attribute.name}</code>
+        ${util.attributeTypeString($attribute)}; ${util.mandatoryString($attribute)}
+</b>
+#if (!$attribute.docString.isEmpty())
+        <p>
+          ${attribute.docString.trim()}
+        </p>
+#end
+#end
\ No newline at end of file
diff --git a/test/testdata/input_template_test/func.vm b/test/testdata/input_template_test/func.vm
new file mode 100644
index 0000000..80ef25c
--- /dev/null
+++ b/test/testdata/input_template_test/func.vm
@@ -0,0 +1,29 @@
+<a name="#${funcInfo.functionName}"></a>
+
+#[[##]]# ${funcInfo.functionName}
+
+<pre>
+${util.funcSummary($funcInfo)}
+</pre>
+
+${funcInfo.docString}
+
+<b>input_template_test BOLD PARAMETERS</b>
+
+#if (!$funcInfo.getParameterList().isEmpty())
+#[[###]]# Parameters
+
+#foreach ($param in $funcInfo.getParameterList())
+<b>
+      <code>${param.name}</code>
+        ${util.mandatoryString($param)}.#if(!$param.getDefaultValue().isEmpty()) default is
+        <code>$param.getDefaultValue()</code>
+        </b>
+        #end
+#if (!$param.docString.isEmpty())
+        <p>
+          ${param.docString.trim()}
+        </p>
+#end
+#end
+#end
diff --git a/test/testdata/input_template_test/golden.txt b/test/testdata/input_template_test/golden.txt
new file mode 100644
index 0000000..aff43e1
--- /dev/null
+++ b/test/testdata/input_template_test/golden.txt
@@ -0,0 +1,138 @@
+<!-- THIS HEADER IS FOR input_template_test ONLY -->
+
+Module Docstring: "Input file for input template test"
+
+<a name="#my_example"></a>
+
+## my_example
+
+<pre>
+my_example(<a href="#my_example-name">name</a>, <a href="#my_example-useless">useless</a>)
+</pre>
+
+Small example of rule using chosen template.
+
+<b>input_template_test BOLD ATTRIBUTES</b>
+
+### Attributes
+
+
+<b>
+      <code>name</code>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+</b>
+        <p>
+          A unique name for this target.
+        </p>
+<b>
+      <code>useless</code>
+        String; optional
+</b>
+        <p>
+          This argument will be ignored.
+        </p>
+
+
+<a name="#example"></a>
+
+## example
+
+<pre>
+example(<a href="#example-foo">foo</a>, <a href="#example-bar">bar</a>, <a href="#example-baz">baz</a>)
+</pre>
+
+Stores information about an example in chosen template.
+
+<b>input_template_test BOLD FIELDS</b>
+
+### Fields
+
+<b>
+      <code>foo</code>
+</b>
+        <p>A string representing foo</p>
+<b>
+      <code>bar</code>
+</b>
+        <p>A string representing bar</p>
+<b>
+      <code>baz</code>
+</b>
+        <p>A string representing baz</p>
+
+
+<a name="#my_aspect_impl"></a>
+
+## my_aspect_impl
+
+<pre>
+my_aspect_impl(<a href="#my_aspect_impl-ctx">ctx</a>)
+</pre>
+
+
+
+<b>input_template_test BOLD PARAMETERS</b>
+
+### Parameters
+
+<b>
+      <code>ctx</code>
+        required.
+
+<a name="#template_function"></a>
+
+## template_function
+
+<pre>
+template_function(<a href="#template_function-foo">foo</a>)
+</pre>
+
+Runs some checks on the given function parameter.
+
+This rule runs checks on a given function parameter in chosen template.
+Use `bazel build` to run the check.
+
+
+<b>input_template_test BOLD PARAMETERS</b>
+
+### Parameters
+
+<b>
+      <code>foo</code>
+        required.        <p>
+          A unique name for this function.
+        </p>
+
+
+<a name="#my_aspect"></a>
+
+## my_aspect
+
+<pre>
+my_aspect(<a href="#my_aspect-name">name</a>, <a href="#my_aspect-first">first</a>)
+</pre>
+
+This is my aspect. It does stuff.
+
+### Aspect Attributes
+
+        deps
+        String; required.
+        attr_aspect
+        String; required.
+
+### Attributes
+
+<b>
+      <code>name</code>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+</b>
+        <p>
+          A unique name for this target.
+        </p>
+<b>
+      <code>first</code>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+</b>
+
+
diff --git a/test/testdata/input_template_test/header.vm b/test/testdata/input_template_test/header.vm
new file mode 100644
index 0000000..2203c76
--- /dev/null
+++ b/test/testdata/input_template_test/header.vm
@@ -0,0 +1,3 @@
+<!-- THIS HEADER IS FOR input_template_test ONLY -->
+
+Module Docstring: "${moduleDocstring}"
diff --git a/test/testdata/input_template_test/input.bzl b/test/testdata/input_template_test/input.bzl
new file mode 100644
index 0000000..37fee12
--- /dev/null
+++ b/test/testdata/input_template_test/input.bzl
@@ -0,0 +1,47 @@
+"""Input file for input template test"""
+
+def template_function(foo):
+    """Runs some checks on the given function parameter.
+
+    This rule runs checks on a given function parameter in chosen template.
+    Use `bazel build` to run the check.
+
+    Args:
+        foo: A unique name for this function.
+    """
+    pass
+
+example = provider(
+    doc = "Stores information about an example in chosen template.",
+    fields = {
+        "foo": "A string representing foo",
+        "bar": "A string representing bar",
+        "baz": "A string representing baz",
+    },
+)
+
+def _rule_impl(ctx):
+    return []
+
+my_example = rule(
+    implementation = _rule_impl,
+    doc = "Small example of rule using chosen template.",
+    attrs = {
+        "useless": attr.string(
+            doc = "This argument will be ignored.",
+            default = "word",
+        ),
+    },
+)
+
+def my_aspect_impl(ctx):
+    return []
+
+my_aspect = aspect(
+    implementation = my_aspect_impl,
+    doc = "This is my aspect. It does stuff.",
+    attr_aspects = ["deps", "attr_aspect"],
+    attrs = {
+        "first": attr.label(mandatory = True, allow_single_file = True),
+    },
+)
diff --git a/test/testdata/input_template_test/provider.vm b/test/testdata/input_template_test/provider.vm
new file mode 100644
index 0000000..269a894
--- /dev/null
+++ b/test/testdata/input_template_test/provider.vm
@@ -0,0 +1,22 @@
+<a name="#${providerName}"></a>
+
+#[[##]]# ${providerName}
+
+<pre>
+${util.providerSummary($providerName, $providerInfo)}
+</pre>
+
+${providerInfo.docString}
+
+<b>input_template_test BOLD FIELDS</b>
+
+#if (!$providerInfo.fieldInfoList.isEmpty())
+#[[###]]# Fields
+
+#foreach ($field in $providerInfo.fieldInfoList)
+<b>
+      <code>${field.name}</code>
+</b>
+        <p>${field.docString}</p>
+#end
+#end
diff --git a/test/testdata/input_template_test/rule.vm b/test/testdata/input_template_test/rule.vm
new file mode 100644
index 0000000..2227637
--- /dev/null
+++ b/test/testdata/input_template_test/rule.vm
@@ -0,0 +1,28 @@
+<a name="#${ruleName}"></a>
+
+#[[##]]# ${ruleName}
+
+<pre>
+${util.ruleSummary($ruleName, $ruleInfo)}
+</pre>
+
+${ruleInfo.docString}
+
+<b>input_template_test BOLD ATTRIBUTES</b>
+
+#[[###]]# Attributes
+
+#if (!$ruleInfo.getAttributeList().isEmpty())
+
+#foreach ($attribute in $ruleInfo.getAttributeList())
+<b>
+      <code>${attribute.name}</code>
+        ${util.attributeTypeString($attribute)}; ${util.mandatoryString($attribute)}
+</b>
+#if (!$attribute.docString.isEmpty())
+        <p>
+          ${attribute.docString.trim()}
+        </p>
+#end
+#end
+#end
diff --git a/test/testdata/java_basic_test/golden.txt b/test/testdata/java_basic_test/golden.txt
new file mode 100644
index 0000000..f165dca
--- /dev/null
+++ b/test/testdata/java_basic_test/golden.txt
@@ -0,0 +1,57 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#java_related_rule"></a>
+
+## java_related_rule
+
+<pre>
+java_related_rule(<a href="#java_related_rule-name">name</a>, <a href="#java_related_rule-first">first</a>, <a href="#java_related_rule-fourth">fourth</a>, <a href="#java_related_rule-second">second</a>, <a href="#java_related_rule-third">third</a>)
+</pre>
+
+This rule does java-related things.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="java_related_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="java_related_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+    <tr id="java_related_rule-fourth">
+      <td><code>fourth</code></td>
+      <td>
+        Boolean; optional
+      </td>
+    </tr>
+    <tr id="java_related_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+    <tr id="java_related_rule-third">
+      <td><code>third</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/java_basic_test/input.bzl b/test/testdata/java_basic_test/input.bzl
new file mode 100644
index 0000000..db0d064
--- /dev/null
+++ b/test/testdata/java_basic_test/input.bzl
@@ -0,0 +1,22 @@
+# buildifier: disable=module-docstring
+# buildifier: disable=function-docstring
+def exercise_the_api():
+    var1 = java_common.JavaRuntimeInfo
+    var2 = JavaInfo
+    var3 = java_proto_common
+
+exercise_the_api()
+
+def my_rule_impl(ctx):
+    return []
+
+java_related_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This rule does java-related things.",
+    attrs = {
+        "first": attr.label(mandatory = True, allow_single_file = True),
+        "second": attr.string_dict(mandatory = True),
+        "third": attr.output(mandatory = True),
+        "fourth": attr.bool(default = False, mandatory = False),
+    },
+)
diff --git a/test/testdata/macro_kwargs_test/golden.txt b/test/testdata/macro_kwargs_test/golden.txt
new file mode 100644
index 0000000..6391577
--- /dev/null
+++ b/test/testdata/macro_kwargs_test/golden.txt
@@ -0,0 +1,168 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#macro_with_args"></a>
+
+## macro_with_args
+
+<pre>
+macro_with_args(<a href="#macro_with_args-name">name</a>, <a href="#macro_with_args-args">args</a>)
+</pre>
+
+My args macro is OK.
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="macro_with_args-name">
+      <td><code>name</code></td>
+      <td>
+        required.
+        <p>
+          The name of the test rule.
+        </p>
+      </td>
+    </tr>
+    <tr id="macro_with_args-args">
+      <td><code>args</code></td>
+      <td>
+        optional.
+        <p>
+          Other arguments to include
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#macro_with_both"></a>
+
+## macro_with_both
+
+<pre>
+macro_with_both(<a href="#macro_with_both-name">name</a>, <a href="#macro_with_both-number">number</a>, <a href="#macro_with_both-args">args</a>, <a href="#macro_with_both-kwargs">kwargs</a>)
+</pre>
+
+Oh wow this macro has both.
+
+Not much else to say.
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="macro_with_both-name">
+      <td><code>name</code></td>
+      <td>
+        required.
+        <p>
+          The name of the test rule.
+        </p>
+      </td>
+    </tr>
+    <tr id="macro_with_both-number">
+      <td><code>number</code></td>
+      <td>
+        optional. default is <code>3</code>
+        <p>
+          Some number used for important things
+        </p>
+      </td>
+    </tr>
+    <tr id="macro_with_both-args">
+      <td><code>args</code></td>
+      <td>
+        optional.
+        <p>
+          Other arguments to include
+        </p>
+      </td>
+    </tr>
+    <tr id="macro_with_both-kwargs">
+      <td><code>kwargs</code></td>
+      <td>
+        optional.
+        <p>
+          Other attributes to include
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#macro_with_kwargs"></a>
+
+## macro_with_kwargs
+
+<pre>
+macro_with_kwargs(<a href="#macro_with_kwargs-name">name</a>, <a href="#macro_with_kwargs-config">config</a>, <a href="#macro_with_kwargs-deps">deps</a>, <a href="#macro_with_kwargs-kwargs">kwargs</a>)
+</pre>
+
+My kwargs macro is the best.
+
+This is a long multi-line doc string.
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer
+elementum, diam vitae tincidunt pulvinar, nunc tortor volutpat dui,
+vitae facilisis odio ligula a tortor. Donec ullamcorper odio eget ipsum tincidunt,
+vel mollis eros pellentesque.
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="macro_with_kwargs-name">
+      <td><code>name</code></td>
+      <td>
+        required.
+        <p>
+          The name of the test rule.
+        </p>
+      </td>
+    </tr>
+    <tr id="macro_with_kwargs-config">
+      <td><code>config</code></td>
+      <td>
+        required.
+        <p>
+          Config to use for my macro
+        </p>
+      </td>
+    </tr>
+    <tr id="macro_with_kwargs-deps">
+      <td><code>deps</code></td>
+      <td>
+        optional. default is <code>[]</code>
+        <p>
+          List of my macro's dependencies
+        </p>
+      </td>
+    </tr>
+    <tr id="macro_with_kwargs-kwargs">
+      <td><code>kwargs</code></td>
+      <td>
+        optional.
+        <p>
+          Other attributes to include
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/macro_kwargs_test/input.bzl b/test/testdata/macro_kwargs_test/input.bzl
new file mode 100644
index 0000000..0f6268d
--- /dev/null
+++ b/test/testdata/macro_kwargs_test/input.bzl
@@ -0,0 +1,52 @@
+"""Tests for functions which use *args or **kwargs"""
+
+def macro_with_kwargs(name, config, deps = [], **kwargs):
+    """My kwargs macro is the best.
+
+    This is a long multi-line doc string.
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer
+    elementum, diam vitae tincidunt pulvinar, nunc tortor volutpat dui,
+    vitae facilisis odio ligula a tortor. Donec ullamcorper odio eget ipsum tincidunt,
+    vel mollis eros pellentesque.
+
+    Args:
+      name: The name of the test rule.
+      config: Config to use for my macro
+      deps: List of my macro's dependencies
+      **kwargs: Other attributes to include
+
+    Returns:
+      An empty list.
+    """
+    _ignore = [name, config, deps, kwargs]
+    return []
+
+def macro_with_args(name, *args):
+    """My args macro is OK.
+
+    Args:
+      name: The name of the test rule.
+      *args: Other arguments to include
+
+    Returns:
+      An empty list.
+    """
+    _ignore = [name, args]
+    return []
+
+def macro_with_both(name, number = 3, *args, **kwargs):
+    """Oh wow this macro has both.
+
+    Not much else to say.
+
+    Args:
+      name: The name of the test rule.
+      number: Some number used for important things
+      *args: Other arguments to include
+      **kwargs: Other attributes to include
+
+    Returns:
+      An empty list.
+    """
+    _ignore = [name, number, args, kwargs]
+    return []
diff --git a/test/testdata/misc_apis_test/golden.txt b/test/testdata/misc_apis_test/golden.txt
new file mode 100644
index 0000000..cfae4f5
--- /dev/null
+++ b/test/testdata/misc_apis_test/golden.txt
@@ -0,0 +1,152 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-deps">deps</a>, <a href="#my_rule-extra_arguments">extra_arguments</a>, <a href="#my_rule-out">out</a>, <a href="#my_rule-src">src</a>, <a href="#my_rule-tool">tool</a>)
+</pre>
+
+This rule exercises some of the build API.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-deps">
+      <td><code>deps</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a>; optional
+        <p>
+          A list of dependencies.
+        </p>
+        <p>
+          The dependencies of this attribute must provide: MyInfo
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-extra_arguments">
+      <td><code>extra_arguments</code></td>
+      <td>
+        List of strings; optional
+      </td>
+    </tr>
+    <tr id="my_rule-out">
+      <td><code>out</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+        <p>
+          The output file.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-src">
+      <td><code>src</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; optional
+        <p>
+          The source file.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-tool">
+      <td><code>tool</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; optional
+        <p>
+          The location of the tool to use.
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#MyInfo"></a>
+
+## MyInfo
+
+<pre>
+MyInfo(<a href="#MyInfo-foo">foo</a>, <a href="#MyInfo-bar">bar</a>)
+</pre>
+
+
+
+### Fields
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="MyInfo-foo">
+      <td><code>foo</code></td>
+      <td>
+        <p>Something foo-related.</p>
+      </td>
+    </tr>
+    <tr id="MyInfo-bar">
+      <td><code>bar</code></td>
+      <td>
+        <p>Something bar-related.</p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#exercise_the_api"></a>
+
+## exercise_the_api
+
+<pre>
+exercise_the_api()
+</pre>
+
+
+
+
+
+<a name="#my_rule_impl"></a>
+
+## my_rule_impl
+
+<pre>
+my_rule_impl(<a href="#my_rule_impl-ctx">ctx</a>)
+</pre>
+
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule_impl-ctx">
+      <td><code>ctx</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/misc_apis_test/input.bzl b/test/testdata/misc_apis_test/input.bzl
new file mode 100644
index 0000000..943442f
--- /dev/null
+++ b/test/testdata/misc_apis_test/input.bzl
@@ -0,0 +1,57 @@
+# This is here to test that built-in names can be shadowed by global names.
+# (Regression test for http://b/35984389).
+# buildifier: disable=module-docstring
+config = "value for global config variable"
+
+def my_rule_impl(ctx):
+    return []
+
+def exercise_the_api():
+    var1 = config_common.FeatureFlagInfo
+    var2 = platform_common.TemplateVariableInfo
+    var3 = repository_rule(
+        implementation = my_rule_impl,
+        doc = "This repository rule has documentation.",
+    )
+    var4 = testing.ExecutionInfo({})
+
+exercise_the_api()
+
+MyInfo = provider(
+    fields = {
+        "foo": "Something foo-related.",
+        "bar": "Something bar-related.",
+    },
+)
+
+my_info = MyInfo(foo = "x", bar = "y")
+
+my_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This rule exercises some of the build API.",
+    attrs = {
+        "src": attr.label(
+            doc = "The source file.",
+            allow_files = [".bzl"],
+        ),
+        "deps": attr.label_list(
+            doc = """
+A list of dependencies.
+""",
+            providers = [MyInfo],
+            allow_files = False,
+        ),
+        "tool": attr.label(
+            doc = "The location of the tool to use.",
+            allow_files = True,
+            default = Label("//foo/bar/baz:target"),
+            cfg = "host",
+            executable = True,
+        ),
+        "out": attr.output(
+            doc = "The output file.",
+            mandatory = True,
+        ),
+        "extra_arguments": attr.string_list(default = []),
+    },
+)
diff --git a/test/testdata/multi_level_namespace_test/golden.txt b/test/testdata/multi_level_namespace_test/golden.txt
new file mode 100644
index 0000000..58f662a
--- /dev/null
+++ b/test/testdata/multi_level_namespace_test/golden.txt
@@ -0,0 +1,119 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_namespace.min"></a>
+
+## my_namespace.min
+
+<pre>
+my_namespace.min(<a href="#my_namespace.min-integers">integers</a>)
+</pre>
+
+Returns the minimum of given elements.
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_namespace.min-integers">
+      <td><code>integers</code></td>
+      <td>
+        required.
+        <p>
+          A list of integers. Must not be empty.
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#my_namespace.math.min"></a>
+
+## my_namespace.math.min
+
+<pre>
+my_namespace.math.min(<a href="#my_namespace.math.min-integers">integers</a>)
+</pre>
+
+Returns the minimum of given elements.
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_namespace.math.min-integers">
+      <td><code>integers</code></td>
+      <td>
+        required.
+        <p>
+          A list of integers. Must not be empty.
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#my_namespace.foo.bar.baz"></a>
+
+## my_namespace.foo.bar.baz
+
+<pre>
+my_namespace.foo.bar.baz()
+</pre>
+
+This function does nothing.
+
+
+
+<a name="#my_namespace.one.two.min"></a>
+
+## my_namespace.one.two.min
+
+<pre>
+my_namespace.one.two.min(<a href="#my_namespace.one.two.min-integers">integers</a>)
+</pre>
+
+Returns the minimum of given elements.
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_namespace.one.two.min-integers">
+      <td><code>integers</code></td>
+      <td>
+        required.
+        <p>
+          A list of integers. Must not be empty.
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#my_namespace.one.three.does_nothing"></a>
+
+## my_namespace.one.three.does_nothing
+
+<pre>
+my_namespace.one.three.does_nothing()
+</pre>
+
+This function does nothing.
+
+
+
diff --git a/test/testdata/multi_level_namespace_test/input.bzl b/test/testdata/multi_level_namespace_test/input.bzl
new file mode 100644
index 0000000..8b0b436
--- /dev/null
+++ b/test/testdata/multi_level_namespace_test/input.bzl
@@ -0,0 +1,32 @@
+"""A test that verifies documenting a multi-leveled namespace of functions."""
+
+def _min(integers):
+    """Returns the minimum of given elements.
+
+    Args:
+      integers: A list of integers. Must not be empty.
+
+    Returns:
+      The minimum integer in the given list.
+    """
+    _ignore = [integers]
+    return 42
+
+def _does_nothing():
+    """This function does nothing."""
+    pass
+
+my_namespace = struct(
+    dropped_field = "Note this field should not be documented",
+    min = _min,
+    math = struct(min = _min),
+    foo = struct(
+        bar = struct(baz = _does_nothing),
+        num = 12,
+        string = "Hello!",
+    ),
+    one = struct(
+        two = struct(min = _min),
+        three = struct(does_nothing = _does_nothing),
+    ),
+)
diff --git a/test/testdata/multi_level_namespace_test_with_whitelist/golden.txt b/test/testdata/multi_level_namespace_test_with_whitelist/golden.txt
new file mode 100644
index 0000000..3eaf093
--- /dev/null
+++ b/test/testdata/multi_level_namespace_test_with_whitelist/golden.txt
@@ -0,0 +1,70 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_namespace.min"></a>
+
+## my_namespace.min
+
+<pre>
+my_namespace.min(<a href="#my_namespace.min-integers">integers</a>)
+</pre>
+
+Returns the minimum of given elements.
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_namespace.min-integers">
+      <td><code>integers</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#my_namespace.math.min"></a>
+
+## my_namespace.math.min
+
+<pre>
+my_namespace.math.min(<a href="#my_namespace.math.min-integers">integers</a>)
+</pre>
+
+Returns the minimum of given elements.
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_namespace.math.min-integers">
+      <td><code>integers</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#other_namespace.foo.nothing"></a>
+
+## other_namespace.foo.nothing
+
+<pre>
+other_namespace.foo.nothing()
+</pre>
+
+This function does nothing.
+
+
+
diff --git a/test/testdata/multi_level_namespace_test_with_whitelist/input.bzl b/test/testdata/multi_level_namespace_test_with_whitelist/input.bzl
new file mode 100644
index 0000000..34eb87b
--- /dev/null
+++ b/test/testdata/multi_level_namespace_test_with_whitelist/input.bzl
@@ -0,0 +1,23 @@
+"""A test that verifies documenting a multi-leveled namespace of functions with whitelist symbols.
+The whitelist symbols should cause everything in my_namespace to to be documented, but only a
+specific symbol in other_namespace to be documented."""
+
+def _min(integers):
+    """Returns the minimum of given elements."""
+    _ignore = [integers]
+    return 42
+
+def _does_nothing():
+    """This function does nothing."""
+    pass
+
+my_namespace = struct(
+    dropped_field = "Note this field should not be documented",
+    min = _min,
+    math = struct(min = _min),
+)
+
+other_namespace = struct(
+    foo = struct(nothing = _does_nothing),
+    min = _min,
+)
diff --git a/test/testdata/multiple_files_test/dep.bzl b/test/testdata/multiple_files_test/dep.bzl
new file mode 100644
index 0000000..b1beece
--- /dev/null
+++ b/test/testdata/multiple_files_test/dep.bzl
@@ -0,0 +1,17 @@
+"""A dependency file for multiple_files_test."""
+
+load(":testdata/multiple_files_test/inner_dep.bzl", "inner_rule_impl", "prep_work")
+
+def some_cool_function(name, srcs = [], beef = ""):
+    """A pretty cool function. You should call it.
+
+    Args:
+      name: Some sort of name.
+      srcs: What sources you want cool stuff to happen to.
+      beef: Your opinion on beef.
+    """
+    x = (name, srcs, beef)
+
+prep_work()
+
+my_rule_impl = inner_rule_impl
diff --git a/test/testdata/multiple_files_test/golden.txt b/test/testdata/multiple_files_test/golden.txt
new file mode 100644
index 0000000..d80a950
--- /dev/null
+++ b/test/testdata/multiple_files_test/golden.txt
@@ -0,0 +1,171 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-first">first</a>, <a href="#my_rule-second">second</a>)
+</pre>
+
+This is my rule. It does stuff.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+        <p>
+          first my_rule doc string
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#other_rule"></a>
+
+## other_rule
+
+<pre>
+other_rule(<a href="#other_rule-name">name</a>, <a href="#other_rule-fourth">fourth</a>, <a href="#other_rule-third">third</a>)
+</pre>
+
+This is another rule.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="other_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="other_rule-fourth">
+      <td><code>fourth</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+    <tr id="other_rule-third">
+      <td><code>third</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+        <p>
+          third other_rule doc string
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#yet_another_rule"></a>
+
+## yet_another_rule
+
+<pre>
+yet_another_rule(<a href="#yet_another_rule-name">name</a>, <a href="#yet_another_rule-fifth">fifth</a>)
+</pre>
+
+This is yet another rule
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="yet_another_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="yet_another_rule-fifth">
+      <td><code>fifth</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#top_fun"></a>
+
+## top_fun
+
+<pre>
+top_fun(<a href="#top_fun-a">a</a>, <a href="#top_fun-b">b</a>, <a href="#top_fun-c">c</a>)
+</pre>
+
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="top_fun-a">
+      <td><code>a</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+    <tr id="top_fun-b">
+      <td><code>b</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+    <tr id="top_fun-c">
+      <td><code>c</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/multiple_files_test/inner_dep.bzl b/test/testdata/multiple_files_test/inner_dep.bzl
new file mode 100644
index 0000000..16d4361
--- /dev/null
+++ b/test/testdata/multiple_files_test/inner_dep.bzl
@@ -0,0 +1,9 @@
+"""A deep dependency file."""
+
+def prep_work():
+    """Does some prep work. Nothing to see here."""
+    return 1
+
+def inner_rule_impl(ctx):
+    _ignore = [ctx]
+    return struct()
diff --git a/test/testdata/multiple_files_test/input.bzl b/test/testdata/multiple_files_test/input.bzl
new file mode 100644
index 0000000..5b46c54
--- /dev/null
+++ b/test/testdata/multiple_files_test/input.bzl
@@ -0,0 +1,41 @@
+"""A direct dependency file of the input file."""
+
+load(":testdata/multiple_files_test/dep.bzl", "my_rule_impl", "some_cool_function")
+
+my_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is my rule. It does stuff.",
+    attrs = {
+        "first": attr.label(
+            mandatory = True,
+            doc = "first my_rule doc string",
+            allow_single_file = True,
+        ),
+        "second": attr.string_dict(mandatory = True),
+    },
+)
+
+def top_fun(a, b, c):
+    some_cool_function(a, b, c)
+    return 6
+
+other_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is another rule.",
+    attrs = {
+        "third": attr.label(
+            mandatory = True,
+            doc = "third other_rule doc string",
+            allow_single_file = True,
+        ),
+        "fourth": attr.string_dict(mandatory = True),
+    },
+)
+
+yet_another_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is yet another rule",
+    attrs = {
+        "fifth": attr.label(mandatory = True, allow_single_file = True),
+    },
+)
diff --git a/test/testdata/multiple_rules_test/golden.txt b/test/testdata/multiple_rules_test/golden.txt
new file mode 100644
index 0000000..4637afa
--- /dev/null
+++ b/test/testdata/multiple_rules_test/golden.txt
@@ -0,0 +1,153 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-first">first</a>, <a href="#my_rule-second">second</a>)
+</pre>
+
+This is my rule. It does stuff.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+    <tr id="my_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#other_rule"></a>
+
+## other_rule
+
+<pre>
+other_rule(<a href="#other_rule-name">name</a>, <a href="#other_rule-fourth">fourth</a>, <a href="#other_rule-third">third</a>)
+</pre>
+
+This is another rule.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="other_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="other_rule-fourth">
+      <td><code>fourth</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+    <tr id="other_rule-third">
+      <td><code>third</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#yet_another_rule"></a>
+
+## yet_another_rule
+
+<pre>
+yet_another_rule(<a href="#yet_another_rule-name">name</a>, <a href="#yet_another_rule-fifth">fifth</a>)
+</pre>
+
+This is yet another rule
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="yet_another_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="yet_another_rule-fifth">
+      <td><code>fifth</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#my_rule_impl"></a>
+
+## my_rule_impl
+
+<pre>
+my_rule_impl(<a href="#my_rule_impl-ctx">ctx</a>)
+</pre>
+
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule_impl-ctx">
+      <td><code>ctx</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/multiple_rules_test/input.bzl b/test/testdata/multiple_rules_test/input.bzl
new file mode 100644
index 0000000..7a2d314
--- /dev/null
+++ b/test/testdata/multiple_rules_test/input.bzl
@@ -0,0 +1,31 @@
+# buildifier: disable=module-docstring
+def my_rule_impl(ctx):
+    return []
+
+my_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is my rule. It does stuff.",
+    attrs = {
+        "first": attr.label(mandatory = True, allow_single_file = True),
+        "second": attr.string_dict(mandatory = True),
+    },
+)
+
+other_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is another rule.",
+    attrs = {
+        "third": attr.label(mandatory = True, allow_single_file = True),
+        "_hidden": attr.string(),
+        "fourth": attr.string_dict(mandatory = True),
+    },
+)
+
+yet_another_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is yet another rule",
+    attrs = {
+        "_hidden": attr.string(),
+        "fifth": attr.label(mandatory = True, allow_single_file = True),
+    },
+)
diff --git a/test/testdata/namespace_test/golden.txt b/test/testdata/namespace_test/golden.txt
new file mode 100644
index 0000000..6fd6bdd
--- /dev/null
+++ b/test/testdata/namespace_test/golden.txt
@@ -0,0 +1,113 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_namespace.assert_non_empty"></a>
+
+## my_namespace.assert_non_empty
+
+<pre>
+my_namespace.assert_non_empty(<a href="#my_namespace.assert_non_empty-some_list">some_list</a>, <a href="#my_namespace.assert_non_empty-other_list">other_list</a>)
+</pre>
+
+Asserts the two given lists are not empty.
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_namespace.assert_non_empty-some_list">
+      <td><code>some_list</code></td>
+      <td>
+        required.
+        <p>
+          The first list
+        </p>
+      </td>
+    </tr>
+    <tr id="my_namespace.assert_non_empty-other_list">
+      <td><code>other_list</code></td>
+      <td>
+        required.
+        <p>
+          The second list
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#my_namespace.min"></a>
+
+## my_namespace.min
+
+<pre>
+my_namespace.min(<a href="#my_namespace.min-integers">integers</a>)
+</pre>
+
+Returns the minimum of given elements.
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_namespace.min-integers">
+      <td><code>integers</code></td>
+      <td>
+        required.
+        <p>
+          A list of integers. Must not be empty.
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#my_namespace.join_strings"></a>
+
+## my_namespace.join_strings
+
+<pre>
+my_namespace.join_strings(<a href="#my_namespace.join_strings-strings">strings</a>, <a href="#my_namespace.join_strings-delimiter">delimiter</a>)
+</pre>
+
+Joins the given strings with a delimiter.
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_namespace.join_strings-strings">
+      <td><code>strings</code></td>
+      <td>
+        required.
+        <p>
+          A list of strings to join.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_namespace.join_strings-delimiter">
+      <td><code>delimiter</code></td>
+      <td>
+        optional. default is <code>", "</code>
+        <p>
+          The delimiter to use
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/namespace_test/input.bzl b/test/testdata/namespace_test/input.bzl
new file mode 100644
index 0000000..69cad64
--- /dev/null
+++ b/test/testdata/namespace_test/input.bzl
@@ -0,0 +1,43 @@
+"""A test that verifies documenting a namespace of functions."""
+
+def _min(integers):
+    """Returns the minimum of given elements.
+
+    Args:
+      integers: A list of integers. Must not be empty.
+
+    Returns:
+      The minimum integer in the given list.
+    """
+    _ignore = [integers]
+    return 42
+
+def _assert_non_empty(some_list, other_list):
+    """Asserts the two given lists are not empty.
+
+    Args:
+      some_list: The first list
+      other_list: The second list
+    """
+    _ignore = [some_list, other_list]
+    fail("Not implemented")
+
+def _join_strings(strings, delimiter = ", "):
+    """Joins the given strings with a delimiter.
+
+    Args:
+      strings: A list of strings to join.
+      delimiter: The delimiter to use
+
+    Returns:
+      The joined string.
+    """
+    _ignore = [strings, delimiter]
+    return ""
+
+my_namespace = struct(
+    dropped_field = "Note this field should not be documented",
+    assert_non_empty = _assert_non_empty,
+    min = _min,
+    join_strings = _join_strings,
+)
diff --git a/test/testdata/proto_format_test/golden.raw b/test/testdata/proto_format_test/golden.raw
new file mode 100644
index 0000000..3c7d256
--- /dev/null
+++ b/test/testdata/proto_format_test/golden.raw
@@ -0,0 +1,16 @@
+
+}
+
+my_exampleSmall example of rule.*
+nameA unique name for this target. +
+uselessThis argument will be ignored.•
+example$Stores information about an example. 
+fooA string representing foo 
+barA string representing bar 
+bazA string representing bazÆ
+check_function%
+fooA unique name for this rule. ÂŒRuns some checks on the given function parameter.
+
+This rule runs checks on a given function parameter.
+Use `bazel build` to run the check.
+* Input file for proto format test
\ No newline at end of file
diff --git a/test/testdata/proto_format_test/input.bzl b/test/testdata/proto_format_test/input.bzl
new file mode 100644
index 0000000..aee171e
--- /dev/null
+++ b/test/testdata/proto_format_test/input.bzl
@@ -0,0 +1,35 @@
+"""Input file for proto format test"""
+
+def check_function(foo):
+    """Runs some checks on the given function parameter.
+
+    This rule runs checks on a given function parameter.
+    Use `bazel build` to run the check.
+
+    Args:
+        foo: A unique name for this rule.
+    """
+    pass
+
+example = provider(
+    doc = "Stores information about an example.",
+    fields = {
+        "foo": "A string representing foo",
+        "bar": "A string representing bar",
+        "baz": "A string representing baz",
+    },
+)
+
+def _rule_impl(ctx):
+    return []
+
+my_example = rule(
+    implementation = _rule_impl,
+    doc = "Small example of rule.",
+    attrs = {
+        "useless": attr.string(
+            doc = "This argument will be ignored.",
+            default = "ignoreme",
+        ),
+    },
+)
diff --git a/test/testdata/provider_basic_test/golden.txt b/test/testdata/provider_basic_test/golden.txt
new file mode 100644
index 0000000..190594c
--- /dev/null
+++ b/test/testdata/provider_basic_test/golden.txt
@@ -0,0 +1,85 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#MyFooInfo"></a>
+
+## MyFooInfo
+
+<pre>
+MyFooInfo(<a href="#MyFooInfo-bar">bar</a>, <a href="#MyFooInfo-baz">baz</a>)
+</pre>
+
+Stores information about a foo.
+
+### Fields
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="MyFooInfo-bar">
+      <td><code>bar</code></td>
+      <td>
+        <p>(Undocumented)</p>
+      </td>
+    </tr>
+    <tr id="MyFooInfo-baz">
+      <td><code>baz</code></td>
+      <td>
+        <p>(Undocumented)</p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#MyPoorlyDocumentedInfo"></a>
+
+## MyPoorlyDocumentedInfo
+
+<pre>
+MyPoorlyDocumentedInfo()
+</pre>
+
+
+
+
+
+<a name="#MyVeryDocumentedInfo"></a>
+
+## MyVeryDocumentedInfo
+
+<pre>
+MyVeryDocumentedInfo(<a href="#MyVeryDocumentedInfo-favorite_food">favorite_food</a>, <a href="#MyVeryDocumentedInfo-favorite_color">favorite_color</a>)
+</pre>
+
+
+A provider with some really neat documentation.
+Look on my works, ye mighty, and despair!
+
+
+### Fields
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="MyVeryDocumentedInfo-favorite_food">
+      <td><code>favorite_food</code></td>
+      <td>
+        <p>A string representing my favorite food</p>
+      </td>
+    </tr>
+    <tr id="MyVeryDocumentedInfo-favorite_color">
+      <td><code>favorite_color</code></td>
+      <td>
+        <p>A string representing my favorite color</p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/provider_basic_test/input.bzl b/test/testdata/provider_basic_test/input.bzl
new file mode 100644
index 0000000..7600f2c
--- /dev/null
+++ b/test/testdata/provider_basic_test/input.bzl
@@ -0,0 +1,18 @@
+# buildifier: disable=module-docstring
+MyPoorlyDocumentedInfo = provider()
+
+MyFooInfo = provider(
+    doc = "Stores information about a foo.",
+    fields = ["bar", "baz"],
+)
+
+MyVeryDocumentedInfo = provider(
+    doc = """
+A provider with some really neat documentation.
+Look on my works, ye mighty, and despair!
+""",
+    fields = {
+        "favorite_food": "A string representing my favorite food",
+        "favorite_color": "A string representing my favorite color",
+    },
+)
diff --git a/test/testdata/providers_for_attributes_test/dep.bzl b/test/testdata/providers_for_attributes_test/dep.bzl
new file mode 100644
index 0000000..56ab3b7
--- /dev/null
+++ b/test/testdata/providers_for_attributes_test/dep.bzl
@@ -0,0 +1,5 @@
+"A file to test providers not defined in the same file."
+
+DepProviderInfo = provider(
+    doc = "This provider does something.",
+)
diff --git a/test/testdata/providers_for_attributes_test/golden.txt b/test/testdata/providers_for_attributes_test/golden.txt
new file mode 100644
index 0000000..fa44df5
--- /dev/null
+++ b/test/testdata/providers_for_attributes_test/golden.txt
@@ -0,0 +1,164 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-fifth">fifth</a>, <a href="#my_rule-first">first</a>, <a href="#my_rule-fourth">fourth</a>, <a href="#my_rule-second">second</a>, <a href="#my_rule-sixth">sixth</a>, <a href="#my_rule-third">third</a>)
+</pre>
+
+This rule does things.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-fifth">
+      <td><code>fifth</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; optional
+        <p>
+          The dependencies of this attribute must provide: LegacyProvider, ObjectProvider; or DefaultInfo, JavaInfo
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: Label -> String</a>; optional
+        <p>
+          this is the first attribute.
+        </p>
+        <p>
+          The dependencies of this attribute must provide: MyProviderInfo, PyInfo, Unknown Provider
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-fourth">
+      <td><code>fourth</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; optional
+        <p>
+          The dependencies of this attribute must provide: ProtoInfo, DefaultInfo, JavaInfo
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a>; optional
+        <p>
+          The dependencies of this attribute must provide: CcInfo; or OtherProviderInfo, DepProviderInfo
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-sixth">
+      <td><code>sixth</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; optional
+        <p>
+          The dependencies of this attribute must provide: LegacyProvider
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-third">
+      <td><code>third</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; optional
+        <p>
+          The dependencies of this attribute must provide: OtherProviderInfo
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#MyProviderInfo"></a>
+
+## MyProviderInfo
+
+<pre>
+MyProviderInfo(<a href="#MyProviderInfo-foo">foo</a>, <a href="#MyProviderInfo-bar">bar</a>)
+</pre>
+
+
+
+### Fields
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="MyProviderInfo-foo">
+      <td><code>foo</code></td>
+      <td>
+        <p>Something foo-related.</p>
+      </td>
+    </tr>
+    <tr id="MyProviderInfo-bar">
+      <td><code>bar</code></td>
+      <td>
+        <p>Something bar-related.</p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
+<a name="#OtherProviderInfo"></a>
+
+## OtherProviderInfo
+
+<pre>
+OtherProviderInfo()
+</pre>
+
+
+
+
+
+<a name="#my_rule_impl"></a>
+
+## my_rule_impl
+
+<pre>
+my_rule_impl(<a href="#my_rule_impl-ctx">ctx</a>)
+</pre>
+
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule_impl-ctx">
+      <td><code>ctx</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/providers_for_attributes_test/input.bzl b/test/testdata/providers_for_attributes_test/input.bzl
new file mode 100644
index 0000000..774a0e9
--- /dev/null
+++ b/test/testdata/providers_for_attributes_test/input.bzl
@@ -0,0 +1,42 @@
+"""The input file for the providers for attributes test"""
+
+load(":testdata/providers_for_attributes_test/dep.bzl", "DepProviderInfo")
+
+def my_rule_impl(ctx):
+    return []
+
+MyProviderInfo = provider(
+    fields = {
+        "foo": "Something foo-related.",
+        "bar": "Something bar-related.",
+    },
+)
+
+OtherProviderInfo = provider()
+other_provider_info = OtherProviderInfo(fields = ["foo"])
+
+my_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This rule does things.",
+    attrs = {
+        "first": attr.label_keyed_string_dict(
+            providers = [MyProviderInfo, PyInfo, cc_common.CcToolchainInfo],
+            doc = "this is the first attribute.",
+        ),
+        "second": attr.label_list(
+            providers = [[CcInfo], [OtherProviderInfo, DepProviderInfo]],
+        ),
+        "third": attr.label(
+            providers = [OtherProviderInfo],
+        ),
+        "fourth": attr.label(
+            providers = [ProtoInfo, DefaultInfo, JavaInfo],
+        ),
+        "fifth": attr.label(
+            providers = [["LegacyProvider", "ObjectProvider"], [DefaultInfo, JavaInfo]],
+        ),
+        "sixth": attr.label(
+            providers = ["LegacyProvider"],
+        ),
+    },
+)
diff --git a/test/testdata/pure_markdown_template_test/golden.txt b/test/testdata/pure_markdown_template_test/golden.txt
new file mode 100644
index 0000000..ecbc516
--- /dev/null
+++ b/test/testdata/pure_markdown_template_test/golden.txt
@@ -0,0 +1,90 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#example_rule"></a>
+
+## example_rule
+
+<pre>
+example_rule(<a href="#example_rule-name">name</a>, <a href="#example_rule-first">first</a>, <a href="#example_rule-second">second</a>)
+</pre>
+
+Small example of rule using a markdown template.
+
+**ATTRIBUTES**
+
+
+| Name  | Description | Type | Mandatory |
+| :-------------: | :-------------: | :-------------: | :-------------: |
+| name |  A unique name for this target.   | <a href="https://bazel.build/docs/build-ref.html#name">Name</a> | required |
+| first |  This is the first attribute   | String | optional |
+| second |  -   | String | optional |
+
+
+<a name="#ExampleProviderInfo"></a>
+
+## ExampleProviderInfo
+
+<pre>
+ExampleProviderInfo(<a href="#ExampleProviderInfo-foo">foo</a>, <a href="#ExampleProviderInfo-bar">bar</a>, <a href="#ExampleProviderInfo-baz">baz</a>)
+</pre>
+
+Small example of provider using a markdown template.
+
+**FIELDS**
+
+
+| Name  | Description |
+| :-------------: | :-------------: |
+| foo |  A string representing foo    |
+| bar |  A string representing bar    |
+| baz |  A string representing baz    |
+
+
+<a name="#example_function"></a>
+
+## example_function
+
+<pre>
+example_function(<a href="#example_function-foo">foo</a>, <a href="#example_function-bar">bar</a>)
+</pre>
+
+Small example of function using a markdown template.
+
+**PARAMETERS**
+
+
+| Name  | Description | Default Value |
+| :-------------: | :-------------: | :-------------: |
+| foo |  This parameter does foo related things.   |  none |
+| bar |  This parameter does bar related things.   |  <code>"bar"</code> |
+
+
+<a name="#example_aspect"></a>
+
+## example_aspect
+
+<pre>
+example_aspect(<a href="#example_aspect-name">name</a>, <a href="#example_aspect-first">first</a>, <a href="#example_aspect-second">second</a>)
+</pre>
+
+Small example of aspect using a markdown template.
+
+**ASPECT ATTRIBUTES**
+
+
+| Name | Type |
+| :-------------: | :-------------: |
+| deps| String |
+| attr_aspect| String |
+
+
+**ATTRIBUTES**
+
+
+| Name  | Description | Type | Mandatory |
+| :-------------: | :-------------: | :-------------: | :-------------: |
+| name |  A unique name for this target.   | <a href="https://bazel.build/docs/build-ref.html#name">Name</a> | required |
+| first |  -   | <a href="https://bazel.build/docs/build-ref.html#labels">Label</a> | required |
+| second |  This is the second attribute.   | String | optional |
+
+
diff --git a/test/testdata/pure_markdown_template_test/input.bzl b/test/testdata/pure_markdown_template_test/input.bzl
new file mode 100644
index 0000000..23f3379
--- /dev/null
+++ b/test/testdata/pure_markdown_template_test/input.bzl
@@ -0,0 +1,44 @@
+"""Input file for markdown template test"""
+
+def example_function(foo, bar = "bar"):
+    """Small example of function using a markdown template.
+
+    Args:
+        foo: This parameter does foo related things.
+        bar: This parameter does bar related things.
+    """
+    pass
+
+ExampleProviderInfo = provider(
+    doc = "Small example of provider using a markdown template.",
+    fields = {
+        "foo": "A string representing foo",
+        "bar": "A string representing bar",
+        "baz": "A string representing baz",
+    },
+)
+
+def _rule_impl(ctx):
+    return []
+
+example_rule = rule(
+    implementation = _rule_impl,
+    doc = "Small example of rule using a markdown template.",
+    attrs = {
+        "first": attr.string(doc = "This is the first attribute"),
+        "second": attr.string(default = "2"),
+    },
+)
+
+def _aspect_impl(ctx):
+    return []
+
+example_aspect = aspect(
+    implementation = _aspect_impl,
+    doc = "Small example of aspect using a markdown template.",
+    attr_aspects = ["deps", "attr_aspect"],
+    attrs = {
+        "first": attr.label(mandatory = True, allow_single_file = True),
+        "second": attr.string(doc = "This is the second attribute."),
+    },
+)
diff --git a/test/testdata/py_rule_test/golden.txt b/test/testdata/py_rule_test/golden.txt
new file mode 100644
index 0000000..76ffdb8
--- /dev/null
+++ b/test/testdata/py_rule_test/golden.txt
@@ -0,0 +1,81 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#py_related_rule"></a>
+
+## py_related_rule
+
+<pre>
+py_related_rule(<a href="#py_related_rule-name">name</a>, <a href="#py_related_rule-fifth">fifth</a>, <a href="#py_related_rule-first">first</a>, <a href="#py_related_rule-fourth">fourth</a>, <a href="#py_related_rule-second">second</a>, <a href="#py_related_rule-sixth">sixth</a>, <a href="#py_related_rule-third">third</a>)
+</pre>
+
+This rule does python-related things.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="py_related_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="py_related_rule-fifth">
+      <td><code>fifth</code></td>
+      <td>
+        Boolean; optional
+        <p>
+          Hey look, its the fifth thing!
+        </p>
+      </td>
+    </tr>
+    <tr id="py_related_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+        <p>
+          this is the first doc string!
+        </p>
+      </td>
+    </tr>
+    <tr id="py_related_rule-fourth">
+      <td><code>fourth</code></td>
+      <td>
+        Boolean; optional
+        <p>
+          the fourth doc string.
+        </p>
+      </td>
+    </tr>
+    <tr id="py_related_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+    <tr id="py_related_rule-sixth">
+      <td><code>sixth</code></td>
+      <td>
+        List of integers; optional
+        <p>
+          it's the sixth thing.
+        </p>
+      </td>
+    </tr>
+    <tr id="py_related_rule-third">
+      <td><code>third</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/py_rule_test/input.bzl b/test/testdata/py_rule_test/input.bzl
new file mode 100644
index 0000000..0d6bc16
--- /dev/null
+++ b/test/testdata/py_rule_test/input.bzl
@@ -0,0 +1,32 @@
+"""The input file for the python rule test"""
+
+def exercise_the_api():
+    var1 = PyRuntimeInfo
+    var2 = PyInfo
+
+exercise_the_api()
+
+def my_rule_impl(ctx):
+    return []
+
+py_related_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This rule does python-related things.",
+    attrs = {
+        "first": attr.label(
+            mandatory = True,
+            doc = "this is the first doc string!",
+            allow_single_file = True,
+        ),
+        "second": attr.string_dict(mandatory = True),
+        "third": attr.output(mandatory = True),
+        "fourth": attr.bool(default = False, doc = "the fourth doc string.", mandatory = False),
+        "fifth": attr.bool(default = True, doc = "Hey look, its the fifth thing!"),
+        "sixth": attr.int_list(
+            default = range(10),
+            doc = "it's the sixth thing.",
+            mandatory = False,
+        ),
+        "_hidden": attr.string(),
+    },
+)
diff --git a/test/testdata/repo_rules_test/golden.txt b/test/testdata/repo_rules_test/golden.txt
new file mode 100644
index 0000000..dc84d8e
--- /dev/null
+++ b/test/testdata/repo_rules_test/golden.txt
@@ -0,0 +1,42 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_repo"></a>
+
+## my_repo
+
+<pre>
+my_repo(<a href="#my_repo-name">name</a>, <a href="#my_repo-useless">useless</a>)
+</pre>
+
+Minimal example of a repository rule.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_repo-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this repository.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_repo-useless">
+      <td><code>useless</code></td>
+      <td>
+        String; optional
+        <p>
+          This argument will be ingored. You don't have to specify it, but you may.
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/repo_rules_test/input.bzl b/test/testdata/repo_rules_test/input.bzl
new file mode 100644
index 0000000..023d92d
--- /dev/null
+++ b/test/testdata/repo_rules_test/input.bzl
@@ -0,0 +1,14 @@
+# buildifier: disable=module-docstring
+def _repo_rule_impl(ctx):
+    ctx.file("BUILD", "")
+
+my_repo = repository_rule(
+    implementation = _repo_rule_impl,
+    doc = "Minimal example of a repository rule.",
+    attrs = {
+        "useless": attr.string(
+            doc = "This argument will be ingored. You don't have to specify it, but you may.",
+            default = "ignoreme",
+        ),
+    },
+)
diff --git a/test/testdata/same_level_file_test/BUILD b/test/testdata/same_level_file_test/BUILD
new file mode 100644
index 0000000..97c2e46
--- /dev/null
+++ b/test/testdata/same_level_file_test/BUILD
@@ -0,0 +1,14 @@
+filegroup(
+    name = "srcs",
+    testonly = 0,
+    srcs = glob(["**"]),
+    visibility = ["//src:__subpackages__"],
+)
+
+exports_files(
+    [
+        "dep.bzl",
+        "golden.txt",
+        "input.bzl",
+    ],
+)
diff --git a/test/testdata/same_level_file_test/dep.bzl b/test/testdata/same_level_file_test/dep.bzl
new file mode 100644
index 0000000..87750b3
--- /dev/null
+++ b/test/testdata/same_level_file_test/dep.bzl
@@ -0,0 +1,5 @@
+# buildifier: disable=module-docstring
+# buildifier: disable=function-docstring
+def my_rule_impl(ctx):
+    _ignore = [ctx]
+    return struct()
diff --git a/test/testdata/same_level_file_test/golden.txt b/test/testdata/same_level_file_test/golden.txt
new file mode 100644
index 0000000..cf02e4c
--- /dev/null
+++ b/test/testdata/same_level_file_test/golden.txt
@@ -0,0 +1,48 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-first">first</a>, <a href="#my_rule-second">second</a>)
+</pre>
+
+This is my rule. It does stuff.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+        <p>
+          first my_rule doc string
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/same_level_file_test/input.bzl b/test/testdata/same_level_file_test/input.bzl
new file mode 100644
index 0000000..b1f67fb
--- /dev/null
+++ b/test/testdata/same_level_file_test/input.bzl
@@ -0,0 +1,15 @@
+# buildifier: disable=module-docstring
+load(":dep.bzl", "my_rule_impl")
+
+my_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is my rule. It does stuff.",
+    attrs = {
+        "first": attr.label(
+            mandatory = True,
+            doc = "first my_rule doc string",
+            allow_single_file = True,
+        ),
+        "second": attr.string_dict(mandatory = True),
+    },
+)
diff --git a/test/testdata/simple_test/golden.txt b/test/testdata/simple_test/golden.txt
new file mode 100644
index 0000000..59a0cf5
--- /dev/null
+++ b/test/testdata/simple_test/golden.txt
@@ -0,0 +1,63 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_rule"></a>
+
+## my_rule
+
+<pre>
+my_rule(<a href="#my_rule-name">name</a>, <a href="#my_rule-first">first</a>, <a href="#my_rule-fourth">fourth</a>, <a href="#my_rule-second">second</a>, <a href="#my_rule-third">third</a>)
+</pre>
+
+This is my rule. It does stuff.
+
+### Attributes
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule-name">
+      <td><code>name</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#name">Name</a>; required
+        <p>
+          A unique name for this target.
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-first">
+      <td><code>first</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+        <p>
+          first doc string
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-fourth">
+      <td><code>fourth</code></td>
+      <td>
+        Boolean; optional
+        <p>
+          fourth doc string
+        </p>
+      </td>
+    </tr>
+    <tr id="my_rule-second">
+      <td><code>second</code></td>
+      <td>
+        <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a>; required
+      </td>
+    </tr>
+    <tr id="my_rule-third">
+      <td><code>third</code></td>
+      <td>
+        <a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; required
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/simple_test/input.bzl b/test/testdata/simple_test/input.bzl
new file mode 100644
index 0000000..fe0ffc7
--- /dev/null
+++ b/test/testdata/simple_test/input.bzl
@@ -0,0 +1,19 @@
+# buildifier: disable=module-docstring
+def my_rule_impl(ctx):
+    return []
+
+my_rule = rule(
+    implementation = my_rule_impl,
+    doc = "This is my rule. It does stuff.",
+    attrs = {
+        "first": attr.label(
+            mandatory = True,
+            doc = "first doc string",
+            allow_single_file = True,
+        ),
+        "second": attr.string_dict(mandatory = True),
+        "third": attr.output(mandatory = True),
+        "fourth": attr.bool(default = False, doc = "fourth doc string", mandatory = False),
+        "_hidden": attr.string(),
+    },
+)
diff --git a/test/testdata/struct_default_value_test/golden.txt b/test/testdata/struct_default_value_test/golden.txt
new file mode 100644
index 0000000..44fe4a5
--- /dev/null
+++ b/test/testdata/struct_default_value_test/golden.txt
@@ -0,0 +1,69 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#check_struct_default_values"></a>
+
+## check_struct_default_values
+
+<pre>
+check_struct_default_values(<a href="#check_struct_default_values-struct_no_args">struct_no_args</a>, <a href="#check_struct_default_values-struct_arg">struct_arg</a>, <a href="#check_struct_default_values-struct_args">struct_args</a>, <a href="#check_struct_default_values-struct_int_args">struct_int_args</a>, <a href="#check_struct_default_values-struct_struct_args">struct_struct_args</a>)
+</pre>
+
+Checks the default values of structs.
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="check_struct_default_values-struct_no_args">
+      <td><code>struct_no_args</code></td>
+      <td>
+        optional. default is <code>struct()</code>
+        <p>
+          struct with no arguments
+        </p>
+      </td>
+    </tr>
+    <tr id="check_struct_default_values-struct_arg">
+      <td><code>struct_arg</code></td>
+      <td>
+        optional. default is <code>struct(foo = "bar")</code>
+        <p>
+          struct with one argument
+        </p>
+      </td>
+    </tr>
+    <tr id="check_struct_default_values-struct_args">
+      <td><code>struct_args</code></td>
+      <td>
+        optional. default is <code>struct(bar = "foo", foo = "bar")</code>
+        <p>
+          struct with multiple arguments
+        </p>
+      </td>
+    </tr>
+    <tr id="check_struct_default_values-struct_int_args">
+      <td><code>struct_int_args</code></td>
+      <td>
+        optional. default is <code>struct(one = 1, three = 3, two = 2)</code>
+        <p>
+          struct with int arguments
+        </p>
+      </td>
+    </tr>
+    <tr id="check_struct_default_values-struct_struct_args">
+      <td><code>struct_struct_args</code></td>
+      <td>
+        optional. default is <code>struct(multiple = struct(one = 1, three = 3, two = 2), none = struct(), one = struct(foo = "bar"))</code>
+        <p>
+          struct with struct arguments
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/struct_default_value_test/input.bzl b/test/testdata/struct_default_value_test/input.bzl
new file mode 100644
index 0000000..ead514b
--- /dev/null
+++ b/test/testdata/struct_default_value_test/input.bzl
@@ -0,0 +1,22 @@
+"""The input file for struct default values test"""
+
+def check_struct_default_values(
+        struct_no_args = struct(),
+        struct_arg = struct(foo = "bar"),
+        struct_args = struct(foo = "bar", bar = "foo"),
+        struct_int_args = struct(one = 1, two = 2, three = 3),
+        struct_struct_args = struct(
+            none = struct(),
+            one = struct(foo = "bar"),
+            multiple = struct(one = 1, two = 2, three = 3),
+        )):
+    """Checks the default values of structs.
+
+    Args:
+        struct_no_args: struct with no arguments
+        struct_arg: struct with one argument
+        struct_args: struct with multiple arguments
+        struct_int_args: struct with int arguments
+        struct_struct_args: struct with struct arguments
+    """
+    pass
diff --git a/test/testdata/unknown_name_test/golden.txt b/test/testdata/unknown_name_test/golden.txt
new file mode 100644
index 0000000..99e1fbd
--- /dev/null
+++ b/test/testdata/unknown_name_test/golden.txt
@@ -0,0 +1,30 @@
+<!-- Generated with Stardoc: http://skydoc.bazel.build -->
+
+<a name="#my_rule_impl"></a>
+
+## my_rule_impl
+
+<pre>
+my_rule_impl(<a href="#my_rule_impl-ctx">ctx</a>)
+</pre>
+
+
+
+### Parameters
+
+<table class="params-table">
+  <colgroup>
+    <col class="col-param" />
+    <col class="col-description" />
+  </colgroup>
+  <tbody>
+    <tr id="my_rule_impl-ctx">
+      <td><code>ctx</code></td>
+      <td>
+        required.
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+
diff --git a/test/testdata/unknown_name_test/input.bzl b/test/testdata/unknown_name_test/input.bzl
new file mode 100644
index 0000000..062f49d
--- /dev/null
+++ b/test/testdata/unknown_name_test/input.bzl
@@ -0,0 +1,13 @@
+# buildifier: disable=module-docstring
+def my_rule_impl(ctx):
+    return []
+
+rule(
+    implementation = my_rule_impl,
+    attrs = {
+        "first": attr.label(mandatory = True, allow_single_file = True),
+        "second": attr.string_dict(mandatory = True),
+        "third": attr.output(mandatory = True),
+        "fourth": attr.bool(default = False, mandatory = False),
+    },
+)