• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright 2012 The Chromium Authors
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 collections
15import copy
16import difflib
17import inspect
18import optparse
19import os
20import sys
21import tempfile
22import unittest
23import jni_generator
24import jni_registration_generator
25import zipfile
26from jni_generator import CalledByNative
27from jni_generator import NativeMethod
28from jni_generator import Param
29from jni_generator import ProxyHelpers
30
31_SCRIPT_NAME = 'base/android/jni_generator/jni_generator.py'
32_INCLUDES = ('base/android/jni_generator/jni_generator_helper.h')
33_JAVA_SRC_DIR = os.path.join('java', 'src', 'org', 'chromium', 'example',
34                             'jni_generator')
35
36# Set this environment variable in order to regenerate the golden text
37# files.
38_REBASELINE_ENV = 'REBASELINE'
39
40
41def _RemoveHashedNames(natives):
42  ret = []
43  for n in natives:
44    ret.append(jni_generator.NativeMethod(**n.__dict__))
45    ret[-1].hashed_proxy_name = None
46  return ret
47
48
49class JniGeneratorOptions(object):
50  """The mock options object which is passed to the jni_generator.py script."""
51
52  def __init__(self):
53    self.namespace = None
54    self.script_name = _SCRIPT_NAME
55    self.includes = _INCLUDES
56    self.ptr_type = 'long'
57    self.cpp = 'cpp'
58    self.javap = 'mock-javap'
59    self.enable_profiling = False
60    self.use_proxy_hash = False
61    self.enable_jni_multiplexing = False
62    self.always_mangle = False
63    self.unchecked_exceptions = False
64    self.split_name = None
65    self.include_test_only = True
66    self.package_prefix = None
67
68
69class JniRegistrationGeneratorOptions(object):
70  """The mock options object which is passed to the jni_generator.py script."""
71
72  def __init__(self):
73    self.sources_exclusions = []
74    self.namespace = None
75    self.enable_proxy_mocks = False
76    self.require_mocks = False
77    self.use_proxy_hash = False
78    self.enable_jni_multiplexing = False
79    self.manual_jni_registration = False
80    self.include_test_only = False
81    self.header_path = None
82    self.module_name = ''
83    self.package_prefix = None
84
85
86class BaseTest(unittest.TestCase):
87
88  @staticmethod
89  def _MergeRegistrationForTests(results,
90                                 header_guard='HEADER_GUARD',
91                                 namespace='test',
92                                 enable_jni_multiplexing=False):
93
94    results.sort(key=lambda d: d['FULL_CLASS_NAME'])
95
96    combined_dict = {}
97    for key in jni_registration_generator.MERGEABLE_KEYS:
98      combined_dict[key] = ''.join(d.get(key, '') for d in results)
99
100    combined_dict['HEADER_GUARD'] = header_guard
101    combined_dict['NAMESPACE'] = namespace
102
103    if enable_jni_multiplexing:
104      proxy_signatures_list = sorted(
105          set(combined_dict['PROXY_NATIVE_SIGNATURES'].split('\n')))
106      combined_dict['PROXY_NATIVE_SIGNATURES'] = '\n'.join(
107          signature for signature in proxy_signatures_list)
108
109      proxy_native_array_list = sorted(
110          set(combined_dict['PROXY_NATIVE_METHOD_ARRAY'].split('},\n')))
111      combined_dict['PROXY_NATIVE_METHOD_ARRAY'] = '},\n'.join(
112          p for p in proxy_native_array_list if p != '') + '}'
113
114      signature_to_cases = collections.defaultdict(list)
115      for d in results:
116        for signature, cases in d['SIGNATURE_TO_CASES'].items():
117          signature_to_cases[signature].extend(cases)
118      combined_dict[
119          'FORWARDING_CALLS'] = jni_registration_generator._AddForwardingCalls(
120              signature_to_cases, '', '')
121
122    return combined_dict
123
124  def _TestEndToEndRegistration(self,
125                                input_java_src_files,
126                                options,
127                                name_to_goldens,
128                                header_golden=None):
129    with tempfile.TemporaryDirectory() as tdir:
130      options.srcjar_path = os.path.join(tdir, 'srcjar.jar')
131      if header_golden:
132        options.header_path = os.path.join(tdir, 'header.h')
133
134      input_java_paths = [
135          self._JoinScriptDir(os.path.join(_JAVA_SRC_DIR, f))
136          for f in input_java_src_files
137      ]
138
139      jni_registration_generator._Generate(options, input_java_paths)
140      with zipfile.ZipFile(options.srcjar_path, 'r') as srcjar:
141        for name in srcjar.namelist():
142          self.assertTrue(
143              name in name_to_goldens,
144              f'Found {name} output, but not present in name_to_goldens map.')
145          contents = srcjar.read(name).decode('utf-8')
146          self.AssertGoldenTextEquals(contents,
147                                      golden_file=name_to_goldens[name])
148      if header_golden:
149        with open(options.header_path, 'r') as f:
150          # Temp directory will cause some diffs each time we run if we don't
151          # normalize.
152          contents = f.read().replace(
153              tdir.replace('/', '_').upper(), 'TEMP_DIR')
154          self.AssertGoldenTextEquals(contents, golden_file=header_golden)
155
156  def _JoinScriptDir(self, path):
157    script_dir = os.path.dirname(sys.argv[0])
158    return os.path.join(script_dir, path)
159
160  def _JoinGoldenPath(self, golden_file_name):
161    return self._JoinScriptDir(os.path.join('golden', golden_file_name))
162
163  def _ReadGoldenFile(self, golden_file_name):
164    golden_file_name = self._JoinGoldenPath(golden_file_name)
165    if not os.path.exists(golden_file_name):
166      return None
167    with open(golden_file_name, 'r') as f:
168      return f.read()
169
170  def _CreateJniHeaderFromFile(self, fname, qualified_clazz, options=None):
171    with open(self._JoinScriptDir(fname)) as f:
172      content = f.read()
173    opts = options
174    if opts is None:
175      opts = JniGeneratorOptions()
176
177    jni_from_java = jni_generator.JNIFromJavaSource(content, qualified_clazz,
178                                                    opts)
179    return jni_from_java.GetContent()
180
181  def AssertObjEquals(self, first, second):
182    if isinstance(first, str):
183      return self.assertEqual(first, second)
184    dict_first = first.__dict__
185    dict_second = second.__dict__
186    self.assertEqual(dict_first.keys(), dict_second.keys())
187    for key, value in dict_first.items():
188      if (type(value) is list and len(value)
189          and isinstance(type(value[0]), object)):
190        self.AssertListEquals(value, second.__getattribute__(key))
191      else:
192        actual = second.__getattribute__(key)
193        self.assertEqual(value, actual,
194                         'Key ' + key + ': ' + str(value) + '!=' + str(actual))
195
196  def AssertListEquals(self, first, second):
197    self.assertEqual(len(first), len(second))
198    for i in range(len(first)):
199      if isinstance(first[i], object):
200        self.AssertObjEquals(first[i], second[i])
201      else:
202        self.assertEqual(first[i], second[i])
203
204  def AssertTextEquals(self, golden_text, generated_text):
205    if not self.CompareText(golden_text, generated_text):
206      self.fail('Golden text mismatch.')
207
208  def CompareText(self, golden_text, generated_text):
209
210    def FilterText(text):
211      return [
212          l.strip() for l in text.split('\n')
213          if not l.startswith('// Copyright')
214      ]
215
216    stripped_golden = FilterText(golden_text)
217    stripped_generated = FilterText(generated_text)
218    if stripped_golden == stripped_generated:
219      return True
220    print(self.id())
221    for line in difflib.context_diff(stripped_golden, stripped_generated):
222      print(line)
223    print('\n\nGenerated')
224    print('=' * 80)
225    print(generated_text)
226    print('=' * 80)
227    print('Run with:')
228    print('REBASELINE=1', sys.argv[0])
229    print('to regenerate the data files.')
230
231  def AssertGoldenTextEquals(self, generated_text, suffix='', golden_file=None):
232    """Compares generated text with the corresponding golden_file
233
234    By default compares generated_text with the file at
235    script_dir/golden/{caller_name}[suffix].golden. If the parameter
236    golden_file is provided it will instead compare the generated text with
237    script_dir/golden/golden_file."""
238    # This is the caller test method.
239    caller = inspect.stack()[1][3]
240
241    if golden_file is None:
242      self.assertTrue(
243          caller.startswith('test'),
244          'AssertGoldenTextEquals can only be called without at golden file '
245          'from a test* method, not %s' % caller)
246      golden_file = '%s%s.golden' % (caller, suffix)
247    golden_text = self._ReadGoldenFile(golden_file)
248    if os.environ.get(_REBASELINE_ENV):
249      if golden_text != generated_text:
250        with open(self._JoinGoldenPath(golden_file), 'w') as f:
251          f.write(generated_text)
252      return
253    # golden_text is None if no file is found. Better to fail than in
254    # AssertTextEquals so we can give a clearer message.
255    if golden_text is None:
256      self.fail(
257          'Golden file %s does not exist.' % self._JoinGoldenPath(golden_file))
258    self.AssertTextEquals(golden_text, generated_text)
259
260
261@unittest.skipIf(os.name == 'nt', 'Not intended to work on Windows')
262class TestGenerator(BaseTest):
263
264  def testInspectCaller(self):
265
266    def willRaise():
267      # This function can only be called from a test* method.
268      self.AssertGoldenTextEquals('')
269
270    self.assertRaises(AssertionError, willRaise)
271
272  def testNatives(self):
273    test_data = """"
274    import android.graphics.Bitmap;
275    import android.view.View;
276
277    interface OnFrameAvailableListener {}
278    private native int nativeInit();
279    private native void nativeDestroy(int nativeChromeBrowserProvider);
280    private native long nativeAddBookmark(
281            int nativeChromeBrowserProvider,
282            String url, String title, boolean isFolder, long parentId);
283    private static native String nativeGetDomainAndRegistry(String url);
284    private static native void nativeCreateHistoricalTabFromState(
285            byte[] state, int tab_index);
286    private native byte[] nativeGetStateAsByteArray(View view);
287    private static native String[] nativeGetAutofillProfileGUIDs();
288    private native void nativeSetRecognitionResults(
289            int sessionId, String[] results);
290    private native long nativeAddBookmarkFromAPI(
291            int nativeChromeBrowserProvider,
292            String url, Long created, Boolean isBookmark,
293            Long date, byte[] favicon, String title, Integer visits);
294    native int nativeFindAll(String find);
295    private static native OnFrameAvailableListener nativeGetInnerClass();
296    private native Bitmap nativeQueryBitmap(
297            int nativeChromeBrowserProvider,
298            String[] projection, String selection,
299            String[] selectionArgs, String sortOrder);
300    private native void nativeGotOrientation(
301            int nativeDataFetcherImplAndroid,
302            double alpha, double beta, double gamma);
303    private static native Throwable nativeMessWithJavaException(Throwable e);
304    """
305    jni_params = jni_generator.JniParams(
306        'org/chromium/example/jni_generator/SampleForTests')
307    jni_params.ExtractImportsAndInnerClasses(test_data)
308    natives = jni_generator.ExtractNatives(test_data, 'int')
309    golden_natives = [
310        NativeMethod(
311            return_type='int',
312            static=False,
313            name='Init',
314            params=[],
315            java_class_name=None),
316        NativeMethod(
317            return_type='void',
318            static=False,
319            name='Destroy',
320            params=[Param(datatype='int', name='nativeChromeBrowserProvider')],
321            java_class_name=None),
322        NativeMethod(
323            return_type='long',
324            static=False,
325            name='AddBookmark',
326            params=[
327                Param(datatype='int', name='nativeChromeBrowserProvider'),
328                Param(datatype='String', name='url'),
329                Param(datatype='String', name='title'),
330                Param(datatype='boolean', name='isFolder'),
331                Param(datatype='long', name='parentId')
332            ],
333            java_class_name=None),
334        NativeMethod(
335            return_type='String',
336            static=True,
337            name='GetDomainAndRegistry',
338            params=[Param(datatype='String', name='url')],
339            java_class_name=None),
340        NativeMethod(
341            return_type='void',
342            static=True,
343            name='CreateHistoricalTabFromState',
344            params=[
345                Param(datatype='byte[]', name='state'),
346                Param(datatype='int', name='tab_index')
347            ],
348            java_class_name=None),
349        NativeMethod(
350            return_type='byte[]',
351            static=False,
352            name='GetStateAsByteArray',
353            params=[Param(datatype='View', name='view')],
354            java_class_name=None),
355        NativeMethod(
356            return_type='String[]',
357            static=True,
358            name='GetAutofillProfileGUIDs',
359            params=[],
360            java_class_name=None),
361        NativeMethod(
362            return_type='void',
363            static=False,
364            name='SetRecognitionResults',
365            params=[
366                Param(datatype='int', name='sessionId'),
367                Param(datatype='String[]', name='results')
368            ],
369            java_class_name=None),
370        NativeMethod(
371            return_type='long',
372            static=False,
373            name='AddBookmarkFromAPI',
374            params=[
375                Param(datatype='int', name='nativeChromeBrowserProvider'),
376                Param(datatype='String', name='url'),
377                Param(datatype='Long', name='created'),
378                Param(datatype='Boolean', name='isBookmark'),
379                Param(datatype='Long', name='date'),
380                Param(datatype='byte[]', name='favicon'),
381                Param(datatype='String', name='title'),
382                Param(datatype='Integer', name='visits')
383            ],
384            java_class_name=None),
385        NativeMethod(
386            return_type='int',
387            static=False,
388            name='FindAll',
389            params=[Param(datatype='String', name='find')],
390            java_class_name=None),
391        NativeMethod(
392            return_type='OnFrameAvailableListener',
393            static=True,
394            name='GetInnerClass',
395            params=[],
396            java_class_name=None),
397        NativeMethod(
398            return_type='Bitmap',
399            static=False,
400            name='QueryBitmap',
401            params=[
402                Param(datatype='int', name='nativeChromeBrowserProvider'),
403                Param(datatype='String[]', name='projection'),
404                Param(datatype='String', name='selection'),
405                Param(datatype='String[]', name='selectionArgs'),
406                Param(datatype='String', name='sortOrder'),
407            ],
408            java_class_name=None),
409        NativeMethod(
410            return_type='void',
411            static=False,
412            name='GotOrientation',
413            params=[
414                Param(datatype='int', name='nativeDataFetcherImplAndroid'),
415                Param(datatype='double', name='alpha'),
416                Param(datatype='double', name='beta'),
417                Param(datatype='double', name='gamma'),
418            ],
419            java_class_name=None),
420        NativeMethod(
421            return_type='Throwable',
422            static=True,
423            name='MessWithJavaException',
424            params=[Param(datatype='Throwable', name='e')],
425            java_class_name=None)
426    ]
427    self.AssertListEquals(golden_natives, natives)
428    h1 = jni_generator.InlHeaderFileGenerator('', '', 'org/chromium/TestJni',
429                                              natives, [], [], jni_params,
430                                              JniGeneratorOptions())
431    self.AssertGoldenTextEquals(h1.GetContent())
432    h2 = jni_registration_generator.DictionaryGenerator(JniGeneratorOptions(),
433                                                        '', '',
434                                                        'org/chromium/TestJni',
435                                                        natives, jni_params)
436    content = TestGenerator._MergeRegistrationForTests([h2.Generate()])
437
438    reg_options = JniRegistrationGeneratorOptions()
439    reg_options.manual_jni_registration = True
440    self.AssertGoldenTextEquals(jni_registration_generator.CreateFromDict(
441        reg_options, '', content),
442                                suffix='Registrations')
443
444  def testInnerClassNatives(self):
445    test_data = """
446    class MyInnerClass {
447      @NativeCall("MyInnerClass")
448      private native int nativeInit();
449    }
450    """
451    natives = jni_generator.ExtractNatives(test_data, 'int')
452    golden_natives = [
453        NativeMethod(
454            return_type='int',
455            static=False,
456            name='Init',
457            params=[],
458            java_class_name='MyInnerClass')
459    ]
460    self.AssertListEquals(golden_natives, natives)
461    jni_params = jni_generator.JniParams('')
462    h = jni_generator.InlHeaderFileGenerator('', '', 'org/chromium/TestJni',
463                                             natives, [], [], jni_params,
464                                             JniGeneratorOptions())
465    self.AssertGoldenTextEquals(h.GetContent())
466
467  def testInnerClassNativesMultiple(self):
468    test_data = """
469    class MyInnerClass {
470      @NativeCall("MyInnerClass")
471      private native int nativeInit();
472    }
473    class MyOtherInnerClass {
474      @NativeCall("MyOtherInnerClass")
475      private native int nativeInit();
476    }
477    """
478    natives = jni_generator.ExtractNatives(test_data, 'int')
479    golden_natives = [
480        NativeMethod(
481            return_type='int',
482            static=False,
483            name='Init',
484            params=[],
485            java_class_name='MyInnerClass'),
486        NativeMethod(
487            return_type='int',
488            static=False,
489            name='Init',
490            params=[],
491            java_class_name='MyOtherInnerClass')
492    ]
493    self.AssertListEquals(golden_natives, natives)
494    jni_params = jni_generator.JniParams('')
495    h = jni_generator.InlHeaderFileGenerator('', '', 'org/chromium/TestJni',
496                                             natives, [], [], jni_params,
497                                             JniGeneratorOptions())
498    self.AssertGoldenTextEquals(h.GetContent())
499
500  def testInnerClassNativesBothInnerAndOuter(self):
501    test_data = """
502    class MyOuterClass {
503      private native int nativeInit();
504      class MyOtherInnerClass {
505        @NativeCall("MyOtherInnerClass")
506        private native int nativeInit();
507      }
508    }
509    """
510    natives = jni_generator.ExtractNatives(test_data, 'int')
511    golden_natives = [
512        NativeMethod(
513            return_type='int',
514            static=False,
515            name='Init',
516            params=[],
517            java_class_name=None),
518        NativeMethod(
519            return_type='int',
520            static=False,
521            name='Init',
522            params=[],
523            java_class_name='MyOtherInnerClass')
524    ]
525    self.AssertListEquals(golden_natives, natives)
526    jni_params = jni_generator.JniParams('')
527    h = jni_generator.InlHeaderFileGenerator('', '', 'org/chromium/TestJni',
528                                             natives, [], [], jni_params,
529                                             JniGeneratorOptions())
530    self.AssertGoldenTextEquals(h.GetContent())
531
532    h2 = jni_registration_generator.DictionaryGenerator(JniGeneratorOptions(),
533                                                        '', '',
534                                                        'org/chromium/TestJni',
535                                                        natives, jni_params)
536    content = TestGenerator._MergeRegistrationForTests([h2.Generate()])
537
538    reg_options = JniRegistrationGeneratorOptions()
539    reg_options.manual_jni_registration = True
540    self.AssertGoldenTextEquals(jni_registration_generator.CreateFromDict(
541        reg_options, '', content),
542                                suffix='Registrations')
543
544  def testCalledByNatives(self):
545    test_data = """"
546    import android.graphics.Bitmap;
547    import android.view.View;
548    import java.io.InputStream;
549    import java.util.List;
550
551    class InnerClass {}
552
553    @CalledByNative
554    @SomeOtherA
555    @SomeOtherB
556    public InnerClass showConfirmInfoBar(int nativeInfoBar,
557            String buttonOk, String buttonCancel, String title, Bitmap icon) {
558        InfoBar infobar = new ConfirmInfoBar(nativeInfoBar, mContext,
559                                             buttonOk, buttonCancel,
560                                             title, icon);
561        return infobar;
562    }
563    @CalledByNative
564    InnerClass showAutoLoginInfoBar(int nativeInfoBar,
565            String realm, String account, String args) {
566        AutoLoginInfoBar infobar = new AutoLoginInfoBar(nativeInfoBar, mContext,
567                realm, account, args);
568        if (infobar.displayedAccountCount() == 0)
569            infobar = null;
570        return infobar;
571    }
572    @CalledByNative("InfoBar")
573    void dismiss();
574    @SuppressWarnings("unused")
575    @CalledByNative
576    private static boolean shouldShowAutoLogin(View view,
577            String realm, String account, String args) {
578        AccountManagerContainer accountManagerContainer =
579            new AccountManagerContainer((Activity)contentView.getContext(),
580            realm, account, args);
581        String[] logins = accountManagerContainer.getAccountLogins(null);
582        return logins.length != 0;
583    }
584    @CalledByNative
585    static InputStream openUrl(String url) {
586        return null;
587    }
588    @CalledByNative
589    private void activateHardwareAcceleration(final boolean activated,
590            final int iPid, final int iType,
591            final int iPrimaryID, final int iSecondaryID) {
592      if (!activated) {
593          return
594      }
595    }
596    @CalledByNative
597    public static @Status int updateStatus(@Status int status) {
598        return getAndUpdateStatus(status);
599    }
600    @CalledByNativeUnchecked
601    private void uncheckedCall(int iParam);
602
603    @CalledByNative
604    public byte[] returnByteArray();
605
606    @CalledByNative
607    public boolean[] returnBooleanArray();
608
609    @CalledByNative
610    public char[] returnCharArray();
611
612    @CalledByNative
613    public short[] returnShortArray();
614
615    @CalledByNative
616    public int[] returnIntArray();
617
618    @CalledByNative
619    public long[] returnLongArray();
620
621    @CalledByNative
622    public double[] returnDoubleArray();
623
624    @CalledByNative
625    public Object[] returnObjectArray();
626
627    @CalledByNative
628    public byte[][] returnArrayOfByteArray();
629
630    @CalledByNative
631    public Bitmap.CompressFormat getCompressFormat();
632
633    @CalledByNative
634    public List<Bitmap.CompressFormat> getCompressFormatList();
635
636    @CalledByNativeForTesting
637    public int[] returnIntArrayForTesting();
638    """
639    jni_params = jni_generator.JniParams('org/chromium/Foo')
640    jni_params.ExtractImportsAndInnerClasses(test_data)
641    called_by_natives = jni_generator.ExtractCalledByNatives(
642        jni_params, test_data, always_mangle=False)
643    golden_called_by_natives = [
644        CalledByNative(
645            return_type='InnerClass',
646            system_class=False,
647            static=False,
648            name='showConfirmInfoBar',
649            method_id_var_name='showConfirmInfoBar',
650            java_class_name='',
651            params=[
652                Param(datatype='int', name='nativeInfoBar'),
653                Param(datatype='String', name='buttonOk'),
654                Param(datatype='String', name='buttonCancel'),
655                Param(datatype='String', name='title'),
656                Param(datatype='Bitmap', name='icon')
657            ],
658            env_call=('Object', ''),
659            unchecked=False,
660        ),
661        CalledByNative(
662            return_type='InnerClass',
663            system_class=False,
664            static=False,
665            name='showAutoLoginInfoBar',
666            method_id_var_name='showAutoLoginInfoBar',
667            java_class_name='',
668            params=[
669                Param(datatype='int', name='nativeInfoBar'),
670                Param(datatype='String', name='realm'),
671                Param(datatype='String', name='account'),
672                Param(datatype='String', name='args')
673            ],
674            env_call=('Object', ''),
675            unchecked=False,
676        ),
677        CalledByNative(
678            return_type='void',
679            system_class=False,
680            static=False,
681            name='dismiss',
682            method_id_var_name='dismiss',
683            java_class_name='InfoBar',
684            params=[],
685            env_call=('Void', ''),
686            unchecked=False,
687        ),
688        CalledByNative(
689            return_type='boolean',
690            system_class=False,
691            static=True,
692            name='shouldShowAutoLogin',
693            method_id_var_name='shouldShowAutoLogin',
694            java_class_name='',
695            params=[
696                Param(datatype='View', name='view'),
697                Param(datatype='String', name='realm'),
698                Param(datatype='String', name='account'),
699                Param(datatype='String', name='args')
700            ],
701            env_call=('Boolean', ''),
702            unchecked=False,
703        ),
704        CalledByNative(
705            return_type='InputStream',
706            system_class=False,
707            static=True,
708            name='openUrl',
709            method_id_var_name='openUrl',
710            java_class_name='',
711            params=[Param(datatype='String', name='url')],
712            env_call=('Object', ''),
713            unchecked=False,
714        ),
715        CalledByNative(
716            return_type='void',
717            system_class=False,
718            static=False,
719            name='activateHardwareAcceleration',
720            method_id_var_name='activateHardwareAcceleration',
721            java_class_name='',
722            params=[
723                Param(datatype='boolean', name='activated'),
724                Param(datatype='int', name='iPid'),
725                Param(datatype='int', name='iType'),
726                Param(datatype='int', name='iPrimaryID'),
727                Param(datatype='int', name='iSecondaryID'),
728            ],
729            env_call=('Void', ''),
730            unchecked=False,
731        ),
732        CalledByNative(
733            return_type='int',
734            system_class=False,
735            static=True,
736            name='updateStatus',
737            method_id_var_name='updateStatus',
738            java_class_name='',
739            params=[
740                Param(annotations=['@Status'], datatype='int', name='status')
741            ],
742            env_call=('Integer', ''),
743            unchecked=False,
744        ),
745        CalledByNative(
746            return_type='void',
747            system_class=False,
748            static=False,
749            name='uncheckedCall',
750            method_id_var_name='uncheckedCall',
751            java_class_name='',
752            params=[Param(datatype='int', name='iParam')],
753            env_call=('Void', ''),
754            unchecked=True,
755        ),
756        CalledByNative(
757            return_type='byte[]',
758            system_class=False,
759            static=False,
760            name='returnByteArray',
761            method_id_var_name='returnByteArray',
762            java_class_name='',
763            params=[],
764            env_call=('Void', ''),
765            unchecked=False,
766        ),
767        CalledByNative(
768            return_type='boolean[]',
769            system_class=False,
770            static=False,
771            name='returnBooleanArray',
772            method_id_var_name='returnBooleanArray',
773            java_class_name='',
774            params=[],
775            env_call=('Void', ''),
776            unchecked=False,
777        ),
778        CalledByNative(
779            return_type='char[]',
780            system_class=False,
781            static=False,
782            name='returnCharArray',
783            method_id_var_name='returnCharArray',
784            java_class_name='',
785            params=[],
786            env_call=('Void', ''),
787            unchecked=False,
788        ),
789        CalledByNative(
790            return_type='short[]',
791            system_class=False,
792            static=False,
793            name='returnShortArray',
794            method_id_var_name='returnShortArray',
795            java_class_name='',
796            params=[],
797            env_call=('Void', ''),
798            unchecked=False,
799        ),
800        CalledByNative(
801            return_type='int[]',
802            system_class=False,
803            static=False,
804            name='returnIntArray',
805            method_id_var_name='returnIntArray',
806            java_class_name='',
807            params=[],
808            env_call=('Void', ''),
809            unchecked=False,
810        ),
811        CalledByNative(
812            return_type='long[]',
813            system_class=False,
814            static=False,
815            name='returnLongArray',
816            method_id_var_name='returnLongArray',
817            java_class_name='',
818            params=[],
819            env_call=('Void', ''),
820            unchecked=False,
821        ),
822        CalledByNative(
823            return_type='double[]',
824            system_class=False,
825            static=False,
826            name='returnDoubleArray',
827            method_id_var_name='returnDoubleArray',
828            java_class_name='',
829            params=[],
830            env_call=('Void', ''),
831            unchecked=False,
832        ),
833        CalledByNative(
834            return_type='Object[]',
835            system_class=False,
836            static=False,
837            name='returnObjectArray',
838            method_id_var_name='returnObjectArray',
839            java_class_name='',
840            params=[],
841            env_call=('Void', ''),
842            unchecked=False,
843        ),
844        CalledByNative(
845            return_type='byte[][]',
846            system_class=False,
847            static=False,
848            name='returnArrayOfByteArray',
849            method_id_var_name='returnArrayOfByteArray',
850            java_class_name='',
851            params=[],
852            env_call=('Void', ''),
853            unchecked=False,
854        ),
855        CalledByNative(
856            return_type='Bitmap.CompressFormat',
857            system_class=False,
858            static=False,
859            name='getCompressFormat',
860            method_id_var_name='getCompressFormat',
861            java_class_name='',
862            params=[],
863            env_call=('Void', ''),
864            unchecked=False,
865        ),
866        CalledByNative(
867            return_type='List<Bitmap.CompressFormat>',
868            system_class=False,
869            static=False,
870            name='getCompressFormatList',
871            method_id_var_name='getCompressFormatList',
872            java_class_name='',
873            params=[],
874            env_call=('Void', ''),
875            unchecked=False,
876        ),
877        CalledByNative(
878            return_type='int[]',
879            system_class=False,
880            static=False,
881            name='returnIntArrayForTesting',
882            method_id_var_name='returnIntArrayForTesting',
883            java_class_name='',
884            params=[],
885            env_call=('Void', ''),
886            unchecked=False,
887        ),
888    ]
889    self.AssertListEquals(golden_called_by_natives, called_by_natives)
890    h = jni_generator.InlHeaderFileGenerator('', '', 'org/chromium/TestJni', [],
891                                             called_by_natives, [], jni_params,
892                                             JniGeneratorOptions())
893    self.AssertGoldenTextEquals(h.GetContent())
894
895  def testCalledByNativeParseError(self):
896    try:
897      jni_params = jni_generator.JniParams('')
898      jni_generator.ExtractCalledByNatives(
899          jni_params,
900          """
901@CalledByNative
902public static int foo(); // This one is fine
903
904@CalledByNative
905scooby doo
906""",
907          always_mangle=False)
908      self.fail('Expected a ParseError')
909    except jni_generator.ParseError as e:
910      self.assertEqual(('@CalledByNative', 'scooby doo'), e.context_lines)
911
912  def testFullyQualifiedClassName(self):
913    contents = """
914// Copyright 2010 The Chromium Authors
915// Use of this source code is governed by a BSD-style license that can be
916// found in the LICENSE file.
917
918package org.chromium.content.browser;
919
920import org.chromium.base.BuildInfo;
921"""
922    self.assertEqual(
923        'org/chromium/content/browser/Foo',
924        jni_generator.ExtractFullyQualifiedJavaClassName(
925            'org/chromium/content/browser/Foo.java', contents))
926    self.assertEqual(
927        'org/chromium/content/browser/Foo',
928        jni_generator.ExtractFullyQualifiedJavaClassName(
929            'frameworks/Foo.java', contents))
930    self.assertRaises(SyntaxError,
931                      jni_generator.ExtractFullyQualifiedJavaClassName,
932                      'com/foo/Bar', 'no PACKAGE line')
933    self.assertRaises(AssertionError,
934                      jni_generator.ExtractFullyQualifiedJavaClassName,
935                      'com/foo/Bar.kt', 'Kotlin not supported')
936
937  def testMethodNameMangling(self):
938    jni_params = jni_generator.JniParams('')
939    self.assertEqual(
940        'closeV',
941        jni_generator.GetMangledMethodName(jni_params, 'close', [], 'void'))
942    self.assertEqual(
943        'readI_AB_I_I',
944        jni_generator.GetMangledMethodName(jni_params, 'read', [
945            Param(name='p1', datatype='byte[]'),
946            Param(name='p2', datatype='int'),
947            Param(name='p3', datatype='int'),
948        ], 'int'))
949    self.assertEqual(
950        'openJIIS_JLS',
951        jni_generator.GetMangledMethodName(jni_params, 'open', [
952            Param(name='p1', datatype='java/lang/String'),
953        ], 'java/io/InputStream'))
954
955  def testMethodNameAlwaysMangle(self):
956    test_data = """
957    import f.o.o.Bar;
958    import f.o.o.Baz;
959
960    class Clazz {
961      @CalledByNative
962      public Baz methodz(Bar bar) {
963        return null;
964      }
965    }
966    """
967    jni_params = jni_generator.JniParams('org/chromium/Foo')
968    jni_params.ExtractImportsAndInnerClasses(test_data)
969    called_by_natives = jni_generator.ExtractCalledByNatives(
970        jni_params, test_data, always_mangle=True)
971    self.assertEqual(1, len(called_by_natives))
972    method = called_by_natives[0]
973    self.assertEqual('methodzFOOB_FOOB', method.method_id_var_name)
974
975  def testFromJavaPGenerics(self):
976    contents = """
977public abstract class java.util.HashSet<T> extends java.util.AbstractSet<E>
978      implements java.util.Set<E>, java.lang.Cloneable, java.io.Serializable {
979    public void dummy();
980      Signature: ()V
981    public java.lang.Class<?> getClass();
982      Signature: ()Ljava/lang/Class<*>;
983    public static void overloadWithVarargs(java.lang.String...);
984      Signature: ([Ljava/lang/String;)V
985    public static void overloadWithVarargs(android.icu.text.DisplayContext...);
986      Signature: ([Landroid/icu/text/DisplayContext;)V
987}
988"""
989    jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'),
990                                                JniGeneratorOptions())
991    self.AssertGoldenTextEquals(jni_from_javap.GetContent())
992
993  def testSnippnetJavap6_7_8(self):
994    content_javap6 = """
995public class java.util.HashSet {
996public boolean add(java.lang.Object);
997 Signature: (Ljava/lang/Object;)Z
998}
999"""
1000
1001    content_javap7 = """
1002public class java.util.HashSet {
1003public boolean add(E);
1004  Signature: (Ljava/lang/Object;)Z
1005}
1006"""
1007
1008    content_javap8 = """
1009public class java.util.HashSet {
1010  public boolean add(E);
1011    descriptor: (Ljava/lang/Object;)Z
1012}
1013"""
1014
1015    jni_from_javap6 = jni_generator.JNIFromJavaP(content_javap6.split('\n'),
1016                                                 JniGeneratorOptions())
1017    jni_from_javap7 = jni_generator.JNIFromJavaP(content_javap7.split('\n'),
1018                                                 JniGeneratorOptions())
1019    jni_from_javap8 = jni_generator.JNIFromJavaP(content_javap8.split('\n'),
1020                                                 JniGeneratorOptions())
1021    self.assertTrue(jni_from_javap6.GetContent())
1022    self.assertTrue(jni_from_javap7.GetContent())
1023    self.assertTrue(jni_from_javap8.GetContent())
1024    # Ensure the javap7 is correctly parsed and uses the Signature field rather
1025    # than the "E" parameter.
1026    self.AssertTextEquals(jni_from_javap6.GetContent(),
1027                          jni_from_javap7.GetContent())
1028    # Ensure the javap8 is correctly parsed and uses the descriptor field.
1029    self.AssertTextEquals(jni_from_javap7.GetContent(),
1030                          jni_from_javap8.GetContent())
1031
1032  def testFromJavaP(self):
1033    contents = self._ReadGoldenFile('testInputStream.javap')
1034    jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'),
1035                                                JniGeneratorOptions())
1036    self.assertEqual(10, len(jni_from_javap.called_by_natives))
1037    self.AssertGoldenTextEquals(jni_from_javap.GetContent())
1038
1039  def testConstantsFromJavaP(self):
1040    for f in ['testMotionEvent.javap', 'testMotionEvent.javap7']:
1041      contents = self._ReadGoldenFile(f)
1042      jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'),
1043                                                  JniGeneratorOptions())
1044      self.assertEqual(86, len(jni_from_javap.called_by_natives))
1045      self.AssertGoldenTextEquals(jni_from_javap.GetContent())
1046
1047  def testREForNatives(self):
1048    # We should not match "native SyncSetupFlow" inside the comment.
1049    test_data = """
1050    /**
1051     * Invoked when the setup process is complete so we can disconnect from the
1052     * private native void nativeSyncSetupFlowHandler();.
1053     */
1054    public void destroy() {
1055        Log.v(TAG, "Destroying native SyncSetupFlow");
1056        if (mNativeSyncSetupFlow != 0) {
1057            nativeSyncSetupEnded(mNativeSyncSetupFlow);
1058            mNativeSyncSetupFlow = 0;
1059        }
1060    }
1061    private native void nativeSyncSetupEnded(
1062        int nativeAndroidSyncSetupFlowHandler);
1063    """
1064    jni_from_java = jni_generator.JNIFromJavaSource(test_data, 'foo/bar',
1065                                                    JniGeneratorOptions())
1066    self.AssertGoldenTextEquals(jni_from_java.GetContent())
1067
1068  def testRaisesOnNonJNIMethod(self):
1069    test_data = """
1070    class MyInnerClass {
1071      private int Foo(int p0) {
1072      }
1073    }
1074    """
1075    self.assertRaises(SyntaxError, jni_generator.JNIFromJavaSource, test_data,
1076                      'foo/bar', JniGeneratorOptions())
1077
1078  def testJniSelfDocumentingExample(self):
1079    generated_text = self._CreateJniHeaderFromFile(
1080        os.path.join(_JAVA_SRC_DIR, 'SampleForTests.java'),
1081        'org/chromium/example/jni_generator/SampleForTests')
1082    self.AssertGoldenTextEquals(
1083        generated_text, golden_file='SampleForTests_jni.golden')
1084
1085  def testNoWrappingPreprocessorLines(self):
1086    test_data = """
1087    package com.google.lookhowextremelylongiam.snarf.icankeepthisupallday;
1088
1089    class ReallyLongClassNamesAreAllTheRage {
1090        private static native int nativeTest();
1091    }
1092    """
1093    jni_from_java = jni_generator.JNIFromJavaSource(
1094        test_data, ('com/google/lookhowextremelylongiam/snarf/'
1095                    'icankeepthisupallday/ReallyLongClassNamesAreAllTheRage'),
1096        JniGeneratorOptions())
1097    jni_lines = jni_from_java.GetContent().split('\n')
1098    line = next(
1099        line for line in jni_lines if line.lstrip().startswith('#ifndef'))
1100    self.assertTrue(
1101        len(line) > 80, ('Expected #ifndef line to be > 80 chars: ', line))
1102
1103  def testImports(self):
1104    import_header = """
1105// Copyright 2012 The Chromium Authors
1106// Use of this source code is governed by a BSD-style license that can be
1107// found in the LICENSE file.
1108
1109package org.chromium.content.app;
1110
1111import android.app.Service;
1112import android.content.Context;
1113import android.content.Intent;
1114import android.graphics.SurfaceTexture;
1115import android.os.Bundle;
1116import android.os.IBinder;
1117import android.os.ParcelFileDescriptor;
1118import android.os.Process;
1119import android.os.RemoteException;
1120import android.util.Log;
1121import android.view.Surface;
1122
1123import java.util.ArrayList;
1124
1125import org.chromium.base.annotations.CalledByNative;
1126import org.chromium.base.annotations.JNINamespace;
1127import org.chromium.content.app.ContentMain;
1128import org.chromium.content.browser.SandboxedProcessConnection;
1129import org.chromium.content.common.ISandboxedProcessCallback;
1130import org.chromium.content.common.ISandboxedProcessService;
1131import org.chromium.content.common.WillNotRaise.AnException;
1132import org.chromium.content.common.WillRaise.AnException;
1133
1134import static org.chromium.Bar.Zoo;
1135
1136class Foo {
1137  public static class BookmarkNode implements Parcelable {
1138  }
1139  public interface PasswordListObserver {
1140  }
1141}
1142    """
1143    jni_params = jni_generator.JniParams('org/chromium/content/app/Foo')
1144    jni_params.ExtractImportsAndInnerClasses(import_header)
1145    self.assertTrue('Lorg/chromium/content/common/ISandboxedProcessService' in
1146                    jni_params._imports)
1147    self.assertTrue('Lorg/chromium/Bar/Zoo' in jni_params._imports)
1148    self.assertTrue('Lorg/chromium/content/app/Foo$BookmarkNode' in jni_params.
1149                    _inner_classes)
1150    self.assertTrue('Lorg/chromium/content/app/Foo$PasswordListObserver' in
1151                    jni_params._inner_classes)
1152    self.assertEqual('Lorg/chromium/content/app/ContentMain$Inner;',
1153                     jni_params.JavaToJni('ContentMain.Inner'))
1154    self.assertRaises(SyntaxError, jni_params.JavaToJni, 'AnException')
1155
1156  def testJniParamsJavaToJni(self):
1157    jni_params = jni_generator.JniParams('')
1158    self.AssertTextEquals('I', jni_params.JavaToJni('int'))
1159    self.AssertTextEquals('[B', jni_params.JavaToJni('byte[]'))
1160    self.AssertTextEquals('[Ljava/nio/ByteBuffer;',
1161                          jni_params.JavaToJni('java/nio/ByteBuffer[]'))
1162
1163  def testNativesLong(self):
1164    test_options = JniGeneratorOptions()
1165    test_options.ptr_type = 'long'
1166    test_data = """"
1167    private native void nativeDestroy(long nativeChromeBrowserProvider);
1168    """
1169    jni_params = jni_generator.JniParams('')
1170    jni_params.ExtractImportsAndInnerClasses(test_data)
1171    natives = jni_generator.ExtractNatives(test_data, test_options.ptr_type)
1172    golden_natives = [
1173        NativeMethod(
1174            return_type='void',
1175            static=False,
1176            name='Destroy',
1177            params=[Param(datatype='long', name='nativeChromeBrowserProvider')],
1178            java_class_name=None,
1179            ptr_type=test_options.ptr_type),
1180    ]
1181    self.AssertListEquals(golden_natives, natives)
1182    h = jni_generator.InlHeaderFileGenerator('', '', 'org/chromium/TestJni',
1183                                             natives, [], [], jni_params,
1184                                             test_options)
1185    self.AssertGoldenTextEquals(h.GetContent())
1186
1187  def testNativeExportsOnlyOption(self):
1188    test_data = """
1189    package org.chromium.example.jni_generator;
1190
1191    /** The pointer to the native Test. */
1192    long nativeTest;
1193
1194    class Test {
1195        private static native int nativeStaticMethod(long nativeTest, int arg1);
1196        private native int nativeMethod(long nativeTest, int arg1);
1197        @CalledByNative
1198        private void testMethodWithParam(int iParam);
1199        @CalledByNative
1200        private String testMethodWithParamAndReturn(int iParam);
1201        @CalledByNative
1202        private static int testStaticMethodWithParam(int iParam);
1203        @CalledByNative
1204        private static double testMethodWithNoParam();
1205        @CalledByNative
1206        private static String testStaticMethodWithNoParam();
1207
1208        class MyInnerClass {
1209          @NativeCall("MyInnerClass")
1210          private native int nativeInit();
1211        }
1212        class MyOtherInnerClass {
1213          @NativeCall("MyOtherInnerClass")
1214          private native int nativeInit();
1215        }
1216    }
1217    """
1218    options = JniGeneratorOptions()
1219    jni_from_java = jni_generator.JNIFromJavaSource(
1220        test_data, 'org/chromium/example/jni_generator/SampleForTests', options)
1221    self.AssertGoldenTextEquals(jni_from_java.GetContent())
1222
1223  def testOuterInnerRaises(self):
1224    test_data = """
1225    package org.chromium.media;
1226
1227    @CalledByNative
1228    static int getCaptureFormatWidth(VideoCapture.CaptureFormat format) {
1229        return format.getWidth();
1230    }
1231    """
1232
1233    def willRaise():
1234      jni_generator.JNIFromJavaSource(test_data,
1235                                      'org/chromium/media/VideoCaptureFactory',
1236                                      JniGeneratorOptions())
1237
1238    self.assertRaises(SyntaxError, willRaise)
1239
1240  def testSingleJNIAdditionalImport(self):
1241    test_data = """
1242    package org.chromium.foo;
1243
1244    @JNIAdditionalImport(Bar.class)
1245    class Foo {
1246
1247    @CalledByNative
1248    private static void calledByNative(Bar.Callback callback) {
1249    }
1250
1251    private static native void nativeDoSomething(Bar.Callback callback);
1252    }
1253    """
1254    jni_from_java = jni_generator.JNIFromJavaSource(test_data,
1255                                                    'org/chromium/foo/Foo',
1256                                                    JniGeneratorOptions())
1257    self.AssertGoldenTextEquals(jni_from_java.GetContent())
1258
1259  def testMultipleJNIAdditionalImport(self):
1260    test_data = """
1261    package org.chromium.foo;
1262
1263    @JNIAdditionalImport({Bar1.class, Bar2.class})
1264    class Foo {
1265
1266    @CalledByNative
1267    private static void calledByNative(Bar1.Callback callback1,
1268                                       Bar2.Callback callback2) {
1269    }
1270
1271    private static native void nativeDoSomething(Bar1.Callback callback1,
1272                                                 Bar2.Callback callback2);
1273    }
1274    """
1275    jni_from_java = jni_generator.JNIFromJavaSource(test_data,
1276                                                    'org/chromium/foo/Foo',
1277                                                    JniGeneratorOptions())
1278    self.AssertGoldenTextEquals(jni_from_java.GetContent())
1279
1280  def testStaticBindingCaller(self):
1281    test_data = """
1282    package org.chromium.foo;
1283
1284    class Bar {
1285      static native void nativeShouldBindCaller(Object caller);
1286      static native void nativeShouldBindCaller(Object caller, int a);
1287      static native void nativeFoo(long nativeNativeObject, Bar caller);
1288      static native void nativeFoo(long nativeNativeObject, Bar caller, int a);
1289      native void nativeCallNativeMethod(long nativePtr);
1290      @NativeClassQualifiedName("Foo::Bar")
1291      native void nativeCallWithQualifiedObject(long nativePtr);
1292    }
1293    """
1294
1295    jni_from_java = jni_generator.JNIFromJavaSource(test_data,
1296                                                    'org/chromium/foo/Foo',
1297                                                    JniGeneratorOptions())
1298    self.AssertGoldenTextEquals(jni_from_java.GetContent())
1299
1300  def testSplitNameExample(self):
1301    opts = JniGeneratorOptions()
1302    opts.split_name = "sample"
1303    generated_text = self._CreateJniHeaderFromFile(
1304        os.path.join(_JAVA_SRC_DIR, 'SampleForTests.java'),
1305        'org/chromium/example/jni_generator/SampleForTests', opts)
1306    self.AssertGoldenTextEquals(
1307        generated_text, golden_file='SampleForTestsWithSplit_jni.golden')
1308
1309
1310@unittest.skipIf(os.name == 'nt', 'Not intended to work on Windows')
1311class ProxyTestGenerator(BaseTest):
1312
1313  def _BuildRegDictFromSample(self):
1314    path = self._JoinScriptDir(
1315        os.path.join(_JAVA_SRC_DIR, 'SampleForAnnotationProcessor.java'))
1316    reg_dict = jni_registration_generator._DictForPath(
1317        JniRegistrationGeneratorOptions(), path)
1318    reg_dict = self._MergeRegistrationForTests([reg_dict])
1319
1320    return reg_dict
1321
1322  def testEndToEndProxyHashed(self):
1323    input_java_files = ['SampleForAnnotationProcessor.java']
1324    options = JniRegistrationGeneratorOptions()
1325    options.use_proxy_hash = True
1326    name_to_goldens = {
1327        'org/chromium/base/natives/GEN_JNI.java':
1328        'HashedSampleForAnnotationProcessorGenJni.2.golden',
1329        'J/N.java': 'HashedSampleForAnnotationProcessorGenJni.golden'
1330    }
1331    self._TestEndToEndRegistration(input_java_files, options, name_to_goldens)
1332
1333  def testEndToEndManualRegistration(self):
1334    input_java_files = ['SampleForAnnotationProcessor.java']
1335    options = JniRegistrationGeneratorOptions()
1336    options.manual_jni_registration = True
1337    name_to_goldens = {
1338        'org/chromium/base/natives/GEN_JNI.java':
1339        'SampleForAnnotationProcessorGenJni.golden'
1340    }
1341    self._TestEndToEndRegistration(
1342        input_java_files,
1343        options,
1344        name_to_goldens,
1345        header_golden='SampleForAnnotationProcessorManualJni.golden')
1346
1347  def testEndToEndProxyJniWithModules(self):
1348    input_java_files = [
1349        'SampleForAnnotationProcessor.java', 'SampleModule.java'
1350    ]
1351    options = JniRegistrationGeneratorOptions()
1352    options.use_proxy_hash = True
1353    options.module_name = 'module'
1354    name_to_goldens = {
1355        'org/chromium/base/natives/GEN_JNI.java':
1356        'HashedSampleForAnnotationProcessorGenJni.2.golden',
1357        'J/N.java': 'HashedSampleForAnnotationProcessorGenJni.golden',
1358        'org/chromium/base/natives/module_GEN_JNI.java': 'ModuleGenJni.golden',
1359        'J/module_N.java': 'ModuleJN.golden'
1360    }
1361    self._TestEndToEndRegistration(input_java_files, options, name_to_goldens)
1362
1363  def testProxyNativesWithNatives(self):
1364    test_data = """
1365    package org.chromium.foo;
1366
1367    class Foo {
1368
1369    @NativeMethods
1370    interface Natives {
1371       void foo();
1372       String bar(String s, int y, char x, short z);
1373       String[] foobar(String[] a);
1374       void baz(long nativePtr, BazClass caller);
1375       void fooBar(long nativePtr);
1376    }
1377
1378    void justARegularFunction();
1379
1380    native void nativeInstanceMethod(long nativeInstance);
1381    static native void nativeStaticMethod();
1382
1383    }
1384    """
1385    options_with_tracing = JniGeneratorOptions()
1386    jni_from_java = jni_generator.JNIFromJavaSource(
1387        test_data, 'org/chromium/foo/Foo', options_with_tracing)
1388    self.AssertGoldenTextEquals(jni_from_java.GetContent())
1389
1390  def testEscapingProxyNatives(self):
1391    test_data = """
1392    class SampleProxyJni {
1393      @NativeMethods
1394      interface Natives {
1395        void foo_bar();
1396        void foo__bar();
1397      }
1398    }
1399    """
1400    qualified_clazz = 'org/chromium/example/SampleProxyJni'
1401
1402    natives, _ = jni_generator.ProxyHelpers.ExtractStaticProxyNatives(
1403        qualified_clazz, test_data, 'long')
1404
1405    golden_natives = [
1406        NativeMethod(
1407            return_type='void',
1408            static=True,
1409            name='foo_bar',
1410            params=[],
1411            java_class_name=None,
1412            is_proxy=True,
1413            proxy_name='org_chromium_example_SampleProxyJni_foo_1bar'),
1414        NativeMethod(
1415            return_type='void',
1416            static=True,
1417            name='foo__bar',
1418            params=[],
1419            java_class_name=None,
1420            is_proxy=True,
1421            proxy_name='org_chromium_example_SampleProxyJni_foo_1_1bar'),
1422    ]
1423
1424    self.AssertListEquals(_RemoveHashedNames(natives), golden_natives)
1425
1426  def testForTestingKept(self):
1427    test_data = """
1428    class SampleProxyJni {
1429      @NativeMethods
1430      interface Natives {
1431        void fooForTesting();
1432        void fooForTest();
1433      }
1434    }
1435    """
1436    qualified_clazz = 'org/chromium/example/SampleProxyJni'
1437
1438    natives, _ = jni_generator.ProxyHelpers.ExtractStaticProxyNatives(
1439        qualified_clazz, test_data, 'long', True)
1440
1441    golden_natives = [
1442        NativeMethod(
1443            return_type='void',
1444            static=True,
1445            name='fooForTesting',
1446            params=[],
1447            java_class_name=None,
1448            is_proxy=True,
1449            proxy_name='org_chromium_example_SampleProxyJni_fooForTesting'),
1450        NativeMethod(
1451            return_type='void',
1452            static=True,
1453            name='fooForTest',
1454            params=[],
1455            java_class_name=None,
1456            is_proxy=True,
1457            proxy_name='org_chromium_example_SampleProxyJni_fooForTest'),
1458    ]
1459
1460    self.AssertListEquals(_RemoveHashedNames(natives), golden_natives)
1461
1462  def testForTestingRemoved(self):
1463    test_data = """
1464    class SampleProxyJni {
1465      @NativeMethods
1466      interface Natives {
1467        void fooForTesting();
1468        void fooForTest();
1469      }
1470    }
1471    """
1472    qualified_clazz = 'org/chromium/example/SampleProxyJni'
1473
1474    natives, _ = jni_generator.ProxyHelpers.ExtractStaticProxyNatives(
1475        qualified_clazz, test_data, 'long', False)
1476
1477    self.AssertListEquals(_RemoveHashedNames(natives), [])
1478
1479  def testProxyNatives(self):
1480    test_data = """
1481    class SampleProxyJni {
1482      private void do_not_match();
1483      @VisibleForTesting
1484      @NativeMethods
1485      @Generated("Test")
1486      interface Natives {
1487        @NativeClassQualifiedName("FooAndroid::BarDelegate")
1488        void foo(long nativePtr);
1489        int bar(int x, int y);
1490        String foobar(String x, String y);
1491      }
1492      void dontmatchme();
1493      public static void metoo();
1494      public static native void this_is_a_non_proxy_native();
1495    }
1496    """
1497
1498    bad_spaced_test_data = """
1499    class SampleProxyJni{
1500      @NativeMethods interface
1501      Natives
1502
1503
1504      { @NativeClassQualifiedName("FooAndroid::BarDelegate") void
1505    foo(long nativePtr);
1506      int              bar(int
1507      x,  int y); String
1508        foobar(String x, String y);
1509      }
1510
1511    }
1512    """
1513
1514    qualified_clazz = 'org/chromium/example/SampleProxyJni'
1515
1516    natives, _ = jni_generator.ProxyHelpers.ExtractStaticProxyNatives(
1517        qualified_clazz, test_data, 'long')
1518    bad_spacing_natives, _ = jni_generator.ProxyHelpers \
1519      .ExtractStaticProxyNatives(qualified_clazz, bad_spaced_test_data, 'long')
1520    golden_natives = [
1521        NativeMethod(
1522            return_type='void',
1523            static=True,
1524            name='foo',
1525            native_class_name='FooAndroid::BarDelegate',
1526            params=[Param(datatype='long', name='nativePtr')],
1527            java_class_name=None,
1528            is_proxy=True,
1529            proxy_name='org_chromium_example_SampleProxyJni_foo',
1530            ptr_type='long'),
1531        NativeMethod(
1532            return_type='int',
1533            static=True,
1534            name='bar',
1535            params=[
1536                Param(datatype='int', name='x'),
1537                Param(datatype='int', name='y')
1538            ],
1539            java_class_name=None,
1540            is_proxy=True,
1541            proxy_name='org_chromium_example_SampleProxyJni_bar'),
1542        NativeMethod(
1543            return_type='String',
1544            static=True,
1545            name='foobar',
1546            params=[
1547                Param(datatype='String', name='x'),
1548                Param(datatype='String', name='y')
1549            ],
1550            java_class_name=None,
1551            is_proxy=True,
1552            proxy_name='org_chromium_example_SampleProxyJni_foobar'),
1553    ]
1554    self.AssertListEquals(golden_natives, _RemoveHashedNames(natives))
1555    self.AssertListEquals(golden_natives,
1556                          _RemoveHashedNames(bad_spacing_natives))
1557    options = JniGeneratorOptions()
1558    reg_options = JniRegistrationGeneratorOptions()
1559    reg_options.manual_jni_registration = True
1560
1561    jni_params = jni_generator.JniParams(qualified_clazz)
1562    h1 = jni_generator.InlHeaderFileGenerator('', '', qualified_clazz, natives,
1563                                              [], [], jni_params, options)
1564    self.AssertGoldenTextEquals(h1.GetContent())
1565    h2 = jni_registration_generator.DictionaryGenerator(reg_options, '', '',
1566                                                        qualified_clazz,
1567                                                        natives, jni_params)
1568    content = TestGenerator._MergeRegistrationForTests([h2.Generate()])
1569
1570
1571    self.AssertGoldenTextEquals(
1572        jni_registration_generator.CreateProxyJavaFromDict(
1573            reg_options, '', content),
1574        suffix='Java')
1575
1576    self.AssertGoldenTextEquals(jni_registration_generator.CreateFromDict(
1577        reg_options, '', content),
1578                                suffix='Registrations')
1579
1580  def testProxyHashedExample(self):
1581    opts = JniGeneratorOptions()
1582    opts.use_proxy_hash = True
1583    path = os.path.join(_JAVA_SRC_DIR, 'SampleForAnnotationProcessor.java')
1584
1585    generated_text = self._CreateJniHeaderFromFile(
1586        path, 'org/chromium/example/jni_generator/SampleForAnnotationProcessor',
1587        opts)
1588    self.AssertGoldenTextEquals(
1589        generated_text,
1590        golden_file='HashedSampleForAnnotationProcessor_jni.golden')
1591
1592  def testProxyJniExample(self):
1593    generated_text = self._CreateJniHeaderFromFile(
1594        os.path.join(_JAVA_SRC_DIR, 'SampleForAnnotationProcessor.java'),
1595        'org/chromium/example/jni_generator/SampleForAnnotationProcessor')
1596    self.AssertGoldenTextEquals(
1597        generated_text, golden_file='SampleForAnnotationProcessor_jni.golden')
1598
1599  def testGenJniFlags(self):
1600    options = JniRegistrationGeneratorOptions()
1601    reg_dict = self._BuildRegDictFromSample()
1602    content = jni_registration_generator.CreateProxyJavaFromDict(
1603        options, '', reg_dict)
1604    self.AssertGoldenTextEquals(content, 'Disabled')
1605
1606    options.enable_proxy_mocks = True
1607    content = jni_registration_generator.CreateProxyJavaFromDict(
1608        options, '', reg_dict)
1609    self.AssertGoldenTextEquals(content, 'MocksEnabled')
1610
1611    options.require_mocks = True
1612    content = jni_registration_generator.CreateProxyJavaFromDict(
1613        options, '', reg_dict)
1614    self.AssertGoldenTextEquals(content, 'MocksRequired')
1615
1616  def testProxyTypeInfoPreserved(self):
1617    test_data = """
1618    package org.chromium.foo;
1619
1620    class Foo {
1621
1622    @NativeMethods
1623    interface Natives {
1624      char[][] fooProxy(byte[][] b);
1625      SomeJavaType[][] barProxy(String[][] s, short z);
1626      String[] foobarProxy(String[] a, int[][] b);
1627      byte[][] bazProxy(long nativePtr, BazClass caller,
1628          SomeJavaType[][] someObjects);
1629    }
1630    """
1631    natives, _ = ProxyHelpers.ExtractStaticProxyNatives(
1632        'org/chromium/foo/FooJni', test_data, 'long')
1633    golden_natives = [
1634        NativeMethod(
1635            static=True,
1636            java_class_name=None,
1637            return_type='char[][]',
1638            name='fooProxy',
1639            params=[Param(datatype='byte[][]', name='b')],
1640            is_proxy=True,
1641            proxy_name='org_chromium_foo_FooJni_fooProxy'),
1642        NativeMethod(
1643            static=True,
1644            java_class_name=None,
1645            return_type='Object[][]',
1646            name='barProxy',
1647            params=[
1648                Param(datatype='String[][]', name='s'),
1649                Param(datatype='short', name='z')
1650            ],
1651            is_proxy=True,
1652            proxy_name='org_chromium_foo_FooJni_barProxy'),
1653        NativeMethod(
1654            static=True,
1655            java_class_name=None,
1656            return_type='String[]',
1657            name='foobarProxy',
1658            params=[
1659                Param(datatype='String[]', name='a'),
1660                Param(datatype='int[][]', name='b')
1661            ],
1662            is_proxy=True,
1663            proxy_name='org_chromium_foo_FooJni_foobarProxy'),
1664        NativeMethod(
1665            static=True,
1666            java_class_name=None,
1667            return_type='byte[][]',
1668            name='bazProxy',
1669            params=[
1670                Param(datatype='long', name='nativePtr'),
1671                Param(datatype='Object', name='caller'),
1672                Param(datatype='Object[][]', name='someObjects')
1673            ],
1674            is_proxy=True,
1675            proxy_name='org_chromium_foo_FooJni_bazProxy',
1676            ptr_type='long')
1677    ]
1678    self.AssertListEquals(golden_natives, _RemoveHashedNames(natives))
1679
1680
1681@unittest.skipIf(os.name == 'nt', 'Not intended to work on Windows')
1682class PackagePrefixTestGenerator(BaseTest):
1683  def testJniSelfDocumentingExampleWithPackagePrefix(self):
1684    options = JniGeneratorOptions()
1685    options.package_prefix = 'this.is.a.package.prefix'
1686    generated_text = self._CreateJniHeaderFromFile(
1687        os.path.join(_JAVA_SRC_DIR, 'SampleForTests.java'),
1688        'org/chromium/example/jni_generator/SampleForTests', options)
1689    self.AssertGoldenTextEquals(
1690        generated_text,
1691        golden_file='SampleForTestsWithPackagePrefix_jni.golden')
1692
1693  def testProxyPackagePrefixWithManualRegistration(self):
1694    input_java_files = ['SampleForAnnotationProcessor.java']
1695    options = JniRegistrationGeneratorOptions()
1696    options.package_prefix = 'this.is.a.package.prefix'
1697    options.manual_jni_registration = True
1698    name_to_goldens = {
1699        'this/is/a/package/prefix/org/chromium/base/natives/GEN_JNI.java':
1700        'testProxyPackagePrefixWithManualRegistration.2.golden',
1701    }
1702    self._TestEndToEndRegistration(
1703        input_java_files, options, name_to_goldens,
1704        'testProxyPackagePrefixWithManualRegistrationHeader.golden')
1705
1706  def testProxyPackagePrefixWithProxyHash(self):
1707    input_java_files = ['SampleForAnnotationProcessor.java']
1708    options = JniRegistrationGeneratorOptions()
1709    options.package_prefix = 'this.is.a.package.prefix'
1710    options.use_proxy_hash = True
1711    name_to_goldens = {
1712        'this/is/a/package/prefix/org/chromium/base/natives/GEN_JNI.java':
1713        'testProxyPackagePrefixWithProxyHash.2.golden',
1714        'this/is/a/package/prefix/J/N.java':
1715        'testProxyPackagePrefixWithProxyHash.golden',
1716    }
1717    self._TestEndToEndRegistration(
1718        input_java_files, options, name_to_goldens,
1719        'testProxyPackagePrefixWithProxyHashHeader.golden')
1720
1721  def testProxyPackagePrefixWithManualRegistrationWithProxyHash(self):
1722    input_java_files = ['SampleForAnnotationProcessor.java']
1723    options = JniRegistrationGeneratorOptions()
1724    options.package_prefix = 'this.is.a.package.prefix'
1725    options.manual_jni_registration = True
1726    options.use_proxy_hash = True
1727    name_to_goldens = {
1728        'this/is/a/package/prefix/org/chromium/base/natives/GEN_JNI.java':
1729        'testProxyPackagePrefixWithManualRegistrationProxyHash.2.golden',
1730        'this/is/a/package/prefix/J/N.java':
1731        'testProxyPackagePrefixWithManualRegistrationProxyHash.golden',
1732    }
1733    self._TestEndToEndRegistration(
1734        input_java_files, options, name_to_goldens,
1735        'testProxyPackagePrefixWithManualRegistrationProxyHashHeader.golden')
1736
1737
1738@unittest.skipIf(os.name == 'nt', 'Not intended to work on Windows')
1739class MultiplexTestGenerator(BaseTest):
1740  options = JniRegistrationGeneratorOptions()
1741  options.enable_jni_multiplexing = True
1742
1743  def testProxyMultiplexGenJni(self):
1744    path = os.path.join(_JAVA_SRC_DIR, 'SampleForAnnotationProcessor.java')
1745    reg_dict = jni_registration_generator._DictForPath(
1746        self.options, self._JoinScriptDir(path))
1747    reg_dict = self._MergeRegistrationForTests([reg_dict],
1748                                               enable_jni_multiplexing=True)
1749
1750    self.AssertGoldenTextEquals(
1751        jni_registration_generator.CreateProxyJavaFromDict(
1752            self.options, '', reg_dict),
1753        golden_file='testProxyMultiplexGenJni.golden')
1754
1755    self.AssertGoldenTextEquals(
1756        jni_registration_generator.CreateProxyJavaFromDict(self.options,
1757                                                           '',
1758                                                           reg_dict,
1759                                                           forwarding=True),
1760        golden_file='testProxyMultiplexGenJni.2.golden')
1761
1762  def testProxyMultiplexNatives(self):
1763    path = os.path.join(_JAVA_SRC_DIR, 'SampleForAnnotationProcessor.java')
1764    reg_dict = jni_registration_generator._DictForPath(
1765        self.options, self._JoinScriptDir(path))
1766    reg_dict = self._MergeRegistrationForTests([reg_dict],
1767                                               enable_jni_multiplexing=True)
1768
1769    self.AssertGoldenTextEquals(jni_registration_generator.CreateFromDict(
1770        self.options, '', reg_dict),
1771                                golden_file='testProxyMultiplexNatives.golden')
1772
1773  def testProxyMultiplexNativesRegistration(self):
1774    path = os.path.join(_JAVA_SRC_DIR, 'SampleForAnnotationProcessor.java')
1775    reg_dict_for_registration = jni_registration_generator._DictForPath(
1776        self.options, self._JoinScriptDir(path))
1777    reg_dict_for_registration = self._MergeRegistrationForTests(
1778        [reg_dict_for_registration], enable_jni_multiplexing=True)
1779
1780    new_options = copy.copy(self.options)
1781    new_options.manual_jni_registration = True
1782    self.AssertGoldenTextEquals(
1783        jni_registration_generator.CreateFromDict(new_options, '',
1784                                                  reg_dict_for_registration),
1785        golden_file='testProxyMultiplexNativesRegistration.golden')
1786
1787
1788def TouchStamp(stamp_path):
1789  dir_name = os.path.dirname(stamp_path)
1790  if not os.path.isdir(dir_name):
1791    os.makedirs(dir_name)
1792
1793  with open(stamp_path, 'a'):
1794    os.utime(stamp_path, None)
1795
1796
1797def main(argv):
1798  parser = optparse.OptionParser()
1799  parser.add_option('--stamp', help='Path to touch on success.')
1800  parser.add_option(
1801      '-v', '--verbose', action='store_true', help='Whether to output details.')
1802  options, _ = parser.parse_args(argv[1:])
1803
1804  test_result = unittest.main(
1805      argv=argv[0:1], exit=False, verbosity=(2 if options.verbose else 1))
1806
1807  if test_result.result.wasSuccessful() and options.stamp:
1808    TouchStamp(options.stamp)
1809
1810  return not test_result.result.wasSuccessful()
1811
1812
1813if __name__ == '__main__':
1814  sys.exit(main(sys.argv))
1815