blob: 3f405b1710b7a8d07bd2554b56e03297fac5a3af [file] [log] [blame]
Marc Koderer6ee82dc2014-02-17 10:26:29 +01001# Copyright 2014 Deutsche Telekom AG
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Marc Kodererf07f5d12014-09-01 09:47:23 +020016import copy
Marc Koderer53b01232014-08-13 15:57:30 +020017import functools
18
Marc Koderer6ee82dc2014-02-17 10:26:29 +010019import jsonschema
20
21from tempest.openstack.common import log as logging
22
23LOG = logging.getLogger(__name__)
24
25
26def _check_for_expected_result(name, schema):
27 expected_result = None
28 if "results" in schema:
29 if name in schema["results"]:
30 expected_result = schema["results"][name]
31 return expected_result
32
33
Marc Kodererf07f5d12014-09-01 09:47:23 +020034def generator_type(*args, **kwargs):
Marc Koderer6ee82dc2014-02-17 10:26:29 +010035 def wrapper(func):
36 func.types = args
Marc Kodererf07f5d12014-09-01 09:47:23 +020037 for key in kwargs:
38 setattr(func, key, kwargs[key])
Marc Koderer6ee82dc2014-02-17 10:26:29 +010039 return func
40 return wrapper
41
42
43def simple_generator(fn):
44 """
45 Decorator for simple generators that return one value
46 """
Marc Koderer53b01232014-08-13 15:57:30 +020047 @functools.wraps(fn)
Marc Koderer6ee82dc2014-02-17 10:26:29 +010048 def wrapped(self, schema):
49 result = fn(self, schema)
50 if result is not None:
51 expected_result = _check_for_expected_result(fn.__name__, schema)
52 return (fn.__name__, result, expected_result)
53 return
54 return wrapped
55
56
57class BasicGeneratorSet(object):
58 _instance = None
59
60 schema = {
61 "type": "object",
62 "properties": {
63 "name": {"type": "string"},
64 "http-method": {
65 "enum": ["GET", "PUT", "HEAD",
66 "POST", "PATCH", "DELETE", 'COPY']
67 },
Marc Kodererf857fda2014-03-05 15:58:00 +010068 "admin_client": {"type": "boolean"},
Marc Koderer6ee82dc2014-02-17 10:26:29 +010069 "url": {"type": "string"},
Marc Kodererf857fda2014-03-05 15:58:00 +010070 "default_result_code": {"type": "integer"},
Marc Kodererfafcc4f2014-03-17 13:20:40 +010071 "json-schema": {},
Marc Koderer6ee82dc2014-02-17 10:26:29 +010072 "resources": {
73 "type": "array",
74 "items": {
75 "oneOf": [
76 {"type": "string"},
77 {
78 "type": "object",
79 "properties": {
80 "name": {"type": "string"},
81 "expected_result": {"type": "integer"}
82 }
83 }
84 ]
85 }
86 },
87 "results": {
88 "type": "object",
89 "properties": {}
90 }
91 },
92 "required": ["name", "http-method", "url"],
93 "additionalProperties": False,
94 }
95
Marc Koderer6ee82dc2014-02-17 10:26:29 +010096 def __init__(self):
97 self.types_dict = {}
98 for m in dir(self):
99 if callable(getattr(self, m)) and not'__' in m:
100 method = getattr(self, m)
101 if hasattr(method, "types"):
102 for type in method.types:
103 if type not in self.types_dict:
104 self.types_dict[type] = []
105 self.types_dict[type].append(method)
106
107 def validate_schema(self, schema):
Marc Kodererfafcc4f2014-03-17 13:20:40 +0100108 if "json-schema" in schema:
109 jsonschema.Draft4Validator.check_schema(schema['json-schema'])
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100110 jsonschema.validate(schema, self.schema)
111
Marc Kodererf07f5d12014-09-01 09:47:23 +0200112 def generate_scenarios(self, schema, path=None):
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100113 """
Marc Kodererf07f5d12014-09-01 09:47:23 +0200114 Generates the scenario (all possible test cases) out of the given
115 schema.
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100116
Marc Kodererf07f5d12014-09-01 09:47:23 +0200117 :param schema: a dict style schema (see ``BasicGeneratorSet.schema``)
118 :param path: the schema path if the given schema is a subschema
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100119 """
Marc Kodererf07f5d12014-09-01 09:47:23 +0200120 schema_type = schema['type']
121 scenarios = []
122
123 if schema_type == 'object':
124 properties = schema["properties"]
125 for attribute, definition in properties.iteritems():
126 current_path = copy.copy(path)
127 if path is not None:
128 current_path.append(attribute)
129 else:
130 current_path = [attribute]
131 scenarios.extend(
132 self.generate_scenarios(definition, current_path))
133 elif isinstance(schema_type, list):
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100134 if "integer" in schema_type:
135 schema_type = "integer"
136 else:
137 raise Exception("non-integer list types not supported")
Marc Koderer6ee82dc2014-02-17 10:26:29 +0100138 for generator in self.types_dict[schema_type]:
Marc Kodererf07f5d12014-09-01 09:47:23 +0200139 if hasattr(generator, "needed_property"):
140 prop = generator.needed_property
141 if (prop not in schema or
142 schema[prop] is None or
143 schema[prop] is False):
144 continue
145
146 name = generator.__name__
Marc Koderere16bef02014-09-30 11:16:55 +0200147 if ("exclude_tests" in schema and
148 name in schema["exclude_tests"]):
149 continue
Marc Kodererf07f5d12014-09-01 09:47:23 +0200150 if path is not None:
151 name = "%s_%s" % ("_".join(path), name)
152 scenarios.append({
153 "_negtest_name": name,
154 "_negtest_generator": generator,
155 "_negtest_schema": schema,
156 "_negtest_path": path})
157 return scenarios
158
159 def generate_payload(self, test, schema):
160 """
161 Generates one jsonschema out of the given test. It's mandatory to use
162 generate_scenarios before to register all needed variables to the test.
163
164 :param test: A test object (scenario) with all _negtest variables on it
165 :param schema: schema for the test
166 """
167 generator = test._negtest_generator
168 ret = generator(test._negtest_schema)
169 path = copy.copy(test._negtest_path)
170 expected_result = None
171
172 if ret is not None:
173 generator_result = generator(test._negtest_schema)
174 invalid_snippet = generator_result[1]
175 expected_result = generator_result[2]
176 element = path.pop()
177 if len(path) > 0:
178 schema_snip = reduce(dict.get, path, schema)
179 schema_snip[element] = invalid_snippet
180 else:
181 schema[element] = invalid_snippet
182 return expected_result