001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.persistence.models.company;
020:
021: import java.beans.*;
022: import java.io.*;
023: import java.util.*;
024: import javax.persistence.*;
025: import junit.framework.*;
026: import org.apache.openjpa.persistence.test.*;
027:
028: /**
029: * Generic test case that will be extended by a concrete company
030: * model subclass.
031: *
032: * @author Marc Prud'hommeaux
033: */
034: public abstract class CompanyModelTest extends SingleEMTestCase {
035:
036: private static Map<Class, Class> factoryClasses;
037: private Map<Class, Class> impls;
038:
039: public void setUp() {
040: // make a map of the implementations based on the class names in
041: // the current package of the test subclass
042: impls = new HashMap<Class, Class>();
043: impls.put(IAddress.class, localClass("Address"));
044: impls.put(ICompany.class, localClass("Company"));
045: impls.put(ICustomer.class, localClass("Customer"));
046: impls.put(IPerson.class, localClass("Person"));
047: impls.put(IEmployee.class, localClass("Employee"));
048: impls.put(IFullTimeEmployee.class,
049: localClass("FullTimeEmployee"));
050: impls.put(ILineItem.class, localClass("LineItem"));
051: impls.put(IProductOrder.class, localClass("ProductOrder"));
052: impls.put(IPartTimeEmployee.class,
053: localClass("PartTimeEmployee"));
054: impls.put(IProduct.class, localClass("Product"));
055:
056: setUp(impls.values().toArray(new Class[impls.size()]));
057: checkModel();
058: }
059:
060: private Class localClass(String name) {
061: String pkg = getClass().getPackage().getName();
062: try {
063: return Class.forName(pkg + "." + name);
064: } catch (Exception e) {
065: throw new IllegalStateException(e);
066: }
067: }
068:
069: /**
070: * Runs through basic queries against all of the properties of all
071: * of the known persistent classes. We're just checking here to
072: * make sure the queries can be executed without problem. Queries
073: * should always return all known instances in the database.
074: */
075: public void testBasicQueries() throws Exception {
076: for (Class c : impls.values()) {
077: for (PropertyDescriptor pd : Introspector.getBeanInfo(c)
078: .getPropertyDescriptors()) {
079:
080: if (pd.getWriteMethod() == null) // ignore read-only
081: continue;
082:
083: Set<String> queries = new TreeSet<String>();
084: getBasicQueries(queries, pd, "x.");
085:
086: StringBuilder str = new StringBuilder();
087:
088: // execute the individual queries
089: for (String query : queries) {
090: find(c, "where " + query);
091: str.append(str.length() > 0 ? " or " : "").append(
092: query);
093: }
094:
095: // now execute all the queries combined
096: find(c, "where " + str);
097: }
098: }
099: }
100:
101: void getBasicQueries(Set<String> queries, PropertyDescriptor pd,
102: String prefix) throws Exception {
103:
104: // max level of field traversal: 3
105: // ### if (prefix.split("\\.").length > 3)
106: if (prefix.split("\\.").length > 2)
107: return;
108:
109: Class type = pd.getPropertyType();
110:
111: String name = prefix + pd.getName();
112:
113: if (!queries.add(name + " is not null"))
114: return;
115:
116: queries.add(name + " is null");
117:
118: if (type.isAssignableFrom(Number.class) || type == int.class
119: || type == double.class || type == float.class
120: || type == long.class || type == short.class) {
121: queries.add(name + " = 0");
122: queries.add(name + " <> 0");
123: queries.add(name + " > 0");
124: queries.add(name + " < 0");
125: queries.add(name + " >= 0");
126: queries.add(name + " <= 0");
127: queries.add("sqrt(" + name + ") <> 0");
128: queries.add("abs(" + name + ") <> 0");
129: // queries.add("mod(" + name + ", 5) <> 0");
130: }
131:
132: if (type.isAssignableFrom(Collection.class)) {
133: queries.add(name + " IS EMPTY");
134: queries.add(name + " IS NOT EMPTY");
135: queries.add("size(" + name + ") <> 0");
136: }
137:
138: if (type.isAssignableFrom(String.class)) {
139: queries.add("lower(" + name + ") = 'x'");
140: queries.add("upper(" + name + ") = 'x'");
141: queries.add("concat(" + name + ", " + name + ") = 'x'");
142: queries.add("substring(" + name + ", 1, 2) = 'x'");
143: queries.add("length(" + name + ") > 0");
144: queries.add("locate(" + name + ", 'x', 1) > 0");
145: queries.add("trim(leading ' ' from " + name + ") = 'x'");
146: }
147:
148: if (type.isAssignableFrom(Date.class)) {
149: queries.add(name + " <> CURRENT_TIMESTAMP");
150: }
151:
152: // relation is an entity ... add all the relations
153: if (impls.containsKey(type) || impls.containsValue(type)) {
154: for (PropertyDescriptor desc : Introspector.getBeanInfo(
155: type).getPropertyDescriptors()) {
156:
157: if (desc.getWriteMethod() == null) // ignore read-only
158: continue;
159:
160: // prevent recursion
161: if (name.endsWith("." + desc.getName() + "."))
162: continue;
163:
164: getBasicQueries(queries, desc, name + ".");
165: }
166: }
167: }
168:
169: void checkModel() {
170: try {
171: verifyModel();
172: } catch (AssertionFailedError e) {
173: // clear all existing instances
174: clear(emf, impls.values().toArray(new Class[impls.size()]));
175:
176: // since the factory method needs to be static, we need to store
177: // the classes statically
178: factoryClasses = impls;
179: try {
180: final List<Exception> exceptions = new LinkedList<Exception>();
181: XMLDecoder decoder = new XMLDecoder(
182: CompanyModelTest.class
183: .getResourceAsStream("companies.xml"));
184: decoder.setExceptionListener(new ExceptionListener() {
185: public void exceptionThrown(Exception e) {
186: exceptions.add(e);
187: }
188: });
189: Collection obs = (Collection) decoder.readObject();
190:
191: if (exceptions.size() > 0) {
192: throw new IllegalStateException(exceptions.get(0));
193: }
194:
195: assertNotNull(obs);
196:
197: persist(obs.toArray());
198: } finally {
199: factoryClasses = null;
200: }
201: }
202:
203: verifyModel();
204: }
205:
206: int queryCount(Class c, String query, Object... params) {
207: return find(c, query, params).size();
208: }
209:
210: int queryCount(Class c) {
211: return find(c, null).size();
212: }
213:
214: Class impl(Class c) {
215: return impls.get(c);
216: }
217:
218: void verifyModel() {
219: assertEquals(2, queryCount(impl(ICompany.class)));
220: assertEquals(11, queryCount(impl(IAddress.class)));
221: assertEquals(3, queryCount(impl(IProduct.class)));
222: assertEquals(2, queryCount(impl(IProductOrder.class)));
223: assertEquals(3, queryCount(impl(ILineItem.class)));
224: assertEquals(1, queryCount(impl(IPartTimeEmployee.class)));
225: assertEquals(3, queryCount(impl(IFullTimeEmployee.class)));
226: assertEquals(4, queryCount(impl(ICustomer.class)));
227:
228: assertEquals(3, queryCount(impl(IAddress.class),
229: "where x.state = 'CA'"));
230:
231: assertEquals(1, queryCount(impl(ICompany.class),
232: "where size(x.employees) = 4"));
233: assertEquals(1, queryCount(impl(ICompany.class),
234: "where size(x.employees) = 0"));
235:
236: assertEquals(2, queryCount(impl(ICustomer.class),
237: "where size(x.orders) = 1"));
238:
239: assertEquals(1, queryCount(impl(IProductOrder.class),
240: "where x.shippedDate is null"));
241: assertEquals(1, queryCount(impl(IProductOrder.class),
242: "where x.shippedDate is not null"));
243:
244: assertEquals(1, queryCount(impl(IEmployee.class),
245: "where x.manager is null"));
246: assertEquals(2, queryCount(impl(IEmployee.class),
247: "where x.manager.manager is null"));
248: assertEquals(1, queryCount(impl(IEmployee.class),
249: "where x.manager.manager.manager is null"));
250:
251: assertEquals(2, queryCount(impl(IPerson.class),
252: "where x.firstName like ?1 and x.lastName like ?1",
253: "M%"));
254: assertEquals(1, queryCount(impl(IPerson.class),
255: "where x.homeAddress.state = 'CA'"));
256: }
257:
258: /**
259: * Factory method that is called from the serialized XML.
260: */
261: public static Object create(Class intf)
262: throws InstantiationException, IllegalAccessException {
263: return factoryClasses.get(intf).newInstance();
264: }
265: }
|