content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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 org.apache.aries.async.promise.test;
  
  import static java.util.concurrent.TimeUnit.MILLISECONDS;
  import static java.util.concurrent.TimeUnit.NANOSECONDS;
  import static java.util.concurrent.TimeUnit.SECONDS;
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertNull;
  import static org.junit.Assert.assertSame;
  import static org.junit.Assert.assertTrue;
  
  import java.util.concurrent.CountDownLatch;
  import java.util.concurrent.atomic.AtomicBoolean;
  import java.util.concurrent.atomic.AtomicLong;
  
  import org.junit.Test;
  import org.osgi.util.function.Callback;
  import org.osgi.util.promise.Deferred;
  import org.osgi.util.promise.Failure;
  import org.osgi.util.promise.Promise;
  import org.osgi.util.promise.Promises;
  import org.osgi.util.promise.Success;
  import org.osgi.util.promise.TimeoutException;
  
  public class ChainTest {
  
      @Test
      public void testThenSuccess() throws Exception {
          Deferred<String> def = new Deferred<String>();
  
          Promise<String> chain = def.getPromise().then(new Success<String, String>() {
              @Override
              public Promise<String> call(Promise<String> resolved) throws Exception {
                  return Promises.resolved("success!");
              }
          });
          assertFalse("chain not resolved", chain.isDone());
  
          def.resolve("ok");
          assertTrue("chain resolved", chain.isDone());
          assertEquals("chain value matches", "success!", chain.getValue());
      }
  
      @Test
      public void testThenSuccessDeferred() throws Exception {
          Deferred<String> def = new Deferred<String>();
          final Deferred<String> def2 = new Deferred<String>();
  
          Promise<String> chain = def.getPromise().then(new Success<String, String>() {
              @Override
              public Promise<String> call(Promise<String> resolved) throws Exception {
                  return def2.getPromise();
              }
          });
          assertFalse("chain not resolved", chain.isDone());
  
          def.resolve("ok");
          assertFalse("chain still not resolved", chain.isDone());
  
          def2.resolve("success!");
          assertTrue("chain resolved", chain.isDone());
          assertEquals("chain value matches", "success!", chain.getValue());
      }
  
      @Test
      public void testThenSuccessFailed() throws Exception {
          Deferred<String> def = new Deferred<String>();
          final Promise<String> promise = def.getPromise();
          final Throwable thenFail = new Throwable("failed!");
  
          Promise<String> chain = promise.then(new Success<String, String>() {
              @Override
              public Promise<String> call(Promise<String> resolved) throws Exception {
                  return Promises.failed(thenFail);
              }
          });
          assertFalse("chain not resolved", chain.isDone());
  
          def.resolve("ok");
          assertTrue("chain resolved", chain.isDone());
          assertEquals("chain failure matches", thenFail, chain.getFailure());
      }
  
      @Test
      public void testThenSuccessException() throws Exception {
          Deferred<String> def = new Deferred<String>();
          final Promise<String> promise = def.getPromise();
          final Exception thenException = new Exception("then exception!");
  
          Promise<String> chain = promise.then(new Success<String, String>() {
              @Override
              public Promise<String> call(Promise<String> resolved) throws Exception {
                  throw thenException;
              }
          });
          assertFalse("chain not resolved", chain.isDone());
  
          def.resolve("ok");
          assertTrue("chain resolved", chain.isDone());
          assertEquals("chain failure matches", thenException, chain.getFailure());
      }
  
      @Test
      public void testThenFail() throws Exception {
          Deferred<String> def = new Deferred<String>();
          final Promise<String> promise = def.getPromise();
  
          Promise<String> chain = promise.then(null, new Failure() {
              @Override
              public void fail(Promise<?> resolved) throws Exception {
              }
          });
          assertFalse("chain not resolved", chain.isDone());
  
          Throwable failure = new Throwable("fail!");
          def.fail(failure);
          assertTrue("chain resolved", chain.isDone());
          assertEquals("chain failure matches", failure, chain.getFailure());
      }
  
      @Test
      public void testThenFailNoCallback() throws Exception {
          Deferred<String> def = new Deferred<String>();
          final Promise<String> promise = def.getPromise();
  
          Promise<String> chain = promise.then((Success)null);
          assertFalse("chain not resolved", chain.isDone());
  
          Throwable failure = new Throwable("fail!");
          def.fail(failure);
          assertTrue("chain resolved", chain.isDone());
          assertEquals("chain failure matches", failure, chain.getFailure());
      }
  
      @Test
      public void testThenFailException() throws Exception {
          Deferred<String> def = new Deferred<String>();
          final Promise<String> promise = def.getPromise();
          final Exception thenException = new Exception("eek!");
  
          Promise<String> chain = promise.then(null, new Failure() {
              @Override
              public void fail(Promise<?> resolved) throws Exception {
                  throw thenException;
              }
          });
          assertFalse("chain not resolved", chain.isDone());
  
          def.fail(new Throwable("failed"));
          assertTrue("chain resolved", chain.isDone());
          assertEquals("chain failure matches", thenException, chain.getFailure());
      }
  
      @Test
      public void testThenNull() throws Exception {
          Deferred<String> def = new Deferred<String>();
          final Promise<String> promise = def.getPromise();
          Promise<String> chain = promise.then((Success)null);
          assertFalse("chain not resolved", chain.isDone());
  
          def.resolve("ok");
          assertTrue("chain resolved", chain.isDone());
          assertNull("chain value null", chain.getValue());
      }
  
      @Test
      public void testThenNullResolved() throws Exception {
          Deferred<String> def = new Deferred<String>();
          def.resolve("ok");
          Promise<String> chain = def.getPromise().then((Success)null);
  
          assertTrue("chain resolved", chain.isDone());
          assertNull("chain value null", chain.getValue());
      }
  
      @Test
      public void testThenAlreadyResolved() throws Exception {
          Deferred<String> def = new Deferred<String>();
          final Promise<String> promise = def.getPromise();
          def.resolve("ok");
  
          Promise<String> chain = promise.then(new Success<String, String>() {
              @Override
              public Promise<String> call(Promise<String> resolved) throws Exception {
                  return Promises.resolved("success!");
              }
          });
          assertTrue("chain resolved", chain.isDone());
          assertEquals("chain value matches", "success!", chain.getValue());
      }
  
      @Test
      public void testExampleChain() throws Exception {
          Success<String, String> doubler = new Success<String, String>() {
              public Promise<String> call(Promise<String> p) throws Exception {
                  return Promises.resolved(p.getValue() + p.getValue());
              }
          };
  
          Deferred<String> def = new Deferred<String>();
          final Promise<String> foo = def.getPromise().then(doubler).then(doubler);
  
          def.resolve("hello!");
          assertEquals("doubler matches", "hello!hello!hello!hello!", foo.getValue());
      }
  
      @Test
      public void testThen2() throws Exception {
          Deferred<String> def = new Deferred<String>();
  
          Promise<String> chain1 = def.getPromise().then(new Success<String, String>() {
              @Override
              public Promise<String> call(Promise<String> resolved) throws Exception {
                  return Promises.resolved("success1");
              }
          });
          assertFalse("chain not resolved", chain1.isDone());
  
          Promise<String> chain2 = def.getPromise().then(new Success<String, String>() {
              @Override
              public Promise<String> call(Promise<String> resolved) throws Exception {
                  return Promises.resolved("success2");
              }
          });
          assertFalse("chain not resolved", chain2.isDone());
  
          def.resolve("ok");
          assertTrue("chain1 resolved", chain1.isDone());
          assertEquals("chain1 value matches", "success1", chain1.getValue());
          assertTrue("chain2 resolved", chain2.isDone());
          assertEquals("chain2 value matches", "success2", chain2.getValue());
      }
  
      @Test
      public void testThenResolved() throws Exception {
          Deferred<String> def = new Deferred<String>();
          def.resolve("already resolved");
  
          Promise<String> chain = def.getPromise().then(new Success<String, String>() {
              @Override
              public Promise<String> call(Promise<String> resolved) throws Exception {
                  return Promises.resolved("success!");
              }
          });
          assertTrue("chain resolved", chain.isDone());
  
          assertEquals("chain value matches", "success!", chain.getValue());
      }
  
      @Test
      public void testThenResolved2() throws Exception {
          Deferred<String> def = new Deferred<String>();
          def.resolve("already resolved");
  
          Promise<String> chain = def.getPromise().then(new Success<String, String>() {
              @Override
              public Promise<String> call(Promise<String> resolved) throws Exception {
                  return Promises.resolved("success1");
              }
          }).then(new Success<String, String>() {
              @Override
              public Promise<String> call(Promise<String> resolved) throws Exception {
                  return Promises.resolved("success2");
              }
          });
  
          assertTrue("chain resolved", chain.isDone());
  
          assertEquals("chain value matches", "success2", chain.getValue());
      }
      
      @Test
      public void testThenCallbackSuccess() throws Exception {
      	Deferred<String> def = new Deferred<String>();
  
      	final AtomicBoolean run = new AtomicBoolean(false);
      	
          Promise<String> chain = def.getPromise().then(new Callback() {
              @Override
              public void run() throws Exception {
                  run.set(true);
              }
          });
          assertFalse("chain should not be resolved", chain.isDone());
          assertFalse("callback should not have been run", run.get());
  
          def.resolve("ok");
          assertTrue("chain resolved", chain.isDone());
          assertEquals("chain value matches", "ok", chain.getValue());
          assertTrue("callback should have been run", run.get());
      	
      }
  
      @Test
      public void testThenCallbackFail() throws Exception {
      	Deferred<String> def = new Deferred<String>();
      	
      	final AtomicBoolean run = new AtomicBoolean(false);
      	
      	Promise<String> chain = def.getPromise().then(new Callback() {
      		@Override
      		public void run() throws Exception {
      			run.set(true);
      		}
      	});
      	
      	Exception failure = new Exception("bang!");
      	
      	assertFalse("chain should not be resolved", chain.isDone());
      	assertFalse("callback should not have been run", run.get());
      	
      	def.fail(failure);
      	assertTrue("chain resolved", chain.isDone());
      	assertSame("chain value matches", failure, chain.getFailure());
      	assertTrue("callback should have been run", run.get());
      	
      }
  
      @Test
      public void testThenCallbackThrowsExceptionSuccess() throws Exception {
      	Deferred<String> def = new Deferred<String>();
      	
      	final Exception failure = new Exception("bang!");
      	
      	Promise<String> chain = def.getPromise().then(new Callback() {
      		@Override
      		public void run() throws Exception {
      			throw failure;
      		}
      	});
      	
      	assertFalse("chain should not be resolved", chain.isDone());
      	
      	def.resolve("ok");
      	assertTrue("chain resolved", chain.isDone());
      	assertSame("chain value matches", failure, chain.getFailure());
      	
      }
  
      @Test
      public void testThenCallbackThrowsExceptionFail() throws Exception {
      	Deferred<String> def = new Deferred<String>();
      	
      	final Exception failure = new Exception("bang!");
      	
      	Promise<String> chain = def.getPromise().then(new Callback() {
      		@Override
      		public void run() throws Exception {
      			throw failure;
      		}
      	});
      	
      	assertFalse("chain should not be resolved", chain.isDone());
      	
      	def.fail(new IllegalStateException());
      	assertTrue("chain resolved", chain.isDone());
      	assertSame("chain value matches", failure, chain.getFailure());
      	
      }
      
      @Test
      public void testTimeout() throws Exception {
      	Deferred<String> def = new Deferred<String>();
  
          Promise<String> promise = def.getPromise();
          
          long start = System.nanoTime();
          final CountDownLatch latch = new CountDownLatch(1);
          final AtomicLong finish = new AtomicLong();
          
  		Promise<String> chain = promise.timeout(500)
  				.onResolve(new Runnable() {
  					@Override
  					public void run() {
  						finish.set(System.nanoTime());
  						latch.countDown();
  					}
  				});
  		
  		assertFalse("promise should not be resolved", promise.isDone());
  		assertFalse("chain should not be resolved", chain.isDone());
  		
  		assertTrue("Did not time out!", latch.await(1, SECONDS));
  		assertTrue("Finished too fast", NANOSECONDS.toMillis(finish.get() - start) > 450);
  
  		assertFalse("promise should not be resolved", promise.isDone());
  		assertTrue("chain should now be resolved", chain.isDone());
  		
          assertTrue("Should fail with a timeout exception", chain.getFailure() instanceof TimeoutException);
      }
  
      @Test
      public void testTimeoutSuccess() throws Exception {
      	Deferred<String> def = new Deferred<String>();
      	
      	Promise<String> promise = def.getPromise();
      	
      	final CountDownLatch latch = new CountDownLatch(1);
      	
      	Promise<String> chain = promise.timeout(500)
      			.onResolve(new Runnable() {
      				@Override
      				public void run() {
      					latch.countDown();
      				}
      			});
      	
      	assertFalse("promise should not be resolved", promise.isDone());
      	assertFalse("chain should not be resolved", chain.isDone());
      	
      	def.resolve("ok");
      	
      	assertTrue("Did not eagerly complete!", latch.await(100, MILLISECONDS));
      	
      	assertTrue("promise should not be resolved", promise.isDone());
      	assertTrue("chain should now be resolved", chain.isDone());
      	
      	assertEquals(promise.getValue(), chain.getValue());
      }
      
      @Test
      public void testTimeoutFailure() throws Exception{
      	Deferred<String> def = new Deferred<String>();
      	
      	Promise<String> promise = def.getPromise();
      	
      	final CountDownLatch latch = new CountDownLatch(1);
      	
      	Promise<String> chain = promise.timeout(500)
      			.onResolve(new Runnable() {
      				@Override
      				public void run() {
      					latch.countDown();
      				}
      			});
      	
      	assertFalse("promise should not be resolved", promise.isDone());
      	assertFalse("chain should not be resolved", chain.isDone());
      	
      	Exception failure = new Exception("bang!");
      	
      	def.fail(failure);
      	
      	assertTrue("Did not eagerly complete!", latch.await(100, MILLISECONDS));
      	
      	assertTrue("promise should not be resolved", promise.isDone());
      	assertTrue("chain should now be resolved", chain.isDone());
      	
      	assertSame(promise.getFailure(), chain.getFailure());
      }
  }
  