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

import static org.junit.Assert.assertEquals;

import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.junit.Test;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.mockito.MockitoTest;
import org.mockito.exceptions.base.MockitoAssertionErrorTest;
import org.mockito.exceptions.base.MockitoExceptionTest;
import org.mockito.internal.AllInvocationsFinderTest;
import org.mockito.internal.InvalidStateDetectionTest;
import org.mockito.internal.creation.bytebuddy.TypeCachingMockBytecodeGeneratorTest;
import org.mockito.internal.exceptions.ReporterTest;
import org.mockito.internal.handler.MockHandlerImplTest;
import org.mockito.internal.invocation.InvocationMatcherTest;
import org.mockito.internal.invocation.InvocationsFinderTest;
import org.mockito.internal.matchers.ComparableMatchersTest;
import org.mockito.internal.matchers.EqualsTest;
import org.mockito.internal.matchers.MatchersToStringTest;
import org.mockito.internal.progress.MockingProgressImplTest;
import org.mockito.internal.progress.TimesTest;
import org.mockito.internal.stubbing.defaultanswers.ReturnsEmptyValuesTest;
import org.mockito.internal.stubbing.defaultanswers.ReturnsGenericDeepStubsTest;
import org.mockito.internal.util.MockUtilTest;
import org.mockito.internal.util.collections.ListUtilTest;
import org.mockito.internal.verification.DefaultRegisteredInvocationsTest;
import org.mockito.internal.verification.checkers.MissingInvocationCheckerTest;
import org.mockito.internal.verification.checkers.MissingInvocationInOrderCheckerTest;
import org.mockito.internal.verification.checkers.NumberOfInvocationsCheckerTest;
import org.mockito.internal.verification.checkers.NumberOfInvocationsInOrderCheckerTest;
import org.mockitousage.basicapi.ReplacingObjectMethodsTest;
import org.mockitousage.basicapi.ResetTest;
import org.mockitousage.basicapi.UsingVarargsTest;
import org.mockitousage.examples.use.ExampleTest;
import org.mockitousage.matchers.CustomMatchersTest;
import org.mockitousage.matchers.InvalidUseOfMatchersTest;
import org.mockitousage.matchers.MatchersTest;
import org.mockitousage.matchers.VerificationAndStubbingUsingMatchersTest;
import org.mockitousage.misuse.InvalidUsageTest;
import org.mockitousage.puzzlers.BridgeMethodPuzzleTest;
import org.mockitousage.puzzlers.OverloadingPuzzleTest;
import org.mockitousage.stacktrace.ClickableStackTracesTest;
import org.mockitousage.stacktrace.PointingStackTraceToActualInvocationTest;
import org.mockitousage.stacktrace.StackTraceFilteringTest;
import org.mockitousage.stubbing.BasicStubbingTest;
import org.mockitousage.stubbing.ReturningDefaultValuesTest;
import org.mockitousage.stubbing.StubbingWithThrowablesTest;
import org.mockitousage.verification.*;
import org.mockitoutil.TestBase;

public class ThreadsRunAllTestsHalfManualTest extends TestBase {

    private static class AllTestsRunner extends Thread {

        private Set<Class<?>> failed = new HashSet<Class<?>>();

        public void run() {
            Result result =
                    JUnitCore.runClasses(
                            EqualsTest.class,
                            ListUtilTest.class,
                            MockingProgressImplTest.class,
                            TimesTest.class,
                            MockHandlerImplTest.class,
                            AllInvocationsFinderTest.class,
                            ReturnsEmptyValuesTest.class,
                            NumberOfInvocationsCheckerTest.class,
                            DefaultRegisteredInvocationsTest.class,
                            MissingInvocationCheckerTest.class,
                            NumberOfInvocationsInOrderCheckerTest.class,
                            MissingInvocationInOrderCheckerTest.class,
                            TypeCachingMockBytecodeGeneratorTest.class,
                            InvocationMatcherTest.class,
                            InvocationsFinderTest.class,
                            MockitoTest.class,
                            MockUtilTest.class,
                            ReporterTest.class,
                            MockitoAssertionErrorTest.class,
                            MockitoExceptionTest.class,
                            StackTraceFilteringTest.class,
                            BridgeMethodPuzzleTest.class,
                            OverloadingPuzzleTest.class,
                            InvalidUsageTest.class,
                            UsingVarargsTest.class,
                            CustomMatchersTest.class,
                            ComparableMatchersTest.class,
                            InvalidUseOfMatchersTest.class,
                            MatchersTest.class,
                            MatchersToStringTest.class,
                            VerificationAndStubbingUsingMatchersTest.class,
                            BasicStubbingTest.class,
                            ReturningDefaultValuesTest.class,
                            StubbingWithThrowablesTest.class,
                            AtMostXVerificationTest.class,
                            BasicVerificationTest.class,
                            ExactNumberOfTimesVerificationTest.class,
                            VerificationInOrderTest.class,
                            NoMoreInteractionsVerificationTest.class,
                            SelectedMocksInOrderVerificationTest.class,
                            VerificationOnMultipleMocksUsingMatchersTest.class,
                            VerificationUsingMatchersTest.class,
                            RelaxedVerificationInOrderTest.class,
                            DescriptiveMessagesWhenVerificationFailsTest.class,
                            DescriptiveMessagesWhenTimesXVerificationFailsTest.class,
                            BasicVerificationInOrderTest.class,
                            VerificationInOrderMixedWithOrdinaryVerificationTest.class,
                            DescriptiveMessagesOnVerificationInOrderErrorsTest.class,
                            InvalidStateDetectionTest.class,
                            ReplacingObjectMethodsTest.class,
                            ClickableStackTracesTest.class,
                            ExampleTest.class,
                            PointingStackTraceToActualInvocationTest.class,
                            VerificationInOrderFromMultipleThreadsTest.class,
                            ResetTest.class,
                            ReturnsGenericDeepStubsTest.class);

            if (!result.wasSuccessful()) {
                System.err.println("Thread[" + Thread.currentThread().getId() + "]: error!");
                List<Failure> failures = result.getFailures();
                System.err.println(failures.size());
                for (Failure failure : failures) {
                    System.err.println(failure.getTrace());
                    failed.add(failure.getDescription().getTestClass());
                }
            }
        }

        public Set<Class<?>> getFailed() {
            return failed;
        }
    }

    @Test
    public void shouldRunInMultipleThreads() throws Exception {
        // this test ALWAYS fails if there is a single failing unit
        assertEquals(
                "Run in multiple thread failed for tests",
                Collections.emptySet(),
                runInMultipleThreads(3));
    }

    public static Set<Class<?>> runInMultipleThreads(int numberOfThreads) throws Exception {
        List<AllTestsRunner> threads = new LinkedList<AllTestsRunner>();
        for (int i = 1; i <= numberOfThreads; i++) {
            threads.add(new AllTestsRunner());
        }

        for (Thread t : threads) {
            t.start();
        }

        Set<Class<?>> failed = new HashSet<Class<?>>();
        for (AllTestsRunner t : threads) {
            t.join();
            failed.addAll(t.getFailed());
        }

        return failed;
    }

    public static void main(String[] args) throws Exception {
        int numberOfThreads = 20;
        long before = System.currentTimeMillis();
        Set<Class<?>> failed = runInMultipleThreads(numberOfThreads);
        long after = System.currentTimeMillis();
        long executionTime = (after - before) / 1000;
        System.out.println(
                "Finished tests in "
                        + numberOfThreads
                        + " threads in "
                        + executionTime
                        + " seconds. ("
                        + failed.size()
                        + " tests failed)");
    }
}
