blob: 2a8107725e09b19a285438ecd865841280a9754a [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
Werner Lewis6ef54362022-08-25 12:29:46 +01009Child classes of test_generation.BaseTarget (file Targets) represent a target
10file. These indicate where test cases will be written to, for all subclasses of
11this Target. Multiple Target classes should not reuse a `target_basename`.
Werner Lewis169034a2022-08-23 16:07:37 +010012
Werner Lewis6ef54362022-08-25 12:29:46 +010013Each subclass derived from a file Target can either be:
Werner Lewis169034a2022-08-23 16:07:37 +010014 - A concrete class, representing a test function, which generates test cases.
15 - An abstract class containing shared methods and attributes, not associated
Werner Lewis6ef54362022-08-25 12:29:46 +010016 with a test function. An example is BignumOperation, which provides
17 common features used for bignum binary operations.
18
19Both concrete and abstract subclasses can be derived from, to implement
20additional test cases (see BignumCmp and BignumCmpAbs for examples of deriving
21from abstract and concrete classes).
Werner Lewis169034a2022-08-23 16:07:37 +010022
23
Werner Lewis6ef54362022-08-25 12:29:46 +010024Adding test case generation for a function:
Werner Lewis169034a2022-08-23 16:07:37 +010025
26A subclass representing the test function should be added, deriving from a
Werner Lewis81f24442022-08-25 16:27:05 +010027file Target such as BignumTarget. This test class must set/implement the
28following:
Werner Lewis169034a2022-08-23 16:07:37 +010029 - test_function: the function name from the associated .function file.
Werner Lewis6ef54362022-08-25 12:29:46 +010030 - test_name: a descriptive name or brief summary to refer to the test
31 function.
32 - arguments(): a method to generate the list of arguments required for the
33 test_function.
34 - generate_function_test(): a method to generate TestCases for the function.
35 This should create instances of the class with required input data, and
36 call `.create_test_case()` to yield the TestCase.
Werner Lewis169034a2022-08-23 16:07:37 +010037
38Additional details and other attributes/methods are given in the documentation
39of BaseTarget in test_generation.py.
Werner Lewis8b2df742022-07-08 13:54:57 +010040"""
41
42# Copyright The Mbed TLS Contributors
43# SPDX-License-Identifier: Apache-2.0
44#
45# Licensed under the Apache License, Version 2.0 (the "License"); you may
46# not use this file except in compliance with the License.
47# You may obtain a copy of the License at
48#
49# http://www.apache.org/licenses/LICENSE-2.0
50#
51# Unless required by applicable law or agreed to in writing, software
52# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
53# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
54# See the License for the specific language governing permissions and
55# limitations under the License.
56
Werner Lewis8b2df742022-07-08 13:54:57 +010057import itertools
Werner Lewis8b2df742022-07-08 13:54:57 +010058import sys
Werner Lewis169034a2022-08-23 16:07:37 +010059
Werner Lewis699e1262022-08-24 12:18:25 +010060from abc import ABCMeta, abstractmethod
Werner Lewis56013082022-09-02 12:57:37 +010061from typing import Iterator, List, Tuple, TypeVar, cast
Werner Lewis8b2df742022-07-08 13:54:57 +010062
63import scripts_path # pylint: disable=unused-import
Werner Lewis8b2df742022-07-08 13:54:57 +010064from mbedtls_dev import test_case
Werner Lewisfbb75e32022-08-24 11:30:03 +010065from mbedtls_dev import test_generation
Werner Lewis8b2df742022-07-08 13:54:57 +010066
67T = TypeVar('T') #pylint: disable=invalid-name
68
Werner Lewis6300b4f2022-08-24 17:46:22 +010069def hex_to_int(val: str) -> int:
Werner Lewis8b2df742022-07-08 13:54:57 +010070 return int(val, 16) if val else 0
71
Werner Lewis6300b4f2022-08-24 17:46:22 +010072def quote_str(val) -> str:
Werner Lewis8b2df742022-07-08 13:54:57 +010073 return "\"{}\"".format(val)
74
75
Werner Lewis699e1262022-08-24 12:18:25 +010076class BignumTarget(test_generation.BaseTarget, metaclass=ABCMeta):
Werner Lewisa16b6172022-08-25 11:17:35 +010077 #pylint: disable=abstract-method
Werner Lewis8b2df742022-07-08 13:54:57 +010078 """Target for bignum (mpi) test case generation."""
Werner Lewis55e638c2022-08-23 14:21:53 +010079 target_basename = 'test_suite_mpi.generated'
Werner Lewis8b2df742022-07-08 13:54:57 +010080
81
Werner Lewis699e1262022-08-24 12:18:25 +010082class BignumOperation(BignumTarget, metaclass=ABCMeta):
Werner Lewis6ef54362022-08-25 12:29:46 +010083 """Common features for bignum binary operations.
Werner Lewis169034a2022-08-23 16:07:37 +010084
85 This adds functionality common in binary operation tests. This includes
86 generation of case descriptions, using descriptions of values and symbols
87 to represent the operation or result.
Werner Lewis8b2df742022-07-08 13:54:57 +010088
89 Attributes:
Werner Lewis169034a2022-08-23 16:07:37 +010090 symbol: Symbol used for the operation in case description.
91 input_values: List of values to use as test case inputs. These are
92 combined to produce pairs of values.
Werner Lewis55e638c2022-08-23 14:21:53 +010093 input_cases: List of tuples containing pairs of test case inputs. This
Werner Lewis8b2df742022-07-08 13:54:57 +010094 can be used to implement specific pairs of inputs.
95 """
Werner Lewis55e638c2022-08-23 14:21:53 +010096 symbol = ""
97 input_values = [
Werner Lewis8b2df742022-07-08 13:54:57 +010098 "", "0", "7b", "-7b",
99 "0000000000000000123", "-0000000000000000123",
100 "1230000000000000000", "-1230000000000000000"
Werner Lewisc442f6a2022-07-20 14:13:44 +0100101 ] # type: List[str]
Werner Lewis9990b302022-08-24 18:03:30 +0100102 input_cases = cast(List[Tuple[str, str]], []) # type: List[Tuple[str, str]]
Werner Lewis8b2df742022-07-08 13:54:57 +0100103
104 def __init__(self, val_l: str, val_r: str) -> None:
Werner Lewis8b2df742022-07-08 13:54:57 +0100105 self.arg_l = val_l
106 self.arg_r = val_r
107 self.int_l = hex_to_int(val_l)
108 self.int_r = hex_to_int(val_r)
109
Werner Lewis6300b4f2022-08-24 17:46:22 +0100110 def arguments(self) -> List[str]:
Werner Lewis55e638c2022-08-23 14:21:53 +0100111 return [quote_str(self.arg_l), quote_str(self.arg_r), self.result()]
Werner Lewis8b2df742022-07-08 13:54:57 +0100112
Werner Lewis6300b4f2022-08-24 17:46:22 +0100113 def description(self) -> str:
Werner Lewis169034a2022-08-23 16:07:37 +0100114 """Generate a description for the test case.
115
116 If not set, case_description uses the form A `symbol` B, where symbol
117 is used to represent the operation. Descriptions of each value are
118 generated to provide some context to the test case.
119 """
Werner Lewis55e638c2022-08-23 14:21:53 +0100120 if not self.case_description:
121 self.case_description = "{} {} {}".format(
122 self.value_description(self.arg_l),
123 self.symbol,
124 self.value_description(self.arg_r)
125 )
126 return super().description()
Werner Lewis8b2df742022-07-08 13:54:57 +0100127
Werner Lewis169034a2022-08-23 16:07:37 +0100128 @abstractmethod
Werner Lewis699e1262022-08-24 12:18:25 +0100129 def result(self) -> str:
Werner Lewis169034a2022-08-23 16:07:37 +0100130 """Get the result of the operation.
131
Werner Lewis81f24442022-08-25 16:27:05 +0100132 This could be calculated during initialization and stored as `_result`
133 and then returned, or calculated when the method is called.
Werner Lewis169034a2022-08-23 16:07:37 +0100134 """
Werner Lewis6d654c62022-08-25 09:56:51 +0100135 raise NotImplementedError
Werner Lewis8b2df742022-07-08 13:54:57 +0100136
137 @staticmethod
Werner Lewis55e638c2022-08-23 14:21:53 +0100138 def value_description(val) -> str:
Werner Lewis169034a2022-08-23 16:07:37 +0100139 """Generate a description of the argument val.
140
Werner Lewis81f24442022-08-25 16:27:05 +0100141 This produces a simple description of the value, which is used in test
142 case naming to add context.
Werner Lewis169034a2022-08-23 16:07:37 +0100143 """
Werner Lewis8b2df742022-07-08 13:54:57 +0100144 if val == "":
145 return "0 (null)"
146 if val == "0":
147 return "0 (1 limb)"
148
149 if val[0] == "-":
150 tmp = "negative"
151 val = val[1:]
152 else:
153 tmp = "positive"
154 if val[0] == "0":
155 tmp += " with leading zero limb"
156 elif len(val) > 10:
157 tmp = "large " + tmp
158 return tmp
159
160 @classmethod
Werner Lewis6300b4f2022-08-24 17:46:22 +0100161 def get_value_pairs(cls) -> Iterator[Tuple[str, str]]:
Werner Lewis6ef54362022-08-25 12:29:46 +0100162 """Generator to yield pairs of inputs.
Werner Lewis169034a2022-08-23 16:07:37 +0100163
164 Combinations are first generated from all input values, and then
165 specific cases provided.
166 """
Werner Lewis9990b302022-08-24 18:03:30 +0100167 yield from cast(
168 Iterator[Tuple[str, str]],
Werner Lewisa4b77202022-08-31 16:55:44 +0100169 itertools.combinations_with_replacement(cls.input_values, 2)
Werner Lewis9990b302022-08-24 18:03:30 +0100170 )
Werner Lewis92c876a2022-08-23 16:07:19 +0100171 yield from cls.input_cases
Werner Lewis8b2df742022-07-08 13:54:57 +0100172
173 @classmethod
Werner Lewis2b527a32022-08-24 12:42:00 +0100174 def generate_function_tests(cls) -> Iterator[test_case.TestCase]:
175 for l_value, r_value in cls.get_value_pairs():
176 cur_op = cls(l_value, r_value)
177 yield cur_op.create_test_case()
Werner Lewis8b2df742022-07-08 13:54:57 +0100178
179
180class BignumCmp(BignumOperation):
Werner Lewis6ef54362022-08-25 12:29:46 +0100181 """Test cases for bignum value comparison."""
Werner Lewis8b2df742022-07-08 13:54:57 +0100182 count = 0
Werner Lewis55e638c2022-08-23 14:21:53 +0100183 test_function = "mbedtls_mpi_cmp_mpi"
184 test_name = "MPI compare"
Werner Lewis8b2df742022-07-08 13:54:57 +0100185 input_cases = [
186 ("-2", "-3"),
187 ("-2", "-2"),
188 ("2b4", "2b5"),
189 ("2b5", "2b6")
190 ]
191
Werner Lewis6300b4f2022-08-24 17:46:22 +0100192 def __init__(self, val_l, val_r) -> None:
Werner Lewis8b2df742022-07-08 13:54:57 +0100193 super().__init__(val_l, val_r)
Werner Lewis6c70d742022-08-24 16:37:44 +0100194 self._result = int(self.int_l > self.int_r) - int(self.int_l < self.int_r)
Werner Lewis55e638c2022-08-23 14:21:53 +0100195 self.symbol = ["<", "==", ">"][self._result + 1]
Werner Lewis8b2df742022-07-08 13:54:57 +0100196
Werner Lewis6300b4f2022-08-24 17:46:22 +0100197 def result(self) -> str:
Werner Lewis8b2df742022-07-08 13:54:57 +0100198 return str(self._result)
199
200
Werner Lewis69a92ce2022-07-18 15:49:43 +0100201class BignumCmpAbs(BignumCmp):
Werner Lewis6ef54362022-08-25 12:29:46 +0100202 """Test cases for absolute bignum value comparison."""
Werner Lewis69a92ce2022-07-18 15:49:43 +0100203 count = 0
Werner Lewis55e638c2022-08-23 14:21:53 +0100204 test_function = "mbedtls_mpi_cmp_abs"
205 test_name = "MPI compare (abs)"
Werner Lewis69a92ce2022-07-18 15:49:43 +0100206
Werner Lewis6300b4f2022-08-24 17:46:22 +0100207 def __init__(self, val_l, val_r) -> None:
Werner Lewis69a92ce2022-07-18 15:49:43 +0100208 super().__init__(val_l.strip("-"), val_r.strip("-"))
209
210
Werner Lewis86caf852022-07-18 17:22:58 +0100211class BignumAdd(BignumOperation):
Werner Lewis6ef54362022-08-25 12:29:46 +0100212 """Test cases for bignum value addition."""
Werner Lewis86caf852022-07-18 17:22:58 +0100213 count = 0
Werner Lewis55e638c2022-08-23 14:21:53 +0100214 test_function = "mbedtls_mpi_add_mpi"
215 test_name = "MPI add"
Werner Lewis9990b302022-08-24 18:03:30 +0100216 input_cases = cast(
217 List[Tuple[str, str]],
Werner Lewisa4b77202022-08-31 16:55:44 +0100218 list(itertools.combinations_with_replacement(
Werner Lewise3ad22e2022-08-25 10:02:06 +0100219 [
220 "1c67967269c6", "9cde3",
221 "-1c67967269c6", "-9cde3",
222 ], 2
Werner Lewis9990b302022-08-24 18:03:30 +0100223 ))
224 )
Werner Lewis86caf852022-07-18 17:22:58 +0100225
Werner Lewis6300b4f2022-08-24 17:46:22 +0100226 def __init__(self, val_l, val_r) -> None:
Werner Lewis86caf852022-07-18 17:22:58 +0100227 super().__init__(val_l, val_r)
Werner Lewis55e638c2022-08-23 14:21:53 +0100228 self.symbol = "+"
Werner Lewis86caf852022-07-18 17:22:58 +0100229
Werner Lewis6300b4f2022-08-24 17:46:22 +0100230 def result(self) -> str:
Werner Lewis86caf852022-07-18 17:22:58 +0100231 return quote_str(hex(self.int_l + self.int_r).replace("0x", "", 1))
232
233
Werner Lewis8b2df742022-07-08 13:54:57 +0100234if __name__ == '__main__':
Werner Lewisa4668a62022-09-02 11:56:34 +0100235 test_generation.main(sys.argv[1:])