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_optional = True
44    self.enable_profiling = 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    @CalledByNative
407    public static @Status int updateStatus(@Status int status) {
408        return getAndUpdateStatus(status);
409    }
410    @CalledByNativeUnchecked
411    private void uncheckedCall(int iParam);
412
413    @CalledByNative
414    public byte[] returnByteArray();
415
416    @CalledByNative
417    public boolean[] returnBooleanArray();
418
419    @CalledByNative
420    public char[] returnCharArray();
421
422    @CalledByNative
423    public short[] returnShortArray();
424
425    @CalledByNative
426    public int[] returnIntArray();
427
428    @CalledByNative
429    public long[] returnLongArray();
430
431    @CalledByNative
432    public double[] returnDoubleArray();
433
434    @CalledByNative
435    public Object[] returnObjectArray();
436
437    @CalledByNative
438    public byte[][] returnArrayOfByteArray();
439
440    @CalledByNative
441    public Bitmap.CompressFormat getCompressFormat();
442
443    @CalledByNative
444    public List<Bitmap.CompressFormat> getCompressFormatList();
445    """
446    jni_generator.JniParams.SetFullyQualifiedClass('org/chromium/Foo')
447    jni_generator.JniParams.ExtractImportsAndInnerClasses(test_data)
448    called_by_natives = jni_generator.ExtractCalledByNatives(test_data)
449    golden_called_by_natives = [
450        CalledByNative(
451            return_type='InnerClass',
452            system_class=False,
453            static=False,
454            name='showConfirmInfoBar',
455            method_id_var_name='showConfirmInfoBar',
456            java_class_name='',
457            params=[Param(datatype='int', name='nativeInfoBar'),
458                    Param(datatype='String', name='buttonOk'),
459                    Param(datatype='String', name='buttonCancel'),
460                    Param(datatype='String', name='title'),
461                    Param(datatype='Bitmap', name='icon')],
462            env_call=('Object', ''),
463            unchecked=False,
464        ),
465        CalledByNative(
466            return_type='InnerClass',
467            system_class=False,
468            static=False,
469            name='showAutoLoginInfoBar',
470            method_id_var_name='showAutoLoginInfoBar',
471            java_class_name='',
472            params=[Param(datatype='int', name='nativeInfoBar'),
473                    Param(datatype='String', name='realm'),
474                    Param(datatype='String', name='account'),
475                    Param(datatype='String', name='args')],
476            env_call=('Object', ''),
477            unchecked=False,
478        ),
479        CalledByNative(
480            return_type='void',
481            system_class=False,
482            static=False,
483            name='dismiss',
484            method_id_var_name='dismiss',
485            java_class_name='InfoBar',
486            params=[],
487            env_call=('Void', ''),
488            unchecked=False,
489        ),
490        CalledByNative(
491            return_type='boolean',
492            system_class=False,
493            static=True,
494            name='shouldShowAutoLogin',
495            method_id_var_name='shouldShowAutoLogin',
496            java_class_name='',
497            params=[Param(datatype='View', name='view'),
498                    Param(datatype='String', name='realm'),
499                    Param(datatype='String', name='account'),
500                    Param(datatype='String', name='args')],
501            env_call=('Boolean', ''),
502            unchecked=False,
503        ),
504        CalledByNative(
505            return_type='InputStream',
506            system_class=False,
507            static=True,
508            name='openUrl',
509            method_id_var_name='openUrl',
510            java_class_name='',
511            params=[Param(datatype='String', name='url')],
512            env_call=('Object', ''),
513            unchecked=False,
514        ),
515        CalledByNative(
516            return_type='void',
517            system_class=False,
518            static=False,
519            name='activateHardwareAcceleration',
520            method_id_var_name='activateHardwareAcceleration',
521            java_class_name='',
522            params=[Param(datatype='boolean', name='activated'),
523                    Param(datatype='int', name='iPid'),
524                    Param(datatype='int', name='iType'),
525                    Param(datatype='int', name='iPrimaryID'),
526                    Param(datatype='int', name='iSecondaryID'),
527                   ],
528            env_call=('Void', ''),
529            unchecked=False,
530        ),
531        CalledByNative(
532          return_type='int',
533          system_class=False,
534          static=True,
535          name='updateStatus',
536          method_id_var_name='updateStatus',
537          java_class_name='',
538          params=[Param(datatype='int', name='status')],
539          env_call=('Integer', ''),
540          unchecked=False,
541        ),
542        CalledByNative(
543            return_type='void',
544            system_class=False,
545            static=False,
546            name='uncheckedCall',
547            method_id_var_name='uncheckedCall',
548            java_class_name='',
549            params=[Param(datatype='int', name='iParam')],
550            env_call=('Void', ''),
551            unchecked=True,
552        ),
553        CalledByNative(
554            return_type='byte[]',
555            system_class=False,
556            static=False,
557            name='returnByteArray',
558            method_id_var_name='returnByteArray',
559            java_class_name='',
560            params=[],
561            env_call=('Void', ''),
562            unchecked=False,
563        ),
564        CalledByNative(
565            return_type='boolean[]',
566            system_class=False,
567            static=False,
568            name='returnBooleanArray',
569            method_id_var_name='returnBooleanArray',
570            java_class_name='',
571            params=[],
572            env_call=('Void', ''),
573            unchecked=False,
574        ),
575        CalledByNative(
576            return_type='char[]',
577            system_class=False,
578            static=False,
579            name='returnCharArray',
580            method_id_var_name='returnCharArray',
581            java_class_name='',
582            params=[],
583            env_call=('Void', ''),
584            unchecked=False,
585        ),
586        CalledByNative(
587            return_type='short[]',
588            system_class=False,
589            static=False,
590            name='returnShortArray',
591            method_id_var_name='returnShortArray',
592            java_class_name='',
593            params=[],
594            env_call=('Void', ''),
595            unchecked=False,
596        ),
597        CalledByNative(
598            return_type='int[]',
599            system_class=False,
600            static=False,
601            name='returnIntArray',
602            method_id_var_name='returnIntArray',
603            java_class_name='',
604            params=[],
605            env_call=('Void', ''),
606            unchecked=False,
607        ),
608        CalledByNative(
609            return_type='long[]',
610            system_class=False,
611            static=False,
612            name='returnLongArray',
613            method_id_var_name='returnLongArray',
614            java_class_name='',
615            params=[],
616            env_call=('Void', ''),
617            unchecked=False,
618        ),
619        CalledByNative(
620            return_type='double[]',
621            system_class=False,
622            static=False,
623            name='returnDoubleArray',
624            method_id_var_name='returnDoubleArray',
625            java_class_name='',
626            params=[],
627            env_call=('Void', ''),
628            unchecked=False,
629        ),
630        CalledByNative(
631            return_type='Object[]',
632            system_class=False,
633            static=False,
634            name='returnObjectArray',
635            method_id_var_name='returnObjectArray',
636            java_class_name='',
637            params=[],
638            env_call=('Void', ''),
639            unchecked=False,
640        ),
641        CalledByNative(
642            return_type='byte[][]',
643            system_class=False,
644            static=False,
645            name='returnArrayOfByteArray',
646            method_id_var_name='returnArrayOfByteArray',
647            java_class_name='',
648            params=[],
649            env_call=('Void', ''),
650            unchecked=False,
651        ),
652        CalledByNative(
653            return_type='Bitmap.CompressFormat',
654            system_class=False,
655            static=False,
656            name='getCompressFormat',
657            method_id_var_name='getCompressFormat',
658            java_class_name='',
659            params=[],
660            env_call=('Void', ''),
661            unchecked=False,
662        ),
663        CalledByNative(
664            return_type='List<Bitmap.CompressFormat>',
665            system_class=False,
666            static=False,
667            name='getCompressFormatList',
668            method_id_var_name='getCompressFormatList',
669            java_class_name='',
670            params=[],
671            env_call=('Void', ''),
672            unchecked=False,
673        ),
674    ]
675    self.assertListEquals(golden_called_by_natives, called_by_natives)
676    h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
677                                             [], called_by_natives, [],
678                                             TestOptions())
679    self.assertGoldenTextEquals(h.GetContent())
680
681  def testCalledByNativeParseError(self):
682    try:
683      jni_generator.ExtractCalledByNatives("""
684@CalledByNative
685public static int foo(); // This one is fine
686
687@CalledByNative
688scooby doo
689""")
690      self.fail('Expected a ParseError')
691    except jni_generator.ParseError, e:
692      self.assertEquals(('@CalledByNative', 'scooby doo'), e.context_lines)
693
694  def testFullyQualifiedClassName(self):
695    contents = """
696// Copyright (c) 2010 The Chromium Authors. All rights reserved.
697// Use of this source code is governed by a BSD-style license that can be
698// found in the LICENSE file.
699
700package org.chromium.content.browser;
701
702import org.chromium.base.BuildInfo;
703"""
704    self.assertEquals('org/chromium/content/browser/Foo',
705                      jni_generator.ExtractFullyQualifiedJavaClassName(
706                          'org/chromium/content/browser/Foo.java', contents))
707    self.assertEquals('org/chromium/content/browser/Foo',
708                      jni_generator.ExtractFullyQualifiedJavaClassName(
709                          'frameworks/Foo.java', contents))
710    self.assertRaises(SyntaxError,
711                      jni_generator.ExtractFullyQualifiedJavaClassName,
712                      'com/foo/Bar', 'no PACKAGE line')
713
714  def testMethodNameMangling(self):
715    self.assertEquals('closeV',
716        jni_generator.GetMangledMethodName('close', [], 'void'))
717    self.assertEquals('readI_AB_I_I',
718        jni_generator.GetMangledMethodName('read',
719            [Param(name='p1',
720                   datatype='byte[]'),
721             Param(name='p2',
722                   datatype='int'),
723             Param(name='p3',
724                   datatype='int'),],
725             'int'))
726    self.assertEquals('openJIIS_JLS',
727        jni_generator.GetMangledMethodName('open',
728            [Param(name='p1',
729                   datatype='java/lang/String'),],
730             'java/io/InputStream'))
731
732  def testFromJavaPGenerics(self):
733    contents = """
734public abstract class java.util.HashSet<T> extends java.util.AbstractSet<E>
735      implements java.util.Set<E>, java.lang.Cloneable, java.io.Serializable {
736    public void dummy();
737  Signature: ()V
738}
739"""
740    jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'),
741                                                TestOptions())
742    self.assertEquals(1, len(jni_from_javap.called_by_natives))
743    self.assertGoldenTextEquals(jni_from_javap.GetContent())
744
745  def testSnippnetJavap6_7_8(self):
746    content_javap6 = """
747public class java.util.HashSet {
748public boolean add(java.lang.Object);
749 Signature: (Ljava/lang/Object;)Z
750}
751"""
752
753    content_javap7 = """
754public class java.util.HashSet {
755public boolean add(E);
756  Signature: (Ljava/lang/Object;)Z
757}
758"""
759
760    content_javap8 = """
761public class java.util.HashSet {
762  public boolean add(E);
763    descriptor: (Ljava/lang/Object;)Z
764}
765"""
766
767    jni_from_javap6 = jni_generator.JNIFromJavaP(content_javap6.split('\n'),
768                                                 TestOptions())
769    jni_from_javap7 = jni_generator.JNIFromJavaP(content_javap7.split('\n'),
770                                                 TestOptions())
771    jni_from_javap8 = jni_generator.JNIFromJavaP(content_javap8.split('\n'),
772                                                 TestOptions())
773    self.assertTrue(jni_from_javap6.GetContent())
774    self.assertTrue(jni_from_javap7.GetContent())
775    self.assertTrue(jni_from_javap8.GetContent())
776    # Ensure the javap7 is correctly parsed and uses the Signature field rather
777    # than the "E" parameter.
778    self.assertTextEquals(jni_from_javap6.GetContent(),
779                          jni_from_javap7.GetContent())
780    # Ensure the javap8 is correctly parsed and uses the descriptor field.
781    self.assertTextEquals(jni_from_javap7.GetContent(),
782                          jni_from_javap8.GetContent())
783
784  def testFromJavaP(self):
785    contents = self._ReadGoldenFile(os.path.join(os.path.dirname(sys.argv[0]),
786        'testInputStream.javap'))
787    jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'),
788                                                TestOptions())
789    self.assertEquals(10, len(jni_from_javap.called_by_natives))
790    self.assertGoldenTextEquals(jni_from_javap.GetContent())
791
792  def testConstantsFromJavaP(self):
793    for f in ['testMotionEvent.javap', 'testMotionEvent.javap7']:
794      contents = self._ReadGoldenFile(os.path.join(os.path.dirname(sys.argv[0]),
795          f))
796      jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'),
797                                                  TestOptions())
798      self.assertEquals(86, len(jni_from_javap.called_by_natives))
799      self.assertGoldenTextEquals(jni_from_javap.GetContent())
800
801  def testREForNatives(self):
802    # We should not match "native SyncSetupFlow" inside the comment.
803    test_data = """
804    /**
805     * Invoked when the setup process is complete so we can disconnect from the
806     * native-side SyncSetupFlowHandler.
807     */
808    public void destroy() {
809        Log.v(TAG, "Destroying native SyncSetupFlow");
810        if (mNativeSyncSetupFlow != 0) {
811            nativeSyncSetupEnded(mNativeSyncSetupFlow);
812            mNativeSyncSetupFlow = 0;
813        }
814    }
815    private native void nativeSyncSetupEnded(
816        int nativeAndroidSyncSetupFlowHandler);
817    """
818    jni_from_java = jni_generator.JNIFromJavaSource(
819        test_data, 'foo/bar', TestOptions())
820
821  def testRaisesOnNonJNIMethod(self):
822    test_data = """
823    class MyInnerClass {
824      private int Foo(int p0) {
825      }
826    }
827    """
828    self.assertRaises(SyntaxError,
829                      jni_generator.JNIFromJavaSource,
830                      test_data, 'foo/bar', TestOptions())
831
832  def testJniSelfDocumentingExample(self):
833    script_dir = os.path.dirname(sys.argv[0])
834    content = file(os.path.join(script_dir,
835        'java/src/org/chromium/example/jni_generator/SampleForTests.java')
836        ).read()
837    golden_file = os.path.join(script_dir, 'SampleForTests_jni.golden')
838    golden_content = file(golden_file).read()
839    jni_from_java = jni_generator.JNIFromJavaSource(
840        content, 'org/chromium/example/jni_generator/SampleForTests',
841        TestOptions())
842    generated_text = jni_from_java.GetContent()
843    if not self.compareText(golden_content, generated_text):
844      if os.environ.get(REBASELINE_ENV):
845        with file(golden_file, 'w') as f:
846          f.write(generated_text)
847        return
848      self.fail('testJniSelfDocumentingExample')
849
850  def testNoWrappingPreprocessorLines(self):
851    test_data = """
852    package com.google.lookhowextremelylongiam.snarf.icankeepthisupallday;
853
854    class ReallyLongClassNamesAreAllTheRage {
855        private static native int nativeTest();
856    }
857    """
858    jni_from_java = jni_generator.JNIFromJavaSource(
859        test_data, ('com/google/lookhowextremelylongiam/snarf/'
860                    'icankeepthisupallday/ReallyLongClassNamesAreAllTheRage'),
861        TestOptions())
862    jni_lines = jni_from_java.GetContent().split('\n')
863    line = filter(lambda line: line.lstrip().startswith('#ifndef'),
864                  jni_lines)[0]
865    self.assertTrue(len(line) > 80,
866                    ('Expected #ifndef line to be > 80 chars: ', line))
867
868  def testImports(self):
869    import_header = """
870// Copyright (c) 2012 The Chromium Authors. All rights reserved.
871// Use of this source code is governed by a BSD-style license that can be
872// found in the LICENSE file.
873
874package org.chromium.content.app;
875
876import android.app.Service;
877import android.content.Context;
878import android.content.Intent;
879import android.graphics.SurfaceTexture;
880import android.os.Bundle;
881import android.os.IBinder;
882import android.os.ParcelFileDescriptor;
883import android.os.Process;
884import android.os.RemoteException;
885import android.util.Log;
886import android.view.Surface;
887
888import java.util.ArrayList;
889
890import org.chromium.base.annotations.CalledByNative;
891import org.chromium.base.annotations.JNINamespace;
892import org.chromium.content.app.ContentMain;
893import org.chromium.content.browser.SandboxedProcessConnection;
894import org.chromium.content.common.ISandboxedProcessCallback;
895import org.chromium.content.common.ISandboxedProcessService;
896import org.chromium.content.common.WillNotRaise.AnException;
897import org.chromium.content.common.WillRaise.AnException;
898
899import static org.chromium.Bar.Zoo;
900
901class Foo {
902  public static class BookmarkNode implements Parcelable {
903  }
904  public interface PasswordListObserver {
905  }
906}
907    """
908    jni_generator.JniParams.SetFullyQualifiedClass(
909        'org/chromium/content/app/Foo')
910    jni_generator.JniParams.ExtractImportsAndInnerClasses(import_header)
911    self.assertTrue('Lorg/chromium/content/common/ISandboxedProcessService' in
912                    jni_generator.JniParams._imports)
913    self.assertTrue('Lorg/chromium/Bar/Zoo' in
914                    jni_generator.JniParams._imports)
915    self.assertTrue('Lorg/chromium/content/app/Foo$BookmarkNode' in
916                    jni_generator.JniParams._inner_classes)
917    self.assertTrue('Lorg/chromium/content/app/Foo$PasswordListObserver' in
918                    jni_generator.JniParams._inner_classes)
919    self.assertEquals('Lorg/chromium/content/app/ContentMain$Inner;',
920                      jni_generator.JniParams.JavaToJni('ContentMain.Inner'))
921    self.assertRaises(SyntaxError,
922                      jni_generator.JniParams.JavaToJni,
923                      'AnException')
924
925  def testJniParamsJavaToJni(self):
926    self.assertTextEquals('I', JniParams.JavaToJni('int'))
927    self.assertTextEquals('[B', JniParams.JavaToJni('byte[]'))
928    self.assertTextEquals(
929        '[Ljava/nio/ByteBuffer;', JniParams.JavaToJni('java/nio/ByteBuffer[]'))
930
931  def testNativesLong(self):
932    test_options = TestOptions()
933    test_options.ptr_type = 'long'
934    test_data = """"
935    private native void nativeDestroy(long nativeChromeBrowserProvider);
936    """
937    jni_generator.JniParams.ExtractImportsAndInnerClasses(test_data)
938    natives = jni_generator.ExtractNatives(test_data, test_options.ptr_type)
939    golden_natives = [
940        NativeMethod(return_type='void', static=False, name='Destroy',
941                     params=[Param(datatype='long',
942                                   name='nativeChromeBrowserProvider')],
943                     java_class_name=None,
944                     type='method',
945                     p0_type='ChromeBrowserProvider',
946                     ptr_type=test_options.ptr_type),
947    ]
948    self.assertListEquals(golden_natives, natives)
949    h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni',
950                                             natives, [], [], test_options)
951    self.assertGoldenTextEquals(h.GetContent())
952
953  def testMainDexFile(self):
954    test_data = """
955    package org.chromium.example.jni_generator;
956
957    @MainDex
958    class Test {
959        private static native int nativeStaticMethod(long nativeTest, int arg1);
960    }
961    """
962    options = TestOptions()
963    jni_from_java = jni_generator.JNIFromJavaSource(
964      test_data, 'org/chromium/foo/Bar', options)
965    self.assertGoldenTextEquals(jni_from_java.GetContent())
966
967  def testNonMainDexFile(self):
968    test_data = """
969    package org.chromium.example.jni_generator;
970
971    class Test {
972        private static native int nativeStaticMethod(long nativeTest, int arg1);
973    }
974    """
975    options = TestOptions()
976    jni_from_java = jni_generator.JNIFromJavaSource(
977      test_data, 'org/chromium/foo/Bar', options)
978    self.assertGoldenTextEquals(jni_from_java.GetContent())
979
980  def testNativeExportsOnlyOption(self):
981    test_data = """
982    package org.chromium.example.jni_generator;
983
984    /** The pointer to the native Test. */
985    long nativeTest;
986
987    class Test {
988        private static native int nativeStaticMethod(long nativeTest, int arg1);
989        private native int nativeMethod(long nativeTest, int arg1);
990        @CalledByNative
991        private void testMethodWithParam(int iParam);
992        @CalledByNative
993        private String testMethodWithParamAndReturn(int iParam);
994        @CalledByNative
995        private static int testStaticMethodWithParam(int iParam);
996        @CalledByNative
997        private static double testMethodWithNoParam();
998        @CalledByNative
999        private static String testStaticMethodWithNoParam();
1000
1001        class MyInnerClass {
1002          @NativeCall("MyInnerClass")
1003          private native int nativeInit();
1004        }
1005        class MyOtherInnerClass {
1006          @NativeCall("MyOtherInnerClass")
1007          private native int nativeInit();
1008        }
1009    }
1010    """
1011    options = TestOptions()
1012    options.native_exports_optional = False
1013    jni_from_java = jni_generator.JNIFromJavaSource(
1014        test_data, 'org/chromium/example/jni_generator/SampleForTests', options)
1015    self.assertGoldenTextEquals(jni_from_java.GetContent())
1016
1017  def testOuterInnerRaises(self):
1018    test_data = """
1019    package org.chromium.media;
1020
1021    @CalledByNative
1022    static int getCaptureFormatWidth(VideoCapture.CaptureFormat format) {
1023        return format.getWidth();
1024    }
1025    """
1026    def willRaise():
1027      jni_generator.JNIFromJavaSource(
1028          test_data,
1029          'org/chromium/media/VideoCaptureFactory',
1030          TestOptions())
1031    self.assertRaises(SyntaxError, willRaise)
1032
1033  def testSingleJNIAdditionalImport(self):
1034    test_data = """
1035    package org.chromium.foo;
1036
1037    @JNIAdditionalImport(Bar.class)
1038    class Foo {
1039
1040    @CalledByNative
1041    private static void calledByNative(Bar.Callback callback) {
1042    }
1043
1044    private static native void nativeDoSomething(Bar.Callback callback);
1045    }
1046    """
1047    jni_from_java = jni_generator.JNIFromJavaSource(test_data,
1048                                                    'org/chromium/foo/Foo',
1049                                                    TestOptions())
1050    self.assertGoldenTextEquals(jni_from_java.GetContent())
1051
1052  def testMultipleJNIAdditionalImport(self):
1053    test_data = """
1054    package org.chromium.foo;
1055
1056    @JNIAdditionalImport({Bar1.class, Bar2.class})
1057    class Foo {
1058
1059    @CalledByNative
1060    private static void calledByNative(Bar1.Callback callback1,
1061                                       Bar2.Callback callback2) {
1062    }
1063
1064    private static native void nativeDoSomething(Bar1.Callback callback1,
1065                                                 Bar2.Callback callback2);
1066    }
1067    """
1068    jni_from_java = jni_generator.JNIFromJavaSource(test_data,
1069                                                    'org/chromium/foo/Foo',
1070                                                    TestOptions())
1071    self.assertGoldenTextEquals(jni_from_java.GetContent())
1072
1073
1074def TouchStamp(stamp_path):
1075  dir_name = os.path.dirname(stamp_path)
1076  if not os.path.isdir(dir_name):
1077    os.makedirs(dir_name)
1078
1079  with open(stamp_path, 'a'):
1080    os.utime(stamp_path, None)
1081
1082
1083def main(argv):
1084  parser = optparse.OptionParser()
1085  parser.add_option('--stamp', help='Path to touch on success.')
1086  options, _ = parser.parse_args(argv[1:])
1087
1088  test_result = unittest.main(argv=argv[0:1], exit=False)
1089
1090  if test_result.result.wasSuccessful() and options.stamp:
1091    TouchStamp(options.stamp)
1092
1093  return not test_result.result.wasSuccessful()
1094
1095
1096if __name__ == '__main__':
1097  sys.exit(main(sys.argv))
1098