Package com.linkedin.parseq

Source Code of com.linkedin.parseq.TestEngine

/*
* Copyright 2012 LinkedIn, Inc
*
* 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 com.linkedin.parseq;

import com.linkedin.parseq.promise.Promise;
import com.linkedin.parseq.promise.Promises;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static com.linkedin.parseq.TestUtil.withDisabledLogging;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertTrue;

/**
* @author Chris Pettitt
*/
public class TestEngine
{
  private Engine _engine;
  private ScheduledExecutorService _scheduler;

  @BeforeMethod
  public void setUp() throws Exception
  {
    final int numCores = Runtime.getRuntime().availableProcessors();
    _scheduler = Executors.newScheduledThreadPool(numCores + 1);
    _engine = new EngineBuilder()
        .setTaskExecutor(_scheduler)
        .setTimerScheduler(_scheduler)
        .build();
  }

  @AfterMethod
  public void tearDown() throws Exception
  {
    _engine.shutdown();
    _engine.awaitTermination(50, TimeUnit.MILLISECONDS);
    _engine = null;
    _scheduler.shutdownNow();
    _scheduler = null;
  }


  @Test
  public void testShutdownWithNoTasks() throws InterruptedException
  {
    _engine.shutdown();
    assertTrue(_engine.isShutdown());
    assertTrue(_engine.awaitTermination(50, TimeUnit.MILLISECONDS));
    assertTrue(_engine.isTerminated());
    assertTrue(_engine.isShutdown());
  }

  @Test
  public void testShutdownThenRunTask() throws InterruptedException
  {
    _engine.shutdown();

    final Task<String> task = TestUtil.value("task executed");
    _engine.run(task);

    // Task should be cancelled immediately
    assertTrue(task.await(50, TimeUnit.MILLISECONDS));
    assertTrue(task.isFailed());
  }

  @Test
  public void testShutdownWithRunningTask() throws InterruptedException
  {
    final CountDownLatch finishLatch = new CountDownLatch(1);
    final String taskValue = "task executed";
    final Task<String> task = new BaseTask<String>()
    {
      @Override
      protected Promise<? extends String> run(final Context context) throws Exception
      {
        finishLatch.await();
        return Promises.value(taskValue);
      }
    };

    _engine.run(task);
    _engine.shutdown();

    // shutdown should not complete until after our task is done
    assertFalse(_engine.awaitTermination(50, TimeUnit.MILLISECONDS));
    assertTrue(_engine.isShutdown());
    assertFalse(_engine.isTerminated());
    finishLatch.countDown();
    assertTrue(_engine.awaitTermination(50, TimeUnit.MILLISECONDS));
    assertTrue(_engine.isShutdown());
    assertTrue(_engine.isTerminated());

    // Task should finish shortly
    assertTrue(task.await(50, TimeUnit.MILLISECONDS));
    assertEquals(taskValue, task.get());
  }

  @Test
  public void testShutdownWithRunningAndSuccessorTask() throws InterruptedException
  {
    final CountDownLatch finishLatch = new CountDownLatch(1);

    final String predValue = "task executed";
    final String sucValue = "task executed";

    final Task<String> predTask = new BaseTask<String>()
    {
      @Override
      protected Promise<? extends String> run(final Context context) throws Exception
      {
        finishLatch.await();
        return Promises.value(predValue);
      }
    };
    final Task<String> sucTask = TestUtil.value(sucValue);
    final Task<String> seq = Tasks.seq(predTask, sucTask);

    _engine.run(seq);
    _engine.shutdown();

    // shutdown should not complete until after our task is done
    assertFalse(_engine.awaitTermination(50, TimeUnit.MILLISECONDS));
    assertTrue(_engine.isShutdown());
    assertFalse(_engine.isTerminated());
    finishLatch.countDown();
    assertTrue(_engine.awaitTermination(50, TimeUnit.MILLISECONDS));
    assertTrue(_engine.isShutdown());
    assertTrue(_engine.isTerminated());

    // Tasks should finish shortly
    assertTrue(predTask.await(50, TimeUnit.MILLISECONDS));
    assertEquals(predValue, predTask.get());

    assertTrue(sucTask.await(50, TimeUnit.MILLISECONDS));
    assertEquals(sucValue, sucTask.get());
  }

  @Test
  public void testFailPlanExecution() throws InterruptedException
  {
    // This test ensures that if execution of a plan's serial executor loop
    // fails, e.g. in the case that the underlying executor is saturated, that
    // we fail the plan. To simplify this test, we constructor our own executor
    // instead of using the default executor set up for test.
    final ExecutorService executorService = new ThreadPoolExecutor(1, 1,
                                                                   0, TimeUnit.SECONDS,
                                                                   new ArrayBlockingQueue<Runnable>(1),
                                                                   new ThreadPoolExecutor.AbortPolicy());
    final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

    try
    {
      final Engine engine = new EngineBuilder()
          .setTaskExecutor(executorService)
          .setTimerScheduler(scheduledExecutorService)
          .build();

      // First we submit two tasks that will never finish. This saturates the
      // underlying executor by using its only thread and saturating its
      // single slot queue.
      engine.run(neverEndingBlockingTask());
      engine.run(neverEndingBlockingTask());

      // Now we submit another task. The execution loop for this task will fail
      // during submit to the underlying executor. We expect that it will be
      // cancelled.
      final Task<?> task = neverEndingBlockingTask();
      withDisabledLogging(new Runnable()
      {
        @Override
        public void run()
        {
          engine.run(task);
          try
          {
            assertTrue(task.await(5, TimeUnit.SECONDS));
          }
          catch (InterruptedException e)
          {
            // Ignore.
          }
        }
      });
      assertTrue(task.isFailed());
      assertTrue("Expected underlying exception to be instance of RejectedExecutionException, but was: " + task.getError().getCause(),
                 task.getError().getCause() instanceof RejectedExecutionException);

      engine.shutdown();
    }
    finally
    {
      scheduledExecutorService.shutdownNow();
      executorService.shutdownNow();
    }
  }

  /**
   * A task that blocks forever when it is executed, tying up whatever thread
   * executes it.
   */
  private Task<?> neverEndingBlockingTask()
  {
    return new BaseTask<Object>()
    {
      @Override
      protected Promise<?> run(Context context) throws Throwable
      {
        new CountDownLatch(1).await();
        return Promises.value("A value that should never be seen!");
      }
    };
  }
}
TOP

Related Classes of com.linkedin.parseq.TestEngine

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.