Add tests for failures
[cql-java-moved-to-github.git] / src / test / java / org / z3950 / zing / cql / CQLParserTest.java
1 /*
2  * Copyright (c) 1995-2014, Index Datassss
3  * All rights reserved.
4  * See the file LICENSE for details.
5  */
6 package org.z3950.zing.cql;
7
8 import java.io.BufferedInputStream;
9 import java.io.BufferedReader;
10 import java.io.File;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.io.InputStreamReader;
14 import java.io.Reader;
15 import java.io.StringReader;
16 import java.net.URISyntaxException;
17 import java.net.URL;
18 import java.net.URLDecoder;
19 import java.util.Enumeration;
20 import java.util.HashSet;
21 import java.util.Set;
22 import java.util.jar.JarEntry;
23 import java.util.jar.JarFile;
24 import org.junit.After;
25 import org.junit.AfterClass;
26 import org.junit.Before;
27 import org.junit.BeforeClass;
28 import org.junit.Test;
29 import static org.junit.Assert.*;
30
31 import static java.lang.System.out;
32 import java.util.Properties;
33
34 /**
35  *
36  * @author jakub
37  */
38 public class CQLParserTest {
39   public CQLParserTest() {
40   }
41   
42   @BeforeClass
43   public static void setUpClass() {
44   }
45   
46   @AfterClass
47   public static void tearDownClass() {
48   }
49   
50   @Before
51   public void setUp() {
52   }
53   
54   @After
55   public void tearDown() {
56   }
57
58   /**
59    * Test of main method, of class CQLParser.
60    */
61   @Test
62   public void testRegressionQueries() throws IOException {
63     System.out.println("Testing the parser using pre-canned regression queries...");
64     //we might be running the test from within the jar
65     //list all resource dirs, then traverse them
66     String[] dirs = getResourceListing(this.getClass(), "regression");
67     for (String dir : dirs) {
68       String files[] = getResourceListing(this.getClass(), "regression/" + dir);
69       for (String file : files) {
70         if (!file.endsWith(".cql")) continue;
71         out.println("Parsing "+dir+"/"+file);
72         InputStream is = this.getClass().getResourceAsStream("/regression/"+dir+"/"+file);
73         BufferedReader reader = null, reader2 = null; 
74         try {
75           reader = new BufferedReader(new InputStreamReader(is));
76           String input = reader.readLine();
77           out.println("Query: "+input);
78           String result;
79           try {
80             CQLParser parser = new CQLParser();
81             CQLNode parsed = parser.parse(input);
82             result = parsed.toXCQL();
83           } catch (CQLParseException pe) {
84             result = pe.getMessage() + "\n";
85           }
86           out.println("Parsed:");
87           out.println(result);
88           //read the expected xcql output
89           String expected = "<expected result file not found>";
90           String prefix = file.substring(0, file.length()-4);
91           InputStream is2 = this.getClass()
92             .getResourceAsStream("/regression/"+dir+"/"+prefix+".xcql");
93           if (is2 != null) {
94             reader2 = new BufferedReader(new InputStreamReader(is2));
95             StringBuilder sb = new StringBuilder();
96             String line;
97             while ((line = reader2.readLine()) != null) {
98               sb.append(line).append("\n");
99             }
100             expected = sb.toString();
101           }
102           out.println("Expected: ");
103           out.println(expected);
104           assertEquals("Assertion failure for "+dir+"/"+file, expected, result);
105         } finally {
106           if (reader != null) reader.close();
107           if (reader2 != null) reader2.close();
108         }
109       }
110     }
111   }
112   
113   /**
114    * Test the integrity of the parser as follows:
115    * - Generate a random tree with CQLGenerator
116    * - Serialize it
117    * - Canonicalise it by running through the parser
118    * - Compare the before-and-after versions.
119    * Since the CQLGenerator output is in canonical form anyway, the
120    * before-and-after versions should be identical.  This process exercises
121    * the comprehensiveness and bullet-proofing of the parser, as well as
122    * the accuracy of the rendering.
123    * @throws IOException
124    * @throws MissingParameterException 
125    */
126   @Test
127   public void testRandomQueries() throws IOException, MissingParameterException {
128     out.println("Testing the parser using 100 randomly generated queries...");
129     Properties params = new Properties();
130     InputStream is = getClass().getResourceAsStream("/generate.properties");
131     if (is == null)
132       fail("Cannot locate generate.properties");
133     params.load(is);
134     is.close();
135     CQLGenerator generator = new CQLGenerator(params);
136     for (int i=0; i<100; i++) {
137       CQLNode random = generator.generate();
138       String expected = random.toCQL();
139       out.println("Generated query: "+expected);
140       CQLParser parser = new CQLParser();
141       try {
142         CQLNode parsed = parser.parse(expected);
143         String result = parsed.toCQL();
144         assertEquals(expected, result);
145       } catch (CQLParseException pe) {
146         fail("Generated query failed to parse: "+pe.getMessage());
147       }
148     }
149   }
150   
151   //helper methods follow
152   //TODO move to masterkey-common
153   
154   @SuppressWarnings("rawtypes")
155   public static String[] getResourceListing(Class clazz, String path) throws
156     IOException {
157     URL dirURL = clazz.getClassLoader().getResource(path);
158     if (dirURL != null && dirURL.getProtocol().equals("file")) {
159       /* A file path: easy enough */
160       try {
161         return new File(dirURL.toURI()).list();
162       } catch (URISyntaxException use) {
163         throw new UnsupportedOperationException(use);
164       }
165     }
166
167     if (dirURL == null) {
168       /* 
169        * In case of a jar file, we can't actually find a directory.
170        * Have to assume the same jar as clazz.
171        */
172       String me = clazz.getName().replace(".", "/") + ".class";
173       dirURL = clazz.getClassLoader().getResource(me);
174     }
175
176     if (dirURL.getProtocol().equals("jar")) {
177       /* A JAR path */
178       String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf(
179         "!")); //strip out only the JAR file
180       JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8"));
181       Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
182       Set<String> result = new HashSet<String>(); //avoid duplicates in case it is a subdirectory
183       while (entries.hasMoreElements()) {
184         String name = entries.nextElement().getName();
185         if (name.startsWith(path)) { //filter according to the path
186           String entry = name.substring(path.length());
187           int checkSubdir = entry.indexOf("/");
188           if (checkSubdir >= 0) {
189             // if it is a subdirectory, we just return the directory name
190             entry = entry.substring(0, checkSubdir);
191           }
192           result.add(entry);
193         }
194       }
195       return result.toArray(new String[result.size()]);
196     }
197
198     throw new UnsupportedOperationException("Cannot list files for URL "
199       + dirURL);
200   }
201 }