
How We Push the Boundaries of Research
Zoom in so far that you can see atoms? These days, that’s not a problem.
Go to content | Go to the main menu | Go to search
It’s 2001. I’m sitting in Building 9 on Microsoft’s main campus in Redmond, and my team is working hard on version 1.1 of the .NET Framework.
I keep asking myself why we’re still struggling to make all classes in what we call the BCL – Base Class Libraries – behave robustly in a truly global environment, across cultures and languages.
My manager calls me, slightly irritated, complaining about the lack of awareness among developers. He sets up a meeting with the static code analysis team. We want to add a few new rules to help developers catch common errors—errors that typically surface only when a user switches the Windows language setting.
One of our own classes comes to mind immediately: System.Globalization.RegionInfo. It has a constructor that takes a string as input. That string can be either an ISO 3166 region code or a culture/language name. An instance of this type then provides region-related information associated with the given code or culture.
I set the Windows active culture to Turkish and try creating a RegionInfo for the code "IT" – Italy. That works. Then I try the same with "it". The constructor should simply normalize the code to "IT" (via ToUpper) and return the same. Instead, it throws an error, saying the region doesn’t exist.
Cold sweat breaks out on my forehead. This is not good. How will my manager react when I tell him that even our own programmers haven’t fully absorbed the very guidance we’ve been trying so hard to promote? And that I, as a tester, hadn’t caught this earlier? How will the millions of developers building on .NET deal with this?
The issue was with normalizing the input using ToUpper. The String class has two overloads of this method:
As a developer, it’s natural to assume that normalizing an ISO 3166 code shouldn’t depend on the user’s language setting. So you pick the simpler version without CultureInfo. But here’s the catch: that overload actually uses CultureInfo.CurrentCulture by default, which reflects the user’s Windows language settings.
This fails in languages with special casing rules. Turkish, for example, has four variants of the letter “i”: i, İ, ı, I. In Turkish, the uppercase of “i” is “İ” (capital I with a dot).
So what happened was: "it".ToUpper() under Turkish culture = "İT". And that’s not a valid region code.
Short answer: incomplete partition into equivalence classes. A basic breakdown of test cases for the RegionInfo(string name) constructor might look like this:
Case |
Input |
Expected Result |
Invalid – null | null | ArgumentNullException |
Invalid – empty | “” | ArgumentException |
Invalid – unknown code/culture | “ABCD” | ArgumentException |
Invalid – neutral culture (no region) | “en” | ArgumentException |
Valid – specific culture | “en-US” | Works as expected |
Valid – ISO 3166 code | “IT” | Works as expected |
Valid – ISO 3166 code, normalization | “it” | Works as expected |
But this partitioning ignored the fact that normalization relies on ToUpper and, in turn, on the current Windows culture. The corrected partitioning looks more like this:
Case |
Parametr name |
Expected Result |
Current Culture |
Invalid – null | null | ArgumentNullException | N/A |
Invalid – empty | “” | ArgumentException | |
Invalid – unknown code/culture | “ABCD” | ArgumentException | |
Invalid – neutral culture without region | “en” | ArgumentException | |
Valid – specific culture | “en-US” | Ok | |
Valid – specific culture | “it-it” | Ok | en-US |
Valid – specific culture | “it-it” | Ok | tr-TR |
Valid – ISO 3166 code | “IT” | Ok | |
Valid – ISO 3166 code, normalization | “it” | Ok | en-US |
Valid – ISO 3166 code, normalization | “it” | Ok | tr-TR |
The RegionInfo constructor wasn’t the only problem. Many string operations had to be rewritten so that, unless explicitly culture-sensitive, they used the invariant culture. This ensured that string operations behaved consistently, regardless of the user’s Windows language setting.
For RegionInfo, that meant using ToUpper(CultureInfo.InvariantCulture).
But the root cause was deeper: the String class itself. Most of its methods default to using CurrentCulture. Changing that default was impossible, since System.String was already public, and compatibility had to be preserved.
Over time, several mitigations were introduced:
None of this could undo the unfortunate original design—it could only mitigate its negative impact.
I can almost hear my teenage sons as I write this: “.NET 1.1, did that even exist?” Every time I invite them to watch one of my favorite movies, they first ask, “Is it in color or black-and-white?”
The present is certainly in color. But some things remain black-and-white: Either your partitioning into equivalence classes is complete, or it isn’t. And if it isn’t, you’ve got a problem.
Zoom in so far that you can see atoms? These days, that’s not a problem.
When OpenAI introduced ChatGPT two years ago, some feared that developers and testers would lose their jobs. But two years on, we can safely say that hasn’t happened. So, where did those predictions go wrong — and what new challenges has AI really brought us?
Robot Framework is a popular Python-based testing tool maintained by the community and available free of charge. This gives it a great price/performance ratio. Its syntax is based on keywords. But can Robot Framework handle BDD and Gherkin? Let’s take a closer look.
Thank you for your interest in subscribing to our newsletter! To complete your registration you need to confirm your subscription. We have just sent you a confirmation link to the email address you provided. Please click on this link to complete your registration. If you do not find the email, please check your spam or "Promotions" folder.