/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * See LICENSE.txt included in this distribution 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 LICENSE.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 (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. */ package org.opensolaris.opengrok.web; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.StringWriter; import java.text.SimpleDateFormat; import java.util.Arrays; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.opensolaris.opengrok.util.FileUtilities; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import static org.junit.Assert.*; import org.opensolaris.opengrok.configuration.RuntimeEnvironment; import org.opensolaris.opengrok.history.RepositoryFactory; /** * JUnit test to test that the DirectoryListing produce the expected result */ public class DirectoryListingTest { private File directory; private FileEntry[] entries; private SimpleDateFormat dateFormatter; class FileEntry implements Comparable { String name; String href; long lastModified; int size; // If negative, don't check it. FileEntry() { dateFormatter = new SimpleDateFormat("dd-MMM-yyyy"); } FileEntry(String name, String href, long lastModified, int size) { this.name = name; this.href = href; this.lastModified = lastModified; this.size = size; } private void create() throws Exception { File file = new File(directory, name); if (!file.exists()) { assertTrue("Failed to create file", file.createNewFile()); } long val = lastModified; if (val == Long.MAX_VALUE) { val = System.currentTimeMillis(); } assertTrue("Failed to set modification time", file.setLastModified(val)); if (size > 0) { FileOutputStream out = new FileOutputStream(file); byte[] buffer = new byte[size]; out.write(buffer); out.close(); } } public int compareTo(Object o) { int ret = -1; if (o instanceof FileEntry) { FileEntry fe = (FileEntry) o; // @todo verify all attributes! if (name.compareTo(fe.name) == 0 && href.compareTo(fe.href) == 0) { ret = 0; } // Negative size is not verified. if (size >= 0 && size == fe.size) { ret = 0; } } return ret; } } public DirectoryListingTest() { } @BeforeClass public static void setUpClass() throws Exception { } @AfterClass public static void tearDownClass() throws Exception { } @Before public void setUp() throws Exception { directory = FileUtilities.createTemporaryDirectory("directory"); entries = new FileEntry[3]; entries[0] = new FileEntry("foo.c", "foo.c", 0, 1); entries[1] = new FileEntry("bar.h", "bar.h", Long.MAX_VALUE, 0); entries[2] = null; for (FileEntry entry : entries) { if (entry != null) { entry.create(); } } // Create the entry that will be ignored separately. FileEntry hgtags = new FileEntry(".hgtags", ".hgtags", 0, 1); hgtags.create(); // Will test getSimplifiedPath() behavior for ignored directories. // Use negative value for length so it is not checked as the return value // of length() is unspecified for directories. entries[2] = new FileEntry("subdir", "subdir/", 0, -1); File subdir = new File(directory, "subdir"); subdir.mkdir(); File SCCSdir = new File(subdir, "SCCS"); SCCSdir.mkdir(); // Need to populate list of ignored entries for all repository types. RuntimeEnvironment env = RuntimeEnvironment.getInstance(); RepositoryFactory.setIgnored(env); } @After public void tearDown() throws Exception { if (directory != null && directory.exists()) { removeDirectory(directory); } } private void removeDirectory(File dir) { File[] childs = dir.listFiles(); if (childs != null) { for (File f : childs) { if (f.isDirectory()) { removeDirectory(f); } f.delete(); } } } /** * Get the href attribute from: <td align="left"><tt><a href="foo" * class="p">foo</a></tt></td> * * @param item * @return * @throws java.lang.Exception */ private String getHref(Node item) throws Exception { Node a = item.getFirstChild(); // a assertNotNull(a); assertEquals(Node.ELEMENT_NODE, a.getNodeType()); Node href = a.getAttributes().getNamedItem("href"); assertNotNull(href); assertEquals(Node.ATTRIBUTE_NODE, href.getNodeType()); return href.getNodeValue(); } /** * Get the filename from: <td align="left"><tt><a href="foo" * class="p">foo</a></tt></td> * * @param item * @return * @throws java.lang.Exception */ private String getFilename(Node item) throws Exception { Node a = item.getFirstChild(); // a assertNotNull(a); assertEquals(Node.ELEMENT_NODE, a.getNodeType()); Node node = a.getFirstChild(); assertNotNull(node); // If this is element node then it is probably a directory in which case // it contains the <b> element. if (node.getNodeType() == Node.ELEMENT_NODE) { node = node.getFirstChild(); assertNotNull(node); assertEquals(Node.TEXT_NODE, node.getNodeType()); } else { assertEquals(Node.TEXT_NODE, node.getNodeType()); } return node.getNodeValue(); } /** * Get the LastModified date from the <td>date</td> * @todo fix the item * @param item the node representing <td> * @return last modified date of the file * @throws java.lang.Exception if an error occurs */ private long getLastModified(Node item) throws Exception { Node val = item.getFirstChild(); assertNotNull(val); assertEquals(Node.TEXT_NODE, val.getNodeType()); String value = val.getNodeValue(); return value.equalsIgnoreCase("Today") ? Long.MAX_VALUE : dateFormatter.parse(value).getTime(); } /** * Get the size from the: <td><tt>size</tt></td> * @param item the node representing <td> * @return The size * @throws java.lang.Exception if an error occurs */ private int getSize(Node item) throws Exception { Node val = item.getFirstChild(); assertNotNull(val); assertEquals(Node.TEXT_NODE, val.getNodeType()); return Integer.parseInt(val.getNodeValue().trim()); } /** * Validate this file-entry in the table * @param element The <tr> element * @throws java.lang.Exception */ private void validateEntry(Element element) throws Exception { FileEntry entry = new FileEntry(); NodeList nl = element.getElementsByTagName("td"); int len = nl.getLength(); // There should be 5 columns or less in the table. if (len < 5) { return; } assertEquals(5, len); // item(0) is a decoration placeholder, i.e. no content entry.name = getFilename(nl.item(1)); entry.href = getHref(nl.item(1)); entry.lastModified = getLastModified(nl.item(3)); try { entry.size = getSize(nl.item(4)); } catch (Exception e) { entry.size = -1; } // Try to look it up in the list of files. for (int ii = 0; ii < entries.length; ++ii) { if (entries[ii] != null && entries[ii].compareTo(entry) == 0) { entries[ii] = null; return; } } fail("Could not find a match for: " + entry.name); } /** * Test directory listing * @throws java.lang.Exception if an error occurs while generating the * list. */ @Test public void directoryListing() throws Exception { StringWriter out = new StringWriter(); out.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<start>\n"); DirectoryListing instance = new DirectoryListing(); instance.listTo("ctx", directory, out, directory.getPath(), Arrays.asList(directory.list())); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); assertNotNull("DocumentBuilderFactory is null", factory); DocumentBuilder builder = factory.newDocumentBuilder(); assertNotNull("DocumentBuilder is null", builder); out.append("</start>\n"); String str = out.toString(); System.out.println(str); Document document = builder.parse(new ByteArrayInputStream(str.getBytes())); NodeList nl = document.getElementsByTagName("tr"); int len = nl.getLength(); // Add one extra for header and one for parent directory link. assertEquals(entries.length + 2, len); // Skip the the header and parent link. for (int i = 2; i < len; ++i) { validateEntry((Element) nl.item(i)); } } }