| /* |
| * 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.swift; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.FSDataOutputStream; |
| import org.apache.hadoop.fs.FileStatus; |
| import org.apache.hadoop.fs.Path; |
| import org.apache.hadoop.fs.swift.exceptions.SwiftOperationFailedException; |
| import org.apache.hadoop.fs.swift.snative.SwiftNativeFileSystem; |
| import org.apache.hadoop.fs.swift.snative.SwiftNativeFileSystemStore; |
| import org.apache.hadoop.fs.swift.util.DurationStats; |
| import org.apache.hadoop.fs.swift.util.SwiftTestUtils; |
| import org.junit.After; |
| import org.junit.AfterClass; |
| import org.junit.Assert; |
| import org.junit.Assume; |
| import org.junit.Before; |
| |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.List; |
| |
| import static org.apache.hadoop.fs.swift.util.SwiftTestUtils.assertPathExists; |
| import static org.apache.hadoop.fs.swift.util.SwiftTestUtils.cleanupInTeardown; |
| import static org.apache.hadoop.fs.swift.util.SwiftTestUtils.getServiceURI; |
| import static org.apache.hadoop.fs.swift.util.SwiftTestUtils.noteAction; |
| |
| /** |
| * This is the base class for most of the Swift tests |
| */ |
| public class SwiftFileSystemBaseTest extends Assert implements |
| SwiftTestConstants { |
| |
| protected static final Log LOG = |
| LogFactory.getLog(SwiftFileSystemBaseTest.class); |
| protected SwiftNativeFileSystem fs; |
| protected static SwiftNativeFileSystem lastFs; |
| protected byte[] data = SwiftTestUtils.dataset(getBlockSize() * 2, 0, 255); |
| private Configuration conf; |
| |
| @Before |
| public void setUp() throws Exception { |
| noteAction("setup"); |
| final URI uri = getFilesystemURI(); |
| conf = createConfiguration(); |
| |
| fs = createSwiftFS(); |
| try { |
| fs.initialize(uri, conf); |
| } catch (IOException e) { |
| //FS init failed, set it to null so that teardown doesn't |
| //attempt to use it |
| fs = null; |
| throw e; |
| } |
| //remember the last FS |
| lastFs = fs; |
| noteAction("setup complete"); |
| } |
| |
| /** |
| * Configuration generator. May be overridden to inject |
| * some custom options |
| * @return a configuration with which to create FS instances |
| */ |
| protected Configuration createConfiguration() { |
| return new Configuration(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| cleanupInTeardown(fs, "/test"); |
| } |
| |
| @AfterClass |
| public static void classTearDown() throws Exception { |
| if (lastFs != null) { |
| List<DurationStats> statistics = lastFs.getOperationStatistics(); |
| for (DurationStats stat : statistics) { |
| LOG.info(stat.toString()); |
| } |
| } |
| } |
| |
| /** |
| * Get the configuration used to set up the FS |
| * @return the configuration |
| */ |
| public Configuration getConf() { |
| return conf; |
| } |
| |
| /** |
| * Describe the test, combining some logging with details |
| * for people reading the code |
| * |
| * @param description test description |
| */ |
| protected void describe(String description) { |
| noteAction(description); |
| } |
| |
| protected URI getFilesystemURI() throws URISyntaxException, IOException { |
| return getServiceURI(createConfiguration()); |
| } |
| |
| protected SwiftNativeFileSystem createSwiftFS() throws IOException { |
| SwiftNativeFileSystem swiftNativeFileSystem = |
| new SwiftNativeFileSystem(); |
| return swiftNativeFileSystem; |
| } |
| |
| protected int getBlockSize() { |
| return 1024; |
| } |
| |
| /** |
| * Is rename supported? |
| * @return true |
| */ |
| protected boolean renameSupported() { |
| return true; |
| } |
| |
| /** |
| * assume in a test that rename is supported; |
| * skip it if not |
| */ |
| protected void assumeRenameSupported() { |
| Assume.assumeTrue(renameSupported()); |
| } |
| |
| /** |
| * Take an unqualified path, and qualify it w.r.t the |
| * current filesystem |
| * @param pathString source path |
| * @return a qualified path instance |
| */ |
| protected Path path(String pathString) { |
| return new Path(pathString).makeQualified(fs); |
| } |
| |
| /** |
| * Get the filesystem |
| * @return the current FS |
| */ |
| public SwiftNativeFileSystem getFs() { |
| return fs; |
| } |
| |
| /** |
| * Create a file using the standard {@link #data} bytes. |
| * |
| * @param path path to write |
| * @throws IOException on any problem |
| */ |
| protected void createFile(Path path) throws IOException { |
| createFile(path, data); |
| } |
| |
| /** |
| * Create a file with the given data. |
| * |
| * @param path path to write |
| * @param sourceData source dataset |
| * @throws IOException on any problem |
| */ |
| protected void createFile(Path path, byte[] sourceData) throws IOException { |
| FSDataOutputStream out = fs.create(path); |
| out.write(sourceData, 0, sourceData.length); |
| out.close(); |
| } |
| |
| /** |
| * Create and then close a file |
| * @param path path to create |
| * @throws IOException on a failure |
| */ |
| protected void createEmptyFile(Path path) throws IOException { |
| FSDataOutputStream out = fs.create(path); |
| out.close(); |
| } |
| |
| /** |
| * Get the inner store -useful for lower level operations |
| * |
| * @return the store |
| */ |
| protected SwiftNativeFileSystemStore getStore() { |
| return fs.getStore(); |
| } |
| |
| /** |
| * Rename a path |
| * @param src source |
| * @param dst dest |
| * @param renameMustSucceed flag to say "this rename must exist" |
| * @param srcExists add assert that the source exists afterwards |
| * @param dstExists add assert the dest exists afterwards |
| * @throws IOException IO trouble |
| */ |
| protected void rename(Path src, Path dst, boolean renameMustSucceed, |
| boolean srcExists, boolean dstExists) throws IOException { |
| if (renameMustSucceed) { |
| renameToSuccess(src, dst, srcExists, dstExists); |
| } else { |
| renameToFailure(src, dst); |
| } |
| } |
| |
| /** |
| * Get a string describing the outcome of a rename, by listing the dest |
| * path and its parent along with some covering text |
| * @param src source patj |
| * @param dst dest path |
| * @return a string for logs and exceptions |
| * @throws IOException IO problems |
| */ |
| private String getRenameOutcome(Path src, Path dst) throws IOException { |
| String lsDst = ls(dst); |
| Path parent = dst.getParent(); |
| String lsParent = parent != null ? ls(parent) : ""; |
| return " result of " + src + " => " + dst |
| + " - " + lsDst |
| + " \n" + lsParent; |
| } |
| |
| /** |
| * Rename, expecting an exception to be thrown |
| * |
| * @param src source |
| * @param dst dest |
| * @throws IOException a failure other than an |
| * expected SwiftRenameException or FileNotFoundException |
| */ |
| protected void renameToFailure(Path src, Path dst) throws IOException { |
| try { |
| getStore().rename(src, dst); |
| fail("Expected failure renaming " + src + " to " + dst |
| + "- but got success"); |
| } catch (SwiftOperationFailedException e) { |
| LOG.debug("Rename failed (expected):" + e); |
| } catch (FileNotFoundException e) { |
| LOG.debug("Rename failed (expected):" + e); |
| } |
| } |
| |
| /** |
| * Rename to success |
| * |
| * @param src source |
| * @param dst dest |
| * @param srcExists add assert that the source exists afterwards |
| * @param dstExists add assert the dest exists afterwards |
| * @throws SwiftOperationFailedException operation failure |
| * @throws IOException IO problems |
| */ |
| protected void renameToSuccess(Path src, Path dst, |
| boolean srcExists, boolean dstExists) |
| throws SwiftOperationFailedException, IOException { |
| getStore().rename(src, dst); |
| String outcome = getRenameOutcome(src, dst); |
| assertEquals("Source " + src + "exists: " + outcome, |
| srcExists, fs.exists(src)); |
| assertEquals("Destination " + dstExists + " exists" + outcome, |
| dstExists, fs.exists(dst)); |
| } |
| |
| /** |
| * 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 SwiftTestUtils.ls(fs, path); |
| } |
| |
| /** |
| * assert that a path exists |
| * @param message message to use in an assertion |
| * @param path path to probe |
| * @throws IOException IO problems |
| */ |
| public void assertExists(String message, Path path) throws IOException { |
| assertPathExists(fs, 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 { |
| SwiftTestUtils.assertPathDoesNotExist(fs, 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 { |
| SwiftTestUtils.assertIsFile(fs, filename); |
| } |
| |
| /** |
| * 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, fs.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 { |
| SwiftTestUtils.assertDeleted(fs, path, recursive); |
| } |
| |
| /** |
| * Assert that a value is not equal to the expected value |
| * @param message message if the two values are equal |
| * @param expected expected value |
| * @param actual actual value |
| */ |
| protected void assertNotEqual(String message, int expected, int actual) { |
| assertTrue(message, |
| actual != expected); |
| } |
| |
| /** |
| * Get the number of partitions written from the Swift Native FS APIs |
| * @param out output stream |
| * @return the number of partitioned files written by the stream |
| */ |
| protected int getPartitionsWritten(FSDataOutputStream out) { |
| return SwiftNativeFileSystem.getPartitionsWritten(out); |
| } |
| |
| /** |
| * Assert that the no. of partitions written matches expectations |
| * @param action operation (for use in the assertions) |
| * @param out output stream |
| * @param expected expected no. of partitions |
| */ |
| protected void assertPartitionsWritten(String action, FSDataOutputStream out, |
| long expected) { |
| OutputStream nativeStream = out.getWrappedStream(); |
| int written = getPartitionsWritten(out); |
| if(written !=expected) { |
| Assert.fail(action + ": " + |
| TestSwiftFileSystemPartitionedUploads.WRONG_PARTITION_COUNT |
| + " + expected: " + expected + " actual: " + written |
| + " -- " + nativeStream); |
| } |
| } |
| |
| /** |
| * 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); |
| } |
| } |