Shredding, coding, arguing etc.

Unit Testing Control Adapters

7 June 2009

Here's a little trick I discovered that can be useful for unit testing protected class methods.

I wanted to TDD an ASP .NET control adapter. But the interesting methods of WebControlAdapter are all protected (e.g. RenderContents etc.).

Here's what I came up with:

[TestFixture]
public class CheckBoxListAdapterTest
{
    private class ExposedAdapter : CheckBoxListAdapter
    {
        public string Render()
        {
            var stringWriter = new StringWriter();
            var writer = new HtmlTextWriter(stringWriter);
            Render(writer);
            return stringWriter.ToString();
        }
    }

    [Test]
    public void EmptyList_Test()
    {
        var checkboxlist = new CheckBoxList();
        var adapter = new ExposedAdapter();

        var result = adapter.Render();

        Assert.That(result, Is.EqualTo("<ul></ul>"));
    }
}

Notice how we can test for the correct HTML directly which makes it very easy to get the control working perfectly before you even fire up a browser.

The trick of publicly exposing protected methods to unit tests can be applied in many situations.

Here I use the same technique to unit test a protected inner class (called HtmlCleaner) within a public HttpModule class:

[TestFixture]
public class HtmlCleanerModuleTest
{
    [Test]
    public void Test()
    {
        /// Get expected output of the HtmlCleaner class
        string expectedHtml;

        using (var stream = new StreamReader("expected.html"))
        {
            expectedHtml = stream.ReadToEnd();
        }

        /// Run the HtmlCleaner over a test html file
        using (var output = new MemoryStream())
        {
            using (var cleaner = ModuleExposer.GetCleaner(output))
            {
                using (var stream = new StreamReader("testInput.html"))
                {
                    var htmlBytes = Encoding.Default.GetBytes(stream.ReadToEnd());
                    cleaner.Write(htmlBytes, 0, htmlBytes.Length);
                    cleaner.Flush();
                }
                var outputHtml = Encoding.Default.GetString(output.GetBuffer());
                Assert.AreEqual(outputHtml, expectedHtml);
            }
        }
    }
    
    /// <summary>
    /// Used to access the protected inner class of HtmlCleanerModule called HtmlCleaner
    /// </summary>
    private class ModuleExposer : HtmlCleanerModule
    {
        public static HtmlResponseFilter GetCleaner(Stream sink)
        {
            return new HtmlCleaner(sink);
        }
    }
}

Alpha-Double-Plus Random Windows Wallpaper Background Changer

The intelligent random Windows background changer

7 June 2009

Features

  • Intelligent image resizing
  • Supports Windows XP and Windows Vista
  • Multiple monitor support (dual monitors, triple monitors or more)
  • Low memory footprint
  • Point to a folder of images
  • Recurses subfolders
  • Regular expression filename filters
  • Supports JPG (JPEG) and BMP
  • Free and open source
  • No adware, spyware, viruses etc

Why would anyone need a new random wallpaper background changer? Because other random wallpaper background changers don't have built in Alpha-Double-Plus Intelligent Image Resizing!

What is Alpha-Double-Plus Intelligent Resizing?

Some pictures look best when they are stretched to the full width of your screen. Other images look best if they keep their original size and are centered in the middle of your screen. If you have two or more monitors, then you may want your larger images to be stretched across more than one monitor. Large images may need to be shrunk down to fit your screen.

The Alpha Double Plus Random Windows Wallpaper Background Changer will try to choose the best size for your image.

100% Free Download

Fast Download (recommended) (32 kB, requires .NET Framework)

Slow Download (5 meg)

Source code (104 kB, C# Visual Studio solution)

I built this app in some spare time that I have had over the past week, but I haven't had a chance to build a proper installer for it yet. If anyone wants to help out by building a nice installer in WiX, I would very much appreciate.

Bye bye Easy Cgi - Hosting with AISO

11 May 2009

Wow - what a terrible company Easy CGI has become. I had a couple of accounts with them which I had created about 8 years ago. Back then it was difficult to get cheap hosting, particularly in Australia and Easy CGI's prices were pretty good and the service was not too bad. For some reason I was still hosting this site's database with them which is why Centrifugal Bumblepuppy has been up and down like Linda Lovelace.

Anyways, the site is now fully hosted with another el-cheapo plan, but supplied by a much more reliable company - AISO. These guys are great. Phil (the guy in sunglasses on their front page) always responds promptly and personally to my support questions. The solar panel thing is also a bit of fun - and no, my sites don't go down when it's a cloudy day.

Telerik RadAjax Performance Issues

5 April 2009

I first started using Telerik's products back in 2006. Back then ASP .NET AJAX was still in beta and Telerik's products were pretty much the best around when it came to out-of-the-box AJAX packages. Nowadays, I'm not so sure, but I still much prefer their RadAjax product over the standard .NET AJAX. I think the price is good. Forum support is hit-and-miss but generally responses are speedy, and the products themselves are still feature rich and well maintained.

Since I last updated the Telerik components of my website, a major overhaul of the RadControls suite has been released. Originally known under the code name Prometheus, but now under the inane Microsft-style nomenclature RadControls for ASP .NET AJAX, the suite is all built on Microsoft's own AJAX platform.

Having spent a couple of days upgrading the controls across all my sites, I was a bit disappointed overall with the upgrade process. Firstly, it was difficult and time consuming to upgrade. Secondly, in some respects the performance of my website had suffered. I am going to write about the latter because I know that a lot of websites using RadAjax could benefit from some simple performance analysis.

In fact, I believe a lot of this information will be relevant to websites using regular ASP .NET AJAX. Take Microsoft's own Codeplex for instance - boy does it suck. Why? Amongst other things because its poor performance often makes it almost impossible to use some of its features. Dave Ward has a great run down of why this is so.

Poor performing websites...

  • Are more frustrating, time consuming and difficult to use
  • Can collapse if usage escalates (i.e. are not scalable)
  • Cost more time and money to maintain and run
  • Cost their users more time and money to access
  • Reflect badly on the company

But hosting is cheap! And page load times hardly matter these days!

Maybe in the States and some parts of Europe, but not everyone lives in countries with $10/month hosting plans and broadband speeds averaging 4Mbs. For instance, at this point in time it looks like it will be at least 5 to 10 years before country Australians can start to upgrade their dial-up connections. If international visitors are important to you, or if you are working in a country with poorer broadband access, then performance still matters.

Furthermore, people judge the quality of a website so blindingly fast that unless web pages are loading literally instantly your users will already be making up their minds about how much your site sucks before all your beautiful Flash has even been requested.

Finally, if you are building simplistic websites with only a little bit of AJAX here-and-there you might find that performance tuning is a waste of time. But once you start building larger, more complex applications, with many AJAX-enabled features and lengthy forms, having a high performing AJAX framework lets you run free with your ideas without the risk of creating an unusable mess.

What Telerik does right - Combining scripts

Compare the resources loaded on Telerik's homepage versus those loaded on their help page. Each page has different settings enabled that illustrate some of the concepts I will speak about.

Help Pages Homepage
/help/ScriptResource.axd?d=bif1-8cUlrSOGbFo... 24 KB /Telerik.Web.UI.WebResource.axd?_TSM_Hidden... 324 KB
/help/ScriptResource.axd?d=VePnZn_eJSfhhXijf1V_mQSU0ihCT6j9-QNjU... 21 KB /WebResource.axd?d=4lC57JcX5z_hdXjKuDDHqg2&t=633362156370167146 20 KB
/help/WebResource.axd?d=4lC57JcX5z_hdXjKuDDHqg2&t=63336215637016... 20 KB /ScriptResource.axd?d=xOkRvb4JEVx0vVaLZyWvbT0dA-dyP7PauoOvFLaVh0... 5 KB
/help/ScriptResource.axd?d=VePnZn_eJSfhhXijf1V_mZm4MDRy7mfyLqDrz... 20 KB
/help/ScriptResource.axd?d=VePnZn_eJSfhhXijf1V_mb0WPQNIlfiG8Wv5R... 16 KB
/help/ScriptResource.axd?d=VePnZn_eJSfhhXijf1V_mQSU0ihCT6j9-QNjU... 10 KB
/help/ScriptResource.axd?d=bif1-8cUlrSOGbFoZlcsnXInXMvWm497euWb9V... 8 KB
/help/ScriptResource.axd?d=VePnZn_eJSfhhXijf1V_mQSU0ihCT6j9-QNjU96... 8 KB
/help/ScriptResource.axd?d=xOkRvb4JEVx0vVaLZyWvbT0dAdyP7PauoOvFLaV... 5 KB

For my discussion we will ignore the fact that the homepage's scripts are altogether larger (349 kilobytes versus 132 kilobytes). For some reason Telerik are not compressing their scripts on the homepage, although they are on their help pages. Why, I don't know... If they were, I imagine that the size of the scripts on the two pages would be comparable. Please make sure you compress your scripts.

So firstly, notice that the homepage has far less scripts than the help page. This is because all the scripts are bundled up and combined into one big script. Combining scripts is a good idea! Most browsers have a maximum number of scripts, images, CSS file etc. that they will attempt to download from one domain at the same time. This maximum is fairly low - almost certainly lower than the number of requests that need to be made on the typical uncached page. This means that rather than downloading resources in parallel, requests are queued and must wait their turn. By combining these resources into larger but fewer files, we can get more of our web page sluicing through the tubes in parallel.

Using Google Chrome's great new developer tools we can get a great visualisation of this principal in action:

Telerik Page Load Times in Google Chrome

Here we can see how the resources are not all downloaded in parallel. Actually there are a variety of reasons for the staggered downloading - I won't go into in detail but check out the research done by the tireless Yahoo folks if you haven't already. They have made all types of resources available of which my favourites are: the YUI Blog and the Yahoo Developer Network, this book, and the YSlow Firefox plugin.

Where Telerik goes wrong - not optimizing for browser caching

Assuming that people are going to browse more than one page of your web site, and are going to come back for more in the future, by far the best thing you can do to ensure an enjoyable, high performing experience, is to make sure you optimize your site's resources so that the poor user's browser can cache them effectively.

This is where Telerik's RadControls performs rather poorly - perhaps even worse than the original 'classic' controls that I started with back in 2006.

Lets browse around their website again to illustrate. We will just focus on the script file - it is a large file with the filename Telerik.Web.UI.WebResource.axd, followed by a querystring that varies.

  1. On the homepage, the file is 324 kilobytes. Our browser dutifully downloads it.
  2. Next we move to the product page. The file is still 324 kilobytes and the location hasn't changed. If we take a look at Google Chrome's resource reporting we can see that the browser can use the cached copy of this file, saving us the trouble of downloading it again. Great!
  3. Let's take a look at the Ajax product page. On this page the script file's querystring has changed ever so slightly - from 480 characters to 491 characters. No doubt a little bit more script is required to generate this page. Unfortunately, we can no longer use the browser's cached copy of the file, as it has a different querystring, so we must redownload the entire script file - all 369 kilobytes of it.
  4. Next we take a look at the RadEditor's demo page. Again the script file has changed slightly, and again we must redownload the entire copy - this time 336 kilobytes.

Rather than improving the situation by combining the script files into one, it is likely that the situation has become worse. If the script files were separated we would at least have the majority of them cached each time we landed on a new page. Instead of retrieving a whole new 300 kilobyte-plus file, we would just retrieve the new files which would be in the range of 10-50 kilobytes!

Fun with master pages

What's more, sometimes the script file itself does not change at all but the browser is still forced to retrieve an entirely new copy.

On my website, I found that the file's querystring was changing between pages, even if the actual script was exactly the same. On my homepage the script file's name was:

/Telerik.Web.UI.WebResource.axd?_TSM_HiddenField_=ctl00_AjaxManager_5_sm_HiddenField&compress=0&_TSM_CombinedScripts_=%3b%3bSystem.Web.Extensions%2c+Version%3d3.5.0.0%2c+Culture%3dneutral%2c+PublicKeyToken%3d31bf3856ad364e35%3aen-AU%3a94b8a2b4-5efc-4f4c-9641-d912b698978d%3aea597d4b%3ab25378d2%3bTelerik.Web.UI%2c+Version%3d2008.3.1314.35%2c+Culture%3dneutral%2c+PublicKeyToken%3d121fae78165ba3d4%3aen-AU%3aef502ffb-86f7-4d96-ad3a-fbb934d602ab%3a16e4e7cd%3aed16cbdc

...whilst on the first subpage the filename was:

/Telerik.Web.UI.WebResource.axd?_TSM_HiddenField_=ctl00_ctl00_AjaxManager_5_sm_HiddenField&compress=0&_TSM_CombinedScripts_=%3b%3bSystem.Web.Extensions%2c+Version%3d3.5.0.0%2c+Culture%3dneutral%2c+PublicKeyToken%3d31bf3856ad364e35%3aen-AU%3a94b8a2b4-5efc-4f4c-9641-d912b698978d%3aea597d4b%3ab25378d2%3bTelerik.Web.UI%2c+Version%3d2008.3.1314.35%2c+Culture%3dneutral%2c+PublicKeyToken%3d121fae78165ba3d4%3aen-AU%3aef502ffb-86f7-4d96-ad3a-fbb934d602ab%3a16e4e7cd%3aed16cbdc

Can you spot the difference?

The reason for the subtle change in file location was not because the script had changed, but because the ClientId of the RadAjaxManager was being appended to the file's querystring. The RadAjaxManager is located on the master page and if the master pages change (as they tend to when you move from a homepage to an inner page), the ClientId of the RadAjaxManager changes.

Even more aggrevating was that in both cases the RadAjaxManager was located on the top level master page (I was employing nested master pages). It seems weird that their ClientId should change when the position of the RadAjaxManager within the naming heirarchy is the same but for some odd reason .NET generates the ClientId of a control on a master page as if the controls is on the lowest level master page, even if the control is actually located on one of the higher level pages. This means that if the number of levels of master pages changes, then so will the ClientIds of controls on the top level master page!

My hacky workaround: I finally gave in and worked around the situation by adding a dummy, empty nested master page to the homepage's master page so that the number of levels of nested master pages were the same on all pages. This meant that the ClientId of the RadAjaxManager rendered the same.

My suggestion for Telerik and other .NET control vendors

  • Make it possible for developers to specify all the scripts required on their website, rather than working out which scripts are required on a page-by-page basis.
  • Combine all these scripts into one large file that is downloaded once, then cached across the whole site.
  • If the one large file is too large (say, above 500 kilobytes or more), make it simple to break up the file into slightly smaller chunks - for example, so that half can be downloaded on the homepage of the website and the remainder downloaded on the first page of the site that requires the additional scripts.
  •  
  • del.icio.us  del.icio.us
  • reddit  reddit
  • StumbleUpon  StumbleUpon