55from pathlib import Path
66import unittest
77
8+ from flitter import configure_logger
9+ from flitter .language .tree import Literal , StoreGlobal , Function
810from flitter .language .parser import parse
911from flitter .model import Vector , Context , StateDict , DummyStateDict , null
1012
1113
12- class TestLanguage (unittest .TestCase ):
13- def _test_integration (self , filepath ):
14+ configure_logger ('WARNING' )
15+
16+
17+ class TestLanguageFeatures (unittest .TestCase ):
18+ def assertCodeOutput (self , code , output , ** names ):
19+ """
20+ Tests that the supplied code produces the supplied output when compiled and
21+ run dynamically in the VM, and also when statically evaluated by the simplifier.
22+ """
23+ top = parse (code .strip () + "\n " )
24+ output = output .strip ()
25+ run_context = Context (names = {name : Vector (value ) for name , value in names .items ()}, state = StateDict ())
26+ vm_output = '\n ' .join (repr (node ) for node in top .compile (initial_lnames = tuple (names )).run (run_context ).root .children )
27+ self .assertEqual (vm_output , output , msg = "VM output is incorrect" )
28+ simplifier_output = []
29+ unknown = []
30+ for expr in top .simplify (static = names ).expressions :
31+ if isinstance (expr , Literal ):
32+ simplifier_output .extend (repr (value ) for value in expr .value )
33+ elif isinstance (expr , (StoreGlobal , Function )):
34+ continue
35+ else :
36+ unknown .append (expr )
37+ self .assertEqual (unknown , [])
38+ self .assertEqual ('\n ' .join (simplifier_output ), output , msg = "Simplifier output is incorrect" )
39+
40+ def test_literal_node (self ):
41+ self .assertCodeOutput (
42+ """
43+ !foo #bar x=5 y=:baz z='Hello world!'
44+ """ ,
45+ """
46+ !foo #bar x=5 y=:baz z='Hello world!'
47+ """ )
48+
49+ def test_names (self ):
50+ self .assertCodeOutput (
51+ """
52+ !foo x=x y=y z=z #bar
53+ """ ,
54+ """
55+ !foo #bar x=5 y=:baz z='Hello world!'
56+ """ , x = 5 , y = Vector .symbol ('baz' ), z = 'Hello world!' )
57+
58+ def test_let (self ):
59+ self .assertCodeOutput (
60+ """
61+ let x=5 y=:baz z='Hello world!'
62+ !foo #bar x=x y=y z=z
63+ """ ,
64+ """
65+ !foo #bar x=5 y=:baz z='Hello world!'
66+ """ )
67+
68+ def test_multibind_loop (self ):
69+ self .assertCodeOutput (
70+ """
71+ for x;y in ..9
72+ !node x=x y=y
73+ """ ,
74+ """
75+ !node x=0 y=1
76+ !node x=2 y=3
77+ !node x=4 y=5
78+ !node x=6 y=7
79+ !node x=8
80+ """ )
81+
82+ def test_nested_loops (self ):
83+ self .assertCodeOutput (
84+ """
85+ for i in ..n
86+ !group i=i
87+ for j in ..i
88+ !item numbers=(k*(j+1) for k in 1..i+1)
89+ """ ,
90+ """
91+ !group i=0
92+ !group i=1
93+ !item numbers=1
94+ !group i=2
95+ !item numbers=1;2
96+ !item numbers=2;4
97+ !group i=3
98+ !item numbers=1;2;3
99+ !item numbers=2;4;6
100+ !item numbers=3;6;9
101+ """ , n = 4 )
102+
103+ def test_recursive_inlined_function (self ):
104+ # Also default parameter values and out-of-order named arguments
105+ self .assertCodeOutput (
106+ """
107+ func fib(n, x=0, y=1)
108+ fib(n-1, y=y+x, x=y) if n > 0 else x
109+
110+ !fib x=fib(10)
111+ """ ,
112+ """
113+ !fib x=55
114+ """ )
115+
116+ def test_builtin_calls (self ):
117+ self .assertCodeOutput (
118+ """
119+ for i in ..n
120+ !color rgb=hsv(i/n;1;1)
121+ """ ,
122+ """
123+ !color rgb=1;0;0
124+ !color rgb=1;1;0
125+ !color rgb=0;1;0
126+ !color rgb=0;1;1
127+ !color rgb=0;0;1
128+ !color rgb=1;0;1
129+ """ , n = 6 )
130+
131+ def test_template_calls (self ):
132+ self .assertCodeOutput (
133+ """
134+ func foo(nodes, x=10, y, z='world')
135+ !foo x=x z='hello';z
136+ nodes #test y=y
137+
138+ @foo
139+ @foo z='you'
140+ !bar
141+ @foo y=5
142+ @foo z='me' x=99
143+ !baz
144+ """ ,
145+ """
146+ !foo x=10 z='hello';'world'
147+ !foo x=10 z='hello';'you'
148+ !bar #test
149+ !foo x=10 z='hello';'world'
150+ !foo #test x=99 z='hello';'me' y=5
151+ !baz #test
152+ """ )
153+
154+
155+ class TestExampleScripts (unittest .TestCase ):
156+ def _test_integration_script (self , filepath ):
14157 self .maxDiff = None
15158 state = StateDict ()
16159 null_state = DummyStateDict ()
@@ -25,27 +168,31 @@ def _test_integration(self, filepath):
25168 self .assertEqual (len (windows ), 1 , msg = "Should be a single window node in program output" )
26169 self .assertGreater (len (tuple (windows [0 ].children )), 0 , "Output window node should have children" )
27170 # Simplify AST with dynamic names, compile and execute:
28- program2 = top .simplify (dynamic = set (names )).compile (initial_lnames = tuple (names ))
171+ top2 = top .simplify (dynamic = set (names ))
172+ self .assertEqual (repr (top2 .simplify (dynamic = set (names ))), repr (top2 ), msg = "Dynamic simplification not complete in one step" )
173+ program2 = top2 .compile (initial_lnames = tuple (names ))
29174 program2 .set_path (filepath )
30- self .assertNotEqual (len (program1 ), len (program2 ), msg = "Program lengths should be different" )
175+ self .assertNotEqual (len (program1 ), len (program2 ), msg = "Dynamically-simplified program length should be different from original " )
31176 root2 = program2 .run (Context (names = dict (names ), state = state )).root
32- self .assertEqual (repr (root2 ), repr (root1 ), msg = "Program outputs should match" )
177+ self .assertEqual (repr (root2 ), repr (root1 ), msg = "Dynamically-simplified program output doesn't match original " )
33178 # Simplify AST with static names and null state, compile and execute:
34- program3 = top .simplify (static = names , state = null_state ).compile ()
179+ top3 = top .simplify (static = names , state = null_state )
180+ self .assertEqual (repr (top3 .simplify (static = names , state = null_state )), repr (top3 ), msg = "Static simplification not complete in one step" )
181+ program3 = top3 .compile ()
35182 program3 .set_path (filepath )
36- self .assertNotEqual (len (program3 ), len (program1 ), msg = "Program lengths should be different" )
183+ self .assertNotEqual (len (program3 ), len (program1 ), msg = "Statically-simplified program length should be different from original " )
37184 root3 = program3 .run (Context (state = state )).root
38- self .assertEqual (repr (root3 ), repr (root1 ), msg = "Program outputs should match" )
185+ self .assertEqual (repr (root3 ), repr (root1 ), msg = "Statically-simplified program output doesn't match original " )
39186
40187 def _test_integration_all_scripts (self , dirpath ):
41188 count = 0
42189 for filename in dirpath .iterdir ():
43190 filepath = dirpath / filename
44191 if filepath .suffix == '.fl' :
45192 with self .subTest (filepath = filepath ):
46- self ._test_integration (filepath )
193+ self ._test_integration_script (filepath )
47194 count += 1
48- self .assertGreater (count , 0 )
195+ self .assertGreater (count , 0 , msg = "No scripts found" )
49196
50197 def test_integration_examples (self ):
51198 self ._test_integration_all_scripts (Path (__file__ ).parent .parent / 'examples' )
0 commit comments