blob: 83c1abbe7ee78e68b9c92f3ff9d9e25ef4237ac3 [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
104 def test_no_vi_headers(self):
105 # NOTE(mtreinish) The lines parameter is used only for finding the
106 # line location in the file. So these tests just pass a list of an
107 # arbitrary length to use for verifying the check function.
108 self.assertTrue(checks.no_vi_headers(
109 '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 1, range(250)))
110 self.assertTrue(checks.no_vi_headers(
111 '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 249, range(250)))
112 self.assertFalse(checks.no_vi_headers(
113 '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 149, range(250)))
114
115 def test_service_tags_not_in_module_path(self):
116 self.assertTrue(checks.service_tags_not_in_module_path(
Andrea Frittolicd368412017-08-14 21:37:56 +0100117 "@utils.services('compute')",
118 './tempest/api/compute/fake_test.py'))
Matthew Treinishe2eee322014-05-02 19:58:54 -0400119 self.assertFalse(checks.service_tags_not_in_module_path(
Andrea Frittolicd368412017-08-14 21:37:56 +0100120 "@utils.services('compute')",
Matthew Treinishe2eee322014-05-02 19:58:54 -0400121 './tempest/scenario/compute/fake_test.py'))
122 self.assertFalse(checks.service_tags_not_in_module_path(
Andrea Frittolicd368412017-08-14 21:37:56 +0100123 "@utils.services('compute')", './tempest/api/image/fake_test.py'))
Matthew Treinish7acaba42014-05-28 09:19:03 -0400124
Ken'ichi Ohmichi80369a92015-04-06 23:41:14 +0000125 def test_no_hyphen_at_end_of_rand_name(self):
126 self.assertIsNone(checks.no_hyphen_at_end_of_rand_name(
127 'data_utils.rand_name("fake-resource")', './tempest/test_foo.py'))
128 self.assertEqual(2, len(list(checks.no_hyphen_at_end_of_rand_name(
129 'data_utils.rand_name("fake-resource-")', './tempest/test_foo.py')
130 )))
131
Ghanshyam2a180b82014-06-16 13:54:22 +0900132 def test_no_mutable_default_args(self):
133 self.assertEqual(1, len(list(checks.no_mutable_default_args(
134 " def function1(para={}):"))))
135
136 self.assertEqual(1, len(list(checks.no_mutable_default_args(
137 "def function2(para1, para2, para3=[])"))))
138
139 self.assertEqual(0, len(list(checks.no_mutable_default_args(
140 "defined = []"))))
141
142 self.assertEqual(0, len(list(checks.no_mutable_default_args(
143 "defined, undefined = [], {}"))))
John Warren3059a092015-08-31 15:34:49 -0400144
145 def test_no_testtools_skip_decorator(self):
146 self.assertEqual(1, len(list(checks.no_testtools_skip_decorator(
147 " @testtools.skip('Bug xxx')"))))
148 self.assertEqual(0, len(list(checks.no_testtools_skip_decorator(
149 " @testtools.skipUnless(CONF.something, 'msg')"))))
150 self.assertEqual(0, len(list(checks.no_testtools_skip_decorator(
151 " @testtools.skipIf(CONF.something, 'msg')"))))
Ken'ichi Ohmichi0dc97472016-03-25 15:10:08 -0700152
153 def test_dont_import_local_tempest_code_into_lib(self):
154 self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
155 "from tempest.common import waiters",
156 './tempest/common/compute.py'))))
157 self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
158 "from tempest import config",
159 './tempest/common/compute.py'))))
160 self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
161 "import tempest.exception",
162 './tempest/common/compute.py'))))
163 self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
164 "from tempest.common import waiters",
165 './tempest/lib/common/compute.py'))))
166 self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
167 "from tempest import config",
168 './tempest/lib/common/compute.py'))))
169 self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
170 "import tempest.exception",
171 './tempest/lib/common/compute.py'))))
Matthew Treinish59d9eaa2016-05-31 23:42:55 -0400172
173 def test_dont_use_config_in_tempest_lib(self):
174 self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
175 'from tempest import config', './tempest/common/compute.py')))
176 self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
177 'from oslo_concurrency import lockutils',
178 './tempest/lib/auth.py')))
179 self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
180 'from tempest import config', './tempest/lib/auth.py')))
181 self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
182 'from oslo_config import cfg', './tempest/lib/decorators.py')))
183 self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
184 'import tempest.config', './tempest/lib/common/rest_client.py')))
junbolibc2ae862017-07-29 15:46:48 +0800185
186 def test_unsupported_exception_attribute_PY3(self):
187 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
188 "raise TestCase.failureException(e.message)"))), 1)
189 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
190 "raise TestCase.failureException(ex.message)"))), 1)
191 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
192 "raise TestCase.failureException(exc.message)"))), 1)
193 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
194 "raise TestCase.failureException(exception.message)"))), 1)
195 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
196 "raise TestCase.failureException(ee.message)"))), 0)
Felipe Monteiro4d011af2018-07-18 00:11:48 -0400197
198 def _test_no_negatve_test_attribute_applied_to_negative_test(
199 self, filename, with_other_decorators=False,
200 with_negative_decorator=True, expected_success=True):
201 check = checks.negative_test_attribute_always_applied_to_negative_tests
202 other_decorators = [
203 "@decorators.idempotent_id(123)",
204 "@utils.requires_ext(extension='ext', service='svc')"
205 ]
206
207 if with_other_decorators:
208 # Include multiple decorators to verify that this check works with
209 # arbitrarily many decorators. These insert decorators above the
210 # @decorators.attr(type=['negative']) decorator.
211 for decorator in other_decorators:
212 self.assertIsNone(check(" %s" % decorator, filename))
213 if with_negative_decorator:
214 self.assertIsNone(
215 check("@decorators.attr(type=['negative'])", filename))
216 if with_other_decorators:
217 # Include multiple decorators to verify that this check works with
218 # arbitrarily many decorators. These insert decorators between
219 # the test and the @decorators.attr(type=['negative']) decorator.
220 for decorator in other_decorators:
221 self.assertIsNone(check(" %s" % decorator, filename))
222 final_result = check(" def test_some_negative_case", filename)
223 if expected_success:
224 self.assertIsNone(final_result)
225 else:
226 self.assertIsInstance(final_result, tuple)
227 self.assertFalse(final_result[0])
228
229 def test_no_negatve_test_attribute_applied_to_negative_test(self):
230 # Check negative filename, negative decorator passes
231 self._test_no_negatve_test_attribute_applied_to_negative_test(
232 "./tempest/api/test_something_negative.py")
233 # Check negative filename, negative decorator, other decorators passes
234 self._test_no_negatve_test_attribute_applied_to_negative_test(
235 "./tempest/api/test_something_negative.py",
236 with_other_decorators=True)
237
238 # Check non-negative filename skips check, causing pass
239 self._test_no_negatve_test_attribute_applied_to_negative_test(
240 "./tempest/api/test_something.py")
241
242 # Check negative filename, no negative decorator fails
243 self._test_no_negatve_test_attribute_applied_to_negative_test(
244 "./tempest/api/test_something_negative.py",
245 with_negative_decorator=False,
246 expected_success=False)
247 # Check negative filename, no negative decorator, other decorators
248 # fails
249 self._test_no_negatve_test_attribute_applied_to_negative_test(
250 "./tempest/api/test_something_negative.py",
251 with_other_decorators=True,
252 with_negative_decorator=False,
253 expected_success=False)