content: package com.termux.shared.file.tests;
  
  import android.content.Context;
  
  import androidx.annotation.NonNull;
  
  import com.termux.shared.errors.Errno;
  import com.termux.shared.file.FileUtils;
  import com.termux.shared.file.FileUtilsErrno;
  import com.termux.shared.logger.Logger;
  import com.termux.shared.errors.Error;
  
  import java.io.File;
  import java.nio.charset.Charset;
  import java.util.Arrays;
  import java.util.List;
  
  public class FileUtilsTests {
  
      private static final String LOG_TAG = "FileUtilsTests";
  
      /**
       * Run basic tests for {@link FileUtils} class.
       *
       * Move tests need to be written, specially for failures.
       *
       * The log level must be set to verbose.
       *
       * Run at app startup like in an activity
       * FileUtilsTests.runTests(this, TermuxConstants.TERMUX_HOME_DIR_PATH + "/FileUtilsTests");
       *
       * @param context The {@link Context} for operations.
       */
      public static void runTests(@NonNull final Context context, @NonNull final String testRootDirectoryPath) {
          try {
              Logger.logInfo(LOG_TAG, "Running tests");
              Logger.logInfo(LOG_TAG, "testRootDirectoryPath: \"" + testRootDirectoryPath + "\"");
  
              String fileUtilsTestsDirectoryCanonicalPath = FileUtils.getCanonicalPath(testRootDirectoryPath, null);
              assertEqual("FileUtilsTests directory path is not a canonical path", testRootDirectoryPath, fileUtilsTestsDirectoryCanonicalPath);
  
              runTestsInner(testRootDirectoryPath);
              Logger.logInfo(LOG_TAG, "All tests successful");
          } catch (Exception e) {
              Logger.logErrorExtended(LOG_TAG, e.getMessage());
              Logger.showToast(context, e.getMessage() != null ? e.getMessage().replaceAll("(?s)\nFull Error:\n.*", "") : null, true);
          }
      }
  
      private static void runTestsInner(@NonNull final String testRootDirectoryPath) throws Exception {
          Error error;
          String label;
          String path;
  
          /*
           * - dir1
           *  - sub_dir1
           *  - sub_reg1
           *  - sub_sym1 (absolute symlink to dir2)
           *  - sub_sym2 (copy of sub_sym1 for symlink to dir2)
           *  - sub_sym3 (relative symlink to dir4)
           * - dir2
           *  - sub_reg1
           *  - sub_reg2 (copy of dir2/sub_reg1)
           * - dir3 (copy of dir1)
           * - dir4 (moved from dir3)
           */
  
          String dir1_label = "dir1";
          String dir1_path = testRootDirectoryPath + "/dir1";
  
          String dir1__sub_dir1_label = "dir1/sub_dir1";
          String dir1__sub_dir1_path = dir1_path + "/sub_dir1";
  
          String dir1__sub_dir2_label = "dir1/sub_dir2";
          String dir1__sub_dir2_path = dir1_path + "/sub_dir2";
  
          String dir1__sub_dir3_label = "dir1/sub_dir3";
          String dir1__sub_dir3_path = dir1_path + "/sub_dir3";
  
          String dir1__sub_dir3__sub_reg1_label = "dir1/sub_dir3/sub_reg1";
          String dir1__sub_dir3__sub_reg1_path = dir1__sub_dir3_path + "/sub_reg1";
  
          String dir1__sub_reg1_label = "dir1/sub_reg1";
          String dir1__sub_reg1_path = dir1_path + "/sub_reg1";
  
          String dir1__sub_sym1_label = "dir1/sub_sym1";
          String dir1__sub_sym1_path = dir1_path + "/sub_sym1";
  
          String dir1__sub_sym2_label = "dir1/sub_sym2";
          String dir1__sub_sym2_path = dir1_path + "/sub_sym2";
  
          String dir1__sub_sym3_label = "dir1/sub_sym3";
          String dir1__sub_sym3_path = dir1_path + "/sub_sym3";
  
  
          String dir2_label = "dir2";
          String dir2_path = testRootDirectoryPath + "/dir2";
  
          String dir2__sub_reg1_label = "dir2/sub_reg1";
          String dir2__sub_reg1_path = dir2_path + "/sub_reg1";
  
          String dir2__sub_reg2_label = "dir2/sub_reg2";
          String dir2__sub_reg2_path = dir2_path + "/sub_reg2";
  
  
          String dir3_label = "dir3";
          String dir3_path = testRootDirectoryPath + "/dir3";
  
          String dir4_label = "dir4";
          String dir4_path = testRootDirectoryPath + "/dir4";
  
  
  
  
  
          // Create or clear test root directory file
          label = "testRootDirectoryPath";
          error = FileUtils.clearDirectory(label, testRootDirectoryPath);
          assertEqual("Failed to create " + label + " directory file", null, error);
  
          if (!FileUtils.directoryFileExists(testRootDirectoryPath, false))
              throwException("The " + label + " directory file does not exist as expected after creation");
  
  
          // Create dir1 directory file
          error = FileUtils.createDirectoryFile(dir1_label, dir1_path);
          assertEqual("Failed to create " + dir1_label + " directory file", null, error);
  
          // Create dir2 directory file
          error = FileUtils.createDirectoryFile(dir2_label, dir2_path);
          assertEqual("Failed to create " + dir2_label + " directory file", null, error);
  
  
  
  
  
          // Create dir1/sub_dir1 directory file
          label = dir1__sub_dir1_label; path = dir1__sub_dir1_path;
          error = FileUtils.createDirectoryFile(label, path);
          assertEqual("Failed to create " + label + " directory file", null, error);
          if (!FileUtils.directoryFileExists(path, false))
              throwException("The " + label + " directory file does not exist as expected after creation");
  
          // Create dir1/sub_reg1 regular file
          label = dir1__sub_reg1_label; path = dir1__sub_reg1_path;
          error = FileUtils.createRegularFile(label, path);
          assertEqual("Failed to create " + label + " regular file", null, error);
          if (!FileUtils.regularFileExists(path, false))
              throwException("The " + label + " regular file does not exist as expected after creation");
  
          // Create dir1/sub_sym1 -> dir2 absolute symlink file
          label = dir1__sub_sym1_label; path = dir1__sub_sym1_path;
          error = FileUtils.createSymlinkFile(label, dir2_path, path);
          assertEqual("Failed to create " + label + " symlink file", null, error);
          if (!FileUtils.symlinkFileExists(path))
              throwException("The " + label + " symlink file does not exist as expected after creation");
  
          // Copy dir1/sub_sym1 symlink file to dir1/sub_sym2
          label = dir1__sub_sym2_label; path = dir1__sub_sym2_path;
          error = FileUtils.copySymlinkFile(label, dir1__sub_sym1_path, path, false);
          assertEqual("Failed to copy " + dir1__sub_sym1_label + " symlink file to " + label, null, error);
          if (!FileUtils.symlinkFileExists(path))
              throwException("The " + label + " symlink file does not exist as expected after copying it from " + dir1__sub_sym1_label);
          if (!new File(path).getCanonicalPath().equals(dir2_path))
              throwException("The " + label + " symlink file does not point to " + dir2_label);
  
  
  
  
  
          // Write "line1" to dir2/sub_reg1 regular file
          label = dir2__sub_reg1_label; path = dir2__sub_reg1_path;
          error = FileUtils.writeTextToFile(label, path, Charset.defaultCharset(), "line1", false);
          assertEqual("Failed to write string to " + label + " file with append mode false", null, error);
          if (!FileUtils.regularFileExists(path, false))
              throwException("The " + label + " file does not exist as expected after writing to it with append mode false");
  
          // Write "line2" to dir2/sub_reg1 regular file
          error = FileUtils.writeTextToFile(label, path, Charset.defaultCharset(), "\nline2", true);
          assertEqual("Failed to write string to " + label + " file with append mode true", null, error);
  
          // Read dir2/sub_reg1 regular file
          StringBuilder dataStringBuilder = new StringBuilder();
          error = FileUtils.readTextFromFile(label, path, Charset.defaultCharset(), dataStringBuilder, false);
          assertEqual("Failed to read from " + label + " file", null, error);
          assertEqual("The data read from " + label + " file in not as expected", "line1\nline2", dataStringBuilder.toString());
  
          // Copy dir2/sub_reg1 regular file to dir2/sub_reg2 file
          label = dir2__sub_reg2_label; path = dir2__sub_reg2_path;
          error = FileUtils.copyRegularFile(label, dir2__sub_reg1_path, path, false);
          assertEqual("Failed to copy " + dir2__sub_reg1_label + " regular file to " + label, null, error);
          if (!FileUtils.regularFileExists(path, false))
              throwException("The " + label + " regular file does not exist as expected after copying it from " + dir2__sub_reg1_label);
  
  
  
  
  
          // Copy dir1 directory file to dir3
          label = dir3_label; path = dir3_path;
          error = FileUtils.copyDirectoryFile(label, dir2_path, path, false);
          assertEqual("Failed to copy " + dir2_label + " directory file to " + label, null, error);
          if (!FileUtils.directoryFileExists(path, false))
              throwException("The " + label + " directory file does not exist as expected after copying it from " + dir2_label);
  
          // Copy dir1 directory file to dir3 again to test overwrite
          label = dir3_label; path = dir3_path;
          error = FileUtils.copyDirectoryFile(label, dir2_path, path, false);
          assertEqual("Failed to copy " + dir2_label + " directory file to " + label, null, error);
          if (!FileUtils.directoryFileExists(path, false))
              throwException("The " + label + " directory file does not exist as expected after copying it from " + dir2_label);
  
          // Move dir3 directory file to dir4
          label = dir4_label; path = dir4_path;
          error = FileUtils.moveDirectoryFile(label, dir3_path, path, false);
          assertEqual("Failed to move " + dir3_label + " directory file to " + label, null, error);
          if (!FileUtils.directoryFileExists(path, false))
              throwException("The " + label + " directory file does not exist as expected after copying it from " + dir3_label);
  
  
  
  
  
          // Create dir1/sub_sym3 -> dir4 relative symlink file
          label = dir1__sub_sym3_label; path = dir1__sub_sym3_path;
          error = FileUtils.createSymlinkFile(label, "../dir4", path);
          assertEqual("Failed to create " + label + " symlink file", null, error);
          if (!FileUtils.symlinkFileExists(path))
              throwException("The " + label + " symlink file does not exist as expected after creation");
  
          // Create dir1/sub_sym3 -> dirX relative dangling symlink file
          // This is to ensure that symlinkFileExists returns true if a symlink file exists but is dangling
          label = dir1__sub_sym3_label; path = dir1__sub_sym3_path;
          error = FileUtils.createSymlinkFile(label, "../dirX", path);
          assertEqual("Failed to create " + label + " symlink file", null, error);
          if (!FileUtils.symlinkFileExists(path))
              throwException("The " + label + " dangling symlink file does not exist as expected after creation");
  
  
  
  
  
          // Delete dir1/sub_sym2 symlink file
          label = dir1__sub_sym2_label; path = dir1__sub_sym2_path;
          error = FileUtils.deleteSymlinkFile(label, path, false);
          assertEqual("Failed to delete " + label + " symlink file", null, error);
          if (FileUtils.fileExists(path, false))
              throwException("The " + label + " symlink file still exist after deletion");
  
          // Check if dir2 directory file still exists after deletion of dir1/sub_sym2 since it was a symlink to dir2
          // When deleting a symlink file, its target must not be deleted
          label = dir2_label; path = dir2_path;
          if (!FileUtils.directoryFileExists(path, false))
              throwException("The " + label + " directory file has unexpectedly been deleted after deletion of " + dir1__sub_sym2_label);
  
  
  
  
  
          // Delete dir1 directory file
          label = dir1_label; path = dir1_path;
          error = FileUtils.deleteDirectoryFile(label, path, false);
          assertEqual("Failed to delete " + label + " directory file", null, error);
          if (FileUtils.fileExists(path, false))
              throwException("The " + label + " directory file still exist after deletion");
  
  
          // Check if dir2 directory file and dir2/sub_reg1 regular file still exist after deletion of
          // dir1 since there was a dir1/sub_sym1 symlink to dir2 in it
          // When deleting a directory, any targets of symlinks must not be deleted when deleting symlink files
          label = dir2_label; path = dir2_path;
          if (!FileUtils.directoryFileExists(path, false))
              throwException("The " + label + " directory file has unexpectedly been deleted after deletion of " + dir1_label);
          label = dir2__sub_reg1_label; path = dir2__sub_reg1_path;
          if (!FileUtils.fileExists(path, false))
              throwException("The " + label + " regular file has unexpectedly been deleted after deletion of " + dir1_label);
  
  
  
  
  
          // Delete dir2/sub_reg1 regular file
          label = dir2__sub_reg1_label; path = dir2__sub_reg1_path;
          error = FileUtils.deleteRegularFile(label, path, false);
          assertEqual("Failed to delete " + label + " regular file", null, error);
          if (FileUtils.fileExists(path, false))
              throwException("The " + label + " regular file still exist after deletion");
  
  
          List<String> ignoredSubFilePaths = Arrays.asList(dir1__sub_dir2_path, dir1__sub_dir3__sub_reg1_path);
  
          // Create dir1 directory file
          error = FileUtils.createDirectoryFile(dir1_label, dir1_path);
          assertEqual("Failed to create " + dir1_label + " directory file", null, error);
  
          // Test empty dir
          error = FileUtils.validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(dir1_label, dir1_path, ignoredSubFilePaths, false);
          assertEqual("Failed to validate if " + dir1_label + " directory file is empty", null, error);
  
  
          // Create dir1/sub_dir3 directory file
          label = dir1__sub_dir3_label; path = dir1__sub_dir3_path;
          error = FileUtils.createDirectoryFile(label, path);
          assertEqual("Failed to create " + label + " directory file", null, error);
          if (!FileUtils.directoryFileExists(path, false))
              throwException("The " + label + " directory file does not exist as expected after creation");
  
          // Test parent dir existing of non existing ignored regular file
          error = FileUtils.validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(dir1_label, dir1_path, ignoredSubFilePaths, false);
          assertErrnoEqual("Failed to validate if " + dir1_label + " directory file is empty with parent dir existing of non existing ignored regular file", FileUtilsErrno.ERRNO_NON_EMPTY_DIRECTORY_FILE, error);
  
  
          // Write "line1" to dir1/sub_dir3/sub_reg1 regular file
          label = dir1__sub_dir3__sub_reg1_label; path = dir1__sub_dir3__sub_reg1_path;
          error = FileUtils.writeTextToFile(label, path, Charset.defaultCharset(), "line1", false);
          assertEqual("Failed to write string to " + label + " file with append mode false", null, error);
          if (!FileUtils.regularFileExists(path, false))
              throwException("The " + label + " file does not exist as expected after writing to it with append mode false");
  
          // Test ignored regular file existing
          error = FileUtils.validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(dir1_label, dir1_path, ignoredSubFilePaths, false);
          assertEqual("Failed to validate if " + dir1_label + " directory file is empty with ignored regular file existing", null, error);
  
  
          // Create dir1/sub_dir2 directory file
          label = dir1__sub_dir2_label; path = dir1__sub_dir2_path;
          error = FileUtils.createDirectoryFile(label, path);
          assertEqual("Failed to create " + label + " directory file", null, error);
          if (!FileUtils.directoryFileExists(path, false))
              throwException("The " + label + " directory file does not exist as expected after creation");
  
          // Test ignored dir file existing
          error = FileUtils.validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(dir1_label, dir1_path, ignoredSubFilePaths, false);
          assertEqual("Failed to validate if " + dir1_label + " directory file is empty with ignored dir file existing", null, error);
  
  
          // Create dir1/sub_dir1 directory file
          label = dir1__sub_dir1_label; path = dir1__sub_dir1_path;
          error = FileUtils.createDirectoryFile(label, path);
          assertEqual("Failed to create " + label + " directory file", null, error);
          if (!FileUtils.directoryFileExists(path, false))
              throwException("The " + label + " directory file does not exist as expected after creation");
  
          // Test non ignored dir file existing
          error = FileUtils.validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(dir1_label, dir1_path, ignoredSubFilePaths, false);
          assertErrnoEqual("Failed to validate if " + dir1_label + " directory file is empty with non ignored dir file existing", FileUtilsErrno.ERRNO_NON_EMPTY_DIRECTORY_FILE, error);
  
  
          // Delete dir1 directory file
          label = dir1_label; path = dir1_path;
          error = FileUtils.deleteDirectoryFile(label, path, false);
          assertEqual("Failed to delete " + label + " directory file", null, error);
  
  
          FileUtils.getFileType("/dev/ptmx", false);
          FileUtils.getFileType("/dev/null", false);
      }
  
  
  
      public static void assertEqual(@NonNull final String message, final String expected, final Error actual) throws Exception {
          String actualString = actual != null ? actual.getMessage() : null;
          if (!equalsRegardingNull(expected, actualString))
              throwException(message + "\nexpected: \"" + expected + "\"\nactual: \"" + actualString + "\"\nFull Error:\n" + (actual != null ? actual.toString() : ""));
      }
  
      public static void assertEqual(@NonNull final String message, final String expected, final String actual) throws Exception {
          if (!equalsRegardingNull(expected, actual))
              throwException(message + "\nexpected: \"" + expected + "\"\nactual: \"" + actual + "\"");
      }
  
      private static boolean equalsRegardingNull(final String expected, final String actual) {
          if (expected == null) {
              return actual == null;
          }
  
          return isEquals(expected, actual);
      }
  
      public static void assertErrnoEqual(@NonNull final String message, final Errno expected, final Error actual) throws Exception {
          if ((expected == null && actual != null) || (expected != null && !expected.equalsErrorTypeAndCode(actual)))
              throwException(message + "\nexpected: \"" + expected + "\"\nactual: \"" + actual + "\"\nFull Error:\n" + (actual != null ? actual.toString() : ""));
      }
  
  
  
      private static boolean isEquals(String expected, String actual) {
          return expected.equals(actual);
      }
  
      public static void throwException(@NonNull final String message) throws Exception {
              throw new Exception(message);
      }
  
  }
  