def test_check_for_invalid_strings_po_missing_header(self): ReportManager.getEnabledReporters()[0].reports = [] path = join(self.path, "missing_header") full_path = join(path, "strings.po") file_index = [{"path": path, "name": "strings.po"}] expected = ['ERROR: Invalid PO file {path}:\n' 'Missing required header:\n' '\tmsgid ""\n\tmsgstr ""'.format(path=relative_path(full_path))] check_for_invalid_strings_po(self.report, file_index) records = [Record.__str__(r) for r in ReportManager.getEnabledReporters()[0].reports] output = [s for s in records if s.startswith(self.report_matches)] self.assertListEqual(expected, output)
def test_start(self): result = start(self.path, self.args, self.all_repo_addons, self.config) records = [ Record.__str__(r) for r in ReportManager.getEnabledReporters()[0].reports ] # Comparing the whitelist with the list of output we get from addon-checker tool for white_str in self.whitelist: for value in records: if white_str.lower() == value.lower(): break else: flag = False else: flag = True self.assertTrue(flag)
def test_check_for_invalid_strings_po_valid_file(self): ReportManager.getEnabledReporters()[0].reports = [] path = join(self.path, "valid_file") language_path = join(path, "resources", "language", "resource.language.en_gb") file_index = [{"path": language_path, "name": "strings.po"}] expected = [] check_for_invalid_strings_po(self.report, file_index) records = [ Record.__str__(r) for r in ReportManager.getEnabledReporters()[0].reports ] output = [s for s in records if s.startswith(self.report_matches)] self.assertListEqual(expected, output)
def _check_artwork(report: Report, addon_path, addon_xml, file_index): # icon, fanart, screenshot - these will also check if the addon.xml links correctly _check_image_type(report, "icon", addon_xml, addon_path) _check_image_type(report, "fanart", addon_xml, addon_path) _check_image_type(report, "screenshot", addon_xml, addon_path) # go through all but the above and try to open the image for file in file_index: if re.match(r"(?!fanart\.jpg|icon\.png).*\.(png|jpg|jpeg|gif)$", file["name"]) is not None: image_path = os.path.join(file["path"], file["name"]) try: # Just try if we can successfully open it Image.open(image_path) except IOError: report.add( Record( PROBLEM, "Could not open image, is the file corrupted? %s" % relative_path(image_path)))
def test_check_for_invalid_strings_po_syntax_error(self): ReportManager.getEnabledReporters()[0].reports = [] path = join(self.path, "syntax_error") language_path = join(path, "resources", "language", "resource.language.en_gb") full_path = join(language_path, "strings.po") file_index = [{"path": language_path, "name": "strings.po"}] expected = [ "ERROR: Invalid PO file {path}: " "Syntax error on line 23".format(path=relative_path(full_path)) ] check_for_invalid_strings_po(self.report, file_index) records = [ Record.__str__(r) for r in ReportManager.getEnabledReporters()[0].reports ] output = [s for s in records if s.startswith(self.report_matches)] self.assertListEqual(expected, output)
def _check_for_legacy_strings_xml(report: Report, addon_path): if _find_file_recursive("strings.xml", addon_path) is not None: report.add( Record(PROBLEM, "Found strings.xml in folder %s please migrate to strings.po." % relative_path(addon_path)))
def _addon_xml_matches_folder(report: Report, addon_path, addon_xml): addon = addon_xml.getroot() if os.path.basename(os.path.normpath(addon_path)) == addon.attrib.get("id"): report.add(Record(INFORMATION, "Addon id matches folder name")) else: report.add(Record(PROBLEM, "Addon id and folder name does not match."))
def _addon_file_exists(report: Report, addon_path, file_name): if _find_file(file_name, addon_path) is None: report.add(Record(PROBLEM, "Not found %s in folder %s" % (file_name, relative_path(addon_path))))
def _check_image_type(report: Report, image_type, addon_xml, addon_path): images = addon_xml.findall("*//" + image_type) icon_fallback = False fanart_fallback = False if not images and image_type == "icon": icon_fallback = True image = type('image', (object,), {'text': 'icon.png'})() images.append(image) elif not images and image_type == "fanart": skip_addon_types = [".module.", "metadata.", "context.", ".language."] for addon_type in skip_addon_types: if addon_type in addon_path: break else: fanart_fallback = True image = type('image', (object,), {'text': 'fanart.jpg'})() images.append(image) for image in images: if image.text: filepath = os.path.join(addon_path, image.text) if os.path.isfile(filepath): report.add(Record(INFORMATION, "Image %s exists" % image_type)) try: im = Image.open(filepath) width, height = im.size if image_type == "icon": if has_transparency(im): report.add(Record(PROBLEM, "Icon.png should be solid. It has transparency.")) if (width != 256 and height != 256) and (width != 512 and height != 512): report.add(Record(PROBLEM, "Icon should have either 256x256 or 512x512 but it has %sx%s" % ( width, height))) else: report.add( Record(INFORMATION, "%s dimensions are fine %sx%s" % (image_type, width, height))) elif image_type == "fanart": fanart_sizes = [(1280, 720), (1920, 1080), (3840, 2160)] fanart_sizes_str = " or ".join(["%dx%d" % (w, h) for w, h in fanart_sizes]) if (width, height) not in fanart_sizes: report.add(Record(PROBLEM, "Fanart should have either %s but it has %sx%s" % ( fanart_sizes_str, width, height))) else: report.add(Record(INFORMATION, "%s dimensions are fine %sx%s" % (image_type, width, height))) else: # screenshots have no size definitions pass except IOError: report.add( Record(PROBLEM, "Could not open image, is the file corrupted? %s" % relative_path(filepath))) else: # if it's a fallback path addons.xml should still be able to # get build if fanart_fallback or icon_fallback: if icon_fallback: report.add(Record(INFORMATION, "You might want to add a icon")) elif fanart_fallback: report.add(Record(INFORMATION, "You might want to add a fanart")) # it's no fallback path, so building addons.xml will crash - # this is a problem ;) else: report.add(Record(PROBLEM, "%s does not exist at specified path." % image_type)) else: report.add(Record(WARNING, "Empty image tag found for %s" % image_type))
def main(): """The entry point to kodi-addon-checker """ choice = ['gotham', 'helix', 'isengard', 'jarvis', 'krypton', 'leia'] load_plugins() parser = argparse.ArgumentParser( prog="kodi-addon-checker", description="Checks Kodi repo for best practices and creates \ problem and warning reports.\r\nIf optional add-on \ directories are provided, check only those add-ons. \ Otherwise, scan current repository and check all add-ons in \ the current directory.") parser.add_argument("--version", action="version", version="%(prog)s 0.0.1") parser.add_argument("dir", type=dir_type, nargs="*", help="optional add-on or repo directories") parser.add_argument( "--branch", choices=choice, required=True, help="Target branch name where the checker will resolve dependencies") parser.add_argument( "--PR", help="Tell if tool is to run on a pull requests or not", action='store_true') parser.add_argument( "--allow-folder-id-mismatch", help="Allow the addon's folder name and id to mismatch", action="store_true") ConfigManager.fill_cmd_args(parser) args = parser.parse_args() log_file_name = os.path.join(os.getcwd(), "kodi-addon-checker.log") logger.Logger.create_logger(log_file_name, __package__) all_repo_addons = check_addon.all_repo_addons() if args.dir: # Following report is a wrapper for all sub reports report = Report("") for directory in args.dir: report.add(check_artifact(directory, args, all_repo_addons)) else: report = check_artifact(os.getcwd(), args, all_repo_addons) if report.problem_count > 0: report.add( Record( PROBLEM, "We found %s problems and %s warnings, please check the logfile." % (report.problem_count, report.warning_count))) sys.exit(1) elif report.warning_count > 0: report.add( Record( WARNING, "We found no problems and %s warnings, please check the logfile." % report.warning_count)) else: report.add( Record( INFORMATION, "We found no problems and no warnings, please enjoy your day.") )