| // Copyright 2017 The Bazel Authors. All rights reserved. |
| // |
| // 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.google.devtools.build.lib.rules.android; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.common.truth.BooleanSubject; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.analysis.config.BuildOptions; |
| import com.google.devtools.build.lib.analysis.config.transitions.PatchTransition; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| import com.google.devtools.build.lib.collect.nestedset.Order; |
| import com.google.devtools.build.lib.packages.AttributeMap; |
| import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; |
| import com.google.devtools.build.lib.rules.android.ResourceFilterFactory.AddDynamicallyConfiguredResourceFilteringTransition; |
| import com.google.devtools.build.lib.rules.android.ResourceFilterFactory.FilterBehavior; |
| import com.google.devtools.build.lib.testutil.FakeAttributeMapper; |
| import java.util.ArrayList; |
| import java.util.List; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Tests {@link ResourceFilterFactory}. */ |
| // TODO(asteinb): Test behavior not already covered in this test, and, when practical, move unit |
| // tests currently located in {@link AndroidBinaryTest} to this class instead. |
| @RunWith(JUnit4.class) |
| public class ResourceFilterFactoryTest extends ResourceTestBase { |
| |
| private NestedSet<ResourceContainer> getResourceContainers(ImmutableList<Artifact>... resources) |
| throws Exception { |
| NestedSetBuilder<ResourceContainer> builder = NestedSetBuilder.naiveLinkOrder(); |
| for (ImmutableList<Artifact> resourceList : resources) { |
| builder.add(getResourceContainer(resourceList)); |
| } |
| return builder.build(); |
| } |
| |
| private ResourceContainer getResourceContainer(ImmutableList<Artifact> resources) |
| throws Exception { |
| // Get dummy objects for irrelevant values required by the builder. |
| Artifact manifest = getResource("manifest"); |
| |
| // Include a hashCode of the resources in the label. A hack in ResourceContainer currently means |
| // that class has a limited hashCode method that doesn't take resources into account. |
| // TODO(bazel-team): Remove this hack once that one no longer exists. |
| Label label = |
| Label.create( |
| manifest.getOwnerLabel().getPackageName(), "resourceContainer_" + resources.hashCode()); |
| |
| return ResourceContainer.builder() |
| .setResources(resources) |
| .setResourcesRoots( |
| LocalResourceContainer.getResourceRoots(errorConsumer, resources, "resource_files")) |
| .setLabel(label) |
| .setManifestExported(false) |
| .setManifest(manifest) |
| .build(); |
| } |
| |
| @Test |
| public void testFilterInExecution() throws Exception { |
| testNoopFilter( |
| "en", |
| "hdpi", |
| FilterBehavior.FILTER_IN_EXECUTION, |
| ImmutableList.of( |
| "values-en/foo.xml", "values/foo.xml", "values-hdpi/foo.png", "values-ldpi/foo.png")); |
| } |
| |
| @Test |
| public void testFilterEmpty() throws Exception { |
| testNoopFilter("", "", FilterBehavior.FILTER_IN_ANALYSIS, ImmutableList.<String>of()); |
| } |
| |
| @Test |
| public void testFilterDefaultAndNonDefault() throws Exception { |
| testNoopFilter( |
| "en", |
| "xhdpi,xxhdpi", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| ImmutableList.of("drawable/ic_clear.xml", "drawable-v21/ic_clear.xml")); |
| } |
| |
| /** |
| * Tests that version qualifiers are ignored for both resource qualifier and density filtering. |
| */ |
| @Test |
| public void testFilterVersionIgnored() throws Exception { |
| testNoopFilter( |
| "v4", |
| "hdpi", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| ImmutableList.of("drawable-hdpi-v4/foo.png", "drawable-hdpi-v11/foo.png")); |
| } |
| |
| @Test |
| public void testFilterByDensityPersistsOrdering() throws Exception { |
| testFilter( |
| "", |
| "hdpi,ldpi", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| // If we add resources to the output list in density order, these resources will be |
| // rearranged. |
| ImmutableList.of( |
| "drawable-hdpi/foo.png", |
| "drawable-ldpi/foo.png", |
| "drawable-ldpi/bar.png", |
| "drawable-hdpi/bar.png"), |
| // Filter out some resources to make sure the original list isn't just returned because the |
| // filtering was a no-op. |
| ImmutableList.of("drawable-mdpi/foo.png", "drawable-mdpi/bar.png")); |
| } |
| |
| /** Tests handling of Aapt's old region format */ |
| @Test |
| public void testFilterOldLanguageFormat() throws Exception { |
| testFilter( |
| "en_US", |
| "", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| ImmutableList.of("values-en/foo.xml", "values-en-rUS/foo.xml"), |
| ImmutableList.of("values-fr/foo.xml", "values-en-rCA/foo.xml")); |
| |
| errorConsumer.assertNoAttributeWarnings( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME); |
| } |
| |
| @Test |
| public void testFilterOldLanguageFormatWithAdditionalQualifiers() throws Exception { |
| testFilter( |
| "en_US-ldrtl", |
| "", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| ImmutableList.of("values-en/foo.xml", "values-en-rUS/foo.xml"), |
| ImmutableList.of("values-fr/foo.xml", "values-en-rCA/foo.xml")); |
| |
| errorConsumer.assertNoAttributeWarnings( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME); |
| } |
| |
| @Test |
| public void testFilterOldLanguageFormatWithMcc() throws Exception { |
| testFilter( |
| "mcc111-en_US", |
| "", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| ImmutableList.of("values-en/foo.xml", "values-en-rUS/foo.xml"), |
| ImmutableList.of("values-fr/foo.xml", "values-en-rCA/foo.xml")); |
| |
| errorConsumer.assertNoAttributeWarnings( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME); |
| } |
| |
| @Test |
| public void testFilterOldLanguageFormatWithMccAndMnc() throws Exception { |
| testFilter( |
| "mcc111-mnc111-en_US", |
| "", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| ImmutableList.of("values-en/foo.xml", "values-en-rUS/foo.xml"), |
| ImmutableList.of("values-fr/foo.xml", "values-en-rCA/foo.xml")); |
| |
| errorConsumer.assertNoAttributeWarnings( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME); |
| } |
| |
| @Test |
| public void testFilterSerbianLatinCharacters() throws Exception { |
| testFilter( |
| "sr-Latn,sr-rLatn,sr_Latn,b+sr+Latn", |
| "", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| ImmutableList.of( |
| "values-sr/foo.xml", |
| "values-b+sr+Latn/foo.xml", |
| "values-sr-Latn/foo.xml", |
| "values-sr-rLatn/foo.xml"), |
| // Serbian in explicitly Cyrillic characters should be filtered out. |
| ImmutableList.of("values-b+sr+Cyrl/foo.xml")); |
| |
| errorConsumer.assertNoAttributeWarnings( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME); |
| } |
| |
| @Test |
| public void testFilterSerbianCharactersNotSpecified() throws Exception { |
| testNoopFilter( |
| "sr", |
| "", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| ImmutableList.of( |
| "values-sr/foo.xml", |
| "values-b+sr+Latn/foo.xml", |
| "values-b+sr+Cyrl/foo.xml", |
| "values-sr-Latn/foo.xml", |
| "values-sr-rLatn/foo.xml")); |
| |
| errorConsumer.assertNoAttributeWarnings( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME); |
| } |
| |
| @Test |
| public void testFilterSpanishLatinAmericaAndCaribbean() throws Exception { |
| testFilter( |
| "es-419,es_419,b+es+419", |
| "", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| ImmutableList.of("values-es/foo.xml", "values-b+es+419/foo.xml", "values-es-419/foo.xml"), |
| // Spanish with another region specified should be filtered out. |
| ImmutableList.of("values-es-rUS/foo.xml")); |
| |
| errorConsumer.assertNoAttributeWarnings( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME); |
| } |
| |
| @Test |
| public void testFilterSpanishRegionNotSpecified() throws Exception { |
| testNoopFilter( |
| "es", |
| "", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| ImmutableList.of( |
| "values-es/foo.xml", |
| "values-b+es+419/foo.xml", |
| "values-es-rUS/foo.xml", |
| "values-es-419/foo.xml")); |
| |
| errorConsumer.assertNoAttributeWarnings( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME); |
| } |
| |
| @Test |
| public void testFilterDeprecatedQualifierFormatsRuleWarnings() throws RuleErrorException { |
| ImmutableList<Artifact> badResources = |
| getResources( |
| "values-es_US/foo.xml", |
| "drawable-sr-Latn/foo.xml", |
| "layout-es-419/foo.xml", |
| "values-mcc310-es_US/foo.xml", |
| "values-sr_rLatn/foo.xml", |
| "drawable-es_419/foo.xml"); |
| |
| ImmutableList<String> expectedWarnings = |
| ImmutableList.of( |
| "For resource folder drawable-sr-Latn, when referring to Serbian in Latin characters, " |
| + "use of qualifier 'sr-Latn' is deprecated. Use 'b+sr+Latn' instead.", |
| "For resource folder layout-es-419, when referring to Spanish for Latin America and " |
| + "the Caribbean, use of qualifier 'es-419' is deprecated. Use 'b+es+419' instead.", |
| "For resource folder values-es_US, when referring to locale qualifiers with regions, " |
| + "use of qualifier 'es_US' is deprecated. Use 'es-rUS' instead."); |
| |
| ResourceFilterFactory filter = |
| new ResourceFilterFactory( |
| ImmutableList.of("en"), ImmutableList.of(), FilterBehavior.FILTER_IN_ANALYSIS); |
| |
| doFilter(filter, badResources); |
| assertThat(errorConsumer.getAndClearRuleWarnings()).containsExactlyElementsIn(expectedWarnings); |
| errorConsumer.assertNoAttributeWarnings( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME); |
| |
| // Filtering again with this filter should not produce additional warnings |
| doFilter(filter, badResources); |
| errorConsumer.assertNoRuleWarnings(); |
| errorConsumer.assertNoAttributeWarnings( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME); |
| |
| // Filtering with a new filter should produce warnings again, since it is working on a different |
| // target |
| filter = |
| new ResourceFilterFactory( |
| ImmutableList.of("en"), ImmutableList.of(), FilterBehavior.FILTER_IN_ANALYSIS); |
| doFilter(filter, badResources); |
| assertThat(errorConsumer.getAndClearRuleWarnings()).containsExactlyElementsIn(expectedWarnings); |
| errorConsumer.assertNoAttributeWarnings( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME); |
| } |
| |
| @Test |
| public void testFilterResourceConflict() throws Exception { |
| testNoopFilter( |
| "en", |
| "hdpi", |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| ImmutableList.of( |
| "first-subdir/res/drawable-en-hdpi/foo.png", |
| "second-subdir/res/drawable-en-hdpi/foo.png")); |
| } |
| |
| @Test |
| public void testFilterWithDynamicConfiguration() throws Exception { |
| testFilter( |
| "en", |
| "hdpi", |
| FilterBehavior.FILTER_IN_ANALYSIS_WITH_DYNAMIC_CONFIGURATION, |
| ImmutableList.of("drawable-en-hdpi/foo.png"), |
| ImmutableList.of("drawable-en-ldpi/foo.png", "drawable-fr-hdpi/foo.png")); |
| } |
| |
| private void testNoopFilter( |
| String resourceConfigurationFilters, |
| String densities, |
| FilterBehavior filterBehavior, |
| List<String> resources) |
| throws Exception { |
| testFilter( |
| resourceConfigurationFilters, |
| densities, |
| filterBehavior, |
| resources, |
| ImmutableList.<String>of()); |
| } |
| |
| private void testFilter( |
| String resourceConfigurationFilters, |
| String densities, |
| FilterBehavior filterBehavior, |
| List<String> resourcesToKeep, |
| List<String> resourcesToDiscard) |
| throws Exception { |
| List<Artifact> unexpectedResources = new ArrayList<>(); |
| for (String resource : resourcesToDiscard) { |
| unexpectedResources.add(getResource(resource)); |
| } |
| |
| List<Artifact> expectedResources = new ArrayList<>(); |
| for (String resource : resourcesToKeep) { |
| expectedResources.add(getResource(resource)); |
| } |
| |
| ImmutableList<Artifact> allArtifacts = |
| ImmutableList.copyOf(Iterables.concat(expectedResources, unexpectedResources)); |
| ResourceFilterFactory resourceFilterFactory = |
| makeResourceFilter(resourceConfigurationFilters, densities, filterBehavior); |
| ImmutableList<Artifact> filtered = doFilter(resourceFilterFactory, allArtifacts); |
| |
| assertThat(filtered).containsExactlyElementsIn(expectedResources).inOrder(); |
| |
| // Only dependencies need to be tracked for ignoring in execution |
| assertThat(resourceFilterFactory.getResourcesToIgnoreInExecution()).isEmpty(); |
| } |
| |
| @Test |
| public void testFilterLocalAndTransitive() throws Exception { |
| Artifact localResourceToKeep = getResource("drawable-en-hdpi/local.png"); |
| Artifact localResourceToDiscard = getResource("drawable-en-ldpi/local.png"); |
| |
| // These resources go in different ResourceContainers to ensure we are filter across all |
| // resources. |
| Artifact directResourceToKeep = getResource("direct/drawable-en-hdpi/direct.png"); |
| Artifact directResourceToDiscard = getResource("direct/drawable-en-ldpi/direct.png"); |
| Artifact transitiveResourceToKeep = getResource("transitive/drawable-en-hdpi/transitive.png"); |
| Artifact transitiveResourceToDiscard = |
| getResource("transitive/drawable-en-ldpi/transitive.png"); |
| |
| LocalResourceContainer localResources = |
| LocalResourceContainer.forResources( |
| errorConsumer, ImmutableList.of(localResourceToKeep, localResourceToDiscard)); |
| |
| ResourceDependencies resourceDependencies = |
| ResourceDependencies.empty() |
| .withResources( |
| getResourceContainers( |
| ImmutableList.of(transitiveResourceToDiscard), |
| ImmutableList.of(transitiveResourceToKeep)), |
| getResourceContainers( |
| ImmutableList.of(directResourceToDiscard), |
| ImmutableList.of(directResourceToKeep)), |
| new NestedSetBuilder<Artifact>(Order.NAIVE_LINK_ORDER) |
| .add(directResourceToDiscard) |
| .add(directResourceToKeep) |
| .addTransitive( |
| NestedSetBuilder.create( |
| Order.NAIVE_LINK_ORDER, |
| transitiveResourceToDiscard, |
| transitiveResourceToKeep)) |
| .build()); |
| |
| ResourceFilterFactory resourceFilterFactory = |
| makeResourceFilter("en", "hdpi", FilterBehavior.FILTER_IN_ANALYSIS); |
| ResourceFilter filter = |
| resourceFilterFactory.getResourceFilter( |
| errorConsumer, resourceDependencies, localResources); |
| |
| assertThat(localResources.filter(errorConsumer, filter).getResources()) |
| .containsExactly(localResourceToKeep); |
| |
| ResourceDependencies filteredResourceDeps = resourceDependencies.filter(filter); |
| |
| // TODO: Remove - assert was same order before |
| assertThat(resourceDependencies.getTransitiveResources()) |
| .containsAllOf(directResourceToKeep, transitiveResourceToKeep) |
| .inOrder(); |
| |
| assertThat(filteredResourceDeps.getTransitiveResources()) |
| .containsExactly(directResourceToKeep, transitiveResourceToKeep) |
| .inOrder(); |
| |
| List<ResourceContainer> directContainers = |
| filteredResourceDeps.getDirectResourceContainers().toList(); |
| assertThat(directContainers).hasSize(2); |
| |
| ResourceContainer directToDiscard = directContainers.get(0); |
| assertThat(directToDiscard.getResources()).isEmpty(); |
| assertThat(directToDiscard.getResourcesRoots()).isEmpty(); |
| |
| ResourceContainer directToKeep = directContainers.get(1); |
| assertThat(directToKeep.getResources()).containsExactly(directResourceToKeep); |
| assertThat(directToKeep.getResourcesRoots()) |
| .containsExactly( |
| directResourceToKeep.getExecPath().getParentDirectory().getParentDirectory()); |
| |
| List<ResourceContainer> transitiveContainers = |
| filteredResourceDeps.getTransitiveResourceContainers().toList(); |
| assertThat(transitiveContainers).hasSize(2); |
| |
| ResourceContainer transitiveToDiscard = transitiveContainers.get(0); |
| assertThat(transitiveToDiscard.getResources()).isEmpty(); |
| assertThat(transitiveToDiscard.getResourcesRoots()).isEmpty(); |
| |
| ResourceContainer transitiveToKeep = transitiveContainers.get(1); |
| assertThat(transitiveToKeep.getResources()).containsExactly(transitiveResourceToKeep); |
| assertThat(transitiveToKeep.getResourcesRoots()) |
| .containsExactly( |
| transitiveResourceToKeep.getExecPath().getParentDirectory().getParentDirectory()); |
| |
| // We tell the resource processing actions to ignore references to filtered resources from |
| // dependencies. |
| assertThat(resourceFilterFactory.getResourcesToIgnoreInExecution()) |
| .containsExactly("drawable-en-ldpi/direct.png", "drawable-en-ldpi/transitive.png"); |
| } |
| |
| @Test |
| public void testIsPrefilteringFilterInExecution() throws Exception { |
| assertIsPrefiltering(FilterBehavior.FILTER_IN_EXECUTION, false); |
| } |
| |
| @Test |
| public void testIsPrefilteringFilterInAnalysis() throws Exception { |
| assertIsPrefiltering(FilterBehavior.FILTER_IN_ANALYSIS, true); |
| } |
| |
| @Test |
| public void testIsPrefilteringFilterInAnalysisWithDynamicConfiguration() throws Exception { |
| assertIsPrefiltering(FilterBehavior.FILTER_IN_ANALYSIS_WITH_DYNAMIC_CONFIGURATION, true); |
| } |
| |
| private void assertIsPrefiltering(FilterBehavior behavior, boolean expectWhenNonEmpty) |
| throws Exception { |
| // Empty filters should never prefilter |
| assertIsPrefiltering(false, false, behavior).isFalse(); |
| |
| // Prefiltering behavior should be the same regardless of which setting is set |
| assertIsPrefiltering(true, false, behavior).isEqualTo(expectWhenNonEmpty); |
| assertIsPrefiltering(false, true, behavior).isEqualTo(expectWhenNonEmpty); |
| assertIsPrefiltering(true, true, behavior).isEqualTo(expectWhenNonEmpty); |
| } |
| |
| private BooleanSubject assertIsPrefiltering( |
| boolean hasConfigurationFilters, boolean hasDensities, FilterBehavior behavior) |
| throws Exception { |
| return assertThat( |
| makeResourceFilter( |
| hasConfigurationFilters ? "en" : "", hasDensities ? "hdpi" : "", behavior) |
| .isPrefiltering()); |
| } |
| |
| @Test |
| public void testGetOutputDirectorySuffixEmpty() throws Exception { |
| assertThat( |
| makeResourceFilter("", "", FilterBehavior.FILTER_IN_ANALYSIS) |
| .getOutputDirectorySuffix()) |
| .isNull(); |
| } |
| |
| @Test |
| public void testGetOutputDirectoryOnlyFilterConfigurations() throws Exception { |
| String configurationFilters = "en,es-rUS,fr"; |
| assertThat( |
| makeResourceFilter(configurationFilters, "", FilterBehavior.FILTER_IN_ANALYSIS) |
| .getOutputDirectorySuffix()) |
| .isEqualTo(configurationFilters + "_"); |
| } |
| |
| @Test |
| public void testGetOutputDirectoryOnlyDensities() throws Exception { |
| String densities = "hdpi,ldpi,xhdpi"; |
| assertThat( |
| makeResourceFilter("", densities, FilterBehavior.FILTER_IN_ANALYSIS) |
| .getOutputDirectorySuffix()) |
| .isEqualTo("_" + densities); |
| } |
| |
| /** |
| * Only densities is a legal (if unhelpful) resource_configuration_filters settings. Ensure it |
| * produces a different output directory than a similar densities value. |
| */ |
| @Test |
| public void testGetOutputDirectoryDensitiesAreDifferentFromDensityConfigurationFilters() |
| throws Exception { |
| ResourceFilterFactory configurationFilter = |
| makeResourceFilter("hdpi", "", FilterBehavior.FILTER_IN_ANALYSIS); |
| ResourceFilterFactory densityFilter = |
| makeResourceFilter("", "hdpi", FilterBehavior.FILTER_IN_ANALYSIS); |
| |
| assertThat(configurationFilter.getOutputDirectorySuffix()) |
| .isNotEqualTo(densityFilter.getOutputDirectorySuffix()); |
| } |
| |
| @Test |
| public void testGetOutputDirectory() throws Exception { |
| assertThat( |
| makeResourceFilter("en,fr-rCA", "hdpi,ldpi", FilterBehavior.FILTER_IN_ANALYSIS) |
| .getOutputDirectorySuffix()) |
| .isEqualTo("en,fr-rCA_hdpi,ldpi"); |
| } |
| |
| /** |
| * Asserts that identical but differently ordered arguments still produce the same output |
| * directory. If filters are identical, there's no reason to rebuild. |
| */ |
| @Test |
| public void testGetOutputDirectoryDifferentlyOrdered() throws Exception { |
| ResourceFilterFactory first = |
| makeResourceFilter("en,fr", "hdpi,ldpi", FilterBehavior.FILTER_IN_ANALYSIS); |
| ResourceFilterFactory second = |
| makeResourceFilter("fr,en", "ldpi,hdpi", FilterBehavior.FILTER_IN_ANALYSIS); |
| assertThat(first.getOutputDirectorySuffix()).isEqualTo(second.getOutputDirectorySuffix()); |
| } |
| |
| @Test |
| public void testGetOutputDirectoryDuplicated() throws Exception { |
| ResourceFilterFactory duplicated = |
| makeResourceFilter("en,en", "hdpi,hdpi", FilterBehavior.FILTER_IN_ANALYSIS); |
| ResourceFilterFactory normal = |
| makeResourceFilter("en", "hdpi", FilterBehavior.FILTER_IN_ANALYSIS); |
| |
| assertThat(duplicated.getOutputDirectorySuffix()).isEqualTo(normal.getOutputDirectorySuffix()); |
| } |
| |
| private ResourceFilterFactory makeResourceFilter( |
| String resourceConfigurationFilters, String densities, FilterBehavior behavior) |
| throws Exception { |
| return makeResourceFilter( |
| ImmutableList.of(resourceConfigurationFilters), ImmutableList.of(densities), behavior); |
| } |
| |
| private ResourceFilterFactory makeResourceFilter( |
| ImmutableList<String> resourceConfigurationFilters, |
| ImmutableList<String> densities, |
| FilterBehavior behavior) |
| throws Exception { |
| |
| return ResourceFilterFactory.forBaseAndAttrs( |
| ResourceFilterFactory.empty(behavior), |
| getAttributeMap(resourceConfigurationFilters, densities)); |
| } |
| |
| private AttributeMap getAttributeMap( |
| ImmutableList<String> resourceConfigurationFilters, ImmutableList<String> densities) { |
| return FakeAttributeMapper.builder() |
| .withStringList( |
| ResourceFilterFactory.RESOURCE_CONFIGURATION_FILTERS_NAME, resourceConfigurationFilters) |
| .withStringList(ResourceFilterFactory.DENSITIES_NAME, densities) |
| .build(); |
| } |
| |
| private ImmutableList<Artifact> doFilter( |
| ResourceFilterFactory resourceFilterFactory, ImmutableList<Artifact> artifacts) |
| throws RuleErrorException { |
| LocalResourceContainer localResourceContainer = |
| LocalResourceContainer.forResources(errorConsumer, artifacts); |
| |
| ResourceDependencies resourceDeps = ResourceDependencies.empty(); |
| |
| ResourceFilter filter = |
| resourceFilterFactory.getResourceFilter( |
| errorConsumer, resourceDeps, localResourceContainer); |
| |
| assertThat(resourceDeps.filter(filter)).isSameAs(resourceDeps); |
| |
| return localResourceContainer.filter(errorConsumer, filter).getResources(); |
| } |
| |
| @Test |
| public void testWithAttrsFromAttrsNotSpecified() throws Exception { |
| assertThat( |
| ResourceFilterFactory.forBaseAndAttrs( |
| ResourceFilterFactory.empty(FilterBehavior.FILTER_IN_ANALYSIS), |
| FakeAttributeMapper.empty()) |
| .hasFilters()) |
| .isFalse(); |
| } |
| |
| @Test |
| public void testGetTopLevelTransitionFilterInExecution() throws Exception { |
| assertThat( |
| getTopLevelTransition( |
| ImmutableList.of("en"), |
| ImmutableList.of("hdpi"), |
| FilterBehavior.FILTER_IN_EXECUTION, |
| true)) |
| .isNull(); |
| } |
| |
| @Test |
| public void testGetTopLevelTransitionFilterInAnalysis() throws Exception { |
| assertThat( |
| getTopLevelTransition( |
| ImmutableList.of("en"), |
| ImmutableList.of("hdpi"), |
| FilterBehavior.FILTER_IN_ANALYSIS, |
| true)) |
| .isNull(); |
| } |
| |
| @Test |
| public void testGetTopLevelTransitionNotBinary() throws Exception { |
| assertThat( |
| getTopLevelTransition( |
| ImmutableList.of("en"), |
| ImmutableList.of("hdpi"), |
| FilterBehavior.FILTER_IN_ANALYSIS_WITH_DYNAMIC_CONFIGURATION, |
| false)) |
| .isSameAs( |
| ResourceFilterFactory.REMOVE_DYNAMICALLY_CONFIGURED_RESOURCE_FILTERING_TRANSITION); |
| } |
| |
| @Test |
| public void testGetTopLevelTransitionNoFilters() throws Exception { |
| assertThat( |
| getTopLevelTransition( |
| ImmutableList.<String>of(), |
| ImmutableList.<String>of(), |
| FilterBehavior.FILTER_IN_ANALYSIS_WITH_DYNAMIC_CONFIGURATION, |
| true)) |
| .isSameAs( |
| ResourceFilterFactory.REMOVE_DYNAMICALLY_CONFIGURED_RESOURCE_FILTERING_TRANSITION); |
| } |
| |
| @Test |
| public void testGetTopLevelTransition() throws Exception { |
| ImmutableList<String> resourceConfigurationFilters = ImmutableList.of("en"); |
| ImmutableList<String> densities = ImmutableList.of("hdpi"); |
| PatchTransition transition = |
| getTopLevelTransition( |
| resourceConfigurationFilters, |
| densities, |
| FilterBehavior.FILTER_IN_ANALYSIS_WITH_DYNAMIC_CONFIGURATION, |
| true); |
| |
| assertThat(transition).isInstanceOf(AddDynamicallyConfiguredResourceFilteringTransition.class); |
| |
| AddDynamicallyConfiguredResourceFilteringTransition addTransition = |
| (AddDynamicallyConfiguredResourceFilteringTransition) transition; |
| ResourceFilterFactory foundFilter = |
| ResourceFilterFactory.forBaseAndAttrs( |
| ResourceFilterFactory.empty( |
| FilterBehavior.FILTER_IN_ANALYSIS_WITH_DYNAMIC_CONFIGURATION), |
| addTransition.getAttrs()); |
| |
| ResourceFilterFactory expectedFilter = |
| makeResourceFilter( |
| resourceConfigurationFilters, |
| densities, |
| FilterBehavior.FILTER_IN_ANALYSIS_WITH_DYNAMIC_CONFIGURATION); |
| assertThat(foundFilter).isEqualTo(expectedFilter); |
| } |
| |
| private PatchTransition getTopLevelTransition( |
| ImmutableList<String> resourceConfigurationFilters, |
| ImmutableList<String> densities, |
| FilterBehavior behavior, |
| boolean isBinary) |
| throws Exception { |
| AttributeMap attrs = getAttributeMap(resourceConfigurationFilters, densities); |
| return makeResourceFilter("", "", behavior) |
| .getTopLevelPatchTransition(isBinary ? "android_binary" : "android_library", attrs); |
| } |
| |
| @Test |
| public void testRemoveDynamicConfigurationTransition() throws Exception { |
| assertPatchTransition( |
| makeResourceFilter( |
| "en", "ldpi", FilterBehavior.FILTER_IN_ANALYSIS_WITH_DYNAMIC_CONFIGURATION), |
| ResourceFilterFactory.REMOVE_DYNAMICALLY_CONFIGURED_RESOURCE_FILTERING_TRANSITION, |
| ResourceFilterFactory.empty(FilterBehavior.FILTER_IN_ANALYSIS)); |
| } |
| |
| @Test |
| public void testAddDynamicConfigurationTransitionDynamicConfiguration() throws Exception { |
| ImmutableList<String> resourceConfigurationFilters = ImmutableList.of("en", "es-rUS", "fr"); |
| ImmutableList<String> densities = ImmutableList.of("ldpi", "hdpi"); |
| |
| AttributeMap attrs = getAttributeMap(resourceConfigurationFilters, densities); |
| |
| assertPatchTransition( |
| ResourceFilterFactory.empty(FilterBehavior.FILTER_IN_ANALYSIS_WITH_DYNAMIC_CONFIGURATION), |
| new ResourceFilterFactory.AddDynamicallyConfiguredResourceFilteringTransition(attrs), |
| makeResourceFilter( |
| resourceConfigurationFilters, |
| densities, |
| FilterBehavior.FILTER_IN_ANALYSIS_WITH_DYNAMIC_CONFIGURATION)); |
| } |
| |
| private void assertPatchTransition( |
| ResourceFilterFactory oldResourceFilterFactory, |
| PatchTransition transition, |
| ResourceFilterFactory expectedNewResourceFilterFactory) { |
| AndroidConfiguration.Options oldAndroidOptions = getAndroidOptions(oldResourceFilterFactory); |
| |
| BuildOptions oldOptions = BuildOptions.builder().add(oldAndroidOptions).build(); |
| BuildOptions newOptions = transition.apply(oldOptions); |
| |
| // The old options should not have been changed |
| assertThat(oldAndroidOptions.resourceFilterFactory).isSameAs(oldResourceFilterFactory); |
| assertThat(oldAndroidOptions).isEqualTo(getAndroidOptions(oldResourceFilterFactory)); |
| |
| // Besides the ResourceFilterFactory, the new options should be the same as the old ones |
| assertThat(newOptions.getOptions()).hasSize(1); |
| AndroidConfiguration.Options newAndroidOptions = |
| newOptions.get(AndroidConfiguration.Options.class); |
| assertThat(newAndroidOptions).isEqualTo(getAndroidOptions(expectedNewResourceFilterFactory)); |
| } |
| |
| private AndroidConfiguration.Options getAndroidOptions( |
| ResourceFilterFactory resourceFilterFactory) { |
| AndroidConfiguration.Options androidOptions = |
| (AndroidConfiguration.Options) new AndroidConfiguration.Options().getDefault(); |
| androidOptions.resourceFilterFactory = resourceFilterFactory; |
| |
| return androidOptions; |
| } |
| } |