Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Thursday, January 19, 2012
( n2 )

Prepare_AllDependencies.bat

checks for .NET4 then runs build..

image

hmm.. possibly a problem with an early version of .NET4.5?

image

also on their wiki is broken.. hmmm

trying to uninstall the CTP

image

cool the build now worked.

Introduction:

N2 first published in 2006.. since then 45 versions have been released with the latest being 2.2 in June 2011.

couldn’t get MVC one to run.. suspect it needs MVC2 and I have 3.

Web Platform Installer has it but version 2.1.

image

can also do webmatrix

Dinamico is the MVC3 one:

image

or templates:

image

Can also get it via NuGet (Dianamico too)…gets Castle Core, NHibernate, Lucene, Castle.Windsor, DotNetZip, SQLLite

image

got this:

image

latest version of log4net i 1.2.11

hmmm..grabbed it from here: http://archive.apache.org/dist/incubator/log4net/1.2.10/  and put the dll in refs.

Then got to installer screen:

image

then admin screen

image

first impressions.. not that slick..looks old…and not many people seem to use it.

videos:

http://training.lithe.net.au/basics/introduction-to-n2

Comments [0] | | # 
# Friday, December 30, 2011
( Patterns | TDD | TekPub )

“An event in C# is a way for a class to provide notifications to clients of that class when some interesting thing happens to an object.”

eg every time we call Add, we want a notification to clients (logger) that something interesting happened.

  • publisher
  • subscriber

Action<string>

- if we were in .NET2 then public event would have to be of some delegate type.

-  Would have to declare a delegate type of function that would return a string

3.5  Action<T>

public class StringCalculator
    {
        //declare and assign it at the same time (so don't have to check it is null every time)
        public event Action<string> OnAdd = delegate { };

        private readonly ILogger logger;
        public ILogger SecondaryLogger { get; set; }

        public StringCalculator(ILogger logger)
        {
            this.logger = logger;
        }

        public int Add(string numbers)
        {
            OnAdd.Invoke(numbers);
            LogNumbers(numbers);
            if (IsEmpty(numbers))
            {
                return DefaultEmptyResult();
            }
            return AllOtherValues(numbers);
        }

declaring the event, and invoking when add is pressed:

[Test]
        public void Add_Always_TriggersOnAddEvent()
        {
            //bool wasTriggered = false;
            string eventParam = null;

            StringCalculator sc = GetDefaultCalc();
            //lambda way
            //sc.OnAdd += s => wasTriggered = true;

            //so whenever someone invokes the event, wasTriggered will turn to true
            //sc.OnAdd += delegate(string s) { wasTriggered = true; };
            
            //register for event
            sc.OnAdd += delegate(string s) { eventParam = s; };

            sc.Add("1");

            //Assert.IsTrue(wasTriggered);
            Assert.AreEqual("1", eventParam);
        }

subscribing to event, passing in a delegate

Extract and Override

very powerful as can break dependencies easily without interfaces

Comments [0] | | # 
# Thursday, December 29, 2011

Delegates – “they encapsulate a bit of behaviour”.. Initally from Jon Skeets Tekpub videos:

    public delegate void Int32Action(int value);

    public class Delegates
    {
        public void RandomRob(int value)
        {
            Console.WriteLine("Delegate implementation: {0}", value);  
        }

        //static means related to the type rather than any specific instance of the type
        public static void StaticRob(int value)
        {
            Console.WriteLine("Static method: {0}", value);  
        }
    }

and calling:

        [Test]
        public void SimpleDelegateFromMethod()
        {
            Delegates target = new Delegates();
            //C#1 syntax
            Int32Action action = new Int32Action(target.RandomRob);

            //same.. calling the delegate which has been setup to be target.RandomRob
            action.Invoke(5);
            action(6);
        }

        [Test]
        public void DelegateFromStaticMethod()
        {
            Int32Action action = new Int32Action(Delegates.StaticRob);
            action.Invoke(7);
        }

 

Events

Observer pattern.

Here we are using pub/sub without events:

    [TestFixture]
    public class FakeEventRaiserTest
    {
        private void ReportToConsole(string text)
        {
            Console.WriteLine("Called: {0}", text);
        }

        [Test]
        public void RaiseEvents()
        {
            //C#1
            //FakeEventHandler handler = new FakeEventHandler(ReportToConsole);
            //C#2 - implicit method group conversions
            FakeEventHandler handler = ReportToConsole;

            FakeEventRaiser raiser = new FakeEventRaiser();
            raiser.DoSomething("Not subscribed");

            raiser.AddHandler(handler);
            raiser.DoSomething("Subscribed");
            
            raiser.AddHandler(handler);
            raiser.DoSomething("Subscribed twice");

            raiser.RemoveHandler(handler);
            raiser.RemoveHandler(handler);
            raiser.DoSomething("Unsubscribed");
        }
    }

asdf

public class FakeEventRaiser
    {
        private FakeEventHandler currentHandler = null;

        public void AddHandler(FakeEventHandler handler)
        {
            currentHandler = currentHandler + handler;
        }

        public void RemoveHandler(FakeEventHandler handler)
        {
            currentHandler = currentHandler - handler;
        }

        public void DoSomething(string text)
        {
            FakeEventHandler tmp = currentHandler;
            if (tmp!=null)
                tmp.Invoke(text);
        }
    }

Longhand EventRaiser:

[TestFixture]
    public class LonghandEventRaiserTest
    {
        private void ReportToConsole(object sender, EventArgs e)
        {
            Console.WriteLine("ReportToConsole was called");
        }

        [Test]
        public void RaiseEvents()
        {
            //C#1
            //FakeEventHandler handler = new FakeEventHandler(ReportToConsole);
            //C#2 - implicit method group conversions
            ClickHandler handler = ReportToConsole;

            var raiser = new LonghandEventRaiser();
            raiser.OnClick("Not subscribed");

            raiser.Click += handler;
            raiser.OnClick("Subscribed");

            raiser.Click += handler;
            raiser.OnClick("Subscribed twice");

            raiser.Click -= handler;
            raiser.Click -= handler;
            raiser.OnClick("Unsubscribed");
        }
    }

asdf

namespace MasteringCSharp.Tests
{
    public delegate void ClickHandler(object sender, EventArgs e);

    public class LonghandEventRaiser
    {
        private ClickHandler currentHandler = null;

        private void AddHandler(ClickHandler handler)
        {
            currentHandler = currentHandler + handler;
        }

        private void RemoveHandler(ClickHandler handler)
        {
            currentHandler = currentHandler - handler;
        }

        public void OnClick(string text)
        {
            ClickHandler tmp = currentHandler;
            if (tmp!=null)
                tmp.Invoke(this, EventArgs.Empty);
        }

        public event ClickHandler Click
        {
            add { AddHandler(value);}
            remove {RemoveHandler(value);}
        }
    }
}

but can simplify..fairly simple implementation of the observer pattern:

    public delegate void ClickHandler(object sender, EventArgs e);

    public class LonghandEventRaiser
    {
        private ClickHandler currentHandler = null;

        public void OnClick(string text)
        {
            ClickHandler tmp = currentHandler;
            if (tmp!=null)
                tmp.Invoke(this, EventArgs.Empty);
        }

        public event ClickHandler Click
        {
            add { currentHandler += value; }
            remove {currentHandler -= value;}
        }
    }

but we can do better!

public void OnClick(string text)
        {
            //Click refers to the field which is auto genned
            ClickHandler tmp = Click;
            if (tmp!=null)
                tmp.Invoke(this, EventArgs.Empty);
        }

        //declared with a field like event
        public event ClickHandler Click;

 

 

Other Ways of thinking of Delegates and Events

“An event in C# is a way for a class to provide notifications to clients of that class when some interesting thing happens to an object”

http://www.akadia.com/services/dotnet_delegates_and_events.html

Calling a function directly:

    class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            string result = mc.Process();
            Console.WriteLine(result);
        }
    }

    public class MyClass
    {
        public string Process()
        {
            return "In Process";
        }
    }

Basic Delegate

Sometimes we don’t want to call a function directly – we’d like to pass it to somebody else so they can call it.

    class Program
    {
        private delegate void LogDelegate(string s);

        static void Main(string[] args)
        {
            LogDelegate consoleLog = new LogDelegate(ConsoleLoggerFunction);
            consoleLog.Invoke("hello world");
            consoleLog("Another way");
        }

        static void ConsoleLoggerFunction(string s)
        {
            Console.WriteLine("Message is: {0}", s);
        }
    }

Calling Static Functions

    class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person();
            Person.LogDelegate logger = new Person.LogDelegate(Logger);
            //implicit method group conversion
            //Person.LogDelegate logger = Logger;
            p.Process(logger);
        }

        private static void Logger(string message)
        {
            Console.WriteLine(message);
        }
    }

    public class Person
    {
        public delegate void LogDelegate(string message);

        public void Process(LogDelegate logDelegate)
        {
            if (logDelegate != null)
                logDelegate.Invoke("Doing stuff");
        }
    }

and then use a FakeLogger in tests:

        [Test]
        public void Process_WhenCalled_ShouldWriteToLogPassedIntoDelegate()
        {
            Person p = new Person();
            Person.LogDelegate logDelegate = new Person.LogDelegate(FakeLogger);

            p.Process(logDelegate);

        }

        private void FakeLogger(string message)
        {
            Console.WriteLine("FakeLogger: {0}", message);
        }

or could have simply used an interface:

******below is just for reference****** actually in the interest of keeping things simple, am going to go for more of an interface style in my TDD/Logging strategy as eventing can get complex quickly.

Calling Member Functions

hmm – could be better

internal class MyClass
    {
        //takes a single string parameter and no return type
        public delegate void LogHandler(string message);

        public void Process(LogHandler lh)
        {
            if (lh != null)
                lh("Process() begin");
            if (lh != null)
                lh("Process() end");
        }
    }

    // The FileLogger class merely encapsulates the file I/O
    public class FileLogger
    {
        FileStream fileStream;
        StreamWriter streamWriter;

        // Constructor
        public FileLogger(string filename)
        {
            fileStream = new FileStream(filename, FileMode.Create);
            streamWriter = new StreamWriter(fileStream);
        }

        // Member Function which is used in the Delegate
        public void Logger(string s)
        {
            streamWriter.WriteLine(s);
        }

        public void Close()
        {
            streamWriter.Close();
            fileStream.Close();
        }
    }

    public class TestApplication
    {
        static void Main(string[] args)
        {
            FileLogger fl = new FileLogger("process.log");
            MyClass mc = new MyClass();

            MyClass.LogHandler myLogger = new MyClass.LogHandler(fl.Logger);
            mc.Process(myLogger);
            fl.Close();
        }
    }

Events

  • Publishers
  • Subscribers

want a simple example of a logger using events - http://stackoverflow.com/questions/8663736/delegate-and-events-architecture-with-a-logger

public class Person
    {
        //takes a single string parameter and no return type
        public delegate void LogHandler(string message);

        // Define an Event based on the above Delegate
        public event LogHandler Log;

        //call the event using OnXXX where XXX is the name of the event
        public void Process()
        {
            OnLog("Process() begin");
            OnLog("Process() end");
        }

        protected void OnLog(string message)
        {
            if (Log != null)
                Log(message);
        }
    }

    public class FileLogger
    {
        FileStream fileStream;
        StreamWriter streamWriter;

        public FileLogger(string filename)
        {
            fileStream = new FileStream(filename, FileMode.Create);
            streamWriter = new StreamWriter(fileStream);
        }

        // Member Function which is used in the Delegate
        public void Logger(string s)
        {
            streamWriter.WriteLine(s);
        }

        public void Close()
        {
            streamWriter.Close();
            fileStream.Close();
        }
    }

    public class TestApplication
    {
        static void ConsoleLogger(string s)
        {
            Console.WriteLine(s);
        }

        static void Main(string[] args)
        {
            FileLogger fl = new FileLogger("process.log");
            Person person = new Person();

            // Subscribe the Functions ConsoleLogger and fl.ConsoleLogger
            person.Log += new Person.LogHandler(ConsoleLogger);
            person.Log += new Person.LogHandler(fl.Logger);

            person.Process();

            fl.Close();
        }
    }

and then:

    public delegate void FakeEventHandler(string reason);

    public class FakeEventRaiser
    {
        private FakeEventHandler currentHandler = null;

        public void AddHandler(FakeEventHandler handler)
        {
            currentHandler = currentHandler + handler;
        }

        public void RemoveHandler(FakeEventHandler handler)
        {
            currentHandler = currentHandler - handler;
        }

        public void DoSomething(string text)
        {
            FakeEventHandler tmp = currentHandler;
            if (tmp!=null)
                tmp.Invoke(text);
        }
    }
Comments [0] | | # 
# Wednesday, December 28, 2011
( TDD | TekPub )

Interaction tests.. ie interaction between objects..usually have to replace one of the objects

source control:

  • git init,
  • git add .
  • git commit –am “initial load”

going back to simple stuff:

If we have a Fake object and we do Asserts against it, it is a Mock object:

//interaction tests!
        [Test]
        public void Add_EmptyString_CallsTheLogger()
        {
            FakeLogger fakeLog = new FakeLogger();
            StringCalculator sc = new StringCalculator(fakeLog);

            sc.Add("");

            StringAssert.Contains("empty", fakeLog.WrittenWith);
        }

poor mans injecting into the constructor a logger

    public class StringCalculator
    {
        private readonly ILogger logger;

        public StringCalculator(ILogger logger)
        {
            this.logger = logger;
        }

        public int Add(string numbers)
        {
            LogNumbers(numbers);
            if (IsEmpty(numbers))
            {
                return DefaultEmptyResult();
            }
            return AllOtherValues(numbers);
        }

        private void LogNumbers(string numbers)
        {
            if (IsEmpty(numbers))
                logger.Write("empty");
            else
                logger.Write(numbers);
        }

        private bool IsEmpty(string numbers)
        {
            return string.IsNullOrEmpty(numbers);
        }

        private static int AllOtherValues(string numbers)
        {
            return int.Parse(numbers);
        }

        private static int DefaultEmptyResult()
        {
            return 0;
        }

 

Call a WebService if the log is full

[TestCase("1")]
        [TestCase("2")]
        [TestCase("333")]
        public void Logging_SingleNumberAndFirstLoggerIsFull_SecondaryLoggerIsCalled(string number)
        {
            FakeLogger firstLogger = new FakeLogger();
            FakeLogger secondLogger = new FakeLogger();

            firstLogger.WillThrow = new Exception("out of space");
            StringCalculator sc = new StringCalculator(firstLogger);
            sc.SecondaryLogger = secondLogger;

            sc.Add(number);

            //secondaryLogger is the mock object
            StringAssert.Contains(number, secondLogger.WrittenWith);
        }

the first test, which then became a testCase.

        public int Add(string numbers)
        {
            LogNumbers(numbers);
            if (IsEmpty(numbers))
            {
                return DefaultEmptyResult();
            }
            return AllOtherValues(numbers);
        }

        private void LogNumbers(string numbers)
        {
            try
            {
                if (IsEmpty(numbers))
                    logger.Write("empty");
                else
                    logger.Write(numbers);
            }
            catch (Exception e)
            {
                SecondaryLogger.Write(numbers);
            }
        }

nice having logic at same level of abstraction.

Comments [0] | | # 
( kata | TDD | TekPub )

Code Kata are exercises that you can do

osherove.com/kata

  • 1 method, 1 parameter
  • 0 or more numbers
  • seperated by a comma

eg var sum = Calculator.Sum(1,3,10);

Alt N for PackageManagerConsole.

Start with simplest test..empty string.

[TestFixture]
    public class StringCalculatorTests
    {
        [Test]
        public void Add_EmptyString_ReturnsZero()
        {
            //start with this even though StringCalculator doesn't exist yet
            StringCalculator sc = new StringCalculator();

            //method doesn't exist
            int result = sc.Add("");

            //first kind of syntax
            //Assert.AreEqual(0, result);
            //second kind of syntax
            Assert.That(result, Is.EqualTo(0));
        }

        [Test]
        public void Add_SingleNumber_ReturnsThatSingleNumber()
        {
            StringCalculator sc = new StringCalculator();

            int result = sc.Add("1");

            Assert.AreEqual(1, result);
        }
    }

    public class StringCalculator
    {
        public int Add(string numbers)
        {
            return 0;
        }
    }

just about to get the 2nd test passing… notice no refactoring yet as only do that when 2 tests are actually passing!

Just assigned Alt Y to rerun all tests so can keep the cursor in the add method.

Ctrl Alt P – assign parameter

String Calc – first bits

nCover

image_thumb1

100% code coverage at the moment.

[TestCase("1,2",3)]
[TestCase("1,3",4)]
[TestCase("4,5",9)]
[TestCase("14,44",58)]
[TestCase("1,2,3", 6)]
[TestCase("1,2,3,4,5", 15)]
public void Add_MultipleNumbers_ReturnsTheSum(string numbers, int expected)
{
StringCalculator sc = MakeCalc();

int result = sc.Add(numbers);

Assert.AreEqual(expected, result);
}

private static StringCalculator MakeCalc()
{
return new StringCalculator();
}
}

public class StringCalculator
{
public int Add(string numbers)
{
if (numbers.Length == 0)
return 0;
if (numbers.Length == 1)
return int.Parse(numbers);

var listOfNumbers = numbers.Split(',');
int sumOfNumbers = 0;
foreach (var number in listOfNumbers)
sumOfNumbers += int.Parse(number);

return sumOfNumbers;

}
}

first shot at getting multiple numbers being summed together

[TestFixture]
    public class StringCalculatorTests
    {
        private static StringCalculator MakeCalc()
        {
            return new StringCalculator();
        }

        [Test]
        public void Add_EmptyString_ReturnsZero()
        {
            //start with this even though StringCalculator doesn't exist yet
            StringCalculator sc = MakeCalc();

            //method doesn't exist
            int result = sc.Add("");

            //first kind of syntax
            //Assert.AreEqual(0, result);
            //second kind of syntax
            Assert.That(result, Is.EqualTo(0));
        }

        [TestCase("1",1)]
        [TestCase("2", 2)]
        public void Add_SingleNumber_ReturnsThatSingleNumber(string numbers, int expected)
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add(numbers);

            Assert.AreEqual(expected, result);
        }

        [TestCase("1,2",3)]
        [TestCase("1,3",4)]
        [TestCase("4,5",9)]
        [TestCase("14,44",58)]
        [TestCase("1,2,3", 6)]
        [TestCase("1,2,3,4,5", 15)]
        public void Add_MultipleNumbers_ReturnsTheSum(string numbers, int expected)
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add(numbers);

            Assert.AreEqual(expected, result);
        }

        [Test]
        public void Add_TwoNumbersWithLineFeedDelimeter_ReturnsTheSum()
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add("1\n2");

            Assert.AreEqual(3, result);
        }

        [TestCase("1\n3", 4)]
        [TestCase("1\n2\n3", 6)]
        [TestCase("1\n222\n3\n20", 246)]
        public void Add_MultipleNumbersWithLineFeedDelimeter_ReturnsTheSum(string numbers, int expected)
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add(numbers);

            Assert.AreEqual(expected, result);
        }

        [Test]
        public void Add_TwoNumbersWithGeneralDelimeter_ReturnsTheSum()
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add("//;\n1;2");

            Assert.AreEqual(3, result);
        }

        [TestCase("//;\n1;2", 3)]
        [TestCase("//;\n1;2;3", 6)]
        [TestCase("//;\n1;2;3;20", 26)]
        [TestCase("//x\n1x2x3x20", 26)]
        public void Add_MultipleNumbersWithGeneralDelimeter_ReturnsTheSum(string numbers, int expected)
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add(numbers);

            Assert.AreEqual(expected, result);
        }

        [Test]
        public void Add_NegativeNumber_ThrowsCorrectExceptionWithNegativeNumber()
        {
            StringCalculator sc = MakeCalc();

            Exception exception = Assert.Throws<Exception>(delegate
                                                                {
                                                                    sc.Add("-1");
                                                                });
            Assert.AreEqual("Negatives not allowed: -1", exception.Message);
        }

        [Test]
        public void Add_MultipleNegativeNumbers_ThrowsAnExceptionWithAllNegativeNumbers()
        {
            StringCalculator sc = MakeCalc();

            Exception exception = Assert.Throws<Exception>(delegate
                                                                {
                                                                    sc.Add("-1,-2");
                                                                });
            
            Assert.AreEqual("Negatives not allowed: -1, -2", exception.Message);
        }

        [TestCase("-1,-2", "-1, -2")]
        [TestCase("-1,-2,-3", "-1, -2, -3")]
        [TestCase("-1,2,-3", "-1, -3")]
        public void Add_MixedMultipleNegativeAndPositiveNumbers_ThrowsAnExceptionWithAllNegativeNumbers(string numbers, string expected)
        {
            StringCalculator sc = MakeCalc();

            Exception exception = Assert.Throws<Exception>(delegate
                                                                {
                                                                    sc.Add(numbers);
                                                                });

            Assert.AreEqual("Negatives not allowed: " + expected, exception.Message);
        }
    }

    public class StringCalculator
    {
        public int Add(string numbers)
        {
            string delimeter;
            if (numbers.Contains("//"))
            {
                delimeter = numbers.Substring(2, 1);
                numbers = numbers.Substring(4, numbers.Length-4);
                numbers = numbers.Replace(delimeter, ",");
            }
            else if (numbers.Contains("\n"))
                numbers = numbers.Replace("\n", ",");

            if (numbers.Length == 0)
                return 0;
            if (numbers.Length == 1)
                return int.Parse(numbers);

            if (numbers.Length == 2)
            {
                int i = int.Parse(numbers);
                if (i < 0)
                    throw new Exception("Negatives not allowed: " + i.ToString());
                return i;
            }

            string[] listOfNumbers = numbers.Split(',');

            string errorMesssage = "Negatives not allowed: ";
            bool showErrorMessage = false;
            foreach (var number in listOfNumbers)
            {
                int i = int.Parse(number);
                if (i < 0)
                {
                    errorMesssage += i.ToString() + ", ";
                    showErrorMessage = true;
                }
            }

            //get rid of final comma and space
            if (showErrorMessage)
            {
                errorMesssage = errorMesssage.Substring(0, errorMesssage.Length - 2);
                throw new Exception(errorMesssage);
            }

            int sumOfNumbers = 0;
            foreach (var number in listOfNumbers)
                sumOfNumbers += int.Parse(number);

            return sumOfNumbers;

        }
    }

first show at just getting tests to pass.

Started refactorugb with help from videos on: http://osherove.com/tdd-kata-1/

linq, extension methods, make more readable using methods

http://vimeo.com/27977192

Comments [0] | | # 
# Tuesday, December 27, 2011

variables or local variables – declared inside methods

fields – variable (public or private) that is a member of a class

property – has a getter or setter

Comments [0] | | # 
( TDD | TekPub )

From tekpub.com

Unit Tests – “.. a test of a small functional piece of code”

run in memory (not db!).. all under our control…always expect unit tests to pass

ie not integration test.. which has dependencies.

 

Creating a new blank solution (actually called it MyProduct)

image

then create a class library called MyProduct.Logic

image

downloaded Resharper.

Alt Enter to get rid of usings

 

image

setting up a separate project for UnitTests (and further down the line, integration tests).

image

installing unit via nuget console window. (Alt t n o)

Don’t need punit or the mock dlls.  So just nunit.framework.

 

tests – don’t test math forumulae.  Just logic.

Test Naming

whats being tested:  Add

scenario:  TwoNumbers

expected behaviour

eg Add_TwoNumbers_ReturnsTheSum

Test Lint – tells you if no asserts.  Logic in tests eg 1+2   Assert.AreEqual(1+2, result)

 

TestDriven.NET

Tools Options Keyboard

he uses alt R to rerun tests ( I use Alt T, and Alt D for debug)

Refactoring

refactor out constructor stuff when > 2 tests

put initialisation in setup – but this can be confusing when looking at a test way down the page.. so refactor to factory

[TestFixture]
    public class MyCalculatorTests
    {
        [Test]
        public void Add_TwoNumbers_ReturnsTheSum()
        {
            MyCalculator c = GetNewCalc();

            int result = c.Add(1, 2);

            Assert.AreEqual(3, result);
        }

        private static MyCalculator GetNewCalc()
        {
            return new MyCalculator();
        }
    }

static factory.

image

but attribute hmmm.. GetNewCalc could throw and exception.

Use Assert.Throws.

Delegates and Lambda expressions

[Test]
        //[ExpectedException(typeof(Exception))]
        public void Add_FirstParamNegative_Throw()
        {
            MyCalculator c = GetNewCalc();

            int ANY_POSITIVE = 1;

            //c.Add(-1, ANY_POSITIVE);

            //parameter is a delegate to a method here
            //but would have same problem as putting in an attribute
            //ie wouldn't know that GetNewCalc didn't fire an exception
            //any line of Code could throw an exception
            //Assert.Throws<Exception>(Code);

            //anonymous delegate or anonymous method
            //even though its in the scope on an anon method
            //it knows about c
            Assert.Throws<Exception>(delegate
                                         {
                                             c.Add(-1, ANY_POSITIVE);
                                         });

            //lambda expression - shorter delegate notation
            //method doesn't have any name.
            //after => body of method only has one line
            //Assert.Throws<Exception>(() => c.Add(-1, ANY_POSITIVE));
        }
  private void Code()
        {
            //c.Add(-1, ANY_POSITIVE);
        }

he prefers the delegate way – things more  readable than lambda

TDD and Learning new things

  • Copy
  • Why
  • Improvise..eg make own naming convention

Start with a failing test

Make test pass – make code as dumb and simple as possible.  Not generic.  Better tests.. better code coverage…how would 11 year old solve it?

Refactor – changing existing code without changing functionality

So, writing the test first for second param being negative throwing:

 

New Feature – Calculator Remembers Last Result

Doing the test first.. so can see that the field / property  c.LastResult doesn’t exist yet!

[Test]
        public void ResultMemory_CallingAdd_ResultIsSavedForLater()
        {
            MyCalculator c = GetNewCalc();
            int result = c.AddPositives(1, 2);
            Assert.AreEqual(3, c.LastResult);
        }

easiest way to make the test pass:

        //a backing field
        private int _lastResult;
        //property (not an auto property)
        public int LastResult
        {
            get { return 3; }
            set { _lastResult = value; }
        }

TestCase – parameterised

[TestCase(1,2,3)]
        public void ResultMemory_CallingAdd_ResultIsSavedForLater(int a, int b, int expected)
        {
            MyCalculator c = GetNewCalc();
            int result = c.AddPositives(a, b);
            Assert.AreEqual(expected, c.LastResult);
        }
Comments [0] | | # 

Alt Enter – get rid of usings, introduce variable

Ctrl R Ctrl R – Rename

Ctrl Alt Space – If have a reference but don’t have a using yet

Naming convention warning:  Alt Enter, Inspection Options, Hint (gets rid of blue squiggly) eg for   Add_TwoNumbers_ReturnsTheSum()

Introduce var.. turn off.

Ctrl R, Ctrl M  - Extract Method   eg MyCalculator c = new MyCalculator();  highlight green bit, then Ctrl R, Ctrl M.  extract as a static method.  Factory.

Resharper Snippets

image

test tab will then do this.  $xxx$ are dynamic, and $END$ is where cursor will end up.

Ctrl E, Ctrl U – Surround with eg Try Catch

Ctrl E, Ctrl C – Cleanup code.. VS is Ctrl K, Ctrl D

image

Ctrl R, Ctrl P – Introduce parameter

Ctrl D – Duplicate

Ctrl R O – Move to Folder

Ctrl x – without highlighting will cut a line

Comments [0] | | # 
# Thursday, December 22, 2011

In my exploration of Open Source projects of interest I came across this one.

http://www.hanselman.com/blog/RemovingDeadTracksDuplicatesThatDontExistFromITunesUsingC.aspx

image

So I think iTunes actually does find duplicates now Smile  .. but interesting code:

 public partial class Form1 : Form
    {
        private volatile bool _shouldStop;
“Volatile: Over-simplifying and paraphrasing:
volatile indicates that every read operation needs to re-read from memory because there might be other threads updating the variable.”
http://stackoverflow.com/questions/1186515/interlocked-and-volatile
Maybe dropped in .NET5? http://www.bluebytesoftware.com/blog/2010/12/04/SayonaraVolatile.aspx

Architecture

All in code behind the winform.

ListView with columns

Label1 and a Progress Bar.

checkBoxRemove

 

Worker is on a different thread to the UI. and is spun up when the button is pressed:

//button1 is find dead tracks
        private void button1_Click(object sender, EventArgs e)
        {
            this._shouldStop = false;
            this.buttonCancel.Enabled = true;
            this.listView1.Items.Clear();

            this.worker = new Thread(this.FindDeadTracks);
            this.worker.Start();
        }
 

Helper methods for FindDeadTracks and RemoveDuplicates

SetupProgress(max value eg trackcount) – this sets up the ProgressBar (we’re on the worker thread so have to invoke back to UI).. which magically calls the SetupProgress again and sets the max value of the progress bar

in FindDeadTracks (worker thread)

//setup the progress control
this.SetupProgress(trackCount);

then in class level

delegate void SetupProgressCallback(int max);
private void SetupProgress(int max)
        {
            if (this.progressBar1.InvokeRequired)
            {
                SetupProgressCallback cb = new SetupProgressCallback(SetupProgress);
                this.Invoke(cb, new object[] { max });
            }
            else
            {
                this.progressBar1.Maximum = max;
                this.progressBar1.Minimum = 1;
                this.progressBar1.Step = 1;
                this.progressBar1.Value = 1;
            }
        }

nifty!  worker thread calls it first, which it then goes into the InvokeRequired section.  Then the delegate calls it.

 

IncrementProgress – both worker methods can call this (its already been setup).  Same as above.. just increments a step

UpdateLabel – updates label1

FindDeadTrack

goes through and looks for an empty filename or an exception on System.File.IO.Exists

Remove Duplicates

Dictionary for trackCollection.  where key string could be:  FarewellYngwie MalmsteenRising Force    notice there are spaces

var trackCollection = new Dictionary<string, IITTrack>();

The app will never add duplicates into the trackCollection

ArrayList for tracksToRemove

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Collections;
using iTunesLib;

namespace iTunesCOMSample
{
    public partial class Form1 : Form
    {
        private volatile bool _shouldStop;
        private Thread worker;

        public Form1()
        {
            InitializeComponent();
        }
        
        private void Form1_Load(object sender, EventArgs e)
        {
            this.label1.Text = "";
            this.buttonCancel.Enabled = false;
        }

        private void RemoveDuplicates()
        {
            //create a reference to iTunes
            var iTunes = new iTunesAppClass();

            //get a reference to the collection of all tracks
            IITTrackCollection tracks = iTunes.LibraryPlaylist.Tracks;

            int trackCount = tracks.Count;
            int numberChecked = 0;
            int numberDuplicateFound = 0;
            //dictionary is key,value
            var trackCollection = new Dictionary<string, IITTrack>();
            var tracksToRemove = new ArrayList();

            //setup the progress control
            this.SetupProgress(trackCount);

            for (int i = trackCount; i > 0; i--)
            {
                if (tracks[i].Kind == ITTrackKind.ITTrackKindFile)
                {
                    if (!this._shouldStop)
                    {
                        numberChecked++;
                        this.IncrementProgress();
                        this.UpdateLabel("Checking track # " + numberChecked.ToString() + " - " + tracks[i].Name);
                        //eg key: FarewellYngwie MalmsteenRising Force"  notice there are spaces

                        string trackKey = tracks[i].Name + tracks[i].Artist + tracks[i].Album;

                        if (!trackCollection.ContainsKey(trackKey))
                            trackCollection.Add(trackKey, tracks[i]);
                        else //if the trackCollection does have this song already
                        {
                            //if not in the same album or not by the same artist?
                            if (trackCollection[trackKey].Album != tracks[i].Album || trackCollection[trackKey].Artist != tracks[i].Artist)
                                trackCollection.Add(trackKey, tracks[i]);
                            //if track in collection has a higherbitrate than current
                            else if (trackCollection[trackKey].BitRate > tracks[i].BitRate)
                            {
                                IITFileOrCDTrack fileTrack = (IITFileOrCDTrack)tracks[i];
                                numberDuplicateFound++;
                                tracksToRemove.Add(tracks[i]);
                            }
                            //bitrate is higher in this one so replace the one in trackCollection with this version.
                            //default just replace the existing track with this one
                            else
                            {
                                IITFileOrCDTrack fileTrack = (IITFileOrCDTrack)tracks[i];
                                trackCollection[trackKey] = fileTrack;
                                numberDuplicateFound++;
                                tracksToRemove.Add(tracks[i]);
                            }                            
                        }
                    }
                }                                
            }

            this.SetupProgress(tracksToRemove.Count);

            //tracksToRemove is an ArrayList
            for (int i = 0; i < tracksToRemove.Count; i++)
            {
                IITFileOrCDTrack track = (IITFileOrCDTrack)tracksToRemove[i];
                this.UpdateLabel("Removing " + track.Name);
                this.IncrementProgress();
                this.AddTrackToList((IITFileOrCDTrack)tracksToRemove[i]);

                if (this.checkBoxRemove.Checked)
                    track.Delete();
            }

            this.UpdateLabel("Checked " + numberChecked.ToString() + " tracks and " + numberDuplicateFound.ToString() + " duplicate tracks found.");
            this.SetupProgress(1);
        }

        private void FindDeadTracks()
        {
            //create a reference to iTunes
            iTunesAppClass iTunes = new iTunesAppClass();

            //get a reference to the collection of all tracks
            IITTrackCollection tracks = iTunes.LibraryPlaylist.Tracks;

            int trackCount = tracks.Count;
            int numberChecked = 0;
            int numberDeadFound = 0;

            //setup the progress control
            this.SetupProgress(trackCount);

            for (int i = trackCount; i > 0; i--)
            {
                if (!this._shouldStop)
                {
                    IITTrack track = tracks[i];
                    numberChecked++;
                    this.IncrementProgress();
                    this.UpdateLabel("Checking track # " + numberChecked.ToString() + " - " + track.Name);
                    
                    if (track.Kind == ITTrackKind.ITTrackKindFile)
                    {
                        IITFileOrCDTrack fileTrack = (IITFileOrCDTrack)track;                        

                        //if the file doesn't exist, we'll delete it from iTunes
                        if (fileTrack.Location == String.Empty)
                        {
                            numberDeadFound++;
                            this.AddTrackToList(fileTrack);

                            if (this.checkBoxRemove.Checked)
                                fileTrack.Delete();
                        }
                        else if (!System.IO.File.Exists(fileTrack.Location))
                        {
                            numberDeadFound++;
                            this.AddTrackToList(fileTrack);

                            if (this.checkBoxRemove.Checked)
                                fileTrack.Delete();
                        }
                    }
                }
            }

            this.UpdateLabel("Checked " + numberChecked.ToString() + " tracks and " + numberDeadFound.ToString() + " dead tracks found.");
            //sets the progressbar back to nothing
            this.SetupProgress(1);
        }

        #region Button clicks
        private void buttonCancel_Click(object sender, EventArgs e)
        {
            this._shouldStop = true;
            this.buttonCancel.Enabled = false;
        }

        //button1 is find dead tracks
        private void button1_Click(object sender, EventArgs e)
        {
            this._shouldStop = false;
            this.buttonCancel.Enabled = true;
            this.listView1.Items.Clear();

            this.worker = new Thread(this.FindDeadTracks);
            this.worker.Start();
        }

        //button2 is duplicates
        private void button2_Click(object sender, EventArgs e)
        {
            this._shouldStop = false;
            this.buttonCancel.Enabled = true;
            this.listView1.Items.Clear();

            this.worker = new Thread(this.RemoveDuplicates);
            this.worker.Start();
        }

        #endregion

        #region Delegate Callbacks
        //delagates for thread-safe access to UI components
        delegate void SetupProgressCallback(int max);
        delegate void IncrementProgressCallback();
        delegate void UpdateLabelCallback(string text);
        delegate void CompleteOperationCallback(string message);
        delegate void AddTrackToListCallback(IITFileOrCDTrack fileTrack);
        
        private void IncrementProgress()
        {
            if (this.progressBar1.InvokeRequired)
            {
                IncrementProgressCallback cb = new IncrementProgressCallback(IncrementProgress);
                this.Invoke(cb, new object[] { });
            }
            else
            {
                this.progressBar1.PerformStep();
            }
        }

        private void UpdateLabel(string text)
        {
            if (this.label1.InvokeRequired)
            {
                UpdateLabelCallback cb = new UpdateLabelCallback(UpdateLabel);
                this.Invoke(cb, new object[] { text });
            }
            else
            {
                this.label1.Text = text;
            }
        }

        private void CompleteOperation(string message)
        {
            if (this.label1.InvokeRequired)
            {
                CompleteOperationCallback cb = new CompleteOperationCallback(CompleteOperation);
                this.Invoke(cb, new object[] { message });
            }
            else
            {
                this.label1.Text = message;
            }
        }

        private void AddTrackToList(IITFileOrCDTrack fileTrack)
        {
            if (this.listView1.InvokeRequired)
            {
                AddTrackToListCallback cb = new AddTrackToListCallback(AddTrackToList);
                this.Invoke(cb, new object[] { fileTrack });
            }
            else
            {
                this.listView1.Items.Add(new ListViewItem(new string[] { fileTrack.Name, fileTrack.Artist, fileTrack.Location, fileTrack.BitRate.ToString() }));
            }
        }

        private void SetupProgress(int max)
        {
            if (this.progressBar1.InvokeRequired)
            {
                SetupProgressCallback cb = new SetupProgressCallback(SetupProgress);
                this.Invoke(cb, new object[] { max });
            }
            else
            {
                this.progressBar1.Maximum = max;
                this.progressBar1.Minimum = 1;
                this.progressBar1.Step = 1;
                this.progressBar1.Value = 1;
            }
        }
        #endregion

    }
}
Comments [0] | | #