/* * Copyright (C) 2019 The Android Open Source Project * * Licensed 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 com.android.documentsui.archives; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.os.ParcelFileDescriptor; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.runner.AndroidJUnit4; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveException; import org.apache.commons.compress.compressors.CompressorException; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.List; import java.util.Locale; @RunWith(AndroidJUnit4.class) public class ArchiveHandleTest { @Rule public ArchiveFileTestRule mArchiveFileTestRule = new ArchiveFileTestRule(); private ArchiveHandle prepareArchiveHandle(String archivePath, String suffix, String mimeType) throws IOException, CompressorException, ArchiveException { ParcelFileDescriptor parcelFileDescriptor = mArchiveFileTestRule .openAssetFile(archivePath, suffix); return ArchiveHandle.create(parcelFileDescriptor, mimeType); } private static ArchiveEntry getFileInArchive(Enumeration<ArchiveEntry> enumeration, String pathInArchive) { while (enumeration.hasMoreElements()) { ArchiveEntry entry = enumeration.nextElement(); if (entry.getName().equals(pathInArchive)) { return entry; } } return null; } private static class ArchiveEntryRecord implements ArchiveEntry { private final String mName; private final long mSize; private final boolean mIsDirectory; private ArchiveEntryRecord(ArchiveEntry archiveEntry) { this(archiveEntry.getName(), archiveEntry.getSize(), archiveEntry.isDirectory()); } private ArchiveEntryRecord(String name, long size, boolean isDirectory) { mName = name; mSize = size; mIsDirectory = isDirectory; } @Override public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } if (obj instanceof ArchiveEntryRecord) { ArchiveEntryRecord recordB = (ArchiveEntryRecord) obj; return mName.equals(recordB.mName) && mSize == recordB.mSize && mIsDirectory == recordB.mIsDirectory; } return false; } @Override public String getName() { return mName; } @Override public long getSize() { return mSize; } @Override public boolean isDirectory() { return mIsDirectory; } @Override public Date getLastModifiedDate() { return null; } @NonNull @Override public String toString() { return String.format(Locale.ENGLISH, "name: %s, size: %d, isDirectory: %b", mName, mSize, mIsDirectory); } } private static List<ArchiveEntry> transformToIterable(Enumeration<ArchiveEntry> enumeration) { List list = new ArrayList<ArchiveEntry>(); while (enumeration.hasMoreElements()) { list.add(new ArchiveEntryRecord(enumeration.nextElement())); } return list; } private static final List<ArchiveEntryRecord> sExpectEntries = List.of( new ArchiveEntryRecord("hello/hello.txt", 48, false), new ArchiveEntryRecord("hello/inside_folder/hello_insside.txt", 14, false), new ArchiveEntryRecord("hello/hello2.txt", 48, false)); @Test public void buildArchiveHandle_withoutFileDescriptor_shouldBeIllegal() throws Exception { try { ArchiveHandle.create(null, "application/x-7z-compressed"); fail("It should not be here!"); } catch (NullPointerException e) { /* do nothing */ } } @Test public void buildArchiveHandle_withWrongMimeType_shouldBeIllegal() throws Exception { ParcelFileDescriptor parcelFileDescriptor = mArchiveFileTestRule .openAssetFile("archives/7z/hello.7z", ".7z"); try { ArchiveHandle.create(parcelFileDescriptor, null); fail("It should not be here!"); } catch (IllegalArgumentException e) { /* do nothing */ } } @Test public void buildArchiveHandle_sevenZFile_shouldNotNull() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/7z/hello.7z", ".7z", "application/x-7z-compressed"); assertThat(archiveHandle).isNotNull(); } @Test public void buildArchiveHandle_zipFile_shouldNotNull() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/zip/hello.zip", ".zip", "application/zip"); assertThat(archiveHandle).isNotNull(); } @Test public void buildArchiveHandle_zipWithWrongMimeType_shouldBeNull() throws Exception { try { prepareArchiveHandle("archives/zip/hello.zip", ".zip", "application/xxxzip"); fail("It should not be here!"); } catch (UnsupportedOperationException e) { /* do nothing */ } } @Test public void buildArchiveHandle_tarFile_shouldNotNull() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/tar/hello.tar", ".tar", "application/x-gtar"); assertThat(archiveHandle).isNotNull(); } @Test public void buildArchiveHandle_tgzFile_shouldNotNull() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/tar_gz/hello.tgz", ".tgz", "application/x-compressed-tar"); assertThat(archiveHandle).isNotNull(); } @Test public void buildArchiveHandle_tarGzFile_shouldNotNull() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/tar_gz/hello_tar_gz", ".tar.gz", "application/x-compressed-tar"); assertThat(archiveHandle).isNotNull(); } @Test public void buildArchiveHandle_tarBzipFile_shouldNotNull() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/tar_bz2/hello.tar.bz2", ".tar.bz2", "application/x-bzip-compressed-tar"); assertThat(archiveHandle).isNotNull(); } @Test public void buildArchiveHandle_tarXzFile_shouldNotNull() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/xz/hello.tar.xz", ".tar.xz", "application/x-xz-compressed-tar"); assertThat(archiveHandle).isNotNull(); } @Test public void buildArchiveHandle_tarBrFile_shouldNotNull() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/brotli/hello.tar.br", ".tar.br", "application/x-brotli-compressed-tar"); assertThat(archiveHandle).isNotNull(); } @Test public void getMimeType_sevenZFile_shouldBeSevenZ() throws CompressorException, ArchiveException, IOException { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/7z/hello.7z", ".7z", "application/x-7z-compressed"); assertThat(archiveHandle.getMimeType()).isEqualTo("application/x-7z-compressed"); } @Test public void getMimeType_tarBrotli_shouldBeBrotliCompressedTar() throws CompressorException, ArchiveException, IOException { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/brotli/hello.tar.br", ".tar.br", "application/x-brotli-compressed-tar"); assertThat(archiveHandle.getMimeType()) .isEqualTo("application/x-brotli-compressed-tar"); } @Test public void getMimeType_tarXz_shouldBeXzCompressedTar() throws CompressorException, ArchiveException, IOException { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/xz/hello.tar.xz", ".tar.xz", "application/x-xz-compressed-tar"); assertThat(archiveHandle.getMimeType()) .isEqualTo("application/x-xz-compressed-tar"); } @Test public void getMimeType_tarGz_shouldBeCompressedTar() throws CompressorException, ArchiveException, IOException { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/tar_gz/hello_tar_gz", ".tar.gz", "application/x-compressed-tar"); assertThat(archiveHandle.getMimeType()) .isEqualTo("application/x-compressed-tar"); } @Test public void getCommonArchive_tarBrFile_shouldBeCommonArchiveInputHandle() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/brotli/hello.tar.br", ".tar.br", "application/x-brotli-compressed-tar"); assertThat(archiveHandle.toString()).contains("CommonArchiveInputHandle"); } @Test public void getCommonArchive_sevenZFile_shouldBeSevenZFileHandle() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/7z/hello.7z", ".7z", "application/x-7z-compressed"); assertThat(archiveHandle.toString()).contains("SevenZFileHandle"); } @Test public void getCommonArchive_zipFile_shouldBeZipFileHandle() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/zip/hello.zip", ".zip", "application/zip"); assertThat(archiveHandle.toString()).contains("ZipFileHandle"); } @Test public void close_zipFile_shouldBeSuccess() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/zip/hello.zip", ".zip", "application/zip"); archiveHandle.close(); } @Test public void close_sevenZFile_shouldBeSuccess() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/7z/hello.7z", ".7z", "application/x-7z-compressed"); archiveHandle.close(); } @Test public void closeInputStream_zipFile_shouldBeSuccess() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/zip/hello.zip", ".zip", "application/zip"); InputStream inputStream = archiveHandle.getInputStream( getFileInArchive(archiveHandle.getEntries(), "hello/inside_folder/hello_insside.txt")); assertThat(inputStream).isNotNull(); inputStream.close(); } @Test public void close_zipFile_shouldNotOpen() throws Exception { ParcelFileDescriptor parcelFileDescriptor = mArchiveFileTestRule .openAssetFile("archives/zip/hello.zip", ".zip"); ArchiveHandle archiveHandle = ArchiveHandle.create(parcelFileDescriptor, "application/zip"); archiveHandle.close(); FileInputStream fileInputStream = new FileInputStream(parcelFileDescriptor.getFileDescriptor()); assertThat(fileInputStream).isNotNull(); } @Test public void getInputStream_zipFile_shouldHaveTheSameContent() throws Exception { ParcelFileDescriptor parcelFileDescriptor = mArchiveFileTestRule .openAssetFile("archives/zip/hello.zip", ".zip"); String expectedContent = mArchiveFileTestRule.getAssetText( "archives/original/hello/inside_folder/hello_insside.txt"); ArchiveHandle archiveHandle = ArchiveHandle.create(parcelFileDescriptor, "application/zip"); InputStream inputStream = archiveHandle.getInputStream( getFileInArchive(archiveHandle.getEntries(), "hello/inside_folder/hello_insside.txt")); assertThat(ArchiveFileTestRule.getStringFromInputStream(inputStream)) .isEqualTo(expectedContent); } @Test public void getInputStream_zipFileNotExistEntry_shouldFail() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/zip/hello.zip", ".zip", "application/zip"); ArchiveEntry archiveEntry = mock(ArchiveEntry.class); when(archiveEntry.getName()).thenReturn("/not_exist_entry"); try { archiveHandle.getInputStream(archiveEntry); fail("It should not be here."); } catch (ClassCastException e) { /* do nothing */ } } @Test public void getInputStream_directoryEntry_shouldFail() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/zip/hello.zip", ".zip", "application/zip"); ArchiveEntry archiveEntry = mock(ArchiveEntry.class); when(archiveEntry.isDirectory()).thenReturn(true); try { archiveHandle.getInputStream(archiveEntry); fail("It should not be here."); } catch (IllegalArgumentException e) { /* expected, do nothing */ } } @Test public void getInputStream_negativeSizeEntry_shouldFail() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/zip/hello.zip", ".zip", "application/zip"); ArchiveEntry archiveEntry = mock(ArchiveEntry.class); when(archiveEntry.isDirectory()).thenReturn(false); when(archiveEntry.getSize()).thenReturn(-1L); try { archiveHandle.getInputStream(archiveEntry); fail("It should not be here."); } catch (IllegalArgumentException e) { /* expected, do nothing */ } } @Test public void getInputStream_emptyStringEntry_shouldFail() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/zip/hello.zip", ".zip", "application/zip"); ArchiveEntry archiveEntry = mock(ArchiveEntry.class); when(archiveEntry.isDirectory()).thenReturn(false); when(archiveEntry.getSize()).thenReturn(14L); when(archiveEntry.getName()).thenReturn(""); try { archiveHandle.getInputStream(archiveEntry); fail("It should not be here."); } catch (IllegalArgumentException e) { /* expected, do nothing */ } } @Test public void getInputStream_sevenZFile_shouldHaveTheSameContent() throws Exception { ParcelFileDescriptor parcelFileDescriptor = mArchiveFileTestRule .openAssetFile("archives/7z/hello.7z", ".7z"); String expectedContent = mArchiveFileTestRule.getAssetText( "archives/original/hello/inside_folder/hello_insside.txt"); ArchiveHandle archiveHandle = ArchiveHandle.create(parcelFileDescriptor, "application/x-7z-compressed"); InputStream inputStream = archiveHandle.getInputStream( getFileInArchive(archiveHandle.getEntries(), "hello/inside_folder/hello_insside.txt")); assertThat(ArchiveFileTestRule.getStringFromInputStream(inputStream)) .isEqualTo(expectedContent); } @Test public void getInputStream_tarGzFile_shouldHaveTheSameContent() throws Exception { ParcelFileDescriptor parcelFileDescriptor = mArchiveFileTestRule .openAssetFile("archives/tar_gz/hello.tgz", ".tar.gz"); String expectedContent = mArchiveFileTestRule.getAssetText( "archives/original/hello/inside_folder/hello_insside.txt"); ArchiveHandle archiveHandle = ArchiveHandle.create(parcelFileDescriptor, "application/x-compressed-tar"); InputStream inputStream = archiveHandle.getInputStream( getFileInArchive(archiveHandle.getEntries(), "hello/inside_folder/hello_insside.txt")); assertThat(ArchiveFileTestRule.getStringFromInputStream(inputStream)) .isEqualTo(expectedContent); } @Test public void getInputStream_tarGzFileNullEntry_getNullInputStream() throws Exception { ParcelFileDescriptor parcelFileDescriptor = mArchiveFileTestRule .openAssetFile("archives/tar_gz/hello.tgz", ".tar.gz"); String expectedContent = mArchiveFileTestRule.getAssetText( "archives/original/hello/inside_folder/hello_insside.txt"); ArchiveHandle archiveHandle = ArchiveHandle.create(parcelFileDescriptor, "application/x-compressed-tar"); try { archiveHandle.getInputStream(null); fail("It should not here"); } catch (IllegalArgumentException | ArchiveException | CompressorException e) { /* expected, do nothing */ } } @Test public void getInputStream_tarGzFileInvalidEntry_getNullInputStream() throws Exception { ParcelFileDescriptor parcelFileDescriptor = mArchiveFileTestRule .openAssetFile("archives/tar_gz/hello.tgz", ".tar.gz"); String expectedContent = mArchiveFileTestRule.getAssetText( "archives/original/hello/inside_folder/hello_insside.txt"); ArchiveHandle archiveHandle = ArchiveHandle.create(parcelFileDescriptor, "application/x-compressed-tar"); ArchiveEntry archiveEntry = mock(ArchiveEntry.class); when(archiveEntry.getName()).thenReturn(""); try { archiveHandle.getInputStream(archiveEntry); fail("It should not here"); } catch (IllegalArgumentException | ArchiveException | CompressorException e) { /* expected, do nothing */ } } @Test public void getInputStream_tarBrotliFile_shouldHaveTheSameContent() throws Exception { ParcelFileDescriptor parcelFileDescriptor = mArchiveFileTestRule .openAssetFile("archives/brotli/hello.tar.br", ".tar.br"); String expectedContent = mArchiveFileTestRule.getAssetText( "archives/original/hello/inside_folder/hello_insside.txt"); ArchiveHandle archiveHandle = ArchiveHandle.create(parcelFileDescriptor, "application/x-brotli-compressed-tar"); InputStream inputStream = archiveHandle.getInputStream( getFileInArchive(archiveHandle.getEntries(), "hello/inside_folder/hello_insside.txt")); assertThat(ArchiveFileTestRule.getStringFromInputStream(inputStream)) .isEqualTo(expectedContent); } @Test public void getEntries_zipFile_shouldTheSameWithList() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/zip/hello.zip", ".zip", "application/zip"); assertThat(transformToIterable(archiveHandle.getEntries())) .containsAtLeastElementsIn(sExpectEntries); } @Test public void getEntries_tarFile_shouldTheSameWithList() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/tar/hello.tar", ".tar", "application/x-gtar"); assertThat(transformToIterable(archiveHandle.getEntries())) .containsAtLeastElementsIn(sExpectEntries); } @Test public void getEntries_tgzFile_shouldTheSameWithList() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/tar_gz/hello.tgz", ".tgz", "application/x-compressed-tar"); assertThat(transformToIterable(archiveHandle.getEntries())) .containsAtLeastElementsIn(sExpectEntries); } @Test public void getEntries_tarBzFile_shouldTheSameWithList() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/tar_bz2/hello.tar.bz2", ".tar.bz2", "application/x-bzip-compressed-tar"); assertThat(transformToIterable(archiveHandle.getEntries())) .containsAtLeastElementsIn(sExpectEntries); } @Test public void getEntries_tarBrotliFile_shouldTheSameWithList() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/brotli/hello.tar.br", ".tar.br", "application/x-brotli-compressed-tar"); assertThat(transformToIterable(archiveHandle.getEntries())) .containsAtLeastElementsIn(sExpectEntries); } @Test public void getEntries_tarXzFile_shouldTheSameWithList() throws Exception { ArchiveHandle archiveHandle = prepareArchiveHandle("archives/xz/hello.tar.xz", ".tar.xz", "application/x-xz-compressed-tar"); assertThat(transformToIterable(archiveHandle.getEntries())) .containsAtLeastElementsIn(sExpectEntries); } }