diff --git a/PantheonCMD/__init__.py b/PantheonCMD/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PantheonCMD/pcmd.py b/PantheonCMD/pcmd.py index 521d32c..a743a97 100644 --- a/PantheonCMD/pcmd.py +++ b/PantheonCMD/pcmd.py @@ -8,11 +8,12 @@ import sys from pcutil import PantheonRepo, get_not_exist, get_exist, is_pantheon_repo -from pcvalidator import validation -from pcyamlchecks import yaml_validation +from validation.pcvalidator import validate_build_files +from validation.pcyamlchecks import yaml_validation from subprocess import call -from pcprvalidator import get_changed_files, get_all_modules, get_all_assemblies, get_undetermined_files, get_no_prefix_files - +from validation.pcprvalidator import validate_merge_request_files +from validation.pcentrypointvalidator import validate_entry_point_files +from validation.pcmsg import print_message, print_report_message def print_header(): @@ -47,6 +48,7 @@ def parse_args(): # 'Validate' command parser_d = subparsers.add_parser('validate', help='Validate entries in your pantheon2.yml file.') parser_d.add_argument('--mr', action='store_true', help='Validate files commited on a merge request.') + parser_d.add_argument('--e', nargs=1, help='Validate files from an entry point.') # 'Generate' command parser_e = subparsers.add_parser('generate', help='Generate pantheon2.yml file from a template.') @@ -76,66 +78,24 @@ def parse_args(): # validate modules and assemblies elif args.command == 'validate': - if args.mr: - - changed_files = get_changed_files() - files_found = get_exist(changed_files) - no_prefix_files = get_no_prefix_files(files_found) - modules_found = get_all_modules(files_found, no_prefix_files) - assemblies_found = get_all_assemblies(files_found, no_prefix_files) - undetermined_file_type = get_undetermined_files(no_prefix_files) - - if undetermined_file_type: - print("\nYour Merge Request contains the following files that can not be classified as modules or assemblies:\n") + # user provides paths to files that are relative to current pwd - for file in undetermined_file_type: + if args.e: + entry_point_list = args.e + validate_entry_point_files(entry_point_list) - print('\t' + file) + elif args.mr: - print("\nTotal: ", str(len(undetermined_file_type))) + validate_merge_request_files() - validate = validation(files_found, modules_found, assemblies_found) - - if validate.count != 0: - print("\nYour Merge Request contains the following files that did not pass validation:\n") - validate.print_report() - sys.exit(2) - else: - print("All files passed validation.") - sys.exit(0) else: - pantheon_repo = PantheonRepo(repo_location) - if os.path.exists('pantheon2.yml'): # call yaml file validation + attribute file validation yaml_validation('pantheon2.yml') - exists = get_not_exist(pantheon_repo.get_content()) - - if exists: - - print("\nYour pantheon2.yml contains the following files that do not exist in your repository:\n") - - for exist in exists: - - print('\t' + exist) - - print("\nTotal: ", str(len(exists))) - - files_found = get_exist(pantheon_repo.get_content()) - modules_found = pantheon_repo.get_existing_content("modules") - assemblies_found = pantheon_repo.get_existing_content("assemblies") - - validate = validation(files_found, modules_found, assemblies_found) - - if validate.count != 0: - print("\nYour pantheon2.yml contains the following files that did not pass validation:\n") - validate.print_report() - sys.exit(2) - else: - print("All files passed validation.") + validate_build_files() else: @@ -153,12 +113,12 @@ def parse_args(): # Else parse actions # Action - preview if args.command == 'preview': - + if args.format == 'pdf': output_format = 'pdf' else: output_format = 'html' - + # Did a user specify a set of files? If so, only build those. if args.files: # Handle different interpretations of directories @@ -211,13 +171,7 @@ def parse_args(): duplicates = pantheon_repo.get_duplicates() if duplicates: - - print("Your pantheon2.yml contains the following duplicate entries:\n") - - for duplicate in duplicates: - print(duplicate) - - print("\nTotal: ", str(len(duplicates))) + print_message(duplicates, 'pantheon2.yml', 'contains the following duplicate entries') else: diff --git a/PantheonCMD/pcchecks.py b/PantheonCMD/validation/pcchecks.py similarity index 95% rename from PantheonCMD/pcchecks.py rename to PantheonCMD/validation/pcchecks.py index c1691c8..fd873f6 100644 --- a/PantheonCMD/pcchecks.py +++ b/PantheonCMD/validation/pcchecks.py @@ -17,8 +17,10 @@ class Tags: class Regex: """Define regular expresiions for the checks.""" + ATTRIBUTE = re.compile(r':[^\s]*:.*(?=\n)') INCLUDE = re.compile(r'include::.*\]\n') - MODULE_TYPE = re.compile(r':_module-type: (PROCEDURE|CONCEPT|REFERENCE)') + INCLUDED_CONTENT = re.compile(r'(?<=include::).*?(?=\[)') + CONTENT_TYPE = re.compile(r':_content-type: (PROCEDURE|CONCEPT|REFERENCE|ASSEMBLY)') PREFIX_ASSEMBLIES = re.compile(r'.*\/assembly.*\.adoc') PREFIX_MODULES = re.compile(r'.*\/con.*\.adoc|.*\/proc.*\.adoc|.*\/ref.*\.adoc') # should exclude pseudo vanilla like <> @@ -109,17 +111,14 @@ def html_markup_check(stripped_file): # Standalone check on modules_found def nesting_in_modules_check(report, stripped_file, file_path): """Check if modules contains nested content.""" - if re.findall(Regex.NESTED_ASSEMBLY, stripped_file): - report.create_report('nesting in modules. nesting', file_path) - if re.findall(Regex.NESTED_MODULES, stripped_file): + if re.findall(Regex.NESTED_ASSEMBLY, stripped_file) or re.findall(Regex.NESTED_MODULES, stripped_file): report.create_report('nesting in modules. nesting', file_path) # Standalone check on modules_found def add_res_section_module_check(report, stripped_file, file_path): - if re.findall(Regex.ADDITIONAL_RES, stripped_file): - if not re.findall(Regex.ADD_RES_MODULE, stripped_file): - report.create_report("Additional resources section for modules should be `.Additional resources`. Wrong section name was", file_path) + if re.findall(Regex.ADD_RES_ASSEMBLY, stripped_file): + report.create_report("Additional resources section for modules should be `.Additional resources`. Wrong section name was", file_path) # Standalone check on assemblies_found diff --git a/PantheonCMD/validation/pcentrypointvalidator.py b/PantheonCMD/validation/pcentrypointvalidator.py new file mode 100644 index 0000000..6d899eb --- /dev/null +++ b/PantheonCMD/validation/pcentrypointvalidator.py @@ -0,0 +1,126 @@ +#!/usr/bin/python3 + +import argparse +import re +import os +from validation.pcchecks import Regex +import sys +from pcutil import get_exist, get_not_exist +from validation.pcprvalidator import get_no_prefix_files, get_all_modules, get_all_assemblies, get_undetermined_files +from validation.pcvalidator import validation +from validation.pcmsg import print_message, print_report_message + +parser = argparse.ArgumentParser() + + +def get_nonexisting_entry_points(entry_point_list): + nonexistent_files = get_not_exist(entry_point_list) + + if nonexistent_files: + print_message(nonexistent_files, 'entry point', 'does not exist in your repository') + sys.exit(2) + + +def get_includes(entry_points): + path_to_includes = [] + + for entry in entry_points: + path_to_entry_point = os.path.dirname(os.path.abspath(entry)) + + with open(entry, 'r') as file: + original = file.read() + stripped = Regex.MULTI_LINE_COMMENT.sub('', original) + stripped = Regex.SINGLE_LINE_COMMENT.sub('', stripped) + + included_files = re.findall(Regex.INCLUDED_CONTENT, stripped) + + if included_files: + + for include in included_files[:]: + if include.startswith('_'): + included_files.remove(include) + + for i in included_files: + path_to_includes.append(os.path.join(path_to_entry_point, i)) + + return path_to_includes + + +def get_level_one_includes(files): + path_to_level_one_includes = get_includes(files) + + return path_to_level_one_includes + + +def get_level_two_includes(files): + path_to_level_two_includes = get_includes(files) + + return path_to_level_two_includes + + +def get_level_three_includes(files): + path_to_level_three_includes = get_includes(files) + + return path_to_level_three_includes + + +def get_level_four_includes(files): + path_to_level_four_includes = get_includes(files) + + return path_to_level_four_includes + + +def get_concatenated_includes(entry_point_list): + existing_entry_points = get_exist(entry_point_list) + level_one_includes = get_level_one_includes(existing_entry_points) + level_two_includes = get_level_two_includes(level_one_includes) + level_three_includes = get_level_three_includes(level_two_includes) + level_four_includes = get_level_four_includes(level_three_includes) + no_prefix_level_four_includes = get_no_prefix_files(level_four_includes) + level_four_modules = get_all_modules(level_four_includes, no_prefix_level_four_includes) + level_four_assemblies = get_all_assemblies(level_four_includes, no_prefix_level_four_includes) + + all_includes = level_one_includes + level_two_includes + level_three_includes + level_four_modules + + return all_includes, level_four_assemblies + + +def get_level_four_assemblies(entry_point_list): + all_includes, level_four_assemblies = get_concatenated_includes(entry_point_list) + + return level_four_assemblies + + +def get_all_includes(entry_point_list): + all_includes, level_four_assemblies = get_concatenated_includes(entry_point_list) + + for entry in entry_point_list: + if not entry.endswith('master.adoc'): + all_includes = all_includes + entry_point_list + + for include in all_includes: + if os.path.basename(include).startswith('_'): + all_includes.remove(include) + + return all_includes + + +def validate_entry_point_files(entry_point_list): + # exit if entry point doesn't exist + get_nonexisting_entry_points(entry_point_list) + existing_entry_points = get_exist(entry_point_list) + includes = get_all_includes(entry_point_list) + no_prefix_files = get_no_prefix_files(includes) + modules_found = get_all_modules(includes, no_prefix_files) + assemblies_found = get_all_assemblies(includes, no_prefix_files) + undetermined_file_type = get_undetermined_files(no_prefix_files) + level_four_assemblies = get_level_four_assemblies(existing_entry_points) + + if level_four_assemblies: + print_message(level_four_assemblies, 'entry point', 'contains unsupported level of nesting for the following files') + + if undetermined_file_type: + print_message(undetermined_file_type, 'entry point', 'contains the following files that can not be classified as modules or assemblies') + + validate = validation(includes, modules_found, assemblies_found) + print_report_message(validate, 'entry point') diff --git a/PantheonCMD/validation/pcmsg.py b/PantheonCMD/validation/pcmsg.py new file mode 100644 index 0000000..c371f31 --- /dev/null +++ b/PantheonCMD/validation/pcmsg.py @@ -0,0 +1,45 @@ +#!/usr/bin/python3 + +import sys + + +class Report(): + """Create and print report. thank u J.""" + + def __init__(self): + """Create placeholder for problem description.""" + self.report = {} + self.count = 0 + + def create_report(self, category, file_path): + """Generate report.""" + self.count += 1 + if not category in self.report: + self.report[category] = [] + self.report[category].append(file_path) + + def print_report(self): + + """Print report.""" + separator = "\n\t" + + for category, files in self.report.items(): + print("\nERROR: {} found in the following files:".format(category)) + print('\t' + separator.join(files)) + + +def print_message(variable, specification, msg): + print(f'\nYour {specification} {msg}:\n') + for var in variable: + print('\t', var) + print("\nTotal: ", str(len(variable))) + + +def print_report_message(variable, specification): + if variable.count != 0: + print(f"\nYour {specification} contains the following files that did not pass validation:\n") + variable.print_report() + sys.exit(2) + else: + print("All files passed validation.") + sys.exit(0) diff --git a/PantheonCMD/pcprvalidator.py b/PantheonCMD/validation/pcprvalidator.py similarity index 76% rename from PantheonCMD/pcprvalidator.py rename to PantheonCMD/validation/pcprvalidator.py index adc7873..7fded7c 100644 --- a/PantheonCMD/pcprvalidator.py +++ b/PantheonCMD/validation/pcprvalidator.py @@ -4,9 +4,11 @@ from pygit2 import Repository import os import sys -import subprocess import re -from pcchecks import Regex +from validation.pcchecks import Regex +from validation.pcvalidator import validation +from validation.pcmsg import print_message, print_report_message +from pcutil import get_exist if subprocess.call(["git", "branch"], stderr=subprocess.STDOUT, stdout=open(os.devnull, 'w')) != 0: @@ -16,6 +18,15 @@ current_branch = Repository('.').head.shorthand +def get_mr(): + if current_branch == 'master': + print('On master. Exiting...') + sys.exit(1) + elif current_branch == 'main': + print('On main. Exiting...') + sys.exit(1) + + def get_changed_files(): """Return a list of the files that werre change on the PR.""" @@ -74,13 +85,13 @@ def get_no_prefefix_file_type(no_prefix_files): stripped = Regex.CODE_BLOCK_DOTS.sub('', stripped) stripped = Regex.INTERNAL_IFDEF.sub('', stripped) - if re.findall(Regex.MODULE_TYPE, stripped): + if re.findall(Regex.CONTENT_TYPE, stripped): no_prefix_module_type.append(path) if re.findall(Regex.INCLUDE, stripped): no_prefix_assembly_type.append(path) - if not re.findall(Regex.MODULE_TYPE, stripped) and not re.findall(Regex.INCLUDE, stripped): + if not re.findall(Regex.CONTENT_TYPE, stripped) and not re.findall(Regex.INCLUDE, stripped): undetermined_file_type.append(path) return no_prefix_module_type, no_prefix_assembly_type, undetermined_file_type @@ -121,3 +132,20 @@ def get_undetermined_files(no_prefix_files): no_prefix_module_type, no_prefix_assembly_type, undetermined_file_type = get_no_prefefix_file_type(no_prefix_files) return(sorted(undetermined_file_type, key=str.lower)) + + +def validate_merge_request_files(): + get_mr() + changed_files = get_changed_files() + files_found = get_exist(changed_files) + no_prefix_files = get_no_prefix_files(files_found) + modules_found = get_all_modules(files_found, no_prefix_files) + assemblies_found = get_all_assemblies(files_found, no_prefix_files) + undetermined_file_type = get_undetermined_files(no_prefix_files) + + if undetermined_file_type: + print_message(undetermined_file_type, 'Merge Request', 'contains the following files that can not be classified as modules or assemblies') + + validate = validation(files_found, modules_found, assemblies_found) + + print_report_message(validate, 'Merge Request') diff --git a/PantheonCMD/pcvalidator.py b/PantheonCMD/validation/pcvalidator.py similarity index 66% rename from PantheonCMD/pcvalidator.py rename to PantheonCMD/validation/pcvalidator.py index 31f32b4..dd8aeb7 100644 --- a/PantheonCMD/pcvalidator.py +++ b/PantheonCMD/validation/pcvalidator.py @@ -1,32 +1,9 @@ #!/usr/bin/python3 -from pcchecks import Regex, checks, nesting_in_modules_check, nesting_in_assemblies_check, add_res_section_module_check, add_res_section_assembly_check, icons_check, toc_check +from validation.pcchecks import Regex, checks, nesting_in_modules_check, nesting_in_assemblies_check, add_res_section_module_check, add_res_section_assembly_check, icons_check, toc_check import sys - - -class Report(): - """Create and print report. thank u J.""" - - def __init__(self): - """Create placeholder for problem description.""" - self.report = {} - self.count = 0 - - def create_report(self, category, file_path): - """Generate report.""" - self.count += 1 - if not category in self.report: - self.report[category] = [] - self.report[category].append(file_path) - - def print_report(self): - - """Print report.""" - separator = "\n\t" - - for category, files in self.report.items(): - print("\nERROR: {} found in the following files:".format(category)) - print('\t' + separator.join(files)) +from validation.pcmsg import print_message, print_report_message, Report +from pcutil import get_not_exist, get_exist, PantheonRepo, is_pantheon_repo def validation(files_found, modules_found, assemblies_found): @@ -68,3 +45,21 @@ def validation(files_found, modules_found, assemblies_found): add_res_section_assembly_check(report, stripped, path) return report + + +def validate_build_files(): + repo_location = is_pantheon_repo() + pantheon_repo = PantheonRepo(repo_location) + + exists = get_not_exist(pantheon_repo.get_content()) + + if exists: + print_message(exists, 'pantheon2.yml', 'contains the following files that do not exist in your repositor') + + files_found = get_exist(pantheon_repo.get_content()) + modules_found = pantheon_repo.get_existing_content("modules") + assemblies_found = pantheon_repo.get_existing_content("assemblies") + + validate = validation(files_found, modules_found, assemblies_found) + + print_report_message(validate, 'pantheon2.yml') diff --git a/PantheonCMD/pcyamlchecks.py b/PantheonCMD/validation/pcyamlchecks.py similarity index 52% rename from PantheonCMD/pcyamlchecks.py rename to PantheonCMD/validation/pcyamlchecks.py index 4df0687..e425153 100644 --- a/PantheonCMD/pcyamlchecks.py +++ b/PantheonCMD/validation/pcyamlchecks.py @@ -5,8 +5,8 @@ import yaml from cerberus import Validator, errors from cerberus.errors import BasicErrorHandler -from pcchecks import Regex, icons_check, toc_check -from pcvalidator import Report +from validation.pcchecks import Regex, icons_check, toc_check +from validation.pcmsg import Report, print_message import glob @@ -25,7 +25,7 @@ def get_yaml_size(yaml_file): sys.exit(2) -def load_doc(yaml_file): +def load_yml(yaml_file): """Load pv2.yml and test for syntax errors.""" with open(yaml_file, 'r') as file: try: @@ -35,6 +35,59 @@ def load_doc(yaml_file): sys.exit(2) +def get_yaml_errors(yaml_schema, yaml_doc): + # load validator with custom error handler + v = Validator(yaml_schema, error_handler=CustomErrorHandler()) + # validate the pv2.yml with schema + v.validate(yaml_doc, yaml_schema) + + if v.errors: + print("ERROR: there is an error in your yaml file:") + for key in v.errors: + print('\t', key, v.errors[key]) + sys.exit(2) + + +def get_resources_paths(yaml_doc): + resources_path_does_not_exist = [] + + for item in yaml_doc['resources']: + path_to_images_resources = os.path.split(item)[0] + if not glob.glob(path_to_images_resources): + resources_path_does_not_exist.append(item) + + return resources_path_does_not_exist + + +# TODO: might need to be rewritten to accept a list, not string (schema) +def get_attribute_paths(yaml_doc): + path_does_not_exist = [] + path_exists = [] + + for variant in yaml_doc['variants']: + if not os.path.exists(variant['path']): + path_does_not_exist.append(variant['path']) + else: + path_exists.append(variant['path']) + + return path_exists, path_does_not_exist + + +def get_attribute_file_path(yaml_doc): + path_exists, path_does_not_exist = get_attribute_paths(yaml_doc) + + return path_exists + + +def get_nonexistent_paths(yaml_doc): + path_exists, path_does_not_exist = get_attribute_paths(yaml_doc) + resources_path_does_not_exist = get_resources_paths(yaml_doc) + + path_does_not_exist = path_does_not_exist + resources_path_does_not_exist + + return path_does_not_exist + + def get_attribute_file_validation_results(attribute_file): """Validate attributes file.""" report = Report() @@ -51,46 +104,6 @@ def get_attribute_file_validation_results(attribute_file): return report -def get_yaml_errors(yaml_schema, yaml_doc): - # load validator with custom error handler - v = Validator(yaml_schema, error_handler=CustomErrorHandler()) - # validate the pv2.yml with schema - v.validate(yaml_doc, yaml_schema) - - if v.errors: - print("FAIL: there is an error in your yaml file:") - for key in v.errors.keys(): - print("\n\t'{}' {}".format(key, ', '.join(str(item) for item in v.errors[key]))) - sys.exit(2) - - else: - - path_does_not_exist = [] - path_exists = [] - - for item in yaml_doc['resources']: - path_to_images_dir = os.path.split(item)[0] - if not glob.glob(path_to_images_dir): - path_does_not_exist.append(item) - - for variant in yaml_doc['variants']: - if not os.path.exists(variant['path']): - path_does_not_exist.append(variant['path']) - else: - path_exists.append(variant['path']) - - if path_does_not_exist: - print('FAIL: Your pantheon2.yml contains the following files or directories that do not exist in your repository:\n') - for path in path_does_not_exist: - print('\t', path) - sys.exit(2) - else: - attribute_file_validation = get_attribute_file_validation_results(path_exists) - if attribute_file_validation.count != 0: - print("Your attributes file has the following errors:\n") - attribute_file_validation.print_report() - - def yaml_validation(yaml_file): """Validate pv2.yml; get path to attributes while we're at it.""" # define path to script @@ -98,7 +111,18 @@ def yaml_validation(yaml_file): # load schema schema = eval(open(path_to_script + '/schema.py', 'r').read()) # load pv2.yml - loaded_yaml = load_doc(yaml_file) + loaded_yaml = load_yml(yaml_file) get_yaml_size(yaml_file) get_yaml_errors(schema, loaded_yaml) + + path_does_not_exist = get_nonexistent_paths(loaded_yaml) + if path_does_not_exist: + print_message(path_does_not_exist, "pantheon2.yml", "contains the following files or directories that do not exist in your repository") + sys.exit(2) + + path_to_attributes = get_attribute_file_path(loaded_yaml) + attribute_file_validation = get_attribute_file_validation_results(path_to_attributes) + if attribute_file_validation.count != 0: + print("Your attributes file has the following errors:\n") + attribute_file_validation.print_report() diff --git a/PantheonCMD/schema.py b/PantheonCMD/validation/schema.py similarity index 100% rename from PantheonCMD/schema.py rename to PantheonCMD/validation/schema.py diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..b8b18a1 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +cd PantheonCMD && python3 -m unittest discover .. -b diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/empty.yml b/test/fixtures/empty.yml new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/syntax-error.yml b/test/fixtures/syntax-error.yml new file mode 100644 index 0000000..a201e2d --- /dev/null +++ b/test/fixtures/syntax-error.yml @@ -0,0 +1,17 @@ +server: https://pantheon.corp.redhat.com +repository: rhel-8-docs +variants: + -name: rhel83 + path: rhel-8/common-content/attributes.adoc + canonical: true + +assemblies: + - some.adoc + + +modules: + - some.adoc + + +resources: + - rhel-8/images/*.png diff --git a/test/fixtures/valid.yml b/test/fixtures/valid.yml new file mode 100644 index 0000000..e61677e --- /dev/null +++ b/test/fixtures/valid.yml @@ -0,0 +1,15 @@ +server: some-server +repository: some-name +variants: + - name: some-name + path: some-file.adoc + canonical: true + +assemblies: + - some-file.adoc + +modules: + - some-file.adoc + +resources: + - some-file.adoc diff --git a/test/test_pcchecks.py b/test/test_pcchecks.py new file mode 100644 index 0000000..eb1b9ac --- /dev/null +++ b/test/test_pcchecks.py @@ -0,0 +1,322 @@ +import unittest +from PantheonCMD.validation.pcchecks import * +from PantheonCMD.validation.pcmsg import Report + + +# class for every function +class TestIconsCheck(unittest.TestCase): + def setUp(self): + self.file_path = "some/path" + + + def test_empty_file(self): + report = Report() + file_contents = "" + + result = icons_check(report, file_contents, self.file_path) + self.assertNotIn('icons attribute', report.report) + + def test_file_with_icons_tag(self): + report = Report() + file_contents = """:toc: +:icons: +:imagesdir: images""" + file_path = "some/path" + + result = icons_check(report, file_contents, self.file_path) + self.assertIn('icons attribute', report.report) + + +class TestTocCheck(unittest.TestCase): + def setUp(self): + self.file_path = "some/path" + + + def test_empty_file(self): + report = Report() + file_contents = "" + + result = toc_check(report, file_contents, self.file_path) + self.assertNotIn('toc attribute', report.report) + + def test_file_with_toc_tag(self): + report = Report() + file_contents = """:toc: +:icons: +:imagesdir: images""" + file_path = "some/path" + + result = toc_check(report, file_contents, self.file_path) + self.assertIn('toc attribute', report.report) + + +class TestVanillaXrefCheck(unittest.TestCase): + def test_tag_present(self): + file_contents = """"= Heading + +<> +<> + +""" + + result = vanilla_xref_check(file_contents) + self.assertTrue(result, "Should return True when file has a vanilla xref.") + + def test_tag_not_present(self): + file_contents = """"= Heading + +<> + +""" + result = vanilla_xref_check(file_contents) + self.assertFalse(result, "Should return False when file has no vanilla xref.") + + +class TestInlineAnchorCheck(unittest.TestCase): + def test_tag_present(self): + file_contents = """"= Heading[[inline-anchor]] + +[role="_abstract"] +This is examle abstract.""" + + result = inline_anchor_check(file_contents) + self.assertTrue(result, "Should return True when file has an inline anchor.") + + def test_tag_not_present(self): + file_contents = """"= Heading + +[role="_abstract"] +This is examle abstract.""" + + result = inline_anchor_check(file_contents) + self.assertFalse(result, "Should return False when file has no inline anchor.") + + +class TestVarInTitleCheck(unittest.TestCase): + def test_var_present(self): + file_contents = """"= Heading {var} + +[role="_abstract"] +This is examle {abstract}.""" + + result = var_in_title_check(file_contents) + self.assertTrue(result, "Should return True when file has a var in heading.") + + def test_var_not_present(self): + file_contents = """"= Heading + +[role="_abstract"] +This is examle {abstract}.""" + + result = var_in_title_check(file_contents) + self.assertFalse(result, "Should return False when file has no var in heading.") + + +class TestExperimentalTagCheck(unittest.TestCase): + def test_tag_present(self): + file_contents = """":experimental: += Heading {var} + +[role="_abstract"] +This is examle {abstract}. +btn:[button] +""" + + results = experimental_tag_check(file_contents) + self.assertIsNone(results, "Should return None when file has an experimental tag and UI macros") + + def test_tag_not_present_but_needed_button(self): + file_contents = """"= Heading {var} + +[role="_abstract"] +This is examle {abstract}. +btn:[button] +""" + + results = experimental_tag_check(file_contents) + self.assertTrue(results, "Should return True when file has no experimental tag but has button macro.") + + def test_tag_not_present_but_needed_menu(self): + file_contents = """"= Heading {var} + +[role="_abstract"] +This is examle {abstract}. +menu:some[random > menu] +""" + + results = experimental_tag_check(file_contents) + self.assertTrue(results, "Should return True when file has no experimental tag but has menu macro.") + + +class TestHumanReadableLabelCheckXref(unittest.TestCase): + def test_label_present(self): + file_contents = """= Heading + +[role="_abstract"] +This is examle abstract and xref:human-readable_label[present].""" + + result = human_readable_label_check_xrefs(file_contents) + self.assertIsNone(result, "Should return None when xref has a human readable label.") + + def test_label_not_present(self): + file_contents = """= Heading + +[role="_abstract"] +This is examle abstract and xref:human-readable_label[present]. +xref:human-readable-label_not-present[].""" + + result = human_readable_label_check_xrefs(file_contents) + self.assertTrue(result, "Should return True when xref has no human readable label.") + + +class TestHumanReadableLabelCheckLinks(unittest.TestCase): + def test_label_present(self): + file_contents = """= Heading + +[role="_abstract"] +This is examle abstract and http://www.sample-link.com[present].""" + + result = human_readable_label_check_links(file_contents) + self.assertIsNone(result, "Should return None when link has a human readable label.") + + def test_label_not_present(self): + file_contents = """= Heading + +[role="_abstract"] +This is examle abstract and http://www.sample-link.com[].""" + + result = human_readable_label_check_links(file_contents) + self.assertTrue(result, "Should return True when link has no human readable label.") + + +class TestHtmlMarkupCheck(unittest.TestCase): + def test_html_markup_present(self): + file_contents = """= Heading + +markup""" + + result = html_markup_check(file_contents) + self.assertTrue(result, "Should return True when file has HTML markup.") + + def test_html_markup_present(self): + file_contents = """= Heading + +markup""" + + result = html_markup_check(file_contents) + self.assertIsNone(result, "Should return None when file has no HTML markup.") + + +class TestNestingInModules(unittest.TestCase): + def setUp(self): + self.file_path = "some/path" + + def test_nested_assembly_in_module(self): + report = Report() + + file_contents = """= Heading + +Some sample text. +include::assembly_some-assembly.adoc[]""" + + result = nesting_in_modules_check(report, file_contents, self.file_path) + self.assertIn('nesting in modules. nesting', report.report) + + def test_nested_module_in_module(self): + report = Report() + + file_contents = """= Heading + +Some sample text. +include::proc_some-module.adoc[]""" + + result = nesting_in_modules_check(report, file_contents, self.file_path) + self.assertIn('nesting in modules. nesting', report.report) + + def test_no_nested_in(self): + report = Report() + + file_contents = "" + + result = nesting_in_modules_check(report, file_contents, self.file_path) + self.assertNotIn('nesting in modules. nesting', report.report) + + +class TestAddResSectionModuleCheck(unittest.TestCase): + def setUp(self): + self.file_path = "some/path" + + def test_add_res_section_none(self): + report = Report() + file_contents = "" + + result = add_res_section_module_check(report, file_contents, self.file_path) + self.assertNotIn('Additional resources section for modules should be `.Additional resources`. Wrong section name was', report.report) + + def test_add_res_section_wrong(self): + report = Report() + file_contents = "== Additional resources" + + result = add_res_section_module_check(report, file_contents, self.file_path) + self.assertIn('Additional resources section for modules should be `.Additional resources`. Wrong section name was', report.report) + + def test_add_res_section_correct(self): + report = Report() + file_contents = ".Additional resources" + + result = add_res_section_module_check(report, file_contents, self.file_path) + self.assertNotIn('Additional resources section for modules should be `.Additional resources`. Wrong section name was', report.report) + + + +class TestLvloffsetCheck(unittest.TestCase): + def test_lvloffset_tag_not_present(self): + file_contents = """= Heading + +include::some-include.adoc[]""" + + result = lvloffset_check(file_contents) + self.assertIsNone(result, "Should return None when file has no :leveloffset: tag.") + + def test_lvloffset_tag_present(self): + file_contents = """= Heading + +:leveloffset: +include::some-include.adoc[]""" + + result = lvloffset_check(file_contents) + self.assertTrue(result, "Should return True when file has no :leveloffset: tag.") + + +class TestAbstractTagCheck(unittest.TestCase): + def test_tag_present(self): + file_contents = """"= Heading + +[role="_abstract"] +This is examle abstract.""" + + result = abstract_tag_check(file_contents) + self.assertTrue(result, "Should return True when file has a single abstract tag.") + + def test_tag_not_present(self): + file_contents = """"= Heading + +This is examle abstract.""" + + result = abstract_tag_check(file_contents) + self.assertFalse(result, "Should return False when file has no abstract tag.") + + def test_multile_tags_present(self): + file_contents = """"= Heading + +[role="_abstract"] +[role="_abstract"] +This is examle abstract.""" + + result = abstract_tag_check(file_contents) + self.assertFalse(result) + + +# run all the tests in this file +if __name__ == '__main__': + unittest.main() diff --git a/test/test_pcyamlchecks.py b/test/test_pcyamlchecks.py new file mode 100644 index 0000000..55f301e --- /dev/null +++ b/test/test_pcyamlchecks.py @@ -0,0 +1,205 @@ +import unittest +import os +import yaml + +from PantheonCMD.validation.pcyamlchecks import * + + +# needs empty.yml, valid.yml +class TestGetYamlSize(unittest.TestCase): + def setUp(self): + self.current_path = os.path.dirname(__file__) + self.fixtures_path = os.path.join(self.current_path, "fixtures") + + def test_empty_yml_file(self): + file_name = os.path.join(self.fixtures_path, "empty.yml") + + with self.assertRaises(SystemExit) as cm: + get_yaml_size(file_name) + + self.assertEqual(cm.exception.code, 2) + + def test_valid_yml_file(self): + file_name = os.path.join(self.fixtures_path, "valid.yml") + + try: + get_yaml_size(file_name) + except ZeroDivisionError as exc: + assert False, f"'valid.yml' raised an exception {exc}" + + +# needs syntax-error.yml, valid.yml +class TestLoadYml(unittest.TestCase): + def setUp(self): + self.current_path = os.path.dirname(__file__) + self.fixtures_path = os.path.join(self.current_path, "fixtures") + + def test_corrupt_yml_syntax(self): + file_name = os.path.join(self.fixtures_path, "syntax-error.yml") + + with self.assertRaises(SystemExit) as cm: + load_yml(file_name) + + self.assertEqual(cm.exception.code, 2) + + def test_valid_yml_syntax(self): + file_name = os.path.join(self.fixtures_path, "valid.yml") + + try: + load_yml(file_name) + except ZeroDivisionError as exc: + assert False, f"'valid.yml' raised an exception {exc}" + + +class TestGetYamlErrors(unittest.TestCase): + def setUp(self): + self.current_path = os.path.dirname(__file__) + self.fixtures_path = os.path.join(self.current_path, "fixtures") + + def test_yml_missing_keys(self): + path_to_script = os.path.dirname(os.path.realpath(__file__)) + file = yaml.safe_load(""" +repository: +variants: + - name: rhel83 + path: rhel-8/common-content/attributes.adoc + canonical: +""") + # load schema + schema = eval(open(path_to_script + '/../PantheonCMD/validation/schema.py', 'r').read()) + + with self.assertRaises(SystemExit) as cm: + get_yaml_errors(schema, file) + + self.assertEqual(cm.exception.code, 2) + + def test_valid_yml(self): + path_to_script = os.path.dirname(os.path.realpath(__file__)) + file_name = (path_to_script + "/fixtures/valid.yml") + loaded_yaml = load_yml(file_name) + # load schema + schema = eval(open(path_to_script + '/../PantheonCMD/validation/schema.py', 'r').read()) + + try: + get_yaml_errors(schema, loaded_yaml) + except ZeroDivisionError as exc: + assert False, f"'valid.yml' raised an exception {exc}" + + +class TestGetResourcesPaths(unittest.TestCase): + def test_fake_path(self): + file = yaml.safe_load(""" +resources: + - fake-path/*.py +""") + + result = get_resources_paths(file) + self.assertEqual(result, ['fake-path/*.py'], "Should return ['fake-path/*.py'] when path to resources does not exist.") + + def test_real_path(self): + file = yaml.safe_load(""" +resources: + - validation/*.py +""") + + result = get_resources_paths(file) + self.assertEqual(result, [], "Should return [] when path to resources exists.") + + +# TODO: might need to be rewritten to accept a list, not string (schema) +class TestGetAttributePaths(unittest.TestCase): + def test_fake_path(self): + file = yaml.safe_load(""" +variants: + - name: some-name + path: fake-path/file.py +""") + result = get_attribute_paths(file) + self.assertEqual(result, ([], ['fake-path/file.py'])) + + def test_real_path(self): + file = yaml.safe_load(""" +variants: + - name: some-name + path: validation/schema.py +""") + result = get_attribute_paths(file) + self.assertEqual(result, (['validation/schema.py'], [])) + + # if rewritten to accept a list, a test case for a fake + valid path is needed + + +class TestGetAttributeFilePath(unittest.TestCase): + def test_fake_path(self): + file = yaml.safe_load(""" +variants: + - name: some-name + path: fake-path/file.py +""") + result = get_attribute_file_path(file) + self.assertEqual(result, [], "Should return [] when path to attributes does not exist.") + + def test_real_path(self): + file = yaml.safe_load(""" +variants: + - name: some-name + path: validation/schema.py +""") + result = get_attribute_file_path(file) + self.assertEqual(result, ['validation/schema.py'], "Should return ['validation/schema.py'] when path to attributes exists.") + + +class TestGetNonexistentPaths(unittest.TestCase): + def test_two_fake_paths(self): + file = yaml.safe_load(""" +variants: + - name: some-name + path: fake-path/file.py +resources: + - fake-path/*.py +""") + result = get_nonexistent_paths(file) + self.assertEqual(result, ['fake-path/file.py', 'fake-path/*.py']) + + def test_two_real_paths(self): + file = yaml.safe_load(""" +variants: + - name: some-name + path: validation/schema.py +resources: + - validation/*.py +""") + result = get_nonexistent_paths(file) + self.assertEqual(result, []) + + def test_real_attribute_path_and_fake_resources_path(self): + file = yaml.safe_load(""" +variants: + - name: some-name + path: validation/schema.py +resources: + - fake-rec-path/*.py +""") + + result = get_nonexistent_paths(file) + self.assertEqual(result, ['fake-rec-path/*.py']) + + def test_fake_attribute_path_and_real_resources_path(self): + file = yaml.safe_load(""" +variants: + - name: some-name + path: fake-att-path/file.adoc +resources: + - validation/*.py +""") + + result = get_nonexistent_paths(file) + self.assertEqual(result, ['fake-att-path/file.adoc']) + +# get_attribute_file_validation_results function is just opening the file and runs toc + icon check. Those checks are tested in tests_pcchecks.py +# yaml_validation function doesn't need to be teted + + +# run all the tests in this file +if __name__ == '__main__': + unittest.main()