blob: 7a8ebd1d8a51e640ff6bf016736cdcd05302b434 [file] [log] [blame]
Werner Lewis8b2df742022-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 Lewis169034a2022-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 Lewis8b2df742022-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 Lewis8b2df742022-07-08 13:54:57 +010048import itertools
Werner Lewis8b2df742022-07-08 13:54:57 +010049import sys
Werner Lewis169034a2022-08-23 16:07:37 +010050
51from abc import abstractmethod
Werner Lewisfbb75e32022-08-24 11:30:03 +010052from typing import Callable, Dict, Iterator, List, Optional, Tuple, TypeVar
Werner Lewis8b2df742022-07-08 13:54:57 +010053
54import scripts_path # pylint: disable=unused-import
Werner Lewis8b2df742022-07-08 13:54:57 +010055from mbedtls_dev import test_case
Werner Lewisfbb75e32022-08-24 11:30:03 +010056from mbedtls_dev import test_generation
Werner Lewis8b2df742022-07-08 13:54:57 +010057
58T = TypeVar('T') #pylint: disable=invalid-name
59
60def hex_to_int(val):
61 return int(val, 16) if val else 0
62
63def quote_str(val):
64 return "\"{}\"".format(val)
65
66
Werner Lewisfbb75e32022-08-24 11:30:03 +010067class BignumTarget(test_generation.BaseTarget):
Werner Lewis8b2df742022-07-08 13:54:57 +010068 """Target for bignum (mpi) test case generation."""
Werner Lewis55e638c2022-08-23 14:21:53 +010069 target_basename = 'test_suite_mpi.generated'
Werner Lewis8b2df742022-07-08 13:54:57 +010070
71
72class BignumOperation(BignumTarget):
Werner Lewis169034a2022-08-23 16:07:37 +010073 """Common features for test cases covering binary bignum operations.
74
75 This adds functionality common in binary operation tests. This includes
76 generation of case descriptions, using descriptions of values and symbols
77 to represent the operation or result.
Werner Lewis8b2df742022-07-08 13:54:57 +010078
79 Attributes:
Werner Lewis169034a2022-08-23 16:07:37 +010080 symbol: Symbol used for the operation in case description.
81 input_values: List of values to use as test case inputs. These are
82 combined to produce pairs of values.
Werner Lewis55e638c2022-08-23 14:21:53 +010083 input_cases: List of tuples containing pairs of test case inputs. This
Werner Lewis8b2df742022-07-08 13:54:57 +010084 can be used to implement specific pairs of inputs.
85 """
Werner Lewis55e638c2022-08-23 14:21:53 +010086 symbol = ""
87 input_values = [
Werner Lewis8b2df742022-07-08 13:54:57 +010088 "", "0", "7b", "-7b",
89 "0000000000000000123", "-0000000000000000123",
90 "1230000000000000000", "-1230000000000000000"
Werner Lewisc442f6a2022-07-20 14:13:44 +010091 ] # type: List[str]
92 input_cases = [] # type: List[Tuple[str, ...]]
Werner Lewis8b2df742022-07-08 13:54:57 +010093
94 def __init__(self, val_l: str, val_r: str) -> None:
95 super().__init__()
96
97 self.arg_l = val_l
98 self.arg_r = val_r
99 self.int_l = hex_to_int(val_l)
100 self.int_r = hex_to_int(val_r)
101
Werner Lewis55e638c2022-08-23 14:21:53 +0100102 def arguments(self):
103 return [quote_str(self.arg_l), quote_str(self.arg_r), self.result()]
Werner Lewis8b2df742022-07-08 13:54:57 +0100104
Werner Lewis8b2df742022-07-08 13:54:57 +0100105 def description(self):
Werner Lewis169034a2022-08-23 16:07:37 +0100106 """Generate a description for the test case.
107
108 If not set, case_description uses the form A `symbol` B, where symbol
109 is used to represent the operation. Descriptions of each value are
110 generated to provide some context to the test case.
111 """
Werner Lewis55e638c2022-08-23 14:21:53 +0100112 if not self.case_description:
113 self.case_description = "{} {} {}".format(
114 self.value_description(self.arg_l),
115 self.symbol,
116 self.value_description(self.arg_r)
117 )
118 return super().description()
Werner Lewis8b2df742022-07-08 13:54:57 +0100119
Werner Lewis169034a2022-08-23 16:07:37 +0100120 @abstractmethod
Werner Lewis8b2df742022-07-08 13:54:57 +0100121 def result(self) -> Optional[str]:
Werner Lewis169034a2022-08-23 16:07:37 +0100122 """Get the result of the operation.
123
124 This may be calculated during initialization and stored as `_result`,
125 or calculated when the method is called.
126 """
127 pass
Werner Lewis8b2df742022-07-08 13:54:57 +0100128
129 @staticmethod
Werner Lewis55e638c2022-08-23 14:21:53 +0100130 def value_description(val) -> str:
Werner Lewis169034a2022-08-23 16:07:37 +0100131 """Generate a description of the argument val.
132
133 This produces a simple description of the value, which are used in test
134 case naming, to avoid most generated cases only being numbered.
135 """
Werner Lewis8b2df742022-07-08 13:54:57 +0100136 if val == "":
137 return "0 (null)"
138 if val == "0":
139 return "0 (1 limb)"
140
141 if val[0] == "-":
142 tmp = "negative"
143 val = val[1:]
144 else:
145 tmp = "positive"
146 if val[0] == "0":
147 tmp += " with leading zero limb"
148 elif len(val) > 10:
149 tmp = "large " + tmp
150 return tmp
151
152 @classmethod
153 def get_value_pairs(cls) -> Iterator[Tuple[str, ...]]:
Werner Lewis169034a2022-08-23 16:07:37 +0100154 """Generator for pairs of inputs.
155
156 Combinations are first generated from all input values, and then
157 specific cases provided.
158 """
Werner Lewis92c876a2022-08-23 16:07:19 +0100159 yield from itertools.combinations(cls.input_values, 2)
160 yield from cls.input_cases
Werner Lewis8b2df742022-07-08 13:54:57 +0100161
162 @classmethod
163 def generate_tests(cls) -> Iterator[test_case.TestCase]:
Werner Lewis55e638c2022-08-23 14:21:53 +0100164 if cls.test_function:
Werner Lewis8b2df742022-07-08 13:54:57 +0100165 # Generate tests for the current class
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()
169 # Once current class completed, check descendants
170 yield from super().generate_tests()
171
172
173class BignumCmp(BignumOperation):
174 """Target for bignum comparison test cases."""
175 count = 0
Werner Lewis55e638c2022-08-23 14:21:53 +0100176 test_function = "mbedtls_mpi_cmp_mpi"
177 test_name = "MPI compare"
Werner Lewis8b2df742022-07-08 13:54:57 +0100178 input_cases = [
179 ("-2", "-3"),
180 ("-2", "-2"),
181 ("2b4", "2b5"),
182 ("2b5", "2b6")
183 ]
184
185 def __init__(self, val_l, val_r):
186 super().__init__(val_l, val_r)
Werner Lewis6c70d742022-08-24 16:37:44 +0100187 self._result = int(self.int_l > self.int_r) - int(self.int_l < self.int_r)
Werner Lewis55e638c2022-08-23 14:21:53 +0100188 self.symbol = ["<", "==", ">"][self._result + 1]
Werner Lewis8b2df742022-07-08 13:54:57 +0100189
Werner Lewis8b2df742022-07-08 13:54:57 +0100190 def result(self):
191 return str(self._result)
192
193
Werner Lewis69a92ce2022-07-18 15:49:43 +0100194class BignumCmpAbs(BignumCmp):
Werner Lewis169034a2022-08-23 16:07:37 +0100195 """Target for bignum comparison, absolute variant."""
Werner Lewis69a92ce2022-07-18 15:49:43 +0100196 count = 0
Werner Lewis55e638c2022-08-23 14:21:53 +0100197 test_function = "mbedtls_mpi_cmp_abs"
198 test_name = "MPI compare (abs)"
Werner Lewis69a92ce2022-07-18 15:49:43 +0100199
200 def __init__(self, val_l, val_r):
201 super().__init__(val_l.strip("-"), val_r.strip("-"))
202
203
Werner Lewis86caf852022-07-18 17:22:58 +0100204class BignumAdd(BignumOperation):
205 """Target for bignum addition test cases."""
206 count = 0
Werner Lewis55e638c2022-08-23 14:21:53 +0100207 test_function = "mbedtls_mpi_add_mpi"
208 test_name = "MPI add"
Werner Lewis86caf852022-07-18 17:22:58 +0100209 input_cases = list(itertools.combinations(
210 [
211 "1c67967269c6", "9cde3",
212 "-1c67967269c6", "-9cde3",
213 ], 2
214 ))
215
216 def __init__(self, val_l, val_r):
217 super().__init__(val_l, val_r)
Werner Lewis55e638c2022-08-23 14:21:53 +0100218 self.symbol = "+"
Werner Lewis86caf852022-07-18 17:22:58 +0100219
Werner Lewis86caf852022-07-18 17:22:58 +0100220 def result(self):
221 return quote_str(hex(self.int_l + self.int_r).replace("0x", "", 1))
222
223
Werner Lewisfbb75e32022-08-24 11:30:03 +0100224class BignumTestGenerator(test_generation.TestGenerator):
225 """Test generator subclass including bignum targets."""
Werner Lewis8b2df742022-07-08 13:54:57 +0100226 TARGETS = {
Werner Lewis55e638c2022-08-23 14:21:53 +0100227 subclass.target_basename: subclass.generate_tests for subclass in
Werner Lewisfbb75e32022-08-24 11:30:03 +0100228 test_generation.BaseTarget.__subclasses__()
229 } # type: Dict[str, Callable[[], test_case.TestCase]]
Werner Lewis8b2df742022-07-08 13:54:57 +0100230
231if __name__ == '__main__':
Werner Lewisfbb75e32022-08-24 11:30:03 +0100232 test_generation.main(sys.argv[1:], BignumTestGenerator)