1#!/usr/bin/env vpython3 2# 3# Copyright 2017 The Chromium Authors 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6"""Tests for java_deobfuscate.""" 7 8import argparse 9import os 10import subprocess 11import sys 12import tempfile 13import unittest 14 15# Set by command-line argument. 16_JAVA_DEOBFUSCATE_PATH = None 17 18LINE_PREFIXES = [ 19 '', 20 # logcat -v threadtime 21 '09-08 14:38:35.535 18029 18084 E qcom_sensors_hal: ', 22 # logcat 23 'W/GCM (15158): ', 24 'W/GCM ( 158): ', 25] 26 27TEST_MAP = """\ 28this.was.Deobfuscated -> FOO: 29 int[] mFontFamily -> a 30 1:3:void someMethod(int,android.os.Bundle):65:67 -> bar 31never.Deobfuscated -> NOTFOO: 32 int[] mFontFamily -> a 33 1:3:void someMethod(int,android.os.Bundle):65:67 -> bar 34""" 35 36TEST_DATA = [ 37 '', 38 'FOO', 39 'FOO.bar', 40 'Here is a FOO', 41 'Here is a class FOO', 42 'Here is a class FOO baz', 43 'Here is a "FOO" baz', 44 'Here is a type "FOO" baz', 45 'Here is a "FOO.bar" baz', 46 'SomeError: SomeFrameworkClass in isTestClass for FOO', 47 'Here is a FOO.bar', 48 'Here is a FOO.bar baz', 49 'END FOO#bar', 50 'new-instance 3810 (LSome/Framework/Class;) in LFOO;', 51 'FOO: Error message', 52 'Caused by: FOO: Error message', 53 '\tat FOO.bar(PG:1)', 54 '\t at\t FOO.bar\t (\t PG:\t 1\t )', 55 '0xfff \t( \tPG:\t 1 \t)\tFOO.bar', 56 ('Unable to start activity ComponentInfo{garbage.in/here.test}:' 57 ' java.lang.NullPointerException: Attempt to invoke interface method' 58 ' \'void FOO.bar(int,android.os.Bundle)\' on a null object reference'), 59 ('Caused by: java.lang.NullPointerException: Attempt to read from field' 60 ' \'int[] FOO.a\' on a null object reference'), 61 'java.lang.VerifyError: FOO', 62 ('java.lang.NoSuchFieldError: No instance field a of type ' 63 'Ljava/lang/Class; in class LFOO;'), 64 'NOTFOO: Object of type FOO was not destroyed...', 65] 66 67EXPECTED_OUTPUT = [ 68 '', 69 'this.was.Deobfuscated', 70 'this.was.Deobfuscated.someMethod', 71 'Here is a FOO', 72 'Here is a class this.was.Deobfuscated', 73 'Here is a class FOO baz', 74 'Here is a "FOO" baz', 75 'Here is a type "this.was.Deobfuscated" baz', 76 'Here is a "this.was.Deobfuscated.someMethod" baz', 77 'SomeError: SomeFrameworkClass in isTestClass for this.was.Deobfuscated', 78 'Here is a this.was.Deobfuscated.someMethod', 79 'Here is a FOO.bar baz', 80 'END this.was.Deobfuscated#someMethod', 81 'new-instance 3810 (LSome/Framework/Class;) in Lthis/was/Deobfuscated;', 82 'this.was.Deobfuscated: Error message', 83 'Caused by: this.was.Deobfuscated: Error message', 84 '\tat this.was.Deobfuscated.someMethod(Deobfuscated.java:65)', 85 ('\t at\t this.was.Deobfuscated.someMethod\t ' 86 '(\t Deobfuscated.java:\t 65\t )'), 87 '0xfff \t( \tDeobfuscated.java:\t 65 \t)\tthis.was.Deobfuscated.someMethod', 88 ('Unable to start activity ComponentInfo{garbage.in/here.test}:' 89 ' java.lang.NullPointerException: Attempt to invoke interface method' 90 ' \'void this.was.Deobfuscated.someMethod(int,android.os.Bundle)\' on a' 91 ' null object reference'), 92 ('Caused by: java.lang.NullPointerException: Attempt to read from field' 93 ' \'int[] this.was.Deobfuscated.mFontFamily\' on a null object reference'), 94 'java.lang.VerifyError: this.was.Deobfuscated', 95 ('java.lang.NoSuchFieldError: No instance field mFontFamily of type ' 96 'Ljava/lang/Class; in class Lthis/was/Deobfuscated;'), 97 'NOTFOO: Object of type this.was.Deobfuscated was not destroyed...', 98] 99TEST_DATA = [s + '\n' for s in TEST_DATA] 100EXPECTED_OUTPUT = [s + '\n' for s in EXPECTED_OUTPUT] 101 102 103class JavaDeobfuscateTest(unittest.TestCase): 104 105 def __init__(self, *args, **kwargs): 106 super().__init__(*args, **kwargs) 107 self._map_file = None 108 109 def setUp(self): 110 self._map_file = tempfile.NamedTemporaryFile() 111 self._map_file.write(TEST_MAP.encode('utf-8')) 112 self._map_file.flush() 113 114 def tearDown(self): 115 if self._map_file: 116 self._map_file.close() 117 118 def _testImpl(self, input_lines=None, expected_output_lines=None, 119 prefix=''): 120 self.assertTrue(bool(input_lines) == bool(expected_output_lines)) 121 122 if not input_lines: 123 input_lines = [prefix + x for x in TEST_DATA] 124 if not expected_output_lines: 125 expected_output_lines = [prefix + x for x in EXPECTED_OUTPUT] 126 127 cmd = [_JAVA_DEOBFUSCATE_PATH, self._map_file.name] 128 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 129 proc_output, _ = proc.communicate(''.join(input_lines).encode()) 130 actual_output_lines = proc_output.decode().splitlines(True) 131 for actual, expected in zip(actual_output_lines, expected_output_lines): 132 self.assertTrue( 133 actual == expected or actual.replace('bar', 'someMethod') == expected, 134 msg=''.join([ 135 'Deobfuscation failed.\n', 136 ' actual: %s' % actual, 137 ' expected: %s' % expected])) 138 139 def testNoPrefix(self): 140 self._testImpl(prefix='') 141 142 def testThreadtimePrefix(self): 143 self._testImpl(prefix='09-08 14:38:35.535 18029 18084 E qcom_sensors_hal: ') 144 145 def testStandardPrefix(self): 146 self._testImpl(prefix='W/GCM (15158): ') 147 148 def testStandardPrefixWithPadding(self): 149 self._testImpl(prefix='W/GCM ( 158): ') 150 151 @unittest.skip('causes java_deobfuscate to hang, see crbug.com/876539') 152 def testIndefiniteHang(self): 153 # Test for crbug.com/876539. 154 self._testImpl( 155 input_lines=[ 156 'VFY: unable to resolve virtual method 2: LFOO;' 157 + '.onDescendantInvalidated ' 158 + '(Landroid/view/View;Landroid/view/View;)V', 159 ], 160 expected_output_lines=[ 161 'VFY: unable to resolve virtual method 2: Lthis.was.Deobfuscated;' 162 + '.onDescendantInvalidated ' 163 + '(Landroid/view/View;Landroid/view/View;)V', 164 ]) 165 166 167if __name__ == '__main__': 168 parser = argparse.ArgumentParser() 169 parser.add_argument('--java-deobfuscate-path', type=os.path.realpath, 170 required=True) 171 known_args, unittest_args = parser.parse_known_args() 172 _JAVA_DEOBFUSCATE_PATH = known_args.java_deobfuscate_path 173 unittest_args = [sys.argv[0]] + unittest_args 174 unittest.main(argv=unittest_args) 175