Home | History | Annotate | Download | only in jni_generator
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """Tests for jni_generator.py.
      7 
      8 This test suite contains various tests for the JNI generator.
      9 It exercises the low-level parser all the way up to the
     10 code generator and ensures the output matches a golden
     11 file.
     12 """
     13 
     14 import difflib
     15 import inspect
     16 import optparse
     17 import os
     18 import sys
     19 import unittest
     20 import jni_generator
     21 import jni_registration_generator
     22 from jni_generator import CalledByNative
     23 from jni_generator import IsMainDexJavaClass
     24 from jni_generator import NativeMethod
     25 from jni_generator import Param
     26 
     27 
     28 SCRIPT_NAME = 'base/android/jni_generator/jni_generator.py'
     29 INCLUDES = (
     30     'base/android/jni_generator/jni_generator_helper.h'
     31 )
     32 
     33 # Set this environment variable in order to regenerate the golden text
     34 # files.
     35 REBASELINE_ENV = 'REBASELINE'
     36 
     37 class TestOptions(object):
     38   """The mock options object which is passed to the jni_generator.py script."""
     39 
     40   def __init__(self):
     41     self.namespace = None
     42     self.script_name = SCRIPT_NAME
     43     self.includes = INCLUDES
     44     self.ptr_type = 'long'
     45     self.cpp = 'cpp'
     46     self.javap = 'javap'
     47     self.native_exports_optional = True
     48     self.enable_profiling = False
     49     self.enable_tracing = False
     50 
     51 class TestGenerator(unittest.TestCase):
     52   def assertObjEquals(self, first, second):
     53     dict_first = first.__dict__
     54     dict_second = second.__dict__
     55     self.assertEquals(dict_first.keys(), dict_second.keys())
     56     for key, value in dict_first.iteritems():
     57       if (type(value) is list and len(value) and
     58           isinstance(type(value[0]), object)):
     59         self.assertListEquals(value, second.__getattribute__(key))
     60       else:
     61         actual = second.__getattribute__(key)
     62         self.assertEquals(value, actual,
     63                           'Key ' + key + ': ' + str(value) + '!=' + str(actual))
     64 
     65   def assertListEquals(self, first, second):
     66     self.assertEquals(len(first), len(second))
     67     for i in xrange(len(first)):
     68       if isinstance(first[i], object):
     69         self.assertObjEquals(first[i], second[i])
     70       else:
     71         self.assertEquals(first[i], second[i])
     72 
     73   def assertTextEquals(self, golden_text, generated_text):
     74     if not self.compareText(golden_text, generated_text):
     75       self.fail('Golden text mismatch.')
     76 
     77   def compareText(self, golden_text, generated_text):
     78     def FilterText(text):
     79       return [
     80           l.strip() for l in text.split('\n')
     81           if not l.startswith('// Copyright')
     82       ]
     83     stripped_golden = FilterText(golden_text)
     84     stripped_generated = FilterText(generated_text)
     85     if stripped_golden == stripped_generated:
     86       return True
     87     print self.id()
     88     for line in difflib.context_diff(stripped_golden, stripped_generated):
     89       print line
     90     print '\n\nGenerated'
     91     print '=' * 80
     92     print generated_text
     93     print '=' * 80
     94     print 'Run with:'
     95     print 'REBASELINE=1', sys.argv[0]
     96     print 'to regenerate the data files.'
     97 
     98   def _ReadGoldenFile(self, golden_file):
     99     if not os.path.exists(golden_file):
    100       return None
    101     with file(golden_file, 'r') as f:
    102       return f.read()
    103 
    104   def assertGoldenTextEquals(self, generated_text, suffix=''):
    105     script_dir = os.path.dirname(sys.argv[0])
    106     # This is the caller test method.
    107     caller = inspect.stack()[1][3]
    108     self.assertTrue(caller.startswith('test'),
    109                     'assertGoldenTextEquals can only be called from a '
    110                     'test* method, not %s' % caller)
    111     golden_file = os.path.join(script_dir, '%s%s.golden' % (caller, suffix))
    112     golden_text = self._ReadGoldenFile(golden_file)
    113     if os.environ.get(REBASELINE_ENV):
    114       if golden_text != generated_text:
    115         with file(golden_file, 'w') as f:
    116           f.write(generated_text)
    117       return
    118     self.assertTextEquals(golden_text, generated_text)
    119 
    120   def testInspectCaller(self):
    121     def willRaise():
    122       # This function can only be called from a test* method.
    123       self.assertGoldenTextEquals('')
    124     self.assertRaises(AssertionError, willRaise)
    125 
    126   def testNatives(self):
    127     test_data = """"
    128     import android.graphics.Bitmap;
    129     import android.view.View;
    130 
    131     interface OnFrameAvailableListener {}
    132     private native int nativeInit();
    133     private native void nativeDestroy(int nativeChromeBrowserProvider);
    134     private native long nativeAddBookmark(
    135             int nativeChromeBrowserProvider,
    136             String url, String title, boolean isFolder, long parentId);
    137     private static native String nativeGetDomainAndRegistry(String url);
    138     private static native void nativeCreateHistoricalTabFromState(
    139             byte[] state, int tab_index);
    140     private native byte[] nativeGetStateAsByteArray(View view);
    141     private static native String[] nativeGetAutofillProfileGUIDs();
    142     private native void nativeSetRecognitionResults(
    143             int sessionId, String[] results);
    144     private native long nativeAddBookmarkFromAPI(
    145             int nativeChromeBrowserProvider,
    146             String url, Long created, Boolean isBookmark,
    147             Long date, byte[] favicon, String title, Integer visits);
    148     native int nativeFindAll(String find);
    149     private static native OnFrameAvailableListener nativeGetInnerClass();
    150     private native Bitmap nativeQueryBitmap(
    151             int nativeChromeBrowserProvider,
    152             String[] projection, String selection,
    153             String[] selectionArgs, String sortOrder);
    154     private native void nativeGotOrientation(
    155             int nativeDataFetcherImplAndroid,
    156             double alpha, double beta, double gamma);
    157     private static native Throwable nativeMessWithJavaException(Throwable e);
    158     """
    159     jni_params = jni_generator.JniParams(
    160         'org/chromium/example/jni_generator/SampleForTests')
    161     jni_params.ExtractImportsAndInnerClasses(test_data)
    162     natives = jni_generator.ExtractNatives(test_data, 'int')
    163     golden_natives = [
    164         NativeMethod(return_type='int', static=False,
    165                      name='Init',
    166                      params=[],
    167                      java_class_name=None,
    168                      type='function'),
    169         NativeMethod(return_type='void', static=False, name='Destroy',
    170                      params=[Param(datatype='int',
    171                                    name='nativeChromeBrowserProvider')],
    172                      java_class_name=None,
    173                      type='method',
    174                      p0_type='ChromeBrowserProvider'),
    175         NativeMethod(return_type='long', static=False, name='AddBookmark',
    176                      params=[Param(datatype='int',
    177                                    name='nativeChromeBrowserProvider'),
    178                              Param(datatype='String',
    179                                    name='url'),
    180                              Param(datatype='String',
    181                                    name='title'),
    182                              Param(datatype='boolean',
    183                                    name='isFolder'),
    184                              Param(datatype='long',
    185                                    name='parentId')],
    186                      java_class_name=None,
    187                      type='method',
    188                      p0_type='ChromeBrowserProvider'),
    189         NativeMethod(return_type='String', static=True,
    190                      name='GetDomainAndRegistry',
    191                      params=[Param(datatype='String',
    192                                    name='url')],
    193                      java_class_name=None,
    194                      type='function'),
    195         NativeMethod(return_type='void', static=True,
    196                      name='CreateHistoricalTabFromState',
    197                      params=[Param(datatype='byte[]',
    198                                    name='state'),
    199                              Param(datatype='int',
    200                                    name='tab_index')],
    201                      java_class_name=None,
    202                      type='function'),
    203         NativeMethod(return_type='byte[]', static=False,
    204                      name='GetStateAsByteArray',
    205                      params=[Param(datatype='View', name='view')],
    206                      java_class_name=None,
    207                      type='function'),
    208         NativeMethod(return_type='String[]', static=True,
    209                      name='GetAutofillProfileGUIDs', params=[],
    210                      java_class_name=None,
    211                      type='function'),
    212         NativeMethod(return_type='void', static=False,
    213                      name='SetRecognitionResults',
    214                      params=[Param(datatype='int', name='sessionId'),
    215                              Param(datatype='String[]', name='results')],
    216                      java_class_name=None,
    217                      type='function'),
    218         NativeMethod(return_type='long', static=False,
    219                      name='AddBookmarkFromAPI',
    220                      params=[Param(datatype='int',
    221                                    name='nativeChromeBrowserProvider'),
    222                              Param(datatype='String',
    223                                    name='url'),
    224                              Param(datatype='Long',
    225                                    name='created'),
    226                              Param(datatype='Boolean',
    227                                    name='isBookmark'),
    228                              Param(datatype='Long',
    229                                    name='date'),
    230                              Param(datatype='byte[]',
    231                                    name='favicon'),
    232                              Param(datatype='String',
    233                                    name='title'),
    234                              Param(datatype='Integer',
    235                                    name='visits')],
    236                      java_class_name=None,
    237                      type='method',
    238                      p0_type='ChromeBrowserProvider'),
    239         NativeMethod(return_type='int', static=False,
    240                      name='FindAll',
    241                      params=[Param(datatype='String',
    242                                    name='find')],
    243                      java_class_name=None,
    244                      type='function'),
    245         NativeMethod(return_type='OnFrameAvailableListener', static=True,
    246                      name='GetInnerClass',
    247                      params=[],
    248                      java_class_name=None,
    249                      type='function'),
    250         NativeMethod(return_type='Bitmap',
    251                      static=False,
    252                      name='QueryBitmap',
    253                      params=[Param(datatype='int',
    254                                    name='nativeChromeBrowserProvider'),
    255                              Param(datatype='String[]',
    256                                    name='projection'),
    257                              Param(datatype='String',
    258                                    name='selection'),
    259                              Param(datatype='String[]',
    260                                    name='selectionArgs'),
    261                              Param(datatype='String',
    262                                    name='sortOrder'),
    263                             ],
    264                      java_class_name=None,
    265                      type='method',
    266                      p0_type='ChromeBrowserProvider'),
    267         NativeMethod(return_type='void', static=False,
    268                      name='GotOrientation',
    269                      params=[Param(datatype='int',
    270                                    name='nativeDataFetcherImplAndroid'),
    271                              Param(datatype='double',
    272                                    name='alpha'),
    273                              Param(datatype='double',
    274                                    name='beta'),
    275                              Param(datatype='double',
    276                                    name='gamma'),
    277                             ],
    278                      java_class_name=None,
    279                      type='method',
    280                      p0_type='content::DataFetcherImplAndroid'),
    281         NativeMethod(return_type='Throwable', static=True,
    282                      name='MessWithJavaException',
    283                      params=[Param(datatype='Throwable', name='e')],
    284                      java_class_name=None,
    285                      type='function')
    286     ]
    287     self.assertListEquals(golden_natives, natives)
    288     h1 = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
    289                                               natives, [], [], jni_params,
    290                                               TestOptions())
    291     self.assertGoldenTextEquals(h1.GetContent())
    292     h2 = jni_registration_generator.HeaderGenerator(
    293         '', 'org/chromium/TestJni', natives, jni_params, True)
    294     content = h2.Generate()
    295     for k in jni_registration_generator.MERGEABLE_KEYS:
    296       content[k] = content.get(k, '')
    297 
    298     self.assertGoldenTextEquals(
    299         jni_registration_generator.CreateFromDict(content),
    300         suffix='Registrations')
    301 
    302 
    303   def testInnerClassNatives(self):
    304     test_data = """
    305     class MyInnerClass {
    306       @NativeCall("MyInnerClass")
    307       private native int nativeInit();
    308     }
    309     """
    310     natives = jni_generator.ExtractNatives(test_data, 'int')
    311     golden_natives = [
    312         NativeMethod(return_type='int', static=False,
    313                      name='Init', params=[],
    314                      java_class_name='MyInnerClass',
    315                      type='function')
    316     ]
    317     self.assertListEquals(golden_natives, natives)
    318     jni_params = jni_generator.JniParams('')
    319     h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
    320                                              natives, [], [], jni_params,
    321                                              TestOptions())
    322     self.assertGoldenTextEquals(h.GetContent())
    323 
    324   def testInnerClassNativesMultiple(self):
    325     test_data = """
    326     class MyInnerClass {
    327       @NativeCall("MyInnerClass")
    328       private native int nativeInit();
    329     }
    330     class MyOtherInnerClass {
    331       @NativeCall("MyOtherInnerClass")
    332       private native int nativeInit();
    333     }
    334     """
    335     natives = jni_generator.ExtractNatives(test_data, 'int')
    336     golden_natives = [
    337         NativeMethod(return_type='int', static=False,
    338                      name='Init', params=[],
    339                      java_class_name='MyInnerClass',
    340                      type='function'),
    341         NativeMethod(return_type='int', static=False,
    342                      name='Init', params=[],
    343                      java_class_name='MyOtherInnerClass',
    344                      type='function')
    345     ]
    346     self.assertListEquals(golden_natives, natives)
    347     jni_params = jni_generator.JniParams('')
    348     h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
    349                                              natives, [], [], jni_params,
    350                                              TestOptions())
    351     self.assertGoldenTextEquals(h.GetContent())
    352 
    353   def testInnerClassNativesBothInnerAndOuter(self):
    354     test_data = """
    355     class MyOuterClass {
    356       private native int nativeInit();
    357       class MyOtherInnerClass {
    358         @NativeCall("MyOtherInnerClass")
    359         private native int nativeInit();
    360       }
    361     }
    362     """
    363     natives = jni_generator.ExtractNatives(test_data, 'int')
    364     golden_natives = [
    365         NativeMethod(return_type='int', static=False,
    366                      name='Init', params=[],
    367                      java_class_name=None,
    368                      type='function'),
    369         NativeMethod(return_type='int', static=False,
    370                      name='Init', params=[],
    371                      java_class_name='MyOtherInnerClass',
    372                      type='function')
    373     ]
    374     self.assertListEquals(golden_natives, natives)
    375     jni_params = jni_generator.JniParams('')
    376     h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
    377                                              natives, [], [], jni_params,
    378                                              TestOptions())
    379     self.assertGoldenTextEquals(h.GetContent())
    380 
    381     h2 = jni_registration_generator.HeaderGenerator(
    382         '', 'org/chromium/TestJni', natives, jni_params, True)
    383     content = h2.Generate()
    384     for k in jni_registration_generator.MERGEABLE_KEYS:
    385       content[k] = content.get(k, '')
    386 
    387     self.assertGoldenTextEquals(
    388         jni_registration_generator.CreateFromDict(content),
    389         suffix='Registrations')
    390 
    391   def testCalledByNatives(self):
    392     test_data = """"
    393     import android.graphics.Bitmap;
    394     import android.view.View;
    395     import java.io.InputStream;
    396     import java.util.List;
    397 
    398     class InnerClass {}
    399 
    400     @CalledByNative
    401     @SomeOtherA
    402     @SomeOtherB
    403     public InnerClass showConfirmInfoBar(int nativeInfoBar,
    404             String buttonOk, String buttonCancel, String title, Bitmap icon) {
    405         InfoBar infobar = new ConfirmInfoBar(nativeInfoBar, mContext,
    406                                              buttonOk, buttonCancel,
    407                                              title, icon);
    408         return infobar;
    409     }
    410     @CalledByNative
    411     InnerClass showAutoLoginInfoBar(int nativeInfoBar,
    412             String realm, String account, String args) {
    413         AutoLoginInfoBar infobar = new AutoLoginInfoBar(nativeInfoBar, mContext,
    414                 realm, account, args);
    415         if (infobar.displayedAccountCount() == 0)
    416             infobar = null;
    417         return infobar;
    418     }
    419     @CalledByNative("InfoBar")
    420     void dismiss();
    421     @SuppressWarnings("unused")
    422     @CalledByNative
    423     private static boolean shouldShowAutoLogin(View view,
    424             String realm, String account, String args) {
    425         AccountManagerContainer accountManagerContainer =
    426             new AccountManagerContainer((Activity)contentView.getContext(),
    427             realm, account, args);
    428         String[] logins = accountManagerContainer.getAccountLogins(null);
    429         return logins.length != 0;
    430     }
    431     @CalledByNative
    432     static InputStream openUrl(String url) {
    433         return null;
    434     }
    435     @CalledByNative
    436     private void activateHardwareAcceleration(final boolean activated,
    437             final int iPid, final int iType,
    438             final int iPrimaryID, final int iSecondaryID) {
    439       if (!activated) {
    440           return
    441       }
    442     }
    443     @CalledByNative
    444     public static @Status int updateStatus(@Status int status) {
    445         return getAndUpdateStatus(status);
    446     }
    447     @CalledByNativeUnchecked
    448     private void uncheckedCall(int iParam);
    449 
    450     @CalledByNative
    451     public byte[] returnByteArray();
    452 
    453     @CalledByNative
    454     public boolean[] returnBooleanArray();
    455 
    456     @CalledByNative
    457     public char[] returnCharArray();
    458 
    459     @CalledByNative
    460     public short[] returnShortArray();
    461 
    462     @CalledByNative
    463     public int[] returnIntArray();
    464 
    465     @CalledByNative
    466     public long[] returnLongArray();
    467 
    468     @CalledByNative
    469     public double[] returnDoubleArray();
    470 
    471     @CalledByNative
    472     public Object[] returnObjectArray();
    473 
    474     @CalledByNative
    475     public byte[][] returnArrayOfByteArray();
    476 
    477     @CalledByNative
    478     public Bitmap.CompressFormat getCompressFormat();
    479 
    480     @CalledByNative
    481     public List<Bitmap.CompressFormat> getCompressFormatList();
    482     """
    483     jni_params = jni_generator.JniParams('org/chromium/Foo')
    484     jni_params.ExtractImportsAndInnerClasses(test_data)
    485     called_by_natives = jni_generator.ExtractCalledByNatives(jni_params,
    486                                                              test_data)
    487     golden_called_by_natives = [
    488         CalledByNative(
    489             return_type='InnerClass',
    490             system_class=False,
    491             static=False,
    492             name='showConfirmInfoBar',
    493             method_id_var_name='showConfirmInfoBar',
    494             java_class_name='',
    495             params=[Param(datatype='int', name='nativeInfoBar'),
    496                     Param(datatype='String', name='buttonOk'),
    497                     Param(datatype='String', name='buttonCancel'),
    498                     Param(datatype='String', name='title'),
    499                     Param(datatype='Bitmap', name='icon')],
    500             env_call=('Object', ''),
    501             unchecked=False,
    502         ),
    503         CalledByNative(
    504             return_type='InnerClass',
    505             system_class=False,
    506             static=False,
    507             name='showAutoLoginInfoBar',
    508             method_id_var_name='showAutoLoginInfoBar',
    509             java_class_name='',
    510             params=[Param(datatype='int', name='nativeInfoBar'),
    511                     Param(datatype='String', name='realm'),
    512                     Param(datatype='String', name='account'),
    513                     Param(datatype='String', name='args')],
    514             env_call=('Object', ''),
    515             unchecked=False,
    516         ),
    517         CalledByNative(
    518             return_type='void',
    519             system_class=False,
    520             static=False,
    521             name='dismiss',
    522             method_id_var_name='dismiss',
    523             java_class_name='InfoBar',
    524             params=[],
    525             env_call=('Void', ''),
    526             unchecked=False,
    527         ),
    528         CalledByNative(
    529             return_type='boolean',
    530             system_class=False,
    531             static=True,
    532             name='shouldShowAutoLogin',
    533             method_id_var_name='shouldShowAutoLogin',
    534             java_class_name='',
    535             params=[Param(datatype='View', name='view'),
    536                     Param(datatype='String', name='realm'),
    537                     Param(datatype='String', name='account'),
    538                     Param(datatype='String', name='args')],
    539             env_call=('Boolean', ''),
    540             unchecked=False,
    541         ),
    542         CalledByNative(
    543             return_type='InputStream',
    544             system_class=False,
    545             static=True,
    546             name='openUrl',
    547             method_id_var_name='openUrl',
    548             java_class_name='',
    549             params=[Param(datatype='String', name='url')],
    550             env_call=('Object', ''),
    551             unchecked=False,
    552         ),
    553         CalledByNative(
    554             return_type='void',
    555             system_class=False,
    556             static=False,
    557             name='activateHardwareAcceleration',
    558             method_id_var_name='activateHardwareAcceleration',
    559             java_class_name='',
    560             params=[Param(datatype='boolean', name='activated'),
    561                     Param(datatype='int', name='iPid'),
    562                     Param(datatype='int', name='iType'),
    563                     Param(datatype='int', name='iPrimaryID'),
    564                     Param(datatype='int', name='iSecondaryID'),
    565                    ],
    566             env_call=('Void', ''),
    567             unchecked=False,
    568         ),
    569         CalledByNative(
    570           return_type='int',
    571           system_class=False,
    572           static=True,
    573           name='updateStatus',
    574           method_id_var_name='updateStatus',
    575           java_class_name='',
    576           params=[Param(datatype='int', name='status')],
    577           env_call=('Integer', ''),
    578           unchecked=False,
    579         ),
    580         CalledByNative(
    581             return_type='void',
    582             system_class=False,
    583             static=False,
    584             name='uncheckedCall',
    585             method_id_var_name='uncheckedCall',
    586             java_class_name='',
    587             params=[Param(datatype='int', name='iParam')],
    588             env_call=('Void', ''),
    589             unchecked=True,
    590         ),
    591         CalledByNative(
    592             return_type='byte[]',
    593             system_class=False,
    594             static=False,
    595             name='returnByteArray',
    596             method_id_var_name='returnByteArray',
    597             java_class_name='',
    598             params=[],
    599             env_call=('Void', ''),
    600             unchecked=False,
    601         ),
    602         CalledByNative(
    603             return_type='boolean[]',
    604             system_class=False,
    605             static=False,
    606             name='returnBooleanArray',
    607             method_id_var_name='returnBooleanArray',
    608             java_class_name='',
    609             params=[],
    610             env_call=('Void', ''),
    611             unchecked=False,
    612         ),
    613         CalledByNative(
    614             return_type='char[]',
    615             system_class=False,
    616             static=False,
    617             name='returnCharArray',
    618             method_id_var_name='returnCharArray',
    619             java_class_name='',
    620             params=[],
    621             env_call=('Void', ''),
    622             unchecked=False,
    623         ),
    624         CalledByNative(
    625             return_type='short[]',
    626             system_class=False,
    627             static=False,
    628             name='returnShortArray',
    629             method_id_var_name='returnShortArray',
    630             java_class_name='',
    631             params=[],
    632             env_call=('Void', ''),
    633             unchecked=False,
    634         ),
    635         CalledByNative(
    636             return_type='int[]',
    637             system_class=False,
    638             static=False,
    639             name='returnIntArray',
    640             method_id_var_name='returnIntArray',
    641             java_class_name='',
    642             params=[],
    643             env_call=('Void', ''),
    644             unchecked=False,
    645         ),
    646         CalledByNative(
    647             return_type='long[]',
    648             system_class=False,
    649             static=False,
    650             name='returnLongArray',
    651             method_id_var_name='returnLongArray',
    652             java_class_name='',
    653             params=[],
    654             env_call=('Void', ''),
    655             unchecked=False,
    656         ),
    657         CalledByNative(
    658             return_type='double[]',
    659             system_class=False,
    660             static=False,
    661             name='returnDoubleArray',
    662             method_id_var_name='returnDoubleArray',
    663             java_class_name='',
    664             params=[],
    665             env_call=('Void', ''),
    666             unchecked=False,
    667         ),
    668         CalledByNative(
    669             return_type='Object[]',
    670             system_class=False,
    671             static=False,
    672             name='returnObjectArray',
    673             method_id_var_name='returnObjectArray',
    674             java_class_name='',
    675             params=[],
    676             env_call=('Void', ''),
    677             unchecked=False,
    678         ),
    679         CalledByNative(
    680             return_type='byte[][]',
    681             system_class=False,
    682             static=False,
    683             name='returnArrayOfByteArray',
    684             method_id_var_name='returnArrayOfByteArray',
    685             java_class_name='',
    686             params=[],
    687             env_call=('Void', ''),
    688             unchecked=False,
    689         ),
    690         CalledByNative(
    691             return_type='Bitmap.CompressFormat',
    692             system_class=False,
    693             static=False,
    694             name='getCompressFormat',
    695             method_id_var_name='getCompressFormat',
    696             java_class_name='',
    697             params=[],
    698             env_call=('Void', ''),
    699             unchecked=False,
    700         ),
    701         CalledByNative(
    702             return_type='List<Bitmap.CompressFormat>',
    703             system_class=False,
    704             static=False,
    705             name='getCompressFormatList',
    706             method_id_var_name='getCompressFormatList',
    707             java_class_name='',
    708             params=[],
    709             env_call=('Void', ''),
    710             unchecked=False,
    711         ),
    712     ]
    713     self.assertListEquals(golden_called_by_natives, called_by_natives)
    714     h = jni_generator.InlHeaderFileGenerator(
    715         '', 'org/chromium/TestJni', [], called_by_natives, [], jni_params,
    716         TestOptions())
    717     self.assertGoldenTextEquals(h.GetContent())
    718 
    719   def testCalledByNativeParseError(self):
    720     try:
    721       jni_params = jni_generator.JniParams('')
    722       jni_generator.ExtractCalledByNatives(jni_params, """
    723 @CalledByNative
    724 public static int foo(); // This one is fine
    725 
    726 @CalledByNative
    727 scooby doo
    728 """)
    729       self.fail('Expected a ParseError')
    730     except jni_generator.ParseError, e:
    731       self.assertEquals(('@CalledByNative', 'scooby doo'), e.context_lines)
    732 
    733   def testFullyQualifiedClassName(self):
    734     contents = """
    735 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
    736 // Use of this source code is governed by a BSD-style license that can be
    737 // found in the LICENSE file.
    738 
    739 package org.chromium.content.browser;
    740 
    741 import org.chromium.base.BuildInfo;
    742 """
    743     self.assertEquals('org/chromium/content/browser/Foo',
    744                       jni_generator.ExtractFullyQualifiedJavaClassName(
    745                           'org/chromium/content/browser/Foo.java', contents))
    746     self.assertEquals('org/chromium/content/browser/Foo',
    747                       jni_generator.ExtractFullyQualifiedJavaClassName(
    748                           'frameworks/Foo.java', contents))
    749     self.assertRaises(SyntaxError,
    750                       jni_generator.ExtractFullyQualifiedJavaClassName,
    751                       'com/foo/Bar', 'no PACKAGE line')
    752 
    753   def testMethodNameMangling(self):
    754     jni_params = jni_generator.JniParams('')
    755     self.assertEquals('closeV',
    756         jni_generator.GetMangledMethodName(jni_params, 'close', [], 'void'))
    757     self.assertEquals('readI_AB_I_I',
    758         jni_generator.GetMangledMethodName(jni_params, 'read',
    759             [Param(name='p1',
    760                    datatype='byte[]'),
    761              Param(name='p2',
    762                    datatype='int'),
    763              Param(name='p3',
    764                    datatype='int'),],
    765              'int'))
    766     self.assertEquals('openJIIS_JLS',
    767         jni_generator.GetMangledMethodName(jni_params, 'open',
    768             [Param(name='p1',
    769                    datatype='java/lang/String'),],
    770              'java/io/InputStream'))
    771 
    772   def testFromJavaPGenerics(self):
    773     contents = """
    774 public abstract class java.util.HashSet<T> extends java.util.AbstractSet<E>
    775       implements java.util.Set<E>, java.lang.Cloneable, java.io.Serializable {
    776     public void dummy();
    777       Signature: ()V
    778     public java.lang.Class<?> getClass();
    779       Signature: ()Ljava/lang/Class<*>;
    780 }
    781 """
    782     jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'),
    783                                                 TestOptions())
    784     self.assertEquals(2, len(jni_from_javap.called_by_natives))
    785     self.assertGoldenTextEquals(jni_from_javap.GetContent())
    786 
    787   def testSnippnetJavap6_7_8(self):
    788     content_javap6 = """
    789 public class java.util.HashSet {
    790 public boolean add(java.lang.Object);
    791  Signature: (Ljava/lang/Object;)Z
    792 }
    793 """
    794 
    795     content_javap7 = """
    796 public class java.util.HashSet {
    797 public boolean add(E);
    798   Signature: (Ljava/lang/Object;)Z
    799 }
    800 """
    801 
    802     content_javap8 = """
    803 public class java.util.HashSet {
    804   public boolean add(E);
    805     descriptor: (Ljava/lang/Object;)Z
    806 }
    807 """
    808 
    809     jni_from_javap6 = jni_generator.JNIFromJavaP(content_javap6.split('\n'),
    810                                                  TestOptions())
    811     jni_from_javap7 = jni_generator.JNIFromJavaP(content_javap7.split('\n'),
    812                                                  TestOptions())
    813     jni_from_javap8 = jni_generator.JNIFromJavaP(content_javap8.split('\n'),
    814                                                  TestOptions())
    815     self.assertTrue(jni_from_javap6.GetContent())
    816     self.assertTrue(jni_from_javap7.GetContent())
    817     self.assertTrue(jni_from_javap8.GetContent())
    818     # Ensure the javap7 is correctly parsed and uses the Signature field rather
    819     # than the "E" parameter.
    820     self.assertTextEquals(jni_from_javap6.GetContent(),
    821                           jni_from_javap7.GetContent())
    822     # Ensure the javap8 is correctly parsed and uses the descriptor field.
    823     self.assertTextEquals(jni_from_javap7.GetContent(),
    824                           jni_from_javap8.GetContent())
    825 
    826   def testFromJavaP(self):
    827     contents = self._ReadGoldenFile(os.path.join(os.path.dirname(sys.argv[0]),
    828         'testInputStream.javap'))
    829     jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'),
    830                                                 TestOptions())
    831     self.assertEquals(10, len(jni_from_javap.called_by_natives))
    832     self.assertGoldenTextEquals(jni_from_javap.GetContent())
    833 
    834   def testConstantsFromJavaP(self):
    835     for f in ['testMotionEvent.javap', 'testMotionEvent.javap7']:
    836       contents = self._ReadGoldenFile(os.path.join(os.path.dirname(sys.argv[0]),
    837           f))
    838       jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'),
    839                                                   TestOptions())
    840       self.assertEquals(86, len(jni_from_javap.called_by_natives))
    841       self.assertGoldenTextEquals(jni_from_javap.GetContent())
    842 
    843   def testREForNatives(self):
    844     # We should not match "native SyncSetupFlow" inside the comment.
    845     test_data = """
    846     /**
    847      * Invoked when the setup process is complete so we can disconnect from the
    848      * native-side SyncSetupFlowHandler.
    849      */
    850     public void destroy() {
    851         Log.v(TAG, "Destroying native SyncSetupFlow");
    852         if (mNativeSyncSetupFlow != 0) {
    853             nativeSyncSetupEnded(mNativeSyncSetupFlow);
    854             mNativeSyncSetupFlow = 0;
    855         }
    856     }
    857     private native void nativeSyncSetupEnded(
    858         int nativeAndroidSyncSetupFlowHandler);
    859     """
    860     jni_from_java = jni_generator.JNIFromJavaSource(
    861         test_data, 'foo/bar', TestOptions())
    862 
    863   def testRaisesOnNonJNIMethod(self):
    864     test_data = """
    865     class MyInnerClass {
    866       private int Foo(int p0) {
    867       }
    868     }
    869     """
    870     self.assertRaises(SyntaxError,
    871                       jni_generator.JNIFromJavaSource,
    872                       test_data, 'foo/bar', TestOptions())
    873 
    874   def testJniSelfDocumentingExample(self):
    875     script_dir = os.path.dirname(sys.argv[0])
    876     content = file(os.path.join(script_dir,
    877         'java/src/org/chromium/example/jni_generator/SampleForTests.java')
    878         ).read()
    879     golden_file = os.path.join(script_dir, 'SampleForTests_jni.golden')
    880     golden_content = file(golden_file).read()
    881     jni_from_java = jni_generator.JNIFromJavaSource(
    882         content, 'org/chromium/example/jni_generator/SampleForTests',
    883         TestOptions())
    884     generated_text = jni_from_java.GetContent()
    885     if not self.compareText(golden_content, generated_text):
    886       if os.environ.get(REBASELINE_ENV):
    887         with file(golden_file, 'w') as f:
    888           f.write(generated_text)
    889         return
    890       self.fail('testJniSelfDocumentingExample')
    891 
    892   def testNoWrappingPreprocessorLines(self):
    893     test_data = """
    894     package com.google.lookhowextremelylongiam.snarf.icankeepthisupallday;
    895 
    896     class ReallyLongClassNamesAreAllTheRage {
    897         private static native int nativeTest();
    898     }
    899     """
    900     jni_from_java = jni_generator.JNIFromJavaSource(
    901         test_data, ('com/google/lookhowextremelylongiam/snarf/'
    902                     'icankeepthisupallday/ReallyLongClassNamesAreAllTheRage'),
    903         TestOptions())
    904     jni_lines = jni_from_java.GetContent().split('\n')
    905     line = filter(lambda line: line.lstrip().startswith('#ifndef'),
    906                   jni_lines)[0]
    907     self.assertTrue(len(line) > 80,
    908                     ('Expected #ifndef line to be > 80 chars: ', line))
    909 
    910   def testImports(self):
    911     import_header = """
    912 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
    913 // Use of this source code is governed by a BSD-style license that can be
    914 // found in the LICENSE file.
    915 
    916 package org.chromium.content.app;
    917 
    918 import android.app.Service;
    919 import android.content.Context;
    920 import android.content.Intent;
    921 import android.graphics.SurfaceTexture;
    922 import android.os.Bundle;
    923 import android.os.IBinder;
    924 import android.os.ParcelFileDescriptor;
    925 import android.os.Process;
    926 import android.os.RemoteException;
    927 import android.util.Log;
    928 import android.view.Surface;
    929 
    930 import java.util.ArrayList;
    931 
    932 import org.chromium.base.annotations.CalledByNative;
    933 import org.chromium.base.annotations.JNINamespace;
    934 import org.chromium.content.app.ContentMain;
    935 import org.chromium.content.browser.SandboxedProcessConnection;
    936 import org.chromium.content.common.ISandboxedProcessCallback;
    937 import org.chromium.content.common.ISandboxedProcessService;
    938 import org.chromium.content.common.WillNotRaise.AnException;
    939 import org.chromium.content.common.WillRaise.AnException;
    940 
    941 import static org.chromium.Bar.Zoo;
    942 
    943 class Foo {
    944   public static class BookmarkNode implements Parcelable {
    945   }
    946   public interface PasswordListObserver {
    947   }
    948 }
    949     """
    950     jni_params = jni_generator.JniParams('org/chromium/content/app/Foo')
    951     jni_params.ExtractImportsAndInnerClasses(import_header)
    952     self.assertTrue('Lorg/chromium/content/common/ISandboxedProcessService' in
    953                     jni_params._imports)
    954     self.assertTrue('Lorg/chromium/Bar/Zoo' in
    955                     jni_params._imports)
    956     self.assertTrue('Lorg/chromium/content/app/Foo$BookmarkNode' in
    957                     jni_params._inner_classes)
    958     self.assertTrue('Lorg/chromium/content/app/Foo$PasswordListObserver' in
    959                     jni_params._inner_classes)
    960     self.assertEquals('Lorg/chromium/content/app/ContentMain$Inner;',
    961                       jni_params.JavaToJni('ContentMain.Inner'))
    962     self.assertRaises(SyntaxError,
    963                       jni_params.JavaToJni, 'AnException')
    964 
    965   def testJniParamsJavaToJni(self):
    966     jni_params = jni_generator.JniParams('')
    967     self.assertTextEquals('I', jni_params.JavaToJni('int'))
    968     self.assertTextEquals('[B', jni_params.JavaToJni('byte[]'))
    969     self.assertTextEquals(
    970         '[Ljava/nio/ByteBuffer;', jni_params.JavaToJni('java/nio/ByteBuffer[]'))
    971 
    972   def testNativesLong(self):
    973     test_options = TestOptions()
    974     test_options.ptr_type = 'long'
    975     test_data = """"
    976     private native void nativeDestroy(long nativeChromeBrowserProvider);
    977     """
    978     jni_params = jni_generator.JniParams('')
    979     jni_params.ExtractImportsAndInnerClasses(test_data)
    980     natives = jni_generator.ExtractNatives(test_data, test_options.ptr_type)
    981     golden_natives = [
    982         NativeMethod(return_type='void', static=False, name='Destroy',
    983                      params=[Param(datatype='long',
    984                                    name='nativeChromeBrowserProvider')],
    985                      java_class_name=None,
    986                      type='method',
    987                      p0_type='ChromeBrowserProvider',
    988                      ptr_type=test_options.ptr_type),
    989     ]
    990     self.assertListEquals(golden_natives, natives)
    991     h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
    992                                              natives, [], [], jni_params,
    993                                              test_options)
    994     self.assertGoldenTextEquals(h.GetContent())
    995 
    996   def testMainDexAnnotation(self):
    997     mainDexEntries = [
    998       '@MainDex public class Test {',
    999       '@MainDex public class Test{',
   1000       """@MainDex
   1001          public class Test {
   1002       """,
   1003       """@MainDex public class Test
   1004          {
   1005       """,
   1006       '@MainDex /* This class is a test */ public class Test {',
   1007       '@MainDex public class Test implements java.io.Serializable {',
   1008       '@MainDex public class Test implements java.io.Serializable, Bidule {',
   1009       '@MainDex public class Test extends BaseTest {',
   1010       """@MainDex
   1011          public class Test extends BaseTest implements Bidule {
   1012       """,
   1013       """@MainDex
   1014          public class Test extends BaseTest implements Bidule, Machin, Chose {
   1015       """,
   1016       """@MainDex
   1017          public class Test implements Testable<java.io.Serializable> {
   1018       """,
   1019       '@MainDex public class Test implements Testable<java.io.Serializable> {',
   1020       '@a.B @MainDex @C public class Test extends Testable<Serializable> {',
   1021       """public class Test extends Testable<java.io.Serializable> {
   1022          @MainDex void func() {}
   1023       """,
   1024     ]
   1025     for entry in mainDexEntries:
   1026       self.assertEquals(True, IsMainDexJavaClass(entry), entry)
   1027 
   1028   def testNoMainDexAnnotation(self):
   1029     noMainDexEntries = [
   1030       'public class Test {',
   1031       '@NotMainDex public class Test {',
   1032       '// @MainDex public class Test {',
   1033       '/* @MainDex */ public class Test {',
   1034       'public class Test implements java.io.Serializable {',
   1035       '@MainDexNot public class Test {',
   1036       'public class Test extends BaseTest {'
   1037     ]
   1038     for entry in noMainDexEntries:
   1039       self.assertEquals(False, IsMainDexJavaClass(entry))
   1040 
   1041   def testNativeExportsOnlyOption(self):
   1042     test_data = """
   1043     package org.chromium.example.jni_generator;
   1044 
   1045     /** The pointer to the native Test. */
   1046     long nativeTest;
   1047 
   1048     class Test {
   1049         private static native int nativeStaticMethod(long nativeTest, int arg1);
   1050         private native int nativeMethod(long nativeTest, int arg1);
   1051         @CalledByNative
   1052         private void testMethodWithParam(int iParam);
   1053         @CalledByNative
   1054         private String testMethodWithParamAndReturn(int iParam);
   1055         @CalledByNative
   1056         private static int testStaticMethodWithParam(int iParam);
   1057         @CalledByNative
   1058         private static double testMethodWithNoParam();
   1059         @CalledByNative
   1060         private static String testStaticMethodWithNoParam();
   1061 
   1062         class MyInnerClass {
   1063           @NativeCall("MyInnerClass")
   1064           private native int nativeInit();
   1065         }
   1066         class MyOtherInnerClass {
   1067           @NativeCall("MyOtherInnerClass")
   1068           private native int nativeInit();
   1069         }
   1070     }
   1071     """
   1072     options = TestOptions()
   1073     options.native_exports_optional = False
   1074     jni_from_java = jni_generator.JNIFromJavaSource(
   1075         test_data, 'org/chromium/example/jni_generator/SampleForTests', options)
   1076     self.assertGoldenTextEquals(jni_from_java.GetContent())
   1077 
   1078   def testOuterInnerRaises(self):
   1079     test_data = """
   1080     package org.chromium.media;
   1081 
   1082     @CalledByNative
   1083     static int getCaptureFormatWidth(VideoCapture.CaptureFormat format) {
   1084         return format.getWidth();
   1085     }
   1086     """
   1087     def willRaise():
   1088       jni_generator.JNIFromJavaSource(
   1089           test_data,
   1090           'org/chromium/media/VideoCaptureFactory',
   1091           TestOptions())
   1092     self.assertRaises(SyntaxError, willRaise)
   1093 
   1094   def testSingleJNIAdditionalImport(self):
   1095     test_data = """
   1096     package org.chromium.foo;
   1097 
   1098     @JNIAdditionalImport(Bar.class)
   1099     class Foo {
   1100 
   1101     @CalledByNative
   1102     private static void calledByNative(Bar.Callback callback) {
   1103     }
   1104 
   1105     private static native void nativeDoSomething(Bar.Callback callback);
   1106     }
   1107     """
   1108     jni_from_java = jni_generator.JNIFromJavaSource(test_data,
   1109                                                     'org/chromium/foo/Foo',
   1110                                                     TestOptions())
   1111     self.assertGoldenTextEquals(jni_from_java.GetContent())
   1112 
   1113   def testMultipleJNIAdditionalImport(self):
   1114     test_data = """
   1115     package org.chromium.foo;
   1116 
   1117     @JNIAdditionalImport({Bar1.class, Bar2.class})
   1118     class Foo {
   1119 
   1120     @CalledByNative
   1121     private static void calledByNative(Bar1.Callback callback1,
   1122                                        Bar2.Callback callback2) {
   1123     }
   1124 
   1125     private static native void nativeDoSomething(Bar1.Callback callback1,
   1126                                                  Bar2.Callback callback2);
   1127     }
   1128     """
   1129     jni_from_java = jni_generator.JNIFromJavaSource(test_data,
   1130                                                     'org/chromium/foo/Foo',
   1131                                                     TestOptions())
   1132     self.assertGoldenTextEquals(jni_from_java.GetContent())
   1133 
   1134   def testTracing(self):
   1135     test_data = """
   1136     package org.chromium.foo;
   1137 
   1138     @JNINamespace("org::chromium_foo")
   1139     class Foo {
   1140 
   1141     @CalledByNative
   1142     Foo();
   1143 
   1144     @CalledByNative
   1145     void callbackFromNative();
   1146 
   1147     native void nativeInstanceMethod(long nativeInstance);
   1148 
   1149     static native void nativeStaticMethod();
   1150     }
   1151     """
   1152     options_with_tracing = TestOptions()
   1153     options_with_tracing.enable_tracing = True
   1154     jni_from_java = jni_generator.JNIFromJavaSource(test_data,
   1155                                                     'org/chromium/foo/Foo',
   1156                                                     options_with_tracing)
   1157     self.assertGoldenTextEquals(jni_from_java.GetContent())
   1158 
   1159 
   1160 def TouchStamp(stamp_path):
   1161   dir_name = os.path.dirname(stamp_path)
   1162   if not os.path.isdir(dir_name):
   1163     os.makedirs(dir_name)
   1164 
   1165   with open(stamp_path, 'a'):
   1166     os.utime(stamp_path, None)
   1167 
   1168 
   1169 def main(argv):
   1170   parser = optparse.OptionParser()
   1171   parser.add_option('--stamp', help='Path to touch on success.')
   1172   parser.add_option('--verbose', action="store_true",
   1173                     help='Whether to output details.')
   1174   options, _ = parser.parse_args(argv[1:])
   1175 
   1176   test_result = unittest.main(
   1177       argv=argv[0:1],
   1178       exit=False,
   1179       verbosity=(2 if options.verbose else 1))
   1180 
   1181   if test_result.result.wasSuccessful() and options.stamp:
   1182     TouchStamp(options.stamp)
   1183 
   1184   return not test_result.result.wasSuccessful()
   1185 
   1186 
   1187 if __name__ == '__main__':
   1188   sys.exit(main(sys.argv))
   1189