Ad

Our DNA is written in Swift
Jump

Radar Double Feature: Xcode Crash and Unit Test with UIFont

While working on unit tests for DTCoreText I stumbled upon an issue that has two aspects, one for how you set up unit tests and the other is a crash that is new as of Xcode 5.0.1.

Consider this simple unit test:

- (void)testExample
{
   UIFont *font = [UIFont fontWithName:@"Helvetica"
                                  size:20];
   STAssertNotNil(font, @"font should not be nil");
}

I consider it esoteric knowledge that you have to have a test host in place if you want to instantiate a UIFont inside a unit test. This is why I am filing it as a bug report, since it “might lead to developer confusion”.

In the least this should be documented somewhere. You will also find a description of a minimalistic workaround for both issues.

Instantiating a UIFont in Unit Test should not require a test host

Filed as rdar://15166866. The mentioned sample app named “UIFontDemo” and can be found on my Radar Samples GitHub repo.

Summary

If you have a unit test that needs to create a UIFont instance there is a HALT event if there is no test host/bundle loader set.

Steps to Reproduce

  1. Load the provided sample app
  2. Select “UnitTest without Host”
  3. CMD+U to run the unit test

Expected Results

A UIFont instance should be able to be created without requiring an application bundle. Or if there is an implementation necessity to have such a bundle then there should be a more useful action than just a HALT.

Actual Results

Execution of the unit test is aborted with an EXC_BREAKPOINT. see Screenshot.

Notes

Due tue a bug in Xcode 5.0.1 (which crashes in this example) you might have to use a different (earlier) version of Xcode to reproduce the effect with this project.

Screen Shot 2013-10-07 at 7.45.18 PM


Xcode 5 crashes trying to run unit test without bundle loader

Filed as rdar://15167061, same sample app as above.

Summary

If you run a unit test which tries to instantiate a UIFont this crashes Xcode.

Steps to Reproduce

1. Load the sample app provided for rdar://15166866 in Xcode 5
2. Select the “Unit Test without Host”
3. Choose iPhone Retina 4″ (32-bit)
4. CMD+U

Expected Results

The unit test should run

Actual Results

Xcode crashes with an exception. I added screen shot and crash report.

Version

5.0.1 (5A2034a)

Screen Shot 2013-10-07 at 7.59.53 PM


Workaround

Experimentation has shown that you can work around these issues by providing a minimalistic bundle loader for your unit tests. Normally such a bundle loader is a fully fledged app which provides a context into which your Application Unit Tests are injected into. You can use the same mechanism, but with a more or less empty application to allow the unit test to work.

For such a minimum dummy bundle loader you need:

  • An info.plist
  • A main.m which creates a non-nil generic app delegate object
  • A blank 4″ launch image to avoid a build warning that it is missing

I’ve implemented this workaround on DTCoreText like so:

Dummy Bundle Loader Setup

You have to create an empty app delegate because if you pass nil there then the system tries some weird things to get it from your info.plist.

int main(int argc, char *argv[])
{
   @autoreleasepool
   {
      // app delegate is inconsequential for unit testing, but it cannot be nil
      return UIApplicationMain(argc, argv, nil, NSStringFromClass([NSObject class]));
   }
}

You need the UIApplicationMain – which is the app’s main run loop – because without it the unit tests won’t finish properly.

I created this BundleLoaderDummy target by adding an empty application and removing all pieces that were not consequential for the unit test. This included the PCH file. The 4″ launch image I am borrowing from the demo app.

The dummy bundle loader target needs to be a dependency of the unit test so that it is built before it.

Dummy is dependency

In the Linking build settings of the iOS unit test you set up the bundle loader with $(BUILT_PRODUCTS_DIR)/BundleLoaderDummy.app/BundleLoaderDummy. This makes it use the current path for each build config.

Setup bundle loader

 

You also need to set the Test Host in the Unit Testing section, for this we reference the bundle loader setting via $(BUNDLE_LOADER)

Setting the Test Host

Having it set up thusly you will find that instantiating a UIFont from within your unit test does no longer cause exception described above.

You can see this in action in a successful build on Travis-CI.

Conclusion

Xcode should definitely never crash, even if UIKit operations – for some esoteric reason – might require a Test Host. And if there is indeed a reason for the latter then it should be documented somewhere or at least produce a more descriptive error than just HALT. Anything angers and confuses developers for quite some time now.


Categories: Bug Reports

7 Comments »