/*
 * This file is part of Nuts Framework.
 * Copyright(C) 2009-2012 Nuts Develop Team.
 *
 * Nuts Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License any later version.
 * 
 * Nuts Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nuts Framework. If not, see <http://www.gnu.org/licenses/>.
 */
package nuts.core.lang.builder;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.junit.Test;

/**
 * Tests concurrent access for the default {@link ToStringStyle}.
 * <p>
 * The {@link ToStringStyle} class includes a registry to avoid infinite loops for objects with circular references. We
 * want to make sure that we do not get concurrency exceptions accessing this registry.
 * </p>
 * <p>
 * This test passes but only tests one aspect of the issue.
 * </p>
 * 
 */
public class ToStringStyleConcurrencyTest {

	static class CollectionHolder<T extends Collection<?>> {
		T collection;

		CollectionHolder(final T collection) {
			this.collection = collection;
		}
	}

	private static final List<Integer> LIST;
	private static final int LIST_SIZE = 100000;
	private static final int REPEAT = 100;

	static {
		LIST = new ArrayList<Integer>(LIST_SIZE);
		for (int i = 0; i < LIST_SIZE; i++) {
			LIST.add(Integer.valueOf(i));
		}
	}

	@Test
	public void testLinkedList() throws InterruptedException, ExecutionException {
		this.testConcurrency(new CollectionHolder<List<Integer>>(new LinkedList<Integer>()));
	}

	@Test
	public void testArrayList() throws InterruptedException, ExecutionException {
		this.testConcurrency(new CollectionHolder<List<Integer>>(new ArrayList<Integer>()));
	}

	@Test
	public void testCopyOnWriteArrayList() throws InterruptedException, ExecutionException {
		this.testConcurrency(new CollectionHolder<List<Integer>>(
			new CopyOnWriteArrayList<Integer>()));
	}

	private void testConcurrency(final CollectionHolder<List<Integer>> holder)
			throws InterruptedException, ExecutionException {
		final List<Integer> list = holder.collection;
		// make a big array that takes a long time to toString()
		list.addAll(LIST);
		// Create a thread pool with two threads to cause the most contention on the underlying
		// resource.
		final ExecutorService threadPool = Executors.newFixedThreadPool(2);
		// Consumes toStrings
		final Callable<Integer> consumer = new Callable<Integer>() {
			public Integer call() {
				for (int i = 0; i < REPEAT; i++) {
					// Calls ToStringStyle
					new ToStringBuilder(holder).append(holder.collection);
				}
				return Integer.valueOf(REPEAT);
			}
		};
		final Collection<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>();
		tasks.add(consumer);
		tasks.add(consumer);
		final List<Future<Integer>> futures = threadPool.invokeAll(tasks);
		for (final Future<Integer> future : futures) {
			future.get();
		}
	}
}
