in

CodePrairie .NET

South Dakota .NET User Group

chrisortman

October 2007 - Posts

  • Testing Helpers in MonoRail

    I have a helper that generates some links and wanted to be able to test that functionality. The difficult thing is that the helper delgates to UrlHelper which Delegates to Controller and to UrlBuilder.

    Thanks to the new testing functionality in MonoRail this was quite easy

    Heres's part of my test fixture.

     

    [TestFixture]
    public class NavigationHelperFixtures : Castle.MonoRail.TestSupport.BaseControllerTest {
    	private NavigationHelper helper;
    	private TestController controller;
    	[SetUp]
    	public void Setup() {			
    		helper = new NavigationHelper();
    		controller = new TestController();
    		PrepareController(controller, "test", "index");
    		helper.SetController(controller);
    	}
    }
     

    This will let me call my helper methods that call UrlHelper without problems.

     

     

    Technorati tags: ,
  • Can your VCS do this?

    Here's an interesting scenario I just ran into that makes me glad I am using git.

    I'm in the middle of working on a project which uses the same database for testing as development (not my idea) and I wanted to change the way that navigation on one of the pages works. This involves changes to the code as well as to a stored procedure. So I begin work like this

    git checkout -b experiments/new-navigation main

    Which will create a new branch for me to do my work in. Once the work is done I notify my testers to see what they think of the proposed change.

    While they are reviewing I want to be able to work on another feature, so I start:

    git checkout -b work/add-update-support main

    Which creates a new branch based on the main (which doesn't have any of the navigation changes I just did).

    The problem I'm going to have is that the database is using a version of the stored procedure I changed, but the change is incompatible with version of the code I have.

    What would you do? I'm not sure what I would do with subversion, probably copy and paste some hacks and leave TODO: markers by them. I'm using git though so I have a better way.

    Conceptually what I want is to go back in time and pretend I had made all the changes for update but started with navigation instead of main. Git has just the thing to do this and it is called rebase.

    git rebase --onto experiments/new-navigation main work/add-update-support

    So what happened after I ran this command?

    Here's a helpful graphic (excuse my poor image editing skills)

    The first diagram in my image shows what my working copy looked like before the rebase, and the second picture shows it after. As you can see I've gone back in time and made git think that my work/add-update-support started from a different base.

    Now I can continue to work on the update feature in the work/add-update-support branch. When I'm all done I have 2 options. If people like the navigation I can merge the work/add-update-support branch back into main, or if they don't I can rebase again and transplant the branch back onto main ala:

    git rebase --onto main experiments/new-navigation work/add-update-support 

     

    Technorati tags:
  • Getting started with the Castle git repo

    Before we dive into commands I feel the need for a little rationale. I'm not going to explain all the virtues of GIT as google will happily do that for you, I am however going to explain why I need to use it.

    I have 5 separate projects that all use something from Castle. Keeping track of which project uses which build can be very difficult espcially since I always run from castle trunk. Subversion would have me create a separate vendor branch in each project and stick mirror the castle repo there. That is all well and good until I start fixing bugs or making enhancements. As soon as that happens things get hairy.

    My solution is to have a git repository that mirrors the castle svn but contains separate branches for each product that is using it.

    On my system git branch -a will list something like

    castle-svn/trunk

    castle-svn/branches

    castle-svn/tags

    Product1/current

    Product2/current

    MyPatches/MR-XXX

    MR-Restsupport

    .....

    Now when I need to jump between projects and dig into castle source it is as simple as

    git checkout Product1/current

    I can also freely move patches around and stay up to date with the latest and greatest being produced by the castle community.

    Setting up the git mirror can be a time consuming process. You do have the option of only pulling history up to the last N revisions, but I like full history so I can visualize how things have changed. Pulling the full history takes a while and puts an increased load on Hammett's servers.

    That is why I did the full import and published the mirror to http://repo.or.cz which provides public git hosting.

    Now the good stuff. To get started you want to install cygwin + git. Git is a cygwin package so this is easy. I believe there is a MinGW port in progress, but I already had cygwin so this was easy for me.

    Now to clone the repo simply do

    git clone -n --origin castle-svn git://repo.or.cz/castle.git

    These are not the standard options, so let me explain.

    -n tells git to not checkout anything after it copies. I like this because a master branch isn't very meaningful to me in this context.

     --origin castle-svn tells git to place branches in from the remote repository into /refs/remotes/castle-svn instead of /refs/remotes/origin (the default). This again is just for simplicity on my part. I like doing git checkout trunk castle-svn/trunk better than git checkout trunk origin/trunk

    What you have at this point is a git repo that has all the SVN history. On my machine this actually takes up less space than the svn checkout

    That is all well and good but you only have the snapshot of the trunk as of when I created the git repo.

    At some point I will setup up an automated pull to keep history up to date, if you want to be able send a commit to the castle server you will need svn metadata anyway. To do that we use:

    git svn init https://svn.castleproject.org/svn/castle -T trunk -t tags -b branches

    This will create a new .git/svn folder and add some options to your .git/config file.

    Because we're doing things a little custom from the default behaviour of git-svn we need to edit this .git/config file to tell it where our subversion revisions go.

    Open up .git/config in your favorite text editor and make the svn-remote section look like this

     

    [svn-remote "svn"]
        url = https://svn.castleproject.org/svn/castle
        fetch = trunk:refs/remotes/castle-svn/trunk
        branches = branches/*:refs/remotes/castle-svn/branches/*
        tags = tags/*:refs/remotes/castle-svn/tags/*

    The stuff to the right of the equals sign is called a refspec and it tells git-svn that heads from svn under trunk goto refs/remotes/castle-svn/trunk (not the wildcard for branches and tags). This is creating remote-tracking branches in git.

    With this in place you can now do git svn fetch and git will check your history against all the castle revisions. This does take a minute or so, but it is much faster than having to pull all the svn file data.

    So now you want to work with RC3?

    git checkout -b RC3 castle-svn/tags/1.0.x-RC3

    What about the trunk?

    git checkout -b trunk castle-svn/trunk

    Maybe you need RC2

    git checkout -b RC2 castle-svn/tags/1.0.x-RC2

    Try switching between RC2 and RC3 and see how it compares to the paultry svn-switch.

    In our next installment we'll look at fixing a bug and creating the patch.

    Cheers

    PS: Trouble committing? Check here
    PPS: My list of git resources so far http://del.icio.us/chrisortman/git http://del.icio.us/chrisortman/git-svn

  • Refactoring in practice

    I've recently inherited some code that needs a bit of polish.

    Here's an example of one method I didn't like:

     

    /********************************************************
     *Sunday == 1 and Saturday == 7
     * In C#, Sunday == 0 and Saturday == 6
     * *****************************************************/
    public DateTime GetWeekEndDayDate(DateTime date, int WeekEndDay) {
    			
    	int temp = 0;
    	DateTime retval = DateTime.Now;
    	int Day = (int)date.DayOfWeek;
    	WeekEndDay--;//decrease by 1 so it lines up with C# enumeration of days
    
    	//get the difference in days
    	if (WeekEndDay > Day)
    		temp = WeekEndDay - Day;
    	else if (Day > WeekEndDay)
    		temp = WeekEndDay - Day + 7;
    	else
    		temp = 0;//currently on WeekEndDay
    
    	retval = date.AddDays((double)temp);
    	return retval;
    
    }

    So, how to fix?

    I need to verify that after my refactoring I still get the correct result. Since this class lacks a constructor that I can use without going through some other non-related initialization I've added my own constructor for the time being:

    public Tables(bool skipInitialization) {}
     

    I will use this in my test case that will check the existing behavior.

     

    [RowTest]
    [Row("10/15/2007",1)]		
    [Row("10/15/2007",2)]
    [Row("10/15/2007",3)]
    [Row("10/16/2007",4)]
    [Row("10/15/2007",5)]
    [Row("10/15/2007",6)]
    [Row("10/15/2007",7)]
    public void TestInputs_WithExpectedValuesFromOldAlgorithm(string startDate, int dayOfWeekIntValue) {
    
    	bool skipInitialization = true;
    	Tables t = new Tables(skipInitialization);
    	DateTime start = DateTime.Parse(startDate);
    	DateTime converted = t.GetWeekEndDayDate(start, dayOfWeekIntValue);
    	System.Diagnostics.Debug.WriteLine(converted);
    }

    All I want to do here is pass in some input and record the output. Once I have the output I change the test method and give it some values to check against.

     

    [RowTest]
    [Row("10/15/2007",1,"10/21/2007")]		
    [Row("10/15/2007",2,"10/15/2007")]
    [Row("10/15/2007",3,"10/16/2007")]
    [Row("10/16/2007",4,"10/17/2007")]
    [Row("10/15/2007",5,"10/18/2007")]
    [Row("10/15/2007",6,"10/19/2007")]
    [Row("10/15/2007",7,"10/20/2007")]
    public void TestInputs_WithExpectedValuesFromOldAlgorithm(string startDate, int dayOfWeekIntValue,string expected) {
    	bool skipInitialization = true;
    	Tables t = new Tables(skipInitialization);
    	DateTime start = DateTime.Parse(startDate);
    	DateTime converted = t.GetWeekEndDayDate(start, dayOfWeekIntValue);
    	Assert.AreEqual(DateTime.Parse(expected),converted);
    }

    This test should of course pass, but now I can do some real work. Since the bulk of this is just a generic date manipulation and not the primary responsibility of this class I want to extract that into a helper method. For now I'm must putting it into a Util class that has a bunch of static methods.

     

    public class Util {
    	public static DateTime GetWeekEndDayDate(DateTime date, int WeekEndDay) {
    		int temp = 0;
    		DateTime retval = DateTime.Now;
    		int Day = (int)date.DayOfWeek;
    		WeekEndDay--;//decrease by 1 so it lines up with C# enumeration of days
    
    		//get the difference in days
    		if (WeekEndDay > Day)
    			temp = WeekEndDay - Day;
    		else if (Day > WeekEndDay)
    			temp = WeekEndDay - Day + 7;
    		else
    			temp = 0;//currently on WeekEndDay
    
    		retval = date.AddDays((double)temp);
    		return retval;
    	}
    }

    The goal of this method should just be to advance the input date to the next DayOfWeek passed as the second argument. So I'm going to rename the method to MoveToNext and change the second argument to take a DayOfWeek enumeration value. This means it will be the respibility of the calling code to do that WeekEndDay-- before calling my method.

    I've now got this Util method

     

    public static DateTime MoveToNext(DateTime date, DayOfWeek day) {
    	int temp = 0;
    	DateTime retval = DateTime.Now;
    	int Day = (int)date.DayOfWeek;
    	int dayToMoveTo = (int) day;
    
    	//get the difference in days
    	if (dayToMoveTo > Day)
    		temp = dayToMoveTo - Day;
    	else if (Day > dayToMoveTo)
    		temp = dayToMoveTo - Day + 7;
    	else
    		temp = 0;//currently on WeekEndDay
    
    	retval = date.AddDays((double)temp);
    	return retval;
    }

    And this in my Tables class

     

    public DateTime GetWeekEndDayDate(DateTime date, int WeekEndDay) {
    	DayOfWeek dayValue = (DayOfWeek) WeekEndDay - 1;
    	return Util.MoveToNext(date, dayValue);
    
    }

    At this point my tests all still pass so now it's time to simplify that move to next method. I find it helps to think of this in terms of looking at a calendar and needing to get to the next Saturday or Monday or whatever. So that's what the code should do. I should be able to remove the conditionals and just walk the calendar until I get to the correct day. Rewritten with this in mind my method becomes

     

    public static DateTime MoveToNext(DateTime date, DayOfWeek day) {
    		
    	DateTime retval = date;
    	while(retval.DayOfWeek != day) {
    		retval = retval.AddDays(1);
    	}
    
    	return retval;
    }

    IMO this is much simpler to read, and since my tests still pass I haven't changed any functionality. I really wish I could use extension methods in this project because this is just screaming at me for a good case for them. No matter though, I can at least be prepared.I think we can improve this just a bit by renaming the method and changing the order of the parameters.

     

    public static class DateTimeExtensions {
    
    	public static DateTime MoveToNext(DateTime date, DayOfWeek day) {
    		DateTime retval = date;
    		while (retval.DayOfWeek != day) {
    			retval = retval.AddDays(1);
    		}
    
    	        return retval;
    	}
    }

    And, since my tests still pass I am all done.

    Posted Oct 16 2007, 07:09 AM by chrisortman with no comments
    Filed under:
Powered by Community Server (Commercial Edition), by Telligent Systems