Package org.linkedin.util.io.resource

Source Code of org.linkedin.util.io.resource.TestResource$Checker

/*
* Copyright 2010-2010 LinkedIn, Inc
* Portions Copyright (c) 2011 Yan Pujante
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package org.linkedin.util.io.resource;

import junit.framework.TestCase;
import org.linkedin.util.clock.SystemClock;
import org.linkedin.util.concurrent.ExternalCommand;
import org.linkedin.util.io.IOUtils;
import org.linkedin.util.io.PathUtils;
import org.linkedin.util.io.ram.RAMDirectory;
import org.linkedin.util.io.resource.internal.InternalResource;
import org.linkedin.util.io.resource.internal.InternalResourceProvider;
import org.linkedin.util.io.resource.internal.ResourceProviderChain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* @author ypujante@linkedin.com
*
*/
public class TestResource extends TestCase
{
  public static final String MODULE = TestResource.class.getName();
  public static final Logger log = LoggerFactory.getLogger(MODULE);

  private final static int ROOT = 0;
  private final static int A = 1;
  private final static int B = 2;
  private final static int C = 3;

  private final static int EMPTY = 4;

  private final static int D = 5;

  private final static String WEB_XML_CONTENT =
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
    "\n" +
    "<!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'>\n" +
    "\n" +
    "<web-app>\n" +
    "  <servlet>\n" +
    "    <servlet-name>TestServletContextResource</servlet-name>\n" +
    "    <servlet-class>" +
    TestResource.class.getName() +
    "</servlet-class>\n" +
    "    <load-on-startup>1</load-on-startup>\n" +
    "  </servlet>\n" +
    "\n" +
    "  <servlet-mapping>\n" +
    "    <servlet-name>TestServletContextResource</servlet-name>\n" +
    "    <url-pattern>/tscr</url-pattern>\n" +
    "  </servlet-mapping>\n" +
    "</web-app>";

  private final List<File> _deleteOnExit = new ArrayList<File>();

  /**
   * Constructor
   */
  public TestResource(String name)
  {
    super(name);
  }

  @Override
  protected void tearDown() throws Exception
  {
    try
    {
      Collections.reverse(_deleteOnExit);
      for(File file : _deleteOnExit)
      {
        try
        {
          IOUtils.deleteFile(file);
        }
        catch(IOException e)
        {
          log.warn("Exception [ignored] while deleting file " + file, e);
        }
      }
    }
    finally
    {
      super.tearDown();
    }
  }

  protected String getWebXmlContent()
  {
    return WEB_XML_CONTENT;
  }

  public static abstract class Checker
  {
    protected void checkIsModifiedSince(Resource resource)
    {
      long lastModified = resource.lastModified();

      if(lastModified == 0L)
      {
        assertFalse(resource.isModifiedSince(lastModified));
        assertFalse(resource.isModifiedSince(1000L));
      }
      else
      {
        // 1ms in the past it was
        assertTrue(resource.isModifiedSince(lastModified - 1));

        // last modified or future
        assertFalse(resource.isModifiedSince(lastModified));
        assertFalse(resource.isModifiedSince(lastModified + 1));

        // 0 (modified)
        assertTrue(resource.isModifiedSince(0));
      }
    }

    protected void checkLastModified(ResourceInfo info, File file) throws IOException
    {
      assertEquals(file.lastModified(), info.getLastModified());
    }

    protected void checkInfo(Resource resource, File file) throws IOException
    {
      ResourceInfo info = resource.getInfo();
      checkLastModified(info, file);
      checkContentLength(info, file);
      checkIsModifiedSince(resource);
    }

    protected void checkContentLength(ResourceInfo info, File file) throws IOException
    {
      assertEquals(file.isDirectory() ? 0 : file.length(), info.getContentLength());
    }

    /**
     * By default the content of a directory is not readable (not as an input stream..)
     */
    protected void checkDirectoryContent(Resource resource) throws IOException
    {
      try
      {
        readContent(resource);
        fail("cannot read content of a directory");
      }
      catch(IOException e)
      {
        // expected
      }
    }

    protected void checkIsDirectory(Resource resource)
    {
      assertTrue(resource.isDirectory());
    }

    protected abstract void checkURI(Resource resource, File file) throws URISyntaxException;

    protected void checkListResource(Resource resource, File file) throws IOException
    {
      Resource[] resources = resource.list();
      assertEquals(file.list().length, resources.length);
    }

    /**
     * Apply the filter to the list and check that the result matches the expected elements.
     *
     * @param resource
     * @param filter
     * @param expected
     * @throws IOException
     */
    protected void checkListResources(Resource resource, final String filter, Resource... expected)
      throws IOException
    {
      Resource[] filteredResources = resource.list(new ResourceFilter()
      {
        @Override
        public boolean accept(Resource resource)
        {
          return resource.getFilename().endsWith(filter);
        }
      });
      assertEqualsOrderDontCare(filter, filteredResources, expected);
    }
  }

  public static class FileChecker extends TestResource.Checker
  {
    @Override
    public void checkURI(Resource resource, File file)
    {
      assertEquals(file.toURI(), resource.toURI());
    }
  }

  public static class RAMChecker extends TestResource.Checker
  {
    private final URI _fileBaseURI;

    public RAMChecker(URI fileBaseURI)
    {
      _fileBaseURI = fileBaseURI;
    }

    @Override
    protected void checkURI(Resource resource, File file) throws URISyntaxException
    {
      String fileURI = file.toURI().toString().substring(_fileBaseURI.toString().length());
      assertEquals(new URI("ram:///" + fileURI), resource.toURI());
    }
  }

  public static class JarChecker extends TestResource.Checker
  {
    private final URI _fileBaseURI;
    private final URI _jarBaseURI;

    public JarChecker(URI jarBaseURI, URI fileBaseURI)
    {
      _fileBaseURI = fileBaseURI;
      _jarBaseURI = jarBaseURI;
    }

    /**
     * It seems that jar and filesystem differ on this field... I wrote a separate program to test
     * my intuition and indeed some (but not all!) last modified are off by a second. My guess is
     * that jar and file round up to the nearest second but they don't seem to do it exactly the
     * same way....
     */
    @Override
    public void checkLastModified(ResourceInfo info, File file) throws IOException
    {
      assertTrue(Math.abs(file.lastModified() - info.getLastModified()) <= 1000);
    }

    /**
     * We recreate the uri resource from the base uri
     */
    @Override
    public void checkURI(Resource resource, File file) throws URISyntaxException
    {
      String fileURI = file.toURI().toString().substring(_fileBaseURI.toString().length());

      assertEquals(new URI("jar:" + PathUtils.removeTrailingSlash(_jarBaseURI.toString()) +
                           "!/" + fileURI),
                   resource.toURI());
    }

    /*
     Test code that proves what I say about last modified...
public static void main(String[] args) throws IOException, InterruptedException
{
   File root = new File("/tmp/ypujante/TestJarURL4");

   readFiles(root, new JarFile(new File("/tmp/ypujante/toto.jar")));
   //createFiles(root);
}

private static void readFiles(File root, JarFile jarFile)
{
   for(int i = 0; i < 100; i++)
   {
     File file = new File(root, "f" + i);
     JarEntry jarEntry = jarFile.getJarEntry("TestJarURL4/f" + i);

     if(file.lastModified() != jarEntry.getTime())
       System.out.println(file.toString() + " | " + jarEntry.toString() + " =>" +
                          file.lastModified() + " (" + new Date(file.lastModified()) + ") " +
                          "/" +
                          jarEntry.getTime() + " (" + new Date(jarEntry.getTime()) + ") ");
   }
}

private static void createFiles(File root)
   throws IOException, InterruptedException
{

   root.mkdirs();

   for(int i = 0; i < 100; i++)
   {
     File file = new File(root, "f" + i);
     FileWriter fw = new FileWriter(file);
     try
     {
       BufferedWriter writer = new BufferedWriter(fw);
       writer.write("content " + i);
       writer.flush();
     }
     finally
     {
       fw.close();
     }

     System.out.println(file.toString() + " =>" + file.lastModified());
     Thread.sleep(100);
   }
}

    */
  }

  /**
   * Test with a non existent file
   *
   * @throws IOException
   */
  public void testNonExistentFile() throws IOException, URISyntaxException
  {
    // we create a directory and then we delete it to make sure it does not exist...
    File root = createTempDirectory(TestResource.class.getName(), "testNonExistentFile");
    IOUtils.deleteFile(root);

    checkNonExistentResource(FileResource.createFromRoot(root), root, new TestResource.FileChecker());
  }

  /**
   * Convenient call which checks that a resource does not exists...
   *
   * @param resource the resource to check
   */
  public static void checkNonExistentResource(Resource resource,
                                              File nonExistenFile,
                                              TestResource.Checker checker)
    throws IOException, URISyntaxException
  {
    assertFalse(resource.exists());
    assertNull(resource.list());
    try
    {
      resource.getInfo();
      fail("non exsitent");
    }
    catch(IOException e)
    {
      // expected
    }

    try
    {
      resource.getInputStream();
      fail("it is a directory...");
    }
    catch(IOException e)
    {
      // expected
    }

    checker.checkURI(resource, nonExistenFile);

    assertFalse(resource.createRelative("bar").exists());
  }

  /**
   * We test with an empty directory.
   */
  public void testEmptyDirectory() throws IOException, URISyntaxException
  {
    File root = createTempDirectory(TestResource.class.getName(), "testEmptyDirectory");
    try
    {
      checkEmptyDirectory(root, FileResource.createFromRoot(root), new TestResource.FileChecker());
    }
    finally
    {
      IOUtils.deleteFile(root);
    }
  }

  /**
   * Checks that the resource points to an empty directory
   *
   * @param emptyDirectory
   * @param resource
   * @throws IOException
   */
  private static void checkEmptyDirectory(File emptyDirectory, Resource resource, TestResource.Checker checker)
    throws IOException, URISyntaxException
  {
    assertTrue(resource.exists());

    checker.checkIsDirectory(resource);

    // empty directory => 0 entries returned
    checker.checkListResource(resource, emptyDirectory);

    // info should be the same as what file returns (since in this case it is a thin wrapper)
    checker.checkInfo(resource, emptyDirectory);

    checker.checkDirectoryContent(resource);

    checker.checkURI(resource, emptyDirectory);

    // the directory is empty so it will point to a non existent resource
    checkNonExistentResource(resource.createRelative("foo"),
                             new File(emptyDirectory, "foo"),
                             checker);
  }

  /**
   * test for file resource
   */
  public void testTreeDirectoryFileResource() throws IOException, URISyntaxException
  {
    File root = createTempDirectory(TestResource.class.getName(), "testTreeDirectoryFileResource");
    try
    {
      File[][] directoryStructure = createDirectoryStructure(root, getWebXmlContent());

      // root
      Resource rootResource = FileResource.createFromRoot(root);

      checkTreeResource(rootResource, directoryStructure, new TestResource.FileChecker());
    }
    finally
    {
      IOUtils.deleteFile(root);
    }
  }

  /**
   * Test for RAM resource
   */
  public void testTreeDirectoryRAMResource() throws IOException, URISyntaxException
  {
    File root = createTempDirectory(TestResource.class.getName(), "testTreeDirectoryRAMResource");
    try
    {
      File[][] directoryStructure = createDirectoryStructure(root, getWebXmlContent());

      // root
      Resource rootResource = RAMResource.create(createRAMDirectory(root));

      checkTreeResource(rootResource, directoryStructure, new TestResource.RAMChecker(root.toURI()));
    }
    finally
    {
      IOUtils.deleteFile(root);
    }
  }

  /**
   * Duplicates the directory structure on the filesystem in a RAMDirectory
   */
  private RAMDirectory createRAMDirectory(File root) throws IOException
  {
    RAMDirectory ramRoot = new RAMDirectory(SystemClock.instance(), "");

    populateRAMDirectory(ramRoot, root);

    return ramRoot;
  }

  /**
   * Recursively populates the ram directory
   */
  private void populateRAMDirectory(RAMDirectory ramDirectory, File directory) throws IOException
  {
    String[] names = directory.list();

    for(String name : names)
    {
      File file = new File(directory, name);
      if(file.isDirectory())
      {
        RAMDirectory subdir = ramDirectory.mkdir(name);
        subdir.touch(file.lastModified());
        populateRAMDirectory(subdir, file);
      }
      else
      {
        ramDirectory.add(name, readContent(file)).touch(file.lastModified());
      }
    }
  }

  /**
   * Test for resource chain: we create 2 directory structures with some similarities and
   * dissimilarities. root1 is the same as the one created in {@link #createDirectoryStructure2(File)}
   *
   * <pre>
   * root2/
   *      a.html
   *      d.txt
   *      d1/
   *         a.html
   *         d.txt
   *         d1_1/
   *              a.html
   *              d.txt
   *              empty/
   *      d3/
   *         a.html
   *         d.txt
   * <p/>
   * </pre>
   *
   * @throws IOException
   * @throws URISyntaxException
   */
  public void testTreeDirectoryResourceChain() throws IOException, URISyntaxException
  {
    File root1 = createTempDirectory(TestResource.class.getName(), "testTreeDirectoryResourceChain1");
    try
    {
      // root1
      File[][] directoryStructure1 = createDirectoryStructure(root1, getWebXmlContent());

      // chain should work the same if there is only one element in it!
      checkTreeResource(ResourceChain.create(FileResource.createFromRoot(root1)),
                                             directoryStructure1,
                                             new TestResource.FileChecker());

      // chain should work the same if there is only one element in it!
      // I need to do this otherwise the previous create method bypass the chain entirely...
      InternalResourceProvider provider =
        (InternalResourceProvider) ((InternalResource) FileResource.createFromRoot(root1)).getResourceProvider();
      checkTreeResource(new ResourceProviderChain(provider).getRootResource(),
                        directoryStructure1,
                        new TestResource.FileChecker());

      File root2 = createTempDirectory(TestResource.class.getName(), "testTreeDirectoryResourceChain2");
      try
      {

        // root2
        File[][] directoryStructure2 = createDirectoryStructure2(root2);

        // here we create a chain where both resources are at the root...
        Resource root = ResourceChain.create(FileResource.createFromRoot(root1),
                                             FileResource.createFromRoot(root2));

        checkChain(root, directoryStructure1, directoryStructure2, new TestResource.FileChecker());
      }
      finally
      {
        IOUtils.deleteFile(root2);
      }
    }
    finally
    {
      IOUtils.deleteFile(root1);
    }
  }

  /**
   * Verifies that the chain is working properly.
   */
  private void checkChain(Resource root,
                          File[][] ds1,
                          File[][] ds2,
                          Checker checker) throws IOException, URISyntaxException
  {
    checkResource(root, "a.html", ds1[0][A], checker); // in root1 (precedence)
    checkResource(root, "b.txt", ds1[0][B], checker)// in root1 (only there)
    checkResource(root, "d.txt", ds2[0][D], checker)// in root2 (only there)
    checkNonExistentResource(root.createRelative("e.txt"), new File(ds2[0][ROOT], "e.txt"), checker);

    // now we go in d1:
    Resource d1 = root.createRelative("d1");
    checkResource(d1, "a.html", ds1[1][A], checker); // in root1/d1 (precedence)
    checkResource(d1, "b.txt", ds1[1][B], checker)// in root1/d1 (only there)
    checkResource(d1, "d.txt", ds2[1][D], checker)// in root2/d1 (only there)

    // now we go in d1_1:
    Resource d1_1 = d1.createRelative("d1_1");
    checkResource(d1_1, "a.html", ds1[2][A], checker); // in root1/d1/d1_1 (precedence)
    checkResource(d1_1, "b.txt", ds1[2][B], checker)// in root1/d1/d1_1 (only there)
    checkResource(d1_1, "d.txt", ds2[2][D], checker)// in root2/d1/d1_1 (only there)

    // now we go in d2:
    Resource d2 = root.createRelative("d2");
    checkResource(d2, "a.html", ds1[3][A], checker); // in root1/d2 (only there)
    checkResource(d2, "b.txt", ds1[3][B], checker)// in root1/d2 (only there)
    checkNonExistentResource(d2.createRelative("d.txt"), new File(ds2[0][ROOT], "d2/d.txt"), checker);

    // now we go in d3:
    Resource d3 = root.createRelative("d3");
    checkResource(d3, "a.html", ds2[3][A], checker); // in root2/d3 (only there)
    checkResource(d3, "d.txt", ds2[3][D], checker)// in root2/d3 (only there)
    checkNonExistentResource(d3.createRelative("b.txt"), new File(ds2[0][ROOT], "d3/b.txt"), checker);

    checker.checkListResources(d1_1, ".txt",
                               d1_1.createRelative("b.txt"),
                               d1_1.createRelative("c.txt"),
                               d1_1.createRelative("d.txt"));

    checker.checkListResources(d3, ".txt",
                               d3.createRelative("d.txt"));
  }

  /**
   * Create the following directory structure
   * <pre>
   * root/
   *      a.html
   *      b.txt
   *      c.txt
   *      empty/
   *      d1/
   *         a.html
   *         b.txt
   *         c.txt
   *         empty/
   *         d1_1/
   *              a.html
   *              b.txt
   *              c.txt
   *              empty/
   *      d2/
   *         a.html
   *         b.txt
   *         c.txt
   *         empty/
   *      WEB-INF/
   *         web.xml
   * <p/>
   * </pre>
   *
   * @throws IOException
   */
  protected File[][] createDirectoryStructure(File root, String webXmlContent) throws IOException
  {
    File[] rootFiles = createSubTree(root);

    File[] d1Files = createSubTree(createDir(new File(rootFiles[ROOT], "d1")));

    File[] d2Files = createSubTree(createDir(new File(rootFiles[ROOT], "d2")));

    File[] d1_1Files = createSubTree(createDir(new File(d1Files[ROOT], "d1_1")));

    File[] webInfFiles = createWebInfFiles(createDir(new File(rootFiles[ROOT], "WEB-INF")),
                                           webXmlContent);

    return new File[][]{rootFiles, d1Files, d1_1Files, d2Files, webInfFiles};
  }

  /**
   * Create the following directory structure
   * <pre>
   * root2/
   *      a.html
   *      d.txt
   *      d1/
   *         a.html
   *         d.txt
   *         d1_1/
   *              a.html
   *              d.txt
   *              empty/
   *      d3/
   *         a.html
   *         d.txt
   * <p/>
   * </pre>
   *
   * @throws IOException
   */
  private File[][] createDirectoryStructure2(File root) throws IOException
  {
    File[] rootFiles = createSubTree(root, "a.html", null, null, null, "d.txt", "d1/", "d3/");

    File[] d1Files = createSubTree(rootFiles[6], "a.html", null, null, null, "d.txt", "d1_1/");

    File[] d3Files = createSubTree(rootFiles[7], "a.html", null, null, "empty/", "d.txt");

    File[] d1_1Files = createSubTree(d1Files[6], "a.html", null, null, null, "d.txt");

    return new File[][]{rootFiles, d1Files, d1_1Files, d3Files};
  }

  /**
   * test for jar resource
   */
  public void testTreeDirectoryJarResource()
    throws IOException, InterruptedException, URISyntaxException
  {
    File root = createTempDirectory(TestResource.class.getName(), "testTreeDirectoryJarResource");
    try
    {
      File nestedRoot = createDir(new File(root, "root"));
     
      File[][] directoryStructure = createDirectoryStructure(nestedRoot, getWebXmlContent());

      // first test where root is /
      File jarFile = createJarFile(TestResource.class.getName(), nestedRoot);
      try
      {
        // the following calls should be equivalent
        checkTreeResource(JarResource.create(FileResource.createFromRoot(jarFile)),
                          directoryStructure,
                          new TestResource.JarChecker(jarFile.toURI(), nestedRoot.toURI()));
        checkTreeResource(JarResource.create(URI.create("jar:" + jarFile.toURI() + "!/")),
                          directoryStructure,
                          new TestResource.JarChecker(jarFile.toURI(), nestedRoot.toURI()));
      }
      finally
      {
        jarFile.delete();
      }

      // second test where root is /root
      jarFile = createJarFile(TestResource.class.getName(), root);
      try
      {
        // the following calls should be equivalent
        checkTreeResource(JarResource.create(FileResource.createFromRoot(jarFile), "/root"),
                          directoryStructure,
                          new TestResource.JarChecker(jarFile.toURI(), root.toURI()));
      }
      finally
      {
        jarFile.delete();
      }

    }
    finally
    {
      IOUtils.deleteFile(root);
    }
  }

  /**
   * Checks the tree resource.
   */
  public static void checkTreeResource(Resource rootResource,
                                       File[][] files,
                                       TestResource.Checker checker)
    throws IOException, URISyntaxException
  {
    assertTrue(rootResource.exists());
   
    checkSubTree(rootResource, files[0], rootResource, files[0], checker);
    checker.checkListResource(rootResource, files[0][ROOT]);

    // d1
    Resource d1Resource = rootResource.createRelative("d1/");
    checkSubTree(rootResource, files[0], d1Resource, files[1], checker);
    checker.checkListResource(d1Resource, files[1][ROOT]);

    // we make sure that a combination of / is not affecting the result (note that since
    // d1 is at the root /d1 and d1 are equivalent)
    d1Resource = rootResource.createRelative("d1/");
    checkSubTree(rootResource, files[0], d1Resource, files[1], checker);
    checker.checkListResource(d1Resource, files[1][ROOT]);

    d1Resource = rootResource.createRelative("/d1");
    checkSubTree(rootResource, files[0], d1Resource, files[1], checker);
    checker.checkListResource(d1Resource, files[1][ROOT]);

    d1Resource = rootResource.createRelative("/d1/");
    checkSubTree(rootResource, files[0], d1Resource, files[1], checker);
    checker.checkListResource(d1Resource, files[1][ROOT]);

    // d1_1
    Resource d1_1Resource = d1Resource.createRelative("d1_1");
    checkSubTree(rootResource, files[0], d1_1Resource, files[2], checker);
    checker.checkListResource(d1_1Resource, files[2][ROOT]);

    // /d1_1 does not exists...
    checkNonExistentResource(d1Resource.getRootResource().createRelative("/d1_1"),
                             new File(files[0][ROOT], "d1_1"), checker);

    // test of chroot
    checkChroot(d1Resource, files[2][A], checker);

    // d2
    Resource d2Resource = rootResource.createRelative("d2");
    checkSubTree(rootResource, files[0], d2Resource, files[3], checker);
    checker.checkListResource(d2Resource, files[3][ROOT]);

    // WEB-INF
    Resource webInfResource = rootResource.createRelative("/WEB-INF");
    checker.checkListResource(webInfResource, files[4][ROOT]);
    checkResource(webInfResource, "web.xml", files[4][1], checker);
  }

  /**
   * Checks chroot
   */
  private static void checkChroot(Resource d1Resource, File expectedFile, TestResource.Checker checker)
    throws IOException, URISyntaxException
  {
    Resource d1_1Resource = d1Resource.chroot("d1_1/");
    assertEquals(d1_1Resource, d1_1Resource.getRootResource());
    assertEquals(d1_1Resource, d1_1Resource.getParentResource());
    checkResource(d1_1Resource, "a.html", expectedFile, checker);

    d1_1Resource = d1Resource.chroot("d1_1");
    assertEquals(d1_1Resource, d1_1Resource.getRootResource());
    assertEquals(d1_1Resource, d1_1Resource.getParentResource());
    checkResource(d1_1Resource, "a.html", expectedFile, checker);

    d1_1Resource = d1Resource.getRootResource().chroot("/d1/d1_1/");
    assertEquals(d1_1Resource, d1_1Resource.getRootResource());
    assertEquals(d1_1Resource, d1_1Resource.getParentResource());
    checkResource(d1_1Resource, "a.html", expectedFile, checker);

    d1_1Resource = d1_1Resource.chroot(".");
    assertEquals(d1_1Resource, d1_1Resource.getRootResource());
    assertEquals(d1_1Resource, d1_1Resource.getParentResource());
    checkResource(d1_1Resource, "a.html", expectedFile, checker);

    d1_1Resource = d1Resource.createRelative("d1_1/empty/").chroot("../");
    assertEquals(d1_1Resource, d1_1Resource.getRootResource());
    assertEquals(d1_1Resource, d1_1Resource.getParentResource());
    checkResource(d1_1Resource, "a.html", expectedFile, checker);

    Resource ar = d1Resource.createRelative("d1_1/a.html").chroot("");
    checkResource(ar, expectedFile, checker);
    assertEquals("/a.html", ar.getPath());

    ar = d1Resource.chroot("d1_1/a.html");
    checkResource(ar, expectedFile, checker);
    assertEquals("/a.html", ar.getPath());

  }

  /**
   * Checks the tree resource.
   */
  public static void checkTreeResourceForURL(Resource rootResource,
                                             File[][] files,
                                             TestResource.Checker checker)
    throws IOException, URISyntaxException
  {
    checkSubTree(rootResource, files[0], rootResource, files[0], checker);
    checker.checkListResource(rootResource, files[0][ROOT]);

    // d1
    Resource d1Resource = rootResource.createRelative("d1/");
    checkSubTree(rootResource, files[0], d1Resource, files[1], checker);
    checker.checkListResource(d1Resource, files[1][ROOT]);

    // d1_1
    Resource d1_1Resource = d1Resource.createRelative("d1_1/");
    checkSubTree(rootResource, files[0], d1_1Resource, files[2], checker);
    checker.checkListResource(d1_1Resource, files[2][ROOT]);

    // d2
    Resource d2Resource = rootResource.createRelative("d2/");
    checkSubTree(rootResource, files[0], d2Resource, files[3], checker);
    checker.checkListResource(d2Resource, files[3][ROOT]);
  }

  /**
   * Check the subtree: verifies createRelative(filter), and each individual resource
   */
  private static void checkSubTree(Resource rooResource,
                                   File[] rootFiles,
                                   Resource resource,
                                   File[] files,
                                   TestResource.Checker checker)
    throws IOException, URISyntaxException
  {
    assertTrue(rooResource.exists());
   
    checker.checkListResources(resource, ".foo");
    checker.checkListResources(resource, ".html", resource.createRelative("a.html"));
    checker.checkListResources(resource,
                               ".txt",
                               resource.createRelative("b.txt"),
                               resource.createRelative("c.txt"));
    checker.checkListResources(resource, "empty", resource.createRelative("empty/"));
    checker.checkListResources(resource, "empty", resource.createRelative("empty"));

    checker.checkListResources(resource, "empty", resource.createRelative("/empty/"));
    checker.checkListResources(resource, "empty", resource.createRelative("/empty"));

    checker.checkListResources(rooResource, "empty", resource.getRootResource().createRelative("/empty/"));
    checker.checkListResources(rooResource, "empty", resource.getRootResource().createRelative("/empty"));

    checkResource(resource, "a.html", files[A], checker);
    checkResource(resource, "b.txt", files[B], checker);
    checkResource(resource, "c.txt", files[C], checker);
    checkResource(resource, "empty/", files[EMPTY], checker);

    checkResource(resource.getRootResource(), "/a.html", rootFiles[A], checker);
    checkResource(resource.getRootResource(), "/b.txt", rootFiles[B], checker);
    checkResource(resource.getRootResource(), "/c.txt", rootFiles[C], checker);
    checkResource(resource.getRootResource(), "/empty/", rootFiles[EMPTY], checker);

    // checking that . ./ and "" are working correctly
    assertEquals(resource.createRelative("empty").toURI(),
                 resource.createRelative("empty").createRelative(".").toURI());

    assertEquals(resource.createRelative("empty/").toURI(),
                 resource.createRelative("empty/").createRelative(".").toURI());

    assertEquals(resource.createRelative("empty/").toURI(),
                 resource.createRelative("empty/").createRelative("./").toURI());

    assertEquals(resource.createRelative("empty").toURI(),
                 resource.createRelative("empty").createRelative("").toURI());

    assertTrue(resource.createRelative("/").getPath().endsWith("/"));
    assertTrue(resource.createRelative("a.html").createRelative("/").getPath().endsWith("/"));

    checkEmptyDirectory(files[EMPTY], resource.createRelative("empty/"), checker);

    checkParentResource(resource);
    checkRootResource(resource, rooResource);
  }

  /**
   * We check the parent resource functionnality
   *
   * @param resourceDir
   * @throws IOException
   */
  private static void checkParentResource(Resource resourceDir) throws IOException
  {
    checkResource(resourceDir.createRelative("a.html").getParentResource(), resourceDir);
    checkResource(resourceDir.createRelative("a.html").createRelative(".."), resourceDir);
    checkResource(resourceDir.createRelative("a.html").createRelative("../b.txt"),
                  resourceDir.createRelative("b.txt"));
  }

  /**
   * Compares 2 resources..
   *
   * @param resource
   * @param expectedResource
   */
  private static void checkResource(Resource resource, Resource expectedResource)
  {
    assertEquals(expectedResource.isDirectory(), resource.isDirectory());
    assertEquals(expectedResource.getFilename(), resource.getFilename());
    checkPath(expectedResource.getPath(), resource.getPath());
    assertEquals(PathUtils.removeTrailingSlash(expectedResource.toURI().toString()),
                 PathUtils.removeTrailingSlash(resource.toURI().toString()));
  }

  /**
   * The problem with paths is that there may be a trailing slash or not depending on the
   * implementation...
   *
   * @param path
   * @param expectedPath
   */
  private static void checkPath(String path, String expectedPath)
  {
    if(expectedPath.equals("/"))
      assertEquals("/", path);
    else
      assertEquals(PathUtils.removeTrailingSlash(expectedPath),
                   PathUtils.removeTrailingSlash(path));
  }

  /**
   * We check the root resource functionnality
   *
   * @throws IOException
   */
  private static void checkRootResource(Resource resource, Resource expectedRootResource) throws IOException
  {
    checkResource(resource.getRootResource(), expectedRootResource);
    checkResource(resource.getRootResource().createRelative("/"), expectedRootResource);
    checkResource(resource.getRootResource().createRelative("/.././"), expectedRootResource);
  }

  /**
   * Check an individual resource and compares it to the original file. Even read the content
   */
  private static void checkResource(Resource root, String relativePath, File file, TestResource.Checker checker)
    throws IOException, URISyntaxException
  {
    checkResource(root.createRelative(relativePath), file, checker);
  }

  /**
   * Check an individual resource and compares it to the original file. Even read the content
   */
  private static void checkResource(Resource resource, File file, TestResource.Checker checker)
    throws IOException, URISyntaxException
  {
    checker.checkInfo(resource, file);

    if(file.isDirectory())
    {
      checker.checkDirectoryContent(resource);
    }
    else
    {
      assertEquals(readContent(file), readContent(resource));
    }

    checker.checkURI(resource, file);

    assertEquals(file.getName(), resource.getFilename());
  }

  /**
   * Compare the 2 arrays without caring about the order of the elements in the array
   *
   * @param msg
   * @param computed
   * @param expected
   * @throws IOException
   */
  private static void assertEqualsOrderDontCare(String msg, Resource[] computed, Resource... expected)
    throws IOException
  {
    if(expected == null)
    {
      assertNull("computed should be null", computed);
      return;
    }

    assertNotNull("computed should not be null", computed);

    assertEquals(expected.length, computed.length);

    if(expected.length == 0)
      return;
   
    if(expected.length > 1)
    {
      Set<String> expectedURIs = new HashSet<String>(expected.length);
      for(Resource resource : expected)
      {
        String uri = resource.toString();

        uri = PathUtils.removeTrailingSlash(uri);

        expectedURIs.add(uri);
      }

      Set<String> computedURIs = new HashSet<String>(computed.length);
      for(Resource resource : computed)
      {
        String uri = resource.toString();

        uri = PathUtils.removeTrailingSlash(uri);

        computedURIs.add(uri);
      }

      assertEquals(msg, expectedURIs, computedURIs);
    }
    else
    {
      checkResource(computed[0], expected[0]);
    }
  }

  /**
   * Creates a subtree in the given directory.
   *
   * @param root
   * @return the file objects created (using the constants)
   * @throws IOException
   */
  private File[] createSubTree(File root) throws IOException
  {
    return createSubTree(root, "a.html", "b.txt", "c.txt", "empty/");
  }

  /**
   * Creates a subtree in the given directory.
   *
   * @param root
   * @return the file objects created (using the constants)
   * @throws IOException
   */
  private File[] createSubTree(File root, String... paths) throws IOException
  {
    File[] res = new File[paths.length + 1];

    res[ROOT] = root;
    for(int i = 0; i < paths.length; i++)
    {
      File file = null;

      String path = paths[i];

      if(path != null)
      {
        if(path.endsWith("/"))
        file = createDir(new File(root, path));
        else
          file = createFile(new File(root, path));
      }

      res[i+1] = file;

    }

    return res;
  }

  /**
   * Creates the web-inf files. Currently web.xml.
   */
  private File[] createWebInfFiles(File dir, String webXmlContent) throws IOException
  {
    File[] res = new File[2];

    res[0] = dir;
    if(webXmlContent != null)
      res[1] = createFile(new File(dir, "web.xml"), webXmlContent);

    return res;
  }

  /**
   * Creates a temporary directory.
   *
   * @param namespace
   * @param name      root name of the temporary directory
   * @return the temp dir
   * @throws IOException if there is a problem
   */
  public File createTempDirectory(String namespace, String name) throws IOException
  {
    File tempDirectory = IOUtils.createTempDirectory(namespace, name);
    _deleteOnExit.add(tempDirectory.getParentFile());
    return tempDirectory;
  }

  /**
   * Creates a file and populate with the content.
   *
   * @param file
   * @return the file provided for convenience...
   */
  public static File createFile(File file) throws IOException
  {
    return createFile(file, "{" + file.getCanonicalPath() + "}");
  }

  /**
   * Creates a file and populate with the content.
   *
   * @param file
   * @param content
   * @return the file provided for convenience...
   */
  public static File createFile(File file, String content) throws IOException
  {
    file.deleteOnExit();

    FileWriter fw = new FileWriter(file);
    try
    {
      BufferedWriter writer = new BufferedWriter(fw);
      writer.write(content);
      writer.flush();

      return file.getCanonicalFile();
    }
    finally
    {
      fw.close();
    }
  }

  /**
   * Encapsulates the creation of a directory
   *
   * @param dir
   * @return the directory provided for convenience
   * @throws IOException
   */
  public static File createDir(File dir) throws IOException
  {
    IOUtils.createNewDirectory(dir);
    dir.deleteOnExit();
    return dir.getCanonicalFile();
  }

  /**
   * Reads the full content of the resource
   *
   * @param resource
   * @return the full content
   * @throws IOException
   */
  public static String readContent(Resource resource) throws IOException
  {
    InputStream is = resource.getInputStream();
    try
    {
      return readContent(is);
    }
    finally
    {
      is.close();
    }
  }

  /**
   * Reads the full content of the file
   *
   * @param file
   * @return the full content
   * @throws IOException
   */
  public static String readContent(File file) throws IOException
  {
    InputStream is = new FileInputStream(file);
    try
    {
      return readContent(is);
    }
    finally
    {
      is.close();
    }
  }

  /**
   * Reads the content of the input stream and return it as a string.
   *
   * @param is
   * @return the content as a string
   * @throws IOException
   */
  public static String readContent(InputStream is) throws IOException
  {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    IOUtils.copy(is, baos);
    return new String(baos.toByteArray());
  }

  /**
   * Creates a jar file with all the files contained in root. (Uses jar command)
   *
   * @param namespace
   * @param root
   * @return the jar file
   * @throws IOException
   * @throws InterruptedException
   */
  public File createJarFile(String namespace, File root)
    throws IOException, InterruptedException
  {
    File jarDirectory = createTempDirectory(namespace, root.getName() + "_jar");
    File jarFile = new File(jarDirectory, root.getName() + ".jar");
    return copyToJar(jarFile, root, false);
  }

  /**
   * Creates a jar file with all the files contained in root. (Uses jar command)
   *
   * @param jarFile to put the files in
   * @param root
   * @return the jar file
   * @throws IOException
   * @throws InterruptedException
   */
  public static File copyToJar(File jarFile, File root, boolean update)
    throws IOException, InterruptedException
  {
    ExternalCommand cmd =
      ExternalCommand.create("jar", "-" + (update ? "u" : "c") "vfM",
                             jarFile.getCanonicalPath(),
                             ".");
    cmd.setWorkingDirectory(root);
    cmd.start();
    cmd.waitFor();

    assertEquals(0, cmd.exitValue());

    jarFile.deleteOnExit();
    return jarFile.getCanonicalFile();
  }

}
TOP

Related Classes of org.linkedin.util.io.resource.TestResource$Checker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.