Added base test generator class
This commit is contained in:
parent
cf5f51ccd8
commit
375c3f2422
|
|
@ -3,35 +3,22 @@ Test case generator for integer literals in the Stack Language.
|
||||||
Generates comprehensive test cases for all integer types and bases.
|
Generates comprehensive test cases for all integer types and bases.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .integer_tests import generate_integer_literal_tests
|
from .general_tests import GeneralTestGenerator
|
||||||
from .float_tests import generate_float_literal_tests
|
from .integer_tests import IntegerTestGenerator
|
||||||
|
from .float_tests import FloatTestGenerator
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# import json
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
# Generate tests
|
# Generate tests
|
||||||
tests = []
|
tests = []
|
||||||
tests += generate_integer_literal_tests()
|
tests += GeneralTestGenerator.generate_tests()
|
||||||
tests += generate_float_literal_tests()
|
tests += IntegerTestGenerator.generate_tests()
|
||||||
|
tests += FloatTestGenerator.generate_tests()
|
||||||
|
|
||||||
# Print summary
|
# Print summary
|
||||||
print(f"Generated {len(tests)} test cases")
|
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
|
# Save as YAML
|
||||||
with open("cases.yaml", "w") as f:
|
with open("cases.yaml", "w") as f:
|
||||||
yaml.dump(tests, f, default_flow_style=False, sort_keys=False)
|
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))
|
|
||||||
|
|
|
||||||
|
|
@ -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}")
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any
|
||||||
from .utils import Token, Operation, StackItem, RuntimeError, TestCase, to_dict
|
from .base_tests import BaseTestGenerator
|
||||||
|
|
||||||
class FloatTestGenerator:
|
class FloatTestGenerator(BaseTestGenerator):
|
||||||
"""Generate test cases for floating point literals."""
|
"""Generate test cases for floating point literals."""
|
||||||
|
|
||||||
# Special float values
|
# Special float values
|
||||||
SPECIAL_VALUES = {
|
SPECIAL_VALUES = {
|
||||||
'f32': {
|
'f32': {
|
||||||
|
|
@ -19,49 +19,7 @@ class FloatTestGenerator:
|
||||||
'epsilon': 2.220446049250313e-16,
|
'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):
|
def generate_basic_tests(self):
|
||||||
"""Generate basic test cases."""
|
"""Generate basic test cases."""
|
||||||
# Simple default floats (f64)
|
# 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 Zero", "0.0", "f64", 0.0)
|
||||||
self.make_success_test("Float Default Negative", "-2.5", "f64", -2.5)
|
self.make_success_test("Float Default Negative", "-2.5", "f64", -2.5)
|
||||||
self.make_success_test("Float Default One", "1.0", "f64", 1.0)
|
self.make_success_test("Float Default One", "1.0", "f64", 1.0)
|
||||||
|
|
||||||
# Simple with type annotation
|
# Simple with type annotation
|
||||||
self.make_success_test("Float f32 Simple", "3.14:f32", "f32", 3.14)
|
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)
|
self.make_success_test("Float f64 Simple", "2.718:f64", "f64", 2.718)
|
||||||
|
|
||||||
def generate_format_tests(self):
|
def generate_format_tests(self):
|
||||||
"""Generate tests for different float formats."""
|
"""Generate tests for different float formats."""
|
||||||
# Leading zeros
|
# Leading zeros
|
||||||
self.make_success_test("Float Default Leading Zeros", "00042.5", "f64", 42.5)
|
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)
|
self.make_success_test("Float Default Leading Zero Decimal", "0.5", "f64", 0.5)
|
||||||
|
|
||||||
# Trailing zeros
|
# Trailing zeros
|
||||||
self.make_success_test("Float Default Trailing Zeros", "3.1400", "f64", 3.14)
|
self.make_success_test("Float Default Trailing Zeros", "3.1400", "f64", 3.14)
|
||||||
|
|
||||||
# No leading digit
|
# 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", ".5", "f64", 0.5)
|
||||||
self.make_success_test("Float Default No Leading Digit Negative", "-.25", "f64", -0.25)
|
self.make_success_test("Float Default No Leading Digit Negative", "-.25", "f64", -0.25)
|
||||||
|
|
||||||
# No trailing digits
|
# 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", "42.", "f64", 42.0)
|
||||||
self.make_success_test("Float Default No Trailing Digits Negative", "-7.", "f64", -7.0)
|
self.make_success_test("Float Default No Trailing Digits Negative", "-7.", "f64", -7.0)
|
||||||
|
|
||||||
# Scientific notation
|
# Scientific notation
|
||||||
self.make_success_test("Float Default Scientific Positive Exp", "1.5e10", "f64", 1.5e10)
|
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 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 Capital E", "3.14E8", "f64", 3.14e8)
|
||||||
self.make_success_test("Float Default Scientific Plus Sign", "1.0e+3", "f64", 1000.0)
|
self.make_success_test("Float Default Scientific Plus Sign", "1.0e+3", "f64", 1000.0)
|
||||||
|
|
||||||
# Very small numbers
|
# Very small numbers
|
||||||
self.make_success_test("Float Default Very Small", "0.000001", "f64", 0.000001)
|
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)
|
self.make_success_test("Float Default Scientific Very Small", "1.0e-20", "f64", 1.0e-20)
|
||||||
|
|
||||||
# Very large numbers
|
# Very large numbers
|
||||||
self.make_success_test("Float Default Very Large", "1000000.0", "f64", 1000000.0)
|
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)
|
self.make_success_test("Float Default Scientific Very Large", "1.0e20", "f64", 1.0e20)
|
||||||
|
|
||||||
def generate_underscore_tests(self):
|
def generate_underscore_tests(self):
|
||||||
"""Generate tests for underscores in floats."""
|
"""Generate tests for underscores in floats."""
|
||||||
# Underscores in integer part
|
# 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)
|
"1_000_000.5", "f64", 1000000.5)
|
||||||
|
|
||||||
# Underscores in decimal part
|
# Underscores in decimal part
|
||||||
self.make_success_test("Float Default Underscore Decimal Part",
|
self.make_success_test("Float Default Underscore Decimal Part",
|
||||||
"3.141_592_653", "f64", 3.141592653)
|
"3.141_592_653", "f64", 3.141592653)
|
||||||
|
|
||||||
# Underscores in both parts
|
# Underscores in both parts
|
||||||
self.make_success_test("Float Default Underscore Both Parts",
|
self.make_success_test("Float Default Underscore Both Parts",
|
||||||
"1_234.567_89", "f64", 1234.56789)
|
"1_234.567_89", "f64", 1234.56789)
|
||||||
|
|
||||||
# Underscores in scientific notation
|
# Underscores in scientific notation
|
||||||
self.make_success_test("Float Default Underscore Scientific Mantissa",
|
self.make_success_test("Float Default Underscore Scientific Mantissa",
|
||||||
"1_000.5e10", "f64", 1000.5e10)
|
"1_000.5e10", "f64", 1000.5e10)
|
||||||
self.make_success_test("Float Default Underscore Scientific Exponent",
|
self.make_success_test("Float Default Underscore Scientific Exponent",
|
||||||
"1.5e1_0", "f64", 1.5e10)
|
"1.5e1_0", "f64", 1.5e10)
|
||||||
|
|
||||||
# Trailing underscore
|
# Trailing underscore
|
||||||
self.make_success_test("Float Default Underscore Trailing", "42.5_", "f64", 42.5)
|
self.make_success_test("Float Default Underscore Trailing", "42.5_", "f64", 42.5)
|
||||||
|
|
||||||
# Double underscore
|
# Double underscore
|
||||||
self.make_success_test("Float Default Underscore Double", "4__2.5", "f64", 42.5)
|
self.make_success_test("Float Default Underscore Double", "4__2.5", "f64", 42.5)
|
||||||
|
|
||||||
# With type annotation
|
# With type annotation
|
||||||
self.make_success_test("Float f32 With Underscores",
|
self.make_success_test("Float f32 With Underscores",
|
||||||
"1_234.567_89:f32", "f32", 1234.56789)
|
"1_234.567_89:f32", "f32", 1234.56789)
|
||||||
|
|
||||||
def generate_special_value_tests(self):
|
def generate_special_value_tests(self):
|
||||||
"""Generate tests for special float values."""
|
"""Generate tests for special float values."""
|
||||||
# Infinity
|
# Infinity
|
||||||
|
|
@ -142,42 +100,42 @@ class FloatTestGenerator:
|
||||||
self.make_success_test("Float Default Negative Infinity", "-inf", "f64", float('-inf'))
|
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 Positive Infinity", "inf:f32", "f32", float('inf'))
|
||||||
self.make_success_test("Float f32 Negative Infinity", "-inf:f32", "f32", float('-inf'))
|
self.make_success_test("Float f32 Negative Infinity", "-inf:f32", "f32", float('-inf'))
|
||||||
|
|
||||||
# NaN
|
# NaN
|
||||||
self.make_success_test("Float Default NaN", "nan", "f64", float('nan'))
|
self.make_success_test("Float Default NaN", "nan", "f64", float('nan'))
|
||||||
self.make_success_test("Float f32 NaN", "nan:f32", "f32", 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
|
# Note: NaN comparison is special - NaN != NaN, so these tests may need
|
||||||
# special handling in the test runner
|
# special handling in the test runner
|
||||||
|
|
||||||
def generate_edge_case_tests(self, type_name: str):
|
def generate_edge_case_tests(self, type_name: str):
|
||||||
"""Generate edge case tests for a specific float type."""
|
"""Generate edge case tests for a specific float type."""
|
||||||
values = self.SPECIAL_VALUES[type_name]
|
values = self.SPECIAL_VALUES[type_name]
|
||||||
|
|
||||||
# Maximum value
|
# Maximum value
|
||||||
self.make_success_test(f"Float {type_name} Max Value",
|
self.make_success_test(f"Float {type_name} Max Value",
|
||||||
f"{values['max']}:{type_name}", type_name, values['max'])
|
f"{values['max']}:{type_name}", type_name, values['max'])
|
||||||
|
|
||||||
# Minimum value (most negative)
|
# Minimum value (most negative)
|
||||||
self.make_success_test(f"Float {type_name} Min Value",
|
self.make_success_test(f"Float {type_name} Min Value",
|
||||||
f"{values['min']}:{type_name}", type_name, values['min'])
|
f"{values['min']}:{type_name}", type_name, values['min'])
|
||||||
|
|
||||||
# Smallest positive normalized value
|
# Smallest positive normalized value
|
||||||
self.make_success_test(f"Float {type_name} Min Positive",
|
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'])
|
type_name, values['min_positive'])
|
||||||
|
|
||||||
# Machine epsilon
|
# Machine epsilon
|
||||||
self.make_success_test(f"Float {type_name} Epsilon",
|
self.make_success_test(f"Float {type_name} Epsilon",
|
||||||
f"{values['epsilon']}:{type_name}",
|
f"{values['epsilon']}:{type_name}",
|
||||||
type_name, values['epsilon'])
|
type_name, values['epsilon'])
|
||||||
|
|
||||||
# Near zero
|
# Near zero
|
||||||
self.make_success_test(f"Float {type_name} Near Zero Positive",
|
self.make_success_test(f"Float {type_name} Near Zero Positive",
|
||||||
f"1e-30:{type_name}", type_name, 1e-30)
|
f"1e-30:{type_name}", type_name, 1e-30)
|
||||||
self.make_success_test(f"Float {type_name} Near Zero Negative",
|
self.make_success_test(f"Float {type_name} Near Zero Negative",
|
||||||
f"-1e-30:{type_name}", type_name, -1e-30)
|
f"-1e-30:{type_name}", type_name, -1e-30)
|
||||||
|
|
||||||
# Subnormal numbers
|
# Subnormal numbers
|
||||||
if type_name == 'f64':
|
if type_name == 'f64':
|
||||||
self.make_success_test("Float f64 Subnormal",
|
self.make_success_test("Float f64 Subnormal",
|
||||||
|
|
@ -185,7 +143,7 @@ class FloatTestGenerator:
|
||||||
elif type_name == 'f32':
|
elif type_name == 'f32':
|
||||||
self.make_success_test("Float f32 Subnormal",
|
self.make_success_test("Float f32 Subnormal",
|
||||||
"1e-40:f32", "f32", 1e-40)
|
"1e-40:f32", "f32", 1e-40)
|
||||||
|
|
||||||
def generate_overflow_tests(self):
|
def generate_overflow_tests(self):
|
||||||
"""Generate overflow tests."""
|
"""Generate overflow tests."""
|
||||||
# f32 overflow
|
# f32 overflow
|
||||||
|
|
@ -195,7 +153,7 @@ class FloatTestGenerator:
|
||||||
self.make_error_test("Float f32 Overflow Negative",
|
self.make_error_test("Float f32 Overflow Negative",
|
||||||
"-1e40:f32",
|
"-1e40:f32",
|
||||||
"Float overflow: value exceeds range for f32.")
|
"Float overflow: value exceeds range for f32.")
|
||||||
|
|
||||||
# f64 overflow (extremely large values)
|
# f64 overflow (extremely large values)
|
||||||
self.make_error_test("Float f64 Overflow Positive",
|
self.make_error_test("Float f64 Overflow Positive",
|
||||||
"1e310:f64",
|
"1e310:f64",
|
||||||
|
|
@ -203,7 +161,7 @@ class FloatTestGenerator:
|
||||||
self.make_error_test("Float f64 Overflow Negative",
|
self.make_error_test("Float f64 Overflow Negative",
|
||||||
"-1e310:f64",
|
"-1e310:f64",
|
||||||
"Float overflow: value exceeds range for f64.")
|
"Float overflow: value exceeds range for f64.")
|
||||||
|
|
||||||
def generate_precision_tests(self):
|
def generate_precision_tests(self):
|
||||||
"""Generate tests for precision limits."""
|
"""Generate tests for precision limits."""
|
||||||
# f32 precision (~7 decimal digits)
|
# f32 precision (~7 decimal digits)
|
||||||
|
|
@ -211,19 +169,19 @@ class FloatTestGenerator:
|
||||||
"1.2345678:f32", "f32", 1.2345678)
|
"1.2345678:f32", "f32", 1.2345678)
|
||||||
self.make_success_test("Float f32 High Precision",
|
self.make_success_test("Float f32 High Precision",
|
||||||
"3.141592653589793:f32", "f32", 3.141592653589793)
|
"3.141592653589793:f32", "f32", 3.141592653589793)
|
||||||
|
|
||||||
# f64 precision (~15 decimal digits)
|
# f64 precision (~15 decimal digits)
|
||||||
self.make_success_test("Float f64 Precision Limit",
|
self.make_success_test("Float f64 Precision Limit",
|
||||||
"1.234567890123456:f64", "f64", 1.234567890123456)
|
"1.234567890123456:f64", "f64", 1.234567890123456)
|
||||||
self.make_success_test("Float f64 High Precision",
|
self.make_success_test("Float f64 High Precision",
|
||||||
"3.141592653589793238:f64", "f64", 3.141592653589793238)
|
"3.141592653589793238:f64", "f64", 3.141592653589793238)
|
||||||
|
|
||||||
# Very close numbers
|
# Very close numbers
|
||||||
self.make_success_test("Float f64 Close Numbers 1",
|
self.make_success_test("Float f64 Close Numbers 1",
|
||||||
"1.0000000000000001:f64", "f64", 1.0000000000000001)
|
"1.0000000000000001:f64", "f64", 1.0000000000000001)
|
||||||
self.make_success_test("Float f64 Close Numbers 2",
|
self.make_success_test("Float f64 Close Numbers 2",
|
||||||
"1.0000000000000002:f64", "f64", 1.0000000000000002)
|
"1.0000000000000002:f64", "f64", 1.0000000000000002)
|
||||||
|
|
||||||
def generate_error_tests(self):
|
def generate_error_tests(self):
|
||||||
"""Generate error tests."""
|
"""Generate error tests."""
|
||||||
# Invalid formats
|
# Invalid formats
|
||||||
|
|
@ -239,7 +197,7 @@ class FloatTestGenerator:
|
||||||
self.make_error_test("Float Invalid Characters",
|
self.make_error_test("Float Invalid Characters",
|
||||||
"3.1a4",
|
"3.1a4",
|
||||||
"Invalid float literal: unexpected 'a' in float.")
|
"Invalid float literal: unexpected 'a' in float.")
|
||||||
|
|
||||||
# Invalid scientific notation
|
# Invalid scientific notation
|
||||||
self.make_error_test("Float Invalid Scientific No Exponent",
|
self.make_error_test("Float Invalid Scientific No Exponent",
|
||||||
"3.14e",
|
"3.14e",
|
||||||
|
|
@ -250,7 +208,7 @@ class FloatTestGenerator:
|
||||||
self.make_error_test("Float Invalid Scientific Invalid Exponent",
|
self.make_error_test("Float Invalid Scientific Invalid Exponent",
|
||||||
"3.14eX",
|
"3.14eX",
|
||||||
"Invalid float literal: invalid exponent 'X'.")
|
"Invalid float literal: invalid exponent 'X'.")
|
||||||
|
|
||||||
# Invalid underscores
|
# Invalid underscores
|
||||||
self.make_error_test("Float Invalid Leading Underscore",
|
self.make_error_test("Float Invalid Leading Underscore",
|
||||||
"_3.14",
|
"_3.14",
|
||||||
|
|
@ -261,7 +219,7 @@ class FloatTestGenerator:
|
||||||
self.make_error_test("Float Invalid Underscore After Decimal",
|
self.make_error_test("Float Invalid Underscore After Decimal",
|
||||||
"3._14",
|
"3._14",
|
||||||
"Invalid float literal: underscore after decimal point.")
|
"Invalid float literal: underscore after decimal point.")
|
||||||
|
|
||||||
# Invalid type annotations
|
# Invalid type annotations
|
||||||
self.make_error_test("Float Invalid Type Annotation",
|
self.make_error_test("Float Invalid Type Annotation",
|
||||||
"3.14:i32",
|
"3.14:i32",
|
||||||
|
|
@ -269,12 +227,12 @@ class FloatTestGenerator:
|
||||||
self.make_error_test("Float Invalid Type Name",
|
self.make_error_test("Float Invalid Type Name",
|
||||||
"3.14:f16",
|
"3.14:f16",
|
||||||
"Invalid type annotation: unknown type 'f16'.")
|
"Invalid type annotation: unknown type 'f16'.")
|
||||||
|
|
||||||
# Comma separators not allowed
|
# Comma separators not allowed
|
||||||
self.make_error_test("Float Invalid Comma Separator",
|
self.make_error_test("Float Invalid Comma Separator",
|
||||||
"1,234.56",
|
"1,234.56",
|
||||||
"Invalid float literal: unexpected ',' in float.")
|
"Invalid float literal: unexpected ',' in float.")
|
||||||
|
|
||||||
def generate_whitespace_tests(self):
|
def generate_whitespace_tests(self):
|
||||||
"""Generate tests with whitespace."""
|
"""Generate tests with whitespace."""
|
||||||
self.make_success_test("Float Default Leading Whitespace",
|
self.make_success_test("Float Default Leading Whitespace",
|
||||||
|
|
@ -285,7 +243,7 @@ class FloatTestGenerator:
|
||||||
" 3.14 ", "f64", 3.14)
|
" 3.14 ", "f64", 3.14)
|
||||||
self.make_success_test("Float f32 With Whitespace",
|
self.make_success_test("Float f32 With Whitespace",
|
||||||
" 2.718:f32 ", "f32", 2.718)
|
" 2.718:f32 ", "f32", 2.718)
|
||||||
|
|
||||||
def generate_mathematical_constants_tests(self):
|
def generate_mathematical_constants_tests(self):
|
||||||
"""Generate tests for common mathematical constants."""
|
"""Generate tests for common mathematical constants."""
|
||||||
# Pi
|
# Pi
|
||||||
|
|
@ -293,75 +251,64 @@ class FloatTestGenerator:
|
||||||
"3.141592653589793", "f64", 3.141592653589793)
|
"3.141592653589793", "f64", 3.141592653589793)
|
||||||
self.make_success_test("Float f32 Pi Approximate",
|
self.make_success_test("Float f32 Pi Approximate",
|
||||||
"3.1415927:f32", "f32", 3.1415927)
|
"3.1415927:f32", "f32", 3.1415927)
|
||||||
|
|
||||||
# Euler's number
|
# Euler's number
|
||||||
self.make_success_test("Float Default Euler Approximate",
|
self.make_success_test("Float Default Euler Approximate",
|
||||||
"2.718281828459045", "f64", 2.718281828459045)
|
"2.718281828459045", "f64", 2.718281828459045)
|
||||||
self.make_success_test("Float f32 Euler Approximate",
|
self.make_success_test("Float f32 Euler Approximate",
|
||||||
"2.7182817:f32", "f32", 2.7182817)
|
"2.7182817:f32", "f32", 2.7182817)
|
||||||
|
|
||||||
# Golden ratio
|
# Golden ratio
|
||||||
self.make_success_test("Float Default Golden Ratio",
|
self.make_success_test("Float Default Golden Ratio",
|
||||||
"1.618033988749895", "f64", 1.618033988749895)
|
"1.618033988749895", "f64", 1.618033988749895)
|
||||||
|
|
||||||
# Square root of 2
|
# Square root of 2
|
||||||
self.make_success_test("Float Default Sqrt2",
|
self.make_success_test("Float Default Sqrt2",
|
||||||
"1.4142135623730951", "f64", 1.4142135623730951)
|
"1.4142135623730951", "f64", 1.4142135623730951)
|
||||||
|
|
||||||
def generate_signed_zero_tests(self):
|
def generate_signed_zero_tests(self):
|
||||||
"""Generate tests for signed zeros."""
|
"""Generate tests for signed zeros."""
|
||||||
self.make_success_test("Float Default Positive Zero", "0.0", "f64", 0.0)
|
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 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 Positive Zero", "0.0:f32", "f32", 0.0)
|
||||||
self.make_success_test("Float f32 Negative 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
|
# Note: In IEEE 754, +0.0 and -0.0 are distinct values but compare equal
|
||||||
|
|
||||||
def generate_all_tests(self) -> List[Dict[str, Any]]:
|
def generate_all_tests(self) -> List[Dict[str, Any]]:
|
||||||
"""Generate all test cases."""
|
"""Generate all test cases."""
|
||||||
# Basic tests
|
# Basic tests
|
||||||
self.generate_basic_tests()
|
self.generate_basic_tests()
|
||||||
|
|
||||||
# Format variations
|
# Format variations
|
||||||
self.generate_format_tests()
|
self.generate_format_tests()
|
||||||
|
|
||||||
# Underscores
|
# Underscores
|
||||||
self.generate_underscore_tests()
|
self.generate_underscore_tests()
|
||||||
|
|
||||||
# Special values (inf, nan)
|
# Special values (inf, nan)
|
||||||
self.generate_special_value_tests()
|
self.generate_special_value_tests()
|
||||||
|
|
||||||
# Edge cases for each type
|
# Edge cases for each type
|
||||||
for type_name in ['f32', 'f64']:
|
for type_name in ['f32', 'f64']:
|
||||||
self.generate_edge_case_tests(type_name)
|
self.generate_edge_case_tests(type_name)
|
||||||
|
|
||||||
# Overflow tests
|
# Overflow tests
|
||||||
self.generate_overflow_tests()
|
self.generate_overflow_tests()
|
||||||
|
|
||||||
# Precision tests
|
# Precision tests
|
||||||
self.generate_precision_tests()
|
self.generate_precision_tests()
|
||||||
|
|
||||||
# Error tests
|
# Error tests
|
||||||
self.generate_error_tests()
|
self.generate_error_tests()
|
||||||
|
|
||||||
# Whitespace tests
|
# Whitespace tests
|
||||||
self.generate_whitespace_tests()
|
self.generate_whitespace_tests()
|
||||||
|
|
||||||
# Mathematical constants
|
# Mathematical constants
|
||||||
self.generate_mathematical_constants_tests()
|
self.generate_mathematical_constants_tests()
|
||||||
|
|
||||||
# Signed zeros
|
# Signed zeros
|
||||||
self.generate_signed_zero_tests()
|
self.generate_signed_zero_tests()
|
||||||
|
|
||||||
return self.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()
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any
|
||||||
from .utils import Token, Operation, StackItem, RuntimeError, TestCase, to_dict
|
from .base_tests import BaseTestGenerator
|
||||||
|
|
||||||
class IntegerTestGenerator:
|
class IntegerTestGenerator(BaseTestGenerator):
|
||||||
"""Generate test cases for integer literals."""
|
"""Generate test cases for integer literals."""
|
||||||
|
|
||||||
# Type ranges
|
# Type ranges
|
||||||
TYPE_RANGES = {
|
TYPE_RANGES = {
|
||||||
'i8': (-128, 127),
|
'i8': (-128, 127),
|
||||||
|
|
@ -15,97 +15,53 @@ class IntegerTestGenerator:
|
||||||
'u32': (0, 4294967295),
|
'u32': (0, 4294967295),
|
||||||
'u64': (0, 18446744073709551615),
|
'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):
|
def generate_basic_tests(self):
|
||||||
"""Generate basic test cases."""
|
"""Generate basic test cases."""
|
||||||
# Empty statement
|
|
||||||
self.add_test("Empty_Statement", "", [], [], [])
|
|
||||||
|
|
||||||
# Simple default integers
|
# Simple default integers
|
||||||
self.make_success_test("Integer Default Decimal 0", "0", "i64", 0)
|
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 -1", "-1", "i64", -1)
|
||||||
self.make_success_test("Integer Default Decimal 42", "42", "i64", 42)
|
self.make_success_test("Integer Default Decimal 42", "42", "i64", 42)
|
||||||
self.make_success_test("Integer Default Decimal Leading Zeros", "00042", "i64", 42)
|
self.make_success_test("Integer Default Decimal Leading Zeros", "00042", "i64", 42)
|
||||||
|
|
||||||
def generate_default_base_tests(self):
|
def generate_default_base_tests(self):
|
||||||
"""Generate tests for default type with different bases."""
|
"""Generate tests for default type with different bases."""
|
||||||
# Hexadecimal
|
# Hexadecimal
|
||||||
self.make_success_test("Integer Default Hex 0xFF", "0xFF", "i64", 255)
|
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 0xdeadbeef", "0xdeadbeef", "i64", 3735928559)
|
||||||
self.make_success_test("Integer Default Hex Max", "0x7FFFFFFFFFFFFFFF", "i64", 9223372036854775807)
|
self.make_success_test("Integer Default Hex Max", "0x7FFFFFFFFFFFFFFF", "i64", 9223372036854775807)
|
||||||
|
|
||||||
# Binary
|
# Binary
|
||||||
self.make_success_test("Integer Default Binary 0b1010", "0b1010", "i64", 10)
|
self.make_success_test("Integer Default Binary 0b1010", "0b1010", "i64", 10)
|
||||||
self.make_success_test("Integer Default Binary All Ones", "0b1111111111111111", "i64", 65535)
|
self.make_success_test("Integer Default Binary All Ones", "0b1111111111111111", "i64", 65535)
|
||||||
|
|
||||||
# Octal
|
# Octal
|
||||||
self.make_success_test("Integer Default Octal 0o755", "0o755", "i64", 493)
|
self.make_success_test("Integer Default Octal 0o755", "0o755", "i64", 493)
|
||||||
self.make_success_test("Integer Default Octal Max Three Digits", "0o777", "i64", 511)
|
self.make_success_test("Integer Default Octal Max Three Digits", "0o777", "i64", 511)
|
||||||
|
|
||||||
def generate_default_edge_cases(self):
|
def generate_default_edge_cases(self):
|
||||||
"""Generate edge case tests for default type."""
|
"""Generate edge case tests for default type."""
|
||||||
# Min/max values
|
# Min/max values
|
||||||
self.make_success_test("Integer Default Decimal Max i64",
|
self.make_success_test("Integer Default Decimal Max i64",
|
||||||
"9223372036854775807", "i64", 9223372036854775807)
|
"9223372036854775807", "i64", 9223372036854775807)
|
||||||
self.make_success_test("Integer Default Decimal Min i64",
|
self.make_success_test("Integer Default Decimal Min i64",
|
||||||
"-9223372036854775808", "i64", -9223372036854775808)
|
"-9223372036854775808", "i64", -9223372036854775808)
|
||||||
|
|
||||||
# Underscores
|
# Underscores
|
||||||
self.make_success_test("Integer Default Decimal with Underscore",
|
self.make_success_test("Integer Default Decimal with Underscore",
|
||||||
"1_000_000", "i64", 1000000)
|
"1_000_000", "i64", 1000000)
|
||||||
self.make_success_test("Integer Default Underscore End", "42_", "i64", 42)
|
self.make_success_test("Integer Default Underscore End", "42_", "i64", 42)
|
||||||
self.make_success_test("Integer Default Underscore Double", "4__2", "i64", 42)
|
self.make_success_test("Integer Default Underscore Double", "4__2", "i64", 42)
|
||||||
|
|
||||||
# Whitespace
|
# Whitespace
|
||||||
self.make_success_test("Integer Default Whitespace", " 42 ", "i64", 42)
|
self.make_success_test("Integer Default Whitespace", " 42 ", "i64", 42)
|
||||||
|
|
||||||
# Zeros in different bases
|
# Zeros in different bases
|
||||||
self.make_success_test("Integer Default Hex Zero", "0x0", "i64", 0)
|
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 Binary Zero", "0b0", "i64", 0)
|
||||||
self.make_success_test("Integer Default Octal Zero", "0o0", "i64", 0)
|
self.make_success_test("Integer Default Octal Zero", "0o0", "i64", 0)
|
||||||
|
|
||||||
def generate_default_error_tests(self):
|
def generate_default_error_tests(self):
|
||||||
"""Generate error tests for default type."""
|
"""Generate error tests for default type."""
|
||||||
self.make_error_test("Integer Default Decimal with Commas Invalid",
|
self.make_error_test("Integer Default Decimal with Commas Invalid",
|
||||||
|
|
@ -117,126 +73,126 @@ class IntegerTestGenerator:
|
||||||
self.make_error_test("Integer Default Invalid Prefix",
|
self.make_error_test("Integer Default Invalid Prefix",
|
||||||
"0b2",
|
"0b2",
|
||||||
"Invalid binary literal: unexpected '2' in binary integer.")
|
"Invalid binary literal: unexpected '2' in binary integer.")
|
||||||
|
|
||||||
def generate_typed_tests(self, type_name: str):
|
def generate_typed_tests(self, type_name: str):
|
||||||
"""Generate tests for a specific type across all bases."""
|
"""Generate tests for a specific type across all bases."""
|
||||||
min_val, max_val = self.TYPE_RANGES[type_name]
|
min_val, max_val = self.TYPE_RANGES[type_name]
|
||||||
is_unsigned = type_name.startswith('u')
|
is_unsigned = type_name.startswith('u')
|
||||||
|
|
||||||
# Basic decimal
|
# Basic decimal
|
||||||
test_val = 42 if max_val >= 42 else max_val
|
test_val = 42 if max_val >= 42 else max_val
|
||||||
self.make_success_test(f"Integer {type_name} Decimal Positive",
|
self.make_success_test(f"Integer {type_name} Decimal Positive",
|
||||||
f"{test_val}:{type_name}", type_name, test_val)
|
f"{test_val}:{type_name}", type_name, test_val)
|
||||||
|
|
||||||
# Zero
|
# Zero
|
||||||
self.make_success_test(f"Integer {type_name} Zero",
|
self.make_success_test(f"Integer {type_name} Zero",
|
||||||
f"0:{type_name}", type_name, 0)
|
f"0:{type_name}", type_name, 0)
|
||||||
|
|
||||||
# Negative (only for signed types)
|
# Negative (only for signed types)
|
||||||
if not is_unsigned:
|
if not is_unsigned:
|
||||||
neg_val = -100 if min_val <= -100 else min_val
|
neg_val = -100 if min_val <= -100 else min_val
|
||||||
self.make_success_test(f"Integer {type_name} Decimal Negative",
|
self.make_success_test(f"Integer {type_name} Decimal Negative",
|
||||||
f"{neg_val}:{type_name}", type_name, neg_val)
|
f"{neg_val}:{type_name}", type_name, neg_val)
|
||||||
|
|
||||||
# Hexadecimal
|
# Hexadecimal
|
||||||
hex_val = min(255, max_val)
|
hex_val = min(255, max_val)
|
||||||
self.make_success_test(f"Integer {type_name} Hex",
|
self.make_success_test(f"Integer {type_name} Hex",
|
||||||
f"0x{hex_val:X}:{type_name}", type_name, hex_val)
|
f"0x{hex_val:X}:{type_name}", type_name, hex_val)
|
||||||
|
|
||||||
# Binary
|
# Binary
|
||||||
bin_val = min(15, max_val)
|
bin_val = min(15, max_val)
|
||||||
self.make_success_test(f"Integer {type_name} Binary",
|
self.make_success_test(f"Integer {type_name} Binary",
|
||||||
f"0b{bin_val:b}:{type_name}", type_name, bin_val)
|
f"0b{bin_val:b}:{type_name}", type_name, bin_val)
|
||||||
|
|
||||||
# Octal
|
# Octal
|
||||||
oct_val = min(63, max_val)
|
oct_val = min(63, max_val)
|
||||||
self.make_success_test(f"Integer {type_name} Octal",
|
self.make_success_test(f"Integer {type_name} Octal",
|
||||||
f"0o{oct_val:o}:{type_name}", type_name, oct_val)
|
f"0o{oct_val:o}:{type_name}", type_name, oct_val)
|
||||||
|
|
||||||
# Max value
|
# Max value
|
||||||
self.make_success_test(f"Integer {type_name} Max Value",
|
self.make_success_test(f"Integer {type_name} Max Value",
|
||||||
f"{max_val}:{type_name}", type_name, max_val)
|
f"{max_val}:{type_name}", type_name, max_val)
|
||||||
|
|
||||||
# Min value
|
# Min value
|
||||||
self.make_success_test(f"Integer {type_name} Min Value",
|
self.make_success_test(f"Integer {type_name} Min Value",
|
||||||
f"{min_val}:{type_name}", type_name, min_val)
|
f"{min_val}:{type_name}", type_name, min_val)
|
||||||
|
|
||||||
# Overflow
|
# Overflow
|
||||||
overflow_val = max_val + 1
|
overflow_val = max_val + 1
|
||||||
self.make_error_test(f"Integer {type_name} Overflow",
|
self.make_error_test(f"Integer {type_name} Overflow",
|
||||||
f"{overflow_val}:{type_name}",
|
f"{overflow_val}:{type_name}",
|
||||||
f"Integer overflow: value exceeds range for {type_name}.")
|
f"Integer overflow: value exceeds range for {type_name}.")
|
||||||
|
|
||||||
# Underflow
|
# Underflow
|
||||||
underflow_val = min_val - 1
|
underflow_val = min_val - 1
|
||||||
self.make_error_test(f"Integer {type_name} Underflow",
|
self.make_error_test(f"Integer {type_name} Underflow",
|
||||||
f"{underflow_val}:{type_name}",
|
f"{underflow_val}:{type_name}",
|
||||||
f"Integer overflow: value exceeds range for {type_name}.")
|
f"Integer overflow: value exceeds range for {type_name}.")
|
||||||
|
|
||||||
def generate_special_typed_tests(self, type_name: str):
|
def generate_special_typed_tests(self, type_name: str):
|
||||||
"""Generate special tests for specific types."""
|
"""Generate special tests for specific types."""
|
||||||
min_val, max_val = self.TYPE_RANGES[type_name]
|
min_val, max_val = self.TYPE_RANGES[type_name]
|
||||||
is_unsigned = type_name.startswith('u')
|
is_unsigned = type_name.startswith('u')
|
||||||
|
|
||||||
# Underscores with type annotation
|
# Underscores with type annotation
|
||||||
if max_val >= 1000000:
|
if max_val >= 1000000:
|
||||||
self.make_success_test(f"Integer {type_name} With Underscores",
|
self.make_success_test(f"Integer {type_name} With Underscores",
|
||||||
f"1_000_000:{type_name}", type_name, 1000000)
|
f"1_000_000:{type_name}", type_name, 1000000)
|
||||||
|
|
||||||
# Special values for specific types
|
# Special values for specific types
|
||||||
if type_name == 'i8':
|
if type_name == 'i8':
|
||||||
self.make_success_test("Integer i8 Hex Max", "0x7F:i8", "i8", 127)
|
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 Binary Max", "0b01111111:i8", "i8", 127)
|
||||||
self.make_success_test("Integer i8 Octal Max", "0o177: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)
|
self.make_success_test("Integer i8 Negative Hex", "-0x80:i8", "i8", -128)
|
||||||
|
|
||||||
elif type_name == 'u8':
|
elif type_name == 'u8':
|
||||||
self.make_success_test("Integer u8 Hex Max", "0xFF:u8", "u8", 255)
|
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 Binary Max", "0b11111111:u8", "u8", 255)
|
||||||
self.make_success_test("Integer u8 Octal Max", "0o377:u8", "u8", 255)
|
self.make_success_test("Integer u8 Octal Max", "0o377:u8", "u8", 255)
|
||||||
|
|
||||||
elif type_name == 'i16':
|
elif type_name == 'i16':
|
||||||
self.make_success_test("Integer i16 Hex Sample", "0x1234:i16", "i16", 4660)
|
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)
|
"0b1111111100000000:i16", "i16", 65280)
|
||||||
self.make_success_test("Integer i16 Octal Sample", "0o1234:i16", "i16", 668)
|
self.make_success_test("Integer i16 Octal Sample", "0o1234:i16", "i16", 668)
|
||||||
|
|
||||||
elif type_name == 'u16':
|
elif type_name == 'u16':
|
||||||
self.make_success_test("Integer u16 Hex Max", "0xFFFF:u16", "u16", 65535)
|
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)
|
"0b1111111111111111:u16", "u16", 65535)
|
||||||
self.make_success_test("Integer u16 Octal Max", "0o177777: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)
|
self.make_success_test("Integer u16 Decimal Mid", "50000:u16", "u16", 50000)
|
||||||
|
|
||||||
elif type_name == 'i32':
|
elif type_name == 'i32':
|
||||||
self.make_success_test("Integer i32 Hex Sample", "0xABCD:i32", "i32", 43981)
|
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)
|
"0b11110000:i32", "i32", 240)
|
||||||
|
|
||||||
elif type_name == 'u32':
|
elif type_name == 'u32':
|
||||||
self.make_success_test("Integer u32 Hex Max", "0xFFFFFFFF:u32", "u32", 4294967295)
|
self.make_success_test("Integer u32 Hex Max", "0xFFFFFFFF:u32", "u32", 4294967295)
|
||||||
self.make_success_test("Integer u32 Binary Sample",
|
self.make_success_test("Integer u32 Binary Sample",
|
||||||
"0b11111111000000001111111100000000:u32",
|
"0b11111111000000001111111100000000:u32",
|
||||||
"u32", 4278255360)
|
"u32", 4278255360)
|
||||||
self.make_success_test("Integer u32 Octal Max",
|
self.make_success_test("Integer u32 Octal Max",
|
||||||
"0o37777777777:u32", "u32", 4294967295)
|
"0o37777777777:u32", "u32", 4294967295)
|
||||||
self.make_success_test("Integer u32 Decimal Mid", "1000000:u32", "u32", 1000000)
|
self.make_success_test("Integer u32 Decimal Mid", "1000000:u32", "u32", 1000000)
|
||||||
|
|
||||||
elif type_name == 'i64':
|
elif type_name == 'i64':
|
||||||
self.make_success_test("Integer i64 Decimal Positive 42", "42:i64", "i64", 42)
|
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 Hex 0xFF", "0xFF:i64", "i64", 255)
|
||||||
self.make_success_test("Integer i64 Binary 0b1010", "0b1010:i64", "i64", 10)
|
self.make_success_test("Integer i64 Binary 0b1010", "0b1010:i64", "i64", 10)
|
||||||
self.make_success_test("Integer i64 Octal 0o755", "0o755:i64", "i64", 493)
|
self.make_success_test("Integer i64 Octal 0o755", "0o755:i64", "i64", 493)
|
||||||
|
|
||||||
elif type_name == 'u64':
|
elif type_name == 'u64':
|
||||||
self.make_success_test("Integer u64 Hex Max",
|
self.make_success_test("Integer u64 Hex Max",
|
||||||
"0xFFFFFFFFFFFFFFFF:u64",
|
"0xFFFFFFFFFFFFFFFF:u64",
|
||||||
"u64", 18446744073709551615)
|
"u64", 18446744073709551615)
|
||||||
self.make_success_test("Integer u64 Binary Sample",
|
self.make_success_test("Integer u64 Binary Sample",
|
||||||
"0b1010101010101010:u64", "u64", 43690)
|
"0b1010101010101010:u64", "u64", 43690)
|
||||||
self.make_success_test("Integer u64 Octal Sample", "0o7777:u64", "u64", 4095)
|
self.make_success_test("Integer u64 Octal Sample", "0o7777:u64", "u64", 4095)
|
||||||
self.make_success_test("Integer u64 Decimal", "42:u64", "u64", 42)
|
self.make_success_test("Integer u64 Decimal", "42:u64", "u64", 42)
|
||||||
|
|
||||||
def generate_underscore_tests(self):
|
def generate_underscore_tests(self):
|
||||||
"""Generate tests for underscores in different bases."""
|
"""Generate tests for underscores in different bases."""
|
||||||
self.make_success_test("Integer Hex With Underscores",
|
self.make_success_test("Integer Hex With Underscores",
|
||||||
|
|
@ -245,34 +201,23 @@ class IntegerTestGenerator:
|
||||||
"0b1111_0000_1010_0101:i32", "i32", 61605)
|
"0b1111_0000_1010_0101:i32", "i32", 61605)
|
||||||
self.make_success_test("Integer Octal With Underscores",
|
self.make_success_test("Integer Octal With Underscores",
|
||||||
"0o7_7_7:i16", "i16", 511)
|
"0o7_7_7:i16", "i16", 511)
|
||||||
|
|
||||||
def generate_all_tests(self) -> List[Dict[str, Any]]:
|
def generate_all_tests(self) -> List[Dict[str, Any]]:
|
||||||
"""Generate all test cases."""
|
"""Generate all test cases."""
|
||||||
# Basic tests
|
# Basic tests
|
||||||
self.generate_basic_tests()
|
self.generate_basic_tests()
|
||||||
|
|
||||||
# Default type (i64) comprehensive tests
|
# Default type (i64) comprehensive tests
|
||||||
self.generate_default_base_tests()
|
self.generate_default_base_tests()
|
||||||
self.generate_default_edge_cases()
|
self.generate_default_edge_cases()
|
||||||
self.generate_default_error_tests()
|
self.generate_default_error_tests()
|
||||||
|
|
||||||
# Tests for each specific type
|
# Tests for each specific type
|
||||||
for type_name in ['i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64']:
|
for type_name in ['i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64']:
|
||||||
self.generate_typed_tests(type_name)
|
self.generate_typed_tests(type_name)
|
||||||
self.generate_special_typed_tests(type_name)
|
self.generate_special_typed_tests(type_name)
|
||||||
|
|
||||||
# Additional edge cases
|
# Additional edge cases
|
||||||
self.generate_underscore_tests()
|
self.generate_underscore_tests()
|
||||||
|
|
||||||
return self.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()
|
|
||||||
|
|
|
||||||
|
|
@ -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}
|
|
||||||
Loading…
Reference in New Issue