/* * Copyright 2012-present Facebook, Inc. * * 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.facebook.buck.android; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import com.facebook.buck.android.FilterResourcesStep.ImageScaler; import com.facebook.buck.file.ProjectFilesystemMatchers; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.step.ExecutionContext; import com.facebook.buck.step.TestExecutionContext; import com.facebook.buck.testutil.FakeProjectFilesystem; import com.facebook.buck.util.DefaultFilteredDirectoryCopier; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Iterator; import java.util.regex.Matcher; import org.hamcrest.Matchers; import org.junit.Test; public class FilterResourcesStepTest { private Path getDrawableFile(String dir, String qualifier, String filename) { return Paths.get(dir, String.format("drawable-%s", qualifier), filename); } @Test public void testFilterDrawables() throws IOException, InterruptedException { final String first = "/first-path/res"; final Path baseDestination = Paths.get("/dest"); final ImmutableBiMap<Path, Path> inResDirToOutResDirMap = ImmutableBiMap.of( Paths.get(first), baseDestination.resolve("1"), Paths.get("/second-path/res"), baseDestination.resolve("2"), Paths.get("/third-path/res"), baseDestination.resolve("3")); ProjectFilesystem filesystem = new FakeProjectFilesystem(); Path scaleSource = getDrawableFile(first, "xhdpi", "other.png"); filesystem.createNewFile(scaleSource); // Create our drawables. for (Path dir : inResDirToOutResDirMap.keySet()) { for (String qualifier : ImmutableSet.of("mdpi", "hdpi", "xhdpi")) { filesystem.createNewFile(getDrawableFile(dir.toString(), qualifier, "some.png")); } } FilterResourcesStep command = new FilterResourcesStep( filesystem, inResDirToOutResDirMap, /* filterByDensity */ true, /* enableStringWhitelisting */ false, /* whitelistedStringDirs */ ImmutableSet.of(), /* locales */ ImmutableSet.of(), DefaultFilteredDirectoryCopier.getInstance(), ImmutableSet.of(ResourceFilters.Density.MDPI), FilterResourcesStep.DefaultDrawableFinder.getInstance(), new ImageScaler() { @Override public boolean isAvailable(ExecutionContext context) { return true; } @Override public void scale( double factor, Path source, Path destination, ExecutionContext context) throws IOException, InterruptedException {} }); // We'll use this to verify the source->destination mappings created by the command. ImmutableMap.Builder<Path, Path> dirMapBuilder = ImmutableMap.builder(); Iterator<Path> destIterator = inResDirToOutResDirMap.values().iterator(); for (Path dir : inResDirToOutResDirMap.keySet()) { Path nextDestination = destIterator.next(); dirMapBuilder.put(dir, nextDestination); // Verify that destination path requirements are observed. assertEquals(baseDestination.toFile(), nextDestination.getParent().toFile()); } // Execute command. assertThat(command.execute(TestExecutionContext.newInstance()).getExitCode(), Matchers.is(0)); assertTrue(filesystem.isFile(baseDestination.resolve("1/drawable-mdpi/some.png"))); } @Test public void testWhitelistFilter() throws IOException, InterruptedException { Predicate<Path> filePredicate = getTestPathPredicate( true, ImmutableSet.of(Paths.get("com/whitelisted/res")), ImmutableSet.of()); assertTrue(filePredicate.apply(Paths.get("com/example/res/drawables/image.png"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values/strings.xml"))); assertTrue(filePredicate.apply(Paths.get("com/whitelisted/res/values-af/strings.xml"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values-af/integers.xml"))); assertFalse(filePredicate.apply(Paths.get("com/example/res/values-af/strings.xml"))); } @Test public void testFilterLocales() throws IOException, InterruptedException { Predicate<Path> filePredicate = getTestPathPredicate(false, ImmutableSet.of(), ImmutableSet.of("es", "es_US")); assertTrue(filePredicate.apply(Paths.get("com/example/res/drawables/image.png"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values/strings.xml"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values-es/strings.xml"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values-es-rUS/strings.xml"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values-es/integers.xml"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values-en/integers.xml"))); assertFalse(filePredicate.apply(Paths.get("com/example/res/values-en/strings.xml"))); assertFalse(filePredicate.apply(Paths.get("com/example/res/values-es-rES/strings.xml"))); } @Test public void testUsingWhitelistIgnoresLocaleFilter() throws IOException, InterruptedException { Predicate<Path> filePredicate = getTestPathPredicate( true, ImmutableSet.of(Paths.get("com/example/res")), ImmutableSet.of("es", "es_US")); assertTrue(filePredicate.apply(Paths.get("com/example/res/drawables/image.png"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values/strings.xml"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values-es/strings.xml"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values-es-rUS/strings.xml"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values-es/integers.xml"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values-en/integers.xml"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values-en/strings.xml"))); assertTrue(filePredicate.apply(Paths.get("com/example/res/values-es-rES/strings.xml"))); } @Test public void testNonEnglishStringsPathRegex() { assertMatchesRegex("path/res/values-es/strings.xml", "es", null); assertNotMatchesRegex("res/values/strings.xml"); assertNotMatchesRegex("res/values-es/integers.xml"); assertNotMatchesRegex("res/values-/strings.xml"); assertMatchesRegex("/res/values-es/strings.xml", "es", null); assertNotMatchesRegex("rootres/values-es/strings.xml"); assertMatchesRegex("root/res/values-es-rUS/strings.xml", "es", "US"); } @Test public void nonDrawableResourcesFiltered() throws IOException, InterruptedException { final ResourceFilters.Density targetDensity = ResourceFilters.Density.MDPI; final ResourceFilters.Density excludedDensity = ResourceFilters.Density.LDPI; final String file = "somefile"; final Path resDir = Paths.get("res"); final Path resOutDir = Paths.get("res-out"); ProjectFilesystem filesystem = new FakeProjectFilesystem(); filesystem.mkdirs(resDir); for (String folderName : ResourceFilters.SUPPORTED_RESOURCE_DIRECTORIES) { if (folderName.equals("drawable")) { continue; } filesystem.createNewFile( resDir.resolve(String.format("%s-%s", folderName, targetDensity)).resolve(file)); filesystem.createNewFile( resDir.resolve(String.format("%s-%s", folderName, excludedDensity)).resolve(file)); } FilterResourcesStep command = new FilterResourcesStep( filesystem, ImmutableBiMap.of(resDir, resOutDir), /* filterByDPI */ true, /* enableStringWhitelisting */ false, /* whitelistedStringDirs */ ImmutableSet.of(), /* locales */ ImmutableSet.of(), DefaultFilteredDirectoryCopier.getInstance(), ImmutableSet.of(targetDensity), FilterResourcesStep.DefaultDrawableFinder.getInstance(), /* imageScaler */ null); command.execute(null); for (String folderName : ResourceFilters.SUPPORTED_RESOURCE_DIRECTORIES) { if (folderName.equals("drawable")) { continue; } assertThat( filesystem, ProjectFilesystemMatchers.pathExists( resOutDir.resolve(String.format("%s-%s", folderName, targetDensity)).resolve(file))); assertThat( filesystem, ProjectFilesystemMatchers.pathDoesNotExist( resOutDir .resolve(String.format("%s-%s", folderName, excludedDensity)) .resolve(file))); } } @Test public void xmlDrawableResourcesFiltered() throws IOException, InterruptedException { final ResourceFilters.Density targetDensity = ResourceFilters.Density.MDPI; final ResourceFilters.Density excludedDensity = ResourceFilters.Density.LDPI; final String file = "somefile.xml"; final Path resDir = Paths.get("res"); final Path resOutDir = Paths.get("res-out"); ProjectFilesystem filesystem = new FakeProjectFilesystem(); filesystem.mkdirs(resDir); filesystem.createNewFile( resDir.resolve(String.format("drawable-%s", targetDensity)).resolve(file)); filesystem.createNewFile( resDir.resolve(String.format("drawable-%s", excludedDensity)).resolve(file)); FilterResourcesStep command = new FilterResourcesStep( filesystem, ImmutableBiMap.of(resDir, resOutDir), /* filterByDPI */ true, /* enableStringWhitelisting */ false, /* whitelistedStringDirs */ ImmutableSet.of(), /* locales */ ImmutableSet.of(), DefaultFilteredDirectoryCopier.getInstance(), ImmutableSet.of(targetDensity), FilterResourcesStep.DefaultDrawableFinder.getInstance(), /* imageScaler */ null); command.execute(null); assertThat( filesystem, ProjectFilesystemMatchers.pathExists( resOutDir.resolve(String.format("drawable-%s", targetDensity)).resolve(file))); assertThat( filesystem, ProjectFilesystemMatchers.pathDoesNotExist( resOutDir.resolve(String.format("drawable-%s", excludedDensity)).resolve(file))); } @Test public void fallsBackToDefaultWhenAllTargetsNotPresent() throws IOException, InterruptedException { final ResourceFilters.Density targetDensity = ResourceFilters.Density.MDPI; final ResourceFilters.Density providedDensity = ResourceFilters.Density.TVDPI; final String file = "somefile"; final Path resDir = Paths.get("res/foo/bar"); final Path resOutDir = Paths.get("res-out"); ProjectFilesystem filesystem = new FakeProjectFilesystem(); filesystem.mkdirs(resDir); for (String folderName : ResourceFilters.SUPPORTED_RESOURCE_DIRECTORIES) { if (folderName.equals("drawable") || folderName.equals("values")) { continue; } filesystem.createNewFile(resDir.resolve(folderName).resolve(file)); filesystem.createNewFile( resDir.resolve(String.format("%s-%s", folderName, providedDensity)).resolve(file)); } FilterResourcesStep command = new FilterResourcesStep( filesystem, ImmutableBiMap.of(resDir, resOutDir), /* filterByDPI */ true, /* enableStringWhitelisting */ false, /* whitelistedStringDirs */ ImmutableSet.of(), /* locales */ ImmutableSet.of(), DefaultFilteredDirectoryCopier.getInstance(), ImmutableSet.of(targetDensity), FilterResourcesStep.DefaultDrawableFinder.getInstance(), /* imageScaler */ null); command.execute(null); for (String folderName : ResourceFilters.SUPPORTED_RESOURCE_DIRECTORIES) { if (folderName.equals("drawable") || folderName.equals("values")) { continue; } assertThat( filesystem, ProjectFilesystemMatchers.pathExists(resOutDir.resolve(folderName).resolve(file))); assertThat( filesystem, ProjectFilesystemMatchers.pathDoesNotExist( resOutDir.resolve(String.format("%s-%s", folderName, targetDensity)))); assertThat( filesystem, ProjectFilesystemMatchers.pathDoesNotExist( resOutDir .resolve(String.format("%s-%s", folderName, providedDensity)) .resolve(file))); } } @Test public void fallsBackToDefaultWhenOneTargetNotPresent() throws IOException, InterruptedException { final ResourceFilters.Density targetDensityIncluded = ResourceFilters.Density.MDPI; final ResourceFilters.Density targetDensityExcluded = ResourceFilters.Density.XHDPI; final String file = "somefile"; final Path resDir = Paths.get("res/foo/bar"); final Path resOutDir = Paths.get("res-out"); ProjectFilesystem filesystem = new FakeProjectFilesystem(); filesystem.mkdirs(resDir); for (String folderName : ResourceFilters.SUPPORTED_RESOURCE_DIRECTORIES) { if (folderName.equals("drawable") || folderName.equals("values")) { continue; } filesystem.createNewFile(resDir.resolve(folderName).resolve(file)); filesystem.createNewFile( resDir.resolve(String.format("%s-%s", folderName, targetDensityIncluded)).resolve(file)); } FilterResourcesStep command = new FilterResourcesStep( filesystem, ImmutableBiMap.of(resDir, resOutDir), /* filterByDPI */ true, /* enableStringWhitelisting */ false, /* whitelistedStringDirs */ ImmutableSet.of(), /* locales */ ImmutableSet.of(), DefaultFilteredDirectoryCopier.getInstance(), ImmutableSet.of(targetDensityIncluded, targetDensityExcluded), FilterResourcesStep.DefaultDrawableFinder.getInstance(), /* imageScaler */ null); command.execute(null); for (String folderName : ResourceFilters.SUPPORTED_RESOURCE_DIRECTORIES) { if (folderName.equals("drawable") || folderName.equals("values")) { continue; } assertThat( filesystem, ProjectFilesystemMatchers.pathExists(resOutDir.resolve(folderName).resolve(file))); assertThat( filesystem, ProjectFilesystemMatchers.pathExists( resOutDir .resolve(String.format("%s-%s", folderName, targetDensityIncluded)) .resolve(file))); assertThat( filesystem, ProjectFilesystemMatchers.pathDoesNotExist( resOutDir.resolve(String.format("%s-%s", folderName, targetDensityExcluded)))); } } @Test public void valuesAlwaysIncludesFallback() throws IOException, InterruptedException { final ResourceFilters.Density targetDensity = ResourceFilters.Density.MDPI; final String file = "somefile.xml"; final Path resDir = Paths.get("res/foo/bar"); final Path resOutDir = Paths.get("res-out"); ProjectFilesystem filesystem = new FakeProjectFilesystem(); filesystem.mkdirs(resDir); filesystem.createNewFile(resDir.resolve("values").resolve(file)); filesystem.createNewFile( resDir.resolve(String.format("values-%s", targetDensity)).resolve(file)); FilterResourcesStep command = new FilterResourcesStep( filesystem, ImmutableBiMap.of(resDir, resOutDir), /* filterByDPI */ true, /* enableStringWhitelisting */ false, /* whitelistedStringDirs */ ImmutableSet.of(), /* locales */ ImmutableSet.of(), DefaultFilteredDirectoryCopier.getInstance(), ImmutableSet.of(targetDensity), FilterResourcesStep.DefaultDrawableFinder.getInstance(), /* imageScaler */ null); command.execute(null); assertThat( filesystem, ProjectFilesystemMatchers.pathExists(resOutDir.resolve("values").resolve(file))); assertThat( filesystem, ProjectFilesystemMatchers.pathExists( resOutDir.resolve(String.format("values-%s", targetDensity)).resolve(file))); } private static void assertMatchesRegex(String path, String language, String country) { Matcher matcher = FilterResourcesStep.NON_ENGLISH_STRINGS_FILE_PATH.matcher(path); assertTrue(matcher.matches()); assertEquals(language, matcher.group(1)); assertEquals(country, matcher.group(2)); } private static void assertNotMatchesRegex(String path) { assertFalse(FilterResourcesStep.NON_ENGLISH_STRINGS_FILE_PATH.matcher(path).matches()); } private static Predicate<Path> getTestPathPredicate( boolean enableStringWhitelisting, ImmutableSet<Path> whitelistedStringDirs, ImmutableSet<String> locales) throws IOException, InterruptedException { FilterResourcesStep step = new FilterResourcesStep( null, /* inResDirToOutResDirMap */ ImmutableBiMap.of(), /* filterByDensity */ false, /* enableStringWhitelisting */ enableStringWhitelisting, /* whitelistedStringDirs */ whitelistedStringDirs, /* locales */ locales, DefaultFilteredDirectoryCopier.getInstance(), /* targetDensities */ null, FilterResourcesStep.DefaultDrawableFinder.getInstance(), /* imageScaler */ null); return step.getFilteringPredicate(TestExecutionContext.newInstance()); } }