/*
 * 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.meta;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;

import alluxio.TestLoggerRule;
import alluxio.shell.CommandReturn;
import alluxio.util.OSUtils;
import alluxio.wire.JournalDiskInfo;

import org.apache.log4j.Level;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mockito;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;

public class JournalSpaceMonitorTest {

  private static final String CMD_RETURN_MOCK =
      "Filesystem  Type  1024-blocks      Used Available Capacity Mounted on\n"
          + "/dev/nvme0n1p2  ext4  959863856 145802864 765232784      17% /";

  @Rule
  public TestLoggerRule mLogger = new TestLoggerRule();

  @BeforeClass
  public static void beforeClass() {
    Assume.assumeTrue(OSUtils.isLinux());
  }

  @Test
  public void testNonExistentJournalPath() {
    assertThrows(IllegalArgumentException.class,
        () -> new JournalSpaceMonitor("/nonexistent/path", 10));
  }

  @Test
  public void testSuccessfulInfo() throws IOException  {
    JournalSpaceMonitor monitor = Mockito.spy(
        new JournalSpaceMonitor(Paths.get(".").toAbsolutePath().toString(), 10));
    doReturn(new CommandReturn(0, CMD_RETURN_MOCK)).when(monitor).getRawDiskInfo();
    List<JournalDiskInfo> infos = monitor.getDiskInfo();
    assertEquals(1, infos.size());
    JournalDiskInfo info = infos.get(0);
    assertEquals("/dev/nvme0n1p2", info.getDiskPath());
    assertEquals(959863856L * 1024, info.getTotalAllocatedBytes());
    assertEquals(145802864L * 1024, info.getUsedBytes());
    assertEquals(765232784L * 1024, info.getAvailableBytes());
    assertEquals(83.0, Math.floor(info.getPercentAvailable()), 0.1);
  }

  @Test
  public void testFailedInfo() throws IOException  {
    JournalSpaceMonitor monitor = Mockito.spy(
        new JournalSpaceMonitor(Paths.get(".").toAbsolutePath().toString(), 10));
    doThrow(new IOException("couldnt run")).when(monitor).getRawDiskInfo();
    assertThrows(IOException.class, monitor::getDiskInfo);
  }

  @Test
  public void testLoggingPositive() throws IOException, InterruptedException {
    JournalSpaceMonitor monitor = Mockito.spy(
        new JournalSpaceMonitor(Paths.get(".").toAbsolutePath().toString(), 90));
    doReturn(new CommandReturn(0, CMD_RETURN_MOCK)).when(monitor).getRawDiskInfo();
    monitor.heartbeat(Long.MAX_VALUE);
    assertTrue(mLogger.wasLoggedWithLevel("The journal disk /dev/nvme0n1p2 backing the journal "
        + "has only .* space left", Level.WARN));
  }

  @Test
  public void testLoggingNegative() throws IOException, InterruptedException {
    JournalSpaceMonitor monitor = Mockito.spy(
        new JournalSpaceMonitor(Paths.get(".").toAbsolutePath().toString(), 10));
    doReturn(new CommandReturn(0, CMD_RETURN_MOCK)).when(monitor).getRawDiskInfo();
    monitor.heartbeat(Long.MAX_VALUE);
    assertFalse(mLogger.wasLoggedWithLevel("The journal disk /dev/nvme0n1p2 backing the journal "
        + "has only .* space left", Level.WARN));
  }
}
