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    *      http://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  
18  package org.apache.commons.jexl3;
19  
20  import java.lang.reflect.Constructor;
21  import java.lang.reflect.Method;
22  import java.util.Arrays;
23  
24  import org.apache.commons.jexl3.internal.Debugger;
25  import org.apache.commons.jexl3.internal.OptionsContext;
26  import org.apache.commons.jexl3.internal.Util;
27  import org.apache.commons.jexl3.internal.introspection.Uberspect;
28  import org.apache.commons.jexl3.introspection.JexlPermissions;
29  import org.junit.After;
30  import org.junit.Assert;
31  
32  /**
33   * Implements runTest methods to dynamically instantiate and invoke a test,
34   * wrapping the call with setUp(), tearDown() calls.
35   * Eases the implementation of main methods to debug.
36   */
37  public class JexlTestCase {
38      // The default options: all tests where engine lexicality is
39      // important can be identified by the builder  calling lexical(...).
40      static {
41          JexlOptions.setDefaultFlags("-safe", "+lexical");
42      }
43      /** No parameters signature for test run. */
44      private static final Class<?>[] NO_PARMS = {};
45      /** String parameter signature for test run. */
46      private static final Class<?>[] STRING_PARM = {String.class};
47  
48      /** A default JEXL engine instance. */
49      protected final JexlEngine JEXL;
50  
51      public JexlTestCase(final String name) {
52          this(name, new JexlBuilder().imports(Arrays.asList("java.lang","java.math")).permissions(null).cache(128).create());
53      }
54  
55      protected JexlTestCase(final String name, final JexlEngine jexl) {
56          //super(name);
57          JEXL = jexl;
58      }
59  
60      public void setUp() throws Exception {
61          // nothing to do
62      }
63  
64      @After
65      public void tearDown() throws Exception {
66          debuggerCheck(JEXL);
67      }
68  
69      static JexlEngine createEngine() {
70          return new JexlBuilder().create();
71      }
72      static JexlEngine createEngine(JexlFeatures features) {
73          return new JexlBuilder().features(features).create();
74      }
75  
76      // define mode pro50
77      static final JexlOptions MODE_PRO50 = new JexlOptions();
78      static {
79          MODE_PRO50.setFlags( "+strict +cancellable +lexical +lexicalShade -safe".split(" "));
80      }
81  
82      public static class PragmaticContext extends OptionsContext implements JexlContext.PragmaProcessor, JexlContext.OptionsHandle {
83          private final JexlOptions options;
84  
85          public PragmaticContext() {
86              this(new JexlOptions());
87          }
88  
89          public PragmaticContext(final JexlOptions o) {
90              super();
91              this.options = o;
92          }
93  
94          @Override
95          public void processPragma(String key, Object value) {
96              processPragma(null, key, value);
97          }
98  
99          @Override
100         public void processPragma(JexlOptions opts, final String key, final Object value) {
101             if ("script.mode".equals(key) && "pro50".equals(value)) {
102                 opts.set(MODE_PRO50);
103             }
104         }
105 
106         @Override
107         public JexlOptions getEngineOptions() {
108             return options;
109         }
110     }
111 
112     /**
113      * Compare strings ignoring white space differences.
114      * <p>This replaces any sequence of whitespaces (ie \\s) by one space (ie ASCII 32) in both
115      * arguments then compares them.</p>
116      * @param lhs left hand side
117      * @param rhs right hand side
118      * @return true if strings are equal besides whitespace
119      */
120     public static boolean equalsIgnoreWhiteSpace(String lhs, String rhs) {
121         String lhsw = lhs.trim().replaceAll("\\s+", "");
122         String rhsw = rhs.trim().replaceAll("\\s+", "");
123         return lhsw.equals(rhsw);
124     }
125 
126     public String simpleWhitespace(String arg) {
127         return arg.trim().replaceAll("\\s+", " ");
128     }
129 
130     public static String toString(JexlScript script) {
131         Debugger d = new Debugger().lineFeed("").indentation(0);
132         d.debug(script);
133         return  d.toString();
134     }
135 
136     /**
137      * A very secure singleton.
138      */
139     public static final JexlPermissions SECURE = JexlPermissions.RESTRICTED;
140 
141     public static JexlEngine createEngine(final boolean lenient) {
142         return createEngine(lenient, SECURE);
143     }
144 
145     public static JexlEngine createEngine(final boolean lenient, JexlPermissions permissions) {
146         return new JexlBuilder()
147                 .uberspect(new Uberspect(null, null, permissions))
148                 .arithmetic(new JexlArithmetic(!lenient)).cache(128).create();
149     }
150 
151     /**
152      * Will force testing the debugger for each derived test class by
153      * recreating each expression from the JexlNode in the JexlEngine cache &
154      * testing them for equality with the origin.
155      * @throws Exception
156      */
157     public static void debuggerCheck(final JexlEngine ijexl) throws Exception {
158          Util.debuggerCheck(ijexl);
159     }
160 
161     /**
162      * Dynamically runs a test method.
163      * @param name the test method to run
164      * @throws Exception if anything goes wrong
165      */
166     public void runTest(final String name) throws Exception {
167         if ("runTest".equals(name)) {
168             return;
169         }
170         Method method = null;
171         try {
172             method = this.getClass().getDeclaredMethod(name, NO_PARMS);
173         }
174         catch(final Exception xany) {
175             Assert.fail("no such test: " + name);
176             return;
177         }
178         try {
179             this.setUp();
180             method.invoke(this);
181         } finally {
182             this.tearDown();
183         }
184     }
185 
186     /**
187      * Instantiate and runs a test method; useful for debugging purpose.
188      * For instance:
189      * <code>
190      * public static void main(String[] args) throws Exception {
191      *   runTest("BitwiseOperatorTest","testAndVariableNumberCoercion");
192      * }
193      * </code>
194      * @param tname the test class name
195      * @param mname the test class method
196      * @throws Exception
197      */
198     public static void runTest(final String tname, final String mname) throws Exception {
199         final String testClassName = "org.apache.commons.jexl3."+tname;
200         Class<JexlTestCase> clazz = null;
201         JexlTestCase test = null;
202         // find the class
203         try {
204             clazz = (Class<JexlTestCase>) Class.forName(testClassName);
205         }
206         catch(final ClassNotFoundException xclass) {
207             Assert.fail("no such class: " + testClassName);
208             return;
209         }
210         // find ctor & instantiate
211         Constructor<JexlTestCase> ctor = null;
212         try {
213             ctor = clazz.getConstructor(STRING_PARM);
214             test = ctor.newInstance("debug");
215         }
216         catch(final NoSuchMethodException xctor) {
217             // instantiate default class ctor
218             try {
219                 test = clazz.newInstance();
220             }
221             catch(final Exception xany) {
222                 Assert.fail("cant instantiate test: " + xany);
223                 return;
224             }
225         }
226         catch(final Exception xany) {
227             Assert.fail("cant instantiate test: " + xany);
228             return;
229         }
230         // Run the test
231         test.runTest(mname);
232     }
233 
234     /**
235      * Runs a test.
236      * @param args where args[0] is the test class name and args[1] the test class method
237      * @throws Exception
238      */
239     public static void main(final String[] args) throws Exception {
240         runTest(args[0], args[1]);
241     }
242 }