The Elegant Chaos Blog
News, thoughts, and other ramblings from the world of Elegant Chaos.

March 12, 2013

Hot on the heels of 1.3b1 came 1.3b2, which fixed the support for OS 10.6 that had accidentally got broken!

And if that weren’t enough, hot on the heels of that came 1.3b3, which adds support for a new service command: “Add To Templates…”.

This command adds a copy of the selected files into Neu’s templates folder.

As always, you can find this version on the beta software page.

The software update mechanism within Neu will also grab this new version, if you have the “Update to beta versions when available” box checked. Unfortunately, if you auto-updated to 1.3b1 on OS X 10.6, you’ll have to download it, since 1.3b1 is totally broken on 10.6.

Read more

Things have been quite quiet on the Neu front, but under the surface we have been doing a bit of paddling!

Available now on the beta software page is a new beta - 1.3b1 - which adds some new features.

Folder Expansion

The first of these is the ability to use folders properly as templates, and to have variables expanded in the contents of the folders (including in their filenames).

Applescript

The second big change is applescript support. You can now use Neu via, applescript, doing this sort of thing:

tell application id "com.elegantchaos.neu"
    make new document
        with properties {template:template "MyClass", extension:"hpp", replacing:yes, revealing:false, opening:true} 
        at POSIX file "/Users/sam/Desktop/"
end tell

Currently there’s also an alternative syntax, which looks like this:

tell application id "com.elegantchaos.neu"
    make new document
        duplicate template (template "MyClass") at destination name "Blah" with revealing, opening and replacing
end tell

We’re interested in hearing if people have a preference.

Finder Menu

Finally, we’ve added experimental support for a proper menu in the Finder.

This is classed as “experimental” because Apple doesn’t officially support allowing apps to add Finder menus, so it involves installing a small hack to the Finder.

You can enable this support in the new “Finder” preferences pane. Note that you may have to log out and in again before the Neu menu appears. You will also need to have Neu running.

Feedback Required

To some extent all of these features are still experimental, and there may be bugs in them.

We’re really keen to hear what you think about the features, and about any problems you encounter.

Read more

In a blog post last month I introduced Mock Server, an Objective-C toolkit to help with the unit testing of networking code.

Basically, Mock Server works by running on a local network port, pretending to be some sort of server (ftp, http, you name it). You point your networking code at it in your unit tests, thus avoiding the need to be connected to the network, and ensuring that you always have an available, predictable server to test against.

Since then I’ve been using MockServer a fair bit, and have expanded it’s capabilities somewhat, with hopefully more to come.

To help everyone else use it, in this post, I thought I’d go into a bit more detail about how exactly to set up a unit test to talk to Mock Server.

Using KMSTestCase

MockServer can be used in a number of different ways, but to simlpify things I’ve tried to make it easy to use it in what I think is the most likely scenario: as part of a suite of unit tests using the standard SenTestKit framework that ships with Xcode.

To do this, you need to make just two things:

  • a responses file
  • a unit test class that inherits from KMSTestCase.

Response file examples for ftp, http and webdav can be found in the MockServer project.

In a future post I’ll talk about how to set up your own response file to mimic a custom protocol, or to mimic something like an FTP server behaving in a very specific way (great for testing those obscure bugs that only show up on really weird FTP servers).

For now though, I’ll just use the example FTP response file, which provides responses for most of the common commands than an FTP client is likely to throw at an FTP server.

Imports

After making a new source file, you first need to import the MockServer headers that you need. You’ll definitely need KMSTestCase.h, and probably also KMSServer.h:

#import "KMSTestCase.h"
#import "KMSServer.h"

A Class And A Test

Next, you want to declare your unit test class, inheriting from KMSTestCase:

@interface KMSCollectionTests : KMSTestCase

@end

Set Up Server

As a simple example, we’ll just implement a single test, which performs an FTP request.

@implementation KMSCollectionTests

- (void)testFTP
{

First, we need to set up a new mock server instance, and start it running.

Luckily KMSTestCase has a method that does all the hard work for us: [KMSTestCase setupServerWithResponseFileNamed:].

This method takes the name of a response file, and sets up a MockServer instance, running on a dynamically allocated port, and using the “default” set of responses from the response file, which should be added as a resource to your unit test bundle.

We have to check the result of this call (which is a BOOL), in case anything goes wrong with the setup.

	// setup a server object, using the ftp: scheme and taking
	// the "default" set of responses from the "ftp.json" file.
	if ([self setupServerWithResponseFileNamed:@"ftp"])
	{

Because we’re going to fake a download, the next thing we have to do is to give the server some data to return to us when it pretends to respond to the download request.

This is the way MockServer works generally. It doesn’t actually perform as a server at all in any real sense; instead, you give it the thing you want it to return, then run the real client code that sends a request to it, then check that the client code got back the thing that you asked MockServer to return.

So, to set some data to be returned, we use the [KMSServer data] property:

        // set up the data that the server will return
        NSString* testData = @"This is some test data";
        self.server.data = [testData dataUsingEncoding:NSUTF8StringEncoding];

Make A Request

Next, we need to make an NSURLRequest object that we’re going to use in our client code to do our actual FTP request.

It’s important to note here that we don’t have to use NSURLRequest, or NSURLConnection, or any particular client-side technology. We can formulate the network request in whatever way we like, and can use any sort of third-party library (or even low level socket code) to perform the client part of the transaction (which is the part that we’re trying to test).

However, I’m using NSURLConnection here for simplicity, so I need an NSURLRequest, which means that I first need an NSURL object, including the address of the server and the path of the file we want to download.

We know that the server is running locally (as that’s the point of MockServer). However, because the server is using a dynamically allocated port, we can’t just hard-code the URL into the test; we have to figure it out on the fly. We do this by grabbing the port number back from the server, grabbing the scheme from the responses collection, and building up a URL from that information.

Luckily KMSTestCase provides a helper method to simplify this.

In this case we’re going to pretend to download a file called “test.txt” from the root of the FTP server:

		// setup an ftp request
		NSURL* url = [self URLForPath:@"test.txt"];
		NSURLRequest* request = [NSURLRequest requestWithURL:url];

Perform The Download

Next, we want to actually peform the download.

Because this is a unit test, the first instinct might be to do this synchronously. After all, a unit test is just one method, and we can’t perform a test on what we got back until we’ve actually got it. For an asynchronous case we’d have to give back control to the main loop for a while until we somehow know that the request is done, and that all sounds a bit complicated. There’s a danger that the unit test would just finish before anything had happened.

To test in real-world conditions though, we really do want to do things asynchronously. A synchronous test at this point really isn’t a good idea, since (hopefully) we aren’t going to be writing synchronous downloads in our apps.

Luckily, MockServer and KMSTestCase have this covered. KMSTestCase has a two methods: runUntilPaused, and pause.

The first of these hands back control to the main run loop, and pumps it until something calls pause. If we arrange to call this in our completion handler, we can happily set up an asynchronous request, wait for it to do it’s thing, and then return control to our unit test so that we can check the results.

Here’s the code:

	__block NSString* string = nil;
	
	[NSURLConnection sendAsynchronousRequest:request 
		queue:[NSOperationQueue currentQueue] 
		completionHandler:
			^(NSURLResponse* response, NSData* data, NSError* error)
			 {
			     if (error)
			     {
			         NSLog(@"got error %@", error);
			     }
			     else
			     {
			         string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
			     }
		 
			     [self pause];
			 }];
	 
	[self runUntilPaused];

Test The Results

Finally, we can check that we got back whatever it is that we were expecting to get back.

In this case we should receive the test string that we asked MockServer to return to us:

    STAssertEqualObjects(string, testData, @"got the wrong response: %@", string);

And that, as they say, is that.

By using KMSTestCase, most of the setup work and all of the cleanup work is done for us, and we can just concentrate on the code that performs whatever network operation it is that we’re trying to test, and then checks the results to verify that they are ok.

Clearly there’s more going on under the hood of KMSTestCase, but most of it is just housekeeping and for a lot of situations it should be sufficient for your needs.

You can obviously use KMSServer directly if you need to do something more complicated - examining the source code of KMSTestCase should give you everything you need.

For More Info

You can find full source code and documentation for MockServer on github.

Read more

Once or twice I’ve had the situation where I ran some unit tests in Xcode, and they seem to run incredibly fast. Too fast.

Spoiler: they didn’t

Read more

January 07, 2013

Well, 2012 turned out to be quite a busy year for me on the contracting front.

Some of the main highlights:

  • Most of the coding for the iOS game Bag It and Bin It was actually done in 2011, but it finally came out at the end of the year, and I did a bit more work on an update during 2012. Written using Cocos2D, this was great to work on, and it was nice to get the chance to do a little game from scratch.

  • On the other end of the spectrum, I spent some time in the summer wrapping up a C++ library and turning it into an Objective-C iOS framework for another client. This couldn’t be more different from writing a game, but designing code to be used by other programmers is also something that I enjoy, so this was also a nice little job.

  • In the early spring, I started doing some MacOS X work for Karelia, which continued through the year (and hopefully on into 2013!). This work has covered all sorts of territory, from unit testing their libraries to some networking and user interface work for their main product Sandvox. They like to keep me on my toes…

  • Finally, in the autumn I also started working on the SVG import/export component of Bohemian Coding’s excellent MacOS X vector graphics app Sketch. Once again, a very different area to work in, with a whole new set of challenges, but definitely fun. The SVG standard is a bit of a monster, and making software that can understand everything in the standard would be a monumental task, so we took a pragmatic approach, trying to identify the areas that are most often used and tackle them first. It feels a bit like painting the Forth Bridge, in that by the time we’ve done everything there will probably be a new version of the standard out :)

So a pretty varied year, and those are just the highlights.

The one down side of this is that the time I’ve had to spend on Neu, Ambientweet, and some other as-yet-unreleased projects of my own, has been somewhat limited. I really hope that I can rectify that in 2013, and give them a bit more love.

The only problem is that the work for Karelia and Bohemian is continuing, and I’m enjoying it too! If anyone invents a time machine, please let me know…

Read more