1   /*
2    * $Id: TestValidWhen.java 569606 2007-08-25 03:54:59Z pbenedict $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  package org.apache.struts.validator;
22  
23  import junit.framework.Test;
24  import junit.framework.TestCase;
25  import junit.framework.TestSuite;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.commons.validator.util.ValidatorUtils;
30  import org.apache.struts.validator.validwhen.ValidWhenLexer;
31  import org.apache.struts.validator.validwhen.ValidWhenParser;
32  
33  import java.io.StringReader;
34  
35  /**
36   * Unit tests for the ValidWhen Parser/Lexer.
37   */
38  public class TestValidWhen extends TestCase {
39      /**
40       * All logging goes through this logger
41       */
42      private static Log log = LogFactory.getLog(TestValidWhen.class);
43      protected PojoBean testBean;
44  
45      /**
46       * Defines the testcase name for JUnit.
47       *
48       * @param theName the testcase's name.
49       */
50      public TestValidWhen(String theName) {
51          super(theName);
52      }
53  
54      /**
55       * Start the tests.
56       *
57       * @param theArgs the arguments. Not used
58       */
59      public static void main(String[] theArgs) {
60          junit.awtui.TestRunner.main(new String[] { TestValidWhen.class.getName() });
61      }
62  
63      /**
64       * @return a test suite (<code>TestSuite</code>) that includes all methods
65       *         starting with "test"
66       */
67      public static Test suite() {
68          // All methods starting with "test" will be executed in the test suite.
69          return new TestSuite(TestValidWhen.class);
70      }
71  
72      public void setUp() {
73          testBean = new PojoBean(123, 789);
74          testBean.setStringValue1("ABC");
75          testBean.setStringValue2(null);
76          testBean.setBeans(new PojoBean[] {
77                  new PojoBean(11, 12), new PojoBean(21, 22), new PojoBean(31, 42),
78                  new PojoBean(41, 52), new PojoBean(51, 62)
79              });
80          testBean.setMapped("testKey", "mappedValue");
81      }
82  
83      public void tearDown() {
84          testBean = null;
85      }
86  
87      /**
88       * Test Operators.
89       */
90      public void testProperty() {
91          // *this*
92          doParse("(*this* == 123)", testBean, 0, "intValue1", true);
93  
94          // Named property
95          doParse("(intValue2 == 789)", testBean, 0, "intValue1", true);
96  
97          // Indexed Property
98          doParse("(beans[].intValue1 == 21)", testBean, 1, "intValue1", true);
99          doParse("(beans[1].intValue1 == 21)", testBean, 4, "intValue1", true);
100 
101         // Mapped Property - *** NOT SUPPORTED ***
102         // doParse("(mapped(mappedValue) == 'testKey')", testBean , 0, "mappedValue", true);
103     }
104 
105     /**
106      * Test Operators.
107      */
108     public void testOperators() {
109         // Equal
110         doParse("(*this* == 123)", testBean, 0, "intValue1", true);
111 
112         // Not Equal
113         doParse("(*this* != 456)", testBean, 0, "intValue1", true);
114 
115         // Less Than
116         doParse("(*this* < 456)", testBean, 0, "intValue1", true);
117 
118         // Greater Than
119         doParse("(*this* > 100)", testBean, 0, "intValue1", true);
120 
121         // Less Than Equal
122         doParse("(*this* <= 123)", testBean, 0, "intValue1", true);
123 
124         // Greater Than Equal
125         doParse("(*this* >= 123)", testBean, 0, "intValue1", true);
126     }
127 
128     /**
129      * Test String values.
130      */
131     public void testString() {
132         doParse("(*this* != '--')", "XX", 0, "stringValue1", true);
133         doParse("(*this* == '--')", "--", 0, "stringValue1", true);
134 
135         String testValue = "dsgUOLMdLsdL";
136 
137         // single quote
138         doParse("(*this* == '" + testValue + "')", testValue, 0,
139             "stringValue1", true);
140 
141         // double quote
142         doParse("(*this* == \"" + testValue + "\")", testValue, 0,
143             "stringValue1", true);
144 
145         // obscure characters
146         doParse("(*this* == \":\")", ":", 0,
147             "stringValue1", true);
148         doParse("(*this* == \"foo:bar\")", "foo:bar", 0,
149             "stringValue1", true);
150     }
151 
152     /**
153      * Test Numeric values.
154      */
155     public void testNumeric() {
156         // Test Zero
157         PojoBean numberBean = new PojoBean(0, -50);
158 
159         doParse("(intValue1 == 0)", numberBean, 0, "intValue1", true);
160         doParse("(intValue2 != 0)", numberBean, 0, "intValue2", true);
161         doParse("(integerValue1 == 0)", numberBean, 0, "integerValue1", true);
162         doParse("(integerValue2 != 0)", numberBean, 0, "integerValue2", true);
163 
164         // int
165         doParse("(intValue1 == 123)", testBean, 0, "intValue1", true);
166 
167         // Integer
168         doParse("(integerValue1 == 123)", testBean, 0, "integerValue1", true);
169 
170         // Negative Numbers
171         doParse("((intValue2 < -10)     and (intValue2 > -60))", numberBean, 0,
172             "intValue2", true);
173         doParse("((integerValue2 < -10) and (integerValue2 > -60))",
174             numberBean, 0, "integerValue2", true);
175 
176         // Hex
177         doParse("(integerValue1 == 0x7B)", testBean, 0, "integerValue1", true);
178 
179         // Octal
180         doParse("(integerValue1 == 0173)", testBean, 0, "integerValue1", true);
181 
182         // Test 'String' numbers
183         PojoBean stringBean = new PojoBean("11", "2");
184 
185         doParse("(stringValue1 > stringValue2)", stringBean, 0, "stringValue1",
186             true);
187         doParse("(stringValue1 < stringValue2)", stringBean, 0, "stringValue1",
188             false);
189     }
190 
191     /**
192      * Test Null.
193      */
194     public void testNull() {
195         // Not Null
196         doParse("(*this* != null)", testBean, 0, "stringValue1", true);
197 
198         // Null
199         doParse("(*this* == null)", testBean, 0, "stringValue2", true);
200 
201         // 0-length empty string
202         testBean.setStringValue2("");
203         doParse("(*this* == null)", testBean, 0, "stringValue2", true);
204 
205         // N-length empty string
206         testBean.setStringValue2("  ");
207         doParse("(*this* == null)", testBean, 0, "stringValue2", true);
208     }
209 
210     /**
211      * Test Joined expressions ('and' or 'or')
212      */
213     public void testJoined() {
214         // Join with 'or'
215         doParse("((*this* == 'ABC') or (stringValue2 == null))", testBean, 0,
216             "stringValue1", true);
217         doParse("((*this* != 'ABC') or (stringValue2 == null))", testBean, 0,
218             "stringValue1", true);
219         doParse("((*this* == 'ABC') or (stringValue2 != null))", testBean, 0,
220             "stringValue1", true);
221         doParse("((*this* != 'ABC') or (stringValue2 != null))", testBean, 0,
222             "stringValue1", false);
223 
224         // Join with 'and'
225         doParse("((*this* == 'ABC') and (stringValue2 == null))", testBean, 0,
226             "stringValue1", true);
227         doParse("((*this* != 'ABC') and (stringValue2 == null))", testBean, 0,
228             "stringValue1", false);
229         doParse("((*this* == 'ABC') and (stringValue2 != null))", testBean, 0,
230             "stringValue1", false);
231         doParse("((*this* != 'ABC') and (stringValue2 != null))", testBean, 0,
232             "stringValue1", false);
233     }
234 
235     /**
236      * Parse the expression and check that the expected result (either true or
237      * false) occurs - fail if an exception is thrown opr the wrong result
238      * occurs.
239      *
240      * @param test     Test expression
241      * @param bean     Test Bean
242      * @param index    index value
243      * @param property Bean property
244      * @param expected Expected Result
245      */
246     private void doParse(String test, Object bean, int index, String property,
247         boolean expected) {
248         boolean result = false;
249 
250         try {
251             result = doParse(test, bean, index, property);
252         } catch (Exception ex) {
253             log.error("Parsing " + test + " for property '" + property + "'", ex);
254             fail("Parsing " + test + " threw " + ex);
255         }
256 
257         if (expected) {
258             assertTrue(test + " didn't return TRUE for " + property, result);
259         } else {
260             assertFalse(test + " didn't return FALSE for " + property, result);
261         }
262     }
263 
264     /**
265      * Parse the expression and check that an Exception is throw. Failes if no
266      * expection is thrown.
267      *
268      * @param test     Test expression
269      * @param bean     Test Bean
270      * @param index    index value
271      * @param property Bean property
272      */
273     private void doParseFail(String test, Object bean, int index,
274         String property) {
275         try {
276             boolean result = doParse(test, bean, index, property);
277 
278             fail("Parsing " + test + " didn't throw exception as expected "
279                 + result);
280         } catch (Exception expected) {
281             // ignore exception - expected result
282         }
283     }
284 
285     /**
286      * Parse the expression returning the result
287      *
288      * @param test     Test expression
289      * @param bean     Test Bean
290      * @param index    index value
291      * @param property Bean property
292      */
293     private boolean doParse(String test, Object bean, int index, String property)
294         throws Exception {
295         if (bean == null) {
296             throw new NullPointerException("Bean is null for property '"
297                 + property + "'");
298         }
299 
300         String value =
301             String.class.isInstance(bean) ? (String) bean
302                                           : ValidatorUtils.getValueAsString(bean,
303                 property);
304 
305         ValidWhenLexer lexer = new ValidWhenLexer(new StringReader(test));
306 
307         ValidWhenParser parser = new ValidWhenParser(lexer);
308 
309         parser.setForm(bean);
310         parser.setIndex(index);
311         parser.setValue(value);
312 
313         parser.expression();
314 
315         return parser.getResult();
316     }
317 }