blob: 7c31185f4fc3f6927286f5fac8a2be04f2ce10b5 [file] [log] [blame]
Matthew Treinishaaa35952014-05-02 18:50:16 -04001# Copyright 2014 Matthew Treinish
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15from tempest.hacking import checks
Matthew Treinishffad78a2016-04-16 14:39:52 -040016from tempest.tests import base
Matthew Treinishaaa35952014-05-02 18:50:16 -040017
18
19class HackingTestCase(base.TestCase):
Ken'ichi Ohmichic864f592015-11-19 08:45:06 +000020 """Test class for hacking rule
21
Matthew Treinishe2eee322014-05-02 19:58:54 -040022 This class tests the hacking checks in tempest.hacking.checks by passing
23 strings to the check methods like the pep8/flake8 parser would. The parser
24 loops over each line in the file and then passes the parameters to the
25 check method. The parameter names in the check method dictate what type of
26 object is passed to the check method. The parameter types are::
27
28 logical_line: A processed line with the following modifications:
29 - Multi-line statements converted to a single line.
30 - Stripped left and right.
31 - Contents of strings replaced with "xxx" of same length.
32 - Comments removed.
33 physical_line: Raw line of text from the input file.
34 lines: a list of the raw lines from the input file
35 tokens: the tokens that contribute to this logical line
36 line_number: line number in the input file
37 total_lines: number of lines in the input file
38 blank_lines: blank lines before this one
39 indent_char: indentation character in this file (" " or "\t")
40 indent_level: indentation (with tabs expanded to multiples of 8)
41 previous_indent_level: indentation on previous line
42 previous_logical: previous logical line
43 filename: Path of the file being run through pep8
44
45 When running a test on a check method the return will be False/None if
46 there is no violation in the sample input. If there is an error a tuple is
47 returned with a position in the line, and a message. So to check the result
48 just assertTrue if the check is expected to fail and assertFalse if it
49 should pass.
50 """
afazekas40fcb9b2019-03-08 11:25:11 +010051
Andrea Frittoli41fa16d2014-09-15 13:41:37 +010052 def test_no_setup_teardown_class_for_tests(self):
53 self.assertTrue(checks.no_setup_teardown_class_for_tests(
Matthew Treinishaaa35952014-05-02 18:50:16 -040054 " def setUpClass(cls):", './tempest/tests/fake_test.py'))
Andrea Frittoli41fa16d2014-09-15 13:41:37 +010055 self.assertIsNone(checks.no_setup_teardown_class_for_tests(
Matthew Treinishaaa35952014-05-02 18:50:16 -040056 " def setUpClass(cls): # noqa", './tempest/tests/fake_test.py'))
Andrea Frittoli41fa16d2014-09-15 13:41:37 +010057 self.assertTrue(checks.no_setup_teardown_class_for_tests(
Matthew Treinishaaa35952014-05-02 18:50:16 -040058 " def setUpClass(cls):", './tempest/api/fake_test.py'))
Andrea Frittoli41fa16d2014-09-15 13:41:37 +010059 self.assertTrue(checks.no_setup_teardown_class_for_tests(
60 " def setUpClass(cls):", './tempest/scenario/fake_test.py'))
61 self.assertFalse(checks.no_setup_teardown_class_for_tests(
62 " def setUpClass(cls):", './tempest/test.py'))
63 self.assertTrue(checks.no_setup_teardown_class_for_tests(
64 " def tearDownClass(cls):", './tempest/tests/fake_test.py'))
65 self.assertIsNone(checks.no_setup_teardown_class_for_tests(
66 " def tearDownClass(cls): # noqa", './tempest/tests/fake_test.py'))
67 self.assertTrue(checks.no_setup_teardown_class_for_tests(
68 " def tearDownClass(cls):", './tempest/api/fake_test.py'))
69 self.assertTrue(checks.no_setup_teardown_class_for_tests(
70 " def tearDownClass(cls):", './tempest/scenario/fake_test.py'))
71 self.assertFalse(checks.no_setup_teardown_class_for_tests(
72 " def tearDownClass(cls):", './tempest/test.py'))
Matthew Treinishe2eee322014-05-02 19:58:54 -040073
ghanshyam50f19472014-11-26 17:04:37 +090074 def test_import_no_clients_in_api_and_scenario_tests(self):
Matthew Treinishe2eee322014-05-02 19:58:54 -040075 for client in checks.PYTHON_CLIENTS:
76 string = "import " + client + "client"
ghanshyam50f19472014-11-26 17:04:37 +090077 self.assertTrue(
78 checks.import_no_clients_in_api_and_scenario_tests(
79 string, './tempest/api/fake_test.py'))
80 self.assertTrue(
81 checks.import_no_clients_in_api_and_scenario_tests(
82 string, './tempest/scenario/fake_test.py'))
83 self.assertFalse(
84 checks.import_no_clients_in_api_and_scenario_tests(
85 string, './tempest/test.py'))
Matthew Treinishe2eee322014-05-02 19:58:54 -040086
87 def test_scenario_tests_need_service_tags(self):
88 self.assertFalse(checks.scenario_tests_need_service_tags(
89 'def test_fake:', './tempest/scenario/test_fake.py',
Andrea Frittolicd368412017-08-14 21:37:56 +010090 "@utils.services('compute')"))
Matthew Treinishe2eee322014-05-02 19:58:54 -040091 self.assertFalse(checks.scenario_tests_need_service_tags(
92 'def test_fake_test:', './tempest/api/compute/test_fake.py',
Andrea Frittolicd368412017-08-14 21:37:56 +010093 "@utils.services('image')"))
Matthew Treinishb12ad762014-06-19 10:18:05 -040094 self.assertFalse(checks.scenario_tests_need_service_tags(
95 'def test_fake:', './tempest/scenario/orchestration/test_fake.py',
Andrea Frittolicd368412017-08-14 21:37:56 +010096 "@utils.services('compute')"))
Matthew Treinishe2eee322014-05-02 19:58:54 -040097 self.assertTrue(checks.scenario_tests_need_service_tags(
98 'def test_fake_test:', './tempest/scenario/test_fake.py',
99 '\n'))
Matthew Treinishb12ad762014-06-19 10:18:05 -0400100 self.assertTrue(checks.scenario_tests_need_service_tags(
101 'def test_fake:', './tempest/scenario/orchestration/test_fake.py',
102 "\n"))
Matthew Treinishe2eee322014-05-02 19:58:54 -0400103
Matthew Treinishe2eee322014-05-02 19:58:54 -0400104 def test_service_tags_not_in_module_path(self):
105 self.assertTrue(checks.service_tags_not_in_module_path(
Andrea Frittolicd368412017-08-14 21:37:56 +0100106 "@utils.services('compute')",
107 './tempest/api/compute/fake_test.py'))
Matthew Treinishe2eee322014-05-02 19:58:54 -0400108 self.assertFalse(checks.service_tags_not_in_module_path(
Andrea Frittolicd368412017-08-14 21:37:56 +0100109 "@utils.services('compute')",
Matthew Treinishe2eee322014-05-02 19:58:54 -0400110 './tempest/scenario/compute/fake_test.py'))
111 self.assertFalse(checks.service_tags_not_in_module_path(
Andrea Frittolicd368412017-08-14 21:37:56 +0100112 "@utils.services('compute')", './tempest/api/image/fake_test.py'))
Matthew Treinish7acaba42014-05-28 09:19:03 -0400113
Ken'ichi Ohmichi80369a92015-04-06 23:41:14 +0000114 def test_no_hyphen_at_end_of_rand_name(self):
115 self.assertIsNone(checks.no_hyphen_at_end_of_rand_name(
116 'data_utils.rand_name("fake-resource")', './tempest/test_foo.py'))
117 self.assertEqual(2, len(list(checks.no_hyphen_at_end_of_rand_name(
118 'data_utils.rand_name("fake-resource-")', './tempest/test_foo.py')
119 )))
120
Ghanshyam2a180b82014-06-16 13:54:22 +0900121 def test_no_mutable_default_args(self):
122 self.assertEqual(1, len(list(checks.no_mutable_default_args(
123 " def function1(para={}):"))))
124
125 self.assertEqual(1, len(list(checks.no_mutable_default_args(
126 "def function2(para1, para2, para3=[])"))))
127
128 self.assertEqual(0, len(list(checks.no_mutable_default_args(
129 "defined = []"))))
130
131 self.assertEqual(0, len(list(checks.no_mutable_default_args(
132 "defined, undefined = [], {}"))))
John Warren3059a092015-08-31 15:34:49 -0400133
134 def test_no_testtools_skip_decorator(self):
135 self.assertEqual(1, len(list(checks.no_testtools_skip_decorator(
136 " @testtools.skip('Bug xxx')"))))
137 self.assertEqual(0, len(list(checks.no_testtools_skip_decorator(
138 " @testtools.skipUnless(CONF.something, 'msg')"))))
139 self.assertEqual(0, len(list(checks.no_testtools_skip_decorator(
140 " @testtools.skipIf(CONF.something, 'msg')"))))
Ken'ichi Ohmichi0dc97472016-03-25 15:10:08 -0700141
142 def test_dont_import_local_tempest_code_into_lib(self):
143 self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
144 "from tempest.common import waiters",
145 './tempest/common/compute.py'))))
146 self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
147 "from tempest import config",
148 './tempest/common/compute.py'))))
149 self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
150 "import tempest.exception",
151 './tempest/common/compute.py'))))
152 self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
153 "from tempest.common import waiters",
154 './tempest/lib/common/compute.py'))))
155 self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
156 "from tempest import config",
157 './tempest/lib/common/compute.py'))))
158 self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
159 "import tempest.exception",
160 './tempest/lib/common/compute.py'))))
Matthew Treinish59d9eaa2016-05-31 23:42:55 -0400161
162 def test_dont_use_config_in_tempest_lib(self):
163 self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
164 'from tempest import config', './tempest/common/compute.py')))
165 self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
166 'from oslo_concurrency import lockutils',
167 './tempest/lib/auth.py')))
168 self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
169 'from tempest import config', './tempest/lib/auth.py')))
170 self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
171 'from oslo_config import cfg', './tempest/lib/decorators.py')))
172 self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
173 'import tempest.config', './tempest/lib/common/rest_client.py')))
junbolibc2ae862017-07-29 15:46:48 +0800174
175 def test_unsupported_exception_attribute_PY3(self):
176 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
177 "raise TestCase.failureException(e.message)"))), 1)
178 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
179 "raise TestCase.failureException(ex.message)"))), 1)
180 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
181 "raise TestCase.failureException(exc.message)"))), 1)
182 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
183 "raise TestCase.failureException(exception.message)"))), 1)
184 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
185 "raise TestCase.failureException(ee.message)"))), 0)
Felipe Monteiro4d011af2018-07-18 00:11:48 -0400186
187 def _test_no_negatve_test_attribute_applied_to_negative_test(
188 self, filename, with_other_decorators=False,
189 with_negative_decorator=True, expected_success=True):
190 check = checks.negative_test_attribute_always_applied_to_negative_tests
191 other_decorators = [
192 "@decorators.idempotent_id(123)",
193 "@utils.requires_ext(extension='ext', service='svc')"
194 ]
195
196 if with_other_decorators:
197 # Include multiple decorators to verify that this check works with
198 # arbitrarily many decorators. These insert decorators above the
199 # @decorators.attr(type=['negative']) decorator.
200 for decorator in other_decorators:
201 self.assertIsNone(check(" %s" % decorator, filename))
202 if with_negative_decorator:
203 self.assertIsNone(
204 check("@decorators.attr(type=['negative'])", filename))
205 if with_other_decorators:
206 # Include multiple decorators to verify that this check works with
207 # arbitrarily many decorators. These insert decorators between
208 # the test and the @decorators.attr(type=['negative']) decorator.
209 for decorator in other_decorators:
210 self.assertIsNone(check(" %s" % decorator, filename))
211 final_result = check(" def test_some_negative_case", filename)
212 if expected_success:
213 self.assertIsNone(final_result)
214 else:
215 self.assertIsInstance(final_result, tuple)
216 self.assertFalse(final_result[0])
217
218 def test_no_negatve_test_attribute_applied_to_negative_test(self):
219 # Check negative filename, negative decorator passes
220 self._test_no_negatve_test_attribute_applied_to_negative_test(
221 "./tempest/api/test_something_negative.py")
222 # Check negative filename, negative decorator, other decorators passes
223 self._test_no_negatve_test_attribute_applied_to_negative_test(
224 "./tempest/api/test_something_negative.py",
225 with_other_decorators=True)
226
227 # Check non-negative filename skips check, causing pass
228 self._test_no_negatve_test_attribute_applied_to_negative_test(
229 "./tempest/api/test_something.py")
230
231 # Check negative filename, no negative decorator fails
232 self._test_no_negatve_test_attribute_applied_to_negative_test(
233 "./tempest/api/test_something_negative.py",
234 with_negative_decorator=False,
235 expected_success=False)
236 # Check negative filename, no negative decorator, other decorators
237 # fails
238 self._test_no_negatve_test_attribute_applied_to_negative_test(
239 "./tempest/api/test_something_negative.py",
240 with_other_decorators=True,
241 with_negative_decorator=False,
242 expected_success=False)