Pages

Saturday, November 14, 2015

Testing Delegate Callbacks Without Mocks

The idea of using protocols in place of mocks was one I first saw at WWDC. More Recently, Eli Perkins explains how to use protocols for an instance of UIApplication.
The basic idea is to declare a protocol that exactly defines what methods I am interested in. When I’m registering for notifications at startup time, I’m not interested in 90% of the features of UIApplication. All I really care about is one method, so I’ll make that explicit with a type:
protocol PushNotificationRegistrar {
    func registerUserNotificationSettings(notificationSettings: UIUserNotificationSettings)
}
Since UIApplication already implements registerUserNotificationSettings(_:), I let the type system know that UIApplications conform to PushNotificationRegistrar with an empty declaration:
extension UIApplication: PushNotificationRegistrar {}
Now for my tests, I’m not trying to mock UIApplication and all of its unrelated baggage. I just need an object (class or struct) that provides the single function registerUserNotificationSettings(_:).
For the full explanation, go read Eli Perkin’s post.
So far so great. The part I haven’t seen discussed is the callback. My custom app delegate has a related delegate call:
optional func application(_ application: UIApplication,
didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings)
Because that is defined in UIApplication, I can’t change the method signature to take anything other than a UIApplication. Testing that code path requires a mock UIApplication.
I find myself in this position with any of the system delegate calls. Core Bluetooth passes back CBPeripherals, and there’s no easy way to create a valid instance in a test environment without a mocking library.
I haven’t found a perfect solution, but the strategy I’ve come to adopt is to make your delegate callback methods as “thin” as possible. To show what I mean, I’ll use a CoreBluetooh delegate call from CBCentralManager:
optional func peripheral(_ peripheral: CBPeripheral,
     didDiscoverServices error: NSError?)
That callback passes you a CBPeripheral. The peripheral contains a services property containing an array of CBService. Each CBService contains a id property of CBUUID. So just to identify the service, you’re three properties deep into system provided objects.
optional func peripheral(_ peripheral: CBPeripheral,
     didDiscoverServices error: NSError?) {
     let services = peripheral.services
     for service in services {
        let id = service.UUID.UUIDString
        switch id {
            case .BatteryInfo:
                break
            case .ManufacterInfo:
                break
            // and on and on
        }
     }
}
The method can grow to inclued enough complex switching that you definetly want to test it.
Here’s the stragegy I came up with:
  1. Extract the required data in as few lines of code as possible.
  2. Redispatch to a helper method that takes only easily-created parameters.
Here’s my example:
// Declared in CBPeripheralDelegate
func peripheral(peripheral: CBPeripheral,
     didDiscoverServices error: NSError?) {
     let UUIDs = peripheral.services.map { $0.UUID }
     self.handleDiscoveredServicesWithUUIDs(UUIDs)
}

func handleDiscoveredServicesWithUUIDs(UUIDs: [CBUUID]) {
    // complicated switch statement here
}
CBUUIDs are easy to create in a test environment, so it will be easy to test my new function handleDiscoveredServicesWithUUIDs(UUIDs: [CBUUID]) That leaves one line of untested code: let UUIDs = peripheral.services.map { $0.UUID }. Even if you insisted on testing that line, keeping it so functionally constrained makes your mocking job easier. Personally, I probablby wouldn’t bother.

Wednesday, September 30, 2015

Go the Language

I've been playing with Go recently. Front-end, I'm an iOS dev. Back-end, I'm usually Python. A few thoughts on Go.

  • Types are nice. One reason I'm looking beyond Python is because I like a type system. Go is statically typed.
  • Compiled is nice. Performance is only part of it. Being able to statically link into one library solves deployment problems of a dynamic language. In interpreted languages, it can be a mystery where you're importing from, and leads to solutions like virtualenv or Bundler.
  • Go is lean. Google has mentioned that they want to keep the language minimal, and it shows. The core language is much simpler than Swift.
  • With such a minimal language, it really shows Google's priorities when the stdlib comes with a web server and HTML templating package.
  • Very useful to have a method of automatically pulling dependencies from github and elsewhere. I'm less sure of the philosophy of "All Go development in one directory."
  • Not everything needs to have objects. I though Django's class-based view system was over-engineered. It's actually refreshing that functions are emphasized over classes
I haven't even begun to dig into the concurrency features, which is supposed to be a big highlight. Overall, an interesting language, and one pretty fun to develop in. It's different enough that I need to spend some time developing in it to really understand it. Syntactically it's probably easier than Python, but it clearly requires patterns I'm not yet familiar with. I was going to write a simple blog engine in Go, but it became evident that would be a distraction. Maybe soon though.

Google Code Prettify Lives

Earlier I had expressed doubt about the maintenance of Google's code prettify syntax highlighter in general, and the availability of a Swift lexer in particular. Turns out I was overly pessimistic and they have a new active github repository with a Swift lexer.

Monday, March 30, 2015

NAS on the Way

I've been running a Zyxel NSA320, a two-bay NAS, for about a year now. I've been going slowly into the world of NAS, partly because I wasn't sure how it would fit into my workflow. I chose the Zyxel based on cost. At first I installed a single 500GB drive I had laying around. Once that filled up, I moved to a 3TB WD Red drive. After a while I was noticing I was keeping somewhat important stuff on the NAS, so I decided to add a second 3TB drive and migrate to a RAID 1. All of the migration was seamless. I've been running that RAID 1 setup for about six months now.

Now I'm taking the next step and replacing this NAS with a more capable device. This step is meant to replace a handful of DAS devices, so I'm going to put virtually all of my important info on the NAS, including photos, videos, and personal documents. To that end, I decided on a 4-bay NAS, thinking that RAID 5 would give me the best balance of capacity, performance, and redundancy. I'm going to reuse the 3TB drives I have.

Here are the factors that went into deciding on a solution.

Expandability

One of the advantages of NAS is the way it abstracts the notion of drives from storage. In the long term, I want to be able to expand my volume size. In the short term, I need to be able to go from a single disk, to a two disk mirror, to a three disk RAID 5. This is necessary to migrate my data from a two-drive mirror to a three drive RAID 5 by adding only one drive total, and directly transferring from one device to another (i.e., not backing everything up to a third device and using that to transfer). Most NASes do this with aplomb; it's really their bread-and-butter. A few lackluster consumer models fail in this regard, which is a deal-killer.

Performance

I'm giving up on my DAS drives, partly because of clutter: more Firewire, USB, and power cables than I want to deal with. I don't want this to feel like a big step down in performance. I need Lightroom, iTunes, Photos, etc., to access data on the NAS without feeling slow.

Backup

I'm a big believer of off-site backup. For homes and home offices, that means some cloud service. I need a way to backup the data on the NAS to the cloud. And I don't mean a half-measure like mount the NAS on a Mac and run Arq. I love Arq, but the NAS should have an integrated solution.

Spouse Friendly

The easier it is for my non-technical family members to use, the better. Not specific bullet points on this score.

Other lesser important factors:

  • Media streaming. A bonus. I'm intrigued by the Synology play series. But not a deciding factor.
  • HDMI. It's going under my desk, not in my TV cabinet. Not needed.
  • Pretty much any feature not related to serving files. No, I will not be running a blog, a wiki, or a forum on my NAS.
  • Time Machine. We're a Mac house, so I wish I could count on this. This is the one thing the Zyxel flat-out failed at. After several weeks the backup was inexplicably corrupt. Hopefully a quality NAS will be more reliable, but I'm not counting on it. It's really Apple’s issue. Nothing outside a Time Capsule or OS X Server is supported, and I'm not going either those routes.

So what did I choose? For that, dear reader, you'll have to come back next time. I'll reveal my choice and the rationale. The good news is that there are lots of good choices. The bad news is there isn't one obvious choice, or a singe standout vendor to make the decision easier.

Tuesday, March 24, 2015

Bell Labs Innovation

From Trust Me I'm a Scientist. The article is about audio sampling rates, but this passage stood out.

The mid-20th century was a heady time at Bell Laboratories. Just before its closing, it employed upward of 25,000 people, dedicated entirely to research and development.

Their innovations were enormous ones, and they lie at the root of the very device you are reading this on: The transistor, the laser, semi-conduction, the solar cell, television, C++ programming, the fax machine, and by the 1960s, the goddamn video phone.

For the sake of contrast, Google, one of our greatest innovators of today, employs roughly 50,000 people across all of its departments, and it’s greatest offerings have been, well… a slightly improved version of the fax machine and the videophone.

This passage is perhaps too dismissive of Google, but it is amazing how one of the technological powerhouses of our era pales in the face of Bell Labs.

Math & Code in Blogger

I use Blogger because of its simplicity and general lack of features. I’ve installed WordPress, and for me it’s a quagmire of tinkering and playing with plugins and themes, all in avoidance of writing. Blogger’s lack of features is itself a feature in the same way that full-screen text editors are a feature: it blocks out lots of potential time wasters.

Having said that, there are a few features I miss on Blogger, namely syntax highlighting and math formatting. Luckily there are a couple of easy javascript libraries that can do the job.

Syntax Highlighting with Google Code Prettify

Google has a javascript library for code syntax. It’s pretty basic in its formatting options, but good enough for me. Most importantly, installation is simple. I simply point to a javascript library hosted by google. Installation requires one line in the template:

<script src='https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js'/>

This add a css class, prettyprint. Use it in a pre tag for a code block. This HTML code:

<pre class="prettyprint">
int main(argc, argv[]) {
    printf("Hello, world!\n");
    return 0;
}
</pre>

is formatted like this:

int main(argc, argv[]) {
    printf("Hello, world!\n");
    return 0;
}

Tag inline code with <code> so that <code class="prettyprint">int i = 0;</code> becomes int i = 0;

Most "normal" (i.e., C-like) languages are recognized with the default. If you like oddball languages, like I do, you might need to specify your language. For example, I specify Scheme when I import the javascript like this:

<script src='https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js?lang=scm'/>

and then again specify the language in side the class

<code class="pretty-print lang-scm">
(define (area-of-disk r) 
  (* 3.14 (* r r)))
</code>

Becomes

(define (area-of-disk r) 
  (* 3.14 (* r r)))

I stick with the default style, which is pretty subtle. If you like more emphatic highlighting, the README has instructions of changing themes and other options like line numbering. It also contains the definitive language list.

Math Formatting with MathJax

I don't do a lot of math, but when I do it's important to me that the notation is clear.MathJax is a javascript library to format math. If you use their CDN, installation is one line in the Blogger template:

<script src='//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML' type='text/javascript' />

Equations are delimited with \[ \]. Now by entering this:

∖[ x = ∖frac{-b ∖pm ∖sqrt{b^2 - 4ac}} {2a} ∖]

You'll get this:

\[ x = \frac{-b \pm \sqrt{b^2 - 4ac}} {2a} \]

Inline latex is delimited with \( \) so that ∖(e = mc^2 ∖) becomes \( e= mc^2 \).

I like working with MathJax so much, it makes me want to include more posts with math.

Drawbacks

One drawback of this method is that you're entering a lot raw HTML in <code> and <pre> tags. It would be nice

Another drawback is that every page is loading the CSS and JavaScript for code and math on every page, regardless of whether there's any need for it or not.

Finally, while MathJax is well maintained, Google Code Prettify is a bit less active. As I write this, the last commit to the subversion repository was Mon, 03 Mar 2014. I won't be holding my breath for a Swift lexer.

Wednesday, December 24, 2014

Why is the Mac DHCP Client so Awesome?

Once upon a time I worked in Apple support, even before the organization was known as AppleCare. I authored a Knowledge Base article that documented the behavior of the Mac DHCP client in Mac OS 8 and 9. It was an implicit answer to the question, “Why don't Macs behave like the other clients?” At the time that meant, “Why don't they behave like Widows?”

With that background, I’m happy to see the article, Rapid DHCP: Or, how do Macs get on the network so fast? These days, the Mac DHCP client kinda kicks ass.