Go to content | Go to the main menu | Go to search

edhouse-CookieGdpr-Policy-s
1073657
2
/en/gdpr/
208650B6A

Back to Blog

Lessons learned SQA

Professional Blunders: Why a Complete Partition into Equivalence Classes Matters

17.9.2025 Juraj Danihel

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.

RegionInfo Sample
RegionInfo Sample

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?

What went wrong?

The issue was with normalizing the input using ToUpper. The String class has two overloads of this method:

  1. Without parameters: returns the string converted to uppercase.
  2. With a CultureInfo parameter: returns the uppercase string according to the rules of the given culture.

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.

Why did tests miss it?

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 

Consequences and Fix

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:

  1. Static analysis rules warning developers about these pitfalls.
  2. New invariant methods, like ToUpperInvariant(), to encourage safer usage.
  3. Documentation outlining best practices for handling strings in .NET.
         

None of this could undo the unfortunate original design—it could only mitigate its negative impact.

Dad, that’s prehistoric!

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.

Share article

Author

Juraj Danihel

Juraj Danihel Test Lead with a passion for technical problem-solving. I enjoy guiding testers across various projects, most recently in the field of electron microscopy.

Edhouse newsletter

Get the latest updates from the world of Edhouse – news, events, and current software and hardware trends.

By signing up, you agree to our Privacy Policy.

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.