1a8e1175bSopenharmony_ci"""Library for constructing an Mbed TLS test case.
2a8e1175bSopenharmony_ci"""
3a8e1175bSopenharmony_ci
4a8e1175bSopenharmony_ci# Copyright The Mbed TLS Contributors
5a8e1175bSopenharmony_ci# SPDX-License-Identifier: Apache-2.0
6a8e1175bSopenharmony_ci#
7a8e1175bSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); you may
8a8e1175bSopenharmony_ci# not use this file except in compliance with the License.
9a8e1175bSopenharmony_ci# You may obtain a copy of the License at
10a8e1175bSopenharmony_ci#
11a8e1175bSopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0
12a8e1175bSopenharmony_ci#
13a8e1175bSopenharmony_ci# Unless required by applicable law or agreed to in writing, software
14a8e1175bSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15a8e1175bSopenharmony_ci# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16a8e1175bSopenharmony_ci# See the License for the specific language governing permissions and
17a8e1175bSopenharmony_ci# limitations under the License.
18a8e1175bSopenharmony_ci
19a8e1175bSopenharmony_ciimport binascii
20a8e1175bSopenharmony_ciimport os
21a8e1175bSopenharmony_ciimport sys
22a8e1175bSopenharmony_cifrom typing import Iterable, List, Optional
23a8e1175bSopenharmony_ci
24a8e1175bSopenharmony_cifrom . import typing_util
25a8e1175bSopenharmony_ci
26a8e1175bSopenharmony_cidef hex_string(data: bytes) -> str:
27a8e1175bSopenharmony_ci    return '"' + binascii.hexlify(data).decode('ascii') + '"'
28a8e1175bSopenharmony_ci
29a8e1175bSopenharmony_ci
30a8e1175bSopenharmony_ciclass MissingDescription(Exception):
31a8e1175bSopenharmony_ci    pass
32a8e1175bSopenharmony_ci
33a8e1175bSopenharmony_ciclass MissingFunction(Exception):
34a8e1175bSopenharmony_ci    pass
35a8e1175bSopenharmony_ci
36a8e1175bSopenharmony_ciclass TestCase:
37a8e1175bSopenharmony_ci    """An Mbed TLS test case."""
38a8e1175bSopenharmony_ci
39a8e1175bSopenharmony_ci    def __init__(self, description: Optional[str] = None):
40a8e1175bSopenharmony_ci        self.comments = [] #type: List[str]
41a8e1175bSopenharmony_ci        self.description = description #type: Optional[str]
42a8e1175bSopenharmony_ci        self.dependencies = [] #type: List[str]
43a8e1175bSopenharmony_ci        self.function = None #type: Optional[str]
44a8e1175bSopenharmony_ci        self.arguments = [] #type: List[str]
45a8e1175bSopenharmony_ci
46a8e1175bSopenharmony_ci    def add_comment(self, *lines: str) -> None:
47a8e1175bSopenharmony_ci        self.comments += lines
48a8e1175bSopenharmony_ci
49a8e1175bSopenharmony_ci    def set_description(self, description: str) -> None:
50a8e1175bSopenharmony_ci        self.description = description
51a8e1175bSopenharmony_ci
52a8e1175bSopenharmony_ci    def set_dependencies(self, dependencies: List[str]) -> None:
53a8e1175bSopenharmony_ci        self.dependencies = dependencies
54a8e1175bSopenharmony_ci
55a8e1175bSopenharmony_ci    def set_function(self, function: str) -> None:
56a8e1175bSopenharmony_ci        self.function = function
57a8e1175bSopenharmony_ci
58a8e1175bSopenharmony_ci    def set_arguments(self, arguments: List[str]) -> None:
59a8e1175bSopenharmony_ci        self.arguments = arguments
60a8e1175bSopenharmony_ci
61a8e1175bSopenharmony_ci    def check_completeness(self) -> None:
62a8e1175bSopenharmony_ci        if self.description is None:
63a8e1175bSopenharmony_ci            raise MissingDescription
64a8e1175bSopenharmony_ci        if self.function is None:
65a8e1175bSopenharmony_ci            raise MissingFunction
66a8e1175bSopenharmony_ci
67a8e1175bSopenharmony_ci    def write(self, out: typing_util.Writable) -> None:
68a8e1175bSopenharmony_ci        """Write the .data file paragraph for this test case.
69a8e1175bSopenharmony_ci
70a8e1175bSopenharmony_ci        The output starts and ends with a single newline character. If the
71a8e1175bSopenharmony_ci        surrounding code writes lines (consisting of non-newline characters
72a8e1175bSopenharmony_ci        and a final newline), you will end up with a blank line before, but
73a8e1175bSopenharmony_ci        not after the test case.
74a8e1175bSopenharmony_ci        """
75a8e1175bSopenharmony_ci        self.check_completeness()
76a8e1175bSopenharmony_ci        assert self.description is not None # guide mypy
77a8e1175bSopenharmony_ci        assert self.function is not None # guide mypy
78a8e1175bSopenharmony_ci        out.write('\n')
79a8e1175bSopenharmony_ci        for line in self.comments:
80a8e1175bSopenharmony_ci            out.write('# ' + line + '\n')
81a8e1175bSopenharmony_ci        out.write(self.description + '\n')
82a8e1175bSopenharmony_ci        if self.dependencies:
83a8e1175bSopenharmony_ci            out.write('depends_on:' + ':'.join(self.dependencies) + '\n')
84a8e1175bSopenharmony_ci        out.write(self.function + ':' + ':'.join(self.arguments) + '\n')
85a8e1175bSopenharmony_ci
86a8e1175bSopenharmony_cidef write_data_file(filename: str,
87a8e1175bSopenharmony_ci                    test_cases: Iterable[TestCase],
88a8e1175bSopenharmony_ci                    caller: Optional[str] = None) -> None:
89a8e1175bSopenharmony_ci    """Write the test cases to the specified file.
90a8e1175bSopenharmony_ci
91a8e1175bSopenharmony_ci    If the file already exists, it is overwritten.
92a8e1175bSopenharmony_ci    """
93a8e1175bSopenharmony_ci    if caller is None:
94a8e1175bSopenharmony_ci        caller = os.path.basename(sys.argv[0])
95a8e1175bSopenharmony_ci    tempfile = filename + '.new'
96a8e1175bSopenharmony_ci    with open(tempfile, 'w') as out:
97a8e1175bSopenharmony_ci        out.write('# Automatically generated by {}. Do not edit!\n'
98a8e1175bSopenharmony_ci                  .format(caller))
99a8e1175bSopenharmony_ci        for tc in test_cases:
100a8e1175bSopenharmony_ci            tc.write(out)
101a8e1175bSopenharmony_ci        out.write('\n# End of automatically generated file.\n')
102a8e1175bSopenharmony_ci    os.replace(tempfile, filename)
103