« February 2007 | Main | April 2007 »

March 11, 2007

NeoPhi Upgrade 2

Most of this weekend was spent upgrading NeoPhi to OpenBSD 4.0. I figured since I was going to patch my machine for the timezone fix I might as well just patch everything. Just as last time I did the less recommended upgrading without install media approach. It really isn't that bad. While I did learn a few tricks from the last time, I managed to make some other ones...

First I forgot one of the userland packages. I guess at some point I added xbase to the set of userland packages I had installed. Thankfully the problem only played out when one of the optional packages failed to upgrade due to a missing dependency. Thankfully I found a post that spoke to the issue, installed both revs of it and things started working again. Guess I just need to keep better track of what I have installed.

I again had postfix upgrade issues. Thankfully this time I had shutoff incoming requests at the firewall so no messages were put into limbo. The problem was during the upgrade of either the userland packages or optional packages the preferred mail server gets reset. As a result you need to rerun the postfix-enable script.

Mailman didn't upgrade properly and I also forgot to test it post upgrade. As a result this generated some bounced messages. I finally tracked it down to a combination of a configuration and permission/ownership issue which means I should be fine for future upgrades. I added an update in my previous upgrade post as that includes the error message and other details.

I have to say the new package update scheme rocks. Using both PKG_PATH and PKG_CACHE I was able to simulate what I'd previously done manually. One problem is the pair.com OpenBSD mirror doesn't play well with pkg_add. I had to switch to another mirror to actually get it to work. I thought I was being cleaver and teeing the output of the pkg_add command. Alas it uses some funky screen redraws and my output file contained almost no useful information. This is kind of bad since unless you are paying attention there is some information that scrolls by that you would otherwise miss. Next time I'll have to look at other switches to pkg_add to maybe change its behavior Probably another reason to do a fresh install.

Once everything was updated I applied the most recent patches based on the errata list. For some reason though I didn't think to reboot. As a result running applications didn't pickup the new timezone information, which was the main reason I went through the entire exercise to begin with...

During the course of the many reboots I also checked in on my RAID disks. Turns out I don't think they have ever been doing the right thing! While OpenBSD does support 3Ware, the drivers don't do everything that a 3Ware setup really needs to do. There is some funky RAID Array rebuilding operation (which my disks are currently in) that has to be initiated by the driver. 3Ware has never supplied the OpenBSD developers with the needed documentation to add this support into the driver. So, should your machine ever experience a power outage you'll need to reboot into some other OS which 3Ware completely supports in order to actually get your RAID Array working again. Ludicrous!

Tags: neophi openbsd

March 9, 2007

Asynchronous Testing with FlexUnit

When testing components that have asynchronous behavior you need to use FlexUnit's addAsync() method in order to correctly handle the events that fire. The reason for this is that unless you tell FlexUnit that you are expecting an asynchronous event, once your test method finishes FlexUnit will assume that the test is done and there were no errors. This can lead to false positives and annoying popup error dialogs with an assert fails. Below are some examples of how to use addAsync().

I'll start off with what not to do so you can get an idea of why you need addAsync(). Let's write a simple test that verifies that the flash.utils.Timer class fires events the way we think it should. The first attempt might look like this:

package com.example {
    import flexunit.framework.TestCase;
    import flash.utils.Timer;
    import flash.events.TimerEvent;
    
    public class TimerTest extends TestCase {
        private var _timerCount:int;
        
        override public function setUp():void {
            _timerCount = 0;
        }
        
        public function testTimer():void {
            var timer:Timer = new Timer(3000, 1);
            timer.addEventListener(TimerEvent.TIMER, incrementCount);
            // the next line should not be written like this, it can produce false positives
            timer.addEventListener(TimerEvent.TIMER_COMPLETE, verifyCount);
            timer.start();
        }
        
        private function incrementCount(timerEvent:TimerEvent):void {
            _timerCount++;
        }
        
        private function verifyCount(timerEvent:TimerEvent):void {
            assertEquals(1, _timerCount);
        }
    }
}

Nothing fancy here, we declare a single test method, create the Timer, and then start it. It should run once and then stop. If you add this test to your test suite and run it, you get a nice green bar. This is a false positive! The assertEquals() in the verifyCount() method didn't really contribute to the test passing. Try changing it to:

assertEquals(2, _timerCount);

When you rerun the test you'll get another green bar. Then a few seconds later an error dialog box pops up with the following message:

Error: expected:<2> but was:<1> at flexunit.framework::Assert$/flexunit.framework:Assert::failWithUserMessage()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\Assert.as:209] at flexunit.framework::Assert$/flexunit.framework:Assert::failNotEquals()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\Assert.as:62] at flexunit.framework::Assert$/assertEquals()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\Assert.as:54] at com.example::TimerTest/com.example:TimerTest::verifyCount()[C:\Work\Eclipse3.2\Fresh\AsyncTest\src\com\example\TimerTest.as:26] at flash.events::EventDispatcher/flash.events:EventDispatcher::dispatchEventFunction() at flash.events::EventDispatcher/dispatchEvent() at flash.utils::Timer/flash.utils:Timer::tick()

But, but, the test bar was green! FlexUnit didn't know you where waiting for an event, as a result when the testTimer() method finished, that test was clean. The error dialog pops up because FlexUnit isn't around to catch the error and turn it into a pretty message. This also shows that even though the test finished our object was still running in the background. We can fix this first issue by adding a tearDown() method and use addAsync() to wrap the function that should be called along with specifying the maximum time to wait for that function to be called. The function returned by addAsync() is used in place of your original function. In the example above, the second listener function passed into the addEventListener() call is where addAsync() will come into play. The changes to TimerTest look like this:

private var _timer:Timer;

override public function tearDown():void {
    _timer.stop();
    _timer = null;
}

public function testTimer():void {
    _timer = new Timer(3000, 1);
    _timer.addEventListener(TimerEvent.TIMER, incrementCount);
    _timer.addEventListener(TimerEvent.TIMER_COMPLETE, addAsync(verifyCount, 3500));
    _timer.start();
}

I've taken the original function and wrapped it in an addAsync() and added the maximum time that the function can take to be called. Since my Timer is set to run in 3000ms I gave myself a little buffer. With this change and the assert testing for a count of 2, I'll get a red bar when the test runs. Additionally I added a tearDown() method so that regardless of what happens in the test the Timer will stop running. When doing asynchronous testing it is important to clean up like this otherwise you can get objects hanging round in memory doing things you don't want. On that note I'd also recommend that the event listeners added to the timer use weak references. That way in the tearDown() function nulls out the Timer that object can be garbage collected. The update code would look like this:

_timer.addEventListener(TimerEvent.TIMER, incrementCount, false, 0, true);
_timer.addEventListener(TimerEvent.TIMER_COMPLETE, addAsync(verifyCount, 1500), false, 0, true);

Now that we added in that timeout to the verifyCount() call, you'll also get a red bar if the function isn't called in the timeout specified. Drop the timeout to 1500ms and rerun the test. You should now get the following error:

Error: Asynchronous function did not fire after 1500 ms at flexunit.framework::Assert$/fail()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\Assert.as:199] at flexunit.framework::AsyncTestHelper/runNext()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\AsyncTestHelper.as:96] at flexunit.framework::TestCase/flexunit.framework:TestCase::runTestOrAsync()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\TestCase.as:271] at flexunit.framework::TestCase/runMiddle()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\TestCase.as:192] at flexunit.framework::ProtectedMiddleTestCase/protect()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\ProtectedMiddleTestCase.as:54] at flexunit.framework::TestResult/flexunit.framework:TestResult::doProtected()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\TestResult.as:237] at flexunit.framework::TestResult/flexunit.framework:TestResult::doContinue()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\TestResult.as:109] at flexunit.framework::TestResult/continueRun()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\TestResult.as:79] at flexunit.framework::AsyncTestHelper/timerHandler()[C:\Documents and Settings\mchamber\My Documents\src\flashplatform\projects\flexunit\trunk\src\actionscript3\flexunit\framework\AsyncTestHelper.as:121] at flash.utils::Timer/flash.utils:Timer::_timerDispatch() at flash.utils::Timer/flash.utils:Timer::tick()

That's the quick introduction to addAsync(). Now onto some of the additional features of addAsync() that can be helpful. Say we wanted to test that a Timer is correctly called twice within a specified period of time. The incrementCount() function can easily be reused but the verifyCount() method has a hard coded assertEquals() in it. The optional 3rd argument to addAsync() is an argument called passThroughData, which can be anything. In this case I'm going to pass what I expect the count to be when the Timer finishes allowing me to reuse the same verify function. With these changes the functions now looks like this:

public function testTimer():void {
    _timer = new Timer(1000, 1);
    _timer.addEventListener(TimerEvent.TIMER, incrementCount, false, 0, true);
    _timer.addEventListener(TimerEvent.TIMER_COMPLETE, addAsync(verifyCount, 1500, 1), false, 0, true);
    _timer.start();
}

public function testTimer2():void {
    _timer = new Timer(500, 2);
    _timer.addEventListener(TimerEvent.TIMER, incrementCount, false, 0, true);
    _timer.addEventListener(TimerEvent.TIMER_COMPLETE, addAsync(verifyCount, 1500, 2), false, 0, true);
    _timer.start();
}

private function verifyCount(timerEvent:TimerEvent, expectedCount:int):void {
    assertEquals(expectedCount, _timerCount);
}

In both cases I'm passing along the expected count and have created a generic verify method. The object that you pass can be anything which makes it a very powerful way to write generic asynchronous event verifiers. But wait there's more!

The last optional argument to addAsync() is a way to verify that the event didn't happen. Instead of getting a red bar and the "Error: Asynchronous function did not fire after 1500 ms", you can verify that based on the event not firing, everything is as it should be. For example I could verify that after 1500ms a 1000ms Timer only fired one event and the complete event never fired. This last version of the test class does that. I also modified things a little since if you include passThroughData, it gets passed to both the regular function and the failFunction. This isn't a bad thing as it provides a way to write a generic failFunction handler like the way the generic verify handler was written. Note that the failFunction handler doesn't need to worry about getting an event passed in like the normal listener function since the event didn't fire.

package com.example {
    import flexunit.framework.TestCase;
    import flash.utils.Timer;
    import flash.events.TimerEvent;
    
    public class TimerTest extends TestCase {
        private var _timerCount:int;
        private var _timer:Timer;
        
        override public function setUp():void {
            _timerCount = 0;
        }
        
        override public function tearDown():void {
            // cleanup to make sure the Timer doesn't keep running
            _timer.stop();
            _timer = null;
        }
        
        public function testTimer():void {
            runTimer(1000, 1, 1500, 1);
        }
        
        public function testTimer2():void {
            runTimer(500, 2, 1500, 2);
        }
        
        public function testNotDone():void {
            runTimer(1000, 2, 1500, -1, 1);
        }
        
        private function runTimer(delay:int, count:int, timeout:int, goodCount:int, badCount:int = -1):void {
            _timer = new Timer(delay, count);
            // use weak references to make sure the Timer gets GC when we are done
            _timer.addEventListener(TimerEvent.TIMER, incrementCount, false, 0, true);
            // pass both a good an bad handler along with counts so the verify methods
            // can detect what happened
            _timer.addEventListener(TimerEvent.TIMER_COMPLETE, addAsync(verifyGoodCount, timeout, {goodCount: goodCount, badCount: badCount}, verifyBadCount), false, 0, true);
            _timer.start();
        }
        
        private function incrementCount(timerEvent:TimerEvent):void {
            _timerCount++;
        }
        
        private function verifyGoodCount(timerEvent:TimerEvent, extraData:Object):void {
            if (extraData.goodCount == -1)
            {
                fail("Should have failed");
            }
            assertEquals(extraData.goodCount, _timerCount);
        }
        
        private function verifyBadCount(extraData:Object):void {
            if (extraData.badCount == -1)
            {
                fail("Should not have failed");
            }
            assertEquals(extraData.badCount, _timerCount);
        }
    }
}

Feel free to play with this final version to test how different asynchronous scenarios are handled in FlexUnit.

Some additional notes:
Make sure the listener function name doesn't start with test, otherwise FlexUnit will think that it is a test method and try to run it.

I've noticed some odd behavior with multiple addAsync()s set at the same time. In general you only want to have one outstanding addAsync() at a time.

If you specify passThroughData and a failFunction, the passThroughData will get passed to both the function and the failFunction.

Make sure that you cleanup any objects that could still fire events in your tearDown() method and either remove event listeners or make them weak. There is more to this topic but I'll cover that in another post.

Tags: as3 flex flexunit testing

March 2, 2007

Programmatic Bindings

Recently I found myself refactoring an MXML component into an AS base class to make it easier to provide multiple views of the data. I wanted to factor as much of the code as I could into the base class including some of the bindings. While I've known for some time that Flex has programmatic binding support I'd not had a real use for it until now. The documentation is reasonable but I think I couple more examples would have helped. Below is a sample binding and the corresponding programmatic equivalent along with some gotchas.

Take an example MXML component that has a model Object set on it. The model exposes an enabled flag which the view needs to respond to in a complex fashion, beyond just disabling itself. If you want you can also replace the setter with a variable in these examples and it all works the same. It might look something like this (a bunch of code has been removed to focus just on the binding):

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script><![CDATA[
import com.example.Model;
[Bindable]
public var model:Model;

private function set controlsEnabled(controlsEnabled:Boolean):void {
    // logic to handle change
}
]]></mx:Script>
<mx:Binding source="model.enabled" destination="controlsEnabled"/>
<!-- child components removed -->
</mx:Canvas>

Now I want to turn that into an AS base class and not have every MXML subclass need to repeat the binding. My AS base class then looks like this:

package com.example {
    import mx.binding.utils.BindingUtils;
    import mx.containers.Canvas;
    
    public class SimpleBase extends Canvas {
        [Bindable]
        public var model:Model;
        
        public function SimpleBase() {
            super();
            BindingUtils.bindProperty(this, "controlsEnabled", this, ["model", "enabled"]);
        }
        
        public function set controlsEnabled(controlsEnabled:Boolean):void {
            // logic to handle change
        }
    }
}

While the view has changed to this:

<?xml version="1.0" encoding="utf-8"?>
<SimpleBase xmlns="com.example.*" xmlns:mx="http://www.adobe.com/2006/mxml">
<!-- child components removed -->
</SimpleBase>

The key to this magic is the BindingUtils.bindProperty() call in the base class. That mimics the mx:Binding that was previously in the MXML code. The documentation for BindingUtils uses site and host instead of source and destination which means you need to do a little translation when converting from one to the other. The most important thing to keep in mind when doing a translation like the one above is the use of this for both the site and host, otherwise you won't pick up all the changes.

The reason is that the bindings need to be anchored on some object that is issuing property change events. In the SimpleBase constructor model is null, so we can't really bind to that. Even if it wasn't null, the bindings would only fire off of the model instance that was active when it was setup. By chaining off of this you pick up changes both to model and the enabled flag within the model object.

The other tricky thing to notice is that the access modifier of the controlsEnabled() method changed from private to public. This needs to happen so that the code in BindingUtils can call it. Since this code is being executed outside of the class when in AS, instead of being compiled in (which is the case with MXML) it needs to be able to have standard access to the method. If you forget to make the setter public you'll get an error like this:

ReferenceError: Error #1056: Cannot create property controlsEnabled on SimpleView2.

The BindingUtils.bindSetter() method allows you to call an arbitrary method with the new value instead of just setting a variable (directly or via a setter).

Tags: bindings flex

About