1 module tests.expressions;
2 
3 private
4 {
5     import std.conv : to;
6 
7     import tests.asserts;
8 }
9 
10 // Literals
11 unittest
12 {
13     // True/False
14     assertRender("{{ true }}", "true");
15     assertRender("{{ True }}", "true");
16     assertRender("{{ false }}", "false");
17     assertRender("{{ False }}", "false");
18 
19     // Numbers
20     assertRender("{{ 10 }}", "10");
21     assertRender("{{ 10.5 }}", (10.5).to!string);
22     assertRender("{{ -10 }}", "-10");
23     assertRender("{{ -10.5 }}", (-10.5).to!string);
24 
25     // String
26     assertRender("{{ 'some string' }}", "some string");
27     assertRender(`{{ "some string" }}`, "some string");
28 
29     // List
30     assertRender("{{ [] }}", "[]");
31     assertRender("{{ [1,2,3] }}", "[1, 2, 3]");
32 
33     // Tuple (also list actually)
34     assertRender("{{ (1,2,3) }}", "[1, 2, 3]");
35     assertRender("{{ (1,)}}", "[1]");
36 
37     // Dict
38     assertRender("{{ {} }}", "{}");
39     assertRender(`{{ {a:10} }}`, "{a: 10}");
40     // can't check print of more than one key due to undefined order:
41     assertRender(`{{ {a:True, 'b':10, "c":"string"} | sort }}`,
42             "[['a', true], ['b', 10], ['c', 'string']]");
43 
44     // Idents
45     struct Dummy
46     {
47         string str;
48         int[]  arr;
49         ubyte  idx;
50     }
51     auto val = Dummy("some string", [1,2,3], 2);
52 
53     assertRender!(val)("{{ val | sort }}", "[['arr', [1, 2, 3]], ['idx', 2], ['str', 'some string']]");
54     assertRender!(val)("{{ val.length }}", "3");
55 
56     assertRender!(val)("{{ val.idx }}", "2");
57 
58     assertRender!(val)("{{ val.str }}", "some string");
59     assertRender!(val)("{{ val['str'] }}", "some string");
60     assertRender!(val)(`{{ val["str"] }}`, "some string");
61     assertRender!(val)(`{{ val.str.length }}`, "11");
62 
63     assertRender!(val)("{{ val.arr }}", "[1, 2, 3]");
64     assertRender!(val)("{{ val.arr[0] }}", "1");
65     assertRender!(val)("{{ val.arr[val.idx - 1] }}", "2");
66     assertRender!(val)("{{ val.arr.length }}", "3");
67 }
68 
69 // Simple math expressions
70 unittest
71 {
72     assertRender("{{ 1 }}", "1");
73     assertRender("{{ 1 + 1 }}", "2");
74     assertRender("{{ 1 - 2 }}", "-1");
75     assertRender("{{ -2 * 3 }}", "-6");
76     assertRender("{{ 10 // 3 }}", "3");
77     assertRender("{{ 10 % 3 }}", "1");
78     assertRender("{{ 10.0 / 3.0 }}", (10.0/3.0).to!string);
79 }
80 
81 
82 // Priority
83 unittest
84 {
85     assertRender("{{ 2+2*2**2 }}", "10");
86     assertRender("{{ ((2+2)*2)**2 }}", "64");
87     assertRender("{{ 2-2/2 }}", "1");
88 }
89 
90 // Associativity
91 unittest
92 {
93     assertRender("{{ 1-2-3-4 }}", "-8");
94     assertRender("{{ 2**2**3 }}", "256");
95     assertRender("{{ 10/5/2 }}", "1");
96 }
97 
98 //Logical expressions
99 unittest
100 {
101     assertRender("{{10 ==  9}}", "false");
102     assertRender("{{10 == 10}}", "true");
103     assertRender("{{10 == 11}}", "false");
104 
105     assertRender("{{10 !=  9}}", "true");
106     assertRender("{{10 != 10}}", "false");
107     assertRender("{{10 != 11}}", "true");
108 
109     assertRender("{{10 >  9}}", "true");
110     assertRender("{{10 > 10}}", "false");
111     assertRender("{{10 > 11}}", "false");
112 
113     assertRender("{{10 >=  9}}", "true");
114     assertRender("{{10 >= 10}}", "true");
115     assertRender("{{10 >= 11}}", "false");
116 
117     assertRender("{{10 <  9}}", "false");
118     assertRender("{{10 < 10}}", "false");
119     assertRender("{{10 < 11}}", "true");
120 
121     assertRender("{{10 <=  9}}", "false");
122     assertRender("{{10 <= 10}}", "true");
123     assertRender("{{10 <= 11}}", "true");
124 }
125 
126 unittest
127 {
128     assertRender("{{ not true }}", "false");
129     assertRender("{{ not false }}", "true");
130 
131     assertRender("{{ false or false }}", "false");
132     assertRender("{{ false or true  }}", "true");
133     assertRender("{{ true  or false }}", "true");
134     assertRender("{{ true  or true  }}", "true");
135 
136     assertRender("{{ false and false }}", "false");
137     assertRender("{{ false and true  }}", "false");
138     assertRender("{{ true  and false }}", "false");
139     assertRender("{{ true  and true  }}", "true");
140 
141     assertRender("{{  true or false  and false }}", "true");
142     assertRender("{{ (true or false) and false }}", "false");
143 
144     // Stop computation prevents error:
145     assertRender("{{ false or false or true or 1 // 0}}", "true");
146     assertRender("{{ true and true and false and 1 // 0}}", "false");
147 }
148 
149 unittest
150 {
151     assertRender("{{ 1 in [1, 2, 3] }}", "true");
152     assertRender("{{ 4 in [1, 2, 3] }}", "false");
153     assertRender("{{ 1 not in [1, 2, 3] }}", "false");
154     assertRender("{{ 4 not in [1, 2, 3] }}", "true");
155 
156     assertRender("{{ 'key' in {key: 'val'} }}", "true");
157     assertRender("{{ 'val' in {key: 'val'} }}", "false");
158     assertRender("{{ 'key' not in {key: 'val'} }}", "false");
159     assertRender("{{ 'val' not in {key: 'val'} }}", "true");
160 
161     assertRender("{{ 'tri' in 'string' }}", "true");
162     assertRender("{{ 'tra' in 'string' }}", "false");
163     assertRender("{{ 'tri' not in 'string' }}", "false");
164     assertRender("{{ 'tra' not in 'string' }}", "true");
165 
166     assertRender("{{ 1.0 is number}}", "true");
167     assertRender("{{ 'a' is number}}", "false");
168     assertRender("{{ 1.0 not is number}}", "false");
169     assertRender("{{ 'a' not is number}}", "true");
170 
171     assertRender("{{ 'string'|upper }}", "STRING");
172     assertRender("{{ undefinedVar | d | upper }}", "");
173     assertRender("{{ undefinedVar | d('undefined') | upper }}", "UNDEFINED");
174     assertRender("{{ 1 ~ (1>2) ~ 'str' ~ [1] }}", "1falsestr[1]");
175     assertRender("{{ 1 ~  1>2  ~ 'str' ~ [1] }}", "false");
176 
177     assertRender("{{ length([1, 2, 3])}}", "3");
178 }
179 
180 unittest
181 {
182     assertRender("{{ 1 if true  else 2}}", "1");
183     assertRender("{{ 1 if false else 2}}", "2");
184 
185     assertRender("{{ '!' ~ (undefVar if undefVar is defined else 'a') ~ '!' }}", "!a!");
186 }
187 
188 unittest
189 {
190     int myStrLen(string str)
191     {
192         return cast(int)str.length;
193     }
194 
195     string str = "abcde";
196 
197     assertRender!(myStrLen, str)("{{ myStrLen( '!' ~ str ~ '!') }}", "7");
198 }
199 
200 
201 // Implicity cast to string
202 unittest
203 {
204     assertRender("{{ '' ~ 123 }}", "123");
205     assertRender("{{ '' ~ 1.5 }}", "1.5");
206     assertRender("{{ '' ~ true }}", "true");
207     assertRender("{{ '' ~ [1,2,3] }}", "[1, 2, 3]");
208     assertRender("{{ '' ~ (1,) }}", "[1]");
209     assertRender("{{ '' ~ {a:1} }}", "{a: 1}");
210 }
211 
212 // Implicity cast to bool
213 unittest
214 {
215     assertRender("{{ false or -1 }}", "true");
216     assertRender("{{ false or  1 }}", "true");
217     assertRender("{{ false or  0 }}", "false");
218 
219     assertRender("{{ false or -1.5 }}", "true");
220     assertRender("{{ false or  1.5 }}", "true");
221     assertRender("{{ false or  0.0 }}", "false");
222 
223     assertRender("{{ false or  'str' }}", "true");
224     assertRender("{{ false or  '   ' }}", "true");
225     assertRender("{{ false or  ''    }}", "false");
226 
227     assertRender("{{ false or [1,2] }}", "true");
228     assertRender("{{ false or (1,)  }}", "true");
229     assertRender("{{ false or []    }}", "false");
230 
231     assertRender("{{ false or {a:1} }}", "true");
232     assertRender("{{ false or {}    }}", "false");
233 
234     assertRender("{{ false or undefVar }}", "false");
235 }
236 
237 // Implicity cast integer to float
238 unittest
239 {
240     assertRender("{{ 1.5 + 15 }}", (16.5).to!string);
241 }
242 
243 
244 // WS eating + UTF8 strings/raw data
245 unittest
246 {
247     assertRender(
248             "\u0061\u0101\u0800\U00010000" ~
249             "{{" ~
250             "\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u202F\u205F\u00A0\u3000\u0020\t" ~
251             "\n\r\n\r\u2028\u2029" ~
252             "'\u0061\u0101\u0800\U00010000'" ~
253             "\n\r\n\r\u2028\u2029" ~
254             "\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u202F\u205F\u00A0\u3000\u0020\t" ~
255             "\n\r\n\r\u2028\u2029" ~
256             "}}" ~
257             "\u0061\u0101\u0800\U00010000"
258             ,
259             "\u0061\u0101\u0800\U00010000" ~
260             "\u0061\u0101\u0800\U00010000" ~
261             "\u0061\u0101\u0800\U00010000"
262         );
263 }