/*
* Copyright (C) 2014 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.build.gradle.integration.common.utils;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import org.junit.Assert;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
/**
* Helper to help read/test the content of generated zip file.
*/
public class ZipHelper {
@Nullable
public static File extractFile(File zipFile, String path) throws IOException {
ZipFile zip = null;
try {
zip = new ZipFile(zipFile);
ZipEntry entry = zip.getEntry(path);
if (entry == null) {
return null;
}
// extract the file
File apk = File.createTempFile("findAndExtractFromZip", "apk");
apk.deleteOnExit();
Files.asByteSink(apk).writeFrom(zip.getInputStream(entry));
return apk;
} catch (IOException e) {
throw new IOException("Failed to open " + zipFile, e);
} finally {
if (zip != null) {
zip.close();
}
}
}
/**
* Checks that a zip file contains a specific file.
*/
public static void checkFileExists(
@NonNull File archive,
@NonNull String path)
throws IOException {
Map<String, byte[]> pathToContent = Collections.singletonMap(path, null);
checkArchive(archive, pathToContent, ImmutableSet.<String>of());
}
/**
* Checks that a zip file contains all files in the list.
*/
public static void checkFileExists(
@NonNull File archive,
@NonNull Set<String> paths)
throws IOException {
Map<String, byte[]> pathToContent = Maps.newLinkedHashMap();
for (String path : paths) {
pathToContent.put(path, null);
}
checkArchive(archive, pathToContent, ImmutableSet.<String>of());
}
/**
* Checks that a zip file do not contains any all files in the set.
*/
public static void checkFileDoesNotExist(
@NonNull File archive,
@NonNull Set<String> paths)
throws IOException {
checkArchive(archive, Collections.<String, byte[]>emptyMap(), paths);
}
/**
* Checks that a zip file contains a file with the specified content
*
* @param archive the zip file to check.
* @param path an expected file inside archive
* @param content the expected content of the file inside archive
*/
public static void checkContent(
@NonNull File archive,
@NonNull String path,
@NonNull String content)
throws IOException {
Map<String, byte[]> pathToContent =
Collections.singletonMap(path, content.getBytes(Charsets.UTF_8));
checkArchive(archive, pathToContent, ImmutableSet.<String>of(), true);
}
public static void checkContent(
@NonNull File archive,
@NonNull String path,
@NonNull byte[] content)
throws IOException {
Map<String, byte[]> pathToContent = Collections.singletonMap(path, content);
checkArchive(archive, pathToContent, ImmutableSet.<String>of());
}
/**
* Checks that a zip file contains files, optionally with specific content.
*
* @param archive the file to check.
* @param pathToContents a map of zip entry path and text-content. if content is null, only
* presence of file inside the zip is checked.
* @param notPresentPaths a list of paths that should *not* be present in the archive.
*/
public static void checkArchive(
@NonNull File archive,
@NonNull Map<String, byte[]> pathToContents,
@NonNull Set<String> notPresentPaths)
throws IOException {
checkArchive(archive, pathToContents, notPresentPaths, false);
}
public static void checkArchive(
@NonNull File archive,
@NonNull Map<String, byte[]> pathToContents,
@NonNull Set<String> notPresentPaths,
boolean isContentString)
throws IOException {
assertTrue("File '" + archive.getPath() + "' does not exist.", archive.isFile());
ZipInputStream zis = null;
FileInputStream fis;
Set<String> notFound = Sets.newHashSet();
notFound.addAll(pathToContents.keySet());
fis = new FileInputStream(archive);
try {
zis = new ZipInputStream(fis);
ZipEntry entry = zis.getNextEntry();
while (entry != null) {
String name = entry.getName();
assertFalse("Found not expected path: " + name,
notPresentPaths.contains(name));
byte[] expected = pathToContents.get(name);
if (expected != null) {
notFound.remove(name);
if (!entry.isDirectory()) {
byte[] bytes = ByteStreams.toByteArray(zis);
if (bytes != null) {
if (isContentString) {
// trim contents if we are checking with string.
String contents = new String(bytes, Charsets.UTF_8).trim();
String expectedContents = new String(expected, Charsets.UTF_8).trim();
Assert.assertEquals("Contents in " + name + " did not match",
expectedContents, contents);
} else {
assertArrayEquals(expected, bytes);
}
}
}
} else if (pathToContents.keySet().contains(name)) {
notFound.remove(name);
}
entry = zis.getNextEntry();
}
} finally {
fis.close();
if (zis != null) {
zis.close();
}
}
assertTrue("Did not find the following paths in the " + archive.getPath() + " file: " +
notFound, notFound.isEmpty());
}
public static void extractFile(
@NonNull File zipFile,
@NonNull String entryPath,
@NonNull File destFile) throws IOException {
assertTrue("File '" + zipFile.getPath() + "' does not exist.", zipFile.isFile());
ZipInputStream zis = null;
FileInputStream fis;
fis = new FileInputStream(zipFile);
try {
zis = new ZipInputStream(fis);
ZipEntry entry = zis.getNextEntry();
while (entry != null) {
String name = entry.getName();
if (entryPath.equals(name)) {
byte[] bytes = ByteStreams.toByteArray(zis);
if (bytes != null) {
Files.write(bytes, destFile);
break;
}
}
entry = zis.getNextEntry();
}
} finally {
fis.close();
if (zis != null) {
zis.close();
}
}
}
}