/*
 * Copyright (c) 2007 Mockito contributors
 * This program is made available under the terms of the MIT License.
 */
package org.mockitousage.serialization;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.withSettings;

import java.nio.charset.CharacterCodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

import org.junit.Test;
import org.mockitousage.IMethods;
import org.mockitoutil.SimpleSerializationUtil;

public class ParallelSerializationTest {

    @Test
    public void single_mock_being_serialized_in_different_classloaders_by_multiple_threads() throws ExecutionException, InterruptedException {
        // given
        int iterations = 2;
        int threadingFactor = 200;
        final ExecutorService executorService = Executors.newFixedThreadPool(threadingFactor);
        final IMethods iMethods_that_store_invocations = mock(IMethods.class, withSettings().serializable());

        // when
        for (int i = 0; i <= iterations; i++) {
            List<Future<?>> futures = new ArrayList<Future<?>>(threadingFactor);
            final CyclicBarrier barrier_that_will_wait_until_threads_are_ready = new CyclicBarrier(threadingFactor);

            // prepare all threads by submitting a callable
            //  - that will serialize the mock a 'threadingFactor' times
            //  - that will use the mock a 'threadingFactor' times
            for (int j = 0; j < threadingFactor; j++) {
                // submit a callable that will serialize the mock 'iMethods'
                futures.add(executorService.submit(new Callable<Object>() {
                    public Object call() throws Exception {
                        barrier_that_will_wait_until_threads_are_ready.await();

                        randomCallOn(iMethods_that_store_invocations);

                        return SimpleSerializationUtil.serializeMock(iMethods_that_store_invocations).toByteArray();
                    }
                }));

                // submit a callable that will only use the mock 'iMethods'
                executorService.submit(new Callable<Object>() {
                    public Object call() throws Exception {
                        barrier_that_will_wait_until_threads_are_ready.await();
                        return iMethods_that_store_invocations.longObjectReturningMethod();
                    }
                });
            }

            // ensure we are getting the futures
            for (Future<?> future : futures) {
                future.get();
            }
        }
    }

    private void randomCallOn(IMethods iMethods) throws CharacterCodingException {
        int random = new Random().nextInt(10);
        switch (random) {
            case 0 : iMethods.arrayReturningMethod(); break;
            case 1 : iMethods.longObjectReturningMethod(); break;
            case 2 : iMethods.linkedListReturningMethod(); break;
            case 3 : iMethods.iMethodsReturningMethod(); break;
            case 4 : iMethods.canThrowException(); break;
            case 5 : iMethods.differentMethod(); break;
            case 6 : iMethods.voidMethod(); break;
            case 7 : iMethods.varargsString(1, ""); break;
            case 8 : iMethods.forMap(null); break;
            case 9 : iMethods.throwsNothing(false); break;
            default:
        }
    }
}
