1191 lines
43 KiB
Plaintext
1191 lines
43 KiB
Plaintext
# DSL Transpiler Test Suite
|
|
# Tests for SimpleDSLTranspiler class
|
|
#
|
|
# Command to run test is:
|
|
# ./berry -s -g -m lib/libesp32/berry_animation -e "import tasmota" lib/libesp32/berry_animation/tests/dsl_transpiler_test.be
|
|
|
|
import animation
|
|
import animation_dsl
|
|
import string
|
|
|
|
# Helper function to extract all tokens from a pull lexer (for testing only)
|
|
def extract_all_tokens(lexer)
|
|
var tokens = []
|
|
lexer.reset() # Start from beginning
|
|
|
|
while !lexer.at_end()
|
|
var token = lexer.next_token()
|
|
|
|
# EOF token removed - check for nil instead
|
|
if token == nil
|
|
break
|
|
end
|
|
|
|
tokens.push(token)
|
|
end
|
|
|
|
return tokens
|
|
end
|
|
|
|
# Test basic transpilation
|
|
def test_basic_transpilation()
|
|
print("Testing basic DSL transpilation...")
|
|
|
|
var dsl_source = "# strip length 60 # TEMPORARILY DISABLED\n" +
|
|
"color custom_red = 0xFF0000\n" +
|
|
"animation solid_red = solid(color=custom_red)\n" +
|
|
"animation red_anim = solid_red\n" +
|
|
"\n" +
|
|
"sequence demo {\n" +
|
|
" play red_anim for 5s\n" +
|
|
"}\n" +
|
|
"\n" +
|
|
"run demo"
|
|
|
|
var berry_code = animation_dsl.compile(dsl_source)
|
|
|
|
assert(berry_code != nil, "Should generate Berry code")
|
|
assert(string.find(berry_code, "var engine = animation.init_strip()") >= 0, "Should generate strip configuration")
|
|
assert(string.find(berry_code, "var custom_red_ = 0xFFFF0000") >= 0, "Should generate color definition")
|
|
assert(string.find(berry_code, "var demo_ = animation.sequence_manager(engine)") >= 0, "Should generate sequence manager")
|
|
assert(string.find(berry_code, "engine.add(demo_)") >= 0, "Should add sequence manager")
|
|
|
|
# print("Generated Berry code:")
|
|
# print("==================================================")
|
|
# print(berry_code)
|
|
# print("==================================================")
|
|
|
|
print("✓ Basic transpilation test passed")
|
|
return true
|
|
end
|
|
|
|
# Test color definitions
|
|
def test_color_definitions()
|
|
print("Testing color definitions...")
|
|
|
|
var color_tests = [
|
|
["color custom_red = 0xFF0000", "var custom_red_ = 0xFFFF0000"],
|
|
["color custom_blue = 0x0000FF", "var custom_blue_ = 0xFF0000FF"],
|
|
["color my_white = white", "var my_white_ = 0xFFFFFFFF"],
|
|
["color my_green = green", "var my_green_ = 0xFF008000"]
|
|
]
|
|
|
|
for test : color_tests
|
|
var dsl_input = test[0]
|
|
var expected_output = test[1]
|
|
|
|
var berry_code = animation_dsl.compile(dsl_input)
|
|
assert(berry_code != nil, "Should compile: " + dsl_input)
|
|
assert(string.find(berry_code, expected_output) >= 0, "Should contain: " + expected_output)
|
|
end
|
|
|
|
print("✓ Color definitions test passed")
|
|
return true
|
|
end
|
|
|
|
# Test color definitions with alpha channel
|
|
def test_color_alpha_channel()
|
|
print("Testing color definitions with alpha channel...")
|
|
|
|
var alpha_color_tests = [
|
|
# Test 8-character hex with alpha (should preserve alpha)
|
|
["color red_opaque = 0xFFFF0000", "var red_opaque_ = 0xFFFF0000"],
|
|
["color red_half = 0x80FF0000", "var red_half_ = 0x80FF0000"],
|
|
["color blue_quarter = 0x400000FF", "var blue_quarter_ = 0x400000FF"],
|
|
["color clear = 0x00000000", "var clear_ = 0x00000000"],
|
|
|
|
# Test 6-character hex without alpha (should add FF for opaque)
|
|
["color custom_red = 0xFF0000", "var custom_red_ = 0xFFFF0000"],
|
|
["color custom_lime = 0x00FF00", "var custom_lime_ = 0xFF00FF00"]
|
|
]
|
|
|
|
for test : alpha_color_tests
|
|
var dsl_input = test[0]
|
|
var expected_output = test[1]
|
|
|
|
var berry_code = animation_dsl.compile(dsl_input)
|
|
assert(berry_code != nil, "Should compile: " + dsl_input)
|
|
assert(string.find(berry_code, expected_output) >= 0, f"Should contain: {expected_output} in: {berry_code}")
|
|
end
|
|
|
|
print("✓ Color alpha channel test passed")
|
|
return true
|
|
end
|
|
|
|
# Test strip configuration
|
|
def test_strip_configuration()
|
|
print("Testing strip configuration...")
|
|
|
|
# Strip directive tests are temporarily disabled
|
|
var config_tests = [
|
|
# ["strip length 30", "var engine = animation.init_strip(30)"], # TEMPORARILY DISABLED
|
|
# ["strip length 60", "var engine = animation.init_strip(60)"], # TEMPORARILY DISABLED
|
|
# ["strip length 120", "var engine = animation.init_strip(120)"] # TEMPORARILY DISABLED
|
|
]
|
|
|
|
for test : config_tests
|
|
var dsl_input = test[0]
|
|
var expected_output = test[1]
|
|
|
|
var berry_code = animation_dsl.compile(dsl_input)
|
|
assert(berry_code != nil, "Should compile: " + dsl_input)
|
|
assert(string.find(berry_code, expected_output) >= 0, "Should contain: " + expected_output)
|
|
end
|
|
|
|
print("✓ Strip configuration test passed")
|
|
return true
|
|
end
|
|
|
|
# Test simple patterns
|
|
def test_simple_patterns()
|
|
print("Testing simple patterns...")
|
|
|
|
var dsl_source = "color custom = 0xFF8080\n"
|
|
"animation solid_red = solid(color=red)\n"
|
|
"animation solid_custom = solid(color=custom)"
|
|
|
|
var berry_code = animation_dsl.compile(dsl_source)
|
|
|
|
# print("Generated Berry code:")
|
|
# print("==================================================")
|
|
# print(berry_code)
|
|
# print("==================================================")
|
|
|
|
assert(berry_code != nil, "Should compile simple pattern")
|
|
assert(string.find(berry_code, "var custom_ = 0xFFFF8080") >= 0, "Should define color")
|
|
assert(string.find(berry_code, "var solid_red_ = animation.solid(engine)") >= 0, "Should define animation")
|
|
assert(string.find(berry_code, "solid_red_.color = 0xFFFF0000") >= 0, "Should set color parameter")
|
|
|
|
print("✓ Simple patterns test passed")
|
|
return true
|
|
end
|
|
|
|
# Test sequences
|
|
def test_sequences()
|
|
print("Testing sequences...")
|
|
|
|
var dsl_source = "color custom_blue = 0x0000FF\n"
|
|
"animation blue_anim = solid(color=custom_blue)\n"
|
|
"\n"
|
|
"sequence test_seq {\n"
|
|
" play blue_anim for 3s\n"
|
|
"}\n"
|
|
"\n"
|
|
"run test_seq"
|
|
|
|
var berry_code = animation_dsl.compile(dsl_source)
|
|
assert(berry_code != nil, "Should compile sequence")
|
|
assert(string.find(berry_code, "var test_seq_ = animation.sequence_manager(engine)") >= 0, "Should define sequence manager")
|
|
assert(string.find(berry_code, ".push_play_step(") >= 0, "Should add play step")
|
|
assert(string.find(berry_code, "3000)") >= 0, "Should reference duration")
|
|
assert(string.find(berry_code, "engine.run()") >= 0, "Should start engine")
|
|
|
|
print("✓ Sequences test passed")
|
|
return true
|
|
end
|
|
|
|
# Test sequence assignments
|
|
def test_sequence_assignments()
|
|
print("Testing sequence assignments...")
|
|
|
|
# Test basic sequence assignment
|
|
var dsl_source = "color my_red = 0xFF0000\n" +
|
|
"set brightness = 128\n" +
|
|
"animation test = solid(color=my_red)\n" +
|
|
"\n" +
|
|
"sequence demo {\n" +
|
|
" play test for 1s\n" +
|
|
" test.opacity = brightness\n" +
|
|
" play test for 1s\n" +
|
|
"}\n" +
|
|
"\n" +
|
|
"run demo"
|
|
|
|
var berry_code = animation_dsl.compile(dsl_source)
|
|
assert(berry_code != nil, "Should compile sequence with assignments")
|
|
assert(string.find(berry_code, "var demo_ = animation.sequence_manager(engine)") >= 0, "Should define sequence manager")
|
|
assert(string.find(berry_code, ".push_closure_step") >= 0, "Should generate closure step")
|
|
assert(string.find(berry_code, "test_.opacity = brightness_") >= 0, "Should generate assignment")
|
|
|
|
# Test multiple assignments in sequence
|
|
var multi_assign_dsl = "color my_red = 0xFF0000\n" +
|
|
"color my_blue = 0x0000FF\n" +
|
|
"set high_brightness = 255\n" +
|
|
"set low_brightness = 50\n" +
|
|
"animation test = solid(color=my_red)\n" +
|
|
"\n" +
|
|
"sequence demo {\n" +
|
|
" play test for 1s\n" +
|
|
" test.opacity = high_brightness\n" +
|
|
" test.color = my_blue\n" +
|
|
" play test for 1s\n" +
|
|
" test.opacity = low_brightness\n" +
|
|
"}\n" +
|
|
"\n" +
|
|
"run demo"
|
|
|
|
var multi_berry_code = animation_dsl.compile(multi_assign_dsl)
|
|
assert(multi_berry_code != nil, "Should compile multiple assignments")
|
|
|
|
# Count assignment steps
|
|
var assign_count = 0
|
|
var pos = 0
|
|
while true
|
|
pos = string.find(multi_berry_code, "push_closure_step", pos)
|
|
if pos < 0 break end
|
|
assign_count += 1
|
|
pos += 1
|
|
end
|
|
assert(assign_count == 3, f"Should have 3 assignment steps, found {assign_count}")
|
|
|
|
# Test assignments in repeat blocks
|
|
var repeat_assign_dsl = "color my_green = 0x00FF00\n" +
|
|
"set brightness = 200\n" +
|
|
"animation test = solid(color=my_green)\n" +
|
|
"\n" +
|
|
"sequence demo {\n" +
|
|
" repeat 2 times {\n" +
|
|
" play test for 500ms\n" +
|
|
" test.opacity = brightness\n" +
|
|
" wait 200ms\n" +
|
|
" }\n" +
|
|
"}\n" +
|
|
"\n" +
|
|
"run demo"
|
|
|
|
var repeat_berry_code = animation_dsl.compile(repeat_assign_dsl)
|
|
print(repeat_berry_code)
|
|
assert(repeat_berry_code != nil, "Should compile repeat with assignments")
|
|
assert(string.find(repeat_berry_code, "push_repeat_subsequence") >= 0, "Should generate repeat loop")
|
|
assert(string.find(repeat_berry_code, "push_closure_step") >= 0, "Should generate closure step in repeat")
|
|
|
|
# Test complex cylon rainbow example
|
|
var cylon_dsl = "set strip_len = strip_length()\n" +
|
|
"palette eye_palette = [ red, yellow, green, violet ]\n" +
|
|
"color eye_color = color_cycle(colors=eye_palette, cycle_period=0)\n" +
|
|
"set cosine_val = cosine_osc(min_value = 0, max_value = strip_len - 2, duration = 5s)\n" +
|
|
"set triangle_val = triangle(min_value = 0, max_value = strip_len - 2, duration = 5s)\n" +
|
|
"\n" +
|
|
"animation red_eye = beacon_animation(\n" +
|
|
" color = eye_color\n" +
|
|
" pos = cosine_val\n" +
|
|
" beacon_size = 3\n" +
|
|
" slew_size = 2\n" +
|
|
" priority = 10\n" +
|
|
")\n" +
|
|
"\n" +
|
|
"sequence cylon_eye {\n" +
|
|
" play red_eye for 3s\n" +
|
|
" red_eye.pos = triangle_val\n" +
|
|
" play red_eye for 3s\n" +
|
|
" red_eye.pos = cosine_val\n" +
|
|
" eye_color.next = 1\n" +
|
|
"}\n" +
|
|
"\n" +
|
|
"run cylon_eye"
|
|
|
|
var cylon_berry_code = animation_dsl.compile(cylon_dsl)
|
|
assert(cylon_berry_code != nil, "Should compile cylon rainbow example")
|
|
|
|
# Check for all expected assignment steps
|
|
assert(string.find(cylon_berry_code, "red_eye_.pos = triangle_val_") >= 0, "Should assign triangle_val to pos")
|
|
assert(string.find(cylon_berry_code, "red_eye_.pos = cosine_val_") >= 0, "Should assign cosine_val to pos")
|
|
assert(string.find(cylon_berry_code, "eye_color_.next = 1") >= 0, "Should assign 1 to next")
|
|
|
|
print("✓ Sequence assignments test passed")
|
|
return true
|
|
end
|
|
|
|
# Test variable duration support
|
|
def test_variable_duration()
|
|
print("Testing variable duration support...")
|
|
|
|
# Test basic variable duration
|
|
var basic_dsl = "set short_time = 2s\n" +
|
|
"set long_time = 5s\n" +
|
|
"color test_color = 0xFF0000\n" +
|
|
"animation test_anim = solid(color=test_color)\n" +
|
|
"\n" +
|
|
"sequence test_seq {\n" +
|
|
" play test_anim for short_time\n" +
|
|
" wait long_time\n" +
|
|
" play test_anim for long_time\n" +
|
|
"}\n" +
|
|
"\n" +
|
|
"run test_seq"
|
|
|
|
var basic_code = animation_dsl.compile(basic_dsl)
|
|
assert(basic_code != nil, "Should compile variable duration")
|
|
assert(string.find(basic_code, "var short_time_ = 2000") >= 0, "Should define short_time variable")
|
|
assert(string.find(basic_code, "var long_time_ = 5000") >= 0, "Should define long_time variable")
|
|
assert(string.find(basic_code, "short_time_") >= 0, "Should reference short_time in play")
|
|
assert(string.find(basic_code, "long_time_") >= 0, "Should reference long_time in wait/play")
|
|
|
|
# Test undefined variable should fail
|
|
var undefined_dsl = "set valid_time = 3s\n" +
|
|
"animation test_anim = solid(color=red)\n" +
|
|
"\n" +
|
|
"sequence test_seq {\n" +
|
|
" play test_anim for invalid_time\n" +
|
|
"}\n" +
|
|
"\n" +
|
|
"run test_seq"
|
|
|
|
var undefined_code = nil
|
|
try
|
|
undefined_code = animation_dsl.compile(undefined_dsl)
|
|
assert(false, "Should fail with undefined variable")
|
|
except "dsl_compilation_error" as e, msg
|
|
assert(string.find(msg, "Undefined reference 'invalid_time' in duration") >= 0, "Should report undefined variable error")
|
|
end
|
|
|
|
# Test value provider duration
|
|
var provider_dsl = "set dynamic_time = triangle(min_value=1000, max_value=3000, duration=10s)\n" +
|
|
"animation test_anim = solid(color=blue)\n" +
|
|
"\n" +
|
|
"sequence test_seq {\n" +
|
|
" play test_anim for dynamic_time\n" +
|
|
"}\n" +
|
|
"\n" +
|
|
"run test_seq"
|
|
|
|
var provider_code = animation_dsl.compile(provider_dsl)
|
|
assert(provider_code != nil, "Should compile value provider duration")
|
|
assert(string.find(provider_code, "animation.triangle(engine)") >= 0, "Should create triangle value provider")
|
|
assert(string.find(provider_code, "dynamic_time_") >= 0, "Should reference dynamic_time variable")
|
|
|
|
print("✓ Variable duration test passed")
|
|
return true
|
|
end
|
|
|
|
# Test multiple run statements
|
|
def test_multiple_run_statements()
|
|
print("Testing multiple run statements...")
|
|
|
|
# Test with multiple animations
|
|
var dsl_source = "# strip length 30 # TEMPORARILY DISABLED\n" +
|
|
"color custom_red = 0xFF0000\n" +
|
|
"color custom_blue = 0x0000FF\n" +
|
|
"color custom_green = 0x00FF00\n" +
|
|
"\n" +
|
|
"animation red_anim = solid(color=custom_red)\n" +
|
|
"animation blue_anim = solid(color=custom_blue)\n" +
|
|
"animation green_anim = solid(color=custom_green)\n" +
|
|
"\n" +
|
|
"red_anim.priority = 5\n" +
|
|
"blue_anim.priority = 15\n" +
|
|
"green_anim.priority = 25\n" +
|
|
"\n" +
|
|
"run red_anim\n" +
|
|
"run blue_anim\n" +
|
|
"run green_anim"
|
|
|
|
var berry_code = animation_dsl.compile(dsl_source)
|
|
assert(berry_code != nil, "Should compile multiple run statements")
|
|
|
|
# Count engine.run() calls - should be exactly 1
|
|
var lines = string.split(berry_code, "\n")
|
|
var start_count = 0
|
|
for line : lines
|
|
if string.find(line, "engine.run()") >= 0
|
|
start_count += 1
|
|
end
|
|
end
|
|
|
|
assert(start_count == 1, f"Should have exactly 1 engine.run() call, found {start_count}")
|
|
|
|
# Check that all animations are added to the engine
|
|
assert(string.find(berry_code, "engine.add(red_anim_)") >= 0, "Should add red_anim to engine")
|
|
assert(string.find(berry_code, "engine.add(blue_anim_)") >= 0, "Should add blue_anim to engine")
|
|
assert(string.find(berry_code, "engine.add(green_anim_)") >= 0, "Should add green_anim to engine")
|
|
|
|
# Verify the engine.run() comes after all animations are added
|
|
var start_line_index = -1
|
|
var last_add_line_index = -1
|
|
|
|
for i : 0..size(lines)-1
|
|
var line = lines[i]
|
|
if string.find(line, "engine.run()") >= 0
|
|
start_line_index = i
|
|
end
|
|
if string.find(line, "engine.add(") >= 0
|
|
last_add_line_index = i
|
|
end
|
|
end
|
|
|
|
assert(start_line_index > last_add_line_index, "engine.run() should come after all engine.add_* calls")
|
|
|
|
# Test with mixed animations and sequences
|
|
var mixed_dsl = "# strip length 30 # TEMPORARILY DISABLED\n" +
|
|
"color custom_red = 0xFF0000\n" +
|
|
"color custom_blue = 0x0000FF\n" +
|
|
"\n" +
|
|
"animation red_anim = solid(color=custom_red)\n" +
|
|
"\n" +
|
|
"sequence blue_seq {\n" +
|
|
" play red_anim for 2s\n" +
|
|
" wait 1s\n" +
|
|
"}\n" +
|
|
"\n" +
|
|
"run red_anim\n" +
|
|
"run blue_seq"
|
|
|
|
var mixed_berry_code = animation_dsl.compile(mixed_dsl)
|
|
assert(mixed_berry_code != nil, "Should compile mixed run statements")
|
|
|
|
# Count engine.run() calls in mixed scenario
|
|
var mixed_lines = string.split(mixed_berry_code, "\n")
|
|
var mixed_start_count = 0
|
|
for line : mixed_lines
|
|
if string.find(line, "engine.run()") >= 0
|
|
mixed_start_count += 1
|
|
end
|
|
end
|
|
|
|
assert(mixed_start_count == 1, f"Mixed scenario should have exactly 1 engine.run() call, found {mixed_start_count}")
|
|
|
|
# Check that both animation and sequence are handled
|
|
assert(string.find(mixed_berry_code, "engine.add(red_anim_)") >= 0, "Should add animation to engine")
|
|
assert(string.find(mixed_berry_code, "engine.add(blue_seq_)") >= 0, "Should add sequence to engine")
|
|
|
|
print("✓ Multiple run statements test passed")
|
|
return true
|
|
end
|
|
|
|
# Test variable assignments
|
|
def test_variable_assignments()
|
|
print("Testing variable assignments...")
|
|
|
|
var dsl_source = "set my_length = 60\n" +
|
|
"set brightness = 80%\n" +
|
|
"set cycle_time = 5s"
|
|
|
|
var berry_code = animation_dsl.compile(dsl_source)
|
|
assert(berry_code != nil, "Should compile variables")
|
|
assert(string.find(berry_code, "var my_length_ = 60") >= 0, "Should define numeric variable")
|
|
assert(string.find(berry_code, "var brightness_ = 204") >= 0, "Should convert percentage to 0-255 range")
|
|
assert(string.find(berry_code, "var cycle_time_ = 5000") >= 0, "Should convert time to milliseconds")
|
|
|
|
# Test value provider assignments
|
|
var value_provider_dsl = "set pos_test = triangle(min_value=2, max_value=57, duration=2s)\n" +
|
|
"set brightness_osc = smooth(min_value=50, max_value=255, duration=3s)"
|
|
|
|
var provider_code = animation_dsl.compile(value_provider_dsl)
|
|
assert(provider_code != nil, "Should compile value provider assignments")
|
|
assert(string.find(provider_code, "animation.triangle(engine)") >= 0, "Should create triangle value provider")
|
|
assert(string.find(provider_code, "animation.smooth(engine)") >= 0, "Should create smooth value provider")
|
|
assert(string.find(provider_code, "min_value = 2") >= 0, "Should set triangle min_value parameter")
|
|
assert(string.find(provider_code, "max_value = 57") >= 0, "Should set triangle max_value parameter")
|
|
assert(string.find(provider_code, "duration = 2000") >= 0, "Should convert triangle duration to milliseconds")
|
|
assert(string.find(provider_code, "duration = 3000") >= 0, "Should convert smooth duration to milliseconds")
|
|
|
|
print("✓ Variable assignments test passed")
|
|
return true
|
|
end
|
|
|
|
# Test computed values and expressions (regression tests)
|
|
def test_computed_values()
|
|
print("Testing computed values and expressions...")
|
|
|
|
# Test computed values with single resolve calls (regression test for double resolve issue)
|
|
var computed_dsl = "set strip_len = strip_length()\n" +
|
|
"animation stream1 = comet_animation(\n" +
|
|
" color=red\n" +
|
|
" tail_length=abs(strip_len / 4)\n" +
|
|
" speed=1.5\n" +
|
|
" priority=10\n" +
|
|
")"
|
|
|
|
var computed_code = animation_dsl.compile(computed_dsl)
|
|
assert(computed_code != nil, "Should compile computed values")
|
|
|
|
# Check for single resolve calls (no double wrapping)
|
|
var expected_single_resolve = "animation._math.abs(animation.resolve(strip_len_) / 4)"
|
|
assert(string.find(computed_code, expected_single_resolve) >= 0, "Should generate single resolve call in computed expression")
|
|
|
|
# Check that there are no double resolve calls
|
|
var double_resolve_count = 0
|
|
var pos = 0
|
|
while true
|
|
pos = string.find(computed_code, "animation.resolve(self.resolve(", pos)
|
|
if pos < 0
|
|
break
|
|
end
|
|
double_resolve_count += 1
|
|
pos += 1
|
|
end
|
|
assert(double_resolve_count == 0, f"Should have no double resolve calls, found {double_resolve_count}")
|
|
|
|
# Test complex expressions with single closure (regression test for nested closure issue)
|
|
var complex_expr_dsl = "set strip_len = strip_length()\n" +
|
|
"set base_value = 5\n" +
|
|
"animation stream2 = comet_animation(\n" +
|
|
" color=blue\n" +
|
|
" tail_length=strip_len / 8 + (2 * strip_len) - 10\n" +
|
|
" speed=(base_value + strip_len) * 2.5\n" +
|
|
" priority=max(1, min(10, strip_len / 6))\n" +
|
|
")"
|
|
|
|
var complex_code = animation_dsl.compile(complex_expr_dsl)
|
|
assert(complex_code != nil, "Should compile complex expressions")
|
|
|
|
# Count closure creations - each computed parameter should have exactly one closure
|
|
var closure_count = 0
|
|
pos = 0
|
|
while true
|
|
pos = string.find(complex_code, "animation.create_closure_value(", pos)
|
|
if pos < 0
|
|
break
|
|
end
|
|
closure_count += 1
|
|
pos += 1
|
|
end
|
|
assert(closure_count == 3, f"Should have exactly 3 closures for 3 computed parameters, found {closure_count}")
|
|
|
|
# Check that complex expressions are in single closures (no nested closures)
|
|
var nested_closure_count = 0
|
|
pos = 0
|
|
while true
|
|
# Look for closure inside closure pattern
|
|
var closure_start = string.find(complex_code, "animation.create_closure_value(", pos)
|
|
if closure_start < 0
|
|
break
|
|
end
|
|
var closure_end = string.find(complex_code, ") end)", closure_start)
|
|
if closure_end < 0
|
|
break
|
|
end
|
|
var closure_content = complex_code[closure_start..closure_end]
|
|
if string.find(closure_content, "animation.create_closure_value(") > 0
|
|
nested_closure_count += 1
|
|
end
|
|
pos = closure_end + 1
|
|
end
|
|
assert(nested_closure_count == 0, f"Should have no nested closures, found {nested_closure_count}")
|
|
|
|
# Verify specific complex expression patterns
|
|
var expected_complex_tail = "animation.resolve(strip_len_) / 8 + (2 * animation.resolve(strip_len_)) - 10"
|
|
assert(string.find(complex_code, expected_complex_tail) >= 0, "Should generate correct complex tail_length expression")
|
|
|
|
var expected_complex_speed = "(animation.resolve(base_value_) + animation.resolve(strip_len_)) * 2.5"
|
|
assert(string.find(complex_code, expected_complex_speed) >= 0, "Should generate correct complex speed expression")
|
|
|
|
var expected_complex_priority = "animation._math.max(1, animation._math.min(10, animation.resolve(strip_len_) / 6))"
|
|
assert(string.find(complex_code, expected_complex_priority) >= 0, "Should generate correct complex priority expression with math functions")
|
|
|
|
# Test simple expressions that don't need closures
|
|
var simple_expr_dsl = "set strip_len = strip_length()\n" +
|
|
"animation simple = comet_animation(\n" +
|
|
" color=red\n" +
|
|
" tail_length=strip_len\n" +
|
|
" speed=1.5\n" +
|
|
" priority=10\n" +
|
|
")"
|
|
|
|
var simple_code = animation_dsl.compile(simple_expr_dsl)
|
|
assert(simple_code != nil, "Should compile simple expressions")
|
|
|
|
# Simple variable reference should not create a closure
|
|
assert(string.find(simple_code, "simple_.tail_length = strip_len_") >= 0, "Should generate direct variable reference without closure")
|
|
|
|
# Test mathematical functions in computed expressions
|
|
var math_expr_dsl = "set strip_len = strip_length()\n" +
|
|
"animation math_test = comet_animation(\n" +
|
|
" color=red\n" +
|
|
" tail_length=max(1, min(strip_len, 20))\n" +
|
|
" speed=abs(strip_len - 30)\n" +
|
|
" priority=round(strip_len / 6)\n" +
|
|
")"
|
|
|
|
var math_code = animation_dsl.compile(math_expr_dsl)
|
|
assert(math_code != nil, "Should compile mathematical expressions")
|
|
|
|
# Check that mathematical functions are prefixed with self. in closures
|
|
assert(string.find(math_code, "animation._math.max(1, animation._math.min(") >= 0, "Should prefix math functions with animation._math. in closures")
|
|
assert(string.find(math_code, "animation._math.abs(") >= 0, "Should prefix abs function with self. in closures")
|
|
assert(string.find(math_code, "animation._math.round(") >= 0, "Should prefix round function with self. in closures")
|
|
|
|
print("✓ Computed values test passed")
|
|
return true
|
|
end
|
|
|
|
# Test error handling
|
|
def test_error_handling()
|
|
print("Testing error handling...")
|
|
|
|
# Test invalid syntax - should raise exception
|
|
var invalid_dsl = "invalid syntax here"
|
|
try
|
|
var berry_code = animation_dsl.compile(invalid_dsl)
|
|
assert(false, "Should have raised exception for invalid syntax")
|
|
except "dsl_compilation_error" as e, msg
|
|
# Expected behavior
|
|
end
|
|
|
|
# Test undefined references - should raise exception
|
|
var undefined_ref_dsl = "animation test = undefined_pattern"
|
|
|
|
try
|
|
var berry_code = animation_dsl.compile(undefined_ref_dsl)
|
|
assert(false, "Should have raised exception for undefined identifier")
|
|
except "dsl_compilation_error" as e, msg
|
|
# Expected behavior - undefined identifiers should raise exceptions
|
|
end
|
|
|
|
print("✓ Error handling test passed")
|
|
return true
|
|
end
|
|
|
|
# Test forward references (deferred resolution)
|
|
def test_forward_references()
|
|
print("Testing forward references...")
|
|
|
|
var dsl_source = "# Forward reference: animation uses color defined later\n" +
|
|
"animation fire_gradient = gradient_animation(color=red)\n" +
|
|
"color red = 0xFF0000\n" +
|
|
"color orange = 0xFF8000"
|
|
|
|
var berry_code = nil
|
|
var compilation_failed = false
|
|
|
|
try
|
|
var lexer = animation_dsl.create_lexer(dsl_source)
|
|
var transpiler = animation_dsl.SimpleDSLTranspiler(lexer)
|
|
berry_code = transpiler.transpile()
|
|
except "dsl_compilation_error" as e, msg
|
|
compilation_failed = true
|
|
print("Forward references not yet supported - compilation failed as expected")
|
|
end
|
|
|
|
# Should resolve forward references if supported
|
|
if berry_code != nil && !compilation_failed
|
|
assert(string.find(berry_code, "var red_ = 0xFFFF0000") >= 0, "Should define red color")
|
|
assert(string.find(berry_code, "var orange_ = 0xFFFF8000") >= 0, "Should define orange color")
|
|
print("Forward references resolved successfully")
|
|
else
|
|
print("Forward references not yet fully implemented - this is expected")
|
|
end
|
|
|
|
print("✓ Forward references test passed")
|
|
return true
|
|
end
|
|
|
|
# Test complex DSL example with core processing features
|
|
def test_complex_dsl()
|
|
print("Testing complex DSL example...")
|
|
|
|
var complex_dsl = "# LED Strip Configuration\n" +
|
|
"# strip length 60 # TEMPORARILY DISABLED\n" +
|
|
"\n" +
|
|
"# Color Definitions\n" +
|
|
"color custom_red = 0xFF0000\n" +
|
|
"color custom_blue = 0x0000FF\n" +
|
|
"\n" +
|
|
"# Variable Definitions\n" +
|
|
"set cycle_time = 5s\n" +
|
|
"set brightness = 80%\n" +
|
|
"\n" +
|
|
"# Animation Definitions\n" +
|
|
"animation red_pulse = pulsating_animation(color=red, period=2000)\n" +
|
|
"animation blue_breathe = breathe_animation(color=blue, period=4000)\n" +
|
|
"\n" +
|
|
"# Sequence Definition with Control Flow\n" +
|
|
"sequence demo {\n" +
|
|
" play red_pulse for 3s\n" +
|
|
" wait 1s\n" +
|
|
" repeat 2 times {\n" +
|
|
" play blue_breathe for 2s\n" +
|
|
" wait 500ms\n" +
|
|
" }\n" +
|
|
"}\n" +
|
|
"\n" +
|
|
"# Execution\n" +
|
|
"run demo"
|
|
|
|
var berry_code = animation_dsl.compile(complex_dsl)
|
|
|
|
if berry_code != nil
|
|
print("Complex DSL compiled successfully!")
|
|
|
|
# Check for key components
|
|
assert(string.find(berry_code, "var engine = animation.init_strip()") >= 0, "Should have default strip initialization")
|
|
assert(string.find(berry_code, "var custom_red_ = 0xFFFF0000") >= 0, "Should have color definitions")
|
|
assert(string.find(berry_code, "var demo_ = animation.sequence_manager(engine)") >= 0, "Should have sequence definition")
|
|
assert(string.find(berry_code, "engine.add(demo_)") >= 0, "Should have execution")
|
|
|
|
print("Generated code structure looks correct")
|
|
else
|
|
print("Complex DSL compilation failed - checking for specific issues...")
|
|
|
|
# Test individual components
|
|
var lexer = animation_dsl.create_lexer(complex_dsl)
|
|
|
|
print("Lexical analysis passed")
|
|
|
|
var transpiler = animation_dsl.SimpleDSLTranspiler(lexer)
|
|
var result = transpiler.transpile()
|
|
end
|
|
|
|
print("✓ Complex DSL test completed")
|
|
return true
|
|
end
|
|
|
|
# Test transpiler components individually
|
|
def test_transpiler_components()
|
|
print("Testing transpiler components...")
|
|
|
|
# Basic transpiler functionality test
|
|
print("Testing basic transpiler instantiation...")
|
|
|
|
# Test token processing
|
|
var lexer = animation_dsl.create_lexer("color red = 0xFF0000")
|
|
var tokens = extract_all_tokens(lexer)
|
|
assert(size(tokens) >= 4, "Should have multiple tokens")
|
|
|
|
# Reset lexer position before creating transpiler
|
|
lexer.reset()
|
|
var transpiler = animation_dsl.SimpleDSLTranspiler(lexer)
|
|
assert(!transpiler.at_end(), "Should not be at end initially")
|
|
|
|
print("✓ Transpiler components test passed")
|
|
return true
|
|
end
|
|
|
|
# Test core processing methods functionality
|
|
def test_core_processing_methods()
|
|
print("Testing core processing methods...")
|
|
|
|
# Test pulse animation generation
|
|
var pulse_dsl = "color custom_red = 0xFF0000\n" +
|
|
"animation solid_red = solid(color=custom_red)\n" +
|
|
"animation pulse_red = pulsating_animation(color=custom_red, period=2000)"
|
|
|
|
var berry_code = animation_dsl.compile(pulse_dsl)
|
|
assert(berry_code != nil, "Should compile pulse animation")
|
|
assert(string.find(berry_code, "animation.pulsating_animation(engine)") >= 0, "Should generate pulse animation")
|
|
|
|
# Test control flow
|
|
var control_dsl = "color custom_blue = 0x0000FF\n" +
|
|
"animation blue_anim = solid(color=custom_blue)\n" +
|
|
"sequence test {\n" +
|
|
" repeat 2 times {\n" +
|
|
" play blue_anim for 1s\n" +
|
|
" wait 500ms\n" +
|
|
" }\n" +
|
|
"}\n" +
|
|
"run test"
|
|
|
|
berry_code = animation_dsl.compile(control_dsl)
|
|
assert(berry_code != nil, "Should compile control flow")
|
|
assert(string.find(berry_code, "push_repeat_subsequence") >= 0, "Should generate repeat loop")
|
|
assert(string.find(berry_code, "push_wait_step") >= 0, "Should generate wait statement")
|
|
|
|
# Test variable assignments
|
|
var var_dsl = "set opacity = 75%\n" +
|
|
"set duration = 3s"
|
|
|
|
berry_code = animation_dsl.compile(var_dsl)
|
|
assert(berry_code != nil, "Should compile variables")
|
|
assert(string.find(berry_code, "var opacity_ = 191") >= 0, "Should convert percentage")
|
|
assert(string.find(berry_code, "var duration_ = 3000") >= 0, "Should convert time")
|
|
|
|
print("✓ Core processing methods test passed")
|
|
return true
|
|
end
|
|
|
|
# Test event system DSL compilation
|
|
def test_event_system_dsl()
|
|
print("Testing event system DSL compilation...")
|
|
|
|
var event_dsl = "# strip length 30 # TEMPORARILY DISABLED\n" +
|
|
"color custom_red = 0xFF0000\n" +
|
|
"color custom_blue = 0x0000FF\n" +
|
|
"\n" +
|
|
"# Event handlers\n" +
|
|
"animation red_solid = solid(color=red)\n" +
|
|
"animation blue_solid = solid(color=blue)\n" +
|
|
"on button_press: red_solid\n" +
|
|
"on timer(5s): blue_solid\n" +
|
|
"on startup: interrupt current\n" +
|
|
"\n" +
|
|
"# Main sequence\n" +
|
|
"sequence main {\n" +
|
|
" play solid(color=red) for 2s\n" +
|
|
"}\n" +
|
|
"\n" +
|
|
"run main"
|
|
|
|
var berry_code = animation_dsl.compile(event_dsl)
|
|
|
|
# Event system is complex and simplified transpiler has basic support
|
|
if berry_code != nil
|
|
print("Event system compiled successfully (basic support)")
|
|
|
|
# Check for basic event handler registration if present
|
|
if string.find(berry_code, "register_event_handler") >= 0
|
|
print("Event handler registration found")
|
|
end
|
|
else
|
|
print("Event system compilation failed - this is expected with simplified transpiler")
|
|
print("Core DSL functionality is working correctly")
|
|
end
|
|
|
|
# print("Generated event system Berry code:")
|
|
# print("==================================================")
|
|
# print(berry_code)
|
|
# print("==================================================")
|
|
|
|
print("✓ Event system DSL test passed")
|
|
return true
|
|
end
|
|
|
|
# Test property assignments
|
|
def test_property_assignments()
|
|
print("Testing property assignments...")
|
|
|
|
var dsl_with_properties = "color custom_red = 0xFF0000\n" +
|
|
"animation red_anim = solid(color=custom_red)\n" +
|
|
"red_anim.opacity = 128\n" +
|
|
"red_anim.priority = 10"
|
|
|
|
var berry_code = animation_dsl.compile(dsl_with_properties)
|
|
|
|
assert(berry_code != nil, "Should generate Berry code with property assignments")
|
|
|
|
# Check that property assignments are generated correctly (new behavior: direct underscore access)
|
|
assert(string.find(berry_code, "red_anim_.opacity = 128") >= 0, "Should generate opacity property assignment")
|
|
assert(string.find(berry_code, "red_anim_.priority = 10") >= 0, "Should generate priority property assignment")
|
|
|
|
# Verify the generated code compiles
|
|
try
|
|
compile(berry_code)
|
|
print("✓ Generated property assignment code compiles successfully")
|
|
except .. as e, msg
|
|
print(f"✗ Generated property assignment code compilation failed: {msg}")
|
|
assert(false, "Generated code should compile")
|
|
end
|
|
|
|
print("✓ Property assignments test passed")
|
|
return true
|
|
end
|
|
|
|
# Test comment preservation in generated Berry code
|
|
def test_comment_preservation()
|
|
print("Testing comment preservation...")
|
|
|
|
var dsl_with_comments = "# Header comment\n" +
|
|
"# strip length 30 # Strip config comment (TEMPORARILY DISABLED)\n" +
|
|
"# Color section\n" +
|
|
"color custom_red = 0xFF0000 # Red color\n" +
|
|
"animation solid_red = solid(color=custom_red) # Red animation\n" +
|
|
"sequence demo {\n" +
|
|
" # Play red\n" +
|
|
" play solid_red for 2s # Red phase\n" +
|
|
" wait 1s # Pause\n" +
|
|
"}\n" +
|
|
"run demo # Execute"
|
|
|
|
var berry_code = animation_dsl.compile(dsl_with_comments)
|
|
|
|
assert(berry_code != nil, "Should generate Berry code with comments")
|
|
|
|
# Check that comments are preserved
|
|
assert(string.find(berry_code, "# Header comment") >= 0, "Should preserve header comment")
|
|
assert(string.find(berry_code, "# Strip config comment") >= 0, "Should preserve inline comment")
|
|
assert(string.find(berry_code, "# Color section") >= 0, "Should preserve section comment")
|
|
assert(string.find(berry_code, "# Red color") >= 0, "Should preserve color comment")
|
|
assert(string.find(berry_code, "# Red animation") >= 0, "Should preserve animation comment")
|
|
assert(string.find(berry_code, " # Play red") >= 0, "Should preserve sequence comment with indentation")
|
|
assert(string.find(berry_code, "# Red phase") >= 0, "Should preserve play statement comment")
|
|
assert(string.find(berry_code, "# Pause") >= 0, "Should preserve wait statement comment")
|
|
assert(string.find(berry_code, "# Execute") >= 0, "Should preserve run statement comment")
|
|
|
|
# Count comment lines
|
|
var lines = string.split(berry_code, "\n")
|
|
var comment_count = 0
|
|
for line : lines
|
|
if string.find(line, "#") >= 0
|
|
comment_count += 1
|
|
end
|
|
end
|
|
|
|
assert(comment_count >= 9, "Should have at least 9 lines with comments")
|
|
|
|
print("✓ Comment preservation test passed")
|
|
return true
|
|
end
|
|
|
|
# Test easing keywords
|
|
def test_easing_keywords()
|
|
print("Testing easing keywords...")
|
|
|
|
var dsl_with_easing = "# strip length 30 # TEMPORARILY DISABLED\n" +
|
|
"# Test all easing keywords\n" +
|
|
"animation linear_anim = solid(color=linear)\n" +
|
|
"animation smooth_anim = solid(color=smooth)\n" +
|
|
"animation ease_in_anim = solid(color=ease_in)\n" +
|
|
"animation ease_out_anim = solid(color=ease_out)\n" +
|
|
"animation ramp_anim = solid(color=ramp)\n" +
|
|
"animation square_anim = solid(color=square)\n" +
|
|
"run linear_anim"
|
|
|
|
var berry_code = animation_dsl.compile(dsl_with_easing)
|
|
|
|
assert(berry_code != nil, "Should generate Berry code with easing keywords")
|
|
|
|
# Check that all easing keywords are properly converted to direct animation module access
|
|
var easing_keywords = ["linear", "smooth", "ease_in", "ease_out", "ramp", "square"]
|
|
for easing : easing_keywords
|
|
# Check if the easing keyword exists in animation module (they're lowercase)
|
|
import introspect
|
|
if introspect.contains(animation, easing)
|
|
assert(string.find(berry_code, f"animation.{easing}") >= 0, f"Should convert {easing} to animation.{easing}")
|
|
else
|
|
assert(string.find(berry_code, f"{easing}_") >= 0, f"Should convert {easing} to {easing}_")
|
|
end
|
|
end
|
|
|
|
# Test easing keywords as function calls (regression test for breathing_colors.anim issue)
|
|
var dsl_with_function_calls = "# strip length 30 # TEMPORARILY DISABLED\n" +
|
|
"color custom_red = 0xFF0000\n" +
|
|
"animation test_anim = solid(color=custom_red)\n" +
|
|
"test_anim.opacity = 128\n" +
|
|
"run test_anim"
|
|
|
|
var function_call_code = animation_dsl.compile(dsl_with_function_calls)
|
|
assert(function_call_code != nil, "Should handle easing keywords as function calls")
|
|
# Note: Function calls like smooth(100, 255, 4s) are handled differently than simple identifiers
|
|
# They should generate animation.smooth(100, 255, 4000) calls
|
|
assert(string.find(function_call_code, "test_anim_.opacity = 128") >= 0, "Should set opacity property correctly")
|
|
|
|
print("✓ Easing keywords test passed")
|
|
return true
|
|
end
|
|
|
|
# Test animation type checking
|
|
def test_animation_type_checking()
|
|
print("Testing animation type checking...")
|
|
|
|
# Test valid animation factory functions
|
|
var valid_animation_dsl = "# strip length 30 # TEMPORARILY DISABLED\n" +
|
|
"color custom_red = 0xFF0000\n" +
|
|
"animation pulse_red = pulsating_animation(color=custom_red, period=2000)\n" +
|
|
"animation solid_blue = solid(color=0x0000FF)\n" +
|
|
"run pulse_red"
|
|
|
|
var berry_code = animation_dsl.compile(valid_animation_dsl)
|
|
assert(berry_code != nil, "Should compile valid animation factories")
|
|
assert(string.find(berry_code, "animation.pulsating_animation(engine)") >= 0, "Should generate pulsating_animation call")
|
|
assert(string.find(berry_code, "animation.solid(engine)") >= 0, "Should generate solid call")
|
|
|
|
# Test invalid animation factory function (should fail at transpile time)
|
|
var invalid_animation_dsl = "# strip length 30 # TEMPORARILY DISABLED\n" +
|
|
"animation invalid_anim = non_existent_animation(color=custom_red)"
|
|
|
|
try
|
|
var invalid_code = animation_dsl.compile(invalid_animation_dsl)
|
|
assert(false, "Should have failed for non-existent animation factory")
|
|
except "dsl_compilation_error" as e, msg
|
|
assert(string.find(msg, "does not exist") >= 0, "Should report non-existent factory")
|
|
end
|
|
|
|
# Test color provider assigned to animation (should fail at transpile time)
|
|
var color_provider_as_animation_dsl = "# strip length 30 # TEMPORARILY DISABLED\n" +
|
|
"animation invalid_anim = rich_palette(colors=breathe_palette)"
|
|
|
|
try
|
|
var invalid_code = animation_dsl.compile(color_provider_as_animation_dsl)
|
|
assert(false, "Should have failed for color provider assigned to animation")
|
|
except "dsl_compilation_error" as e, msg
|
|
assert(string.find(msg, "does not create an instance of animation.animation class") >= 0, "Should report type mismatch")
|
|
end
|
|
|
|
print("✓ Animation type checking test passed")
|
|
return true
|
|
end
|
|
|
|
# Test color type checking and color providers
|
|
def test_color_type_checking()
|
|
print("Testing color type checking...")
|
|
|
|
# Test simple color values
|
|
var simple_color_dsl = "# strip length 30 # TEMPORARILY DISABLED\n" +
|
|
"color custom_red = 0xFF0000\n" +
|
|
"color custom_blue = 0x0000FF\n" +
|
|
"color named_green = green\n" +
|
|
"animation test_anim = solid(color=custom_red)\n" +
|
|
"run test_anim"
|
|
|
|
var berry_code = animation_dsl.compile(simple_color_dsl)
|
|
assert(berry_code != nil, "Should compile simple color values")
|
|
assert(string.find(berry_code, "var custom_red_ = 0xFFFF0000") >= 0, "Should generate red color")
|
|
assert(string.find(berry_code, "var custom_blue_ = 0xFF0000FF") >= 0, "Should generate blue color")
|
|
|
|
# Test color provider functions (if they exist)
|
|
var color_provider_dsl = "# strip length 30 # TEMPORARILY DISABLED\n" +
|
|
"color cycle_colors = color_cycle(colors=[0xFF0000, 0x00FF00, 0x0000FF])\n" +
|
|
"animation cycle_anim = solid(color=cycle_colors)\n" +
|
|
"run cycle_anim"
|
|
|
|
try
|
|
var provider_code = animation_dsl.compile(color_provider_dsl)
|
|
if provider_code != nil
|
|
print("Color provider compilation successful")
|
|
assert(string.find(provider_code, "animation.color_cycle(engine)") >= 0, "Should generate color provider call")
|
|
else
|
|
print("Color provider compilation failed - this may be expected if color providers don't exist")
|
|
end
|
|
except "dsl_compilation_error" as e, msg
|
|
print("Color provider compilation failed (expected if not implemented): " + msg)
|
|
end
|
|
|
|
# Test invalid color provider function (should fail at transpile time)
|
|
var invalid_color_dsl = "# strip length 30 # TEMPORARILY DISABLED\n" +
|
|
"color invalid_color = non_existent_color_provider(param=value)"
|
|
|
|
try
|
|
var invalid_code = animation_dsl.compile(invalid_color_dsl)
|
|
# This might succeed if the transpiler doesn't validate color providers yet
|
|
print("Invalid color provider compiled - validation may not be fully implemented")
|
|
except "dsl_compilation_error" as e, msg
|
|
print("Invalid color provider correctly rejected: " + msg)
|
|
end
|
|
|
|
print("✓ Color type checking test passed")
|
|
return true
|
|
end
|
|
|
|
# Test invalid sequence commands
|
|
def test_invalid_sequence_commands()
|
|
print("Testing invalid sequence commands...")
|
|
|
|
# Test 1: Invalid command in sequence
|
|
var invalid_command_dsl =
|
|
"animation test_anim = solid(color=red)\n" +
|
|
"sequence bad {\n" +
|
|
" do_bad_things anim\n" +
|
|
" play test_anim for 1s\n" +
|
|
"}"
|
|
|
|
try
|
|
var result1 = animation_dsl.compile(invalid_command_dsl)
|
|
assert(false, "Should have thrown an exception for invalid command")
|
|
except "dsl_compilation_error"
|
|
# Expected - invalid command should cause compilation error
|
|
end
|
|
|
|
# Test 2: Another invalid command
|
|
var invalid_command_dsl2 =
|
|
"animation test_anim = solid(color=red)\n" +
|
|
"sequence bad {\n" +
|
|
" play test_anim for 1s\n" +
|
|
" invalid_command\n" +
|
|
" wait 500ms\n" +
|
|
"}"
|
|
|
|
try
|
|
var result2 = animation_dsl.compile(invalid_command_dsl2)
|
|
assert(false, "Should have thrown an exception for invalid command")
|
|
except "dsl_compilation_error"
|
|
# Expected - invalid command should cause compilation error
|
|
end
|
|
|
|
# Test 3: Invalid command in repeat block
|
|
var invalid_repeat_dsl =
|
|
"animation test_anim = solid(color=red)\n" +
|
|
"sequence bad {\n" +
|
|
" repeat 3 times {\n" +
|
|
" play test_anim for 1s\n" +
|
|
" bad_command_in_repeat\n" +
|
|
" wait 500ms\n" +
|
|
" }\n" +
|
|
"}"
|
|
|
|
try
|
|
var result3 = animation_dsl.compile(invalid_repeat_dsl)
|
|
assert(false, "Should have thrown an exception for invalid command in repeat")
|
|
except "dsl_compilation_error"
|
|
# Expected - invalid command should cause compilation error
|
|
end
|
|
|
|
# Test 4: Valid sequence should still work
|
|
var valid_sequence_dsl =
|
|
"animation test_anim = solid(color=red)\n" +
|
|
"sequence good {\n" +
|
|
" play test_anim for 1s\n" +
|
|
" wait 500ms\n" +
|
|
" log(\"test message\")\n" +
|
|
" test_anim.opacity = 128\n" +
|
|
"}"
|
|
|
|
var result4 = animation_dsl.compile(valid_sequence_dsl)
|
|
assert(result4 != nil, "Should compile valid sequence successfully")
|
|
assert(string.find(result4, "sequence_manager") >= 0, "Should generate sequence manager")
|
|
assert(string.find(result4, "push_play_step") >= 0, "Should generate play step")
|
|
assert(string.find(result4, "push_wait_step") >= 0, "Should generate wait step")
|
|
assert(string.find(result4, "log(f\"test message\", 3)") >= 0, "Should generate log statement")
|
|
assert(string.find(result4, "push_closure_step") >= 0, "Should generate closure steps")
|
|
|
|
print("✓ Invalid sequence commands test passed")
|
|
return true
|
|
end
|
|
|
|
# Run all tests
|
|
def run_dsl_transpiler_tests()
|
|
print("=== DSL Transpiler Test Suite ===")
|
|
|
|
var tests = [
|
|
test_transpiler_components,
|
|
test_basic_transpilation,
|
|
test_color_definitions,
|
|
test_color_alpha_channel,
|
|
test_strip_configuration,
|
|
test_simple_patterns,
|
|
test_sequences,
|
|
test_sequence_assignments,
|
|
test_variable_duration,
|
|
test_multiple_run_statements,
|
|
test_variable_assignments,
|
|
test_computed_values,
|
|
test_error_handling,
|
|
test_forward_references,
|
|
test_complex_dsl,
|
|
test_core_processing_methods,
|
|
test_event_system_dsl,
|
|
test_property_assignments,
|
|
test_comment_preservation,
|
|
test_easing_keywords,
|
|
test_animation_type_checking,
|
|
test_color_type_checking,
|
|
test_invalid_sequence_commands
|
|
]
|
|
|
|
var passed = 0
|
|
var total = size(tests)
|
|
|
|
for test_func : tests
|
|
try
|
|
if test_func()
|
|
passed += 1
|
|
else
|
|
print("✗ Test failed")
|
|
end
|
|
except .. as error_type, error_message
|
|
print("✗ Test crashed: " + str(error_type) + " - " + str(error_message))
|
|
end
|
|
print("") # Add spacing between tests
|
|
end
|
|
|
|
print("=== Results: " + str(passed) + "/" + str(total) + " tests passed ===")
|
|
|
|
if passed == total
|
|
print("🎉 All DSL transpiler tests passed!")
|
|
return true
|
|
else
|
|
print("❌ Some DSL transpiler tests failed")
|
|
raise "test_failed"
|
|
end
|
|
end
|
|
|
|
# Auto-run tests when file is executed
|
|
run_dsl_transpiler_tests() |