/**
 * Licensed to the Apache Software Foundation (ASF) 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 org.apache.hadoop.fs;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.StringReader;

import org.apache.hadoop.conf.Configuration;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

public class TestStat extends FileSystemTestHelper {
  static {
    FileSystem.enableSymlinks();
  }
  private static Stat stat;

  @BeforeAll
  public static void setup() throws Exception {
    stat = new Stat(new Path("/dummypath"),
        4096l, false, FileSystem.get(new Configuration()));
  }

  private class StatOutput {
    final String doesNotExist;
    final String directory;
    final String file;
    final String[] symlinks;
    final String stickydir;

    StatOutput(String doesNotExist, String directory, String file,
        String[] symlinks, String stickydir) {
      this.doesNotExist = doesNotExist;
      this.directory = directory;
      this.file = file;
      this.symlinks = symlinks;
      this.stickydir = stickydir;
    }

    void test() throws Exception {
      BufferedReader br;
      FileStatus status;

      try {
        br = new BufferedReader(new StringReader(doesNotExist));
        stat.parseExecResult(br);
      } catch (FileNotFoundException e) {
        // expected
      }

      br = new BufferedReader(new StringReader(directory));
      stat.parseExecResult(br);
      status = stat.getFileStatusForTesting();
      assertTrue(status.isDirectory());

      br = new BufferedReader(new StringReader(file));
      stat.parseExecResult(br);
      status = stat.getFileStatusForTesting();
      assertTrue(status.isFile());

      for (String symlink : symlinks) {
        br = new BufferedReader(new StringReader(symlink));
        stat.parseExecResult(br);
        status = stat.getFileStatusForTesting();
        assertTrue(status.isSymlink());
      }

      br = new BufferedReader(new StringReader(stickydir));
      stat.parseExecResult(br);
      status = stat.getFileStatusForTesting();
      assertTrue(status.isDirectory());
      assertTrue(status.getPermission().getStickyBit());
    }
  }

  @Test
  @Timeout(value = 10)
  public void testStatLinux() throws Exception {
    String[] symlinks = new String[] {
        "6,symbolic link,1373584236,1373584236,777,andrew,andrew,`link' -> `target'",
        "6,symbolic link,1373584236,1373584236,777,andrew,andrew,'link' -> 'target'"
    };
    StatOutput linux = new StatOutput(
        "stat: cannot stat `watermelon': No such file or directory",
        "4096,directory,1373584236,1373586485,755,andrew,root,`.'",
        "0,regular empty file,1373584228,1373584228,644,andrew,andrew,`target'",
        symlinks,
        "4096,directory,1374622334,1375124212,1755,andrew,andrew,`stickydir'");
    linux.test();
  }

  @Test
  @Timeout(value = 10)
  public void testStatFreeBSD() throws Exception {
    String[] symlinks = new String[] {
        "6,Symbolic Link,1373508941,1373508941,120755,awang,awang,`link' -> `target'"
    };
    
    StatOutput freebsd = new StatOutput(
        "stat: symtest/link: stat: No such file or directory",
        "512,Directory,1373583695,1373583669,40755,awang,awang,`link' -> `'",
        "0,Regular File,1373508937,1373508937,100644,awang,awang,`link' -> `'",
        symlinks,
        "512,Directory,1375139537,1375139537,41755,awang,awang,`link' -> `'");
    freebsd.test();
  }

  @Test
  @Timeout(value = 10)
  public void testStatFileNotFound() throws Exception {
    assumeTrue(Stat.isAvailable());
    try {
      stat.getFileStatus();
      fail("Expected FileNotFoundException");
    } catch (FileNotFoundException e) {
      // expected
    }
  }

  @Test
  @Timeout(value = 10)
  public void testStatEnvironment() throws Exception {
    assertEquals("C", stat.getEnvironment("LANG"));
  }

  @Test
  @Timeout(value = 10)
  public void testStat() throws Exception {
    assumeTrue(Stat.isAvailable());
    FileSystem fs = FileSystem.getLocal(new Configuration());
    Path testDir = new Path(getTestRootPath(fs), "teststat");
    fs.mkdirs(testDir);
    Path sub1 = new Path(testDir, "sub1");
    Path sub2 = new Path(testDir, "sub2");
    fs.mkdirs(sub1);
    fs.createSymlink(sub1, sub2, false);
    FileStatus stat1 = new Stat(sub1, 4096l, false, fs).getFileStatus();
    FileStatus stat2 = new Stat(sub2, 0, false, fs).getFileStatus();
    assertTrue(stat1.isDirectory());
    assertFalse(stat2.isDirectory());
    fs.delete(testDir, true);
  }
}
