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