1#
2# Copyright (C) 2016 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain 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,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17import logging
18import sys
19
20from vts.proto import ComponentSpecificationMessage_pb2 as CompSpecMsg
21
22
23def PyValue2PbEnum(message, pb_spec, py_value):
24    """Converts Python value to VTS VariableSecificationMessage (Enum).
25
26    Args:
27        message: VariableSpecificationMessage is the current and result
28                 value message.
29        pb_spec: VariableSpecificationMessage which captures the
30                 specification of a target attribute.
31        py_value: Python value provided by a test case.
32
33    Returns:
34        Converted VariableSpecificationMessage if found, None otherwise
35    """
36    if pb_spec.name:
37        message.name = pb_spec.name
38    message.type = CompSpecMsg.TYPE_ENUM
39    # Look for the enum definition and retrieve the scalar type.
40    scalar_type = pb_spec.enum_value.scalar_type
41    if scalar_type != "":
42        # If the scalar type definition is found, set it and return.
43        setattr(message.scalar_value, scalar_type, py_value)
44        return
45    # Use default scalar_type int32_t for enum definition if the definition
46    # is not found.
47    setattr(message.scalar_value, "int32_t", py_value)
48
49
50def PyValue2PbScalar(message, pb_spec, py_value):
51    """Converts Python value to VTS VariableSecificationMessage (Scalar).
52
53    Args:
54        message: VariableSpecificationMessage is the current and result
55                 value message.
56        pb_spec: VariableSpecificationMessage which captures the
57                 specification of a target attribute.
58        py_value: Python value provided by a test case.
59
60    Returns:
61        Converted VariableSpecificationMessage if found, None otherwise
62    """
63    if pb_spec.name:
64        message.name = pb_spec.name
65    message.type = CompSpecMsg.TYPE_SCALAR
66    message.scalar_type = pb_spec.scalar_type
67    setattr(message.scalar_value, pb_spec.scalar_type, py_value)
68
69
70def PyString2PbString(message, pb_spec, py_value):
71    """Converts Python string to VTS VariableSecificationMessage (String).
72
73    Args:
74        message: VariableSpecificationMessage is the current and result
75                 value message.
76        pb_spec: VariableSpecificationMessage which captures the
77                 specification of a target attribute.
78        py_value: Python value provided by a test case.
79
80    Returns:
81        Converted VariableSpecificationMessage if found, None otherwise
82    """
83    if pb_spec.name:
84        message.name = pb_spec.name
85    message.type = CompSpecMsg.TYPE_STRING
86    message.string_value.message = py_value
87    message.string_value.length = len(py_value)
88
89
90def PyList2PbVector(message, pb_spec, py_value):
91    """Converts Python list value to VTS VariableSecificationMessage (Vector).
92
93    Args:
94        message: VariableSpecificationMessage is the current and result
95                 value message.
96        pb_spec: VariableSpecificationMessage which captures the
97                 specification of a target attribute.
98        py_value: Python value provided by a test case.
99
100    Returns:
101        Converted VariableSpecificationMessage if found, None otherwise
102    """
103    if pb_spec.name:
104        message.name = pb_spec.name
105    message.type = CompSpecMsg.TYPE_VECTOR
106    if len(py_value) == 0:
107        return message
108
109    vector_spec = pb_spec.vector_value[0]
110    for curr_value in py_value:
111        new_vector_message = message.vector_value.add()
112        new_vector_message.CopyFrom(Convert(vector_spec, curr_value))
113    message.vector_size = len(py_value)
114    return message
115
116
117def FindSubStructType(pb_spec, sub_struct_name):
118    """Finds a specific sub_struct type.
119
120    Args:
121        pb_spec: VariableSpecificationMessage which captures the
122                 specification of a target attribute.
123        sub_struct_name: string, the name of a sub struct to look up.
124
125    Returns:
126        VariableSpecificationMessage if found or None otherwise.
127    """
128    for sub_struct in pb_spec.sub_struct:
129        if sub_struct.name == sub_struct_name:
130            return sub_struct
131    return None
132
133
134def FindSubUnionType(pb_spec, sub_union_name):
135    """Finds a specific sub_union type.
136
137    Args:
138        pb_spec: VariableSpecificationMessage which captures the
139                 specification of a target attribute.
140        sub_union_name: string, the name of a sub union to look up.
141
142    Returns:
143        VariableSpecificationMessage if found or None otherwise.
144    """
145    for sub_union in pb_spec.sub_union:
146        if sub_union.name == sub_union_name:
147            return sub_union
148    return None
149
150
151def PyDict2PbStruct(message, pb_spec, py_value):
152    """Converts Python dict to VTS VariableSecificationMessage (struct).
153
154    Args:
155        pb_spec: VariableSpecificationMessage which captures the
156                 specification of a target attribute.
157        py_value: A dictionary that represents a struct.
158
159    Returns:
160        Converted VariableSpecificationMessage if found, None otherwise
161    """
162    if pb_spec.name:
163        message.name = pb_spec.name
164    message.type = CompSpecMsg.TYPE_STRUCT
165    provided_attrs = set(py_value.keys())
166    for attr in pb_spec.struct_value:
167        if attr.name in py_value:
168            provided_attrs.remove(attr.name)
169            curr_value = py_value[attr.name]
170            attr_msg = message.struct_value.add()
171            if attr.type == CompSpecMsg.TYPE_ENUM:
172                PyValue2PbEnum(attr_msg, attr, curr_value)
173            elif attr.type == CompSpecMsg.TYPE_SCALAR:
174                PyValue2PbScalar(attr_msg, attr, curr_value)
175            elif attr.type == CompSpecMsg.TYPE_STRING:
176                PyString2PbString(attr_msg, attr, curr_value)
177            elif attr.type == CompSpecMsg.TYPE_VECTOR:
178                PyList2PbVector(attr_msg, attr, curr_value)
179            elif attr.type == CompSpecMsg.TYPE_STRUCT:
180                sub_attr = FindSubStructType(pb_spec, attr.predefined_type)
181                if sub_attr:
182                    PyDict2PbStruct(attr_msg, sub_attr, curr_value)
183                else:
184                    logging.error("PyDict2PbStruct: substruct not found.")
185                    return None
186            elif attr.type == CompSpecMsg.TYPE_UNION:
187                sub_attr = FindSubStructType(pb_spec, attr.predefined_type)
188                if sub_attr:
189                    PyDict2PbUnion(attr_msg, sub_attr, curr_value)
190                else:
191                    logging.error("PyDict2PbStruct: subunion not found.")
192                    return None
193            else:
194                logging.error("PyDict2PbStruct: unsupported type %s",
195                              attr.type)
196                return None
197        else:
198            # TODO: instead crash the test, consider to generate default value
199            # in case not provided in the py_value.
200            logging.error("PyDict2PbStruct: attr %s not provided", attr.name)
201            return None
202    if len(provided_attrs) > 0:
203        logging.error("PyDict2PbStruct: provided dictionary included elements" +
204                      " not part of the type being converted to: %s",
205                      provided_attrs)
206        return None
207    return message
208
209
210def PyDict2PbUnion(message, pb_spec, py_value):
211    """Converts Python dict to VTS VariableSecificationMessage (union).
212
213    Args:
214        pb_spec: VariableSpecificationMessage which captures the
215                 specification of a target attribute.
216        py_value: A dictionary that represents a struct.
217
218    Returns:
219        Converted VariableSpecificationMessage if found, None otherwise
220    """
221    if len(py_value) > 1:
222        logging.error("PyDict2PbUnion: Union only allows specifying " +
223                      "at most one field. Current Python dictionary " +
224                      "has size %d", len(py_value))
225        return None
226
227    if pb_spec.name:
228        message.name = pb_spec.name
229    message.type = CompSpecMsg.TYPE_UNION
230    provided_attrs = set(py_value.keys())
231    for attr in pb_spec.union_value:
232        # Since it is a union type, we stop after finding one field name
233        # that matches, and shouldn't throw an error when name is not found.
234        if attr.name in py_value:
235            provided_attrs.remove(attr.name)
236            curr_value = py_value[attr.name]
237            attr_msg = message.union_value.add()
238            if attr.type == CompSpecMsg.TYPE_ENUM:
239                PyValue2PbEnum(attr_msg, attr, curr_value)
240            elif attr.type == CompSpecMsg.TYPE_SCALAR:
241                PyValue2PbScalar(attr_msg, attr, curr_value)
242            elif attr.type == CompSpecMsg.TYPE_STRING:
243                PyString2PbString(attr_msg, attr, curr_value)
244            elif attr.type == CompSpecMsg.TYPE_VECTOR:
245                PyList2PbVector(attr_msg, attr, curr_value)
246            elif attr.type == CompSpecMsg.TYPE_STRUCT:
247                # TODO: is a nested struct in union stored in sub_union field.
248                sub_attr = FindSubUnionType(pb_spec, attr.predefined_type)
249                if sub_attr:
250                    PyDict2PbStruct(attr_msg, sub_attr, curr_value)
251                else:
252                    logging.error("PyDict2PbStruct: substruct not found.")
253                    return None
254            elif attr.type == CompSpecMsg.TYPE_UNION:
255                sub_attr = FindSubUnionType(pb_spec, attr.predefined_type)
256                if sub_attr:
257                    PyDict2PbUnion(attr_msg, sub_attr, curr_value)
258                else:
259                    logging.error("PyDict2PbUnion: subunion not found.")
260                    return None
261            else:
262                logging.error("PyDict2PbStruct: unsupported type %s",
263                              attr.type)
264                return None
265        else:
266            # Add a field, where name field is initialized as an empty string.
267            # In generated driver implementation, driver knows this field is
268            # not used, and skip reading it.
269            message.union_value.add()
270
271    if len(provided_attrs) > 0:
272        logging.error("PyDict2PbUnion: specified field is not in the union " +
273                      "definition for union type %s", provided_attrs)
274        return None
275    return message
276
277
278def Convert(pb_spec, py_value):
279    """Converts Python native data structure to VTS VariableSecificationMessage.
280
281    Args:
282        pb_spec: VariableSpecificationMessage which captures the
283                 specification of a target attribute.
284        py_value: Python value provided by a test case.
285
286    Returns:
287        Converted VariableSpecificationMessage if found, None otherwise
288    """
289    if not pb_spec:
290        logging.error("py2pb.Convert: ProtoBuf spec is None", pb_spec)
291        return None
292
293    message = CompSpecMsg.VariableSpecificationMessage()
294    message.name = pb_spec.name
295
296    if isinstance(py_value, CompSpecMsg.VariableSpecificationMessage):
297        message.CopyFrom(py_value)
298    elif pb_spec.type == CompSpecMsg.TYPE_STRUCT:
299        PyDict2PbStruct(message, pb_spec, py_value)
300    elif pb_spec.type == CompSpecMsg.TYPE_UNION:
301        PyDict2PbUnion(message, pb_spec, py_value)
302    elif pb_spec.type == CompSpecMsg.TYPE_ENUM:
303        PyValue2PbEnum(message, pb_spec, py_value)
304    elif pb_spec.type == CompSpecMsg.TYPE_SCALAR:
305        PyValue2PbScalar(message, pb_spec, py_value)
306    elif pb_spec.type == CompSpecMsg.TYPE_STRING:
307        PyString2PbString(message, pb_spec, py_value)
308    elif pb_spec.type == CompSpecMsg.TYPE_VECTOR:
309        PyList2PbVector(message, pb_spec, py_value)
310    else:
311        logging.error("py2pb.Convert: unsupported type %s",
312                      pb_spec.type)
313        return None
314
315    return message
316