I’d like to make it to NSCoder Night tonight, but I won’t be able to as I’ll be looking at a new place. Wish me luck, and have fun everybody!
Build LLVM and clang!
I’ve talked about the LLVM Compiler Infrastructure in the past, but what I haven’t talked about yet is just how easy and quickly you can build it on your own Mac running Leopard! This is a great way to get into hacking on compiler lexical analyzers and parsers, code generators, optimizers, and so on.
What’s more, you can build both LLVM and the new C front-end clang
very easily and in five to ten minutes.
First, create a work area to check them out into, wherever you normally create your projects.
[~]% cd /Projects [/Projects]% mkdir LLVM [/Projects]% cd LLVM [/Projects/LLVM]%
Then check out LLVM itself and clang
from the LLVM Subversion repository.
[/Projects/LLVM]% svn checkout http://llvm.org/svn/llvm-project/llvm/trunk llvm [/Projects/LLVM]% cd llvm/tools [/Projects/LLVM/llvm/tools]% svn checkout http://llvm.org/svn/llvm-project/cfe/trunk clang [/Projects/LLVM/llvm/tools]% cd ../.. [/Projects/LLVM]%
Then edit the PARALLEL_DIRS
definition in llvm/tools/Makefile
to tell it about clang
. Just add clang
onto the end, like this:
PARALLEL_DIRS := llvm-config \ opt llvm-as llvm-dis \ llc llvm-ranlib llvm-ar llvm-nm \ llvm-ld llvm-prof llvm-link \ lli gccas gccld llvm-extract llvm-db \ bugpoint llvm-bcanalyzer llvm-stub llvmc2 \ clang
Now create a directory to build into, next to your llvm
directory, and change into it.
[/Projects/LLVM]% mkdir build [/Projects/LLVM]% cd build [/Projects/LLVM/build]%
This is where you’ll actually run configure
. This will ensure your source tree isn’t polluted with build products, and that everything stays self-contained while you hack.
[/Projects/LLVM/build]% ../llvm/configure --enable-targets=host-only # lots of logging [/Projects/LLVM/build]%
You’ll note that above I passed an argument to configure
. This ensures that LLVM is only built to target the architecture I’m running on, to speed up the build process; this is generally fine for simple front-end development.
Now, to build LLVM as well as clang
all I have to do is invoke make
. LLVM is set up to correctly do parallel builds, so I’ll pass the number of CPUs I have in my machine via make -j 4
.
[/Projects/LLVM/build]% make -j 4 # lots of logging [/Projects/LLVM/build]%
That’s it! LLVM is now (hopefully) successfully built. All of the pieces are in the build
directory under Debug/bin
and Debug/lib
and so on; see the LLVM web site for details about what the various components are.
CocoaHeads Silicon Valley at Apple on Thursday, Apple 17, 2008
The next CocoaHeads Silicon Valley meeting will be on Thursday, April 17, 2008 — that’s tonight! — at 7:30 in the De Anza 3 auditorium at Apple. That’s just inside the south side of De Anza 3, right across Mariani Avenue from Apple’s Infinite Loop campus in Cupertino. See the web site for directions.
This month’s presentation is all about designing and implementing your human interface. User experience and human interface design are critical for Mac OS X software to get right. To that end, there’s even going to be a UI makeover as Scott describes in his post on the meeting!
Thanks a ton to Scott Stevenson, Steve Zyszkiewicz, Michael Jurewitz and Joar Wingfors for organizing!
In general, at a CocoaHeads meeting we do some introductions, have a presentation including Q&A time with the presenter, and then have an open Q&A and demo-your-cool-app period. After the meeting there’s more independent mingling and discussion until it’s time to go at 9:30. Often a subset of the meeting moves to BJ’s Brewhouse in Cupertino, which is right in front of the Apple Infinite Loop campus on De Anza Boulevard.
NSCoder Night Silicon Valley (and hopefully elsewhere)
I’ve been toying with this idea for a while now, and now that Leopard is coming out, it’s finally time to make it happen! I call it NSCoder Night! (Major thanks to Scott Stevenson for coming up with the name!) Here’s the inaugural announcement:
> NSCoder Night Silicon Valley will be having its inaugural get-together between 7:00 and 9:00 PM on October 30, 2007 — next Tuesday night — at Orchard Valley Coffee in Campbell, CA. From then on, we’ll be getting together there every Tuesday!
>
> What’s the point? To get together with other folks who are also writing Cocoa code for Mac OS X, of course! There’s no formal structure, which is why I described it as a “get-together” rather than a meeting. There’s no need to RSVP, either; just show up if you like, or don’t if you can’t make it. Just bring your brain, laptop, and enthusiasm, pick up one of the tasty drinks available at Orchard Valley Coffee, and have a great time!
>
> Why did I pick Orchard Valley Coffee for the inaugural Silicon Valley get-together? I first went there one afternoon during a little vacation I took after WWDC, and I found it a nice, large, relaxing place. It has wireless connectivity, reasonable hours, plenty of room, and great drinks, and it’s also quite accessible since it’s right in the heart of downtown Campbell.
>
> So come on down, say hi, and see just how much fun it is to hang out and hack for a couple hours!
Want to hold your own NSCoder Night? Just let me know — here, on the NSCoder Night web site, or via email — and I’ll post the announcement. Hopefully we’ll have get-togethers across the world, even in places where there’s not yet a CocoaHeads chapter.
What’s the difference between NSCoder Night and CocoaHeads? CocoaHeads is a once-a-month user group meeting, typically with presentations and demos. NSCoder Night is a weekly get-together, a time to talk to your fellow hackers, to get help with issues you’re bumping into, to show off cool tricks, and to actually have some time specifically set aside for your side projects.
LLVM Developers’ Meeting 2007-05
The LLVM Compiler Infrastructure is a great technology that came out of the computer science research community and can be used to develop extensible compiler platforms. Among other things, it provides a platform-independent assembly and object code (the “low level virtual machine” that its name is taken from), and a great object-oriented compilation, linking, optimization and code-generation infrastructure that you can use to efficiently target real hardware. The main idea is that LLVM provides a comprehensive back-end that you can easily build a front-end to target.
There’s a huge amount of material available on the LLVM web site, including the LLVM Assembly Language Reference Manual and LLVM Programmer’s Manual, a wide variety of papers on LLVM, and a great walkthrough of the creation of Stacker, a Forth front-end that targets LLVM. It shows how the budding language creator might leverage the tools available as part of the LLVM infrastructure. I fully expect that in time, “little languages” will have no excuse not to be JIT compiled simply because targeting LLVM is actually easier than writing your own interpreter or bytecode engine! Just walk your AST and generate naïve LLVM code for what you encounter, and let the infrastructure handle the rest. (For those who aren’t developer tools weenies, an Abstract Syntax Tree is the internal representation of a program’s structure before it’s turned into instructions to execute.)
A couple months back, the May 2007 LLVM Developers’ Meeting was held at Apple. The proceedings from this meeting — the actual session content, both in slides and in video form — are available online, and I’ve even created an LLVM Developers’ Meeting podcast (including a subscribe-directly-in-iTunes version) for easy viewing. The video may be low bit rate, but it has a 16:9 aspect ratio so you can even pretend it’s HD. (I put together the podcast primarily so I could watch the sessions on my Apple TV, since I couldn’t attend the meeting.)
So if you’re at all interested in compilers, language design or development, optimization, or development platforms in general, you’ll be very well-served by checking out LLVM. It is a seriously cool enabling technology.
Unit testing Cocoa user interfaces: Cocoa Bindings
About a year ago, I wrote about unit testing target-action connections for Cocoa user interfaces. That covers the traditional mechanism by which user interfaces have typically been constructed in Cocoa since the NeXTstep days. However, with the release of Mac OS X 10.3 Panther we’ve had a newer interface technology available — Cocoa bindings — which has presented some interesting application design and testing challenges.
Among other hurdles, to properly use Cocoa bindings in your own applications, you need to ensure that the code you write properly supports key-value coding and key-value observing. However, since the release of Mac OS X 10.4 Tiger, the necessary APIs have been available to easily do test-driven development of your application’s use of Cocoa bindings, following a trust, but verify approach. (It’s also been quite easy from the start to test your support for key-value coding and key-value observing, to ensure that your code meets the necessary prerequisites for supporting bindings. I can write more on this topic in another post if anyone is interested.)
The key to writing unit tests for Cocoa bindings is the -infoForBinding:
method in AppKit’s NSKeyValueBindingCreation informal protocol. Using this simple method, you can interrogate any object that has a binding for all of the information about that binding! It simply returns a dictionary with three keys:
NSObservedObjectKey
, which is the object that the binding is bound to;NSObservedKeyPathKey
, which is the key path that is bound — in Interface Builder terms, this is the controller key path combined with the model key path, with a dot in between them; andNSOptionsKey
, which is a dictionary of additional binding options unique to the binding. These are all of those additional checkboxes and pop-ups in the Interface Builder bindings inspector for setting things like a value transformer.
By specifying what this dictionary should contain for a particular binding, you can describe the binding itself and thus start doing test-driven development of your Cocoa bindings-based user interface. Note that all of the system-supported binding names — as well as the binding option names — are specified in <AppKit/NSKeyValueBinding.h>
and are documented, too!
Let’s take a simple example, like the one in last year’s target-action example, of a window controller whose window has a static text field in it. The field should have its value bound to the name of a person through an object controller for that person. Assume that I’ve already created the test case and set up some internal methods on my window controller to refer to the contents of the window via outlets, and to load the window (without displaying it) in -setUp
just like in the target-action example.
First, to see that my text field has a value binding, I might write something like this:
- (void)testPersonNameFieldHasValueBinding { NSTextField *personNameField = [_windowController personNameField]; NSDictionary *valueBindingInfo = [personNameField infoForBinding:NSValueBinding]; STAssertNotNil(valueBindingInfo, @"The person name field's value should be bound."); }
Of course, this tells us nothing about how the binding should be configured, so it needs some fleshing out…
Let’s check the object and key path for the binding.
- (void)testPersonNameFieldHasValueBinding { NSTextField *personNameField = [_windowController personNameField]; NSDictionary *valueBindingInfo = [personNameField infoForBinding:NSValueBinding]; STAssertNotNil(valueBindingInfo, @"The person name field's value should be bound."); NSObjectController *personController = [_windowController personController]; STAssertEquals([valueBindingInfo objectForKey:NSObservedObjectKey], personController, @"The person name field should be bound to the person controller."); STAssertEqualObjects([valueBindingInfo objectForKey:NSObservedKeyPathKey], @"name", @"The person name field's value should be bound to the 'name' key."); }
Not very exciting, and a little verbose, but it'll easily lead us through what needs to be set up in Interface Builder for this binding to work. If you want to cut down the verbosity, you can of course extract a method to do the basic checking...
- (BOOL)object:(id)object shouldHaveBinding:(NSString *)binding to:(id)boundObject throughKeyPath:(NSString *)keyPath { NSDictionary *info = [object infoForBinding:binding]; return ([info objectForKey:NSObservedObjectKey] == boundObject) && [[info objectForKey:NSObservedKeyPathKey] isEqualToString:keyPath]; } - (void)testPersonNameFieldHasValueBinding { NSTextField *personNameField = [_windowController personNameField]; NSObjectController *personController = [_windowController personController]; STAssertTrue([self object:personNameField shouldHaveBinding:NSValue to:personController throughKeyPath:@"name"], @"Bind person name field's value to the person controller's 'name' key path."); }
If you're writing code that needs, say, a value transformer, it's a simple matter to extend this model to also check that the correct value transformer class name is specified for the NSValueTransformerNameBindingOption
key in the binding options dictionary returned for NSOptionsKey
.
You can even extract these kinds of checks into your own subclass of SenTestCase
that you use as the basis for all of your application test cases. This will let you write very concise specifications for how your user interface should be wired to the rest of the code, that you can use to just walk through Interface Builder and connect things together — as well as use to ensure that you don't break it accidentally by making changes to other items in Interface Builder.
This is the real power of test-driven development when combined with Cocoa: Because you can trust that the framework will do the right thing as long as it's set up right, you simply need to write tests that specify how your application's interface should be set up. You don't need to figure out how to create events manually, push them through the run loop or through the window's -sendEvent:
method, how to deal with showing or not showing the window during tests, or anything like that. Just ensure that your user interface is wired up correctly and Cocoa will take care of the rest.
It’s not the dynamic dispatch!
Joel has decided to blame the use of dynamic dispatch for Ruby’s speed issues:
Without knowing much about the implementation of Ruby, I would guess that the biggest issue is around late binding and especially duck typing, which prevents type inference or strong typing, which means that function calls will always be slow because you can never get something compiled down to the point where a function call is just a single CALL instruction (on x86)… you always have to be exploring the object, possibly even scanning a hash table to find the function you want to call.
In other words, “it’s always going to be slow because if it’s not strongly-typed it can’t be statically compiled to the most minimal possible instruction sequence!” Which is, simply put, a bullshit argument even if you ignore the fact that he said right up front it was bullshit.
There’s a lot that you can do to optimize dynamic dispatch. If you don’t believe me, take a look at the implementation of the Apple/NeXT Objective-C runtime, objc4
. Go ahead, it’s Open Source. (The link is for the version that’s part of Mac OS X 10.4.7 for Intel processors.) It implements full Smalltalk-style dynamic dispatch — the same type of dynamic dispatch that Ruby uses. And what’s more, Ridiculous Fish also wrote a great article on how dynamic dispatch is implemented in the objc_msgSend()
primitive — with particular attention focused on its performance characteristics!
No, it’s not message-based dynamic dispatch or “duck typing” (runtime polymorphism) that makes Ruby slow. It’s the fact that Ruby is a single-pass interpreted language. It’s not compiled to bytecode. It’s not compiled to native code. It’s scanned, parsed, and then immediately executed.
Imagine if your C compiler, or your Fortran compiler, or your Java compiler — or your Visual Basic compiler, for that matter — had to be invoked every time you ran your program. Imagine how slow that would be! That’s essentially what Ruby is doing, and that’s why it’s slow. Ruby 2.0 is planned to run on the YARV virtual machine, and there has also been work to compile Ruby code for LLVM. There’s nothing in Ruby’s nature that makes this a particularly difficult problem, especially since all of the issues of efficiently compiling dynamic languages with reflection and self-modification features were solved by Lisp in the 1960s and Smalltalk in the 1970s.
Incidentally, this is why I react so vehemently when people talk about “Lisp interpreters.” Lisp is almost never interpreted, specifically to avoid these types of performance issues. At the least most Lisp systems compile to a custom bytecode and then use an optimized bytecode engine to execute that. That way they can eliminate the scanning and parsing overhead — yes, it does exist for Lisp, because contrary to what Lispers may say, the language does have syntax — while still stay portable.
Others have also been piling on, such as Avi Bryant and Obie Fernandez. As Obie points out, Avi knows what he’s talking about. And so do folks who work with Objective-C intimately, day in and day out.
Unit testing Cocoa user interfaces: Target-Action
It’s really great to see that a lot of people are adopting unit testing for their projects and dramatically improving their quality. Test-driven development and agile development methodologies built around it are really taking off. However, a lot of people still feel that their user interface is difficult to test through code, and either requires a capture-playback tool or requires a different design approach based heavily on interfaces/protocols to get right.
In last year’s post Trust, but verify. I tried to dispel some of the mystery of testing your application’s user interface when using the Cocoa frameworks. However, I’ve still had a lot of (entirely well-justified!) requests for examples of how to put it into practice. So here’s a simple example of what I’d do to write a unit test for a button in a window that’s supposed to perform some action.
First, when implementing my window, I’d follow the standard Cocoa pattern of having a custom NSWindowController
subclass to manage my window. This window controller will have an outlet connected to each of the views in the window, and will also wind up with a private accessor method — used only within the class and any subclasses, and in testing — for getting the value of each of its outlets. This design flows naturally from the test which I would write to specify that the window should contain a button. First, here’s the skeleton into which I’d put tests:
// TestMyWindow.h
#import <SenTestingKit/SenTestingKit.h>
@class MyWindowController;
@interface TestMyWindow : SenTestCase {
MyWindowController *_windowController;
NSWindow *_window;
}
@end
// TestMyWindow.m
#import "TestMyWindow.h"
#import "MyWindowController_Private.h"
@implementation TestMyWindow
- (void)setUp {
// MyWindowController knows its nib name and
// invokes -initWithWindowNibName: in -init
_windowController = [[MyWindowController alloc] init];
// Load the window, but don't show it.
_window = [_windowController window];
}
- (void)tearDown {
[_windowController release];
_window = nil; // owned by _windowController
}
@end
That’s the infrastructure into which I’d put my other test methods for this window. For example, I’ll want to specify the nib name for the window controller and ensure that it actually knows its window:
- (void)testNibName {
STAssertEqualObjects([_windowController windowNibName], @"MyWindow",
@"The nib for this window should be MyWindow.nib");
}
- (void)testWindowLoading {
STAssertNotNil(_window,
@"The window should be connected to the window controller.");
}
Now let’s check that I have a “Do Something” button in the window, and that it sends an action directly to the window controller.
- (void)testDoSomethingButton {
// _doSomethingButton is a private method that returns the button
// conected to the doSomethingButton outlet
NSButton *doSomethingButton = [_windowController _doSomethingButton];
STAssertNotNil(doSomethingButton,
@"The window should have a 'Do something' button.");
STAssertEqualObjects([doSomethingButton title], @"Do Something",
@"The button should be titled accordingly.");
STAssertEquals([doSomethingButton action], @selector(doSomething:),
@"The button should send -doSomething: to its target.");
STAssertEquals([doSomethingButton target], _windowController,
@"The button should send its action to the window controller.");
}
You’ll notice something I’m not doing in the above: I’m not simulating interaction with the interface. This is the core of the trust, but verify approach to unit testing of your user interface.
I can trust that as long as I verify everything is hooked up properly that Cocoa will cause the button to send its action message to its target — whether it’s a specific object or, if the target is nil
, the responder chain — whenever the button is clicked while it’s enabled and not hidden. I don’t need to simulate a user event, and I don’t even need to display the interface while running the unit tests. All I need to do is inspect, through code, that everything is wired up correctly.
Note that I can do way more than the above in testing my interface design, too. For example, I can ensure that the control layout is correct according to what my interface designer has specified, by checking bounding rectangles for example. But testing only the functionality of my interface has significant advantages, too. For example, it doesn’t matter if I wind up using a custom kind of button to achieve exactly the kind of look and feel or behavior I need. It doesn’t matter if I wind up changing the layout in response to feedback. No matter what I do, I’ll know that functionality won’t accidentally break while I’m messing around in Interface Builder — even if I completely rip out my interface and replace it with a new one!
This approach can also be used for testing Cocoa bindings using the -infoForBinding:
method that was introduced in Mac OS X 10.4 Tiger. I hope to write up a post soon on how to approach Cocoa bindings using these same techniques, but it should be fairly straightforward given the above and the above documentation.
Update: I’ve struck through the check of the button’s title above, because you may or may not want to do that. For example, if you’re primarily running your unit tests against your development localization, you may want to put it in. But if you want to run your unit tests against a localized build of your application, you’ll probably want to avoid checking a localized title against an English string. A “have your cake and eat it too” strategy might be to keep a variable somewhere in your application that can be used to selectively disable checks of only localized strings.
Update July 7, 2007: I’ve finally written a post, Unit testing Cocoa user interfaces: Cocoa bindings, on how to write tests for Cocoa bindings. Now there’s no excuse for not doing test-driven development of your Cocoa user interfaces!
Steve Yegge describes what’s wrong with Lisp
Steve Yegge, Lisp is Not an Acceptable Lisp:
You’ve all read about the Road to Lisp. I was on it for a little over a year. It’s a great road, very enlightening, blah blah blah, but what they fail to mention is that Lisp isn’t the at the end of it. Lisp is just the last semi-civilized outpost you hit before it turns into a dirt road, one that leads into the godawful swamp most of us spend our programming careers slugging around in. I guarantee you there isn’t one single Lisp programmer out there who uses exclusively Lisp. Instead we spend our time hacking around its inadequacies, often in other languages.
Steve does a very good job of articulating a lot of the things I dislike about Lisp, especially Common Lisp. One interesting thing, though, is that a lot (but not all) of the issues he raises are addressed by Dylan.
One of the more interesting things about Dylan in this context is that, despite not adopting a traditional message-based object system like Smalltalk or Objective-C (or their more static cousins C++ and Java), Dylan does push objects all the way down, but in a functional style. It appears to work pretty well, making it easy to define both nouns and verbs in the combinations a developer might need, and even (through its hygienic macro system) allow developers to extend language syntax too.
Xcode: Debugging Cocoa application unit tests
A couple weeks ago as part of my Unit Testing Series I talked about how to use Xcode to write unit tests for Cocoa frameworks, debug unit tests in Cocoa frameworks, and write unit tests for Cocoa applications. However, I haven’t yet described how to debug your unit tests in Objective-C Cocoa applications. I’ll take care of that tonight.
After you’ve set up unit testing in your Cocoa application, debugging your unit tests is similar to debugging them in a Cocoa framework. All you have to do is adjust the arguments and environment variables your application’s Executable is configured to use in Xcode. You don’t even have to create a new executable.
To start, bring up the Info window for your application’s executable (which is its entry in Xcode’s Executable smart group). In the Arguments tab, add the argument -SenTest All
. This tells the unit testing infrastructure that you want to run all of the unit tests, not just the ones that are built in to the executable. (After all, you don’t have any unit tests in your executable itself.)
Now we’ll need to engage in a little bit of environment variable magic. When you test an application, what you’re really doing is injecting your unit test bundle into the application and telling it to run its tests at its first opportunity. This is accomplished through by telling dyld
to insert a private framework, DevToolsBundleInjection.framework
, into your application on launch, and telling that framework via an environment variable, XCInjectBundle
, the path of the test bundle to inject.
You also have to tell the injection framework itself the full path to the application executable you want to inject the bundle into, via the XCInjectBundleInto
environment variable. This is needed to avoid injecting your test bundle into other executables that are run by the application you’re testing, or that are run as a side-effect of running the application you’re testing. (For example, gdb
generally wants to run applications from within a shell, so that environment variables are expanded in its environment and command-line parameters.)
In the Arguments tab of your application executable, first add an environment variable named DYLD_INSERT_LIBRARIES
. Set its value to the path to the DevToolsBundleInjection.framework/DevToolsBundleInjection
library in the framework of the same name that’s included in the developer tools. Prior to Xcode 2.5, this was in $(SYSTEM_LIBRARY_DIR)/PrivateFrameworks
but as of Xcode 2.5 and Xcode 3.0, it has been moved to $(DEVELOPER_LIBRARY_DIR)/PrivateFrameworks
Then add a second environment variable, XCInjectBundle
. Set its value to $(BUILT_PRODUCTS_DIR)/MyTestBundle.octest
.
Add a third environment variable, XCInjectBundleInto
. Set its value to the full path to your application’s executable — not just the application bundle — e.g. $(BUILT_PRODUCTS_DIR)/MyApplication.app/Contents/MacOS/MyApplication
. This is the debugging equivalent of the Test Host build setting you used to tell Xcode what executable to inject your tests into when running them.
For Xcode 3.0 and later, add a final environment variable, DYLD_FALLBACK_FRAMEWORK_PATH
to your executable. Set its value to $(DEVELOPER_LIBRARY_DIR)/Frameworks
.
Why do you need to do this? In order to support moving and renaming the Developer folder, all of the frameworks within it — including OCUnit — use runpath search paths. This means that the internal name of the framework, including the one copied into your test bundle, will start with @rpath
rather than an absolute path starting with /Developer/Library/Frameworks
. Unfortunately this means that your unit tests won’t find SenTestingKit.framework
without some extra help. That’s what DYLD_FALLBACK_FRAMEWORK_PATH
does: It tells dyld
to try an additional set of directories in place of @rpath
when it can’t be resolved. (More information on runpath-relative install names can be found in the ld(1)
man page.)
Make sure the check marks next to all three of these environment variables — and your -SenTest All
argument, of course — are set.
Troubleshooting note: Troubleshooting note: If this doesn’t work — that is, if your test bundle isn’t found and run — change the executable’s working directory (in the General tab) to Built Products Directory and remove $(BUILT_PRODUCTS_DIR)
above. Generally this is caused by $(BUILT_PRODUCTS_DIR)
not being expanded to a full path, but rather to a partial path relative to your project directory.
Now if you choose Run Executable from the Debug menu, your application should launch, you should see the results of executing your unit tests in the Run Log, and as soon as your unit tests are complete your application should quit!
To debug a failing test, build your tests and set a breakpoint on the line where the failure occurs. Now choose Debug Executable from the Debug menu. As with a Cocoa framework, do not choose Build and Debug from the Build menu. You need to use Debug Executable because your build will fail due to the failing test. Debug Executable will work as long as your executable itself is actually present.
Having done all this, you should be stopped at the breakpoint!
Just as any other time you use OCUnit, instead of -SenTest All
you can specify -SenTest MyTestCaseClassName
to run just the tests in the test case class MyTestCaseClassName
, or -SenTest MyTestCaseClassName/testMethodName
to run just a single test.
Update July 7, 2007: Added the troubleshooting note about removing $(BUILT_PRODUCTS_DIR)
if you get errors about not being able to load the bundle.
Update March 17, 2008: I’ve updated this a bit to handle some changes in the process introduced with Xcode 3.0.
Update September 5, 2008: I’ve updated this again to cover the changes that were made to bundle injection for Xcode 3.1; the change is the introduction of the XCInjectBundleInto
environment variable.