/** * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * muCommander is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.mucommander.commons.file; import org.testng.annotations.Test; import java.net.MalformedURLException; /** * A generic test case for {@link com.mucommander.commons.file.FileURL}. This class is abstract and must be extended for * each URL scheme to be tested. Those tests should be completed by additional scheme-specific tests for * certain particularities that would not covered by this generic test case. * * @see com.mucommander.commons.file.FileURL * @author Maxence Bernard */ public abstract class FileURLTestCase { //////////////////// // Helper methods // //////////////////// /** * Returns the scheme-specific version of the path. * * @param path a forward slash-separated path * @return the corresponding scheme-specific path */ protected String getSchemePath(String path) { String separator = getPathSeparator(); if(!separator.equals("/")) path = "/" + path.replace("/", separator); return path; } /** * Creates a URL from the given parts, ensuring that parsing it yields the given parts, and returns it. * * @param login login part, may be <code>null</code> * @param password password part, may be <code>null</code> * @param host host part, may be <code>null</code> * @param port port part, <code>-1</code> for none * @param path path part * @param query query part, may be <code>null</code> * @return a URL corresponding to the given parts * @throws MalformedURLException if the URL cannot be parsed */ protected FileURL getURL(String login, String password, String host, int port, String path, String query) throws MalformedURLException { String scheme = getScheme(); StringBuffer sb = new StringBuffer(scheme+"://"); if(host!=null) { if(login!=null) { sb.append(login); if(password!=null) { sb.append(':'); sb.append(password); } sb.append('@'); } sb.append(host); if(port!=-1) { sb.append(':'); sb.append(port); } } path = getSchemePath(path); sb.append(path); if(query!=null) { sb.append('?'); sb.append(query); } // Assert that each of the url's parts match FileURL url = FileURL.getFileURL(sb.toString()); assert scheme.equals(url.getScheme()); if(host!=null) { if(login!=null) { assert login.equals(url.getLogin()); assert url.containsCredentials(); if(password!=null) assert password.equals(url.getPassword()); assert new Credentials(login, password).equals(url.getCredentials(), true); } assert host.equals(url.getHost()); assert port == url.getPort(); } if(query!=null && !isQueryParsed()) { assert url.getQuery() == null; path = path+"?"+query; } else if(query == null) assert url.getQuery() == null; else assert query.equals(url.getQuery()); assertPathEquals(path, url); // Test the URL's string representation assert url.equals(FileURL.getFileURL(url.toString(true, false))); assert url.equals(FileURL.getFileURL(url.toString(false, false)), false, false); return url; } /** * Shorthand for {@link #getURL(String, String, String, int, String, String)} called with just a path. * * @param path the path part * @return a URL corresponding to the given part * @throws MalformedURLException if the URL cannot be parsed */ protected FileURL getURL(String path) throws MalformedURLException { return getURL(null, null, null, -1, path, null); } /** * Shorthand for {@link #getURL(String, String, String, int, String, String)} called with just a host and path. * * @param host the host part, may be <code>null</code> * @param path the path part * @return a URL corresponding to the given part * @throws MalformedURLException if the URL cannot be parsed */ protected FileURL getURL(String host, String path) throws MalformedURLException { return getURL(null, null, host, -1, path, null); } /** * Returns the simplest FileURL instance possible using the scheme returned by {@link #getScheme()}, containing * only the scheme and '/' as a path. For instance, if <code>http</code> is the current scheme, a FileURL with * <code>http:///</code> as a string representation will be returned. * * @return a 'root' FileURL instance with the scheme returned by {@link # getScheme ()} * @throws MalformedURLException should never happen */ protected FileURL getRootURL() throws MalformedURLException { return FileURL.getFileURL(getScheme()+":///"); } /** * Attemps to parse the specified url using {@link FileURL#getFileURL(String)} and returns <code>true</code> if it * succeeded, <code>false</code> if it threw a <code>MalformedURLException</code>. * * @param url the URL to try and parse * @return <code>true</code> if the URL could be parsed, <code>false</code> if a <code>MalformedURLException</code> was thrown */ protected boolean canParse(String url) { try { FileURL.getFileURL(url); return true; } catch(MalformedURLException e) { return false; } } /** * Asserts that both URLs are equal, and that their hashcodes are the same. * * @param url1 first url to test * @param url2 second url to test */ protected void assertEquals(FileURL url1, FileURL url2) { assert url1.equals(url2); assert url2.equals(url1); assert url1.hashCode()==url2.hashCode(); } /** * Asserts that both URLs are equal, comparing credentials and properties as requested. If both the * <code>compareCredentials</code> and <code>compareProperties</code> parameters are <code>true</code>, this method * asserts that the hashcode of both URLs are the same. * * @param url1 first url to test * @param url2 second url to test * @param compareCredentials if <code>true</code>, the login and password parts of both FileURL need to be * equal (case-sensitive) for the FileURL instances to be equal * @param compareProperties if <code>true</code>, all properties need to be equal (case-sensitive) in both * FileURL for them to be equal */ protected void assertEquals(FileURL url1, FileURL url2, boolean compareCredentials, boolean compareProperties) { assert url1.equals(url2, compareCredentials, compareProperties); assert url2.equals(url1, compareCredentials, compareProperties); // Compare hash codes only if both flags are true. if(compareCredentials && compareProperties) assert url1.hashCode()==url2.hashCode(); } /** * Asserts that both URLs are not equal. * * @param url1 first url to test * @param url2 second url to test */ protected void assertNotEquals(FileURL url1, FileURL url2) { assert !url1.equals(url2); assert !url2.equals(url1); } /** * Asserts that both URLs are not equal, comparing credentials and properties as requested. * * @param url1 first url to test * @param url2 second url to test * @param compareCredentials if <code>true</code>, the login and password parts of both FileURL need to be * equal (case-sensitive) for the FileURL instances to be equal * @param compareProperties if <code>true</code>, all properties need to be equal (case-sensitive) in both * FileURL for them to be equal */ protected void assertNotEquals(FileURL url1, FileURL url2, boolean compareCredentials, boolean compareProperties) { assert !url1.equals(url2, compareCredentials, compareProperties); assert !url2.equals(url1, compareCredentials, compareProperties); } /** * Asserts that the path of the given URL is equal to the given expected path. * * @param expectedPath the expected path * @param url URL to test */ protected void assertPathEquals(String expectedPath, FileURL url) { assert expectedPath.equals(url.getPath()); } ////////////////// // Test methods // ////////////////// /** * Ensures that the values returned by {@link FileURL#getStandardPort()} and {@link SchemeHandler#getStandardPort()} * match the expected one returned by {@link #getDefaultPort()}. * * @throws MalformedURLException should not happen */ @Test public void testDefaultPort() throws MalformedURLException { FileURL url = getRootURL(); int expectedDefaultPort = getDefaultPort(); // Assert that the default port value returned by the FileURL and its handler match the expected one // and are consistent assert expectedDefaultPort == url.getStandardPort(); assert expectedDefaultPort == url.getHandler().getStandardPort(); // Assert that the default port value is valid: either -1 or comprised between 1 and 65535 assert expectedDefaultPort==-1||(expectedDefaultPort>0 && expectedDefaultPort<65536); } /** * Ensures that the values returned by {@link FileURL#getGuestCredentials()} and {@link SchemeHandler#getGuestCredentials()} * match the expected one returned by {@link #getGuestCredentials()}. * * @throws MalformedURLException should not happen */ @Test public void testGuestCredentials() throws MalformedURLException { FileURL url = getRootURL(); Credentials expectedGuestCredentials = getGuestCredentials(); // Assert that the guest credentials values returned by the FileURL and its handler match the expected one // and are consistent if(expectedGuestCredentials == null) { assert url.getGuestCredentials() == null; assert url.getHandler().getGuestCredentials() == null; } else { assert expectedGuestCredentials.equals(url.getGuestCredentials()); assert expectedGuestCredentials.equals(url.getHandler().getGuestCredentials()); } } /** * Ensures that the values returned by {@link FileURL#getAuthenticationType()} ()} and {@link SchemeHandler#getAuthenticationType()} * match the expected value returned by {@link #getAuthenticationType()}, and that the value is one of the constants * defined in {@link AuthenticationType}. * If the authentication type is {@link AuthenticationType#NO_AUTHENTICATION}, verifies that * {@link #getGuestCredentials()} returns <code>null</code>. * * @throws MalformedURLException should not happen */ @Test public void testAuthenticationType() throws MalformedURLException { FileURL url = getRootURL(); AuthenticationType expectedAuthenticationType = getAuthenticationType(); assert expectedAuthenticationType.equals(url.getAuthenticationType()); assert expectedAuthenticationType.equals(url.getHandler().getAuthenticationType()); assert expectedAuthenticationType==AuthenticationType.NO_AUTHENTICATION || expectedAuthenticationType==AuthenticationType.AUTHENTICATION_REQUIRED || expectedAuthenticationType==AuthenticationType.AUTHENTICATION_OPTIONAL; if(expectedAuthenticationType== AuthenticationType.NO_AUTHENTICATION) assert url.getGuestCredentials() == null; } /** * Ensures that the values returned by {@link FileURL#getPathSeparator()} and {@link SchemeHandler#getPathSeparator()} * match the expected one returned by {@link #getPathSeparator()}. * * @throws MalformedURLException should not happen */ @Test public void testPathSeparator() throws MalformedURLException { FileURL url = getRootURL(); String expectedPathSeparator = url.getPathSeparator(); // Assert that the path separator values returned by the FileURL and its handler match the expected one // and are consistent assert expectedPathSeparator.equals(url.getPathSeparator()); assert expectedPathSeparator.equals(url.getHandler().getPathSeparator()); } /** * Tests {@link com.mucommander.commons.file.FileURL#getRealm()} by ensuring that it returns the same URL only with the * path stripped out. * <p> * <b>Important:</b> this method must be overridden for protocols that have a specific realm notion (like SMB) or * else the test will fail. * </p> * * * @throws MalformedURLException should not happen */ @Test public void testRealm() throws MalformedURLException { assertEquals(getURL("host", "/"), getURL("host", "/path/to/file").getRealm()); } /** * Ensures that the query part is parsed only if it should be, as specified by {@link #isQueryParsed()}. * * @throws MalformedURLException should not happen */ @Test public void testQueryParsing() throws MalformedURLException { FileURL url = getURL(null, null, "host", -1, "/path", "query¶m=value"); String query = url.getQuery(); if(isQueryParsed()) { assert "query¶m=value".equals(query); } else { assert query == null; } } /** * Ensures that FileURL#getParent() works as expected. * * @throws MalformedURLException should not happen */ @Test public void testParent() throws MalformedURLException { FileURL url = getURL("login", "password", "host", 10000, "/path/to", "query¶m=value"); url.setProperty("key", "value"); FileURL parentURL = url.getParent(); // Test path and filename assertPathEquals(getSchemePath("/path/"), parentURL); assert "path".equals(parentURL.getFilename()); // Assert that schemes, hosts and ports match assert url.getScheme().equals(parentURL.getScheme()); assert url.getHost().equals(parentURL.getHost()); assert url.getPort() == parentURL.getPort(); // Assert that credentials match assert url.getCredentials().equals(parentURL.getCredentials()); assert url.getLogin().equals(parentURL.getLogin()); assert url.getPassword().equals(parentURL.getPassword()); // Assert that the sample property is in the parent URL assert "value".equals(parentURL.getProperty("key")); // Assert that handlers match assert url.getHandler().equals(parentURL.getHandler()); // Assert that the query part is null assert parentURL.getQuery() == null; // One more time, the parent path is now "/" url = parentURL; parentURL = url.getParent(); // Test path and filename assertPathEquals(getSchemePath("/"), parentURL); assert parentURL.getFilename() == null; // The parent URL should now be null // Test path and filename url = parentURL; assert url.getParent() == null; } /** * Parses URLs, some borderline but that we consider nonetheless valid, and ensures that they parse without error * and that getters return proper part values. * * @throws MalformedURLException should not happen */ @Test public void testParsing() throws MalformedURLException { // Test a sample URL with all parts used getURL("login", "password", "host", 10000, "/path/to", "query"); // Ensure that login and password parts can contain '@' characters without disrupting the parsing getURL("login@domain.com", "password@domain.com", "host", 10000, "/path/to", "query"); // Ensure that paths can contain '@' characters without disrupting the parsing getURL("login", "password", "host", 10000, "/path@at/to@at", "query"); // Ensure that empty port parts are tolerated getURL("login", "password", "host", -1, "/path@at/to@at", "query"); } /** * Ensure that non URL-safe characters in login and password parts are properly handled, both when parsing * and representing URLs as string. * * @throws MalformedURLException should not happen */ @Test public void testCredentialsURLEncoding() throws MalformedURLException { FileURL url = getRootURL(); String urlDecodedString = ":@&=+$,/?t%#[]"; String urlEncodedString = "%3A%40%26%3D%2B%24%2C%2F%3Ft%25%23%5B%5D"; url.setCredentials(new Credentials(urlDecodedString, urlDecodedString)); String urlRep = url.getScheme()+"://"+urlEncodedString+":"+urlEncodedString+"@"; assert urlRep.equals(url.toString(true, false)); url = FileURL.getFileURL(urlRep); Credentials credentials = url.getCredentials(); assert credentials.getLogin().equals(urlDecodedString); assert credentials.getPassword().equals(urlDecodedString); } /** * Tests FileURL's getters and setters. * * @throws MalformedURLException should not happen */ @Test public void testAccessors() throws MalformedURLException { FileURL url = getRootURL(); String scheme = getScheme(); Credentials credentials = new Credentials("login", "password"); String host = "host"; int port = 10000; String path = getSchemePath("/path/to"); String query = "query"; url.setScheme(scheme); url.setCredentials(credentials); url.setHost(host); url.setPort(port); url.setPath(path); url.setQuery(query); url.setProperty("name", "value"); assert scheme.equals(url.getScheme()); assert credentials.equals(url.getCredentials(), true); assert host.equals(url.getHost()); assert port == url.getPort(); assert path.equals(url.getPath()); assert query.equals(url.getQuery()); assert "to".equals(url.getFilename()); assert "value".equals(url.getProperty("name")); // Test null values url.setCredentials(null); url.setHost(null); url.setPort(-1); url.setPath("/"); url.setQuery(null); url.setProperty("name", null); assert scheme.equals(url.getScheme()); assert url.getCredentials() == null; assert !url.containsCredentials(); assert url.getHost() == null; assert -1 == url.getPort(); assert "/".equals(url.getPath()); assert url.getQuery() == null; assert url.getFilename() == null; assert url.getProperty("name") == null; assert (scheme+"://").equals(url.toString(true, false)); // Path cannot be null, the path is supposed to be "/" if a null or empty value is specified url.setPath(null); assert "/".equals(url.getPath()); url.setPath(""); assert "/".equals(url.getPath()); // Path must always start with a leading '/', if the specified does not then a '/' is automatically added url.setPath("path/to"); assert "/path/to".equals(url.getPath()); } /** * Tests {@link FileURL#getFilename()} method. * * @throws MalformedURLException should not happen */ @Test public void testFilename() throws MalformedURLException { assert "file".equals(getURL("/path/to/file").getFilename()); assert "file".equals(getURL("/path/to/file/").getFilename()); assert "path".equals(getURL("/path").getFilename()); assert "path".equals(getURL("/path/").getFilename()); assert getRootURL().getFilename() == null : "/"; assert (isQueryParsed()?"file":"file?param=value").equals(getURL(null, null, null, -1, "/path/to/file", "param=value").getFilename()); } /** * Tests FileURL's <code>toString</code> methods. * * @throws MalformedURLException should not happen */ @Test public void testStringRepresentation() throws MalformedURLException { FileURL url = getURL("login", "password", "host", 10000, "/path", "query"); String path = getSchemePath("/path"); String urlString = getScheme()+"://host:10000"+path+"?query"; assert urlString.equals(url.toString()); assert urlString.equals(url.toString(false)); assert urlString.equals(url.toString(false, false)); urlString = getScheme()+"://login:password@host:10000"+path+"?query"; assert urlString.equals(url.toString(true)); assert urlString.equals(url.toString(true, false)); urlString = getScheme()+"://login:********@host:10000"+path+"?query"; assert urlString.equals(url.toString(true, true)); } /** * Tests <code>equals</code> methods. * * @throws MalformedURLException should not happen */ @Test public void testEquals() throws MalformedURLException { // No query part, as it is not parsed by all schemes FileURL url1 = getURL("login", "password", "host", 10000, "/path", null); url1.setProperty("name", "value"); FileURL url2 = (FileURL)url1.clone(); // Assert that both URLs are equal assertEquals(url1, url2); assertEquals(url1, url2, true, true); // Add a trailing path separator to one of the URL's path and assert they are still equal url1.setPath(url1.getPath()+url1.getPathSeparator()); assertEquals(url1, url2); assertEquals(url1, url2, true, true); // Assert that having the port part set to the standart port is equivalent to not having a port part (-1) url1.setPort(url1.getStandardPort()); url2.setPort(-1); assertEquals(url1, url2); assertEquals(url1, url2, true, true); // Assert that the scheme comparison is case-insensitive url1.setScheme(url1.getScheme().toUpperCase()); assertEquals(url1, url2); assertEquals(url1, url2, true, true); // Assert that the host comparison is case-insensitive url1.setHost(url1.getHost().toUpperCase()); assertEquals(url1, url2); assertEquals(url1, url2, true, true); // Assert that the path comparison is case-sensitive url1.setPath(url1.getPath().toUpperCase()); assertNotEquals(url1, url2); assertNotEquals(url1, url2, true, true); // Make both URLs equal again url1.setPath(url2.getPath()); assertEquals(url1, url2); assertEquals(url1, url2, true, true); // Assert that the query comparison is case-sensitive url1.setQuery("query"); url2.setQuery("QUERY"); assertNotEquals(url1, url2); assertNotEquals(url1, url2, true, true); // Make both URLs equal again url1.setQuery(url2.getQuery()); assertEquals(url1, url2); assertEquals(url1, url2, true, true); // Assert that the credentials comparison is case-sensitive url1.setCredentials(new Credentials("LOGIN", "password")); assertEquals(url1, url2, false, false); assertNotEquals(url1, url2, true, true); url1.setCredentials(new Credentials("login", "PASSWORD")); assertEquals(url1, url2, false, false); assertNotEquals(url1, url2, true, true); // Assert that URLs are equal if credentials comparison is disabled assertEquals(url1, url2, false, true); // Make both URLs equal again url1.setCredentials(new Credentials("login", "password")); assertEquals(url1, url2, true, true); // Assert that the properties comparison is case-sensitive url1.setProperty("name", null); url1.setProperty("NAME", "value"); assertEquals(url1, url2, false, false); assertNotEquals(url1, url2, true, true); url1.setProperty("name", "VALUE"); assertEquals(url1, url2, false, false); assertNotEquals(url1, url2, true, true); // Assert that URLs are equal if properties comparison is disabled assertEquals(url1, url2, true, false); // Make both URLs equal again url1.setProperty("NAME", null); url1.setProperty("name", "value"); assertEquals(url1, url2, true, true); // Assert that the properties comparison fails if an extra property is added to one of the URLs url1.setProperty("name2", "value2"); assertNotEquals(url1, url2, true, true); // Assert that URLs are equal if properties comparison is disabled assertEquals(url1, url2, true, false); // Make both URLs equal again url1.setProperty("name2", null); assertEquals(url1, url2, true, true); } /** * Tests {@link FileURL#clone()}. * * @throws MalformedURLException should not happen */ @Test public void testClone() throws MalformedURLException { FileURL url = getURL("login", "password", "host", 10000, "/path/to", "query"); url.setProperty("name", "value"); FileURL clonedURL = (FileURL)url.clone(); // Assert that both instances are equal according to FileURL#equals assertEquals(url, clonedURL); // Assert that both URL's string representations (with credentials) are equal assert url.toString(true).equals(clonedURL.toString(true)); // Assert that both instances are not one and the same assert url!=clonedURL; // Assert that the property has survived the cloning assert "value".equals(clonedURL.getProperty("name")); } /** * Tests a few invalid URLs and makes sure {@link FileURL#getFileURL} throws a <code>MalformedURLException</code>. * * @throws MalformedURLException should not happen */ @Test public void testInvalidURLs() throws MalformedURLException { // relative URLs assert !canParse("relative"); assert !canParse("C:"); assert !canParse("scheme:/"); // Invalid port (non-numeric) assert !canParse(getScheme()+"://host:port/path"); } ////////////////////// // Abstract methods // ////////////////////// protected abstract String getScheme(); protected abstract int getDefaultPort(); protected abstract AuthenticationType getAuthenticationType(); protected abstract Credentials getGuestCredentials(); protected abstract String getPathSeparator(); protected abstract boolean isQueryParsed(); }