/* Copyright 2020 Google LLC 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 https://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. */ package warn import "testing" func TestModuleDocstring(t *testing.T) { checkFindings(t, "module-docstring", ``, []string{}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` # empty file`, []string{}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` """This is the module""" load("foo", "bar") bar()`, []string{}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` load("foo", "bar") """This is the module""" bar()`, []string{":1: The file has no module docstring."}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` # comment # comment """This is the module""" load("foo", "bar") bar()`, []string{}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` # comment load("foo", "bar") # comment """This is the module""" bar()`, []string{":3: The file has no module docstring."}, scopeBzl|scopeDefault) checkFindings(t, "module-docstring", ` def foo(bar): if bar: f() return g()`, []string{":1: The file has no module docstring."}, scopeBzl|scopeDefault) } func TestFunctionDocstringExists(t *testing.T) { checkFindings(t, "function-docstring", ` def f(x): # short function return x `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring", ` def f(x): """Short function with a docstring""" return x `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring", ` def f(x): # long function x += 1 x *= 2 x /= 3 x -= 4 x %= 5 return x `, []string{":1: The function \"f\" has no docstring."}, scopeEverywhere) checkFindings(t, "function-docstring", ` def f(x): def g(x): # long function x += 1 x *= 2 x /= 3 x -= 4 x %= 5 return x return g `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring", ` def _f(x): # long private function x += 1 x *= 2 x /= 3 x -= 4 x %= 5 return x `, []string{}, scopeEverywhere) } func TestFunctionDocstringHeader(t *testing.T) { checkFindings(t, "function-docstring-header", ` def f(): """This is a function. this is the description """ pass pass pass pass pass `, []string{`2: The docstring for the function "f" should start with a one-line summary.`}, scopeEverywhere) checkFindings(t, "function-docstring-header", ` def f(): def g(): """This is a function. this is the description """ pass pass pass pass pass return f `, []string{`3: The docstring for the function "g" should start with a one-line summary.`}, scopeEverywhere) checkFindings(t, "function-docstring-header", ` def _f(x): """Long private function with a docstring""" x += 1 x *= 2 x /= 3 x -= 4 x %= 5 return x `, []string{ `:2: The docstring for the function "_f" should start with a one-line summary.`, }, scopeEverywhere) checkFindings(t, "function-docstring-header", ` def f(x): """Long function with a docstring Docstring body """ x += 1 x *= 2 x /= 3 x -= 4 x %= 5 return x `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-header", ` def f(): """ This is a function. This is a multiline description""" pass pass pass pass pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-header", ` def f(): """\r Header in a CRLF formatted file.\r \r This is a\r multiline description""" `, []string{}, scopeEverywhere) } func TestFunctionDocstringArgs(t *testing.T) { checkFindings(t, "function-docstring-args", ` def f(x): """This is a function. Documented here: http://example.com Args: x: something, as described at http://example.com Returns: something, as described at https://example.com """ pass pass pass pass pass return x `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x): """This is a function. Args: x: something """ passf pass pass pass pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x, y): """Short function with a docstring Arguments: x: smth """ return x + y `, []string{ `2: Argument "y" is not documented.`, `4: Prefer "Args:" to "Arguments:" when documenting function arguments.`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(): def g(x, y): """Short function with a docstring Arguments: x: smth """ return x + y return g `, []string{ `3: Argument "y" is not documented.`, `5: Prefer "Args:" to "Arguments:" when documenting function arguments.`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(): def g(x, y): """Short function with a docstring """ return x + y return g `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def _f(x, y): """Long private function Args: x: something z: something """ x *= 2 x /= 3 x -= 4 x %= 5 return x `, []string{ `:2: Argument "y" is not documented.`, `:6: Argument "z" is documented but doesn't exist in the function signature.`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x, y): """This is a function. Arguments: x: something y: something (this is in fact the description of x continued) z: something else Returns: None """ pass pass pass pass pass `, []string{ `2: Argument "y" is not documented.`, `4: Prefer "Args:" to "Arguments:" when documenting function arguments.`, `7: Argument "z" is documented but doesn't exist in the function signature.`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def my_function(x, y, z = None, *args, **kwargs): """This is a function. """ pass pass pass pass pass `, []string{ `2: Arguments "x", "y", "z", "*args", "**kwargs" are not documented. If the documentation for the arguments exists but is not recognized by Buildifier make sure it follows the line "Args:" which has the same indentation as the opening """, and the argument description starts with ":" and indented with at least one (preferably two) space more than "Args:", for example: def my_function(x): """Function description. Args: x: argument description, can be multiline with additional indentation. """`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x, y, z = None, *args, **kwargs): """This is a function. Args: x (Map[string, int]): x y (deprecated, mutable): y z: z *args (List): the args **kwargs: the kwargs """ pass pass pass pass pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x, *, y, z = None): """This is a function. Args: x: x y: y z: z """ pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x, *, y, z = None): """This is a function. Args: x: x *: a separator y: y : argument without a name z: z """ pass `, []string{ `6: Argument "*" is documented but doesn't exist in the function signature.`, `8: Argument "" is documented but doesn't exist in the function signature.`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x): """ This is a function. Args: The function signature is extremely complicated x: something Returns: nothing """ pass pass pass pass pass return None `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(foobar, *bar, **baz): """Some function Args: foobar: something foo: something bar: something baz: something """ pass `, []string{ `:2: Arguments "*bar", "**baz" are not documented.`, `:6: Argument "foo" is documented but doesn't exist in the function signature.`, `:7: Argument "bar" is documented but doesn't exist in the function signature. Do you mean "*bar"?`, `:8: Argument "baz" is documented but doesn't exist in the function signature. Do you mean "**baz"?`, }, scopeEverywhere) checkFindings(t, "function-docstring-args", ` def f(x: int, y: str, z: bool = False, *, *bar: List[int], **baz: Mapping[str, bool]): """Some function Args: x: something t: something """ pass `, []string{ `:2: Arguments "y", "z", "*bar", "**baz" are not documented.`, `:6: Argument "t" is documented but doesn't exist in the function signature.`, }, scopeEverywhere) } func TestFunctionDocstringReturn(t *testing.T) { checkFindings(t, "function-docstring-return", ` def f(x): """This is a function. Args: x: something Returns: something """ pass pass pass pass pass return x `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-return", ` def f(x): """This is a function. Args: x: something """ pass pass pass pass pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-return", ` def f(x): """This is a function. Args: x: something """ def g(y): return y pass pass pass pass pass `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-return", ` def f(x): """This is a function. Args: x: something """ pass pass pass pass pass return x `, []string{`2: Return value of "f" is not documented.`}, scopeEverywhere) checkFindings(t, "function-docstring-return", ` def f(): def g(x): """This is a function. Args: x: something """ pass pass pass pass pass return x return g `, []string{}, scopeEverywhere) checkFindings(t, "function-docstring-return", ` def f(x): """This is a function. Args: x: something """ pass pass pass pass pass if foo: return else: return x `, []string{`2: Return value of "f" is not documented.`}, scopeEverywhere) }