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