/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2013-2015 ForgeRock AS.
 */
package org.opends.server.tools;



import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

import org.opends.server.TestCaseUtils;
import org.opends.server.core.DirectoryServer;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import static org.opends.server.protocols.ldap.LDAPResultCode.*;
import static org.testng.Assert.*;



/**
 * A set of test cases for the LDIFDiff tool.
 */
public class LDIFDiffTestCase
       extends ToolsTestCase
{
  /**
   * The path to the file that will be used if there are no differences between
   * the source and target LDIF data sets.
   */
  private String noDiffsFile =
       System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT) + File.separator +
       "tests" + File.separator + "unit-tests-testng" + File.separator +
       "resource" + File.separator + "ldif-diff" + File.separator +
       "diff-nochanges.ldif";



  /**
   * Make sure that the server is running, since we need it for schema
   * handling.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass
  public void startServer()
         throws Exception
  {
    TestCaseUtils.startServer();
  }


  /** Calculates the checksum of a file. */
  private long calcChecksum(String filename) throws Exception
  {
    return calcChecksum(new File(filename));
  }

  private long calcChecksum(File file) throws Exception
  {
    long checksum = 0L;
    BufferedReader reader = new BufferedReader(new FileReader(file));
    String line = null;
    while ((line =reader.readLine()) != null)
    {
      checksum += line.hashCode();
    }
    reader.close();
    return checksum;
  }


  /**
   * Tests the LDIFDiff tool with an argument that will simply cause it to
   * display usage information.
   */
  @Test
  public void testUsage()
  {
    String[] args = { "--help" };
    assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 0);

    args = new String[] { "-H" };
    assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 0);

    args = new String[] { "-?" };
    assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 0);
  }



  /**
   * Tests the LDIFDiff tool with an invalid set of arguments.
   */
  @Test
  public void testInvalidArguments()
  {
    String[] args =
    {
      "--invalid"
    };

    assertFalse(LDIFDiff.mainDiff(args, true, System.out, System.err) == 0);
  }



  /**
   * Retrieves the names of the files that should be used when testing the
   * ldif-diff tool.  Each element of the outer array should be an array
   * containing the following elements:
   * <OL>
   *   <LI>The path to the source LDIF file</LI>
   *   <LI>The path to the target LDIF file</LI>
   *   <LI>The path to the diff file, or {@code null} if the diff is supposed
   *       to fail</LI>
   * </OL>
   */
  @DataProvider(name = "testdata")
  public Object[][] getTestData()
  {
    String buildRoot = System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT);
    String ldifRoot  = buildRoot + File.separator + "tests" + File.separator +
                       "unit-tests-testng" + File.separator + "resource" +
                       File.separator + "ldif-diff" + File.separator;

    return new Object[][]
    {
      // Both files are empty.
      new Object[] { ldifRoot + "source-empty.ldif",
                     ldifRoot + "target-empty.ldif",
                     noDiffsFile, noDiffsFile,
                     COMPARE_TRUE },

      // Both files are the single-entry source.
      new Object[] { ldifRoot + "source-singleentry.ldif",
                     ldifRoot + "source-singleentry.ldif",
                     noDiffsFile, noDiffsFile,
                     COMPARE_TRUE },

      // Both files are the single-entry target.
      new Object[] { ldifRoot + "target-singleentry.ldif",
                     ldifRoot + "target-singleentry.ldif",
                     noDiffsFile, noDiffsFile,
                     COMPARE_TRUE },

      // Both files are the multiple-entry source.
      new Object[] { ldifRoot + "source-multipleentries.ldif",
                     ldifRoot + "source-multipleentries.ldif",
                     noDiffsFile, noDiffsFile,
                     COMPARE_TRUE },

      // Both files are the multiple-entry target.
      new Object[] { ldifRoot + "target-multipleentries.ldif",
                     ldifRoot + "target-multipleentries.ldif",
                     noDiffsFile, noDiffsFile,
                     COMPARE_TRUE },

      // The source is empty but the target has a single entry.
      new Object[] { ldifRoot + "source-empty.ldif",
                     ldifRoot + "target-singleentry.ldif",
                     ldifRoot + "diff-emptytosingle.ldif",
                     ldifRoot + "diff-emptytosingle.ldif",
                     COMPARE_FALSE },

      // The source has a single entry but the target is empty.
      new Object[] { ldifRoot + "source-singleentry.ldif",
                     ldifRoot + "target-empty.ldif",
                     ldifRoot + "diff-singletoempty.ldif",
                     ldifRoot + "diff-singletoempty.ldif",
                     COMPARE_FALSE },

      // Make a change to only a single entry in the source->target direction.
      new Object[] { ldifRoot + "source-singleentry.ldif",
                     ldifRoot + "target-singleentry.ldif",
                     ldifRoot + "diff-singleentry.ldif",
                     ldifRoot + "diff-singleentry.ldif",
                     COMPARE_FALSE },

      // Make a change to only a single entry in the target->source direction.
      new Object[] { ldifRoot + "target-singleentry.ldif",
                     ldifRoot + "source-singleentry.ldif",
                     ldifRoot + "diff-singleentry-reverse.ldif",
                     ldifRoot + "diff-singleentry-reverse.ldif",
                     COMPARE_FALSE },

      // Make changes to multiple entries in the source->target direction.
      new Object[] { ldifRoot + "source-multipleentries.ldif",
                     ldifRoot + "target-multipleentries.ldif",
                     ldifRoot + "diff-multipleentries.ldif",
                     ldifRoot + "diff-multipleentries-singlevalue.ldif",
                     COMPARE_FALSE },

      // Make changes to multiple entries in the target->source direction.
      new Object[] { ldifRoot + "target-multipleentries.ldif",
                     ldifRoot + "source-multipleentries.ldif",
                     ldifRoot + "diff-multipleentries-reverse.ldif",
                     ldifRoot +
                          "diff-multipleentries-reverse-singlevalue.ldif",
                     COMPARE_FALSE },

      // Go from one entry to multiple in the source->target direction.
      new Object[] { ldifRoot + "source-singleentry.ldif",
                     ldifRoot + "target-multipleentries.ldif",
                     ldifRoot + "diff-singletomultiple.ldif",
                     ldifRoot + "diff-singletomultiple-singlevalue.ldif",
                     COMPARE_FALSE },

      // Go from one entry to multiple in the target->source direction.
      new Object[] { ldifRoot + "target-singleentry.ldif",
                     ldifRoot + "source-multipleentries.ldif",
                     ldifRoot + "diff-singletomultiple-reverse.ldif",
                     ldifRoot + "diff-singletomultiple-reverse.ldif",
                     COMPARE_FALSE },

      // Go from multiple entries to one in the source->target direction.
      new Object[] { ldifRoot + "source-multipleentries.ldif",
                     ldifRoot + "target-singleentry.ldif",
                     ldifRoot + "diff-multipletosingle.ldif",
                     ldifRoot + "diff-multipletosingle.ldif",
                     COMPARE_FALSE },

      // Go from multiple entries to one in the target->source direction.
      new Object[] { ldifRoot + "target-multipleentries.ldif",
                     ldifRoot + "source-singleentry.ldif",
                     ldifRoot + "diff-multipletosingle-reverse.ldif",
                     ldifRoot +
                          "diff-multipletosingle-reverse-singlevalue.ldif",
                     COMPARE_FALSE },

      // The source file doesn't exist.
      new Object[] { ldifRoot + "source-notfound.ldif",
                     ldifRoot + "target-singleentry.ldif",
                     null, null,
                     COMPARE_FALSE },

      // The target file doesn't exist.
      new Object[] { ldifRoot + "source-singleentry.ldif",
                     ldifRoot + "target-notfound.ldif",
                     null, null,
                     COMPARE_FALSE }
    };
  }


  /**
   * Tests the LDIFDiff tool with the provided information to ensure that the
   * normal mode of operation works as expected.  This is a bit tricky because
   * the attributes and values will be written in an indeterminate order, so we
   * can't just use string equality.  We'll have to use a crude checksum
   * mechanism to test whether they are equal.  Combined with other methods in
   * this class, this should be good enough.
   *
   * @param  sourceFile           The path to the file containing the source
   *                              data set.
   * @param  targetFile           The path to the file containing the target
   *                              data set.
   * @param  normalDiffFile       The path to the file containing the expected
   *                              diff in "normal" form (at most one record per
   *                              entry), or {@code null} if the diff is
   *                              supposed to fail.
   * @param  singleValueDiffFile  The path to the file containing the expected
   *                              diff in "single-value" form, where each
   *                              attribute-level change results in a separate
   *                              entry per attribute value.
   * @param resultCode            The result code that should be returned with
   *                              --useCompareResultCode flag
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testdata")
  public void testVerifyNormal(String sourceFile, String targetFile,
      String normalDiffFile, String singleValueDiffFile, int resultCode)
      throws Exception
  {
    File outputFile = File.createTempFile("difftest", "ldif");
    outputFile.deleteOnExit();

    String[] args =
    {
      "-s", sourceFile,
      "-t", targetFile,
      "-o", outputFile.getAbsolutePath(),
      "-O"
    };
    String[] argsUseCompare =
    {
      "-s", sourceFile,
      "-t", targetFile,
      // No need to write to the outputFile
      "--useCompareResultCode"
    };

    if (normalDiffFile == null)
    {
      // We expect this to fail, so just make sure that it does.
      assertFalse(LDIFDiff.mainDiff(args, true, System.out, System.err) == 0);
      outputFile.delete();
      return;
    }

    assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 0);
    assertEquals(calcChecksum(outputFile), calcChecksum(normalDiffFile));

    assertEquals(LDIFDiff
        .mainDiff(argsUseCompare, true, System.out, System.err), resultCode);
    outputFile.delete();
  }




  /**
   * Tests the LDIFDiff tool with the provided information to ensure that the
   * single value changes mode of operation works as expected.  This is a bit
   * tricky because the attributes and values will be written in an
   * indeterminate order, so we can't just use string equality.  We'll have to
   * use a crude checksum mechanism to test whether they are equal.  Combined
   * with other methods in this class, this should be good enough.
   *
   * @param  sourceFile           The path to the file containing the source
   *                              data set.
   * @param  targetFile           The path to the file containing the target
   *                              data set.
   * @param  normalDiffFile       The path to the file containing the expected
   *                              diff in "normal" form (at most one record per
   *                              entry), or {@code null} if the diff is
   *                              supposed to fail.
   * @param  singleValueDiffFile  The path to the file containing the expected
   *                              diff in "single-value" form, where each
   *                              attribute-level change results in a separate
   *                              entry per attribute value.
   * @param resultCode            The result code that should be returned with
   *                              --useCompareResultCode flag
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testdata")
  public void testVerifySingleValue(String sourceFile, String targetFile,
      String normalDiffFile, String singleValueDiffFile, int resultCode)
      throws Exception
  {
    File outputFile = File.createTempFile("difftest", "ldif");
    outputFile.deleteOnExit();

    String[] args =
    {
      "-s", sourceFile,
      "-t", targetFile,
      "-o", outputFile.getAbsolutePath(),
      "-O",
      "-S"
    };
    String[] argsUseCompare =
    {
      "-s", sourceFile,
      "-t", targetFile,
      // No need to write to the outputFile
      "--useCompareResultCode"
    };

    if (singleValueDiffFile == null)
    {
      // We expect this to fail, so just make sure that it does.
      assertFalse(LDIFDiff.mainDiff(args, true, System.out, System.err) == 0);
      outputFile.delete();
      return;
    }

    assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 0);
    assertEquals(calcChecksum(outputFile), calcChecksum(singleValueDiffFile));

    assertEquals(LDIFDiff
        .mainDiff(argsUseCompare, true, System.out, System.err), resultCode);
    outputFile.delete();
  }



  /**
   * Tests the LDIFDiff tool by first identifying the differences between the
   * source and the target and then using the LDIFModify tool to apply the
   * identified changes to the source LDIF and verify that it matches the
   * target.
   *
   * @param  sourceFile           The path to the file containing the source
   *                              data set.
   * @param  targetFile           The path to the file containing the target
   *                              data set.
   * @param  normalDiffFile       The path to the file containing the expected
   *                              diff in "normal" form (at most one record per
   *                              entry), or {@code null} if the diff is
   *                              supposed to fail.
   * @param  singleValueDiffFile  The path to the file containing the expected
   *                              diff in "single-value" form, where each
   *                              attribute-level change results in a separate
   *                              entry per attribute value.
   * @param resultCode            The result code that should be returned with
   *                              --useCompareResultCode flag
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testdata")
  public void testReconstructNormal(String sourceFile, String targetFile,
      String normalDiffFile, String singleValueDiffFile, int resultCode)
      throws Exception
  {
    // If the command is expected to fail, or if there aren't any differences,
    // then bail out now.
    if (normalDiffFile == null || normalDiffFile.equals(noDiffsFile))
    {
      return;
    }


    // Generate the diff file.
    File diffOutputFile = File.createTempFile("difftest", "ldif");
    diffOutputFile.deleteOnExit();

    String[] args =
    {
      "-s", sourceFile,
      "-t", targetFile,
      "-o", diffOutputFile.getAbsolutePath()
    };
    String[] argsUseCompare =
    {
      "-s", sourceFile,
      "-t", targetFile,
      // No need to write to the outputFile
      "--useCompareResultCode"
    };

    assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 0);
    assertEquals(LDIFDiff
        .mainDiff(argsUseCompare, true, System.out, System.err), resultCode);


    // Use LDIFModify to generate a new target file.
    File newTargetFile = File.createTempFile("difftest", "newtarget.ldif");
    newTargetFile.deleteOnExit();

    DirectoryServer.getInstance();
    args = new String[]
    {
      "-c", DirectoryServer.getConfigFile(),
      "-s", sourceFile,
      "-m", diffOutputFile.getAbsolutePath(),
      "-t", newTargetFile.getAbsolutePath()
    };

    assertEquals(LDIFModify.ldifModifyMain(args, true, System.out, System.err),
                 0);


    // Use LDIFDiff again to verify that there are effectively no differences
    // between the original target and the new target.
    File newDiffFile = File.createTempFile("difftest", "newdiff.ldif");
    newDiffFile.deleteOnExit();

    args = new String[]
    {
      "-s", targetFile,
      "-t", newTargetFile.getAbsolutePath(),
      "-o", newDiffFile.getAbsolutePath()
    };
    argsUseCompare = new String[]
    {
      "-s", targetFile,
      "-t", newTargetFile.getAbsolutePath(),
      // No need to write to the outputFile
      "--useCompareResultCode"
    };

    assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 0);
    assertEquals(calcChecksum(newDiffFile), calcChecksum(noDiffsFile));
    assertEquals(LDIFDiff
        .mainDiff(argsUseCompare, true, System.out, System.err), COMPARE_TRUE);

    diffOutputFile.delete();
    newTargetFile.delete();
    newDiffFile.delete();
  }



  /**
   * Tests the LDIFDiff tool by first identifying the differences between the
   * source and the target (using the single-value format) and then using the
   * LDIFModify tool to apply the identified changes to the source LDIF and
   * verify that it matches the target.
   *
   * @param  sourceFile           The path to the file containing the source
   *                              data set.
   * @param  targetFile           The path to the file containing the target
   *                              data set.
   * @param  normalDiffFile       The path to the file containing the expected
   *                              diff in "normal" form (at most one record per
   *                              entry), or {@code null} if the diff is
   *                              supposed to fail.
   * @param  singleValueDiffFile  The path to the file containing the expected
   *                              diff in "single-value" form, where each
   *                              attribute-level change results in a separate
   *                              entry per attribute value.
   * @param resultCode            The result code that should be returned with
   *                              --useCompareResultCode flag
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "testdata")
  public void testReconstructSingleValue(String sourceFile, String targetFile,
      String normalDiffFile, String singleValueDiffFile, int resultCode)
      throws Exception
  {
    // If the command is expected to fail, or if there aren't any differences,
    // then bail out now.
    if (normalDiffFile == null || singleValueDiffFile.equals(noDiffsFile))
    {
      return;
    }


    // Generate the diff file.
    File diffOutputFile = File.createTempFile("difftest", "ldif");
    diffOutputFile.deleteOnExit();

    String[] args =
    {
      "-s", sourceFile,
      "-t", targetFile,
      "-o", diffOutputFile.getAbsolutePath(),
      "-S"
    };
    String[] argsUseCompare =
    {
      "-s", sourceFile,
      "-t", targetFile,
      // No need to write to the outputFile
      "--useCompareResultCode"
    };

    assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 0);
    assertEquals(LDIFDiff
        .mainDiff(argsUseCompare, true, System.out, System.err), resultCode);


    // Use LDIFModify to generate a new target file.
    File newTargetFile = File.createTempFile("difftest", "newtarget.ldif");
    newTargetFile.deleteOnExit();

    DirectoryServer.getInstance();
    args = new String[]
    {
      "-c", DirectoryServer.getConfigFile(),
      "-s", sourceFile,
      "-m", diffOutputFile.getAbsolutePath(),
      "-t", newTargetFile.getAbsolutePath()
    };

    assertEquals(LDIFModify.ldifModifyMain(args, true, System.out, System.err),
                 0);


    // Use LDIFDiff again to verify that there are effectively no differences
    // between the original target and the new target.
    File newDiffFile = File.createTempFile("difftest", "newdiff.ldif");
    newDiffFile.deleteOnExit();

    args = new String[]
    {
      "-s", targetFile,
      "-t", newTargetFile.getAbsolutePath(),
      "-o", newDiffFile.getAbsolutePath()
    };
    argsUseCompare = new String[]
    {
      "-s", targetFile,
      "-t", newTargetFile.getAbsolutePath(),
      // No need to write to the outputFile
      "--useCompareResultCode"
    };

    assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 0);
    assertEquals(calcChecksum(newDiffFile), calcChecksum(noDiffsFile));
    assertEquals(LDIFDiff
        .mainDiff(argsUseCompare, true, System.out, System.err), COMPARE_TRUE);

    diffOutputFile.delete();
    newTargetFile.delete();
    newDiffFile.delete();
  }


  /**
   * Retrieves the names of the files that should be used when testing the
   * ldif-diff tool.  Each element of the outer array should be an array
   * containing the following elements:
   * <OL>
   *   <LI>The path to the source LDIF file</LI>
   *   <LI>The path to the target LDIF file</LI>
   *   <LI>The path to the file with attributes to be ignored</LI>
   *   <LI>The path to the diff file</LI>
   * </OL>
   */
  @DataProvider(name = "ignoreattributesdata")
  public Object[][] getIATestData()
  {
    String buildRoot = System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT);
    String ldifRoot  = buildRoot + File.separator + "tests" + File.separator +
                       "unit-tests-testng" + File.separator + "resource" +
                       File.separator + "ldif-diff" + File.separator;

    return new Object[][]
    {
      // Make changes to multiple entries in the target->source direction.
      new Object[] { ldifRoot + "source-multipleentries.ldif",
                     ldifRoot + "target-multipleentries.ldif",
                     ldifRoot + "ignore-attributes",
                     ldifRoot + "diff-multipleentries-ignore-attributes.ldif" },

      new Object[] { ldifRoot + "source-multipleentries.ldif",
                     ldifRoot + "target-multipleentries.ldif",
                     ldifRoot + "does-not-exist",
                     ldifRoot + "diff-multipleentries-ignore-attributes.ldif" }
    };

  }



  /**
   * Tests the LDIFDiff tool with the provided information to ensure that the
   * normal mode of operation works as expected.  This is a bit tricky because
   * the attributes and values will be written in an indeterminate order, so we
   * can't just use string equality.  We'll have to use a crude checksum
   * mechanism to test whether they are equal.  Combined with other methods in
   * this class, this should be good enough.
   *
   * @param  sourceFile           The path to the file containing the source
   *                              data set.
   * @param  targetFile           The path to the file containing the target
   *                              data set.
   * @param  normalDiffFile       The path to the file containing the expected
   *                              diff in "normal" form (at most one record per
   *                              entry), or {@code null} if the diff is
   *                              supposed to fail.
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "ignoreattributesdata")
  public void testVerifyIgnoreAttributes(String sourceFile, String targetFile,
                               String ignoreAttributesFile,
                               String normalDiffFile)
         throws Exception
  {
    File outputFile = File.createTempFile("difftest", "ldif");
    outputFile.deleteOnExit();

    String[] args =
    {
      "-s", sourceFile,
      "-t", targetFile,
      "-a", ignoreAttributesFile,
      "-o", outputFile.getAbsolutePath(),
      "-O"
    };

    if (ignoreAttributesFile.endsWith("does-not-exist"))
    {
      // We expect this to fail, so just make sure that it does.
      assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 1);
      return;
    }

    assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 0);
    assertEquals(calcChecksum(outputFile), calcChecksum(normalDiffFile));

    outputFile.delete();
  }


  /**
   * Retrieves the names of the files that should be used when testing the
   * ldif-diff tool.  Each element of the outer array should be an array
   * containing the following elements:
   * <OL>
   *   <LI>The path to the source LDIF file</LI>
   *   <LI>The path to the target LDIF file</LI>
   *   <LI>The path to the file with entries to be ignored</LI>
   *   <LI>The path to the diff file</LI>
   * </OL>
   */
  @DataProvider(name = "ignoreentriesdata")
  public Object[][] getIETestData()
  {
    String buildRoot = System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT);
    String ldifRoot  = buildRoot + File.separator + "tests" + File.separator +
                       "unit-tests-testng" + File.separator + "resource" +
                       File.separator + "ldif-diff" + File.separator;

    return new Object[][]
    {
      // Make changes to multiple entries in the target->source direction.
      new Object[] { ldifRoot + "source-multipleentries.ldif",
                     ldifRoot + "target-multipleentries.ldif",
                     ldifRoot + "ignore-entries",
                     ldifRoot + "diff-multipleentries-ignore-entries.ldif" },

      new Object[] { ldifRoot + "source-multipleentries.ldif",
                     ldifRoot + "target-multipleentries.ldif",
                     ldifRoot + "does-not-exist",
                     ldifRoot + "diff-multipleentries-ignore-entries.ldif" }
    };

  }



  /**
   * Tests the LDIFDiff tool with the provided information to ensure that the
   * normal mode of operation works as expected.  This is a bit tricky because
   * the attributes and values will be written in an indeterminate order, so we
   * can't just use string equality.  We'll have to use a crude checksum
   * mechanism to test whether they are equal.  Combined with other methods in
   * this class, this should be good enough.
   *
   * @param  sourceFile           The path to the file containing the source
   *                              data set.
   * @param  targetFile           The path to the file containing the target
   *                              data set.
   * @param  normalDiffFile       The path to the file containing the expected
   *                              diff in "normal" form (at most one record per
   *                              entry), or {@code null} if the diff is
   *                              supposed to fail.
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(dataProvider = "ignoreentriesdata")
  public void testVerifyIgnoreEntries(String sourceFile, String targetFile,
                               String ignoreEntriesFile,
                               String normalDiffFile)
         throws Exception
  {
    File outputFile = File.createTempFile("difftest", "ldif");
    outputFile.deleteOnExit();

    String[] args =
    {
      "-s", sourceFile,
      "-t", targetFile,
      "-e", ignoreEntriesFile,
      "-o", outputFile.getAbsolutePath(),
      "-O"
    };

    if (ignoreEntriesFile.endsWith("does-not-exist"))
    {
      // We expect this to fail, so just make sure that it does.
      assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 1);
      return;
    }

    assertEquals(LDIFDiff.mainDiff(args, true, System.out, System.err), 0);
    assertEquals(calcChecksum(outputFile), calcChecksum(normalDiffFile));

    outputFile.delete();
  }
}

