View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl3;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertNull;
21  import static org.junit.jupiter.api.Assertions.assertThrows;
22  import static org.junit.jupiter.api.Assertions.assertTrue;
23  
24  import org.junit.jupiter.api.Test;
25  
26  /**
27   * Test cases for the if statement.
28   */
29  @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
30  class IfTest extends JexlTestCase {
31      public IfTest() {
32          super("IfTest");
33      }
34  
35      /**
36       * Test the if statement handles blocks in the else statement correctly
37       *
38       * @throws Exception on any error
39       */
40      @Test
41      void testBlockElse() throws Exception {
42          final JexlScript e = JEXL.createScript("if (false) {1} else {2 ; 3}");
43          final JexlContext jc = new MapContext();
44  
45          final Object o = e.execute(jc);
46          assertEquals(Integer.valueOf(3), o, "Result is wrong");
47      }
48  
49      /**
50       * Test the if statement handles blocks correctly
51       *
52       * @throws Exception on any error
53       */
54      @Test
55      void testBlockIfTrue() throws Exception {
56          final JexlScript e = JEXL.createScript("if (true) { 'hello'; }");
57          final JexlContext jc = new MapContext();
58  
59          final Object o = e.execute(jc);
60          assertEquals("hello", o, "Result is wrong");
61      }
62  
63      @Test
64      void testIfElseIfExpression() throws Exception {
65          final JexlScript e = JEXL.createScript("if (x == 1) { 10; } else if (x == 2) 20  else 30", "x");
66          Object o = e.execute(null, 1);
67          assertEquals(10, o);
68          o = e.execute(null, 2);
69          assertEquals(20, o);
70          o = e.execute(null, 4);
71          assertEquals(30, o);
72      }
73  
74      @Test
75      void testIfElseIfReturnExpression() throws Exception {
76          final JexlScript e = JEXL.createScript(
77                  "if (x == 1) return 10;  if (x == 2) return 20  else if (x == 3) return 30; else return 40;",
78                  "x");
79          Object o = e.execute(null, 1);
80          assertEquals(10, o);
81          o = e.execute(null, 2);
82          assertEquals(20, o);
83          o = e.execute(null, 3);
84          assertEquals(30, o);
85          o = e.execute(null, 4);
86          assertEquals(40, o);
87      }
88  
89      @Test
90      void testIfElseIfReturnExpression0() throws Exception {
91          final JexlScript e = JEXL.createScript(
92                  "if (x == 1) return 10; if (x == 2)  return 20; else if (x == 3) return 30  else { return 40 }",
93                  "x");
94          Object o = e.execute(null, 1);
95          assertEquals(10, o);
96          o = e.execute(null, 2);
97          assertEquals(20, o);
98          o = e.execute(null, 3);
99          assertEquals(30, o);
100         o = e.execute(null, 4);
101         assertEquals(40, o);
102     }
103 
104     /**
105      * Test the if statement evaluates arithmetic expressions correctly
106      *
107      * @throws Exception on any error
108      */
109     @Test
110     void testIfWithArithmeticExpression() throws Exception {
111         final JexlScript e = JEXL.createScript("if ((x * 2) + 1 == 5) true;");
112         final JexlContext jc = new MapContext();
113         jc.set("x", Integer.valueOf(2));
114 
115         final Object o = e.execute(jc);
116         assertEquals(Boolean.TRUE, o);
117     }
118 
119     /**
120      * Test the if statement works with assignment
121      *
122      * @throws Exception on any error
123      */
124     @Test
125     void testIfWithAssignment() throws Exception {
126         final JexlScript e = JEXL.createScript("if ((x * 2) == 5) {y = 1} else {y = 2;}");
127         final JexlContext jc = new MapContext();
128         jc.set("x", Float.valueOf(2.5f));
129 
130         e.execute(jc);
131         final Object result = jc.get("y");
132         assertEquals(Integer.valueOf(1), result, "y has the wrong value");
133     }
134 
135     /**
136      * Test the if statement evaluates decimal arithmetic expressions correctly
137      *
138      * @throws Exception on any error
139      */
140     @Test
141     void testIfWithDecimalArithmeticExpression() throws Exception {
142         final JexlScript e = JEXL.createScript("if ((x * 2) == 5) true");
143         final JexlContext jc = new MapContext();
144         jc.set("x", Float.valueOf(2.5f));
145 
146         final Object o = e.execute(jc);
147         assertEquals(Boolean.TRUE, o);
148     }
149 
150     /**
151      * Test the if statement evaluates expressions correctly
152      *
153      * @throws Exception on any error
154      */
155     @Test
156     void testIfWithSimpleExpression() throws Exception {
157         final JexlScript e = JEXL.createScript("if (x == 1) true;");
158         final JexlContext jc = new MapContext();
159         jc.set("x", Integer.valueOf(1));
160 
161         final Object o = e.execute(jc);
162         assertEquals(Boolean.TRUE, o);
163     }
164 
165     @Test
166     void testNullCoaelescing() throws Exception {
167         Object o;
168         final JexlEvalContext jc = new JexlEvalContext();
169         final JexlExpression xtrue = JEXL.createExpression("x??true");
170         o = xtrue.evaluate(jc);
171         assertEquals(true, o);
172         jc.set("x", false);
173         o = xtrue.evaluate(jc);
174         assertEquals(false, o);
175         final JexlExpression yone = JEXL.createExpression("y??1");
176         o = yone.evaluate(jc);
177         assertEquals(1, o);
178         jc.set("y", 0);
179         o = yone.evaluate(jc);
180         assertEquals(0, o);
181         debuggerCheck(JEXL);
182     }
183 
184     @Test
185     void testNullCoaelescingScript() throws Exception {
186         Object o;
187         final JexlEvalContext jc = new JexlEvalContext();
188         final JexlScript xtrue = JEXL.createScript("x??true");
189         o = xtrue.execute(jc);
190         assertEquals(true, o);
191         jc.set("x", false);
192         o = xtrue.execute(jc);
193         assertEquals(false, o);
194         final JexlScript yone = JEXL.createScript("y??1");
195         o = yone.execute(jc);
196         assertEquals(1, o);
197         jc.set("y", 0);
198         o = yone.execute(jc);
199         assertEquals(0, o);
200         debuggerCheck(JEXL);
201     }
202 
203     /**
204      * Make sure if false executes the false statement
205      *
206      * @throws Exception on any error
207      */
208     @Test
209     void testSimpleElse() throws Exception {
210         final JexlScript e = JEXL.createScript("if (false) 1 else 2;");
211         final JexlContext jc = new MapContext();
212 
213         final Object o = e.execute(jc);
214         assertEquals(Integer.valueOf(2), o);
215     }
216 
217     /**
218      * Make sure if false doesn't execute the true statement
219      *
220      * @throws Exception on any error
221      */
222     @Test
223     void testSimpleIfFalse() throws Exception {
224         final JexlScript e = JEXL.createScript("if (false) 1");
225         final JexlContext jc = new MapContext();
226 
227         final Object o = e.execute(jc);
228         assertNull(o);
229     }
230 
231     /**
232      * Make sure if true executes the true statement
233      *
234      * @throws Exception on any error
235      */
236     @Test
237     void testSimpleIfTrue() throws Exception {
238         final JexlScript e = JEXL.createScript("if (true) 1");
239         final JexlContext jc = new MapContext();
240 
241         final Object o = e.execute(jc);
242         assertEquals(Integer.valueOf(1), o);
243     }
244 
245     /**
246      * Ternary operator condition undefined or null evaluates to false
247      * independently of engine flags.
248      * @throws Exception
249      */
250     @Test
251     void testTernary() throws Exception {
252         final JexlEngine jexl = JEXL;
253 
254         final JexlEvalContext jc = new JexlEvalContext();
255         final JexlOptions options = jc.getEngineOptions();
256         final JexlExpression e = jexl.createExpression("x.y.z = foo ?'bar':'quux'");
257         Object o;
258 
259         // undefined foo
260         for (int l = 0; l < 4; ++l) {
261             options.setStrict((l & 1) == 0);
262             options.setSilent((l & 2) != 0);
263             o = e.evaluate(jc);
264             assertEquals("quux", o);
265             o = jc.get("x.y.z");
266             assertEquals("quux", o);
267         }
268 
269         jc.set("foo", null);
270 
271         for (int l = 0; l < 4; ++l) {
272             options.setStrict((l & 1) == 0);
273             options.setSilent((l & 2) != 0);
274             o = e.evaluate(jc);
275             assertEquals("quux", o);
276             o = jc.get("x.y.z");
277             assertEquals("quux", o);
278         }
279 
280         jc.set("foo", Boolean.FALSE);
281 
282         for (int l = 0; l < 4; ++l) {
283             options.setStrict((l & 1) == 0);
284             options.setSilent((l & 2) != 0);
285             o = e.evaluate(jc);
286             assertEquals("quux", o);
287             o = jc.get("x.y.z");
288             assertEquals("quux", o);
289         }
290 
291         jc.set("foo", Boolean.TRUE);
292 
293         for (int l = 0; l < 4; ++l) {
294             options.setStrict((l & 1) == 0);
295             options.setSilent((l & 2) != 0);
296             o = e.evaluate(jc);
297             assertEquals("bar", o);
298             o = jc.get("x.y.z");
299             assertEquals("bar", o);
300         }
301 
302         debuggerCheck(jexl);
303     }
304 
305     @Test
306     void testTernaryFail() throws Exception {
307         final JexlEvalContext jc = new JexlEvalContext();
308         final JexlOptions options = jc.getEngineOptions();
309         final JexlExpression e = JEXL.createExpression("false ? bar : quux");
310         options.setStrict(true);
311         options.setSilent(false);
312         final JexlException xjexl = assertThrows(JexlException.class, () -> e.evaluate(jc));
313         assertTrue(xjexl.toString().contains("quux"));
314     }
315 
316     /**
317      * Ternary operator condition undefined or null evaluates to false
318      * independently of engine flags; same for null coalescing operator.
319      * @throws Exception
320      */
321     @Test
322     void testTernaryShorthand() throws Exception {
323         final JexlEvalContext jc = new JexlEvalContext();
324         final JexlOptions options = jc.getEngineOptions();
325         final JexlExpression e = JEXL.createExpression("x.y.z = foo?:'quux'");
326         final JexlExpression f = JEXL.createExpression("foo??'quux'");
327         Object o;
328 
329         // undefined foo
330         for (int l = 0; l < 4; ++l) {
331             options.setStrict((l & 1) == 0);
332             options.setSilent((l & 2) != 0);
333             o = e.evaluate(jc);
334             assertEquals("quux", o);
335             o = jc.get("x.y.z");
336             assertEquals("quux", o);
337             o = f.evaluate(jc);
338             assertEquals("quux", o);
339         }
340 
341         jc.set("foo", null);
342 
343         for (int l = 0; l < 4; ++l) {
344             options.setStrict((l & 1) == 0);
345             options.setSilent((l & 2) != 0);
346             o = e.evaluate(jc);
347             assertEquals("quux", o);
348             o = jc.get("x.y.z");
349             assertEquals("quux", o);
350             o = f.evaluate(jc);
351             assertEquals("quux", o);
352         }
353 
354         jc.set("foo", Boolean.FALSE);
355 
356         for (int l = 0; l < 4; ++l) {
357             options.setStrict((l & 1) == 0);
358             options.setSilent((l & 2) != 0);
359             o = e.evaluate(jc);
360             assertEquals("quux", o);
361             o = jc.get("x.y.z");
362             assertEquals("quux", o);
363             o = f.evaluate(jc);
364             assertEquals(false, o);
365         }
366 
367         jc.set("foo", Double.NaN);
368 
369         for (int l = 0; l < 4; ++l) {
370             options.setStrict((l & 1) == 0);
371             options.setSilent((l & 2) != 0);
372             o = e.evaluate(jc);
373             assertEquals("quux", o);
374             o = jc.get("x.y.z");
375             assertEquals("quux", o);
376             o = f.evaluate(jc);
377             assertTrue(Double.isNaN((Double) o), "Should be NaN");
378         }
379 
380         jc.set("foo", "");
381 
382         for (int l = 0; l < 4; ++l) {
383             options.setStrict((l & 1) == 0);
384             options.setSilent((l & 2) != 0);
385             o = e.evaluate(jc);
386             assertEquals("quux", o);
387             o = jc.get("x.y.z");
388             assertEquals("quux", o);
389             o = f.evaluate(jc);
390             assertEquals("", o, "Should be empty string");
391         }
392 
393         jc.set("foo", "false");
394 
395         for (int l = 0; l < 4; ++l) {
396             options.setStrict((l & 1) == 0);
397             options.setSilent((l & 2) != 0);
398             o = e.evaluate(jc);
399             assertEquals("quux", o);
400             o = jc.get("x.y.z");
401             assertEquals("quux", o);
402             o = f.evaluate(jc);
403             assertEquals("false", o);
404         }
405 
406         jc.set("foo", 0d);
407 
408         for (int l = 0; l < 4; ++l) {
409             options.setStrict((l & 1) == 0);
410             options.setSilent((l & 2) != 0);
411             o = e.evaluate(jc);
412             assertEquals("quux", o);
413             o = jc.get("x.y.z");
414             assertEquals("quux", o);
415             o = f.evaluate(jc);
416             assertEquals(0.d, o);
417         }
418 
419         jc.set("foo", 0);
420 
421         for (int l = 0; l < 4; ++l) {
422             options.setStrict((l & 1) == 0);
423             options.setSilent((l & 2) != 0);
424             o = e.evaluate(jc);
425             assertEquals("quux", o);
426             o = jc.get("x.y.z");
427             assertEquals("quux", o);
428             o = f.evaluate(jc);
429             assertEquals(0, o);
430         }
431 
432         jc.set("foo", "bar");
433 
434         for (int l = 0; l < 4; ++l) {
435             options.setStrict((l & 1) == 0);
436             options.setSilent((l & 2) != 0);
437             o = e.evaluate(jc);
438             assertEquals("bar", o);
439             o = jc.get("x.y.z");
440             assertEquals("bar", o);
441         }
442 
443         debuggerCheck(JEXL);
444     }
445 }