class TestLineTooLongRule(unittest.TestCase): collection = RulesCollection() collection.register(LineTooLongRule()) def setUp(self): self.runner = RunFromText(self.collection) def test_long_line(self): results = self.runner.run_state(LONG_LINE) self.assertEqual(1, len(results))
class TestTrailingWhitespaceRule(unittest.TestCase): collection = RulesCollection() collection.register(TrailingWhitespaceRule()) def setUp(self): self.runner = RunFromText(self.collection) def test_trailing_whitespace(self): results = self.runner.run_state(LINE_AND_WHITESPACE) print(results) self.assertEqual(1, len(results))
class TestLineTooLongRule(unittest.TestCase): collection = RulesCollection() def setUp(self): self.collection.register(JinjaVariableHasSpacesRule()) self.runner = RunFromText(self.collection) def test_statement_positive(self): results = self.runner.run_state(GOOD_VARIABLE_LINE) self.assertEqual(0, len(results)) def test_statement_negative(self): results = self.runner.run_state(BAD_VARIABLE_LINE) self.assertEqual(1, len(results))
class TestJinjaPillarGrainsGetFormatRule(unittest.TestCase): collection = RulesCollection() def setUp(self): self.collection.register(JinjaPillarGrainsGetFormatRule()) self.runner = RunFromText(self.collection) def test_statement_positive(self): results = self.runner.run_state(GOOD_STATEMENT_LINE) self.assertEqual(0, len(results)) def test_statement_negative(self): results = self.runner.run_state(BAD_STATEMENT_LINE) self.assertEqual(2, len(results))
class TestModeQuotationRule(unittest.TestCase): collection = RulesCollection() def setUp(self): self.collection.register(FileModeQuotationRule()) self.runner = RunFromText(self.collection) def test_statement_positive(self): results = self.runner.run_state(GOOD_MODE_QUOTATION_LINE) self.assertEqual(0, len(results)) def test_statement_negative(self): results = self.runner.run_state(BAD_MODE_QUOTATION_LINE) self.assertEqual(3, len(results))
class TestYamlHasOctalValueRule(unittest.TestCase): collection = RulesCollection() def setUp(self): self.collection.register(YamlHasOctalValueRule()) def test_statement_positive(self): runner = RunFromText(self.collection) results = runner.run_state(GOOD_NUMBER_STATE) self.assertEqual(0, len(results)) def test_statement_negative(self): runner = RunFromText(self.collection) results = runner.run_state(BAD_NUMBER_STATE) self.assertEqual(4, len(results))
class TestFileModeLeadingZeroRule(unittest.TestCase): collection = RulesCollection() def setUp(self): self.collection.register(FileModeLeadingZeroRule()) def test_statement_positive(self): runner = RunFromText(self.collection) results = runner.run_state(GOOD_MODE_LEADING_ZERO_LINE) self.assertEqual(0, len(results)) def test_statement_negative(self): runner = RunFromText(self.collection) results = runner.run_state(BAD_MODE_LEADING_ZERO_LINE) self.assertEqual(3, len(results))
class TestNoIrregularSpacesRule(unittest.TestCase): collection = RulesCollection() collection.register(NoIrregularSpacesRule()) def setUp(self): self.runner = RunFromText(self.collection) def test_with_irregular_spaces(self): for irregular in NoIrregularSpacesRule.irregular_spaces: results = self.runner.run_state(LINE.format(space=irregular)) self.assertEqual(1, len(results)) def test_without_irregular_spaces(self): results = self.runner.run_state(LINE.format(space=" ")) self.assertEqual(0, len(results))
class TestJinjaTemplateHasRightExtensionRule(unittest.TestCase): collection = RulesCollection() def setUp(self): self.collection.register(JinjaTemplateHasRightExtensionRule()) def test_file_extension_positive(self): path = 'tests/test-jinja-template.right.j2' runner = Runner(self.collection, path, SaltLintConfig()) self.assertEqual([], runner.run()) def test_file_extenion_negative(self): path = 'tests/test-jinja-template.bad' runner = Runner(self.collection, path, SaltLintConfig()) errors = runner.run() self.assertEqual(1, len(errors))
class TestLineTooLongRule(unittest.TestCase): collection = RulesCollection() def setUp(self): self.collection.register(FileExtensionRule()) def test_file_positive(self): path = 'tests/test-extension-success.sls' runner = Runner(self.collection, path, SaltLintConfig()) self.assertEqual([], runner.run()) def test_file_negative(self): path = 'tests/test-extension-failure' runner = Runner(self.collection, path, SaltLintConfig()) errors = runner.run() self.assertEqual(1, len(errors))
class TestNoTabsRule(unittest.TestCase): collection = RulesCollection() collection.register(NoTabsRule()) def setUp(self): self.runner = RunFromText(self.collection) def test_with_tabs(self): results = self.runner.run_state(LINE_WITH_TABS) self.assertEqual(2, len(results)) def test_with_spaces(self): results = self.runner.run_state(LINE_WITH_SPACES) self.assertEqual(0, len(results)) def test_mixed_tab_space(self): results = self.runner.run_state(LINE_MIXED_TAB_SPACE) self.assertEqual(1, len(results))
class TestSkipRule(unittest.TestCase): collection = RulesCollection() def setUp(self): self.collection.register(LineTooLongRule()) self.collection.register(JinjaVariableHasSpacesRule()) def test_no_skip_rule(self): runner = RunFromText(self.collection) results = runner.run_state(LINE) self.assertEqual(1, len(results)) def test_skip_multiple_rules(self): runner = RunFromText(self.collection) results = runner.run_state(LINE_SKIP_MULTIPLE) self.assertEqual(0, len(results)) def test_skip_rule(self): runner = RunFromText(self.collection) results = runner.run_state(LINE_SKIP) self.assertEqual(0, len(results))
class TestJinjaVariableHasSpaces(unittest.TestCase): collection = RulesCollection() def setUp(self): self.collection.register(JinjaVariableHasSpacesRule()) self.runner = RunFromText(self.collection) def test_statement_positive(self): results = self.runner.run_state(GOOD_VARIABLE_LINE) self.assertEqual(0, len(results)) def test_statement_negative(self): results = self.runner.run_state(BAD_VARIABLE_LINE) self.assertEqual(1, len(results)) def test_double_quoted_integer(self): results = self.runner.run_state(DOUBLE_QUOTED_INTEGER_IS_VALID) self.assertEqual(0, len(results)) def test_double_quoted_integer_trailing_space_invalid(self): results = self.runner.run_state( DOUBLE_QUOTED_INTEGER_TRAILING_SPACE_IS_INVALID) self.assertEqual(1, len(results)) def test_double_quoted_integer_leading_space_invalid(self): results = self.runner.run_state( DOUBLE_QUOTED_INTEGER_LEADING_SPACE_IS_INVALID) self.assertEqual(1, len(results)) def test_variable_bad_ends_with_integer(self): results = self.runner.run_state(BAD_VARIABLE_ENDING_IN_INTEGER) self.assertEqual(1, len(results)) def test_variable_bad_ends_with_integer_right(self): results = self.runner.run_state(BAD_VARIABLE_ENDING_IN_INTEGER_RIGHT) self.assertEqual(1, len(results))
def run(args=None): # Wrap `sys.stdout` in an object that automatically encodes an unicode # string into utf-8, in Python 2 only. The default encoding for Python 3 # is already utf-8. if sys.version_info[0] < 3: sys.stdout = codecs.getwriter('utf-8')(sys.stdout) parser = optparse.OptionParser("%prog [options] init.sls [state ...]", version='{} {}'.format(NAME, VERSION)) parser.add_option('-L', dest='listrules', default=False, action='store_true', help="list all the rules") parser.add_option('-r', action='append', dest='rulesdir', default=[], type='str', help="specify one or more rules directories using " "one or more -r arguments. Any -r flags override " "the default rules in %s, unless -R is also used." % default_rulesdir) parser.add_option('-R', action='store_true', default=False, dest='use_default_rules', help="Use default rules in %s in addition to any extra " "rules directories specified with -r. There is " "no need to specify this if no -r flags are used." % default_rulesdir) parser.add_option('-t', dest='tags', action='append', default=[], help="only check rules whose id/tags match these values") parser.add_option('-T', dest='listtags', action='store_true', help="list all the tags") parser.add_option('-v', dest='verbosity', action='count', help="Increase verbosity level", default=0) parser.add_option('-x', dest='skip_list', default=[], action='append', help="only check rules whose id/tags do not " + "match these values") parser.add_option('--nocolor', '--nocolour', dest='colored', default=hasattr(sys.stdout, 'isatty') and sys.stdout.isatty(), action='store_false', help="disable colored output") parser.add_option('--force-color', '--force-colour', dest='colored', action='store_true', help="Try force colored output (relying on salt's code)") parser.add_option('--exclude', dest='exclude_paths', action='append', help='path to directories or files to skip. This option' ' is repeatable.', default=[]) parser.add_option('--json', dest='json', action='store_true', default=False, help='parse the output as JSON') parser.add_option('--severity', dest='severity', action='store_true', default=False, help='add the severity to the standard output') parser.add_option( '-c', help='Specify configuration file to use. Defaults to ".salt-lint"') (options, parsed_args ) = parser.parse_args(args if args is not None else sys.argv[1:]) stdin_state = None states = set(parsed_args) matches = [] checked_files = set() # Read input from stdin if not sys.stdin.isatty(): stdin_state = tempfile.NamedTemporaryFile('w', suffix='.sls', delete=False) stdin_state.write(sys.stdin.read()) stdin_state.flush() states.add(stdin_state.name) # Read, parse and validate the configuration options_dict = vars(options) try: config = SaltLintConfig(options_dict) except SaltLintConfigError as exc: print(exc) return 2 # Show a help message on the screen if not states and not (options.listrules or options.listtags): parser.print_help(file=sys.stderr) return 1 # Collect the rules from the configution rules = RulesCollection(config) for rulesdir in config.rulesdirs: rules.extend(RulesCollection.create_from_directory(rulesdir, config)) # Show the rules listing if options.listrules: print(rules) return 0 # Show the tags listing if options.listtags: print(rules.listtags()) return 0 # Define the formatter if config.json: formatter = formatters.JsonFormatter() elif config.severity: formatter = formatters.SeverityFormatter(config.colored) else: formatter = formatters.Formatter(config.colored) for state in states: runner = Runner(rules, state, config, checked_files) matches.extend(runner.run()) # Sort the matches matches.sort(key=lambda x: (x.filename, x.linenumber, x.rule.id)) # Show the matches on the screen formatter.process(matches) # Delete stdin temporary file if stdin_state: os.unlink(stdin_state.name) # Return the exit code if matches: return 2 return 0
def run(args=None): formatter = formatters.Formatter() parser = optparse.OptionParser("%prog [options] init.sls [state ...]", version='{} {}'.format(NAME, VERSION)) parser.add_option('-L', dest='listrules', default=False, action='store_true', help="list all the rules") parser.add_option('-r', action='append', dest='rulesdir', default=[], type='str', help="specify one or more rules directories using " "one or more -r arguments. Any -r flags override " "the default rules in %s, unless -R is also used." % default_rulesdir) parser.add_option('-R', action='store_true', default=False, dest='use_default_rules', help="Use default rules in %s in addition to any extra " "rules directories specified with -r. There is " "no need to specify this if no -r flags are used." % default_rulesdir) parser.add_option('-t', dest='tags', action='append', default=[], help="only check rules whose id/tags match these values") parser.add_option('-T', dest='listtags', action='store_true', help="list all the tags") parser.add_option('-v', dest='verbosity', action='count', help="Increase verbosity level", default=0) parser.add_option('-x', dest='skip_list', default=[], action='append', help="only check rules whose id/tags do not " + "match these values") parser.add_option('--nocolor', dest='colored', default=hasattr(sys.stdout, 'isatty') and sys.stdout.isatty(), action='store_false', help="disable colored output") parser.add_option('--force-color', dest='colored', action='store_true', help="Try force colored output (relying on salt's code)") parser.add_option('--exclude', dest='exclude_paths', action='append', help='path to directories or files to skip. This option' ' is repeatable.', default=[]) parser.add_option('-c', help='Specify configuration file to use. Defaults to ".salt-lint"') (options, parsed_args) = parser.parse_args(args if args is not None else sys.argv[1:]) # Read, parse and validate the configration options_dict = vars(options) try: config = SaltLintConfig(options_dict) except SaltLintConfigError as exc: print(exc) return 2 # Show a help message on the screen if len(parsed_args) == 0 and not (options.listrules or options.listtags): parser.print_help(file=sys.stderr) return 1 # Collect the rules from the configution rules = RulesCollection(config) for rulesdir in config.rulesdirs: rules.extend(RulesCollection.create_from_directory(rulesdir, config)) # Show the rules listing if options.listrules: print(rules) return 0 # Show the tags listing if options.listtags: print(rules.listtags()) return 0 states = set(parsed_args) matches = list() checked_files = set() for state in states: runner = Runner(rules, state, config, checked_files) matches.extend(runner.run()) # Sort the matches matches.sort(key=lambda x: (x.filename, x.linenumber, x.rule.id)) # Show the matches on the screen for match in matches: print(formatter.format(match, config.colored).encode('utf-8')) # Return the exit code if len(matches): return 2 else: return 0