/*
 * Licensed to the University of California, Berkeley 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 tachyon;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.Assert;
import org.junit.Test;

/**
 * Unit tests for {@link HeartbeatThread}. This test run
 * {@link HeartbeatThreadTest#NUMBER_OF_THREADS} {@link HeartbeatThread} in parallel with different
 * {@link HeartbeatThread#mFixedExecutionIntervalMs}. It checks that every scheduled thread executes
 * an heartbeat for the specified interval with a variance lesser than
 * {@link HeartbeatThreadTest#VARIANCE_UPPER_BOUND}.
 */
public final class HeartbeatThreadTest {

  private static final String THREAD_NAME = "thread-Name-%s";

  private static final int NUMBER_OF_THREADS = 3;

  private static final long EXECUTION_TIME = 2000;

  private static final double VARIANCE_UPPER_BOUND = 5.0;

  private final ExecutorService mExecutorService = Executors.newFixedThreadPool(NUMBER_OF_THREADS);

  private final List<DummyHeartbeatExecutor> mHeartbeatExecutors =
      new ArrayList<DummyHeartbeatExecutor>();

  @Test
  public void createHeartbeatThreadTest() throws Exception {

    for (int i = 1; i <= NUMBER_OF_THREADS; i ++) {
      long interval = i * 10;
      mExecutorService.submit(createHeartbeatThread(String.format(THREAD_NAME, i), interval));
    }

    Thread.sleep(EXECUTION_TIME);
    mExecutorService.shutdown();

    for (DummyHeartbeatExecutor hbe : mHeartbeatExecutors) {
      Assert.assertTrue("The HeartbeatThread has been scheduled with a bad precision",
          hbe.getVariance() < VARIANCE_UPPER_BOUND);
    }
  }

  private HeartbeatThread createHeartbeatThread(String threadName, long fixedExecutionIntervalMs) {
    DummyHeartbeatExecutor heartbeatExecutor = new DummyHeartbeatExecutor(threadName);
    mHeartbeatExecutors.add(heartbeatExecutor);
    return new HeartbeatThread(threadName, heartbeatExecutor, fixedExecutionIntervalMs);
  }

  private class DummyHeartbeatExecutor implements HeartbeatExecutor {

    private final String mName;

    private int mCount;

    private long mStartingTIme = -1;

    private long mLastExecutionTime = -1;

    private List<Long> mIntervals = new ArrayList<Long>();

    public DummyHeartbeatExecutor(String name) {
      mName = name;
    }

    @Override
    public void heartbeat() {
      if (mStartingTIme == -1) {
        mStartingTIme = System.currentTimeMillis();
        mLastExecutionTime = mStartingTIme;
      } else {
        long mCurrTime = System.currentTimeMillis();
        getIntervals().add(mCurrTime - mLastExecutionTime);
        mLastExecutionTime = mCurrTime;
        mCount ++;
      }
    }

    /**
     * This method is synchronized to avoid to get a
     * {@link java.util.ConcurrentModificationException}.
     */
    public synchronized List<Long> getIntervals() {
      return mIntervals;
    }

    public double getMean() {
      return mCount != 0 ? (mLastExecutionTime - mStartingTIme) * 1d / mCount : 0d;
    }

    public String getName() {
      return mName;
    }

    private double getVariance() {
      double mean = getMean();
      double temp = 0d;
      for (Long x : getIntervals()) {
        temp += (mean - x) * (mean - x);
      }
      return temp / getIntervals().size();
    }
  }
}

@@ -1049,16 +1049,17 @@
 test run
+s
 %0A * %7B@li
@@ -1256,17 +1256,16 @@
 tes%0A * a
-n
  heartbe
@@ -1473,17 +1473,17 @@
 %22thread-
-N
+n
 ame-%25s%22;
@@ -2881,19 +2881,21 @@
 tartingT
-I
+i
 me
+Ms
  = -1;%0A%0A
@@ -2929,16 +2929,18 @@
 tionTime
+Ms
  = -1;%0A%0A
@@ -3139,19 +3139,21 @@
 tartingT
-I
+i
 me
+Ms
  == -1) 
@@ -3172,19 +3172,21 @@
 tartingT
-I
+i
 me
+Ms
  = Syste
@@ -3230,24 +3230,26 @@
 xecutionTime
+Ms
  = mStarting
@@ -3249,19 +3249,21 @@
 tartingT
-I
+i
 me
+Ms
 ;%0A      
@@ -3381,16 +3381,18 @@
 tionTime
+Ms
 );%0A     
@@ -3412,16 +3412,18 @@
 tionTime
+Ms
  = mCurr
@@ -3745,24 +3745,26 @@
 xecutionTime
+Ms
  - mStarting
@@ -3768,11 +3768,13 @@
 ingT
-I
+i
 me
+Ms
 ) * 

Write unit test for the tachyon.HeartbeatThread class