/*
 * eXist Open Source Native XML Database
 * Copyright (C) 2016 The eXist Project
 * http://exist-db.org
 *
 * This program 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
 * of the License, or (at your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software Foundation
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.exist.xquery;

import org.exist.storage.DBBroker;
import org.exist.security.Subject;
import org.exist.xquery.value.BinaryValue;
import org.junit.Test;
import org.easymock.EasyMock;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;

import static org.easymock.EasyMock.*;
import static org.junit.Assert.assertEquals;

/**
 * @author Adam Retter <adam.retter@googlemail.com>
 */
public class XQueryContextTest {

    @Test
    public void prepareForExecution_setsUserFromSession() {

        //partial mock context
        XQueryContext context = EasyMock.createMockBuilder(XQueryContext.class)
                .withConstructor()
                .withArgs()
                .addMockedMethod("getUserFromHttpSession")
                .addMockedMethod("getBroker")
                .createMock();

        DBBroker mockBroker = createMock(DBBroker.class);

        Subject mockSubject = createMock(Subject.class);

        //expectations
        expect(context.getUserFromHttpSession()).andReturn(mockSubject);
        expect(context.getBroker()).andReturn(mockBroker).times(2);
        mockBroker.pushSubject(mockSubject);

        //test
        replay(context);

        context.prepareForExecution();

        verify(context);
    }

    /**
     * Test to ensure that BinaryValueInstances are
     * correctly cleaned up by the XQueryContext
     * between reuse of the context
     */
    @Test
    public void cleanUp_BinaryValueInstances() throws NoSuchFieldException, IllegalAccessException, IOException {
        final XQueryContext context = new XQueryContext();
        final XQueryWatchDog mockWatchdog = createMock(XQueryWatchDog.class);
        context.setWatchDog(mockWatchdog);

        final BinaryValue mockBin1 = createMock(BinaryValue.class);
        final BinaryValue mockBin2 = createMock(BinaryValue.class);
        final BinaryValue mockBin3 = createMock(BinaryValue.class);
        final BinaryValue mockBin4 = createMock(BinaryValue.class);
        final BinaryValue mockBin5 = createMock(BinaryValue.class);
        final BinaryValue mockBin6 = createMock(BinaryValue.class);
        final BinaryValue mockBin7 = createMock(BinaryValue.class);

        // expectations on our mocks
        mockBin1.close();
        expectLastCall().times(1);
        mockBin2.close();
        expectLastCall().times(1);
        mockBin3.close();
        expectLastCall().times(1);
        mockBin4.close();
        expectLastCall().times(1);
        mockBin5.close();
        expectLastCall().times(1);
        mockBin6.close();
        expectLastCall().times(1);
        mockBin7.close();
        expectLastCall().times(1);
        mockWatchdog.reset();
        expectLastCall().times(3);

        // prepare our mocks for our test
        replay(mockBin1, mockBin2, mockBin3, mockBin4, mockBin5, mockBin6, mockBin7, mockWatchdog);


        /* round 1 */

        // use some binary streams
        context.registerBinaryValueInstance(mockBin1);
        context.registerBinaryValueInstance(mockBin2);
        context.registerBinaryValueInstance(mockBin3);
        assertEquals(3, countBinaryValueInstances(context));
        assertEquals(1, countCleanupTasks(context));

        // cleanup those streams
        context.runCleanupTasks();
        assertEquals(0, countBinaryValueInstances(context));

        //reset the context (for reuse(), just as XQueryPool#returnCompiledXQuery(org.exist.source.Source, CompiledXQuery) would do)
        context.reset();
        assertEquals(0, countCleanupTasks(context));


        /* round 2, let's reuse the context... */

        // use some more binary streams
        context.registerBinaryValueInstance(mockBin4);
        context.registerBinaryValueInstance(mockBin5);
        assertEquals(2, countBinaryValueInstances(context));
        assertEquals(1, countCleanupTasks(context));

        // cleanup those streams
        context.runCleanupTasks();
        assertEquals(0, countBinaryValueInstances(context));

        //reset the context (for reuse(), just as XQueryPool#returnCompiledXQuery(org.exist.source.Source, CompiledXQuery) would do)
        context.reset();
        assertEquals(0, countCleanupTasks(context));


        /* round 3, let's reuse the context a second time... */

        // again, use some more binary streams
        context.registerBinaryValueInstance(mockBin6);
        context.registerBinaryValueInstance(mockBin7);
        assertEquals(2, countBinaryValueInstances(context));
        assertEquals(1, countCleanupTasks(context));

        // cleanup those streams
        context.runCleanupTasks();
        assertEquals(0, countBinaryValueInstances(context));

        //reset the context (for reuse(), just as XQueryPool#returnCompiledXQuery(org.exist.source.Source, CompiledXQuery) would do)
        context.reset();
        assertEquals(0, countCleanupTasks(context));


        // verify the expectations of our mocks
        verify(mockBin1, mockBin2, mockBin3, mockBin4, mockBin5, mockBin6, mockBin7, mockWatchdog);
    }

    private int countBinaryValueInstances(final XQueryContext context) throws NoSuchFieldException, IllegalAccessException {
        final Field fldBinaryValueInstances = context.getClass().getDeclaredField("binaryValueInstances");
        fldBinaryValueInstances.setAccessible(true);
        final List<BinaryValue> binaryValueInstances = (List<BinaryValue>)fldBinaryValueInstances.get(context);
        return binaryValueInstances.size();
    }

    private int countCleanupTasks(final XQueryContext context) throws NoSuchFieldException, IllegalAccessException {
        final Field fldCleanupTasks = context.getClass().getDeclaredField("cleanupTasks");
        fldCleanupTasks.setAccessible(true);
        final List<XQueryContext.CleanupTask> cleanupTasks = (List<XQueryContext.CleanupTask>)fldCleanupTasks.get(context);
        return cleanupTasks.size();
    }
}
@@ -1099,16 +1099,40 @@
 .Field;%0A
+import java.util.Deque;%0A
 import j
@@ -1547,36 +1547,8 @@
 r()%0A
-                .withArgs()%0A
     
@@ -6203,20 +6203,21 @@
   final 
-List
+Deque
 %3CBinaryV
@@ -6246,20 +6246,21 @@
 nces = (
-List
+Deque
 %3CBinaryV

Fix failing test which previously was not executed