/*
 * Copyright 2004-2014 the Seasar Foundation and the Others.
 *
 * Licensed 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.seasar.extension.jta;

import javax.transaction.Status;
import javax.transaction.Synchronization;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

/**
 * @author nakamura
 *
 */
public class TransactionSynchronizationRegistryImplTest {

    /** TransactionSynchronizationRegistryImpl */
    private TransactionSynchronizationRegistryImpl tsr = null;
    /** TransactionManager */
    private TransactionManagerImpl tm = null;

    /**
     * setUp
     */
    @Before
    public void setUp() {
        this.tm = new TransactionManagerImpl();
        this.tm.setCurrent(null);
        this.tsr = new TransactionSynchronizationRegistryImpl(this.tm);
    }

    /**
     * testPutResourceForException
     */
    @Test
    public void testPutResourceForException() {
        try {
            this.tsr.putResource("hoge", "foo");
            Assert.fail();
        } catch (final IllegalStateException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * testPutResourceForExceptionTx
     * @throws Exception Exception
     */
    @Test
    public void testPutResourceForExceptionTx() throws Exception {
        this.tm.begin();
        this.tsr.putResource("hoge", "foo");
        try {
            this.tsr.putResource(null, "foo");
            Assert.fail();
        } catch (final NullPointerException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * testGetResourceForException
     */
    @Test
    public void testGetResourceForException() {
        try {
            this.tsr.getResource("hoge");
            Assert.fail();
        } catch (final IllegalStateException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * testGetResourceTx
     * @throws Exception Exception
     */
    @Test
    public void testGetResourceTx() throws Exception {
        this.tm.begin();
        Assert.assertNull(this.tsr.getResource("hoge"));
        this.tsr.putResource("hoge", "foo");
        Assert.assertEquals("foo", this.tsr.getResource("hoge"));
    }

    /**
     * testSetRollbackOnlyForException
     */
    @Test
    public void testSetRollbackOnlyForException() {
        try {
            this.tsr.setRollbackOnly();
            Assert.fail();
        } catch (final IllegalStateException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * testSetRollbackOnlyTx
     * @throws Exception Exception
     */
    @Test
    public void testSetRollbackOnlyTx() throws Exception {
        this.tm.begin();
        this.tsr.setRollbackOnly();
        Assert.assertEquals(Status.STATUS_MARKED_ROLLBACK, this.tsr.getTransactionStatus());
    }

    /**
     * testGetRollbackOnlyForException
     */
    @Test
    public void testGetRollbackOnlyForException() {
        try {
            this.tsr.getRollbackOnly();
            Assert.fail();
        } catch (final IllegalStateException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * testGetRollbackOnlyTx
     * @throws Exception Exception
     */
    @Test
    public void testGetRollbackOnlyTx() throws Exception {
        this.tm.begin();
        Assert.assertFalse(this.tsr.getRollbackOnly());
        this.tsr.setRollbackOnly();
        Assert.assertTrue(this.tsr.getRollbackOnly());
    }

    /**
     * testGetTransactionKeyForException
     */
    @Test
    public void testGetTransactionKeyForException() {
        Assert.assertNull(this.tsr.getTransactionKey());
    }

    /**
     * testGetTransactionKeyTx
     * @throws Exception Exception
     */
    @Test
    public void testGetTransactionKeyTx() throws Exception {
        this.tm.begin();
        Assert.assertNotNull(this.tsr.getTransactionKey());
    }

    /**
     * testRegisterInterposedSynchronizationForException
     */
    @Test
    public void testRegisterInterposedSynchronizationForException() {
        try {
            this.tsr.registerInterposedSynchronization(new NoOpSync());
            Assert.fail();
        } catch (final IllegalStateException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * testRegisterInterposedSynchronization
     * @throws Exception Exception
     */
    @Test
    public void testRegisterInterposedSynchronization() throws Exception {
        Sync.setResult(new StringBuilder());
        this.tm.begin();
        this.tsr.registerInterposedSynchronization(new Sync('a', 'b'));
        this.tsr.registerInterposedSynchronization(new Sync('c', 'd'));
        this.tm.getTransaction().registerSynchronization(new Sync('e', 'f'));
        this.tm.commit();
        Assert.assertEquals("eacbdf", Sync.getResult().toString());
    }

    /**
     * NoOpSync
     *
     */
    static final class NoOpSync implements Synchronization {
        /**
         * @see javax.transaction.Synchronization#beforeCompletion()
         */
        @Override
        public void beforeCompletion() {
            return;
        }

        /**
         * @see javax.transaction.Synchronization#afterCompletion(int)
         */
        @Override
        public void afterCompletion(final int arg0) {
            return;
        }
    }

    /**
     * @author tadashi
     *
     */
    static final class Sync implements Synchronization {

        /** result */
        private static StringBuilder result;

        /** before */
        private final char before;
        /** after */
        private final char after;

        /**
         * @param b before
         * @param a after
         */
        Sync(final char b, final char a) {
            this.before = b;
            this.after = a;
        }

        /**
         * @return the result
         */
        public static StringBuilder getResult() {
            return result;
        }

        /**
         * @param rslt the result to set
         */
        public static void setResult(final StringBuilder rslt) {
            Sync.result = rslt;
        }

        /**
         * @return the before
         */
        public char getBefore() {
            return this.before;
        }

        /**
         * @return the after
         */
        public char getAfter() {
            return this.after;
        }

        /**
         * @see javax.transaction.Synchronization#beforeCompletion()
         */
        @Override
        public void beforeCompletion() {
            result.append(this.before);
        }

        /**
         * @see javax.transaction.Synchronization#afterCompletion(int)
         */
        @Override
        public void afterCompletion(final int status) {
            result.append(this.after);
        }
    }
}
