/*
 * Copyright (c) 2012 Philipp Meinen <philipp@bind.ch>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software
 * is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
 * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package ch.bind.philib.util;

import static org.junit.Assert.*;

import org.junit.Test;

public class LeakyBucketTest {

	private static final long SEC = 1000000000L;

	@Test
	public void normalTimeInitialCalc() {
		LeakyBucket bc = LeakyBucket.withReleasePerSecond(2500, 1000);
		assertEquals(1000, bc.available());
		assertEquals(1000, bc.getCapacity());
	}

	@Test
	public void fakeTimeInitialCalc() {
		LeakyBucket bc = LeakyBucket.withReleasePerSecond(100, 1000);
		// not quite a second
		long time = SEC - 1;
		assertEquals(99, bc.available(time));
		time++; // 1sec
		assertEquals(100, bc.available(time));
	}

	@Test
	public void fakeTimeCountSmallSteps() {
		LeakyBucket bc = LeakyBucket.withReleasePerSecond(2500, 2500);
		long time = SEC;
		long interval = 400000; // SEC/2500
		assertEquals(2500, bc.available(time));
		assertEquals(0, bc.nextAvailableNano(time));
		// simulate one whole day
		for (int numSeconds = 0; numSeconds < 86400; numSeconds++) {
			assertEquals(2500, bc.available(time));
			assertEquals(0, bc.nextAvailableNano(time));

			bc.acquire(2500, time);
			assertEquals(0, bc.available(time));
			assertEquals(interval, bc.nextAvailableNano(time));
			time++; // x sec + 1 nano
			assertEquals(0, bc.available(time));
			assertEquals(interval - 1, bc.nextAvailableNano(time));

			// x.5 sec - 1 nano
			time += (SEC / 2 - 2);
			assertEquals(1249, bc.available(time));
			// x.5 sec
			time++;
			assertEquals(1250, bc.available(time));

			// x.5 sec + 1 nano
			time++;
			assertEquals(1250, bc.available(time));

			// x + 1 sec - 1 nano
			time += (SEC / 2 - 2);
			assertEquals(2499, bc.available(time));

			// x + 1sec
			time++;
			assertEquals(2500, bc.available(time));
		}
	}

	@Test
	public void acquireWithRealTime() {
		LeakyBucket bc = LeakyBucket.withReleasePerSecond(2500, 1000);
		long start = System.nanoTime();
		assertEquals(1000, bc.available());
		bc.acquire(1000);
		long moreAcquired = 0;
		long loops = 0;
		long badNextAvailLoops = 0;
		while (moreAcquired < 5000) {
			loops++;
			long nextAvail = bc.nextAvailableNano();
			long a = bc.available();
			if (a > 0) {
				if (nextAvail > 0) {
					badNextAvailLoops++;
				}
				bc.acquire(a);
				moreAcquired += a;
			} else {
				assertTrue(nextAvail > 0);
			}
		}
		long end = System.nanoTime();
		System.out.printf("bad nextAvail loops: %d/%d%n", badNextAvailLoops, loops);

		long totalTime = end - start;
		// 2 milliseconds or 0.1% should be ok even for lame computers
		long delta = 5 * 1000 * 1000;
		// should take 2 seconds
		long min = 2 * 1000 * 1000 * 1000 - delta;
		long max = 2 * 1000 * 1000 * 1000 + delta;
		assertTrue(totalTime >= min && totalTime <= max);
		System.out.println("time: " + totalTime);
	}
}

@@ -1965,17 +1965,19 @@
 ; // SEC
-/
+ / 
 2500%0D%0A%09%09
@@ -3180,58 +3180,8 @@
 0;%0D%0A
-%09%09long loops = 0;%0D%0A%09%09long badNextAvailLoops = 0;%0D%0A
 %09%09wh
@@ -3214,21 +3214,43 @@
 %7B%0D%0A%09%09%09lo
-ops++
+ng time = System.nanoTime()
 ;%0D%0A%09%09%09lo
@@ -3285,16 +3285,20 @@
 bleNano(
+time
 );%0D%0A%09%09%09l
@@ -3318,16 +3318,20 @@
 ailable(
+time
 );%0D%0A%09%09%09i
@@ -3347,19 +3347,28 @@
  %7B%0D%0A%09%09%09%09
-if 
+assertEquals
 (nextAva
@@ -3373,49 +3373,30 @@
 vail
- %3E 0) %7B%0D%0A%09%09%09%09%09badNextAvailLoops++;%0D%0A%09%09%09%09%7D
+, 0); // available now
 %0D%0A%09%09
@@ -3409,16 +3409,22 @@
 cquire(a
+, time
 );%0D%0A%09%09%09%09
@@ -3536,90 +3536,8 @@
 );%0D%0A
-%09%09System.out.printf(%22bad nextAvail loops: %25d/%25d%25n%22, badNextAvailLoops, loops);%0D%0A%0D%0A
 %09%09lo

improve unit test