Log in Help
Print
Homegatewikicowtestunitgateyamdepend 〉 DependenciesTests.java
 
/*
 *  DependenciesTests.java
 *  Copyright (c) 1998-2008, The University of Sheffield.
 *
 *  This code is from the GATE project (http://gate.ac.uk/) and is free
 *  software licenced under the GNU General Public License version 3. It is
 *  distributed without any warranty. For more details see COPYING.txt in the
 *  top level directory (or at http://gatewiki.sf.net/COPYING.txt).
 */

package gate.yam.depend;

import gate.persist.PersistenceException;
import gate.util.GateException;
import gate.yam.YamFile;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.log4j.Logger;
import org.springframework.core.io.FileSystemResource;

import java.io.*;
import java.nio.channels.FileChannel;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Tests for Dependencies   
 * @author Angus Roberts
 */
public class DependenciesTests extends TestCase {

  /** Logger */
  static Logger log = Logger.getLogger("gate.yam.depend.DependenciesTests");

  /**
   * Matches the names of YamFiles as used in testing, capturing the numeric
   * part for use as a shorthand. (see @link #shorthandToPaths(String).
   * YamFiles used in testing are named using a conventionally, as defined in
   * this pattern.
   */
  private static Pattern testFilePattern
          = Pattern.compile("yam-depends-(\\d+).yam");

  /**
   * Some test files come in to versions - their original, and their modified
   * version. This pattern matches the original, capturing the parts that make
   * up the name of the actual file as used in tests.
   */
  private static Pattern originalTestFilePattern
          = Pattern.compile("(yam-depends-)ORIGINAL-(\\d+.yam)");

  /**
   * A FilenameFilter that accepts yam files used by DependenciesTests.
   * By convention, filenames are "yam-depends-\d+.yam"
   */
  class DependenciesTestsFileFilter implements FilenameFilter {
    /** Accept a file if it is a yam file used in DependenciesTests*/
    public boolean accept(File dir, String name) {
      return testFilePattern.matcher(name).matches();
    }
  }

  /**
   * A FilenameFilter that accepts original versions of yam files used by
   * DependenciesTests. By convention, filenames are
   * "yam-depends-ORIGINAL-\d+.yam"
   */
  class DependenciesTestsOriginalFileFilter implements FilenameFilter {
    /** Accept a file if it is the original version of
     * a yam file used in DependenciesTests
     */
    public boolean accept(File dir, String name) {
      return originalTestFilePattern.matcher(name).matches();
    }
  }

  /**
   * The directory that test yam files are found in.
   */
  private static File yamDir;

  /**
   * By convention, each yam file used in testing is named
   * "yam-depends-\d+.yam". This Map maps the digit part to the full name.
   * It is used so that we can refer to sets and graphs of files by just their
   * digit part.
   */
  Map<String, String> yamFileNameMap = new HashMap<String, String>();

  /**
   * As for @link #yamFileNameMap, but maps the digit part of the name to the
   * actual YamFile, post-generate().
   */
  Map<String, YamFile> yamFileMap = new HashMap<String, YamFile>();

  /**
   * A List of parsed YamFiles that make up a wiki
   */
  private List<YamFile> wiki1 = new ArrayList<YamFile>();

  /**
   * A Dependencies instance for wiki1
   */
  private Dependencies dep1;


  /** Create a Dependencies test case
   *
   * @param testName  The name of the test
   */
  public DependenciesTests(String testName) {
    super(testName);
  }



  // TODO remove setting of directories to strings - get from where?
  /**
   * Set up the tests for Dependencies
   */
  protected void setUp() {

    // set up the directory to which dependencies will be serialized
    String testDirName = System.getProperty("gate.yam.depend.test.dir");
    if (testDirName == null) {
      testDirName = "test/scratch/dependencies";
    }

    File testDirFile = new File(testDirName);
    testDirFile.mkdirs();
    Dependencies.setSerializationDirectory(testDirFile);

    // get a directory of yam files for testing
    String yamDirName = System.getProperty("java.yam.resources.dir");
    if (yamDirName == null) {
      yamDirName = this.getClass().getResource("/gate/yam/resources").getFile();
    }

    log.info("Getting test yam files from: " + yamDirName);

    yamDir = new File(yamDirName);

    // Some of our test files have original and modified versions, named
    // yam-depends-ORIGINAL-\d+.yam and yam-depends-MODIFIED-\d+.yam
    // Copy the original versions to their "proper" names for loading
    File[] originalVersions
            = yamDir.listFiles(new DependenciesTestsOriginalFileFilter());
    for(File origFile : originalVersions) {
      //Work out the name as used in tests
      Matcher matcher = originalTestFilePattern.matcher(origFile.getName());
      if (!matcher.matches()) {
        fail("Failed to setup - filename not conventional: "
                + origFile.getName());
      }
      String newName = matcher.group(1) + matcher.group(2);
      File newFile = new File(yamDir, newName);
      copy(origFile, newFile);
           
    }

    // Get all the yam files used by DependenciesTests
    File[] testFiles = yamDir.listFiles(new DependenciesTestsFileFilter());
    for(File file : testFiles ) {
      YamFile yamFile = YamFile.get(new FileSystemResource(file));
      try{
        String canPath = yamFile.getCanonicalPath();
        log.info("Getting test file: " + canPath);
        // Group 1 of testFilePattern is the numeric part
        Matcher matcher = testFilePattern.matcher(file.getName());
        if(!matcher.matches()) {
          fail("Failed to setup - filename not conventional: "
                  + file.getName());        
        }
        String number = matcher.group(1);
        yamFile.generate();        
        yamFileNameMap.put(number, canPath);
        yamFileMap.put(number, yamFile);
      } catch(GateException ge) {
        fail("Failed to setup: " + ge.getMessage());
      }
    }


    // Add some files to a list, to use as a wiki
    wiki1.add(yamFileMap.get("1"));
    wiki1.add(yamFileMap.get("2"));
    wiki1.add(yamFileMap.get("3"));
    wiki1.add(yamFileMap.get("4"));
    wiki1.add(yamFileMap.get("5"));

    // Get dependencies for the wiki
    try{
      // Clear it before getting a new one - could have persisted on disk
      Dependencies.remove("1");
      dep1 = Dependencies.get("1");
    } catch(PersistenceException pe) {
      fail("Failed to set up: " + pe.getMessage());
    }
    assertTrue("New Dependencies not empty", dep1.isEmpty());



    // Add all the files in the wiki to the dependencies
    for(YamFile yam : wiki1) {
      dep1.created(yam);
    }

    assertFalse(
      "Dependencies is empty after adding links", dep1.isEmpty()
    );

    
  }


  /**
   * Test a few Dependencies basics: equality, hashCode, YamFile creation
   * and removal.
   * @throws Exception if the test fails
   */
  public void testCreateAndBasics() throws Exception {
    log.info("========== DependenciesTests.tesCreateAndBasics() ==============");
    log.info("testing dependencies.created(YamFile) and equality methods");


    // Create a Dependencies that is the same as our global dep1
    Dependencies dep2 = Dependencies.get("2");
    for(YamFile yam : wiki1) {
      dep2.created(yam);
    }

    // What does this dependencies look like?
    assertEquals("Dependencies linksTo not correct after create",
            shorthandToPaths("1:[2,3];2:[4,5];3:[5]"),
            dep2.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after create",
            shorthandToPaths("2:[1];3:[1];4:[2];5:[2,3]"),
            dep2.linkedByAsString());

    assertEquals("Dependencies includes not correct after create",
            shorthandToPaths("1:[2,3,4];2:[4]"),
            dep2.includesAsString());

    assertEquals("Dependencies includedBy not correct after create",
            shorthandToPaths("2:[1];3:[1];4:[1,2]"),
            dep2.includedByAsString());


    // Test equality
    assertEquals("Same Dependencies not equal", dep1, dep1);

    assertEquals(
            "Dependencies not equal after get", dep1, Dependencies.get("1"));

    assertEquals("Identical Dependencies not equal", dep1, dep2);

    assertEquals(
            "Identical Dependencies with different hash codes",
            dep1.hashCode(), dep2.hashCode());

    // Can we remove a Dependencies?
    Dependencies.remove("2");
    assertFalse("Dependencies still exists after removal",
            Dependencies.exists("2"));

  }

  /**
   * Test Dependencies YamFile deletion
   * @throws Exception if the test fails
   */
  public void testDelete() throws Exception {
    log.info("============== DependenciesTests.testDelete() ==================");
    log.info("testing dependencies.deleted(YamFile)");

    // Holds results from Dependencies event operations
    Set<String> toRegenerate;

    // What happens if we delete a YamFile?
    // First, delete one that has links and includes in it
    toRegenerate = dep1.deleted(yamFileMap.get("1"));

    assertEquals("Regenerate set not correct after delete",
            new HashSet<String>(),
            toRegenerate);

    assertEquals("Dependencies linksTo not correct after delete",
            shorthandToPaths("2:[4,5];3:[5]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after delete",
            shorthandToPaths("4:[2];5:[2,3]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after delete",
            shorthandToPaths("2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after delete",
            shorthandToPaths("4:[2]"),
            dep1.includedByAsString());

    // Now recreate it - put it back
    toRegenerate = dep1.created(yamFileMap.get("1"));

    assertEquals("Regenerate set not correct after create",
            new HashSet<String>(),
            toRegenerate);


    assertEquals("Dependencies linksTo not correct after create",
            shorthandToPaths("1:[2,3];2:[4,5];3:[5]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after create",
            shorthandToPaths("2:[1];3:[1];4:[2];5:[2,3]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after create",
            shorthandToPaths("1:[2,3,4];2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after create",
            shorthandToPaths("2:[1];3:[1];4:[1,2]"),
            dep1.includedByAsString());

  }

  /**
   * Test Dependencies YamFile renaming
   * @throws Exception if the test fails
   */
  public void testRename() throws Exception {
    log.info("============== DependenciesTests.testRename() ==================");
    log.info("testing dependencies.renamed(YamFile)");

    // Holds results from Dependencies event operations
    Set<String> toRegenerate;
    List<String> toRegenerateSorted;


    // Rename a YamFile. We don't really, just pretend that one file is a rename
    // of another
    toRegenerate = dep1.renamed(yamFileMap.get("2"), yamFileMap.get("6"));
    toRegenerateSorted = new ArrayList<String>(toRegenerate);
    Collections.sort(toRegenerateSorted);

    assertEquals("Regenerate set not correct after rename",
            numbersToList("1"),
            toRegenerateSorted);

    assertEquals("Dependencies linksTo not correct after rename",
            shorthandToPaths("1:[3,6];3:[5];6:[4,5]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after rename",
            shorthandToPaths("3:[1];4:[6];5:[3,6];6:[1]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after rename",
            shorthandToPaths("1:[3,4,6];6:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after rename",
            shorthandToPaths("3:[1];4:[1,6];6:[1]"),
            dep1.includedByAsString());

    // Put everything back in its original form for further tests
    dep1.renamed(yamFileMap.get("6"), yamFileMap.get("2"));


  }

  /**
   * Test Dependencies - further YamFile deletion tests
   * @throws Exception if the test fails
   */
  public void testDelete2() throws Exception {
    log.info("============== DependenciesTests.testDelete2() =================");
    log.info("testing dependencies.deleted(YamFile)");

    // Holds results from Dependencies event operations
    Set<String> toRegenerate;
    List<String> toRegenerateSorted;

    // Delete a YamFile that will need a bit nore regeneration
    toRegenerate = dep1.deleted(yamFileMap.get("4"));
    toRegenerateSorted = new ArrayList<String>(toRegenerate);
    Collections.sort(toRegenerateSorted);

    assertEquals("Regenerate set not correct after delete",
            numbersToList("1,2"),
            toRegenerateSorted);

    // And delete another
    toRegenerate = dep1.deleted(yamFileMap.get("5"));
    toRegenerateSorted = new ArrayList<String>(toRegenerate);
    Collections.sort(toRegenerateSorted);

    assertEquals("Regenerate set not correct after delete",
            numbersToList("2,3"),
            toRegenerateSorted);

    // Put everything back in its original form for further tests
    dep1.created(yamFileMap.get("4"));
    dep1.created(yamFileMap.get("5"));

    // Check all is how it should be
    assertEquals("Dependencies linksTo not correct after delete and create",
            shorthandToPaths("1:[2,3];2:[4,5];3:[5]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after delete and create",
            shorthandToPaths("2:[1];3:[1];4:[2];5:[2,3]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after delete and create",
            shorthandToPaths("1:[2,3,4];2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after delete and create",
            shorthandToPaths("2:[1];3:[1];4:[1,2]"),
            dep1.includedByAsString());

  }
  
  /**
   * Test Dependencies YamFile modification
   * @throws Exception if the test fails
   */
  public void testModify() throws Exception {
    log.info("============== DependenciesTests.testModify() ==================");
    log.info("testing dependencies.modified(YamFile)");

    // Holds results from Dependencies event operations
    Set<String> toRegenerate;
    List<String> toRegenerateSorted;


    // File modification. We fake this by copying a modified version over
    // an existing file, and re-generating the yam.
    File currentFile = new File(yamDir, "yam-depends-2.yam");
    File modFile = new File(yamDir, "yam-depends-MODIFIED-2.yam");
    copy(modFile, currentFile);

    // Regenerate the yam
    yamFileMap.get("2").generate();

    // Notify the modification
    toRegenerate = dep1.modified(yamFileMap.get("2"));
    toRegenerateSorted = new ArrayList<String>(toRegenerate);
    Collections.sort(toRegenerateSorted);

    assertEquals("Regenerate set not correct after modify",
            numbersToList("1"),
            toRegenerateSorted);

    // Check all is how it should be
    assertEquals("Dependencies linksTo not correct after modify",
            shorthandToPaths("1:[2,3];2:[4,7];3:[5]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after modify",
            shorthandToPaths("2:[1];3:[1];4:[2];5:[3];7:[2]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after modify",
            shorthandToPaths("1:[2,3,4];2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after modify",
            shorthandToPaths("2:[1];3:[1];4:[1,2]"),
            dep1.includedByAsString());                           

    // Another File modification.
    currentFile = new File(yamDir, "yam-depends-1.yam");
    modFile = new File(yamDir, "yam-depends-MODIFIED-1.yam");
    copy(modFile, currentFile);

    // Regenerate the yam
    yamFileMap.get("1").generate();

    // Notify the modification
    toRegenerate = dep1.modified(yamFileMap.get("1"));

    assertEquals("Regenerate set not correct after modify",
            new HashSet<String>(),
            toRegenerate);

    // Check all is how it should be
    assertEquals("Dependencies linksTo not correct after modify",
            shorthandToPaths("1:[2,3];2:[4,7];3:[5]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after modify",
            shorthandToPaths("2:[1];3:[1];4:[2];5:[3];7:[2]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after modify",
            shorthandToPaths("1:[3,4];2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after modify",
            shorthandToPaths("3:[1];4:[1,2]"),
            dep1.includedByAsString());

    // Modify all files back
    copy(new File(yamDir, "yam-depends-ORIGINAL-1.yam"),
         new File(yamDir, "yam-depends-1.yam"));
    copy(new File(yamDir, "yam-depends-ORIGINAL-2.yam"),
         new File(yamDir, "yam-depends-2.yam"));

    // Regenerate the yams
    yamFileMap.get("1").generate();
    yamFileMap.get("2").generate();


  }

  /**
   * Test links to non-yam files and urls
   * @throws Exception if the test fails
   */
  public void testNonYamLinks() throws Exception {
    log.info("============ DependenciesTests.testNonYamLinks() ===============");
    log.info("testing links to non yam files and urls");
   
    // Add a new file to the wiki, create it.
    YamFile yf = yamFileMap.get("8");
    wiki1.add(yf);
    dep1.created(yf);

    // Check all is how it should be
    assertEquals("Dependencies linksTo not correct after create",
            shorthandToPaths("1:[2,3];2:[4,5];3:[5];"
                    + "8:[../../non-existent.html,non-existent.html]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after create",
            shorthandToPaths("../../non-existent.html:[8];" + "" +
                    "non-existent.html:[8];2:[1];3:[1];4:[2];5:[2,3]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after create",
            shorthandToPaths("1:[2,3,4];2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after create",
            shorthandToPaths("2:[1];3:[1];4:[1,2]"),
            dep1.includedByAsString());


    // Get rid of the new file
    dep1.deleted(yf);

    // Check all is how it should be
    assertEquals("Dependencies linksTo not correct after delete",
            shorthandToPaths("1:[2,3];2:[4,5];3:[5]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after delete",
            shorthandToPaths("2:[1];3:[1];4:[2];5:[2,3]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after delete",
            shorthandToPaths("1:[2,3,4];2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after delete",
            shorthandToPaths("2:[1];3:[1];4:[1,2]"),
            dep1.includedByAsString());


  }


  /**
   * Test Dependencies File creation, deletion, and renaming
   * @throws Exception if the test fails
   */
  public void testNonYamChanges() throws Exception {
    log.info("========= DependenciesTests.testNonYamChanges() =============");

    // Holds results from Dependencies event operations
    Set<String> toRegenerate;
    List<String> toRegenerateSorted;

    // Create, ie upload, a file
    log.info("testing dependencies.created(File)");
    File nonYam = new File(yamDir, "nonYamFile.abc");
    toRegenerate = dep1.created(nonYam);

    // Should be nothing to regenerate, nothing links to it
    assertEquals("Regenerate set not correct after created(File)",
            new HashSet<String>(),
            toRegenerate);

    // Check all is how it should be - no changes
    assertEquals("Dependencies linksTo not correct after create",
            shorthandToPaths("1:[2,3];2:[4,5];3:[5]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after create",
            shorthandToPaths("2:[1];3:[1];4:[2];5:[2,3]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after create",
            shorthandToPaths("1:[2,3,4];2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after create",
            shorthandToPaths("2:[1];3:[1];4:[1,2]"),
            dep1.includedByAsString());

    // Now delete the non yam
    toRegenerate = dep1.deleted(nonYam);
    
    // Should be nothing to regenerate, nothing links to it
    assertEquals("Regenerate set not correct after deleted(File)",
            new HashSet<String>(),
            toRegenerate);

    // Now add in a page that links to a non-existent non-yam
    YamFile linkingFile = yamFileMap.get("9");
    wiki1.add(linkingFile);
    toRegenerate = dep1.created(linkingFile);

    // Should be nothing to regenerate, nothing dependent on 9 has changed
    assertEquals("Regenerate set not correct after created(YamFile)",
            new HashSet<String>(),
            toRegenerate);

    // Check all is how it should be - a new link to a non yam file
    assertEquals("Dependencies linksTo not correct after create",
            shorthandToPaths("1:[2,3];2:[4,5];3:[5];9:[nonYamFile.abc]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after create",
            shorthandToPaths("nonYamFile.abc:[9];2:[1];3:[1];4:[2];5:[2,3]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after create",
            shorthandToPaths("1:[2,3,4];2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after create",
            shorthandToPaths("2:[1];3:[1];4:[1,2]"),
            dep1.includedByAsString());

    // Now create the non yam file again
    toRegenerate = dep1.created(nonYam);
    toRegenerateSorted = new ArrayList<String>(toRegenerate);
    Collections.sort(toRegenerateSorted);

    // We should need to regenerate the web page that links to the non yam
    assertEquals("Regenerate set not correct after created(File)",
            numbersToList("9"),
            toRegenerateSorted);

    // Rename: we should have to regenerate the linker
    // We don't really rename a file on the fiesystem, just pretend
    log.info("testing dependencies.renamed(File, File)");
    File nonYamRenamed = new File(yamDir, "nonYamFileRenamed.abc");
    toRegenerate = dep1.renamed(nonYam, nonYamRenamed);
    toRegenerateSorted = new ArrayList<String>(toRegenerate);
    Collections.sort(toRegenerateSorted);

    assertEquals("Regenerate set not correct after renamed(File)",
            numbersToList("9"),
            toRegenerateSorted);

    // Check all is how it should be - link to the renamed file
    assertEquals("Dependencies linksTo not correct after rename",
            shorthandToPaths("1:[2,3];2:[4,5];3:[5];9:[nonYamFileRenamed.abc]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after rename",
            shorthandToPaths("nonYamFileRenamed.abc:[9];2:[1];3:[1];4:[2]"
                    + ";5:[2,3]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after rename",
            shorthandToPaths("1:[2,3,4];2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after rename",
            shorthandToPaths("2:[1];3:[1];4:[1,2]"),
            dep1.includedByAsString());

    // Delete: we should get some files to regenerate
    log.info("testing dependencies.deleted(File)");
    toRegenerate = dep1.deleted(nonYamRenamed);
    toRegenerateSorted = new ArrayList<String>(toRegenerate);
    Collections.sort(toRegenerateSorted);

    assertEquals("Regenerate set not correct after deleted(File)",
            numbersToList("9"),
            toRegenerateSorted);

    // Check all is how it should be - , with a link to a now non-existent file
    assertEquals("Dependencies linksTo not correct after delete",
            shorthandToPaths("1:[2,3];2:[4,5];3:[5];9:[nonYamFileRenamed.abc]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after delete",
            shorthandToPaths("nonYamFileRenamed.abc:[9];2:[1];3:[1];4:[2]"
                    + ";5:[2,3]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after delete",
            shorthandToPaths("1:[2,3,4];2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after delete",
            shorthandToPaths("2:[1];3:[1];4:[1,2]"),
            dep1.includedByAsString());

    // Remove the linking yam file - should be nothing to regenerate
    toRegenerate = dep1.deleted(linkingFile);
    assertEquals("Regenerate set not correct after deleted(YamFile)",
            new HashSet<String>(),
            toRegenerate);

    // ... and we should be back to the beggining
    assertEquals("Dependencies linksTo not correct after delete",
            shorthandToPaths("1:[2,3];2:[4,5];3:[5]"),
            dep1.linksToAsString());

    assertEquals("Dependencies linkedBy not correct after delete",
            shorthandToPaths("2:[1];3:[1];4:[2]"
                    + ";5:[2,3]"),
            dep1.linkedByAsString());

    assertEquals("Dependencies includes not correct after delete",
            shorthandToPaths("1:[2,3,4];2:[4]"),
            dep1.includesAsString());

    assertEquals("Dependencies includedBy not correct after delete",
            shorthandToPaths("2:[1];3:[1];4:[1,2]"),
            dep1.includedByAsString());



  }

  /**
   * Test Dependencies serialization
   * @throws Exception if the test fails
   */
  public void testSerialization() throws Exception {
    log.info("============= DependenciesTests.testSerialization() ============");
    log.info("testing serialization and deserialization of Dependencies");

    Dependencies.remove("1");
    Dependencies dep1 = Dependencies.get("1");

    // Add all the files in a wiki to the depencies
    for(YamFile yam : wiki1) {
      dep1.created(yam);
    }

    Dependencies.serialize();
    Dependencies.clear();

    assertTrue("Dependencies removed", Dependencies.exists("1"));

    Dependencies dep1Reloaded = Dependencies.get("1");

    assertEquals(
      "Dependencies not consistently serialized / deserialized",
      dep1, dep1Reloaded
    );

    Dependencies.remove("1");

  }


  /**
   * Suite of tests for Dependencies
   * @return The suite of tests
   */
  public static Test suite() {
    TestSuite suite= new TestSuite();    
    suite.addTest(new DependenciesTests("testCreateAndBasics"));
    suite.addTest(new DependenciesTests("testDelete"));
    suite.addTest(new DependenciesTests("testRename"));
    suite.addTest(new DependenciesTests("testDelete2"));
    suite.addTest(new DependenciesTests("testModify"));
    suite.addTest(new DependenciesTests("testNonYamLinks"));
    suite.addTest(new DependenciesTests("testNonYamChanges"));
    suite.addTest(new DependenciesTests("testSerialization"));
    return suite;
  }

  /**
   * <p>Replace all file numbers in str with the equivalent canonical paths in
   * yamFileNameMap. All files used in Dependencies testing are named by
   * convention as "yam-depends-\d+.yam". Dependencies will report its
   * internal state graphs in the form "pathA:[pathB,pathC];pathB[pathD]",
   * where pathX is the canonical path of a file.</p>
   * <p>For test files, we can refer to a Dependencies graph in a shorthand form
   * using just the digit part of the test name, e.g. "1:[2,3];2[4]" and then
   * translate this to full canonical paths using this method.</p>
   * <p>Non-yam files are referred to by their path relative to their linking
   * node. They are replaced by their full canonical path. For example,
   * "1:[some-file.html]", where some-file is not from yam, will become
   * "/parent/path/yam-depends-1.yam:[/parent/path/some-file.html]"
   * </p>
   * @param str A string in which we want to replace numbers with test file
   * canonical paths
   * @return   The equivalent string with numbers replaced by test file
   * canonical paths
   */
  public String shorthandToPaths(String str) {

    StringBuilder bldr = new StringBuilder();

    for(String nodeAndArcs : str.split("(\\]\\;)|(\\])")) {
      String[] nodeAndArcsSplit = nodeAndArcs.split("\\:\\[");
      String node = nodeAndArcsSplit[0];
      String arcs = nodeAndArcsSplit[1];

      String nodePath = null;
      if(node.matches("\\d+")) {
        // We've got a number representing a yam file: map it to a filename
        nodePath = yamFileNameMap.get(node);
      } else {
        // We've got a string representing a non-yam file
        // A non-yam node is relative to its sole arc
        // e.g. abc.html:[2]
        File yamParent = new File(yamFileNameMap.get(arcs)).getParentFile();        
        try{
          nodePath = new File(yamParent, node).getCanonicalPath();          
        } catch(IOException ioe) {
          fail("Couldn't resolve link canonical path: " + ioe.getMessage());
        }
      }

      bldr.append(nodePath);
      bldr.append(":[");
      for(String arc: arcs.split("\\,")) {
        if(arc.matches("\\d+")) {
          // We've got a number representing a yam file: map it to a filename
          bldr.append(yamFileNameMap.get(arc));
        } else {
          // We've got a string representing a non-yam file
          // An arc to a non-yam is relative to its node
          File yamParent = new File(nodePath).getParentFile();
          File link = new File(yamParent, arc);
          try{
          bldr.append(link.getCanonicalPath());
          } catch(IOException ioe) {
            fail("Couldn't resolve link canonical path: " + ioe.getMessage());
          }
        }
        bldr.append(",");
      }
      bldr.deleteCharAt(bldr.length() - 1);
      bldr.append("];");
    }
    bldr.deleteCharAt(bldr.length() - 1); 

    return bldr.toString();
  }

  /**
   * Replace all file numbers in str with the equivalent canonical paths in
   * yamFileNameMap, as described in
   * @link DependenciesTests#shorthandToPaths(String).
   * By convention, the file numbers in str are comma separated
   * @param str A comma separated list of numbers
   * @return A List of canonical paths equivalent to the numbers in str
   */
  private List<String> numbersToList(String str) {

    List<String> pathList = new ArrayList<String>();

    for(String number : str.split(",")) {
      pathList.add(yamFileNameMap.get(number));
    }

    return pathList;
  }

  /**
   * Copy one File to another. Testing fails if the copy fails.
   * @param in The File that will be copied
   * @param out The File to which in will be copied
   */
  private void copy(File in, File out) {

    log.info("Copying yam file from: " + in.getName()
              + " to: " + out.getName());

    try {
      FileChannel ic = new FileInputStream(in).getChannel();
      FileChannel oc = new FileOutputStream(out).getChannel();
      ic.transferTo(0, ic.size(), oc);
      ic.close();
      oc.close();
    } catch (IOException ioe) {
      fail("Failed testing while copying modified file: " + ioe.getMessage());
    }
  }
  
}