-
Notifications
You must be signed in to change notification settings - Fork 206
/
AbstractQueryTest.java
375 lines (345 loc) · 12.9 KB
/
AbstractQueryTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.test.api.query;
import org.apache.jackrabbit.test.AbstractJCRTest;
import org.apache.jackrabbit.test.NotExecutableException;
import org.apache.jackrabbit.test.api.util.ISO9075;
import javax.jcr.query.QueryResult;
import javax.jcr.query.RowIterator;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.qom.QueryObjectModelFactory;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Session;
import javax.jcr.ValueFactory;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Abstract base class for query test cases.
*/
public abstract class AbstractQueryTest extends AbstractJCRTest {
/**
* Resolved Name for jcr:score
*/
protected String jcrScore;
/**
* Resolved Name for jcr:path
*/
protected String jcrPath;
/**
* Resolved Name for jcr:root
*/
protected String jcrRoot;
/**
* Resolved Name for jcr:contains
*/
protected String jcrContains;
/**
* Resolved Name for jcr:deref
*/
protected String jcrDeref;
/**
* The string /${jcrRoot}${testRoot} with all components of the test path
* properly escaped for XPath.
*
* @see <a href="https://issues.apache.org/jira/browse/JCR-714">JCR-714</a>
*/
protected String xpathRoot;
/**
* The query object model factory for {@link #superuser}.
*/
protected QueryObjectModelFactory qf;
/**
* The value factory for creating literals for the query object model.
*/
protected ValueFactory vf;
/**
* The query manager for {@link #superuser}
*/
protected QueryManager qm;
/**
* Set-up the configuration values used for the test. Per default retrieves
* a session, configures testRoot, and nodetype and checks if the query
* language for the current language is available.<br>
*/
protected void setUp() throws Exception {
super.setUp();
jcrScore = superuser.getNamespacePrefix(NS_JCR_URI) + ":score";
jcrPath = superuser.getNamespacePrefix(NS_JCR_URI) + ":path";
jcrRoot = superuser.getNamespacePrefix(NS_JCR_URI) + ":root";
jcrContains = superuser.getNamespacePrefix(NS_JCR_URI) + ":contains";
jcrDeref = superuser.getNamespacePrefix(NS_JCR_URI) + ":deref";
xpathRoot = "/" + jcrRoot + ISO9075.encodePath(testRoot);
qm = superuser.getWorkspace().getQueryManager();
qf = qm.getQOMFactory();
vf = superuser.getValueFactory();
}
protected void tearDown() throws Exception {
qm = null;
qf = null;
vf = null;
super.tearDown();
}
/**
* Create a {@link Query} for a given {@link Statement}.
*
* @param statement the query should be created for
* @return
*
* @throws RepositoryException
* @see #createQuery(String, String)
*/
protected Query createQuery(Statement statement) throws RepositoryException {
return createQuery(statement.getStatement(), statement.getLanguage());
}
/**
* Creates a {@link Query} for the given statement in the requested
* language
*
* @param statement the query should be created for
* @param language query language to be used for Query creation
* @return
*
* @throws RepositoryException
*/
protected Query createQuery(String statement, String language) throws RepositoryException {
log.println("Creating query: " + statement);
return superuser.getWorkspace().getQueryManager().createQuery(statement,
language);
}
/**
* Creates and executes a {@link Query} for the given {@link Statement}
*
* @param statement to execute
* @return
*
* @throws RepositoryException
* @see #execute(String, String)
*/
protected QueryResult execute(Statement statement) throws RepositoryException {
return execute(statement.getStatement(), statement.getLanguage());
}
/**
* Creates and executes a {@link Query} for a given Statement in a given
* query language
*
* @param statement the query should be build for
* @param language query language the stement is written in
* @return
*
* @throws RepositoryException
*/
protected QueryResult execute(String statement, String language)
throws RepositoryException {
Query query = createQuery(statement, language);
return query.execute();
}
/**
* Checks if the <code>result</code> contains a number of
* <code>hits</code>.
*
* @param result the <code>QueryResult</code>.
* @param hits the number of expected hits.
* @throws RepositoryException if an error occurs while iterating over the
* result nodes.
*/
protected void checkResult(QueryResult result, int hits)
throws RepositoryException {
RowIterator itr = result.getRows();
long count = itr.getSize();
if (count == 0) {
log.println(" NONE");
} else if (count == -1) {
// have to count in a loop
count = 0;
while (itr.hasNext()) {
itr.nextRow();
count++;
}
}
assertEquals("Wrong hit count.", hits, count);
}
/**
* Checks if the <code>result</code> contains a number of <code>hits</code>
* and <code>properties</code>.
*
* @param result the <code>QueryResult</code>.
* @param hits the number of expected hits.
* @param properties the number of expected properties.
* @throws RepositoryException if an error occurs while iterating over the
* result nodes.
*/
protected void checkResult(QueryResult result, int hits, int properties)
throws RepositoryException {
checkResult(result, hits);
// now check property count
int count = 0;
log.println("Properties:");
String[] propNames = result.getColumnNames();
for (RowIterator it = result.getRows(); it.hasNext();) {
StringBuffer msg = new StringBuffer();
Value[] values = it.nextRow().getValues();
for (int i = 0; i < propNames.length; i++, count++) {
msg.append(" ").append(propNames[i]).append(": ");
if (values[i] == null) {
msg.append("null");
} else {
msg.append(values[i].getString());
}
}
log.println(msg);
}
if (count == 0) {
log.println(" NONE");
}
assertEquals("Wrong property count.", properties, count);
}
/**
* Checks if the {@link QueryResult} is ordered according order property in
* direction of related argument.
*
* @param queryResult to be tested
* @param propName Name of the porperty to order by
* @param descending if <code>true</code> order has to be descending
* @throws RepositoryException
* @throws NotExecutableException in case of less than two results or all
* results have same size of value in its
* order-property
*/
protected void evaluateResultOrder(QueryResult queryResult, String propName,
boolean descending)
throws RepositoryException, NotExecutableException {
NodeIterator nodes = queryResult.getNodes();
if (getSize(nodes) < 2) {
fail("Workspace does not contain sufficient content to test ordering on result nodes.");
}
// need to re-aquire nodes, {@link #getSize} may consume elements.
nodes = queryResult.getNodes();
int changeCnt = 0;
String last = descending ? "\uFFFF" : "";
while (nodes.hasNext()) {
String value = nodes.nextNode().getProperty(propName).getString();
int cp = value.compareTo(last);
// if value changed evaluate if the ordering is correct
if (cp != 0) {
changeCnt++;
if (cp > 0 && descending) {
fail("Repository doesn't order properly descending");
} else if (cp < 0 && !descending) {
fail("Repository doesn't order properly ascending");
}
}
last = value;
}
if (changeCnt < 1) {
fail("Workspace does not contain distinct values for " + propName);
}
}
/**
* Executes the <code>xpath</code> query and checks the results against
* the specified <code>nodes</code>.
* @param session the session to use for the query.
* @param xpath the xpath query.
* @param nodes the expected result nodes.
*/
protected void executeXPathQuery(Session session, String xpath, Node[] nodes)
throws RepositoryException {
QueryResult res = session.getWorkspace().getQueryManager().createQuery(xpath, Query.XPATH).execute();
checkResult(res, nodes);
}
/**
* Executes the <code>sql</code> query and checks the results against
* the specified <code>nodes</code>.
* @param session the session to use for the query.
* @param sql the sql query.
* @param nodes the expected result nodes.
*/
protected void executeSqlQuery(Session session, String sql, Node[] nodes)
throws RepositoryException {
QueryResult res = session.getWorkspace().getQueryManager().createQuery(sql, Query.SQL).execute();
checkResult(res, nodes);
}
/**
* Checks if the result set contains exactly the <code>nodes</code>.
* @param result the query result.
* @param nodes the expected nodes in the result set.
*/
protected void checkResult(QueryResult result, Node[] nodes)
throws RepositoryException {
// collect paths
Set expectedPaths = new HashSet();
for (int i = 0; i < nodes.length; i++) {
expectedPaths.add(nodes[i].getPath());
}
Set resultPaths = new HashSet();
for (NodeIterator it = result.getNodes(); it.hasNext();) {
resultPaths.add(it.nextNode().getPath());
}
// check if all expected are in result
for (Iterator it = expectedPaths.iterator(); it.hasNext();) {
String path = (String) it.next();
assertTrue(path + " is not part of the result set", resultPaths.contains(path));
}
// check result does not contain more than expected
for (Iterator it = resultPaths.iterator(); it.hasNext();) {
String path = (String) it.next();
assertTrue(path + " is not expected to be part of the result set", expectedPaths.contains(path));
}
}
/**
* Returns the nodes in <code>it</code> as an array of Nodes.
* @param it the NodeIterator.
* @return the elements of the iterator as an array of Nodes.
*/
protected Node[] toArray(NodeIterator it) {
List nodes = new ArrayList();
while (it.hasNext()) {
nodes.add(it.nextNode());
}
return (Node[]) nodes.toArray(new Node[nodes.size()]);
}
/**
* Escape an identifier suitable for the SQL parser
* @TODO currently only handles dash character
*/
protected String escapeIdentifierForSQL(String identifier) {
boolean needsEscaping = identifier.indexOf('-') >= 0;
if (!needsEscaping) {
return identifier;
}
else {
return '"' + identifier + '"';
}
}
/**
* @param language a query language.
* @return <code>true</code> if <code>language</code> is supported;
* <code>false</code> otherwise.
* @throws RepositoryException if an error occurs.
*/
protected boolean isSupportedLanguage(String language)
throws RepositoryException {
return Arrays.asList(qm.getSupportedQueryLanguages()).contains(language);
}
}