org.cryptomator.cryptofs.CryptoDirectoryStreamTest.java Source code

Java tutorial

Introduction

Here is the source code for org.cryptomator.cryptofs.CryptoDirectoryStreamTest.java

Source

/*******************************************************************************
 * Copyright (c) 2016 Sebastian Stenzel and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the accompanying LICENSE.txt.
 *
 * Contributors:
 *     Sebastian Stenzel - initial API and implementation
 *******************************************************************************/
package org.cryptomator.cryptofs;

import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.spi.FileSystemProvider;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Spliterators;
import java.util.function.Consumer;

import org.apache.commons.lang3.StringUtils;
import org.cryptomator.cryptofs.CryptoPathMapper.Directory;
import org.cryptomator.cryptolib.DaggerCryptoLibComponent;
import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.FileNameCryptor;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;

public class CryptoDirectoryStreamTest {

    private static final Consumer<CryptoDirectoryStream> DO_NOTHING_ON_CLOSE = ignored -> {
    };
    private static final Filter<? super Path> ACCEPT_ALL = ignored -> true;

    private static CryptorProvider cryptorProvider;

    @BeforeClass
    public static void setupClass() {
        cryptorProvider = DaggerCryptoLibComponent.builder() //
                .secureRandomModule(new TestSecureRandomModule()) //
                .build() //
                .version1();
    }

    private FileNameCryptor filenameCryptor;
    private Path ciphertextDirPath;
    private DirectoryStream<Path> dirStream;
    private CryptoPathMapper cryptoPathMapper;
    private LongFileNameProvider longFileNameProvider;
    private ConflictResolver conflictResolver;
    private FinallyUtil finallyUtil;

    @Before
    @SuppressWarnings("unchecked")
    public void setup() throws IOException {
        filenameCryptor = cryptorProvider.createNew().fileNameCryptor();

        ciphertextDirPath = Mockito.mock(Path.class);
        FileSystem fs = Mockito.mock(FileSystem.class);
        Mockito.when(ciphertextDirPath.getFileSystem()).thenReturn(fs);
        FileSystemProvider provider = Mockito.mock(FileSystemProvider.class);
        Mockito.when(fs.provider()).thenReturn(provider);
        dirStream = Mockito.mock(DirectoryStream.class);
        Mockito.when(provider.newDirectoryStream(Mockito.same(ciphertextDirPath), Mockito.any()))
                .thenReturn(dirStream);
        longFileNameProvider = Mockito.mock(LongFileNameProvider.class);
        conflictResolver = Mockito.mock(ConflictResolver.class);
        finallyUtil = mock(FinallyUtil.class);
        Mockito.when(longFileNameProvider.inflate(Mockito.anyString())).then(invocation -> {
            String shortName = invocation.getArgument(0);
            if (shortName.contains("invalid")) {
                throw new IOException("invalid shortened name");
            } else {
                return StringUtils.removeEnd(shortName, ".lng");
            }
        });
        cryptoPathMapper = Mockito.mock(CryptoPathMapper.class);
        Mockito.when(cryptoPathMapper.resolveDirectory(Mockito.any())).then(invocation -> {
            Path dirFilePath = invocation.getArgument(0);
            if (dirFilePath.toString().contains("invalid")) {
                throw new IOException("Invalid directory.");
            }
            Path dirPath = Mockito.mock(Path.class);
            BasicFileAttributes attrs = Mockito.mock(BasicFileAttributes.class);
            Mockito.when(dirPath.getFileSystem()).thenReturn(fs);
            Mockito.when(provider.readAttributes(dirPath, BasicFileAttributes.class)).thenReturn(attrs);
            Mockito.when(attrs.isDirectory()).thenReturn(!dirFilePath.toString().contains("noDirectory"));
            return new Directory("asdf", dirPath);
        });

        Mockito.when(conflictResolver.resolveConflictsIfNecessary(Mockito.any(), Mockito.any()))
                .then(returnsFirstArg());

        doAnswer(invocation -> {
            for (Object runnable : invocation.getArguments()) {
                ((RunnableThrowingException<?>) runnable).run();
            }
            return null;
        }).when(finallyUtil).guaranteeInvocationOf(any(RunnableThrowingException.class),
                any(RunnableThrowingException.class), any(RunnableThrowingException.class));
    }

    @Test
    public void testDirListing() throws IOException {
        Path cleartextPath = Paths.get("/foo/bar");

        List<String> ciphertextFileNames = new ArrayList<>();
        ciphertextFileNames.add(filenameCryptor.encryptFilename("one", "foo".getBytes()));
        ciphertextFileNames.add(filenameCryptor.encryptFilename("two", "foo".getBytes()) + "_conflict");
        ciphertextFileNames.add("0" + filenameCryptor.encryptFilename("three", "foo".getBytes()));
        ciphertextFileNames.add("0invalidDirectory");
        ciphertextFileNames.add("0noDirectory");
        ciphertextFileNames.add("invalidLongName.lng");
        ciphertextFileNames.add(filenameCryptor.encryptFilename("four", "foo".getBytes()) + ".lng");
        ciphertextFileNames.add(filenameCryptor.encryptFilename("invalid", "bar".getBytes()));
        ciphertextFileNames.add("alsoInvalid");
        Mockito.when(dirStream.spliterator())
                .thenReturn(ciphertextFileNames.stream().map(cleartextPath::resolve).spliterator());

        try (CryptoDirectoryStream stream = new CryptoDirectoryStream(new Directory("foo", ciphertextDirPath),
                cleartextPath, filenameCryptor, cryptoPathMapper, longFileNameProvider, conflictResolver,
                ACCEPT_ALL, DO_NOTHING_ON_CLOSE, finallyUtil)) {
            Iterator<Path> iter = stream.iterator();
            Assert.assertTrue(iter.hasNext());
            Assert.assertEquals(cleartextPath.resolve("one"), iter.next());
            Assert.assertTrue(iter.hasNext());
            Assert.assertEquals(cleartextPath.resolve("two"), iter.next());
            Assert.assertTrue(iter.hasNext());
            Assert.assertEquals(cleartextPath.resolve("three"), iter.next());
            Assert.assertTrue(iter.hasNext());
            Assert.assertEquals(cleartextPath.resolve("four"), iter.next());
            Assert.assertFalse(iter.hasNext());
            Mockito.verify(dirStream, Mockito.never()).close();
        }
        Mockito.verify(dirStream).close();
    }

    @Test(expected = NoSuchElementException.class)
    public void testDirListingForEmptyDir() throws IOException {
        Path cleartextPath = Paths.get("/foo/bar");

        Mockito.when(dirStream.spliterator()).thenReturn(Spliterators.emptySpliterator());

        try (CryptoDirectoryStream stream = new CryptoDirectoryStream(new Directory("foo", ciphertextDirPath),
                cleartextPath, filenameCryptor, cryptoPathMapper, longFileNameProvider, conflictResolver,
                ACCEPT_ALL, DO_NOTHING_ON_CLOSE, finallyUtil)) {
            Iterator<Path> iter = stream.iterator();
            Assert.assertFalse(iter.hasNext());
            iter.next();
        }
    }

}