posts - 19 , comments - 8 , trackbacks - 0

Testing code with time dependency in C#

Introduction

Testing code that is date or time dependent is clearly problematic! The first step is to create an interface which we will use instead of accessing the DateTime object itself.

The interface

using System;

namespace JetBlack.Common.Timers
{
    public interface IDateTimeProvider
    {
        DateTime Today { get; }
        DateTime Now { get; }
        long Ticks { get; }
    }
}

A native date time provider

We can wrap up the standard date time object with the following code.

using System;

namespace JetBlack.Common.Timers
{
    public class DateTimeProvider : IDateTimeProvider
    {
        public DateTime Today { get { return DateTime.Today; } }
        public DateTime Now { get { return DateTime.Now; } }
        public long Ticks { get { return DateTime.Now.Ticks; } }
    }
}

A test provider

The following code implements a test provider which takes a start date/time and adds an increment every time it is called.

using System;

namespace JetBlack.Common.Timers.Testing
{
    public class DiscreteDateTimeProvider : IDateTimeProvider
    {
        private readonly TimeSpan _interval;
        private DateTime _dateTime;

        public DiscreteDateTimeProvider(DateTime startDateTime, TimeSpan interval)
        {
            _dateTime = startDateTime;
            _interval = interval;
        }

        public DateTime Today
        {
            get { return Now.Date; }
        }

        public DateTime Now
        {
            get
            {
                var prev = _dateTime;
                _dateTime += _interval;
                return prev;
            }
        }

        public long Ticks { get { return Now.Ticks; } }
    }
}

A faster DateTime

Finally we can improve the performance of time dependent code by implementing our own DateTime class. The problem with the system supplied implementation is speed. While the CPU contains timers, it does not know the time. This ability is provided by the real time clock (RTC) which is a separate chip on the motherboard that has a battery to keep it running when the computer is switched off. Whenever we call DateTime the CPU makes a call to the RTC which takes time (a lot of time).

The solution is to initialise the datetime once, and from then on query the internal timers in the CPU. This means the CPU only has to call the RTC once.

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace JetBlack.Common.Timers
{
    public class FastDateTime : IDateTimeProvider
    {
        private static DateTime _epochDateTime = default(DateTime);
        private static readonly long CountDivisor = QueryPerformance.Frequency / CountsPerMs;
        private static long _epochCount = long.MaxValue;
        private const long TicksPerMs = 10000;
        private const long CountsPerMs = 1000;

        public static long Ticks
        {
            get { return Now.Ticks; }
        }

        public static DateTime Now
        {
            get
            {
                var count = QueryPerformance.Counter;

                if (count < _epochCount)
                {
                    _epochDateTime = DateTime.Now;
                    _epochCount = count;
                    return _epochDateTime;
                }

                var elapsed = count - _epochCount;
                var ticks = (elapsed * TicksPerMs) / CountDivisor;
                return _epochDateTime.AddTicks(ticks);
            }
        }

        public static DateTime Today
        {
            get { return Now.Date; }
        }

        #region IDateTimeProvider

        public static IDateTimeProvider Provider = new FastDateTime();

        DateTime IDateTimeProvider.Now
        {
            get { return Now; }
        }

        DateTime IDateTimeProvider.Today
        {
            get { return Today; }
        }

        long IDateTimeProvider.Ticks
        {
            get { return Ticks; }
        }

        #endregion

        private static class QueryPerformance
        {
            [DllImport("kernel32.dll", SetLastError = true)]
            private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

            [DllImport("kernel32.dll", SetLastError = true)]
            private static extern bool QueryPerformanceFrequency(out long frequency);

            /// <summary>
            /// Retrieves the current value of the high-resolution performance counter.
            /// </summary>
            public static long Counter
            {
                get
                {
                    long performanceCount;
                    if (!QueryPerformanceCounter(out performanceCount))
                        throw new Win32Exception();
                    return performanceCount;
                }
            }

            /// <summary>
            /// Returns the number of counts per second for the high-performance counter.
            /// </summary>
            public static long Frequency
            {
                get
                {
                    long frequency;
                    if (!QueryPerformanceFrequency(out frequency))
                        throw new Win32Exception();
                    return frequency;
                }
            }
        }
    }
}

Print | posted on Thursday, December 18, 2014 4:55 PM | Filed Under [ C# DateTime Testing ]

Feedback

No comments posted yet.
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

Powered by: