New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SR-1541] List XCTest test methods from swiftpm #5287
Comments
This would be like However, XCTest on OS X supports dynamic test case construction, which makes this non trivial and I am not sure if it can be made to work without changing the API. I will investigate though. |
It looks like OS X XCTest can support this via a small shim which uses the XCTest APIs directly. Here is code which lists the tests in a bundle: @import Foundation;
@import XCTest;
int main(int argc, char **argv) {
@autoreleasepool {
NSString *bundlePath = @(argv[1]);
// Normalize the path.
if (!bundlePath.absolutePath) {
bundlePath = [NSFileManager.defaultManager.currentDirectoryPath stringByAppendingPathComponent:bundlePath];
}
bundlePath = [bundlePath stringByStandardizingPath];
// Load the bundle from the command line.
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
if (![bundle load]) {
NSLog(@"unable to load bundle: %s", argv[1]);
}
// Get the test suite for the given bundle.
printf("{ \"testSpecifiers\": [\n");
XCTestSuite *suite = [XCTestSuite testSuiteForBundlePath:bundlePath];
NSString *suitePrefix = [suite.name stringByDeletingPathExtension];
NSCharacterSet *splitSet = [NSCharacterSet characterSetWithCharactersInString:@" ]:"];
for (XCTestSuite *testCaseSuite in suite.tests) {
for (XCTestCase *test in testCaseSuite.tests) {
// Extract the test name.
NSString *name = [test.name componentsSeparatedByCharactersInSet:splitSet][1];
// Unmangle names for Swift test cases which throw.
if ([name hasSuffix:@"AndReturnError"]) {
name = [name substringWithRange:NSMakeRange(0, name.length - 14)];
}
printf(" \"%s/%s\",\n", [[test class] debugDescription].UTF8String, name.UTF8String);
}
}
printf(" null] }\n");
}
return 0;
} You can build it with: clang -fmodules -o BundleTestFinder BundleTestFinder.m -F $(xcode-select -p)/Platforms/MacOSX.platform/Developer/Library/Frameworks and run it with something like: DYLD_FRAMEWORK_PATH=$(xcode-select -p)/Platforms/MacOSX.platform/Developer/Library/Frameworks LitTestAdaptor/BundleTestFinder ../build/Ninja-ReleaseAssert/swiftpm-macosx-x86_64/debug/SwiftPMTests.xctest We should see how this works with XCTest bundles which generate test cases automatically – that isn't really possible in Swift, but Obj-C can do it |
To use that, we would presumably need to build that into a shim tool (or a special hidden subcommand) – it loads the test bundles into the calling process, so we wouldn't want to actually run that code directly in the same process as the actual SwiftPM logic. |
I gave this tool a try on a couple of test bundles which do dynamic test case generation. It generally worked as expected, and I do think that this is the correct method to use for this purpose. Specifically, I ran the tool on test bundles that used these BDD-style test frameworks:
Here is a modified version of @ddunbar's code that performs the stdout redirection: @import Foundation;
@import XCTest;
id withRedirectedStdout(id (^block)(void)) {
fflush(stdout);
int oldFd = dup(1);
int newFd = open("/dev/null", O_WRONLY);
dup2(newFd, 1);
close(newFd);
id result = block();
fflush(stdout);
dup2(oldFd, 1);
close(oldFd);
return result;
}
int main(int argc, char **argv) {
@autoreleasepool {
NSString *bundlePath = @(argv[1]);
// Normalize the path.
if (!bundlePath.absolutePath) {
bundlePath = [NSFileManager.defaultManager.currentDirectoryPath stringByAppendingPathComponent:bundlePath];
}
bundlePath = [bundlePath stringByStandardizingPath];
XCTestSuite *suite = withRedirectedStdout(^{
// Load the bundle from the command line.
NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
if (![bundle load]) {
NSLog(@"unable to load bundle: %s", argv[1]);
}
// Get the test suite for the given bundle.
return [XCTestSuite testSuiteForBundlePath:bundlePath];
});
printf("{ \"testSpecifiers\": [\n");
NSString *suitePrefix = [suite.name stringByDeletingPathExtension];
NSCharacterSet *splitSet = [NSCharacterSet characterSetWithCharactersInString:@" ]:"];
for (XCTestSuite *testCaseSuite in suite.tests) {
for (XCTestCase *test in testCaseSuite.tests) {
// Extract the test name.
NSString *name = [test.name componentsSeparatedByCharactersInSet:splitSet][1];
// Unmangle names for Swift test cases which throw.
if ([name hasSuffix:@"AndReturnError"]) {
name = [name substringWithRange:NSMakeRange(0, name.length - 14)];
}
printf(" \"%s/%s\",\n", [[test class] debugDescription].UTF8String, name.UTF8String);
}
}
printf(" null] }\n");
}
return 0;
} |
@ddunbar Is there a particular reason why you chose to use |
@briancroom: Thanks for the investigation, I was also curious how well test listing would work with third-party frameworks. I assume the Quick prints to stdout when tests are marked "pending", correct? That's definitely something we can fix. We should create a GitHub issue to track it. |
@modocache Yes, that's right. As it turns out, there is already a relevant issue and languoshong PR on Quick about pending tests: |
Fixed by #397 |
Additional Detail from JIRA
md5: f5ad431ff78b7866f4edf75bcf5d432a
Issue Description:
It'll be a good feature to list all the tests in a package and will make easier to run a specified test/test case if we can just copy from the output and pass as an argument in `swift test -s <specifier>`
It might be easier to achieve on Linux by dumping the allTests property (atleast until SR-710 is resolved)
@ddunbar can you find out about OS X?
The text was updated successfully, but these errors were encountered: