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

import static java.util.Collections.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

@Ignore
public class MultithreadedStubbingHalfManualTest {

    /**
     * Class with two methods, one of them is repeatedly mocked while another is repeatedly called.
     */
    public interface ToMock {
        public Integer getValue(Integer param);

        public List<Integer> getValues(Integer param);
    }

    /**
     * Thread pool for concurrent invocations.
     */
    private Executor executor;

    private List exceptions = synchronizedList(new LinkedList());

    @Before
    public void setUp() {
        this.executor = Executors.newSingleThreadExecutor();
    }

    /**
     * The returned runnable simply calls ToMock.getValues(int).
     *
     * @param toMock The mocked object
     * @return The runnable.
     */
    private Runnable getConflictingRunnable(final ToMock toMock) {
        return new Runnable() {
            public void run() {
                while (true) {
                    try {
                        Thread.sleep((long) (Math.random() * 10));
                    } catch (InterruptedException e) {
                    }
                    if (!toMock.getValues(0).isEmpty()) {
                        fail("Shouldn't happen, were just making sure it wasn't optimized away...");
                    }
                }
            }
        };
    }

    @Test
    //this problem shows at 4 out of 5 executions
    //it is not strictly a bug because Mockito does not support simultanous stubbing (see FAQ)
    //however I decided to synchronize some calls in order to make the exceptions nicer 
    public void tryToRevealTheProblem() {
        ToMock toMock = mock(ToMock.class);
        for (int i = 0; i < 100; i++) {
            int j = i % 11;

            // Repeated mocking
            when(toMock.getValue(i)).thenReturn(j);
            //TODO make it also showing errors for doReturn()
//            doReturn(j).when(toMock).getValue(i);

            while (true) {
                try {
                    // Scheduling invocation
                    this.executor.execute(getConflictingRunnable(toMock));
                    break;
                } catch (RejectedExecutionException ex) {
                    fail();
                }
            }

            try {
                Thread.sleep(10 / ((i % 10) + 1)); //NOPMD
            } catch (InterruptedException e) {
            }
        }
    }
}