1#!/usr/bin/env python3
2#
3#   Copyright 2019 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16import functools
17import inspect
18
19from mobly import asserts
20
21from acts.test_decorators import test_info
22
23
24def _fail_decorator(msg):
25
26    def fail_decorator(func):
27
28        @functools.wraps(func)
29        def fail(*args, **kwargs):
30            asserts.fail(msg)
31
32        return fail
33
34    return fail_decorator
35
36
37def metadata(_do_not_use=None, pts_test_id=None, pts_test_name=None):
38    """
39    Record a piece of test metadata in the Extra section of the test Record in
40    the test summary file. The metadata will come with a timestamp, but there
41    is no guarantee on the order of when the metadata will be written
42
43    Note:
44    - Metadata is recorded per test case as key-value pairs.
45    - Metadata is only guaranteed to be written when the test result is PASS,
46      FAIL or SKIPPED. When there are test infrastructural errors, metadata
47      might not be written successfully
48    :param _do_not_use: a positional argument with default value. This argument
49                        is to ensure that @metadata(key=value) is used in a
50                        functional form instead of @metadata or @metadata(a)
51    :param pts_test_id: A fully qualified PTS test ID such as
52                        L2CAP/COS/IEX/BV-01-C
53    :param pts_test_name: A human readable test name such as
54                          "Request Connection" for the above example
55    :return: decorated test case function object
56    """
57    if _do_not_use is not None:
58
59        def fail(*args, **kwargs):
60            asserts.fail("@metadata must be used in functional form such " "as @metadta(key=value)")
61
62        return fail
63
64    # Create a dictionary of optional parameters
65    values = locals()
66    args = {arg: values[arg] for arg in inspect.getfullargspec(metadata).args}
67    del args["_do_not_use"]
68
69    # Check if at least one optional parameter is valid
70    if not any(args.values()):
71        return _fail_decorator("at least one optional argument should be valid")
72
73    # Validate pts_test_id and pts_test_name
74    if any((pts_test_id, pts_test_name)) and \
75            not all((pts_test_id, pts_test_name)):
76        return _fail_decorator("pts_test_id and pts_test_name must both " "be valid if one of them is valid")
77
78    return test_info(**args)
79