diff --git a/tests/hacking/README.rst b/tests/hacking/README.rst index 1102b8b9..7cc7b264 100644 --- a/tests/hacking/README.rst +++ b/tests/hacking/README.rst @@ -21,15 +21,6 @@ Rally Specific Commandments * [N322] - Ensure that ``assertEqual(A, None)`` and ``assertEqual(None, A)`` are not used * [N323] - Ensure that ``assertTrue/assertFalse(A in/not in B)`` are not used with collection contents * [N324] - Ensure that ``assertEqual(A in/not in B, True/False)`` and ``assertEqual(True/False, A in/not in B)`` are not used with collection contents -* [N33x] - Reserved for rules related to Python 3 compatibility - * [N330] - Ensure that ``dict.iterkeys()``, ``dict.itervalues()``, ``dict.iteritems()`` and ``dict.iterlist()`` are not used - * [N331] - Ensure that ``basestring`` is not used - * [N332] - Ensure that ``StringIO.StringIO`` is not used - * [N333] - Ensure that ``urlparse`` is not used - * [N334] - Ensure that ``itertools.imap`` is not used - * [N335] - Ensure that ``xrange`` is not used - * [N336] - Ensure that ``string.lowercase`` and ``string.uppercase`` are not used - * [N337] - Ensure that ``next()`` method on iterator objects is not used - * [N338] - Ensure that ``+`` operand is not used to concatenate dict.items() * [N340] - Ensure that we are importing always ``from rally import objects`` -* [N341] - Ensure that we are importing oslo_xyz packages instead of deprecated oslo.xyz ones \ No newline at end of file +* [N341] - Ensure that we are importing oslo_xyz packages instead of deprecated oslo.xyz ones +* [N350] - Ensure that single quotes are not used diff --git a/tests/hacking/checks.py b/tests/hacking/checks.py index 252cc448..4a3d5c29 100644 --- a/tests/hacking/checks.py +++ b/tests/hacking/checks.py @@ -24,6 +24,7 @@ Guidelines for writing new hacking checks """ +import functools import re @@ -45,16 +46,18 @@ re_assert_equal_in_end_with_true_or_false = re.compile( r"assertEqual\((\w|[][.'\"])+( not)? in (\w|[][.'\", ])+, (True|False)\)") re_assert_equal_in_start_with_true_or_false = re.compile( r"assertEqual\((True|False), (\w|[][.'\"])+( not)? in (\w|[][.'\", ])+\)") -re_basestring_method = re.compile(r"(^|[\s,(\[=])basestring([\s,)\]]|$)") -re_StringIO_method = re.compile(r"StringIO\.StringIO\(") -re_urlparse_method = re.compile(r"(^|[\s=])urlparse\.") -re_itertools_imap_method = re.compile(r"(^|[\s=])itertools\.imap\(") -re_xrange_method = re.compile(r"(^|[\s=])xrange\(") -re_string_lower_upper_case_method = re.compile( - r"(^|[(,\s=])string\.(lower|upper)case([)\[,\s]|$)") -re_next_on_iterator_method = re.compile(r"\.next\(\)") -re_concatenate_dict = re.compile( - r".*\.items\(\)(\s*\+\s*.*\.items\(\))+.*") + + +def skip_ignored_lines(func): + + @functools.wraps(func) + def wrapper(logical_line, filename): + line = logical_line.strip() + if not line or line.startswith("#") or line.endswith("# noqa"): + return + yield next(func(logical_line, filename)) + + return wrapper def _parse_assert_mock_str(line): @@ -67,6 +70,7 @@ def _parse_assert_mock_str(line): return None, None, None +@skip_ignored_lines def check_assert_methods_from_mock(logical_line, filename): """Ensure that ``assert_*`` methods from ``mock`` library is used correctly @@ -112,6 +116,7 @@ def check_assert_methods_from_mock(logical_line, filename): "custom_msg": custom_msg}) +@skip_ignored_lines def check_import_of_logging(logical_line, filename): """Check correctness import of logging module @@ -131,7 +136,8 @@ def check_import_of_logging(logical_line, filename): "use `rally.common.log` instead.") -def no_translate_debug_logs(logical_line): +@skip_ignored_lines +def no_translate_debug_logs(logical_line, filename): """Check for "LOG.debug(_(" As per our translation policy, @@ -148,6 +154,7 @@ def no_translate_debug_logs(logical_line): yield(0, "N311 Don't translate debug level logs") +@skip_ignored_lines def no_use_conf_debug_check(logical_line, filename): """Check for "cfg.CONF.debug" @@ -167,7 +174,8 @@ def no_use_conf_debug_check(logical_line, filename): "should be used instead.") -def assert_true_instance(logical_line): +@skip_ignored_lines +def assert_true_instance(logical_line, filename): """Check for assertTrue(isinstance(a, b)) sentences N320 @@ -177,7 +185,8 @@ def assert_true_instance(logical_line): "you should use assertIsInstance(a, b) instead.") -def assert_equal_type(logical_line): +@skip_ignored_lines +def assert_equal_type(logical_line, filename): """Check for assertEqual(type(A), B) sentences N321 @@ -187,7 +196,8 @@ def assert_equal_type(logical_line): "you should use assertIsInstance(a, b) instead.") -def assert_equal_none(logical_line): +@skip_ignored_lines +def assert_equal_none(logical_line, filename): """Check for assertEqual(A, None) or assertEqual(None, A) sentences N322 @@ -200,7 +210,8 @@ def assert_equal_none(logical_line): "instead.") -def assert_true_or_false_with_in(logical_line): +@skip_ignored_lines +def assert_true_or_false_with_in(logical_line, filename): """Check assertTrue/False(A in/not in B) with collection contents Check for assertTrue/False(A in B), assertTrue/False(A not in B), @@ -217,7 +228,8 @@ def assert_true_or_false_with_in(logical_line): " instead.") -def assert_equal_in(logical_line): +@skip_ignored_lines +def assert_equal_in(logical_line, filename): """Check assertEqual(A in/not in B, True/False) with collection contents Check for assertEqual(A in B, True/False), assertEqual(True/False, A in B), @@ -234,134 +246,7 @@ def assert_equal_in(logical_line): "collection contents.") -def check_iteritems_method(logical_line): - """Check that collections are iterated in Python 3 compatible way - - The correct forms: - six.iterkeys(collection) - six.itervalues(collection) - six.iteritems(collection) - six.iterlist(collection) - - N330 - """ - iter_functions = ["iterkeys()", "itervalues()", - "iteritems()", "iterlist()"] - for func in iter_functions: - pos = logical_line.find(func) - if pos != -1: - yield (pos, "N330: Use six.%(func)s(dict) rather than " - "dict.%(func)s() to iterate a collection." % - {"func": func[:-2]}) - - -def check_basestring_method(logical_line): - """Check if basestring is properly called for compatibility with Python 3 - - There is no global variable "basestring" in Python 3.The correct form - is six.string_types, instead of basestring. - - N331 - """ - res = re_basestring_method.search(logical_line) - if res: - yield (0, "N331: Use six.string_types rather than basestring.") - - -def check_StringIO_method(logical_line): - """Check if StringIO is properly called for compatibility with Python 3 - - In Python 3, StringIO module is gone. The correct form is - six.moves.StringIO instead of StringIO.StringIO. - - N332 - """ - res = re_StringIO_method.search(logical_line) - if res: - yield (0, "N332: Use six.moves.StringIO " - "rather than StringIO.StringIO.") - - -def check_urlparse_method(logical_line): - """Check if urlparse is properly called for compatibility with Python 3 - - The correct form is six.moves.urllib.parse instead of "urlparse". - - N333 - """ - res = re_urlparse_method.search(logical_line) - if res: - yield (0, "N333: Use six.moves.urllib.parse rather than urlparse.") - - -def check_itertools_imap_method(logical_line): - """Check if itertools.imap is properly called for compatibility with Python 3 - - The correct form is six.moves.map instead of itertools.imap. - - N334 - """ - res = re_itertools_imap_method.search(logical_line) - if res: - yield (0, "N334: Use six.moves.map rather than itertools.imap.") - - -def check_xrange_method(logical_line): - """Check if xrange is properly called for compatibility with Python 3 - - The correct form is six.moves.range instead of xrange. - - N335 - """ - res = re_xrange_method.search(logical_line) - if res: - yield (0, "N335: Use six.moves.range rather than xrange.") - - -def check_string_lower_upper_case_method(logical_line): - """Check if string.lowercase and string.uppercase are properly called - - In Python 3, string.lowercase and string.uppercase are gone. - The correct form is "string.ascii_lowercase" and "string.ascii_uppercase". - - N336 - """ - res = re_string_lower_upper_case_method.search(logical_line) - if res: - yield (0, "N336: Use string.ascii_lowercase or string.ascii_uppercase " - "rather than string.lowercase or string.uppercase.") - - -def check_next_on_iterator_method(logical_line): - """Check if next() method on iterator objects are properly called - - Python 3 introduced a next() function to replace the next() method on - iterator objects. Rather than calling the method on the iterator, - the next() function is called with the iterable object as it's sole - parameter, which calls the underlying __next__() method. - - N337 - """ - res = re_next_on_iterator_method.search(logical_line) - if res: - yield (0, "N337: Use next(iterator) rather than iterator.next().") - - -def check_concatenate_dict_with_plus_operand(logical_line): - """Check if a dict is being sum with + operator - - Python 3 dict.items() return a dict_items object instead of a list, and - this object, doesn't support + operator. Need to use the update method - instead in order to concatenate two dictionaries. - - N338 - """ - res = re_concatenate_dict.search(logical_line) - if res: - yield (0, "N338: Use update() method instead of '+'' operand to " - "concatenate dictionaries") - - +@skip_ignored_lines def check_no_direct_rally_objects_import(logical_line, filename): """Check if rally.objects are properly imported. @@ -379,6 +264,7 @@ def check_no_direct_rally_objects_import(logical_line, filename): "After that you can use directly objects e.g. objects.Task") +@skip_ignored_lines def check_no_oslo_deprecated_import(logical_line, filename): """Check if oslo.foo packages are not imported instead of oslo_foo ones. @@ -394,6 +280,59 @@ def check_no_oslo_deprecated_import(logical_line, filename): "instead") +@skip_ignored_lines +def check_quotes(logical_line, filename): + """Check that single quotation marks are not used + + N350 + """ + + in_string = False + in_multiline_string = False + single_quotas_are_used = False + + check_tripple = ( + lambda line, i, char: ( + i + 2 < len(line) and + (char == line[i] == line[i + 1] == line[i + 2]) + ) + ) + + i = 0 + while i < len(logical_line): + char = logical_line[i] + + if in_string: + if char == "\"": + in_string = False + if char == "\\": + i += 1 # ignore next char + + elif in_multiline_string: + if check_tripple(logical_line, i, "\""): + i += 2 # skip next 2 chars + in_multiline_string = False + + elif char == "#": + break + + elif char == "'": + single_quotas_are_used = True + break + + elif char == "\"": + if check_tripple(logical_line, i, "\""): + in_multiline_string = True + i += 3 + continue + in_string = True + + i += 1 + + if single_quotas_are_used: + yield (i, "N350 Remove Single quotes") + + def factory(register): register(check_assert_methods_from_mock) register(check_import_of_logging) @@ -404,14 +343,6 @@ def factory(register): register(assert_equal_none) register(assert_true_or_false_with_in) register(assert_equal_in) - register(check_iteritems_method) - register(check_basestring_method) - register(check_StringIO_method) - register(check_urlparse_method) - register(check_itertools_imap_method) - register(check_xrange_method) - register(check_string_lower_upper_case_method) - register(check_next_on_iterator_method) register(check_no_direct_rally_objects_import) - register(check_concatenate_dict_with_plus_operand) register(check_no_oslo_deprecated_import) + register(check_quotes) diff --git a/tests/unit/test_hacking.py b/tests/unit/test_hacking.py index b94c2c57..44ee8aeb 100644 --- a/tests/unit/test_hacking.py +++ b/tests/unit/test_hacking.py @@ -29,6 +29,17 @@ class HackingTestCase(test.TestCase): self.assertIsNone(method) self.assertIsNone(obj) + def test_skip_ignored_lines(self): + + @checks.skip_ignored_lines + def any_gen(logical_line, file_name): + yield 42 + + self.assertEqual([], list(any_gen("fdafadfdas # noqa", "f"))) + self.assertEqual([], list(any_gen(" # fdafadfdas", "f"))) + self.assertEqual([], list(any_gen(" ", "f"))) + self.assertEqual(42, next(any_gen("otherstuff", "f"))) + def test_correct_usage_of_assert_from_mock(self): correct_method_names = ["assert_any_call", "assert_called_once_with", "assert_called_with", "assert_has_calls"] @@ -61,6 +72,14 @@ class HackingTestCase(test.TestCase): self.assertEqual(4, actual_number) self.assertTrue(actual_msg.startswith("N303")) + def _assert_good_samples(self, checker, samples, module_file="f"): + for s in samples: + self.assertEqual([], list(checker(s, module_file)), s) + + def _assert_bad_samples(self, checker, samples, module_file="f"): + for s in samples: + self.assertEqual(1, len(list(checker(s, module_file))), s) + def test_check_wrong_logging_import(self): bad_imports = ["from oslo_log import log", "import oslo_log", @@ -84,284 +103,145 @@ class HackingTestCase(test.TestCase): self.assertEqual([], list(checkres)) def test_no_translate_debug_logs(self): - self.assertEqual(len(list(checks.no_translate_debug_logs( - "LOG.debug(_('foo'))"))), 1) - self.assertEqual(len(list(checks.no_translate_debug_logs( - "LOG.debug('foo')"))), 0) - - self.assertEqual(len(list(checks.no_translate_debug_logs( - "LOG.info(_('foo'))"))), 0) + bad_samples = ["LOG.debug(_('foo'))"] + self._assert_bad_samples(checks.no_translate_debug_logs, bad_samples) + good_samples = ["LOG.debug('foo')", "LOG.info(_('foo'))"] + self._assert_good_samples(checks.no_translate_debug_logs, good_samples) def test_no_use_conf_debug_check(self): - self.assertEqual(len(list(checks.no_use_conf_debug_check( - "if CONF.debug:", "fakefile"))), 1) + bad_samples = [ + "if CONF.debug:", + "if cfg.CONF.debug" + ] + self._assert_bad_samples(checks.no_use_conf_debug_check, bad_samples) - self.assertEqual(len(list(checks.no_use_conf_debug_check( - "if cfg.CONF.debug", "fakefile"))), 1) - - self.assertEqual(len(list(checks.no_use_conf_debug_check( - "if logging.is_debug()", "fakefile"))), 0) + good_samples = ["if logging.is_debug()"] + self._assert_good_samples(checks.no_use_conf_debug_check, good_samples) def test_assert_true_instance(self): self.assertEqual(len(list(checks.assert_true_instance( "self.assertTrue(isinstance(e, " - "exception.BuildAbortException))"))), 1) + "exception.BuildAbortException))", "f"))), 1) self.assertEqual( - len(list(checks.assert_true_instance("self.assertTrue()"))), 0) + 0, + len(list(checks.assert_true_instance("self.assertTrue()", "f")))) def test_assert_equal_type(self): self.assertEqual(len(list(checks.assert_equal_type( - "self.assertEqual(type(als['QuicAssist']), list)"))), 1) + "self.assertEqual(type(als['QuicAssist']), list)", "f"))), 1) self.assertEqual( - len(list(checks.assert_equal_type("self.assertTrue()"))), 0) - - def test_check_iteritems_method(self): - self.assertEqual(len(list(checks.check_iteritems_method( - "dict.iteritems()"))), 1) - - self.assertEqual(len(list(checks.check_iteritems_method( - "iteritems(dict)"))), 0) - - self.assertEqual(len(list(checks.check_iteritems_method( - "dict.items()"))), 0) - - def test_check_concatenate_dict_with_plus_operand(self): - self.assertEqual(len(list( - checks.check_concatenate_dict_with_plus_operand( - "dict1.items() + dict2.items()"))), 1) - - self.assertEqual(len(list( - checks.check_concatenate_dict_with_plus_operand( - "dict(self.endpoint.to_dict().items() + " - "endpoint.items())"))), 1) - - self.assertEqual(len(list( - checks.check_concatenate_dict_with_plus_operand( - "dict(self.endpoint.to_dict().items() + " - "endpoint.items() + something.items())"))), 1) - - self.assertEqual(len(list( - checks.check_concatenate_dict_with_plus_operand( - "dict1.item() + dict2.item()"))), 0) - - self.assertEqual(len(list( - checks.check_concatenate_dict_with_plus_operand( - "obj.getitems() + obj.getitems()"))), 0) - - self.assertEqual(len(list( - checks.check_concatenate_dict_with_plus_operand( - "obj.getitems() + dict2.items()"))), 0) - - def test_check_basestring_method(self): - self.assertEqual(len(list(checks.check_basestring_method( - "basestring"))), 1) - - self.assertEqual(len(list(checks.check_basestring_method( - "six.string_types"))), 0) - - def test_check_StringIO_method(self): - self.assertEqual(len(list(checks.check_StringIO_method( - "StringIO.StringIO()"))), 1) - - self.assertEqual(len(list(checks.check_StringIO_method( - "six.moves.StringIO()"))), 0) - - def test_check_urlparse_method(self): - self.assertEqual(len(list(checks.check_urlparse_method( - "urlparse.urlparse(url)"))), 1) - - self.assertEqual(len(list(checks.check_urlparse_method( - "six.moves.urllib.parse.urlparse(url)"))), 0) - - def test_check_itertools_imap_method(self): - self.assertEqual(len(list(checks.check_itertools_imap_method( - "itertools.imap()"))), 1) - - self.assertEqual(len(list(checks.check_itertools_imap_method( - "six.moves.map()"))), 0) - - def test_check_xrange_imap_method(self): - self.assertEqual(len(list(checks.check_xrange_method( - "xrange()"))), 1) - - self.assertEqual(len(list(checks.check_xrange_method( - "six.moves.range()"))), 0) - - def test_check_string_lower_upper_case_method(self): - self.assertEqual(len(list(checks.check_string_lower_upper_case_method( - "string.lowercase[:16]"))), 1) - - self.assertEqual(len(list(checks.check_string_lower_upper_case_method( - "string.uppercase[:16]"))), 1) - - self.assertEqual(len(list(checks.check_string_lower_upper_case_method( - "string.ascii_lowercase[:16]"))), 0) - - self.assertEqual(len(list(checks.check_string_lower_upper_case_method( - "string.ascii_uppercase[:16]"))), 0) - - def test_check_next_on_iterator_method(self): - self.assertEqual(len(list(checks.check_next_on_iterator_method( - "iterator.next()"))), 1) - - self.assertEqual(len(list(checks.check_next_on_iterator_method( - "next(iterator)"))), 0) + len(list(checks.assert_equal_type("self.assertTrue()", "f"))), 0) def test_assert_equal_none(self): self.assertEqual(len(list(checks.assert_equal_none( - "self.assertEqual(A, None)"))), 1) + "self.assertEqual(A, None)", "f"))), 1) self.assertEqual(len(list(checks.assert_equal_none( - "self.assertEqual(None, A)"))), 1) + "self.assertEqual(None, A)", "f"))), 1) self.assertEqual( - len(list(checks.assert_equal_none("self.assertIsNone()"))), 0) + len(list(checks.assert_equal_none("self.assertIsNone()", "f"))), 0) def test_assert_true_or_false_with_in_or_not_in(self): - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertTrue(A in B)"))), 1) + good_lines = [ + "self.assertTrue(any(A > 5 for A in B))", + "self.assertTrue(any(A > 5 for A in B), 'some message')", + "self.assertFalse(some in list1 and some2 in list2)" + ] + self._assert_good_samples(checks.assert_true_or_false_with_in, + good_lines) - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertFalse(A in B)"))), 1) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertTrue(A not in B)"))), 1) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertFalse(A not in B)"))), 1) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertTrue(A in B, 'some message')"))), 1) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertFalse(A in B, 'some message')"))), 1) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertTrue(A not in B, 'some message')"))), 1) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertFalse(A not in B, 'some message')"))), 1) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertTrue(A in 'some string with spaces')"))), 1) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertTrue(A in 'some string with spaces')"))), 1) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertTrue(A in ['1', '2', '3'])"))), 1) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertTrue(A in [1, 2, 3])"))), 1) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertTrue(any(A > 5 for A in B))"))), 0) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertTrue(any(A > 5 for A in B), 'some message')"))), 0) - - self.assertEqual(len(list(checks.assert_true_or_false_with_in( - "self.assertFalse(some in list1 and some2 in list2)"))), 0) + bad_lines = [ + "self.assertTrue(A in B)", + "self.assertFalse(A in B)", + "self.assertTrue(A not in B)", + "self.assertFalse(A not in B)", + "self.assertTrue(A in B, 'some message')", + "self.assertFalse(A in B, 'some message')", + "self.assertTrue(A not in B, 'some message')", + "self.assertFalse(A not in B, 'some message')", + "self.assertTrue(A in 'some string with spaces')", + "self.assertTrue(A in 'some string with spaces')", + "self.assertTrue(A in ['1', '2', '3'])", + "self.assertTrue(A in [1, 2, 3])" + ] + self._assert_bad_samples(checks.assert_true_or_false_with_in, + bad_lines) def test_assert_equal_in(self): - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(a in b, True)"))), 1) + good_lines = [ + "self.assertEqual(any(a==1 for a in b), True)", + "self.assertEqual(True, any(a==1 for a in b))", + "self.assertEqual(any(a==1 for a in b), False)", + "self.assertEqual(False, any(a==1 for a in b))" + ] + self._assert_good_samples(checks.assert_equal_in, good_lines) - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(a not in b, True)"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual('str' in 'string', True)"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual('str' not in 'string', True)"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(any(a==1 for a in b), True)"))), 0) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(True, a in b)"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(True, a not in b)"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(True, 'str' in 'string')"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(True, 'str' not in 'string')"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(True, any(a==1 for a in b))"))), 0) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(a in b, False)"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(a not in b, False)"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual('str' in 'string', False)"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual('str' not in 'string', False)"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(any(a==1 for a in b), False)"))), 0) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(False, a in b)"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(False, a not in b)"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(False, 'str' in 'string')"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(False, 'str' not in 'string')"))), 1) - - self.assertEqual(len(list(checks.assert_equal_in( - "self.assertEqual(False, any(a==1 for a in b))"))), 0) + bad_lines = [ + "self.assertEqual(a in b, True)", + "self.assertEqual(a not in b, True)", + "self.assertEqual('str' in 'string', True)", + "self.assertEqual('str' not in 'string', True)", + "self.assertEqual(True, a in b)", + "self.assertEqual(True, a not in b)", + "self.assertEqual(True, 'str' in 'string')", + "self.assertEqual(True, 'str' not in 'string')", + "self.assertEqual(a in b, False)", + "self.assertEqual(a not in b, False)", + "self.assertEqual('str' in 'string', False)", + "self.assertEqual('str' not in 'string', False)", + "self.assertEqual(False, a in b)", + "self.assertEqual(False, a not in b)", + "self.assertEqual(False, 'str' in 'string')", + "self.assertEqual(False, 'str' not in 'string')", + ] + self._assert_bad_samples(checks.assert_equal_in, bad_lines) def test_check_no_direct_rally_objects_import(self): - bad_imports = ["from rally.objects import task", "import rally.objects.task"] - good_import = "from rally import objects" + self._assert_bad_samples(checks.check_no_direct_rally_objects_import, + bad_imports) - for bad_import in bad_imports: - checkres = checks.check_no_direct_rally_objects_import(bad_import, - "fakefile") - self.assertIsNotNone(next(checkres)) + self._assert_good_samples(checks.check_no_direct_rally_objects_import, + bad_imports, + module_file="./rally/objects/__init__.py") - for bad_import in bad_imports: - checkres = checks.check_no_direct_rally_objects_import( - bad_import, "./rally/objects/__init__.py") - self.assertEqual([], list(checkres)) - - checkres = checks.check_no_direct_rally_objects_import(good_import, - "fakefile") - self.assertEqual([], list(checkres)) + good_imports = ["from rally import objects"] + self._assert_good_samples(checks.check_no_direct_rally_objects_import, + good_imports) def test_check_no_oslo_deprecated_import(self): - bad_imports = ["from oslo.config", - "import oslo.config," + "import oslo.config", "from oslo.db", - "import oslo.db," + "import oslo.db", "from oslo.i18n", - "import oslo.i18n," + "import oslo.i18n", "from oslo.serialization", - "import oslo.serialization," + "import oslo.serialization", "from oslo.utils", - "import oslo.utils,"] + "import oslo.utils"] - for bad_import in bad_imports: - checkres = checks.check_no_oslo_deprecated_import(bad_import, - "fakefile") - self.assertIsNotNone(next(checkres)) + self._assert_bad_samples(checks.check_no_oslo_deprecated_import, + bad_imports) + + def test_check_quotas(self): + bad_lines = [ + "a = '1'", + "a = \"a\" + 'a'", + "'", + "\"\"\"\"\"\" + ''''''" + ] + self._assert_bad_samples(checks.check_quotes, bad_lines) + + good_lines = [ + "\"'a'\" + \"\"\"a'''fdfd'''\"\"\"", + "\"fdfdfd\" + \"''''''\"", + "a = '' # noqa " + ] + self._assert_good_samples(checks.check_quotes, good_lines)