You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pc...@apache.org on 2006/07/01 00:37:29 UTC

svn commit: r418401 [19/32] - in /incubator/openjpa/trunk: openjpa-lib/ openjpa-lib/src/main/java/org/apache/openjpa/lib/ant/ openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/ openjpa-lib/src/...

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/ResultListTest.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/ResultListTest.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/ResultListTest.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/ResultListTest.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.rop;
+
+import java.util.*;
+import org.apache.openjpa.lib.test.*;
+
+/**
+ * Tests for {@link ResultList} implementations.
+ * 
+ * @author Abe White
+ */
+public abstract class ResultListTest extends AbstractTestCase {
+    private ResultList[] _lists = null;
+
+    public ResultListTest(String test) {
+        super(test);
+    }
+
+    /**
+     * Return a result list to use with the given provider.
+     */
+    protected abstract ResultList getResultList(ResultObjectProvider provider);
+
+    /**
+     * Override to customize the result object provider being used. You
+     * can return multiple providers to test with each.
+     */
+    protected ResultObjectProvider[] getResultObjectProviders(List list) {
+        return new ResultObjectProvider[] {
+            new ListResultObjectProvider(list)
+        };
+    }
+
+    public void setUp() {
+        List results = new ArrayList(100);
+        for (int i = 0; i < 100; i++)
+            results.add(String.valueOf(i));
+        ResultObjectProvider[] rops = getResultObjectProviders(results);
+        _lists = new ResultList[rops.length];
+        for (int i = 0; i < _lists.length; i++)
+            _lists[i] = getResultList(rops[i]);
+    }
+
+    public void testIterator() {
+        for (int i = 0; i < _lists.length; i++) {
+            Iterator itr = _lists[i].iterator();
+            int count = 0;
+            for (; itr.hasNext(); count++)
+                assertEquals(String.valueOf(count), itr.next());
+            assertEquals(100, count);
+            try {
+                itr.next();
+                fail("After last.");
+            } catch (IndexOutOfBoundsException ioob) {
+            } catch (NoSuchElementException nse) {
+            }
+        }
+    }
+
+    public void testIteratorModification() {
+        for (int i = 0; i < _lists.length; i++) {
+            try {
+                Iterator itr = _lists[i].iterator();
+                itr.next();
+                itr.remove();
+                fail("Allowed modification.");
+            } catch (Exception e) {
+            }
+        }
+    }
+
+    public void testListIteratorForward() {
+        for (int i = 0; i < _lists.length; i++) {
+            ListIterator itr = _lists[i].listIterator();
+            int count = 0;
+            for (; itr.hasNext(); count++) {
+                assertEquals(count, itr.nextIndex());
+                assertEquals(String.valueOf(count), itr.next());
+            }
+            assertEquals(100, count);
+            try {
+                itr.next();
+                fail("After last.");
+            } catch (IndexOutOfBoundsException ioob) {
+            } catch (NoSuchElementException nse) {
+            }
+        }
+    }
+
+    public void testListIteratorIndex() {
+        for (int i = 0; i < _lists.length; i++) {
+            ListIterator itr = _lists[i].listIterator(50);
+            int count = 50;
+            for (; itr.hasNext(); count++) {
+                assertEquals(count, itr.nextIndex());
+                assertEquals(String.valueOf(count), itr.next());
+            }
+            assertEquals(100, count);
+            try {
+                itr.next();
+                fail("After last.");
+            } catch (IndexOutOfBoundsException ioob) {
+            } catch (NoSuchElementException nse) {
+            }
+        }
+    }
+
+    public void testListIteratorReverse() {
+        for (int i = 0; i < _lists.length; i++) {
+            ListIterator itr = _lists[i].listIterator(100);
+            int count = 99;
+            for (; itr.hasPrevious(); count--) {
+                assertEquals(count, itr.previousIndex());
+                assertEquals(String.valueOf(count), itr.previous());
+            }
+            assertEquals(-1, count);
+            try {
+                itr.previous();
+                fail("Before first.");
+            } catch (IndexOutOfBoundsException ioob) {
+            } catch (NoSuchElementException nse) {
+            }
+        }
+    }
+
+    public void testListIteratorModification() {
+        for (int i = 0; i < _lists.length; i++) {
+            try {
+                ListIterator itr = _lists[i].listIterator();
+                itr.next();
+                itr.set("foo");
+                fail("Allowed modification.");
+            } catch (Exception e) {
+            }
+        }
+    }
+
+    public void testMultipleIterations() {
+        testListIteratorIndex();
+        testListIteratorForward();
+        testListIteratorReverse();
+    }
+
+    public void testContains() {
+        for (int i = 0; i < _lists.length; i++) {
+            assertTrue(_lists[i].contains("0"));
+            assertTrue(_lists[i].contains("50"));
+            assertTrue(_lists[i].contains("99"));
+            assertFalse(_lists[i].contains("-1"));
+            assertFalse(_lists[i].contains("100"));
+            assertFalse(_lists[i].contains(null));
+            assertTrue(_lists[i].containsAll(Arrays.asList(new String[]
+                { "0", "50", "99" })));
+            assertFalse(_lists[i].containsAll(Arrays.asList(new String[]
+                { "0", "-1", "99" })));
+        }
+    }
+
+    public void testModification() {
+        for (int i = 0; i < _lists.length; i++) {
+            try {
+                _lists[i].add("foo");
+                fail("Allowed modification.");
+            } catch (UnsupportedOperationException uoe) {
+            }
+            try {
+                _lists[i].remove("1");
+                fail("Allowed modification.");
+            } catch (UnsupportedOperationException uoe) {
+            }
+            try {
+                _lists[i].set(0, "foo");
+                fail("Allowed modification.");
+            } catch (UnsupportedOperationException uoe) {
+            }
+        }
+    }
+
+    public void testGetBegin() {
+        for (int i = 0; i < _lists.length; i++) {
+            for (int j = 0; j < 10; j++)
+                assertEquals(String.valueOf(j), _lists[i].get(j));
+            try {
+                _lists[i].get(-1);
+                fail("Before begin.");
+            } catch (IndexOutOfBoundsException ioob) {
+            } catch (NoSuchElementException nse) {
+            }
+        }
+    }
+
+    public void testGetMiddle() {
+        for (int i = 0; i < _lists.length; i++)
+            for (int j = 50; j < 60; j++)
+                assertEquals(String.valueOf(j), _lists[i].get(j));
+    }
+
+    public void testGetEnd() {
+        for (int i = 0; i < _lists.length; i++) {
+            for (int j = 90; j < 100; j++)
+                assertEquals(String.valueOf(j), _lists[i].get(j));
+            try {
+                _lists[i].get(100);
+                fail("Past end.");
+            } catch (IndexOutOfBoundsException ioob) {
+            } catch (NoSuchElementException nse) {
+            }
+        }
+    }
+
+    public void testGetReverse() {
+        for (int i = 0; i < _lists.length; i++)
+            for (int j = 99; j > -1; j--)
+                assertEquals(String.valueOf(j), _lists[i].get(j));
+    }
+
+    public void testMultipleGet() {
+        testGetMiddle();
+        testGetBegin();
+        testGetEnd();
+
+        // take list size and traverse list to cache values if not already
+        for (int i = 0; i < _lists.length; i++)
+            _lists[i].size();
+        testListIteratorForward();
+
+        testGetMiddle();
+        testGetBegin();
+        testGetEnd();
+    }
+
+    public void testSize() {
+        for (int i = 0; i < _lists.length; i++)
+            assertTrue(_lists[i].size() == 100
+                || _lists[i].size() == Integer.MAX_VALUE);
+    }
+
+    public void testEmpty() {
+        ResultObjectProvider[] rops = getResultObjectProviders
+            (Collections.EMPTY_LIST);
+        for (int i = 0; i < rops.length; i++) {
+            ResultList list = getResultList(rops[i]);
+            assertEquals(0, list.size());
+            assertTrue(list.isEmpty());
+        }
+    }
+}

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestEagerResultList.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestEagerResultList.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestEagerResultList.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestEagerResultList.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.rop;
+
+import java.util.*;
+
+/**
+ * Tests the {@link EagerResultList}.
+ * 
+ * @author Abe White
+ */
+public class TestEagerResultList extends ResultListTest {
+    public TestEagerResultList(String test) {
+        super(test);
+    }
+
+    protected ResultList getResultList(ResultObjectProvider provider) {
+        return new EagerResultList(provider);
+    }
+
+    public static void main(String[] args) {
+        main();
+    }
+}

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestLazyForwardResultList.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestLazyForwardResultList.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestLazyForwardResultList.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestLazyForwardResultList.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.rop;
+
+import java.util.*;
+
+/**
+ * Tests the {@link LazyForwardResultList}.
+ * 
+ * @author Abe White
+ */
+public class TestLazyForwardResultList extends ResultListTest {
+    public TestLazyForwardResultList(String test) {
+        super(test);
+    }
+
+    protected ResultList getResultList(ResultObjectProvider provider) {
+        return new LazyForwardResultList(provider);
+    }
+
+    public static void main(String[] args) {
+        main();
+    }
+}

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestListResultList.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestListResultList.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestListResultList.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestListResultList.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.rop;
+
+import java.util.*;
+
+/**
+ * Tests the {@link ListResultList}.
+ * 
+ * @author Abe White
+ */
+public class TestListResultList extends ResultListTest {
+    public TestListResultList(String test) {
+        super(test);
+    }
+
+    protected ResultList getResultList(ResultObjectProvider provider) {
+        return new ListResultList(((ListResultObjectProvider) provider).
+            getDelegate());
+    }
+
+    public static void main(String[] args) {
+        main();
+    }
+}

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestMergedResultObjectProvider.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestMergedResultObjectProvider.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestMergedResultObjectProvider.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestMergedResultObjectProvider.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.rop;
+
+import java.util.*;
+
+/**
+ * Tests the {@link MergedResultObjectProvider}.
+ * 
+ * @author Abe White
+ */
+public class TestMergedResultObjectProvider extends ResultListTest {
+    public TestMergedResultObjectProvider(String test) {
+        super(test);
+    }
+
+    protected ResultList getResultList(ResultObjectProvider provider) {
+        return new WindowResultList(provider, 10);
+    }
+
+    protected ResultObjectProvider[] getResultObjectProviders(List list) {
+        // test 3 merges:
+        // 1. first rop empty,
+        // 2. neither rop empty
+        // 3. both rops empty
+        ResultObjectProvider[] merges = new ResultObjectProvider[3];
+        merges[0] = new MergedResultObjectProvider(new ResultObjectProvider[] {
+            new ListResultObjectProvider(Collections.EMPTY_LIST),
+            new ListResultObjectProvider(list), });
+
+        int mid = list.size() / 2;
+        List list1 = list.subList(0, mid);
+        List list2 = list.subList(mid, list.size());
+        merges[1] = new MergedResultObjectProvider(new ResultObjectProvider[] {
+            new ListResultObjectProvider(list1),
+            new ListResultObjectProvider(list2), });
+
+        merges[2] = new MergedResultObjectProvider(new ResultObjectProvider[] {
+            new ListResultObjectProvider(list),
+            new ListResultObjectProvider(Collections.EMPTY_LIST), });
+
+        return merges;
+    }
+
+    public static void main(String[] args) {
+        main();
+    }
+}

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestOrderedMergedResultObjectProvider.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestOrderedMergedResultObjectProvider.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestOrderedMergedResultObjectProvider.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestOrderedMergedResultObjectProvider.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.rop;
+
+import java.util.*;
+import org.apache.commons.collections.comparators.*;
+
+/**
+ * Tests the {@link MergedResultObjectProvider}.
+ * 
+ * @author Abe White
+ */
+public class TestOrderedMergedResultObjectProvider extends ResultListTest {
+    public TestOrderedMergedResultObjectProvider(String test) {
+        super(test);
+    }
+
+    protected ResultList getResultList(ResultObjectProvider provider) {
+        return new WindowResultList(provider, 10);
+    }
+
+    protected ResultObjectProvider[] getResultObjectProviders(List list) {
+        Collections.shuffle(list);
+        int quart = list.size() / 4;
+        List list1 = new ArrayList(list.subList(0, quart));
+        List list2 = new ArrayList(list.subList(quart, quart * 2));
+        List list3 = new ArrayList(list.subList(quart * 2, quart * 3));
+        List list4 = new ArrayList(list.subList(quart * 3, list.size()));
+
+        Comparator comp = new IntValueComparator();
+        Collections.sort(list1, comp);
+        Collections.sort(list2, comp);
+        Collections.sort(list3, comp);
+        Collections.sort(list4, comp);
+
+        ResultObjectProvider[] rops = new ResultObjectProvider[] {
+            new ListResultObjectProvider(list1),
+            new ListResultObjectProvider(list2),
+            new ListResultObjectProvider(list3),
+            new ListResultObjectProvider(list4), };
+        return new ResultObjectProvider[] {
+            new MergedResultObjectProvider(rops, comp)
+        };
+    }
+
+    public static void main(String[] args) {
+        main();
+    }
+
+    private static class IntValueComparator implements Comparator {
+        public int compare(Object o1, Object o2) {
+            return Integer.valueOf(o1.toString()).
+                compareTo(Integer.valueOf(o2.toString()));
+        }
+    }
+}

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestRandomAccessResultList.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestRandomAccessResultList.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestRandomAccessResultList.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestRandomAccessResultList.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.rop;
+
+import java.util.*;
+
+/**
+ * Tests the {@link RandomAccessResultList}.
+ * 
+ * @author Abe White
+ */
+public class TestRandomAccessResultList extends ResultListTest {
+    public TestRandomAccessResultList(String test) {
+        super(test);
+    }
+
+    protected ResultList getResultList(ResultObjectProvider provider) {
+        return new RandomAccessResultList(provider);
+    }
+
+    public static void main(String[] args) {
+        main();
+    }
+}

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestRangeResultObjectProvider.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestRangeResultObjectProvider.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestRangeResultObjectProvider.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestRangeResultObjectProvider.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.rop;
+
+import java.util.*;
+
+/**
+ * Tests the {@link RangeResultObjectProvider}.
+ * 
+ * @author Abe White
+ */
+public class TestRangeResultObjectProvider extends ResultListTest {
+    public TestRangeResultObjectProvider(String test) {
+        super(test);
+    }
+
+    protected ResultList getResultList(ResultObjectProvider provider) {
+        return new WindowResultList(provider, 10);
+    }
+
+    protected ResultObjectProvider[] getResultObjectProviders(List list) {
+        // test 3 ranges:
+        // 1. 0 to infinite
+        // 2. 0 to N
+        // 3. N to N + X
+        ResultObjectProvider[] ranges = new ResultObjectProvider[3];
+        ranges[0] = new RangeResultObjectProvider
+            (new ListResultObjectProvider(list), 0, Integer.MAX_VALUE);
+
+        List copy = new ArrayList(list.size() + 10);
+        copy.addAll(list);
+        for (int i = list.size(); i < list.size() + 10; i++)
+            copy.add(String.valueOf(i));
+        ranges[1] = new RangeResultObjectProvider
+            (new ListResultObjectProvider(copy), 0, list.size());
+
+        copy = new ArrayList(list.size() + 20);
+        for (int i = -10; i < 0; i++)
+            copy.add(String.valueOf(i));
+        copy.addAll(list);
+        for (int i = list.size(); i < list.size() + 10; i++)
+            copy.add(String.valueOf(i));
+        ranges[2] = new RangeResultObjectProvider
+            (new ListResultObjectProvider(copy), 10, list.size() + 10);
+
+        return ranges;
+    }
+
+    public static void main(String[] args) {
+        main();
+    }
+}

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestSimpleResultList.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestSimpleResultList.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestSimpleResultList.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestSimpleResultList.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.rop;
+
+import java.util.*;
+
+/**
+ * Tests the {@link SimpleResultList}.
+ * 
+ * @author Abe White
+ */
+public class TestSimpleResultList extends ResultListTest {
+    public TestSimpleResultList(String test) {
+        super(test);
+    }
+
+    protected ResultList getResultList(ResultObjectProvider provider) {
+        return new SimpleResultList(provider);
+    }
+
+    public static void main(String[] args) {
+        main();
+    }
+}

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestSoftRandomAccessResultList.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestSoftRandomAccessResultList.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestSoftRandomAccessResultList.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestSoftRandomAccessResultList.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.rop;
+
+import java.util.*;
+
+/**
+ * Tests the {@link SoftRandomAccessResultList}.
+ * 
+ * @author Abe White
+ */
+public class TestSoftRandomAccessResultList extends ResultListTest {
+    public TestSoftRandomAccessResultList(String test) {
+        super(test);
+    }
+
+    protected ResultList getResultList(ResultObjectProvider provider) {
+        return new SoftRandomAccessResultList(provider);
+    }
+
+    public static void main(String[] args) {
+        main();
+    }
+}

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestWindowResultList.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestWindowResultList.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestWindowResultList.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/rop/TestWindowResultList.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.rop;
+
+import java.util.*;
+
+/**
+ * Tests the {@link WindowResultList}.
+ * 
+ * @author Abe White
+ */
+public class TestWindowResultList extends ResultListTest {
+    public TestWindowResultList(String test) {
+        super(test);
+    }
+
+    protected ResultList getResultList(ResultObjectProvider provider) {
+        return new WindowResultList(provider, 10);
+    }
+
+    public static void main(String[] args) {
+        main();
+    }
+}

Added: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/test/AbstractTestCase.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/test/AbstractTestCase.java?rev=418401&view=auto
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/test/AbstractTestCase.java (added)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/test/AbstractTestCase.java Fri Jun 30 15:37:18 2006
@@ -0,0 +1,1502 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *  Licensed 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.openjpa.lib.test;
+
+import java.beans.*;
+import java.io.*;
+import java.lang.reflect.*;
+import java.math.*;
+import java.net.*;
+import java.text.*;
+import java.util.*;
+import junit.framework.*;
+import junit.textui.*;
+import org.apache.openjpa.lib.log.*;
+import org.apache.regexp.*;
+import org.apache.tools.ant.*;
+
+/**
+ * TestCase framework to run various tests against solarmetric code.
+ *  This class contains various utility methods for the following functions:
+ * <ul>
+ * <li>Using multiple, isolated ClassLoaders</li>
+ * <li>Running a test in multiple concurrent threads</li>
+ * <li>Assertion helpers</li>
+ * <li>Creating random Strings, numbers, etc.</li>
+ * </ul>
+ * 
+ * @author Marc Prud'hommeaux
+ * @author Patrick Linskey
+ */
+public abstract class AbstractTestCase extends TestCase {
+    public static final String TEST_METHODS =
+        System.getProperty(AbstractTestCase.class.getName() + ".testMethods");
+    public static final long PLATFORM_ALL = 2 << 1;
+    public static final long PLATFORM_UNKNOWN = 2 << 2;
+
+    public static final String SKIP_TOKEN = "SOLARSKIP";
+    public static final String SKIP_DELIMITER = "|";
+
+    protected String multiThreadExecuting = null;
+    protected boolean inTimeoutThread = false;
+
+    private Log log = null;
+
+    private static Map _times = new HashMap();
+
+    private static AbstractTestCase _lastTest = null;
+
+    private static WatchdogThread _watchdog = new WatchdogThread();
+    private long _timeout;
+
+    /**
+     * Constructor. Create a test case with the specified name.
+     */
+    public AbstractTestCase(String test) {
+        super(test);
+    }
+
+    public AbstractTestCase() {
+    }
+
+    protected final Log getLog() {
+        if (log == null)
+            log = newLog();
+        return log;
+    }
+
+    protected Log newLog() {
+        // this implementation leaves much to be desired, as it just
+        // creates a new LogFactoryImpl each time, and does not apply
+        // any configurations.
+        return new LogFactoryImpl().getLog(getLogName());
+    }
+
+    protected String getLogName() {
+        return "com.solarmetric.Runtime";
+    }
+
+    /**
+     * Called before the watchdog thread is about to kill the entire
+     * JVM due to a test case's timeout. This method offers the
+     * ability to try to resolve whatever contention is taking place
+     * in the test. It will be given 10 seconds to try to end the
+     * test peacefully before the watchdog exits the JVM.
+     */
+    protected void preTimeout() {
+    }
+
+    public void run(TestResult result) {
+        if (skipTest()) {
+            // keep track of the tests we skip so that we can get an
+            // idea in the autobuild status
+            System.err.println(SKIP_TOKEN + SKIP_DELIMITER
+                + ("" + getClass().getName())
+                + "." + getName() + SKIP_DELIMITER);
+            return;
+        }
+
+        if (_lastTest != null && _lastTest.getClass() != getClass()) {
+            try {
+                _lastTest.tearDownTestClass();
+            } catch (Throwable t) {
+                getLog().error(t);
+            }
+        }
+
+        if (_lastTest == null || _lastTest.getClass() != getClass()) {
+            try {
+                setUpTestClass();
+            } catch (Throwable t) {
+                getLog().error(t);
+            }
+        }
+
+        _lastTest = this;
+
+        // inform the watchdog thread that we are entering the test
+        _watchdog.enteringTest(this);
+        try {
+            super.run(result);
+        }
+        finally {
+            _watchdog.leavingTest(this);
+        }
+    }
+
+    /**
+     * If this test should be skipped given the current
+     * environment, return <code>true</code>. This allows a unit test
+     * class to disable test cases on a per-method granularity, and
+     * prevents the test from showing up as a passed test just
+     * because it was skipped.
+     *  For example, if a particular test case method should not be
+     * run against a certain database, this method could check the
+     * name of the test result and the current database configuration
+     * in order to make the decision:
+     * 
+     * <code> protected boolean skipTest() {
+ // don't run with pointbase: it uses a DataSource, which
+ // can't be translated into a JBoss DataSource configuration.
+ if ("testJBoss".equals(getName()) &&
+ getCurrentPlatform() == PLATFORM_POINTBASE)
+ return true;
+ }
+     * </code>
+     *  If you want to disable execution of an entire test case
+     * class for a given database, you might want to add the class to
+     * the excluded test list in that database's properties file.
+     */
+    protected boolean skipTest() {
+        if (TEST_METHODS != null && TEST_METHODS.length() > 0)
+            return TEST_METHODS.indexOf(getName()) == -1;
+
+        return false;
+    }
+
+    /**
+     * This method is called before the first test in this test class
+     * is executed.
+     */
+    public void setUpTestClass() throws Exception {
+    }
+
+    /**
+     * This method is called after the last test in this test class
+     * is executed. It can be used to do things like clean up
+     * large, slow processes that may have been started.
+     */
+    public void tearDownTestClass() throws Exception {
+    }
+
+
+    public void tearDown() throws Exception {
+        if ("true".equals(System.getProperty("meminfo", "true")))
+            printMemoryInfo();
+
+        super.tearDown();
+    }
+
+    //////////////////////////
+    // Generating random data
+    //////////////////////////
+
+    /**
+     * Support method to get a random Integer for testing.
+     */
+    public static Integer randomInt() {
+        return new Integer((int)(Math.random() * Integer.MAX_VALUE));
+    }
+
+    /**
+     * Support method to get a random Character for testing.
+     */
+    public static Character randomChar() {
+        char [] TEST_CHAR_ARRAY = new char [] {
+            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+            'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
+            's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1',
+            '2', '3', '4', '5', '6', '7', '8', '9' };
+
+        return new Character(TEST_CHAR_ARRAY [
+            (int)(Math.random() * TEST_CHAR_ARRAY.length)]);
+    }
+
+    /**
+     * Support method to get a random Long for testing.
+     */
+    public static Long randomLong() {
+        return new Long((long)(Math.random() * Long.MAX_VALUE));
+    }
+
+    /**
+     * Support method to get a random Short for testing.
+     */
+    public static Short randomShort() {
+        return new Short((short)(Math.random() * Short.MAX_VALUE));
+    }
+
+    /**
+     * Support method to get a random Double for testing.
+     */
+    public static Double randomDouble() {
+        return new Double((double)(Math.round(Math.random() * 5000d))/1000d);
+    }
+
+    /**
+     * Support method to get a random Float for testing.
+     */
+    public static Float randomFloat() {
+        return new Float((float)(Math.round(Math.random() * 5000f))/1000f);
+    }
+
+    /**
+     * Support method to get a random Byte for testing.
+     */
+    public static Byte randomByte() {
+        return new Byte((byte)(Math.random() * Byte.MAX_VALUE));
+    }
+
+    /**
+     * Support method to get a random Boolean for testing.
+     */
+    public static Boolean randomBoolean() {
+        return new Boolean(Math.random() > 0.5 ? true : false);
+    }
+
+    /**
+     * Support method to get a random Date for testing.
+     */
+    public static Date randomDate() {
+        long millis = (long)(Math.random() * System.currentTimeMillis());
+
+        // round millis to the nearest 1000: this is because some
+        // databases do not store the milliseconds correctly(e.g., MySQL).
+        // This is a really a bug we should fix. FC #27.
+        millis -= (millis % 1000);
+
+        return new Date(millis);
+    }
+
+    /**
+     * Support method to get a random String for testing.
+     */
+    public static String randomString() {
+        // default to a small string, in case column sizes are
+        // limited(such as with a string primary key)
+        return randomString(50);
+    }
+
+    /**
+     * Support method to get a random String for testing.
+     */
+    public static String randomString(int len) {
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < (int)(Math.random() * len) + 1; i++)
+            buf.append(randomChar());
+        return buf.toString();
+    }
+
+    /**
+     * Support method to get a random clob for testing.
+     */
+    public static String randomClob() {
+        StringBuffer sbuf = new StringBuffer();
+        while (sbuf.length() < (5 * 1024)) { // at least 5K
+            sbuf.append(randomString(1024));
+        }
+
+        return sbuf.toString();
+    }
+
+    /**
+     * Support method to get a random BigInteger for testing.
+     */
+    public static BigInteger randomBigInteger() {
+        // too many of our test databases don't support bigints > MAX_LONG:
+        // I don't like it, but for now, let's only test below MAX_LONG
+        BigInteger lng = new BigInteger(
+            ((long)(Math.random() * Long.MAX_VALUE)) + "");
+
+        BigInteger multiplier = new BigInteger("1");
+            // (1 + (int)(Math.random() * 10000)) + "");
+        if (Math.random() < 0.5)
+            multiplier = multiplier.multiply(new BigInteger("-1"));
+
+        return lng.multiply(multiplier);
+    }
+
+    /**
+     * Support method to get a random BigDecimal for testing.
+     */
+    public static BigDecimal randomBigDecimal() {
+        BigInteger start = randomBigInteger();
+        String str = start.toString();
+        // truncate off the last 8 digits: we still get some
+        // overflows with lame databases.
+        for (int i = 0; i < 8; i++)
+            if (str.length() > 2)
+                str = str.substring(0, str.length() - 1);
+        start = new BigInteger(str);
+
+        String val = start + "." + ((int)(Math.random() * 10))
+            + ((int)(Math.random() * 10))
+            + ((int)(Math.random() * 10))
+            + ((int)(Math.random() * 10))
+            + ((int)(Math.random() * 10))
+            + ((int)(Math.random() * 10))
+            + ((int)(Math.random() * 10))
+            + ((int)(Math.random() * 10))
+            + ((int)(Math.random() * 10))
+            + ((int)(Math.random() * 10));
+
+        return new BigDecimal(val);
+    }
+
+    /**
+     * Support method to get a random blob for testing.
+     */
+    public static byte[] randomBlob() {
+        // up to 100K blob
+        byte [] blob = new byte [(int)(Math.random() * 1024 * 100)];
+        for (int i = 0; i < blob.length; i++)
+            blob [i] = randomByte().byteValue();
+
+        return blob;
+    }
+
+    /**
+     * Invoke setters for pimitives and primitive wrappers on the
+     * specified object.
+     */
+    public static Object randomizeBean(Object bean)
+        throws IntrospectionException, IllegalAccessException,
+            InvocationTargetException {
+        BeanInfo info = Introspector.getBeanInfo(bean.getClass());
+        PropertyDescriptor [] props = info.getPropertyDescriptors();
+        for (int i = 0 ; i < props.length; i++) {
+            Method write = props [i].getWriteMethod();
+            if (write == null)
+                continue;
+
+            Class [] params = write.getParameterTypes();
+            if (params == null || params.length != 1)
+                continue;
+
+            Class paramType = params [0];
+            Object arg = null;
+
+            if (paramType == boolean.class || paramType == Boolean.class)
+                arg = randomBoolean();
+            else if (paramType == byte.class || paramType == Byte.class)
+                arg = randomByte();
+            else if (paramType == char.class || paramType == Character.class)
+                arg = randomChar();
+            else if (paramType == short.class || paramType == Short.class)
+                arg = randomShort();
+            else if (paramType == int.class || paramType == Integer.class)
+                arg = randomInt();
+            else if (paramType == long.class || paramType == Long.class)
+                arg = randomLong();
+            else if (paramType == double.class || paramType == Double.class)
+                arg = randomDouble();
+            else if (paramType == float.class || paramType == Float.class)
+                arg = randomFloat();
+            else if (paramType == String.class)
+                arg = randomString();
+            else if (paramType == BigInteger.class)
+                arg = randomBigInteger();
+            else if (paramType == BigDecimal.class)
+                arg = randomBigDecimal();
+            else if (paramType == Date.class)
+                arg = randomDate();
+
+            if (arg != null)
+                write.invoke(bean, new Object [] { arg });
+        }
+
+        return bean;
+    }
+
+    ///////////////////
+    // Multi threading
+    ///////////////////
+
+    /**
+     * Re-execute the invoking method a random number of times
+     * in a random number of Threads.
+     */
+    public void mttest() throws ThreadingException {
+        // 6 iterations in 8 threads is a good trade-off between
+        // tests taking way too long and having a decent chance of
+        // identifying MT problems.
+        int iterations = 6;
+        int threads = 8;
+
+        mttest(threads, iterations);
+    }
+
+    /**
+     * Execute the calling method <code>iterations</code>
+     * times in <code>threads</code> Threads.
+     */
+    public void mttest(int threads, int iterations) {
+        mttest(0, threads, iterations);
+    }
+
+    public void mttest(int serialCount, int threads, int iterations)
+        throws ThreadingException {
+        String methodName = callingMethod("mttest");
+        mttest(serialCount, threads, iterations, methodName, new Object [0]);
+    }
+
+    /**
+     * Execute a test method in multiple threads.
+     * 
+     * @throws ThreadingException if an errors occur in
+     * any of the Threads. The actual exceptions
+     * will be embedded in the exception. Note that
+     * this means that assert() failures will be
+     * treated as errors rather than warnings.
+     * 
+     * @param thread the number of Threads to run in
+     * @param iterations the number of times the method should
+     * be execute in a single Thread
+     * @param method the name of the method to execute
+     * @param args the arguments to pass to the method
+     * 
+     * @author Marc Prud'hommeaux
+     */
+    public void mttest(int threads, int iterations, final String method,
+        final Object [] args) throws ThreadingException {
+        mttest(0, threads, iterations, method, args);
+    }
+
+    public void mttest(int serialCount,
+        int threads, int iterations, final String method, final Object [] args)
+        throws ThreadingException {
+        if (multiThreadExecuting != null && multiThreadExecuting.equals(method)) {
+            // we are currently executing in multi-threaded mode:
+            // don't deadlock!
+            return;
+        }
+
+        multiThreadExecuting = method;
+
+        try {
+            Class [] paramClasses = new Class [args.length];
+            for (int i = 0; i < paramClasses.length; i++)
+                paramClasses [i] = args [i].getClass();
+
+            final Method meth;
+
+            try {
+                meth = getClass().getMethod( method, paramClasses);
+            } catch (NoSuchMethodException nsme) {
+                throw new ThreadingException(nsme.toString(), nsme);
+            }
+
+            final Object thiz = this;
+
+            mttest("reflection invocation: (" + method + ")",
+                serialCount, threads, iterations, new VolatileRunnable() {
+                public void run() throws Exception {
+                    meth.invoke(thiz, args);
+                }
+            });
+        }
+        finally {
+            multiThreadExecuting = null;
+        }
+    }
+
+    public void mttest(String title, final int threads, final int iterations,
+        final VolatileRunnable runner) throws ThreadingException {
+        mttest(title, 0, threads, iterations, runner);
+    }
+
+    /**
+     * Execute a test method in multiple threads.
+     * 
+     * @throws ThreadingException if an errors occur in
+     * any of the Threads. The actual exceptions
+     * will be embedded in the exception. Note that
+     * this means that assert() failures will be
+     * treated as errors rather than warnings.
+     * 
+     * @param title a description of the test, for inclusion in the
+     * error message
+     * @param serialCount the number of times to run the method
+     * serially before spawning threads.
+     * @param thread the number of Threads to run in
+     * @param iterations the number of times the method should
+     * @author Marc Prud'hommeaux be execute in a single Thread
+     * @param runnner the VolatileRunnable that will execute
+     * the actual test from within the Thread.
+     * 
+     * @author Marc Prud'hommeaux
+     */
+    public void mttest(String title, final int serialCount,
+        final int threads, final int iterations, final VolatileRunnable runner)
+        throws ThreadingException {
+        final List exceptions = Collections.synchronizedList( new LinkedList());
+
+        Thread [] runners = new Thread [threads];
+
+        final long startMillis = System.currentTimeMillis() + 1000;
+
+        for (int i = 1; i <= threads; i++) {
+            final int thisThread = i;
+
+            runners [i - 1] =
+                new Thread(title + " [" + i + " of " + threads + "]") {
+                public void run() {
+                    // do our best to have all threads start at the exact
+                    // same time. This is imperfect, but the closer we
+                    // get to everyone starting at the same time, the
+                    // better chance we have for identifying MT problems.
+                    while (System.currentTimeMillis() < startMillis)
+                        yield();
+
+                    int thisIteration = 1;
+                    try {
+                        for (; thisIteration <= iterations; thisIteration++) {
+                            // go go go!
+                            runner.run();
+                        }
+                    } catch (Throwable error) {
+                        synchronized (exceptions) {
+                            // embed the exception into something that gives
+                            // us some more information about the threading
+                            // environment
+                            exceptions.add(new ThreadingException( "thread="
+                                + this.toString()
+                                + ";threadNum=" + thisThread
+                                + ";maxThreads=" + threads
+                                + ";iteration=" + thisIteration
+                                + ";maxIterations=" + iterations, error));
+                        }
+                    }
+                }
+            };
+        }
+
+        // start the serial tests(does not spawn the threads)
+        for (int i = 0; i < serialCount; i++) {
+            runners [0].run();
+        }
+
+        // start the multithreaded
+        for (int i = 0; i < threads; i++) {
+            runners [i].start();
+        }
+
+        // wait for them all to complete
+        for (int i = 0; i < threads; i++) {
+            try {
+                runners [i].join();
+            } catch (InterruptedException e) {
+            }
+        }
+
+        if (exceptions.size() == 0)
+            return; // sweeeeeeeet: no errors
+
+        // embed all the exceptions that were throws into a
+        // ThreadingException
+        Throwable [] errors = (Throwable [])exceptions.toArray(
+            new Throwable [0]);
+        throw new ThreadingException("The "
+            + errors.length + " embedded errors "
+            + "occured in the execution of " + iterations + " iterations "
+            + "of " + threads + " threads: [" + title + "]", errors);
+    }
+
+    /**
+     * Check to see if we are in the top-level execution stack.
+     */
+    public boolean isRootThread() {
+        return multiThreadExecuting == null;
+    }
+
+    /**
+     * Return the last method name that called this one by
+     * parsing the current stack trace.
+     * 
+     * @param exclude a method name to skip
+     * 
+     * @throws IllegalStateException If the calling method could not be
+     * identified.
+     * 
+     * @author Marc Prud'hommeaux
+     */
+    public String callingMethod(String exclude) {
+        // determine the currently executing method by
+        // looking at the stack track. Hackish, but convenient.
+        StringWriter sw = new StringWriter();
+        new Exception().printStackTrace(new PrintWriter(sw));
+        for (StringTokenizer stackTrace = new StringTokenizer(sw.toString(),
+            System.getProperty("line.separator"));
+            stackTrace.hasMoreTokens(); ) {
+            String line = stackTrace.nextToken().trim();
+
+            // not a stack trace element
+            if (!(line.startsWith("at ")))
+                continue;
+
+            String fullMethodName = line.substring(0, line.indexOf("("));
+
+            String shortMethodName = fullMethodName.substring(
+                fullMethodName.lastIndexOf(".") + 1);
+
+            // skip our own methods!
+            if (shortMethodName.equals("callingMethod"))
+                continue;
+            if (exclude != null && shortMethodName.equals(exclude))
+                continue;
+
+            return shortMethodName;
+        }
+
+        throw new IllegalStateException("Could not identify calling "
+            + "method in stack trace");
+    }
+
+    /**
+     * A Runnable that can throw an Exception: used to test cases.
+     */
+    public static interface VolatileRunnable {
+        public void run() throws Exception;
+    }
+
+    /**
+     * Exception for errors caught during threading tests.
+     */
+    public class ThreadingException extends RuntimeException {
+        private final Throwable[] _nested;
+
+        public ThreadingException(String msg, Throwable nested) {
+            super(msg);
+            if (nested == null)
+                _nested = new Throwable[0];
+            else
+                _nested = new Throwable[] { nested };
+        }
+
+        public ThreadingException(String msg, Throwable[] nested) {
+            super(msg);
+            if (nested == null)
+                _nested = new Throwable[0];
+            else
+                _nested = nested;
+        }
+
+        public void printStackTrace() {
+            printStackTrace(System.out);
+        }
+
+        public void printStackTrace(PrintStream out) {
+            printStackTrace(new PrintWriter(out));
+        }
+
+        public void printStackTrace(PrintWriter out) {
+            super.printStackTrace(out);
+            for (int i = 0; i < _nested.length; i++) {
+                out.print("Nested Throwable #" + (i + 1) + ": ");
+                _nested[i].printStackTrace(out);
+            }
+        }
+    }
+
+    //////////
+    // Timing
+    //////////
+
+    /**
+     * Sleep the current Thread for a random amount of time from 0-1000 ms.
+     */
+    public void sleepRandom() {
+        sleepRandom(1000);
+    }
+
+    /**
+     * Sleep the current Thread for a random amount of time from
+     * 0-<code>max</code> ms.
+     */
+    public void sleepRandom(int max) {
+        try {
+            Thread.currentThread().sleep((long)(Math.random() * max));
+        } catch (InterruptedException ex) {}
+    }
+
+    /**
+     * Re-run this method in the current thread, timing out
+     * after the specified number of seconds.
+     *  Usage:
+     * <pre> public void timeOutOperation() { if (timeout(5 * 1000)) return;
+     *  Thread.currentThread().sleep(10 * 1000); }
+     * </pre>
+     * 
+     * 
+     * <strong>Warning</strong> this method should be used sparingly,
+     * and only when you expect that a timeout will <strong>not</strong>
+     * occur. It utilized the deprecated {@link Thread.stop} and
+     * {@link Thread.interrupt} methods, which can leave monitors in an
+     * invalid state. It is only used because it provides more
+     * meaningful information than just seeing that the entire autobuild
+     * timed out.
+     * 
+     * @param millis the number of milliseconds we should wait.
+     * @return true if we are are in the thread that requested the
+     * timeout, false if we are in the timeout thread itself.
+     */
+    public boolean timeout(long millis) throws Throwable {
+        String methodName = callingMethod("timeout");
+        return timeout(millis, methodName);
+    }
+
+    /**
+     * @see timeout(long)
+     */
+    public boolean timeout(long millis, String methodName) throws Throwable {
+        // we are in the timing out-thread: do nothing so the
+        // actual test method can run
+        if (inTimeoutThread)
+            return false;
+
+        inTimeoutThread = true;
+        long endTime = System.currentTimeMillis() + millis;
+
+        try {
+            final Method method = getClass().
+                getMethod(methodName, (Class[])null);
+            final Object thz = this;
+
+            // spawn thread
+            TimeOutThread tot = new TimeOutThread("TimeOutThread ["
+                + methodName + "] (" + millis + "ms)") {
+                public void run() {
+                    try {
+                        method.invoke(thz, (Object[])null);
+                    } catch (Throwable t) {
+                        throwable = t;
+                    }
+                    finally {
+                        completed = true;
+                    }
+                }
+            };
+
+            tot.start();
+
+            // wait for the completion or a timeout to occur
+            tot.join(millis);
+
+            // have we timed out? Kill the thread and throw an exception
+            if (System.currentTimeMillis() >= endTime) {
+                // if we are waiting on a monitor, this will give
+                // us a useful stack trace.
+                try { tot.interrupt(); } catch (Throwable e) { }
+                Thread.currentThread().sleep(500);
+
+                // try to kill the thread
+                try { tot.stop(); } catch (Throwable e) { }
+                Thread.currentThread().sleep(500);
+
+                throw new OperationTimedOutException("Execution of \""
+                    + methodName + "\" timed out after "
+                    + millis + " milliseconds", tot.throwable);
+            }
+
+            // throw any exceptions that may have occured
+            if (tot.throwable != null)
+                throw tot.throwable;
+
+            // I guess everything was OK
+            return true;
+        }
+        finally {
+            inTimeoutThread = false;
+        }
+    }
+
+    /**
+     * Utility method to start a profile.
+     * 
+     * @see #endProfile(java.lang.String)
+     */
+    public void startProfile(String name) {
+        _times.put(name, new Long(System.currentTimeMillis()));
+    }
+
+    /**
+     * Utility to end the profile and print out the time. Example usage:
+     * 
+     * <pre><code> startProfile("Some long task"); doSomeLongTask();
+     * endProfile("Some long task");
+     * </code></pre>
+     * 
+     * @return the amount of time that this profile invocation took, or
+     * -1 if <code>name</code> was never started.
+     * 
+     * @param name
+     */
+    public long endProfile(String name) {
+        Long time = (Long)_times.remove(name);
+
+        long elapsed = -1;
+        if (time != null)
+            elapsed = System.currentTimeMillis() - time.longValue();
+
+        getLog().info(name + ": " + (time == null ? "???" : "" + elapsed) + "ms");
+        return elapsed;
+    }
+
+    private static class TimeOutThread extends Thread {
+        public Throwable throwable = null;
+        public boolean completed = false;
+
+        public TimeOutThread(String name) {
+            super(name);
+            setDaemon(true);
+        }
+    }
+
+    /**
+     * Indicates that a timeout occured.
+     */
+    public static class OperationTimedOutException extends RuntimeException {
+        private final Throwable _err;
+
+        public OperationTimedOutException(String msg, Throwable throwable) {
+            super(msg);
+            _err = throwable;
+        }
+
+        public void printStackTrace() {
+            printStackTrace(System.out);
+        }
+
+        public void printStackTrace(PrintStream out) {
+            printStackTrace(new PrintWriter(out));
+        }
+
+        public void printStackTrace(PrintWriter out) {
+            super.printStackTrace(out);
+            if (_err != null) {
+                out.print("Nested Throwable: ");
+                _err.printStackTrace(out);
+            }
+        }
+    }
+
+    /////////////////////////
+    // ClassLoader functions
+    /////////////////////////
+
+    /**
+     * Create a ClassLoader that will not use the parent
+     * ClassLoader to resolve classes. This is useful for
+     * testing interactions between Kodo in running
+     * in ClassLoaderA and instances in ClassLoaderB.
+     */
+    public ClassLoader createIsolatedClassLoader() {
+        return new IsolatedClassLoader();
+    }
+
+    public NestedClassLoader createNestedClassLoader() {
+        return new NestedClassLoader(false);
+    }
+
+    public NestedClassLoader createNestedParentClassLoader() {
+        return new NestedClassLoader(true);
+    }
+
+    /**
+     * Reload the specified class in an isolated ClassLoader.
+     * 
+     * @param target the target class to load
+     * @return the Class as reloaded in an new ClassLoader
+     */
+    public Class isolate(Class target) throws ClassNotFoundException {
+        Class result = isolate(target.getName());
+        assertTrue(result != target);
+        assertNotEquals(result, target);
+        assertTrue(result.getClassLoader() != target.getClassLoader());
+        return result;
+    }
+
+    public Class isolate(String target) throws ClassNotFoundException {
+        ClassLoader il = createIsolatedClassLoader();
+        Class result = il.loadClass(target);
+        assertEquals(result.getName(), target);
+
+        return result;
+    }
+
+    public Class nest(Class target) throws ClassNotFoundException {
+        ClassLoader il = createNestedClassLoader();
+        Class result = il.loadClass(target.getName());
+        assertTrue(result != target);
+        assertNotEquals(result, target);
+        assertTrue(result.getClassLoader() != target.getClassLoader());
+        assertEquals(result.getName(), target.getName());
+
+        return result;
+    }
+
+    public Object isolateNew(Class target)
+        throws ClassNotFoundException, IllegalAccessException,
+            InstantiationException {
+        return isolate(target).newInstance();
+    }
+
+    private static class NestedClassLoader extends AntClassLoader {
+        public NestedClassLoader(boolean useParent) {
+            super(ClassLoader.getSystemClassLoader(), useParent);
+
+            for (StringTokenizer cltok = new StringTokenizer(
+                System.getProperty("java.class.path"), File.pathSeparator);
+                cltok.hasMoreTokens(); ) {
+                String path = cltok.nextToken();
+
+                // only load test paths, not jar files
+                if (path.indexOf(".jar") != -1)
+                    continue;
+                if (path.indexOf(".zip") != -1)
+                    continue;
+
+                addPathElement(path);
+            }
+
+            try {
+                if (!useParent) {
+                    assertTrue(loadClass
+                        (AbstractTestCase.class.getName()).getClassLoader()
+                        != AbstractTestCase.class.getClassLoader());
+                }
+            } catch (ClassNotFoundException cnfe) {
+                fail(cnfe.toString());
+            }
+        }
+
+        public Class findClass(String name) throws ClassNotFoundException {
+            // don't isolate PC and related classes in kodo.enhnace
+            if (name.indexOf(".enhance.") != -1)
+                throw new ClassNotFoundException(name);
+            if (name.indexOf("/enhance/") != -1)
+                throw new ClassNotFoundException(name);
+            return super.findClass(name);
+        }
+    }
+
+    /**
+     * A ClassLoader that is completely isolated with respect to
+     * any classes that are loaded in the System ClassLoader.
+     * 
+     * @author <a href="mailto:marc@solarmetric.com">Marc Prud'hommeaux</a>
+     */
+    private static class IsolatedClassLoader extends NestedClassLoader {
+        public IsolatedClassLoader() {
+            super(false);
+            setIsolated(false);
+        }
+
+    }
+
+    ///////////////
+    // Collections
+    ///////////////
+
+    /**
+     * Validate that the specified {@link Collection} fulfills the
+     * Collection contract as specified by the Collections API.
+     * 
+     * <strong>Note</strong>: does not validate mutable operations
+     */
+    public static void validateCollection(Collection collection) {
+        int size = collection.size();
+        int iterated = 0;
+        // ensure we can walk along the iterator
+        for (Iterator i = collection.iterator(); i.hasNext(); ) {
+            iterated++;
+            i.next();
+        }
+
+        // ensure the number of values iterated is the same as the list size
+        assertEquals(size, iterated);
+
+        // also validate the list
+        if (collection instanceof List) {
+            List ll = new ArrayList();
+            for (int i = 0; i < 100; i++)
+                ll.add(new Integer(i));
+            validateList((List)ll);
+            validateList((List)collection);
+        }
+    }
+
+    /**
+     * Validate that the specified {@link List} fulfills the
+     * List contract as specified by the Collections API.
+     * 
+     * <strong>Note</strong>: does not validate mutable operations
+     */
+    public static void validateList(List list) {
+        Object [] coreValues = list.toArray();
+        Object [] values1 = new Object [list.size()];
+        Object [] values2 = new Object [list.size()];
+        Object [] values3 = new Object [list.size()];
+        Object [] values4 = new Object [list.size()];
+
+        // fill sequential index access list
+        for (int i = 0; i < list.size(); i++)
+            values1 [i] = list.get(i);
+
+        // fill sequential list
+        int index = 0;
+        ListIterator iter;
+        for (iter = list.listIterator(0); iter.hasNext(); ) {
+            assertEquals(index, iter.nextIndex());
+            assertEquals(index, iter.previousIndex() + 1);
+            values2 [index] = iter.next();
+            assertTrue(list.contains(values2 [index]));
+            index++;
+        }
+
+        // ensure NoSuchElementException is thrown as appropriate
+        try {
+            iter.next();
+            fail("next() should have resulted in a NoSuchElementException");
+        } catch (NoSuchElementException e) { } // as expected
+
+        // fill reverse sequential list
+        int back = 0;
+        for (iter = list.listIterator(list.size()); iter.hasPrevious(); ) {
+            assertEquals(index, iter.previousIndex() + 1);
+            assertEquals(index, iter.nextIndex());
+            values3 [--index] = iter.previous();
+            back++;
+        }
+        assertEquals(list.size(), back);
+
+        // ensure NoSuchElementException is thrown as appropriate
+        try {
+            iter.previous();
+            fail("previous() should have resulted in a "
+                + "NoSuchElementException");
+        } catch (NoSuchElementException e) { } // as expected
+
+        // fill random access list
+        List indices = new LinkedList();
+        for (int i = 0; i < list.size(); i++)
+            indices.add(new Integer(i));
+
+        for (int i = 0; i < list.size(); i++) {
+            int rand = (int)(Math.random() * indices.size());
+            Integer randIndex = (Integer)indices.remove(rand);
+            values4 [randIndex.intValue()] = list.get(randIndex.intValue());
+        }
+
+        assertEquals(Arrays.asList(coreValues), Arrays.asList(values1));
+        assertIdentical(Arrays.asList(coreValues), Arrays.asList(values1));
+        assertEquals(Arrays.asList(coreValues), Arrays.asList(values2));
+        assertIdentical(Arrays.asList(coreValues), Arrays.asList(values2));
+        assertEquals(Arrays.asList(coreValues), Arrays.asList(values4));
+        assertIdentical(Arrays.asList(coreValues), Arrays.asList(values4));
+        assertEquals(Arrays.asList(coreValues), Arrays.asList(values3));
+        assertIdentical(Arrays.asList(coreValues), Arrays.asList(values3));
+    }
+
+    /**
+     * Assert that the given List contain the exact same
+     * elements. This is different than the normal List contract, which
+     * states that list1.equals(list2) if each element e1.equals(e2).
+     * This method asserts that e1 == n2.
+     */
+    public static void assertIdentical(List c1, List c2) {
+        assertEquals(c1.size(), c2.size());
+        for (Iterator i1 = c1.iterator(), i2 = c2.iterator();
+                i1.hasNext() && i2.hasNext(); )
+            assertTrue(i1.next() == i2.next());
+    }
+
+    /**
+     * Assert that the collection parameter is already ordered
+     * according to the specified comparator.
+     */
+    public void assertOrdered(Collection c, Comparator comp) {
+        List l1 = new LinkedList(c);
+        List l2 = new LinkedList(c);
+        assertEquals(l1, l2);
+        Collections.sort(l2, comp);
+        assertEquals(l1, l2);
+        Collections.sort(l1, comp);
+        assertEquals(l1, l2);
+    }
+
+    ////////////////////
+    // Assertion Helpers
+    ////////////////////
+
+    public void assertNotEquals(Object a, Object b) {
+        if (a == null && b != null)
+            return;
+        if (a != null && b == null)
+            return;
+        if (!(a.equals(b)))
+            return;
+        if (!(b.equals(a)))
+            return;
+
+        fail("expected !<" + a + ">.equals(<" + b + ">)");
+    }
+
+    public void assertSize(int size, Object ob) {
+        if (ob == null) {
+            assertEquals(size, 0);
+            return;
+        }
+
+        if (ob instanceof Collection)
+            ob = ((Collection) ob).iterator();
+        if (ob instanceof Iterator) {
+            Iterator i = (Iterator)ob;
+            int count = 0;
+            while (i.hasNext()) {
+                count++;
+                i.next();
+            }
+
+            assertEquals(size, count);
+        } else
+            fail("assertSize: expected Collection, Iterator, "
+                + "Query, or Extent, but got " + ob.getClass().getName());
+    }
+
+    /////////////////////
+    // Generic utilities
+    /////////////////////
+
+    public void copy(File from, File to) throws IOException {
+        copy(new FileInputStream(from), to);
+    }
+
+    public void copy(InputStream in, File to) throws IOException {
+        FileOutputStream fout = new FileOutputStream(to);
+
+        byte[] b = new byte[1024];
+
+        for (int n = 0; (n = in.read(b)) != -1; )
+            fout.write(b, 0, n);
+    }
+
+    /**
+     * Print out information on memory usage.
+     */
+    public void printMemoryInfo() {
+        Runtime rt = Runtime.getRuntime();
+        long total = rt.totalMemory();
+        long free = rt.freeMemory();
+        long used = total - free;
+
+        NumberFormat nf = NumberFormat.getInstance();
+        getLog().warn("memory:" + " used: " + nf.format(used)
+            + " total: " + nf.format(total)
+            + " free: " + nf.format(free));
+    }
+
+    /**
+     * Return a list of all values iterated by the given iterator.
+     */
+    public static List iteratorToList(Iterator i) {
+        LinkedList list = new LinkedList();
+        while (i.hasNext())
+            list.add(i.next());
+        return list;
+    }
+
+    /**
+     * Return an array of the objects iterated by the given iterator.
+     */
+    public static Object [] iteratorToArray(Iterator i, Class [] clazz) {
+        return iteratorToList(i).toArray(clazz);
+    }
+
+    /**
+     * Run ant on the specified build file.
+     * 
+     * @param buildFile the build file to use
+     * @param target the name of the target to invoke
+     */
+    public void ant(File buildFile, String target) {
+        assertTrue(buildFile.isFile());
+
+        Project project = new Project();
+        project.init();
+        project.setUserProperty("ant.file", buildFile.getAbsolutePath());
+        ProjectHelper.configureProject(project, buildFile);
+        project.executeTarget(target);
+    }
+
+    /**
+     * Serialize and deserialize the object.
+     * 
+     * @param validateEquality make sure the hashCode and equals
+     * methods hold true
+     */
+    public static Object roundtrip(Object orig, boolean validateEquality)
+        throws IOException, ClassNotFoundException {
+        assertNotNull(orig);
+
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream(bout);
+        out.writeObject(orig);
+        ByteArrayInputStream bin = new ByteArrayInputStream(
+            bout.toByteArray());
+        ObjectInputStream in = new ObjectInputStream(bin);
+        Object result = in.readObject();
+
+        if (validateEquality) {
+            assertEquals(orig.hashCode(), result.hashCode());
+            assertEquals(orig, result);
+        }
+
+        return result;
+    }
+
+    /**
+     * @return true if the specified input matches the regular expression regex.
+     */
+    public static boolean matches(String regex, String input)
+        throws RESyntaxException {
+        RE re = REUtil.createRE(regex);
+        return re.match(input);
+    }
+
+    public static void assertMatches(String regex, String input) {
+        try {
+            if (!(matches(regex, input)))
+                fail("Expected regular expression: <" + regex + ">"
+                    + " did not match: <" + input + ">");
+        } catch (RESyntaxException e) {
+            throw new IllegalArgumentException(e.toString());
+        }
+    }
+
+    public static void assertNotMatches(String regex, String input) {
+        try {
+            if (matches(regex, input))
+                fail("Regular expression: <" + regex + ">"
+                    + " should not match: <" + input + ">");
+        } catch (RESyntaxException e) {
+            throw new IllegalArgumentException(e.toString());
+        }
+    }
+
+    /**
+     * Check the list if strings and return the ones that match
+     * the specified match.
+     */
+    public static List matches(String regex, Collection input)
+        throws RESyntaxException {
+        List matches = new ArrayList();
+        for (Iterator i = input.iterator(); i.hasNext(); ) {
+            String check = (String)i.next();
+            if (matches(regex, check))
+                matches.add(check);
+        }
+
+        return matches;
+    }
+
+    /**
+     * Assert that the specified collection of Strings contains at least
+     * one string that matches the specified regular expression.
+     */
+    public static void assertMatches(String regex, Collection input) {
+        try {
+            if (matches(regex, input).size() == 0)
+                fail("The specified list of size " + input.size()
+                    + " did not contain any strings that match the"
+                    + " specified regular expression(\"" + regex + "\")");
+        } catch (RESyntaxException e) {
+            throw new IllegalArgumentException(e.toString());
+        }
+    }
+
+    /**
+     * Assert that the specified collection of Strings does not match
+     * the specified regular expression.
+     */
+    public static void assertNotMatches(String regex, Collection input) {
+        try {
+            List matches;
+
+            if (((matches = matches(regex, input))).size() > 0)
+                fail("The specified list of size " + input.size()
+                    + " did contain one or more strings that matchs the"
+                    + " specified illegal regular expression"
+                    + " (\"" + regex + "\")."
+                    + " First example of a matching message is: "
+                    + matches.iterator().next());
+        } catch (RESyntaxException e) {
+            throw new IllegalArgumentException(e.toString());
+        }
+    }
+
+    private static String trim(String str, int max) {
+        if (str.length() < max)
+            return str;
+
+        return str.substring(0, max) + "...";
+    }
+
+    /**
+     * To be called by the child. E.g.:
+     * <code> public static void main(String [] args) { main(TestBug375.class);
+     * }
+     * </code>
+     */
+    public static void main(Class c) {
+        TestRunner.run(c);
+    }
+
+    /**
+     * To be called by child. Figures out the class from the calling context.
+     */
+    public static void main() {
+        String caller = new SecurityManager() {
+            public String toString() {
+                return getClassContext()[2].getName();
+            }
+        }.toString();
+
+        try {
+            main(Class.forName(caller));
+        } catch (ClassNotFoundException cnfe) {
+            throw new RuntimeException(cnfe.toString());
+        }
+    }
+
+    /**
+     * Returns the jar file in which the class is contained.
+     * 
+     * @return the jar file, or none if the class is not in a jar
+     * @throws FileNotFoundException if the jar file cannot located
+     */
+    public static File getJarFile(Class clazz) throws FileNotFoundException {
+        URL url = clazz.getResource(clazz.getName().substring(
+            clazz.getName().lastIndexOf(".") + 1) + ".class");
+        if (url == null)
+            throw new FileNotFoundException(clazz.toString());
+
+        String file = url.getFile();
+        if (file == null)
+            throw new FileNotFoundException(url.toString());
+        int index = file.indexOf("!");
+        if (index == -1)
+            throw new FileNotFoundException(file);
+
+        file = file.substring(0, index);
+        file = file.substring("file:".length());
+
+        File f = new File(file);
+        if (!(f.isFile()))
+            throw new FileNotFoundException(file);
+
+        return f.getAbsoluteFile();
+    }
+
+    /**
+     * The number of milliseconds each test case will have for a timeout.
+     */
+    public void setTimeout(long timeout) {
+        _timeout = timeout;
+    }
+
+    /**
+     * The number of milliseconds each test case will have for a timeout.
+     */
+    public long getTimeout() {
+        return _timeout;
+    }
+
+    /**
+     * A watchdog that just exits the JVM if a test has not completed in
+     * a certain amount of time. This speeds up the mechanism of determining
+     * if a timeout has occurred, since we can exit the entire test run
+     * if a test hasn't completed in a shorted amount of time than
+     * the global test timeout.
+     * 
+     * @author <a href="mailto:marc@solarmetric.com">Marc Prud'hommeaux</a>
+     */
+    private static class WatchdogThread extends Thread {
+        private final long _timeoutms;
+        private long _endtime = -1;
+        private AbstractTestCase _curtest = null;
+
+        public WatchdogThread() {
+            super("Kodo test case watchdog thread");
+            setDaemon(true);
+
+            int timeoutMin = new Integer
+                (System.getProperty("autobuild.testcase.timeout", "20"))
+                .intValue();
+
+            _timeoutms = timeoutMin * 60 * 1000;
+        }
+
+        public void run() {
+            while (true) {
+                try { sleep(200); } catch (InterruptedException ie) { }
+
+                if (_endtime > 0 && System.currentTimeMillis() > _endtime) {
+                    Thread preTimeout = new Thread
+                        ("Attempting pre-timeout for " + _curtest) {
+                        public void run() {
+                            _curtest.preTimeout();
+                        }
+                    };
+                    preTimeout.start();
+
+                    // wait a little while for the pre-timeout
+                    // thread to complete
+                    try { preTimeout.join(10 * 1000); } catch (Exception e) { }
+
+                    // give it a few more seconds...
+                    try { sleep(5 * 1000); } catch (Exception e) { }
+
+                    // new endtime? resume...
+                    if (System.currentTimeMillis() < _endtime)
+                        continue;
+
+                    new Exception("test case "
+                        + (_curtest != null ? _curtest.getName()
+                            : "UNKNOWN") + " timed out after "
+                        + _timeoutms + "ms").printStackTrace();
+
+                    // also run "killall -QUIT java" to try to grab
+                    // a stack trace
+                    try {
+                        Runtime.getRuntime().exec
+                            (new String[] { "killall", "-QUIT", "java" });
+                    } catch (Exception e) {
+                    }
+
+                    try { sleep(1000); } catch (InterruptedException ie) { }
+
+                    // now actually exit
+                    System.exit(111);
+                }
+            }
+        }
+
+        public synchronized void enteringTest(AbstractTestCase test) {
+            long timeout = test.getTimeout();
+            if (timeout <= 0)
+                timeout = _timeoutms;
+
+            _endtime = System.currentTimeMillis() + timeout;
+            _curtest = test;
+
+            if (!isAlive())
+                start();
+        }
+
+        public synchronized void leavingTest(AbstractTestCase test) {
+            _endtime = -1;
+            _curtest = null;
+        }
+    }
+}

Propchange: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/test/AbstractTestCase.java
------------------------------------------------------------------------------
    svn:executable = *

Modified: incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestAbstractEventManager.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestAbstractEventManager.java?rev=418401&r1=418400&r2=418401&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestAbstractEventManager.java (original)
+++ incubator/openjpa/trunk/openjpa-lib/src/test/java/org/apache/openjpa/lib/util/TestAbstractEventManager.java Fri Jun 30 15:37:18 2006
@@ -1,13 +1,10 @@
 /*
  * Copyright 2006 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
+ *  Licensed 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
+ *  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
@@ -15,17 +12,14 @@
  */
 package org.apache.openjpa.lib.util;
 
+import java.util.*;
 import junit.framework.*;
-
 import junit.textui.*;
 
-import java.util.*;
-
-
 /**
- *  <p>Tests the {@link AbstractEventManager}.</p>
- *
- *  @author Abe White
+ * Tests the {@link AbstractEventManager}.
+ * 
+ * @author Abe White
  */
 public class TestAbstractEventManager extends TestCase {
     private EventManager _em = new EventManager();
@@ -75,6 +69,7 @@
         public static final int NONE = 0;
         public static final int ADD = 1;
         public static final int REMOVE = 2;
+
         public boolean fired;
         private final int _action;
 
@@ -84,12 +79,11 @@
 
         public void fire() {
             fired = true;
-
-            if (_action == ADD) {
+            if (_action == ADD)
                 _em.addListener(new Listener(NONE));
-            } else if (_action == REMOVE) {
+            else if (_action == REMOVE)
                 assertTrue(_em.removeListener(this));
-            }
         }
     }
 }
+