• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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