/* * JBoss, Home of Professional Open Source. * Copyright 2014, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.wildfly.clustering.ee.infinispan; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import org.junit.After; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.wildfly.clustering.ee.BatchContext; import org.wildfly.clustering.ee.Batcher; /** * Unit test for {@link InfinispanBatcher}. * @author Paul Ferraro */ public class InfinispanBatcherTestCase { private final TransactionManager tm = mock(TransactionManager.class); private final Batcher<TransactionBatch> batcher = new InfinispanBatcher(this.tm); @After public void destroy() { InfinispanBatcher.CURRENT_BATCH.remove(); } @Test public void createExistingBatch() throws Exception { TransactionBatch existingBatch = mock(TransactionBatch.class); InfinispanBatcher.CURRENT_BATCH.set(existingBatch); when(existingBatch.interpose()).thenReturn(existingBatch); TransactionBatch result = this.batcher.createBatch(); verify(existingBatch).interpose(); verifyZeroInteractions(this.tm); assertSame(existingBatch, result); } @Test public void createBatchClose() throws Exception { Transaction tx = mock(Transaction.class); ArgumentCaptor<Synchronization> capturedSync = ArgumentCaptor.forClass(Synchronization.class); when(this.tm.getTransaction()).thenReturn(tx); try (TransactionBatch batch = this.batcher.createBatch()) { verify(this.tm).begin(); verify(tx).registerSynchronization(capturedSync.capture()); assertSame(tx, batch.getTransaction()); } finally { capturedSync.getValue().afterCompletion(Status.STATUS_COMMITTED); } verify(tx).commit(); assertNull(InfinispanBatcher.CURRENT_BATCH.get()); } @Test public void createBatchDiscard() throws Exception { Transaction tx = mock(Transaction.class); ArgumentCaptor<Synchronization> capturedSync = ArgumentCaptor.forClass(Synchronization.class); when(this.tm.getTransaction()).thenReturn(tx); try (TransactionBatch batch = this.batcher.createBatch()) { verify(this.tm).begin(); verify(tx).registerSynchronization(capturedSync.capture()); assertSame(tx, batch.getTransaction()); batch.discard(); } finally { capturedSync.getValue().afterCompletion(Status.STATUS_ROLLEDBACK); } verify(tx, never()).commit(); verify(tx).rollback(); assertNull(InfinispanBatcher.CURRENT_BATCH.get()); } @Test public void createNestedBatchClose() throws Exception { Transaction tx = mock(Transaction.class); ArgumentCaptor<Synchronization> capturedSync = ArgumentCaptor.forClass(Synchronization.class); when(this.tm.getTransaction()).thenReturn(tx); try (TransactionBatch outerBatch = this.batcher.createBatch()) { verify(this.tm).begin(); verify(tx).registerSynchronization(capturedSync.capture()); reset(this.tm); assertSame(tx, outerBatch.getTransaction()); when(this.tm.getTransaction()).thenReturn(tx); try (TransactionBatch innerBatch = this.batcher.createBatch()) { verify(this.tm, never()).begin(); verify(this.tm, never()).suspend(); } verify(tx, never()).rollback(); verify(tx, never()).commit(); } finally { capturedSync.getValue().afterCompletion(Status.STATUS_COMMITTED); } verify(tx, never()).rollback(); verify(tx).commit(); assertNull(InfinispanBatcher.CURRENT_BATCH.get()); } @Test public void createNestedBatchDiscard() throws Exception { Transaction tx = mock(Transaction.class); ArgumentCaptor<Synchronization> capturedSync = ArgumentCaptor.forClass(Synchronization.class); when(this.tm.getTransaction()).thenReturn(tx); try (TransactionBatch outerBatch = this.batcher.createBatch()) { verify(this.tm).begin(); verify(tx).registerSynchronization(capturedSync.capture()); reset(this.tm); assertSame(tx, outerBatch.getTransaction()); when(tx.getStatus()).thenReturn(Status.STATUS_ACTIVE); when(this.tm.getTransaction()).thenReturn(tx); try (TransactionBatch innerBatch = this.batcher.createBatch()) { verify(this.tm, never()).begin(); innerBatch.discard(); } verify(tx, never()).commit(); verify(tx, never()).rollback(); } finally { capturedSync.getValue().afterCompletion(Status.STATUS_ROLLEDBACK); } verify(tx).rollback(); verify(tx, never()).commit(); assertNull(InfinispanBatcher.CURRENT_BATCH.get()); } @SuppressWarnings("resource") @Test public void createOverlappingBatchClose() throws Exception { Transaction tx = mock(Transaction.class); ArgumentCaptor<Synchronization> capturedSync = ArgumentCaptor.forClass(Synchronization.class); when(this.tm.getTransaction()).thenReturn(tx); TransactionBatch batch = this.batcher.createBatch(); verify(this.tm).begin(); verify(tx).registerSynchronization(capturedSync.capture()); reset(this.tm); try { assertSame(tx, batch.getTransaction()); when(this.tm.getTransaction()).thenReturn(tx); when(tx.getStatus()).thenReturn(Status.STATUS_ACTIVE); try (TransactionBatch innerBatch = this.batcher.createBatch()) { verify(this.tm, never()).begin(); batch.close(); verify(tx, never()).rollback(); verify(tx, never()).commit(); } } finally { capturedSync.getValue().afterCompletion(Status.STATUS_COMMITTED); } verify(tx, never()).rollback(); verify(tx).commit(); assertNull(InfinispanBatcher.CURRENT_BATCH.get()); } @SuppressWarnings("resource") @Test public void createOverlappingBatchDiscard() throws Exception { Transaction tx = mock(Transaction.class); ArgumentCaptor<Synchronization> capturedSync = ArgumentCaptor.forClass(Synchronization.class); when(this.tm.getTransaction()).thenReturn(tx); TransactionBatch batch = this.batcher.createBatch(); verify(this.tm).begin(); verify(tx).registerSynchronization(capturedSync.capture()); reset(this.tm); try { assertSame(tx, batch.getTransaction()); when(this.tm.getTransaction()).thenReturn(tx); when(tx.getStatus()).thenReturn(Status.STATUS_ACTIVE); try (TransactionBatch innerBatch = this.batcher.createBatch()) { verify(this.tm, never()).begin(); innerBatch.discard(); batch.close(); verify(tx, never()).commit(); verify(tx, never()).rollback(); } } finally { capturedSync.getValue().afterCompletion(Status.STATUS_ROLLEDBACK); } verify(tx).rollback(); verify(tx, never()).commit(); assertNull(InfinispanBatcher.CURRENT_BATCH.get()); } @Test public void resumeNullBatch() throws Exception { TransactionBatch batch = mock(TransactionBatch.class); InfinispanBatcher.CURRENT_BATCH.set(batch); try (BatchContext context = this.batcher.resumeBatch(null)) { verifyZeroInteractions(this.tm); assertNull(InfinispanBatcher.CURRENT_BATCH.get()); } verifyZeroInteractions(this.tm); assertSame(batch, InfinispanBatcher.CURRENT_BATCH.get()); } @Test public void resumeNonTxBatch() throws Exception { TransactionBatch existingBatch = mock(TransactionBatch.class); InfinispanBatcher.CURRENT_BATCH.set(existingBatch); TransactionBatch batch = mock(TransactionBatch.class); try (BatchContext context = this.batcher.resumeBatch(batch)) { verifyZeroInteractions(this.tm); assertSame(batch, InfinispanBatcher.CURRENT_BATCH.get()); } verifyZeroInteractions(this.tm); assertSame(existingBatch, InfinispanBatcher.CURRENT_BATCH.get()); } @Test public void resumeBatch() throws Exception { TransactionBatch batch = mock(TransactionBatch.class); Transaction tx = mock(Transaction.class); when(batch.getTransaction()).thenReturn(tx); try (BatchContext context = this.batcher.resumeBatch(batch)) { verify(this.tm, never()).suspend(); verify(this.tm).resume(tx); reset(this.tm); assertSame(batch, InfinispanBatcher.CURRENT_BATCH.get()); } verify(this.tm).suspend(); verify(this.tm, never()).resume(any()); assertNull(InfinispanBatcher.CURRENT_BATCH.get()); } @Test public void resumeBatchExisting() throws Exception { TransactionBatch existingBatch = mock(TransactionBatch.class); Transaction existingTx = mock(Transaction.class); InfinispanBatcher.CURRENT_BATCH.set(existingBatch); TransactionBatch batch = mock(TransactionBatch.class); Transaction tx = mock(Transaction.class); when(existingBatch.getTransaction()).thenReturn(existingTx); when(batch.getTransaction()).thenReturn(tx); when(this.tm.suspend()).thenReturn(existingTx); try (BatchContext context = this.batcher.resumeBatch(batch)) { verify(this.tm).resume(tx); reset(this.tm); assertSame(batch, InfinispanBatcher.CURRENT_BATCH.get()); when(this.tm.suspend()).thenReturn(tx); } verify(this.tm).resume(existingTx); assertSame(existingBatch, InfinispanBatcher.CURRENT_BATCH.get()); } @Test public void suspendBatch() throws Exception { TransactionBatch batch = mock(TransactionBatch.class); InfinispanBatcher.CURRENT_BATCH.set(batch); TransactionBatch result = this.batcher.suspendBatch(); verify(this.tm).suspend(); assertSame(batch, result); assertNull(InfinispanBatcher.CURRENT_BATCH.get()); } @Test public void suspendNoBatch() throws Exception { TransactionBatch result = this.batcher.suspendBatch(); verify(this.tm, never()).suspend(); assertNull(result); } }