Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(231)

Side by Side Diff: scripts/slave/ios/test_runner.py

Issue 2232723004: Revert of Use iossim/test-without-building for Earlgrey tests. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/build.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « scripts/slave/ios/run.py ('k') | scripts/slave/recipe_modules/ios/api.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Contains test runners for launching tests on simulators and devices.""" 6 """Contains test runners for launching tests on simulators and devices."""
7 7
8 # pylint: disable=relative-import 8 # pylint: disable=relative-import
9 import environment_setup 9 import environment_setup
10 10
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
84 'Unexpected app path: %s. %s' % (app_path, valid_extensions)) 84 'Unexpected app path: %s. %s' % (app_path, valid_extensions))
85 85
86 86
87 class SimulatorNotFoundError(TestRunnerError): 87 class SimulatorNotFoundError(TestRunnerError):
88 """The iossim path was not found.""" 88 """The iossim path was not found."""
89 def __init__(self, iossim_path): 89 def __init__(self, iossim_path):
90 super(SimulatorNotFoundError, self).__init__( 90 super(SimulatorNotFoundError, self).__init__(
91 'Simulator does not exist: %s.' % iossim_path) 91 'Simulator does not exist: %s.' % iossim_path)
92 92
93 93
94 class XctestNotFoundError(TestRunnerError):
95 """The xctest intended to be run was not found."""
96 def __init__(self, xctest_path):
97 super(XctestNotFoundError, self).__init__(
98 'Xctest does not exist: %s.' % xctest_path)
99
100
101 class AppLaunchError(TestRunnerError): 94 class AppLaunchError(TestRunnerError):
102 """There was an error launching the app.""" 95 """There was an error launching the app."""
103 pass 96 pass
104 97
105 98
106 class TestRunner(object): 99 class TestRunner(object):
107 """Base class containing common TestRunner functionality.""" 100 """Base class containing common TestRunner functionality."""
108 def __init__( 101 def __init__(
109 self, 102 self,
110 app_path, 103 app_path,
(...skipping 675 matching lines...) Expand 10 before | Expand all | Expand 10 after
786 result = self._Run(self.GetLaunchCommand(), *args, **kwargs) 779 result = self._Run(self.GetLaunchCommand(), *args, **kwargs)
787 780
788 return self.RunAllTests(result, *args, **kwargs) 781 return self.RunAllTests(result, *args, **kwargs)
789 782
790 783
791 class XCTestRunner(TestRunner): 784 class XCTestRunner(TestRunner):
792 """Base class containing common functionalities to run xctests.""" 785 """Base class containing common functionalities to run xctests."""
793 def __init__( 786 def __init__(
794 self, 787 self,
795 app_path, 788 app_path,
789 test_host,
790 test_project_dir,
796 xcode_version=None, 791 xcode_version=None,
797 gs_bucket=None, 792 gs_bucket=None,
798 perf_bot_name=None, 793 perf_bot_name=None,
799 perf_build_number=None, 794 perf_build_number=None,
800 perf_builder_name=None, 795 perf_builder_name=None,
801 perf_master_name=None, 796 perf_master_name=None,
802 perf_revision=None, 797 perf_revision=None,
803 perf_x_value=None, 798 perf_x_value=None,
804 test_args=None, 799 test_args=None,
805 env_vars=None, 800 env_vars=None,
806 ): 801 ):
807 """Initializes an instance of the SimulatorXCTestRunner class. 802 """Initializes an instance of the SimulatorXCTestRunner class.
808 803
809 Args: 804 Args:
810 app_path: Full path to the compiled app to run. 805 app_path: Full path to the compiled app to run.
806 test_host: Name of the compiled test host app to run tests.
807 test_project_dir: Directory of the dummy test project.
811 xcode_version: Version of Xcode to use. 808 xcode_version: Version of Xcode to use.
812 gs_bucket: Google Storage bucket to upload test data to, or None if the 809 gs_bucket: Google Storage bucket to upload test data to, or None if the
813 test data should not be uploaded. 810 test data should not be uploaded.
814 perf_bot_name: Name of this bot as indicated to the perf dashboard. 811 perf_bot_name: Name of this bot as indicated to the perf dashboard.
815 perf_build_number: Build number to indicate to the perf dashboard. 812 perf_build_number: Build number to indicate to the perf dashboard.
816 perf_builder_name: Name of this builder as indicated to the perf 813 perf_builder_name: Name of this builder as indicated to the perf
817 dashboard. 814 dashboard.
818 perf_master_name: Name of the master as indicated to the perf dashboard. 815 perf_master_name: Name of the master as indicated to the perf dashboard.
819 perf_revision: Revision to indicate to the perf dashboard. 816 perf_revision: Revision to indicate to the perf dashboard.
820 perf_x_value: Value to use on the x axis for all data uploaded to the 817 perf_x_value: Value to use on the x axis for all data uploaded to the
(...skipping 12 matching lines...) Expand all
833 perf_bot_name=perf_bot_name, 830 perf_bot_name=perf_bot_name,
834 perf_build_number=perf_build_number, 831 perf_build_number=perf_build_number,
835 perf_builder_name=perf_builder_name, 832 perf_builder_name=perf_builder_name,
836 perf_master_name=perf_master_name, 833 perf_master_name=perf_master_name,
837 perf_revision=perf_revision, 834 perf_revision=perf_revision,
838 perf_x_value=perf_x_value, 835 perf_x_value=perf_x_value,
839 test_args=test_args, 836 test_args=test_args,
840 xcode_version=xcode_version, 837 xcode_version=xcode_version,
841 ) 838 )
842 self.app_path = os.path.abspath(app_path) 839 self.app_path = os.path.abspath(app_path)
840 self.test_host_name = test_host
841 # Test target name is its host name without '_host' suffix.
842 self.test_target_name = test_host.rsplit('_', 1)[0]
843 self.test_project_dir = test_project_dir
843 self.timeout = '120' 844 self.timeout = '120'
844 self.homedir = '' 845 self.homedir = ''
845 self.start_time = None 846 self.start_time = None
846 847
847 def TearDown(self): 848 def TearDown(self):
848 """Performs post-test tear down.""" 849 """Performs post-test tear down."""
849 raise NotImplementedError 850 raise NotImplementedError
850 851
851 def HandleJsonFileWithPath(self, summary): 852 def HandleJsonFileWithPath(self, summary):
852 """Parse data in summarydir and send to perf dashboard.""" 853 """Parse data in summarydir and send to perf dashboard."""
853 with open(summary) as jsonFile: 854 with open(summary) as jsonFile:
854 return json.load(jsonFile) 855 return json.load(jsonFile)
855 856
856 def GetLaunchEnvironment(self): 857 def GetLaunchEnvironment(self):
857 """Returns the environment which is used to run the xctest. 858 """Returns the environment which is used to run the xctest.
858 """ 859 """
859 env = dict(os.environ, 860 env = dict(os.environ, APP_TARGET_NAME=self.test_host_name,
861 TEST_TARGET_NAME=self.test_target_name,
860 NSUnbufferedIO='YES') 862 NSUnbufferedIO='YES')
861 return env 863 return env
862 864
863 def GetLaunchCommand(self, test_filter=None, blacklist=False): 865 def GetLaunchCommand(self, test_filter=None, blacklist=False):
864 """Returns the command which is used to launch the test. 866 """Returns the command which is used to launch the test.
865 867
866 Args: 868 Args:
867 test_filter: A list of tests to filter by, or None to mean all. 869 test_filter: A list of tests to filter by, or None to mean all.
868 blacklist: Whether to blacklist the elements of test_filter or not. Only 870 blacklist: Whether to blacklist the elements of test_filter or not. Only
869 works when test_filter is not None. 871 works when test_filter is not None.
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
1011 # At this point, all the tests have run, so used failed_tests to determine 1013 # At this point, all the tests have run, so used failed_tests to determine
1012 # the success/failure. 1014 # the success/failure.
1013 return not failed_tests 1015 return not failed_tests
1014 1016
1015 1017
1016 class SimulatorXCTestRunner(XCTestRunner): 1018 class SimulatorXCTestRunner(XCTestRunner):
1017 """Class for running xctests on an iOS simulator.""" 1019 """Class for running xctests on an iOS simulator."""
1018 def __init__( 1020 def __init__(
1019 self, 1021 self,
1020 app_path, 1022 app_path,
1021 xctest_path, 1023 test_host,
1022 iossim_path, 1024 test_project_dir,
1023 platform, 1025 platform,
1024 version, 1026 version,
1025 xcode_version=None, 1027 xcode_version=None,
1026 gs_bucket=None, 1028 gs_bucket=None,
1027 perf_bot_name=None, 1029 perf_bot_name=None,
1028 perf_build_number=None, 1030 perf_build_number=None,
1029 perf_builder_name=None, 1031 perf_builder_name=None,
1030 perf_master_name=None, 1032 perf_master_name=None,
1031 perf_revision=None, 1033 perf_revision=None,
1032 perf_x_value=None, 1034 perf_x_value=None,
1033 test_args=None, 1035 test_args=None,
1034 env_vars=None, 1036 env_vars=None,
1035 ): 1037 ):
1036 """Initializes an instance of the SimulatorXCTestRunner class. 1038 """Initializes an instance of the SimulatorXCTestRunner class.
1037 1039
1038 Args: 1040 Args:
1039 app_path: Full path to the compiled app to run. 1041 app_path: Full path to the compiled app to run.
1040 iossim_path: Full path to the iossim executable to launch. 1042 test_host: Name of the compiled test host app to run tests.
1041 xctest_path: Full path to the compiled test bundle. 1043 test_project_dir: Directory of the dummy test project.
1042 platform: The platform to simulate. Supported values can be found by 1044 platform: The platform to simulate. Supported values can be found by
1043 running 'xcodebuild -list'. e.g. 'iPhone 5', 'iPhone 5s'. 1045 running 'xcodebuild -list'. e.g. 'iPhone 5', 'iPhone 5s'.
1044 version: The iOS version the simulator should be running. Supported values 1046 version: The iOS version the simulator should be running. Supported values
1045 can be found by running 'xcodebuild -list'. e.g. '8.0', '7.1'. 1047 can be found by running 'xcodebuild -list'. e.g. '8.0', '7.1'.
1046 xcode_version: Version of Xcode to use. 1048 xcode_version: Version of Xcode to use.
1047 gs_bucket: Google Storage bucket to upload test data to, or None if the 1049 gs_bucket: Google Storage bucket to upload test data to, or None if the
1048 test data should not be uploaded. 1050 test data should not be uploaded.
1049 perf_bot_name: Name of this bot as indicated to the perf dashboard. 1051 perf_bot_name: Name of this bot as indicated to the perf dashboard.
1050 perf_build_number: Build number to indicate to the perf dashboard. 1052 perf_build_number: Build number to indicate to the perf dashboard.
1051 perf_builder_name: Name of this builder as indicated to the perf 1053 perf_builder_name: Name of this builder as indicated to the perf
1052 dashboard. 1054 dashboard.
1053 perf_master_name: Name of the master as indicated to the perf dashboard. 1055 perf_master_name: Name of the master as indicated to the perf dashboard.
1054 perf_revision: Revision to indicate to the perf dashboard. 1056 perf_revision: Revision to indicate to the perf dashboard.
1055 perf_x_value: Value to use on the x axis for all data uploaded to the 1057 perf_x_value: Value to use on the x axis for all data uploaded to the
1056 perf dashboard. 1058 perf dashboard.
1057 test_args: Arguments to pass when launching the test. 1059 test_args: Arguments to pass when launching the test.
1058 env_vars: Environment variables to set when launching the test. 1060 env_vars: Environment variables to set when launching the test.
1059 1061
1060 Raises: 1062 Raises:
1061 SimulatorNotFoundError: If the given iossim path cannot be found. 1063 SimulatorNotFoundError: If the given iossim path cannot be found.
1062 """ 1064 """
1063 super(SimulatorXCTestRunner, self).__init__( 1065 super(SimulatorXCTestRunner, self).__init__(
1064 app_path, 1066 app_path,
1067 test_host,
1068 test_project_dir,
1065 env_vars=env_vars, 1069 env_vars=env_vars,
1066 gs_bucket=gs_bucket, 1070 gs_bucket=gs_bucket,
1067 perf_bot_name=perf_bot_name, 1071 perf_bot_name=perf_bot_name,
1068 perf_build_number=perf_build_number, 1072 perf_build_number=perf_build_number,
1069 perf_builder_name=perf_builder_name, 1073 perf_builder_name=perf_builder_name,
1070 perf_master_name=perf_master_name, 1074 perf_master_name=perf_master_name,
1071 perf_revision=perf_revision, 1075 perf_revision=perf_revision,
1072 perf_x_value=perf_x_value, 1076 perf_x_value=perf_x_value,
1073 test_args=test_args, 1077 test_args=test_args,
1074 xcode_version=xcode_version, 1078 xcode_version=xcode_version,
1075 ) 1079 )
1076
1077 if not os.path.exists(iossim_path):
1078 raise SimulatorNotFoundError(iossim_path)
1079
1080 if not os.path.exists(xctest_path):
1081 raise XctestNotFoundError(xctest_path)
1082
1083 self.iossim_path = iossim_path
1084 self.xctest_path = xctest_path
1085 self.platform = platform 1080 self.platform = platform
1086 self.version = version 1081 self.version = version
1087 1082
1088 def UploadTestData(self): 1083 def UploadTestData(self):
1089 """Uploads the contents of the test's Documents directory. 1084 """Uploads the contents of the test's Documents directory.
1090 1085
1091 Returns: 1086 Returns:
1092 True if test data was uploaded, False otherwise. 1087 True if test data was uploaded, False otherwise.
1093 """ 1088 """
1094 if not self.gs_bucket: 1089 if not self.gs_bucket:
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
1263 """Returns the invocation command which is used to run the test. 1258 """Returns the invocation command which is used to run the test.
1264 1259
1265 Args: 1260 Args:
1266 test_filter: A list of tests to filter by, or None to mean all. 1261 test_filter: A list of tests to filter by, or None to mean all.
1267 blacklist: Whether to blacklist the elements of test_filter or not. Only 1262 blacklist: Whether to blacklist the elements of test_filter or not. Only
1268 works when test_filter is not None. 1263 works when test_filter is not None.
1269 1264
1270 Returns: 1265 Returns:
1271 A list whose elements are the args representing the command. 1266 A list whose elements are the args representing the command.
1272 """ 1267 """
1268 built_dir = os.path.split(self.app_path)[0]
1269
1273 cmd = [ 1270 cmd = [
1274 self.iossim_path, 1271 'xcodebuild', 'test-without-building',
1275 '-d', self.platform, 1272 'BUILT_PRODUCTS_DIR=%s' % built_dir,
1276 '-s', self.version, 1273 '-project', self.test_project_dir,
1277 self.app_path, 1274 '-scheme','TestProject',
1278 self.xctest_path 1275 '-destination','platform=iOS Simulator,name=%s,OS=%s'
1276 % (self.platform, self.version),
1277 '-archivePath', self.homedir,
1278 'APP_TARGET_NAME=%s' % self.test_host_name,
1279 'TEST_TARGET_NAME=%s' % self.test_target_name,
1280 'NSUnbufferedIO=YES'
1279 ] 1281 ]
1280 return cmd 1282 return cmd
1281 1283
1282 @TestRunner.RequireTearDown 1284 @TestRunner.RequireTearDown
1283 def Launch(self, *args, **kwargs): 1285 def Launch(self, *args, **kwargs):
1284 """Launches the test.""" 1286 """Launches the test."""
1285 self.SetUp() 1287 self.SetUp()
1286 1288
1287 result = self._Run( 1289 result = self._Run(
1288 self.GetLaunchCommand(), self.GetLaunchEnvironment(), *args, **kwargs) 1290 self.GetLaunchCommand(), self.GetLaunchEnvironment(), *args, **kwargs)
(...skipping 15 matching lines...) Expand all
1304 self.GetLaunchCommand(), self.GetLaunchEnvironment(), *args, **kwargs) 1306 self.GetLaunchCommand(), self.GetLaunchEnvironment(), *args, **kwargs)
1305 1307
1306 return self.RunAllTests(result, *args, **kwargs) 1308 return self.RunAllTests(result, *args, **kwargs)
1307 1309
1308 1310
1309 class DeviceXCTestRunner(XCTestRunner): 1311 class DeviceXCTestRunner(XCTestRunner):
1310 """Class for running xctests on an iOS device.""" 1312 """Class for running xctests on an iOS device."""
1311 def __init__( 1313 def __init__(
1312 self, 1314 self,
1313 app_path, 1315 app_path,
1314 xctest_path, 1316 test_host,
1315 xcodeproj_path, 1317 test_project_dir,
1316 xcode_version=None, 1318 xcode_version=None,
1317 gs_bucket=None, 1319 gs_bucket=None,
1318 perf_bot_name=None, 1320 perf_bot_name=None,
1319 perf_build_number=None, 1321 perf_build_number=None,
1320 perf_builder_name=None, 1322 perf_builder_name=None,
1321 perf_master_name=None, 1323 perf_master_name=None,
1322 perf_revision=None, 1324 perf_revision=None,
1323 perf_x_value=None, 1325 perf_x_value=None,
1324 test_args=None, 1326 test_args=None,
1325 env_vars=None, 1327 env_vars=None,
(...skipping 19 matching lines...) Expand all
1345 env_vars: Environment variables to set when launching the test. 1347 env_vars: Environment variables to set when launching the test.
1346 1348
1347 Raises: 1349 Raises:
1348 DeviceDetectionError: If this machine does not have exactly one device 1350 DeviceDetectionError: If this machine does not have exactly one device
1349 connected. Having more than one device connected causes problems when 1351 connected. Having more than one device connected causes problems when
1350 trying to issue commands to any one device, which interfere with 1352 trying to issue commands to any one device, which interfere with
1351 installing and running the test app. 1353 installing and running the test app.
1352 """ 1354 """
1353 super(DeviceXCTestRunner, self).__init__( 1355 super(DeviceXCTestRunner, self).__init__(
1354 app_path, 1356 app_path,
1357 test_host,
1358 test_project_dir,
1355 env_vars=env_vars, 1359 env_vars=env_vars,
1356 gs_bucket=gs_bucket, 1360 gs_bucket=gs_bucket,
1357 perf_bot_name=perf_bot_name, 1361 perf_bot_name=perf_bot_name,
1358 perf_build_number=perf_build_number, 1362 perf_build_number=perf_build_number,
1359 perf_builder_name=perf_builder_name, 1363 perf_builder_name=perf_builder_name,
1360 perf_master_name=perf_master_name, 1364 perf_master_name=perf_master_name,
1361 perf_revision=perf_revision, 1365 perf_revision=perf_revision,
1362 perf_x_value=perf_x_value, 1366 perf_x_value=perf_x_value,
1363 test_args=test_args, 1367 test_args=test_args,
1364 xcode_version=xcode_version, 1368 xcode_version=xcode_version,
1365 ) 1369 )
1366 # The scheme name is the app name without the '.app' suffix.
1367 app_name = os.path.split(self.app_path)[1]
1368 self.scheme_name = app_name.split('.', 1)[0]
1369 self.xcodeproj_path = xcodeproj_path
1370 self.cfbundleid = utils.call( 1370 self.cfbundleid = utils.call(
1371 utils.PLIST_BUDDY, 1371 utils.PLIST_BUDDY,
1372 '-c', 'Print:CFBundleIdentifier', 1372 '-c', 'Print:CFBundleIdentifier',
1373 os.path.join(self.app_path, 'Info.plist'), 1373 os.path.join(self.app_path, 'Info.plist'),
1374 ).stdout[0] 1374 ).stdout[0]
1375 1375
1376 call_result = utils.call('idevice_id', '--list') 1376 call_result = utils.call('idevice_id', '--list')
1377 self.device_id = call_result.stdout[0] 1377 self.device_id = call_result.stdout[0]
1378 1378
1379 if len(call_result.stdout) != 1: 1379 if len(call_result.stdout) != 1:
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1420 """Returns the invocation command which is used to run the test. 1420 """Returns the invocation command which is used to run the test.
1421 1421
1422 Args: 1422 Args:
1423 test_filter: A list of tests to filter by, or None to mean all. 1423 test_filter: A list of tests to filter by, or None to mean all.
1424 blacklist: Whether to blacklist the elements of test_filter or not. Only 1424 blacklist: Whether to blacklist the elements of test_filter or not. Only
1425 works when test_filter is not None. 1425 works when test_filter is not None.
1426 1426
1427 Returns: 1427 Returns:
1428 A list whose elements are the args representing the command. 1428 A list whose elements are the args representing the command.
1429 """ 1429 """
1430 built_dir = os.path.split(self.app_path)[0]
1431
1430 cmd = [ 1432 cmd = [
1431 'xcodebuild', 'test-without-building', 1433 'xcodebuild', 'test-without-building',
1432 '-project', self.xcodeproj_path, 1434 'BUILT_PRODUCTS_DIR=%s' % built_dir,
1433 '-scheme', self.scheme_name, 1435 'CONFIGURATION_BUILD_DIR=%s' % built_dir,
1434 '-destination', 'id=%s' % self.device_id 1436 '-project', self.test_project_dir,
1437 '-configuration', 'iphoneos',
1438 '-scheme', 'TestProject',
1439 '-destination','id=%s' % self.device_id,
1440 'APP_TARGET_NAME=%s' % self.test_host_name,
1441 'TEST_TARGET_NAME=%s' % self.test_target_name,
1442 'NSUnbufferedIO=YES'
1435 ] 1443 ]
1436 return cmd 1444 return cmd
1437 1445
1438 @TestRunner.RequireTearDown 1446 @TestRunner.RequireTearDown
1439 def Launch(self, *args, **kwargs): 1447 def Launch(self, *args, **kwargs):
1440 """Launches the test.""" 1448 """Launches the test."""
1441 self.InstallApp() 1449 self.InstallApp()
1442 1450
1443 result = self._Run( 1451 result = self._Run(
1444 self.GetLaunchCommand(), self.GetLaunchEnvironment(), *args, **kwargs) 1452 self.GetLaunchCommand(), self.GetLaunchEnvironment(), *args, **kwargs)
1445 1453
1446 if result.crashed and not result.crashed_test: 1454 if result.crashed and not result.crashed_test:
1447 # If the app crashed, but there is no specific test which crashed, 1455 # If the app crashed, but there is no specific test which crashed,
1448 # then the app must have failed to even start. Try one more time. 1456 # then the app must have failed to even start. Try one more time.
1449 self.Print( 1457 self.Print(
1450 '%s appears to have crashed on startup. Retrying...' % self.app_name, 1458 '%s appears to have crashed on startup. Retrying...' % self.app_name,
1451 blank_lines=2, 1459 blank_lines=2,
1452 time_to_sleep=5, 1460 time_to_sleep=5,
1453 ) 1461 )
1454 1462
1455 # Uninstall and re-install the app. 1463 # Uninstall and re-install the app.
1456 self.UninstallApp() 1464 self.UninstallApp()
1457 self.InstallApp() 1465 self.InstallApp()
1458 1466
1459 result = self._Run( 1467 result = self._Run(
1460 self.GetLaunchCommand(), self.GetLaunchEnvironment(), *args, **kwargs) 1468 self.GetLaunchCommand(), self.GetLaunchEnvironment(), *args, **kwargs)
1461 1469
1462 return self.RunAllTests(result, *args, **kwargs) 1470 return self.RunAllTests(result, *args, **kwargs)
OLDNEW
« no previous file with comments | « scripts/slave/ios/run.py ('k') | scripts/slave/recipe_modules/ios/api.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698