/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in compliance with the License, which is
 * available at www.apache.org/licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.master.file.meta;

import alluxio.master.file.options.CreateFileOptions;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.internal.util.reflection.Whitebox;

import java.util.Collections;
import java.util.List;

/**
 * Unit tests for {@link TtlBucketList}.
 */
public class TtlBucketListTest {
  private static final long BUCKET_INTERVAL = 10;
  private static final long BUCKET1_START = 0;
  private static final long BUCKET1_END = BUCKET1_START + BUCKET_INTERVAL;
  private static final long BUCKET2_START = BUCKET1_END;
  private static final long BUCKET2_END =  BUCKET2_START + BUCKET_INTERVAL;
  private static final InodeFile BUCKET1_FILE1 =
      InodeFile.create(0, 0, "ignored", CreateFileOptions.defaults().setTtl(BUCKET1_START));
  private static final InodeFile BUCKET1_FILE2 =
      InodeFile.create(1, 0, "ignored", CreateFileOptions.defaults().setTtl(BUCKET1_END - 1));
  private static final InodeFile BUCKET2_FILE =
      InodeFile.create(2, 0, "ignored", CreateFileOptions.defaults().setTtl(BUCKET2_START));
  private static long sOldTtlIntervalMs;

  private TtlBucketList mBucketList;

  /**
   * Sets up the TTL interval before a single test runs.
   */
  @BeforeClass
  public static void beforeClass() {
    sOldTtlIntervalMs = TtlBucket.getTtlIntervalMs();
    TtlBucketPrivateAccess.setTtlIntervalMs(BUCKET_INTERVAL);
    Whitebox.setInternalState(BUCKET1_FILE1, "mCreationTimeMs", 0);
    Whitebox.setInternalState(BUCKET1_FILE2, "mCreationTimeMs", 0);
    Whitebox.setInternalState(BUCKET2_FILE, "mCreationTimeMs", 0);
  }

  /**
   * Resets the TTL interval after all test ran.
   */
  @AfterClass
  public static void afterClass() {
    TtlBucketPrivateAccess.setTtlIntervalMs(sOldTtlIntervalMs);
  }

  /**
   * Sets up a new {@link TtlBucketList} before a test runs.
   */
  @Before
  public void before() {
    mBucketList = new TtlBucketList();
  }

  private List<TtlBucket> getSortedExpiredBuckets(long expireTime) {
    List<TtlBucket> buckets = Lists.newArrayList(mBucketList.getExpiredBuckets(expireTime));
    Collections.sort(buckets);
    return buckets;
  }

  private void assertExpired(List<TtlBucket> expiredBuckets, int bucketIndex, InodeFile... files) {
    TtlBucket bucket = expiredBuckets.get(bucketIndex);
    Assert.assertEquals(files.length, bucket.getFiles().size());
    Assert.assertTrue(bucket.getFiles().containsAll(Lists.newArrayList(files)));
  }

  /**
   * Tests the {@link TtlBucketList#insert(InodeFile)} method.
   */
  @Test
  public void insertTest() {
    // No bucket should expire.
    List<TtlBucket> expired = getSortedExpiredBuckets(BUCKET1_START);
    Assert.assertTrue(expired.isEmpty());

    mBucketList.insert(BUCKET1_FILE1);
    // The first bucket should expire.
    expired = getSortedExpiredBuckets(BUCKET1_END);
    assertExpired(expired, 0, BUCKET1_FILE1);

    mBucketList.insert(BUCKET1_FILE2);
    // Only the first bucket should expire.
    for (long end = BUCKET2_START; end < BUCKET2_END; end++) {
      expired = getSortedExpiredBuckets(end);
      assertExpired(expired, 0, BUCKET1_FILE1, BUCKET1_FILE2);
    }

    mBucketList.insert(BUCKET2_FILE);
    // All buckets should expire.
    expired = getSortedExpiredBuckets(BUCKET2_END);
    assertExpired(expired, 0, BUCKET1_FILE1, BUCKET1_FILE2);
    assertExpired(expired, 1, BUCKET2_FILE);
  }

  /**
   * Tests the {@link TtlBucketList#remove(InodeFile)} method.
   */
  @Test
  public void removeTest() {
    mBucketList.insert(BUCKET1_FILE1);
    mBucketList.insert(BUCKET1_FILE2);
    mBucketList.insert(BUCKET2_FILE);

    List<TtlBucket> expired = getSortedExpiredBuckets(BUCKET1_END);
    assertExpired(expired, 0, BUCKET1_FILE1, BUCKET1_FILE2);

    mBucketList.remove(BUCKET1_FILE1);
    expired = getSortedExpiredBuckets(BUCKET1_END);
    // Only the first bucket should expire, and there should be only one BUCKET1_FILE2 in it.
    assertExpired(expired, 0, BUCKET1_FILE2);

    mBucketList.remove(BUCKET1_FILE2);
    expired = getSortedExpiredBuckets(BUCKET1_END);
    // Only the first bucket should expire, and there should be no files in it.
    assertExpired(expired, 0); // nothing in bucket 0.

    expired = getSortedExpiredBuckets(BUCKET2_END);
    // All buckets should expire.
    assertExpired(expired, 0); // nothing in bucket 0.
    assertExpired(expired, 1, BUCKET2_FILE);

    // Remove bucket 0.
    expired = getSortedExpiredBuckets(BUCKET1_END);
    mBucketList.removeBuckets(Sets.newHashSet(expired));

    expired = getSortedExpiredBuckets(BUCKET2_END);
    // The only remaining bucket is bucket 1, it should expire.
    assertExpired(expired, 0, BUCKET2_FILE);

    mBucketList.remove(BUCKET2_FILE);
    expired = getSortedExpiredBuckets(BUCKET2_END);
    assertExpired(expired, 0); // nothing in bucket.

    mBucketList.removeBuckets(Sets.newHashSet(expired));
    // No bucket should exist now.
    expired = getSortedExpiredBuckets(BUCKET2_END);
    Assert.assertEquals(0, expired.size());
  }
}

@@ -680,37 +680,8 @@
 ts;%0A
-import org.junit.AfterClass;%0A
 impo
@@ -756,16 +756,44 @@
 eClass;%0A
+import org.junit.ClassRule;%0A
 import o
@@ -1722,32 +1722,33 @@
 UCKET2_START));%0A
+%0A
   private static
@@ -1745,75 +1745,136 @@
 ate 
-static long sOldTtlIntervalMs;%0A%0A  private TtlBucketList mBucketList
+TtlBucketList mBucketList;%0A%0A  @ClassRule%0A  public static TtlIntervalRule sTtlIntervalRule = new TtlIntervalRule(BUCKET_INTERVAL)
 ;%0A%0A 
@@ -1997,124 +1997,8 @@
 ) %7B%0A
-    sOldTtlIntervalMs = TtlBucket.getTtlIntervalMs();%0A    TtlBucketPrivateAccess.setTtlIntervalMs(BUCKET_INTERVAL);%0A
     
@@ -2205,188 +2205,8 @@
  %7D%0A%0A
-  /**%0A   * Resets the TTL interval after all test ran.%0A   */%0A  @AfterClass%0A  public static void afterClass() %7B%0A    TtlBucketPrivateAccess.setTtlIntervalMs(sOldTtlIntervalMs);%0A  %7D%0A%0A
   /*

Apply TtlBucketIntervalRule to TtlBucketListTest