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