org.apache.taverna.robundle.TestBundles.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.taverna.robundle.TestBundles.java

Source

package org.apache.taverna.robundle;

/*
 * 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.
 */

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.nio.charset.Charset;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.io.IOUtils;
import org.apache.taverna.robundle.Bundle;
import org.apache.taverna.robundle.Bundles;
import org.apache.taverna.robundle.fs.BundleFileSystem;
import org.apache.taverna.robundle.fs.BundleFileSystemProvider;
import org.apache.taverna.robundle.manifest.Manifest;
import org.apache.taverna.robundle.manifest.PathMetadata;
import org.apache.taverna.robundle.utils.TemporaryFiles;
import org.junit.Test;
import org.junit.Ignore;

public class TestBundles {

    private static final int MIME_OFFSET = 30;

    protected void checkSignature(Path zip) throws IOException {
        String MEDIATYPE = "application/vnd.wf4ever.robundle+zip";
        // Check position 30++ according to RO Bundle specification
        // http://purl.org/wf4ever/ro-bundle#ucf
        byte[] expected = ("mimetype" + MEDIATYPE + "PK").getBytes("ASCII");

        try (InputStream in = Files.newInputStream(zip)) {
            byte[] signature = new byte[expected.length];
            int MIME_OFFSET = 30;
            assertEquals(MIME_OFFSET, in.skip(MIME_OFFSET));
            assertEquals(expected.length, in.read(signature));
            assertArrayEquals(expected, signature);
        }
    }

    @Test
    public void closeDeleteTemp() throws Exception {
        Bundle bundle = Bundles.createBundle();
        assertTrue(Files.exists(bundle.getSource()));
        assertTrue(bundle.getFileSystem().isOpen());
        assertTrue(bundle.isDeleteOnClose());
        bundle.close();
        assertFalse(Files.exists(bundle.getSource()));
        assertFalse(bundle.getFileSystem().isOpen());
    }

    @Test
    public void closeNotDelete() throws Exception {
        Path path = Files.createTempFile("bundle", ".zip");
        Bundle bundle = Bundles.createBundle(path);
        assertFalse(bundle.isDeleteOnClose());
        assertTrue(Files.exists(bundle.getSource()));
        assertTrue(bundle.getFileSystem().isOpen());

        bundle.close();
        assertTrue(Files.exists(bundle.getSource()));
        assertFalse(bundle.getFileSystem().isOpen());
    }

    @Test
    public void closeAndOpenBundle() throws Exception {
        Bundle bundle = Bundles.createBundle();
        Path zip = Bundles.closeBundle(bundle);
        Bundles.openBundle(zip).close();
    }

    @Test
    public void closeAndOpenBundleWithStringValue() throws Exception {
        Bundle bundle = Bundles.createBundle();
        Path hello = bundle.getRoot().resolve("hello.txt");
        Bundles.setStringValue(hello, "Hello");
        Path zip = Bundles.closeBundle(bundle);

        try (Bundle newBundle = Bundles.openBundle(zip)) {
            Path newHello = newBundle.getRoot().resolve("hello.txt");
            assertEquals("Hello", Bundles.getStringValue(newHello));
        }
    }

    @Test
    public void closeAndSaveBundleDelete() throws Exception {
        Bundle bundle = Bundles.createBundle();
        Path destination = Files.createTempFile("test", ".zip");
        destination.toFile().deleteOnExit();
        Files.delete(destination);
        assertFalse(Files.exists(destination));
        Bundles.closeAndSaveBundle(bundle, destination);
        assertTrue(Files.exists(destination));
        assertFalse(Files.exists(bundle.getSource()));
    }

    @Test
    public void closeAndSaveBundleNotDelete() throws Exception {
        Path path = Files.createTempFile("bundle", ".zip");
        Bundle bundle = Bundles.createBundle(path);
        Path destination = Files.createTempFile("test", ".zip");
        destination.toFile().deleteOnExit();
        Files.delete(destination);
        assertFalse(Files.exists(destination));
        Bundles.closeAndSaveBundle(bundle, destination);
        assertTrue(Files.exists(destination));
        assertTrue(Files.exists(bundle.getSource()));
    }

    @Test
    public void closeBundle() throws Exception {
        Bundle bundle = Bundles.createBundle();
        Path zip = Bundles.closeBundle(bundle);
        assertTrue(Files.isReadable(zip));
        assertEquals(zip, bundle.getSource());
        checkSignature(zip);
    }

    @Ignore("Broken in OpenJDK8 zipfs")
    @Test
    public void mimeTypePosition() throws Exception {
        Bundle bundle = Bundles.createBundle();
        String mimetype = "application/x-test";
        Bundles.setMimeType(bundle, mimetype);
        assertEquals(mimetype, Bundles.getMimeType(bundle));
        Path zip = Bundles.closeBundle(bundle);

        assertTrue(Files.exists(zip));
        try (ZipFile zipFile = new ZipFile(zip.toFile())) {
            // Must be first entry
            ZipEntry mimeEntry = zipFile.entries().nextElement();
            assertEquals("First zip entry is not 'mimetype'", "mimetype", mimeEntry.getName());
            assertEquals("mimetype should be uncompressed, but compressed size mismatch",
                    mimeEntry.getCompressedSize(), mimeEntry.getSize());
            assertEquals("mimetype should have STORED method", ZipEntry.STORED, mimeEntry.getMethod());
            assertEquals("Wrong mimetype", mimetype, IOUtils.toString(zipFile.getInputStream(mimeEntry), "ASCII"));
        }

        // Check position 30++ according to
        // http://livedocs.adobe.com/navigator/9/Navigator_SDK9_HTMLHelp/wwhelp/wwhimpl/common/html/wwhelp.htm?context=Navigator_SDK9_HTMLHelp&file=Appx_Packaging.6.1.html#1522568
        byte[] expected = ("mimetype" + mimetype + "PK").getBytes("ASCII");
        FileInputStream in = new FileInputStream(zip.toFile());
        byte[] actual = new byte[expected.length];

        try {

            assertEquals(MIME_OFFSET, in.skip(MIME_OFFSET));
            assertEquals(expected.length, in.read(actual));
        } finally {
            in.close();
        }
        assertArrayEquals(expected, actual);
    }

    @Test
    public void createBundle() throws Exception {
        Path source = null;
        try (Bundle bundle = Bundles.createBundle()) {
            assertTrue(Files.isDirectory(bundle.getRoot()));
            source = bundle.getSource();
            assertTrue(Files.exists(source));
        }
        // As it was temporary file it should be deleted on close
        assertFalse(Files.exists(source));
    }

    @Test
    public void createBundlePath() throws Exception {
        Path source = Files.createTempFile("test", ".zip");
        source.toFile().deleteOnExit();
        Files.delete(source);
        try (Bundle bundle = Bundles.createBundle(source)) {
            assertTrue(Files.isDirectory(bundle.getRoot()));
            assertEquals(source, bundle.getSource());
            assertTrue(Files.exists(source));
        }
        // As it was a specific path, it should NOT be deleted on close
        assertTrue(Files.exists(source));
    }

    @Test
    public void createBundlePathExists() throws Exception {
        Path source = Files.createTempFile("test", ".zip");
        source.toFile().deleteOnExit();

        assertTrue(Files.exists(source)); // will be overwritten
        try (Bundle bundle = Bundles.createBundle(source)) {
        }
        // As it was a specific path, it should NOT be deleted on close
        assertTrue(Files.exists(source));
    }

    @Test(expected = IOException.class)
    public void createBundleExistsAsDirFails() throws Exception {
        Path source = Files.createTempDirectory("test");
        source.toFile().deleteOnExit();
        try (Bundle bundle = Bundles.createBundle(source)) {
        }
    }

    @Test
    public void getMimeType() throws Exception {
        Path bundlePath = TemporaryFiles.temporaryBundle();
        try (BundleFileSystem bundleFs = BundleFileSystemProvider.newFileSystemFromNew(bundlePath,
                "application/x-test")) {
            Bundle bundle = new Bundle(bundleFs.getPath("/"), false);
            assertEquals("application/x-test", Bundles.getMimeType(bundle));
        }
    }

    @Test
    public void setMimeType() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Path mimetypePath = bundle.getRoot().resolve("mimetype");
            assertEquals("application/vnd.wf4ever.robundle+zip", Bundles.getStringValue(mimetypePath));

            Bundles.setMimeType(bundle, "application/x-test");
            assertEquals("application/x-test", Bundles.getStringValue(mimetypePath));
        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void setMimeTypeNoNewlines() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Bundles.setMimeType(bundle, "application/x-test\nNo newlines allowed");
        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void setMimeTypeNoSlash() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Bundles.setMimeType(bundle, "test");
        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void setMimeTypeEmpty() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Bundles.setMimeType(bundle, "");
        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void setMimeTypeNonAscii() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Bundles.setMimeType(bundle, "application/x-test-\u00E9");
            // Include the e-acute-accent from latin1
        }
    }

    @Test
    public void getMimeTypeMissing() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Path mimetypePath = bundle.getRoot().resolve("mimetype");
            Files.delete(mimetypePath);
            // Fall back according to our spec
            assertEquals("application/vnd.wf4ever.robundle+zip", Bundles.getMimeType(bundle));
        }
    }

    @Test(expected = IOException.class)
    public void setMimeTypeMissing() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Path mimetypePath = bundle.getRoot().resolve("mimetype");
            Files.delete(mimetypePath);
            // sadly now we can't set it (the mimetype file must be uncompressed
            // and at beginning of file,
            // which we don't have the possibility to do now that file system is
            // open)
            Bundles.setMimeType(bundle, "application/x-test");
        }
    }

    @Test
    public void getReference() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Path hello = bundle.getRoot().resolve("hello");
            Bundles.setReference(hello, URI.create("http://example.org/test"));
            URI uri = Bundles.getReference(hello);
            assertEquals("http://example.org/test", uri.toASCIIString());
        }
    }

    @Test
    public void getReferenceFromWin8() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Path win8 = bundle.getRoot().resolve("win8");
            Path win8Url = bundle.getRoot().resolve("win8.url");
            Files.copy(getClass().getResourceAsStream("/win8.url"), win8Url);

            URI uri = Bundles.getReference(win8);
            assertEquals("http://example.com/made-in-windows-8", uri.toASCIIString());
        }
    }

    @Test
    public void getStringValue() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Path hello = bundle.getRoot().resolve("hello");
            String string = "A string";
            Bundles.setStringValue(hello, string);
            assertEquals(string, Bundles.getStringValue(hello));
            assertEquals(null, Bundles.getStringValue(null));
        }
    }

    protected boolean isEmpty(Path path) throws IOException {
        try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {
            return !ds.iterator().hasNext();
        }
    }

    @Test
    public void isMissing() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Path missing = bundle.getRoot().resolve("missing");
            assertFalse(Bundles.isValue(missing));
            assertTrue(Bundles.isMissing(missing));
            assertFalse(Bundles.isReference(missing));
        }
    }

    @Test
    public void isReference() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Path ref = bundle.getRoot().resolve("ref");
            Bundles.setReference(ref, URI.create("http://example.org/test"));
            assertTrue(Bundles.isReference(ref));
            assertFalse(Bundles.isMissing(ref));
            assertFalse(Bundles.isValue(ref));
        }
    }

    @Test
    public void isValue() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {

            Path hello = bundle.getRoot().resolve("hello");
            Bundles.setStringValue(hello, "Hello");
            assertTrue(Bundles.isValue(hello));
            assertFalse(Bundles.isReference(hello));
        }
    }

    protected List<String> ls(Path path) throws IOException {
        List<String> paths = new ArrayList<>();
        try (DirectoryStream<Path> ds = Files.newDirectoryStream(path)) {
            for (Path p : ds) {
                paths.add(p.getFileName() + "");
            }
        }
        Collections.sort(paths);
        return paths;
    }

    @Test
    public void safeMove() throws Exception {
        Path tmp = Files.createTempDirectory("test");
        tmp.toFile().deleteOnExit();
        Path f1 = tmp.resolve("f1");
        f1.toFile().deleteOnExit();
        Files.createFile(f1);
        assertFalse(isEmpty(tmp));

        try (Bundle db = Bundles.createBundle()) {
            Path f2 = db.getRoot().resolve("f2");
            Bundles.safeMove(f1, f2);
            assertFalse(Files.exists(f1));
            assertTrue(isEmpty(tmp));
            assertEquals(Arrays.asList("f2", "mimetype"), ls(db.getRoot()));
        }

    }

    private void checkWorkflowrunBundle(Bundle b) throws IOException {
        Path path = b.getRoot().resolve("/workflowrun.prov.ttl");
        assertTrue(Files.exists(path));
        // Ensure manifest was read
        Manifest manifest = b.getManifest();
        PathMetadata aggregation = manifest.getAggregation(path);
        assertNotNull(aggregation);
        @SuppressWarnings("deprecation")
        URI proxy = aggregation.getProxy();
        if (proxy != null)// FIXME proxy is normally null here? WHY?
            assertEquals(URI.create("urn:uuid:ac1c89cc-3ba2-462d-bd82-ab5b8297f98e"), proxy);
    }

    @Test
    public void openBundleInputStream() throws Exception {
        try (InputStream stream = getClass().getResourceAsStream("/workflowrun.bundle.zip")) {
            assertNotNull(stream);
            try (Bundle b = Bundles.openBundle(stream)) {
                checkWorkflowrunBundle(b);
            }
        }
    }

    @Test
    public void openBundleURL() throws Exception {
        URL url = getClass().getResource("/workflowrun.bundle.zip");
        assertNotNull(url);
        try (Bundle b = Bundles.openBundle(url)) {
            checkWorkflowrunBundle(b);
        }
    }

    @Test
    public void openBundleURLNonFile() throws Exception {
        final URL url = getClass().getResource("/workflowrun.bundle.zip");
        assertNotNull(url);
        URLStreamHandler handler = new URLStreamHandler() {
            @Override
            protected URLConnection openConnection(URL u) throws IOException {
                return url.openConnection();
            }
        };
        URL testUrl = new URL("test", "test", 0, "test", handler);
        try (Bundle b = Bundles.openBundle(testUrl)) {
            checkWorkflowrunBundle(b);
        }
    }

    @Test
    public void openBundleReadOnly() throws Exception {
        Path untouched = Files.createTempFile("test-openBundleReadOnly", ".zip");
        try (Bundle bundle = Bundles.createBundle(untouched)) {
            Bundles.setStringValue(bundle.getRoot().resolve("file.txt"), "Untouched");
        }
        try (Bundle readOnly = Bundles.openBundleReadOnly(untouched)) {
            Path file = readOnly.getRoot().resolve("file.txt");
            // You can change the open file system
            Bundles.setStringValue(file, "Modified");
            assertEquals("Modified", Bundles.getStringValue(file));
            // and even make new resources
            Path newFile = readOnly.getRoot().resolve("newfile.txt");
            Files.createFile(newFile);
            assertTrue(Files.exists(newFile));

        }
        try (Bundle readOnly = Bundles.openBundleReadOnly(untouched)) {
            // But that is not persisted in the zip
            Path file = readOnly.getRoot().resolve("file.txt");
            assertEquals("Untouched", Bundles.getStringValue(file));
            Path newfile = readOnly.getRoot().resolve("newfile.txt");
            assertFalse(Files.exists(newfile));
        }
    }

    @Test
    public void safeCopy() throws Exception {
        Path tmp = Files.createTempDirectory("test");
        tmp.toFile().deleteOnExit();
        Path f1 = tmp.resolve("f1");
        f1.toFile().deleteOnExit();
        Files.createFile(f1);
        assertFalse(isEmpty(tmp));

        try (Bundle db = Bundles.createBundle()) {
            Path f2 = db.getRoot().resolve("f2");
            Bundles.safeCopy(f1, f2);
            assertTrue(Files.exists(f1));
            assertTrue(Files.exists(f2));
            assertEquals(Arrays.asList("f2", "mimetype"), ls(db.getRoot()));
        }

    }

    @Test(expected = DirectoryNotEmptyException.class)
    public void safeCopyFails() throws Exception {
        Path tmp = Files.createTempDirectory("test");
        tmp.toFile().deleteOnExit();
        Path f1 = tmp.resolve("f1");
        f1.toFile().deleteOnExit();
        Path d1 = tmp.resolve("d1");
        d1.toFile().deleteOnExit();
        Files.createFile(f1);

        // Make d1 difficult to overwrite
        Files.createDirectory(d1);
        Files.createFile(d1.resolve("child"));

        try {
            // Files.copy(f1, d1, StandardCopyOption.REPLACE_EXISTING);
            Bundles.safeCopy(f1, d1);
        } finally {
            assertEquals(Arrays.asList("d1", "f1"), ls(tmp));
            assertTrue(Files.exists(f1));
            assertTrue(Files.isDirectory(d1));
        }
    }

    @Test(expected = IOException.class)
    public void safeMoveFails() throws Exception {
        Path tmp = Files.createTempDirectory("test");
        tmp.toFile().deleteOnExit();
        Path f1 = tmp.resolve("f1");
        f1.toFile().deleteOnExit();
        Path d1 = tmp.resolve("d1");
        d1.toFile().deleteOnExit();
        Files.createFile(f1);

        // Make d1 difficult to overwrite
        Files.createDirectory(d1);
        Files.createFile(d1.resolve("child"));

        try {
            Bundles.safeMove(f1, d1);
        } finally {
            assertTrue(Files.exists(f1));
            assertEquals(Arrays.asList("d1", "f1"), ls(tmp));
        }
    }

    @Test
    public void setReference() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {

            Path ref = bundle.getRoot().resolve("ref");
            Bundles.setReference(ref, URI.create("http://example.org/test"));

            URI uri = URI.create("http://example.org/test");
            Path f = Bundles.setReference(ref, uri);
            assertEquals("ref.url", f.getFileName().toString());
            assertEquals(bundle.getRoot(), f.getParent());
            assertFalse(Files.exists(ref));

            List<String> uriLines = Files.readAllLines(f, Charset.forName("ASCII"));
            assertEquals(3, uriLines.size());
            assertEquals("[InternetShortcut]", uriLines.get(0));
            assertEquals("URL=http://example.org/test", uriLines.get(1));
            assertEquals("", uriLines.get(2));
        }
    }

    @Test
    public void setReferenceIri() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Path ref = bundle.getRoot().resolve("ref");
            URI uri = new URI("http", "xn--bcher-kva.example.com", "/s\u00F8iland/\u2603snowman", "\u2605star");
            Path f = Bundles.setReference(ref, uri);
            List<String> uriLines = Files.readAllLines(f, Charset.forName("ASCII"));
            // TODO: Double-check that this is actually correct escaping :)
            assertEquals("URL=http://xn--bcher-kva.example.com/s%C3%B8iland/%E2%98%83snowman#%E2%98%85star",
                    uriLines.get(1));
        }
    }

    @Test
    public void setStringValue() throws Exception {
        try (Bundle bundle = Bundles.createBundle()) {
            Path file = bundle.getRoot().resolve("file");
            String string = "A string";
            Bundles.setStringValue(file, string);
            assertEquals(string, Files.readAllLines(file, Charset.forName("UTF-8")).get(0));
        }
    }

    @Test
    public void withExtension() throws Exception {
        Path testDir = Files.createTempDirectory("test");
        testDir.toFile().deleteOnExit();
        Path fileTxt = testDir.resolve("file.txt");
        fileTxt.toFile().deleteOnExit();
        assertEquals("file.txt", fileTxt.getFileName().toString()); // better
        // be!

        Path fileHtml = Bundles.withExtension(fileTxt, ".html");
        assertEquals(fileTxt.getParent(), fileHtml.getParent());
        assertEquals("file.html", fileHtml.getFileName().toString());

        Path fileDot = Bundles.withExtension(fileTxt, ".");
        assertEquals("file.", fileDot.getFileName().toString());

        Path fileEmpty = Bundles.withExtension(fileTxt, "");
        assertEquals("file", fileEmpty.getFileName().toString());

        Path fileDoc = Bundles.withExtension(fileEmpty, ".doc");
        assertEquals("file.doc", fileDoc.getFileName().toString());

        Path fileManyPdf = Bundles.withExtension(fileTxt, ".test.many.pdf");
        assertEquals("file.test.many.pdf", fileManyPdf.getFileName().toString());

        Path fileManyTxt = Bundles.withExtension(fileManyPdf, ".txt");
        assertEquals("file.test.many.txt", fileManyTxt.getFileName().toString());
    }

}