blob: 2443f659b308c4fad9ff5ca32f991a9e6482d65e [file] [log] [blame]
Werner Lewis545911f2022-07-08 13:54:57 +01001#!/usr/bin/env python3
2"""Generate test data for bignum functions.
3
4With no arguments, generate all test data. With non-option arguments,
5generate only the specified files.
Werner Lewis008d90d2022-08-23 16:07:37 +01006
7Class structure:
8
9Target classes are directly derived from test_generation.BaseTarget,
10representing a target file. These indicate where test cases will be written
11to in classes derived from the Target. Multiple Target classes must not
12represent the same target_basename.
13
14Each subclass derived from a Target can either be:
15 - A concrete class, representing a test function, which generates test cases.
16 - An abstract class containing shared methods and attributes, not associated
17 with a test function. An example is BignumOperation, which provides common
18 features used in binary bignum operations.
19
20
21Adding test generation for a function:
22
23A subclass representing the test function should be added, deriving from a
24Target class or a descendant. This subclass must set/implement the following:
25 - test_function: the function name from the associated .function file.
26 - arguments(): generation of the arguments required for the test_function.
27 - generate_function_test(): generation of the test cases for the function.
28
29Additional details and other attributes/methods are given in the documentation
30of BaseTarget in test_generation.py.
Werner Lewis545911f2022-07-08 13:54:57 +010031"""
32
33# Copyright The Mbed TLS Contributors
34# SPDX-License-Identifier: Apache-2.0
35#
36# Licensed under the Apache License, Version 2.0 (the "License"); you may
37# not use this file except in compliance with the License.
38# You may obtain a copy of the License at
39#
40# http://www.apache.org/licenses/LICENSE-2.0
41#
42# Unless required by applicable law or agreed to in writing, software
43# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
44# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
45# See the License for the specific language governing permissions and
46# limitations under the License.
47
Werner Lewis545911f2022-07-08 13:54:57 +010048import itertools
Werner Lewis545911f2022-07-08 13:54:57 +010049import sys
Werner Lewis008d90d2022-08-23 16:07:37 +010050
Werner Lewis47e37b32022-08-24 12:18:25 +010051from abc import ABCMeta, abstractmethod
Werner Lewis412c4972022-08-25 10:02:06 +010052from typing import Callable, Dict, Iterable, Iterator, List, Tuple, TypeVar, cast
Werner Lewis545911f2022-07-08 13:54:57 +010053
54import scripts_path # pylint: disable=unused-import
Werner Lewis545911f2022-07-08 13:54:57 +010055from mbedtls_dev import test_case
Werner Lewisdcad1e92022-08-24 11:30:03 +010056from mbedtls_dev import test_generation
Werner Lewis545911f2022-07-08 13:54:57 +010057
58T = TypeVar('T') #pylint: disable=invalid-name
59
Werner Lewis9509f442022-08-24 17:46:22 +010060def hex_to_int(val: str) -> int:
Werner Lewis545911f2022-07-08 13:54:57 +010061 return int(val, 16) if val else 0
62
Werner Lewis9509f442022-08-24 17:46:22 +010063def quote_str(val) -> str:
Werner Lewis545911f2022-07-08 13:54:57 +010064 return "\"{}\"".format(val)
65
66
Werner Lewis47e37b32022-08-24 12:18:25 +010067class BignumTarget(test_generation.BaseTarget, metaclass=ABCMeta):
Werner Lewisb29f59f2022-08-25 11:17:35 +010068 #pylint: disable=abstract-method
Werner Lewis545911f2022-07-08 13:54:57 +010069 """Target for bignum (mpi) test case generation."""
Werner Lewis70d3f3d2022-08-23 14:21:53 +010070 target_basename = 'test_suite_mpi.generated'
Werner Lewis545911f2022-07-08 13:54:57 +010071
72
Werner Lewis47e37b32022-08-24 12:18:25 +010073class BignumOperation(BignumTarget, metaclass=ABCMeta):
Werner Lewis008d90d2022-08-23 16:07:37 +010074 """Common features for test cases covering binary bignum operations.
75
76 This adds functionality common in binary operation tests. This includes
77 generation of case descriptions, using descriptions of values and symbols
78 to represent the operation or result.
Werner Lewis545911f2022-07-08 13:54:57 +010079
80 Attributes:
Werner Lewis008d90d2022-08-23 16:07:37 +010081 symbol: Symbol used for the operation in case description.
82 input_values: List of values to use as test case inputs. These are
83 combined to produce pairs of values.
Werner Lewis70d3f3d2022-08-23 14:21:53 +010084 input_cases: List of tuples containing pairs of test case inputs. This
Werner Lewis545911f2022-07-08 13:54:57 +010085 can be used to implement specific pairs of inputs.
86 """
Werner Lewis70d3f3d2022-08-23 14:21:53 +010087 symbol = ""
88 input_values = [
Werner Lewis545911f2022-07-08 13:54:57 +010089 "", "0", "7b", "-7b",
90 "0000000000000000123", "-0000000000000000123",
91 "1230000000000000000", "-1230000000000000000"
Werner Lewisd76c5ed2022-07-20 14:13:44 +010092 ] # type: List[str]
Werner Lewis478a4ce2022-08-24 18:03:30 +010093 input_cases = cast(List[Tuple[str, str]], []) # type: List[Tuple[str, str]]
Werner Lewis545911f2022-07-08 13:54:57 +010094
95 def __init__(self, val_l: str, val_r: str) -> None:
Werner Lewis545911f2022-07-08 13:54:57 +010096 self.arg_l = val_l
97 self.arg_r = val_r
98 self.int_l = hex_to_int(val_l)
99 self.int_r = hex_to_int(val_r)
100
Werner Lewis9509f442022-08-24 17:46:22 +0100101 def arguments(self) -> List[str]:
Werner Lewis70d3f3d2022-08-23 14:21:53 +0100102 return [quote_str(self.arg_l), quote_str(self.arg_r), self.result()]
Werner Lewis545911f2022-07-08 13:54:57 +0100103
Werner Lewis9509f442022-08-24 17:46:22 +0100104 def description(self) -> str:
Werner Lewis008d90d2022-08-23 16:07:37 +0100105 """Generate a description for the test case.
106
107 If not set, case_description uses the form A `symbol` B, where symbol
108 is used to represent the operation. Descriptions of each value are
109 generated to provide some context to the test case.
110 """
Werner Lewis70d3f3d2022-08-23 14:21:53 +0100111 if not self.case_description:
112 self.case_description = "{} {} {}".format(
113 self.value_description(self.arg_l),
114 self.symbol,
115 self.value_description(self.arg_r)
116 )
117 return super().description()
Werner Lewis545911f2022-07-08 13:54:57 +0100118
Werner Lewis008d90d2022-08-23 16:07:37 +0100119 @abstractmethod
Werner Lewis47e37b32022-08-24 12:18:25 +0100120 def result(self) -> str:
Werner Lewis008d90d2022-08-23 16:07:37 +0100121 """Get the result of the operation.
122
123 This may be calculated during initialization and stored as `_result`,
124 or calculated when the method is called.
125 """
Werner Lewisd77d33d2022-08-25 09:56:51 +0100126 raise NotImplementedError
Werner Lewis545911f2022-07-08 13:54:57 +0100127
128 @staticmethod
Werner Lewis70d3f3d2022-08-23 14:21:53 +0100129 def value_description(val) -> str:
Werner Lewis008d90d2022-08-23 16:07:37 +0100130 """Generate a description of the argument val.
131
132 This produces a simple description of the value, which are used in test
Werner Lewis47e37b32022-08-24 12:18:25 +0100133 case naming, to add context to the test cases.
Werner Lewis008d90d2022-08-23 16:07:37 +0100134 """
Werner Lewis545911f2022-07-08 13:54:57 +0100135 if val == "":
136 return "0 (null)"
137 if val == "0":
138 return "0 (1 limb)"
139
140 if val[0] == "-":
141 tmp = "negative"
142 val = val[1:]
143 else:
144 tmp = "positive"
145 if val[0] == "0":
146 tmp += " with leading zero limb"
147 elif len(val) > 10:
148 tmp = "large " + tmp
149 return tmp
150
151 @classmethod
Werner Lewis9509f442022-08-24 17:46:22 +0100152 def get_value_pairs(cls) -> Iterator[Tuple[str, str]]:
Werner Lewis008d90d2022-08-23 16:07:37 +0100153 """Generator for pairs of inputs.
154
155 Combinations are first generated from all input values, and then
156 specific cases provided.
157 """
Werner Lewis478a4ce2022-08-24 18:03:30 +0100158 yield from cast(
159 Iterator[Tuple[str, str]],
160 itertools.combinations(cls.input_values, 2)
161 )
Werner Lewis02998c42022-08-23 16:07:19 +0100162 yield from cls.input_cases
Werner Lewis545911f2022-07-08 13:54:57 +0100163
164 @classmethod
Werner Lewisc34d0372022-08-24 12:42:00 +0100165 def generate_function_tests(cls) -> Iterator[test_case.TestCase]:
166 for l_value, r_value in cls.get_value_pairs():
167 cur_op = cls(l_value, r_value)
168 yield cur_op.create_test_case()
Werner Lewis545911f2022-07-08 13:54:57 +0100169
170
171class BignumCmp(BignumOperation):
172 """Target for bignum comparison test cases."""
173 count = 0
Werner Lewis70d3f3d2022-08-23 14:21:53 +0100174 test_function = "mbedtls_mpi_cmp_mpi"
175 test_name = "MPI compare"
Werner Lewis545911f2022-07-08 13:54:57 +0100176 input_cases = [
177 ("-2", "-3"),
178 ("-2", "-2"),
179 ("2b4", "2b5"),
180 ("2b5", "2b6")
181 ]
182
Werner Lewis9509f442022-08-24 17:46:22 +0100183 def __init__(self, val_l, val_r) -> None:
Werner Lewis545911f2022-07-08 13:54:57 +0100184 super().__init__(val_l, val_r)
Werner Lewis1c2a7322022-08-24 16:37:44 +0100185 self._result = int(self.int_l > self.int_r) - int(self.int_l < self.int_r)
Werner Lewis70d3f3d2022-08-23 14:21:53 +0100186 self.symbol = ["<", "==", ">"][self._result + 1]
Werner Lewis545911f2022-07-08 13:54:57 +0100187
Werner Lewis9509f442022-08-24 17:46:22 +0100188 def result(self) -> str:
Werner Lewis545911f2022-07-08 13:54:57 +0100189 return str(self._result)
190
191
Werner Lewis423f99b2022-07-18 15:49:43 +0100192class BignumCmpAbs(BignumCmp):
Werner Lewis008d90d2022-08-23 16:07:37 +0100193 """Target for bignum comparison, absolute variant."""
Werner Lewis423f99b2022-07-18 15:49:43 +0100194 count = 0
Werner Lewis70d3f3d2022-08-23 14:21:53 +0100195 test_function = "mbedtls_mpi_cmp_abs"
196 test_name = "MPI compare (abs)"
Werner Lewis423f99b2022-07-18 15:49:43 +0100197
Werner Lewis9509f442022-08-24 17:46:22 +0100198 def __init__(self, val_l, val_r) -> None:
Werner Lewis423f99b2022-07-18 15:49:43 +0100199 super().__init__(val_l.strip("-"), val_r.strip("-"))
200
201
Werner Lewis5c1173b2022-07-18 17:22:58 +0100202class BignumAdd(BignumOperation):
203 """Target for bignum addition test cases."""
204 count = 0
Werner Lewis70d3f3d2022-08-23 14:21:53 +0100205 test_function = "mbedtls_mpi_add_mpi"
206 test_name = "MPI add"
Werner Lewis478a4ce2022-08-24 18:03:30 +0100207 input_cases = cast(
208 List[Tuple[str, str]],
209 list(itertools.combinations(
Werner Lewis412c4972022-08-25 10:02:06 +0100210 [
211 "1c67967269c6", "9cde3",
212 "-1c67967269c6", "-9cde3",
213 ], 2
Werner Lewis478a4ce2022-08-24 18:03:30 +0100214 ))
215 )
Werner Lewis5c1173b2022-07-18 17:22:58 +0100216
Werner Lewis9509f442022-08-24 17:46:22 +0100217 def __init__(self, val_l, val_r) -> None:
Werner Lewis5c1173b2022-07-18 17:22:58 +0100218 super().__init__(val_l, val_r)
Werner Lewis70d3f3d2022-08-23 14:21:53 +0100219 self.symbol = "+"
Werner Lewis5c1173b2022-07-18 17:22:58 +0100220
Werner Lewis9509f442022-08-24 17:46:22 +0100221 def result(self) -> str:
Werner Lewis5c1173b2022-07-18 17:22:58 +0100222 return quote_str(hex(self.int_l + self.int_r).replace("0x", "", 1))
223
224
Werner Lewisdcad1e92022-08-24 11:30:03 +0100225class BignumTestGenerator(test_generation.TestGenerator):
226 """Test generator subclass including bignum targets."""
Werner Lewis545911f2022-07-08 13:54:57 +0100227 TARGETS = {
Werner Lewis70d3f3d2022-08-23 14:21:53 +0100228 subclass.target_basename: subclass.generate_tests for subclass in
Werner Lewisdcad1e92022-08-24 11:30:03 +0100229 test_generation.BaseTarget.__subclasses__()
Werner Lewis412c4972022-08-25 10:02:06 +0100230 } # type: Dict[str, Callable[[], Iterable[test_case.TestCase]]]
Werner Lewis545911f2022-07-08 13:54:57 +0100231
232if __name__ == '__main__':
Werner Lewisdcad1e92022-08-24 11:30:03 +0100233 test_generation.main(sys.argv[1:], BignumTestGenerator)