""" Tests for paver quality tasks """ import os import tempfile import unittest import paver.tasks from ddt import data, ddt, file_data, unpack from mock import Mock, patch from paver.easy import call_task import pavelib.quality @ddt class TestPaverQualityViolations(unittest.TestCase): """ For testing the paver violations-counting tasks """ def setUp(self): super(TestPaverQualityViolations, self).setUp() self.f = tempfile.NamedTemporaryFile(delete=False) self.f.close() self.addCleanup(os.remove, self.f.name) def test_pylint_parser_checks_formatting(self): """ Tests that only correctly formatted lines are considered as violations. """ with open(self.f.name, 'w') as f: f.write("hello") num = pavelib.quality._count_pylint_violations(f.name) # pylint: disable=protected-access self.assertEqual(num, 0) @file_data('pylint_test_list.json') def test_pylint_parser_count_violations(self, value): """ Tests that pylint parser works as exepcted. Tests: - Different types of violations - One violation covering multiple lines """ with open(self.f.name, 'w') as f: f.write(value) num = pavelib.quality._count_pylint_violations(f.name) # pylint: disable=protected-access self.assertEqual(num, 1) def test_pep8_parser(self): """ Tests that pep8 parser works as expected. """ with open(self.f.name, 'w') as f: f.write("hello\nhithere") num, __ = pavelib.quality._count_pep8_violations(f.name) # pylint: disable=protected-access self.assertEqual(num, 2) @ddt class TestPaverQuality(unittest.TestCase): """ For testing the paver quality tasks """ def setUp(self): super(TestPaverQuality, self).setUp() # this is required so that each test will clean environment paver.tasks.environment = paver.tasks.Environment() self.test_code_dir = os.path.join(os.path.dirname(__file__), 'pavelib_test_code') # Mock _count_pep8_violations to return a violation self._mock_count_pep8_violations = Mock( return_value=(1, ['abc/envs/common.py:32:2: E225 missing whitespace around operator']) ) # Mock _count_pylint_violations to return a violation self._mock_count_pylint_violations = Mock(return_value=1) @staticmethod def assert_pylint_sh_call(mock_quality_sh): """ Assert that correct pylint sh call is executed """ mock_quality_sh.assert_called_once_with( 'PYTHONPATH={python_path} pylint {packages} {flags} --msg-template={msg_template} | ' 'tee {report_dir}/pylint.report'.format( python_path=pavelib.quality.ROOT_DIR, packages=' '.join(pavelib.quality.PACKAGES), flags=' '.join([]), msg_template='"{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}"', report_dir=pavelib.quality.REPORTS_DIR ) ) @data( { 'options': {} }, { 'options': {'limit': 1} }, { 'options': {'limit': 2} } ) @unpack @patch('pavelib.quality.sh') @patch('pavelib.quality.violation_message') def test_run_pep8_success(self, mock_violation_message, mock_quality_sh, options): """ Tests that run_pep8 task works as expected when violations does not exceed the limit. """ with patch('pavelib.quality._count_pep8_violations', self._mock_count_pep8_violations): call_task('pavelib.quality.run_pep8', options=options) limit = options.get('limit', -1) mock_violation_message.assert_called_once_with('pep8', limit, 1) mock_quality_sh.assert_called_once_with( 'pep8 . | tee {report_dir}/pep8.report'.format(report_dir=pavelib.quality.REPORTS_DIR) ) @patch('pavelib.quality.sh') @patch('pavelib.quality.violation_message') def test_run_pep8_failure(self, mock_violation_message, mock_quality_sh): """ Tests that run_pep8 task works as expected when violations exceed the limit. """ with patch('pavelib.quality._count_pep8_violations', self._mock_count_pep8_violations): with self.assertRaises(SystemExit): call_task('pavelib.quality.run_pep8', options={'limit': 0}) mock_violation_message.assert_called_once_with('pep8', 0, 1) mock_quality_sh.assert_called_once_with( 'pep8 . | tee {report_dir}/pep8.report'.format(report_dir=pavelib.quality.REPORTS_DIR) ) @data( { 'options': {} }, { 'options': {'limit': 1} }, { 'options': {'limit': 2} } ) @unpack @patch('pavelib.quality.sh') @patch('pavelib.quality.violation_message') def test_pylint_success(self, mock_violation_message, mock_quality_sh, options): """ Tests that run_pylint task works as expected when violations does not exceed the limit. """ with patch('pavelib.quality._count_pylint_violations', self._mock_count_pylint_violations): call_task('pavelib.quality.run_pylint', options=options) limit = options.get('limit', -1) mock_violation_message.assert_called_once_with('pylint', limit, 1) self.assert_pylint_sh_call(mock_quality_sh) @patch('pavelib.quality.sh') @patch('pavelib.quality.violation_message') def test_pylint_failure(self, mock_violation_message, mock_quality_sh): """ Tests that run_pylint task works as expected when violations exceed the limit. """ with patch('pavelib.quality._count_pylint_violations', self._mock_count_pylint_violations): with self.assertRaises(SystemExit): call_task('pavelib.quality.run_pylint', options={'limit': 0}) mock_violation_message.assert_called_once_with('pylint', 0, 1) self.assert_pylint_sh_call(mock_quality_sh) @patch('pavelib.quality.violation_message') def test_pylint_with_test_code(self, mock_violation_message): """ Tests that run_pylint task works as expected for actual python code. """ with patch('pavelib.quality.PACKAGES', [self.test_code_dir]): call_task('pavelib.quality.run_pylint', options={'limit': 3}) mock_violation_message.assert_called_once_with('pylint', 3, 3) @patch('pavelib.quality.violation_message') def test_pylint_with_errors_only(self, mock_violation_message): """ Tests that run_pylint task works as expected with `errors` option. """ with patch('pavelib.quality.PACKAGES', [self.test_code_dir]): call_task('pavelib.quality.run_pylint', options={'errors': True, 'limit': 1}) mock_violation_message.assert_called_once_with('pylint', 1, 1) @data( { 'task_name': 'pep8', 'violations_limit': 3, 'num_violations': 2 }, { 'task_name': 'pyilnt', 'violations_limit': 1, 'num_violations': 2 }, ) def test_violation_message(self, kwargs): """ Tests that violation_message function works as expected. """ message = pavelib.quality.violation_message(**kwargs) self.assertEqual( message, 'Too many {} violations. Number of {} violations: {}. The limit is {}.'.format( kwargs['task_name'], kwargs['task_name'], kwargs['num_violations'], kwargs['violations_limit'], ) )