/*
 * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     07/16/2009 Andrei Ilitchev
//       - Bug 282553: JPA 2.0 JoinTable support for OneToOne and ManyToOne
package org.eclipse.persistence.testing.tests.onetoonejointable;

import java.util.List;
import java.util.Vector;

import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.expressions.ExpressionMath;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.platform.database.TimesTenPlatform;
import org.eclipse.persistence.queries.Call;
import org.eclipse.persistence.queries.ReadAllQuery;
import org.eclipse.persistence.queries.ReportQuery;
import org.eclipse.persistence.queries.SQLCall;
import org.eclipse.persistence.sessions.UnitOfWork;
import org.eclipse.persistence.tools.schemaframework.PopulationManager;
import org.eclipse.persistence.testing.framework.*;
import org.eclipse.persistence.testing.models.onetoonejointable.*;
import org.eclipse.persistence.testing.tests.expressions.ReadObjectExpressionTest;
import org.eclipse.persistence.testing.tests.writing.ComplexUpdateTest;

/**
 * This model tests reading/writing/deleting through using the employee demo.
 */
public class OneToOneJoinTableEmployeeBasicTestModel extends TestModel {

    /**
     * Return the JUnit suite to allow JUnit runner to find it.
     * Unfortunately JUnit only allows suite methods to be static,
     * so it is not possible to generically do this.
     */
    public static junit.framework.TestSuite suite() {
        return new OneToOneJoinTableEmployeeBasicTestModel();
    }

    public OneToOneJoinTableEmployeeBasicTestModel() {
        setDescription("This model tests reading/writing/deleting using the employee demo.");
    }

    public OneToOneJoinTableEmployeeBasicTestModel(boolean isSRG) {
        this();
        this.isSRG = isSRG;
    }

    @Override
    public void addRequiredSystems() {
        addRequiredSystem(new EmployeeSystem());
    }

    @Override
    public void addTests() {
        addTest(getReadObjectTestSuite());
        addTest(getUpdateObjectTestSuite());
        addTest(getInsertObjectTestSuite());
        addTest(getUOWInsertObjectTestSuite());
        addTest(getDeleteObjectTestSuite());
        addTest(getReadAllTestSuite());
        addTest(new ExpressionTestSuite());
    }

    public static TestSuite getDeleteObjectTestSuite() {
        TestSuite suite = getSRGDeleteObjectTestSuite();

        //Add new tests here ...
        return suite;
    }

    //SRG test set is maintained by QA only, do NOT add any new tests into it.
    public static TestSuite getSRGDeleteObjectTestSuite() {
        TestSuite suite = new TestSuite();
        suite.setName("EmployeeDeleteObjectTestSuite");
        suite.setDescription("This suite tests the deletion of each object in the employee demo.");

        Class<Employee> employeeClass = Employee.class;
        Class<LargeProject> largeProjectClass = LargeProject.class;
        Class<SmallProject> smallProjectClass = SmallProject.class;
        PopulationManager manager = PopulationManager.getDefaultManager();

        suite.addTest(new EmployeeDeleteTest(manager.getObject(employeeClass, "0001")));
        suite.addTest(new EmployeeDeleteTest(manager.getObject(employeeClass, "0002")));
        suite.addTest(new EmployeeDeleteTest(manager.getObject(employeeClass, "0003")));
        suite.addTest(new EmployeeDeleteTest(manager.getObject(employeeClass, "0004")));
        suite.addTest(new EmployeeDeleteTest(manager.getObject(employeeClass, "0005")));

        suite.addTest(new ProjectDeleteTest(manager.getObject(smallProjectClass, "0001")));
        suite.addTest(new ProjectDeleteTest(manager.getObject(smallProjectClass, "0002")));
        suite.addTest(new ProjectDeleteTest(manager.getObject(smallProjectClass, "0003")));

        suite.addTest(new ProjectDeleteTest(manager.getObject(largeProjectClass, "0001")));
        suite.addTest(new ProjectDeleteTest(manager.getObject(largeProjectClass, "0002")));
        suite.addTest(new ProjectDeleteTest(manager.getObject(largeProjectClass, "0003")));

        return suite;

    }

    public static TestSuite getInsertObjectTestSuite() {
        TestSuite suite = getSRGInsertObjectTestSuite();

        //Add new tests here ...
        return suite;
    }

    //SRG test set is maintained by QA only, do NOT add any new tests into it.
    public static TestSuite getSRGInsertObjectTestSuite() {
        TestSuite suite = new TestSuite();
        suite.setName("EmployeeInsertObjectTestSuite");
        suite.setDescription("This suite tests the insertion of each object in the employee demo.");
        EmployeePopulator system = new EmployeePopulator();

        suite.addTest(new InsertObjectTest(system.basicEmployeeExample1()));
        suite.addTest(new InsertObjectTest(system.basicEmployeeExample2()));
        suite.addTest(new InsertObjectTest(system.basicEmployeeExample3()));
        suite.addTest(new InsertObjectTest(system.basicEmployeeExample4()));
        suite.addTest(new InsertObjectTest(system.basicEmployeeExample5()));

        suite.addTest(new InsertObjectTest(system.basicSmallProjectExample1()));
        suite.addTest(new InsertObjectTest(system.basicSmallProjectExample2()));
        suite.addTest(new InsertObjectTest(system.basicSmallProjectExample3()));

        suite.addTest(new InsertObjectTest(system.basicLargeProjectExample1()));
        suite.addTest(new InsertObjectTest(system.basicLargeProjectExample2()));
        suite.addTest(new InsertObjectTest(system.basicLargeProjectExample3()));

        return suite;
    }

    public static TestSuite getReadAllTestSuite() {
        TestSuite suite = getSRGReadAllTestSuite();

        //Add new tests here ...
        Expression orderBy = new ExpressionBuilder().get("firstName").ascending();
        Call call = new SQLCall("SELECT t0.VERSION, t1.EMP_ID, t0.L_NAME, t0.F_NAME, t1.SALARY, t0.EMP_ID, t0.GENDER FROM OTOJT_EMPLOYEE t0, OTOJT_SALARY t1 WHERE t1.EMP_ID = t0.EMP_ID");
        suite.addTest(new ReadAllCallWithOrderingTest(Employee.class, 12, call, orderBy));

        suite.addTest(new JoinTest());
        suite.addTest(new JoinTest_SelectByFirstName());
        suite.addTest(new BatchReadingTest());
        suite.addTest(new BatchReadingTest_SelectByFirstName());

        return suite;
    }

    //SRG test set is maintained by QA only, do NOT add any new tests into it.
    public static TestSuite getSRGReadAllTestSuite() {
        TestSuite suite = new TestSuite();
        suite.setName("EmployeeReadAllTestSuite");
        suite.setDescription("This suite tests the reading of all the objects of each class in the employee demo.");

        suite.addTest(new ReadAllTest(Employee.class, 12));
        suite.addTest(new ReadAllTest(Project.class, 15));
        suite.addTest(new ReadAllTest(LargeProject.class, 5));
        suite.addTest(new ReadAllTest(SmallProject.class, 10));

        suite.addTest(new ReadAllCallTest(Employee.class, 12, new SQLCall("SELECT  t0.VERSION, t1.EMP_ID, t0.L_NAME, t0.F_NAME, t1.SALARY, t0.EMP_ID, t0.GENDER FROM OTOJT_EMPLOYEE t0, OTOJT_SALARY t1 WHERE t1.EMP_ID = t0.EMP_ID")));

        return suite;
    }

    public static TestSuite getReadObjectTestSuite() {
        TestSuite suite = getSRGReadObjectTestSuite();

        //Add new tests here ...
        return suite;
    }

    //SRG test set is maintained by QA only, do NOT add any new tests into it.
    public static TestSuite getSRGReadObjectTestSuite() {
        TestSuite suite = new TestSuite();
        suite.setName("EmployeeReadObjectTestSuite");
        suite.setDescription("This suite test the reading of each object in the employee demo.");

        Class<Employee> employeeClass = Employee.class;
        Class<LargeProject> largeProjectClass = LargeProject.class;
        Class<SmallProject> smallProjectClass = SmallProject.class;
        PopulationManager manager = PopulationManager.getDefaultManager();

        suite.addTest(new ReadObjectTest(manager.getObject(employeeClass, "0001")));
        suite.addTest(new ReadObjectTest(manager.getObject(employeeClass, "0002")));
        suite.addTest(new ReadObjectTest(manager.getObject(employeeClass, "0003")));
        suite.addTest(new ReadObjectTest(manager.getObject(employeeClass, "0004")));
        suite.addTest(new ReadObjectTest(manager.getObject(employeeClass, "0005")));
        Employee employee = (Employee)manager.getObject(employeeClass, "0001");

        suite.addTest(new ReadObjectCallTest(employeeClass, new SQLCall("SELECT t0.VERSION, t1.EMP_ID, t0.L_NAME, t0.F_NAME, t1.SALARY, t0.EMP_ID, t0.GENDER FROM OTOJT_EMPLOYEE t0, OTOJT_SALARY t1 WHERE t1.EMP_ID = t0.EMP_ID AND t0.F_NAME = '"+employee.getFirstName()+"' AND t0.L_NAME = '"+employee.getLastName()+"'")));
        employee = (Employee)manager.getObject(employeeClass, "0002");
        suite.addTest(new ReadObjectCallTest(employeeClass, new SQLCall("SELECT  t0.VERSION, t1.EMP_ID, t0.L_NAME, t0.F_NAME, t1.SALARY, t0.EMP_ID, t0.GENDER FROM OTOJT_EMPLOYEE t0, OTOJT_SALARY t1 WHERE t1.EMP_ID = t0.EMP_ID AND t0.F_NAME = '"+employee.getFirstName()+"' AND t0.L_NAME = '"+employee.getLastName()+"'")));
        employee = (Employee)manager.getObject(employeeClass, "0003");
        suite.addTest(new ReadObjectCallTest(employeeClass, new SQLCall("SELECT  t0.VERSION, t1.EMP_ID, t0.L_NAME, t0.F_NAME, t1.SALARY, t0.EMP_ID, t0.GENDER FROM OTOJT_EMPLOYEE t0, OTOJT_SALARY t1 WHERE t1.EMP_ID = t0.EMP_ID AND t0.F_NAME = '"+employee.getFirstName()+"' AND t0.L_NAME = '"+employee.getLastName()+"'")));

        Project project = (Project)manager.getObject(largeProjectClass, "0001");
        ReadObjectTest test = new ReadObjectTest(project);
        test.setQuery(new org.eclipse.persistence.queries.ReadObjectQuery(Project.class, new org.eclipse.persistence.expressions.ExpressionBuilder().get("id").equal(project.getId())));
        suite.addTest(test);

        suite.addTest(new ReadObjectTest(manager.getObject(smallProjectClass, "0001")));
        suite.addTest(new ReadObjectTest(manager.getObject(smallProjectClass, "0002")));
        suite.addTest(new ReadObjectTest(manager.getObject(smallProjectClass, "0003")));

        suite.addTest(new ReadObjectTest(manager.getObject(largeProjectClass, "0001")));
        suite.addTest(new ReadObjectTest(manager.getObject(largeProjectClass, "0002")));
        suite.addTest(new ReadObjectTest(manager.getObject(largeProjectClass, "0003")));

        return suite;
    }

    public static TestSuite getUpdateObjectTestSuite() {
        TestSuite suite = getSRGUpdateObjectTestSuite();

        //Add new tests here ...
        suite.addTest(new ManyToManyReadOnlyMappingUpdateTest());
        suite.addTest(new AddRemoveTest());
        return suite;
    }

    //SRG test set is maintained by QA only, do NOT add any new tests into it.
    public static TestSuite getSRGUpdateObjectTestSuite() {
        TestSuite suite = new TestSuite();
        suite.setName("EmployeeUpdateObjectTestSuite");
        suite.setDescription("This suite tests the updating of each object in the employee demo.");

        Class<Employee> employeeClass = Employee.class;
        Class<LargeProject> largeProjectClass = LargeProject.class;
        Class<SmallProject> smallProjectClass = SmallProject.class;
        PopulationManager manager = PopulationManager.getDefaultManager();

        suite.addTest(new WriteObjectTest(manager.getObject(employeeClass, "0001")));
        suite.addTest(new UnitOfWorkBasicUpdateObjectTest(manager.getObject(employeeClass, "0001")));
        suite.addTest(new UnitOfWorkBasicUpdateObjectTest(manager.getObject(employeeClass, "0002")));
        suite.addTest(new UnitOfWorkBasicUpdateObjectTest(manager.getObject(employeeClass, "0003")));
        suite.addTest(new UnitOfWorkBasicUpdateObjectTest(manager.getObject(employeeClass, "0004")));
        suite.addTest(new UnitOfWorkBasicUpdateObjectTest(manager.getObject(employeeClass, "0005")));

        suite.addTest(new WriteObjectTest(manager.getObject(smallProjectClass, "0001")));
        suite.addTest(new UnitOfWorkBasicUpdateObjectTest(manager.getObject(smallProjectClass, "0001")));
        suite.addTest(new UnitOfWorkBasicUpdateObjectTest(manager.getObject(smallProjectClass, "0002")));
        suite.addTest(new UnitOfWorkBasicUpdateObjectTest(manager.getObject(smallProjectClass, "0003")));

        suite.addTest(new WriteObjectTest(manager.getObject(largeProjectClass, "0001")));
        suite.addTest(new UnitOfWorkBasicUpdateObjectTest(manager.getObject(largeProjectClass, "0001")));
        suite.addTest(new UnitOfWorkBasicUpdateObjectTest(manager.getObject(largeProjectClass, "0002")));
        suite.addTest(new UnitOfWorkBasicUpdateObjectTest(manager.getObject(largeProjectClass, "0003")));

        return suite;
    }

    static class ManyToManyReadOnlyMappingUpdateTest extends ComplexUpdateTest {
        ManyToManyReadOnlyMappingUpdateTest() {
            super();
            usesUnitOfWork = true;
            setName("ManyToManyReadOnlyMappingUpdateTest");
        }
        @Override
        public void setup() {
            // find Project with at least one Employee
            List<Project> projects = getSession().readAllObjects(Project.class);
            for(int i=0; i < projects.size(); i++) {
                if(projects.get(i).getEmployees().size() > 0) {
                    originalObject = projects.get(i);
                    break;
                }
            }

            super.setup();
        }
        // remove all employees
        @Override
        protected void changeObject() {
            Project project = (Project)this.workingCopy;
            int size = project.getEmployees().size();
            if(size == 0) {
                throw new TestProblemException("Project was supposed to have Employees, but doesn't have any.");
            } else {
                for(int i = size-1; 0 <= i; i--) {
                    project.removeEmployee(project.getEmployees().get(i));
                }
            }
        }
    }

    static class AddRemoveTest extends TestCase {
        Employee employee;
        Address address1, address2;
        Project project1, project2;
        boolean useAddress;
        boolean useProjects;
        AddRemoveTest() {
            super();
            setName("AddRemoveTest");
        }
        @Override
        public void setup() {
            useAddress = true;
            useProjects = true;

        }
        @Override
        public void test() {
            employee = new Employee();
            employee.setFirstName("AddRemoveTest");
            if(useAddress) {
                address1 = new Address();
                address1.setCity("city1");
            }
            if(useProjects) {
                project1 = new SmallProject("project1");
            }

            // insert employee with address/project
            UnitOfWork uow = getSession().acquireUnitOfWork();
            Employee employeeClone = (Employee)uow.registerObject(employee);
            if(useAddress) {
                Address address1Clone = (Address)uow.registerObject(address1);
                employeeClone.setAddress(address1Clone);
            }
            if(useProjects) {
                Project project1Clone = (Project)uow.registerObject(project1);
                employeeClone.addProject(project1Clone);
            }
            uow.commit();

            // remove address/project from employee
            uow = getSession().acquireUnitOfWork();
            employeeClone = (Employee)uow.registerObject(employee);
            if(useAddress) {
                employeeClone.setAddress(null);
            }
            if(useProjects) {
                employeeClone.removeProject(employeeClone.getProjects().get(0));
            }
            uow.commit();

            // set address/project into employee
            uow = getSession().acquireUnitOfWork();
            employeeClone = (Employee)uow.registerObject(employee);
            if(useAddress) {
                Address address1Clone = (Address)uow.registerObject(address1);
                employeeClone.setAddress(address1Clone);
            }
            if(useProjects) {
                Project project1Clone = (Project)uow.registerObject(project1);
                employeeClone.addProject(project1Clone);
            }
            uow.commit();

            // override address/project in employee
            uow = getSession().acquireUnitOfWork();
            employeeClone = (Employee)uow.registerObject(employee);
            if(useAddress) {
                address2 = new Address();
                address2.setCity("city2");
                Address address2Clone = (Address)uow.registerObject(address2);
                employeeClone.setAddress(address2Clone);
            }
            if(useProjects) {
                project2 = new LargeProject("project2");
                Project project2Clone = (Project)uow.registerObject(project2);
                employeeClone.addProject(project2Clone);
            }
            uow.commit();

            // delete employee
            uow = getSession().acquireUnitOfWork();
            uow.deleteObject(employee);
            uow.commit();
            employee = null;
        }
        @Override
        public void reset() {
            UnitOfWork uow = getSession().acquireUnitOfWork();
            if(employee != null) {
                uow.deleteObject(employee);
            }
            if(useAddress) {
                if(address1 != null) {
                    uow.deleteObject(address1);
                }
                if(address2 != null) {
                    uow.deleteObject(address2);
                }
            }
            if(useProjects) {
                if(project1 != null) {
                    uow.deleteObject(project1);
                }
                if(project2 != null) {
                    uow.deleteObject(project2);
                }
            }
            uow.commit();
        }
    }

    static class JoinTest extends TestCase {
        public JoinTest() {
            super();
            setName("JoinTest - no selection criteria");
            setDescription("Tests reading of Employees with join expressions.");
        }
        void setSelectionCriteria(ReadAllQuery query) {
        }
        @Override
        public void test() {
            ReadAllQuery query = new ReadAllQuery();
            query.setReferenceClass(Employee.class);
            setSelectionCriteria(query);

            ReadAllQuery controlQuery = (ReadAllQuery)query.clone();

            Expression address = query.getExpressionBuilder().getAllowingNull("address");
            query.addJoinedAttribute(address);
            Expression projectLed = query.getExpressionBuilder().getAllowingNull("projectLed");
            query.addJoinedAttribute(projectLed);
            Expression manager = query.getExpressionBuilder().getAllowingNull("manager");
            query.addJoinedAttribute(manager);
            Expression managerProjectLed = manager.getAllowingNull("projectLed");
            query.addJoinedAttribute(managerProjectLed);

            String errorMsg = JoinedAttributeTestHelper.executeQueriesAndCompareResults(controlQuery, query, (AbstractSession)getSession());
            if(errorMsg.length() > 0) {
                throw new TestErrorException(errorMsg);
            }
        }
    }
    static class JoinTest_SelectByFirstName extends JoinTest {
        public JoinTest_SelectByFirstName() {
            super();
            setName("JoinTest - select by first name");
        }
        @Override
        void setSelectionCriteria(ReadAllQuery query) {
            query.setSelectionCriteria(query.getExpressionBuilder().get("firstName").like("J%"));
        }
    }

    static class BatchReadingTest extends TestCase {
        boolean shouldPrintDebugOutput = false;
        public BatchReadingTest() {
            setName("EmployeeBatchReadingTest - no selection criteria");
            setDescription("Tests batch reading of Employees with batch expression address");
        }
        void setSelectionCriteria(ReadAllQuery query) {
        }
        @Override
        public void test() {
            // clear cache
            getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
            // create batch read query, set its selectionCriteria
            ReadAllQuery query = new ReadAllQuery(Employee.class);
            setSelectionCriteria(query);
            // before adding batch read attributes clone the query to create control query
            ReadAllQuery controlQuery = (ReadAllQuery)query.clone();
            // add batch read attributes
            Expression addressExp = query.getExpressionBuilder().get("address");
            query.addBatchReadAttribute(addressExp);
            // execute the query
            List employees = (List)getSession().executeQuery(query);
            if(employees.isEmpty()) {
                throw new TestProblemException("No Employees were read");
            }
            // need to instantiate only a single Address to trigger sql that reads data from the db for all.
            // still need to trigger all the indirections - but (except the first one) they are not accessing the db
            // (the data is already cached in the value holders).
            printDebug("Trigger batch reading results");
            boolean isConnected = true;
            for(int i=0; i < employees.size(); i++) {
                Address address = ((Employee)employees.get(i)).getAddress();
                if(isConnected) {
                    // need to instantiate only a single Address to trigger sql that reads data from the db for all Addresses.
                    // to ensure that no other sql is issued close connection.
                    ((AbstractSession)getSession()).getAccessor().closeConnection();
                    isConnected = false;
                }
            }
            if(!isConnected) {
                // reconnect connection
                ((AbstractSession)getSession()).getAccessor().reestablishConnection((AbstractSession)getSession());
            }
            printDebug("");

            // obtain control results
            // clear cache
            getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
            // execute control query
            List controlEmployees = (List)getSession().executeQuery(controlQuery);
            // instantiate all value holders that the batch query expected to instantiate
            printDebug("Trigger control results");
            for(int i=0; i < controlEmployees.size(); i++) {
                Address address = ((Employee)controlEmployees.get(i)).getAddress();
            }

            // compare results
            String errorMsg = JoinedAttributeTestHelper.compareCollections(employees, controlEmployees, getSession().getClassDescriptor(Employee.class), ((AbstractSession)getSession()));
            if(errorMsg.length() > 0) {
                throw new TestErrorException(errorMsg);
            }
        }
        void printDebug(String msg) {
            if(this.shouldPrintDebugOutput) {
                System.out.println(msg);
            }
        }
    }
    static class BatchReadingTest_SelectByFirstName extends BatchReadingTest {
        public BatchReadingTest_SelectByFirstName() {
            super();
            setName("EmployeeBatchReadingTest - select by first name");
        }
        @Override
        void setSelectionCriteria(ReadAllQuery query) {
            query.setSelectionCriteria(query.getExpressionBuilder().get("firstName").like("J%"));
        }
    }

    // copied 6 methods from org.eclipse.persistence.testing.tests.expressions.ExpressionTestSuite
    static class ExpressionTestSuite extends TestSuite {
        public ExpressionTestSuite() {
            setName("ExpressionTestSuite");
            setDescription("This suite tests expressions.");
        }
        @Override
        public void addTests() {
            addMultipleAndsTest();
            addMultipleAndsTest2();
            addMultipleAndsTest3();
            addMultipleAndsTest4();
            addMultipleAndsTest5();
            addMultipleAndsTest6();
            addEqualUnneccessaryJoinTest();
            addManagersOfWealthyMarriedAtWorkEmployeesTest();
            addOneToManyJoin2WithBatchReadTest();
        }
        private void addMultipleAndsTest() {
            ExpressionBuilder builder = new ExpressionBuilder();

            //this is a bug, wrong generated SQL like... ADDRESS.ADDRESS_ID = EMPLOYEE.ADDR_ID...
            //however, it should be ... ADDRESS.ADDRESS_ID = 0...
            Expression expression = builder.get("address").equal(new Address()).and(builder.get("lastName").notEqual("foopoyp"));

            ReadAllExpressionTest test = new ReadAllExpressionTest(Employee.class, 0);
            test.setExpression(expression);
            test.setName("MultipleAndsExpressionTest");
            test.setDescription("Test object equality with object will null primary key");
            addTest(test);
        }

        private void addMultipleAndsTest2() {
            //there is a bug, generated SQL looks like ... (ADDRESS.ADDRESS_ID = 123456)) AND (ADDRESS.ADDRESS_ID = EMPLOYEE.ADDR_ID)...
            //should be: ...EMPLOYEE.ADDR_ID = 123456...  no join needed!
            ExpressionBuilder builder = new ExpressionBuilder();
            Address a = new Address();
            a.setId(new java.math.BigDecimal(123456));

            Expression expression = builder.get("address").equal(a).and(builder.get("id").greaterThan(800));

            ReadAllExpressionTest test = new ReadAllExpressionTest(Employee.class, 0);
            test.setQuery(new ReadAllQuery(Employee.class, expression));
            test.getQuery().addAscendingOrdering("id");
            test.setExpression(expression);
            test.setName("MultipleAndsExpressionTest2");
            test.setDescription("Test multiple ands with object equality");
            addTest(test);
        }

        private void addMultipleAndsTest3() {
            //there is a bug, generated wrong SQL looks like ...  FROM ADDRESS t3, SALARY t2, EMPLOYEE t1 WHERE (((((t1.EMP_ID > '800') AND ) AND (t1.EMP_ID = t2.EMP_ID)) AND (t3.ADDRESS_ID = 123456))...
            ExpressionBuilder builder = new ExpressionBuilder();

            Address a = new Address();
            a.setId(new java.math.BigDecimal(123456));

            Expression expression = builder.get("id").greaterThan(800).and(builder.get("address").equal(a));
            ReadAllExpressionTest test = new ReadAllExpressionTest(Employee.class, 0);
            test.setQuery(new ReadAllQuery(Employee.class, expression));
            test.getQuery().addAscendingOrdering("id");
            test.setExpression(expression);
            test.setName("MultipleAndsExpressionTest3");
            test.setDescription("Test multiple ands with object equality");
            addTest(test);
        }

        private void addMultipleAndsTest4() {
            // This tests the case of a tree where the top-level AND should print, even though
            // both branches underneath it are partly suppressed.
            ExpressionBuilder builder = new ExpressionBuilder();
            Address address1 = new Address();
            address1.setId(new java.math.BigDecimal(999999876));

            Address address2 = new Address();
            address2.setId(new java.math.BigDecimal(999999877));

            Expression expression1 = builder.get("address").equal(address1).or(builder.get("lastName").equal("Smith"));
            Expression expression2 = builder.get("address").equal(address2).or(builder.get("firstName").equal("Bob"));

            ReadAllExpressionTest test = new ReadAllExpressionTest(Employee.class, 1);
            test.setExpression(expression1.and(expression2));
            test.setName("MultipleAndsExpressionTest4");
            test.setDescription("Test multiple booleans with supression in each branch");
            addTest(test);
        }

        private void addMultipleAndsTest5() {
            ExpressionBuilder builder = new ExpressionBuilder();

            //this is a bug, Ill-formed expression in query, attempting to print an object reference into a
            //SQL statement for Query Key address{DatabaseTable(t1)=DatabaseTable(ADDRESS)}
            //however, it should be ... EMPLOYEE.ADDRESS_ID = 0 (null)...
            Expression expression = builder.get("address").equal(null).and(builder.get("lastName").notEqual("foopoyp"));

            ReadAllExpressionTest test = new ReadAllExpressionTest(Employee.class, 0);
            test.setExpression(expression);
            test.setName("MultipleAndsExpressionTest5");
            test.setDescription("Test multiple ands expression");
            addTest(test);
        }

        private void addMultipleAndsTest6() {
            ExpressionBuilder builder = new ExpressionBuilder();

            //this is a bug, Ill-formed expression in query, attempting to print an object reference into a
            //SQL statement for Query Key address{DatabaseTable(t1)=DatabaseTable(ADDRESS)}
            //however, it should be ... EMPLOYEE.ADDRESS_ID = 0 (null)...
            Expression expression = builder.get("address").isNull().and(builder.get("lastName").notEqual("foopoyp"));

            ReadAllExpressionTest test = new ReadAllExpressionTest(Employee.class, 0);
            test.setExpression(expression);
            test.setName("MultipleAndsExpressionTest6");
            test.setDescription("Test multiple ands expression");
            addTest(test);
        }

        /*
         * bug 5683148/2380: Reducing unnecessary joins on an equality check between the a statement
         *   and itself
         */
        private void addEqualUnneccessaryJoinTest() {
            Employee employee = (Employee)PopulationManager.getDefaultManager().getObject(new Employee().getClass(), "0008");

            ExpressionBuilder builder = new ExpressionBuilder();
            Expression expression = builder.get("firstName").equal("Fred").or(builder.get("manager").notEqual(builder.get("manager")));

            ReadObjectExpressionTest test = new ReadObjectExpressionTest(employee, expression);
            test.setName("EqualUnneccessaryJoinTest");
            test.setDescription("Test = expression does not create an extra unneccessary join");
            addTest(test);
        }

        /**
         *  @bug 2612185 Support ReportItems,OrderBy Expressions from Parallel Builders.
         *  Find all managers of employees who have a spouse at work and a family
         *  income in excess of 100,000.
         */
        private void addManagersOfWealthyMarriedAtWorkEmployeesTest() {
            ExpressionBuilder builder = new ExpressionBuilder();
            ExpressionBuilder innerBuilder = new ExpressionBuilder(Employee.class);
            ExpressionBuilder innerSpouses = new ExpressionBuilder(Employee.class);

            Expression innerExpression = innerBuilder.get("manager").equal(builder);
            innerExpression = innerExpression.and(innerBuilder.get("lastName").equal(innerSpouses.get("lastName")));
            innerExpression = innerExpression.and(innerBuilder.get("gender").notEqual(innerSpouses.get("gender")));

            ReportQuery subquery = new ReportQuery(Employee.class, innerBuilder);
            subquery.addAverage("family income", ExpressionMath.add(innerBuilder.get("salary"), innerSpouses.get("salary")));
            subquery.setSelectionCriteria(innerExpression);

            Expression expression = builder.subQuery(subquery).equal(140000);

            ReadAllExpressionTest test = new ReadAllExpressionTest(Employee.class, 2);
            test.setExpression(expression);
            test.testBatchAttributesOnEmployee();
            test.setName("ManagersOfWealthyMarriedAtWorkEmployeesTest");
            test.setDescription("Test executing query where subselect is a ReportQuery with ReportItems from multiple builders.  Does not tests batch reading.  For 2612185.");
            test.addUnsupportedPlatform(TimesTenPlatform.class);
            addTest(test);
        }
        /**
         * @bug 2720149 INVALID SQL WHEN USING BATCH READS AND MULTIPLE ANYOFS
         */
        private void addOneToManyJoin2WithBatchReadTest() {
            ExpressionBuilder builder = new ExpressionBuilder();
            Expression expression = builder.anyOf("managedEmployees").get("lastName").like("Sa%");
            Expression exp2 = builder.anyOf("managedEmployees").get("firstName").like("B%");

            expression = expression.and(exp2);

            ReadAllExpressionTest test = new ReadAllExpressionTest(Employee.class, 1);
            test.setExpression(expression);
            test.testBatchAttributesOnEmployee();
            test.setName("OneToManyJoin2WithBatchReadTest");
            test.setDescription("Test a join across a 1:many relation with 2 anyOf clauses, and test again as part of a batch read.");
            addTest(test);
        }
    }

    public static TestSuite getUOWInsertObjectTestSuite() {
        TestSuite suite = new TestSuite();
        suite.setName("EmployeeUOWBasicInsertObjectTestSuite");
        suite.setDescription("This suite tests the insertion of each object in the employee demo using uow.");
        EmployeePopulator populator = new EmployeePopulator();

        suite.addTest(new UnitOfWorkBasicInsertObjectTest(populator.basicEmployeeExample1()));
        suite.addTest(new UnitOfWorkBasicInsertObjectTest(populator.basicEmployeeExample2()));
        suite.addTest(new UnitOfWorkBasicInsertObjectTest(populator.basicEmployeeExample3()));
        suite.addTest(new UnitOfWorkBasicInsertObjectTest(populator.basicEmployeeExample4()));
        suite.addTest(new UnitOfWorkBasicInsertObjectTest(populator.basicEmployeeExample5()));

        return suite;
    }

    static class EmployeeDeleteTest extends DeleteObjectTest {
        public EmployeeDeleteTest(Object originalObject) {
            super(originalObject);
        }

        @Override
        protected void setup() {
            super.setup();
            // Must drop references first to appease constraints.
            getSession().executeNonSelectingCall(new SQLCall("delete from OTOJT_PROJ_LEADER where EMP_ID = " + ((Employee)getOriginalObject()).getId()));
            getSession().executeNonSelectingCall(new SQLCall("delete from OTOJT_EMP_MANAGER where MANAGER_ID = " + ((Employee)getOriginalObject()).getId()));
        }
    }
    static class ProjectDeleteTest extends DeleteObjectTest {
        public ProjectDeleteTest(Object originalObject) {
            super(originalObject);
        }

        @Override
        protected void setup() {
            super.setup();
            // Must drop references first to appease constraints.
            getSession().executeNonSelectingCall(new SQLCall("delete from OTOJT_PROJ_EMP where PROJ_ID = " + ((Project)getOriginalObject()).getId()));
        }
    }

    static class ReadAllExpressionTest extends org.eclipse.persistence.testing.tests.expressions.ReadAllExpressionTest {
        public ReadAllExpressionTest(Class<?> referenceClass, int originalObjectsSize) {
            super(referenceClass, originalObjectsSize);
        }

        @Override
        protected void setupBatchAttributes() {
            getQuery().addBatchReadAttribute("children");
            getQuery().addBatchReadAttribute("address");
            getQuery().addBatchReadAttribute("responsibilitiesList");
            getQuery().addBatchReadAttribute("projects");
        }

        @Override
        protected void testBatchAttributes() {
            Vector result = (Vector) this.objectsFromDatabase;
            List<Child> children = ((Employee) result.elementAt(0)).getChildren();
            ((Employee) result.elementAt(0)).getResponsibilitiesList().size();
            ((Employee) result.elementAt(0)).getProjects().size();
            if ((children == null) || (children.size() == 0)) {
                    throw new TestErrorException("The original query was corrupted when made part of a batch query.");
            }
            if (((Employee) result.elementAt(0)).getAddress() == null) {
                    throw new TestErrorException("The original query was corrupted when made part of a batch query.");
            }
        }
    }
}
