Sierra
Sierra

Reputation: 609

C#: Unit Test to Differentiate CurrentCulture from InvariantCulture

I have a method which takes a StringComparison argument, and it really needs a unit test to prove it is performing the right comparison type.

This is basically what I have so far. It differentiates Ordinal from the rest by comparing "oe" and "œ" (see System.String). I haven't, however, found a way to differentiate CurrentCulture from InvariantCulture.

using System;
using NUnit.Framework;
using System.Threading;
using System.Globalization;

namespace NUnitTests
{
    [TestFixture]
    public class IndexOfTests
    {
        [Test]
        public void IndexOf_uses_specified_comparison_type()
        {
            StringComparison comparisonTypePerformed;

            result = TestComparisonType(StringComparison.CurrentCulture);
            Assert.AreEqual(StringComparison.CurrentCulture, comparisonTypePerformed);

            result = TestComparisonType(StringComparison.InvariantCulture);
            Assert.AreEqual(StringComparison.CurrentCulture, comparisonTypePerformed);

            result = TestComparisonType(StringComparison.Ordinal);
            Assert.AreEqual(StringComparison.CurrentCulture, comparisonTypePerformed);
        }

        bool TestComparisonType(StringComparison comparisonType)
        {
            int result;

            // Ensure the current culture is consistent for test
            var prevCulture = Thread.CurrentThread.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", false);

            try
            {
                result = IndexOf("oe", "œ", comparisonType);
                if (result == 0)
                {
                    // The comparison type performed was either CurrentCulture,
                    // InvariantCulture, or one of the case-insensitive variants.

                    // TODO: Add test to differentiate between CurrentCulture and InvariantCulture
                    throw new NotImplementedException();
                    result = IndexOf("???????", "???????", StringComparison.CurrentCulture);

                    //...
                }
                else // result == -1
                {
                    // The comparison type performed was either Ordinal or OrdinalIgnoreCase

                    result = IndexOf("a", "A", StringComparison.CurrentCulture);
                    if (result)
                        Console.WriteLine("Comparison type was OrdinalIgnoreCase");
                    else
                        Console.WriteLine("Comparison type was Ordinal");
                }
            }
            finally
            {
                Thread.CurrentThread.CurrentCulture = prevCulture;
            }
        }
    }
}

I considered using the Turkish problem by comparing "i" and "I", but it only works for case-insensitive comparisons.

I've looked around and couldn't find a case where InvariantCulture differs from other cultures in a string equality test (only sort order and parsing/serialization). For example, depending on the culture, "ô" and "o" will sort differently, but all cultures return the same result in equality tests.

Is there an equality test to differentiate InvariantCulture from any other culture?

EDIT: The german "ß" and "ss" give the same result for all cultures so they won't work.

EDIT: Really, all that is needed is two strings considered equal in one culture, and unequal in another.

Upvotes: 8

Views: 3728

Answers (3)

Sierra
Sierra

Reputation: 609

Thanks to blackSphere, I was able to find a few differences between some cultures and the InvariantCulture, as shown in the following code:

void Demonstration_For_LL()
{
    int foundPos;

    foundPos = "ll".IndexOf("l", StringComparison.InvariantCulture);
    Assert.AreEqual(0, foundPos);

    Thread.CurrentThread.CurrentCulture = new CultureInfo("sq", false);
    foundPos = "ll".IndexOf("l", StringComparison.CurrentCulture);
    Assert.AreEqual(-1, foundPos);

    Thread.CurrentThread.CurrentCulture = new CultureInfo("sq-AL", false);
    foundPos = "ll".IndexOf("l", StringComparison.CurrentCulture);
    Assert.AreEqual(-1, foundPos);
}

void Demonstration_For_CH()
{
    int foundPos;

    foundPos = "ch".IndexOf("c", StringComparison.InvariantCulture);
    Assert.AreEqual(0, foundPos);

    Thread.CurrentThread.CurrentCulture = new CultureInfo("cs", false);
    foundPos = "ch".IndexOf("c", StringComparison.InvariantCulture);
    Assert.AreEqual(-1, foundPos);

    Thread.CurrentThread.CurrentCulture = new CultureInfo("cs-CZ", false);
    foundPos = "ch".IndexOf("c", StringComparison.InvariantCulture);
    Assert.AreEqual(-1, foundPos);

    Thread.CurrentThread.CurrentCulture = new CultureInfo("sk", false);
    foundPos = "ch".IndexOf("c", StringComparison.InvariantCulture);
    Assert.AreEqual(-1, foundPos);

    Thread.CurrentThread.CurrentCulture = new CultureInfo("sk-SK", false);
    foundPos = "ch".IndexOf("c", StringComparison.InvariantCulture);
    Assert.AreEqual(-1, foundPos);

    Thread.CurrentThread.CurrentCulture = new CultureInfo("vi", false);
    foundPos = "ch".IndexOf("c", StringComparison.InvariantCulture);
    Assert.AreEqual(-1, foundPos);

    Thread.CurrentThread.CurrentCulture = new CultureInfo("vi-VN", false);
    foundPos = "ch".IndexOf("c", StringComparison.InvariantCulture);
    Assert.AreEqual(-1, foundPos);
}

Unfortunately, the string.Compare method doesn't seem to show these differences. Anyway, my test method looks like this now:

class Example
{
    public static void Main()
    {
        var comparisonTypePerformed =
            GetComparisonTypePerformed(
                new TestedMethod(string.IndexOf));

        Console.WriteLine("Comparison type performed was: " + comparisonTypePerformed);
    }

    public static StringComparison GetComparisonTypePerformed(TestedMethod testedMethod)
    {
        StringComparison comparisonTypePerformed;

        var prevCulture = Thread.CurrentThread.CurrentCulture;
        try
        {
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", false);

            // Check if method performs an Ordinal search
            int result = testedMethod("œ", "oe", 0, 1, comparisonType);

            if (result == StringHelper.NPOS)
            {
                // Check if method performs a case-sensitive search
                result = testedMethod("a", "A", 0, 1, comparisonType);

                if (result == StringHelper.NPOS)
                    comparisonTypePerformed = StringComparison.Ordinal;
                else
                    comparisonTypePerformed = StringComparison.OrdinalIgnoreCase;
            }
            else
            {
                Thread.CurrentThread.CurrentCulture = new CultureInfo("sq-AL", false);

                // Check if method uses CurrentCulture or InvariantCulture
                result = testedMethod("ll", new string[] { "l" }, 0, 2, comparisonType);

                if (result == StringHelper.NPOS)
                {
                    // Check if method performs a case-sensitive search
                    result = testedMethod("a", new string[] { "A" }, 0, 1, comparisonType);

                    if (result == StringHelper.NPOS)
                        comparisonTypePerformed = StringComparison.CurrentCulture;
                    else
                        comparisonTypePerformed = StringComparison.CurrentCultureIgnoreCase;
                }
                else
                {
                    // Check if method performs a case-sensitive search
                    result = testedMethod("a", new string[] { "A" }, 0, 1, comparisonType);

                    if (result == StringHelper.NPOS)
                        comparisonTypePerformed = StringComparison.InvariantCulture;
                    else
                        comparisonTypePerformed = StringComparison.InvariantCultureIgnoreCase;
                }
            }
        }
        finally
        {
            Thread.CurrentThread.CurrentCulture = prevCulture;
        }

        return comparisonTypePerformed;
    }

    delegate int TestedMethod(string source, string value, int startIndex, int count, StringComparison comparisonType);
}

Upvotes: 3

shinybluesphere
shinybluesphere

Reputation: 355

The MSDN Sample on CultureInfo has an example with different sort orders depending on culture. I think that's your most likely bet for a starting place. However you may be able to take advantage of some of the information below if that doesn't work.

It looks like a similar set of tests is being performed on the String.Compare MSDN page. In this example though, it is in the en-US culture.

If you look at the CultureInfo.InvarientCulture page, you'll notice that the InvarientCulture is based on English, but without any regional or country encoding. And the StringComparison page mentions that hyphens specifically have a different weight, depending on language. You may be able to take advantage of that by finding a language that weights hyphens differently than English.

Wikipedia has some interesting notes on sorting in the collation article, that may also be useful. Look specifically through Sorting, alphabetical, and then look at the Spanish notes.

For example, the 29-letter alphabet of Spanish treats ñ as a basic letter following n, and formerly treated ch and ll as basic letters following c and l, respectively. Ch and ll are still considered letters, but are now alphabetized as two-letter combinations.

I'm not sure how helpful this will be, but it may lead you to an answer.

Upvotes: 3

to StackOverflow
to StackOverflow

Reputation: 124696

Try coté and côte with fr-FR as your current culture.

Or use this MSDN resource to find other examples.

I've looked around and couldn't find a case where InvariantCulture differs from other cultures by string comparison (only sort order and parsing/serialization).

I don't understand this comment - surely the sort order uses a StringComparison.

Upvotes: 1

Related Questions