• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 optparse
17import os
18import sys
19import unittest
20import jni_generator
21import jni_registration_generator
22from jni_generator import CalledByNative
23from jni_generator import IsMainDexJavaClass
24from jni_generator import NativeMethod
25from jni_generator import Param
26
27
28SCRIPT_NAME = 'base/android/jni_generator/jni_generator.py'
29INCLUDES = (
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.
35REBASELINE_ENV = 'REBASELINE'
36
37class 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
51class 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
724public static int foo(); // This one is fine
725
726@CalledByNative
727scooby 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
739package org.chromium.content.browser;
740
741import 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 = """
774public 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 = """
789public class java.util.HashSet {
790public boolean add(java.lang.Object);
791 Signature: (Ljava/lang/Object;)Z
792}
793"""
794
795    content_javap7 = """
796public class java.util.HashSet {
797public boolean add(E);
798  Signature: (Ljava/lang/Object;)Z
799}
800"""
801
802    content_javap8 = """
803public 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
916package org.chromium.content.app;
917
918import android.app.Service;
919import android.content.Context;
920import android.content.Intent;
921import android.graphics.SurfaceTexture;
922import android.os.Bundle;
923import android.os.IBinder;
924import android.os.ParcelFileDescriptor;
925import android.os.Process;
926import android.os.RemoteException;
927import android.util.Log;
928import android.view.Surface;
929
930import java.util.ArrayList;
931
932import org.chromium.base.annotations.CalledByNative;
933import org.chromium.base.annotations.JNINamespace;
934import org.chromium.content.app.ContentMain;
935import org.chromium.content.browser.SandboxedProcessConnection;
936import org.chromium.content.common.ISandboxedProcessCallback;
937import org.chromium.content.common.ISandboxedProcessService;
938import org.chromium.content.common.WillNotRaise.AnException;
939import org.chromium.content.common.WillRaise.AnException;
940
941import static org.chromium.Bar.Zoo;
942
943class 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
1160def 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
1169def 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
1187if __name__ == '__main__':
1188  sys.exit(main(sys.argv))
1189