1a8e1175bSopenharmony_ci#!/usr/bin/env python3
2a8e1175bSopenharmony_ci"""Generate test data for bignum functions.
3a8e1175bSopenharmony_ci
4a8e1175bSopenharmony_ciWith no arguments, generate all test data. With non-option arguments,
5a8e1175bSopenharmony_cigenerate only the specified files.
6a8e1175bSopenharmony_ci
7a8e1175bSopenharmony_ciClass structure:
8a8e1175bSopenharmony_ci
9a8e1175bSopenharmony_ciChild classes of test_data_generation.BaseTarget (file targets) represent an output
10a8e1175bSopenharmony_cifile. These indicate where test cases will be written to, for all subclasses of
11a8e1175bSopenharmony_cithis target. Multiple file targets should not reuse a `target_basename`.
12a8e1175bSopenharmony_ci
13a8e1175bSopenharmony_ciEach subclass derived from a file target can either be:
14a8e1175bSopenharmony_ci  - A concrete class, representing a test function, which generates test cases.
15a8e1175bSopenharmony_ci  - An abstract class containing shared methods and attributes, not associated
16a8e1175bSopenharmony_ci        with a test function. An example is BignumOperation, which provides
17a8e1175bSopenharmony_ci        common features used for bignum binary operations.
18a8e1175bSopenharmony_ci
19a8e1175bSopenharmony_ciBoth concrete and abstract subclasses can be derived from, to implement
20a8e1175bSopenharmony_ciadditional test cases (see BignumCmp and BignumCmpAbs for examples of deriving
21a8e1175bSopenharmony_cifrom abstract and concrete classes).
22a8e1175bSopenharmony_ci
23a8e1175bSopenharmony_ci
24a8e1175bSopenharmony_ciAdding test case generation for a function:
25a8e1175bSopenharmony_ci
26a8e1175bSopenharmony_ciA subclass representing the test function should be added, deriving from a
27a8e1175bSopenharmony_cifile target such as BignumTarget. This test class must set/implement the
28a8e1175bSopenharmony_cifollowing:
29a8e1175bSopenharmony_ci  - test_function: the function name from the associated .function file.
30a8e1175bSopenharmony_ci  - test_name: a descriptive name or brief summary to refer to the test
31a8e1175bSopenharmony_ci        function.
32a8e1175bSopenharmony_ci  - arguments(): a method to generate the list of arguments required for the
33a8e1175bSopenharmony_ci        test_function.
34a8e1175bSopenharmony_ci  - generate_function_tests(): a method to generate TestCases for the function.
35a8e1175bSopenharmony_ci        This should create instances of the class with required input data, and
36a8e1175bSopenharmony_ci        call `.create_test_case()` to yield the TestCase.
37a8e1175bSopenharmony_ci
38a8e1175bSopenharmony_ciAdditional details and other attributes/methods are given in the documentation
39a8e1175bSopenharmony_ciof BaseTarget in test_data_generation.py.
40a8e1175bSopenharmony_ci"""
41a8e1175bSopenharmony_ci
42a8e1175bSopenharmony_ci# Copyright The Mbed TLS Contributors
43a8e1175bSopenharmony_ci# SPDX-License-Identifier: Apache-2.0
44a8e1175bSopenharmony_ci#
45a8e1175bSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); you may
46a8e1175bSopenharmony_ci# not use this file except in compliance with the License.
47a8e1175bSopenharmony_ci# You may obtain a copy of the License at
48a8e1175bSopenharmony_ci#
49a8e1175bSopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0
50a8e1175bSopenharmony_ci#
51a8e1175bSopenharmony_ci# Unless required by applicable law or agreed to in writing, software
52a8e1175bSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
53a8e1175bSopenharmony_ci# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
54a8e1175bSopenharmony_ci# See the License for the specific language governing permissions and
55a8e1175bSopenharmony_ci# limitations under the License.
56a8e1175bSopenharmony_ci
57a8e1175bSopenharmony_ciimport sys
58a8e1175bSopenharmony_ci
59a8e1175bSopenharmony_cifrom abc import ABCMeta
60a8e1175bSopenharmony_cifrom typing import List
61a8e1175bSopenharmony_ci
62a8e1175bSopenharmony_ciimport scripts_path # pylint: disable=unused-import
63a8e1175bSopenharmony_cifrom mbedtls_dev import test_data_generation
64a8e1175bSopenharmony_cifrom mbedtls_dev import bignum_common
65a8e1175bSopenharmony_ci# Import modules containing additional test classes
66a8e1175bSopenharmony_ci# Test function classes in these modules will be registered by
67a8e1175bSopenharmony_ci# the framework
68a8e1175bSopenharmony_cifrom mbedtls_dev import bignum_core, bignum_mod_raw, bignum_mod # pylint: disable=unused-import
69a8e1175bSopenharmony_ci
70a8e1175bSopenharmony_ciclass BignumTarget(test_data_generation.BaseTarget):
71a8e1175bSopenharmony_ci    #pylint: disable=too-few-public-methods
72a8e1175bSopenharmony_ci    """Target for bignum (legacy) test case generation."""
73a8e1175bSopenharmony_ci    target_basename = 'test_suite_bignum.generated'
74a8e1175bSopenharmony_ci
75a8e1175bSopenharmony_ci
76a8e1175bSopenharmony_ciclass BignumOperation(bignum_common.OperationCommon, BignumTarget,
77a8e1175bSopenharmony_ci                      metaclass=ABCMeta):
78a8e1175bSopenharmony_ci    #pylint: disable=abstract-method
79a8e1175bSopenharmony_ci    """Common features for bignum operations in legacy tests."""
80a8e1175bSopenharmony_ci    unique_combinations_only = True
81a8e1175bSopenharmony_ci    input_values = [
82a8e1175bSopenharmony_ci        "", "0", "-", "-0",
83a8e1175bSopenharmony_ci        "7b", "-7b",
84a8e1175bSopenharmony_ci        "0000000000000000123", "-0000000000000000123",
85a8e1175bSopenharmony_ci        "1230000000000000000", "-1230000000000000000"
86a8e1175bSopenharmony_ci    ]
87a8e1175bSopenharmony_ci
88a8e1175bSopenharmony_ci    def description_suffix(self) -> str:
89a8e1175bSopenharmony_ci        #pylint: disable=no-self-use # derived classes need self
90a8e1175bSopenharmony_ci        """Text to add at the end of the test case description."""
91a8e1175bSopenharmony_ci        return ""
92a8e1175bSopenharmony_ci
93a8e1175bSopenharmony_ci    def description(self) -> str:
94a8e1175bSopenharmony_ci        """Generate a description for the test case.
95a8e1175bSopenharmony_ci
96a8e1175bSopenharmony_ci        If not set, case_description uses the form A `symbol` B, where symbol
97a8e1175bSopenharmony_ci        is used to represent the operation. Descriptions of each value are
98a8e1175bSopenharmony_ci        generated to provide some context to the test case.
99a8e1175bSopenharmony_ci        """
100a8e1175bSopenharmony_ci        if not self.case_description:
101a8e1175bSopenharmony_ci            self.case_description = "{} {} {}".format(
102a8e1175bSopenharmony_ci                self.value_description(self.arg_a),
103a8e1175bSopenharmony_ci                self.symbol,
104a8e1175bSopenharmony_ci                self.value_description(self.arg_b)
105a8e1175bSopenharmony_ci            )
106a8e1175bSopenharmony_ci            description_suffix = self.description_suffix()
107a8e1175bSopenharmony_ci            if description_suffix:
108a8e1175bSopenharmony_ci                self.case_description += " " + description_suffix
109a8e1175bSopenharmony_ci        return super().description()
110a8e1175bSopenharmony_ci
111a8e1175bSopenharmony_ci    @staticmethod
112a8e1175bSopenharmony_ci    def value_description(val) -> str:
113a8e1175bSopenharmony_ci        """Generate a description of the argument val.
114a8e1175bSopenharmony_ci
115a8e1175bSopenharmony_ci        This produces a simple description of the value, which is used in test
116a8e1175bSopenharmony_ci        case naming to add context.
117a8e1175bSopenharmony_ci        """
118a8e1175bSopenharmony_ci        if val == "":
119a8e1175bSopenharmony_ci            return "0 (null)"
120a8e1175bSopenharmony_ci        if val == "-":
121a8e1175bSopenharmony_ci            return "negative 0 (null)"
122a8e1175bSopenharmony_ci        if val == "0":
123a8e1175bSopenharmony_ci            return "0 (1 limb)"
124a8e1175bSopenharmony_ci
125a8e1175bSopenharmony_ci        if val[0] == "-":
126a8e1175bSopenharmony_ci            tmp = "negative"
127a8e1175bSopenharmony_ci            val = val[1:]
128a8e1175bSopenharmony_ci        else:
129a8e1175bSopenharmony_ci            tmp = "positive"
130a8e1175bSopenharmony_ci        if val[0] == "0":
131a8e1175bSopenharmony_ci            tmp += " with leading zero limb"
132a8e1175bSopenharmony_ci        elif len(val) > 10:
133a8e1175bSopenharmony_ci            tmp = "large " + tmp
134a8e1175bSopenharmony_ci        return tmp
135a8e1175bSopenharmony_ci
136a8e1175bSopenharmony_ci
137a8e1175bSopenharmony_ciclass BignumCmp(BignumOperation):
138a8e1175bSopenharmony_ci    """Test cases for bignum value comparison."""
139a8e1175bSopenharmony_ci    count = 0
140a8e1175bSopenharmony_ci    test_function = "mpi_cmp_mpi"
141a8e1175bSopenharmony_ci    test_name = "MPI compare"
142a8e1175bSopenharmony_ci    input_cases = [
143a8e1175bSopenharmony_ci        ("-2", "-3"),
144a8e1175bSopenharmony_ci        ("-2", "-2"),
145a8e1175bSopenharmony_ci        ("2b4", "2b5"),
146a8e1175bSopenharmony_ci        ("2b5", "2b6")
147a8e1175bSopenharmony_ci        ]
148a8e1175bSopenharmony_ci
149a8e1175bSopenharmony_ci    def __init__(self, val_a, val_b) -> None:
150a8e1175bSopenharmony_ci        super().__init__(val_a, val_b)
151a8e1175bSopenharmony_ci        self._result = int(self.int_a > self.int_b) - int(self.int_a < self.int_b)
152a8e1175bSopenharmony_ci        self.symbol = ["<", "==", ">"][self._result + 1]
153a8e1175bSopenharmony_ci
154a8e1175bSopenharmony_ci    def result(self) -> List[str]:
155a8e1175bSopenharmony_ci        return [str(self._result)]
156a8e1175bSopenharmony_ci
157a8e1175bSopenharmony_ci
158a8e1175bSopenharmony_ciclass BignumCmpAbs(BignumCmp):
159a8e1175bSopenharmony_ci    """Test cases for absolute bignum value comparison."""
160a8e1175bSopenharmony_ci    count = 0
161a8e1175bSopenharmony_ci    test_function = "mpi_cmp_abs"
162a8e1175bSopenharmony_ci    test_name = "MPI compare (abs)"
163a8e1175bSopenharmony_ci
164a8e1175bSopenharmony_ci    def __init__(self, val_a, val_b) -> None:
165a8e1175bSopenharmony_ci        super().__init__(val_a.strip("-"), val_b.strip("-"))
166a8e1175bSopenharmony_ci
167a8e1175bSopenharmony_ci
168a8e1175bSopenharmony_ciclass BignumAdd(BignumOperation):
169a8e1175bSopenharmony_ci    """Test cases for bignum value addition."""
170a8e1175bSopenharmony_ci    count = 0
171a8e1175bSopenharmony_ci    symbol = "+"
172a8e1175bSopenharmony_ci    test_function = "mpi_add_mpi"
173a8e1175bSopenharmony_ci    test_name = "MPI add"
174a8e1175bSopenharmony_ci    input_cases = bignum_common.combination_pairs(
175a8e1175bSopenharmony_ci        [
176a8e1175bSopenharmony_ci            "1c67967269c6", "9cde3",
177a8e1175bSopenharmony_ci            "-1c67967269c6", "-9cde3",
178a8e1175bSopenharmony_ci        ]
179a8e1175bSopenharmony_ci    )
180a8e1175bSopenharmony_ci
181a8e1175bSopenharmony_ci    def __init__(self, val_a: str, val_b: str) -> None:
182a8e1175bSopenharmony_ci        super().__init__(val_a, val_b)
183a8e1175bSopenharmony_ci        self._result = self.int_a + self.int_b
184a8e1175bSopenharmony_ci
185a8e1175bSopenharmony_ci    def description_suffix(self) -> str:
186a8e1175bSopenharmony_ci        if (self.int_a >= 0 and self.int_b >= 0):
187a8e1175bSopenharmony_ci            return "" # obviously positive result or 0
188a8e1175bSopenharmony_ci        if (self.int_a <= 0 and self.int_b <= 0):
189a8e1175bSopenharmony_ci            return "" # obviously negative result or 0
190a8e1175bSopenharmony_ci        # The sign of the result is not obvious, so indicate it
191a8e1175bSopenharmony_ci        return ", result{}0".format('>' if self._result > 0 else
192a8e1175bSopenharmony_ci                                    '<' if self._result < 0 else '=')
193a8e1175bSopenharmony_ci
194a8e1175bSopenharmony_ci    def result(self) -> List[str]:
195a8e1175bSopenharmony_ci        return [bignum_common.quote_str("{:x}".format(self._result))]
196a8e1175bSopenharmony_ci
197a8e1175bSopenharmony_ciif __name__ == '__main__':
198a8e1175bSopenharmony_ci    # Use the section of the docstring relevant to the CLI as description
199a8e1175bSopenharmony_ci    test_data_generation.main(sys.argv[1:], "\n".join(__doc__.splitlines()[:4]))
200