Skip to content

Commit

Permalink
Added warnings (#50)
Browse files Browse the repository at this point in the history
* Added test case for license file not referenced by package.xml
* checked the todo box for the task
* added warning string with yellow function
* introduced enum for levels of success a test can have, added warning
* Test for LicenseTagInSpdxList now gives out warning if not found, not an error. Also added test case
* Adapted package overall and all packages message to also display warnings. Changed case of tag and file not matching into warning
* Changed LicenseTextExists check to only return warning if tag and file differ. Added tests for new cases
* implementing requested changes
* Adapted warning message for LicenseTextExistCheck
* Changed status enum to numbers, added scan for warning to test for not spdx label
* Added Test for two licenses, one correct, one not in SPDX without license file
* Added two negative tests, one for not SPDX license tag without file, one for tag with corresponding SPDX file, but in the directory are other license files
* removed test case three files one tag
* renamed test python file, added python code with license

---------

Signed-off-by: Anton Utz <[email protected]>
Signed-off-by: Anton Utz <[email protected]>
  • Loading branch information
ant-u authored Feb 27, 2024
1 parent a1a2be3 commit 0d980fd
Show file tree
Hide file tree
Showing 15 changed files with 329 additions and 37 deletions.
77 changes: 55 additions & 22 deletions src/ros_license_toolkit/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"""This module contains the checks for the linter."""

import os
from enum import IntEnum
from pprint import pformat
from typing import Dict, List, Optional

Expand All @@ -25,7 +26,14 @@
from ros_license_toolkit.package import (Package, PackageException,
get_spdx_license_name,
is_license_text_file)
from ros_license_toolkit.ui_elements import NO_REASON_STR, green, red
from ros_license_toolkit.ui_elements import NO_REASON_STR, green, red, yellow


class Status(IntEnum):
"""Levels of success or failure for the output"""
SUCCESS = 0
WARNING = 1
FAILURE = 2


class Check:
Expand All @@ -34,7 +42,7 @@ class Check:
def __init__(self: 'Check'):
"""Initialize a check."""
# overall success of this check
self.success: bool = False
self.status: Status = Status.FAILURE

# explanation for success or failure for normal output
self.reason: str = NO_REASON_STR
Expand All @@ -43,13 +51,18 @@ def __init__(self: 'Check'):
self.verbose_output: str = ''

def _failed(self, reason: str):
"""Set this check as failed for reason `r`."""
self.success = False
"""Set this check as failed for `reason`."""
self.status = Status.FAILURE
self.reason = reason

def _warning(self, reason: str):
"""Set this check as passed but display a warning for reason `r`."""
self.status = Status.WARNING
self.reason = reason

def _success(self, reason: str):
"""Set this check as successful for reason `r`."""
self.success = True
self.status = Status.SUCCESS
if self.reason == NO_REASON_STR:
self.reason = ''
else:
Expand All @@ -58,23 +71,26 @@ def _success(self, reason: str):

def __str__(self) -> str:
"""Return formatted string for normal output."""
if self.success:
info: str = str(
type(self).__name__) + "\n" + \
green(f" SUCCESS {self.reason}")
info: str = str(type(self).__name__) + "\n"
if self.status == Status.SUCCESS:
info += green(f" SUCCESS {self.reason}")
elif self.status == Status.WARNING:
info += yellow(f" WARNING {self.reason}")
else:
info = str(
type(self).__name__) + "\n" + \
red(f" FAILURE {self.reason}")
info += red(f" FAILURE {self.reason}")
return info

def verbose(self) -> str:
"""Return string with additional information for verbose output."""
return self.verbose_output

def __bool__(self) -> bool:
"""Evaluate success of check as bool."""
return self.success
"""Evaluate success of check as bool. Warning is treated as success"""
return self.status != Status.FAILURE

def get_status(self) -> Status:
"""Get the level of success to also consider possible warnings"""
return self.status

def check(self, package: Package):
"""
Expand Down Expand Up @@ -119,9 +135,10 @@ def _check(self, package: Package):
license_tag):
licenses_not_in_spdx_list.append(license_tag)
if len(licenses_not_in_spdx_list) > 0:
self._failed(
self._warning(
f"Licenses {licenses_not_in_spdx_list} are "
"not in SPDX list of licenses."
"not in SPDX list of licenses. "
"Make sure to exactly match one of https://spdx.org/licenses/."
)
else:
self._success("All license tags are in SPDX list of licenses.")
Expand All @@ -135,48 +152,64 @@ def _check(self, package: Package):
self._failed("No license tag defined.")
return
license_tags_without_license_text: Dict[LicenseTag, str] = {}
missing_license_texts_status: Dict[LicenseTag, Status] = {}
found_license_texts = package.found_license_texts
for license_tag in package.license_tags.values():
if not license_tag.has_license_text_file():
license_tags_without_license_text[
license_tag] = "No license text file defined."
missing_license_texts_status[license_tag] = Status.FAILURE
continue
license_text_file = license_tag.get_license_text_file()
if not os.path.exists(
os.path.join(package.abspath, license_text_file)):
license_tags_without_license_text[license_tag] =\
f"License text file '{license_text_file}' does not exist."
missing_license_texts_status[license_tag] = Status.FAILURE
continue
if license_text_file not in found_license_texts:
license_tags_without_license_text[license_tag] =\
f"License text file '{license_text_file}' not included" +\
" in scan results."
missing_license_texts_status[license_tag] = Status.FAILURE
continue
if not is_license_text_file(
found_license_texts[license_text_file]):
license_tags_without_license_text[license_tag] =\
f"License text file '{license_text_file}' is not " +\
"recognized as license text."
missing_license_texts_status[license_tag] = Status.FAILURE
continue
actual_license: Optional[str] = get_spdx_license_name(
found_license_texts[license_text_file])
if actual_license is None:
license_tags_without_license_text[license_tag] =\
f"License text file '{license_text_file}'" +\
" is not recognized as license text."
missing_license_texts_status[license_tag] = Status.FAILURE
continue
if actual_license != license_tag.get_license_id():
license_tags_without_license_text[license_tag] =\
f"License text file '{license_text_file}' is " +\
f"of license {actual_license} but should be " +\
f"of license {actual_license} but tag is " +\
f"{license_tag.get_license_id()}."
missing_license_texts_status[license_tag] = Status.WARNING
continue
if len(license_tags_without_license_text) > 0:
self._failed(
"The following license tags do not have a valid license text "
"file:\n" + "\n".join(
[f" '{x[0]}': {x[1]}" for x in
license_tags_without_license_text.items()]))
if max(missing_license_texts_status.values()) == Status.WARNING:
self._warning(
"Since they are not in the SPDX list, "
"we can not check if these tags have the correct "
"license text:\n" + "\n".join(
[f" '{x[0]}': {x[1]}" for x in
license_tags_without_license_text.items()]))
else:
self._failed(
"The following license tags do not "
"have a valid license text "
"file:\n" + "\n".join(
[f" '{x[0]}': {x[1]}" for x in
license_tags_without_license_text.items()]))
self.verbose_output = red(
"\n".join([f" '{x[0]}': {x[1]}" for x in
found_license_texts.items()]))
Expand Down
29 changes: 18 additions & 11 deletions src/ros_license_toolkit/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
from ros_license_toolkit.checks import (LicensesInCodeCheck,
LicenseTagExistsCheck,
LicenseTagIsInSpdxListCheck,
LicenseTextExistsCheck)
LicenseTextExistsCheck, Status)
from ros_license_toolkit.package import get_packages_in_path
from ros_license_toolkit.ui_elements import (FAILURE_STR, SUCCESS_STR,
Verbosity, major_sep, minor_sep,
red, rll_print_factory)
WARNING_STR, Verbosity, major_sep,
minor_sep, red, rll_print_factory)


def main(args: Optional[Sequence[str]] = None) -> int:
Expand Down Expand Up @@ -112,9 +112,12 @@ def main(args: Optional[Sequence[str]] = None) -> int:
rll_print(f'Execution time: {stop - start:.2f} seconds', Verbosity.QUIET)

# Print the overall results
if all(results_per_package.values()):
if max(results_per_package.values()) == Status.SUCCESS:
rll_print(f"All packages:\n {SUCCESS_STR}", Verbosity.QUIET)
return os.EX_OK
if max(results_per_package.values()) == Status.WARNING:
rll_print(f"All packages:\n {WARNING_STR}", Verbosity.QUIET)
return os.EX_OK
rll_print(f"All packages:\n {FAILURE_STR}", Verbosity.QUIET)
return os.EX_DATAERR

Expand All @@ -138,16 +141,20 @@ def process_one_pkg(rll_print, package):
rll_print(check)
rll_print(check.verbose(), Verbosity.VERBOSE)

if all(checks_to_perform):
rll_print(minor_sep())
rll_print(minor_sep())
# Every check is successful, no warning
if max(check.status for check in checks_to_perform) == Status.SUCCESS:
rll_print(f"[{package.name}] Overall:\n {SUCCESS_STR}")
rll_print(major_sep())
results_per_package[package.abspath] = True
results_per_package[package.abspath] = Status.SUCCESS
# Either every check is successful or contains a warning
elif max(check.status for check in checks_to_perform) == Status.WARNING:
rll_print(f"[{package.name}] Overall:\n {WARNING_STR}")
results_per_package[package.abspath] = Status.WARNING
# At least one check contains an error
else:
rll_print(minor_sep())
rll_print(f"[{package.name}] Overall:\n {FAILURE_STR}")
rll_print(major_sep())
results_per_package[package.abspath] = False
results_per_package[package.abspath] = Status.FAILURE
rll_print(major_sep())
return results_per_package


Expand Down
7 changes: 7 additions & 0 deletions src/ros_license_toolkit/ui_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
# colors

GREEN = "\033[92m"
YELLOW = "\033[93m"
RED = "\033[91m"
NC = "\033[00m"

Expand All @@ -32,6 +33,11 @@ def red(message: str):
return f"{RED}{message}{NC}"


def yellow(message: str):
"""Make this `message` yellow """
return f"{YELLOW}{message}{NC}"


def green(message: str):
"""Make this `message` green"""
return f"{GREEN}{message}{NC}"
Expand All @@ -40,6 +46,7 @@ def green(message: str):


FAILURE_STR = red("FAILURE")
WARNING_STR = yellow("WARNING")
SUCCESS_STR = green("SUCCESS")
NO_REASON_STR = "No reason provided."
N_SEP = 20
Expand Down
8 changes: 8 additions & 0 deletions test/_test_data/test_pkg_name_not_in_spdx/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Copyright (c) 2015 Foo Bar

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6 changes: 6 additions & 0 deletions test/_test_data/test_pkg_name_not_in_spdx/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>test_pkg_name_not_in_spdx</name>
<license file="LICENSE">BSD</license>
</package>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Copyright 1995 Foo Bar

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright (C) 2000 An Author

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:

# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.

import sys

if __name__ == "__main__":
print("hi")
sys.exit(0)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>test_pkg_one_correct_one_license_file_missing</name>
<license file="bsd.LICENSE" source-files="code_with_BSD.py">BSD-3-Clause</license>
<license file="nisl.LICENSE">not-in-spdx-license</license>
</package>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>test_pkg_unknown_license_missing_file</name>
<license file="LICENSE">my own fancy license 1.0</license>
</package>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Copyright 1995 Foo Bar

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright (C) 2000 An Author

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:

# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.

import sys

if __name__ == "__main__":
print("hi")
sys.exit(0)
Loading

0 comments on commit 0d980fd

Please sign in to comment.