YREA-SLS/SLS_Tests/generate_tests.py

356 lines
15 KiB
Python

"""
Test case generator for integer literals in the Stack Language.
Generates comprehensive test cases for all integer types and bases.
"""
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}
class IntegerTestGenerator:
"""Generate test cases for integer literals."""
# Type ranges
TYPE_RANGES = {
'i8': (-128, 127),
'i16': (-32768, 32767),
'i32': (-2147483648, 2147483647),
'i64': (-9223372036854775808, 9223372036854775807),
'u8': (0, 255),
'u16': (0, 65535),
'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",
"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",
"1,000,000",
"Invalid decimal literal: unexpected ',' in decimal integer.")
self.make_error_test("Integer Default Invalid Characters",
"12a3",
"Invalid decimal literal: unexpected 'a' in decimal integer.")
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: {overflow_val} 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 underflow: {underflow_val} 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",
"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",
"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",
"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",
"u32", 4278255360)
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",
"u64", 18446744073709551615)
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",
"0xDEAD_BEEF:i64", "i64", 3735928559)
self.make_success_test("Integer Binary With Underscores",
"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()
if __name__ == "__main__":
# import json
import yaml
# Generate tests
tests = generate_integer_literal_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))