/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package woolpack.dom;

import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.Semaphore;

import junit.framework.TestCase;
import woolpack.LoadAllTests;
import woolpack.el.PathEL;
import woolpack.test.DomExpressionRunnable;
import woolpack.test.RunnableDomExpression;
import woolpack.test.RunnableGate;
import woolpack.test.TestUtils;
import woolpack.test.RunnableGate.Context;
import woolpack.utils.FixSwitch;
import woolpack.utils.SwitchBuilder;
import woolpack.utils.concurrent.Acquirable;
import woolpack.utils.concurrent.TrySemaphore;

public class DoAcquireTest extends TestCase {

	public void testConstructor(){
		try{
			new DoAcquire(null, DomConstants.NULL, DomConstants.NULL);
			fail();
		}catch(final NullPointerException expected){
		}
		try{
			new DoAcquire(new FixSwitch<String,Acquirable>(null), null, DomConstants.NULL);
			fail();
		}catch(final NullPointerException expected){
		}
		try{
			new DoAcquire(new FixSwitch<String,Acquirable>(null), DomConstants.NULL, null);
			fail();
		}catch(final NullPointerException expected){
		}
	}
	
	public void testNormal() throws InterruptedException{
		final DomExpression thrower = new Branch<Object>(
				new EvalEL(null, new PathEL("context.request.empty")), 
				new SwitchBuilder<Object,DomExpression>().get());
		
		final RunnableGate gate = new RunnableGate(LoadAllTests.THREAD_POOL);
		final Semaphore semaphore = new Semaphore(1, true);
		final DomExpression expression = new DoAcquire(
				new SwitchBuilder<String,Acquirable>().put("id0", new TrySemaphore(semaphore)).get(),
				new Serial(new RunnableDomExpression(gate.getPause("true_start", "true_end")), thrower), 
				new RunnableDomExpression(gate.getGate("false")));

		assertEquals(1, semaphore.availablePermits());
		
		// 実行権を取得するのに成功する。
		final Context t0;
		{
			final DomContext c = new DomContext();
			c.setId("id0");
			c.setRequest(new HashMap<String,Object>());
			t0 = gate.execute(new DomExpressionRunnable(expression, c));
		}
		assertTrue(t0.waitFor("true_start"));
		assertEquals(0, semaphore.availablePermits());
		assertFalse(t0.waitFor("true_end"));

		{
			// 実行権を取得するのに失敗する。処理をブロックしない。
			final Context t;
			{
				final DomContext c = new DomContext();
				c.setId("id0");
				c.setRequest(new HashMap<String,Object>());
				t = gate.execute(new DomExpressionRunnable(expression, c));
			}
			assertTrue(TestUtils.equals(Arrays.asList("false"), t.getEndList()));
		}
		assertEquals(0, semaphore.availablePermits());
		{
			// 関係ないidのリクエストとは干渉しない。
			final Context t;
			{
				final DomContext c = new DomContext();
				c.setId("id1");
				c.setRequest(new HashMap<String,Object>());
				t = gate.execute(new DomExpressionRunnable(expression, c));
			}
			assertTrue(t.waitFor("true_start"));
			assertFalse(t.waitFor("true_end"));
			t.resume();
			assertTrue(TestUtils.equals(Arrays.asList("true_start", "true_end"), t.getEndList()));
		}
		assertEquals(0, semaphore.availablePermits());
		
		// 取得した実行権を開放する。
		t0.resume();
		assertTrue(TestUtils.equals(Arrays.asList("true_start", "true_end"), t0.getEndList()));

		assertEquals(1, semaphore.availablePermits());
		
		{
			// 実行権を再取得するのに成功する。
			final Context t;
			{
				final DomContext c = new DomContext();
				c.setId("id0");
				c.setRequest(new HashMap<String,Object>());
				t = gate.execute(new DomExpressionRunnable(expression, c));
			}
			assertTrue(t.waitFor("true_start"));
			assertFalse(t.waitFor("true_end"));
			t.resume();
			assertTrue(TestUtils.equals(Arrays.asList("true_start", "true_end"), t.getEndList()));
		}
		assertEquals(1, semaphore.availablePermits());
		{
			// 関係ないidのリクエストとは干渉しない。
			final Context t;
			{
				final DomContext c = new DomContext();
				c.setId("id1");
				t = gate.execute(new DomExpressionRunnable(expression, c));
			}
			assertTrue(t.waitFor("true_start"));
			assertFalse(t.waitFor("true_end"));
			t.resume();
			assertTrue(TestUtils.equals(Arrays.asList("true_start", "true_end"), t.getEndList()));
		}
		assertEquals(1, semaphore.availablePermits());
		{
			// 委譲先で異常が発生した場合。
			final Context t;
			{
				final DomContext c = new DomContext();
				c.setId("id0");
				c.setRequest(null);
				t = gate.execute(new DomExpressionRunnable(expression, c));
			}
			assertTrue(t.waitFor("true_start"));
			assertFalse(t.waitFor("true_end"));
			t.resume();
			assertTrue(TestUtils.equals(Arrays.asList("true_start", "true_end"), t.getEndList()));
		}
		assertEquals(1, semaphore.availablePermits());
	}
}
