/*
 * FileOperation07Move2Test class.
 *
 * Copyright (C) 2007 SATOH Takayuki All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package ts.util.file;

import ts.tester.function.FunctionTester;
import java.io.*;
import java.util.*;

/**
 * {@link ts.util.file.FileOperation06Move FileOperation06Move}クラスの機能試験クラス。
 *
 * @author  佐藤隆之.
 * @version $Revision: 1.1.1.1 $, $Date: 2010-10-16 00:03:46 $
 */
public class FileOperation07Move2Test extends FunctionTester
{
  public static void main(String[] args)
  {
    try {
      run(FileOperation07Move2Test.class, (args.length == 0) ? null : args[0]);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }

  final File WORKDIR = new File("test/data/ts/util/file/FileOperation");
  File[] DIRS = new File[6];
  File[] FILES = new File[10];

  protected void preInvocation(String method)
  {
    WORKDIR.mkdirs();
    DIRS[0] = new File(WORKDIR, "DIRS0");
    DIRS[1] = new File(DIRS[0], "DIRS1");
    DIRS[2] = new File(DIRS[0], "DIRS2");
    DIRS[3] = new File(DIRS[1], "DIRS3");
    DIRS[4] = new File(DIRS[2], "DIRS4");
    DIRS[5] = new File(DIRS[2], "DIRS5");
    FILES[0] = new File(DIRS[0], "FILES0");
    FILES[1] = new File(DIRS[1], "FILES1");
    FILES[2] = new File(DIRS[2], "FILES2");
    FILES[3] = new File(DIRS[3], "FILES3");
    FILES[4] = new File(DIRS[4], "FILES4");
    FILES[5] = new File(DIRS[0], "FILES5");
    FILES[6] = new File(DIRS[1], "FILES6");
    FILES[7] = new File(DIRS[2], "FILES7");
    FILES[8] = new File(DIRS[3], "FILES8");
    FILES[9] = new File(DIRS[4], "FILES9");
    try {
      for (File d : DIRS) d.mkdirs();
      for (File f : FILES) f.createNewFile();
    } catch (Exception e) {}

    /*
       DIRS[0]
        +- DIRS[1]
          +- DIRS[3]
            +- FILES[3]
            +- FILES[8]
          +- FILES[1]
          +- FILES[6]
        +- DIRS[2]
          +- DIRS[4]
            +- FILES[4]
            +- FILES[9]
          +- DIRS[5]
          +- FILES[2]
          +- FILES[7]
        +- FILES[0]
        +- FILES[5]
    */
  }

  protected void postInvocation(String method)
  {
    try {
      FileOperation.deleteRecursive(WORKDIR);
    } catch (Exception e) {}
  }
  
  private boolean isMoveToTemporary(File f) {
    if (f == null) return false;
    while (true) {
      File p = f.getParentFile();
      if (p == null) break;
      if (p.getName().startsWith("._")) {
        return true;
      }
      f = p;
    }
    return false;
  }

  /* -- test case -- */

  public void move_renameTo_failed()
  {
    MSG("UNIX環境で別ファイル・システムにrenameToを実行すると失敗するのを模擬。");
    MSG("通常のファイルを同じディレクトリ下に別名で移動した場合。");

    final File dst = new File(DIRS[4], "aaa");

    @SuppressWarnings("serial")
    File src = new File(FILES[4].getPath()) {
      @Override
      public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };
    TRUE(src.exists());
    FALSE(dst.exists());

    try {
      FileOperation.move(src, dst);
    } catch (Exception e) {
      e.printStackTrace();
      NG(e);
    }

    FALSE(src.exists());
    TRUE(dst.exists());
  }

  public void move_renameTo_failed_rollback()
  {
    MSG("通常のファイルを同じディレクトリ下に別名で移動した場合。");

    File dst = new File(DIRS[4], "bbb");

    @SuppressWarnings("serial")
    File src = new File(FILES[4].getPath()) {
      @Override
      public boolean renameTo(File f) {
        return false;
      }
    };
    try {
      FileOperation.move(src, dst);
      NG();
    } catch (Exception e) {
      OK(e);
    }
    TRUE(src.exists());
    FALSE(dst.exists());
  }

  public void move_renameTo_failed_001()
  {
    MSG("通常のファイルを別のディレクトリに移動した場合。");

    final File dst = new File(WORKDIR, "aaa/bbb/f3");

    @SuppressWarnings("serial")
    File src = new File(FILES[3].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    FALSE(dst.exists());
    FALSE(dst.getParentFile().exists());

    try {
      FileOperation.move(src, dst);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(src.exists());
    TRUE(dst.exists());
  }

  public void move_renameTo_failed_rollback_001()
  {
    MSG("通常のファイルを別のディレクトリに移動した場合。");

    final File dst = new File(WORKDIR, "aaa/bbb/f3");

    @SuppressWarnings("serial")
    File src = new File(FILES[3].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (Exception e) {
      OK(e);
    }

    TRUE(src.exists());
    FALSE(dst.exists());
  }

  public void move_renameTo_failed_002()
  {
    MSG("空のディレクトリを同じディレクトリ下に別名で移動した場合。");

    final File dst = new File(DIRS[2], "sss");
    @SuppressWarnings("serial")
    File src = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return false;
        }
        return super.renameTo(f);
      }
    };

    TRUE(src.exists());
    TRUE(src.isDirectory());
    TRUE(src.listFiles().length == 0);
    FALSE(dst.exists());

    try {
      FileOperation.move(src, dst);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(src.exists());
    TRUE(dst.exists());
    TRUE(dst.isDirectory());
    TRUE(dst.listFiles().length == 0);
  }

  public void move_renameTo_failed_rollback_002()
  {
    MSG("空のディレクトリを同じディレクトリ下に別名で移動した場合。");

    final File dst = new File(DIRS[2], "sss");
    @SuppressWarnings("serial")
    File src = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (Exception e) {
      OK(e);
    }

    TRUE(src.exists());
    FALSE(dst.exists());
  }

  public void move_renameTo_failed_003()
  {
    MSG("空のディレクトリを別のディレクトリ下に移動した場合。");

    final File dst = new File(DIRS[3], "f5");

    @SuppressWarnings("serial")
    File src = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return false;
        }
        return super.renameTo(f);
      }
    };

    TRUE(src.exists());
    TRUE(src.isDirectory());
    TRUE(src.listFiles().length == 0);
    FALSE(dst.exists());

    try {
      FileOperation.move(src, dst);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(src.exists());
    TRUE(dst.exists());
    TRUE(dst.isDirectory());
    TRUE(dst.listFiles().length == 0);
  }

  public void move_renameTo_failed_rollback_003()
  {
    MSG("空のディレクトリを別のディレクトリ下に移動した場合。");

    final File dst = new File(DIRS[3], "f5");

    @SuppressWarnings("serial")
    File src = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (Exception e) {
      OK(e);
    }

    TRUE(src.exists());
    FALSE(dst.exists());
  }


  public void move_renameTo_failed_004()
  {
    MSG("空でないディレクトリを移動した場合。");

    final File dst = new File(DIRS[2], "xxx");

    @SuppressWarnings("serial")
    File src = new File(DIRS[3].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    TRUE(src.isDirectory());
    TRUE(src.listFiles().length > 0);
    FALSE(dst.exists());

    try {
      FileOperation.move(src, dst);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(src.exists());
    TRUE(dst.exists());
    TRUE(dst.isDirectory());
    TRUE(dst.listFiles().length > 0);
  }

  public void move_renameTo_failed_rollback_004()
  {
    MSG("空でないディレクトリを移動した場合。");

    final File dst = new File(DIRS[2], "xxx");

    @SuppressWarnings("serial")
    File src = new File(DIRS[3].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (Exception e) {
      OK(e);
    }

    TRUE(src.exists());
    FALSE(dst.exists());
  }

  public void move_renameTo_failed_005()
  {
    MSG("移動先のファイル（＝通常のファイル）が既に存在する場合。");

    final File dst = FILES[6];
    @SuppressWarnings("serial")
    File src = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    FALSE(src.isDirectory());
    TRUE(dst.exists());
    FALSE(dst.isDirectory());

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
      e.printStackTrace();
    }

    final File dst2 = FILES[2];
    @SuppressWarnings("serial")
    File src2 = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        if (!dst2.equals(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src2.exists());
    FALSE(src2.isDirectory());
    TRUE(dst2.exists());
    FALSE(dst2.isDirectory());

    try {
      FileOperation.move(src2, dst2);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    final File dst3 = FILES[2];
    @SuppressWarnings("serial")
    File src3 = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        if (!dst3.equals(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src3.exists());
    TRUE(src3.isDirectory());
    TRUE(dst3.exists());
    FALSE(dst3.isDirectory());

    try {
      FileOperation.move(src3, dst3);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void move_renameTo_failed_rollback_005()
  {
    MSG("移動先のファイル（＝通常のファイル）が既に存在する場合。");

    final File dst = FILES[6];
    @SuppressWarnings("serial")
    File src = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
      e.printStackTrace();
    }

    final File dst2 = FILES[2];
    @SuppressWarnings("serial")
    File src2 = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    TRUE(src2.exists());
    FALSE(src2.isDirectory());
    TRUE(dst2.exists());
    FALSE(dst2.isDirectory());

    try {
      FileOperation.move(src2, dst2);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    final File dst3 = FILES[2];
    @SuppressWarnings("serial")
    File src3 = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    TRUE(src3.exists());
    TRUE(src3.isDirectory());
    TRUE(dst3.exists());
    FALSE(dst3.isDirectory());

    try {
      FileOperation.move(src3, dst3);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void move_renameTo_failed_006()
  {
    MSG("移動先のファイル（＝ディレクトリ）が既に存在する場合。");

    final File dst = DIRS[3];
    @SuppressWarnings("serial")
    File src = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    TRUE(src.isDirectory());
    TRUE(dst.exists());
    TRUE(dst.isDirectory());

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    final File dst2 = DIRS[5];
    @SuppressWarnings("serial")
    File src2 = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        if (!dst2.equals(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src2.exists());
    FALSE(src2.isDirectory());
    TRUE(dst2.exists());
    TRUE(dst2.isDirectory());

    try {
      FileOperation.move(src2, dst2);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void move_renameTo_failed_rollback_006()
  {
    MSG("移動先のファイル（＝ディレクトリ）が既に存在する場合。");

    final File dst = DIRS[3];
    @SuppressWarnings("serial")
    File src = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    TRUE(src.exists());
    TRUE(src.isDirectory());
    TRUE(dst.exists());
    TRUE(dst.isDirectory());

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    final File dst2 = DIRS[5];
    @SuppressWarnings("serial")
    File src2 = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    TRUE(src2.exists());
    FALSE(src2.isDirectory());
    TRUE(dst2.exists());
    TRUE(dst2.isDirectory());

    try {
      FileOperation.move(src2, dst2);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void move_renameTo_failed_007()
  {
    MSG("引数がヌルの場合。");

    @SuppressWarnings("serial")
    File f = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    try {
      FileOperation.move(null, f);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      FileOperation.move(f, null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void move_renameTo_failed_008()
  {
    MSG("移動元のファイルが共有ロックされている場合。");

    final File dst = new File(WORKDIR, "ffff");
    @SuppressWarnings("serial")
    File src = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    FALSE(src.isDirectory());
    FALSE(dst.exists());

    try {
      FileOperationTest.lockFile("shared", src);
      Thread.sleep(1000);
    } catch (Exception e) {
      NG(e);
    }

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    } finally {
      FileOperationTest.releaseLock();
    }

    TRUE(src.exists());
    FALSE(src.isDirectory());
    FALSE(dst.exists());
  }

  public void move_renameTo_failed_rollback_008()
  {
    MSG("移動元のファイルが共有ロックされている場合。");

    final File dst = new File(WORKDIR, "ffff");
    @SuppressWarnings("serial")
    File src = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    try {
      FileOperationTest.lockFile("shared", src);
      Thread.sleep(1000);
    } catch (Exception e) {
      NG(e);
    }

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    } finally {
      FileOperationTest.releaseLock();
    }

    TRUE(src.exists());
    FALSE(src.isDirectory());
    FALSE(dst.exists());
  }

  public void move_renameTo_failed_009()
  {
    MSG("移動元のファイルが排他ロックされている場合。");

    final File dst = new File(WORKDIR, "ggggg");
    @SuppressWarnings("serial")
    File src = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    FALSE(src.isDirectory());
    FALSE(dst.exists());

    try {
      FileOperationTest.lockFile("exclusive", src);
      Thread.sleep(1000);
    } catch (Exception e) {
      NG(e);
    }

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    } finally {
      FileOperationTest.releaseLock();
    }

    TRUE(src.exists());
    FALSE(src.isDirectory());
    FALSE(dst.exists());
  }

  public void move_renameTo_failed_rollback_009()
  {
    MSG("移動元のファイルが排他ロックされている場合。");

    final File dst = new File(WORKDIR, "ggggg");
    @SuppressWarnings("serial")
    File src = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    TRUE(src.exists());
    FALSE(src.isDirectory());
    FALSE(dst.exists());

    try {
      FileOperationTest.lockFile("exclusive", src);
      Thread.sleep(1000);
    } catch (Exception e) {
      NG(e);
    }

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    } finally {
      FileOperationTest.releaseLock();
    }

    TRUE(src.exists());
    FALSE(src.isDirectory());
    FALSE(dst.exists());
  }

  public void move_renameTo_failed_010()
  {
    MSG("移動元のファイルが存在しない場合。");

    final File dst = new File(WORKDIR, "bbb");
    @SuppressWarnings("serial")
    File src = new File(WORKDIR, "aaa") {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (FileNotFoundException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void move_renameTo_failed_rollback_010()
  {
    MSG("移動元のファイルが存在しない場合。");

    final File dst = new File(WORKDIR, "bbb");
    @SuppressWarnings("serial")
    File src = new File(WORKDIR, "aaa") {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };

    try {
      FileOperation.move(src, dst);
      NG();
    } catch (FileNotFoundException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void move_renameTo_failed_withFilter()
  {
    MSG("通常のファイルを同じディレクトリ下に別名で移動した場合。");

    final File dst = new File(DIRS[4], "aaa");
    @SuppressWarnings("serial")
    File src = new File(FILES[4].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };
    TRUE(src.exists());
    FALSE(dst.exists());

    FileFilter filterX = new FileTypeFilter(FileType.Directory);
    try {
      FileOperation.move(src, dst, filterX);
    } catch (Exception e) {
      NG(e);
    }

    final File dst2 = new File(DIRS[4], "aaa");
    @SuppressWarnings("serial")
    File src2 = new File(FILES[4].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };
    TRUE(src2.exists());
    FALSE(dst2.exists());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      FileOperation.move(src2, dst2, filter);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(src2.exists());
    TRUE(dst2.exists());
  }

  public void move_withFilter_renameTo_failed001()
  {
    MSG("通常のファイルを別のディレクトリに移動した場合。");

    final File dst = new File(WORKDIR, "aaa/bbb/f3");
    @SuppressWarnings("serial")
    File src = new File(FILES[3].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    FALSE(dst.exists());
    FALSE(dst.getParentFile().exists());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      FileOperation.move(src, dst, filter);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(src.exists());
    TRUE(dst.exists());
  }

  public void move_withFilter_renameTo_failed002()
  {
    MSG("空のディレクトリを同じディレクトリ下に別名で移動した場合。");

    final File dst = new File(DIRS[2], "sss");
    @SuppressWarnings("serial")
    File src = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    TRUE(src.isDirectory());
    TRUE(src.listFiles().length == 0);
    FALSE(dst.exists());

    FileFilter filterX = new FileTypeFilter(FileType.File);
    try {
      FileOperation.move(src, dst, filterX);
    } catch (Exception e) {
      NG(e);
    }

    TRUE(src.exists());
    TRUE(src.isDirectory());
    TRUE(src.listFiles().length == 0);
    FALSE(dst.exists());

    FileFilter filter = new FileTypeFilter(FileType.Directory);
    try {
      FileOperation.move(src, dst, filter);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(src.exists());
    TRUE(dst.exists());
    TRUE(dst.isDirectory());
    TRUE(dst.listFiles().length == 0);
  }

  public void move_withFilter_renameTo_failed003()
  {
    MSG("空のディレクトリを別のディレクトリ下に移動した場合。");

    final File dst = new File(DIRS[3], "f5");
    @SuppressWarnings("serial")
    File src = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    TRUE(src.isDirectory());
    TRUE(src.listFiles().length == 0);
    FALSE(dst.exists());

    FileFilter filter = new FileTypeFilter(FileType.Directory);
    try {
      FileOperation.move(src, dst, filter);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(src.exists());
    TRUE(dst.exists());
    TRUE(dst.isDirectory());
    TRUE(dst.listFiles().length == 0);
  }

  public void move_withFilter_renameTo_failed004()
  {
    MSG("空でないディレクトリを移動した場合。");

    final File dst = new File(DIRS[2], "xxx");
    @SuppressWarnings("serial")
    File src = new File(DIRS[1].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    TRUE(src.isDirectory());
    TRUE(src.listFiles().length > 0);
    FALSE(dst.exists());

    FileFilter filter = new FileTypeFilter(FileType.Directory);
    try {
      FileOperation.move(src, dst, filter);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(src.exists());
    TRUE(dst.exists());
    TRUE(dst.isDirectory());
    TRUE(dst.listFiles().length > 0);
  }

  public void move_withFilter_renameTo_failed004a()
  {
    MSG("空でないディレクトリを移動した場合。");

    final File dst = new File(DIRS[1], "xxx");
    @SuppressWarnings("serial")
    File src = new File(DIRS[2].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    FALSE(dst.exists());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      FileOperation.move(src, dst, filter);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(dst.exists());
  }

  public void move_withFilter_renameTo_failed005()
  {
    MSG("移動先のファイル（＝通常のファイル）が既に存在する場合。");

    final File dst = FILES[6];
    @SuppressWarnings("serial")
    File src = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    FALSE(src.isDirectory());
    TRUE(dst.exists());
    FALSE(dst.isDirectory());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      FileOperation.move(src, dst, filter);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
      e.printStackTrace();
    }

    final File dst2 = FILES[2];
    @SuppressWarnings("serial")
    File src2 = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        if (!dst2.equals(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src2.exists());
    FALSE(src2.isDirectory());
    TRUE(dst2.exists());
    FALSE(dst2.isDirectory());

    filter = new FileTypeFilter(FileType.File);
    try {
      FileOperation.move(src2, dst2, filter);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    final File dst3 = FILES[2];
    @SuppressWarnings("serial")
    File src3 = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        if (!dst3.equals(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src3.exists());
    TRUE(src3.isDirectory());
    TRUE(dst3.exists());
    FALSE(dst3.isDirectory());

    filter = new FileTypeFilter(FileType.Directory);
    try {
      FileOperation.move(src3, dst3, filter);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void move_withFilter_renameTo_failed006()
  {
    MSG("移動先のファイル（＝ディレクトリ）が既に存在する場合。");

    final File dst = DIRS[3];
    @SuppressWarnings("serial")
    File src = new File(DIRS[5].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src.exists());
    TRUE(src.isDirectory());
    TRUE(dst.exists());
    TRUE(dst.isDirectory());

    FileFilter filter = new FileTypeFilter(FileType.Directory);
    try {
      FileOperation.move(src, dst, filter);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    final File dst2 = DIRS[5];
    @SuppressWarnings("serial")
    File src2 = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        if (!dst2.equals(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    TRUE(src2.exists());
    FALSE(src2.isDirectory());
    TRUE(dst2.exists());
    TRUE(dst2.isDirectory());

    filter = new FileTypeFilter(FileType.File);
    try {
      FileOperation.move(src2, dst2, filter);
      NG();
    } catch (FileAlreadyExistsException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void move_withFilter_renameTo_failed007()
  {
    MSG("引数がヌルの場合。");

    @SuppressWarnings("serial")
    File f = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        return false;
      }
    };
    FileFilter filter = new FileTypeFilter(FileType.File);
    
    try {
      FileOperation.move(null, f, filter);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      FileOperation.move(f, null, filter);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }

    try {
      FileOperation.move(f, f, null);
      NG();
    } catch (AssertionError e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void move_withFilter_renameTo_failed008()
  {
    MSG("移動元のファイルが共有ロックされている場合。");

    final File dst = new File(WORKDIR, "ffff");
    @SuppressWarnings("serial")
    File src = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };
    FileFilter filter = new FileTypeFilter(FileType.File);

    TRUE(src.exists());
    FALSE(src.isDirectory());
    FALSE(dst.exists());

    try {
      FileOperationTest.lockFile("shared", src);
      Thread.sleep(1000);
    } catch (Exception e) {
      NG(e);
    }

    try {
      FileOperation.move(src, dst, filter);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    } finally {
      FileOperationTest.releaseLock();
    }

    TRUE(src.exists());
    FALSE(src.isDirectory());
    FALSE(dst.exists());
  }

  public void move_withFilter_renameTo_failed009()
  {
    MSG("移動元のファイルが排他ロックされている場合。");

    final File dst = new File(WORKDIR, "ggggg");
    @SuppressWarnings("serial")
    File src = new File(FILES[1].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };
    FileFilter filter = new FileTypeFilter(FileType.File);

    TRUE(src.exists());
    FALSE(src.isDirectory());
    FALSE(dst.exists());

    try {
      FileOperationTest.lockFile("exclusive", src);
      Thread.sleep(1000);
    } catch (Exception e) {
      NG(e);
    }

    try {
      FileOperation.move(src, dst, filter);
      NG();
    } catch (IOException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    } finally {
      FileOperationTest.releaseLock();
    }

    TRUE(src.exists());
    FALSE(src.isDirectory());
    FALSE(dst.exists());
  }

  public void move_withFilter_renameTo_failed010()
  {
    MSG("移動元のファイルが存在しない場合。");

    final File dst = new File(WORKDIR, "bbb");
    @SuppressWarnings("serial")
    File src = new File(WORKDIR, "aaa") {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    FALSE(src.exists());
    FALSE(dst.exists());

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      FileOperation.move(src, dst, filter);
      NG();
    } catch (FileNotFoundException e) {
      OK(e);
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveRecursive_renameTo_failed()
  {
    MSG("空でないディレクトリを移動する場合。");
    
    final File dst = new File(WORKDIR, "aaa/bbb/ccc");
    @SuppressWarnings("serial")
    File src = new File(DIRS[0].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    List<File> lst = new ArrayList<File>(20);
    lst.add(src);
    try {
      lst.addAll(FileOperation.listRecursive(src));
    } catch (Exception e) {
      NG(e);
    }
    EQUAL(lst.size(), 16);
    for (File d : DIRS) TRUE(lst.contains(d));
    for (File f : FILES) TRUE(lst.contains(f));

    FALSE(dst.exists());

    try {
      FileOperation.moveRecursive(src, dst);
    } catch (Exception e) {
      NG(e);
    }

    FALSE(src.exists());
    FileOperation fo = new FileOperation();
    try {
      lst.clear();
      lst.add(dst);
      lst.addAll(FileOperation.listRecursive(dst));

      EQUAL(lst.size(), 16);
      for (File d : DIRS) TRUE(lst.contains(fo.getDestinationFile(d,src,dst)));
      for (File f : FILES) TRUE(lst.contains(fo.getDestinationFile(f,src,dst)));
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveRecursive_withFilter_renameTo_failed()
  {
    MSG("空でないディレクトリをコピーする場合。");

    final File dst = new File(WORKDIR, "aaa/bbb/ccc");
    @SuppressWarnings("serial")
    File src = new File(DIRS[0].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    FALSE(dst.exists());
    List<File> lst = new ArrayList<File>(20);
    lst.add(src);
    try {
      lst.addAll(FileOperation.listRecursive(src));
    } catch (Exception e) {
      NG(e);
    }
    EQUAL(lst.size(), 16);
    for (File d : DIRS) TRUE(lst.contains(d));
    for (File f : FILES) TRUE(lst.contains(f));

    FileFilter filter = new FileTypeFilter(FileType.File);
    try {
      FileOperation.moveRecursive(src, dst, filter);
    } catch (Exception e) {
      NG(e);
    }

    FileOperation fo = new FileOperation();
    try {
      lst.clear();
      lst.add(src);
      lst.addAll(FileOperation.listRecursive(src));

      EQUAL(lst.size(), 6);
      for (File d : DIRS) TRUE(lst.contains(d));
      for (File f : FILES) FALSE(lst.contains(f));
    } catch (Exception e) {
      NG(e);
    }
    try {
      lst.clear();
      lst.add(dst);
      lst.addAll(FileOperation.listRecursive(dst));

      EQUAL(lst.size(), 15);
      for (File d : DIRS) {
        if (d.equals(DIRS[5])) continue;
        TRUE(lst.contains(fo.getDestinationFile(d,src,dst)));
      }
      for (File f : FILES) TRUE(lst.contains(fo.getDestinationFile(f,src,dst)));
    } catch (Exception e) {
      NG(e);
    }
  }

  public void moveRecursive_withFilter001_renameTo_failed()
  {
    MSG("空でないディレクトリをコピーする場合。");

    final File dst = new File(WORKDIR, "aaa/bbb/ccc");
    @SuppressWarnings("serial")
    File src = new File(DIRS[0].getPath()) {
      @Override public boolean renameTo(File f) {
        if (isMoveToTemporary(f)) {
          return super.renameTo(f);
        }
        return false;
      }
    };

    FALSE(dst.exists());
    List<File> lst = new ArrayList<File>(20);
    lst.add(src);
    try {
      lst.addAll(FileOperation.listRecursive(src));
    } catch (Exception e) {
      NG(e);
    }
    EQUAL(lst.size(), 16);
    for (File d : DIRS) TRUE(lst.contains(d));
    for (File f : FILES) TRUE(lst.contains(f));

    FileFilter filter = new FileTypeFilter(FileType.Directory);
    try {
      FileOperation.moveRecursive(src, dst, filter);
    } catch (Exception e) {
      NG(e);
    }

    FileOperation fo = new FileOperation();
    FALSE(src.exists());
    try {
      lst.clear();
      lst.add(dst);
      lst.addAll(FileOperation.listRecursive(dst));

      EQUAL(lst.size(), 16);
      for (File d : DIRS) TRUE(lst.contains(fo.getDestinationFile(d,src,dst)));
      for (File f : FILES) TRUE(lst.contains(fo.getDestinationFile(f,src,dst)));
    } catch (Exception e) {
      NG(e);
    }
  }
}

