1 /*------------------------------------------------------------------------------
  2 Name:      Sql92SelectorTest.java
  3 Project:   xmlBlaster.org
  4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
  5 ------------------------------------------------------------------------------*/
  6 package org.xmlBlaster.test.classtest;
  7 
  8 import java.io.BufferedReader;
  9 import java.io.InputStreamReader;
 10 import java.util.ArrayList;
 11 import java.util.HashMap;
 12 import java.util.Map;
 13 
 14 import junit.framework.TestCase;
 15 
 16 import java.util.logging.Logger;
 17 import java.util.logging.Level;
 18 import org.xmlBlaster.util.Global;
 19 import org.xmlBlaster.util.XmlBlasterException;
 20 import org.xmlBlaster.util.lexical.LikeOpWrapper;
 21 import org.xmlBlaster.util.lexical.Sql92Selector;
 22 import org.xmlBlaster.util.qos.ClientProperty;
 23 
 24 /**
 25  * Test ClientProperty. 
 26  * <p />
 27  * All methods starting with 'test' and without arguments are invoked automatically
 28  * <p />
 29  * Invoke: java -Djava.compiler= junit.textui.TestRunner -noloading org.xmlBlaster.test.classtest.Sql92SelectorTest
 30  * @see org.xmlBlaster.util.qos.ClientProperty
 31  * @see <a href="http://www.xmlblaster.org/xmlBlaster/doc/requirements/engine.qos.clientProperty.html">The client.qos.clientProperty requirement</a>
 32  */
 33 public class Sql92SelectorTest extends TestCase {
 34    
 35    private final static String ME = "Sql92SelectorTest"; 
 36    protected Global glob;
 37    private static Logger log = Logger.getLogger(Sql92SelectorTest.class.getName());
 38    private Map[] dataSet;
 39    private boolean[][] resultSet;
 40    private String[] querySet;
 41    
 42    public Sql92SelectorTest(String name) {
 43       this(null, name);
 44    }
 45    
 46    public Sql92SelectorTest(Global global, String name) {
 47       super(name);
 48       if (global == null) this.glob = Global.instance();
 49       else this.glob = global;
 50 
 51    }
 52 
 53    protected void setUp() {
 54       setupDataSets();
 55    }
 56 
 57    protected void tearDown() {
 58    }
 59 
 60    /**
 61     * Change this method if you want to add a new data set to be checked
 62     *
 63     */
 64    private void setupDataSets() {
 65       ArrayList datas = new ArrayList();
 66       Map map;
 67       String encoding = null;
 68       ClientProperty prop1, prop2, prop3;
 69       prop1 = new ClientProperty("age"   , "integer", encoding, "23"         );
 70       prop2 = new ClientProperty("city"  ,      null, encoding, "London"     );
 71       prop3 = new ClientProperty("amount",  "double", encoding, "100.1234567");
 72       
 73       // set : 0  (0:0:0)
 74       map = new HashMap();
 75       datas.add(map);
 76       
 77       // set : 1  (0:0:1)
 78       map = new HashMap();
 79       map.put("amount", prop3);
 80       datas.add(map);
 81 
 82       // set : 2  (0:1:0)
 83       map = new HashMap();
 84       map.put("city"  , prop2);
 85       datas.add(map);
 86 
 87       // set : 3  (0:1:1)
 88       map = new HashMap();
 89       map.put("city"  , prop2);
 90       map.put("amount", prop3);
 91       datas.add(map);
 92 
 93       // set : 4  (1:0:0)
 94       map = new HashMap();
 95       map.put("age"   , prop1);
 96       datas.add(map);
 97 
 98       // set : 5  (1:0:1)
 99       map = new HashMap();
100       map.put("age"   , prop1);
101       map.put("amount", prop3);
102       datas.add(map);
103 
104       // set : 6  (1:1:0)
105       map = new HashMap();
106       map.put("age"   , prop1);
107       map.put("city"  , prop2);
108       datas.add(map);
109 
110       // set : 7  (1:1:1)
111       map = new HashMap();
112       map.put("age"   , prop1);
113       map.put("city"  , prop2);
114       map.put("amount", prop3);
115       datas.add(map);
116       this.dataSet = (Map[])datas.toArray(new Map[datas.size()]);
117       
118    }
119 
120    /**
121     * Checks if the provided data sets, the queries and the results are
122     * consistent with eachother (this is invoked before starting the real testing)
123     */
124    private void consistencyCheck() {
125       int numData = this.dataSet.length;
126       int numQueries = this.querySet.length;
127       int numResults = this.resultSet.length;
128       assertEquals("The number of queries '" + numQueries + "' differes from the number of results '" + numResults + "'", numQueries, numResults);
129       for (int i=0; i < numResults; i++) {
130          assertEquals("The number of results for query '" + i + "' is wrong", numData, this.resultSet[i].length);
131       }
132    }
133 
134    private String getDataAsText(int pos) {
135       Map map = this.dataSet[pos];
136       StringBuffer buffer = new StringBuffer("[");
137       Object[] keys = map.keySet().toArray();
138       for (int i=0; i < keys.length; i++) {
139          if (i != 0) buffer.append(";");
140          buffer.append(keys[i]).append("=");
141          ClientProperty cp = (ClientProperty)map.get(keys[i]);
142          String tmp = "null";
143          if (cp != null) tmp = cp.getStringValue();
144          buffer.append(tmp);
145       }
146       buffer.append("]");
147       return buffer.toString();
148    }
149    
150    /**
151     * This is the fully automatized initial (general) test. Since it is 
152     * difficult to predict all possible problems, additional tests should 
153     * be added once a bug is encountered. For each such bug an own test
154     * method should be added.
155     */
156    private void selectorPerformTest() {
157       // for each data set one selector
158       consistencyCheck();
159       /*
160       Sql92Selector[] selectors = new Sql92Selector[this.dataSet.length];
161       for (int i=0; i < this.dataSet.length; i++) {
162          if (log.isLoggable(Level.FINE)) log.fine("testSelectorStandard: creating selector nr. " + i);
163          selectors[i] = new Sql92Selector(this.glob);
164       }
165       */
166       Sql92Selector selector = new Sql92Selector(this.glob);
167       
168       for (int i=0; i <  this.querySet.length; i++) {
169          String query = this.querySet[i];
170          log.info("testSelectorStandard: process query '" + query + "'");
171          boolean[] shouldAnswers = this.resultSet[i];
172          for (int j=0; j < this.dataSet.length; j++) {
173             if (log.isLoggable(Level.FINE)) log.fine("testSelectorStandard: query '" + query + "' on set '" + getDataAsText(j));
174             try {
175                boolean response = selector.select(query, this.dataSet[j]);
176                assertEquals("wrong answer for query '" + i + "'\"" + query + "\" on set '" + j + "' " + getDataAsText(j), shouldAnswers[j], response);
177             }
178             catch (XmlBlasterException ex) {
179                ex.printStackTrace();
180                assertTrue("An exception should not occur on query '" + i + "'\"" + query + "\" for dataset " + getDataAsText(j), false);
181             }
182          }
183       }
184    }
185 
186    
187    /**
188     * 
189     * @return the milliseconds per request
190     */
191    private void performanceCheck() {
192       if (log.isLoggable(Level.FINER)) log.finer("performanceCheck");
193       // for each data set one selector
194       consistencyCheck();
195       /*
196       Sql92Selector[] selectors = new Sql92Selector[this.dataSet.length];
197       for (int i=0; i < this.dataSet.length; i++) {
198          selectors[i] = new Sql92Selector(this.glob);
199       }
200       */
201       Sql92Selector selector = new Sql92Selector(this.glob);
202       try {
203          int kmax = 100;
204          long t0 = System.currentTimeMillis();
205          for (int k=0; k <  kmax; k++) {
206             for (int i=0; i <  this.querySet.length; i++) {
207                String query = this.querySet[i];
208                for (int j=0; j < this.dataSet.length; j++) {
209                   boolean response = selector.select(query, this.dataSet[j]);
210                }
211             }
212          }
213          long dt = System.currentTimeMillis() - t0;
214          int nmax = kmax * this.dataSet.length * this.querySet.length;
215          log.info("performance: '" + nmax + "' requests in '" + dt + "' ms");
216          double ret = 1.0 * dt / nmax;
217          log.info("performance: '" + ret + "' ms per request");
218          log.info("performance: '" + ((int)(1000.0 / ret)) + "' request per second (rps)");
219       }
220       catch (XmlBlasterException ex) {
221          ex.printStackTrace();
222       }
223    }
224    
225    
226    
227    public void interactive() {
228       BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
229       System.out.println("\n- input new query: ");
230       while (true) {
231          try {
232             String line = br.readLine();
233             if (line == null) break;
234             System.out.print("Result: ");
235             for (int i=0; i < this.dataSet.length; i++) {
236                Sql92Selector selector = new Sql92Selector(this.glob);
237                boolean ret = selector.select(line, this.dataSet[i]);
238                System.out.print(ret + "\t");
239             }
240          }
241          catch (Exception ex) {
242             ex.printStackTrace();
243          }
244          System.out.println("\n- input new query: ");
245       }
246    }
247 
248    
249    // THE TESTING METHODS COME HERE .....
250    
251    /**
252     * Tests the outer logical operators AND, OR, NOT with and without brackets. 
253     * For each data there must be a boolean value telling if the result is true or false
254     */
255    public void testLogicalOps() {
256       ArrayList queries = new ArrayList();
257       ArrayList results = new ArrayList();
258       boolean t = true;
259       boolean f = false;
260       
261       // query 0
262       queries.add("age=23 AND city='London' AND amount<200.2");
263       /*  helper                 0.0.0  0.0.1  0.1.0  0.1.1  1.0.0  1.0.1  1.1.0  1.1.1  */
264       results.add(new boolean[] {false, false, false, false, false, false, false, true });
265       
266       // query 1
267       queries.add("age=23 OR city='London' OR amount < 200.2");
268       /*  helper                 0  0  0, 0  0  1, 0  1  0, 0  1  1, 1  0  0  1  0  1  1  1  0  1  1  1  */
269       results.add(new boolean[] {f||f||f, f||f||t, f||t||f, f||t||t, t||f||f, t||f||t, t||t||f, t||t||t });
270       
271       // query 2
272       queries.add("age=23 OR city='London' AND amount < 200.2");
273       /*  helper                 0  0  0, 0  0  1, 0  1  0, 0  1  1, 1  0  0  1  0  1  1  1  0  1  1  1  */
274       results.add(new boolean[] {f||f&&f, f||f&&t, f||t&&f, f||t&&t, t||f&&f, t||f&&t, t||t&&f, t||t&&t });
275       
276       // query 3
277       queries.add("age=23 OR city='London' AND amount < 200.2");
278       /*  helper                 0  0  0, 0  0  1, 0  1  0, 0  1  1, 1  0  0  1  0  1  1  1  0  1  1  1  */
279       results.add(new boolean[] {f||f&&f, f||f&&t, f||t&&f, f||t&&t, t||f&&f, t||f&&t, t||t&&f, t||t&&t });
280       
281       // query 4
282       queries.add("(age=23 AND city='London') OR amount < 200.2");
283       /*  helper                 0  0  0, 0  0  1, 0  1  0, 0  1  1, 1  0  0  1  0  1  1  1  0  1  1  1  */
284       results.add(new boolean[] {f&&f||f, f&&f||t, f&&t||f, f&&t||t, t&&f||f, t&&f||t, t&&t||f, t&&t||t });
285       
286       // query 5
287       queries.add("age=23 OR (city='London' AND amount < 200.2)");
288       /*  helper                 0   0  0 , 0   0  1 , 0   1  0 , 0   1  1 , 1   0  0,  1   0  1 , 1   1  0 , 1   1  1  */
289       results.add(new boolean[] {f||(f&&f), f||(f&&t), f||(t&&f), f||(t&&t), t||(f&&f), t||(f&&t), t||(t&&f), t||(t&&t) });
290 
291       // query 6
292       queries.add("NOT age=23 OR (city='London' AND amount < 200.2)");
293       /*  helper                  0   0  0 ,  0   0  1 ,  0   1  0 ,  0   1  1 ,  1   0  0 ,  1   0  1 ,  1   1  0 ,  1   1  1  */
294       results.add(new boolean[] {!f||(f&&f), !f||(f&&t), !f||(t&&f), !f||(t&&t), !t||(f&&f), !t||(f&&t), !t||(t&&f), !t||(t&&t) });
295 
296       // query 7
297       queries.add("age=23 OR NOT (city='London' AND amount < 200.2)");
298       /*  helper                 0    0  0 , 0    0  1 , 0    1  0 , 0    1  1 , 1    0  0,  1    0  1 , 1    1  0 , 1    1  1  */
299       results.add(new boolean[] {f||!(f&&f), f||!(f&&t), f||!(t&&f), f||!(t&&t), t||!(f&&f), t||!(f&&t), t||!(t&&f), t||!(t&&t) });
300 
301       // query 8
302       queries.add("(age=23 OR NOT (city='London' AND amount < 200.2))");
303       /*  helper                 0    0  0 , 0    0  1 , 0    1  0 , 0    1  1 , 1    0  0,  1    0  1 , 1    1  0 , 1    1  1  */
304       results.add(new boolean[] {f||!(f&&f), f||!(f&&t), f||!(t&&f), f||!(t&&t), t||!(f&&f), t||!(f&&t), t||!(t&&f), t||!(t&&t) });
305 
306       // query 9
307       queries.add("NOT (age=23 OR NOT (city='London' AND amount < 200.2))");
308       /*  helper                   0    0  0 ,    0    0  1 ,    0    1  0 ,    0    1  1 ,    1    0  0,     1    0  1 ,    1    1  0 ,    1    1  1  */
309       results.add(new boolean[] {!(f||!(f&&f)), !(f||!(f&&t)), !(f||!(t&&f)), !(f||!(t&&t)), !(t||!(f&&f)), !(t||!(f&&t)), !(t||!(t&&f)), !(t||!(t&&t)) });
310 
311       // query 10
312       queries.add("NOT (age=23 OR NOT (city='London' AND (amount < 200.2)))");
313       /*  helper                   0    0  0 ,    0    0  1 ,    0    1  0 ,    0    1  1 ,    1    0  0,     1    0  1 ,    1    1  0 ,    1    1  1  */
314       results.add(new boolean[] {!(f||!(f&&f)), !(f||!(f&&t)), !(f||!(t&&f)), !(f||!(t&&t)), !(t||!(f&&f)), !(t||!(f&&t)), !(t||!(t&&f)), !(t||!(t&&t)) });
315 
316       // query 11
317       queries.add("age=23 OR NOT ((city='London') AND amount < 200.2)");
318       /*  helper                 0    0  0 , 0    0  1 , 0    1  0 , 0    1  1 , 1    0  0,  1    0  1 , 1    1  0 , 1    1  1  */
319       results.add(new boolean[] {f||!(f&&f), f||!(f&&t), f||!(t&&f), f||!(t&&t), t||!(f&&f), t||!(f&&t), t||!(t&&f), t||!(t&&t) });
320 
321       // query 12
322       queries.add("age=23 OR NOT ((city='London') AND (amount < 200.2))");
323       /*  helper                 0    0  0 , 0    0  1 , 0    1  0 , 0    1  1 , 1    0  0,  1    0  1 , 1    1  0 , 1    1  1  */
324       results.add(new boolean[] {f||!(f&&f), f||!(f&&t), f||!(t&&f), f||!(t&&t), t||!(f&&f), t||!(f&&t), t||!(t&&f), t||!(t&&t) });
325 
326       // query 13
327       queries.add("age=23 OR NOT ((city='London') AND NOT(amount < 200.2))");
328       /*  helper                 0    0   0 , 0    0   1 , 0    1   0 , 0    1   1 , 1    0   0,  1    0   1 , 1    1   0 , 1    1   1  */
329       results.add(new boolean[] {f||!(f&&!f), f||!(f&&!t), f||!(t&&!f), f||!(t&&!t), t||!(f&&!f), t||!(f&&!t), t||!(t&&!f), t||!(t&&!t) });
330 
331       this.querySet = (String[])queries.toArray(new String[queries.size()]);
332       this.resultSet = (boolean[][])results.toArray(new boolean[results.size()][]);
333       // here the real testing is performed (the algorithm is the same for all tests)
334       selectorPerformTest();
335    }
336 
337    
338    /**
339     * Tests the NULL and NOT NULL statements.  
340     * For each data there must be a boolean value telling if the result is true or false
341     */
342    public void testNullOps() {
343       ArrayList queries = new ArrayList();
344       ArrayList results = new ArrayList();
345       boolean t = true;
346       boolean f = false;
347       
348       // query 0
349       queries.add("age IS NULL OR city IS NULL OR amount IS NULL");
350       /*  helper                 0  0  0, 0  0  1, 0  1  0, 0  1  1, 1  0  0  1  0  1  1  1  0  1  1  1  */
351       results.add(new boolean[] {t||t||t, t||t||f, t||f||t, t||f||f, f||t||t, f||t||f, f||f||t, f||f||f });
352       
353       // query 1
354       queries.add("age IS NULL AND city IS NULL AND amount IS NULL");
355       /*  helper                 0  0  0, 0  0  1, 0  1  0, 0  1  1, 1  0  0  1  0  1  1  1  0  1  1  1  */
356       results.add(new boolean[] {t&&t&&t, t&&t&&f, t&&f&&t, t&&f&&f, f&&t&&t, f&&t&&f, f&&f&&t, f&&f&&f });
357       
358       // query 2
359       queries.add("age IS NOT NULL OR city IS NOT NULL OR amount IS NOT NULL");
360       /*  helper                 0  0  0, 0  0  1, 0  1  0, 0  1  1, 1  0  0  1  0  1  1  1  0  1  1  1  */
361       results.add(new boolean[] {f||f||f, f||f||t, f||t||f, f||t||t, t||f||f, t||f||t, t||t||f, t||t||t });
362       
363       // query 3
364       queries.add("age IS NOT NULL AND city IS NOT NULL AND amount IS NOT NULL");
365       /*  helper                 0  0  0, 0  0  1, 0  1  0, 0  1  1, 1  0  0  1  0  1  1  1  0  1  1  1  */
366       results.add(new boolean[] {f&&f&&f, f&&f&&t, f&&t&&f, f&&t&&t, t&&f&&f, t&&f&&t, t&&t&&f, t&&t&&t });
367 
368       this.querySet = (String[])queries.toArray(new String[queries.size()]);
369       this.resultSet = (boolean[][])results.toArray(new boolean[results.size()][]);
370       // here the real testing is performed (the algorithm is the same for all tests)
371       selectorPerformTest();
372    }
373 
374    /**
375     * Tests the outer logical operators AND, OR, NOT with and without brackets. 
376     * For each data there must be a boolean value telling if the result is true or false
377     */
378    public void testArithmeticOps() {
379       ArrayList queries = new ArrayList();
380       ArrayList results = new ArrayList();
381       boolean t = true;
382       boolean f = false;
383       
384       // query 0
385       queries.add("age = 15+8 AND amount < 300.0+10.0");
386       /*  helper                 0  0, 0  1, 0  0, 0  1, 1  0  1  1  1  0  1  1  */
387       results.add(new boolean[] {f&&f, f&&t, f&&f, f&&t, t&&f, t&&t, t&&f, t&&t });
388       
389       // query 1
390       queries.add("age = 26-3 AND amount < 300.0-10.0");
391       /*  helper                 0  0, 0  1, 0  0, 0  1, 1  0  1  1  1  0  1  1  */
392       results.add(new