package org.junit.experimental;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.junit.runner.Computer;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.Suite;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;

public class ParallelComputer extends Computer {
	private final boolean fClasses;
	private final boolean fMethods;

	public ParallelComputer(boolean classes, boolean methods) {
		fClasses= classes;
		fMethods= methods;
	}

	public static Computer classes() {
		return new ParallelComputer(true, false);
	}
	
//TODO(parallel) extract commonality from ParallelSuite and ParallelRunner
	public static class ParallelSuite extends Suite {
		public ParallelSuite(RunnerBuilder builder, Class<?>[] classes) throws InitializationError {
			super(builder, classes);
		}
		private ExecutorService fService= Executors.newCachedThreadPool();
		private List<Future<Object>> fResults= new ArrayList<Future<Object>>();
	
		@Override
		protected void runChild(final Runner runner, final RunNotifier notifier) {
			Callable<Object> callable= new Callable<Object>() {
				public Object call() throws Exception {
					superRunChild(runner, notifier);
					return null;
				}
			};
			fResults.add(fService.submit(callable));
		}
		
		protected void superRunChild(Runner runner, RunNotifier notifier) {
			super.runChild(runner, notifier);
		}
		
		@Override
		public void run(RunNotifier notifier) {
			super.run(notifier);
			for (Future<Object> each : fResults)
				try {
					each.get(2000, TimeUnit.MILLISECONDS);
				} catch (Exception e) {
					e.printStackTrace();
				} 
		}
	}

	public static class ParallelRunner extends BlockJUnit4ClassRunner {
		public ParallelRunner(Class<?> klass) throws InitializationError {
			super(klass);
		}
		private ExecutorService fService= Executors.newCachedThreadPool();
		private List<Future<Object>> fResults= new ArrayList<Future<Object>>();
	
		@Override
		protected void runChild(final FrameworkMethod method, final RunNotifier notifier) {
			Callable<Object> callable= new Callable<Object>() {
				public Object call() throws Exception {
					superRunChild(method, notifier);
					return null;
				}
			};
			fResults.add(fService.submit(callable));
		}
		
		protected void superRunChild(FrameworkMethod method, RunNotifier notifier) {
			super.runChild(method, notifier);
		}
		
		@Override
		public void run(RunNotifier notifier) {
			super.run(notifier);
			for (Future<Object> each : fResults)
				try {
					each.get(2000, TimeUnit.MILLISECONDS); // TODO(parallel) what should this really be?
				} catch (Exception e) {
					e.printStackTrace();
				} 
		}
	}

	@Override
	public Suite getSuite(RunnerBuilder builder, java.lang.Class<?>[] classes) throws InitializationError {
		return fClasses
			? new ParallelSuite(builder, classes)
			: super.getSuite(builder, classes);
	}
	
	@Override
	protected Runner getRunner(RunnerBuilder builder, Class<?> testClass)
			throws Throwable {
		return fMethods
			? new ParallelRunner(testClass)
			: super.getRunner(builder, testClass);
	}

	public static Computer methods() {
		return new ParallelComputer(false, true);
	}
}
@@ -115,16 +115,54 @@
 llable;%0A
+import java.util.concurrent.Executor;%0A
 import j
@@ -1209,32 +1209,35 @@
 , classes);%0A%09%09%7D%0A
+%09%09%0A
 %09%09private Execut
@@ -1234,137 +1234,62 @@
 ate 
-ExecutorService fService= Executors.newCachedThreadPool();%0A%09%09private List%3CFuture%3CObject%3E%3E fResults= new ArrayList%3CFuture%3CObject%3E%3E
+final ParallelCollator collator = new ParallelCollator
 ();%0A
@@ -1553,36 +1553,24 @@
 %0A%09%09%09
-fResults.add(fService.submit
+collator.process
 (cal
@@ -1567,33 +1567,32 @@
 rocess(callable)
-)
 ;%0A%09%09%7D%0A%09%09%0A%09%09prote
@@ -1798,34 +1798,42 @@
 %3CObject%3E each : 
-fR
+collator.r
 esults)%0A%09%09%09%09try 
@@ -1852,35 +1852,8 @@
 get(
-2000, TimeUnit.MILLISECONDS
 );%0A%09
@@ -2079,16 +2079,19 @@
 s);%0A%09%09%7D%0A
+%09%09%0A
 %09%09privat
@@ -2096,137 +2096,62 @@
 ate 
-ExecutorService fService= Executors.newCachedThreadPool();%0A%09%09private List%3CFuture%3CObject%3E%3E fResults= new ArrayList%3CFuture%3CObject%3E%3E
+final ParallelCollator collator = new ParallelCollator
 ();%0A
@@ -2424,36 +2424,24 @@
 %0A%09%09%09
-fResults.add(fService.submit
+collator.process
 (cal
@@ -2446,17 +2446,16 @@
 allable)
-)
 ;%0A%09%09%7D%0A%09%09
@@ -2690,10 +2690,18 @@
 h : 
-fR
+collator.r
 esul
@@ -2732,35 +2732,8 @@
 get(
-2000, TimeUnit.MILLISECONDS
 ); /
@@ -3351,9 +3351,312 @@
 ue);%0A%09%7D%0A
-%7D
+%09%0A%09private static class ParallelCollator %7B%0A%09%09%0A%09%09final List%3CFuture%3CObject%3E%3E results = new ArrayList%3CFuture%3CObject%3E%3E();%0A%09%09%0A%09%09private final ExecutorService service = Executors.newCachedThreadPool();%0A%0A%09%09public void process(Callable%3CObject%3E callable) %7B%0A%09%09%09this.results.add(service.submit(callable));%0A%09%09%7D%0A%09%7D%0A%7D%0A

remove timeout and try reducing duplication between ParallelSuite and ParallelRunner