/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.hadoop.fs.contract; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.internal.AssumptionViolatedException; import org.junit.rules.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.URI; import static org.apache.hadoop.fs.contract.ContractTestUtils.cleanup; import static org.apache.hadoop.fs.contract.ContractTestUtils.skip; /** * This is the base class for all the contract tests */ public abstract class AbstractFSContractTestBase extends Assert implements ContractOptions { private static final Logger LOG = LoggerFactory.getLogger(AbstractFSContractTestBase.class); /** * Length of files to work with: {@value} */ public static final int TEST_FILE_LEN = 1024; /** * standard test timeout: {@value} */ public static final int DEFAULT_TEST_TIMEOUT = 180 * 1000; /** * The FS contract used for these tets */ private AbstractFSContract contract; /** * The test filesystem extracted from it */ private FileSystem fileSystem; /** * The path for tests */ private Path testPath; /** * This must be implemented by all instantiated test cases * -provide the FS contract * @return the FS contract */ protected abstract AbstractFSContract createContract(Configuration conf); /** * Get the contract * @return the contract, which will be non-null once the setup operation has * succeeded */ protected AbstractFSContract getContract() { return contract; } /** * Get the filesystem created in startup * @return the filesystem to use for tests */ public FileSystem getFileSystem() { return fileSystem; } /** * Get the log of the base class * @return a logger */ public static Logger getLog() { return LOG; } /** * Skip a test if a feature is unsupported in this FS * @param feature feature to look for * @throws IOException IO problem */ protected void skipIfUnsupported(String feature) throws IOException { if (!isSupported(feature)) { skip("Skipping as unsupported feature: " + feature); } } /** * Is a feature supported? * @param feature feature * @return true iff the feature is supported * @throws IOException IO problems */ protected boolean isSupported(String feature) throws IOException { return contract.isSupported(feature, false); } /** * Include at the start of tests to skip them if the FS is not enabled. */ protected void assumeEnabled() { if (!contract.isEnabled()) throw new AssumptionViolatedException("test cases disabled for " + contract); } /** * Create a configuration. May be overridden by tests/instantiations * @return a configuration */ protected Configuration createConfiguration() { return new Configuration(); } /** * Set the timeout for every test */ @Rule public Timeout testTimeout = new Timeout(getTestTimeoutMillis()); /** * Option for tests to override the default timeout value * @return the current test timeout */ protected int getTestTimeoutMillis() { return DEFAULT_TEST_TIMEOUT; } /** * Setup: create the contract then init it * @throws Exception on any failure */ @Before public void setup() throws Exception { contract = createContract(createConfiguration()); contract.init(); //skip tests if they aren't enabled assumeEnabled(); //extract the test FS fileSystem = contract.getTestFileSystem(); assertNotNull("null filesystem", fileSystem); URI fsURI = fileSystem.getUri(); LOG.info("Test filesystem = {} implemented by {}", fsURI, fileSystem); //sanity check to make sure that the test FS picked up really matches //the scheme chosen. This is to avoid defaulting back to the localFS //which would be drastic for root FS tests assertEquals("wrong filesystem of " + fsURI, contract.getScheme(), fsURI.getScheme()); //create the test path testPath = getContract().getTestPath(); mkdirs(testPath); } /** * Teardown * @throws Exception on any failure */ @After public void teardown() throws Exception { deleteTestDirInTeardown(); } /** * Delete the test dir in the per-test teardown * @throws IOException */ protected void deleteTestDirInTeardown() throws IOException { cleanup("TEARDOWN", getFileSystem(), testPath); } /** * Create a path under the test path provided by * the FS contract * @param filepath path string in * @return a path qualified by the test filesystem * @throws IOException IO problems */ protected Path path(String filepath) throws IOException { return getFileSystem().makeQualified( new Path(getContract().getTestPath(), filepath)); } /** * Take a simple path like "/something" and turn it into * a qualified path against the test FS * @param filepath path string in * @return a path qualified by the test filesystem * @throws IOException IO problems */ protected Path absolutepath(String filepath) throws IOException { return getFileSystem().makeQualified(new Path(filepath)); } /** * List a path in the test FS * @param path path to list * @return the contents of the path/dir * @throws IOException IO problems */ protected String ls(Path path) throws IOException { return ContractTestUtils.ls(fileSystem, path); } /** * Describe a test. This is a replacement for javadocs * where the tests role is printed in the log output * @param text description */ protected void describe(String text) { LOG.info(text); } /** * Handle the outcome of an operation not being the strictest * exception desired, but one that, while still within the boundary * of the contract, is a bit looser. * * If the FS contract says that they support the strictest exceptions, * that is what they must return, and the exception here is rethrown * @param action Action * @param expectedException what was expected * @param e exception that was received */ protected void handleRelaxedException(String action, String expectedException, Exception e) throws Exception { if (getContract().isSupported(SUPPORTS_STRICT_EXCEPTIONS, false)) { throw e; } LOG.warn("The expected exception {} was not the exception class" + " raised on {}: {}", action , e.getClass(), expectedException, e); } /** * Handle expected exceptions through logging and/or other actions * @param e exception raised. */ protected void handleExpectedException(Exception e) { getLog().debug("expected :{}" ,e, e); } /** * assert that a path exists * @param message message to use in an assertion * @param path path to probe * @throws IOException IO problems */ public void assertPathExists(String message, Path path) throws IOException { ContractTestUtils.assertPathExists(fileSystem, message, path); } /** * assert that a path does not * @param message message to use in an assertion * @param path path to probe * @throws IOException IO problems */ public void assertPathDoesNotExist(String message, Path path) throws IOException { ContractTestUtils.assertPathDoesNotExist(fileSystem, message, path); } /** * Assert that a file exists and whose {@link FileStatus} entry * declares that this is a file and not a symlink or directory. * * @param filename name of the file * @throws IOException IO problems during file operations */ protected void assertIsFile(Path filename) throws IOException { ContractTestUtils.assertIsFile(fileSystem, filename); } /** * Assert that a file exists and whose {@link FileStatus} entry * declares that this is a file and not a symlink or directory. * * @param path name of the file * @throws IOException IO problems during file operations */ protected void assertIsDirectory(Path path) throws IOException { ContractTestUtils.assertIsDirectory(fileSystem, path); } /** * Assert that a file exists and whose {@link FileStatus} entry * declares that this is a file and not a symlink or directory. * * @throws IOException IO problems during file operations */ protected void mkdirs(Path path) throws IOException { assertTrue("Failed to mkdir " + path, fileSystem.mkdirs(path)); } /** * Assert that a delete succeeded * @param path path to delete * @param recursive recursive flag * @throws IOException IO problems */ protected void assertDeleted(Path path, boolean recursive) throws IOException { ContractTestUtils.assertDeleted(fileSystem, path, recursive); } /** * Assert that the result value == -1; which implies * that a read was successful * @param text text to include in a message (usually the operation) * @param result read result to validate */ protected void assertMinusOne(String text, int result) { assertEquals(text + " wrong read result " + result, -1, result); } boolean rename(Path src, Path dst) throws IOException { return getFileSystem().rename(src, dst); } protected String generateAndLogErrorListing(Path src, Path dst) throws IOException { FileSystem fs = getFileSystem(); getLog().error( "src dir " + ContractTestUtils.ls(fs, src.getParent())); String destDirLS = ContractTestUtils.ls(fs, dst.getParent()); if (fs.isDirectory(dst)) { //include the dir into the listing destDirLS = destDirLS + "\n" + ContractTestUtils.ls(fs, dst); } return destDirLS; } }