blob: 9534ce8df5753f5601c995635f175de73c047000 [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 """
Andrea Frittoli41fa16d2014-09-15 13:41:37 +010051 def test_no_setup_teardown_class_for_tests(self):
52 self.assertTrue(checks.no_setup_teardown_class_for_tests(
Matthew Treinishaaa35952014-05-02 18:50:16 -040053 " def setUpClass(cls):", './tempest/tests/fake_test.py'))
Andrea Frittoli41fa16d2014-09-15 13:41:37 +010054 self.assertIsNone(checks.no_setup_teardown_class_for_tests(
Matthew Treinishaaa35952014-05-02 18:50:16 -040055 " def setUpClass(cls): # noqa", './tempest/tests/fake_test.py'))
Andrea Frittoli41fa16d2014-09-15 13:41:37 +010056 self.assertTrue(checks.no_setup_teardown_class_for_tests(
Matthew Treinishaaa35952014-05-02 18:50:16 -040057 " def setUpClass(cls):", './tempest/api/fake_test.py'))
Andrea Frittoli41fa16d2014-09-15 13:41:37 +010058 self.assertTrue(checks.no_setup_teardown_class_for_tests(
59 " def setUpClass(cls):", './tempest/scenario/fake_test.py'))
60 self.assertFalse(checks.no_setup_teardown_class_for_tests(
61 " def setUpClass(cls):", './tempest/test.py'))
62 self.assertTrue(checks.no_setup_teardown_class_for_tests(
63 " def tearDownClass(cls):", './tempest/tests/fake_test.py'))
64 self.assertIsNone(checks.no_setup_teardown_class_for_tests(
65 " def tearDownClass(cls): # noqa", './tempest/tests/fake_test.py'))
66 self.assertTrue(checks.no_setup_teardown_class_for_tests(
67 " def tearDownClass(cls):", './tempest/api/fake_test.py'))
68 self.assertTrue(checks.no_setup_teardown_class_for_tests(
69 " def tearDownClass(cls):", './tempest/scenario/fake_test.py'))
70 self.assertFalse(checks.no_setup_teardown_class_for_tests(
71 " def tearDownClass(cls):", './tempest/test.py'))
Matthew Treinishe2eee322014-05-02 19:58:54 -040072
ghanshyam50f19472014-11-26 17:04:37 +090073 def test_import_no_clients_in_api_and_scenario_tests(self):
Matthew Treinishe2eee322014-05-02 19:58:54 -040074 for client in checks.PYTHON_CLIENTS:
75 string = "import " + client + "client"
ghanshyam50f19472014-11-26 17:04:37 +090076 self.assertTrue(
77 checks.import_no_clients_in_api_and_scenario_tests(
78 string, './tempest/api/fake_test.py'))
79 self.assertTrue(
80 checks.import_no_clients_in_api_and_scenario_tests(
81 string, './tempest/scenario/fake_test.py'))
82 self.assertFalse(
83 checks.import_no_clients_in_api_and_scenario_tests(
84 string, './tempest/test.py'))
Matthew Treinishe2eee322014-05-02 19:58:54 -040085
86 def test_scenario_tests_need_service_tags(self):
87 self.assertFalse(checks.scenario_tests_need_service_tags(
88 'def test_fake:', './tempest/scenario/test_fake.py',
Andrea Frittolicd368412017-08-14 21:37:56 +010089 "@utils.services('compute')"))
Matthew Treinishe2eee322014-05-02 19:58:54 -040090 self.assertFalse(checks.scenario_tests_need_service_tags(
91 'def test_fake_test:', './tempest/api/compute/test_fake.py',
Andrea Frittolicd368412017-08-14 21:37:56 +010092 "@utils.services('image')"))
Matthew Treinishb12ad762014-06-19 10:18:05 -040093 self.assertFalse(checks.scenario_tests_need_service_tags(
94 'def test_fake:', './tempest/scenario/orchestration/test_fake.py',
Andrea Frittolicd368412017-08-14 21:37:56 +010095 "@utils.services('compute')"))
Matthew Treinishe2eee322014-05-02 19:58:54 -040096 self.assertTrue(checks.scenario_tests_need_service_tags(
97 'def test_fake_test:', './tempest/scenario/test_fake.py',
98 '\n'))
Matthew Treinishb12ad762014-06-19 10:18:05 -040099 self.assertTrue(checks.scenario_tests_need_service_tags(
100 'def test_fake:', './tempest/scenario/orchestration/test_fake.py',
101 "\n"))
Matthew Treinishe2eee322014-05-02 19:58:54 -0400102
103 def test_no_vi_headers(self):
104 # NOTE(mtreinish) The lines parameter is used only for finding the
105 # line location in the file. So these tests just pass a list of an
106 # arbitrary length to use for verifying the check function.
107 self.assertTrue(checks.no_vi_headers(
108 '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 1, range(250)))
109 self.assertTrue(checks.no_vi_headers(
110 '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 249, range(250)))
111 self.assertFalse(checks.no_vi_headers(
112 '# vim: tabstop=4 shiftwidth=4 softtabstop=4', 149, range(250)))
113
114 def test_service_tags_not_in_module_path(self):
115 self.assertTrue(checks.service_tags_not_in_module_path(
Andrea Frittolicd368412017-08-14 21:37:56 +0100116 "@utils.services('compute')",
117 './tempest/api/compute/fake_test.py'))
Matthew Treinishe2eee322014-05-02 19:58:54 -0400118 self.assertFalse(checks.service_tags_not_in_module_path(
Andrea Frittolicd368412017-08-14 21:37:56 +0100119 "@utils.services('compute')",
Matthew Treinishe2eee322014-05-02 19:58:54 -0400120 './tempest/scenario/compute/fake_test.py'))
121 self.assertFalse(checks.service_tags_not_in_module_path(
Andrea Frittolicd368412017-08-14 21:37:56 +0100122 "@utils.services('compute')", './tempest/api/image/fake_test.py'))
Matthew Treinish7acaba42014-05-28 09:19:03 -0400123
Ken'ichi Ohmichi80369a92015-04-06 23:41:14 +0000124 def test_no_hyphen_at_end_of_rand_name(self):
125 self.assertIsNone(checks.no_hyphen_at_end_of_rand_name(
126 'data_utils.rand_name("fake-resource")', './tempest/test_foo.py'))
127 self.assertEqual(2, len(list(checks.no_hyphen_at_end_of_rand_name(
128 'data_utils.rand_name("fake-resource-")', './tempest/test_foo.py')
129 )))
130
Ghanshyam2a180b82014-06-16 13:54:22 +0900131 def test_no_mutable_default_args(self):
132 self.assertEqual(1, len(list(checks.no_mutable_default_args(
133 " def function1(para={}):"))))
134
135 self.assertEqual(1, len(list(checks.no_mutable_default_args(
136 "def function2(para1, para2, para3=[])"))))
137
138 self.assertEqual(0, len(list(checks.no_mutable_default_args(
139 "defined = []"))))
140
141 self.assertEqual(0, len(list(checks.no_mutable_default_args(
142 "defined, undefined = [], {}"))))
John Warren3059a092015-08-31 15:34:49 -0400143
144 def test_no_testtools_skip_decorator(self):
145 self.assertEqual(1, len(list(checks.no_testtools_skip_decorator(
146 " @testtools.skip('Bug xxx')"))))
147 self.assertEqual(0, len(list(checks.no_testtools_skip_decorator(
148 " @testtools.skipUnless(CONF.something, 'msg')"))))
149 self.assertEqual(0, len(list(checks.no_testtools_skip_decorator(
150 " @testtools.skipIf(CONF.something, 'msg')"))))
Ken'ichi Ohmichi0dc97472016-03-25 15:10:08 -0700151
152 def test_dont_import_local_tempest_code_into_lib(self):
153 self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
154 "from tempest.common import waiters",
155 './tempest/common/compute.py'))))
156 self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
157 "from tempest import config",
158 './tempest/common/compute.py'))))
159 self.assertEqual(0, len(list(checks.dont_import_local_tempest_into_lib(
160 "import tempest.exception",
161 './tempest/common/compute.py'))))
162 self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
163 "from tempest.common import waiters",
164 './tempest/lib/common/compute.py'))))
165 self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
166 "from tempest import config",
167 './tempest/lib/common/compute.py'))))
168 self.assertEqual(1, len(list(checks.dont_import_local_tempest_into_lib(
169 "import tempest.exception",
170 './tempest/lib/common/compute.py'))))
Matthew Treinish59d9eaa2016-05-31 23:42:55 -0400171
172 def test_dont_use_config_in_tempest_lib(self):
173 self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
174 'from tempest import config', './tempest/common/compute.py')))
175 self.assertFalse(list(checks.dont_use_config_in_tempest_lib(
176 'from oslo_concurrency import lockutils',
177 './tempest/lib/auth.py')))
178 self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
179 'from tempest import config', './tempest/lib/auth.py')))
180 self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
181 'from oslo_config import cfg', './tempest/lib/decorators.py')))
182 self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
183 'import tempest.config', './tempest/lib/common/rest_client.py')))
junbolibc2ae862017-07-29 15:46:48 +0800184
185 def test_unsupported_exception_attribute_PY3(self):
186 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
187 "raise TestCase.failureException(e.message)"))), 1)
188 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
189 "raise TestCase.failureException(ex.message)"))), 1)
190 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
191 "raise TestCase.failureException(exc.message)"))), 1)
192 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
193 "raise TestCase.failureException(exception.message)"))), 1)
194 self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
195 "raise TestCase.failureException(ee.message)"))), 0)
Felipe Monteiro4d011af2018-07-18 00:11:48 -0400196
197 def _test_no_negatve_test_attribute_applied_to_negative_test(
198 self, filename, with_other_decorators=False,
199 with_negative_decorator=True, expected_success=True):
200 check = checks.negative_test_attribute_always_applied_to_negative_tests
201 other_decorators = [
202 "@decorators.idempotent_id(123)",
203 "@utils.requires_ext(extension='ext', service='svc')"
204 ]
205
206 if with_other_decorators:
207 # Include multiple decorators to verify that this check works with
208 # arbitrarily many decorators. These insert decorators above the
209 # @decorators.attr(type=['negative']) decorator.
210 for decorator in other_decorators:
211 self.assertIsNone(check(" %s" % decorator, filename))
212 if with_negative_decorator:
213 self.assertIsNone(
214 check("@decorators.attr(type=['negative'])", filename))
215 if with_other_decorators:
216 # Include multiple decorators to verify that this check works with
217 # arbitrarily many decorators. These insert decorators between
218 # the test and the @decorators.attr(type=['negative']) decorator.
219 for decorator in other_decorators:
220 self.assertIsNone(check(" %s" % decorator, filename))
221 final_result = check(" def test_some_negative_case", filename)
222 if expected_success:
223 self.assertIsNone(final_result)
224 else:
225 self.assertIsInstance(final_result, tuple)
226 self.assertFalse(final_result[0])
227
228 def test_no_negatve_test_attribute_applied_to_negative_test(self):
229 # Check negative filename, negative decorator passes
230 self._test_no_negatve_test_attribute_applied_to_negative_test(
231 "./tempest/api/test_something_negative.py")
232 # Check negative filename, negative decorator, other decorators passes
233 self._test_no_negatve_test_attribute_applied_to_negative_test(
234 "./tempest/api/test_something_negative.py",
235 with_other_decorators=True)
236
237 # Check non-negative filename skips check, causing pass
238 self._test_no_negatve_test_attribute_applied_to_negative_test(
239 "./tempest/api/test_something.py")
240
241 # Check negative filename, no negative decorator fails
242 self._test_no_negatve_test_attribute_applied_to_negative_test(
243 "./tempest/api/test_something_negative.py",
244 with_negative_decorator=False,
245 expected_success=False)
246 # Check negative filename, no negative decorator, other decorators
247 # fails
248 self._test_no_negatve_test_attribute_applied_to_negative_test(
249 "./tempest/api/test_something_negative.py",
250 with_other_decorators=True,
251 with_negative_decorator=False,
252 expected_success=False)