Monday, 23 July 2012

Umbraco 4.7.2 Linux Port: UI Issues Part 1

I have started looking at the UI. This looks like it will be the most challenging aspect of the port. We will be looking at various errors and resolving them. In most instances, there are no clear error messages. But so far errors in the UI have been one of the following: javascript which does not load, casing issues, possible mono bugs or idiosyncracies.

But first an addition to getting the application to load.

After switching all projects to .NET 4, we get:
.../umbraco/cms/businesslogic/member/Member.cs(43,43): Error CS0246: The type or namespace name `MembershipProvider' could not be found. Are you missing a using directive or an assembly reference? (CS0246) (umbraco.cms)
Add References to System.Web.ApplicationServices in umbraco.cms, umbraco.providers

Final ScriptResource not found error.
Traces to js/dualSelectBox.js in umbraco.aspx lines 53::63
We have a CompositeScript control with one script. Remove the CompositeScript tags, i.e. delete lines 54 and 58.
Not sure why this generates an error but I am suspecting a mono issue.

   <asp:ScriptManager runat="server" ID="umbracoScriptManager" ScriptMode="Release">
            <Scripts>
                <asp:ScriptReference Path="js/dualSelectBox.js" ScriptMode="Release"  />
            </Scripts>
        <Services>
            <asp:ServiceReference Path="webservices/legacyAjaxCalls.asmx" />
            <asp:ServiceReference Path="webservices/nodeSorter.asmx" />
        </Services>
    </asp:ScriptManager>


Now let's move on to the UI

UI testing - Document types

General casing issues: replace as follows -
editNodeTypeNew.aspx to EditNodeTypeNew.aspx
settingDataType.gif to settingDatatype.gif
/genericProperties to /GenericProperties

Cannot get to DocumentType edit screen
.../umbraco/settings/EditNodeTypeNew.aspx?id=...
System.Web.HttpException Multiple controls with the same ID 'ctl00' were found. FindControl requires that controls have unique IDs.

EditNodeTypeNew.aspx, Lines 9, 15, and  give property panels IDs. Generally if you have multiple controls of the same type on the page make sure you explicitly give them distinct ids. E.g.
<cc1:PropertyPanel id="pp1"...
...
<cc1:PropertyPanel id="pp2"...

No save button image:
editor/save.gif to editor/Save.GIF

Save bubble:
speechbubble/ to speechBubble/

umbraco splash
umbracosplash. to umbracoSplash.

In Info and Structure tabs, list item selection does not persist after save.
This used to be a pre mono 2.6 bug, but the investigation continues

UI Testing - Templates

System.Web.Compilation.ParseException expecting '>'. Got 'asp'
Mono is very particular. Do this - but only for the following sections. .../umbraco/presentation/umbraco/settings/editTemplate.aspx lines 11::244 IF you do it for the whole of the script tag, then inline asp will not execute. This is most likely a mono bug. Search for '\n

       <!--
        function umbracoTemplateInsertMasterPageContentContainerDo(result) {
          UmbEditor.Insert(result + '\n', '\n</asp\:Content>\n', '<%= editorSource.ClientID%>');
        }
        // -->


I did this too
change .master extensions to .Master - this is tricky. Do not do a global S & R. Do it by hand. Will need to revisit but using .Master for compatibility for now.

UI Testing - Media
airinstallbadge to AIRInstallBadge

Sunday, 15 July 2012

Setting up a Linux test system

I will digress a little bit and write a few notes about setting up a Linux test system.


Setting up a Linux test system


1. At present this requires setting up a parallel mono development environment with mono 2.11.2


2. There are great instructions  here.


3. Important caveats: if possible avoid using gnome as your window manager. gnome makes heavy use of C# / mono, and you may find that you have to replicate a lot of additional libraries. Actually, avoid using KDE as well, to avoid any potential adverse interactions, use the simplest possible window manager you can find: E.g. openbox, or if you are like me and want your compositing as well, go for Compiz.


4. I found adding the following to the environment setup was necessary:


export MONO_GAC_PREFIX=/opt/mono:/usr

5. Quick description of the Compiz set-up on Arch Linux:


Install the following:


community/ccsm 0.8.4-3
community/compiz-bcop 0.8.8-2
community/compiz-core 0.8.8-3
community/compiz-fusion-plugins-main 0.8.8-2
community/compiz-manager 0.6.0-5
community/compizconfig-python 0.8.4-4
community/emerald 0.8.8-2
community/emerald-themes 0.6.0-4
community/fusion-icon 1:0.1-1
community/libcompizconfig 0.8.8-2


#.xinitrc
exec compiz-alone-session
setxkbmap -layout uk
#if using vmware
vmware-user-suid-wrapper

# ~/.config/compiz/autostart.sh
# This shell script is run before Compiz launches.
# Environment variables set here are passed to the Compiz session.

# Set a background color
BG=""
if which hsetroot >/dev/null 2>&1; then
BG=hsetroot
else
if which esetroot >/dev/null 2>&1; then
BG=esetroot
else
if which xsetroot >/dev/null 2>&1; then
BG=xsetroot
fi
fi
fi
test -z $BG || $BG -solid "#202020"

# D-bus
if which dbus-launch >/dev/null 2>&1 && test -z "$DBUS_SESSION_BUS_ADDRESS"; then
eval `dbus-launch --sh-syntax --exit-with-session`
fi

# Run XDG autostart things. By default don't run anything desktop-specific
# See xdg-autostart --help more info
DESKTOP_ENV="COMPIZ"
if which /usr/bin/xdg-autostart >/dev/null 2>&1; then
/usr/bin/xdg-autostart $DESKTOP_ENV
fi

xrdb -merge ~/.Xresources
export _JAVA_OPTIONS='-Dawt.useSystemAAFontSettings=lcd'
xrdb -merge ~/.Xresources &
xrandr -s 1280x800 &
thunar --daemon &
emerald --replace &
nitrogen --restore &
cairo-dock &


 PS: On Arch Linux with open-vmware-tools you can set in the VMware machine settings, 3D acceleration to yes, and you will get compositing and more.


You can also add archlinux bashrun, and compiz-boxmenu and compiz-deskmenu - though the menu set-up is slightly buggy: I ended up configuring the menu with the compiz-deskmenu tool, and then using that config file with compiz-boxmenu (using option-alt-m to launch the menu through ccsm commands).


Oh, and I m using a pure 32-bit machine:


[mono] ~ @ uname -a
Linux 3.4.4-2-ARCH #1 SMP PREEMPT Sun Jun 24 17:28:37 UTC 2012 i686 GNU/Linux

[mono] ~ @ mono -V
Mono JIT compiler version 2.11.2 (master/a7e01ca Tue Jun 19 21:41:42 BST 2012)
Copyright (C) 2002-2012 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
TLS: __thread
SIGSEGV: altstack
Notifications: epoll
Architecture: x86
Disabled: none
Misc: softdebug
LLVM: supported, not enabled.
GC: Included Boehm (with typed GC and Parallel Mark)


 

Saturday, 14 July 2012

Getting Unit tests working

Before we put the UI through its paces, let's look at the TDD aspects of our port.


Getting Unit tests working



From a TDD perspective, with NUnit & NMock, we cannot escape the conclusion that major parts of the source need to be re-written. In particular, the 4.7.2 source code contains a long static chain - a chain of objects with static variables & functions, that render testing with NUnit & NMock impossible. In general, many testing frameworks do not allow for mocking of static chain elements, and this style of coding has consequently come into dis-favor.


It would be preferable for example to have a very small static application loader class, that instantiates all relevant variables & constants, and initializes data services. In .NET, this could be done in the global.asax file. Then for testing, we isolate all database read / write calls, which are tested separately. The functions that use database services are tested separately using a test data service and mocks. This has the benefit of testing application functionality and database access in isolation of each other and is especially helpful for diagnosing any multi-platform issues.


Of course, a major re-write of 4.7.2 is beyond the scope of this porting exercise - which is all about porting an existing application with minimal changes. But these issues can be addressed by the next 4.x version.


In this section, I will show what minimum changes are needed to get unit tests working in Linux. The changes rely on some clever minor re-writing of the existing code to inject test values through dependency injection. It is not the prettiest approach, but it works.


Monodevelop does not recognize the Umbraco.Test project
Open the Umbraco.Test.csproj file and delete the {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} line
The Umbraco.Test project will not load in the solution.
Now in the Solution Options > Build Configurations, Configuration Mappings, add Umbraco.Test to the build.


Setting up Nunit tests
Download the NUnit dlls. In the project references, delete the 'Microsoft.VisualStudio.QualityTools.UnitTestFramework, and add nunit.framework.dll


Test Conversion
Using search replace tools, for every test replace 'using Microsoft.VisualStudio.TestTools.UnitTesting;' with 'using NUnit.Framework;'
Using this link convert all test calls.
The tests now compile.


Getting Tests to Pass
All tests run but fail. I have started fixing these tests. Fix 1: establish database access. NUnit does not have access to the .NET Application environment, and calls to Configuration.Appsettings[key] return null. This causes tests requring database access to fail.
Solution: inject database settings.


Code re-writes for injecting test Appsettings
I target the static umbraco/businesslogic/GlobalSettings.cs class DbDSN property (line 139), and replace the static system call with a lazy loading singleton that mimics static behavior:
//return ConfigurationManager.AppSettings["umbracoDbDSN"];
return ConfigurationManagerService.Instance.AppSettings["umbracoDbDSN"];


The ConfigurationManagerService class creates and returns an instance of the default or a test configurator.
In tests we can set the test configurator with: ConfigurationManagerService.ConfigManager = new ConfigurationManagerTest(SetUpUtilities.GetAppSettings());
If the 'ConfigManager' property is null, then the service defaults to the runtime, non-test configurator.


All of this is probably not making much sense, and seeing the whole code will help.


 


1. First we create an interface for the ConfigurationManager calls that we are using.:


// .../umbraco/interfaces/IConfigurationManager.cs
using System;
using System.Collections.Specialized;
using System.Web;

namespace umbraco.interfaces
{
public interface IConfigurationManager
{
NameValueCollection AppSettings {get;}

Object GetSection(string SectionName);
void RefreshSection(string SectionName);
}
}

 


2. Next we create our wrappers (aka Facade pattern)


// .../umbraco/businesslogic/ConfigurationManager/ConfigurationManagerDefault.cs
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Web;

using umbraco.interfaces;

namespace umbraco.BusinessLogic
{
public class ConfigurationManagerDefault : IConfigurationManager
{
public ConfigurationManagerDefault (){}

public NameValueCollection AppSettings
{
get
{
return ConfigurationManager.AppSettings;
}
}

public Object GetSection(string SectionName)
{
return ConfigurationManager.GetSection(SectionName);
}

public void RefreshSection(string SectionName)
{
ConfigurationManager.RefreshSection(SectionName);
}
}
}

 


// .../umbraco/businesslogic/ConfigurationManager/ConfigurationManagerTest.cs
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Web;

using umbraco.interfaces;

namespace umbraco.BusinessLogic
{
public class ConfigurationManagerTest : IConfigurationManager
{
public ConfigurationManagerTest(NameValueCollection appSettings)
{
_appSettings = new NameValueCollection();
_appSettings.Add(appSettings);
}

private NameValueCollection _appSettings;

public NameValueCollection AppSettings
{
get
{
return MergeAppSettings(ConfigurationManager.AppSettings);
}
}

public Object GetSection(string SectionName)
{
return ConfigurationManager.GetSection(SectionName);
}

public void RefreshSection(string SectionName)
{
ConfigurationManager.RefreshSection(SectionName);
}

private NameValueCollection MergeAppSettings(NameValueCollection appSettings)
{
NameValueCollection mergedAppSettings;
mergedAppSettings = new NameValueCollection();

if (appSettings.HasKeys())
foreach (string key in appSettings)
{
if (_appSettings[key] != null)
mergedAppSettings.Add(key.ToString(), _appSettings[key].ToString());
else
mergedAppSettings.Add(key.ToString(), appSettings[key].ToString());
}
else
mergedAppSettings.Add(_appSettings);


return mergedAppSettings;
}
}
}

 


3. We create the injector


// .../umbraco/businesslogic/ConfigurationManager/ConfigurationManagerFactory.cs
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Web;

using umbraco.interfaces;

namespace umbraco.BusinessLogic
{
public class ConfigurationManagerFactory
{

private ConfigurationManagerFactory() {}


public static IConfigurationManager GetConfigManager(IConfigurationManager configManager)
{
if (configManager == null)
return new ConfigurationManagerDefault();
else
return configManager;
}

}
}

 


4. We create the singleton


// .../umbraco/businesslogic/ConfigurationManager/ConfigurationManagerService.cs
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Web;

using umbraco.interfaces;

namespace umbraco.BusinessLogic
{
public sealed class ConfigurationManagerService
{

private static volatile IConfigurationManager _instance;
private static object _syncRoot = new Object();

private static IConfigurationManager _configManager = null;
public static IConfigurationManager ConfigManager
{
get {return _configManager;}
set
{
_configManager = value;
}
}

private ConfigurationManagerService (){}

public static IConfigurationManager Instance
{
get
{
if (_instance == null)
{
lock(_syncRoot)
{
if (_instance == null)
_instance = ConfigurationManagerFactory.GetConfigManager(_configManager);
}
}

return _instance;
}
}

}
}

 


5. So far, we have not modified any existing source code but now we do that.


// .../umbraco/businesslogic/GlobalSettings.cs line 139
public static string DbDSN
{
get
{
try
{
//return ConfigurationManager.AppSettings["umbracoDbDSN"];
return ConfigurationManagerService.Instance.AppSettings["umbracoDbDSN"];
}
// ...

 


6. We can use the following quick and dirty test setup (it would be better to read the settings from a file).


// .../umbraco.Test/SetUpUtilities.cs
using System;
using System.Collections.Specialized;

namespace umbraco.Test
{
public class SetUpUtilities
{
public SetUpUtilities () {}

private const string _umbracoDbDSN = "server=127.0.0.1;database=someDB;user id=someUser;password=somePwd;datalayer=MySql";

public static NameValueCollection GetAppSettings()
{
NameValueCollection appSettings = new NameValueCollection();

//add application settings
appSettings.Add("umbracoDbDSN", _umbracoDbDSN);

return appSettings;
}

}
}

 


7. A simple test example


// .../umbraco.Test/ApplicationTest.cs
using NUnit.Framework;
using System;
using umbraco.interfaces;
using System.Collections.Generic;
using umbraco.DataLayer;
using System.Linq;

namespace umbraco.Test
{


///
///This is a test class for ApplicationTest and is intended
///to contain all ApplicationTest Unit Tests
///
[TestFixture]
public class ApplicationTest
{

[TestFixtureSetUp]
public void InitTestFixture()
{
ConfigurationManagerService.ConfigManager = new ConfigurationManagerTest(SetUpUtilities.GetAppSettings());
}
[Test]
public void Application_Make_New()
{
var name = Guid.NewGuid().ToString("N");
Application.MakeNew(name, name, "icon.jpg");

//check if it exists
var app = Application.getByAlias(name);
Assert.IsNotNull(app);

//now remove it
app.Delete();
Assert.IsNull(Application.getByAlias(name));
}

}
}

Thursday, 12 July 2012

Getting the application started and loaded

OK. let's have a look at the individual customer stories in detail.


Getting the application started and loaded without any 404 Resource XYZ not found errors


Let's have a look at what happens here.


System.IO.FileNotFoundException
Could not load file or assembly 'System.Web.Entity' or one of its dependencies. The system cannot find the file specified.
Remove reference from umbraco.presentation and rebuild.


System.Configuration.ConfigurationErrorsException
Unable to open configSource file '.../umbraco/presentation/config\UrlRewriting.config.
Unfortunately  mono cannot deal with the '\' paths in web.config. Set all such paths from '\' to '/'.


System.Web.Configuration.nBrowser.Exception
Parent not found with id = default.
Make sure the .NET 4 default.browser file is available and in the application path.. e.g. in ../umbraco/presentation/App_Browsers
This file is not available in mono, you will need to get it from your windows .NET 4 files.


umbraco.businesslogic.Exceptions.UserAuthorizationException
There was attempt to redirect to '/umbraco/' which is another domain than where you've logged in.
Cross site scripting barrier kicks in. Not sure why - could be a localdomain issue.
Comment out css barrier for now, .../umbraco/presentation/umbraco/login.aspx.cs lines 137:144
Fix is deferred for now


The installer screen does not appear
In web.config set the umbracoConfigurationStatus key to ""


The installer appears but no styles / scripts are loaded.
Change ../umbraco_client/installer references to ../umbraco_client/Installer


Script resources (*.axd) not found.
In web.config change all 3.5.0.0 assembly references to 4.0.0.0


Cannot remember why I did this anymore...
In .../umbraco/presentation/install/default.aspx added references to: System.Web.Configuration, System.Text, and System.Drawing


System.Web.HttpException
The file '/install/steps/defaultuser.ascx' does not exist.
This is the case for these install steps again it is a casing issue
In umbraco.presentation/install/steps/Definitions/*.cs set the UserControl property value casing to match referenced file name casing. I.e., /steps/defaultuser.ascx to /steps/defaultUser.ascx, and /steps/validatepermissions.ascx to /steps/validatePermissions.ascx


Starter Kits step 'No Thanks' image does not show
In umbraco.presentation/install/Skinning/locadStarterKits.aspx line 35 change installer to Installer.

Login panel appears on the left javascript and files do no load
In .../components/umbraco.controls/Panel.cs, replace references to panel/ with Panel/
OK, now we can login.


But... upon logging in you are greeted with: System.InvalidCastException
at System.Web.Script.Serialization.JavaScriptSerializer..ctor in mono/mcs/class/System.Web.Extensions/System.Web.Script.Serialization/JavaScriptSerializer.cs:67
This is a particularly tough error to sort out. The trick is to look at the Application output. System.Web.Extensions is loaded twice as .NET 4 (by the web site) and as .NET 3.5 (by the Our.Umbraco.uGoLive.dll). The latter gains precendence and causes a crash. This is a documented mono bug with workarounds.
You can choose from one of these: (1) recompile the Our.Umbraco.uGoLive.dll in .NET4 but the source is not available, (2) disable Our.Umbraco.uGoLive.dll, or (3) use an assembly redirect to redirect 3.5.0.0 to 4.0.0.0. We will do (3).


Whew, that was something... Now we log in and are greeted by An empty screen with a loading bar
Using Firebug, focus on the GET language.asp 500 internal server error.
The error results because language.asp cannot find the appropriate language xml file to load.
In umbraco/businesslogic/IO/IOHelper.cs change in line 99, path[1] to path[0].
This is a small but significant change and may need revisiting.


The language file now loads but the screen still looks the same
In umbraco.presentation/umbraco/umbraco.aspx, and in umbraco.presentation/umbraco/masterpages/umbracoPage.master change Application/jQuery to Application/JQuery. Do a search and replace for this pattern to correct all solution occurrences.


Now, the UI loads but the right hand side Dasboard panel gives a file not found error
In umbraco/businesslogic/IO/SystemFiles.cs change dashboard.config to Dashboard.config.
In the same file also change these:
xsltExtensions to xsltExtensions
restExtensions to restExtensions

We ll be proactive and clear a few more casing mismatches: In umbraco.presentation/umbraco/umbraco.aspx change all occurrences of UmbracoSpeechBubbleBackend to UmbracoSpeechBubbleBackEnd, and also in the whole solution replace tabView with tabview (for umbraco_client/tabview/images)
Also do a search and replace changing speechBubble_close to speechbubble_close (for the speech bubble close image)
Now we are left with two ScriptResource not found errors, and some Dashboard load errors.


Fix the dashboard
In umbraco.presentation/config/Dashboard.config, replace file references to Pascal casing so as to match the file names.
There are a few images fixed in the same manner: tv.png to TV.png, and listitemorange.gif to listItemOrange.gif, feedproxy.aspx to FeedProxy.aspx
This completes the fixes for the Dashboard. The last two tabs are not in working order, and I will ignore them for now.


SUMMARY


This completes the changes needed to make the interface load. Most of the changes have to do with mismatches between actual file name casing and the hard coded references to those files. There are a number of approaches possible, e.g., all file names can be abstracted into a separate file - where possible. This would help. Fixes do not need to be applied in code, but can be applied to file and folder names - even symbolic links can be used. I have gone the hard way - replacing hard coded values - to illustrate the extent of the issue.

Wednesday, 11 July 2012

Getting 4.7.2 compiling on mono

OK. let's have a look at the individual customer stories in detail.


Getting 4.7.2 compiling on mono


The current stable release of mono is 2.10.x. The next stable version will be 2.12. The current SVN version is 2.11.2. All my builds are against 2.11.2.


Errors resolved:
umbraco/datalayer/SqlHelpers/MySql/SqlResources.resx - Position: Line 123, Column 5.
Inner exception: Could not find a part of the path ".../umbraco/datalayer/SqlHelpers/MySql/sql\total.sql".
Inner exception: Could not find a part of the path ".../umbraco/datalayer/SqlHelpers/MySql/sql\version4_upgrade.sql.sql".


Change all: sql\total.sql references to Sql\Total.sql, Sql\Version4_Upgrade.sql (mono can deal with the '\')


This also happens under the SqlHelpers/SqlServer folder.


 


umbraco/cms/businesslogic/Packager/FileResources/PackageFiles.resx: Error: Error: Invalid ResX input.
Position: Line 123, Column 5.
Inner exception: Could not find file ".../umbraco/cms/businesslogic/Packager/FileResources/packages.config".
Another casing issue - use Packages.config.


 


umbraco/presentation/umbraco/templateControls/Resources.resx: Error: Error: Invalid ResX input.
Position: Line 123, Column 5.
Inner exception: Could not find file ".../umbraco/templateControls/inlinexslt.xsltTemplate".
Another casing issue - use InlineXslt.xsltTemplate.


 


components/SQLCE4Umbraco/SqlCEResources.resx: Error: Error: Invalid ResX input.
Position: Line 123, Column 5.
Inner exception: Could not find a part of the path ".../components/SQLCE4Umbraco/sql\total.sql".
As above Sql\Total.sql


 


umbraco/presentation/umbraco/Search/ExamineEvents.cs(7,7): Error CS0246: The type or namespace name `Lucene' could not be found. Are you missing a using directive or an assembly reference? (CS0246)
Re-set reference from Shannon's c drive : )  to foreign dlls/Lucene.Net.dll


 


Edit: I forgot the following...


.../umbraco/actions/delete.aspx.designer.cs(21,21): Error CS0234: The type or namespace name `uicontrols' does not exist in the namespace `umbraco.presentation.umbraco'. Are you missing an assembly reference? (CS0234)
You will get about a 170!! of these - apparently there are some differences in how mono resolves references.


The error is easily fixed: Replace protected umbraco.uicontrols... with protected global::umbraco.uicontrols


Then you might need to do the same for: (about 8-9 occurences)
protected umbraco.controls... to protected global::umbraco.controls.



Compile now builds all projects (Tests ignored at the moment)

Tuesday, 10 July 2012

Porting Umbraco to mono / Linux

What follows are a series of posts looking at the porting process. I am going to kick off with methodology and considerations.


 


Methodology


At this stage I am working on a proof of concept. The idea is to get Umbraco 4.7.2 up and running on Linux in the quickest possible way with the fewest possible changes. The methodology combines Agile techniques with signal extraction techniques - where the released stable Umbraco 4.7.2 source constitutes the signal. I am doing multiple passes (iterations) over the same signal - as it were to tune into the Linux channel.  We can say that the Umbraco signal is arriving to the Linux channel in a degraded manner resulting in incomplete reconstitution. Our goal is to create a clean signal that will give us the clean "HD" CMS experience.


As with all signal extraction problems the GIGO, Garbage In - Garbage Out, principle applies. The rationale for choosing 4.7.2 is its high stable quality.


Here are the basic customer stories with the present status (the customer(s) being developer(s).



  1. Get 4.7.2 compiling on mono. Status: yes with mono 2.11.2, .NET 4.

  2. Get the application started and loaded without any 404 Resource XYZ not found errors. Minor code changes. Status: partially completed.

  3. Verify all UI functions. SQL errors, and further 404 Resource errors. Status: in progress.

  4. Get Unit tests working. Requires serious code changes. Status: partially completed.


 


 Considerations


An important consideration is whether  when the application becomes multi-platform, will the code for the other platforms be compiled and tested on each target platform? Or whether the code will be compiled and tested only on the source platform? I would recommend that the application is always compiled and tested on each target platform. Msunit, Pex & Moles do not work with mono, and unit tests would have to be ported and run on the target platform.

Monday, 9 July 2012

Progress on Umbraco 4.7.2 on Linux

In the past week, I have been busy getting the unit tests working. Porting the tests over to NUnit was easy enough - just a lot of search and replace, and the results compiled succesfully. Running the tests, however, required some modifications to the core code. The gist of the changes was to be able to inject configuration values, which are available in the .NET runtime, but not necessarily so at times when NUnit is running.



Usually we would put these values in through a mocking mechanism (e.g. NMock2 - which is what I m using). But the Umbraco Web App contains a long static chain, which NMock cannot deal with. An option would have been to use TypeMock - but TypeMock is quite pricey, and in my opinion would have created a high entry price for contributing to the source code and tests.



Having had a quick perusal of the tests, it seemed that the easiest approach was to Interface out the .NET 'ConfigurationManager' class, then using the facade pattern create a couple of derived classes - one for testing and one for live use. To break the static chain, I replaced all references to 'ConfigurationManager' with a Singleton that instantiates the required ConfigurationManager class through dependency injection. This approach kept the changes to the original source to a minimum (hence, minimised the possibility of introducing bugs), but required the additionof the new ConfigManager classes. The approach is quite standart. At some point, it would be nice to revisit the long static chain - and make it non-static, e.g. initialize in global.asax - but that would require pretty big changes.



In sum, using this approach I can make the unit tests pass. At present some units are passing, and I am working through the remaining ones.