From 375c3f2422866762e7823a60057332f6c599e527 Mon Sep 17 00:00:00 2001 From: Kyler Date: Wed, 12 Nov 2025 13:11:40 -0700 Subject: [PATCH] Added base test generator class --- SLS_Tests/generate_tests/__main__.py | 31 +- SLS_Tests/generate_tests/base_tests.py | 352 ++++++++++++++++++++++ SLS_Tests/generate_tests/float_tests.py | 175 ++++------- SLS_Tests/generate_tests/general_tests.py | 11 + SLS_Tests/generate_tests/integer_tests.py | 155 +++------- SLS_Tests/generate_tests/utils.py | 45 --- 6 files changed, 483 insertions(+), 286 deletions(-) create mode 100644 SLS_Tests/generate_tests/base_tests.py create mode 100644 SLS_Tests/generate_tests/general_tests.py delete mode 100644 SLS_Tests/generate_tests/utils.py diff --git a/SLS_Tests/generate_tests/__main__.py b/SLS_Tests/generate_tests/__main__.py index 7e165bd..33227c1 100644 --- a/SLS_Tests/generate_tests/__main__.py +++ b/SLS_Tests/generate_tests/__main__.py @@ -3,35 +3,22 @@ Test case generator for integer literals in the Stack Language. Generates comprehensive test cases for all integer types and bases. """ -from .integer_tests import generate_integer_literal_tests -from .float_tests import generate_float_literal_tests +from .general_tests import GeneralTestGenerator +from .integer_tests import IntegerTestGenerator +from .float_tests import FloatTestGenerator if __name__ == "__main__": - # import json import yaml - + # Generate tests tests = [] - tests += generate_integer_literal_tests() - tests += generate_float_literal_tests() - + tests += GeneralTestGenerator.generate_tests() + tests += IntegerTestGenerator.generate_tests() + tests += FloatTestGenerator.generate_tests() + # Print summary print(f"Generated {len(tests)} test cases") - - # # Save as JSON - # with open("integer_literal_tests.json", "w") as f: - # json.dump(tests, f, indent=2) - # print("Saved to integer_literal_tests.json") - - # # Save as YAML - # with open("integer_literal_tests.yaml", "w") as f: - # yaml.dump(tests, f, default_flow_style=False, sort_keys=False) - # print("Saved to integer_literal_tests.yaml") - + # Save as YAML with open("cases.yaml", "w") as f: yaml.dump(tests, f, default_flow_style=False, sort_keys=False) - - # Print first few tests as example - # print("\nFirst 3 test cases:") - # print(yaml.dump(tests[:3], default_flow_style=False, sort_keys=False)) diff --git a/SLS_Tests/generate_tests/base_tests.py b/SLS_Tests/generate_tests/base_tests.py new file mode 100644 index 0000000..362023b --- /dev/null +++ b/SLS_Tests/generate_tests/base_tests.py @@ -0,0 +1,352 @@ +from typing import List, Dict, Any, Optional +from abc import ABC, abstractmethod +from dataclasses import dataclass, asdict + +@dataclass +class Token: + type: str + value: Any + + +@dataclass +class Operation: + function: str + type: str + value: Any + + +@dataclass +class StackItem: + type: str + value: Any + + +@dataclass +class RuntimeError: + message: str + + +@dataclass +class TestCase: + name: str + code: str + tokens: List[Dict[str, Any]] + operations: Optional[List[Dict[str, Any]]] = None + stack_final: Optional[List[Dict[str, Any]]] = None + runtime_error: Optional[Dict[str, str]] = None + + +def to_dict(obj) -> Dict[str, Any]: + """Convert dataclass to dict, removing None values.""" + if obj is None: + raise ValueError("Obj cannot be None") + d = asdict(obj) if hasattr(obj, '__dataclass_fields__') else obj + return {k: v for k, v in d.items() if v is not None} + + +class BaseTestGenerator(ABC): + """ + Abstract base class for test case generators. + + Provides common functionality for generating test cases including + test creation, operation building, and error handling. + """ + + def __init__(self): + """Initialize the test generator with an empty test list.""" + self.tests: List[Dict[str, Any]] = [] + + # ========================================================================= + # Test Case Management + # ========================================================================= + + def add_test(self, name: str, code: str, tokens: List[Token], + operations: Optional[List[Operation]] = None, + stack_final: Optional[List[StackItem]] = None, + runtime_error: Optional[RuntimeError] = None): + """ + Add a test case to the test suite. + + Args: + name: Descriptive name for the test + code: Source code being tested + tokens: List of expected tokens from lexing + operations: Optional list of operations for evaluation phase + stack_final: Optional final stack state after execution + runtime_error: Optional runtime error if test should fail + """ + test = TestCase( + name=name, + code=code, + tokens=[to_dict(t) for t in tokens], + operations=[to_dict(o) for o in operations] if operations else None, + stack_final=[to_dict(s) for s in stack_final] if stack_final else None, + runtime_error=to_dict(runtime_error) if runtime_error else None + ) + self.tests.append(to_dict(test)) + + # ========================================================================= + # Factory Methods + # ========================================================================= + + def make_push_op(self, type_name: str, value: Any) -> Operation: + """ + Create a push operation. + + Args: + type_name: Type of the value being pushed + value: The value to push + + Returns: + Operation object representing a push + """ + return Operation(function="push", type=type_name, value=value) + + def make_stack_item(self, type_name: str, value: Any) -> StackItem: + """ + Create a stack item. + + Args: + type_name: Type of the stack item + value: The value of the stack item + + Returns: + StackItem object + """ + return StackItem(type=type_name, value=value) + + def make_error_token(self, message: str) -> Token: + """ + Create an error token. + + Args: + message: Error message + + Returns: + Token object with type "error" + """ + return Token(type="error", value=message) + + def make_runtime_error(self, message: str) -> RuntimeError: + """ + Create a runtime error. + + Args: + message: Error message + + Returns: + RuntimeError object + """ + return RuntimeError(message=message) + + # ========================================================================= + # Convenience Test Creators + # ========================================================================= + + def make_success_test(self, name: str, code: str, type_name: str, value: Any): + """ + Create a successful test case with standard push operation. + + Args: + name: Test name + code: Source code + type_name: Type of the value + value: The value + """ + token = Token(type=type_name, value=value) + op = self.make_push_op(type_name, value) + stack = self.make_stack_item(type_name, value) + self.add_test(name, code, [token], [op], [stack]) + + def make_error_test(self, name: str, code: str, error_msg: str): + """ + Create an error test case (lexing error). + + Args: + name: Test name + code: Source code + error_msg: Expected error message + """ + token = self.make_error_token(error_msg) + self.add_test(name, code, [token]) + + def make_runtime_error_test(self, name: str, code: str, tokens: List[Token], + operations: List[Operation], error_msg: str): + """ + Create a runtime error test case. + + Args: + name: Test name + code: Source code + tokens: Tokens that were successfully lexed + operations: Operations that led to the error + error_msg: Expected runtime error message + """ + runtime_error = self.make_runtime_error(error_msg) + self.add_test(name, code, tokens, operations, None, runtime_error) + + def make_empty_test(self, name: str, code: str): + """ + Create a test case with empty result (e.g., comments, whitespace). + + Args: + name: Test name + code: Source code + """ + self.add_test(name, code, [], [], []) + + # ========================================================================= + # Multi-Value Test Helpers + # ========================================================================= + + def make_multi_value_test(self, name: str, code: str, + values: List[tuple[str, Any]]): + """ + Create a test with multiple values on the stack. + + Args: + name: Test name + code: Source code + values: List of (type_name, value) tuples in stack order + """ + tokens = [Token(type=t, value=v) for t, v in values] + operations = [self.make_push_op(t, v) for t, v in values] + stack = [self.make_stack_item(t, v) for t, v in values] + self.add_test(name, code, tokens, operations, stack) + + # ========================================================================= + # Test Suite Generation + # ========================================================================= + + @abstractmethod + def generate_all_tests(self) -> List[Dict[str, Any]]: + """ + Generate all test cases for this generator. + + Must be implemented by subclasses to define their specific test suite. + + Returns: + List of test case dictionaries ready for serialization + """ + pass + + def get_tests(self) -> List[Dict[str, Any]]: + """ + Get the current list of tests. + + Returns: + List of test case dictionaries + """ + return self.tests + + def clear_tests(self): + """Clear all tests from the generator.""" + self.tests = [] + + def test_count(self) -> int: + """ + Get the number of tests generated. + + Returns: + Number of tests + """ + return len(self.tests) + + @classmethod + def generate_tests(cls) -> List[Dict[str, Any]]: + gen = cls() + tests = gen.generate_all_tests() + gen.print_statistics() + return tests + + # ========================================================================= + # Test Organization Helpers + # ========================================================================= + + def add_test_group_comment(self, comment: str): + """ + Add a comment to organize test groups (for documentation). + + Note: This doesn't add an actual test, just tracks organization + in subclass implementations. + + Args: + comment: Comment describing the test group + """ + # Subclasses can override to add metadata or logging + pass + + # ========================================================================= + # Validation Helpers + # ========================================================================= + + def validate_test_names_unique(self) -> bool: + """ + Check if all test names are unique. + + Returns: + True if all test names are unique, False otherwise + """ + names = [test['name'] for test in self.tests] + return len(names) == len(set(names)) + + def get_duplicate_test_names(self) -> List[str]: + """ + Get list of duplicate test names. + + Returns: + List of test names that appear more than once + """ + names = [test['name'] for test in self.tests] + seen = set() + duplicates = set() + for name in names: + if name in seen: + duplicates.add(name) + seen.add(name) + return list(duplicates) + + # ========================================================================= + # Statistics + # ========================================================================= + + def get_test_statistics(self) -> Dict[str, int]: + """ + Get statistics about the generated tests. + + Returns: + Dictionary with test statistics + """ + stats = { + 'total': len(self.tests), + 'success': 0, + 'lex_error': 0, + 'runtime_error': 0, + 'empty': 0, + } + + for test in self.tests: + if test.get('runtime_error'): + stats['runtime_error'] += 1 + elif test.get('tokens') and test['tokens'][0].get('type') == 'error': + stats['lex_error'] += 1 + elif not test.get('tokens'): + stats['empty'] += 1 + else: + stats['success'] += 1 + + return stats + + def print_statistics(self): + """Print test statistics to console.""" + stats = self.get_test_statistics() + print(f"Test Statistics for {self.__class__.__name__}:") + print(f" Total tests: {stats['total']}") + print(f" Success tests: {stats['success']}") + print(f" Lex error tests: {stats['lex_error']}") + print(f" Runtime error tests: {stats['runtime_error']}") + print(f" Empty tests: {stats['empty']}") + + if not self.validate_test_names_unique(): + duplicates = self.get_duplicate_test_names() + print(f" WARNING: Duplicate test names found: {duplicates}") diff --git a/SLS_Tests/generate_tests/float_tests.py b/SLS_Tests/generate_tests/float_tests.py index bf70c0f..302ecd0 100644 --- a/SLS_Tests/generate_tests/float_tests.py +++ b/SLS_Tests/generate_tests/float_tests.py @@ -1,9 +1,9 @@ -from typing import List, Dict, Any, Optional -from .utils import Token, Operation, StackItem, RuntimeError, TestCase, to_dict +from typing import List, Dict, Any +from .base_tests import BaseTestGenerator -class FloatTestGenerator: +class FloatTestGenerator(BaseTestGenerator): """Generate test cases for floating point literals.""" - + # Special float values SPECIAL_VALUES = { 'f32': { @@ -19,49 +19,7 @@ class FloatTestGenerator: 'epsilon': 2.220446049250313e-16, } } - - def __init__(self): - self.tests = [] - - def add_test(self, name: str, code: str, tokens: List[Token], - operations: Optional[List[Operation]] = None, - stack_final: Optional[List[StackItem]] = None, - runtime_error: Optional[RuntimeError] = None): - """Add a test case.""" - test = TestCase( - name=name, - code=code, - tokens=[to_dict(t) for t in tokens], - operations=[to_dict(o) for o in operations] if operations else None, - stack_final=[to_dict(s) for s in stack_final] if stack_final else None, - runtime_error=to_dict(runtime_error) if runtime_error else None - ) - self.tests.append(to_dict(test)) - - def make_push_op(self, type_name: str, value: Any) -> Operation: - """Create a push operation.""" - return Operation(function="push", type=type_name, value=value) - - def make_stack_item(self, type_name: str, value: Any) -> StackItem: - """Create a stack item.""" - return StackItem(type=type_name, value=value) - - def make_error_token(self, message: str) -> Token: - """Create an error token.""" - return Token(type="error", value=message) - - def make_success_test(self, name: str, code: str, type_name: str, value: float): - """Create a successful test case.""" - token = Token(type=type_name, value=value) - op = self.make_push_op(type_name, value) - stack = self.make_stack_item(type_name, value) - self.add_test(name, code, [token], [op], [stack]) - - def make_error_test(self, name: str, code: str, error_msg: str): - """Create an error test case.""" - token = self.make_error_token(error_msg) - self.add_test(name, code, [token]) - + def generate_basic_tests(self): """Generate basic test cases.""" # Simple default floats (f64) @@ -69,72 +27,72 @@ class FloatTestGenerator: self.make_success_test("Float Default Zero", "0.0", "f64", 0.0) self.make_success_test("Float Default Negative", "-2.5", "f64", -2.5) self.make_success_test("Float Default One", "1.0", "f64", 1.0) - + # Simple with type annotation self.make_success_test("Float f32 Simple", "3.14:f32", "f32", 3.14) self.make_success_test("Float f64 Simple", "2.718:f64", "f64", 2.718) - + def generate_format_tests(self): """Generate tests for different float formats.""" # Leading zeros self.make_success_test("Float Default Leading Zeros", "00042.5", "f64", 42.5) self.make_success_test("Float Default Leading Zero Decimal", "0.5", "f64", 0.5) - + # Trailing zeros self.make_success_test("Float Default Trailing Zeros", "3.1400", "f64", 3.14) - + # No leading digit self.make_success_test("Float Default No Leading Digit", ".5", "f64", 0.5) self.make_success_test("Float Default No Leading Digit Negative", "-.25", "f64", -0.25) - + # No trailing digits self.make_success_test("Float Default No Trailing Digits", "42.", "f64", 42.0) self.make_success_test("Float Default No Trailing Digits Negative", "-7.", "f64", -7.0) - + # Scientific notation self.make_success_test("Float Default Scientific Positive Exp", "1.5e10", "f64", 1.5e10) self.make_success_test("Float Default Scientific Negative Exp", "2.5e-5", "f64", 2.5e-5) self.make_success_test("Float Default Scientific Capital E", "3.14E8", "f64", 3.14e8) self.make_success_test("Float Default Scientific Plus Sign", "1.0e+3", "f64", 1000.0) - + # Very small numbers self.make_success_test("Float Default Very Small", "0.000001", "f64", 0.000001) self.make_success_test("Float Default Scientific Very Small", "1.0e-20", "f64", 1.0e-20) - + # Very large numbers self.make_success_test("Float Default Very Large", "1000000.0", "f64", 1000000.0) self.make_success_test("Float Default Scientific Very Large", "1.0e20", "f64", 1.0e20) - + def generate_underscore_tests(self): """Generate tests for underscores in floats.""" # Underscores in integer part - self.make_success_test("Float Default Underscore Integer Part", + self.make_success_test("Float Default Underscore Integer Part", "1_000_000.5", "f64", 1000000.5) - + # Underscores in decimal part self.make_success_test("Float Default Underscore Decimal Part", "3.141_592_653", "f64", 3.141592653) - + # Underscores in both parts self.make_success_test("Float Default Underscore Both Parts", "1_234.567_89", "f64", 1234.56789) - + # Underscores in scientific notation self.make_success_test("Float Default Underscore Scientific Mantissa", "1_000.5e10", "f64", 1000.5e10) self.make_success_test("Float Default Underscore Scientific Exponent", "1.5e1_0", "f64", 1.5e10) - + # Trailing underscore self.make_success_test("Float Default Underscore Trailing", "42.5_", "f64", 42.5) - + # Double underscore self.make_success_test("Float Default Underscore Double", "4__2.5", "f64", 42.5) - + # With type annotation self.make_success_test("Float f32 With Underscores", "1_234.567_89:f32", "f32", 1234.56789) - + def generate_special_value_tests(self): """Generate tests for special float values.""" # Infinity @@ -142,42 +100,42 @@ class FloatTestGenerator: self.make_success_test("Float Default Negative Infinity", "-inf", "f64", float('-inf')) self.make_success_test("Float f32 Positive Infinity", "inf:f32", "f32", float('inf')) self.make_success_test("Float f32 Negative Infinity", "-inf:f32", "f32", float('-inf')) - + # NaN self.make_success_test("Float Default NaN", "nan", "f64", float('nan')) self.make_success_test("Float f32 NaN", "nan:f32", "f32", float('nan')) - + # Note: NaN comparison is special - NaN != NaN, so these tests may need # special handling in the test runner - + def generate_edge_case_tests(self, type_name: str): """Generate edge case tests for a specific float type.""" values = self.SPECIAL_VALUES[type_name] - + # Maximum value self.make_success_test(f"Float {type_name} Max Value", f"{values['max']}:{type_name}", type_name, values['max']) - + # Minimum value (most negative) self.make_success_test(f"Float {type_name} Min Value", f"{values['min']}:{type_name}", type_name, values['min']) - + # Smallest positive normalized value self.make_success_test(f"Float {type_name} Min Positive", - f"{values['min_positive']}:{type_name}", + f"{values['min_positive']}:{type_name}", type_name, values['min_positive']) - + # Machine epsilon self.make_success_test(f"Float {type_name} Epsilon", f"{values['epsilon']}:{type_name}", type_name, values['epsilon']) - + # Near zero self.make_success_test(f"Float {type_name} Near Zero Positive", f"1e-30:{type_name}", type_name, 1e-30) self.make_success_test(f"Float {type_name} Near Zero Negative", f"-1e-30:{type_name}", type_name, -1e-30) - + # Subnormal numbers if type_name == 'f64': self.make_success_test("Float f64 Subnormal", @@ -185,7 +143,7 @@ class FloatTestGenerator: elif type_name == 'f32': self.make_success_test("Float f32 Subnormal", "1e-40:f32", "f32", 1e-40) - + def generate_overflow_tests(self): """Generate overflow tests.""" # f32 overflow @@ -195,7 +153,7 @@ class FloatTestGenerator: self.make_error_test("Float f32 Overflow Negative", "-1e40:f32", "Float overflow: value exceeds range for f32.") - + # f64 overflow (extremely large values) self.make_error_test("Float f64 Overflow Positive", "1e310:f64", @@ -203,7 +161,7 @@ class FloatTestGenerator: self.make_error_test("Float f64 Overflow Negative", "-1e310:f64", "Float overflow: value exceeds range for f64.") - + def generate_precision_tests(self): """Generate tests for precision limits.""" # f32 precision (~7 decimal digits) @@ -211,19 +169,19 @@ class FloatTestGenerator: "1.2345678:f32", "f32", 1.2345678) self.make_success_test("Float f32 High Precision", "3.141592653589793:f32", "f32", 3.141592653589793) - + # f64 precision (~15 decimal digits) self.make_success_test("Float f64 Precision Limit", "1.234567890123456:f64", "f64", 1.234567890123456) self.make_success_test("Float f64 High Precision", "3.141592653589793238:f64", "f64", 3.141592653589793238) - + # Very close numbers self.make_success_test("Float f64 Close Numbers 1", "1.0000000000000001:f64", "f64", 1.0000000000000001) self.make_success_test("Float f64 Close Numbers 2", "1.0000000000000002:f64", "f64", 1.0000000000000002) - + def generate_error_tests(self): """Generate error tests.""" # Invalid formats @@ -239,7 +197,7 @@ class FloatTestGenerator: self.make_error_test("Float Invalid Characters", "3.1a4", "Invalid float literal: unexpected 'a' in float.") - + # Invalid scientific notation self.make_error_test("Float Invalid Scientific No Exponent", "3.14e", @@ -250,7 +208,7 @@ class FloatTestGenerator: self.make_error_test("Float Invalid Scientific Invalid Exponent", "3.14eX", "Invalid float literal: invalid exponent 'X'.") - + # Invalid underscores self.make_error_test("Float Invalid Leading Underscore", "_3.14", @@ -261,7 +219,7 @@ class FloatTestGenerator: self.make_error_test("Float Invalid Underscore After Decimal", "3._14", "Invalid float literal: underscore after decimal point.") - + # Invalid type annotations self.make_error_test("Float Invalid Type Annotation", "3.14:i32", @@ -269,12 +227,12 @@ class FloatTestGenerator: self.make_error_test("Float Invalid Type Name", "3.14:f16", "Invalid type annotation: unknown type 'f16'.") - + # Comma separators not allowed self.make_error_test("Float Invalid Comma Separator", "1,234.56", "Invalid float literal: unexpected ',' in float.") - + def generate_whitespace_tests(self): """Generate tests with whitespace.""" self.make_success_test("Float Default Leading Whitespace", @@ -285,7 +243,7 @@ class FloatTestGenerator: " 3.14 ", "f64", 3.14) self.make_success_test("Float f32 With Whitespace", " 2.718:f32 ", "f32", 2.718) - + def generate_mathematical_constants_tests(self): """Generate tests for common mathematical constants.""" # Pi @@ -293,75 +251,64 @@ class FloatTestGenerator: "3.141592653589793", "f64", 3.141592653589793) self.make_success_test("Float f32 Pi Approximate", "3.1415927:f32", "f32", 3.1415927) - + # Euler's number self.make_success_test("Float Default Euler Approximate", "2.718281828459045", "f64", 2.718281828459045) self.make_success_test("Float f32 Euler Approximate", "2.7182817:f32", "f32", 2.7182817) - + # Golden ratio self.make_success_test("Float Default Golden Ratio", "1.618033988749895", "f64", 1.618033988749895) - + # Square root of 2 self.make_success_test("Float Default Sqrt2", "1.4142135623730951", "f64", 1.4142135623730951) - + def generate_signed_zero_tests(self): """Generate tests for signed zeros.""" self.make_success_test("Float Default Positive Zero", "0.0", "f64", 0.0) self.make_success_test("Float Default Negative Zero", "-0.0", "f64", -0.0) self.make_success_test("Float f32 Positive Zero", "0.0:f32", "f32", 0.0) self.make_success_test("Float f32 Negative Zero", "-0.0:f32", "f32", -0.0) - + # Note: In IEEE 754, +0.0 and -0.0 are distinct values but compare equal - + def generate_all_tests(self) -> List[Dict[str, Any]]: """Generate all test cases.""" # Basic tests self.generate_basic_tests() - + # Format variations self.generate_format_tests() - + # Underscores self.generate_underscore_tests() - + # Special values (inf, nan) self.generate_special_value_tests() - + # Edge cases for each type for type_name in ['f32', 'f64']: self.generate_edge_case_tests(type_name) - + # Overflow tests self.generate_overflow_tests() - + # Precision tests self.generate_precision_tests() - + # Error tests self.generate_error_tests() - + # Whitespace tests self.generate_whitespace_tests() - + # Mathematical constants self.generate_mathematical_constants_tests() - + # Signed zeros self.generate_signed_zero_tests() - + return self.tests - - -def generate_float_literal_tests() -> List[Dict[str, Any]]: - """ - Main function to generate all floating point literal test cases. - - Returns: - List of test case dictionaries ready for serialization - """ - generator = FloatTestGenerator() - return generator.generate_all_tests() diff --git a/SLS_Tests/generate_tests/general_tests.py b/SLS_Tests/generate_tests/general_tests.py new file mode 100644 index 0000000..a0c75d9 --- /dev/null +++ b/SLS_Tests/generate_tests/general_tests.py @@ -0,0 +1,11 @@ +from typing import List, Dict, Any +from .base_tests import BaseTestGenerator + +class GeneralTestGenerator(BaseTestGenerator): + + def generate_all_tests(self) -> List[Dict[str, Any]]: + """Generate all test cases.""" + + self.add_test("Empty_Statement", "", [], [], []) + + return self.tests diff --git a/SLS_Tests/generate_tests/integer_tests.py b/SLS_Tests/generate_tests/integer_tests.py index 896d9df..55e6b8a 100644 --- a/SLS_Tests/generate_tests/integer_tests.py +++ b/SLS_Tests/generate_tests/integer_tests.py @@ -1,9 +1,9 @@ -from typing import List, Dict, Any, Optional -from .utils import Token, Operation, StackItem, RuntimeError, TestCase, to_dict +from typing import List, Dict, Any +from .base_tests import BaseTestGenerator -class IntegerTestGenerator: +class IntegerTestGenerator(BaseTestGenerator): """Generate test cases for integer literals.""" - + # Type ranges TYPE_RANGES = { 'i8': (-128, 127), @@ -15,97 +15,53 @@ class IntegerTestGenerator: 'u32': (0, 4294967295), 'u64': (0, 18446744073709551615), } - - def __init__(self): - self.tests = [] - - def add_test(self, name: str, code: str, tokens: List[Token], - operations: Optional[List[Operation]] = None, - stack_final: Optional[List[StackItem]] = None, - runtime_error: Optional[RuntimeError] = None): - """Add a test case.""" - test = TestCase( - name=name, - code=code, - tokens=[to_dict(t) for t in tokens], - operations=[to_dict(o) for o in operations] if operations else None, - stack_final=[to_dict(s) for s in stack_final] if stack_final else None, - runtime_error=to_dict(runtime_error) if runtime_error else None - ) - self.tests.append(to_dict(test)) - - def make_push_op(self, type_name: str, value: Any) -> Operation: - """Create a push operation.""" - return Operation(function="push", type=type_name, value=value) - - def make_stack_item(self, type_name: str, value: Any) -> StackItem: - """Create a stack item.""" - return StackItem(type=type_name, value=value) - - def make_error_token(self, message: str) -> Token: - """Create an error token.""" - return Token(type="error", value=message) - - def make_success_test(self, name: str, code: str, type_name: str, value: Any): - """Create a successful test case.""" - token = Token(type=type_name, value=value) - op = self.make_push_op(type_name, value) - stack = self.make_stack_item(type_name, value) - self.add_test(name, code, [token], [op], [stack]) - - def make_error_test(self, name: str, code: str, error_msg: str): - """Create an error test case.""" - token = self.make_error_token(error_msg) - self.add_test(name, code, [token]) - + def generate_basic_tests(self): """Generate basic test cases.""" - # Empty statement - self.add_test("Empty_Statement", "", [], [], []) - + # Simple default integers self.make_success_test("Integer Default Decimal 0", "0", "i64", 0) self.make_success_test("Integer Default Decimal -1", "-1", "i64", -1) self.make_success_test("Integer Default Decimal 42", "42", "i64", 42) self.make_success_test("Integer Default Decimal Leading Zeros", "00042", "i64", 42) - + def generate_default_base_tests(self): """Generate tests for default type with different bases.""" # Hexadecimal self.make_success_test("Integer Default Hex 0xFF", "0xFF", "i64", 255) self.make_success_test("Integer Default Hex 0xdeadbeef", "0xdeadbeef", "i64", 3735928559) self.make_success_test("Integer Default Hex Max", "0x7FFFFFFFFFFFFFFF", "i64", 9223372036854775807) - + # Binary self.make_success_test("Integer Default Binary 0b1010", "0b1010", "i64", 10) self.make_success_test("Integer Default Binary All Ones", "0b1111111111111111", "i64", 65535) - + # Octal self.make_success_test("Integer Default Octal 0o755", "0o755", "i64", 493) self.make_success_test("Integer Default Octal Max Three Digits", "0o777", "i64", 511) - + def generate_default_edge_cases(self): """Generate edge case tests for default type.""" # Min/max values - self.make_success_test("Integer Default Decimal Max i64", + self.make_success_test("Integer Default Decimal Max i64", "9223372036854775807", "i64", 9223372036854775807) self.make_success_test("Integer Default Decimal Min i64", "-9223372036854775808", "i64", -9223372036854775808) - + # Underscores self.make_success_test("Integer Default Decimal with Underscore", "1_000_000", "i64", 1000000) self.make_success_test("Integer Default Underscore End", "42_", "i64", 42) self.make_success_test("Integer Default Underscore Double", "4__2", "i64", 42) - + # Whitespace self.make_success_test("Integer Default Whitespace", " 42 ", "i64", 42) - + # Zeros in different bases self.make_success_test("Integer Default Hex Zero", "0x0", "i64", 0) self.make_success_test("Integer Default Binary Zero", "0b0", "i64", 0) self.make_success_test("Integer Default Octal Zero", "0o0", "i64", 0) - + def generate_default_error_tests(self): """Generate error tests for default type.""" self.make_error_test("Integer Default Decimal with Commas Invalid", @@ -117,126 +73,126 @@ class IntegerTestGenerator: self.make_error_test("Integer Default Invalid Prefix", "0b2", "Invalid binary literal: unexpected '2' in binary integer.") - + def generate_typed_tests(self, type_name: str): """Generate tests for a specific type across all bases.""" min_val, max_val = self.TYPE_RANGES[type_name] is_unsigned = type_name.startswith('u') - + # Basic decimal test_val = 42 if max_val >= 42 else max_val self.make_success_test(f"Integer {type_name} Decimal Positive", f"{test_val}:{type_name}", type_name, test_val) - + # Zero self.make_success_test(f"Integer {type_name} Zero", f"0:{type_name}", type_name, 0) - + # Negative (only for signed types) if not is_unsigned: neg_val = -100 if min_val <= -100 else min_val self.make_success_test(f"Integer {type_name} Decimal Negative", f"{neg_val}:{type_name}", type_name, neg_val) - + # Hexadecimal hex_val = min(255, max_val) self.make_success_test(f"Integer {type_name} Hex", f"0x{hex_val:X}:{type_name}", type_name, hex_val) - + # Binary bin_val = min(15, max_val) self.make_success_test(f"Integer {type_name} Binary", f"0b{bin_val:b}:{type_name}", type_name, bin_val) - + # Octal oct_val = min(63, max_val) self.make_success_test(f"Integer {type_name} Octal", f"0o{oct_val:o}:{type_name}", type_name, oct_val) - + # Max value self.make_success_test(f"Integer {type_name} Max Value", f"{max_val}:{type_name}", type_name, max_val) - + # Min value self.make_success_test(f"Integer {type_name} Min Value", f"{min_val}:{type_name}", type_name, min_val) - + # Overflow overflow_val = max_val + 1 self.make_error_test(f"Integer {type_name} Overflow", f"{overflow_val}:{type_name}", f"Integer overflow: value exceeds range for {type_name}.") - + # Underflow underflow_val = min_val - 1 self.make_error_test(f"Integer {type_name} Underflow", f"{underflow_val}:{type_name}", f"Integer overflow: value exceeds range for {type_name}.") - + def generate_special_typed_tests(self, type_name: str): """Generate special tests for specific types.""" min_val, max_val = self.TYPE_RANGES[type_name] is_unsigned = type_name.startswith('u') - + # Underscores with type annotation if max_val >= 1000000: self.make_success_test(f"Integer {type_name} With Underscores", f"1_000_000:{type_name}", type_name, 1000000) - + # Special values for specific types if type_name == 'i8': self.make_success_test("Integer i8 Hex Max", "0x7F:i8", "i8", 127) self.make_success_test("Integer i8 Binary Max", "0b01111111:i8", "i8", 127) self.make_success_test("Integer i8 Octal Max", "0o177:i8", "i8", 127) self.make_success_test("Integer i8 Negative Hex", "-0x80:i8", "i8", -128) - + elif type_name == 'u8': self.make_success_test("Integer u8 Hex Max", "0xFF:u8", "u8", 255) self.make_success_test("Integer u8 Binary Max", "0b11111111:u8", "u8", 255) self.make_success_test("Integer u8 Octal Max", "0o377:u8", "u8", 255) - + elif type_name == 'i16': self.make_success_test("Integer i16 Hex Sample", "0x1234:i16", "i16", 4660) - self.make_success_test("Integer i16 Binary Sample", + self.make_success_test("Integer i16 Binary Sample", "0b1111111100000000:i16", "i16", 65280) self.make_success_test("Integer i16 Octal Sample", "0o1234:i16", "i16", 668) - + elif type_name == 'u16': self.make_success_test("Integer u16 Hex Max", "0xFFFF:u16", "u16", 65535) - self.make_success_test("Integer u16 Binary Max", + self.make_success_test("Integer u16 Binary Max", "0b1111111111111111:u16", "u16", 65535) self.make_success_test("Integer u16 Octal Max", "0o177777:u16", "u16", 65535) self.make_success_test("Integer u16 Decimal Mid", "50000:u16", "u16", 50000) - + elif type_name == 'i32': self.make_success_test("Integer i32 Hex Sample", "0xABCD:i32", "i32", 43981) - self.make_success_test("Integer i32 Binary Sample", + self.make_success_test("Integer i32 Binary Sample", "0b11110000:i32", "i32", 240) - + elif type_name == 'u32': self.make_success_test("Integer u32 Hex Max", "0xFFFFFFFF:u32", "u32", 4294967295) self.make_success_test("Integer u32 Binary Sample", - "0b11111111000000001111111100000000:u32", + "0b11111111000000001111111100000000:u32", "u32", 4278255360) - self.make_success_test("Integer u32 Octal Max", + self.make_success_test("Integer u32 Octal Max", "0o37777777777:u32", "u32", 4294967295) self.make_success_test("Integer u32 Decimal Mid", "1000000:u32", "u32", 1000000) - + elif type_name == 'i64': self.make_success_test("Integer i64 Decimal Positive 42", "42:i64", "i64", 42) self.make_success_test("Integer i64 Hex 0xFF", "0xFF:i64", "i64", 255) self.make_success_test("Integer i64 Binary 0b1010", "0b1010:i64", "i64", 10) self.make_success_test("Integer i64 Octal 0o755", "0o755:i64", "i64", 493) - + elif type_name == 'u64': - self.make_success_test("Integer u64 Hex Max", - "0xFFFFFFFFFFFFFFFF:u64", + self.make_success_test("Integer u64 Hex Max", + "0xFFFFFFFFFFFFFFFF:u64", "u64", 18446744073709551615) - self.make_success_test("Integer u64 Binary Sample", + self.make_success_test("Integer u64 Binary Sample", "0b1010101010101010:u64", "u64", 43690) self.make_success_test("Integer u64 Octal Sample", "0o7777:u64", "u64", 4095) self.make_success_test("Integer u64 Decimal", "42:u64", "u64", 42) - + def generate_underscore_tests(self): """Generate tests for underscores in different bases.""" self.make_success_test("Integer Hex With Underscores", @@ -245,34 +201,23 @@ class IntegerTestGenerator: "0b1111_0000_1010_0101:i32", "i32", 61605) self.make_success_test("Integer Octal With Underscores", "0o7_7_7:i16", "i16", 511) - + def generate_all_tests(self) -> List[Dict[str, Any]]: """Generate all test cases.""" # Basic tests self.generate_basic_tests() - + # Default type (i64) comprehensive tests self.generate_default_base_tests() self.generate_default_edge_cases() self.generate_default_error_tests() - + # Tests for each specific type for type_name in ['i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64']: self.generate_typed_tests(type_name) self.generate_special_typed_tests(type_name) - + # Additional edge cases self.generate_underscore_tests() - + return self.tests - - -def generate_integer_literal_tests() -> List[Dict[str, Any]]: - """ - Main function to generate all integer literal test cases. - - Returns: - List of test case dictionaries ready for serialization - """ - generator = IntegerTestGenerator() - return generator.generate_all_tests() diff --git a/SLS_Tests/generate_tests/utils.py b/SLS_Tests/generate_tests/utils.py deleted file mode 100644 index 126e5d6..0000000 --- a/SLS_Tests/generate_tests/utils.py +++ /dev/null @@ -1,45 +0,0 @@ - -from typing import List, Dict, Any, Optional -from dataclasses import dataclass, asdict - - -@dataclass -class Token: - type: str - value: Any - - -@dataclass -class Operation: - function: str - type: str - value: Any - - -@dataclass -class StackItem: - type: str - value: Any - - -@dataclass -class RuntimeError: - message: str - - -@dataclass -class TestCase: - name: str - code: str - tokens: List[Dict[str, Any]] - operations: Optional[List[Dict[str, Any]]] = None - stack_final: Optional[List[Dict[str, Any]]] = None - runtime_error: Optional[Dict[str, str]] = None - - -def to_dict(obj): - """Convert dataclass to dict, removing None values.""" - if obj is None: - return None - d = asdict(obj) if hasattr(obj, '__dataclass_fields__') else obj - return {k: v for k, v in d.items() if v is not None}