blob: aff9dee8304b8e72f292538e0e436194029f7205 [file] [log] [blame]
Giampaolo Lauria1b837ce2013-05-01 11:22:07 -04001# Copyright 2013 IBM Corp.
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
Matthew Treinish662bc3c2014-04-07 17:55:39 -040015import os
Giampaolo Lauria1b837ce2013-05-01 11:22:07 -040016import re
17
Matthew Treinishaaa35952014-05-02 18:50:16 -040018import pep8
19
Giampaolo Lauria1b837ce2013-05-01 11:22:07 -040020
Matthew Treinish7d710f92014-03-15 21:29:08 -040021PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
ghanshyame4796f82016-04-13 15:49:22 +090022 'trove', 'ironic', 'savanna', 'heat', 'sahara']
Giampaolo Lauriab8424eb2013-05-23 15:56:21 -040023
Giampaolo Lauriab8424eb2013-05-23 15:56:21 -040024PYTHON_CLIENT_RE = re.compile('import (%s)client' % '|'.join(PYTHON_CLIENTS))
Matthew Treinish6ba951a2013-09-09 22:06:18 +000025TEST_DEFINITION = re.compile(r'^\s*def test.*')
Andrea Frittoli41fa16d2014-09-15 13:41:37 +010026SETUP_TEARDOWN_CLASS_DEFINITION = re.compile(r'^\s+def (setUp|tearDown)Class')
Matthew Treinish662bc3c2014-04-07 17:55:39 -040027SCENARIO_DECORATOR = re.compile(r'\s*@.*services\((.*)\)')
Masayuki Igawafcacf962014-02-19 14:00:01 +090028VI_HEADER_RE = re.compile(r"^#\s+vim?:.+")
Ken'ichi Ohmichi80369a92015-04-06 23:41:14 +000029RAND_NAME_HYPHEN_RE = re.compile(r".*rand_name\(.+[\-\_][\"\']\)")
Ghanshyam2a180b82014-06-16 13:54:22 +090030mutable_default_args = re.compile(r"^\s*def .+\((.+=\{\}|.+=\[\])")
John Warren3059a092015-08-31 15:34:49 -040031TESTTOOLS_SKIP_DECORATOR = re.compile(r'\s*@testtools\.skip\((.*)\)')
Ken'ichi Ohmichic0d96be2015-11-11 12:33:48 +000032METHOD = re.compile(r"^ def .+")
33METHOD_GET_RESOURCE = re.compile(r"^\s*def (list|show)\_.+")
Ken'ichi Ohmichib8461cb2015-11-20 08:10:51 +000034METHOD_DELETE_RESOURCE = re.compile(r"^\s*def delete_.+")
Ken'ichi Ohmichic0d96be2015-11-11 12:33:48 +000035CLASS = re.compile(r"^class .+")
Giampaolo Lauria1b837ce2013-05-01 11:22:07 -040036
37
ghanshyam50f19472014-11-26 17:04:37 +090038def import_no_clients_in_api_and_scenario_tests(physical_line, filename):
39 """Check for client imports from tempest/api & tempest/scenario tests
Giampaolo Lauria1b837ce2013-05-01 11:22:07 -040040
Giampaolo Lauriab8424eb2013-05-23 15:56:21 -040041 T102: Cannot import OpenStack python clients
42 """
Giampaolo Lauria1b837ce2013-05-01 11:22:07 -040043
ghanshyam50f19472014-11-26 17:04:37 +090044 if "tempest/api" in filename or "tempest/scenario" in filename:
Giampaolo Lauriab8424eb2013-05-23 15:56:21 -040045 res = PYTHON_CLIENT_RE.match(physical_line)
46 if res:
47 return (physical_line.find(res.group(1)),
48 ("T102: python clients import not allowed"
ghanshyam50f19472014-11-26 17:04:37 +090049 " in tempest/api/* or tempest/scenario/* tests"))
Giampaolo Lauriad50c27d2013-05-23 15:23:12 -040050
51
Matthew Treinish6ba951a2013-09-09 22:06:18 +000052def scenario_tests_need_service_tags(physical_line, filename,
53 previous_logical):
54 """Check that scenario tests have service tags
55
56 T104: Scenario tests require a services decorator
57 """
58
Matthew Treinishb12ad762014-06-19 10:18:05 -040059 if 'tempest/scenario/' in filename and '/test_' in filename:
Matthew Treinish6ba951a2013-09-09 22:06:18 +000060 if TEST_DEFINITION.match(physical_line):
61 if not SCENARIO_DECORATOR.match(previous_logical):
62 return (physical_line.find('def'),
63 "T104: Scenario tests require a service decorator")
64
65
Andrea Frittoli41fa16d2014-09-15 13:41:37 +010066def no_setup_teardown_class_for_tests(physical_line, filename):
Matthew Treinishaaa35952014-05-02 18:50:16 -040067
68 if pep8.noqa(physical_line):
69 return
70
Matthew Treinish9e26ca82016-02-23 11:43:20 -050071 if 'tempest/test.py' in filename or 'tempest/lib/' in filename:
72 return
73
74 if SETUP_TEARDOWN_CLASS_DEFINITION.match(physical_line):
75 return (physical_line.find('def'),
76 "T105: (setUp|tearDown)Class can not be used in tests")
Matthew Treinishecf212c2013-12-06 18:23:54 +000077
78
Masayuki Igawafcacf962014-02-19 14:00:01 +090079def no_vi_headers(physical_line, line_number, lines):
80 """Check for vi editor configuration in source files.
81
82 By default vi modelines can only appear in the first or
83 last 5 lines of a source file.
84
85 T106
86 """
87 # NOTE(gilliard): line_number is 1-indexed
88 if line_number <= 5 or line_number > len(lines) - 5:
89 if VI_HEADER_RE.match(physical_line):
90 return 0, "T106: Don't put vi configuration in source files"
91
92
Matthew Treinish662bc3c2014-04-07 17:55:39 -040093def service_tags_not_in_module_path(physical_line, filename):
94 """Check that a service tag isn't in the module path
95
96 A service tag should only be added if the service name isn't already in
97 the module path.
98
99 T107
100 """
101 # NOTE(mtreinish) Scenario tests always need service tags, but subdirs are
102 # created for services like heat which would cause false negatives for
103 # those tests, so just exclude the scenario tests.
104 if 'tempest/scenario' not in filename:
105 matches = SCENARIO_DECORATOR.match(physical_line)
106 if matches:
107 services = matches.group(1).split(',')
108 for service in services:
109 service_name = service.strip().strip("'")
110 modulepath = os.path.split(filename)[0]
111 if service_name in modulepath:
112 return (physical_line.find(service_name),
113 "T107: service tag should not be in path")
114
115
Ken'ichi Ohmichi80369a92015-04-06 23:41:14 +0000116def no_hyphen_at_end_of_rand_name(logical_line, filename):
117 """Check no hyphen at the end of rand_name() argument
118
119 T108
120 """
Ken'ichi Ohmichi80369a92015-04-06 23:41:14 +0000121 msg = "T108: hyphen should not be specified at the end of rand_name()"
122 if RAND_NAME_HYPHEN_RE.match(logical_line):
123 return 0, msg
124
125
Ghanshyam2a180b82014-06-16 13:54:22 +0900126def no_mutable_default_args(logical_line):
127 """Check that mutable object isn't used as default argument
128
129 N322: Method's default argument shouldn't be mutable
130 """
131 msg = "N322: Method's default argument shouldn't be mutable!"
132 if mutable_default_args.match(logical_line):
133 yield (0, msg)
134
135
John Warren3059a092015-08-31 15:34:49 -0400136def no_testtools_skip_decorator(logical_line):
137 """Check that methods do not have the testtools.skip decorator
138
139 T109
140 """
141 if TESTTOOLS_SKIP_DECORATOR.match(logical_line):
142 yield (0, "T109: Cannot use testtools.skip decorator; instead use "
Andrea Frittoli (andreaf)1370baf2016-04-29 14:26:22 -0500143 "decorators.skip_because from tempest.lib")
John Warren3059a092015-08-31 15:34:49 -0400144
145
Ken'ichi Ohmichi53346602015-11-20 07:23:54 +0000146def _common_service_clients_check(logical_line, physical_line, filename,
147 ignored_list_file=None):
Ken'ichi Ohmichi12b28e92016-04-06 10:43:51 -0700148 if not re.match('tempest/(lib/)?services/.*', filename):
Ken'ichi Ohmichi53346602015-11-20 07:23:54 +0000149 return False
150
151 if ignored_list_file is not None:
152 ignored_list = []
153 with open('tempest/hacking/' + ignored_list_file) as f:
154 for line in f:
155 ignored_list.append(line.strip())
156
157 if filename in ignored_list:
158 return False
159
160 if not METHOD.match(physical_line):
161 return False
162
163 if pep8.noqa(physical_line):
164 return False
165
166 return True
167
168
Ken'ichi Ohmichic0d96be2015-11-11 12:33:48 +0000169def get_resources_on_service_clients(logical_line, physical_line, filename,
170 line_number, lines):
171 """Check that service client names of GET should be consistent
172
173 T110
174 """
Ken'ichi Ohmichi53346602015-11-20 07:23:54 +0000175 if not _common_service_clients_check(logical_line, physical_line,
176 filename, 'ignored_list_T110.txt'):
Ken'ichi Ohmichic0d96be2015-11-11 12:33:48 +0000177 return
178
179 for line in lines[line_number:]:
180 if METHOD.match(line) or CLASS.match(line):
181 # the end of a method
182 return
183
Ken'ichi Ohmichif878e6e2016-01-13 05:10:17 +0000184 if 'self.get(' not in line and ('self.show_resource(' not in line and
185 'self.list_resources(' not in line):
Ken'ichi Ohmichic0d96be2015-11-11 12:33:48 +0000186 continue
187
188 if METHOD_GET_RESOURCE.match(logical_line):
189 return
190
191 msg = ("T110: [GET /resources] methods should be list_<resource name>s"
192 " or show_<resource name>")
193 yield (0, msg)
194
195
Ken'ichi Ohmichib8461cb2015-11-20 08:10:51 +0000196def delete_resources_on_service_clients(logical_line, physical_line, filename,
197 line_number, lines):
198 """Check that service client names of DELETE should be consistent
199
200 T111
201 """
202 if not _common_service_clients_check(logical_line, physical_line,
203 filename, 'ignored_list_T111.txt'):
204 return
205
206 for line in lines[line_number:]:
207 if METHOD.match(line) or CLASS.match(line):
208 # the end of a method
209 return
210
Ken'ichi Ohmichif878e6e2016-01-13 05:10:17 +0000211 if 'self.delete(' not in line and 'self.delete_resource(' not in line:
Ken'ichi Ohmichib8461cb2015-11-20 08:10:51 +0000212 continue
213
214 if METHOD_DELETE_RESOURCE.match(logical_line):
215 return
216
217 msg = ("T111: [DELETE /resources/<id>] methods should be "
218 "delete_<resource name>")
219 yield (0, msg)
220
221
Ken'ichi Ohmichi0dc97472016-03-25 15:10:08 -0700222def dont_import_local_tempest_into_lib(logical_line, filename):
223 """Check that tempest.lib should not import local tempest code
224
225 T112
226 """
227 if 'tempest/lib/' not in filename:
228 return
229
230 if not ('from tempest' in logical_line
231 or 'import tempest' in logical_line):
232 return
233
234 if ('from tempest.lib' in logical_line
235 or 'import tempest.lib' in logical_line):
236 return
237
238 msg = ("T112: tempest.lib should not import local tempest code to avoid "
239 "circular dependency")
240 yield (0, msg)
241
242
Ken'ichi Ohmichid079c892016-04-19 11:23:36 -0700243def use_rand_uuid_instead_of_uuid4(logical_line, filename):
244 """Check that tests use data_utils.rand_uuid() instead of uuid.uuid4()
245
246 T113
247 """
248 if 'tempest/lib/' in filename:
249 return
250
251 if 'uuid.uuid4()' not in logical_line:
252 return
253
254 msg = ("T113: Tests should use data_utils.rand_uuid()/rand_uuid_hex() "
255 "instead of uuid.uuid4()/uuid.uuid4().hex")
256 yield (0, msg)
257
258
Giampaolo Lauriad50c27d2013-05-23 15:23:12 -0400259def factory(register):
ghanshyam50f19472014-11-26 17:04:37 +0900260 register(import_no_clients_in_api_and_scenario_tests)
Matthew Treinish6ba951a2013-09-09 22:06:18 +0000261 register(scenario_tests_need_service_tags)
Andrea Frittoli41fa16d2014-09-15 13:41:37 +0100262 register(no_setup_teardown_class_for_tests)
Masayuki Igawafcacf962014-02-19 14:00:01 +0900263 register(no_vi_headers)
Matthew Treinish662bc3c2014-04-07 17:55:39 -0400264 register(service_tags_not_in_module_path)
Ken'ichi Ohmichi80369a92015-04-06 23:41:14 +0000265 register(no_hyphen_at_end_of_rand_name)
Ghanshyam2a180b82014-06-16 13:54:22 +0900266 register(no_mutable_default_args)
John Warren3059a092015-08-31 15:34:49 -0400267 register(no_testtools_skip_decorator)
Ken'ichi Ohmichic0d96be2015-11-11 12:33:48 +0000268 register(get_resources_on_service_clients)
Ken'ichi Ohmichib8461cb2015-11-20 08:10:51 +0000269 register(delete_resources_on_service_clients)
Ken'ichi Ohmichi0dc97472016-03-25 15:10:08 -0700270 register(dont_import_local_tempest_into_lib)
Ken'ichi Ohmichid079c892016-04-19 11:23:36 -0700271 register(use_rand_uuid_instead_of_uuid4)