You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pp...@apache.org on 2008/08/08 08:47:15 UTC
svn commit: r683878 - in
/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence:
exception/ jdbc/unique/ jdbc/update/ test/
Author: ppoddar
Date: Thu Aug 7 23:47:14 2008
New Revision: 683878
URL: http://svn.apache.org/viewvc?rev=683878&view=rev
Log:
Assorted changes in test utilities
1. Added a new Test class CombinatorialPersistenceTestCase to run test with auto-generated configurations as combination of possible values of many configuration properties
2. Added a Test for Parent-Child insertion under different foreign key constraint + update startaegies scenarios as an exemplar of this new utility
3. Moved exception handling routines to base Persistence TestCase
Added:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/Child.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/Parent.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/TestParentChild.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinationGenerator.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinatorialPersistenceTestCase.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinatorialTestHelper.java
Modified:
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraint.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraintWithXMLDescriptor.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SingleEMFTestCase.java
Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java?rev=683878&r1=683877&r2=683878&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java Thu Aug 7 23:47:14 2008
@@ -163,7 +163,7 @@
* Otherwise fails assertion and prints the given throwable and its nested
* exception on the console.
*/
- void assertException(Throwable t, Class expectedType) {
+ public void assertException(Throwable t, Class expectedType) {
if (!isExpectedException(t, expectedType)) {
t.printStackTrace();
print(t, 0);
Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraint.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraint.java?rev=683878&r1=683877&r2=683878&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraint.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraint.java Thu Aug 7 23:47:14 2008
@@ -53,27 +53,23 @@
List<String> sqls = super.sql;
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_A",
- "UNIQUE \\w*\\(a1, a2\\)",
- "UNIQUE \\w*\\(a3, a4\\)");
+ "UNIQUE .*\\(a1, a2\\)",
+ "UNIQUE .*\\(a3, a4\\).*");
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_B",
- "UNIQUE \\w*\\(b1, b2\\)");
+ "UNIQUE .*\\(b1, b2\\).*");
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_SECONDARY",
- "UNIQUE \\w*\\(sa1\\)");
+ "UNIQUE .*\\(sa1\\)");
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_GENERATOR",
- "UNIQUE \\w*\\(GEN1, GEN2\\)");
+ "UNIQUE .*\\(GEN1, GEN2\\)");
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_JOINTABLE",
- "UNIQUE \\w*\\(FK_A, FK_B\\)");
+ "UNIQUE .*\\(FK_A, FK_B\\)");
}
void assertSQLFragnments(List<String> list, String... keys) {
if (SQLSniffer.matches(list, keys))
return;
- int i = 0;
- for (String sql : list) {
- i++;
- System.out.println("" + i + ":" + sql);
- }
- fail("None of the " + sql.size() + " SQL contains all keys "
- + Arrays.toString(keys));
+ fail("None of the following " + sql.size() + " SQL \r\n" +
+ toString(sql) + "\r\n contains all keys \r\n"
+ + toString(Arrays.asList(keys)));
}
}
Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraintWithXMLDescriptor.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraintWithXMLDescriptor.java?rev=683878&r1=683877&r2=683878&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraintWithXMLDescriptor.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraintWithXMLDescriptor.java Thu Aug 7 23:47:14 2008
@@ -60,27 +60,23 @@
// Following verification techniques is fragile as databases DDL
// syntax vary greatly on UNIQUE CONSTRAINT
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_A_XML",
- "UNIQUE \\w*\\(a1x, a2x\\)",
- "UNIQUE \\w*\\(a3x, a4x\\)");
+ "UNIQUE .*\\(a1x, a2x\\)",
+ "UNIQUE .*\\(a3x, a4x\\)");
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_B_XML",
- "UNIQUE \\w*\\(b1x, b2x\\)");
+ "UNIQUE .*\\(b1x, b2x\\)");
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_SECONDARY_XML",
- "UNIQUE \\w*\\(sa1x\\)");
+ "UNIQUE .*\\(sa1x\\)");
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_GENERATOR_XML",
- "UNIQUE \\w*\\(GEN1_XML, GEN2_XML\\)");
+ "UNIQUE .*\\(GEN1_XML, GEN2_XML\\)");
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_JOINTABLE_XML",
- "UNIQUE \\w*\\(FK_A_XML, FK_B_XML\\)");
+ "UNIQUE .*\\(FK_A_XML, FK_B_XML\\)");
}
void assertSQLFragnments(List<String> list, String... keys) {
if (SQLSniffer.matches(list, keys))
return;
- int i = 0;
- for (String sql : list) {
- i++;
- System.out.println("" + i + ":" + sql);
- }
- fail("None of the " + sql.size() + " SQL contains all keys "
- + Arrays.toString(keys));
+ fail("None of the following " + sql.size() + " SQL \r\n" +
+ toString(sql) + "\r\n contains all keys \r\n"
+ + toString(Arrays.asList(keys)));
}
}
Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/Child.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/Child.java?rev=683878&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/Child.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/Child.java Thu Aug 7 23:47:14 2008
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.jdbc.update;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+
+/**
+ * Child in a bidirectional parent-child relationship.
+ *
+ * Notes:
+ * a) there is no mutator for id because it is generated by JPA provider.
+ *
+ * @author Pinaki Poddar
+ *
+ */
+@Entity
+public class Child {
+ @Id
+ @GeneratedValue
+ private String id;
+
+ private String name;
+
+ @ManyToOne(fetch=FetchType.LAZY)
+ private Parent parent;
+
+ /**
+ * Restrict access to constructor for Parent to create the Child.
+ */
+ public Child() {
+
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String city) {
+ this.name = city;
+ }
+
+ public Parent getParent() {
+ return parent;
+ }
+
+ void setParent(Parent owner) {
+ this.parent = owner;
+ }
+}
Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/Parent.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/Parent.java?rev=683878&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/Parent.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/Parent.java Thu Aug 7 23:47:14 2008
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.jdbc.update;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+
+/**
+ * Parent in a bidirectional parent-child relationship.
+ *
+ * Note:
+ * a) there is no mutator for id because it is generated by JPA provider.
+ *
+ * @author Pinaki Poddar
+ *
+ */
+@Entity
+public class Parent {
+ @Id
+ @GeneratedValue
+ private long id;
+
+ private String name;
+
+ /**
+ * This field is mapped by the child. The child's table will hold a foreign
+ * key linking to the primary key of this Parent's table. In JPA terminology,
+ * that makes the Child the owner of this bi-directional relationship.
+ */
+ @OneToMany(mappedBy="parent", cascade = CascadeType.ALL)
+ private Collection<Child> children;
+
+ public long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Collection<Child> getChildren() {
+ return children;
+ }
+
+ /**
+ * Creates and adds a child to this receiver. Creating child via the parent
+ * is the preferred pattern to ensure referential integrity of domain model.
+ */
+ public Child newChild(String name) {
+ Child child = new Child();
+ child.setName(name);
+ child.setParent(this);
+ if (children == null)
+ children = new ArrayList<Child>();
+ children.add(child);
+ return child;
+ }
+
+ public boolean removeChild(Child child) {
+ return children != null && children.remove(child);
+ }
+
+ /**
+ * Unsafe way of adding a child. Does not warranty referential integrity.
+ * The caller has to ensure bi-directionality of parent-child relation is
+ * consistent.
+ */
+ public void add(Child child) {
+ if (children == null)
+ children = new ArrayList<Child>();
+ children.add(child);
+ }
+}
Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/TestParentChild.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/TestParentChild.java?rev=683878&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/TestParentChild.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/update/TestParentChild.java Thu Aug 7 23:47:14 2008
@@ -0,0 +1,232 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.jdbc.update;
+
+import javax.persistence.EntityManager;
+
+import org.apache.openjpa.jdbc.meta.FieldMapping;
+import org.apache.openjpa.jdbc.schema.ForeignKey;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.persistence.test.CombinatorialPersistenceTestCase;
+
+/**
+ * Tests for SQL statement ordering capabilities of different update strategies
+ * for a Parent-Child model against different physical database constraints.
+ *
+ * SQL statement ordering is influenced by
+ * 1. In-memory schema model: The in-memory schema model can be aware of logical
+ * or physical foreign keys.
+ * a) This is configured by <code>jdbc.SchemaFactory</code> property setting
+ * to <code>native(ForeignKeys=true|false)</code> which makes OpenJPA to
+ * read physical foreign key information from database
+ * b) @ForeignKey annotation on the relation -- OpenJPA then considers
+ * logical foreign key
+ *
+ * 2. Physical Schema: The database schema can be defined with physical foreign
+ * keys. This is configured by <code>jdbc.MappingDefaults</code> property
+ * setting to <code>ForeignKeyDeleteAction</code>
+ *
+ * 3. Update Strategy: the update manager is configured by
+ * <code>jdbc.UpdateManager</code>
+ *
+ * 4. Order of persistence operation: The order in which the application calls
+ * persistence operations such as persist() or remove(). In this test, we
+ * control this by PersistOrder enum. This application order is maintained
+ * if the update manager is set to <code>'operation-order'</code>.
+ *
+ *
+ * This test case also demonstrates how to write a test case that runs with
+ * multiple combination of configurations. Each configuration discussed above
+ * has multiple possible values and testing all possible combination can be
+ * an arduous task. The {@link CombinatorialPersistenceTestCase combinatorial}
+ * test case utility helps to auto-generate these multiple configurations and
+ * execute the same test with all the combinations.
+ *
+ * @author Pinaki Poddar
+ *
+ */
+public class TestParentChild extends CombinatorialPersistenceTestCase {
+ // Each of these property keys can take multiple possible values
+ private static String Key_UpdateManager = "openjpa.jdbc.UpdateManager";
+ private static String Key_SchemaFactory = "openjpa.jdbc.SchemaFactory";
+ private static String Key_MappingDefaults = "openjpa.jdbc.MappingDefaults";
+ private static String Key_PersistOrder = "persist-order";
+
+ private static String[] Option_MappingDefaults = {
+ "ForeignKeyDeleteAction=restrict, JoinForeignKeyDeleteAction=restrict",
+ "ForeignKeyDeleteAction=none, JoinForeignKeyDeleteAction=none" };
+
+ private static String[] Option_SchemaFactory = {
+ "native(ForeignKeys=false)",
+ "native(ForeignKeys=true)" };
+
+ private static String[] Option_UpdateManager = {
+ "operation-order",
+ "constraint" };
+
+ private static enum PersistOrder {
+ IMPLICIT_CASCADE,
+ CHILD_THEN_PARENT,
+ PARENT_THEN_CHILD
+ };
+
+
+ // The options are added in a static block, so that we can count on
+ // total number of combinations before the test is set up.
+ static {
+ getHelper().addOption(Key_MappingDefaults, Option_MappingDefaults);
+ getHelper().addOption(Key_SchemaFactory, Option_SchemaFactory);
+ getHelper().addOption(Key_UpdateManager, Option_UpdateManager);
+
+ // The last argument tells that this is a runtime option. So the
+ // values are included to generate combinations but are excluded
+ // from generating OpenJPA configuration.
+ getHelper().addOption("persist-order", PersistOrder.values(), true);
+ }
+
+ public void setUp() {
+ // The options can also be added in setup() as well but then
+ // coutTestCase() will only record test methods and not multiply them
+ // with number of configuration combinations the same tests will run.
+ getHelper().addOption(Key_MappingDefaults, Option_MappingDefaults);
+ getHelper().addOption(Key_SchemaFactory, Option_SchemaFactory);
+ getHelper().addOption(Key_UpdateManager, Option_UpdateManager);
+
+ getHelper().addOption("persist-order", PersistOrder.values(), true);
+
+ sql.clear();
+ super.setUp(DROP_TABLES, Parent.class, Child.class);
+ }
+
+ /**
+ * This test will run in 2*2*2*3 = 24 times with different configurations.
+ */
+ public void testInsert() {
+ Parent parent = createData(getPersistOrder(), 3);
+ validateData(parent.getId(), 3);
+
+ // verification can be challenging under multiple configuration options
+ // see the methods as exemplars how verification can vary based on
+ // configuration.
+ assertLogicalOrPhysicalForeignKey();
+ assertPostInsertUpdate();
+ assertPhysicalForeignKeyCreation();
+ }
+
+ Parent createData(PersistOrder order, int nChild) {
+ EntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+
+ Parent parent = new Parent();
+ parent.setName("parent");
+ for (int i = 1; i <= nChild; i++)
+ parent.newChild("Child-" + i);
+ switch (order) {
+ case IMPLICIT_CASCADE:
+ em.persist(parent);
+ break;
+ case CHILD_THEN_PARENT:
+ for (Child child : parent.getChildren()) {
+ em.persist(child);
+ }
+ em.persist(parent);
+ break;
+ case PARENT_THEN_CHILD:
+ em.persist(parent);
+ for (Child child : parent.getChildren()) {
+ em.persist(child);
+ }
+ break;
+ default:
+ throw new RuntimeException("Bad order " + order);
+ }
+ em.getTransaction().commit();
+ em.clear();
+ return parent;
+ }
+
+ void validateData(Object pid, int childCount) {
+ EntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ Parent parent = em.find(Parent.class, pid);
+ assertNotNull(parent);
+ assertEquals(childCount, parent.getChildren().size());
+ em.getTransaction().rollback();
+ }
+
+ /**
+ * Asserts that foreign key constraint will be defined on the database
+ * for certain combinations of configurations.
+ */
+ void assertPhysicalForeignKeyCreation() {
+ String regex = "ALTER TABLE .* ADD FOREIGN KEY \\(PARENT_ID\\) "
+ + "REFERENCES Parent \\(id\\)";
+ if (getMappingDefaults().contains("restrict")) {
+ assertSQL(regex);
+ } else {
+ assertNotSQL(regex);
+ }
+ }
+
+ /**
+ * Asserts that update SQL will be issued to set the foreign key value
+ * after the insert under some configuration.
+ */
+ void assertPostInsertUpdate() {
+ if (getPersistOrder().equals(PersistOrder.CHILD_THEN_PARENT)
+ && getMappingDefaults().contains("restrict")) {
+ assertSQL("UPDATE .* SET PARENT_ID .* WHERE .*");
+ }
+ }
+
+ /**
+ * Asserts that foreign key will be logical or physical under different
+ * combination of configuration.
+ */
+ void assertLogicalOrPhysicalForeignKey() {
+ ForeignKey fk = getChildParentForeignKey();
+ boolean physicalKeyExists = getMappingDefaults().contains("restrict");
+ boolean keyRead = getSchemaFactory().contains("ForeignKeys=true");
+ if (physicalKeyExists && keyRead)
+ assertFalse(fk.isLogical());
+ else if (keyRead)
+ assertTrue(fk.isLogical());
+ }
+
+ ForeignKey getChildParentForeignKey() {
+ MetaDataRepository repos = emf.getConfiguration()
+ .getMetaDataRepositoryInstance();
+ ClassMetaData child = repos.getCachedMetaData(Child.class);
+ FieldMapping parent = (FieldMapping) child.getField("parent");
+ return parent.getForeignKey();
+ }
+
+ PersistOrder getPersistOrder() {
+ return (PersistOrder) getHelper().getOption(Key_PersistOrder);
+ }
+
+ String getMappingDefaults() {
+ return getHelper().getOptionAsString(Key_MappingDefaults);
+ }
+
+ String getSchemaFactory() {
+ return getHelper().getOptionAsString(Key_SchemaFactory);
+ }
+}
Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinationGenerator.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinationGenerator.java?rev=683878&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinationGenerator.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinationGenerator.java Thu Aug 7 23:47:14 2008
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Generates combinations given multiple choices in each dimension.
+ *
+ * Usage:
+ * <code>
+ * CombinationGenerator combo = new CombinationGenerator();
+ * combo.addDimension(new String[]{"A","B","C"});
+ * combo.addDimension(new int[]{1,2});
+ * List[] combos = combo.generate();
+ * </code>
+ * will generate 3*2=6 combinations
+ * <code>
+ * combos[0] => List("A",1);
+ * combos[1] => List("B",1);
+ * combos[2] => List("C",1);
+ * combos[3] => List("A",2);
+ * combos[4] => List("B",2);
+ * combos[5] => List("C",2);
+ * </code>
+ *
+ * @author Pinaki Poddar
+ *
+ */
+public class CombinationGenerator {
+ private List<List> dimensions = new ArrayList();
+
+ /**
+ * Adds a dimension. null or empty argument has no effect.
+ */
+ public void addDimension(List dim) {
+ if (dim == null || dim.isEmpty())
+ return;
+ dimensions.add(dim);
+
+ }
+
+ /**
+ * Adds a dimension. null or empty argument has no effect.
+ */
+ public void addDimension(Object[] dim) {
+ if (dim == null || dim.length == 0)
+ return;
+ dimensions.add(Arrays.asList(dim));
+ }
+
+ /**
+ * Generates all combinations.
+ * Each array element is a list which has elements in the same order as
+ * the dimensions were added.
+ */
+ public List[] generate() {
+ int n = getSize();
+ List[] result = new ArrayList[n];
+ for (int i = 0; i < n; i++) {
+ ArrayList elem = new ArrayList(dimensions.size());
+ for (int j=0; j < dimensions.size(); j++)
+ elem.add(null);
+ result[i] = elem;
+ }
+
+ for (int i = 0; i < dimensions.size(); i++) {
+ fill(i, dimensions.get(i), result);
+ }
+ return result;
+ }
+
+ private void fill(int where, List elements, List[] fill) {
+ if (fill.length%elements.size() != 0)
+ throw new RuntimeException();
+ int n = fill.length/elements.size();
+ int k = 0;
+ for (int i = 0; i < n; i++) {
+ for (Object e : elements)
+ fill[k++].set(where, e);
+ }
+ }
+
+ /**
+ * Gets the total number of combinations generated. The total number is
+ * the product of cardinality of each dimension.
+ *
+ */
+ public int getSize() {
+ int size = 1;
+ for (List d : dimensions)
+ size *= d.size();
+ return size;
+ }
+}
Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinatorialPersistenceTestCase.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinatorialPersistenceTestCase.java?rev=683878&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinatorialPersistenceTestCase.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinatorialPersistenceTestCase.java Thu Aug 7 23:47:14 2008
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.openjpa.persistence.test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.AssertionFailedError;
+
+
+/**
+ * Adds ability to run tests under combinations of options.
+ *
+ * @author Pinaki Poddar
+ *
+ */
+public abstract class CombinatorialPersistenceTestCase
+ extends SQLListenerTestCase {
+
+ protected static CombinatorialTestHelper helper;
+
+ public void setUp(Object...props) {
+ super.setUp(getHelper().setCombinatorialOption(props));
+ }
+
+ @Override
+ public int countTestCases() {
+ return super.countTestCases() * getHelper().getCombinationSize();
+ }
+
+ @Override
+ public void runBare() throws Throwable {
+ Map<String, Throwable> errors = new HashMap<String, Throwable>();
+ Map<String, AssertionFailedError> failures = new HashMap<String, AssertionFailedError>();
+ do {
+ try {
+ super.runBare();
+ } catch (Throwable t) {
+ if (t instanceof AssertionFailedError) {
+ failures.put(getHelper().getOptionsAsString(), (AssertionFailedError)t);
+ testResult.addFailure(this, (AssertionFailedError)t);
+ } else {
+ errors.put(getHelper().getOptionsAsString(), t);
+ testResult.addError(this, t);
+ }
+ }
+ } while (getHelper().hasMoreCombination());
+
+ if (testResult.errorCount() + testResult.failureCount() > 0) {
+ if (!failures.isEmpty())
+ System.err.println(failures.size() + " assertion failures");
+ for (String o : failures.keySet()) {
+ System.err.println("Combination:\r\n" + o);
+ failures.get(o).printStackTrace();
+ }
+ if (!errors.isEmpty())
+ System.err.println(errors.size() + " errors");
+ for (String o : errors.keySet()) {
+ System.err.println("Combination:\r\n" + o);
+ errors.get(o).printStackTrace();
+ }
+ throw new Throwable(getName() + ": "
+ + getHelper().getCombinationSize() + " combinations, "
+ + errors.size() + " errors, " + failures.size() + " failures\r\n"
+ + "Stack trace for each error/failure is printed on console");
+ }
+ }
+
+
+
+ public static CombinatorialTestHelper getHelper() {
+ if (helper == null)
+ helper = new CombinatorialTestHelper();
+ return helper;
+ }
+
+ public void assertSQL(String sqlExp) {
+ try {
+ super.assertSQL(sqlExp);
+ } catch (AssertionFailedError e) {
+ String newMessage = "Combination\r\n" + getHelper().getOptionsAsString()
+ + " failed \r\n " + e.getMessage();
+ fail(newMessage);
+ }
+ }
+
+}
Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinatorialTestHelper.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinatorialTestHelper.java?rev=683878&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinatorialTestHelper.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/CombinatorialTestHelper.java Thu Aug 7 23:47:14 2008
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.List;
+
+import org.apache.openjpa.persistence.jdbc.update.TestParentChild;
+
+/**
+ * Aids to run a single test under different combination of configuration
+ * parameters.
+ *
+ * Each configurable property can be registered to this receiver with all its
+ * possible values. This class generates all combinations of all the possible
+ * property values as configuration and invokes the same test with each
+ * configuration combination.
+ * The properties can be designated as <em>runtime</code> to be included in
+ * combination for execution but excluded from configuration.
+ *
+ * For example,
+ * @see TestParentChild
+ *
+ * @author Pinaki Poddar
+ *
+ */
+public class CombinatorialTestHelper {
+ private CombinationGenerator geneartor;
+ private List<String> propertyKeys;
+ private List currentOption;
+ private BitSet runtimeKeys = new BitSet();
+
+ private List[] combos;
+ private int cursor;
+
+ public CombinatorialTestHelper() {
+ geneartor = new CombinationGenerator();
+ propertyKeys = new ArrayList<String>();
+ currentOption = null;
+ runtimeKeys = new BitSet();
+ combos = null;
+ cursor = 0;
+ }
+
+ /**
+ * Generates the key-value property array as expected by its superclass
+ * by appending the current combinatorially generated properties.
+ *
+ * The important side effect of this method is to set the current
+ * configuration options.
+ *
+ * If no property is configured for combinatorial generation then returns
+ * the given list as it is.
+ *
+ */
+ Object[] setCombinatorialOption(Object[] props) {
+ if (propertyKeys.isEmpty() ||
+ propertyKeys.size() == runtimeKeys.cardinality())
+ return props;
+
+ if (combos == null) {
+ combos = geneartor.generate();
+ cursor = 0;
+ }
+ // Each non-runtime property contributes a key-value pair
+ Object[] options = new Object[2*(propertyKeys.size()-
+ runtimeKeys.cardinality())];
+ currentOption = combos[cursor++];
+ int k = 0;
+ for (int i = 0; i < propertyKeys.size(); i++) {
+ if (runtimeKeys.get(i))
+ continue;
+ options[k++] = propertyKeys.get(i);
+ options[k++] = currentOption.get(i);
+ }
+ if (props == null || props.length == 0)
+ return options;
+
+ Object[] newProps = new Object[props.length + options.length];
+ System.arraycopy(props, 0, newProps, 0, props.length);
+ System.arraycopy(options, 0, newProps, props.length, options.length);
+ return newProps;
+ }
+
+ /**
+ * Adds options for the given configuration property.
+ */
+ public void addOption(String property, Object[] options) {
+ addOption(property, options, false);
+ }
+
+ /**
+ * Adds options for the given configuration property.
+ */
+ public void addOption(String property, List options) {
+ addOption(property, options, false);
+ }
+
+ /**
+ * Adds options for the given property.
+ * If runtime is true then this property is not added to configuration.
+ */
+ public void addOption(String property, Object[] options, boolean runtime) {
+ addOption(property, Arrays.asList(options), runtime);
+ }
+
+ /**
+ * Adds options for the given property.
+ * If runtime is true then this property is not added to configuration.
+ */
+ public void addOption(String property, List options, boolean runtime) {
+ if (geneartor == null) {
+ geneartor = new CombinationGenerator();
+ }
+ if (propertyKeys == null) {
+ propertyKeys = new ArrayList<String>();
+ }
+ if (!propertyKeys.contains(property)) {
+ geneartor.addDimension(options);
+ propertyKeys.add(property);
+ if (runtime) runtimeKeys.set(propertyKeys.size()-1);
+ }
+ }
+
+ /**
+ * Gets the value of current option for the given key.
+ * Raises exception if the given key is not an option.
+ */
+ public Object getOption(String key) {
+ int index = propertyKeys.indexOf(key);
+ if (index == -1)
+ throw new IllegalArgumentException("Unknown option " + key);
+ return currentOption.get(index);
+ }
+
+ /**
+ * Gets the string value of current option for the given key.
+ * Raises exception if the given key is not an option.
+ */
+ public String getOptionAsString(String key) {
+ return getOption(key).toString();
+ }
+
+ /**
+ * Gets the values of the current options.
+ */
+ public List getOptions() {
+ return currentOption;
+ }
+
+ /**
+ * Gets the key and values of the current options as printable string.
+ */
+ public String getOptionsAsString() {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i <propertyKeys.size(); i++) {
+ String key = propertyKeys.get(i);
+ if (!runtimeKeys.get(i))
+ buf.append(key + " : " + getOption(key)).append("\r\n");
+ }
+ for (int i = 0; i <propertyKeys.size(); i++) {
+ String key = propertyKeys.get(i);
+ if (runtimeKeys.get(i))
+ buf.append("* " + key + " : " + getOption(key)).append("\r\n");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Affirms if this receiver has more combinations.
+ */
+ public boolean hasMoreCombination() {
+ return cursor < combos.length-1;
+ }
+
+ /**
+ * Gets total number of combinations.
+ */
+ public int getCombinationSize() {
+ return geneartor == null ? 0 : geneartor.getSize();
+ }
+}
Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java?rev=683878&r1=683877&r2=683878&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java Thu Aug 7 23:47:14 2008
@@ -19,6 +19,7 @@
package org.apache.openjpa.persistence.test;
import java.lang.reflect.Modifier;
+import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -296,4 +297,87 @@
else if (o1.equals(o2))
fail("expected args to be different; compared equal.");
}
+
+ // ================================================
+ // Utility methods for exception handling
+ // ================================================
+ /**
+ * Asserts that the given targetType is assignable from given actual
+ * Throwable.
+ */
+ protected void assertException(final Throwable actual, Class targetType) {
+ assertException(actual, targetType, null);
+ }
+
+ /**
+ * Asserts that the given targetType is assignable from given actual
+ * Throwable. Asserts that the nestedType is nested (possibly recursively)
+ * within the given actual Throwable.
+ *
+ * @param actual is the actual throwable to be tested
+ * @param targetType is expected type or super type of actual. If null, then
+ * the check is omitted.
+ * @param nestedTargetType is expected type of exception nested within
+ * actual. If null this search is omitted.
+ *
+ */
+ protected void assertException(final Throwable actual, Class targetType,
+ Class nestedTargetType) {
+ assertNotNull(actual);
+ Class actualType = actual.getClass();
+ if (targetType != null && !targetType.isAssignableFrom(actualType)) {
+ actual.printStackTrace();
+ fail(targetType.getName() + " is not assignable from "
+ + actualType.getName());
+ }
+
+ if (nestedTargetType != null) {
+ Throwable nested = actual.getCause();
+ Class nestedActualType = (nested == null) ? null : nested.getClass();
+ while (nestedActualType != null) {
+ if (nestedTargetType.isAssignableFrom(nestedActualType)) {
+ return;
+ } else {
+ Throwable next = nested.getCause();
+ if (next == null || next == nested)
+ break;
+ nestedActualType = next.getClass();
+ nested = next;
+ }
+ }
+ actual.printStackTrace();
+ fail("No nested type " + nestedTargetType + " in " + actual);
+ }
+ }
+
+ /**
+ * Assert that each of given keys are present in the message of the given
+ * Throwable.
+ */
+ protected void assertMessage(Throwable actual, String... keys) {
+ if (actual == null || keys == null)
+ return;
+ String message = actual.getMessage();
+ for (String key : keys) {
+ assertTrue(key + " is not in " + message, message.contains(key));
+ }
+ }
+
+ public void printException(Throwable t) {
+ printException(t, 2);
+ }
+
+ public void printException(Throwable t, int tab) {
+ if (t == null) return;
+ for (int i=0; i<tab*4;i++) System.out.print(" ");
+ String sqlState = (t instanceof SQLException) ?
+ "(SQLState=" + ((SQLException)t).getSQLState() + ":"
+ + t.getMessage() + ")" : "";
+ System.out.println(t.getClass().getName() + sqlState);
+ if (t.getCause() == t)
+ return;
+ printException(t.getCause(), tab+2);
+ }
+
+
}
Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java?rev=683878&r1=683877&r2=683878&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java Thu Aug 7 23:47:14 2008
@@ -18,6 +18,7 @@
*/
package org.apache.openjpa.persistence.test;
+import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
@@ -57,10 +58,10 @@
return;
}
- fail("Expected regular expression <" + sqlExp + "> to have"
- + " existed in SQL statements: " + sql);
+ fail("Expected regular expression\r\n <" + sqlExp
+ + ">\r\n to have existed in SQL statements: \r\n" + toString(sql));
}
-
+
/**
* Confirm that the specified SQL has not been executed.
*
@@ -75,8 +76,8 @@
}
if (failed)
- fail("Regular expression <" + sqlExp + ">"
- + " should not have been executed in SQL statements: " + sql);
+ fail("Regular expression\r\n <" + sqlExp + ">\r\n should not have"
+ + " been executed in SQL statements: \r\n" + toString(sql));
}
/**
@@ -90,8 +91,8 @@
return;
}
- fail("Expected regular expression <" + sqlExp + "> to be"
- + " contained in SQL statements: " + sql);
+ fail("Expected regular expression\r\n <" + sqlExp + ">\r\n to be"
+ + " contained in SQL statements: \r\n" + toString(sql));
}
/**
@@ -111,6 +112,13 @@
return tmp;
}
+ public String toString(List<String> list) {
+ StringBuffer buf = new StringBuffer();
+ for (String s : list)
+ buf.append(s).append("\r\n");
+ return buf.toString();
+ }
+
public class Listener
extends AbstractJDBCListener {
Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SingleEMFTestCase.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SingleEMFTestCase.java?rev=683878&r1=683877&r2=683878&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SingleEMFTestCase.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SingleEMFTestCase.java Thu Aug 7 23:47:14 2008
@@ -18,6 +18,10 @@
*/
package org.apache.openjpa.persistence.test;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
@@ -123,4 +127,25 @@
public int count(Class c) {
return count(c.getSimpleName());
}
+
+ /**
+ * Get all the instances of given type.
+ * The returned instances are obtained without a persistence context.
+ */
+ public <T> List<T> getAll(Class<T> t) {
+ String alias = t.getSimpleName();
+ return (List<T>)emf.createEntityManager()
+ .createQuery("SELECT p FROM " + alias + " p")
+ .getResultList();
+ }
+
+ /**
+ * Get all the instances of given type.
+ * The returned instances are obtained within the given persistence context.
+ */
+ public <T> List<T> getAll(EntityManager em, Class<T> t) {
+ String alias = t.getSimpleName();
+ return (List<T>)em.createQuery("SELECT p FROM " + alias + " p")
+ .getResultList();
+ }
}