/*
 * 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:
        }
    }
}
