Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this assert throw a format exception when comparing structures?

I'm trying to assert the equality of two System.Drawing.Size structures, and I'm getting a format exception instead of the expected assert failure.

[TestMethod] public void AssertStructs() {     var struct1 = new Size(0, 0);     var struct2 = new Size(1, 1);      //This throws a format exception, "System.FormatException: Input string was not in a correct format."     Assert.AreEqual(struct1, struct2, "Failed. Expected {0}, actually it is {1}", struct1, struct2);       //This assert fails properly, "Failed. Expected {Width=0, Height=0}, actually it is {Width=1, Height=1}".     Assert.AreEqual(struct1, struct2, "Failed. Expected " + struct1 + ", actually it is " + struct2);  } 

Is this intended behavior? Am I doing something wrong here?

like image 573
Kyle Avatar asked Feb 19 '13 18:02

Kyle


People also ask

What is a format exception?

A FormatException exception can be thrown for one of the following reasons: In a call to a method that converts a string to some other data type, the string doesn't conform to the required pattern. This typically occurs when calling some methods of the Convert class and the Parse and ParseExact methods of some types.

Which exception may be thrown when a user inputs a string when the application expected a decimal number?

The NumberFormatException occurs when an attempt is made to convert a string with improper format into a numeric value. That means, when it is not possible to convert a string in any numeric type (float, int, etc), this exception is thrown.

What is string format in Java?

In java, String format() method returns a formatted string using the given locale, specified format string, and arguments. We can concatenate the strings using this method and at the same time, we can format the output concatenated string. Syntax: There is two types of string format() method.


2 Answers

I've got it. And yes, it's a bug.

The problem is that there are two levels of string.Format going on here.

The first level of formatting is something like:

string template  = string.Format("Expected: {0}; Actual: {1}; Message: {2}",                                  expected, actual, message); 

Then we use string.Format with the parameters you've supplied:

string finalMessage = string.Format(template, parameters); 

(Obviously there's cultures being provided, and some sort of sanitization... but not enough.)

That looks fine - unless the expected and actual values themselves end up with braces in, after being converted to a string - which they do for Size. For example, your first size ends up being converted to:

{Width=0, Height=0} 

So the second level of formatting is something like:

string.Format("Expected: {Width=0, Height=0}; Actual: {Width=1, Height=1 }; " +               "Message = Failed expected {0} actually is {1}", struct1, struct2); 

... and that's what's failing. Ouch.

Indeed, we can prove this really easily by fooling the formatting to use our parameters for the expected and actual parts:

var x = "{0}"; var y = "{1}"; Assert.AreEqual<object>(x, y, "What a surprise!", "foo", "bar"); 

The result is:

Assert.AreEqual failed. Expected:<foo>. Actual:<bar>. What a surprise! 

Clearly broken, as we weren't expecting foo nor was the actual value bar!

Basically this is like a SQL injection attack, but in the rather less scary context of string.Format.

As a workaround, you can use string.Formatas StriplingWarrior suggests. That avoids the second level of formatting being performed on the result of formatting with the actual/expected values.

like image 134
Jon Skeet Avatar answered Oct 13 '22 00:10

Jon Skeet


I think you've found a bug.

This works (throws an assert exception):

var a = 1; var b = 2; Assert.AreEqual(a, b, "Not equal {0} {1}", a, b); 

And this works (outputs the message):

var a = new{c=1}; var b = new{c=2}; Console.WriteLine(string.Format("Not equal {0} {1}", a, b)); 

But this doesn't work (throws a FormatException):

var a = new{c=1}; var b = new{c=2}; Assert.AreEqual(a, b, "Not equal {0} {1}", a, b); 

I can't think of any reason this would be expected behavior. I'd submit a bug report. In the meantime, here's a workaround:

var a = new{c=1}; var b = new{c=2}; Assert.AreEqual(a, b, string.Format("Not equal {0} {1}", a, b)); 
like image 44
StriplingWarrior Avatar answered Oct 12 '22 23:10

StriplingWarrior