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.assertArrayEquals;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.util.Arrays;
27  import java.util.List;
28  import java.util.Set;
29  import java.util.concurrent.Callable;
30  
31  import org.apache.commons.jexl3.internal.Closure;
32  import org.apache.commons.jexl3.internal.Script;
33  import org.junit.jupiter.api.Test;
34  import org.junit.jupiter.params.ParameterizedTest;
35  import org.junit.jupiter.params.provider.CsvSource;
36  
37  /**
38   * Tests function/lambda/closure features.
39   */
40  @SuppressWarnings({"AssertEqualsBetweenInconvertibleTypes"})
41  class LambdaTest extends JexlTestCase {
42  
43      public LambdaTest() {
44          super("LambdaTest");
45      }
46  
47      @Test void test270() {
48          final JexlEngine jexl = createEngine();
49          final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
50          final String text = base.toString();
51          JexlScript script = base.curry(5, 15);
52          assertEquals(text, script.toString());
53  
54          final JexlContext ctxt = new JexlEvalContext();
55          ctxt.set("s", base);
56          script = jexl.createScript("return s");
57          Object result = script.execute(ctxt);
58          assertEquals(text, result.toString());
59  
60          script = jexl.createScript("return s.curry(1)");
61          result = script.execute(ctxt);
62          assertEquals(text, result.toString());
63      }
64  
65      @Test void test271a() {
66          final JexlEngine jexl = createEngine();
67          final JexlScript base = jexl.createScript("var base = 1; var x = (a)->{ var y = (b) -> {base + b}; return base + y(a)}; x(40)");
68          final Object result = base.execute(null);
69          assertEquals(42, result);
70      }
71  
72      @Test void test271b() {
73          final JexlEngine jexl = createEngine();
74          final JexlScript base = jexl.createScript("var base = 2; var sum = (x, y, z)->{ base + x + y + z }; var y = sum.curry(1); y(2,3)");
75          final Object result = base.execute(null);
76          assertEquals(8, result);
77      }
78  
79      @Test void test271c() {
80          final JexlEngine jexl = createEngine();
81          final JexlScript base = jexl.createScript("(x, y, z)->{ 2 + x + y + z };");
82          final JexlScript y = base.curry(1);
83          final Object result = y.execute(null, 2, 3);
84          assertEquals(8, result);
85      }
86  
87      @Test void test271d() {
88          final JexlEngine jexl = createEngine();
89          final JexlScript base = jexl.createScript("var base = 2; (x, y, z)->base + x + y + z;");
90          final JexlScript y = ((JexlScript) base.execute(null)).curry(1);
91          final Object result = y.execute(null, 2, 3);
92          assertEquals(8, result);
93      }
94  
95      // Redefining a captured var is not resolved correctly in left-hand side;
96      // declare the var in local frame, resolved in local frame instead of parent.
97      @Test void test271e() {
98          final JexlEngine jexl = createEngine();
99          final JexlScript base = jexl.createScript("var base = 1000; var f = (x, y)->{ var base = x + y + (base?:-1000); base; }; f(100, 20)");
100         final Object result = base.execute(null);
101         assertEquals(1120, result);
102     }
103 
104     @Test void test405a() {
105         final JexlEngine jexl = new JexlBuilder()
106             .cache(4).strict(true).safe(false)
107             .create();
108         final String libSrc = "var theFunction = argFn -> { var fn = argFn; fn() }; { 'theFunction' : theFunction }";
109         final String src1 = "var v0 = 42; var v1 = -42; lib.theFunction(()->{ v1 + v0 }) ";
110         final JexlScript libMap = jexl.createScript(libSrc);
111         final Object theLib = libMap.execute(null);
112         final JexlScript f1 = jexl.createScript(src1, "lib");
113         final Object result = f1.execute(null, theLib);
114         assertEquals(0, result);
115     }
116 
117     @Test void test405b() {
118         final JexlEngine jexl = new JexlBuilder()
119             .cache(4).strict(true).safe(false)
120             .create();
121         final String libSrc = "function theFunction(argFn) { var fn = argFn; fn() }; { 'theFunction' : theFunction }";
122         final String src1 = "var v0 = 42; var v1 = -42; lib.theFunction(()->{ v1 + v0 }) ";
123         final JexlScript libMap = jexl.createScript(libSrc);
124         final Object theLib = libMap.execute(null);
125         final JexlScript f1 = jexl.createScript(src1, "lib");
126         final Object result = f1.execute(null, theLib);
127         assertEquals(0, result);
128     }
129 
130     @Test void testCompareLambdaRecurse() throws Exception {
131         final JexlEngine jexl = createEngine();
132         final String factSrc = "function fact(x) { x < 2? 1 : x * fact(x - 1) }";
133         final JexlScript fact0 = jexl.createScript(factSrc);
134         final JexlScript fact1 = jexl.createScript(fact0.toString());
135         assertEquals(fact0, fact1);
136         final Closure r0 = (Closure) fact0.execute(null);
137         final Closure r1 = (Closure) fact1.execute(null);
138         assertEquals(720, r0.execute(null, 6));
139         assertEquals(720, r1.execute(null, 6));
140         assertEquals(r0, r1);
141         assertEquals(r1, r0);
142         // ensure we did not break anything through equals
143         assertEquals(720, r0.execute(null, 6));
144         assertEquals(720, r1.execute(null, 6));
145     }
146 
147     @Test void testCurry1() {
148         final JexlEngine jexl = createEngine();
149         JexlScript script;
150         Object result;
151         String[] parms;
152 
153         final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
154         parms = base.getUnboundParameters();
155         assertEquals(3, parms.length);
156         script = base.curry(5);
157         parms = script.getUnboundParameters();
158         assertEquals(2, parms.length);
159         script = script.curry(15);
160         parms = script.getUnboundParameters();
161         assertEquals(1, parms.length);
162         script = script.curry(22);
163         parms = script.getUnboundParameters();
164         assertEquals(0, parms.length);
165         result = script.execute(null);
166         assertEquals(42, result);
167     }
168 
169     @Test void testCurry2() {
170         final JexlEngine jexl = createEngine();
171         JexlScript script;
172         Object result;
173         String[] parms;
174 
175         final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
176         script = base.curry(5, 15);
177         parms = script.getUnboundParameters();
178         assertEquals(1, parms.length);
179         script = script.curry(22);
180         result = script.execute(null);
181         assertEquals(42, result);
182     }
183 
184     @Test void testCurry3() {
185         final JexlEngine jexl = createEngine();
186         JexlScript script;
187         Object result;
188 
189         final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
190         script = base.curry(5, 15);
191         result = script.execute(null, 22);
192         assertEquals(42, result);
193     }
194 
195     @Test void testCurry4() {
196         final JexlEngine jexl = createEngine();
197         JexlScript script;
198         Object result;
199 
200         final JexlScript base = jexl.createScript("(x, y, z)->{ x + y + z }");
201         script = base.curry(5);
202         result = script.execute(null, 15, 22);
203         assertEquals(42, result);
204     }
205 
206     @Test void testCurry5() {
207         final JexlEngine jexl = createEngine();
208         JexlScript script;
209         Object result;
210 
211         final JexlScript base = jexl.createScript("var t = x + y + z; return t", "x", "y", "z");
212         script = base.curry(5);
213         result = script.execute(null, 15, 22);
214         assertEquals(42, result);
215     }
216 
217     @ParameterizedTest
218     @CsvSource({
219             "'if (false) function foo(x) { x + x }; var foo = 1', 'function'",
220             "'if (false) let foo = (x) -> { x + x }; var foo = 1', 'let'",
221             "'function foo(x) { x + x }; var foo = 42', 'foo'"
222     })
223     void testFailParseFunc(final String src, final String expectedKeyword) {
224         final JexlEngine jexl = createEngine();
225         final JexlException.Parsing xparse = assertThrows(JexlException.Parsing.class, () -> jexl.createScript(src));
226         assertTrue(xparse.getMessage().contains(expectedKeyword));
227     }
228 
229     @Test void testFatFact0() {
230         final JexlFeatures features = new JexlFeatures();
231         features.fatArrow(true);
232         final String src = "function (a) { const fact = x =>{ x <= 1? 1 : x * fact(x - 1) }; fact(a) }";
233         final JexlEngine jexl = createEngine(features);
234         final JexlScript script = jexl.createScript(src);
235         final Object result = script.execute(null, 6);
236         assertEquals(720, result);
237     }
238 
239     @Test void testFatFact1() {
240         final String src = "function (a) { const fact = (x)=> x <= 1? 1 : x * fact(x - 1) ; fact(a) }";
241         final JexlFeatures features = new JexlFeatures();
242         features.fatArrow(true);
243         final JexlEngine jexl = createEngine(features);
244         final JexlScript script = jexl.createScript(src);
245         final Object result = script.execute(null, 6);
246         assertEquals(720, result);
247         features.fatArrow(false);
248         final JexlEngine jexl1 = createEngine(features);
249         final JexlException.Feature xfeature = assertThrows(JexlException.Feature.class, () -> jexl1.createScript(src));
250         assertTrue(xfeature.getMessage().contains("fat-arrow"));
251     }
252 
253     @Test void testHoistLambda() {
254         final JexlEngine jexl = createEngine();
255         final JexlEvalContext ctx = new JexlEvalContext();
256         ctx.getEngineOptions().setLexical(false);
257         JexlScript s42;
258         Object result;
259         JexlScript s15;
260         String[] localv;
261         Set<List<String>> hvars;
262         String strs;
263 
264         // hosted variables are NOT local variables
265         strs = "(x)->{ (y)->{ x + y } }";
266         s42 = jexl.createScript(strs);
267         result = s42.execute(ctx, 15);
268         assertInstanceOf(JexlScript.class, result);
269         s15 = (JexlScript) result;
270         localv = s15.getLocalVariables();
271         assertEquals(0, localv.length);
272         hvars = s15.getVariables();
273         assertEquals(1, hvars.size());
274 
275         // declaring a local that overrides captured
276         // in 3.1, such a local was considered local
277         // per 3.2, this local is considered captured
278         strs = "(x)->{ (y)->{ var z = 169; var x; x + y } }";
279         s42 = jexl.createScript(strs);
280         result = s42.execute(ctx, 15);
281         assertInstanceOf(JexlScript.class, result);
282         s15 = (JexlScript) result;
283         localv = s15.getLocalVariables();
284         assertNotNull(localv);
285         assertEquals(1, localv.length);
286         hvars = s15.getVariables();
287         assertEquals(1, hvars.size());
288         // evidence this is not (strictly) a local since it inherited a captured value
289         result = s15.execute(ctx, 27);
290         assertEquals(42, result);
291     }
292 
293     @Test void testIdentity() {
294         final JexlEngine jexl = createEngine();
295         JexlScript script;
296         Object result;
297 
298         script = jexl.createScript("(x)->{ x }");
299         assertArrayEquals(new String[]{"x"}, script.getParameters());
300         result = script.execute(null, 42);
301         assertEquals(42, result);
302     }
303 
304     @Test void testLambda() {
305         final JexlEngine jexl = createEngine();
306         String strs = "var s = function(x) { x + x }; s(21)";
307         JexlScript s42 = jexl.createScript(strs);
308         Object result = s42.execute(null);
309         assertEquals(42, result);
310         strs = "var s = function(x, y) { x + y }; s(15, 27)";
311         s42 = jexl.createScript(strs);
312         result = s42.execute(null);
313         assertEquals(42, result);
314     }
315 
316     @Test void testLambdaClosure()  {
317         final JexlEngine jexl = createEngine();
318         String strs = "var t = 20; var s = function(x, y) { x + y + t}; s(15, 7)";
319         JexlScript s42 = jexl.createScript(strs);
320         Object result = s42.execute(null);
321         assertEquals(42, result);
322         strs = "var t = 19; var s = function(x, y) { var t = 20; x + y + t}; s(15, 7)";
323         s42 = jexl.createScript(strs);
324         result = s42.execute(null);
325         assertEquals(42, result);
326         strs = "var t = 20; var s = function(x, y) {x + y + t}; t = 54; s(15, 7)";
327         s42 = jexl.createScript(strs);
328         result = s42.execute(null);
329         assertEquals(42, result);
330         strs = "var t = 19; var s = function(x, y) { var t = 20; x + y + t}; t = 54; s(15, 7)";
331         s42 = jexl.createScript(strs);
332         result = s42.execute(null);
333         assertEquals(42, result);
334     }
335 
336     @Test void testLambdaExpr0() {
337         final String src = "(x, y) -> x + y";
338         final JexlEngine jexl = createEngine();
339         final JexlScript script = jexl.createScript(src);
340         final Object result = script.execute(null, 11, 31);
341         assertEquals(42, result);
342     }
343 
344     @Test void testLambdaExpr1() {
345         final String src = "x -> x + x";
346         final JexlEngine jexl = createEngine();
347         final JexlScript script = jexl.createScript(src);
348         final Object result = script.execute(null, 21);
349         assertEquals(42, result);
350     }
351 
352     @Test void testLambdaExpr10() {
353         final String src = "(a)->{ var x = x -> x + x; x(a) }";
354         final JexlEngine jexl = createEngine();
355         final JexlScript script = jexl.createScript(src);
356         final Object result = script.execute(null, 21);
357         assertEquals(42, result);
358     }
359 
360     @Test void testLambdaExpr2() {
361         final String src = "x -> { { x + x } }";
362         final JexlEngine jexl = createEngine();
363         final JexlScript script = jexl.createScript(src);
364         final Object result = script.execute(null, 21);
365         assertInstanceOf(Set.class, result);
366         final Set<?> set = (Set<?>) result;
367         assertEquals(1, set.size());
368         assertTrue(set.contains(42));
369     }
370 
371     @Test void testLambdaExpr3() {
372         final String src = "x -> ( { x + x } )";
373         final JexlEngine jexl = createEngine();
374         final JexlScript script = jexl.createScript(src);
375         final Object result = script.execute(null, 21);
376         assertInstanceOf(Set.class, result);
377         final Set<?> set = (Set<?>) result;
378         assertEquals(1, set.size());
379         assertTrue(set.contains(42));
380     }
381 
382     @Test void testLambdaLambda() {
383         final JexlEngine jexl = createEngine();
384         String strs = "var t = 19; ( (x, y)->{ var t = 20; x + y + t} )(15, 7);";
385         JexlScript s42 = jexl.createScript(strs);
386         Object result = s42.execute(null);
387         assertEquals(42, result);
388 
389         strs = "( (x, y)->{ ( (xx, yy)->{xx + yy } )(x, y) } )(15, 27)";
390         s42 = jexl.createScript(strs);
391         result = s42.execute(null);
392         assertEquals(42, result);
393 
394         strs = "var t = 19; var s = (x, y)->{ var t = 20; x + y + t}; t = 54; s(15, 7)";
395         s42 = jexl.createScript(strs);
396         result = s42.execute(null);
397         assertEquals(42, result);
398     }
399 
400     @Test void testNamedFunc() {
401         final String src = "(let a)->{ function fact(const x) { x <= 1? 1 : x * fact(x - 1); } fact(a); }";
402         final JexlEngine jexl = createEngine();
403         final JexlScript script = jexl.createScript(src);
404         final Object result = script.execute(null, 6);
405         assertEquals(720, result);
406         final String parsed = simpleWhitespace(script.getParsedText());
407         assertEquals(simpleWhitespace(src), parsed);
408     }
409 
410 
411     @Test void testNestLambada() throws Exception {
412         final JexlEngine jexl = createEngine();
413         final String strs = "(x)->{ (y)->{ x + y } }";
414         final JexlScript s42 = jexl.createScript(strs);
415         final JexlScript s42b = jexl.createScript(s42.toString());
416         assertEquals(s42.hashCode(), s42b.hashCode());
417         assertEquals(s42, s42b);
418         Object result = s42.execute(null, 15);
419         assertInstanceOf(JexlScript.class, result);
420         final Object resultb = s42.execute(null, 15);
421         assertEquals(result.hashCode(), resultb.hashCode());
422         assertEquals(result, resultb);
423         assertEquals(result, jexl.createScript(resultb.toString(), "x").execute(null, 15));
424         final JexlScript s15 = (JexlScript) result;
425         final Callable<Object> s15b = s15.callable(null, 27);
426         result = s15.execute(null, 27);
427         assertEquals(42, result);
428         result = s15b.call();
429         assertEquals(42, result);
430     }
431 
432     @Test void testNestLambda() {
433         final JexlEngine jexl = createEngine();
434         final String strs = "( (x)->{ (y)->{ x + y } })(15)(27)";
435         final JexlScript s42 = jexl.createScript(strs);
436         final Object result = s42.execute(null);
437         assertEquals(42, result);
438     }
439 
440     @Test void testRecurse() {
441         final JexlEngine jexl = createEngine();
442         final JexlContext jc = new MapContext();
443         final JexlScript script = jexl.createScript("var fact = (x)->{ if (x <= 1) 1; else x * fact(x - 1) }; fact(5)");
444         final int result = (Integer) script.execute(jc);
445         assertEquals(120, result);
446     }
447 
448     @Test void testRecurse1() {
449         final JexlEngine jexl = createEngine();
450         final JexlContext jc = new MapContext();
451         final String src = "var fact = (x)-> x <= 1? 1 : x * fact(x - 1);\nfact(5);\n";
452         final JexlScript script = jexl.createScript(src);
453         final int result = (Integer) script.execute(jc);
454         assertEquals(120, result);
455         final String parsed = script.getParsedText();
456         assertEquals(src, parsed);
457     }
458 
459     @Test void testRecurse2() {
460         final JexlEngine jexl = createEngine();
461         final JexlContext jc = new MapContext();
462         // adding some captured vars to get it confused
463         final JexlScript script = jexl.createScript(
464                 "var y = 1; var z = 1; "
465                 +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) }; fact(6)");
466         final int result = (Integer) script.execute(jc);
467         assertEquals(720, result);
468     }
469 
470     @Test void testRecurse2b() {
471         final JexlEngine jexl = createEngine();
472         final JexlContext jc = new MapContext();
473         // adding some captured vars to get it confused
474         final JexlScript fact = jexl.createScript(
475                 "var y = 1; var z = 1; "
476                         +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) };" +
477                         "fact");
478         final Script func = (Script) fact.execute(jc);
479         final String[] captured = func.getCapturedVariables();
480         assertEquals(3, captured.length);
481         assertTrue(Arrays.asList(captured).containsAll(Arrays.asList("z", "y", "fact")));
482         final int result = (Integer) func.execute(jc, 6);
483         assertEquals(720, result);
484     }
485 
486     @Test void testRecurse3() {
487         final JexlEngine jexl = createEngine();
488         final JexlContext jc = new MapContext();
489         // adding some captured vars to get it confused
490         final JexlScript script = jexl.createScript(
491                 "var y = 1; var z = 1;var foo = (x)->{y + z}; "
492                 +"var fact = (x)->{ if (x <= y) z; else x * fact(x - 1) }; fact(6)");
493         final int result = (Integer) script.execute(jc);
494         assertEquals(720, result);
495     }
496 
497     @Test void testScriptArguments() {
498         final JexlEngine jexl = createEngine();
499         final JexlScript s = jexl.createScript(" x + x ", "x");
500         final JexlScript s42 = jexl.createScript("s(21)", "s");
501         final Object result = s42.execute(null, s);
502         assertEquals(42, result);
503     }
504 
505     @Test void testScriptContext() {
506         final JexlEngine jexl = createEngine();
507         final JexlScript s = jexl.createScript("function(x) { x + x }");
508         final String fsstr = s.getParsedText(0);
509         assertEquals("(x)->{ x + x; }", fsstr);
510         assertEquals(42, s.execute(null, 21));
511         JexlScript s42 = jexl.createScript("s(21)");
512         final JexlContext ctxt = new JexlEvalContext();
513         ctxt.set("s", s);
514         Object result = s42.execute(ctxt);
515         assertEquals(42, result);
516         result = s42.execute(ctxt);
517         assertEquals(42, result);
518         s42 = jexl.createScript("x-> { x + x }");
519         result = s42.execute(ctxt, 21);
520         assertEquals(42, result);
521     }
522 
523     /**
524      * see JEXL-426
525      */
526     @Test void testRefCapture1() {
527         final String src = "let x = 10;\n" +
528                 "let foo = () -> {\n" +
529                 "x += 2;\n" +
530                 "}\n" +
531                 "x = 40;\n" +
532                 "foo();\n" +
533                 "x";
534         final JexlEngine jexl = new JexlBuilder()
535                 .features(new JexlFeatures().referenceCapture(true))
536                 .create();
537         JexlScript script;
538         Object result;
539         script = jexl.createScript(src);
540         result = script.execute(null);
541         assertEquals(42, result);
542     }
543 
544     @Test void testRefCapture2() {
545         final String src = "let x = 10; let f = () -> { x + 2 }; x = 40; f()";
546         final JexlEngine jexl = new JexlBuilder()
547                 .features(new JexlFeatures().constCapture(true).referenceCapture(true))
548                 .create();
549         JexlScript script;
550         Object result;
551         script = jexl.createScript(src);
552         result = script.execute(null);
553         assertEquals(42, result);
554     }
555 
556     @Test void testRefCapture3() {
557         final String src = "let x = 10; let f = () -> { x + 2 }; x = 40; f";
558         final JexlEngine jexl = new JexlBuilder()
559                 .features(new JexlFeatures().constCapture(true).referenceCapture(true))
560                 .create();
561         JexlScript script;
562         Object result;
563         script = jexl.createScript(src);
564         result = script.execute(null);
565         assertInstanceOf(JexlScript.class, result);
566         script = jexl.createScript("f()", "f");
567         result = script.execute(null, result);
568         assertEquals(42, result);
569     }
570 
571     @Test void testRefCapture4() {
572         final String src = "let x = 10; let f = () -> { let x = 142; x }; x = 40; f";
573         final JexlEngine jexl = new JexlBuilder()
574                 .features(new JexlFeatures().referenceCapture(true))
575                 .create();
576         JexlScript script;
577         Object result;
578         script = jexl.createScript(src);
579         result = script.execute(null);
580         assertInstanceOf(JexlScript.class, result);
581         script = jexl.createScript("f()", "f");
582         result = script.execute(null, result);
583         assertEquals(142, result);
584     }
585 
586     @Test void testRefCapture5() {
587         final JexlFeatures f426 = new JexlFeatures().referenceCapture(true);
588         final JexlEngine jexl = new JexlBuilder().features(f426).create();
589         final String src = "let z = 32; let x = 40; function foo() { x += 2; }; function bar() { let x = -169; foo(); x;}; bar();";
590         final JexlScript script = jexl.createScript(src);
591         assertNotNull(script);
592         final Object result = script.execute(null);
593         assertEquals(-169, result);
594     }
595 
596     @Test void testRefCapture6() {
597         final JexlFeatures f426 = new JexlFeatures().referenceCapture(true);
598         final JexlEngine jexl = new JexlBuilder().features(f426).create();
599         final String src = "let x = 40; function foo() { x += 2; }; function bar() { x = -169; () -> { foo(); }}; bar();";
600         JexlScript script = jexl.createScript(src);
601         assertNotNull(script);
602         Object result = script.execute(null);
603         assertInstanceOf(JexlScript.class, result);
604         script = jexl.createScript("f()", "f");
605         result = script.execute(null, result);
606         assertEquals(-167, result);
607     }
608 }