mirror of https://github.com/python/cpython
gh-126167: Modify iOS Testbed to read arguments from Info.plist (#126169)
Modify iOS Testbed to read arguments from Info.plist.
This commit is contained in:
parent
0c5c80928c
commit
500a4712bb
|
@ -0,0 +1,2 @@
|
|||
The iOS testbed was modified so that it can be used by third-party projects
|
||||
for testing purposes.
|
|
@ -351,13 +351,13 @@ Running specific tests
|
|||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As the test suite is being executed on an iOS simulator, it is not possible to
|
||||
pass in command line arguments to configure test suite operation. To work around
|
||||
this limitation, the arguments that would normally be passed as command line
|
||||
arguments are configured as a static string at the start of the XCTest method
|
||||
``- (void)testPython`` in ``iOSTestbedTests.m``. To pass an argument to the test
|
||||
suite, add a a string to the ``argv`` definition. These arguments will be passed
|
||||
to the test suite as if they had been passed to ``python -m test`` at the
|
||||
command line.
|
||||
pass in command line arguments to configure test suite operation. To work
|
||||
around this limitation, the arguments that would normally be passed as command
|
||||
line arguments are configured as part of the ``iOSTestbed-Info.plist`` file
|
||||
that is used to configure the iOS testbed app. In this file, the ``TestArgs``
|
||||
key is an array containing the arguments that would be passed to ``python -m``
|
||||
on the command line (including ``test`` in position 0, the name of the test
|
||||
module to be executed).
|
||||
|
||||
Disabling automated breakpoints
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
607A66502B0EFFE00010BFC8 /* Python.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; };
|
||||
607A66512B0EFFE00010BFC8 /* Python.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 607A664A2B0EFB310010BFC8 /* Python.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */ = {isa = PBXBuildFile; fileRef = 607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */; };
|
||||
608619542CB77BA900F46182 /* app_packages in Resources */ = {isa = PBXBuildFile; fileRef = 608619532CB77BA900F46182 /* app_packages */; };
|
||||
608619562CB7819B00F46182 /* app in Resources */ = {isa = PBXBuildFile; fileRef = 608619552CB7819B00F46182 /* app */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -66,6 +68,8 @@
|
|||
607A664A2B0EFB310010BFC8 /* Python.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = Python.xcframework; sourceTree = "<group>"; };
|
||||
607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "dylib-Info-template.plist"; sourceTree = "<group>"; };
|
||||
607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iOSTestbed-Info.plist"; sourceTree = "<group>"; };
|
||||
608619532CB77BA900F46182 /* app_packages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app_packages; sourceTree = "<group>"; };
|
||||
608619552CB7819B00F46182 /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -111,6 +115,8 @@
|
|||
607A66142B0EFA380010BFC8 /* iOSTestbed */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
608619552CB7819B00F46182 /* app */,
|
||||
608619532CB77BA900F46182 /* app_packages */,
|
||||
607A66592B0F08600010BFC8 /* iOSTestbed-Info.plist */,
|
||||
607A66572B0F079F0010BFC8 /* dylib-Info-template.plist */,
|
||||
607A66152B0EFA380010BFC8 /* AppDelegate.h */,
|
||||
|
@ -223,7 +229,9 @@
|
|||
files = (
|
||||
607A66252B0EFA390010BFC8 /* LaunchScreen.storyboard in Resources */,
|
||||
607A66582B0F079F0010BFC8 /* dylib-Info-template.plist in Resources */,
|
||||
608619562CB7819B00F46182 /* app in Resources */,
|
||||
607A66222B0EFA390010BFC8 /* Assets.xcassets in Resources */,
|
||||
608619542CB77BA900F46182 /* app_packages in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -273,7 +281,7 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n";
|
||||
shellScript = "set -e\n\ninstall_dylib () {\n INSTALL_BASE=$1\n FULL_EXT=$2\n\n # The name of the extension file\n EXT=$(basename \"$FULL_EXT\")\n # The location of the extension file, relative to the bundle\n RELATIVE_EXT=${FULL_EXT#$CODESIGNING_FOLDER_PATH/} \n # The path to the extension file, relative to the install base\n PYTHON_EXT=${RELATIVE_EXT/$INSTALL_BASE/}\n # The full dotted name of the extension module, constructed from the file path.\n FULL_MODULE_NAME=$(echo $PYTHON_EXT | cut -d \".\" -f 1 | tr \"/\" \".\"); \n # A bundle identifier; not actually used, but required by Xcode framework packaging\n FRAMEWORK_BUNDLE_ID=$(echo $PRODUCT_BUNDLE_IDENTIFIER.$FULL_MODULE_NAME | tr \"_\" \"-\")\n # The name of the framework folder.\n FRAMEWORK_FOLDER=\"Frameworks/$FULL_MODULE_NAME.framework\"\n\n # If the framework folder doesn't exist, create it.\n if [ ! -d \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\" ]; then\n echo \"Creating framework for $RELATIVE_EXT\" \n mkdir -p \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER\"\n cp \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleExecutable -string \"$FULL_MODULE_NAME\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n plutil -replace CFBundleIdentifier -string \"$FRAMEWORK_BUNDLE_ID\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/Info.plist\"\n fi\n \n echo \"Installing binary for $FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" \n mv \"$FULL_EXT\" \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\"\n # Create a placeholder .fwork file where the .so was\n echo \"$FRAMEWORK_FOLDER/$FULL_MODULE_NAME\" > ${FULL_EXT%.so}.fwork\n # Create a back reference to the .so file location in the framework\n echo \"${RELATIVE_EXT%.so}.fwork\" > \"$CODESIGNING_FOLDER_PATH/$FRAMEWORK_FOLDER/$FULL_MODULE_NAME.origin\" \n}\n\nPYTHON_VER=$(ls -1 \"$CODESIGNING_FOLDER_PATH/python/lib\")\necho \"Install Python $PYTHON_VER standard library extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/python/lib/$PYTHON_VER/lib-dynload\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib python/lib/$PYTHON_VER/lib-dynload/ \"$FULL_EXT\"\ndone\necho \"Install app package extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app_packages\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app_packages/ \"$FULL_EXT\"\ndone\necho \"Install app extension modules...\"\nfind \"$CODESIGNING_FOLDER_PATH/app\" -name \"*.so\" | while read FULL_EXT; do\n install_dylib app/ \"$FULL_EXT\"\ndone\n\n# Clean up dylib template \nrm -f \"$CODESIGNING_FOLDER_PATH/dylib-Info-template.plist\"\necho \"Signing frameworks as $EXPANDED_CODE_SIGN_IDENTITY_NAME ($EXPANDED_CODE_SIGN_IDENTITY)...\"\nfind \"$CODESIGNING_FOLDER_PATH/Frameworks\" -name \"*.framework\" -exec /usr/bin/codesign --force --sign \"$EXPANDED_CODE_SIGN_IDENTITY\" ${OTHER_CODE_SIGN_FLAGS:-} -o runtime --timestamp=none --preserve-metadata=identifier,entitlements,flags --generate-entitlement-der \"{}\" \\; \n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
This folder can contain any Python application code.
|
||||
|
||||
During the build, any binary modules found in this folder will be processed into
|
||||
iOS Framework form.
|
||||
|
||||
When the test suite runs, this folder will be on the PYTHONPATH, and will be the
|
||||
working directory for the test suite.
|
|
@ -0,0 +1,7 @@
|
|||
This folder can be a target for installing any Python dependencies needed by the
|
||||
test suite.
|
||||
|
||||
During the build, any binary modules found in this folder will be processed into
|
||||
iOS Framework form.
|
||||
|
||||
When the test suite runs, this folder will be on the PYTHONPATH.
|
|
@ -41,8 +41,18 @@
|
|||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>MainModule</key>
|
||||
<string>ios</string>
|
||||
<key>TestArgs</key>
|
||||
<array>
|
||||
<string>test</string> <!-- Invoke "python -m test" -->
|
||||
<string>-uall</string> <!-- Enable all resources -->
|
||||
<string>--single-process</string> <!-- always run all tests sequentially in a single process -->
|
||||
<string>--rerun</string> <!-- Re-run failed tests in verbose mode -->
|
||||
<string>-W</string> <!-- Display test output on failure -->
|
||||
<!-- To run a subset of tests, add the test names below; e.g.,
|
||||
<string>test_os</string>
|
||||
<string>test_sys</string>
|
||||
-->
|
||||
</array>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
|
|
|
@ -9,30 +9,38 @@
|
|||
|
||||
|
||||
- (void)testPython {
|
||||
// Arguments to pass into the test suite runner.
|
||||
// argv[0] must identify the process; any subsequent arg
|
||||
// will be handled as if it were an argument to `python -m test`
|
||||
const char *argv[] = {
|
||||
"iOSTestbed", // argv[0] is the process that is running.
|
||||
"-uall", // Enable all resources
|
||||
"--single-process", // always run all tests sequentially in a single process
|
||||
"--rerun", // Re-run failed tests in verbose mode
|
||||
"-W", // Display test output on failure
|
||||
// To run a subset of tests, add the test names below; e.g.,
|
||||
// "test_os",
|
||||
// "test_sys",
|
||||
};
|
||||
|
||||
// Start a Python interpreter.
|
||||
const char **argv;
|
||||
int exit_code;
|
||||
int failed;
|
||||
PyStatus status;
|
||||
PyPreConfig preconfig;
|
||||
PyConfig config;
|
||||
PyObject *sys_module;
|
||||
PyObject *sys_path_attr;
|
||||
NSArray *test_args;
|
||||
NSString *python_home;
|
||||
NSString *path;
|
||||
wchar_t *wtmp_str;
|
||||
|
||||
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
|
||||
|
||||
// Disable all color, as the Xcode log can't display color
|
||||
setenv("NO_COLOR", "1", true);
|
||||
|
||||
// Arguments to pass into the test suite runner.
|
||||
// argv[0] must identify the process; any subsequent arg
|
||||
// will be handled as if it were an argument to `python -m test`
|
||||
test_args = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"TestArgs"];
|
||||
if (test_args == NULL) {
|
||||
NSLog(@"Unable to identify test arguments.");
|
||||
}
|
||||
argv = malloc(sizeof(char *) * ([test_args count] + 1));
|
||||
argv[0] = "iOSTestbed";
|
||||
for (int i = 1; i < [test_args count]; i++) {
|
||||
argv[i] = [[test_args objectAtIndex:i] UTF8String];
|
||||
}
|
||||
NSLog(@"Test command: %@", test_args);
|
||||
|
||||
// Generate an isolated Python configuration.
|
||||
NSLog(@"Configuring isolated Python...");
|
||||
PyPreConfig_InitIsolatedConfig(&preconfig);
|
||||
|
@ -50,7 +58,7 @@
|
|||
// Ensure that signal handlers are installed
|
||||
config.install_signal_handlers = 1;
|
||||
// Run the test module.
|
||||
config.run_module = Py_DecodeLocale("test", NULL);
|
||||
config.run_module = Py_DecodeLocale([[test_args objectAtIndex:0] UTF8String], NULL);
|
||||
// For debugging - enable verbose mode.
|
||||
// config.verbose = 1;
|
||||
|
||||
|
@ -83,7 +91,7 @@
|
|||
}
|
||||
|
||||
NSLog(@"Configure argc/argv...");
|
||||
status = PyConfig_SetBytesArgv(&config, sizeof(argv) / sizeof(char *), (char**) argv);
|
||||
status = PyConfig_SetBytesArgv(&config, [test_args count], (char**) argv);
|
||||
if (PyStatus_Exception(status)) {
|
||||
XCTFail(@"Unable to configure argc/argv: %s", status.err_msg);
|
||||
PyConfig_Clear(&config);
|
||||
|
@ -98,11 +106,47 @@
|
|||
return;
|
||||
}
|
||||
|
||||
sys_module = PyImport_ImportModule("sys");
|
||||
if (sys_module == NULL) {
|
||||
XCTFail(@"Could not import sys module");
|
||||
return;
|
||||
}
|
||||
|
||||
sys_path_attr = PyObject_GetAttrString(sys_module, "path");
|
||||
if (sys_path_attr == NULL) {
|
||||
XCTFail(@"Could not access sys.path");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the app packages path
|
||||
path = [NSString stringWithFormat:@"%@/app_packages", resourcePath, nil];
|
||||
NSLog(@"App packages path: %@", path);
|
||||
wtmp_str = Py_DecodeLocale([path UTF8String], NULL);
|
||||
failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String]));
|
||||
if (failed) {
|
||||
XCTFail(@"Unable to add app packages to sys.path");
|
||||
return;
|
||||
}
|
||||
PyMem_RawFree(wtmp_str);
|
||||
|
||||
path = [NSString stringWithFormat:@"%@/app", resourcePath, nil];
|
||||
NSLog(@"App path: %@", path);
|
||||
wtmp_str = Py_DecodeLocale([path UTF8String], NULL);
|
||||
failed = PyList_Insert(sys_path_attr, 0, PyUnicode_FromString([path UTF8String]));
|
||||
if (failed) {
|
||||
XCTFail(@"Unable to add app to sys.path");
|
||||
return;
|
||||
}
|
||||
PyMem_RawFree(wtmp_str);
|
||||
|
||||
// Ensure the working directory is the app folder.
|
||||
chdir([path UTF8String]);
|
||||
|
||||
// Start the test suite. Print a separator to differentiate Python startup logs from app logs
|
||||
NSLog(@"---------------------------------------------------------------------------");
|
||||
|
||||
exit_code = Py_RunMain();
|
||||
XCTAssertEqual(exit_code, 0, @"Python test suite did not pass");
|
||||
XCTAssertEqual(exit_code, 0, @"Test suite did not pass");
|
||||
|
||||
NSLog(@"---------------------------------------------------------------------------");
|
||||
|
||||
|
|
Loading…
Reference in New Issue