PSA test case generation: dependency inference class: key not supported

Use psa_information.TestCase for not-supported test cases for key import and
generation.

No change to the generated output.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
diff --git a/scripts/mbedtls_dev/psa_information.py b/scripts/mbedtls_dev/psa_information.py
index 2c24a3a..1474657 100644
--- a/scripts/mbedtls_dev/psa_information.py
+++ b/scripts/mbedtls_dev/psa_information.py
@@ -6,7 +6,7 @@
 
 
 import re
-from typing import Dict, FrozenSet, List, Optional
+from typing import Dict, FrozenSet, List, Optional, Set
 
 from . import macro_collector
 from . import test_case
@@ -116,6 +116,20 @@
     def __init__(self) -> None:
         super().__init__()
         self.key_bits = None #type: Optional[int]
+        self.negated_dependencies = set() #type: Set[str]
+
+    def assumes_not_supported(self, name: str) -> None:
+        """Negate the given mechanism for automatic dependency generation.
+
+        Call this function before set_arguments() for a test case that should
+        run if the given mechanism is not supported.
+
+        A mechanism is a PSA_XXX symbol, e.g. PSA_KEY_TYPE_AES, PSA_ALG_HMAC,
+        etc. For mechanisms like ECC curves where the support status includes
+        the key bit-size, this class assumes that only one bit-size is
+        involved in a given test case.
+        """
+        self.negated_dependencies.add(psa_want_symbol(name))
 
     def set_key_bits(self, key_bits: Optional[int]) -> None:
         """Use the given key size for automatic dependency generation.
@@ -131,6 +145,9 @@
         """Set test case arguments and automatically infer dependencies."""
         super().set_arguments(arguments)
         dependencies = automatic_dependencies(*arguments)
+        for i in range(len(dependencies)): #pylint: disable=consider-using-enumerate
+            if dependencies[i] in self.negated_dependencies:
+                dependencies[i] = '!' + dependencies[i]
         if self.key_bits is not None:
             dependencies = finish_family_dependencies(dependencies, self.key_bits)
         hack_dependencies_not_implemented(dependencies)
diff --git a/tests/scripts/generate_psa_tests.py b/tests/scripts/generate_psa_tests.py
index eae9376..cd800bd 100755
--- a/tests/scripts/generate_psa_tests.py
+++ b/tests/scripts/generate_psa_tests.py
@@ -25,23 +25,21 @@
 
 def test_case_for_key_type_not_supported(
         verb: str, key_type: str, bits: int,
-        dependencies: List[str],
+        not_supported_mechanism: str,
         *args: str,
         param_descr: str = ''
 ) -> test_case.TestCase:
     """Return one test case exercising a key creation method
     for an unsupported key type or size.
     """
-    psa_information.hack_dependencies_not_implemented(dependencies)
-    tc = test_case.TestCase()
+    tc = psa_information.TestCase()
     short_key_type = crypto_knowledge.short_expression(key_type)
-    adverb = 'not' if dependencies else 'never'
-    if param_descr:
-        adverb = param_descr + ' ' + adverb
-    tc.set_description('PSA {} {} {}-bit {} supported'
-                       .format(verb, short_key_type, bits, adverb))
-    tc.set_dependencies(sorted(dependencies))
+    tc.set_description('PSA {} {} {}-bit{} not supported'
+                       .format(verb, short_key_type, bits,
+                               ' ' + param_descr if param_descr else ''))
     tc.set_function(verb + '_not_supported')
+    tc.set_key_bits(bits)
+    tc.assumes_not_supported(not_supported_mechanism)
     tc.set_arguments([key_type] + list(args))
     return tc
 
@@ -71,34 +69,27 @@
             # Don't generate test cases for key types that are always supported.
             # They would be skipped in all configurations, which is noise.
             return
-        import_dependencies = [('!' if param is None else '') +
-                               psa_information.psa_want_symbol(kt.name)]
-        if kt.params is not None:
-            import_dependencies += [('!' if param == i else '') +
-                                    psa_information.psa_want_symbol(sym)
-                                    for i, sym in enumerate(kt.params)]
-        if kt.name.endswith('_PUBLIC_KEY'):
-            generate_dependencies = []
+        if param is None:
+            not_supported_mechanism = kt.name
         else:
-            generate_dependencies = import_dependencies
+            assert kt.params is not None
+            not_supported_mechanism = kt.params[param]
         for bits in kt.sizes_to_test():
             yield test_case_for_key_type_not_supported(
                 'import', kt.expression, bits,
-                psa_information.finish_family_dependencies(import_dependencies, bits),
+                not_supported_mechanism,
                 test_case.hex_string(kt.key_material(bits)),
                 param_descr=param_descr,
             )
-            if not generate_dependencies and param is not None:
-                # If generation is impossible for this key type, rather than
-                # supported or not depending on implementation capabilities,
-                # only generate the test case once.
-                continue
-                # For public key we expect that key generation fails with
-                # INVALID_ARGUMENT. It is handled by KeyGenerate class.
+            # Don't generate not-supported test cases for key generation of
+            # public keys. Our implementation always returns
+            # PSA_ERROR_INVALID_ARGUMENT when attempting to generate a
+            # public key, so we cover this together with the positive cases
+            # in the KeyGenerate class.
             if not kt.is_public():
                 yield test_case_for_key_type_not_supported(
                     'generate', kt.expression, bits,
-                    psa_information.finish_family_dependencies(generate_dependencies, bits),
+                    not_supported_mechanism,
                     str(bits),
                     param_descr=param_descr,
                 )