blob: f1b24409ea4c4a9fc0724f8e7ea493aa52c7bdfd [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
Gilles Peskine64f2efd2022-09-16 21:41:47 +02009Child classes of test_data_generation.BaseTarget (file targets) represent an output
Werner Lewis6ef54362022-08-25 12:29:46 +010010file. These indicate where test cases will be written to, for all subclasses of
Werner Lewis52ae3262022-09-14 16:26:54 +010011this target. Multiple file targets should not reuse a `target_basename`.
Werner Lewis169034a2022-08-23 16:07:37 +010012
Werner Lewis52ae3262022-09-14 16:26:54 +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 Lewis52ae3262022-09-14 16:26:54 +010027file target such as BignumTarget. This test class must set/implement the
Werner Lewis81f24442022-08-25 16:27:05 +010028following:
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.
Gilles Peskine22514eb2022-09-21 23:13:04 +020034 - generate_function_tests(): a method to generate TestCases for the function.
Werner Lewis6ef54362022-08-25 12:29:46 +010035 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
Gilles Peskine64f2efd2022-09-16 21:41:47 +020039of BaseTarget in test_data_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 sys
Werner Lewis169034a2022-08-23 16:07:37 +010058
Werner Lewis99e81782022-09-30 16:28:43 +010059from abc import ABCMeta
Werner Lewis1b20e7e2022-10-12 14:53:17 +010060from typing import Iterator, List
Werner Lewis8b2df742022-07-08 13:54:57 +010061
62import scripts_path # pylint: disable=unused-import
Werner Lewis8b2df742022-07-08 13:54:57 +010063from mbedtls_dev import test_case
Gilles Peskine64f2efd2022-09-16 21:41:47 +020064from mbedtls_dev import test_data_generation
Werner Lewis99e81782022-09-30 16:28:43 +010065from mbedtls_dev import bignum_common
66# Import modules containing additional test classes
67# Test function classes in these modules will be registered by
68# the framework
Minos Galanakis5566eff2022-11-07 16:02:21 +000069from mbedtls_dev import bignum_core, bignum_mod_raw # pylint: disable=unused-import
Werner Lewis8b2df742022-07-08 13:54:57 +010070
Gilles Peskine64f2efd2022-09-16 21:41:47 +020071class BignumTarget(test_data_generation.BaseTarget, metaclass=ABCMeta):
Werner Lewisa16b6172022-08-25 11:17:35 +010072 #pylint: disable=abstract-method
Gilles Peskinece220662022-10-21 18:54:43 +020073 """Target for bignum (legacy) test case generation."""
74 target_basename = 'test_suite_bignum.generated'
Werner Lewis8b2df742022-07-08 13:54:57 +010075
76
Werner Lewis99e81782022-09-30 16:28:43 +010077class BignumOperation(bignum_common.OperationCommon, BignumTarget, metaclass=ABCMeta):
78 #pylint: disable=abstract-method
79 """Common features for bignum operations in legacy tests."""
Werner Lewis55e638c2022-08-23 14:21:53 +010080 input_values = [
Gilles Peskine35af0212022-11-15 20:43:33 +010081 "", "0", "-", "-0",
82 "7b", "-7b",
Werner Lewis8b2df742022-07-08 13:54:57 +010083 "0000000000000000123", "-0000000000000000123",
84 "1230000000000000000", "-1230000000000000000"
Werner Lewis99e81782022-09-30 16:28:43 +010085 ]
Werner Lewis8b2df742022-07-08 13:54:57 +010086
Gilles Peskine35af0212022-11-15 20:43:33 +010087 def description_suffix(self) -> str:
88 """Text to add at the end of the test case description."""
89 return ""
90
Werner Lewis6300b4f2022-08-24 17:46:22 +010091 def description(self) -> str:
Werner Lewis169034a2022-08-23 16:07:37 +010092 """Generate a description for the test case.
93
94 If not set, case_description uses the form A `symbol` B, where symbol
95 is used to represent the operation. Descriptions of each value are
96 generated to provide some context to the test case.
97 """
Werner Lewis55e638c2022-08-23 14:21:53 +010098 if not self.case_description:
99 self.case_description = "{} {} {}".format(
Werner Lewis3dc45192022-09-12 17:35:27 +0100100 self.value_description(self.arg_a),
Werner Lewis55e638c2022-08-23 14:21:53 +0100101 self.symbol,
Werner Lewis3dc45192022-09-12 17:35:27 +0100102 self.value_description(self.arg_b)
Werner Lewis55e638c2022-08-23 14:21:53 +0100103 )
Gilles Peskine35af0212022-11-15 20:43:33 +0100104 description_suffix = self.description_suffix()
105 if description_suffix:
106 self.case_description += " " + description_suffix
Werner Lewis55e638c2022-08-23 14:21:53 +0100107 return super().description()
Werner Lewis8b2df742022-07-08 13:54:57 +0100108
Werner Lewis8b2df742022-07-08 13:54:57 +0100109 @staticmethod
Werner Lewis55e638c2022-08-23 14:21:53 +0100110 def value_description(val) -> str:
Werner Lewis169034a2022-08-23 16:07:37 +0100111 """Generate a description of the argument val.
112
Werner Lewis81f24442022-08-25 16:27:05 +0100113 This produces a simple description of the value, which is used in test
114 case naming to add context.
Werner Lewis169034a2022-08-23 16:07:37 +0100115 """
Werner Lewis8b2df742022-07-08 13:54:57 +0100116 if val == "":
117 return "0 (null)"
Gilles Peskine35af0212022-11-15 20:43:33 +0100118 if val == "-":
119 return "negative 0 (null)"
Werner Lewis8b2df742022-07-08 13:54:57 +0100120 if val == "0":
121 return "0 (1 limb)"
122
123 if val[0] == "-":
124 tmp = "negative"
125 val = val[1:]
126 else:
127 tmp = "positive"
128 if val[0] == "0":
129 tmp += " with leading zero limb"
130 elif len(val) > 10:
131 tmp = "large " + tmp
132 return tmp
133
134 @classmethod
Werner Lewis2b527a32022-08-24 12:42:00 +0100135 def generate_function_tests(cls) -> Iterator[test_case.TestCase]:
Werner Lewis3dc45192022-09-12 17:35:27 +0100136 for a_value, b_value in cls.get_value_pairs():
Werner Lewis99e81782022-09-30 16:28:43 +0100137 yield cls(a_value, b_value).create_test_case()
Werner Lewis8b2df742022-07-08 13:54:57 +0100138
139
140class BignumCmp(BignumOperation):
Werner Lewis6ef54362022-08-25 12:29:46 +0100141 """Test cases for bignum value comparison."""
Werner Lewis8b2df742022-07-08 13:54:57 +0100142 count = 0
Tom Cosgrove119eae22022-09-21 12:19:18 +0100143 test_function = "mpi_cmp_mpi"
Werner Lewis55e638c2022-08-23 14:21:53 +0100144 test_name = "MPI compare"
Werner Lewis8b2df742022-07-08 13:54:57 +0100145 input_cases = [
146 ("-2", "-3"),
147 ("-2", "-2"),
148 ("2b4", "2b5"),
149 ("2b5", "2b6")
150 ]
151
Werner Lewis3dc45192022-09-12 17:35:27 +0100152 def __init__(self, val_a, val_b) -> None:
153 super().__init__(val_a, val_b)
154 self._result = int(self.int_a > self.int_b) - int(self.int_a < self.int_b)
Werner Lewis55e638c2022-08-23 14:21:53 +0100155 self.symbol = ["<", "==", ">"][self._result + 1]
Werner Lewis8b2df742022-07-08 13:54:57 +0100156
Werner Lewis1b20e7e2022-10-12 14:53:17 +0100157 def result(self) -> List[str]:
158 return [str(self._result)]
Werner Lewis8b2df742022-07-08 13:54:57 +0100159
160
Werner Lewis69a92ce2022-07-18 15:49:43 +0100161class BignumCmpAbs(BignumCmp):
Werner Lewis6ef54362022-08-25 12:29:46 +0100162 """Test cases for absolute bignum value comparison."""
Werner Lewis69a92ce2022-07-18 15:49:43 +0100163 count = 0
Tom Cosgrove119eae22022-09-21 12:19:18 +0100164 test_function = "mpi_cmp_abs"
Werner Lewis55e638c2022-08-23 14:21:53 +0100165 test_name = "MPI compare (abs)"
Werner Lewis69a92ce2022-07-18 15:49:43 +0100166
Werner Lewis3dc45192022-09-12 17:35:27 +0100167 def __init__(self, val_a, val_b) -> None:
168 super().__init__(val_a.strip("-"), val_b.strip("-"))
Werner Lewis69a92ce2022-07-18 15:49:43 +0100169
170
Werner Lewis86caf852022-07-18 17:22:58 +0100171class BignumAdd(BignumOperation):
Werner Lewis6ef54362022-08-25 12:29:46 +0100172 """Test cases for bignum value addition."""
Werner Lewis86caf852022-07-18 17:22:58 +0100173 count = 0
Werner Lewis1fade8a2022-09-12 17:34:15 +0100174 symbol = "+"
Tom Cosgrove119eae22022-09-21 12:19:18 +0100175 test_function = "mpi_add_mpi"
Werner Lewis55e638c2022-08-23 14:21:53 +0100176 test_name = "MPI add"
Werner Lewis99e81782022-09-30 16:28:43 +0100177 input_cases = bignum_common.combination_pairs(
Werner Lewisac446c82022-09-14 15:12:46 +0100178 [
179 "1c67967269c6", "9cde3",
180 "-1c67967269c6", "-9cde3",
181 ]
Werner Lewis9990b302022-08-24 18:03:30 +0100182 )
Werner Lewis86caf852022-07-18 17:22:58 +0100183
Gilles Peskine35af0212022-11-15 20:43:33 +0100184 def __init__(self, val_a: str, val_b: str) -> None:
185 super().__init__(val_a, val_b)
186 self._result = self.int_a + self.int_b
Werner Lewis99e81782022-09-30 16:28:43 +0100187
Gilles Peskine35af0212022-11-15 20:43:33 +0100188 def description_suffix(self) -> str:
189 if (self.int_a >= 0 and self.int_b >= 0):
190 return "" # obviously positive result or 0
191 if (self.int_a <= 0 and self.int_b <= 0):
192 return "" # obviously negative result or 0
193 # The sign of the result is not obvious, so indicate it
194 return ", result{}0".format('>' if self._result > 0 else
195 '<' if self._result < 0 else '=')
196
197 def result(self) -> List[str]:
198 return [bignum_common.quote_str("{:x}".format(self._result))]
Werner Lewis86caf852022-07-18 17:22:58 +0100199
Werner Lewis8b2df742022-07-08 13:54:57 +0100200if __name__ == '__main__':
Werner Lewisc2fb5402022-09-16 17:03:54 +0100201 # Use the section of the docstring relevant to the CLI as description
Gilles Peskine64f2efd2022-09-16 21:41:47 +0200202 test_data_generation.main(sys.argv[1:], "\n".join(__doc__.splitlines()[:4]))