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  
18  package org.apache.commons.jexl3;
19  
20  import java.util.Arrays;
21  
22  import org.apache.commons.jexl3.internal.Debugger;
23  import org.apache.commons.jexl3.internal.OptionsContext;
24  import org.apache.commons.jexl3.internal.Util;
25  import org.apache.commons.jexl3.internal.introspection.Uberspect;
26  import org.apache.commons.jexl3.introspection.JexlPermissions;
27  import org.junit.jupiter.api.AfterEach;
28  import org.junit.jupiter.api.BeforeAll;
29  
30  
31  /**
32   * Implements runTest methods to dynamically instantiate and invoke a test,
33   * wrapping the call with setUp(), tearDown() calls.
34   * Eases the implementation of main methods to debug.
35   */
36  public class JexlTestCase {
37    /**
38     * The restricted permissions that are used by default in JEXL tests.
39     * <p>Need to add jexl3 package but removes most internals</p>
40     */
41    public static final JexlPermissions TEST_PERMS = JexlPermissions.RESTRICTED.compose(
42  "org.apache.commons.jexl3.*",
43        "org.apache.commons.jexl3 +{ -JexlBuilder{} }",
44        "org.apache.commons.jexl3.introspection -{ JexlPermissions{} JexlPermissions$ClassPermissions{} }",
45        "org.apache.commons.jexl3.internal -{ Engine{} Engine32{} TemplateEngine{} }",
46        "org.apache.commons.jexl3.internal.introspection -{ Uberspect{} Introspector{} }",
47        "java.net -{ URI { toURL() } }");
48  
49    // define mode pro50
50    static final JexlOptions MODE_PRO50 = new JexlOptions();
51  
52    // The default options: all tests where engine lexicality is
53    // important can be identified by the builder  calling lexical(...).
54    static {
55      JexlBuilder.setDefaultPermissions(TEST_PERMS);
56      // Keep the full permissive feature set so all test subclasses can use new(), loops, side-effects.
57      // The 3.7 hardened default is the correct library default; tests bring their own configuration.
58      JexlBuilder.setDefaultFeatures(JexlFeatures.createDefault());
59      JexlOptions.setDefaultFlags("-safe", "+lexical");
60      MODE_PRO50.setFlags("+strict +cancellable +lexical +lexicalShade -safe".split(" "));
61    }
62  
63    /**
64     * A default JEXL engine instance.
65     */
66    protected final JexlEngine JEXL;
67  
68    public JexlTestCase(final String name) {
69      this(name, new JexlBuilder().imports(Arrays.asList("java.lang", "java.math")).permissions(null).cache(128).create());
70    }
71  
72    protected JexlTestCase(final String name, final JexlEngine jexl) {
73      //super(name);
74      JEXL = jexl;
75    }
76  
77    static JexlEngine createEngine() {
78      return new JexlBuilder().create();
79    }
80  
81    public static JexlEngine createEngine(final boolean lenient) {
82      return createEngine(lenient, TEST_PERMS);
83    }
84  
85    public static JexlEngine createEngine(final boolean lenient, final JexlPermissions permissions) {
86      return new JexlBuilder().uberspect(new Uberspect(null, null, permissions)).arithmetic(new JexlArithmetic(!lenient)).cache(128).create();
87    }
88  
89    static JexlEngine createEngine(final JexlFeatures features) {
90      return new JexlBuilder().features(features).create();
91    }
92  
93    /**
94     * Will force testing the debugger for each derived test class by
95     * recreating each expression from the JexlNode in the JexlEngine cache &
96     * testing them for equality with the origin.
97     *
98     * @throws Exception
99     */
100   public static void debuggerCheck(final JexlEngine ijexl) throws Exception {
101     Util.debuggerCheck(ijexl);
102   }
103 
104   /**
105    * Compare strings ignoring white space differences.
106    * <p>This replaces any sequence of whitespaces (ie \\s) by one space (ie ASCII 32) in both
107    * arguments then compares them.</p>
108    *
109    * @param lhs left hand side
110    * @param rhs right hand side
111    * @return true if strings are equal besides whitespace
112    */
113   public static boolean equalsIgnoreWhiteSpace(final String lhs, final String rhs) {
114     final String lhsw = lhs.trim().replaceAll("\\s+", "");
115     final String rhsw = rhs.trim().replaceAll("\\s+", "");
116     return lhsw.equals(rhsw);
117   }
118 
119   public static String toString(final JexlScript script) {
120     final Debugger d = new Debugger().lineFeed("").indentation(0);
121     d.debug(script);
122     return d.toString();
123   }
124 
125   @BeforeAll
126   static void setUpClass() {
127     JexlBuilder.setDefaultPermissions(TEST_PERMS);
128     JexlBuilder.setDefaultFeatures(JexlFeatures.createDefault());
129   }
130 
131   public String simpleWhitespace(final String arg) {
132     return arg.trim().replaceAll("\\s+", " ");
133   }
134 
135   public void setUp() throws Exception {
136     // nothing to do
137   }
138 
139   @AfterEach
140   public void tearDown() throws Exception {
141     debuggerCheck(JEXL);
142   }
143 
144   public static class PragmaticContext extends OptionsContext implements JexlContext.PragmaProcessor, JexlContext.OptionsHandle {
145     private final JexlOptions options;
146 
147     public PragmaticContext() {
148       this(new JexlOptions());
149     }
150 
151     public PragmaticContext(final JexlOptions o) {
152       this.options = o;
153     }
154 
155     @Override
156     public JexlOptions getEngineOptions() {
157       return options;
158     }
159 
160     @Override
161     public void processPragma(final JexlOptions opts, final String key, final Object value) {
162       if ("script.mode".equals(key) && "pro50".equals(value)) {
163         opts.set(MODE_PRO50);
164       }
165     }
166 
167     @Override
168     public void processPragma(final String key, final Object value) {
169       processPragma(null, key, value);
170     }
171   }
172 
173    public static int featureVersion() {
174     final String spec = System.getProperty("java.specification.version");
175     return spec.startsWith("1.") ? Integer.parseInt(spec.substring(2)) : Integer.parseInt(spec);
176   }
177 }