blob: 8ac97d713588683cad1770342f396dc8125c7337 [file] [log] [blame]
/*
* Copyright (C) 2016 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.internal.transforms;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import com.android.annotations.NonNull;
import com.android.build.api.transform.Context;
import com.android.build.api.transform.Format;
import com.android.build.api.transform.QualifiedContent;
import com.android.build.api.transform.Status;
import com.android.build.api.transform.TransformException;
import com.android.build.api.transform.TransformInput;
import com.android.build.api.transform.TransformOutputProvider;
import com.android.build.gradle.internal.pipeline.IntermediateFolderUtils;
import com.android.build.gradle.internal.pipeline.SubStream;
import com.android.build.gradle.internal.pipeline.TransformInvocationBuilder;
import com.android.utils.FileUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;
import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import org.gradle.api.logging.Logger;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
public class ExtractJarsTransformTest {
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Mock
Context context;
@Mock
Logger logger;
@Before
public void setLogger() {
ExtractJarsTransform.LOGGER = logger;
}
@Test
public void checkWarningForPotentialIssuesOnCaseSensitiveFileSystems()
throws Exception {
File jar = temporaryFolder.newFile("Jar with case issues.jar");
try (JarOutputStream jarOutputStream = new JarOutputStream(
new BufferedOutputStream(new FileOutputStream(jar)))) {
jarOutputStream.putNextEntry(new ZipEntry("com/example/a.class"));
jarOutputStream.closeEntry();
jarOutputStream.putNextEntry(new ZipEntry("com/example/A.class"));
jarOutputStream.closeEntry();
jarOutputStream.putNextEntry(new ZipEntry("com/example/B.class"));
jarOutputStream.closeEntry();
}
checkWarningForCaseIssues(jar, true /*expectingWarning*/);
}
@Test
public void checkNoWarningWhenWillNotHaveIssuesOnCaseSensitiveFileSystems()
throws Exception {
File jar = temporaryFolder.newFile("Jar without case issues.jar");
try (JarOutputStream jarOutputStream = new JarOutputStream(
new BufferedOutputStream(new FileOutputStream(jar)))) {
jarOutputStream.putNextEntry(new ZipEntry("com/example/a.class"));
jarOutputStream.closeEntry();
jarOutputStream.putNextEntry(new ZipEntry("com/example/B.class"));
jarOutputStream.closeEntry();
}
checkWarningForCaseIssues(jar, false /*expectingWarning*/);
}
@Test
public void checkIncrementalDeleteJar() throws IOException, TransformException, InterruptedException {
Path jar1 = temporaryFolder.newFile("jar1.jar").toPath();
try (JarOutputStream jarOutputStream =
new JarOutputStream(new BufferedOutputStream(Files.newOutputStream(jar1)))) {
jarOutputStream.putNextEntry(new ZipEntry("com/example/A.class"));
jarOutputStream.closeEntry();
}
Path jar2 = temporaryFolder.newFile("jar2.jar").toPath();
try (JarOutputStream jarOutputStream =
new JarOutputStream(new BufferedOutputStream(Files.newOutputStream(jar2)))) {
jarOutputStream.putNextEntry(new ZipEntry("com/example/B.class"));
jarOutputStream.closeEntry();
}
final ImmutableSet<QualifiedContent.ContentType> types =
ImmutableSet.of(QualifiedContent.DefaultContentType.CLASSES);
final ImmutableSet<QualifiedContent.Scope> scopes =
ImmutableSet.of(QualifiedContent.Scope.SUB_PROJECTS);
ExtractJarsTransform transform = new ExtractJarsTransform(types, scopes);
Path outputDirectory = temporaryFolder.newFolder().toPath();
// Initial Run
final TransformOutputProviderImpl transformOutputProvider =
new TransformOutputProviderImpl(outputDirectory, types, scopes);
transform.transform(
new TransformInvocationBuilder(context)
.addInputs(
ImmutableList.of(
asJarInput(jar1, Status.NOTCHANGED),
asJarInput(jar2, Status.NOTCHANGED)))
.addOutputProvider(transformOutputProvider)
.build());
// we need to save the output state so that the next run picks it up
transformOutputProvider.save();
assertThat(getFileNames(outputDirectory))
.containsExactly("A.class", "B.class", SubStream.FN_FOLDER_CONTENT);
// Delete jar
Files.delete(jar2);
// Initial Run
transform.transform(
new TransformInvocationBuilder(context)
.addInputs(ImmutableList.of(asJarInput(jar2, Status.REMOVED)))
.addOutputProvider(
new TransformOutputProviderImpl(outputDirectory, types, scopes))
.setIncrementalMode(true)
.build());
assertThat(getFileNames(outputDirectory))
.containsExactly("A.class", SubStream.FN_FOLDER_CONTENT);
}
private static List<String> getFileNames(Path outputDirectory) throws IOException {
return Files.walk(outputDirectory)
.filter(Files::isRegularFile)
.map(Path::getFileName)
.map(Path::toString)
.collect(Collectors.toList());
}
private void checkWarningForCaseIssues(@NonNull File jar, boolean expectingWarning)
throws IOException, TransformException, InterruptedException {
final ImmutableSet<QualifiedContent.ContentType> types =
ImmutableSet.of(QualifiedContent.DefaultContentType.CLASSES);
final ImmutableSet<QualifiedContent.Scope> scopes =
ImmutableSet.of(QualifiedContent.Scope.SUB_PROJECTS);
ExtractJarsTransform transform = new ExtractJarsTransform(types, scopes);
List<TransformInput> inputList = ImmutableList.of(asJarInput(jar, Status.NOTCHANGED));
transform.transform(
new TransformInvocationBuilder(context)
.addInputs(inputList)
.addOutputProvider(
new TransformOutputProviderImpl(
temporaryFolder.newFolder().toPath(), types, scopes))
.build());
if (expectingWarning) {
verify(logger).error(anyString(), eq((Object)jar.getAbsolutePath()));
}
verifyNoMoreInteractions(logger);
}
private static TransformInput asJarInput(@NonNull Path jarFile, @NonNull Status status) {
return asJarInput(jarFile.toFile(), status);
}
private static TransformInput asJarInput(@NonNull File jarFile, @NonNull Status status) {
return TransformTestHelper.singleJarBuilder(jarFile)
.setScopes(QualifiedContent.Scope.SUB_PROJECTS)
.setContentTypes(QualifiedContent.DefaultContentType.CLASSES)
.setStatus(status)
.build();
}
static class TransformOutputProviderImpl implements TransformOutputProvider {
@NonNull private final File rootLocation;
private final ImmutableSet<QualifiedContent.ContentType> types;
private final ImmutableSet<QualifiedContent.Scope> scopes;
private IntermediateFolderUtils folderUtils;
TransformOutputProviderImpl(
@NonNull Path rootLocation,
ImmutableSet<QualifiedContent.ContentType> types,
ImmutableSet<QualifiedContent.Scope> scopes) {
this.rootLocation = rootLocation.toFile();
this.types = types;
this.scopes = scopes;
}
@Override
public void deleteAll() throws IOException {
FileUtils.cleanOutputDir(rootLocation);
}
public void save() throws IOException {
folderUtils.save();
}
@NonNull
@Override
public File getContentLocation(
@NonNull String name,
@NonNull Set<QualifiedContent.ContentType> types,
@NonNull Set<? super QualifiedContent.Scope> scopes,
@NonNull Format format) {
if (folderUtils == null) {
folderUtils = new IntermediateFolderUtils(rootLocation, this.types, this.scopes);
}
return folderUtils.getContentLocation(name, types, scopes, format);
}
}
}