The Elegant Chaos Blog

It’s fairly old news by now, but I’ve recently had a slight change of direction and taken a permanent role at Bohemian Coding. As a result, it is past time for me to mention it here, and to address this important question:

What does this mean for Elegant Chaos and our products?

Ambientweet

I’ve made the decision to effectively retire Ambientweet, and from today onwards it will be free (in fact, I changed the price a couple of days back). Ever since Twitter started discouraging third-party clients, it has been hard to justify continuing to work on it, and some recent API changes at Twitter’s end seem to have broken it completely. I will endeavour to fix it again, if I can do so without expending an insane amount of effort, but after that, it isn’t likely to receive much love.

I actually had a lot of plans for Ambientweet, and had also been hoping to do a version for app.net. Perhaps one day this will still happen, but it’s not likely to be soon.

Neu

On the other hand, Neu is still very much alive, and there are some new features on the way.

However, to streamline things slightly, I am going to move directly to releasing a 2.0 version, which will require OS X 10.7 or greater (or possibly even 10.8). This will allow me to tidy up and modernise the code, which will have some internal benefits as well as speeding the development process.

Additionally, I may well remove Neu from the Mac App store. Neu is in that category of helper applications which have a difficult time complying with Apple’s rules for the store.

It doesn’t do anything particularly complicated, but it integrates quite tightly with the Finder, and in the future I have some plans to integrate it further - both with the Finder and with other apps. Doing this whilst staying correctly sandboxed is quite difficult, and is deflecting time from the development of new features.

Early on, I made the decision that I didn’t want to release a new version on my website until I’d had the same version approved for release on the store, so that store customers didn’t feel that they were lagging behind.

Unfortunately, this has resulted in no new official version of Neu being released since Apple required sandboxing. Given that I now have a limited amount of time to work on it, I would prefer to move it forward by adding features, rather than by working around sandboxing problems.

The most pragmatic way to do this is to remove it from the store, for now at least. This is likely to happen when I release 2.0.

What does this mean if you already own a license for 1.0? The good news is that I intend Neu 2.0 to be a free update. If you bought Neu through my store, it will continue to accept your existing license file.

Unfortunately there is no way for a developer to obtain a list of the people that bought a product on the app store. I have some existing technology in Neu which allows non-app store versions to spot that you once ran a licensed app store copy, and to accept this as proof of purchase. This should continue to work for Neu 2.0, but it does have the slight annoyance that would will have to have downloaded Neu from the store and run it at least once on your machine. I will continue to explore other options - possibly I may be able to add something that allows you to request a new license file when the app spots that you have an app store license on the machine. More on this later, no doubt…

Other Products

I have various other products that I was planning on. Most of these are things that I want for myself, so they may well happen, but unless someone invents a time machine, they will take a while…

## Welcome To The Future

So… that’s my news. Hopefully it’s all good. Do continue to keep the feature requests and bug reports coming in (especially for Neu). Please bear with me if I take a while to respond, but I am definitely still here and beavering away!

more...

May 28, 2013

Fresh off the press today comes Neu 1.3b4.

The main change in this version is an additional template substitution:

{% clipboard %}

When you create a new document from a template, this substitution will be replaced by any text that was on the clipboard.

This allows you to easily copy some text, fire up Neu and select a template, and have it create a new document containing your selection.

You can download 1.3b4 from the beta software page.

more...

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.

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.

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.

more...