Tuesday 28 May 2013

Don't write new unit tests during the refactoring phase

If you're doing unit tests, you will be familiar with the Red-Green-Refactor cycle - write a failing test (red), make it pass (green) then drive out the design of the code (refactor).

When it comes to the refactor phase, you shouldn't need to write new tests - the refactor phase is when you change the implementation of the code, not the behaviour.

Take the following green code, which returns the min and max temperatures for three states of water:
using System;
namespace H2OLib
{
    public class H2O
    {
        public enum State
        {
            Gas,
            Liquid,
            Solid
        }

        private readonly State _state;

        public H2O(State state)
        {
            if (state != State.Gas && state != State.Liquid && state != State.Solid)
            {
                throw new ArgumentOutOfRangeException();
            }
            _state = state;
        }

        public int MaxTemp
        {
            get
            {
                switch (_state)
                {
                    case State.Gas:
                        return 374;
                    case State.Liquid:
                        return 100;
                    default:
                        return 0;
                }
            }
        }

        public int MinTemp
        {
            get
            {
                switch (_state)
                {
                    case State.Gas:
                        return 100;
                    case State.Liquid:
                        return 0;
                    default:
                        return -230;
                }
            }
        }
    }
}
This code is tested by the following tests:
    [TestFixture]
    public class H2OTests
    {
        [Test]
        public void GivenGasStateThenExpectCorrectTemps()
        {
            var h2o = new H2O(H2O.State.Gas);
            Assert.That(h2o.MinTemp, Is.EqualTo(100));
            Assert.That(h2o.MaxTemp, Is.EqualTo(374));
        }

        [Test]
        public void GivenLiquidStateThenExpectCorrectTemps()
        {
            var h2o = new H2O(H2O.State.Liquid);
            Assert.That(h2o.MinTemp, Is.EqualTo(0));
            Assert.That(h2o.MaxTemp, Is.EqualTo(100));
        }

        [Test]
        public void GivenSolidStateThenExpectCorrectTemps()
        {
            var h2o = new H2O(H2O.State.Solid);
            Assert.That(h2o.MinTemp, Is.EqualTo(-230));
            Assert.That(h2o.MaxTemp, Is.EqualTo(0));
        }

        [Test]
        public void GivenUnknownStateThenExceptionThrown()
        {
            Assert.Throws<ArgumentOutOfRangeException>(() => new H2O((H2O.State)99));
        }
    }

The code behaves exactly as planned, but the switch statements are a bit smelly. One solution is to refactor to the State pattern, with a factory to instantiate the correct state class based on the value that is passed to the H2O constructor. So, what's the next thing you do? Is it a) write a test for the StateFactory:
namespace H2OLib.Test
{
    [TestFixture]
    public class StateFactoryTests
    {
        [Test]
        public void ConstructExpectNoError()
        {
            Assert.DoesNotThrow(() => new StateFactory());
        }
    }
}
or b) don't write a test for the StateFactory:
// This line left intentionally blank
If you read the title of the post, you'll probably know that the answer is b. Given the tests that are in place, the code can be refactored to the following with no additional tests required:
using System;

namespace H2OLib
{
    public class H2O
    {
        private static StateFactory _stateFactory = new StateFactory();

        public enum State
        {
            Gas,
            Liquid,
            Solid
        }

        private readonly IState _state;

        public H2O(State state)
        {
            _state = _stateFactory.CreateState(state);
        }

        public int MaxTemp
        {
            get
            {
                return _state.MaxTemp;
            }
        }

        public int MinTemp
        {
            get
            {
                return _state.MinTemp;
            }
        }
    }

    public interface IState
    {
        int MinTemp { get; }
        int MaxTemp { get; }
    }

    public class StateFactory
    {
        public IState CreateState(H2OLib.H2O.State state)
        {
            switch (state)
            {
                case H2O.State.Gas:
                    return new GasState();

                case H2O.State.Liquid:
                    return new LiquidState();

                case H2O.State.Solid:
                    return new SolidState();

                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }

    public class GasState : IState
    {
        public int MinTemp
        {
            get { return 100; }
        }

        public int MaxTemp
        {
            get { return 374; }
        }
    }

    public class LiquidState : IState
    {
        public int MinTemp
        {
            get { return 0; }
        }

        public int MaxTemp
        {
            get { return 100; }
        }
    }

    public class SolidState : IState
    {
        public int MinTemp
        {
            get { return -230; }
        }

        public int MaxTemp
        {
            get { return 0; }
        }
    }
}
When you write a new test you're not refactoring; it's a new red phase.

Sunday 19 May 2013

LongTouch event on single-touch LCD

I have a T35 LCD touchscreen as part of my Gadgeteer kit. I was disappointed to find that although the drivers for the screen suggested gestures, in fact the display is a single-touch and so registers only TouchDown and TouchUp events.

I needed a second gesture for an app I was writing so created a subclass of the Display_T35 class. Here's how it works.

The Class

As it subclasses the Display_T35 class, it implements the public constructors of that class. There's one additional parameter on each constructor; the interval in milliseconds after which the LongTouch event will fire - this defaults to a second. The constructors also register handlers for the TouchDown and TouchUp events of the base class.

    public class GestureEnabledDisplay_T35 : Gadgeteer.Modules.GHIElectronics.Display_T35
    {
        public GestureEnabledDisplay_T35(int socket1, int socket2, int socket3, long longTouchRegisterInterval = 1000)
            : base(socket1, socket2, socket3)
        {
            Initialise(longTouchRegisterInterval);
        }

        public GestureEnabledDisplay_T35(int socket1, int socket2, int socket3, int socket4, long longTouchRegisterInterval = 1000)
            : base(socket1, socket2, socket3, socket4)
        {
            Initialise(longTouchRegisterInterval);
        }

        private void Initialise(long longTouchRegisterInterval)
        {
            base.WPFWindow.TouchDown += new Microsoft.SPOT.Input.TouchEventHandler(WPFWindow_TouchDown);
            base.WPFWindow.TouchUp += new Microsoft.SPOT.Input.TouchEventHandler(WPFWindow_TouchUp);
            LongTouchRegisterInterval = longTouchRegisterInterval;
        }
The subclass needs to expose the new LongTouch event. This is the .Net Micro Framework, so generic event handlers are not available - remember .Net 1.1 anyone?

        public delegate void LongTouchEventHandler(object sender, EventArgs args);
        public event LongTouchEventHandler LongTouch;
        
  private void OnLongTouch()
        {
            if (LongTouch != null)
            {
                LongTouch(this, EventArgs.Empty);
            }
        }
The functionality is fairly simple. When the TouchDown event is raised, the current clock tick is recorded. When the TouchUp event is raised, the number of ticks since TouchDown is calculated and compared with the LongTouchRegisterInterval. If the interval has passed, the LongTouch event is raised.

        public long LongTouchRegisterInterval { get; private set; }

  private long _touchdownTicks;
        private const long NanosecondsInMilliseconds = 10000;

        void WPFWindow_TouchUp(object sender, Microsoft.SPOT.Input.TouchEventArgs e)
        {
            var touchupTicks = DateTime.Now.Ticks;
            if (touchupTicks - _touchdownTicks > (NanosecondsInMilliseconds * LongTouchRegisterInterval))
            {
                OnLongTouch();
            }
        }

        void WPFWindow_TouchDown(object sender, Microsoft.SPOT.Input.TouchEventArgs e)
        {
            _touchdownTicks = DateTime.Now.Ticks;
        }

The Usage

If you use the standard Gadgeteer designer to connect your module...
...you'll have an instance of the Display_T35 defined for you in the generated.cs file:

        private Gadgeteer.Modules.GHIElectronics.Display_T35 display;

        private void InitializeModules()
        {
            this.display = new GTM.GHIElectronics.Display_T35(14, 13, 12, 10);
        }
This will need to be edited to use the new GestureEnabledDisplay_T35 class

        private GestureEnabledDisplay_T35 display;

        private void InitializeModules()
        {
            this.display = new GestureEnabledDisplay_T35(14, 13, 12, 10);
        }
However, this means that each time you edit the diagram in the designer, your changes will be overwritten. And the generated class is called generated for a reason - it's not yours so keep your mitts off. A better way to create the display class is to leave the module unconnected in the designer, and create the class explicitly in your code.

    public partial class Program
    {
        GestureEnabledDisplay_T35 gestureEnabledDisplay;

        void ProgramStarted()
        {
            gestureEnabledDisplay = new GestureEnabledDisplay_T35(14, 13, 12, 10);
            gestureEnabledDisplay.LongTouch += new GestureEnabledDisplay_T35.LongTouchEventHandler(display_LongTouch);
        }
 }

Known Issues

There are a couple of annoyances with the implementation, but not enough to make me swear lots:

  • The TouchDown and TouchUp events are defined in the Display_T35.WPFWindow class, but the LongTouch event is defined in the Display_T35 class.
  • If the WPFWindow.TouchUp event is handled as well as the LongTouch event, both events will still fire - the LongTouch event does not swallow the TouchUp event.

Where's the code?

The code's available as a GitHub Gist, here.

Saturday 18 May 2013

CLR_E_FAIL in .Net Micro Framework

While porting a library from .Net 4.5 to .Net Micro Framework 4.2, I had a large number of build failures - mainly the expected errors around generics and LINQ, which are not available in the Micro Framework.

Having worked my way through the obvious failures - IEnumerable<T> to arrays, EventHandler<T> to delegate(object, EventArgs), the compiler was reporting this:
I presumed the Micro Framework was having trouble with the foreach keyword, so changed the code to use a standard for loop. Then I received this error:


It turns out that what the Micro Framework doesn't like is multi-dimensional arrays. Where I had Points defined as Point[,] it needed to be defined as Point[][]. It was easy enough to fix, but a meaningful error message would have saved some of Google's bandwidth.