/*
* 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.ide.common.build;
import static org.mockito.Mockito.when;
import com.android.annotations.NonNull;
import com.android.build.FilterData;
import com.android.build.OutputFile;
import com.android.build.VariantOutput;
import com.android.builder.testing.api.DeviceConfigProvider;
import com.android.ide.common.process.ProcessException;
import com.android.ide.common.process.ProcessExecutor;
import com.android.resources.Density;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import junit.framework.TestCase;
import org.mockito.Mockito;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
public class SplitOutputMatcherTest extends TestCase {
/**
* Helper to run InstallHelper.computeMatchingOutput with variable ABI list.
*/
private static List<File> computeBestOutput(
@NonNull List<? extends VariantOutput> outputs,
int density,
@NonNull String... abis) throws ProcessException{
DeviceConfigProvider deviceConfigProvider = Mockito.mock(DeviceConfigProvider.class);
when(deviceConfigProvider.getDensity()).thenReturn(density);
when(deviceConfigProvider.getAbis()).thenReturn(Arrays.asList(abis));
return SplitOutputMatcher.computeBestOutput(
Mockito.mock(ProcessExecutor.class),
null /* splitSelectExe */,
deviceConfigProvider,
outputs,
null /* variantAbiFilters */);
}
private static List<File> computeBestOutput(
@NonNull List<? extends VariantOutput> outputs,
@NonNull Set<String> variantAbis,
int density,
@NonNull String... abis) throws ProcessException {
DeviceConfigProvider deviceConfigProvider = Mockito.mock(DeviceConfigProvider.class);
when(deviceConfigProvider.getDensity()).thenReturn(density);
when(deviceConfigProvider.getAbis()).thenReturn(new ArrayList<String>(variantAbis));
return SplitOutputMatcher.computeBestOutput(
Mockito.mock(ProcessExecutor.class),
null /* splitSelectExec */,
deviceConfigProvider,
outputs,
Arrays.asList(abis));
}
/**
* Fake implementation of FilteredOutput
*/
private static final class FakeSplitOutput implements OutputFile {
private final String densityFilter;
private final String abiFilter;
private final File file;
FakeSplitOutput(String densityFilter, String abiFilter) {
this.densityFilter = densityFilter;
this.abiFilter = abiFilter;
file = new File(densityFilter + abiFilter);
}
@Override
public String getOutputType() {
return OutputFile.FULL_SPLIT;
}
@NonNull
@Override
public Collection<String> getFilterTypes() {
ImmutableList.Builder<String> splitTypeBuilder = ImmutableList.builder();
if (densityFilter != null) {
splitTypeBuilder.add(OutputFile.DENSITY);
}
if (abiFilter != null) {
splitTypeBuilder.add(OutputFile.ABI);
}
return splitTypeBuilder.build();
}
@NonNull
@Override
public Collection<FilterData> getFilters() {
ImmutableList.Builder<FilterData> filters = ImmutableList.builder();
if (densityFilter != null) {
filters.add(FakeFilterData.Builder.build(OutputFile.DENSITY, densityFilter));
}
if (abiFilter != null) {
filters.add(FakeFilterData.Builder.build(OutputFile.ABI, abiFilter));
}
return filters.build();
}
@NonNull
@Override
public File getOutputFile() {
return file;
}
@Override
public String toString() {
return "FilteredOutput{" + densityFilter + ':' + abiFilter + '}';
}
}
private static final class FakeFilterData implements FilterData {
private final String filterType;
private final String identifier;
FakeFilterData(String filterType, String identifier) {
this.filterType = filterType;
this.identifier = identifier;
}
@NonNull
@Override
public String getIdentifier() {
return identifier;
}
@NonNull
@Override
public String getFilterType() {
return filterType;
}
public static class Builder {
public static FilterData build(final String filterType, final String identifier) {
return new FakeFilterData(filterType, identifier);
}
}
}
private static class FakeVariantOutput implements VariantOutput {
private final OutputFile mainOutputFile;
private final int versionCode;
private FakeVariantOutput(OutputFile mainOutputFile,
int versionCode) {
this.mainOutputFile = mainOutputFile;
this.versionCode = versionCode;
}
@NonNull
@Override
public OutputFile getMainOutputFile() {
return mainOutputFile;
}
@NonNull
@Override
public Collection<? extends OutputFile> getOutputs() {
return ImmutableList.of(mainOutputFile);
}
@Override
public int getVersionCode() {
return versionCode;
}
@NonNull
@Override
public File getSplitFolder() {
return null;
}
}
public void testSingleOutput() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
list.add(match = getUniversalOutput(1));
List<File> result = computeBestOutput(list, 160, "foo");
assertEquals(1, result.size());
assertEquals(match.getMainOutputFile().getOutputFile(), result.get(0));
}
public void testDensityOnlyWithMatch() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
list.add(getUniversalOutput(1));
list.add(match = getDensityOutput(160, 2));
list.add(getDensityOutput(320, 3));
List<File> result = computeBestOutput(list, 160, "foo");
assertEquals(1, result.size());
assertEquals(match.getMainOutputFile().getOutputFile(), result.get(0));
}
public void testDensityOnlyWithUniversalMatch() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
list.add(match = getUniversalOutput(3));
list.add(getDensityOutput(320, 2));
list.add(getDensityOutput(480, 1));
List<File> result = computeBestOutput(list, 160, "foo");
assertEquals(1, result.size());
assertEquals(match.getMainOutputFile().getOutputFile(), result.get(0));
}
public void testDensityOnlyWithNoMatch() throws ProcessException {
List<VariantOutput> list = Lists.newArrayList();
list.add(getDensityOutput(320, 1));
list.add(getDensityOutput(480, 2));
List<File> result = computeBestOutput(list, 160, "foo");
assertEquals(0, result.size());
}
public void testDensityOnlyWithCustomDeviceDensity() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
list.add(match = getUniversalOutput(1));
list.add(getDensityOutput(320, 2));
list.add(getDensityOutput(480, 3));
List<File> result = computeBestOutput(list, 1, "foo");
assertEquals(1, result.size());
assertEquals(match.getMainOutputFile().getOutputFile(), result.get(0));
}
public void testAbiOnlyWithMatch() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
list.add(getUniversalOutput(1));
list.add(match = getAbiOutput("foo", 2));
list.add(getAbiOutput("bar", 3));
List<File> result = computeBestOutput(list, 160, "foo");
assertEquals(1, result.size());
assertEquals(match.getMainOutputFile().getOutputFile(), result.get(0));
}
public void testAbiOnlyWithMultiMatch() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
// test where the versionCode match the abi order
list.add(getUniversalOutput(1));
list.add(getAbiOutput("foo", 2));
list.add(match = getAbiOutput("bar", 3));
// bar is preferred over foo
List<File> result = computeBestOutput(list, 160, "bar", "foo");
assertEquals(1, result.size());
assertEquals(match.getMainOutputFile().getOutputFile(), result.get(0));
}
public void testAbiOnlyWithMultiMatch2() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
// test where the versionCode does not match the abi order
list.add(getUniversalOutput(1));
list.add(getAbiOutput("foo", 2));
list.add(match = getAbiOutput("bar", 3));
// bar is preferred over foo
List<File> result = computeBestOutput(list, 160, "foo", "bar");
assertEquals(1, result.size());
assertEquals(match.getMainOutputFile().getOutputFile(), result.get(0));
}
public void testAbiOnlyWithUniversalMatch() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
list.add(match = getUniversalOutput(1));
list.add(getAbiOutput("foo", 2));
list.add(getAbiOutput("bar", 3));
List<File> result = computeBestOutput(list, 160, "zzz");
assertEquals(1, result.size());
assertEquals(match.getMainOutputFile().getOutputFile(), result.get(0));
}
public void testAbiOnlyWithNoMatch() throws ProcessException {
List<VariantOutput> list = Lists.newArrayList();
list.add(getAbiOutput("foo", 1));
list.add(getAbiOutput("bar", 2));
List<File> result = computeBestOutput(list, 160, "zzz");
assertEquals(0, result.size());
}
public void testMultiFilterWithMatch() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
list.add(getUniversalOutput(1));
list.add(getOutput(160, "zzz",2));
list.add(match = getOutput(160, "foo", 4));
list.add(getOutput(320, "foo", 3));
List<File> result = computeBestOutput(list, 160, "foo");
assertEquals(1, result.size());
assertEquals(match.getMainOutputFile().getOutputFile(), result.get(0));
}
public void testMultiFilterWithUniversalMatch() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
list.add(match = getUniversalOutput(4));
list.add(getOutput(320, "zzz", 3));
list.add(getOutput(160, "bar", 2));
list.add(getOutput(320, "foo", 1));
List<File> result = computeBestOutput(list, 160, "zzz");
assertEquals(1, result.size());
assertEquals(match.getMainOutputFile().getOutputFile(), result.get(0));
}
public void testMultiFilterWithNoMatch() throws ProcessException {
List<VariantOutput> list = Lists.newArrayList();
list.add(getOutput(320, "zzz", 1));
list.add(getOutput(160, "bar", 2));
list.add(getOutput(320, "foo", 3));
List<File> result = computeBestOutput(list, 160, "zzz");
assertEquals(0, result.size());
}
public void testVariantLevelAbiFilter() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
list.add(match = getUniversalOutput(1));
List<File> result = computeBestOutput(list, Sets.newHashSet("bar", "foo"), 160, "foo",
"zzz");
assertEquals(1, result.size());
assertEquals(match.getMainOutputFile().getOutputFile(), result.get(0));
}
public void testWrongVariantLevelAbiFilter() throws ProcessException {
List<VariantOutput> list = Lists.newArrayList();
list.add(getUniversalOutput(1));
List<File> result = computeBestOutput(list, Sets.newHashSet("bar", "foo"), 160, "zzz");
assertEquals(0, result.size());
}
public void testDensitySplitPlugVariantLevelAbiFilter() throws ProcessException {
VariantOutput match;
List<VariantOutput> list = Lists.newArrayList();
list.add(getUniversalOutput(1));
list.add(getDensityOutput(240, 2));
list.add(match = getDensityOutput(320, 3));
list.add(getDensityOutput(480, 4));
List<File> result = computeBestOutput(list, Sets.newHashSet("bar", "foo"), 320, "foo", "zzz");
assertEquals(1, result.size());
}
private static VariantOutput getUniversalOutput(int versionCode) {
return new FakeVariantOutput(new FakeSplitOutput(null, null), versionCode);
}
private static VariantOutput getDensityOutput(int densityFilter, int versionCode) {
Density densityEnum = Density.getEnum(densityFilter);
return new FakeVariantOutput(
new FakeSplitOutput(densityEnum.getResourceValue(), null), versionCode);
}
private static VariantOutput getAbiOutput(String filter, int versionCode) {
return new FakeVariantOutput(
new FakeSplitOutput( null, filter), versionCode);
}
private static VariantOutput getOutput(int densityFilter, String abiFilter, int versionCode) {
Density densityEnum = Density.getEnum(densityFilter);
return new FakeVariantOutput(
new FakeSplitOutput(densityEnum.getResourceValue(), abiFilter), versionCode);
}
}