| // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| package com.android.tools.r8.shaking; |
| |
| import static com.android.tools.r8.DiagnosticsChecker.checkDiagnostics; |
| import static com.android.tools.r8.shaking.ProguardConfigurationSourceStrings.createConfigurationForTesting; |
| import static org.hamcrest.core.StringContains.containsString; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertSame; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import com.android.tools.r8.TestBase; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.ToolHelper.ProcessResult; |
| import com.android.tools.r8.graph.ClassAccessFlags; |
| import com.android.tools.r8.graph.DexItemFactory; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.FieldAccessFlags; |
| import com.android.tools.r8.graph.MethodAccessFlags; |
| import com.android.tools.r8.position.Position; |
| import com.android.tools.r8.position.TextRange; |
| import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards; |
| import com.android.tools.r8.utils.AbortException; |
| import com.android.tools.r8.utils.DefaultDiagnosticsHandler; |
| import com.android.tools.r8.utils.FileUtils; |
| import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode; |
| import com.android.tools.r8.utils.KeepingDiagnosticHandler; |
| import com.android.tools.r8.utils.Reporter; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import com.google.common.collect.ImmutableList; |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.function.BiConsumer; |
| import java.util.function.Function; |
| import java.util.function.Supplier; |
| import java.util.regex.Matcher; |
| import java.util.stream.Collectors; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| class EmptyMainClassForProguardTests { |
| |
| public static void main(String[] args) { |
| } |
| } |
| |
| public class ProguardConfigurationParserTest extends TestBase { |
| |
| private static final String VALID_PROGUARD_DIR = "src/test/proguard/valid/"; |
| private static final String INVALID_PROGUARD_DIR = "src/test/proguard/invalid/"; |
| private static final String PROGUARD_SPEC_FILE = VALID_PROGUARD_DIR + "proguard.flags"; |
| private static final String MULTIPLE_NAME_PATTERNS_FILE = |
| VALID_PROGUARD_DIR + "multiple-name-patterns.flags"; |
| private static final String ACCESS_FLAGS_FILE = VALID_PROGUARD_DIR + "access-flags.flags"; |
| private static final String WHY_ARE_YOU_KEEPING_FILE = |
| VALID_PROGUARD_DIR + "why-are-you-keeping.flags"; |
| private static final String ASSUME_NO_SIDE_EFFECTS = |
| VALID_PROGUARD_DIR + "assume-no-side-effects.flags"; |
| private static final String ASSUME_NO_SIDE_EFFECTS_WITH_RETURN_VALUE = |
| VALID_PROGUARD_DIR + "assume-no-side-effects-with-return-value.flags"; |
| private static final String ASSUME_VALUES_WITH_RETURN_VALUE = |
| VALID_PROGUARD_DIR + "assume-values-with-return-value.flags"; |
| private static final String INCLUDING = |
| VALID_PROGUARD_DIR + "including.flags"; |
| private static final String INVALID_INCLUDING_1 = |
| INVALID_PROGUARD_DIR + "including-1.flags"; |
| private static final String INVALID_INCLUDING_2 = |
| INVALID_PROGUARD_DIR + "including-2.flags"; |
| private static final String LIBRARY_JARS = |
| VALID_PROGUARD_DIR + "library-jars.flags"; |
| private static final String LIBRARY_JARS_WIN = |
| VALID_PROGUARD_DIR + "library-jars-win.flags"; |
| private static final String SEEDS = |
| VALID_PROGUARD_DIR + "seeds.flags"; |
| private static final String SEEDS_2 = |
| VALID_PROGUARD_DIR + "seeds-2.flags"; |
| private static final String VERBOSE = |
| VALID_PROGUARD_DIR + "verbose.flags"; |
| private static final String KEEPDIRECTORIES = |
| VALID_PROGUARD_DIR + "keepdirectories.flags"; |
| private static final String DONT_OBFUSCATE = |
| VALID_PROGUARD_DIR + "dontobfuscate.flags"; |
| private static final String PACKAGE_OBFUSCATION_1 = |
| VALID_PROGUARD_DIR + "package-obfuscation-1.flags"; |
| private static final String PACKAGE_OBFUSCATION_2 = |
| VALID_PROGUARD_DIR + "package-obfuscation-2.flags"; |
| private static final String PACKAGE_OBFUSCATION_3 = |
| VALID_PROGUARD_DIR + "package-obfuscation-3.flags"; |
| private static final String PACKAGE_OBFUSCATION_4 = |
| VALID_PROGUARD_DIR + "package-obfuscation-4.flags"; |
| private static final String PACKAGE_OBFUSCATION_5 = |
| VALID_PROGUARD_DIR + "package-obfuscation-5.flags"; |
| private static final String PACKAGE_OBFUSCATION_6 = |
| VALID_PROGUARD_DIR + "package-obfuscation-6.flags"; |
| private static final String APPLY_MAPPING = |
| VALID_PROGUARD_DIR + "applymapping.flags"; |
| private static final String APPLY_MAPPING_WITHOUT_FILE = |
| INVALID_PROGUARD_DIR + "applymapping-without-file.flags"; |
| private static final String DONT_SHRINK = |
| VALID_PROGUARD_DIR + "dontshrink.flags"; |
| private static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES = |
| VALID_PROGUARD_DIR + "dontskipnonpubliclibraryclasses.flags"; |
| private static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS = |
| VALID_PROGUARD_DIR + "dontskipnonpubliclibraryclassmembers.flags"; |
| private static final String IDENTIFIER_NAME_STRING = |
| VALID_PROGUARD_DIR + "identifiernamestring.flags"; |
| private static final String OVERLOAD_AGGRESIVELY = |
| VALID_PROGUARD_DIR + "overloadaggressively.flags"; |
| private static final String DONT_OPTIMIZE = |
| VALID_PROGUARD_DIR + "dontoptimize.flags"; |
| private static final String DONT_OPTIMIZE_OVERRIDES_PASSES = |
| VALID_PROGUARD_DIR + "dontoptimize-overrides-optimizationpasses.flags"; |
| private static final String OPTIMIZATION_PASSES = |
| VALID_PROGUARD_DIR + "optimizationpasses.flags"; |
| private static final String OPTIMIZATION_PASSES_WITHOUT_N = |
| INVALID_PROGUARD_DIR + "optimizationpasses-without-n.flags"; |
| private static final String SKIP_NON_PUBLIC_LIBRARY_CLASSES = |
| VALID_PROGUARD_DIR + "skipnonpubliclibraryclasses.flags"; |
| private static final String PARSE_AND_SKIP_SINGLE_ARGUMENT = |
| VALID_PROGUARD_DIR + "parse-and-skip-single-argument.flags"; |
| private static final String PRINT_USAGE = |
| VALID_PROGUARD_DIR + "printusage.flags"; |
| private static final String PRINT_USAGE_TO_FILE = |
| VALID_PROGUARD_DIR + "printusage-to-file.flags"; |
| private static final String TARGET = |
| VALID_PROGUARD_DIR + "target.flags"; |
| |
| private Reporter reporter; |
| private KeepingDiagnosticHandler handler; |
| private ProguardConfigurationParser parser; |
| private List<String> whiteSpace = |
| ImmutableList.of("", " ", " ", "\t", " \t", " \t", " \t ", " \t\t \t "); |
| private List<String> lineSeparators = ImmutableList.of("\n", "\r\n"); |
| |
| @Before |
| public void reset() { |
| handler = new KeepingDiagnosticHandler(); |
| reporter = new Reporter(handler); |
| parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| } |
| |
| @Before |
| public void resetAllowPartiallyImplementedOptions() { |
| handler = new KeepingDiagnosticHandler(); |
| reporter = new Reporter(handler); |
| parser = new ProguardConfigurationParser(new DexItemFactory(), reporter, false, false); |
| } |
| |
| @Before |
| public void resetAllowTestOptions() { |
| handler = new KeepingDiagnosticHandler(); |
| reporter = new Reporter(handler); |
| parser = new ProguardConfigurationParser(new DexItemFactory(), reporter, true, true); |
| } |
| |
| @Test |
| public void parse() throws Exception { |
| ProguardConfigurationParser parser; |
| |
| // Parse from file. |
| parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(PROGUARD_SPEC_FILE)); |
| verifyParserEndsCleanly(); |
| List<ProguardConfigurationRule> rules = parser.getConfig().getRules(); |
| assertEquals(24, rules.size()); |
| assertEquals(1, rules.get(0).getMemberRules().size()); |
| |
| // Parse from strings. |
| parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| List<String> lines = FileUtils.readAllLines(Paths.get(PROGUARD_SPEC_FILE)); |
| parser.parse(createConfigurationForTesting(lines)); |
| rules = parser.getConfig().getRules(); |
| assertEquals(24, rules.size()); |
| assertEquals(1, rules.get(0).getMemberRules().size()); |
| } |
| |
| @Test |
| public void parseMultipleNamePatterns() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(MULTIPLE_NAME_PATTERNS_FILE)); |
| verifyParserEndsCleanly(); |
| List<ProguardConfigurationRule> rules = parser.getConfig().getRules(); |
| assertEquals(1, rules.size()); |
| ProguardConfigurationRule rule = rules.get(0); |
| assertEquals(1, rule.getMemberRules().size()); |
| assertEquals("com.company.hello.**,com.company.world.**", rule.getClassNames().toString()); |
| assertEquals(ProguardKeepRuleType.KEEP, ((ProguardKeepRule) rule).getType()); |
| assertTrue(rule.getInheritanceIsExtends()); |
| assertEquals("some.library.Class", rule.getInheritanceClassName().toString()); |
| ProguardMemberRule memberRule = rule.getMemberRules().iterator().next(); |
| assertTrue(memberRule.getAccessFlags().isProtected()); |
| assertEquals(ProguardNameMatcher.create( |
| IdentifierPatternWithWildcards.withoutWildcards("getContents")), memberRule.getName()); |
| assertEquals("java.lang.Object[][]", memberRule.getType().toString()); |
| assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType()); |
| assertEquals(0, memberRule.getArguments().size()); |
| } |
| |
| @Test |
| public void parseNonJavaIdentifiers() throws Exception { |
| DexItemFactory dexItemFactory = new DexItemFactory(); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(dexItemFactory, |
| new Reporter(new DefaultDiagnosticsHandler())); |
| String nonJavaIdentifiers = |
| String.join("\n", ImmutableList.of( |
| "-keep class -package-.-ClassNameWithDash-{", |
| " -NameWithDash- -field-;", |
| " p-.-OtherNameWithDash- -method-(-p.-WithDash-, -package-.-ClassNameWithDash-[]); ", |
| "}")); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(nonJavaIdentifiers))); |
| verifyParserEndsCleanly(); |
| List<ProguardConfigurationRule> rules = parser.getConfig().getRules(); |
| assertEquals(1, rules.size()); |
| assertEquals(ProguardClassType.CLASS, rules.get(0).getClassType()); |
| assertEquals(1, rules.get(0).getClassNames().size()); |
| List<DexType> classTypes = rules.get(0).getClassNames().asSpecificDexTypes(); |
| assertEquals(1, classTypes.size()); |
| assertSame(dexItemFactory.createType("L-package-/-ClassNameWithDash-;"), classTypes.get(0)); |
| ProguardConfigurationRule rule = rules.get(0); |
| assertEquals(2, rule.getMemberRules().size()); |
| int matches = 0; |
| for (ProguardMemberRule memberRule : rule.getMemberRules()) { |
| if (memberRule.getRuleType() == ProguardMemberType.FIELD) { |
| assertTrue(memberRule.getName().matches("-field-")); |
| matches |= 0x01; |
| } else { |
| assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType()); |
| assertTrue(memberRule.getName().matches("-method-")); |
| assertFalse(memberRule.getArguments().get(0).getSpecificType().isArrayType()); |
| assertSame(dexItemFactory.createType("L-p/-WithDash-;"), |
| memberRule.getArguments().get(0).getSpecificType()); |
| assertSame(dexItemFactory.createType("[L-package-/-ClassNameWithDash-;"), |
| memberRule.getArguments().get(1).getSpecificType()); |
| matches |= 0x02; |
| } |
| } |
| assertEquals(0x03, matches); |
| } |
| |
| private void testDontXXX(String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) |
| throws Exception { |
| DexItemFactory dexItemFactory = new DexItemFactory(); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(dexItemFactory, reporter); |
| String configuration = "-dont" + xxx + " !foobar,*bar"; |
| parser.parse(createConfigurationForTesting(ImmutableList.of(configuration))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertFalse(pattern.apply(config).matches(dexItemFactory.createType("Lboobaz;"))); |
| assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lboobar;"))); |
| assertFalse(pattern.apply(config).matches(dexItemFactory.createType("Lfoobar;"))); |
| } |
| |
| @Test |
| public void testDontXXX() throws Exception { |
| testDontXXX("warn", ProguardConfiguration::getDontWarnPatterns); |
| testDontXXX("note", ProguardConfiguration::getDontNotePatterns); |
| } |
| |
| private void testDontXXXMultiple( |
| String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) throws Exception { |
| DexItemFactory dexItemFactory = new DexItemFactory(); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(dexItemFactory, reporter); |
| List<String> configuration1 = ImmutableList.of("-dont" + xxx + " foo.**, bar.**"); |
| List<String> configuration2 = ImmutableList.of("-dont" + xxx + " foo.**", "-dontwarn bar.**"); |
| for (List<String> configuration : ImmutableList.of(configuration1, configuration2)) { |
| parser.parse(createConfigurationForTesting(configuration)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lfoo/Bar;"))); |
| assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lfoo/bar7Bar;"))); |
| assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lbar/Foo;"))); |
| } |
| } |
| |
| @Test |
| public void testDontWarnMultiple() throws Exception { |
| testDontXXXMultiple("warn", ProguardConfiguration::getDontWarnPatterns); |
| testDontXXXMultiple("note", ProguardConfiguration::getDontNotePatterns); |
| } |
| |
| private void testDontXXXAllExplicitly( |
| String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) throws Exception { |
| DexItemFactory dexItemFactory = new DexItemFactory(); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(dexItemFactory, reporter); |
| String dontwarnAll = "-dont" + xxx + " *"; |
| parser.parse(createConfigurationForTesting(ImmutableList.of(dontwarnAll))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lboobaz;"))); |
| assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lboobar;"))); |
| assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lfoobar;"))); |
| } |
| |
| @Test |
| public void testDontWarnAllExplicitly() throws Exception { |
| testDontXXXAllExplicitly("warn", ProguardConfiguration::getDontWarnPatterns); |
| testDontXXXAllExplicitly("note", ProguardConfiguration::getDontNotePatterns); |
| } |
| |
| private void testDontXXXAllImplicitly( |
| String xxx, Function<ProguardConfiguration, ProguardClassFilter> pattern) throws Exception { |
| DexItemFactory dexItemFactory = new DexItemFactory(); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(dexItemFactory, reporter); |
| String dontwarnAll = "-dont" + xxx; |
| String otherOption = "-keep class *"; |
| parser.parse(createConfigurationForTesting(ImmutableList.of(dontwarnAll, otherOption))); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lboobaz;"))); |
| assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lboobar;"))); |
| assertTrue(pattern.apply(config).matches(dexItemFactory.createType("Lfoobar;"))); |
| } |
| |
| @Test |
| public void testDontWarnAllImplicitly() throws Exception { |
| testDontXXXAllImplicitly("warn", ProguardConfiguration::getDontWarnPatterns); |
| testDontXXXAllImplicitly("note", ProguardConfiguration::getDontNotePatterns); |
| } |
| |
| @Test |
| public void parseAccessFlags() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(ACCESS_FLAGS_FILE)); |
| verifyParserEndsCleanly(); |
| List<ProguardConfigurationRule> rules = parser.getConfig().getRules(); |
| assertEquals(1, rules.size()); |
| ProguardConfigurationRule rule = rules.get(0); |
| ClassAccessFlags publicAndFinalFlags = ClassAccessFlags.fromSharedAccessFlags(0); |
| publicAndFinalFlags.setPublic(); |
| publicAndFinalFlags.setFinal(); |
| assertTrue(rule.getClassAccessFlags().containsNone(publicAndFinalFlags)); |
| assertTrue(rule.getNegatedClassAccessFlags().containsAll(publicAndFinalFlags)); |
| ClassAccessFlags abstractFlags = ClassAccessFlags.fromSharedAccessFlags(0); |
| abstractFlags.setAbstract(); |
| assertTrue(rule.getClassAccessFlags().containsAll(abstractFlags)); |
| assertTrue(rule.getNegatedClassAccessFlags().containsNone(abstractFlags)); |
| for (ProguardMemberRule member : rule.getMemberRules()) { |
| if (member.getRuleType() == ProguardMemberType.ALL_FIELDS) { |
| FieldAccessFlags publicFlags = FieldAccessFlags.fromSharedAccessFlags(0); |
| publicFlags.setPublic(); |
| assertTrue(member.getAccessFlags().containsAll(publicFlags)); |
| assertTrue(member.getNegatedAccessFlags().containsNone(publicFlags)); |
| FieldAccessFlags staticFlags = FieldAccessFlags.fromSharedAccessFlags(0); |
| staticFlags.setStatic(); |
| assertTrue(member.getAccessFlags().containsNone(staticFlags)); |
| assertTrue(member.getNegatedAccessFlags().containsAll(staticFlags)); |
| } else { |
| assertTrue(member.getRuleType() == ProguardMemberType.ALL_METHODS); |
| |
| MethodAccessFlags publicNativeFlags = MethodAccessFlags.fromSharedAccessFlags(0, false); |
| publicNativeFlags.setPublic(); |
| publicNativeFlags.setNative(); |
| assertTrue(member.getAccessFlags().containsAll(publicNativeFlags)); |
| assertFalse(member.getNegatedAccessFlags().containsNone(publicNativeFlags)); |
| |
| MethodAccessFlags protectedNativeFlags = MethodAccessFlags.fromSharedAccessFlags(0, false); |
| protectedNativeFlags.setProtected(); |
| protectedNativeFlags.setNative(); |
| assertTrue(member.getAccessFlags().containsAll(protectedNativeFlags)); |
| assertFalse(member.getNegatedAccessFlags().containsNone(protectedNativeFlags)); |
| } |
| } |
| } |
| |
| @Test |
| public void parseWhyAreYouKeeping() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(WHY_ARE_YOU_KEEPING_FILE)); |
| verifyParserEndsCleanly(); |
| List<ProguardConfigurationRule> rules = parser.getConfig().getRules(); |
| assertEquals(1, rules.size()); |
| ProguardConfigurationRule rule = rules.get(0); |
| assertEquals(1, rule.getClassNames().size()); |
| assertEquals("*", rule.getClassNames().toString()); |
| assertTrue(rule.getInheritanceIsExtends()); |
| assertEquals("foo.bar", rule.getInheritanceClassName().toString()); |
| } |
| |
| @Test |
| public void parseAssumeNoSideEffects() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(ASSUME_NO_SIDE_EFFECTS)); |
| verifyParserEndsCleanly(); |
| List<ProguardConfigurationRule> assumeNoSideEffects = parser.getConfig().getRules(); |
| assertEquals(1, assumeNoSideEffects.size()); |
| assumeNoSideEffects.get(0).getMemberRules().forEach(rule -> { |
| assertFalse(rule.hasReturnValue()); |
| }); |
| } |
| |
| @Test |
| public void parseAssumeNoSideEffectsWithReturnValue() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(ASSUME_NO_SIDE_EFFECTS_WITH_RETURN_VALUE)); |
| verifyParserEndsCleanly(); |
| List<ProguardConfigurationRule> assumeNoSideEffects = parser.getConfig().getRules(); |
| assertEquals(1, assumeNoSideEffects.size()); |
| int matches = 0; |
| for (ProguardMemberRule rule : assumeNoSideEffects.get(0).getMemberRules()) { |
| assertTrue(rule.hasReturnValue()); |
| if (rule.getName().matches("returnsTrue") || rule.getName().matches("returnsFalse")) { |
| assertTrue(rule.getReturnValue().isBoolean()); |
| assertFalse(rule.getReturnValue().isValueRange()); |
| assertFalse(rule.getReturnValue().isField()); |
| assertFalse(rule.getReturnValue().isNull()); |
| assertEquals(rule.getName().matches("returnsTrue"), rule.getReturnValue().getBoolean()); |
| matches |= 1 << 0; |
| } else if (rule.getName().matches("returns1")) { |
| assertFalse(rule.getReturnValue().isBoolean()); |
| assertTrue(rule.getReturnValue().isValueRange()); |
| assertFalse(rule.getReturnValue().isField()); |
| assertFalse(rule.getReturnValue().isNull()); |
| assertTrue(rule.getReturnValue().isSingleValue()); |
| assertEquals(1, rule.getReturnValue().getValueRange().getMin()); |
| assertEquals(1, rule.getReturnValue().getValueRange().getMax()); |
| assertEquals(1, rule.getReturnValue().getSingleValue()); |
| matches |= 1 << 1; |
| } else if (rule.getName().matches("returns2To4")) { |
| assertFalse(rule.getReturnValue().isBoolean()); |
| assertTrue(rule.getReturnValue().isValueRange()); |
| assertFalse(rule.getReturnValue().isField()); |
| assertFalse(rule.getReturnValue().isNull()); |
| assertFalse(rule.getReturnValue().isSingleValue()); |
| assertEquals(2, rule.getReturnValue().getValueRange().getMin()); |
| assertEquals(4, rule.getReturnValue().getValueRange().getMax()); |
| matches |= 1 << 2; |
| } else if (rule.getName().matches("returns234To567")) { |
| assertFalse(rule.getReturnValue().isBoolean()); |
| assertTrue(rule.getReturnValue().isValueRange()); |
| assertFalse(rule.getReturnValue().isField()); |
| assertFalse(rule.getReturnValue().isNull()); |
| assertFalse(rule.getReturnValue().isSingleValue()); |
| assertEquals(234, rule.getReturnValue().getValueRange().getMin()); |
| assertEquals(567, rule.getReturnValue().getValueRange().getMax()); |
| matches |= 1 << 3; |
| } else if (rule.getName().matches("returnsField")) { |
| assertFalse(rule.getReturnValue().isBoolean()); |
| assertFalse(rule.getReturnValue().isValueRange()); |
| assertTrue(rule.getReturnValue().isField()); |
| assertFalse(rule.getReturnValue().isNull()); |
| assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString()); |
| assertEquals("int", rule.getReturnValue().getField().type.toString()); |
| assertEquals("X", rule.getReturnValue().getField().name.toString()); |
| matches |= 1 << 4; |
| } else if (rule.getName().matches("returnsNull")) { |
| assertFalse(rule.getReturnValue().isBoolean()); |
| assertFalse(rule.getReturnValue().isValueRange()); |
| assertFalse(rule.getReturnValue().isField()); |
| assertTrue(rule.getReturnValue().isNull()); |
| assertTrue(rule.getReturnValue().isSingleValue()); |
| matches |= 1 << 5; |
| } else { |
| fail("Unexpected"); |
| } |
| } |
| assertEquals((1 << 6) - 1, matches); |
| } |
| |
| @Test |
| public void parseAssumeValuesWithReturnValue() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(ASSUME_VALUES_WITH_RETURN_VALUE)); |
| verifyParserEndsCleanly(); |
| List<ProguardConfigurationRule> assumeValues = parser.getConfig().getRules(); |
| assertEquals(1, assumeValues.size()); |
| int matches = 0; |
| for (ProguardMemberRule rule : assumeValues.get(0).getMemberRules()) { |
| assertTrue(rule.hasReturnValue()); |
| if (rule.getName().matches("isTrue") || rule.getName().matches("isFalse")) { |
| assertTrue(rule.getReturnValue().isBoolean()); |
| assertFalse(rule.getReturnValue().isValueRange()); |
| assertFalse(rule.getReturnValue().isField()); |
| assertFalse(rule.getReturnValue().isNull()); |
| assertEquals(rule.getName().matches("isTrue"), rule.getReturnValue().getBoolean()); |
| matches |= 1 << 0; |
| } else if (rule.getName().matches("is1")) { |
| assertFalse(rule.getReturnValue().isBoolean()); |
| assertTrue(rule.getReturnValue().isValueRange()); |
| assertFalse(rule.getReturnValue().isField()); |
| assertFalse(rule.getReturnValue().isNull()); |
| assertTrue(rule.getReturnValue().isSingleValue()); |
| assertEquals(1, rule.getReturnValue().getValueRange().getMin()); |
| assertEquals(1, rule.getReturnValue().getValueRange().getMax()); |
| assertEquals(1, rule.getReturnValue().getSingleValue()); |
| matches |= 1 << 1; |
| } else if (rule.getName().matches("is2To4")) { |
| assertFalse(rule.getReturnValue().isBoolean()); |
| assertTrue(rule.getReturnValue().isValueRange()); |
| assertFalse(rule.getReturnValue().isField()); |
| assertFalse(rule.getReturnValue().isNull()); |
| assertFalse(rule.getReturnValue().isSingleValue()); |
| assertEquals(2, rule.getReturnValue().getValueRange().getMin()); |
| assertEquals(4, rule.getReturnValue().getValueRange().getMax()); |
| matches |= 1 << 2; |
| } else if (rule.getName().matches("is234To567")) { |
| assertFalse(rule.getReturnValue().isBoolean()); |
| assertTrue(rule.getReturnValue().isValueRange()); |
| assertFalse(rule.getReturnValue().isField()); |
| assertFalse(rule.getReturnValue().isNull()); |
| assertFalse(rule.getReturnValue().isSingleValue()); |
| assertEquals(234, rule.getReturnValue().getValueRange().getMin()); |
| assertEquals(567, rule.getReturnValue().getValueRange().getMax()); |
| matches |= 1 << 3; |
| } else if (rule.getName().matches("isField")) { |
| assertFalse(rule.getReturnValue().isBoolean()); |
| assertFalse(rule.getReturnValue().isValueRange()); |
| assertTrue(rule.getReturnValue().isField()); |
| assertFalse(rule.getReturnValue().isNull()); |
| assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString()); |
| assertEquals("int", rule.getReturnValue().getField().type.toString()); |
| assertEquals("X", rule.getReturnValue().getField().name.toString()); |
| matches |= 1 << 4; |
| } else if (rule.getName().matches("isNull")) { |
| assertFalse(rule.getReturnValue().isBoolean()); |
| assertFalse(rule.getReturnValue().isValueRange()); |
| assertFalse(rule.getReturnValue().isField()); |
| assertTrue(rule.getReturnValue().isNull()); |
| assertTrue(rule.getReturnValue().isSingleValue()); |
| matches |= 1 << 5; |
| } else { |
| fail("Unexpected"); |
| } |
| } |
| assertEquals((1 << 6) - 1, matches); |
| } |
| |
| @Test |
| public void testAdaptClassStrings() throws Exception { |
| DexItemFactory dexItemFactory = new DexItemFactory(); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(dexItemFactory, reporter); |
| String adaptClassStrings = "-adaptclassstrings !foobar,*bar"; |
| parser.parse(createConfigurationForTesting(ImmutableList.of(adaptClassStrings))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertFalse( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobaz;"))); |
| assertTrue( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobar;"))); |
| assertFalse( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lfoobar;"))); |
| } |
| |
| @Test |
| public void testAdaptClassStringsMultiple() throws Exception { |
| DexItemFactory dexItemFactory = new DexItemFactory(); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(dexItemFactory, reporter); |
| List<String> configuration1 = ImmutableList.of("-adaptclassstrings foo.**, bar.**"); |
| List<String> configuration2 = |
| ImmutableList.of("-adaptclassstrings foo.**", "-adaptclassstrings bar.**"); |
| for (List<String> configuration : ImmutableList.of(configuration1, configuration2)) { |
| parser.parse(createConfigurationForTesting(configuration)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lfoo/Bar;"))); |
| assertTrue( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lfoo/bar7Bar;"))); |
| assertTrue( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lbar/Foo;"))); |
| } |
| } |
| |
| @Test |
| public void testAdaptClassStringsAllExplicitly() throws Exception { |
| DexItemFactory dexItemFactory = new DexItemFactory(); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(dexItemFactory, reporter); |
| String adaptAll = "-adaptclassstrings *"; |
| parser.parse(createConfigurationForTesting(ImmutableList.of(adaptAll))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobaz;"))); |
| assertTrue( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobar;"))); |
| assertTrue( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lfoobar;"))); |
| } |
| |
| @Test |
| public void testAdaptClassStringsAllImplicitly() throws Exception { |
| DexItemFactory dexItemFactory = new DexItemFactory(); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(dexItemFactory, reporter); |
| String adaptAll = "-adaptclassstrings"; |
| parser.parse(createConfigurationForTesting(ImmutableList.of(adaptAll))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobaz;"))); |
| assertTrue( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobar;"))); |
| assertTrue( |
| config.getAdaptClassStrings().matches(dexItemFactory.createType("Lfoobar;"))); |
| } |
| |
| @Test |
| public void testIdentifierNameString() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| String config1 = |
| "-identifiernamestring class a.b.c.*GeneratedClass {\n" |
| + " static java.lang.String CONTAINING_TYPE_*;\n" |
| + "}"; |
| String config2 = |
| "-identifiernamestring class x.y.z.ReflectionBasedFactory {\n" |
| + " private static java.lang.reflect.Field field(java.lang.Class,java.lang.String);\n" |
| + "}"; |
| String config3 = |
| "-identifiernamestring class * {\n" |
| + " @my.annotations.IdentifierNameString *;\n" |
| + "}"; |
| parser.parse(createConfigurationForTesting(ImmutableList.of(config1, config2, config3))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| List<ProguardConfigurationRule> identifierNameStrings = config.getRules(); |
| assertEquals(3, identifierNameStrings.size()); |
| assertEquals(1, identifierNameStrings.get(0).getClassNames().size()); |
| assertEquals( |
| "a.b.c.*GeneratedClass", |
| identifierNameStrings.get(0).getClassNames().toString()); |
| identifierNameStrings.get(0).getMemberRules().forEach(memberRule -> { |
| assertTrue(memberRule.getRuleType().includesFields()); |
| assertTrue(memberRule.getName().matches("CONTAINING_TYPE_ABC")); |
| }); |
| assertEquals(1, identifierNameStrings.get(1).getClassNames().size()); |
| assertEquals( |
| "x.y.z.ReflectionBasedFactory", |
| identifierNameStrings.get(1).getClassNames().toString()); |
| identifierNameStrings.get(1).getMemberRules().forEach(memberRule -> { |
| assertTrue(memberRule.getRuleType().includesMethods()); |
| assertTrue(memberRule.getName().matches("field")); |
| }); |
| assertEquals(1, identifierNameStrings.get(2).getClassNames().size()); |
| assertEquals("*", identifierNameStrings.get(2).getClassNames().toString()); |
| identifierNameStrings.get(2).getMemberRules().forEach(memberRule -> { |
| assertEquals(ProguardMemberType.ALL, memberRule.getRuleType()); |
| assertTrue(memberRule.getAnnotation().toString().endsWith("IdentifierNameString")); |
| }); |
| } |
| |
| @Test |
| public void parseDontobfuscate() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(DONT_OBFUSCATE)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertFalse(config.isObfuscating()); |
| } |
| |
| @Test |
| public void parseRepackageClassesEmpty() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(PACKAGE_OBFUSCATION_1)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertEquals(PackageObfuscationMode.REPACKAGE, config.getPackageObfuscationMode()); |
| assertNotNull(config.getPackagePrefix()); |
| assertEquals("", config.getPackagePrefix()); |
| } |
| |
| @Test |
| public void parseRepackageClassesNonEmpty() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(PACKAGE_OBFUSCATION_2)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertEquals(PackageObfuscationMode.REPACKAGE, config.getPackageObfuscationMode()); |
| assertNotNull(config.getPackagePrefix()); |
| assertEquals("p.q.r", config.getPackagePrefix()); |
| } |
| |
| @Test |
| public void parseFlattenPackageHierarchyEmpty() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(PACKAGE_OBFUSCATION_3)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertEquals(PackageObfuscationMode.FLATTEN, config.getPackageObfuscationMode()); |
| assertNotNull(config.getPackagePrefix()); |
| assertEquals("", config.getPackagePrefix()); |
| } |
| |
| @Test |
| public void parseFlattenPackageHierarchyNonEmpty() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(PACKAGE_OBFUSCATION_4)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertEquals(PackageObfuscationMode.FLATTEN, config.getPackageObfuscationMode()); |
| assertNotNull(config.getPackagePrefix()); |
| assertEquals("p.q.r", config.getPackagePrefix()); |
| } |
| |
| @Test |
| public void flattenPackageHierarchyCannotOverrideRepackageClasses() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| Path path = Paths.get(PACKAGE_OBFUSCATION_5); |
| parser.parse(path); |
| checkDiagnostics(handler.warnings, path, 6, 1, |
| "repackageclasses", "overrides", "flattenpackagehierarchy"); |
| ProguardConfiguration config = parser.getConfig(); |
| assertEquals(PackageObfuscationMode.REPACKAGE, config.getPackageObfuscationMode()); |
| assertNotNull(config.getPackagePrefix()); |
| assertEquals("top", config.getPackagePrefix()); |
| } |
| |
| @Test |
| public void repackageClassesOverridesFlattenPackageHierarchy() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| Path path = Paths.get(PACKAGE_OBFUSCATION_6); |
| parser.parse(path); |
| checkDiagnostics(handler.warnings, path, 6, 1, |
| "repackageclasses", "overrides", "flattenpackagehierarchy"); |
| ProguardConfiguration config = parser.getConfig(); |
| assertEquals(PackageObfuscationMode.REPACKAGE, config.getPackageObfuscationMode()); |
| assertNotNull(config.getPackagePrefix()); |
| assertEquals("top", config.getPackagePrefix()); |
| } |
| |
| @Test |
| public void parseApplyMapping() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(APPLY_MAPPING)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(config.hasApplyMappingFile()); |
| } |
| |
| @Test |
| public void parseApplyMappingWithoutFile() throws Exception { |
| Path path = Paths.get(APPLY_MAPPING_WITHOUT_FILE); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(path); |
| fail("Expect to fail due to the lack of file name."); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, path, 6, 14, "File name expected"); |
| } |
| } |
| |
| @Test |
| public void parseIncluding() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(INCLUDING)); |
| verifyParserEndsCleanly(); |
| } |
| |
| @Test |
| public void parseInvalidIncluding1() throws IOException { |
| Path path = Paths.get(INVALID_INCLUDING_1); |
| try { |
| new ProguardConfigurationParser(new DexItemFactory(), reporter) |
| .parse(path); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, path, 6, 10,"does-not-exist.flags"); |
| } |
| } |
| |
| @Test |
| public void parseInvalidIncluding2() throws IOException { |
| Path path = Paths.get(INVALID_INCLUDING_2); |
| try { |
| new ProguardConfigurationParser(new DexItemFactory(), reporter) |
| .parse(path); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, path, 6,2, "does-not-exist.flags"); |
| } |
| } |
| |
| @Test |
| public void parseLibraryJars() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| if (!ToolHelper.isLinux() && !ToolHelper.isMac()) { |
| parser.parse(Paths.get(LIBRARY_JARS_WIN)); |
| } else { |
| parser.parse(Paths.get(LIBRARY_JARS)); |
| } |
| assertEquals(4, parser.getConfig().getLibraryjars().size()); |
| } |
| |
| @Test |
| public void parseInvalidFilePattern() throws IOException { |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(createConfigurationForTesting( |
| Collections.singletonList("-injars abc.jar(*.zip;*.class)"))); |
| fail(); |
| } catch (AbortException e) { |
| assertEquals(1, handler.errors.size()); |
| } |
| } |
| |
| @Test |
| public void parseKeepModifiers() { |
| for (String before : whiteSpace) { |
| for (String after : whiteSpace) { |
| reset(); |
| parseAndVerifyParserEndsCleanly(ImmutableList.of( |
| "-keep" |
| + before + "," + after + "includedescriptorclasses" |
| + before + "," + after + "allowshrinking" |
| + before + "," + after + "allowobfuscation" |
| + before + "," + after + "allowoptimization " |
| + "class A { *; }" |
| )); |
| } |
| } |
| } |
| |
| @Test |
| public void parseKeepAnnotation() { |
| for (String space : whiteSpace) { |
| reset(); |
| parseAndVerifyParserEndsCleanly(ImmutableList.of( |
| "-keep @" + space + "interface A" |
| )); |
| } |
| } |
| |
| @Test |
| public void regress78442725() { |
| parseAndVerifyParserEndsCleanly(ImmutableList.of( |
| "-keep, includedescriptorclasses class in.uncod.android.bypass.Document { *; }", |
| "-keep, includedescriptorclasses class in.uncod.android.bypass.Element { *; }" |
| )); |
| } |
| |
| @Test |
| public void parseSeeds() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(SEEDS)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(config.isPrintSeeds()); |
| assertNull(config.getSeedFile()); |
| } |
| |
| @Test |
| public void parseSeeds2() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(SEEDS_2)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(config.isPrintSeeds()); |
| assertNotNull(config.getSeedFile()); |
| } |
| |
| @Test |
| public void parseVerbose() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(VERBOSE)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(config.isVerbose()); |
| } |
| |
| @Test |
| public void parseKeepdirectories() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter, false, false); |
| parser.parse(Paths.get(KEEPDIRECTORIES)); |
| verifyParserEndsCleanly(); |
| } |
| |
| @Test |
| public void parseDontshrink() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(DONT_SHRINK)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertFalse(config.isShrinking()); |
| } |
| |
| @Test |
| public void parseDontSkipNonPublicLibraryClasses() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES)); |
| verifyParserEndsCleanly(); |
| } |
| |
| @Test |
| public void parseDontskipnonpubliclibraryclassmembers() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS)); |
| verifyParserEndsCleanly(); |
| } |
| |
| @Test |
| public void parseIdentifiernamestring() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| Path source = Paths.get(IDENTIFIER_NAME_STRING); |
| parser.parse(source); |
| verifyParserEndsCleanly(); |
| } |
| |
| @Test |
| public void parseOverloadAggressively() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(OVERLOAD_AGGRESIVELY)); |
| verifyParserEndsCleanly(); |
| } |
| |
| @Test |
| public void parseDontOptimize() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(DONT_OPTIMIZE)); |
| ProguardConfiguration config = parser.getConfig(); |
| verifyParserEndsCleanly(); |
| assertFalse(config.isOptimizing()); |
| } |
| |
| @Test |
| public void parseDontOptimizeOverridesPasses() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| Path path = Paths.get(DONT_OPTIMIZE_OVERRIDES_PASSES); |
| parser.parse(path); |
| checkDiagnostics(handler.warnings, path, 7, 1, |
| "Ignoring", "-optimizationpasses"); |
| ProguardConfiguration config = parser.getConfig(); |
| assertFalse(config.isOptimizing()); |
| } |
| |
| @Test |
| public void parseOptimizationPasses() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| Path path = Paths.get(OPTIMIZATION_PASSES); |
| parser.parse(path); |
| checkDiagnostics(handler.warnings, path, 5, 1, |
| "Ignoring", "-optimizationpasses"); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(config.isOptimizing()); |
| } |
| |
| @Test |
| public void parseOptimizationPassesError() throws Exception { |
| Path path = Paths.get(OPTIMIZATION_PASSES_WITHOUT_N); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(path); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, path, 6, 1, "Missing n"); |
| } |
| } |
| |
| @Test |
| public void parseSkipNonPublicLibraryClasses() throws IOException { |
| Path path = Paths.get(SKIP_NON_PUBLIC_LIBRARY_CLASSES); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(path); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, path, 5, 1, |
| "Unsupported option", "-skipnonpubliclibraryclasses"); |
| } |
| } |
| |
| @Test |
| public void parseAndskipSingleArgument() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(PARSE_AND_SKIP_SINGLE_ARGUMENT)); |
| verifyParserEndsCleanly(); |
| } |
| |
| @Test |
| public void parse_printconfiguration_noArguments() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(createConfigurationForTesting(ImmutableList.of( |
| "-printconfiguration" |
| ))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(config.isPrintConfiguration()); |
| assertNull(config.getPrintConfigurationFile()); |
| } |
| |
| @Test |
| public void parse_printconfiguration_argument() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(createConfigurationForTesting(ImmutableList.of( |
| "-printconfiguration file_name" |
| ))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(config.isPrintConfiguration()); |
| assertEquals("file_name", config.getPrintConfigurationFile().toString()); |
| } |
| |
| @Test |
| public void parse_optimizations() throws Exception { |
| for (String option : |
| ImmutableList.of( |
| "-optimizations", |
| "-optimizations xxx", |
| "-optimizations xxx", |
| "-optimizations xxx/yyy", |
| "-optimizations xxx/yyy", |
| "-optimizations xxx/yyy,zzz*", |
| "-optimizations xxx/yyy , zzz*", |
| "-optimizations !xxx", |
| "-optimizations ! xxx", |
| "-optimizations !xxx,!yyy", |
| "-optimizations ! xxx, ! yyy", |
| "-optimizations !code/simplification/advanced,code/simplification/*")) { |
| reset(); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(option))); |
| verifyParserEndsCleanly(); |
| } |
| } |
| |
| @Test |
| public void parse_optimizationpasses() throws Exception { |
| for (String lineSeparator : lineSeparators) { |
| reset(); |
| Path proguardConfig = writeTextToTempFile(lineSeparator, |
| ImmutableList.of( |
| "-optimizations xxx", |
| "-optimizationpasses 5", |
| "-optimizations yyy")); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| checkDiagnostics(handler.warnings, proguardConfig, 2, 1, |
| "Ignoring", "-optimizationpasses"); |
| Position p = handler.warnings.get(0).getPosition(); |
| assertTrue(p instanceof TextRange); |
| TextRange r = (TextRange) p; |
| assertEquals(2, r.getStart().getLine()); |
| assertEquals(1, r.getStart().getColumn()); |
| assertEquals(2, r.getEnd().getLine()); |
| assertEquals(22, r.getEnd().getColumn()); |
| } |
| } |
| |
| @Test |
| public void parsePrintUsage() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(PRINT_USAGE)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(config.isPrintUsage()); |
| assertNull(config.getPrintUsageFile()); |
| } |
| |
| @Test |
| public void parsePrintUsageToFile() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(PRINT_USAGE_TO_FILE)); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(config.isPrintUsage()); |
| assertNotNull(config.getPrintUsageFile()); |
| } |
| |
| @Test |
| public void parseTarget() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(Paths.get(TARGET)); |
| verifyParserEndsCleanly(); |
| } |
| |
| @Test |
| public void parseInvalidKeepClassOption() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-keepclassx public class * { ", |
| " native <methods>; ", |
| "} " |
| ); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, proguardConfig, 1, 1, |
| "Unknown option", "-keepclassx"); |
| } |
| } |
| |
| @Test |
| public void parseCustomFlags() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| // Custom Proguard flags -runtype and -laststageoutput are ignored. |
| Path proguardConfig = writeTextToTempFile( |
| "-runtype FINAL ", |
| "-laststageoutput /some/file/name " |
| ); |
| parser.parse(proguardConfig); |
| verifyParserEndsCleanly(); |
| } |
| |
| @Test |
| public void testRenameSourceFileAttribute() throws Exception { |
| for (String lineSeparator : lineSeparators) { |
| reset(); |
| Path proguardConfig = writeTextToTempFile(lineSeparator, |
| ImmutableList.of( |
| "-renamesourcefileattribute PG", |
| "-keepattributes SourceFile,SourceDir")); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfigRawForTesting(); |
| assertEquals("PG", config.getRenameSourceFileAttribute()); |
| assertTrue(config.getKeepAttributes().sourceFile); |
| assertTrue(config.getKeepAttributes().sourceDir); |
| } |
| } |
| |
| @Test |
| public void testRenameSourceFileAttribute_empty() throws Exception { |
| for (String lineSeparator : lineSeparators) { |
| reset(); |
| Path proguardConfig = writeTextToTempFile(lineSeparator, |
| ImmutableList.of( |
| "-renamesourcefileattribute", |
| "-keepattributes SourceFile")); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfigRawForTesting(); |
| assertEquals("", config.getRenameSourceFileAttribute()); |
| assertTrue(config.getKeepAttributes().sourceFile); |
| assertFalse(config.getKeepAttributes().sourceDir); |
| } |
| } |
| |
| private void testKeepattributes(List<String> expected, String config) throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(config))); |
| verifyParserEndsCleanly(); |
| assertEquals( |
| ProguardKeepAttributes.fromPatterns(expected), |
| parser.getConfigRawForTesting().getKeepAttributes()); |
| } |
| |
| @Test |
| public void parseKeepattributes() throws Exception { |
| List<String> xxxYYY = ImmutableList.of("xxx", "yyy"); |
| testKeepattributes(xxxYYY, "-keepattributes xxx,yyy"); |
| testKeepattributes(xxxYYY, "-keepattributes xxx, yyy"); |
| testKeepattributes(xxxYYY, "-keepattributes xxx ,yyy"); |
| testKeepattributes(xxxYYY, "-keepattributes xxx , yyy"); |
| testKeepattributes(xxxYYY, "-keepattributes xxx , yyy "); |
| testKeepattributes(xxxYYY, "-keepattributes xxx , yyy \n"); |
| String config = |
| "-keepattributes Exceptions,InnerClasses,Signature,Deprecated,\n" |
| + " SourceFile,LineNumberTable,*Annotation*,EnclosingMethod\n"; |
| List<String> expected = ImmutableList.of( |
| "Exceptions", "InnerClasses", "Signature", "Deprecated", |
| "SourceFile", "LineNumberTable", "*Annotation*", "EnclosingMethod"); |
| testKeepattributes(expected, config); |
| } |
| |
| @Test |
| public void parseInvalidKeepattributes() throws Exception { |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(createConfigurationForTesting(ImmutableList.of("-keepattributes xxx,"))); |
| fail(); |
| } catch (AbortException e) { |
| assertTrue(handler.errors.get(0).getDiagnosticMessage().contains("Expected list element at ")); |
| } |
| } |
| |
| @Test |
| public void parseUseUniqueClassMemberNames() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(createConfigurationForTesting(ImmutableList.of( |
| "-useuniqueclassmembernames" |
| ))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(config.isUseUniqueClassMemberNames()); |
| } |
| |
| @Test |
| public void parseKeepParameterNames() throws Exception { |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(createConfigurationForTesting(ImmutableList.of( |
| "-keepparameternames" |
| ))); |
| parser.getConfig(); |
| fail(); |
| } catch (AbortException e) { |
| assertTrue(handler.errors.get(0).getDiagnosticMessage().contains( |
| "-keepparameternames is not supported")); |
| } |
| } |
| |
| @Test |
| public void parseKeepParameterNamesWithoutMinification() throws Exception { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(createConfigurationForTesting(ImmutableList.of( |
| "-keepparameternames", |
| "-dontobfuscate" |
| ))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertTrue(config.isKeepParameterNames()); |
| |
| parser = new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(createConfigurationForTesting(ImmutableList.of( |
| "-keepparameternames" |
| ))); |
| verifyParserEndsCleanly(); |
| parser.parse(createConfigurationForTesting(ImmutableList.of( |
| "-dontobfuscate" |
| ))); |
| verifyParserEndsCleanly(); |
| config = parser.getConfig(); |
| assertTrue(config.isKeepParameterNames()); |
| } |
| |
| @Test |
| public void parseShortLine() throws IOException { |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(createConfigurationForTesting(Collections.singletonList("-"))); |
| fail(); |
| } catch (AbortException e) { |
| assertEquals(1, handler.errors.size()); |
| assertTrue(handler.errors.get(0).getDiagnosticMessage().contains("-")); |
| } |
| } |
| |
| @Test |
| public void parseNoLocals() throws IOException { |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(createConfigurationForTesting(Collections.singletonList("--no-locals"))); |
| fail(); |
| } catch (AbortException e) { |
| |
| assertEquals(1, handler.errors.size()); |
| assertTrue(handler.errors.get(0).getDiagnosticMessage().contains("--no-locals")); |
| } |
| } |
| |
| private void checkFileFilterMatchAnything(ProguardPathFilter filter) { |
| assertTrue(filter.matches("x")); |
| assertTrue(filter.matches("x/y")); |
| assertTrue(filter.matches("x/y/x")); |
| } |
| |
| @Test |
| public void parse_adaptresourcexxx_keepdirectories_noArguments1() { |
| resetAllowPartiallyImplementedOptions(); |
| ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of( |
| "-adaptresourcefilenames", |
| "-adaptresourcefilecontents", |
| "-keepdirectories" |
| )); |
| checkFileFilterMatchAnything(config.getAdaptResourceFilenames()); |
| checkFileFilterMatchAnything(config.getAdaptResourceFileContents()); |
| checkFileFilterMatchAnything(config.getKeepDirectories()); |
| } |
| |
| @Test |
| public void parse_adaptresourcexxx_keepdirectories_noArguments2() { |
| resetAllowPartiallyImplementedOptions(); |
| ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of( |
| "-keepdirectories", |
| "-adaptresourcefilenames", |
| "-adaptresourcefilecontents" |
| )); |
| checkFileFilterMatchAnything(config.getAdaptResourceFilenames()); |
| checkFileFilterMatchAnything(config.getAdaptResourceFileContents()); |
| checkFileFilterMatchAnything(config.getKeepDirectories()); |
| } |
| |
| @Test |
| public void parse_adaptresourcexxx_keepdirectories_noArguments3() { |
| resetAllowPartiallyImplementedOptions(); |
| ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of( |
| "-adaptresourcefilecontents", |
| "-keepdirectories", |
| "-adaptresourcefilenames" |
| )); |
| checkFileFilterMatchAnything(config.getAdaptResourceFilenames()); |
| checkFileFilterMatchAnything(config.getAdaptResourceFileContents()); |
| checkFileFilterMatchAnything(config.getKeepDirectories()); |
| } |
| |
| private String FILE_FILTER_SINGLE = "xxx/*"; |
| |
| private void checkFileFilterSingle(ProguardPathFilter filter) { |
| assertTrue(filter.matches("xxx/x")); |
| assertTrue(filter.matches("xxx/")); |
| assertFalse(filter.matches("xxx/yyy/z")); |
| assertFalse(filter.matches("xxx")); |
| } |
| |
| @Test |
| public void parse_adaptresourcexxx_keepdirectories_singleArgument() { |
| resetAllowPartiallyImplementedOptions(); |
| ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of( |
| "-adaptresourcefilenames " + FILE_FILTER_SINGLE, |
| "-adaptresourcefilecontents " + FILE_FILTER_SINGLE, |
| "-keepdirectories " + FILE_FILTER_SINGLE |
| )); |
| checkFileFilterSingle(config.getAdaptResourceFilenames()); |
| checkFileFilterSingle(config.getAdaptResourceFileContents()); |
| checkFileFilterSingle(config.getKeepDirectories()); |
| } |
| |
| private String FILE_FILTER_MULTIPLE = |
| "xxx/*, !**.gif ,images/** , com/myapp/**/*.xml,com/mylib/*/*.xml"; |
| |
| private void checkFileFilterMultiple(ProguardPathFilter filter) { |
| assertTrue(filter.matches("xxx/x")); |
| assertTrue(filter.matches("xxx/x.gif")); |
| assertTrue(filter.matches("images/x.jpg")); |
| assertTrue(filter.matches("images/xxx/x.jpg")); |
| assertTrue(filter.matches("com/myapp/package1/x.xml")); |
| assertTrue(filter.matches("com/myapp/package1/package2/x.xml")); |
| assertTrue(filter.matches("com/mylib/package1/x.xml")); |
| assertFalse(filter.matches("x.gif")); |
| assertFalse(filter.matches("images/x.gif")); |
| assertFalse(filter.matches("images/xxx/y.gif")); |
| assertFalse(filter.matches("images/xxx/yyy/z.gif")); |
| assertFalse(filter.matches("com/myapp/package1/x.jpg")); |
| assertFalse(filter.matches("com/myapp/package1/package2/x.jpg")); |
| assertFalse(filter.matches("com/mylib/package1/package2/x.xml")); |
| } |
| |
| @Test |
| public void parse_adaptresourcexxx_keepdirectories_multipleArgument() { |
| resetAllowPartiallyImplementedOptions(); |
| ProguardConfiguration config = parseAndVerifyParserEndsCleanly(ImmutableList.of( |
| "-adaptresourcefilenames " + FILE_FILTER_MULTIPLE, |
| "-adaptresourcefilecontents " + FILE_FILTER_MULTIPLE, |
| "-keepdirectories " + FILE_FILTER_MULTIPLE |
| )); |
| checkFileFilterMultiple(config.getAdaptResourceFilenames()); |
| checkFileFilterMultiple(config.getAdaptResourceFileContents()); |
| checkFileFilterMultiple(config.getKeepDirectories()); |
| } |
| |
| @Test |
| public void parse_adaptresourcexxx_keepdirectories_leadingComma() { |
| List<String> options = ImmutableList.of( |
| "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories"); |
| for (String option : options) { |
| try { |
| resetAllowPartiallyImplementedOptions(); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(option + " ,"))); |
| fail("Expect to fail due to the lack of path filter."); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, null, 1, option.length() + 2, "Path filter expected"); |
| } |
| } |
| } |
| |
| @Test |
| public void parse_adaptresourcexxx_keepdirectories_emptyListElement() { |
| List<String> options = ImmutableList.of( |
| "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories"); |
| for (String option : options) { |
| try { |
| resetAllowPartiallyImplementedOptions(); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(option + " xxx,,yyy"))); |
| fail("Expect to fail due to the lack of path filter."); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, null, 1, option.length() + 6, "Path filter expected"); |
| } |
| } |
| } |
| |
| @Test |
| public void parse_adaptresourcexxx_keepdirectories_trailingComma() { |
| List<String> options = ImmutableList.of( |
| "-adaptresourcefilenames", "-adaptresourcefilecontents", "-keepdirectories"); |
| for (String option : options) { |
| try { |
| resetAllowPartiallyImplementedOptions(); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(option + " xxx,"))); |
| fail("Expect to fail due to the lack of path filter."); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, null, 1, option.length() + 6, "Path filter expected"); |
| } |
| } |
| } |
| |
| @Test |
| public void parse_testInlineOptions() { |
| List<String> options = ImmutableList.of( |
| "-neverinline", "-forceinline"); |
| for (String option : options) { |
| try { |
| reset(); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(option + " class A { *; }"))); |
| fail("Expect to fail due to testing option being turned off."); |
| } catch (AbortException e) { |
| assertEquals(2, handler.errors.size()); |
| checkDiagnostics(handler.errors, 0, null, 1, 1, "Unknown option \"" + option + "\""); |
| } |
| } |
| } |
| |
| @Test |
| public void parse_if() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$$ModuleAdapter", |
| "-keep class A", |
| "-if class **$$InjectAdapter", |
| "-keep class B", |
| "-if class **$$StaticInjection", |
| "-keep class C", |
| "-keepnames class dagger.Lazy" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| // Three -if rules and one independent -keepnames |
| assertEquals(4, config.getRules().size()); |
| long ifCount = |
| config.getRules().stream().filter(rule -> rule instanceof ProguardIfRule).count(); |
| assertEquals(3, ifCount); |
| ProguardIfRule if0 = (ProguardIfRule) config.getRules().get(0); |
| assertEquals("**$$ModuleAdapter", if0.getClassNames().toString()); |
| assertEquals(ProguardKeepRuleType.KEEP, if0.subsequentRule.getType()); |
| assertEquals("A", if0.subsequentRule.getClassNames().toString()); |
| ProguardIfRule if1 = (ProguardIfRule) config.getRules().get(1); |
| assertEquals("**$$InjectAdapter", if1.getClassNames().toString()); |
| assertEquals(ProguardKeepRuleType.KEEP, if1.subsequentRule.getType()); |
| assertEquals("B", if1.subsequentRule.getClassNames().toString()); |
| ProguardIfRule if2 = (ProguardIfRule) config.getRules().get(2); |
| assertEquals("**$$StaticInjection", if2.getClassNames().toString()); |
| assertEquals(ProguardKeepRuleType.KEEP, if2.subsequentRule.getType()); |
| assertEquals("C", if2.subsequentRule.getClassNames().toString()); |
| } |
| |
| @Test |
| public void parse_if_nthWildcard() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$R**", |
| "-keep class **$D<2>" // <2> corresponds to the 2nd ** in -if rule. |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| assertEquals(1, config.getRules().size()); |
| ProguardIfRule if0 = (ProguardIfRule) config.getRules().get(0); |
| assertEquals("**$R**", if0.getClassNames().toString()); |
| assertEquals(ProguardKeepRuleType.KEEP, if0.subsequentRule.getType()); |
| assertEquals("**$D<2>", if0.subsequentRule.getClassNames().toString()); |
| |
| // Test <2> literally refers to the second wildcard in the rule. |
| Iterator<ProguardWildcard> it1 = if0.getClassNames().getWildcards().iterator(); |
| it1.next(); |
| ProguardWildcard secondWildcardInIf = it1.next(); |
| assertTrue(secondWildcardInIf.isPattern()); |
| Iterator<ProguardWildcard> it2 = if0.subsequentRule.getWildcards().iterator(); |
| it2.next(); |
| ProguardWildcard backReference = it2.next(); |
| assertTrue(backReference.isBackReference()); |
| assertSame(secondWildcardInIf.asPattern(), backReference.asBackReference().reference); |
| |
| verifyWithProguard6(proguardConfig); |
| } |
| |
| @Test |
| public void parse_if_nthWildcard_notNumber_literalN() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$R**", |
| "-keep class **D<n>" |
| ); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, proguardConfig, 2, 13, |
| "Use of generics not allowed for java type"); |
| } |
| verifyFailWithProguard6(proguardConfig, "Use of generics not allowed for java type"); |
| } |
| |
| @Test |
| public void parse_if_nthWildcard_notNumber_asterisk_inClassName() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$R**", |
| "-keep class **D<*>" |
| ); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, proguardConfig, 2, 13, |
| "Use of generics not allowed for java type"); |
| } |
| verifyFailWithProguard6(proguardConfig, "Use of generics not allowed for java type"); |
| } |
| |
| @Test |
| public void parse_if_nthWildcard_notNumber_asterisk_inMemberName() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$R**", |
| "-keep class **D<2> {", |
| " int id<*>;", |
| "}" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| verifyParserEndsCleanly(); |
| |
| verifyWithProguard6(proguardConfig); |
| } |
| |
| @Test |
| public void parse_if_nestedAngularBrackets_inMemberName() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$R**", |
| "-keep class **D<2> {", |
| " int id<<*>>;", |
| "}" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| checkDiagnostics(handler.warnings, proguardConfig, 3, 7, "The field name \"id<<*>>\" is"); |
| |
| verifyWithProguard6(proguardConfig); |
| } |
| |
| @Test |
| public void parse_if_nestedAngularBrackets_outOfRange() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$R**", |
| "-keep class **D<2> {", |
| " int id<<4>>;", // There are 3 previous referable wildcards in this rule. |
| "}" |
| ); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, proguardConfig, 4, 2, |
| "Wildcard", "<4>", "invalid"); |
| } |
| verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (4,"); |
| } |
| |
| @Test |
| public void parse_if_nthWildcard_outOfRange_tooSmall() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$R**", |
| "-keep class **D<0>" // nth wildcard starts from 1, not 0. |
| ); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, proguardConfig, 2, 13, |
| "Wildcard", "<0>", "invalid"); |
| } |
| verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (0,"); |
| } |
| |
| @Test |
| public void parse_if_nthWildcard_outOfRange_tooBig() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$R**", |
| "-keep class **D<4>" // There are 3 previous wildcards in this rule. |
| ); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, proguardConfig, 3, 1, |
| "Wildcard", "<4>", "invalid"); |
| } |
| verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (4,"); |
| } |
| |
| @Test |
| public void parse_if_nthWildcard_outOfRange_inIf() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$R<2>", // There is only one wildcard prior to <2>. |
| "-keep class **D<2>" |
| ); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, proguardConfig, 3, 1, |
| "Wildcard", "<2>", "invalid"); |
| } |
| verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (2,"); |
| } |
| |
| @Test |
| public void parse_if_nthWildcard_not_referable() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **.R {", |
| " int id?;", |
| "}", |
| "-keep class <1>.D<2> {", |
| " int id<3>;", // Only ** and ? are referable wildcards. |
| "}" |
| ); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, proguardConfig, 6, 2, |
| "Wildcard", "<3>", "invalid"); |
| } |
| verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (3,"); |
| } |
| |
| @Test |
| public void parse_if_nthWildcard_not_referable_after_backreference() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **.*User {", |
| " @<1>.*<2> <methods>;", // As back reference starts, * in the middle is not referable. |
| "}", |
| "-keep @interface <1>.<3><2>" |
| ); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, proguardConfig, 5, 1, |
| "Wildcard", "<3>", "invalid"); |
| } |
| verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (3,"); |
| } |
| |
| @Test |
| public void parse_if_if() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$$ModuleAdapter", |
| "-if class **$$InjectAdapter" |
| ); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, proguardConfig, 1, 1, |
| "Expecting", "'-keep'", "after", "'-if'"); |
| } |
| verifyFailWithProguard6(proguardConfig, "Expecting '-keep' option after '-if' option"); |
| } |
| |
| @Test |
| public void parse_if_end() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-if class **$$ModuleAdapter" |
| ); |
| try { |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| fail(); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, proguardConfig, 1, 1, |
| "Expecting", "'-keep'", "after", "'-if'"); |
| } |
| verifyFailWithProguard6(proguardConfig, "Expecting '-keep' option after '-if' option"); |
| } |
| |
| @Test |
| public void parse_assumenoexternalsideeffects() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-assumenoexternalsideeffects class java.lang.StringBuilder {", |
| " public java.lang.StringBuilder();", |
| " public java.lang.StringBuilder(int);", |
| " public java.lang.StringBuilder(java.lang.String);", |
| " public java.lang.StringBuilder append(java.lang.Object);", |
| " public java.lang.StringBuilder append(java.lang.String);", |
| " public java.lang.StringBuilder append(java.lang.StringBuffer);", |
| " public java.lang.StringBuilder append(char[]);", |
| " public java.lang.StringBuilder append(char[], int, int);", |
| " public java.lang.StringBuilder append(boolean);", |
| " public java.lang.StringBuilder append(char);", |
| " public java.lang.StringBuilder append(int);", |
| " public java.lang.StringBuilder append(long);", |
| " public java.lang.StringBuilder append(float);", |
| " public java.lang.StringBuilder append(double);", |
| " public java.lang.String toString();", |
| "}" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| checkDiagnostics(handler.warnings, proguardConfig, 1, 1, |
| "Ignoring", "-assumenoexternalsideeffects"); |
| } |
| |
| @Test |
| public void parse_assumenoescapingparameters() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-assumenoescapingparameters class java.lang.System {", |
| " public static void arraycopy(java.lang.Object, int, java.lang.Object, int, int);", |
| "}" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| checkDiagnostics(handler.warnings, proguardConfig, 1, 1, |
| "Ignoring", "-assumenoescapingparameters"); |
| } |
| |
| @Test |
| public void parse_assumenoexternalreturnvalues() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-assumenoexternalreturnvalues class java.lang.StringBuilder {", |
| " public java.lang.StringBuilder append(java.lang.Object);", |
| " public java.lang.StringBuilder append(java.lang.String);", |
| " public java.lang.StringBuilder append(java.lang.StringBuffer);", |
| " public java.lang.StringBuilder append(char[]);", |
| " public java.lang.StringBuilder append(char[], int, int);", |
| " public java.lang.StringBuilder append(boolean);", |
| " public java.lang.StringBuilder append(char);", |
| " public java.lang.StringBuilder append(int);", |
| " public java.lang.StringBuilder append(long);", |
| " public java.lang.StringBuilder append(float);", |
| " public java.lang.StringBuilder append(double);", |
| "}" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| checkDiagnostics(handler.warnings, proguardConfig, 1, 1, |
| "Ignoring", "-assumenoexternalreturnvalues"); |
| } |
| |
| @Test |
| public void parse_android() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-android" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| verifyParserEndsCleanly(); |
| } |
| |
| @Test |
| public void parse_mergeinterfaceaggressively() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-mergeinterfacesaggressively" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| verifyParserEndsCleanly(); |
| } |
| |
| @Test |
| public void parse_addconfigurationdebugging() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-addconfigurationdebugging" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| checkDiagnostics(handler.warnings, proguardConfig, 1, 1, |
| "Ignoring", "-addconfigurationdebugging"); |
| } |
| |
| @Test |
| public void parse_regress74508478() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-keep class A {", |
| " A <fields>;", |
| "}" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| checkDiagnostics(handler.warnings, proguardConfig, 2, 5, "The field name \"<fields>\" is"); |
| verifyWithProguard(proguardConfig); |
| } |
| |
| @Test |
| public void parse_regress79925760() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-keep public @ interface test.MyAnnotation" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| verifyParserEndsCleanly(); |
| |
| ProguardConfiguration config = parser.getConfig(); |
| assertEquals(1, config.getRules().size()); |
| ProguardKeepRule rule = (ProguardKeepRule) config.getRules().get(0); |
| assertEquals(ProguardClassType.ANNOTATION_INTERFACE, rule.getClassType()); |
| |
| verifyWithProguard(proguardConfig); |
| } |
| |
| @Test |
| public void parse_regress110021323() throws Exception { |
| Path proguardConfig = writeTextToTempFile( |
| "-keepclassmembernames class A {", |
| " <public methods>;", |
| " <public fields>;", |
| "}" |
| ); |
| ProguardConfigurationParser parser = |
| new ProguardConfigurationParser(new DexItemFactory(), reporter); |
| parser.parse(proguardConfig); |
| assertEquals(4, handler.warnings.size()); |
| checkDiagnostics(handler.warnings, 0, proguardConfig, 2, 3, "The type \"<public\" is"); |
| checkDiagnostics(handler.warnings, 1, proguardConfig, 2, 11, "The field name \"methods>\" is"); |
| checkDiagnostics(handler.warnings, 2, proguardConfig, 3, 3, "The type \"<public\" is"); |
| checkDiagnostics(handler.warnings, 3, proguardConfig, 3, 11, "The field name \"fields>\" is"); |
| |
| verifyWithProguard(proguardConfig); |
| } |
| |
| public void testNotSupported(String option) { |
| try { |
| reset(); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(option))); |
| fail("Expect to fail due to unsupported option."); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, null, 1, 1, "Option " + option + " currently not supported"); |
| } |
| } |
| |
| private void checkRulesSourceSnippet(List<String> sourceRules) { |
| checkRulesSourceSnippet(sourceRules, sourceRules, false); |
| } |
| |
| private void checkRulesSourceSnippet( |
| List<String> sourceRules, List<String> expected, boolean trim) { |
| reset(); |
| parser.parse(createConfigurationForTesting(sourceRules)); |
| verifyParserEndsCleanly(); |
| List<ProguardConfigurationRule> rules = parser.getConfig().getRules(); |
| assertEquals(expected.size(), rules.size()); |
| for (int i = 0; i < expected.size(); i++) { |
| assertEquals(trim ? expected.get(i).trim() : expected.get(i), rules.get(i).getSource()); |
| } |
| } |
| |
| @Test |
| public void accurateSourceSnippet() { |
| String rule1 = String.join(System.lineSeparator(), ImmutableList.of("-keep class A { *; }")); |
| String rule2 = |
| String.join(System.lineSeparator(), ImmutableList.of("-keep class A ", "{ *; ", "}")); |
| String rule3 = |
| String.join( |
| System.lineSeparator(), ImmutableList.of("-checkdiscard class A ", "{ *; ", "}")); |
| |
| checkRulesSourceSnippet(ImmutableList.of(rule1)); |
| checkRulesSourceSnippet(ImmutableList.of(rule2)); |
| checkRulesSourceSnippet(ImmutableList.of(rule3)); |
| checkRulesSourceSnippet(ImmutableList.of(rule1, rule2, rule3)); |
| } |
| |
| @Test |
| public void accurateSourceSnippet_withWhitespace() { |
| Iterable<String> nonEmptyWhiteSpace = |
| whiteSpace.stream().filter(space -> !space.equals("")).collect(Collectors.toList()); |
| for (String space : nonEmptyWhiteSpace) { |
| String rule1 = |
| String.join(System.lineSeparator(), ImmutableList.of(" -keep class A { *; } ")) |
| .replaceAll(" {2}", space); |
| String rule2 = |
| String.join( |
| System.lineSeparator(), ImmutableList.of("-keep class A ", "{ *; ", "}", "")) |
| .replaceAll(" {2}", space); |
| |
| checkRulesSourceSnippet(ImmutableList.of(rule1), ImmutableList.of(rule1), true); |
| checkRulesSourceSnippet( |
| ImmutableList.of("#Test comment ", "", rule1), ImmutableList.of(rule1), true); |
| checkRulesSourceSnippet( |
| ImmutableList.of("#Test comment ", "", rule1, "", "#Test comment ", ""), |
| ImmutableList.of(rule1), |
| true); |
| checkRulesSourceSnippet(ImmutableList.of(rule2), ImmutableList.of(rule2), true); |
| checkRulesSourceSnippet(ImmutableList.of(rule1, rule2), ImmutableList.of(rule1, rule2), true); |
| checkRulesSourceSnippet( |
| ImmutableList.of( |
| "#Test comment ", "", rule1, " ", "#Test comment ", "", rule2, "#Test comment ", ""), |
| ImmutableList.of(rule1, rule2), |
| true); |
| } |
| } |
| |
| private void testFlagWithFilenames( |
| String flag, |
| List<String> values, |
| Function<ProguardConfiguration, Path> extractPath, |
| BiConsumer<String, String> check) { |
| for (String value : values) { |
| reset(); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(flag + " " + value))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| check.accept(value, extractPath.apply(config).toString()); |
| } |
| } |
| |
| @Test |
| public void parseSingleFleNameOptions() { |
| List<String> fileNames = ImmutableList.of( |
| "xxx", |
| "xxx" + File.pathSeparatorChar + "xxx"); |
| testFlagWithFilenames("-printusage", fileNames, |
| ProguardConfiguration::getPrintUsageFile, Assert::assertEquals); |
| testFlagWithFilenames("-printconfiguration", fileNames, |
| ProguardConfiguration::getPrintConfigurationFile, Assert::assertEquals); |
| testFlagWithFilenames("-printmapping", fileNames, |
| ProguardConfiguration::getPrintMappingFile, Assert::assertEquals); |
| testFlagWithFilenames("-applymapping", fileNames, |
| ProguardConfiguration::getApplyMappingFile, Assert::assertEquals); |
| // The parsed value of -basedirectory is not available in the configuration. |
| testFlagWithFilenames("-basedirectory", fileNames, (x) -> Paths.get(""), (x, y) -> {}); |
| testFlagWithFilenames("-printseeds", fileNames, |
| ProguardConfiguration::getSeedFile, Assert::assertEquals); |
| // TODO(sgjesse): Add tests for -obfuscationdictionary, -classobfuscationdictionary, |
| // -packageobfuscationdictionary and -include |
| } |
| |
| private void checkQuotedFileName(String quoted, String parsed) { |
| assertTrue(quoted.charAt(0) == '\'' || quoted.charAt(0) == '\"'); |
| assertTrue(quoted.charAt(0) == quoted.charAt(quoted.length() - 1)); |
| assertEquals(quoted.substring(1, quoted.length() - 1), parsed); |
| } |
| |
| @Test |
| public void parseSingleFleNameOptionsQuoted() { |
| List<String> fileNames = new ArrayList<>(); |
| fileNames.add("'xxx'"); |
| fileNames.add("\"xxx\""); |
| fileNames.add("'xxx xxx\'"); |
| fileNames.add("\"xxx xxx\""); |
| fileNames.add("\"'xxx'\""); |
| fileNames.add("\" xxx xxx (\""); |
| fileNames.add("' xxx xxx ('"); |
| // Java Path implementation on Windows does not allow " or trailing |
| // spaces in file names. |
| if (!ToolHelper.isWindows()) { |
| fileNames.add("'\"xxx\"'"); |
| fileNames.add("\" xxx xxx \""); |
| fileNames.add("' xxx xxx '"); |
| } |
| testFlagWithFilenames("-printusage", fileNames, |
| ProguardConfiguration::getPrintUsageFile, this::checkQuotedFileName); |
| testFlagWithFilenames("-printconfiguration", fileNames, |
| ProguardConfiguration::getPrintConfigurationFile, this::checkQuotedFileName); |
| testFlagWithFilenames("-printmapping", fileNames, |
| ProguardConfiguration::getPrintMappingFile, this::checkQuotedFileName); |
| testFlagWithFilenames("-applymapping", fileNames, |
| ProguardConfiguration::getApplyMappingFile, this::checkQuotedFileName); |
| // The parsed value of -basedirectory is not available in the configuration. |
| testFlagWithFilenames("-basedirectory", fileNames, (x) -> Paths.get(""), (x, y) -> {}); |
| testFlagWithFilenames("-printseeds", fileNames, |
| ProguardConfiguration::getSeedFile, this::checkQuotedFileName); |
| // TODO(sgjesse): Add tests for -obfuscationdictionary, -classobfuscationdictionary, |
| // -packageobfuscationdictionary and -include |
| } |
| |
| private void testFlagWithFilenamesWithSystemProperty( |
| String flag, |
| List<String> values, |
| Function<ProguardConfiguration, Path> extractPath, |
| BiConsumer<String, String> check) { |
| for (String value : values) { |
| reset(); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(flag + " " + value))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| Path path = extractPath.apply(config); |
| check.accept( |
| value |
| .replaceAll("<java.home>", Matcher.quoteReplacement(System.getProperty("java.home"))) |
| .replaceAll("<user.home>", Matcher.quoteReplacement(System.getProperty("user.home"))), |
| path.toString()); |
| } |
| } |
| |
| @Test |
| public void parseFlagWithFilenamesWithSystemProperty() { |
| List<String> fileNames = new ArrayList<>(); |
| fileNames.add("<java.home>"); |
| fileNames.add("<user.home>"); |
| if (!ToolHelper.isWindows()) { |
| // If the system property has e.g. C:\ prefix, then these file names |
| // will not work on Windows. |
| fileNames.add("xxx<java.home>"); |
| fileNames.add("xxx<user.home>"); |
| } |
| fileNames.add("<java.home>" + File.separatorChar + "xxx"); |
| fileNames.add("<user.home>" + File.separatorChar + "xxx"); |
| if (!ToolHelper.isWindows()) { |
| fileNames.add("xxx<java.home>/xxx"); |
| fileNames.add("xxx<user.home>/xxx"); |
| fileNames.add("<java.home><java.home>"); |
| fileNames.add("<user.home><user.home>"); |
| fileNames.add("<java.home><user.home>"); |
| fileNames.add("<user.home><java.home>"); |
| // The characters < and > are not allowed in paths on Windows. |
| fileNames.add(">"); |
| fileNames.add("<"); |
| fileNames.add("<<<<"); |
| fileNames.add("><"); |
| fileNames.add(">><<"); |
| } |
| testFlagWithFilenamesWithSystemProperty("-printusage", fileNames, |
| ProguardConfiguration::getPrintUsageFile, Assert::assertEquals); |
| testFlagWithFilenamesWithSystemProperty("-printconfiguration", fileNames, |
| ProguardConfiguration::getPrintConfigurationFile, Assert::assertEquals); |
| testFlagWithFilenamesWithSystemProperty("-printmapping", fileNames, |
| ProguardConfiguration::getPrintMappingFile, Assert::assertEquals); |
| testFlagWithFilenamesWithSystemProperty("-applymapping", fileNames, |
| ProguardConfiguration::getApplyMappingFile, Assert::assertEquals); |
| // The parsed value of -basedirectory is not available in the configuration. |
| testFlagWithFilenamesWithSystemProperty("-basedirectory", fileNames, |
| (x) -> Paths.get(""), (x, y) -> {}); |
| testFlagWithFilenamesWithSystemProperty("-printseeds", fileNames, |
| ProguardConfiguration::getSeedFile, Assert::assertEquals); |
| // TODO(sgjesse): Add tests for -obfuscationdictionary, -classobfuscationdictionary, |
| // -packageobfuscationdictionary and -include |
| } |
| |
| @Test |
| public void pasteFlagWithFilenamesWithSystemProperty_empty() throws Exception { |
| try { |
| parser.parse(createConfigurationForTesting(ImmutableList.of("-printusage <>"))); |
| fail("Expect to fail due to the lack of file name."); |
| } catch (AbortException e) { |
| checkDiagnostics(handler.errors, null, 1, 15, "Value of system property '' not found"); |
| } |
| } |
| |
| @Test |
| public void pasteFlagWithFilenamesWithSystemProperty_notFound() throws Exception { |
| // Find a non-existent system property. |
| String property = "x"; |
| while (System.getProperty(property) != null) { |
| property = property + "x"; |
| } |
| |
| try { |
| parser.parse( |
| createConfigurationForTesting(ImmutableList.of("-printusage <" + property + ">"))); |
| fail("Expect to fail due to the lack of file name."); |
| } catch (AbortException e) { |
| checkDiagnostics( |
| handler.errors, null, 1, 16, "Value of system property '" + property + "' not found"); |
| } |
| } |
| |
| private void testFlagWithQuotedValue( |
| String flag, String value, BiConsumer<PackageObfuscationMode, String> checker) { |
| reset(); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(flag + " " + value))); |
| verifyParserEndsCleanly(); |
| ProguardConfiguration config = parser.getConfig(); |
| checker.accept(config.getPackageObfuscationMode(), config.getPackagePrefix()); |
| } |
| |
| private void testFlagWithQuotedValueFailure( |
| String flag, String value, Supplier<Void> checker) { |
| try { |
| reset(); |
| parser.parse(createConfigurationForTesting(ImmutableList.of(flag + " " + value))); |
| fail("Expect to fail due to un-closed quote."); |
| } catch (AbortException e) { |
| checker.get(); |
| } |
| } |
| |
| @Test |
| public void parseRepackageclassesQuotes() { |
| testFlagWithQuotedValue("-repackageclasses", "'xxx'", (mode, prefix) -> { |
| assertEquals(PackageObfuscationMode.REPACKAGE, mode); |
| assertEquals("xxx", prefix); |
| }); |
| testFlagWithQuotedValue("-repackageclasses", "\"xxx\"", (mode, prefix) -> { |
| assertEquals(PackageObfuscationMode.REPACKAGE, mode); |
| assertEquals("xxx", prefix); |
| }); |
| testFlagWithQuotedValueFailure("-repackageclasses", "'xxx", () -> { |
| checkDiagnostics(handler.errors, null, 1, 23, "Missing closing quote"); |
| return null; |
| }); |
| testFlagWithQuotedValueFailure("-repackageclasses", "'xxx\"", () -> { |
| checkDiagnostics(handler.errors, null, 1, 23, "Missing closing quote"); |
| return null; |
| }); |
| testFlagWithQuotedValueFailure("-repackageclasses", "\"xxx", () -> { |
| checkDiagnostics(handler.errors, null, 1, 23, "Missing closing quote"); |
| return null; |
| }); |
| testFlagWithQuotedValueFailure("-repackageclasses", "\"xxx'", () -> { |
| checkDiagnostics(handler.errors, null, 1, 23, "Missing closing quote"); |
| return null; |
| }); |
| } |
| |
| @Test |
| public void parseFlattenpackagehierarchyQuotes() { |
| testFlagWithQuotedValue("-flattenpackagehierarchy", "'xxx'", (mode, prefix) -> { |
| assertEquals(PackageObfuscationMode.FLATTEN, mode); |
| assertEquals("xxx", prefix); |
| }); |
| testFlagWithQuotedValue("-flattenpackagehierarchy", "\"xxx\"", (mode, prefix) -> { |
| assertEquals(PackageObfuscationMode.FLATTEN, mode); |
| assertEquals("xxx", prefix); |
| }); |
| testFlagWithQuotedValueFailure("-flattenpackagehierarchy", "'xxx", () -> { |
| checkDiagnostics(handler.errors, null, 1, 30, "Missing closing quote"); |
| return null; |
| }); |
| testFlagWithQuotedValueFailure("-flattenpackagehierarchy", "'xxx\"", () -> { |
| checkDiagnostics(handler.errors, null, 1, 30, "Missing closing quote"); |
| return null; |
| }); |
| testFlagWithQuotedValueFailure("-flattenpackagehierarchy", "\"xxx", () -> { |
| checkDiagnostics(handler.errors, null, 1, 30, "Missing closing quote"); |
| return null; |
| }); |
| testFlagWithQuotedValueFailure("-flattenpackagehierarchy", "\"xxx'", () -> { |
| checkDiagnostics(handler.errors, null, 1, 30, "Missing closing quote"); |
| return null; |
| }); |
| } |
| |
| private ProguardConfiguration parseAndVerifyParserEndsCleanly(List<String> config) { |
| parser.parse(createConfigurationForTesting(config)); |
| verifyParserEndsCleanly(); |
| return parser.getConfig(); |
| } |
| |
| private void verifyParserEndsCleanly() { |
| assertEquals(0, handler.infos.size()); |
| assertEquals(0, handler.warnings.size()); |
| assertEquals(0, handler.errors.size()); |
| } |
| |
| private void verifyWithProguard(Path proguardConfig) throws Exception { |
| if (isRunProguard()) { |
| // Add a keep rule for the test class as Proguard will fail if the resulting output jar is |
| // empty |
| Class classToKeepForTest = EmptyMainClassForProguardTests.class; |
| Path additionalProguardConfig = writeTextToTempFile( |
| "-keep class " + classToKeepForTest.getCanonicalName() + " {", |
| " public static void main(java.lang.String[]);", |
| "}" |
| ); |
| Path proguardedJar = |
| File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath(); |
| ProcessResult result = ToolHelper.runProguardRaw( |
| jarTestClasses(ImmutableList.of(classToKeepForTest)), |
| proguardedJar, |
| ImmutableList.of(proguardConfig, additionalProguardConfig), |
| null); |
| assertEquals(0, result.exitCode); |
| CodeInspector proguardInspector = new CodeInspector(readJar(proguardedJar)); |
| assertEquals(1, proguardInspector.allClasses().size()); |
| } |
| } |
| |
| private void verifyWithProguard6(Path proguardConfig) throws Exception { |
| if (isRunProguard()) { |
| // Add a keep rule for the test class as Proguard will fail if the resulting output jar is |
| // empty |
| Class classToKeepForTest = EmptyMainClassForProguardTests.class; |
| Path additionalProguardConfig = writeTextToTempFile( |
| "-keep class " + classToKeepForTest.getCanonicalName() + " {", |
| " public static void main(java.lang.String[]);", |
| "}" |
| ); |
| Path proguardedJar = |
| File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath(); |
| ProcessResult result = ToolHelper.runProguard6Raw( |
| jarTestClasses(ImmutableList.of(classToKeepForTest)), |
| proguardedJar, |
| ImmutableList.of(proguardConfig, additionalProguardConfig), |
| null); |
| assertEquals(0, result.exitCode); |
| CodeInspector proguardInspector = new CodeInspector(readJar(proguardedJar)); |
| assertEquals(1, proguardInspector.allClasses().size()); |
| } |
| } |
| |
| private void verifyFailWithProguard6(Path proguardConfig, String expectedMessage) |
| throws Exception{ |
| if (isRunProguard()) { |
| // No need for a keep rule for this class, as we are expecting Proguard to fail with the |
| // specified message. |
| Class classForTest = EmptyMainClassForProguardTests.class; |
| Path proguardedJar = |
| File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath(); |
| ProcessResult result = ToolHelper.runProguard6Raw( |
| jarTestClasses(ImmutableList.of(classForTest)), proguardedJar, proguardConfig, null); |
| assertNotEquals(0, result.exitCode); |
| assertThat(result.stderr, containsString(expectedMessage)); |
| } |
| } |
| } |