-
Bug
-
Resolution: Unresolved
-
Major
-
None
-
None
-
None
-
None
-
False
-
-
False
-
NEW
-
NEW
-
---
-
---
- The Problem
Some unit tests are non-idempotent, as they pass in the first run but fail in the second run in the same environment. A fix is necessary since unit tests shall be self-contained, ensuring that the state of the system under test is consistent at the beginning of each test. In practice, fixing non-idempotent tests can help proactively avoid state pollution that results in test order dependency (which could cause problems under test selection , prioritization or parallelization).
- Reproduce
Using the `NIOInspector` plugin that supports rerunning JUnit tests in the same environment. Use `EventHnadlingTest#testRunMultiEventProcessPerRequestRuntimeManager` as an example:
```
cd jbpm-runtime-manager
mvn edu.illinois:NIOInspector:rerun -Dtest=org.jbpm.runtime.manager.concurrent.EventHnadlingTest#testRunMultiEventProcessPerRequestRuntimeManager
```
- 3 Non-Idempotent Tests & Proposed Fix
- EventHnadlingTest.testRunMultiEventProcessPerRequestRuntimeManager
Reason: With "Per Request" strategy, for every call to `getRuntimeEngine()`, a new instance will usually be delivered with brand new `KieSession`. However, the exception to this is when `getRuntimeEngine()` invoked within the same transaction from different places (which would be the case if `testRunMultiEventProcessPerRequestRuntimeManager` is re-executed). In that case, the manager caches the currently active instance. Therefore, in the second execution of the test, an error will be thrown since the retrieved (cached) `KieSession` is already closed by the previous test execution. To resolve this, the runtime engine manager shall be disposed to ensure that its `KieSession` is destroyed as well.
Error message of one of the tests in the repeated run:
```
java.lang.IllegalStateException: Session/EntityManager is closed
- EventHnadlingTest.testRunMultiEventProcessPerRequestRuntimeManager
at org.hibernate.internal.AbstractSharedSessionContract.checkOpen(AbstractSharedSessionContract.java:375)
at org.hibernate.engine.spi.SharedSessionContractImplementor.checkOpen(SharedSessionContractImplementor.java:148)
at org.hibernate.internal.SessionImpl.joinTransaction(SessionImpl.java:3633)
at org.drools.persistence.jpa.JpaPersistenceContext.joinTransaction(JpaPersistenceContext.java:106)
at org.drools.persistence.PersistableRunner$TransactionInterceptor.execute(PersistableRunner.java:603)
at org.drools.persistence.PersistableRunner$TransactionInterceptor.execute(PersistableRunner.java:572)
at org.drools.core.command.impl.AbstractInterceptor.executeNext(AbstractInterceptor.java:39)
at org.drools.persistence.jpa.OptimisticLockRetryInterceptor.internalExecute(OptimisticLockRetryInterceptor.java:102)
at org.drools.persistence.jpa.OptimisticLockRetryInterceptor.execute(OptimisticLockRetryInterceptor.java:83)
at org.drools.persistence.jpa.OptimisticLockRetryInterceptor.execute(OptimisticLockRetryInterceptor.java:44)
at org.drools.core.command.impl.AbstractInterceptor.executeNext(AbstractInterceptor.java:39)
at org.drools.persistence.jta.TransactionLockInterceptor.execute(TransactionLockInterceptor.java:73)
at org.drools.persistence.jta.TransactionLockInterceptor.execute(TransactionLockInterceptor.java:45)
at org.drools.core.command.impl.AbstractInterceptor.executeNext(AbstractInterceptor.java:39)
at org.jbpm.runtime.manager.impl.error.ExecutionErrorHandlerInterceptor.internalExecute(ExecutionErrorHandlerInterceptor.java:66)
at org.jbpm.runtime.manager.impl.error.ExecutionErrorHandlerInterceptor.execute(ExecutionErrorHandlerInterceptor.java:52)
at org.jbpm.runtime.manager.impl.error.ExecutionErrorHandlerInterceptor.execute(ExecutionErrorHandlerInterceptor.java:29)
at org.drools.persistence.PersistableRunner.execute(PersistableRunner.java:400)
at org.drools.persistence.PersistableRunner.execute(PersistableRunner.java:68)
at org.drools.core.runtime.InternalLocalRunner.execute(InternalLocalRunner.java:37)
at org.drools.core.runtime.InternalLocalRunner.execute(InternalLocalRunner.java:41)
at org.drools.core.command.impl.CommandBasedStatefulKnowledgeSession.startProcess(CommandBasedStatefulKnowledgeSession.java:275)
at org.drools.core.command.impl.CommandBasedStatefulKnowledgeSession.startProcess(CommandBasedStatefulKnowledgeSession.java:259)
at org.jbpm.runtime.manager.concurrent.EventHnadlingTest.testRunMultiEventProcessPerRequestRuntimeManager(EventHnadlingTest.java:86)
```
Fix: Add the line `manager.disposeRuntimeEngine(runtime);` before closing the manager
-
- PerCaseRuntimeManagerTest#testMultipleProcessesInSingleCaseCompletedInSequence
Reason: The "Per Case" strategy specifies that every process instance that belongs to same case will be bound to a single `ksession` for its entire life time. Once started, whenever such operations are invoked, the manager will retrieve the same `ksession`. Similarly, `manager.disposeRuntimeEngine(runtime)` shall be called before `manager.close()` to ensure that the second test execution does not directly retrieve the already closed `ksession`.
Error message in the repeated run:
```
java.lang.IllegalStateException: Session/EntityManager is closed
- PerCaseRuntimeManagerTest#testMultipleProcessesInSingleCaseCompletedInSequence
at org.hibernate.internal.AbstractSharedSessionContract.checkOpen(AbstractSharedSessionContract.java:375)
at org.hibernate.engine.spi.SharedSessionContractImplementor.checkOpen(SharedSessionContractImplementor.java:148)
at org.hibernate.internal.SessionImpl.joinTransaction(SessionImpl.java:3633)
at org.drools.persistence.jpa.JpaPersistenceContext.joinTransaction(JpaPersistenceContext.java:106)
at org.drools.persistence.PersistableRunner$TransactionInterceptor.execute(PersistableRunner.java:603)
at org.drools.persistence.PersistableRunner$TransactionInterceptor.execute(PersistableRunner.java:572)
at org.drools.core.command.impl.AbstractInterceptor.executeNext(AbstractInterceptor.java:39)
at org.drools.persistence.jpa.OptimisticLockRetryInterceptor.internalExecute(OptimisticLockRetryInterceptor.java:102)
at org.drools.persistence.jpa.OptimisticLockRetryInterceptor.execute(OptimisticLockRetryInterceptor.java:83)
at org.drools.persistence.jpa.OptimisticLockRetryInterceptor.execute(OptimisticLockRetryInterceptor.java:44)
at org.drools.core.command.impl.AbstractInterceptor.executeNext(AbstractInterceptor.java:39)
at org.drools.persistence.jta.TransactionLockInterceptor.execute(TransactionLockInterceptor.java:73)
at org.drools.persistence.jta.TransactionLockInterceptor.execute(TransactionLockInterceptor.java:45)
at org.drools.core.command.impl.AbstractInterceptor.executeNext(AbstractInterceptor.java:39)
at org.jbpm.runtime.manager.impl.error.ExecutionErrorHandlerInterceptor.internalExecute(ExecutionErrorHandlerInterceptor.java:66)
at org.jbpm.runtime.manager.impl.error.ExecutionErrorHandlerInterceptor.execute(ExecutionErrorHandlerInterceptor.java:52)
at org.jbpm.runtime.manager.impl.error.ExecutionErrorHandlerInterceptor.execute(ExecutionErrorHandlerInterceptor.java:29)
at org.drools.persistence.PersistableRunner.execute(PersistableRunner.java:400)
at org.drools.persistence.PersistableRunner.execute(PersistableRunner.java:68)
at org.drools.core.runtime.InternalLocalRunner.execute(InternalLocalRunner.java:37)
at org.drools.core.runtime.InternalLocalRunner.execute(InternalLocalRunner.java:41)
at org.drools.core.command.impl.CommandBasedStatefulKnowledgeSession.getIdentifier(CommandBasedStatefulKnowledgeSession.java:134)
at org.jbpm.runtime.manager.impl.PerCaseRuntimeManagerTest.testMultipleProcessesInSingleCaseCompletedInSequence(PerCaseRuntimeManagerTest.java:349)
```
Fix: Same as the previous test: add `manager.disposeRuntimeEngine(runtime);` before closing the manager
-
- PerRequestRuntimeManagerTest#testMultiplePerRequestManagerFromSingleThread
Reason: This is actually a typo - the process instance `runtime2` is managed by `manager2`, not `manager`.
The original code is
```
manager.disposeRuntimeEngine(runtime1);
manager.disposeRuntimeEngine(runtime2);
```
It shall be changed to
```
manager.disposeRuntimeEngine(runtime1);
manager2.disposeRuntimeEngine(runtime2);
```
Error message of one of the tests in the repeated run:
```
java.lang.IllegalStateException: RuntimeManager with id second is already active
- PerRequestRuntimeManagerTest#testMultiplePerRequestManagerFromSingleThread
at org.jbpm.runtime.manager.impl.AbstractRuntimeManager.<init>(AbstractRuntimeManager.java:141)
at org.jbpm.runtime.manager.impl.PerRequestRuntimeManager.<init>(PerRequestRuntimeManager.java:74)
at org.jbpm.runtime.manager.impl.RuntimeManagerFactoryImpl.newPerRequestRuntimeManager(RuntimeManagerFactoryImpl.java:73)
at org.jbpm.runtime.manager.impl.PerRequestRuntimeManagerTest.testMultiplePerRequestManagerFromSingleThread(PerRequestRuntimeManagerTest.java:543)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
```
Fix: As described above
- Verifying this change
After the patch, running the tests repeatedly in the same environment will not lead to failures.