Reformat .bzl files with buildifier and add format check.

Buildifier 0.12.0 includes initial support for reformatting .bzl files.
- Reformat all the bzl files.
- Expand the travis check to check the .bzl files also.
This commit is contained in:
Thomas Van Lenten 2018-06-12 13:09:57 -04:00
parent a5431b7bab
commit e5203c0f5d
26 changed files with 1727 additions and 1770 deletions

View File

@ -26,9 +26,14 @@ fi
# Asked to do a buildifier run. # Asked to do a buildifier run.
if [[ -n "${BUILDIFER:-}" ]]; then if [[ -n "${BUILDIFER:-}" ]]; then
# bazelbuild/buildtools/issues/220 - diff doesn't include the file that needs updating # bazelbuild/buildtools/issues/220 - diff doesn't include the file that needs updating
if ! find . -name BUILD -print | xargs buildifier -d > /dev/null 2>&1 ; then if ! find . \( -name BUILD -o -name "*.bzl" \) -print | xargs buildifier -d > /dev/null 2>&1 ; then
echo "ERROR: BUILD file formatting issue(s)" echo "ERROR: BUILD/.bzl file formatting issue(s):"
find . -name BUILD -print -exec buildifier -v -d {} \; echo ""
find . \( -name BUILD -o -name "*.bzl" \) -print -exec buildifier -v -d {} \;
echo ""
echo "Please download the latest buildifier"
echo " https://github.com/bazelbuild/buildtools/releases"
echo "and run it over the changed BUILD/.bzl files."
exit 1 exit 1
fi fi
fi fi

25
lib.bzl
View File

@ -14,22 +14,21 @@
"""Index from which multiple modules can be loaded.""" """Index from which multiple modules can be loaded."""
load("//lib:collections.bzl", _collections="collections") load("//lib:collections.bzl", _collections = "collections")
load("//lib:dicts.bzl", _dicts="dicts") load("//lib:dicts.bzl", _dicts = "dicts")
load("//lib:new_sets.bzl", _new_sets="sets") load("//lib:new_sets.bzl", _new_sets = "sets")
load("//lib:partial.bzl", _partial="partial") load("//lib:partial.bzl", _partial = "partial")
load("//lib:paths.bzl", _paths="paths") load("//lib:paths.bzl", _paths = "paths")
load("//lib:selects.bzl", _selects="selects") load("//lib:selects.bzl", _selects = "selects")
load("//lib:sets.bzl", _sets="sets") load("//lib:sets.bzl", _sets = "sets")
load("//lib:shell.bzl", _shell="shell") load("//lib:shell.bzl", _shell = "shell")
load("//lib:structs.bzl", _structs="structs") load("//lib:structs.bzl", _structs = "structs")
load("//lib:types.bzl", _types="types") load("//lib:types.bzl", _types = "types")
load("//lib:versions.bzl", _versions="versions") load("//lib:versions.bzl", _versions = "versions")
# The unittest module is treated differently to give more convenient names to # The unittest module is treated differently to give more convenient names to
# the assert functions, while keeping them in the same .bzl file. # the assert functions, while keeping them in the same .bzl file.
load("//lib:unittest.bzl", _asserts="asserts", _unittest="unittest") load("//lib:unittest.bzl", _asserts = "asserts", _unittest = "unittest")
collections = _collections collections = _collections
dicts = _dicts dicts = _dicts

View File

@ -14,60 +14,56 @@
"""Skylib module containing functions that operate on collections.""" """Skylib module containing functions that operate on collections."""
def _after_each(separator, iterable): def _after_each(separator, iterable):
"""Inserts `separator` after each item in `iterable`. """Inserts `separator` after each item in `iterable`.
Args: Args:
separator: The value to insert after each item in `iterable`. separator: The value to insert after each item in `iterable`.
iterable: The list into which to intersperse the separator. iterable: The list into which to intersperse the separator.
Returns: Returns:
A new list with `separator` after each item in `iterable`. A new list with `separator` after each item in `iterable`.
""" """
result = [] result = []
for x in iterable: for x in iterable:
result.append(x) result.append(x)
result.append(separator) result.append(separator)
return result
return result
def _before_each(separator, iterable): def _before_each(separator, iterable):
"""Inserts `separator` before each item in `iterable`. """Inserts `separator` before each item in `iterable`.
Args: Args:
separator: The value to insert before each item in `iterable`. separator: The value to insert before each item in `iterable`.
iterable: The list into which to intersperse the separator. iterable: The list into which to intersperse the separator.
Returns: Returns:
A new list with `separator` before each item in `iterable`. A new list with `separator` before each item in `iterable`.
""" """
result = [] result = []
for x in iterable: for x in iterable:
result.append(separator) result.append(separator)
result.append(x) result.append(x)
return result
return result
def _uniq(iterable): def _uniq(iterable):
"""Returns a list of unique elements in `iterable`. """Returns a list of unique elements in `iterable`.
Requires all the elements to be hashable. Requires all the elements to be hashable.
Args: Args:
iterable: An iterable to filter. iterable: An iterable to filter.
Returns:
A new list with all unique elements from `iterable`.
"""
unique_elements = {element: None for element in iterable}
return unique_elements.keys()
Returns:
A new list with all unique elements from `iterable`.
"""
unique_elements = {element: None for element in iterable}
return unique_elements.keys()
collections = struct( collections = struct(
after_each=_after_each, after_each = _after_each,
before_each=_before_each, before_each = _before_each,
uniq=_uniq, uniq = _uniq,
) )

View File

@ -14,30 +14,28 @@
"""Skylib module containing functions that operate on dictionaries.""" """Skylib module containing functions that operate on dictionaries."""
def _add(*dictionaries): def _add(*dictionaries):
"""Returns a new `dict` that has all the entries of the given dictionaries. """Returns a new `dict` that has all the entries of the given dictionaries.
If the same key is present in more than one of the input dictionaries, the If the same key is present in more than one of the input dictionaries, the
last of them in the argument list overrides any earlier ones. last of them in the argument list overrides any earlier ones.
This function is designed to take zero or one arguments as well as multiple This function is designed to take zero or one arguments as well as multiple
dictionaries, so that it follows arithmetic identities and callers can avoid dictionaries, so that it follows arithmetic identities and callers can avoid
special cases for their inputs: the sum of zero dictionaries is the empty special cases for their inputs: the sum of zero dictionaries is the empty
dictionary, and the sum of a single dictionary is a copy of itself. dictionary, and the sum of a single dictionary is a copy of itself.
Args: Args:
*dictionaries: Zero or more dictionaries to be added. *dictionaries: Zero or more dictionaries to be added.
Returns:
A new `dict` that has all the entries of the given dictionaries.
"""
result = {}
for d in dictionaries:
result.update(d)
return result
Returns:
A new `dict` that has all the entries of the given dictionaries.
"""
result = {}
for d in dictionaries:
result.update(d)
return result
dicts = struct( dicts = struct(
add=_add, add = _add,
) )

View File

@ -22,230 +22,215 @@
load(":dicts.bzl", "dicts") load(":dicts.bzl", "dicts")
def _make(elements = None):
"""Creates a new set.
def _make(elements=None): All elements must be hashable.
"""Creates a new set.
All elements must be hashable. Args:
elements: Optional sequence to construct the set out of.
Args:
elements: Optional sequence to construct the set out of.
Returns:
A set containing the passed in values.
"""
elements = elements if elements else []
return struct(_values = {e: None for e in elements})
Returns:
A set containing the passed in values.
"""
elements = elements if elements else []
return struct(_values = {e: None for e in elements})
def _copy(s): def _copy(s):
"""Creates a new set from another set. """Creates a new set from another set.
Args: Args:
s: A set, as returned by `sets.make()`. s: A set, as returned by `sets.make()`.
Returns:
A new set containing the same elements as `s`.
"""
return struct(_values = dict(s._values))
Returns:
A new set containing the same elements as `s`.
"""
return struct(_values = dict(s._values))
def _to_list(s): def _to_list(s):
"""Creates a list from the values in the set. """Creates a list from the values in the set.
Args: Args:
s: A set, as returned by `sets.make()`. s: A set, as returned by `sets.make()`.
Returns:
A list of values inserted into the set.
"""
return s._values.keys()
Returns:
A list of values inserted into the set.
"""
return s._values.keys()
def _insert(s, e): def _insert(s, e):
"""Inserts an element into the set. """Inserts an element into the set.
Element must be hashable. This mutates the orginal set. Element must be hashable. This mutates the orginal set.
Args: Args:
s: A set, as returned by `sets.make()`. s: A set, as returned by `sets.make()`.
e: The element to be inserted. e: The element to be inserted.
Returns:
The set `s` with `e` included.
"""
s._values[e] = None
return s
Returns:
The set `s` with `e` included.
"""
s._values[e] = None
return s
def _remove(s, e): def _remove(s, e):
"""Removes an element from the set. """Removes an element from the set.
Element must be hashable. This mutates the orginal set. Element must be hashable. This mutates the orginal set.
Args: Args:
s: A set, as returned by `sets.make()`. s: A set, as returned by `sets.make()`.
e: The element to be removed. e: The element to be removed.
Returns:
The set `s` with `e` removed.
"""
s._values.pop(e)
return s
Returns:
The set `s` with `e` removed.
"""
s._values.pop(e)
return s
def _contains(a, e): def _contains(a, e):
"""Checks for the existence of an element in a set. """Checks for the existence of an element in a set.
Args: Args:
a: A set, as returned by `sets.make()`. a: A set, as returned by `sets.make()`.
e: The element to look for. e: The element to look for.
Returns:
True if the element exists in the set, False if the element does not.
"""
return e in a._values
Returns:
True if the element exists in the set, False if the element does not.
"""
return e in a._values
def _get_shorter_and_longer(a, b): def _get_shorter_and_longer(a, b):
"""Returns two sets in the order of shortest and longest. """Returns two sets in the order of shortest and longest.
Args: Args:
a: A set, as returned by `sets.make()`. a: A set, as returned by `sets.make()`.
b: A set, as returned by `sets.make()`. b: A set, as returned by `sets.make()`.
Returns:
`a`, `b` if `a` is shorter than `b` - or `b`, `a` if `b` is shorter than `a`.
"""
if _length(a) < _length(b):
return a, b
return b, a
Returns:
`a`, `b` if `a` is shorter than `b` - or `b`, `a` if `b` is shorter than `a`.
"""
if _length(a) < _length(b):
return a, b
return b, a
def _is_equal(a, b): def _is_equal(a, b):
"""Returns whether two sets are equal. """Returns whether two sets are equal.
Args: Args:
a: A set, as returned by `sets.make()`. a: A set, as returned by `sets.make()`.
b: A set, as returned by `sets.make()`. b: A set, as returned by `sets.make()`.
Returns:
True if `a` is equal to `b`, False otherwise.
"""
return a._values == b._values
Returns:
True if `a` is equal to `b`, False otherwise.
"""
return a._values == b._values
def _is_subset(a, b): def _is_subset(a, b):
"""Returns whether `a` is a subset of `b`. """Returns whether `a` is a subset of `b`.
Args: Args:
a: A set, as returned by `sets.make()`. a: A set, as returned by `sets.make()`.
b: A set, as returned by `sets.make()`. b: A set, as returned by `sets.make()`.
Returns:
True if `a` is a subset of `b`, False otherwise.
"""
for e in a._values.keys():
if e not in b._values:
return False
return True
Returns:
True if `a` is a subset of `b`, False otherwise.
"""
for e in a._values.keys():
if e not in b._values:
return False
return True
def _disjoint(a, b): def _disjoint(a, b):
"""Returns whether two sets are disjoint. """Returns whether two sets are disjoint.
Two sets are disjoint if they have no elements in common. Two sets are disjoint if they have no elements in common.
Args: Args:
a: A set, as returned by `sets.make()`. a: A set, as returned by `sets.make()`.
b: A set, as returned by `sets.make()`. b: A set, as returned by `sets.make()`.
Returns:
True if `a` and `b` are disjoint, False otherwise.
"""
shorter, longer = _get_shorter_and_longer(a, b)
for e in shorter._values.keys():
if e in longer._values:
return False
return True
Returns:
True if `a` and `b` are disjoint, False otherwise.
"""
shorter, longer = _get_shorter_and_longer(a, b)
for e in shorter._values.keys():
if e in longer._values:
return False
return True
def _intersection(a, b): def _intersection(a, b):
"""Returns the intersection of two sets. """Returns the intersection of two sets.
Args: Args:
a: A set, as returned by `sets.make()`. a: A set, as returned by `sets.make()`.
b: A set, as returned by `sets.make()`. b: A set, as returned by `sets.make()`.
Returns:
A set containing the elements that are in both `a` and `b`.
"""
shorter, longer = _get_shorter_and_longer(a, b)
return struct(_values = {e: None for e in shorter._values.keys() if e in longer._values})
Returns:
A set containing the elements that are in both `a` and `b`.
"""
shorter, longer = _get_shorter_and_longer(a, b)
return struct(_values = {e: None for e in shorter._values.keys() if e in longer._values})
def _union(*args): def _union(*args):
"""Returns the union of several sets. """Returns the union of several sets.
Args: Args:
*args: An arbitrary number of sets or lists. *args: An arbitrary number of sets or lists.
Returns:
The set union of all sets or lists in `*args`.
"""
return struct(_values = dicts.add(*[s._values for s in args]))
Returns:
The set union of all sets or lists in `*args`.
"""
return struct(_values = dicts.add(*[s._values for s in args]))
def _difference(a, b): def _difference(a, b):
"""Returns the elements in `a` that are not in `b`. """Returns the elements in `a` that are not in `b`.
Args: Args:
a: A set, as returned by `sets.make()`. a: A set, as returned by `sets.make()`.
b: A set, as returned by `sets.make()`. b: A set, as returned by `sets.make()`.
Returns:
A set containing the elements that are in `a` but not in `b`.
"""
return struct(_values = {e: None for e in a._values.keys() if e not in b._values})
Returns:
A set containing the elements that are in `a` but not in `b`.
"""
return struct(_values = {e: None for e in a._values.keys() if e not in b._values})
def _length(s): def _length(s):
"""Returns the number of elements in a set. """Returns the number of elements in a set.
Args: Args:
s: A set, as returned by `sets.make()`. s: A set, as returned by `sets.make()`.
Returns: Returns:
An integer representing the number of elements in the set. An integer representing the number of elements in the set.
""" """
return len(s._values) return len(s._values)
def _repr(s): def _repr(s):
"""Returns a string value representing the set. """Returns a string value representing the set.
Args: Args:
s: A set, as returned by `sets.make()`. s: A set, as returned by `sets.make()`.
Returns:
A string representing the set.
"""
return repr(s._values.keys())
Returns:
A string representing the set.
"""
return repr(s._values.keys())
sets = struct( sets = struct(
make = _make, make = _make,
copy = _copy, copy = _copy,
to_list = _to_list, to_list = _to_list,
insert = _insert, insert = _insert,
contains = _contains, contains = _contains,
is_equal = _is_equal, is_equal = _is_equal,
is_subset = _is_subset, is_subset = _is_subset,
disjoint = _disjoint, disjoint = _disjoint,
intersection = _intersection, intersection = _intersection,
union = _union, union = _union,
difference = _difference, difference = _difference,
length = _length, length = _length,
remove = _remove, remove = _remove,
repr = _repr, repr = _repr,
str = _repr, str = _repr,
) )

View File

@ -20,111 +20,111 @@ Similar to https://docs.python.org/3/library/functools.html#functools.partial.
""" """
def _call(partial, *args, **kwargs): def _call(partial, *args, **kwargs):
"""Calls a partial created using `make`. """Calls a partial created using `make`.
Args: Args:
partial: The partial to be called. partial: The partial to be called.
*args: Additional positional arguments to be appended to the ones given to *args: Additional positional arguments to be appended to the ones given to
make. make.
**kwargs: Additional keyword arguments to augment and override the ones **kwargs: Additional keyword arguments to augment and override the ones
given to make. given to make.
Returns: Returns:
Whatever the function in the partial returns. Whatever the function in the partial returns.
""" """
function_args = partial.args + args function_args = partial.args + args
function_kwargs = dict(partial.kwargs) function_kwargs = dict(partial.kwargs)
function_kwargs.update(kwargs) function_kwargs.update(kwargs)
return partial.function(*function_args, **function_kwargs) return partial.function(*function_args, **function_kwargs)
def _make(func, *args, **kwargs): def _make(func, *args, **kwargs):
"""Creates a partial that can be called using `call`. """Creates a partial that can be called using `call`.
A partial can have args assigned to it at the make site, and can have args A partial can have args assigned to it at the make site, and can have args
passed to it at the call sites. passed to it at the call sites.
A partial 'function' can be defined with positional args and kwargs: A partial 'function' can be defined with positional args and kwargs:
# function with no args # function with no args
def function1(): def function1():
... ...
# function with 2 args # function with 2 args
def function2(arg1, arg2): def function2(arg1, arg2):
... ...
# function with 2 args and keyword args # function with 2 args and keyword args
def function3(arg1, arg2, x, y): def function3(arg1, arg2, x, y):
... ...
The positional args passed to the function are the args passed into make The positional args passed to the function are the args passed into make
followed by any additional positional args given to call. The below example followed by any additional positional args given to call. The below example
illustrates a function with two positional arguments where one is supplied by illustrates a function with two positional arguments where one is supplied by
make and the other by call: make and the other by call:
# function demonstrating 1 arg at make site, and 1 arg at call site # function demonstrating 1 arg at make site, and 1 arg at call site
def _foo(make_arg1, func_arg1): def _foo(make_arg1, func_arg1):
print(make_arg1 + " " + func_arg1 + "!") print(make_arg1 + " " + func_arg1 + "!")
For example: For example:
hi_func = partial.make(_foo, "Hello") hi_func = partial.make(_foo, "Hello")
bye_func = partial.make(_foo, "Goodbye") bye_func = partial.make(_foo, "Goodbye")
partial.call(hi_func, "Jennifer") partial.call(hi_func, "Jennifer")
partial.call(hi_func, "Dave") partial.call(hi_func, "Dave")
partial.call(bye_func, "Jennifer") partial.call(bye_func, "Jennifer")
partial.call(bye_func, "Dave") partial.call(bye_func, "Dave")
prints: prints:
"Hello, Jennifer!" "Hello, Jennifer!"
"Hello, Dave!" "Hello, Dave!"
"Goodbye, Jennifer!" "Goodbye, Jennifer!"
"Goodbye, Dave!" "Goodbye, Dave!"
The keyword args given to the function are the kwargs passed into make The keyword args given to the function are the kwargs passed into make
unioned with the keyword args given to call. In case of a conflict, the unioned with the keyword args given to call. In case of a conflict, the
keyword args given to call take precedence. This allows you to set a default keyword args given to call take precedence. This allows you to set a default
value for keyword arguments and override it at the call site. value for keyword arguments and override it at the call site.
Example with a make site arg, a call site arg, a make site kwarg and a Example with a make site arg, a call site arg, a make site kwarg and a
call site kwarg: call site kwarg:
def _foo(make_arg1, call_arg1, make_location, call_location): def _foo(make_arg1, call_arg1, make_location, call_location):
print(make_arg1 + " is from " + make_location + " and " + print(make_arg1 + " is from " + make_location + " and " +
call_arg1 + " is from " + call_location + "!") call_arg1 + " is from " + call_location + "!")
func = partial.make(_foo, "Ben", make_location="Hollywood") func = partial.make(_foo, "Ben", make_location="Hollywood")
partial.call(func, "Jennifer", call_location="Denver") partial.call(func, "Jennifer", call_location="Denver")
Prints "Ben is from Hollywood and Jennifer is from Denver!". Prints "Ben is from Hollywood and Jennifer is from Denver!".
partial.call(func, "Jennifer", make_location="LA", call_location="Denver") partial.call(func, "Jennifer", make_location="LA", call_location="Denver")
Prints "Ben is from LA and Jennifer is from Denver!". Prints "Ben is from LA and Jennifer is from Denver!".
Note that keyword args may not overlap with positional args, regardless of Note that keyword args may not overlap with positional args, regardless of
whether they are given during the make or call step. For instance, you can't whether they are given during the make or call step. For instance, you can't
do: do:
def foo(x): def foo(x):
pass pass
func = partial.make(foo, 1) func = partial.make(foo, 1)
partial.call(func, x=2) partial.call(func, x=2)
Args: Args:
func: The function to be called. func: The function to be called.
*args: Positional arguments to be passed to function. *args: Positional arguments to be passed to function.
**kwargs: Keyword arguments to be passed to function. Note that these can **kwargs: Keyword arguments to be passed to function. Note that these can
be overridden at the call sites. be overridden at the call sites.
Returns: Returns:
A new `partial` that can be called using `call` A new `partial` that can be called using `call`
""" """
return struct(function=func, args=args, kwargs=kwargs) return struct(function = func, args = args, kwargs = kwargs)
partial = struct( partial = struct(
make=_make, make = _make,
call=_call, call = _call,
) )

View File

@ -19,233 +19,224 @@ path separators (forward slash, "/"); they do not handle Windows-style paths
with backslash separators or drive letters. with backslash separators or drive letters.
""" """
def _basename(p): def _basename(p):
"""Returns the basename (i.e., the file portion) of a path. """Returns the basename (i.e., the file portion) of a path.
Note that if `p` ends with a slash, this function returns an empty string. Note that if `p` ends with a slash, this function returns an empty string.
This matches the behavior of Python's `os.path.basename`, but differs from This matches the behavior of Python's `os.path.basename`, but differs from
the Unix `basename` command (which would return the path segment preceding the Unix `basename` command (which would return the path segment preceding
the final slash). the final slash).
Args: Args:
p: The path whose basename should be returned. p: The path whose basename should be returned.
Returns:
The basename of the path, which includes the extension.
"""
return p.rpartition("/")[-1]
Returns:
The basename of the path, which includes the extension.
"""
return p.rpartition("/")[-1]
def _dirname(p): def _dirname(p):
"""Returns the dirname of a path. """Returns the dirname of a path.
The dirname is the portion of `p` up to but not including the file portion The dirname is the portion of `p` up to but not including the file portion
(i.e., the basename). Any slashes immediately preceding the basename are not (i.e., the basename). Any slashes immediately preceding the basename are not
included, unless omitting them would make the dirname empty. included, unless omitting them would make the dirname empty.
Args: Args:
p: The path whose dirname should be returned. p: The path whose dirname should be returned.
Returns:
The dirname of the path.
"""
prefix, sep, _ = p.rpartition("/")
if not prefix:
return sep
else:
# If there are multiple consecutive slashes, strip them all out as Python's
# os.path.dirname does.
return prefix.rstrip("/")
Returns:
The dirname of the path.
"""
prefix, sep, _ = p.rpartition("/")
if not prefix:
return sep
else:
# If there are multiple consecutive slashes, strip them all out as Python's
# os.path.dirname does.
return prefix.rstrip("/")
def _is_absolute(path): def _is_absolute(path):
"""Returns `True` if `path` is an absolute path. """Returns `True` if `path` is an absolute path.
Args: Args:
path: A path (which is a string). path: A path (which is a string).
Returns:
`True` if `path` is an absolute path.
"""
return path.startswith("/") or (len(path)>2 and path[1] == ":")
Returns:
`True` if `path` is an absolute path.
"""
return path.startswith("/") or (len(path) > 2 and path[1] == ":")
def _join(path, *others): def _join(path, *others):
"""Joins one or more path components intelligently. """Joins one or more path components intelligently.
This function mimics the behavior of Python's `os.path.join` function on POSIX This function mimics the behavior of Python's `os.path.join` function on POSIX
platform. It returns the concatenation of `path` and any members of `others`, platform. It returns the concatenation of `path` and any members of `others`,
inserting directory separators before each component except the first. The inserting directory separators before each component except the first. The
separator is not inserted if the path up until that point is either empty or separator is not inserted if the path up until that point is either empty or
already ends in a separator. already ends in a separator.
If any component is an absolute path, all previous components are discarded. If any component is an absolute path, all previous components are discarded.
Args: Args:
path: A path segment. path: A path segment.
*others: Additional path segments. *others: Additional path segments.
Returns: Returns:
A string containing the joined paths. A string containing the joined paths.
""" """
result = path result = path
for p in others: for p in others:
if _is_absolute(p): if _is_absolute(p):
result = p result = p
elif not result or result.endswith("/"): elif not result or result.endswith("/"):
result += p result += p
else: else:
result += "/" + p result += "/" + p
return result
return result
def _normalize(path): def _normalize(path):
"""Normalizes a path, eliminating double slashes and other redundant segments. """Normalizes a path, eliminating double slashes and other redundant segments.
This function mimics the behavior of Python's `os.path.normpath` function on This function mimics the behavior of Python's `os.path.normpath` function on
POSIX platforms; specifically: POSIX platforms; specifically:
- If the entire path is empty, "." is returned. - If the entire path is empty, "." is returned.
- All "." segments are removed, unless the path consists solely of a single - All "." segments are removed, unless the path consists solely of a single
"." segment. "." segment.
- Trailing slashes are removed, unless the path consists solely of slashes. - Trailing slashes are removed, unless the path consists solely of slashes.
- ".." segments are removed as long as there are corresponding segments - ".." segments are removed as long as there are corresponding segments
earlier in the path to remove; otherwise, they are retained as leading ".." earlier in the path to remove; otherwise, they are retained as leading ".."
segments. segments.
- Single and double leading slashes are preserved, but three or more leading - Single and double leading slashes are preserved, but three or more leading
slashes are collapsed into a single leading slash. slashes are collapsed into a single leading slash.
- Multiple adjacent internal slashes are collapsed into a single slash. - Multiple adjacent internal slashes are collapsed into a single slash.
Args: Args:
path: A path. path: A path.
Returns: Returns:
The normalized path. The normalized path.
""" """
if not path: if not path:
return "." return "."
if path.startswith("//") and not path.startswith("///"): if path.startswith("//") and not path.startswith("///"):
initial_slashes = 2 initial_slashes = 2
elif path.startswith("/"): elif path.startswith("/"):
initial_slashes = 1 initial_slashes = 1
else:
initial_slashes = 0
is_relative = (initial_slashes == 0)
components = path.split("/")
new_components = []
for component in components:
if component in ("", "."):
continue
if component == "..":
if new_components and new_components[-1] != "..":
# Only pop the last segment if it isn't another "..".
new_components.pop()
elif is_relative:
# Preserve leading ".." segments for relative paths.
new_components.append(component)
else: else:
new_components.append(component) initial_slashes = 0
is_relative = (initial_slashes == 0)
path = "/".join(new_components) components = path.split("/")
if not is_relative: new_components = []
path = ("/" * initial_slashes) + path
return path or "." for component in components:
if component in ("", "."):
continue
if component == "..":
if new_components and new_components[-1] != "..":
# Only pop the last segment if it isn't another "..".
new_components.pop()
elif is_relative:
# Preserve leading ".." segments for relative paths.
new_components.append(component)
else:
new_components.append(component)
path = "/".join(new_components)
if not is_relative:
path = ("/" * initial_slashes) + path
return path or "."
def _relativize(path, start): def _relativize(path, start):
"""Returns the portion of `path` that is relative to `start`. """Returns the portion of `path` that is relative to `start`.
Because we do not have access to the underlying file system, this Because we do not have access to the underlying file system, this
implementation differs slightly from Python's `os.path.relpath` in that it implementation differs slightly from Python's `os.path.relpath` in that it
will fail if `path` is not beneath `start` (rather than use parent segments to will fail if `path` is not beneath `start` (rather than use parent segments to
walk up to the common file system root). walk up to the common file system root).
Relativizing paths that start with parent directory references only works if Relativizing paths that start with parent directory references only works if
the path both start with the same initial parent references. the path both start with the same initial parent references.
Args: Args:
path: The path to relativize. path: The path to relativize.
start: The ancestor path against which to relativize. start: The ancestor path against which to relativize.
Returns: Returns:
The portion of `path` that is relative to `start`. The portion of `path` that is relative to `start`.
""" """
segments = _normalize(path).split("/") segments = _normalize(path).split("/")
start_segments = _normalize(start).split("/") start_segments = _normalize(start).split("/")
if start_segments == ["."]: if start_segments == ["."]:
start_segments = [] start_segments = []
start_length = len(start_segments) start_length = len(start_segments)
if (path.startswith("/") != start.startswith("/") or if (path.startswith("/") != start.startswith("/") or
len(segments) < start_length): len(segments) < start_length):
fail("Path '%s' is not beneath '%s'" % (path, start)) fail("Path '%s' is not beneath '%s'" % (path, start))
for ancestor_segment, segment in zip(start_segments, segments): for ancestor_segment, segment in zip(start_segments, segments):
if ancestor_segment != segment: if ancestor_segment != segment:
fail("Path '%s' is not beneath '%s'" % (path, start)) fail("Path '%s' is not beneath '%s'" % (path, start))
length = len(segments) - start_length
result_segments = segments[-length:]
return "/".join(result_segments)
length = len(segments) - start_length
result_segments = segments[-length:]
return "/".join(result_segments)
def _replace_extension(p, new_extension): def _replace_extension(p, new_extension):
"""Replaces the extension of the file at the end of a path. """Replaces the extension of the file at the end of a path.
If the path has no extension, the new extension is added to it. If the path has no extension, the new extension is added to it.
Args: Args:
p: The path whose extension should be replaced. p: The path whose extension should be replaced.
new_extension: The new extension for the file. The new extension should new_extension: The new extension for the file. The new extension should
begin with a dot if you want the new filename to have one. begin with a dot if you want the new filename to have one.
Returns:
The path with the extension replaced (or added, if it did not have one).
"""
return _split_extension(p)[0] + new_extension
Returns:
The path with the extension replaced (or added, if it did not have one).
"""
return _split_extension(p)[0] + new_extension
def _split_extension(p): def _split_extension(p):
"""Splits the path `p` into a tuple containing the root and extension. """Splits the path `p` into a tuple containing the root and extension.
Leading periods on the basename are ignored, so Leading periods on the basename are ignored, so
`path.split_extension(".bashrc")` returns `(".bashrc", "")`. `path.split_extension(".bashrc")` returns `(".bashrc", "")`.
Args: Args:
p: The path whose root and extension should be split. p: The path whose root and extension should be split.
Returns: Returns:
A tuple `(root, ext)` such that the root is the path without the file A tuple `(root, ext)` such that the root is the path without the file
extension, and `ext` is the file extension (which, if non-empty, contains extension, and `ext` is the file extension (which, if non-empty, contains
the leading dot). The returned tuple always satisfies the relationship the leading dot). The returned tuple always satisfies the relationship
`root + ext == p`. `root + ext == p`.
""" """
b = _basename(p) b = _basename(p)
last_dot_in_basename = b.rfind(".") last_dot_in_basename = b.rfind(".")
# If there is no dot or the only dot in the basename is at the front, then # If there is no dot or the only dot in the basename is at the front, then
# there is no extension. # there is no extension.
if last_dot_in_basename <= 0: if last_dot_in_basename <= 0:
return (p, "") return (p, "")
dot_distance_from_end = len(b) - last_dot_in_basename
return (p[:-dot_distance_from_end], p[-dot_distance_from_end:])
dot_distance_from_end = len(b) - last_dot_in_basename
return (p[:-dot_distance_from_end], p[-dot_distance_from_end:])
paths = struct( paths = struct(
basename=_basename, basename = _basename,
dirname=_dirname, dirname = _dirname,
is_absolute=_is_absolute, is_absolute = _is_absolute,
join=_join, join = _join,
normalize=_normalize, normalize = _normalize,
relativize=_relativize, relativize = _relativize,
replace_extension=_replace_extension, replace_extension = _replace_extension,
split_extension=_split_extension, split_extension = _split_extension,
) )

View File

@ -14,71 +14,69 @@
"""Skylib module containing convenience interfaces for select().""" """Skylib module containing convenience interfaces for select()."""
def _with_or(input_dict, no_match_error=''): def _with_or(input_dict, no_match_error = ""):
"""Drop-in replacement for `select()` that supports ORed keys. """Drop-in replacement for `select()` that supports ORed keys.
Args: Args:
input_dict: The same dictionary `select()` takes, except keys may take input_dict: The same dictionary `select()` takes, except keys may take
either the usual form `"//foo:config1"` or either the usual form `"//foo:config1"` or
`("//foo:config1", "//foo:config2", ...)` to signify `("//foo:config1", "//foo:config2", ...)` to signify
`//foo:config1` OR `//foo:config2` OR `...`. `//foo:config1` OR `//foo:config2` OR `...`.
no_match_error: Optional custom error to report if no condition matches. no_match_error: Optional custom error to report if no condition matches.
Example: Example:
```build ```build
deps = selects.with_or({ deps = selects.with_or({
"//configs:one": [":dep1"], "//configs:one": [":dep1"],
("//configs:two", "//configs:three"): [":dep2or3"], ("//configs:two", "//configs:three"): [":dep2or3"],
"//configs:four": [":dep4"], "//configs:four": [":dep4"],
"//conditions:default": [":default"] "//conditions:default": [":default"]
}) })
``` ```
Key labels may appear at most once anywhere in the input. Key labels may appear at most once anywhere in the input.
Returns: Returns:
A native `select()` that expands A native `select()` that expands
`("//configs:two", "//configs:three"): [":dep2or3"]` `("//configs:two", "//configs:three"): [":dep2or3"]`
to to
```build
"//configs:two": [":dep2or3"],
"//configs:three": [":dep2or3"],
```
"""
return select(_with_or_dict(input_dict), no_match_error=no_match_error)
```build
"//configs:two": [":dep2or3"],
"//configs:three": [":dep2or3"],
```
"""
return select(_with_or_dict(input_dict), no_match_error = no_match_error)
def _with_or_dict(input_dict): def _with_or_dict(input_dict):
"""Variation of `with_or` that returns the dict of the `select()`. """Variation of `with_or` that returns the dict of the `select()`.
Unlike `select()`, the contents of the dict can be inspected by Skylark Unlike `select()`, the contents of the dict can be inspected by Skylark
macros. macros.
Args: Args:
input_dict: Same as `with_or`. input_dict: Same as `with_or`.
Returns:
A dictionary usable by a native `select()`.
"""
output_dict = {}
for (key, value) in input_dict.items():
if type(key) == type(()):
for config_setting in key:
if config_setting in output_dict.keys():
fail("key %s appears multiple times" % config_setting)
output_dict[config_setting] = value
else:
if key in output_dict.keys():
fail("key %s appears multiple times" % key)
output_dict[key] = value
return output_dict
Returns:
A dictionary usable by a native `select()`.
"""
output_dict = {}
for (key, value) in input_dict.items():
if type(key) == type(()):
for config_setting in key:
if config_setting in output_dict.keys():
fail("key %s appears multiple times" % config_setting)
output_dict[config_setting] = value
else:
if key in output_dict.keys():
fail("key %s appears multiple times" % key)
output_dict[key] = value
return output_dict
selects = struct( selects = struct(
with_or=_with_or, with_or = _with_or,
with_or_dict=_with_or_dict with_or_dict = _with_or_dict,
) )

View File

@ -26,113 +26,105 @@ duplicate elements are ignored). Functions that return new sets always return
them as the `set` type, regardless of the types of the inputs. them as the `set` type, regardless of the types of the inputs.
""" """
def _precondition_only_sets_or_lists(*args): def _precondition_only_sets_or_lists(*args):
"""Verifies that all arguments are either sets or lists. """Verifies that all arguments are either sets or lists.
The build will fail if any of the arguments is neither a set nor a list. The build will fail if any of the arguments is neither a set nor a list.
Args:
*args: A list of values that must be sets or lists.
"""
for a in args:
t = type(a)
if t not in("depset", "list"):
fail("Expected arguments to be depset or list, but found type %s: %r" %
(t, a))
Args:
*args: A list of values that must be sets or lists.
"""
for a in args:
t = type(a)
if t not in ("depset", "list"):
fail("Expected arguments to be depset or list, but found type %s: %r" %
(t, a))
def _is_equal(a, b): def _is_equal(a, b):
"""Returns whether two sets are equal. """Returns whether two sets are equal.
Args: Args:
a: A depset or a list. a: A depset or a list.
b: A depset or a list. b: A depset or a list.
Returns:
True if `a` is equal to `b`, False otherwise.
"""
_precondition_only_sets_or_lists(a, b)
return sorted(depset(a)) == sorted(depset(b))
Returns:
True if `a` is equal to `b`, False otherwise.
"""
_precondition_only_sets_or_lists(a, b)
return sorted(depset(a)) == sorted(depset(b))
def _is_subset(a, b): def _is_subset(a, b):
"""Returns whether `a` is a subset of `b`. """Returns whether `a` is a subset of `b`.
Args: Args:
a: A depset or a list. a: A depset or a list.
b: A depset or a list. b: A depset or a list.
Returns:
True if `a` is a subset of `b`, False otherwise.
"""
_precondition_only_sets_or_lists(a, b)
for e in a:
if e not in b:
return False
return True
Returns:
True if `a` is a subset of `b`, False otherwise.
"""
_precondition_only_sets_or_lists(a, b)
for e in a:
if e not in b:
return False
return True
def _disjoint(a, b): def _disjoint(a, b):
"""Returns whether two sets are disjoint. """Returns whether two sets are disjoint.
Two sets are disjoint if they have no elements in common. Two sets are disjoint if they have no elements in common.
Args: Args:
a: A set or list. a: A set or list.
b: A set or list. b: A set or list.
Returns:
True if `a` and `b` are disjoint, False otherwise.
"""
_precondition_only_sets_or_lists(a, b)
for e in a:
if e in b:
return False
return True
Returns:
True if `a` and `b` are disjoint, False otherwise.
"""
_precondition_only_sets_or_lists(a, b)
for e in a:
if e in b:
return False
return True
def _intersection(a, b): def _intersection(a, b):
"""Returns the intersection of two sets. """Returns the intersection of two sets.
Args: Args:
a: A set or list. a: A set or list.
b: A set or list. b: A set or list.
Returns:
A set containing the elements that are in both `a` and `b`.
"""
_precondition_only_sets_or_lists(a, b)
return depset([e for e in a if e in b])
Returns:
A set containing the elements that are in both `a` and `b`.
"""
_precondition_only_sets_or_lists(a, b)
return depset([e for e in a if e in b])
def _union(*args): def _union(*args):
"""Returns the union of several sets. """Returns the union of several sets.
Args: Args:
*args: An arbitrary number of sets or lists. *args: An arbitrary number of sets or lists.
Returns:
The set union of all sets or lists in `*args`.
"""
_precondition_only_sets_or_lists(*args)
args_deps = [depset(x) if type(x) == type([]) else x for x in args]
return depset(transitive=args_deps)
Returns:
The set union of all sets or lists in `*args`.
"""
_precondition_only_sets_or_lists(*args)
args_deps = [depset(x) if type(x) == type([]) else x for x in args]
return depset(transitive = args_deps)
def _difference(a, b): def _difference(a, b):
"""Returns the elements in `a` that are not in `b`. """Returns the elements in `a` that are not in `b`.
Args: Args:
a: A set or list. a: A set or list.
b: A set or list. b: A set or list.
Returns:
A set containing the elements that are in `a` but not in `b`.
"""
_precondition_only_sets_or_lists(a, b)
return depset([e for e in a if e not in b])
Returns:
A set containing the elements that are in `a` but not in `b`.
"""
_precondition_only_sets_or_lists(a, b)
return depset([e for e in a if e not in b])
sets = struct( sets = struct(
difference = _difference, difference = _difference,

View File

@ -14,44 +14,41 @@
"""Skylib module containing shell utility functions.""" """Skylib module containing shell utility functions."""
def _array_literal(iterable): def _array_literal(iterable):
"""Creates a string from a sequence that can be used as a shell array. """Creates a string from a sequence that can be used as a shell array.
For example, `shell.array_literal(["a", "b", "c"])` would return the string For example, `shell.array_literal(["a", "b", "c"])` would return the string
`("a" "b" "c")`, which can be used in a shell script wherever an array `("a" "b" "c")`, which can be used in a shell script wherever an array
literal is needed. literal is needed.
Note that all elements in the array are quoted (using `shell.quote`) for Note that all elements in the array are quoted (using `shell.quote`) for
safety, even if they do not need to be. safety, even if they do not need to be.
Args: Args:
iterable: A sequence of elements. Elements that are not strings will be iterable: A sequence of elements. Elements that are not strings will be
converted to strings first, by calling `str()`. converted to strings first, by calling `str()`.
Returns:
A string that represents the sequence as a shell array; that is,
parentheses containing the quoted elements.
"""
return "(" + " ".join([_quote(str(i)) for i in iterable]) + ")"
Returns:
A string that represents the sequence as a shell array; that is,
parentheses containing the quoted elements.
"""
return "(" + " ".join([_quote(str(i)) for i in iterable]) + ")"
def _quote(s): def _quote(s):
"""Quotes the given string for use in a shell command. """Quotes the given string for use in a shell command.
This function quotes the given string (in case it contains spaces or other This function quotes the given string (in case it contains spaces or other
shell metacharacters.) shell metacharacters.)
Args: Args:
s: The string to quote. s: The string to quote.
Returns:
A quoted version of the string that can be passed to a shell command.
"""
return "'" + s.replace("'", "'\\''") + "'"
Returns:
A quoted version of the string that can be passed to a shell command.
"""
return "'" + s.replace("'", "'\\''") + "'"
shell = struct( shell = struct(
array_literal=_array_literal, array_literal = _array_literal,
quote=_quote, quote = _quote,
) )

View File

@ -14,24 +14,22 @@
"""Skylib module containing functions that operate on structs.""" """Skylib module containing functions that operate on structs."""
def _to_dict(s): def _to_dict(s):
"""Converts a `struct` to a `dict`. """Converts a `struct` to a `dict`.
Args: Args:
s: A `struct`. s: A `struct`.
Returns:
A `dict` whose keys and values are the same as the fields in `s`. The
transformation is only applied to the struct's fields and not to any
nested values.
"""
attributes = dir(s)
attributes.remove("to_json")
attributes.remove("to_proto")
return {key: getattr(s, key) for key in attributes}
Returns:
A `dict` whose keys and values are the same as the fields in `s`. The
transformation is only applied to the struct's fields and not to any
nested values.
"""
attributes = dir(s)
attributes.remove("to_json")
attributes.remove("to_proto")
return {key: getattr(s, key) for key in attributes}
structs = struct( structs = struct(
to_dict=_to_dict, to_dict = _to_dict,
) )

View File

@ -13,7 +13,6 @@
# limitations under the License. # limitations under the License.
"""Skylib module containing functions checking types.""" """Skylib module containing functions checking types."""
# create instance singletons to avoid unnecessary allocations # create instance singletons to avoid unnecessary allocations
_a_bool = True _a_bool = True
_a_dict = {} _a_dict = {}
@ -22,10 +21,8 @@ _a_string = ""
_a_tuple = () _a_tuple = ()
_an_int = 1 _an_int = 1
def _a_function(): def _a_function():
pass pass
def _is_list(v): def _is_list(v):
"""Returns True if v is an instance of a list. """Returns True if v is an instance of a list.
@ -38,7 +35,6 @@ def _is_list(v):
""" """
return type(v) == type(_a_list) return type(v) == type(_a_list)
def _is_string(v): def _is_string(v):
"""Returns True if v is an instance of a string. """Returns True if v is an instance of a string.
@ -50,7 +46,6 @@ def _is_string(v):
""" """
return type(v) == type(_a_string) return type(v) == type(_a_string)
def _is_bool(v): def _is_bool(v):
"""Returns True if v is an instance of a bool. """Returns True if v is an instance of a bool.
@ -62,7 +57,6 @@ def _is_bool(v):
""" """
return type(v) == type(_a_bool) return type(v) == type(_a_bool)
def _is_none(v): def _is_none(v):
"""Returns True if v has the type of None. """Returns True if v has the type of None.
@ -74,7 +68,6 @@ def _is_none(v):
""" """
return type(v) == type(None) return type(v) == type(None)
def _is_int(v): def _is_int(v):
"""Returns True if v is an instance of a signed integer. """Returns True if v is an instance of a signed integer.
@ -86,7 +79,6 @@ def _is_int(v):
""" """
return type(v) == type(_an_int) return type(v) == type(_an_int)
def _is_tuple(v): def _is_tuple(v):
"""Returns True if v is an instance of a tuple. """Returns True if v is an instance of a tuple.
@ -98,7 +90,6 @@ def _is_tuple(v):
""" """
return type(v) == type(_a_tuple) return type(v) == type(_a_tuple)
def _is_dict(v): def _is_dict(v):
"""Returns True if v is an instance of a dict. """Returns True if v is an instance of a dict.
@ -110,7 +101,6 @@ def _is_dict(v):
""" """
return type(v) == type(_a_dict) return type(v) == type(_a_dict)
def _is_function(v): def _is_function(v):
"""Returns True if v is an instance of a function. """Returns True if v is an instance of a function.
@ -122,14 +112,13 @@ def _is_function(v):
""" """
return type(v) == type(_a_function) return type(v) == type(_a_function)
types = struct( types = struct(
is_list=_is_list, is_list = _is_list,
is_string=_is_string, is_string = _is_string,
is_bool=_is_bool, is_bool = _is_bool,
is_none=_is_none, is_none = _is_none,
is_int=_is_int, is_int = _is_int,
is_tuple=_is_tuple, is_tuple = _is_tuple,
is_dict=_is_dict, is_dict = _is_dict,
is_function=_is_function, is_function = _is_function,
) )

View File

@ -20,272 +20,265 @@ assertions used to within tests.
""" """
load(":sets.bzl", "sets") load(":sets.bzl", "sets")
load(":new_sets.bzl", new_sets="sets") load(":new_sets.bzl", new_sets = "sets")
def _make(impl, attrs = None):
"""Creates a unit test rule from its implementation function.
def _make(impl, attrs=None): Each unit test is defined in an implementation function that must then be
"""Creates a unit test rule from its implementation function. associated with a rule so that a target can be built. This function handles
the boilerplate to create and return a test rule and captures the
implementation function's name so that it can be printed in test feedback.
Each unit test is defined in an implementation function that must then be The optional `attrs` argument can be used to define dependencies for this
associated with a rule so that a target can be built. This function handles test, in order to form unit tests of rules.
the boilerplate to create and return a test rule and captures the
implementation function's name so that it can be printed in test feedback.
The optional `attrs` argument can be used to define dependencies for this An example of a unit test:
test, in order to form unit tests of rules.
An example of a unit test: ```
def _your_test(ctx):
env = unittest.begin(ctx)
``` # Assert statements go here
def _your_test(ctx):
env = unittest.begin(ctx)
# Assert statements go here unittest.end(env)
unittest.end(env) your_test = unittest.make(_your_test)
```
your_test = unittest.make(_your_test) Recall that names of test rules must end in `_test`.
```
Recall that names of test rules must end in `_test`. Args:
impl: The implementation function of the unit test.
attrs: An optional dictionary to supplement the attrs passed to the
unit test's `rule()` constructor.
Args: Returns:
impl: The implementation function of the unit test. A rule definition that should be stored in a global whose name ends in
attrs: An optional dictionary to supplement the attrs passed to the `_test`.
unit test's `rule()` constructor. """
Returns: # Derive the name of the implementation function for better test feedback.
A rule definition that should be stored in a global whose name ends in # Skylark currently stringifies a function as "<function NAME>", so we use
`_test`. # that knowledge to parse the "NAME" portion out. If this behavior ever
""" # changes, we'll need to update this.
# TODO(bazel-team): Expose a ._name field on functions to avoid this.
impl_name = str(impl)
impl_name = impl_name.partition("<function ")[-1]
impl_name = impl_name.rpartition(">")[0]
# Derive the name of the implementation function for better test feedback. attrs = dict(attrs) if attrs else {}
# Skylark currently stringifies a function as "<function NAME>", so we use attrs["_impl_name"] = attr.string(default = impl_name)
# that knowledge to parse the "NAME" portion out. If this behavior ever
# changes, we'll need to update this.
# TODO(bazel-team): Expose a ._name field on functions to avoid this.
impl_name = str(impl)
impl_name = impl_name.partition("<function ")[-1]
impl_name = impl_name.rpartition(">")[0]
attrs = dict(attrs) if attrs else {}
attrs["_impl_name"] = attr.string(default=impl_name)
return rule(
impl,
attrs=attrs,
_skylark_testable=True,
test=True,
)
return rule(
impl,
attrs = attrs,
_skylark_testable = True,
test = True,
)
def _suite(name, *test_rules): def _suite(name, *test_rules):
"""Defines a `test_suite` target that contains multiple tests. """Defines a `test_suite` target that contains multiple tests.
After defining your test rules in a `.bzl` file, you need to create targets After defining your test rules in a `.bzl` file, you need to create targets
from those rules so that `blaze test` can execute them. Doing this manually from those rules so that `blaze test` can execute them. Doing this manually
in a BUILD file would consist of listing each test in your `load` statement in a BUILD file would consist of listing each test in your `load` statement
and then creating each target one by one. To reduce duplication, we recommend and then creating each target one by one. To reduce duplication, we recommend
writing a macro in your `.bzl` file to instantiate all targets, and calling writing a macro in your `.bzl` file to instantiate all targets, and calling
that macro from your BUILD file so you only have to load one symbol. that macro from your BUILD file so you only have to load one symbol.
For the case where your unit tests do not take any (non-default) attributes -- For the case where your unit tests do not take any (non-default) attributes --
i.e., if your unit tests do not test rules -- you can use this function to i.e., if your unit tests do not test rules -- you can use this function to
create the targets and wrap them in a single test_suite target. In your create the targets and wrap them in a single test_suite target. In your
`.bzl` file, write: `.bzl` file, write:
``` ```
def your_test_suite(): def your_test_suite():
unittest.suite( unittest.suite(
"your_test_suite", "your_test_suite",
your_test, your_test,
your_other_test, your_other_test,
yet_another_test, yet_another_test,
)
```
Then, in your `BUILD` file, simply load the macro and invoke it to have all
of the targets created:
```
load("//path/to/your/package:tests.bzl", "your_test_suite")
your_test_suite()
```
If you pass _N_ unit test rules to `unittest.suite`, _N_ + 1 targets will be
created: a `test_suite` target named `${name}` (where `${name}` is the name
argument passed in here) and targets named `${name}_test_${i}`, where `${i}`
is the index of the test in the `test_rules` list, which is used to uniquely
name each target.
Args:
name: The name of the `test_suite` target, and the prefix of all the test
target names.
*test_rules: A list of test rules defines by `unittest.test`.
"""
test_names = []
for index, test_rule in enumerate(test_rules):
test_name = "%s_test_%d" % (name, index)
test_rule(name = test_name)
test_names.append(test_name)
native.test_suite(
name = name,
tests = [":%s" % t for t in test_names],
) )
```
Then, in your `BUILD` file, simply load the macro and invoke it to have all
of the targets created:
```
load("//path/to/your/package:tests.bzl", "your_test_suite")
your_test_suite()
```
If you pass _N_ unit test rules to `unittest.suite`, _N_ + 1 targets will be
created: a `test_suite` target named `${name}` (where `${name}` is the name
argument passed in here) and targets named `${name}_test_${i}`, where `${i}`
is the index of the test in the `test_rules` list, which is used to uniquely
name each target.
Args:
name: The name of the `test_suite` target, and the prefix of all the test
target names.
*test_rules: A list of test rules defines by `unittest.test`.
"""
test_names = []
for index, test_rule in enumerate(test_rules):
test_name = "%s_test_%d" % (name, index)
test_rule(name=test_name)
test_names.append(test_name)
native.test_suite(
name=name,
tests=[":%s" % t for t in test_names]
)
def _begin(ctx): def _begin(ctx):
"""Begins a unit test. """Begins a unit test.
This should be the first function called in a unit test implementation This should be the first function called in a unit test implementation
function. It initializes a "test environment" that is used to collect function. It initializes a "test environment" that is used to collect
assertion failures so that they can be reported and logged at the end of the assertion failures so that they can be reported and logged at the end of the
test. test.
Args: Args:
ctx: The Skylark context. Pass the implementation function's `ctx` argument ctx: The Skylark context. Pass the implementation function's `ctx` argument
in verbatim. in verbatim.
Returns:
A test environment struct that must be passed to assertions and finally to
`unittest.end`. Do not rely on internal details about the fields in this
struct as it may change.
"""
return struct(ctx=ctx, failures=[])
Returns:
A test environment struct that must be passed to assertions and finally to
`unittest.end`. Do not rely on internal details about the fields in this
struct as it may change.
"""
return struct(ctx = ctx, failures = [])
def _end(env): def _end(env):
"""Ends a unit test and logs the results. """Ends a unit test and logs the results.
This must be called before the end of a unit test implementation function so This must be called before the end of a unit test implementation function so
that the results are reported. that the results are reported.
Args:
env: The test environment returned by `unittest.begin`.
"""
cmd = "\n".join([
"cat << EOF",
"\n".join(env.failures),
"EOF",
"exit %d" % len(env.failures),
])
env.ctx.file_action(
output=env.ctx.outputs.executable,
content=cmd,
executable=True,
)
Args:
env: The test environment returned by `unittest.begin`.
"""
cmd = "\n".join([
"cat << EOF",
"\n".join(env.failures),
"EOF",
"exit %d" % len(env.failures),
])
env.ctx.file_action(
output = env.ctx.outputs.executable,
content = cmd,
executable = True,
)
def _fail(env, msg): def _fail(env, msg):
"""Unconditionally causes the current test to fail. """Unconditionally causes the current test to fail.
Args: Args:
env: The test environment returned by `unittest.begin`. env: The test environment returned by `unittest.begin`.
msg: The message to log describing the failure. msg: The message to log describing the failure.
""" """
full_msg = "In test %s: %s" % (env.ctx.attr._impl_name, msg) full_msg = "In test %s: %s" % (env.ctx.attr._impl_name, msg)
print(full_msg) print(full_msg)
env.failures.append(full_msg) env.failures.append(full_msg)
def _assert_true(
env,
condition,
msg = "Expected condition to be true, but was false."):
"""Asserts that the given `condition` is true.
def _assert_true(env, Args:
condition, env: The test environment returned by `unittest.begin`.
msg="Expected condition to be true, but was false."): condition: A value that will be evaluated in a Boolean context.
"""Asserts that the given `condition` is true. msg: An optional message that will be printed that describes the failure.
If omitted, a default will be used.
"""
if not condition:
_fail(env, msg)
Args: def _assert_false(
env: The test environment returned by `unittest.begin`. env,
condition: A value that will be evaluated in a Boolean context. condition,
msg: An optional message that will be printed that describes the failure. msg = "Expected condition to be false, but was true."):
If omitted, a default will be used. """Asserts that the given `condition` is false.
"""
if not condition:
_fail(env, msg)
Args:
env: The test environment returned by `unittest.begin`.
condition: A value that will be evaluated in a Boolean context.
msg: An optional message that will be printed that describes the failure.
If omitted, a default will be used.
"""
if condition:
_fail(env, msg)
def _assert_false(env, def _assert_equals(env, expected, actual, msg = None):
condition, """Asserts that the given `expected` and `actual` values are equal.
msg="Expected condition to be false, but was true."):
"""Asserts that the given `condition` is false.
Args: Args:
env: The test environment returned by `unittest.begin`. env: The test environment returned by `unittest.begin`.
condition: A value that will be evaluated in a Boolean context. expected: The expected value of some computation.
msg: An optional message that will be printed that describes the failure. actual: The actual value returned by some computation.
If omitted, a default will be used. msg: An optional message that will be printed that describes the failure.
""" If omitted, a default will be used.
if condition: """
_fail(env, msg) if expected != actual:
expectation_msg = 'Expected "%s", but got "%s"' % (expected, actual)
if msg:
full_msg = "%s (%s)" % (msg, expectation_msg)
else:
full_msg = expectation_msg
_fail(env, full_msg)
def _assert_set_equals(env, expected, actual, msg = None):
"""Asserts that the given `expected` and `actual` sets are equal.
def _assert_equals(env, expected, actual, msg=None): Args:
"""Asserts that the given `expected` and `actual` values are equal. env: The test environment returned by `unittest.begin`.
expected: The expected set resulting from some computation.
actual: The actual set returned by some computation.
msg: An optional message that will be printed that describes the failure.
If omitted, a default will be used.
"""
if type(actual) != type(depset()) or not sets.is_equal(expected, actual):
expectation_msg = "Expected %r, but got %r" % (expected, actual)
if msg:
full_msg = "%s (%s)" % (msg, expectation_msg)
else:
full_msg = expectation_msg
_fail(env, full_msg)
Args: def _assert_new_set_equals(env, expected, actual, msg = None):
env: The test environment returned by `unittest.begin`. """Asserts that the given `expected` and `actual` sets are equal.
expected: The expected value of some computation.
actual: The actual value returned by some computation.
msg: An optional message that will be printed that describes the failure.
If omitted, a default will be used.
"""
if expected != actual:
expectation_msg = 'Expected "%s", but got "%s"' % (expected, actual)
if msg:
full_msg = "%s (%s)" % (msg, expectation_msg)
else:
full_msg = expectation_msg
_fail(env, full_msg)
Args:
def _assert_set_equals(env, expected, actual, msg=None): env: The test environment returned by `unittest.begin`.
"""Asserts that the given `expected` and `actual` sets are equal. expected: The expected set resulting from some computation.
actual: The actual set returned by some computation.
Args: msg: An optional message that will be printed that describes the failure.
env: The test environment returned by `unittest.begin`. If omitted, a default will be used.
expected: The expected set resulting from some computation. """
actual: The actual set returned by some computation. if not new_sets.is_equal(expected, actual):
msg: An optional message that will be printed that describes the failure. expectation_msg = "Expected %r, but got %r" % (expected, actual)
If omitted, a default will be used. if msg:
""" full_msg = "%s (%s)" % (msg, expectation_msg)
if type(actual) != type(depset()) or not sets.is_equal(expected, actual): else:
expectation_msg = "Expected %r, but got %r" % (expected, actual) full_msg = expectation_msg
if msg: _fail(env, full_msg)
full_msg = "%s (%s)" % (msg, expectation_msg)
else:
full_msg = expectation_msg
_fail(env, full_msg)
def _assert_new_set_equals(env, expected, actual, msg=None):
"""Asserts that the given `expected` and `actual` sets are equal.
Args:
env: The test environment returned by `unittest.begin`.
expected: The expected set resulting from some computation.
actual: The actual set returned by some computation.
msg: An optional message that will be printed that describes the failure.
If omitted, a default will be used.
"""
if not new_sets.is_equal(expected, actual):
expectation_msg = "Expected %r, but got %r" % (expected, actual)
if msg:
full_msg = "%s (%s)" % (msg, expectation_msg)
else:
full_msg = expectation_msg
_fail(env, full_msg)
asserts = struct( asserts = struct(
equals=_assert_equals, equals = _assert_equals,
false=_assert_false, false = _assert_false,
set_equals=_assert_set_equals, set_equals = _assert_set_equals,
new_set_equals = _assert_new_set_equals, new_set_equals = _assert_new_set_equals,
true=_assert_true, true = _assert_true,
) )
unittest = struct( unittest = struct(
make=_make, make = _make,
suite=_suite, suite = _suite,
begin=_begin, begin = _begin,
end=_end, end = _end,
fail=_fail, fail = _fail,
) )

View File

@ -15,111 +15,113 @@
"""Skylib module containing functions for checking Bazel versions.""" """Skylib module containing functions for checking Bazel versions."""
def _get_bazel_version(): def _get_bazel_version():
"""Returns the current Bazel version""" """Returns the current Bazel version"""
return native.bazel_version
return native.bazel_version
def _extract_version_number(bazel_version): def _extract_version_number(bazel_version):
"""Extracts the semantic version number from a version string """Extracts the semantic version number from a version string
Args: Args:
bazel_version: the version string that begins with the semantic version bazel_version: the version string that begins with the semantic version
e.g. "1.2.3rc1 abc1234" where "abc1234" is a commit hash. e.g. "1.2.3rc1 abc1234" where "abc1234" is a commit hash.
Returns: Returns:
The semantic version string, like "1.2.3". The semantic version string, like "1.2.3".
""" """
for i in range(len(bazel_version)): for i in range(len(bazel_version)):
c = bazel_version[i] c = bazel_version[i]
if not (c.isdigit() or c == "."): if not (c.isdigit() or c == "."):
return bazel_version[:i] return bazel_version[:i]
return bazel_version return bazel_version
# Parse the bazel version string from `native.bazel_version`. # Parse the bazel version string from `native.bazel_version`.
# e.g. # e.g.
# "0.10.0rc1 abc123d" => (0, 10, 0) # "0.10.0rc1 abc123d" => (0, 10, 0)
# "0.3.0" => (0, 3, 0) # "0.3.0" => (0, 3, 0)
def _parse_bazel_version(bazel_version): def _parse_bazel_version(bazel_version):
"""Parses a version string into a 3-tuple of ints """Parses a version string into a 3-tuple of ints
int tuples can be compared directly using binary operators (<, >). int tuples can be compared directly using binary operators (<, >).
Args: Args:
bazel_version: the Bazel version string bazel_version: the Bazel version string
Returns: Returns:
An int 3-tuple of a (major, minor, patch) version. An int 3-tuple of a (major, minor, patch) version.
""" """
version = _extract_version_number(bazel_version)
return tuple([int(n) for n in version.split(".")])
version = _extract_version_number(bazel_version)
return tuple([int(n) for n in version.split(".")])
def _is_at_most(threshold, version): def _is_at_most(threshold, version):
"""Check that a version is lower or equals to a threshold. """Check that a version is lower or equals to a threshold.
Args: Args:
threshold: the maximum version string threshold: the maximum version string
version: the version string to be compared to the threshold version: the version string to be compared to the threshold
Returns:
True if version <= threshold.
"""
return _parse_bazel_version(version) <= _parse_bazel_version(threshold)
Returns:
True if version <= threshold.
"""
return _parse_bazel_version(version) <= _parse_bazel_version(threshold)
def _is_at_least(threshold, version): def _is_at_least(threshold, version):
"""Check that a version is higher or equals to a threshold. """Check that a version is higher or equals to a threshold.
Args: Args:
threshold: the minimum version string threshold: the minimum version string
version: the version string to be compared to the threshold version: the version string to be compared to the threshold
Returns: Returns:
True if version >= threshold. True if version >= threshold.
""" """
return _parse_bazel_version(version) >= _parse_bazel_version(threshold) return _parse_bazel_version(version) >= _parse_bazel_version(threshold)
def _check_bazel_version(minimum_bazel_version, maximum_bazel_version = None, bazel_version = None):
"""Check that the version of Bazel is valid within the specified range.
def _check_bazel_version(minimum_bazel_version, maximum_bazel_version=None, bazel_version=None): Args:
"""Check that the version of Bazel is valid within the specified range. minimum_bazel_version: minimum version of Bazel expected
maximum_bazel_version: maximum version of Bazel expected
bazel_version: the version of Bazel to check. Used for testing, defaults to native.bazel_version
"""
if not bazel_version:
if "bazel_version" not in dir(native):
fail("\nCurrent Bazel version is lower than 0.2.1, expected at least %s\n" % minimum_bazel_version)
elif not native.bazel_version:
print("\nCurrent Bazel is not a release version, cannot check for compatibility.")
print("Make sure that you are running at least Bazel %s.\n" % minimum_bazel_version)
return
else:
bazel_version = native.bazel_version
Args: if not _is_at_least(
minimum_bazel_version: minimum version of Bazel expected threshold = minimum_bazel_version,
maximum_bazel_version: maximum version of Bazel expected version = bazel_version,
bazel_version: the version of Bazel to check. Used for testing, defaults to native.bazel_version ):
""" fail("\nCurrent Bazel version is {}, expected at least {}\n".format(
if not bazel_version: bazel_version,
if "bazel_version" not in dir(native): minimum_bazel_version,
fail("\nCurrent Bazel version is lower than 0.2.1, expected at least %s\n" % minimum_bazel_version) ))
elif not native.bazel_version:
print("\nCurrent Bazel is not a release version, cannot check for compatibility.")
print("Make sure that you are running at least Bazel %s.\n" % minimum_bazel_version)
return
else:
bazel_version = native.bazel_version
if not _is_at_least( if maximum_bazel_version:
threshold = minimum_bazel_version, if not _is_at_most(
version = bazel_version): threshold = maximum_bazel_version,
fail("\nCurrent Bazel version is {}, expected at least {}\n".format( version = bazel_version,
bazel_version, minimum_bazel_version)) ):
fail("\nCurrent Bazel version is {}, expected at most {}\n".format(
bazel_version,
maximum_bazel_version,
))
if maximum_bazel_version: pass
if not _is_at_most(
threshold = maximum_bazel_version,
version = bazel_version):
fail("\nCurrent Bazel version is {}, expected at most {}\n".format(
bazel_version, maximum_bazel_version))
pass
versions = struct( versions = struct(
get=_get_bazel_version, get = _get_bazel_version,
parse=_parse_bazel_version, parse = _parse_bazel_version,
check=_check_bazel_version, check = _check_bazel_version,
is_at_most=_is_at_most, is_at_most = _is_at_most,
is_at_least=_is_at_least, is_at_least = _is_at_least,
) )

View File

@ -15,46 +15,46 @@
"""Skylib module containing a library rule for aggregating rules files.""" """Skylib module containing a library rule for aggregating rules files."""
SkylarkLibraryInfo = provider( SkylarkLibraryInfo = provider(
'Information on contained Skylark rules.', "Information on contained Skylark rules.",
fields={ fields = {
'srcs': 'Top level rules files.', "srcs": "Top level rules files.",
'transitive_srcs': 'Transitive closure of rules files required for ' + "transitive_srcs": "Transitive closure of rules files required for " +
'interpretation of the srcs', "interpretation of the srcs",
}, },
) )
def _skylark_library_impl(ctx): def _skylark_library_impl(ctx):
deps_files = [depset(x.files, order="postorder") for x in ctx.attr.deps] deps_files = [depset(x.files, order = "postorder") for x in ctx.attr.deps]
all_files = depset(ctx.files.srcs, order="postorder", transitive=deps_files) all_files = depset(ctx.files.srcs, order = "postorder", transitive = deps_files)
return [ return [
# All dependent files should be listed in both `files` and in `runfiles`; # All dependent files should be listed in both `files` and in `runfiles`;
# this ensures that a `skylark_library` can be referenced as `data` from # this ensures that a `skylark_library` can be referenced as `data` from
# a separate program, or from `tools` of a genrule(). # a separate program, or from `tools` of a genrule().
DefaultInfo( DefaultInfo(
files=all_files, files = all_files,
runfiles=ctx.runfiles(files=list(all_files)), runfiles = ctx.runfiles(files = list(all_files)),
), ),
# We also define our own provider struct, for aggregation and testing. # We also define our own provider struct, for aggregation and testing.
SkylarkLibraryInfo( SkylarkLibraryInfo(
srcs=ctx.files.srcs, srcs = ctx.files.srcs,
transitive_srcs=all_files, transitive_srcs = all_files,
), ),
] ]
skylark_library = rule( skylark_library = rule(
implementation=_skylark_library_impl, implementation = _skylark_library_impl,
attrs={ attrs = {
"srcs": attr.label_list( "srcs": attr.label_list(
allow_files=[".bzl"], allow_files = [".bzl"],
), ),
"deps": attr.label_list( "deps": attr.label_list(
allow_files=[".bzl"], allow_files = [".bzl"],
providers=[ providers = [
[SkylarkLibraryInfo], [SkylarkLibraryInfo],
], ],
) ),
} },
) )
"""Creates a logical collection of Skylark .bzl files. """Creates a logical collection of Skylark .bzl files.

View File

@ -14,68 +14,99 @@
"""Unit tests for collections.bzl.""" """Unit tests for collections.bzl."""
load("//:lib.bzl", "collections", "asserts", "unittest") load("//:lib.bzl", "asserts", "collections", "unittest")
def _after_each_test(ctx): def _after_each_test(ctx):
"""Unit tests for collections.after_each.""" """Unit tests for collections.after_each."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.equals(env, [], collections.after_each("1", [])) asserts.equals(env, [], collections.after_each("1", []))
asserts.equals(env, ["a", "1"], collections.after_each("1", ["a"])) asserts.equals(env, ["a", "1"], collections.after_each("1", ["a"]))
asserts.equals(env, ["a", "1", "b", "1"], asserts.equals(
collections.after_each("1", ["a", "b"])) env,
["a", "1", "b", "1"],
collections.after_each("1", ["a", "b"]),
)
# We don't care what type the separator is, we just put it there; so None # We don't care what type the separator is, we just put it there; so None
# should be just as valid as anything else. # should be just as valid as anything else.
asserts.equals(env, ["a", None, "b", None], asserts.equals(
collections.after_each(None, ["a", "b"])) env,
["a", None, "b", None],
collections.after_each(None, ["a", "b"]),
)
unittest.end(env) unittest.end(env)
after_each_test = unittest.make(_after_each_test) after_each_test = unittest.make(_after_each_test)
def _before_each_test(ctx): def _before_each_test(ctx):
"""Unit tests for collections.before_each.""" """Unit tests for collections.before_each."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.equals(env, [], collections.before_each("1", [])) asserts.equals(env, [], collections.before_each("1", []))
asserts.equals(env, ["1", "a"], collections.before_each("1", ["a"])) asserts.equals(env, ["1", "a"], collections.before_each("1", ["a"]))
asserts.equals(env, ["1", "a", "1", "b"], asserts.equals(
collections.before_each("1", ["a", "b"])) env,
["1", "a", "1", "b"],
collections.before_each("1", ["a", "b"]),
)
# We don't care what type the separator is, we just put it there; so None # We don't care what type the separator is, we just put it there; so None
# should be just as valid as anything else. # should be just as valid as anything else.
asserts.equals(env, [None, "a", None, "b"], asserts.equals(
collections.before_each(None, ["a", "b"])) env,
[None, "a", None, "b"],
collections.before_each(None, ["a", "b"]),
)
unittest.end(env) unittest.end(env)
before_each_test = unittest.make(_before_each_test) before_each_test = unittest.make(_before_each_test)
def _uniq_test(ctx): def _uniq_test(ctx):
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.equals(env, collections.uniq([0, 1, 2, 3]), [0, 1, 2, 3]) asserts.equals(env, collections.uniq([0, 1, 2, 3]), [0, 1, 2, 3])
asserts.equals(env, collections.uniq([]), []) asserts.equals(env, collections.uniq([]), [])
asserts.equals(env, collections.uniq([1, 1, 1, 1, 1]), [1]) asserts.equals(env, collections.uniq([1, 1, 1, 1, 1]), [1])
asserts.equals(env, collections.uniq([True, 5, "foo", 5, False, struct(a=1), asserts.equals(
True, struct(b=2), "bar", (1,), "foo", env,
struct(a=1), (1,)]), collections.uniq([
[True, 5, "foo", False, struct(a=1), struct(b=2), True,
"bar", (1,)]) 5,
"foo",
5,
False,
struct(a = 1),
True,
struct(b = 2),
"bar",
(1,),
"foo",
struct(a = 1),
(1,),
]),
[
True,
5,
"foo",
False,
struct(a = 1),
struct(b = 2),
"bar",
(1,),
],
)
unittest.end(env) unittest.end(env)
uniq_test = unittest.make(_uniq_test) uniq_test = unittest.make(_uniq_test)
def collections_test_suite(): def collections_test_suite():
"""Creates the test targets and test suite for collections.bzl tests.""" """Creates the test targets and test suite for collections.bzl tests."""
unittest.suite( unittest.suite(
"collections_tests", "collections_tests",
after_each_test, after_each_test,
before_each_test, before_each_test,
uniq_test, uniq_test,
) )

View File

@ -14,54 +14,61 @@
"""Unit tests for dicts.bzl.""" """Unit tests for dicts.bzl."""
load("//:lib.bzl", "dicts", "asserts", "unittest") load("//:lib.bzl", "asserts", "dicts", "unittest")
def _add_test(ctx): def _add_test(ctx):
"""Unit tests for dicts.add.""" """Unit tests for dicts.add."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# Test zero- and one-argument behavior. # Test zero- and one-argument behavior.
asserts.equals(env, {}, dicts.add()) asserts.equals(env, {}, dicts.add())
asserts.equals(env, {"a": 1}, dicts.add({"a": 1})) asserts.equals(env, {"a": 1}, dicts.add({"a": 1}))
# Test simple two-argument behavior. # Test simple two-argument behavior.
asserts.equals(env, {"a": 1, "b": 2}, dicts.add({"a": 1}, {"b": 2})) asserts.equals(env, {"a": 1, "b": 2}, dicts.add({"a": 1}, {"b": 2}))
# Test simple more-than-two-argument behavior. # Test simple more-than-two-argument behavior.
asserts.equals(env, {"a": 1, "b": 2, "c": 3, "d": 4}, asserts.equals(
dicts.add({"a": 1}, {"b": 2}, {"c": 3}, {"d": 4})) env,
{"a": 1, "b": 2, "c": 3, "d": 4},
dicts.add({"a": 1}, {"b": 2}, {"c": 3}, {"d": 4}),
)
# Test same-key overriding. # Test same-key overriding.
asserts.equals(env, {"a": 100}, dicts.add({"a": 1}, {"a": 100})) asserts.equals(env, {"a": 100}, dicts.add({"a": 1}, {"a": 100}))
asserts.equals(env, {"a": 10}, dicts.add({"a": 1}, {"a": 100}, {"a": 10})) asserts.equals(env, {"a": 10}, dicts.add({"a": 1}, {"a": 100}, {"a": 10}))
asserts.equals(env, {"a": 100, "b": 10}, asserts.equals(
dicts.add({"a": 1}, {"a": 100}, {"b": 10})) env,
asserts.equals(env, {"a": 10}, dicts.add({"a": 1}, {}, {"a": 10})) {"a": 100, "b": 10},
asserts.equals(env, {"a": 10, "b": 5}, dicts.add({"a": 1}, {"a": 100}, {"b": 10}),
dicts.add({"a": 1}, {"a": 10, "b": 5})) )
asserts.equals(env, {"a": 10}, dicts.add({"a": 1}, {}, {"a": 10}))
asserts.equals(
env,
{"a": 10, "b": 5},
dicts.add({"a": 1}, {"a": 10, "b": 5}),
)
# Test some other boundary cases. # Test some other boundary cases.
asserts.equals(env, {"a": 1}, dicts.add({"a": 1}, {})) asserts.equals(env, {"a": 1}, dicts.add({"a": 1}, {}))
# Since dictionaries are passed around by reference, make sure that the # Since dictionaries are passed around by reference, make sure that the
# result of dicts.add is always a *copy* by modifying it afterwards and # result of dicts.add is always a *copy* by modifying it afterwards and
# ensuring that the original argument doesn't also reflect the change. We do # ensuring that the original argument doesn't also reflect the change. We do
# this to protect against someone who might attempt to optimize the function # this to protect against someone who might attempt to optimize the function
# by returning the argument itself in the one-argument case. # by returning the argument itself in the one-argument case.
original = {"a": 1} original = {"a": 1}
result = dicts.add(original) result = dicts.add(original)
result["a"] = 2 result["a"] = 2
asserts.equals(env, 1, original["a"]) asserts.equals(env, 1, original["a"])
unittest.end(env) unittest.end(env)
add_test = unittest.make(_add_test) add_test = unittest.make(_add_test)
def dicts_test_suite(): def dicts_test_suite():
"""Creates the test targets and test suite for dicts.bzl tests.""" """Creates the test targets and test suite for dicts.bzl tests."""
unittest.suite( unittest.suite(
"dicts_tests", "dicts_tests",
add_test, add_test,
) )

View File

@ -14,272 +14,264 @@
"""Unit tests for new_sets.bzl.""" """Unit tests for new_sets.bzl."""
load("//:lib.bzl", "new_sets", "asserts", "unittest") load("//:lib.bzl", "asserts", "new_sets", "unittest")
def _is_equal_test(ctx): def _is_equal_test(ctx):
"""Unit tests for new_sets.is_equal.""" """Unit tests for new_sets.is_equal."""
# Note that if this test fails, the results for the other `sets` tests will # Note that if this test fails, the results for the other `sets` tests will
# be inconclusive because they use `asserts.new_set_equals`, which in turn # be inconclusive because they use `asserts.new_set_equals`, which in turn
# calls `new_sets.is_equal`. # calls `new_sets.is_equal`.
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.true(env, new_sets.is_equal(new_sets.make(), new_sets.make())) asserts.true(env, new_sets.is_equal(new_sets.make(), new_sets.make()))
asserts.false(env, new_sets.is_equal(new_sets.make(), new_sets.make([1]))) asserts.false(env, new_sets.is_equal(new_sets.make(), new_sets.make([1])))
asserts.false(env, new_sets.is_equal(new_sets.make([1]), new_sets.make())) asserts.false(env, new_sets.is_equal(new_sets.make([1]), new_sets.make()))
asserts.true(env, new_sets.is_equal(new_sets.make([1]), new_sets.make([1]))) asserts.true(env, new_sets.is_equal(new_sets.make([1]), new_sets.make([1])))
asserts.false(env, new_sets.is_equal(new_sets.make([1]), new_sets.make([1, 2]))) asserts.false(env, new_sets.is_equal(new_sets.make([1]), new_sets.make([1, 2])))
asserts.false(env, new_sets.is_equal(new_sets.make([1]), new_sets.make([2]))) asserts.false(env, new_sets.is_equal(new_sets.make([1]), new_sets.make([2])))
asserts.false(env, new_sets.is_equal(new_sets.make([1]), new_sets.make([1, 2]))) asserts.false(env, new_sets.is_equal(new_sets.make([1]), new_sets.make([1, 2])))
# Verify that the implementation is not using == on the sets directly. # Verify that the implementation is not using == on the sets directly.
asserts.true(env, new_sets.is_equal(new_sets.make(depset([1])), new_sets.make(depset([1])))) asserts.true(env, new_sets.is_equal(new_sets.make(depset([1])), new_sets.make(depset([1]))))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.true(env, new_sets.is_equal(new_sets.make([1, 1]), new_sets.make([1]))) asserts.true(env, new_sets.is_equal(new_sets.make([1, 1]), new_sets.make([1])))
unittest.end(env) unittest.end(env)
is_equal_test = unittest.make(_is_equal_test) is_equal_test = unittest.make(_is_equal_test)
def _is_subset_test(ctx): def _is_subset_test(ctx):
"""Unit tests for new_sets.is_subset.""" """Unit tests for new_sets.is_subset."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.true(env, new_sets.is_subset(new_sets.make(), new_sets.make())) asserts.true(env, new_sets.is_subset(new_sets.make(), new_sets.make()))
asserts.true(env, new_sets.is_subset(new_sets.make(), new_sets.make([1]))) asserts.true(env, new_sets.is_subset(new_sets.make(), new_sets.make([1])))
asserts.false(env, new_sets.is_subset(new_sets.make([1]), new_sets.make())) asserts.false(env, new_sets.is_subset(new_sets.make([1]), new_sets.make()))
asserts.true(env, new_sets.is_subset(new_sets.make([1]), new_sets.make([1]))) asserts.true(env, new_sets.is_subset(new_sets.make([1]), new_sets.make([1])))
asserts.true(env, new_sets.is_subset(new_sets.make([1]), new_sets.make([1, 2]))) asserts.true(env, new_sets.is_subset(new_sets.make([1]), new_sets.make([1, 2])))
asserts.false(env, new_sets.is_subset(new_sets.make([1]), new_sets.make([2]))) asserts.false(env, new_sets.is_subset(new_sets.make([1]), new_sets.make([2])))
asserts.true(env, new_sets.is_subset(new_sets.make([1]), new_sets.make(depset([1, 2])))) asserts.true(env, new_sets.is_subset(new_sets.make([1]), new_sets.make(depset([1, 2]))))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.true(env, new_sets.is_subset(new_sets.make([1, 1]), new_sets.make([1, 2]))) asserts.true(env, new_sets.is_subset(new_sets.make([1, 1]), new_sets.make([1, 2])))
unittest.end(env) unittest.end(env)
is_subset_test = unittest.make(_is_subset_test) is_subset_test = unittest.make(_is_subset_test)
def _disjoint_test(ctx): def _disjoint_test(ctx):
"""Unit tests for new_sets.disjoint.""" """Unit tests for new_sets.disjoint."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.true(env, new_sets.disjoint(new_sets.make(), new_sets.make())) asserts.true(env, new_sets.disjoint(new_sets.make(), new_sets.make()))
asserts.true(env, new_sets.disjoint(new_sets.make(), new_sets.make([1]))) asserts.true(env, new_sets.disjoint(new_sets.make(), new_sets.make([1])))
asserts.true(env, new_sets.disjoint(new_sets.make([1]), new_sets.make())) asserts.true(env, new_sets.disjoint(new_sets.make([1]), new_sets.make()))
asserts.false(env, new_sets.disjoint(new_sets.make([1]), new_sets.make([1]))) asserts.false(env, new_sets.disjoint(new_sets.make([1]), new_sets.make([1])))
asserts.false(env, new_sets.disjoint(new_sets.make([1]), new_sets.make([1, 2]))) asserts.false(env, new_sets.disjoint(new_sets.make([1]), new_sets.make([1, 2])))
asserts.true(env, new_sets.disjoint(new_sets.make([1]), new_sets.make([2]))) asserts.true(env, new_sets.disjoint(new_sets.make([1]), new_sets.make([2])))
asserts.true(env, new_sets.disjoint(new_sets.make([1]), new_sets.make(depset([2])))) asserts.true(env, new_sets.disjoint(new_sets.make([1]), new_sets.make(depset([2]))))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.false(env, new_sets.disjoint(new_sets.make([1, 1]), new_sets.make([1, 2]))) asserts.false(env, new_sets.disjoint(new_sets.make([1, 1]), new_sets.make([1, 2])))
unittest.end(env) unittest.end(env)
disjoint_test = unittest.make(_disjoint_test) disjoint_test = unittest.make(_disjoint_test)
def _intersection_test(ctx): def _intersection_test(ctx):
"""Unit tests for new_sets.intersection.""" """Unit tests for new_sets.intersection."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.new_set_equals(env, new_sets.make(), new_sets.intersection(new_sets.make(), new_sets.make())) asserts.new_set_equals(env, new_sets.make(), new_sets.intersection(new_sets.make(), new_sets.make()))
asserts.new_set_equals(env, new_sets.make(), new_sets.intersection(new_sets.make(), new_sets.make([1]))) asserts.new_set_equals(env, new_sets.make(), new_sets.intersection(new_sets.make(), new_sets.make([1])))
asserts.new_set_equals(env, new_sets.make(), new_sets.intersection(new_sets.make([1]), new_sets.make())) asserts.new_set_equals(env, new_sets.make(), new_sets.intersection(new_sets.make([1]), new_sets.make()))
asserts.new_set_equals(env, new_sets.make([1]), new_sets.intersection(new_sets.make([1]), new_sets.make([1]))) asserts.new_set_equals(env, new_sets.make([1]), new_sets.intersection(new_sets.make([1]), new_sets.make([1])))
asserts.new_set_equals(env, new_sets.make([1]), new_sets.intersection(new_sets.make([1]), new_sets.make([1, 2]))) asserts.new_set_equals(env, new_sets.make([1]), new_sets.intersection(new_sets.make([1]), new_sets.make([1, 2])))
asserts.new_set_equals(env, new_sets.make(), new_sets.intersection(new_sets.make([1]), new_sets.make([2]))) asserts.new_set_equals(env, new_sets.make(), new_sets.intersection(new_sets.make([1]), new_sets.make([2])))
asserts.new_set_equals(env, new_sets.make([1]), new_sets.intersection(new_sets.make([1]), new_sets.make(depset([1])))) asserts.new_set_equals(env, new_sets.make([1]), new_sets.intersection(new_sets.make([1]), new_sets.make(depset([1]))))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.new_set_equals(env, new_sets.make([1]), new_sets.intersection(new_sets.make([1, 1]), new_sets.make([1, 2]))) asserts.new_set_equals(env, new_sets.make([1]), new_sets.intersection(new_sets.make([1, 1]), new_sets.make([1, 2])))
unittest.end(env) unittest.end(env)
intersection_test = unittest.make(_intersection_test) intersection_test = unittest.make(_intersection_test)
def _union_test(ctx): def _union_test(ctx):
"""Unit tests for new_sets.union.""" """Unit tests for new_sets.union."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.new_set_equals(env, new_sets.make(), new_sets.union()) asserts.new_set_equals(env, new_sets.make(), new_sets.union())
asserts.new_set_equals(env, new_sets.make([1]), new_sets.union(new_sets.make([1]))) asserts.new_set_equals(env, new_sets.make([1]), new_sets.union(new_sets.make([1])))
asserts.new_set_equals(env, new_sets.make(), new_sets.union(new_sets.make(), new_sets.make())) asserts.new_set_equals(env, new_sets.make(), new_sets.union(new_sets.make(), new_sets.make()))
asserts.new_set_equals(env, new_sets.make([1]), new_sets.union(new_sets.make(), new_sets.make([1]))) asserts.new_set_equals(env, new_sets.make([1]), new_sets.union(new_sets.make(), new_sets.make([1])))
asserts.new_set_equals(env, new_sets.make([1]), new_sets.union(new_sets.make([1]), new_sets.make())) asserts.new_set_equals(env, new_sets.make([1]), new_sets.union(new_sets.make([1]), new_sets.make()))
asserts.new_set_equals(env, new_sets.make([1]), new_sets.union(new_sets.make([1]), new_sets.make([1]))) asserts.new_set_equals(env, new_sets.make([1]), new_sets.union(new_sets.make([1]), new_sets.make([1])))
asserts.new_set_equals(env, new_sets.make([1, 2]), new_sets.union(new_sets.make([1]), new_sets.make([1, 2]))) asserts.new_set_equals(env, new_sets.make([1, 2]), new_sets.union(new_sets.make([1]), new_sets.make([1, 2])))
asserts.new_set_equals(env, new_sets.make([1, 2]), new_sets.union(new_sets.make([1]), new_sets.make([2]))) asserts.new_set_equals(env, new_sets.make([1, 2]), new_sets.union(new_sets.make([1]), new_sets.make([2])))
asserts.new_set_equals(env, new_sets.make([1]), new_sets.union(new_sets.make([1]), new_sets.make(depset([1])))) asserts.new_set_equals(env, new_sets.make([1]), new_sets.union(new_sets.make([1]), new_sets.make(depset([1]))))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.new_set_equals(env, new_sets.make([1, 2]), new_sets.union(new_sets.make([1, 1]), new_sets.make([1, 2]))) asserts.new_set_equals(env, new_sets.make([1, 2]), new_sets.union(new_sets.make([1, 1]), new_sets.make([1, 2])))
unittest.end(env) unittest.end(env)
union_test = unittest.make(_union_test) union_test = unittest.make(_union_test)
def _difference_test(ctx): def _difference_test(ctx):
"""Unit tests for new_sets.difference.""" """Unit tests for new_sets.difference."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.new_set_equals(env, new_sets.make(), new_sets.difference(new_sets.make(), new_sets.make())) asserts.new_set_equals(env, new_sets.make(), new_sets.difference(new_sets.make(), new_sets.make()))
asserts.new_set_equals(env, new_sets.make(), new_sets.difference(new_sets.make(), new_sets.make([1]))) asserts.new_set_equals(env, new_sets.make(), new_sets.difference(new_sets.make(), new_sets.make([1])))
asserts.new_set_equals(env, new_sets.make([1]), new_sets.difference(new_sets.make([1]), new_sets.make())) asserts.new_set_equals(env, new_sets.make([1]), new_sets.difference(new_sets.make([1]), new_sets.make()))
asserts.new_set_equals(env, new_sets.make(), new_sets.difference(new_sets.make([1]), new_sets.make([1]))) asserts.new_set_equals(env, new_sets.make(), new_sets.difference(new_sets.make([1]), new_sets.make([1])))
asserts.new_set_equals(env, new_sets.make(), new_sets.difference(new_sets.make([1]), new_sets.make([1, 2]))) asserts.new_set_equals(env, new_sets.make(), new_sets.difference(new_sets.make([1]), new_sets.make([1, 2])))
asserts.new_set_equals(env, new_sets.make([1]), new_sets.difference(new_sets.make([1]), new_sets.make([2]))) asserts.new_set_equals(env, new_sets.make([1]), new_sets.difference(new_sets.make([1]), new_sets.make([2])))
asserts.new_set_equals(env, new_sets.make(), new_sets.difference(new_sets.make([1]), new_sets.make(depset([1])))) asserts.new_set_equals(env, new_sets.make(), new_sets.difference(new_sets.make([1]), new_sets.make(depset([1]))))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.new_set_equals(env, new_sets.make([2]), new_sets.difference(new_sets.make([1, 2]), new_sets.make([1, 1]))) asserts.new_set_equals(env, new_sets.make([2]), new_sets.difference(new_sets.make([1, 2]), new_sets.make([1, 1])))
unittest.end(env) unittest.end(env)
difference_test = unittest.make(_difference_test) difference_test = unittest.make(_difference_test)
def _to_list_test(ctx): def _to_list_test(ctx):
"""Unit tests for new_sets.to_list.""" """Unit tests for new_sets.to_list."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.equals(env, [], new_sets.to_list(new_sets.make())) asserts.equals(env, [], new_sets.to_list(new_sets.make()))
asserts.equals(env, [1], new_sets.to_list(new_sets.make([1, 1, 1]))) asserts.equals(env, [1], new_sets.to_list(new_sets.make([1, 1, 1])))
asserts.equals(env, [1, 2, 3], new_sets.to_list(new_sets.make([1, 2, 3]))) asserts.equals(env, [1, 2, 3], new_sets.to_list(new_sets.make([1, 2, 3])))
unittest.end(env) unittest.end(env)
to_list_test = unittest.make(_to_list_test) to_list_test = unittest.make(_to_list_test)
def _make_test(ctx): def _make_test(ctx):
"""Unit tests for new_sets.make.""" """Unit tests for new_sets.make."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.equals(env, {}, new_sets.make()._values) asserts.equals(env, {}, new_sets.make()._values)
asserts.equals(env, {x: None for x in [1, 2, 3]}, new_sets.make([1, 1, 2, 2, 3, 3])._values) asserts.equals(env, {x: None for x in [1, 2, 3]}, new_sets.make([1, 1, 2, 2, 3, 3])._values)
asserts.equals(env, {1: None, 2: None}, new_sets.make(depset([1, 2]))._values) asserts.equals(env, {1: None, 2: None}, new_sets.make(depset([1, 2]))._values)
unittest.end(env) unittest.end(env)
make_test = unittest.make(_make_test) make_test = unittest.make(_make_test)
def _copy_test(ctx): def _copy_test(ctx):
"""Unit tests for new_sets.copy.""" """Unit tests for new_sets.copy."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.new_set_equals(env, new_sets.copy(new_sets.make()), new_sets.make()) asserts.new_set_equals(env, new_sets.copy(new_sets.make()), new_sets.make())
asserts.new_set_equals(env, new_sets.copy(new_sets.make([1, 2, 3])), new_sets.make([1, 2, 3])) asserts.new_set_equals(env, new_sets.copy(new_sets.make([1, 2, 3])), new_sets.make([1, 2, 3]))
# Ensure mutating the copy does not mutate the original
original = new_sets.make([1, 2, 3])
copy = new_sets.copy(original)
copy._values[5] = None
asserts.false(env, new_sets.is_equal(original, copy))
unittest.end(env) # Ensure mutating the copy does not mutate the original
original = new_sets.make([1, 2, 3])
copy = new_sets.copy(original)
copy._values[5] = None
asserts.false(env, new_sets.is_equal(original, copy))
unittest.end(env)
copy_test = unittest.make(_copy_test) copy_test = unittest.make(_copy_test)
def _insert_test(ctx): def _insert_test(ctx):
"""Unit tests for new_sets.insert.""" """Unit tests for new_sets.insert."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.new_set_equals(env, new_sets.make([1, 2, 3]), new_sets.insert(new_sets.make([1, 2]), 3)) asserts.new_set_equals(env, new_sets.make([1, 2, 3]), new_sets.insert(new_sets.make([1, 2]), 3))
# Ensure mutating the inserted set does mutate the original set.
original = new_sets.make([1, 2, 3])
after_insert = new_sets.insert(original, 4)
asserts.new_set_equals(env, original, after_insert,
msg="Insert creates a new set which is an O(n) operation, insert should be O(1).")
unittest.end(env) # Ensure mutating the inserted set does mutate the original set.
original = new_sets.make([1, 2, 3])
after_insert = new_sets.insert(original, 4)
asserts.new_set_equals(
env,
original,
after_insert,
msg = "Insert creates a new set which is an O(n) operation, insert should be O(1).",
)
unittest.end(env)
insert_test = unittest.make(_insert_test) insert_test = unittest.make(_insert_test)
def _contains_test(ctx): def _contains_test(ctx):
"""Unit tests for new_sets.contains.""" """Unit tests for new_sets.contains."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.false(env, new_sets.contains(new_sets.make(), 1)) asserts.false(env, new_sets.contains(new_sets.make(), 1))
asserts.true(env, new_sets.contains(new_sets.make([1]), 1)) asserts.true(env, new_sets.contains(new_sets.make([1]), 1))
asserts.true(env, new_sets.contains(new_sets.make([1, 2]), 1)) asserts.true(env, new_sets.contains(new_sets.make([1, 2]), 1))
asserts.false(env, new_sets.contains(new_sets.make([2, 3]), 1)) asserts.false(env, new_sets.contains(new_sets.make([2, 3]), 1))
unittest.end(env) unittest.end(env)
contains_test = unittest.make(_contains_test) contains_test = unittest.make(_contains_test)
def _length_test(ctx): def _length_test(ctx):
"""Unit test for new_sets.length.""" """Unit test for new_sets.length."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.equals(env, 0, new_sets.length(new_sets.make())) asserts.equals(env, 0, new_sets.length(new_sets.make()))
asserts.equals(env, 1, new_sets.length(new_sets.make([1]))) asserts.equals(env, 1, new_sets.length(new_sets.make([1])))
asserts.equals(env, 2, new_sets.length(new_sets.make([1, 2]))) asserts.equals(env, 2, new_sets.length(new_sets.make([1, 2])))
unittest.end(env) unittest.end(env)
length_test = unittest.make(_length_test) length_test = unittest.make(_length_test)
def _remove_test(ctx): def _remove_test(ctx):
"""Unit test for new_sets.remove.""" """Unit test for new_sets.remove."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.new_set_equals(env, new_sets.make([1, 2]), new_sets.remove(new_sets.make([1, 2, 3]), 3)) asserts.new_set_equals(env, new_sets.make([1, 2]), new_sets.remove(new_sets.make([1, 2, 3]), 3))
# Ensure mutating the inserted set does mutate the original set.
original = new_sets.make([1, 2, 3])
after_removal = new_sets.remove(original, 3)
asserts.new_set_equals(env, original, after_removal)
unittest.end(env) # Ensure mutating the inserted set does mutate the original set.
original = new_sets.make([1, 2, 3])
after_removal = new_sets.remove(original, 3)
asserts.new_set_equals(env, original, after_removal)
unittest.end(env)
remove_test = unittest.make(_remove_test) remove_test = unittest.make(_remove_test)
def _repr_str_test(ctx): def _repr_str_test(ctx):
"""Unit test for new_sets.repr and new_sets.str.""" """Unit test for new_sets.repr and new_sets.str."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.equals(env, "[]", new_sets.repr(new_sets.make())) asserts.equals(env, "[]", new_sets.repr(new_sets.make()))
asserts.equals(env, "[1]", new_sets.repr(new_sets.make([1]))) asserts.equals(env, "[1]", new_sets.repr(new_sets.make([1])))
asserts.equals(env, "[1, 2]", new_sets.repr(new_sets.make([1, 2]))) asserts.equals(env, "[1, 2]", new_sets.repr(new_sets.make([1, 2])))
asserts.equals(env, "[]", new_sets.str(new_sets.make())) asserts.equals(env, "[]", new_sets.str(new_sets.make()))
asserts.equals(env, "[1]", new_sets.str(new_sets.make([1]))) asserts.equals(env, "[1]", new_sets.str(new_sets.make([1])))
asserts.equals(env, "[1, 2]", new_sets.str(new_sets.make([1, 2]))) asserts.equals(env, "[1, 2]", new_sets.str(new_sets.make([1, 2])))
unittest.end(env) unittest.end(env)
repr_str_test = unittest.make(_repr_str_test) repr_str_test = unittest.make(_repr_str_test)
def new_sets_test_suite(): def new_sets_test_suite():
"""Creates the test targets and test suite for new_sets.bzl tests.""" """Creates the test targets and test suite for new_sets.bzl tests."""
unittest.suite( unittest.suite(
"new_sets_tests", "new_sets_tests",
disjoint_test, disjoint_test,
intersection_test, intersection_test,
is_equal_test, is_equal_test,
is_subset_test, is_subset_test,
difference_test, difference_test,
union_test, union_test,
to_list_test, to_list_test,
make_test, make_test,
copy_test, copy_test,
insert_test, insert_test,
contains_test, contains_test,
length_test, length_test,
remove_test, remove_test,
repr_str_test, repr_str_test,
) )

View File

@ -14,72 +14,70 @@
"""Unit tests for partial.bzl.""" """Unit tests for partial.bzl."""
load("//:lib.bzl", "partial", "asserts", "unittest") load("//:lib.bzl", "asserts", "partial", "unittest")
def _make_noargs_nokwargs(): def _make_noargs_nokwargs():
"""Test utility for no args no kwargs case""" """Test utility for no args no kwargs case"""
return 1 return 1
def _make_args_nokwargs(arg1, arg2, arg3): def _make_args_nokwargs(arg1, arg2, arg3):
"""Test utility for args no kwargs case""" """Test utility for args no kwargs case"""
return arg1 + arg2 + arg3 return arg1 + arg2 + arg3
def _make_args_kwargs(arg1, arg2, arg3, **kwargs): def _make_args_kwargs(arg1, arg2, arg3, **kwargs):
"""Test utility for args and kwargs case""" """Test utility for args and kwargs case"""
return arg1 + arg2 + arg3 + kwargs["x"] + kwargs["y"] return arg1 + arg2 + arg3 + kwargs["x"] + kwargs["y"]
def _call_noargs_nokwargs(call_arg1): def _call_noargs_nokwargs(call_arg1):
"""Test utility no args no kwargs case where values passed from call site""" """Test utility no args no kwargs case where values passed from call site"""
return call_arg1; return call_arg1
def _call_args_nokwargs(func_arg1, call_arg1): def _call_args_nokwargs(func_arg1, call_arg1):
"""Test utility for args no kwargs case where values passed from call site""" """Test utility for args no kwargs case where values passed from call site"""
return func_arg1 + call_arg1; return func_arg1 + call_arg1
def _call_args_kwargs(func_arg1, call_arg1, func_mult, call_mult): def _call_args_kwargs(func_arg1, call_arg1, func_mult, call_mult):
"""Test utility for args and kwargs case where values passed from call site""" """Test utility for args and kwargs case where values passed from call site"""
return (func_arg1 + call_arg1) * func_mult * call_mult; return (func_arg1 + call_arg1) * func_mult * call_mult
def _make_call_test(ctx): def _make_call_test(ctx):
"""Unit tests for partial.make and partial.call.""" """Unit tests for partial.make and partial.call."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# Test cases where there are no args (or kwargs) at the make site, only # Test cases where there are no args (or kwargs) at the make site, only
# at the call site. # at the call site.
foo = partial.make(_make_noargs_nokwargs) foo = partial.make(_make_noargs_nokwargs)
asserts.equals(env, 1, partial.call(foo)) asserts.equals(env, 1, partial.call(foo))
foo = partial.make(_make_args_nokwargs) foo = partial.make(_make_args_nokwargs)
asserts.equals(env, 6, partial.call(foo, 1, 2, 3)) asserts.equals(env, 6, partial.call(foo, 1, 2, 3))
foo = partial.make(_make_args_kwargs) foo = partial.make(_make_args_kwargs)
asserts.equals(env, 15, partial.call(foo, 1, 2, 3, x=4, y=5)) asserts.equals(env, 15, partial.call(foo, 1, 2, 3, x = 4, y = 5))
# Test cases where there are args (and/or kwargs) at the make site and the # Test cases where there are args (and/or kwargs) at the make site and the
# call site. # call site.
foo = partial.make(_call_noargs_nokwargs, 100) foo = partial.make(_call_noargs_nokwargs, 100)
asserts.equals(env, 100, partial.call(foo)) asserts.equals(env, 100, partial.call(foo))
foo = partial.make(_call_args_nokwargs, 100) foo = partial.make(_call_args_nokwargs, 100)
asserts.equals(env, 112, partial.call(foo, 12)) asserts.equals(env, 112, partial.call(foo, 12))
foo = partial.make(_call_args_kwargs, 100, func_mult=10) foo = partial.make(_call_args_kwargs, 100, func_mult = 10)
asserts.equals(env, 2240, partial.call(foo, 12, call_mult=2)) asserts.equals(env, 2240, partial.call(foo, 12, call_mult = 2))
# Test case where there are args and kwargs ath the make site, and the call # Test case where there are args and kwargs ath the make site, and the call
# site overrides some make site args. # site overrides some make site args.
foo = partial.make(_call_args_kwargs, 100, func_mult=10) foo = partial.make(_call_args_kwargs, 100, func_mult = 10)
asserts.equals(env, 1120, partial.call(foo, 12, func_mult=5, call_mult=2)) asserts.equals(env, 1120, partial.call(foo, 12, func_mult = 5, call_mult = 2))
unittest.end(env) unittest.end(env)
make_call_test = unittest.make(_make_call_test) make_call_test = unittest.make(_make_call_test)
def partial_test_suite(): def partial_test_suite():
"""Creates the test targets and test suite for partial.bzl tests.""" """Creates the test targets and test suite for partial.bzl tests."""
unittest.suite( unittest.suite(
"partial_tests", "partial_tests",
make_call_test, make_call_test,
) )

View File

@ -14,271 +14,271 @@
"""Unit tests for paths.bzl.""" """Unit tests for paths.bzl."""
load("//:lib.bzl", "paths", "asserts", "unittest") load("//:lib.bzl", "asserts", "paths", "unittest")
def _basename_test(ctx): def _basename_test(ctx):
"""Unit tests for paths.basename.""" """Unit tests for paths.basename."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# Verify some degenerate cases. # Verify some degenerate cases.
asserts.equals(env, "", paths.basename("")) asserts.equals(env, "", paths.basename(""))
asserts.equals(env, "", paths.basename("/")) asserts.equals(env, "", paths.basename("/"))
asserts.equals(env, "bar", paths.basename("foo///bar")) asserts.equals(env, "bar", paths.basename("foo///bar"))
# Verify some realistic cases. # Verify some realistic cases.
asserts.equals(env, "foo", paths.basename("foo")) asserts.equals(env, "foo", paths.basename("foo"))
asserts.equals(env, "foo", paths.basename("/foo")) asserts.equals(env, "foo", paths.basename("/foo"))
asserts.equals(env, "foo", paths.basename("bar/foo")) asserts.equals(env, "foo", paths.basename("bar/foo"))
asserts.equals(env, "foo", paths.basename("/bar/foo")) asserts.equals(env, "foo", paths.basename("/bar/foo"))
# Verify that we correctly duplicate Python's os.path.basename behavior, # Verify that we correctly duplicate Python's os.path.basename behavior,
# where a trailing slash means the basename is empty. # where a trailing slash means the basename is empty.
asserts.equals(env, "", paths.basename("foo/")) asserts.equals(env, "", paths.basename("foo/"))
asserts.equals(env, "", paths.basename("/foo/")) asserts.equals(env, "", paths.basename("/foo/"))
unittest.end(env) unittest.end(env)
basename_test = unittest.make(_basename_test) basename_test = unittest.make(_basename_test)
def _dirname_test(ctx): def _dirname_test(ctx):
"""Unit tests for paths.dirname.""" """Unit tests for paths.dirname."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# Verify some degenerate cases. # Verify some degenerate cases.
asserts.equals(env, "", paths.dirname("")) asserts.equals(env, "", paths.dirname(""))
asserts.equals(env, "/", paths.dirname("/")) asserts.equals(env, "/", paths.dirname("/"))
asserts.equals(env, "foo", paths.dirname("foo///bar")) asserts.equals(env, "foo", paths.dirname("foo///bar"))
# Verify some realistic cases. # Verify some realistic cases.
asserts.equals(env, "", paths.dirname("foo")) asserts.equals(env, "", paths.dirname("foo"))
asserts.equals(env, "/", paths.dirname("/foo")) asserts.equals(env, "/", paths.dirname("/foo"))
asserts.equals(env, "bar", paths.dirname("bar/foo")) asserts.equals(env, "bar", paths.dirname("bar/foo"))
asserts.equals(env, "/bar", paths.dirname("/bar/foo")) asserts.equals(env, "/bar", paths.dirname("/bar/foo"))
# Verify that we correctly duplicate Python's os.path.dirname behavior, # Verify that we correctly duplicate Python's os.path.dirname behavior,
# where a trailing slash means the dirname is the same as the original # where a trailing slash means the dirname is the same as the original
# path (without the trailing slash). # path (without the trailing slash).
asserts.equals(env, "foo", paths.dirname("foo/")) asserts.equals(env, "foo", paths.dirname("foo/"))
asserts.equals(env, "/foo", paths.dirname("/foo/")) asserts.equals(env, "/foo", paths.dirname("/foo/"))
unittest.end(env) unittest.end(env)
dirname_test = unittest.make(_dirname_test) dirname_test = unittest.make(_dirname_test)
def _is_absolute_test(ctx): def _is_absolute_test(ctx):
"""Unit tests for paths.is_absolute.""" """Unit tests for paths.is_absolute."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# Try a degenerate case. # Try a degenerate case.
asserts.false(env, paths.is_absolute("")) asserts.false(env, paths.is_absolute(""))
# Try some relative paths. # Try some relative paths.
asserts.false(env, paths.is_absolute("foo")) asserts.false(env, paths.is_absolute("foo"))
asserts.false(env, paths.is_absolute("foo/")) asserts.false(env, paths.is_absolute("foo/"))
asserts.false(env, paths.is_absolute("foo/bar")) asserts.false(env, paths.is_absolute("foo/bar"))
# Try some absolute paths. # Try some absolute paths.
asserts.true(env, paths.is_absolute("/")) asserts.true(env, paths.is_absolute("/"))
asserts.true(env, paths.is_absolute("/foo")) asserts.true(env, paths.is_absolute("/foo"))
asserts.true(env, paths.is_absolute("/foo/")) asserts.true(env, paths.is_absolute("/foo/"))
asserts.true(env, paths.is_absolute("/foo/bar")) asserts.true(env, paths.is_absolute("/foo/bar"))
unittest.end(env) unittest.end(env)
is_absolute_test = unittest.make(_is_absolute_test) is_absolute_test = unittest.make(_is_absolute_test)
def _join_test(ctx): def _join_test(ctx):
"""Unit tests for paths.join.""" """Unit tests for paths.join."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# Try a degenerate case. # Try a degenerate case.
asserts.equals(env, "", paths.join("")) asserts.equals(env, "", paths.join(""))
# Try some basic paths. # Try some basic paths.
asserts.equals(env, "foo", paths.join("foo")) asserts.equals(env, "foo", paths.join("foo"))
asserts.equals(env, "foo/bar", paths.join("foo", "bar")) asserts.equals(env, "foo/bar", paths.join("foo", "bar"))
asserts.equals(env, "foo/bar/baz", paths.join("foo", "bar", "baz")) asserts.equals(env, "foo/bar/baz", paths.join("foo", "bar", "baz"))
# Make sure an initially absolute path stays absolute. # Make sure an initially absolute path stays absolute.
asserts.equals(env, "/foo", paths.join("/foo")) asserts.equals(env, "/foo", paths.join("/foo"))
asserts.equals(env, "/foo/bar", paths.join("/foo", "bar")) asserts.equals(env, "/foo/bar", paths.join("/foo", "bar"))
# Make sure an absolute path later in the list resets the result. # Make sure an absolute path later in the list resets the result.
asserts.equals(env, "/baz", paths.join("foo", "bar", "/baz")) asserts.equals(env, "/baz", paths.join("foo", "bar", "/baz"))
asserts.equals(env, "/baz", paths.join("foo", "/bar", "/baz")) asserts.equals(env, "/baz", paths.join("foo", "/bar", "/baz"))
asserts.equals(env, "/bar/baz", paths.join("foo", "/bar", "baz")) asserts.equals(env, "/bar/baz", paths.join("foo", "/bar", "baz"))
asserts.equals(env, "/bar", paths.join("/foo", "/bar")) asserts.equals(env, "/bar", paths.join("/foo", "/bar"))
# Make sure a leading empty segment doesn't make it absolute. # Make sure a leading empty segment doesn't make it absolute.
asserts.equals(env, "foo", paths.join("", "foo")) asserts.equals(env, "foo", paths.join("", "foo"))
# Try some trailing slash scenarios. # Try some trailing slash scenarios.
asserts.equals(env, "foo/", paths.join("foo", "")) asserts.equals(env, "foo/", paths.join("foo", ""))
asserts.equals(env, "foo/", paths.join("foo/")) asserts.equals(env, "foo/", paths.join("foo/"))
asserts.equals(env, "foo/", paths.join("foo/", "")) asserts.equals(env, "foo/", paths.join("foo/", ""))
asserts.equals(env, "foo//", paths.join("foo//", "")) asserts.equals(env, "foo//", paths.join("foo//", ""))
asserts.equals(env, "foo//", paths.join("foo//")) asserts.equals(env, "foo//", paths.join("foo//"))
asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz", "")) asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz", ""))
asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz/")) asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz/"))
asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz/", "")) asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz/", ""))
# Make sure that adjacent empty segments don't add extra path separators. # Make sure that adjacent empty segments don't add extra path separators.
asserts.equals(env, "foo/", paths.join("foo", "", "")) asserts.equals(env, "foo/", paths.join("foo", "", ""))
asserts.equals(env, "foo", paths.join("", "", "foo")) asserts.equals(env, "foo", paths.join("", "", "foo"))
asserts.equals(env, "foo/bar", paths.join("foo", "", "", "bar")) asserts.equals(env, "foo/bar", paths.join("foo", "", "", "bar"))
unittest.end(env) unittest.end(env)
join_test = unittest.make(_join_test) join_test = unittest.make(_join_test)
def _normalize_test(ctx): def _normalize_test(ctx):
"""Unit tests for paths.normalize.""" """Unit tests for paths.normalize."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# Try the most basic case. # Try the most basic case.
asserts.equals(env, ".", paths.normalize("")) asserts.equals(env, ".", paths.normalize(""))
# Try some basic adjacent-slash removal. # Try some basic adjacent-slash removal.
asserts.equals(env, "foo/bar", paths.normalize("foo//bar")) asserts.equals(env, "foo/bar", paths.normalize("foo//bar"))
asserts.equals(env, "foo/bar", paths.normalize("foo////bar")) asserts.equals(env, "foo/bar", paths.normalize("foo////bar"))
# Try some "." removal. # Try some "." removal.
asserts.equals(env, "foo/bar", paths.normalize("foo/./bar")) asserts.equals(env, "foo/bar", paths.normalize("foo/./bar"))
asserts.equals(env, "foo/bar", paths.normalize("./foo/bar")) asserts.equals(env, "foo/bar", paths.normalize("./foo/bar"))
asserts.equals(env, "foo/bar", paths.normalize("foo/bar/.")) asserts.equals(env, "foo/bar", paths.normalize("foo/bar/."))
asserts.equals(env, "/", paths.normalize("/.")) asserts.equals(env, "/", paths.normalize("/."))
# Try some ".." removal. # Try some ".." removal.
asserts.equals(env, "bar", paths.normalize("foo/../bar")) asserts.equals(env, "bar", paths.normalize("foo/../bar"))
asserts.equals(env, "foo", paths.normalize("foo/bar/..")) asserts.equals(env, "foo", paths.normalize("foo/bar/.."))
asserts.equals(env, ".", paths.normalize("foo/..")) asserts.equals(env, ".", paths.normalize("foo/.."))
asserts.equals(env, ".", paths.normalize("foo/bar/../..")) asserts.equals(env, ".", paths.normalize("foo/bar/../.."))
asserts.equals(env, "..", paths.normalize("foo/../..")) asserts.equals(env, "..", paths.normalize("foo/../.."))
asserts.equals(env, "/", paths.normalize("/foo/../..")) asserts.equals(env, "/", paths.normalize("/foo/../.."))
asserts.equals(env, "../../c", paths.normalize("a/b/../../../../c/d/..")) asserts.equals(env, "../../c", paths.normalize("a/b/../../../../c/d/.."))
# Make sure one or two initial slashes are preserved, but three or more are # Make sure one or two initial slashes are preserved, but three or more are
# collapsed to a single slash. # collapsed to a single slash.
asserts.equals(env, "/foo", paths.normalize("/foo")) asserts.equals(env, "/foo", paths.normalize("/foo"))
asserts.equals(env, "//foo", paths.normalize("//foo")) asserts.equals(env, "//foo", paths.normalize("//foo"))
asserts.equals(env, "/foo", paths.normalize("///foo")) asserts.equals(env, "/foo", paths.normalize("///foo"))
# Trailing slashes should be removed unless the entire path is a trailing # Trailing slashes should be removed unless the entire path is a trailing
# slash. # slash.
asserts.equals(env, "/", paths.normalize("/")) asserts.equals(env, "/", paths.normalize("/"))
asserts.equals(env, "foo", paths.normalize("foo/")) asserts.equals(env, "foo", paths.normalize("foo/"))
asserts.equals(env, "foo/bar", paths.normalize("foo/bar/")) asserts.equals(env, "foo/bar", paths.normalize("foo/bar/"))
unittest.end(env) unittest.end(env)
normalize_test = unittest.make(_normalize_test) normalize_test = unittest.make(_normalize_test)
def _relativize_test(ctx): def _relativize_test(ctx):
"""Unit tests for paths.relativize.""" """Unit tests for paths.relativize."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# Make sure that relative-to-current-directory works in all forms. # Make sure that relative-to-current-directory works in all forms.
asserts.equals(env, "foo", paths.relativize("foo", "")) asserts.equals(env, "foo", paths.relativize("foo", ""))
asserts.equals(env, "foo", paths.relativize("foo", ".")) asserts.equals(env, "foo", paths.relativize("foo", "."))
# Try some regular cases. # Try some regular cases.
asserts.equals(env, "bar", paths.relativize("foo/bar", "foo")) asserts.equals(env, "bar", paths.relativize("foo/bar", "foo"))
asserts.equals(env, "baz", paths.relativize("foo/bar/baz", "foo/bar")) asserts.equals(env, "baz", paths.relativize("foo/bar/baz", "foo/bar"))
asserts.equals(env, "bar/baz", paths.relativize("foo/bar/baz", "foo")) asserts.equals(env, "bar/baz", paths.relativize("foo/bar/baz", "foo"))
# Try a case where a parent directory is normalized away. # Try a case where a parent directory is normalized away.
asserts.equals(env, "baz", paths.relativize("foo/bar/../baz", "foo")) asserts.equals(env, "baz", paths.relativize("foo/bar/../baz", "foo"))
# Relative paths work, as long as they share a common start. # Relative paths work, as long as they share a common start.
asserts.equals(env, "file", paths.relativize("../foo/bar/baz/file", "../foo/bar/baz")) asserts.equals(env, "file", paths.relativize("../foo/bar/baz/file", "../foo/bar/baz"))
asserts.equals(env, "baz/file", paths.relativize("../foo/bar/baz/file", "../foo/bar")) asserts.equals(env, "baz/file", paths.relativize("../foo/bar/baz/file", "../foo/bar"))
# TODO(allevato): Test failure cases, once that is possible. # TODO(allevato): Test failure cases, once that is possible.
unittest.end(env) unittest.end(env)
relativize_test = unittest.make(_relativize_test) relativize_test = unittest.make(_relativize_test)
def _replace_extension_test(ctx): def _replace_extension_test(ctx):
"""Unit tests for paths.replace_extension.""" """Unit tests for paths.replace_extension."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# Try some degenerate cases. # Try some degenerate cases.
asserts.equals(env, ".foo", paths.replace_extension("", ".foo")) asserts.equals(env, ".foo", paths.replace_extension("", ".foo"))
asserts.equals(env, "/.foo", paths.replace_extension("/", ".foo")) asserts.equals(env, "/.foo", paths.replace_extension("/", ".foo"))
asserts.equals(env, "foo.bar", paths.replace_extension("foo", ".bar")) asserts.equals(env, "foo.bar", paths.replace_extension("foo", ".bar"))
# Try a directory with an extension and basename that doesn't have one. # Try a directory with an extension and basename that doesn't have one.
asserts.equals(env, "foo.bar/baz.quux", asserts.equals(
paths.replace_extension("foo.bar/baz", ".quux")) env,
"foo.bar/baz.quux",
paths.replace_extension("foo.bar/baz", ".quux"),
)
# Now try some things with legit extensions. # Now try some things with legit extensions.
asserts.equals(env, "a.z", paths.replace_extension("a.b", ".z")) asserts.equals(env, "a.z", paths.replace_extension("a.b", ".z"))
asserts.equals(env, "a.b.z", paths.replace_extension("a.b.c", ".z")) asserts.equals(env, "a.b.z", paths.replace_extension("a.b.c", ".z"))
asserts.equals(env, "a/b.z", paths.replace_extension("a/b.c", ".z")) asserts.equals(env, "a/b.z", paths.replace_extension("a/b.c", ".z"))
asserts.equals(env, "a.b/c.z", paths.replace_extension("a.b/c.d", ".z")) asserts.equals(env, "a.b/c.z", paths.replace_extension("a.b/c.d", ".z"))
asserts.equals(env, ".a/b.z", paths.replace_extension(".a/b.c", ".z")) asserts.equals(env, ".a/b.z", paths.replace_extension(".a/b.c", ".z"))
asserts.equals(env, ".a.z", paths.replace_extension(".a.b", ".z")) asserts.equals(env, ".a.z", paths.replace_extension(".a.b", ".z"))
# Verify that we don't insert a period on the extension if none is provided. # Verify that we don't insert a period on the extension if none is provided.
asserts.equals(env, "foobaz", paths.replace_extension("foo.bar", "baz")) asserts.equals(env, "foobaz", paths.replace_extension("foo.bar", "baz"))
unittest.end(env) unittest.end(env)
replace_extension_test = unittest.make(_replace_extension_test) replace_extension_test = unittest.make(_replace_extension_test)
def _split_extension_test(ctx): def _split_extension_test(ctx):
"""Unit tests for paths.split_extension.""" """Unit tests for paths.split_extension."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# Try some degenerate cases. # Try some degenerate cases.
asserts.equals(env, ("", ""), paths.split_extension("")) asserts.equals(env, ("", ""), paths.split_extension(""))
asserts.equals(env, ("/", ""), paths.split_extension("/")) asserts.equals(env, ("/", ""), paths.split_extension("/"))
asserts.equals(env, ("foo", ""), paths.split_extension("foo")) asserts.equals(env, ("foo", ""), paths.split_extension("foo"))
# Try some paths whose basenames start with ".". # Try some paths whose basenames start with ".".
asserts.equals(env, (".", ""), paths.split_extension(".")) asserts.equals(env, (".", ""), paths.split_extension("."))
asserts.equals(env, (".bashrc", ""), paths.split_extension(".bashrc")) asserts.equals(env, (".bashrc", ""), paths.split_extension(".bashrc"))
asserts.equals(env, ("foo/.bashrc", ""), paths.split_extension("foo/.bashrc")) asserts.equals(env, ("foo/.bashrc", ""), paths.split_extension("foo/.bashrc"))
asserts.equals(env, (".foo/.bashrc", ""), asserts.equals(
paths.split_extension(".foo/.bashrc")) env,
(".foo/.bashrc", ""),
paths.split_extension(".foo/.bashrc"),
)
# Try some directories with extensions with basenames that don't have one. # Try some directories with extensions with basenames that don't have one.
asserts.equals(env, ("foo.bar/baz", ""), paths.split_extension("foo.bar/baz")) asserts.equals(env, ("foo.bar/baz", ""), paths.split_extension("foo.bar/baz"))
asserts.equals(env, ("foo.bar/.bashrc", ""), asserts.equals(
paths.split_extension("foo.bar/.bashrc")) env,
("foo.bar/.bashrc", ""),
paths.split_extension("foo.bar/.bashrc"),
)
# Now try some things that will actually get split. # Now try some things that will actually get split.
asserts.equals(env, ("a", ".b"), paths.split_extension("a.b")) asserts.equals(env, ("a", ".b"), paths.split_extension("a.b"))
asserts.equals(env, ("a.b", ".c"), paths.split_extension("a.b.c")) asserts.equals(env, ("a.b", ".c"), paths.split_extension("a.b.c"))
asserts.equals(env, ("a/b", ".c"), paths.split_extension("a/b.c")) asserts.equals(env, ("a/b", ".c"), paths.split_extension("a/b.c"))
asserts.equals(env, ("a.b/c", ".d"), paths.split_extension("a.b/c.d")) asserts.equals(env, ("a.b/c", ".d"), paths.split_extension("a.b/c.d"))
asserts.equals(env, (".a/b", ".c"), paths.split_extension(".a/b.c")) asserts.equals(env, (".a/b", ".c"), paths.split_extension(".a/b.c"))
asserts.equals(env, (".a", ".b"), paths.split_extension(".a.b")) asserts.equals(env, (".a", ".b"), paths.split_extension(".a.b"))
unittest.end(env) unittest.end(env)
split_extension_test = unittest.make(_split_extension_test) split_extension_test = unittest.make(_split_extension_test)
def paths_test_suite(): def paths_test_suite():
"""Creates the test targets and test suite for paths.bzl tests.""" """Creates the test targets and test suite for paths.bzl tests."""
unittest.suite( unittest.suite(
"paths_tests", "paths_tests",
basename_test, basename_test,
dirname_test, dirname_test,
is_absolute_test, is_absolute_test,
join_test, join_test,
normalize_test, normalize_test,
relativize_test, relativize_test,
replace_extension_test, replace_extension_test,
split_extension_test, split_extension_test,
) )

View File

@ -14,40 +14,51 @@
"""Unit tests for selects.bzl.""" """Unit tests for selects.bzl."""
load("//:lib.bzl", "selects", "asserts", "unittest") load("//:lib.bzl", "asserts", "selects", "unittest")
def _with_or_test(ctx): def _with_or_test(ctx):
"""Unit tests for with_or.""" """Unit tests for with_or."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# We actually test on with_or_dict because Skylark can't get the # We actually test on with_or_dict because Skylark can't get the
# dictionary from a select(). # dictionary from a select().
# Test select()-compatible input syntax. # Test select()-compatible input syntax.
input_dict = {":foo": ":d1", "//conditions:default": ":d1"} input_dict = {":foo": ":d1", "//conditions:default": ":d1"}
asserts.equals(env, input_dict, selects.with_or_dict(input_dict)) asserts.equals(env, input_dict, selects.with_or_dict(input_dict))
# Test OR syntax. # Test OR syntax.
or_dict = {(":foo", ":bar"): ":d1"} or_dict = {(":foo", ":bar"): ":d1"}
asserts.equals(env, {":foo": ":d1", ":bar": ":d1"}, asserts.equals(
selects.with_or_dict(or_dict)) env,
{":foo": ":d1", ":bar": ":d1"},
selects.with_or_dict(or_dict),
)
# Test mixed syntax. # Test mixed syntax.
mixed_dict = {":foo": ":d1", (":bar", ":baz"): ":d2", mixed_dict = {
"//conditions:default": ":d3"} ":foo": ":d1",
asserts.equals(env, {":foo": ":d1", ":bar": ":d2", ":baz": ":d2", (":bar", ":baz"): ":d2",
"//conditions:default": ":d3"}, "//conditions:default": ":d3",
selects.with_or_dict(mixed_dict)) }
asserts.equals(
env,
{
":foo": ":d1",
":bar": ":d2",
":baz": ":d2",
"//conditions:default": ":d3",
},
selects.with_or_dict(mixed_dict),
)
unittest.end(env) unittest.end(env)
with_or_test = unittest.make(_with_or_test) with_or_test = unittest.make(_with_or_test)
def selects_test_suite(): def selects_test_suite():
"""Creates the test targets and test suite for selects.bzl tests.""" """Creates the test targets and test suite for selects.bzl tests."""
unittest.suite( unittest.suite(
"selects_tests", "selects_tests",
with_or_test, with_or_test,
) )

View File

@ -14,146 +14,139 @@
"""Unit tests for sets.bzl.""" """Unit tests for sets.bzl."""
load("//:lib.bzl", "sets", "asserts", "unittest") load("//:lib.bzl", "asserts", "sets", "unittest")
def _is_equal_test(ctx): def _is_equal_test(ctx):
"""Unit tests for sets.is_equal.""" """Unit tests for sets.is_equal."""
# Note that if this test fails, the results for the other `sets` tests will # Note that if this test fails, the results for the other `sets` tests will
# be inconclusive because they use `asserts.set_equals`, which in turn calls # be inconclusive because they use `asserts.set_equals`, which in turn calls
# `sets.is_equal`. # `sets.is_equal`.
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.true(env, sets.is_equal([], [])) asserts.true(env, sets.is_equal([], []))
asserts.false(env, sets.is_equal([], [1])) asserts.false(env, sets.is_equal([], [1]))
asserts.false(env, sets.is_equal([1], [])) asserts.false(env, sets.is_equal([1], []))
asserts.true(env, sets.is_equal([1], [1])) asserts.true(env, sets.is_equal([1], [1]))
asserts.false(env, sets.is_equal([1], [1, 2])) asserts.false(env, sets.is_equal([1], [1, 2]))
asserts.false(env, sets.is_equal([1], [2])) asserts.false(env, sets.is_equal([1], [2]))
asserts.false(env, sets.is_equal([1], depset([1, 2]))) asserts.false(env, sets.is_equal([1], depset([1, 2])))
# Verify that the implementation is not using == on the sets directly. # Verify that the implementation is not using == on the sets directly.
asserts.true(env, sets.is_equal(depset([1]), depset([1]))) asserts.true(env, sets.is_equal(depset([1]), depset([1])))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.true(env, sets.is_equal([1, 1], [1])) asserts.true(env, sets.is_equal([1, 1], [1]))
unittest.end(env) unittest.end(env)
is_equal_test = unittest.make(_is_equal_test) is_equal_test = unittest.make(_is_equal_test)
def _is_subset_test(ctx): def _is_subset_test(ctx):
"""Unit tests for sets.is_subset.""" """Unit tests for sets.is_subset."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.true(env, sets.is_subset([], [])) asserts.true(env, sets.is_subset([], []))
asserts.true(env, sets.is_subset([], [1])) asserts.true(env, sets.is_subset([], [1]))
asserts.false(env, sets.is_subset([1], [])) asserts.false(env, sets.is_subset([1], []))
asserts.true(env, sets.is_subset([1], [1])) asserts.true(env, sets.is_subset([1], [1]))
asserts.true(env, sets.is_subset([1], [1, 2])) asserts.true(env, sets.is_subset([1], [1, 2]))
asserts.false(env, sets.is_subset([1], [2])) asserts.false(env, sets.is_subset([1], [2]))
asserts.true(env, sets.is_subset([1], depset([1, 2]))) asserts.true(env, sets.is_subset([1], depset([1, 2])))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.true(env, sets.is_subset([1, 1], [1, 2])) asserts.true(env, sets.is_subset([1, 1], [1, 2]))
unittest.end(env) unittest.end(env)
is_subset_test = unittest.make(_is_subset_test) is_subset_test = unittest.make(_is_subset_test)
def _disjoint_test(ctx): def _disjoint_test(ctx):
"""Unit tests for sets.disjoint.""" """Unit tests for sets.disjoint."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.true(env, sets.disjoint([], [])) asserts.true(env, sets.disjoint([], []))
asserts.true(env, sets.disjoint([], [1])) asserts.true(env, sets.disjoint([], [1]))
asserts.true(env, sets.disjoint([1], [])) asserts.true(env, sets.disjoint([1], []))
asserts.false(env, sets.disjoint([1], [1])) asserts.false(env, sets.disjoint([1], [1]))
asserts.false(env, sets.disjoint([1], [1, 2])) asserts.false(env, sets.disjoint([1], [1, 2]))
asserts.true(env, sets.disjoint([1], [2])) asserts.true(env, sets.disjoint([1], [2]))
asserts.true(env, sets.disjoint([1], depset([2]))) asserts.true(env, sets.disjoint([1], depset([2])))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.false(env, sets.disjoint([1, 1], [1, 2])) asserts.false(env, sets.disjoint([1, 1], [1, 2]))
unittest.end(env) unittest.end(env)
disjoint_test = unittest.make(_disjoint_test) disjoint_test = unittest.make(_disjoint_test)
def _intersection_test(ctx): def _intersection_test(ctx):
"""Unit tests for sets.intersection.""" """Unit tests for sets.intersection."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.set_equals(env, [], sets.intersection([], [])) asserts.set_equals(env, [], sets.intersection([], []))
asserts.set_equals(env, [], sets.intersection([], [1])) asserts.set_equals(env, [], sets.intersection([], [1]))
asserts.set_equals(env, [], sets.intersection([1], [])) asserts.set_equals(env, [], sets.intersection([1], []))
asserts.set_equals(env, [1], sets.intersection([1], [1])) asserts.set_equals(env, [1], sets.intersection([1], [1]))
asserts.set_equals(env, [1], sets.intersection([1], [1, 2])) asserts.set_equals(env, [1], sets.intersection([1], [1, 2]))
asserts.set_equals(env, [], sets.intersection([1], [2])) asserts.set_equals(env, [], sets.intersection([1], [2]))
asserts.set_equals(env, [1], sets.intersection([1], depset([1]))) asserts.set_equals(env, [1], sets.intersection([1], depset([1])))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.set_equals(env, [1], sets.intersection([1, 1], [1, 2])) asserts.set_equals(env, [1], sets.intersection([1, 1], [1, 2]))
unittest.end(env) unittest.end(env)
intersection_test = unittest.make(_intersection_test) intersection_test = unittest.make(_intersection_test)
def _union_test(ctx): def _union_test(ctx):
"""Unit tests for sets.union.""" """Unit tests for sets.union."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.set_equals(env, [], sets.union()) asserts.set_equals(env, [], sets.union())
asserts.set_equals(env, [1], sets.union([1])) asserts.set_equals(env, [1], sets.union([1]))
asserts.set_equals(env, [], sets.union([], [])) asserts.set_equals(env, [], sets.union([], []))
asserts.set_equals(env, [1], sets.union([], [1])) asserts.set_equals(env, [1], sets.union([], [1]))
asserts.set_equals(env, [1], sets.union([1], [])) asserts.set_equals(env, [1], sets.union([1], []))
asserts.set_equals(env, [1], sets.union([1], [1])) asserts.set_equals(env, [1], sets.union([1], [1]))
asserts.set_equals(env, [1, 2], sets.union([1], [1, 2])) asserts.set_equals(env, [1, 2], sets.union([1], [1, 2]))
asserts.set_equals(env, [1, 2], sets.union([1], [2])) asserts.set_equals(env, [1, 2], sets.union([1], [2]))
asserts.set_equals(env, [1], sets.union([1], depset([1]))) asserts.set_equals(env, [1], sets.union([1], depset([1])))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.set_equals(env, [1, 2], sets.union([1, 1], [1, 2])) asserts.set_equals(env, [1, 2], sets.union([1, 1], [1, 2]))
unittest.end(env) unittest.end(env)
union_test = unittest.make(_union_test) union_test = unittest.make(_union_test)
def _difference_test(ctx): def _difference_test(ctx):
"""Unit tests for sets.difference.""" """Unit tests for sets.difference."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.set_equals(env, [], sets.difference([], [])) asserts.set_equals(env, [], sets.difference([], []))
asserts.set_equals(env, [], sets.difference([], [1])) asserts.set_equals(env, [], sets.difference([], [1]))
asserts.set_equals(env, [1], sets.difference([1], [])) asserts.set_equals(env, [1], sets.difference([1], []))
asserts.set_equals(env, [], sets.difference([1], [1])) asserts.set_equals(env, [], sets.difference([1], [1]))
asserts.set_equals(env, [], sets.difference([1], [1, 2])) asserts.set_equals(env, [], sets.difference([1], [1, 2]))
asserts.set_equals(env, [1], sets.difference([1], [2])) asserts.set_equals(env, [1], sets.difference([1], [2]))
asserts.set_equals(env, [], sets.difference([1], depset([1]))) asserts.set_equals(env, [], sets.difference([1], depset([1])))
# If passing a list, verify that duplicate elements are ignored. # If passing a list, verify that duplicate elements are ignored.
asserts.set_equals(env, [2], sets.difference([1, 2], [1, 1])) asserts.set_equals(env, [2], sets.difference([1, 2], [1, 1]))
unittest.end(env) unittest.end(env)
difference_test = unittest.make(_difference_test) difference_test = unittest.make(_difference_test)
def sets_test_suite(): def sets_test_suite():
"""Creates the test targets and test suite for sets.bzl tests.""" """Creates the test targets and test suite for sets.bzl tests."""
unittest.suite( unittest.suite(
"sets_tests", "sets_tests",
disjoint_test, disjoint_test,
intersection_test, intersection_test,
is_equal_test, is_equal_test,
is_subset_test, is_subset_test,
difference_test, difference_test,
union_test, union_test,
) )

View File

@ -14,98 +14,94 @@
"""Unit tests for shell.bzl.""" """Unit tests for shell.bzl."""
load("//:lib.bzl", "shell", "asserts", "unittest") load("//:lib.bzl", "asserts", "shell", "unittest")
def _shell_array_literal_test(ctx): def _shell_array_literal_test(ctx):
"""Unit tests for shell.array_literal.""" """Unit tests for shell.array_literal."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.equals(env, "()", shell.array_literal([])) asserts.equals(env, "()", shell.array_literal([]))
asserts.equals(env, "('1')", shell.array_literal([1])) asserts.equals(env, "('1')", shell.array_literal([1]))
asserts.equals(env, "('1' '2' '3')", shell.array_literal([1, 2, 3])) asserts.equals(env, "('1' '2' '3')", shell.array_literal([1, 2, 3]))
asserts.equals(env, "('$foo')", shell.array_literal(["$foo"])) asserts.equals(env, "('$foo')", shell.array_literal(["$foo"]))
asserts.equals(env, "('qu\"o\"te')", shell.array_literal(['qu"o"te'])) asserts.equals(env, "('qu\"o\"te')", shell.array_literal(['qu"o"te']))
unittest.end(env) unittest.end(env)
shell_array_literal_test = unittest.make(_shell_array_literal_test) shell_array_literal_test = unittest.make(_shell_array_literal_test)
def _shell_quote_test(ctx): def _shell_quote_test(ctx):
"""Unit tests for shell.quote.""" """Unit tests for shell.quote."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.equals(env, "'foo'", shell.quote("foo")) asserts.equals(env, "'foo'", shell.quote("foo"))
asserts.equals(env, "'foo bar'", shell.quote("foo bar")) asserts.equals(env, "'foo bar'", shell.quote("foo bar"))
asserts.equals(env, "'three spaces'", shell.quote("three spaces")) asserts.equals(env, "'three spaces'", shell.quote("three spaces"))
asserts.equals(env, "' leading'", shell.quote(" leading")) asserts.equals(env, "' leading'", shell.quote(" leading"))
asserts.equals(env, "'trailing '", shell.quote("trailing ")) asserts.equals(env, "'trailing '", shell.quote("trailing "))
asserts.equals(env, "'new\nline'", shell.quote("new\nline")) asserts.equals(env, "'new\nline'", shell.quote("new\nline"))
asserts.equals(env, "'tab\tcharacter'", shell.quote("tab\tcharacter")) asserts.equals(env, "'tab\tcharacter'", shell.quote("tab\tcharacter"))
asserts.equals(env, "'$foo'", shell.quote("$foo")) asserts.equals(env, "'$foo'", shell.quote("$foo"))
asserts.equals(env, "'qu\"o\"te'", shell.quote('qu"o"te')) asserts.equals(env, "'qu\"o\"te'", shell.quote('qu"o"te'))
asserts.equals(env, "'it'\\''s'", shell.quote("it's")) asserts.equals(env, "'it'\\''s'", shell.quote("it's"))
asserts.equals(env, "'foo\\bar'", shell.quote(r"foo\bar")) asserts.equals(env, "'foo\\bar'", shell.quote("foo\\bar"))
asserts.equals(env, "'back`echo q`uote'", shell.quote(r"back`echo q`uote")) asserts.equals(env, "'back`echo q`uote'", shell.quote("back`echo q`uote"))
unittest.end(env) unittest.end(env)
shell_quote_test = unittest.make(_shell_quote_test) shell_quote_test = unittest.make(_shell_quote_test)
def _shell_spawn_e2e_test_impl(ctx): def _shell_spawn_e2e_test_impl(ctx):
"""Test spawning a real shell.""" """Test spawning a real shell."""
args = [ args = [
"foo", "foo",
"foo bar", "foo bar",
"three spaces", "three spaces",
" leading", " leading",
"trailing ", "trailing ",
"new\nline", "new\nline",
"tab\tcharacter", "tab\tcharacter",
"$foo", "$foo",
'qu"o"te', 'qu"o"te',
"it's", "it's",
r"foo\bar", "foo\\bar",
"back`echo q`uote", "back`echo q`uote",
] ]
script_content = "\n".join([ script_content = "\n".join([
"#!/bin/bash", "#!/bin/bash",
"myarray=" + shell.array_literal(args), "myarray=" + shell.array_literal(args),
'output=$(echo "${myarray[@]}")', 'output=$(echo "${myarray[@]}")',
# For logging: # For logging:
'echo "DEBUG: output=[${output}]" >&2', 'echo "DEBUG: output=[${output}]" >&2',
# The following is a shell representation of what the echo of the quoted # The following is a shell representation of what the echo of the quoted
# array will look like. It looks a bit confusing considering it's shell # array will look like. It looks a bit confusing considering it's shell
# quoted into Python. Shell using single quotes to minimize shell # quoted into Python. Shell using single quotes to minimize shell
# escaping, so only the single quote needs to be escaped as '\'', all # escaping, so only the single quote needs to be escaped as '\'', all
# others are essentially kept literally. # others are essentially kept literally.
"expected='foo foo bar three spaces leading trailing new", "expected='foo foo bar three spaces leading trailing new",
"line tab\tcharacter $foo qu\"o\"te it'\\''s foo\\bar back`echo q`uote'", "line tab\tcharacter $foo qu\"o\"te it'\\''s foo\\bar back`echo q`uote'",
'[[ "${output}" == "${expected}" ]]', '[[ "${output}" == "${expected}" ]]',
]) ])
script_file = ctx.actions.declare_file("%s.sh" % (ctx.label.name)) script_file = ctx.actions.declare_file("%s.sh" % (ctx.label.name))
ctx.actions.write( ctx.actions.write(
output = script_file, output = script_file,
content = script_content, content = script_content,
is_executable = True, is_executable = True,
) )
return [ return [
DefaultInfo(executable=script_file), DefaultInfo(executable = script_file),
] ]
shell_spawn_e2e_test = rule( shell_spawn_e2e_test = rule(
test = True, test = True,
implementation = _shell_spawn_e2e_test_impl, implementation = _shell_spawn_e2e_test_impl,
) )
def shell_test_suite(): def shell_test_suite():
"""Creates the test targets and test suite for shell.bzl tests.""" """Creates the test targets and test suite for shell.bzl tests."""
unittest.suite( unittest.suite(
"shell_tests", "shell_tests",
shell_array_literal_test, shell_array_literal_test,
shell_quote_test, shell_quote_test,
shell_spawn_e2e_test, shell_spawn_e2e_test,
) )

View File

@ -14,36 +14,40 @@
"""Unit tests for structs.bzl.""" """Unit tests for structs.bzl."""
load("//:lib.bzl", "structs", "asserts", "unittest") load("//:lib.bzl", "asserts", "structs", "unittest")
def _add_test(ctx): def _add_test(ctx):
"""Unit tests for dicts.add.""" """Unit tests for dicts.add."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
# Test zero- and one-argument behavior. # Test zero- and one-argument behavior.
asserts.equals(env, {}, structs.to_dict(struct())) asserts.equals(env, {}, structs.to_dict(struct()))
asserts.equals(env, {"a": 1}, structs.to_dict(struct(a=1))) asserts.equals(env, {"a": 1}, structs.to_dict(struct(a = 1)))
# Test simple two-argument behavior. # Test simple two-argument behavior.
asserts.equals(env, {"a": 1, "b": 2}, structs.to_dict(struct(a=1, b=2))) asserts.equals(env, {"a": 1, "b": 2}, structs.to_dict(struct(a = 1, b = 2)))
# Test simple more-than-two-argument behavior. # Test simple more-than-two-argument behavior.
asserts.equals(env, {"a": 1, "b": 2, "c": 3, "d": 4}, asserts.equals(
structs.to_dict(struct(a=1, b=2, c=3, d=4))) env,
{"a": 1, "b": 2, "c": 3, "d": 4},
structs.to_dict(struct(a = 1, b = 2, c = 3, d = 4)),
)
# Test transformation is not applied transitively. # Test transformation is not applied transitively.
asserts.equals(env, {"a": 1, "b": struct(bb=1)}, asserts.equals(
structs.to_dict(struct(a=1, b=struct(bb=1)))) env,
{"a": 1, "b": struct(bb = 1)},
structs.to_dict(struct(a = 1, b = struct(bb = 1))),
)
unittest.end(env) unittest.end(env)
add_test = unittest.make(_add_test) add_test = unittest.make(_add_test)
def structs_test_suite(): def structs_test_suite():
"""Creates the test targets and test suite for structs.bzl tests.""" """Creates the test targets and test suite for structs.bzl tests."""
unittest.suite( unittest.suite(
"structs_tests", "structs_tests",
add_test, add_test,
) )

View File

@ -13,14 +13,12 @@
# limitations under the License. # limitations under the License.
"""Unit tests for types.bzl.""" """Unit tests for types.bzl."""
load("//:lib.bzl", "types", "asserts", "unittest") load("//:lib.bzl", "asserts", "types", "unittest")
def _a_function(): def _a_function():
"""A dummy function for testing.""" """A dummy function for testing."""
pass pass
def _is_string_test(ctx): def _is_string_test(ctx):
"""Unit tests for types.is_string.""" """Unit tests for types.is_string."""
@ -39,10 +37,8 @@ def _is_string_test(ctx):
unittest.end(env) unittest.end(env)
is_string_test = unittest.make(_is_string_test) is_string_test = unittest.make(_is_string_test)
def _is_bool_test(ctx): def _is_bool_test(ctx):
"""Unit tests for types.is_bool.""" """Unit tests for types.is_bool."""
@ -61,10 +57,8 @@ def _is_bool_test(ctx):
unittest.end(env) unittest.end(env)
is_bool_test = unittest.make(_is_bool_test) is_bool_test = unittest.make(_is_bool_test)
def _is_list_test(ctx): def _is_list_test(ctx):
"""Unit tests for types.is_list.""" """Unit tests for types.is_list."""
@ -83,10 +77,8 @@ def _is_list_test(ctx):
unittest.end(env) unittest.end(env)
is_list_test = unittest.make(_is_list_test) is_list_test = unittest.make(_is_list_test)
def _is_none_test(ctx): def _is_none_test(ctx):
"""Unit tests for types.is_none.""" """Unit tests for types.is_none."""
@ -105,10 +97,8 @@ def _is_none_test(ctx):
unittest.end(env) unittest.end(env)
is_none_test = unittest.make(_is_none_test) is_none_test = unittest.make(_is_none_test)
def _is_int_test(ctx): def _is_int_test(ctx):
"""Unit tests for types.is_int.""" """Unit tests for types.is_int."""
@ -128,17 +118,15 @@ def _is_int_test(ctx):
unittest.end(env) unittest.end(env)
is_int_test = unittest.make(_is_int_test) is_int_test = unittest.make(_is_int_test)
def _is_tuple_test(ctx): def _is_tuple_test(ctx):
"""Unit tests for types.is_tuple.""" """Unit tests for types.is_tuple."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.true(env, types.is_tuple(())) asserts.true(env, types.is_tuple(()))
asserts.true(env, types.is_tuple((1, ))) asserts.true(env, types.is_tuple((1,)))
asserts.false(env, types.is_tuple(1)) asserts.false(env, types.is_tuple(1))
asserts.false(env, types.is_tuple("s")) asserts.false(env, types.is_tuple("s"))
@ -151,17 +139,15 @@ def _is_tuple_test(ctx):
unittest.end(env) unittest.end(env)
is_tuple_test = unittest.make(_is_tuple_test) is_tuple_test = unittest.make(_is_tuple_test)
def _is_dict_test(ctx): def _is_dict_test(ctx):
"""Unit tests for types.is_dict.""" """Unit tests for types.is_dict."""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.true(env, types.is_dict({})) asserts.true(env, types.is_dict({}))
asserts.true(env, types.is_dict({'key': 'value'})) asserts.true(env, types.is_dict({"key": "value"}))
asserts.false(env, types.is_dict(1)) asserts.false(env, types.is_dict(1))
asserts.false(env, types.is_dict("s")) asserts.false(env, types.is_dict("s"))
@ -174,10 +160,8 @@ def _is_dict_test(ctx):
unittest.end(env) unittest.end(env)
is_dict_test = unittest.make(_is_dict_test) is_dict_test = unittest.make(_is_dict_test)
def _is_function_test(ctx): def _is_function_test(ctx):
"""Unit tests for types.is_dict.""" """Unit tests for types.is_dict."""
@ -196,10 +180,8 @@ def _is_function_test(ctx):
unittest.end(env) unittest.end(env)
is_function_test = unittest.make(_is_function_test) is_function_test = unittest.make(_is_function_test)
def types_test_suite(): def types_test_suite():
"""Creates the test targets and test suite for types.bzl tests.""" """Creates the test targets and test suite for types.bzl tests."""
unittest.suite( unittest.suite(

View File

@ -14,55 +14,55 @@
"""Unit tests for versions.bzl.""" """Unit tests for versions.bzl."""
load("//:lib.bzl", "versions", "asserts", "unittest") load("//:lib.bzl", "asserts", "unittest", "versions")
def _parse_test(ctx): def _parse_test(ctx):
"""Unit tests for versions.parse""" """Unit tests for versions.parse"""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.equals(env, (0, 10, 0), versions.parse("0.10.0rc1 abcd123")) asserts.equals(env, (0, 10, 0), versions.parse("0.10.0rc1 abcd123"))
asserts.equals(env, (0, 4, 0), versions.parse("0.4.0 abcd123")) asserts.equals(env, (0, 4, 0), versions.parse("0.4.0 abcd123"))
asserts.equals(env, (0, 4, 0), versions.parse("0.4.0")) asserts.equals(env, (0, 4, 0), versions.parse("0.4.0"))
asserts.equals(env, (0, 4, 0), versions.parse("0.4.0rc")) asserts.equals(env, (0, 4, 0), versions.parse("0.4.0rc"))
unittest.end(env) unittest.end(env)
def _version_comparison_test(ctx): def _version_comparison_test(ctx):
"""Unit tests for versions.is_at_least and is_at_most""" """Unit tests for versions.is_at_least and is_at_most"""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.false(env, versions.is_at_least("0.11.0 123abcd", "0.10.0rc1 abcd123")) asserts.false(env, versions.is_at_least("0.11.0 123abcd", "0.10.0rc1 abcd123"))
asserts.true(env, versions.is_at_least("0.9.0", "0.10.0rc2")) asserts.true(env, versions.is_at_least("0.9.0", "0.10.0rc2"))
asserts.true(env, versions.is_at_least("0.9.0", "0.9.0rc3")) asserts.true(env, versions.is_at_least("0.9.0", "0.9.0rc3"))
asserts.true(env, versions.is_at_least("0.9.0", "1.2.3")) asserts.true(env, versions.is_at_least("0.9.0", "1.2.3"))
asserts.false(env, versions.is_at_most("0.4.0 123abcd", "0.10.0rc1 abcd123")) asserts.false(env, versions.is_at_most("0.4.0 123abcd", "0.10.0rc1 abcd123"))
asserts.true(env, versions.is_at_most("0.4.0", "0.3.0rc2")) asserts.true(env, versions.is_at_most("0.4.0", "0.3.0rc2"))
asserts.true(env, versions.is_at_most("0.4.0", "0.4.0rc3")) asserts.true(env, versions.is_at_most("0.4.0", "0.4.0rc3"))
asserts.true(env, versions.is_at_most("1.4.0", "0.4.0rc3")) asserts.true(env, versions.is_at_most("1.4.0", "0.4.0rc3"))
unittest.end(env) unittest.end(env)
def _check_test(ctx): def _check_test(ctx):
"""Unit tests for versions.check""" """Unit tests for versions.check"""
env = unittest.begin(ctx) env = unittest.begin(ctx)
asserts.equals(env, None, versions.check("0.4.5 abcdef", bazel_version="0.10.0rc1 abcd123")) asserts.equals(env, None, versions.check("0.4.5 abcdef", bazel_version = "0.10.0rc1 abcd123"))
asserts.equals(env, None, versions.check("0.4.5", bazel_version="0.4.5")) asserts.equals(env, None, versions.check("0.4.5", bazel_version = "0.4.5"))
asserts.equals(env, None, versions.check("0.4.5", bazel_version="0.10.0rc1 abcd123")) asserts.equals(env, None, versions.check("0.4.5", bazel_version = "0.10.0rc1 abcd123"))
asserts.equals(env, None, versions.check("0.4.5", maximum_bazel_version="1.0.0", bazel_version="0.10.0rc1 abcd123")) asserts.equals(env, None, versions.check("0.4.5", maximum_bazel_version = "1.0.0", bazel_version = "0.10.0rc1 abcd123"))
unittest.end(env) unittest.end(env)
parse_test = unittest.make(_parse_test) parse_test = unittest.make(_parse_test)
version_comparison_test = unittest.make(_version_comparison_test) version_comparison_test = unittest.make(_version_comparison_test)
check_test = unittest.make(_check_test) check_test = unittest.make(_check_test)
def versions_test_suite(): def versions_test_suite():
"""Creates the test targets and test suite for versions.bzl tests.""" """Creates the test targets and test suite for versions.bzl tests."""
unittest.suite( unittest.suite(
"versions_tests", "versions_tests",
parse_test, parse_test,
version_comparison_test, version_comparison_test,
check_test, check_test,
) )