Saturday, January 8, 2011

Roy Osherove's String Calculator Kata - My first version

I recently heard about programming kata. So I tried Roy Osherove's String Calculator Kata .

This was my first time and will try it probably more because I like to make it into a video.

The class.

/// 
/// http://www.osherove.com/tdd-kata-1/
/// 
class Calculator {

    private static string[] Split(string value, string [] splitingWords) {

        return value.Split(splitingWords, StringSplitOptions.None);
    }
    private static string [] Split(string value, string splitingWord){

        return value.Split(new string[] { splitingWord }, StringSplitOptions.None);
    }
    private static bool IsNumber(string n) {
        int a = 0;
        return int.TryParse(n, out a);
    }        
    public static int Add(string numbers) {

        string delimiter    = ",";
        string[] delimiters = null;

        if (numbers.StartsWith("//[")) {
                                
            var newLinePos  = numbers.IndexOf("\n");
            delimiters      = numbers.Substring(3, newLinePos - 4).Replace("[", "").Split(']');
            numbers         = numbers.Substring(newLinePos + 1);
        }
        else if (numbers.StartsWith("//")) {

            var newLinePos = numbers.IndexOf("\n");
            if (newLinePos == -1) throw new System.FormatException(String.Format("Invalid custom delimiters in expression '{0}'", numbers));
            delimiter = numbers[newLinePos - 1].ToString();
            numbers   = numbers.Substring(newLinePos + 1);
        }

        numbers = numbers.Replace("\n", delimiter);

        if (numbers.Contains(delimiter + delimiter))
            throw new System.FormatException(String.Format("Invalid expression '{0}'", numbers));

        string[] values;

        if (delimiters != null) {
            values = Split(numbers, delimiters);
        }
        else if (delimiter.Length > 1) {
            values = Split(numbers, delimiter);
        }
        else {
            values = numbers.Split(delimiter[0]);
        }
        int sum    = 0;
        var negativeNumbers = "";

        foreach (var i in values) {

            if ((i != "") && (int.Parse(i) < 0)) {
                negativeNumbers += i + ",";
            }
            if ((i != "") && (int.Parse(i) >= 1000)) { 
            }
            else {
                sum += int.Parse(i == "" ? "0" : i);
            }
        }
        if(!String.IsNullOrEmpty(negativeNumbers))
            throw new System.ArgumentException(String.Format("negatives not allowed '{0}'", negativeNumbers));

        return sum;
    }
}





The unit tests.
[TestClass] 
public class CalculatorUnitTests {
        
    [TestMethod] public void Add_EmptyString() {

        Assert.AreEqual(0, Calculator.Add(""));
    }
    [TestMethod] public void Add_1() {

        Assert.AreEqual(1, Calculator.Add("1"));
    }
    [TestMethod]
    public void Add_1Coma2() {

        Assert.AreEqual(3, Calculator.Add("1,2"));
    }
    [TestMethod]
    public void Add_1Coma2Coma3() {

        Assert.AreEqual(6, Calculator.Add("1,2,3"));
    }
    [TestMethod]
    public void Add_1Coma2Coma3Coma4() {

        Assert.AreEqual(10, Calculator.Add("1,2,3,4"));
    }
    [TestMethod]
    public void Add_1NewLine2Coma3() {

        Assert.AreEqual(6, Calculator.Add("1\n2,3"));
    }
    [TestMethod, ExpectedException(typeof(System.FormatException))]
    public void Add_InvalidFormatMissingParameter() {

        Calculator.Add("1,\n");
    }       
    [TestMethod] public void Add_CustomDelimiter() {

        Assert.AreEqual(3, Calculator.Add("//;\n1;2"));
    }
    [TestMethod, ExpectedException(typeof(System.ArgumentException))]
    public void Add_OneNegativeNumber() {

        Assert.AreEqual(3, Calculator.Add("-1"));
    }
    [TestMethod, ExpectedException(typeof(System.ArgumentException))]
    public void Add_OnePositiveOneNegativeNumber() {

        Assert.AreEqual(3, Calculator.Add("1,-1"));
    }
    [TestMethod]
    public void Add_NumberGreaterThan1000() {

        Assert.AreEqual(2, Calculator.Add("2,1000"));
    }
    [TestMethod]
    public void Add_CustomStringDelimiter() {

        Assert.AreEqual(6, Calculator.Add("//[***]\n1***2***3"));
    }
    [TestMethod]
    public void Add_MultipleCustomStringDelimiter() {

        Assert.AreEqual(6, Calculator.Add("//[*][%]\n1*2%3"));
    }
    [TestMethod]
    public void Add_MultipleCustomStringDelimiterOfSizeGreaterThan1() {

        Assert.AreEqual(6, Calculator.Add("//[***][%%%]\n1***2%%%3"));
    }

    public CalculatorUnitTests() { } private TestContext testContextInstance; public TestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } }
}

No comments:

Post a Comment