blob: b855e91abfdfc3b68d6a7065fd16a8efbd6eae49 [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
Dave Rodgman16799db2023-11-02 19:47:20 +000043# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
Werner Lewis8b2df742022-07-08 13:54:57 +010044
Werner Lewis8b2df742022-07-08 13:54:57 +010045import sys
Werner Lewis169034a2022-08-23 16:07:37 +010046
Werner Lewis99e81782022-09-30 16:28:43 +010047from abc import ABCMeta
Janos Follath351e6882022-11-09 16:04:41 +000048from typing import List
Werner Lewis8b2df742022-07-08 13:54:57 +010049
50import scripts_path # pylint: disable=unused-import
David Horstmanncd84bb22024-05-03 14:36:12 +010051from mbedtls_framework import test_data_generation
52from mbedtls_framework import bignum_common
Werner Lewis99e81782022-09-30 16:28:43 +010053# Import modules containing additional test classes
54# Test function classes in these modules will be registered by
55# the framework
David Horstmanncd84bb22024-05-03 14:36:12 +010056from mbedtls_framework import bignum_core, bignum_mod_raw, bignum_mod # pylint: disable=unused-import
Werner Lewis8b2df742022-07-08 13:54:57 +010057
Janos Follath0cd89672022-11-09 12:14:14 +000058class BignumTarget(test_data_generation.BaseTarget):
Janos Follath351e6882022-11-09 16:04:41 +000059 #pylint: disable=too-few-public-methods
Gilles Peskinece220662022-10-21 18:54:43 +020060 """Target for bignum (legacy) test case generation."""
61 target_basename = 'test_suite_bignum.generated'
Werner Lewis8b2df742022-07-08 13:54:57 +010062
63
Janos Follath0cd89672022-11-09 12:14:14 +000064class BignumOperation(bignum_common.OperationCommon, BignumTarget,
65 metaclass=ABCMeta):
Werner Lewis99e81782022-09-30 16:28:43 +010066 #pylint: disable=abstract-method
67 """Common features for bignum operations in legacy tests."""
Janos Follathf4579762022-11-20 13:32:54 +000068 unique_combinations_only = True
Werner Lewis55e638c2022-08-23 14:21:53 +010069 input_values = [
Gilles Peskine35af0212022-11-15 20:43:33 +010070 "", "0", "-", "-0",
71 "7b", "-7b",
Werner Lewis8b2df742022-07-08 13:54:57 +010072 "0000000000000000123", "-0000000000000000123",
73 "1230000000000000000", "-1230000000000000000"
Werner Lewis99e81782022-09-30 16:28:43 +010074 ]
Werner Lewis8b2df742022-07-08 13:54:57 +010075
Gilles Peskine35af0212022-11-15 20:43:33 +010076 def description_suffix(self) -> str:
Gilles Peskineb9b90262022-11-10 09:15:21 +010077 #pylint: disable=no-self-use # derived classes need self
Gilles Peskine35af0212022-11-15 20:43:33 +010078 """Text to add at the end of the test case description."""
79 return ""
80
Werner Lewis6300b4f2022-08-24 17:46:22 +010081 def description(self) -> str:
Werner Lewis169034a2022-08-23 16:07:37 +010082 """Generate a description for the test case.
83
84 If not set, case_description uses the form A `symbol` B, where symbol
85 is used to represent the operation. Descriptions of each value are
86 generated to provide some context to the test case.
87 """
Werner Lewis55e638c2022-08-23 14:21:53 +010088 if not self.case_description:
89 self.case_description = "{} {} {}".format(
Werner Lewis3dc45192022-09-12 17:35:27 +010090 self.value_description(self.arg_a),
Werner Lewis55e638c2022-08-23 14:21:53 +010091 self.symbol,
Werner Lewis3dc45192022-09-12 17:35:27 +010092 self.value_description(self.arg_b)
Werner Lewis55e638c2022-08-23 14:21:53 +010093 )
Gilles Peskine35af0212022-11-15 20:43:33 +010094 description_suffix = self.description_suffix()
95 if description_suffix:
96 self.case_description += " " + description_suffix
Werner Lewis55e638c2022-08-23 14:21:53 +010097 return super().description()
Werner Lewis8b2df742022-07-08 13:54:57 +010098
Werner Lewis8b2df742022-07-08 13:54:57 +010099 @staticmethod
Werner Lewis55e638c2022-08-23 14:21:53 +0100100 def value_description(val) -> str:
Werner Lewis169034a2022-08-23 16:07:37 +0100101 """Generate a description of the argument val.
102
Werner Lewis81f24442022-08-25 16:27:05 +0100103 This produces a simple description of the value, which is used in test
104 case naming to add context.
Werner Lewis169034a2022-08-23 16:07:37 +0100105 """
Werner Lewis8b2df742022-07-08 13:54:57 +0100106 if val == "":
107 return "0 (null)"
Gilles Peskine35af0212022-11-15 20:43:33 +0100108 if val == "-":
109 return "negative 0 (null)"
Werner Lewis8b2df742022-07-08 13:54:57 +0100110 if val == "0":
111 return "0 (1 limb)"
112
113 if val[0] == "-":
114 tmp = "negative"
115 val = val[1:]
116 else:
117 tmp = "positive"
118 if val[0] == "0":
119 tmp += " with leading zero limb"
120 elif len(val) > 10:
121 tmp = "large " + tmp
122 return tmp
123
Werner Lewis8b2df742022-07-08 13:54:57 +0100124
125class BignumCmp(BignumOperation):
Werner Lewis6ef54362022-08-25 12:29:46 +0100126 """Test cases for bignum value comparison."""
Werner Lewis8b2df742022-07-08 13:54:57 +0100127 count = 0
Tom Cosgrove119eae22022-09-21 12:19:18 +0100128 test_function = "mpi_cmp_mpi"
Werner Lewis55e638c2022-08-23 14:21:53 +0100129 test_name = "MPI compare"
Werner Lewis8b2df742022-07-08 13:54:57 +0100130 input_cases = [
131 ("-2", "-3"),
132 ("-2", "-2"),
133 ("2b4", "2b5"),
134 ("2b5", "2b6")
135 ]
136
Werner Lewis3dc45192022-09-12 17:35:27 +0100137 def __init__(self, val_a, val_b) -> None:
138 super().__init__(val_a, val_b)
139 self._result = int(self.int_a > self.int_b) - int(self.int_a < self.int_b)
Werner Lewis55e638c2022-08-23 14:21:53 +0100140 self.symbol = ["<", "==", ">"][self._result + 1]
Werner Lewis8b2df742022-07-08 13:54:57 +0100141
Werner Lewis1b20e7e2022-10-12 14:53:17 +0100142 def result(self) -> List[str]:
143 return [str(self._result)]
Werner Lewis8b2df742022-07-08 13:54:57 +0100144
145
Werner Lewis69a92ce2022-07-18 15:49:43 +0100146class BignumCmpAbs(BignumCmp):
Werner Lewis6ef54362022-08-25 12:29:46 +0100147 """Test cases for absolute bignum value comparison."""
Werner Lewis69a92ce2022-07-18 15:49:43 +0100148 count = 0
Tom Cosgrove119eae22022-09-21 12:19:18 +0100149 test_function = "mpi_cmp_abs"
Werner Lewis55e638c2022-08-23 14:21:53 +0100150 test_name = "MPI compare (abs)"
Werner Lewis69a92ce2022-07-18 15:49:43 +0100151
Werner Lewis3dc45192022-09-12 17:35:27 +0100152 def __init__(self, val_a, val_b) -> None:
153 super().__init__(val_a.strip("-"), val_b.strip("-"))
Werner Lewis69a92ce2022-07-18 15:49:43 +0100154
155
Werner Lewis86caf852022-07-18 17:22:58 +0100156class BignumAdd(BignumOperation):
Werner Lewis6ef54362022-08-25 12:29:46 +0100157 """Test cases for bignum value addition."""
Werner Lewis86caf852022-07-18 17:22:58 +0100158 count = 0
Werner Lewis1fade8a2022-09-12 17:34:15 +0100159 symbol = "+"
Tom Cosgrove119eae22022-09-21 12:19:18 +0100160 test_function = "mpi_add_mpi"
Werner Lewis55e638c2022-08-23 14:21:53 +0100161 test_name = "MPI add"
Werner Lewis99e81782022-09-30 16:28:43 +0100162 input_cases = bignum_common.combination_pairs(
Werner Lewisac446c82022-09-14 15:12:46 +0100163 [
164 "1c67967269c6", "9cde3",
165 "-1c67967269c6", "-9cde3",
166 ]
Werner Lewis9990b302022-08-24 18:03:30 +0100167 )
Werner Lewis86caf852022-07-18 17:22:58 +0100168
Gilles Peskine35af0212022-11-15 20:43:33 +0100169 def __init__(self, val_a: str, val_b: str) -> None:
170 super().__init__(val_a, val_b)
171 self._result = self.int_a + self.int_b
Werner Lewis99e81782022-09-30 16:28:43 +0100172
Gilles Peskine35af0212022-11-15 20:43:33 +0100173 def description_suffix(self) -> str:
174 if (self.int_a >= 0 and self.int_b >= 0):
175 return "" # obviously positive result or 0
176 if (self.int_a <= 0 and self.int_b <= 0):
177 return "" # obviously negative result or 0
178 # The sign of the result is not obvious, so indicate it
179 return ", result{}0".format('>' if self._result > 0 else
180 '<' if self._result < 0 else '=')
181
182 def result(self) -> List[str]:
183 return [bignum_common.quote_str("{:x}".format(self._result))]
Werner Lewis86caf852022-07-18 17:22:58 +0100184
Werner Lewis8b2df742022-07-08 13:54:57 +0100185if __name__ == '__main__':
Werner Lewisc2fb5402022-09-16 17:03:54 +0100186 # Use the section of the docstring relevant to the CLI as description
Gilles Peskine64f2efd2022-09-16 21:41:47 +0200187 test_data_generation.main(sys.argv[1:], "\n".join(__doc__.splitlines()[:4]))