| /* | |
| * 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 | |
| * | |
| * https://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 javax.jdo; | |
| import java.lang.reflect.InvocationHandler; | |
| import java.lang.reflect.Method; | |
| import java.lang.reflect.Proxy; | |
| import javax.jdo.spi.PersistenceCapable; | |
| import javax.jdo.util.AbstractTest; | |
| import org.junit.jupiter.api.Assertions; | |
| import org.junit.jupiter.api.Test; | |
| /* | |
| * ObjectStateTest.java | |
| * This test class verifies the proper returned ObjectState for | |
| * each life cycle state. See Table 3 in section 7.4. | |
| * | |
| * @since 2.1 | |
| */ | |
| class ObjectStateTest extends AbstractTest { | |
| static final int PERSISTENT = 1; | |
| static final int TRANSACTIONAL = 2; | |
| static final int DIRTY = 4; | |
| static final int NEW = 8; | |
| static final int DELETED = 16; | |
| static final int DETACHED = 32; | |
| private static final Method jdoIsPersistent = | |
| getDeclaredMethod(PersistenceCapable.class, "jdoIsPersistent", null); | |
| private static final Method jdoIsTransactional = | |
| getDeclaredMethod(PersistenceCapable.class, "jdoIsTransactional", null); | |
| private static final Method jdoIsDirty = | |
| getDeclaredMethod(PersistenceCapable.class, "jdoIsDirty", null); | |
| private static final Method jdoIsNew = | |
| getDeclaredMethod(PersistenceCapable.class, "jdoIsNew", null); | |
| private static final Method jdoIsDeleted = | |
| getDeclaredMethod(PersistenceCapable.class, "jdoIsDeleted", null); | |
| private static final Method jdoIsDetached = | |
| getDeclaredMethod(PersistenceCapable.class, "jdoIsDetached", null); | |
| /** */ | |
| @Test | |
| void testNull() { | |
| PersistenceCapable mock = null; | |
| assertObjectState("null", null, mock); | |
| } | |
| @Test | |
| void testTransient() { | |
| PersistenceCapable mock = newMock(0); | |
| assertObjectState("transient", ObjectState.TRANSIENT, mock); | |
| } | |
| @Test | |
| void testTransientClean() { | |
| PersistenceCapable mock = newMock(TRANSACTIONAL); | |
| assertObjectState("transient-clean", ObjectState.TRANSIENT_CLEAN, mock); | |
| } | |
| @Test | |
| void testTransientDirty() { | |
| PersistenceCapable mock = newMock(TRANSACTIONAL + DIRTY); | |
| assertObjectState("transient-dirty", ObjectState.TRANSIENT_DIRTY, mock); | |
| } | |
| @Test | |
| void testPersistentNew() { | |
| PersistenceCapable mock = newMock(PERSISTENT + TRANSACTIONAL + NEW + DIRTY); | |
| assertObjectState("persistent-new", ObjectState.PERSISTENT_NEW, mock); | |
| } | |
| @Test | |
| void testPersistentNontransactional() { | |
| PersistenceCapable mock = newMock(PERSISTENT); | |
| assertObjectState( | |
| "persistent-nontransactional", ObjectState.HOLLOW_PERSISTENT_NONTRANSACTIONAL, mock); | |
| } | |
| @Test | |
| void testPersistentNontransactionalDirty() { | |
| PersistenceCapable mock = newMock(PERSISTENT + DIRTY); | |
| assertObjectState( | |
| "persistent-nontransactional-dirty", ObjectState.PERSISTENT_NONTRANSACTIONAL_DIRTY, mock); | |
| } | |
| @Test | |
| void testPersistentClean() { | |
| PersistenceCapable mock = newMock(PERSISTENT + TRANSACTIONAL); | |
| assertObjectState("persistent-clean", ObjectState.PERSISTENT_CLEAN, mock); | |
| } | |
| @Test | |
| void testPersistentDirty() { | |
| PersistenceCapable mock = newMock(PERSISTENT + TRANSACTIONAL + DIRTY); | |
| assertObjectState("persistent-dirty", ObjectState.PERSISTENT_DIRTY, mock); | |
| } | |
| @Test | |
| void testPersistentDeleted() { | |
| PersistenceCapable mock = newMock(PERSISTENT + TRANSACTIONAL + DIRTY + DELETED); | |
| assertObjectState("persistent-deleted", ObjectState.PERSISTENT_DELETED, mock); | |
| } | |
| @Test | |
| void testPersistentNewDeleted() { | |
| PersistenceCapable mock = newMock(PERSISTENT + TRANSACTIONAL + NEW + DIRTY + DELETED); | |
| assertObjectState("persistent-new-deleted", ObjectState.PERSISTENT_NEW_DELETED, mock); | |
| } | |
| @Test | |
| void testDetachedClean() { | |
| PersistenceCapable mock = newMock(DETACHED); | |
| assertObjectState("detached-clean", ObjectState.DETACHED_CLEAN, mock); | |
| } | |
| @Test | |
| void testDetachedDirty() { | |
| PersistenceCapable mock = newMock(DETACHED + DIRTY); | |
| assertObjectState("detached-dirty", ObjectState.DETACHED_DIRTY, mock); | |
| } | |
| private void assertObjectState(String string, ObjectState expected, PersistenceCapable pc) { | |
| ObjectState actual = JDOHelper.getObjectState(pc); | |
| // test for == here because enums should be singleton | |
| if (actual == expected) return; | |
| Assertions.fail( | |
| "ObjectState failure for " + string + NL + "expected: " + expected + ", actual: " + actual); | |
| } | |
| /** | |
| * Construct a new mock instance of PersistenceCapable that responds to methods jdoIsXXX by | |
| * returning the bit value of its constructor masked with the value of XXX. | |
| * | |
| * @param i the sum of bit masks representing the life cycle state | |
| * @return a mock instance of PersistenceCapable | |
| */ | |
| private PersistenceCapable newMock(final int i) { | |
| return (PersistenceCapable) | |
| Proxy.newProxyInstance( | |
| PersistenceCapable.class.getClassLoader(), | |
| new Class[] {PersistenceCapable.class}, | |
| new MockInvocationHandler(i)); | |
| } | |
| private class MockInvocationHandler implements InvocationHandler { | |
| /** States is the sum of all life cycle interrogatives. */ | |
| private final int states; | |
| /** | |
| * Constructs an invocation handler with the specified bit fields set according to the sum of | |
| * values of PERSISTENT, TRANSACTIONAL, DIRTY, NEW, DELETED, and DETACHED. | |
| * | |
| * @param i the bit field values for the life cycle interrogatives | |
| */ | |
| private MockInvocationHandler(int i) { | |
| states = i; | |
| } | |
| /** | |
| * @param object the PersistenceCapable instance | |
| * @param method the method being invoked | |
| * @param parameters parameters (should be null) | |
| * @throws java.lang.Throwable unused | |
| * @return for jdoIsXXX methods only, returns whether the bit field selected by the method is | |
| * set in the mock handler | |
| */ | |
| public Object invoke(Object object, Method method, Object[] parameters) throws Throwable { | |
| if (method.equals(jdoIsPersistent)) { | |
| return (0 != (states & PERSISTENT)); | |
| } | |
| if (method.equals(jdoIsTransactional)) { | |
| return (0 != (states & TRANSACTIONAL)); | |
| } | |
| if (method.equals(jdoIsDirty)) { | |
| return (0 != (states & DIRTY)); | |
| } | |
| if (method.equals(jdoIsNew)) { | |
| return (0 != (states & NEW)); | |
| } | |
| if (method.equals(jdoIsDeleted)) { | |
| return (0 != (states & DELETED)); | |
| } | |
| if (method.equals(jdoIsDetached)) { | |
| return (0 != (states & DETACHED)); | |
| } | |
| Assertions.fail("Unexpected method called: " + method.getName()); | |
| return Boolean.FALSE; // not reached | |
| } | |
| } | |
| private static Method getDeclaredMethod( | |
| Class<?> clazz, String methodName, Class<?>[] parameters) { | |
| try { | |
| @SuppressWarnings("unchecked") | |
| Method result = clazz.getDeclaredMethod(methodName, parameters); | |
| return result; | |
| } catch (Exception ex) { | |
| // human-readable class.methodName(parameter[,parameter]) | |
| StringBuffer sb = new StringBuffer(methodName); | |
| String delimiter = "("; | |
| for (Class<?> parameter : parameters) { | |
| sb.append(delimiter); | |
| sb.append(parameter.getName()); | |
| delimiter = ","; | |
| } | |
| sb.append(")"); | |
| throw new RuntimeException( | |
| "getDeclaredMethod for " + clazz.getName() + "." + sb + " threw..." + ex); | |
| } | |
| } | |
| } |