001: /*
002: * Copyright 2004 (C) TJDO.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the TJDO License version 1.0.
006: * See the terms of the TJDO License in the documentation provided with this software.
007: *
008: * $Id: StorageTestCase.java,v 1.7 2004/01/18 03:01:07 jackknifebarber Exp $
009: */
010:
011: package com.triactive.jdo.test;
012:
013: import java.util.Collection;
014: import java.util.HashSet;
015: import java.util.Iterator;
016: import java.util.Random;
017: import java.util.Set;
018: import javax.jdo.Extent;
019: import javax.jdo.JDOHelper;
020: import javax.jdo.PersistenceManager;
021: import javax.jdo.Transaction;
022: import org.apache.log4j.Category;
023:
024: /**
025: * Abstract base class for all JDO unit tests that perform tests on objects
026: * derived from {@link TestObject}. Provides functions to create, update,
027: * validate, and remove such objects.
028: *
029: * @author <a href="mailto:mmartin5@austin.rr.com">Mike Martin</a>
030: * @version $Revision: 1.7 $
031: */
032:
033: public abstract class StorageTestCase extends PersistenceTestCase {
034: private static final Category LOG = Category
035: .getInstance(StorageTestCase.class);
036:
037: protected static final int TEST_OBJECT_COUNT = 50;
038:
039: protected Object[] ids = new Object[TEST_OBJECT_COUNT];
040: protected TestObject[] objs = new TestObject[TEST_OBJECT_COUNT];
041:
042: /**
043: * Used by the JUnit framework to construct tests. Normally, programmers
044: * would never explicitly use this constructor.
045: *
046: * @param name Name of the <tt>TestCase</tt>.
047: */
048:
049: public StorageTestCase(String name) {
050: super (name);
051: }
052:
053: protected void runStorageTestFor(Class c) throws Exception {
054: insertObjects(c);
055: validateObjects(c);
056: updateObjects(c);
057: validateObjects(c);
058: iterateUsingExtent(c);
059: validateTransactionalRefresh(c);
060: removeObjects();
061: validateNewObjectRollback(c);
062: validateTransientTransactional(c);
063: }
064:
065: /**
066: * Asserts that the persistent fields of two test objects are equal using
067: * the <tt>compareTo()</tt> method. The <tt>equals()</tt> method cannot be
068: * used for this purpose because, for most persistence-capable objects
069: * (including all our test widgets), it only compares JDO identity.
070: *
071: * @param expected An object having the expected field values.
072: * @param actual The object to compare fields against.
073: *
074: * @see TestObject#compareTo
075: */
076:
077: protected void assertFieldsEqual(TestObject expected,
078: TestObject actual) {
079: assertTrue("Incorrect field values in object, was " + actual
080: + ", should be " + expected, actual.compareTo(expected));
081: }
082:
083: protected void assertResultsEqual(Set expected, Collection results) {
084: assertTrue("Query has no expected results (test is broken)",
085: !expected.isEmpty());
086: assertTrue("Query returned no rows", !results.isEmpty());
087:
088: HashSet actual = new HashSet(results);
089:
090: assertEquals("Query returned duplicate rows", results.size(),
091: actual.size());
092: assertEquals("Query did not return expected results", expected,
093: actual);
094: }
095:
096: protected void insertObjects(Class c) throws Exception {
097: /*
098: * Insert TEST_OBJECT_COUNT random objects.
099: */
100:
101: LOG.info("Inserting " + TEST_OBJECT_COUNT + " " + c.getName()
102: + " objects");
103: PersistenceManager pm = pmf.getPersistenceManager();
104: Transaction tx = pm.currentTransaction();
105:
106: try {
107: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
108: tx.begin();
109:
110: TestObject obj = (TestObject) c.newInstance();
111: obj.fillRandom();
112:
113: objs[i] = (TestObject) obj.clone();
114:
115: assertFieldsEqual(obj, objs[i]);
116:
117: pm.makePersistent(obj);
118:
119: ids[i] = JDOHelper.getObjectId(obj);
120:
121: tx.commit();
122: }
123: } finally {
124: if (tx.isActive())
125: tx.rollback();
126:
127: pm.close();
128: }
129: }
130:
131: protected void validateObjects(Class c) throws Exception {
132: /*
133: * Read them back and verify that they contain what they should.
134: */
135:
136: LOG.info("Validating " + TEST_OBJECT_COUNT + " " + c.getName()
137: + " objects:");
138: LOG.info(" Normal read");
139: PersistenceManager pm = pmf.getPersistenceManager();
140: Transaction tx = pm.currentTransaction();
141:
142: try {
143: TestObject[] loaded = new TestObject[TEST_OBJECT_COUNT];
144:
145: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
146: tx.begin();
147:
148: TestObject obj = (TestObject) pm.getObjectById(ids[i],
149: true);
150:
151: assertFieldsEqual(objs[i], obj);
152:
153: loaded[i] = obj;
154:
155: tx.commit();
156: }
157: } finally {
158: if (tx.isActive())
159: tx.rollback();
160:
161: pm.close();
162: }
163:
164: /*
165: * Read some of them back and verify them using non-transactional reads.
166: * Only some are done because non-transactional reads are much slower
167: * unless connection pooling is used (eventually we should use pooling
168: * when testing).
169: */
170:
171: LOG.info(" Non-transactional read");
172: pm = pmf.getPersistenceManager();
173: tx = pm.currentTransaction();
174:
175: try {
176: tx.setNontransactionalRead(true);
177:
178: for (int i = 0; i < TEST_OBJECT_COUNT; i += 10) {
179: TestObject obj = (TestObject) pm.getObjectById(ids[i],
180: false);
181:
182: assertFieldsEqual(objs[i], obj);
183: }
184: } finally {
185: pm.close();
186: }
187:
188: /*
189: * Read some of them back, verify them, then verify values get retained
190: * after commit when retainValues mode is on.
191: */
192:
193: LOG.info(" Retain values mode");
194: pm = pmf.getPersistenceManager();
195: tx = pm.currentTransaction();
196:
197: try {
198: tx.setRetainValues(true);
199: tx.begin();
200:
201: TestObject[] loaded = new TestObject[TEST_OBJECT_COUNT];
202:
203: for (int i = 0; i < TEST_OBJECT_COUNT; i += 10) {
204: TestObject obj = (TestObject) pm.getObjectById(ids[i],
205: true);
206:
207: assertFieldsEqual(objs[i], obj);
208:
209: loaded[i] = obj;
210: }
211:
212: tx.commit();
213:
214: for (int i = 0; i < TEST_OBJECT_COUNT; i += 10)
215: assertFieldsEqual(objs[i], loaded[i]);
216: } finally {
217: if (tx.isActive())
218: tx.rollback();
219:
220: pm.close();
221: }
222: }
223:
224: protected void updateObjects(Class c) throws Exception {
225: /*
226: * Update them all with new values.
227: */
228:
229: LOG.info("Updating " + TEST_OBJECT_COUNT + " " + c.getName()
230: + " objects");
231: PersistenceManager pm = pmf.getPersistenceManager();
232: Transaction tx = pm.currentTransaction();
233:
234: try {
235: /*
236: * Test basic update functionality by filling each object with new
237: * random data.
238: */
239: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
240: tx.begin();
241:
242: TestObject obj = (TestObject) pm.getObjectById(ids[i],
243: false);
244: obj.fillRandom();
245:
246: objs[i] = (TestObject) obj.clone();
247:
248: assertFieldsEqual(obj, objs[i]);
249:
250: tx.commit();
251: }
252:
253: if (Widget.class.isAssignableFrom(c)) {
254: /*
255: * Test updates where we write a default-fetch-group field
256: * before the default fetch group has been loaded.
257: * validateObjects() will ensure that the update does not get
258: * lost.
259: */
260: for (int i = 0; i < TEST_OBJECT_COUNT; i += 10) {
261: tx.begin();
262:
263: Widget w = (Widget) pm.getObjectById(ids[i], false);
264: byte b = w.setByteFieldRandom();
265: ((Widget) objs[i]).setByteField(b);
266:
267: tx.commit();
268: }
269: }
270: } finally {
271: if (tx.isActive())
272: tx.rollback();
273:
274: pm.close();
275: }
276: }
277:
278: protected void iterateUsingExtent(Class c) throws Exception {
279: /*
280: * Iterate over them using an Extent and verify that they're all
281: * returned.
282: */
283:
284: LOG.info("Iterating over " + TEST_OBJECT_COUNT + " "
285: + c.getName() + " objects with an Extent");
286: PersistenceManager pm = pmf.getPersistenceManager();
287: Transaction tx = pm.currentTransaction();
288:
289: try {
290: tx.begin();
291:
292: Extent extent = pm.getExtent(c, true);
293: Iterator ei = extent.iterator();
294:
295: try {
296: HashSet returned = new HashSet();
297:
298: while (ei.hasNext()) {
299: TestObject obj = (TestObject) ei.next();
300:
301: assertTrue(
302: "Object returned twice from Extent iterator: "
303: + obj, returned.add(obj));
304: }
305:
306: assertEquals(TEST_OBJECT_COUNT, returned.size());
307:
308: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
309: TestObject obj = (TestObject) pm.getObjectById(
310: ids[i], true);
311:
312: assertTrue(
313: "Object never returned from Extent iterator: "
314: + obj, returned.remove(obj));
315: }
316: } finally {
317: extent.close(ei);
318: }
319:
320: tx.commit();
321: } finally {
322: if (tx.isActive())
323: tx.rollback();
324:
325: pm.close();
326: }
327: }
328:
329: protected void validateTransactionalRefresh(Class c)
330: throws Exception {
331: /*
332: * Validate that persistent non-transactional objects transition to
333: * persistent, and refresh themselves, when accessed from within a
334: * transaction.
335: */
336:
337: LOG.info("Validating transactional refresh on "
338: + TEST_OBJECT_COUNT + " " + c.getName() + " objects");
339: PersistenceManager pm1 = pmf.getPersistenceManager();
340: Transaction tx1 = pm1.currentTransaction();
341: tx1.setRetainValues(true);
342:
343: try {
344: PersistenceManager pm2 = pmf.getPersistenceManager();
345: Transaction tx2 = pm2.currentTransaction();
346:
347: Random rnd = new Random(0);
348: TestObject[] pobjs = new TestObject[TEST_OBJECT_COUNT];
349:
350: try {
351: /* Load all of the objects using pm1. */
352: tx1.begin();
353:
354: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
355: /* Half will be Hollow and half PersistentClean. */
356: boolean validate = rnd.nextBoolean();
357:
358: pobjs[i] = (TestObject) pm1.getObjectById(ids[i],
359: validate);
360:
361: /* Half of the PersistentClean will be fully loaded. */
362: if (validate && rnd.nextBoolean())
363: assertFieldsEqual(objs[i], pobjs[i]);
364: }
365:
366: tx1.commit();
367:
368: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
369: assertTrue("Object is not persistent: " + ids[i],
370: JDOHelper.isPersistent(pobjs[i]));
371: assertTrue("Object is transactional: " + ids[i],
372: !JDOHelper.isTransactional(pobjs[i]));
373: }
374:
375: /* Modify them all using pm2. */
376: tx2.begin();
377:
378: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
379: TestObject obj = (TestObject) pm2.getObjectById(
380: ids[i], false);
381: obj.fillRandom();
382:
383: objs[i] = (TestObject) obj.clone();
384:
385: assertFieldsEqual(obj, objs[i]);
386: }
387:
388: tx2.commit();
389:
390: /* Access them all inside a transaction using pm1. */
391: tx1.begin();
392:
393: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
394: assertTrue("Object is not persistent: " + ids[i],
395: JDOHelper.isPersistent(pobjs[i]));
396: assertTrue("Object is transactional: " + ids[i],
397: !JDOHelper.isTransactional(pobjs[i]));
398:
399: assertFieldsEqual(objs[i], pobjs[i]);
400:
401: assertTrue("Object is not persistent: " + ids[i],
402: JDOHelper.isPersistent(pobjs[i]));
403: assertTrue(
404: "Object is not transactional: " + ids[i],
405: JDOHelper.isTransactional(pobjs[i]));
406: }
407:
408: tx1.commit();
409: } finally {
410: if (tx2.isActive())
411: tx2.rollback();
412:
413: pm2.close();
414: }
415: } finally {
416: if (tx1.isActive())
417: tx1.rollback();
418:
419: pm1.close();
420: }
421: }
422:
423: protected void removeObjects() throws Exception {
424: /*
425: * Remove all of the objects.
426: */
427:
428: LOG.info("Removing " + TEST_OBJECT_COUNT + " objects");
429: PersistenceManager pm = pmf.getPersistenceManager();
430: Transaction tx = pm.currentTransaction();
431:
432: try {
433: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
434: tx.begin();
435:
436: TestObject obj = (TestObject) pm.getObjectById(ids[i],
437: false);
438:
439: pm.deletePersistent(obj);
440:
441: tx.commit();
442: }
443: } finally {
444: if (tx.isActive())
445: tx.rollback();
446:
447: pm.close();
448: }
449: }
450:
451: protected void validateNewObjectRollback(Class c) throws Exception {
452: /*
453: * Create TEST_OBJECT_COUNT random objects, update them, rollback the
454: * transaction, and verify they return to being transient objects
455: * having their former values. Requires RestoreValues == true in order
456: * to get the restoration on rollback.
457: */
458:
459: LOG.info("Testing rollback of updates on " + TEST_OBJECT_COUNT
460: + " new " + c.getName() + " objects");
461: PersistenceManager pm = pmf.getPersistenceManager();
462: Transaction tx = pm.currentTransaction();
463: tx.setRestoreValues(true);
464:
465: try {
466: TestObject[] pobjs = new TestObject[TEST_OBJECT_COUNT];
467:
468: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
469: objs[i] = (TestObject) c.newInstance();
470: objs[i].fillRandom();
471:
472: pobjs[i] = (TestObject) objs[i].clone();
473: }
474:
475: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
476: tx.begin();
477:
478: pm.makePersistent(pobjs[i]);
479:
480: pobjs[i].fillRandom();
481:
482: tx.rollback();
483: }
484:
485: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
486: assertNull(JDOHelper.getPersistenceManager(pobjs[i]));
487: assertFieldsEqual(objs[i], pobjs[i]);
488: }
489: } finally {
490: if (tx.isActive())
491: tx.rollback();
492:
493: pm.close();
494: }
495: }
496:
497: protected void validateTransientTransactional(Class c)
498: throws Exception {
499: /*
500: * Create TEST_OBJECT_COUNT random objects, make them TransientClean,
501: * update them, rollback the transaction, and verify they return to
502: * being TransientClean objects having their former values. Requires
503: * RestoreValues == true in order to get the restoration on rollback.
504: */
505:
506: LOG.info("Testing rollback of updates on " + TEST_OBJECT_COUNT
507: + " transient transactional " + c.getName()
508: + " objects");
509: PersistenceManager pm = pmf.getPersistenceManager();
510: Transaction tx = pm.currentTransaction();
511: tx.setRestoreValues(true);
512:
513: try {
514: TestObject[] pobjs = new TestObject[TEST_OBJECT_COUNT];
515:
516: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
517: objs[i] = (TestObject) c.newInstance();
518: objs[i].fillRandom();
519:
520: pobjs[i] = (TestObject) objs[i].clone();
521: }
522:
523: tx.begin();
524:
525: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
526: TestObject obj = pobjs[i];
527:
528: assertFalse("Object should not be transactional: "
529: + obj, JDOHelper.isTransactional(obj));
530: assertFalse("Object should not be dirty: " + obj,
531: JDOHelper.isDirty(obj));
532:
533: pm.makeTransactional(obj);
534:
535: assertTrue("Object should be transactional: " + obj,
536: JDOHelper.isTransactional(obj));
537: assertFalse("Object should not be dirty: " + obj,
538: JDOHelper.isDirty(obj));
539:
540: obj.fillRandom();
541:
542: assertTrue("Object should be dirty: " + obj, JDOHelper
543: .isDirty(obj));
544: }
545:
546: tx.rollback();
547:
548: for (int i = 0; i < TEST_OBJECT_COUNT; ++i) {
549: TestObject obj = pobjs[i];
550:
551: assertTrue("Object should be transactional: " + obj,
552: JDOHelper.isTransactional(obj));
553: assertFalse("Object should not be dirty: " + obj,
554: JDOHelper.isDirty(obj));
555: assertFieldsEqual(objs[i], obj);
556:
557: pm.makeNontransactional(obj);
558:
559: assertNull(
560: "Object should have transitioned to an unmanaged transient",
561: JDOHelper.getPersistenceManager(obj));
562: }
563: } finally {
564: if (tx.isActive())
565: tx.rollback();
566:
567: pm.close();
568: }
569: }
570: }
|