|
2 | 2 |
|
3 | 3 | import textwrap
|
4 | 4 | import unittest
|
| 5 | +from typing import List |
5 | 6 |
|
6 | 7 | import tests.common
|
7 | 8 | from aas_core_codegen import intermediate
|
@@ -51,6 +52,59 @@ def execute(source: str) -> None:
|
51 | 52 | len(inferrer.errors) == 0
|
52 | 53 | ), tests.common.most_underlying_messages(inferrer.errors)
|
53 | 54 |
|
| 55 | + def expect_type_inference_to_fail( |
| 56 | + self, source: str, expected_joined_message: str |
| 57 | + ) -> None: |
| 58 | + """Execute a smoke test and expect type inference to fail.""" |
| 59 | + symbol_table, error = tests.common.translate_source_to_intermediate( |
| 60 | + source=source |
| 61 | + ) |
| 62 | + assert error is None, tests.common.most_underlying_messages(error) |
| 63 | + |
| 64 | + assert symbol_table is not None |
| 65 | + |
| 66 | + base_environment = intermediate_type_inference.populate_base_environment( |
| 67 | + symbol_table=symbol_table |
| 68 | + ) |
| 69 | + |
| 70 | + type_inference_errors = [] # type: List[str] |
| 71 | + |
| 72 | + for our_type in symbol_table.our_types: |
| 73 | + if isinstance( |
| 74 | + our_type, (intermediate.AbstractClass, intermediate.ConcreteClass) |
| 75 | + ): |
| 76 | + environment = intermediate_type_inference.MutableEnvironment( |
| 77 | + parent=base_environment |
| 78 | + ) |
| 79 | + environment.set( |
| 80 | + Identifier("self"), |
| 81 | + intermediate_type_inference.OurTypeAnnotation(our_type=our_type), |
| 82 | + ) |
| 83 | + |
| 84 | + for invariant in our_type.invariants: |
| 85 | + canonicalizer = intermediate_type_inference.Canonicalizer() |
| 86 | + canonicalizer.transform(invariant.body) |
| 87 | + |
| 88 | + inferrer = intermediate_type_inference.Inferrer( |
| 89 | + symbol_table=symbol_table, |
| 90 | + environment=environment, |
| 91 | + representation_map=canonicalizer.representation_map, |
| 92 | + ) |
| 93 | + |
| 94 | + inferrer.transform(invariant.body) |
| 95 | + if len(inferrer.errors) > 0: |
| 96 | + type_inference_errors.append( |
| 97 | + tests.common.most_underlying_messages(inferrer.errors) |
| 98 | + ) |
| 99 | + |
| 100 | + assert len(type_inference_errors) > 0, ( |
| 101 | + f"Expected one or more type inference errors, " |
| 102 | + f"but got none on the source code:\n{source}" |
| 103 | + ) |
| 104 | + |
| 105 | + joined_message = "\n".join(type_inference_errors) |
| 106 | + self.assertEqual(expected_joined_message, joined_message, source) |
| 107 | + |
54 | 108 | def test_enumeration_literal_as_member(self) -> None:
|
55 | 109 | source = textwrap.dedent(
|
56 | 110 | """\
|
@@ -165,6 +219,60 @@ def __init__(self, some_instance: Optional[Some_class] = None) -> None:
|
165 | 219 |
|
166 | 220 | Test_with_smoke.execute(source=source)
|
167 | 221 |
|
| 222 | + def test_is_none_fails_on_non_optional(self) -> None: |
| 223 | + source = textwrap.dedent( |
| 224 | + """\ |
| 225 | + @invariant( |
| 226 | + lambda self: |
| 227 | + self.something is None, |
| 228 | + "Dummy invariant description" |
| 229 | + ) |
| 230 | + class Some_class: |
| 231 | + something: str |
| 232 | +
|
| 233 | + def __init__(self, something: str) -> None: |
| 234 | + self.something = something |
| 235 | +
|
| 236 | + __version__ = "dummy" |
| 237 | + __xml_namespace__ = "https://dummy.com" |
| 238 | + """ |
| 239 | + ) |
| 240 | + |
| 241 | + self.expect_type_inference_to_fail( |
| 242 | + source=source, |
| 243 | + expected_joined_message=( |
| 244 | + "Expected the value to be of an optional type " |
| 245 | + "for a nullness check (``is None``), but got str" |
| 246 | + ), |
| 247 | + ) |
| 248 | + |
| 249 | + def test_is_not_none_fails_on_non_optional(self) -> None: |
| 250 | + source = textwrap.dedent( |
| 251 | + """\ |
| 252 | + @invariant( |
| 253 | + lambda self: |
| 254 | + self.something is not None, |
| 255 | + "Dummy invariant description" |
| 256 | + ) |
| 257 | + class Some_class: |
| 258 | + something: str |
| 259 | +
|
| 260 | + def __init__(self, something: str) -> None: |
| 261 | + self.something = something |
| 262 | +
|
| 263 | + __version__ = "dummy" |
| 264 | + __xml_namespace__ = "https://dummy.com" |
| 265 | + """ |
| 266 | + ) |
| 267 | + |
| 268 | + self.expect_type_inference_to_fail( |
| 269 | + source=source, |
| 270 | + expected_joined_message=( |
| 271 | + "Expected the value to be of an optional type " |
| 272 | + "for a non-nullness check (``is not None``), but got str" |
| 273 | + ), |
| 274 | + ) |
| 275 | + |
168 | 276 |
|
169 | 277 | if __name__ == "__main__":
|
170 | 278 | unittest.main()
|
0 commit comments