From 730238f3bab1c680424e0ac74178c33b15b43eb5 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 7 Sep 2014 22:30:28 -0400 Subject: added some basic tests for the deobufscator and the jar index --- build.gradle | 15 +- src/cuchaz/enigma/analysis/JarIndex.java | 11 +- src/cuchaz/enigma/convert/ClassIdentity.java | 14 +- test/cuchaz/enigma/EntryFactory.java | 52 ++++++ test/cuchaz/enigma/TestDeobfuscator.java | 13 +- test/cuchaz/enigma/TestJarIndexLoneClass.java | 201 +++++++++++++++++++++ test/cuchaz/enigma/inputs/LoneClass.java | 16 -- test/cuchaz/enigma/inputs/loneClass/LoneClass.java | 16 ++ 8 files changed, 307 insertions(+), 31 deletions(-) create mode 100644 test/cuchaz/enigma/EntryFactory.java create mode 100644 test/cuchaz/enigma/TestJarIndexLoneClass.java delete mode 100644 test/cuchaz/enigma/inputs/LoneClass.java create mode 100644 test/cuchaz/enigma/inputs/loneClass/LoneClass.java diff --git a/build.gradle b/build.gradle index 4fd004fd..14f66145 100644 --- a/build.gradle +++ b/build.gradle @@ -49,6 +49,7 @@ dependencies { compile "org.javassist:javassist:3.18.1-GA" testCompile "junit:junit:4.11" + testCompile "org.hamcrest:hamcrest-all:1.3" } fatJar { @@ -65,23 +66,25 @@ fatJar { } } -task jarTestCases( type: Jar ) { +task jarLoneClass( type: Jar ) { from( sourceSets.test.output ) { - include( "cuchaz/enigma/inputs/**" ) + include( "cuchaz/enigma/inputs/Keep.class" ) + include( "cuchaz/enigma/inputs/loneClass/**" ) } - archiveName( "testCases.jar" ) + archiveName( "testLoneClass.jar" ) } task obfTestCases( type: proguard.gradle.ProGuardTask ) { - dependsOn jarTestCases + dependsOn jarLoneClass - injars( "build/libs/testCases.jar" ) - outjars( "build/libs/testCases.obf.jar" ) + injars( "build/libs/testLoneClass.jar" ) + outjars( "build/libs/testLoneClass.obf.jar" ) libraryjars( "${System.getProperty('java.home')}/lib/rt.jar" ) overloadaggressively repackageclasses allowaccessmodification + dontoptimize keep( "class cuchaz.enigma.inputs.Keep" ) dontshrink diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index a8ac0013..b4096e9d 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -587,9 +587,14 @@ public class JarIndex public ClassImplementationsTreeNode getClassImplementations( Translator deobfuscatingTranslator, ClassEntry obfClassEntry ) { - ClassImplementationsTreeNode node = new ClassImplementationsTreeNode( deobfuscatingTranslator, obfClassEntry ); - node.load( this ); - return node; + // is this even an interface? + if( isInterface( obfClassEntry.getClassName() ) ) + { + ClassImplementationsTreeNode node = new ClassImplementationsTreeNode( deobfuscatingTranslator, obfClassEntry ); + node.load( this ); + return node; + } + return null; } public MethodInheritanceTreeNode getMethodInheritance( Translator deobfuscatingTranslator, MethodEntry obfMethodEntry ) diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index bd2824b3..b3b043e5 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -111,12 +111,16 @@ public class ClassIdentity // stuff from the jar index m_implementations = HashMultiset.create(); - @SuppressWarnings( "unchecked" ) - Enumeration implementations = index.getClassImplementations( null, m_classEntry ).children(); - while( implementations.hasMoreElements() ) + ClassImplementationsTreeNode implementationsNode = index.getClassImplementations( null, m_classEntry ); + if( implementationsNode != null ) { - ClassImplementationsTreeNode node = implementations.nextElement(); - m_implementations.add( scrubClassName( node.getClassEntry().getName() ) ); + @SuppressWarnings( "unchecked" ) + Enumeration implementations = implementationsNode.children(); + while( implementations.hasMoreElements() ) + { + ClassImplementationsTreeNode node = implementations.nextElement(); + m_implementations.add( scrubClassName( node.getClassEntry().getName() ) ); + } } m_references = HashMultiset.create(); diff --git a/test/cuchaz/enigma/EntryFactory.java b/test/cuchaz/enigma/EntryFactory.java new file mode 100644 index 00000000..b275719b --- /dev/null +++ b/test/cuchaz/enigma/EntryFactory.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.ConstructorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; + +public class EntryFactory +{ + public static ClassEntry newClass( String name ) + { + return new ClassEntry( name ); + } + + public static FieldEntry newField( String className, String fieldName ) + { + return new FieldEntry( newClass( className ), fieldName ); + } + + public static MethodEntry newMethod( String className, String methodName, String methodSignature ) + { + return new MethodEntry( newClass( className ), methodName, methodSignature ); + } + + public static ConstructorEntry newConstructor( String className, String signature ) + { + return new ConstructorEntry( newClass( className ), signature ); + } + + public static EntryReference newFieldReferenceByMethod( String fieldClassName, String fieldName, String callerClassName, String callerName, String callerSignature ) + { + return new EntryReference( newField( fieldClassName, fieldName ), newMethod( callerClassName, callerName, callerSignature ) ); + } + + public static EntryReference newFieldReferenceByConstructor( String fieldClassName, String fieldName, String callerClassName, String callerSignature ) + { + return new EntryReference( newField( fieldClassName, fieldName ), newConstructor( callerClassName, callerSignature ) ); + } +} diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java index 3310fbcc..3e679fb9 100644 --- a/test/cuchaz/enigma/TestDeobfuscator.java +++ b/test/cuchaz/enigma/TestDeobfuscator.java @@ -1,3 +1,14 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ package cuchaz.enigma; import static org.junit.Assert.*; @@ -17,7 +28,7 @@ public class TestDeobfuscator private Deobfuscator getDeobfuscator( ) throws IOException { - return new Deobfuscator( new File( "build/libs/testCases.obf.jar" ) ); + return new Deobfuscator( new File( "build/libs/testLoneClass.obf.jar" ) ); } @Test diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java new file mode 100644 index 00000000..56031ddc --- /dev/null +++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2014 Jeff Martin.\ + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + * + * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ +package cuchaz.enigma; + +import static cuchaz.enigma.EntryFactory.newClass; +import static cuchaz.enigma.EntryFactory.newField; +import static cuchaz.enigma.EntryFactory.newFieldReferenceByConstructor; +import static cuchaz.enigma.EntryFactory.newFieldReferenceByMethod; +import static cuchaz.enigma.EntryFactory.newMethod; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import java.util.Collection; +import java.util.Set; +import java.util.jar.JarFile; + +import org.junit.Test; + +import cuchaz.enigma.analysis.Access; +import cuchaz.enigma.analysis.ClassImplementationsTreeNode; +import cuchaz.enigma.analysis.ClassInheritanceTreeNode; +import cuchaz.enigma.analysis.EntryReference; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.analysis.MethodImplementationsTreeNode; +import cuchaz.enigma.analysis.MethodInheritanceTreeNode; +import cuchaz.enigma.mapping.BehaviorEntry; +import cuchaz.enigma.mapping.FieldEntry; +import cuchaz.enigma.mapping.MethodEntry; +import cuchaz.enigma.mapping.Translator; + +public class TestJarIndexLoneClass +{ + private JarIndex m_index; + + public TestJarIndexLoneClass( ) + throws Exception + { + m_index = new JarIndex(); + m_index.indexJar( new JarFile( "build/libs/testLoneClass.obf.jar" ), false ); + } + + @Test + public void obfEntries( ) + { + assertThat( m_index.getObfClassEntries(), containsInAnyOrder( + newClass( "cuchaz/enigma/inputs/Keep" ), + newClass( "none/a" ) + ) ); + } + + @Test + public void translationIndex( ) + { + assertThat( m_index.getTranslationIndex().getSuperclassName( "none/a" ), is( nullValue() ) ); + assertThat( m_index.getTranslationIndex().getSuperclassName( "cuchaz/enigma/inputs/Keep" ), is( nullValue() ) ); + assertThat( m_index.getTranslationIndex().getAncestry( "none/a" ), is( empty() ) ); + assertThat( m_index.getTranslationIndex().getAncestry( "cuchaz/enigma/inputs/Keep" ), is( empty() ) ); + assertThat( m_index.getTranslationIndex().getSubclassNames( "none/a" ), is( empty() ) ); + assertThat( m_index.getTranslationIndex().getSubclassNames( "cuchaz/enigma/inputs/Keep" ), is( empty() ) ); + } + + @Test + public void access( ) + { + assertThat( m_index.getAccess( newField( "none/a", "a" ) ), is( Access.Private ) ); + assertThat( m_index.getAccess( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( Access.Public ) ); + assertThat( m_index.getAccess( newField( "none/a", "b" ) ), is( nullValue() ) ); + } + + @Test + public void isImplemented( ) + { + assertThat( m_index.isMethodImplemented( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( true ) ); + assertThat( m_index.isMethodImplemented( newMethod( "none/a", "b", "()Ljava/lang/String;" ) ), is( false ) ); + } + + @Test + public void classInheritance( ) + { + ClassInheritanceTreeNode node = m_index.getClassInheritance( new Translator(), newClass( "none/a" ) ); + assertThat( node, is( not( nullValue() ) ) ); + assertThat( node.getObfClassName(), is( "none/a" ) ); + assertThat( node.getChildCount(), is( 0 ) ); + } + + + @Test + public void methodInheritance( ) + { + MethodEntry source = newMethod( "none/a", "a", "()Ljava/lang/String;" ); + MethodInheritanceTreeNode node = m_index.getMethodInheritance( new Translator(), source ); + assertThat( node, is( not( nullValue() ) ) ); + assertThat( node.getMethodEntry(), is( source ) ); + assertThat( node.getChildCount(), is( 0 ) ); + } + + @Test + public void classImplementations( ) + { + ClassImplementationsTreeNode node = m_index.getClassImplementations( new Translator(), newClass( "none/a" ) ); + assertThat( node, is( nullValue() ) ); + } + + @Test + public void methodImplementations( ) + { + MethodEntry source = newMethod( "none/a", "a", "()Ljava/lang/String;" ); + MethodImplementationsTreeNode node = m_index.getMethodImplementations( new Translator(), source ); + assertThat( node, is( nullValue() ) ); + } + + @Test + public void relatedMethodImplementations( ) + { + Set entries = m_index.getRelatedMethodImplementations( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ); + assertThat( entries, containsInAnyOrder( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ) ); + } + + @Test + @SuppressWarnings( "unchecked" ) + public void fieldReferences( ) + { + Collection> references = m_index.getFieldReferences( newField( "none/a", "a" ) ); + assertThat( references, containsInAnyOrder( + newFieldReferenceByConstructor( "none/a", "a", "none/a", "(Ljava/lang/String;)V" ), + newFieldReferenceByMethod( "none/a", "a", "none/a", "a", "()Ljava/lang/String;" ) + ) ); + } + + @Test + public void behaviorReferences( ) + { + assertThat( m_index.getBehaviorReferences( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( empty() ) ); + } + + @Test + public void innerClasses( ) + { + assertThat( m_index.getInnerClasses( "none/a" ), is( empty() ) ); + } + + @Test + public void outerClass( ) + { + assertThat( m_index.getOuterClass( "none/a" ), is( nullValue() ) ); + } + + @Test + public void isAnonymousClass( ) + { + assertThat( m_index.isAnonymousClass( "none/a" ), is( false ) ); + } + + @Test + public void interfaces( ) + { + assertThat( m_index.getInterfaces( "none/a" ), is( empty() ) ); + } + + @Test + public void implementingClasses( ) + { + assertThat( m_index.getImplementingClasses( "none/a" ), is( empty() ) ); + } + + @Test + public void isInterface( ) + { + assertThat( m_index.isInterface( "none/a" ), is( false ) ); + } + + @Test + public void bridgeMethods( ) + { + assertThat( m_index.getBridgeMethod( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( nullValue() ) ); + } + + @Test + public void contains( ) + { + assertThat( m_index.containsObfClass( newClass( "none/a" ) ), is( true ) ); + assertThat( m_index.containsObfClass( newClass( "none/b" ) ), is( false ) ); + assertThat( m_index.containsObfField( newField( "none/a", "a" ) ), is( true ) ); + assertThat( m_index.containsObfField( newField( "none/a", "b" ) ), is( false ) ); + assertThat( m_index.containsObfMethod( newMethod( "none/a", "a", "()Ljava/lang/String;" ) ), is( true ) ); + assertThat( m_index.containsObfMethod( newMethod( "none/a", "b", "()Ljava/lang/String;" ) ), is( false ) ); + } +} diff --git a/test/cuchaz/enigma/inputs/LoneClass.java b/test/cuchaz/enigma/inputs/LoneClass.java deleted file mode 100644 index a3d8cded..00000000 --- a/test/cuchaz/enigma/inputs/LoneClass.java +++ /dev/null @@ -1,16 +0,0 @@ -package cuchaz.enigma.inputs; - -public class LoneClass -{ - private String m_name; - - public LoneClass( String name ) - { - m_name = name; - } - - public String getName( ) - { - return m_name; - } -} diff --git a/test/cuchaz/enigma/inputs/loneClass/LoneClass.java b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java new file mode 100644 index 00000000..961b012e --- /dev/null +++ b/test/cuchaz/enigma/inputs/loneClass/LoneClass.java @@ -0,0 +1,16 @@ +package cuchaz.enigma.inputs.loneClass; + +public class LoneClass +{ + private String m_name; + + public LoneClass( String name ) + { + m_name = name; + } + + public String getName( ) + { + return m_name; + } +} -- cgit v1.2.3