Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Add System.ComponentModel.Annotations tests #20934

Merged
merged 4 commits into from
Jun 27, 2017
Merged

Add System.ComponentModel.Annotations tests #20934

merged 4 commits into from
Jun 27, 2017

Conversation

hughbe
Copy link

@hughbe hughbe commented Jun 12, 2017

No description provided.

<data name="MinLengthAttribute_InvalidMinLength" xml:space="preserve">
<value>MinLengthAttribute must have a Length value that is zero or greater.</value>
</data>
<data name="MinLengthAttribute_ValidationError" xml:space="preserve">
<value>The field {0} must be a string or array type with a minimum length of '{1}'.</value>
</data>
<data name="MinLengthAttribute_InvalidValueType" xml:space="preserve">
<value>The field of type {0} must be a string, array or ICollection type.</value>
</data>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same string as MaxLengthAttribute_InvalidValueType... why do we need both?

@@ -56,35 +56,23 @@ protected override ValidationResult IsValid(object value, ValidationContext vali
{
if (OtherPropertyDisplayName == null)
{
OtherPropertyDisplayName = GetDisplayNameForProperty(validationContext.ObjectType, OtherProperty);
OtherPropertyDisplayName = GetDisplayNameForProperty(validationContext.ObjectType, otherPropertyInfo);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look above, we have the following code:

var otherPropertyInfo = validationContext.ObjectType.GetRuntimeProperty(OtherProperty);

In the GetDisplayNameForProperty method, we used to have the buggy code:

var property = containerType.GetRuntimeProperties()
 .SingleOrDefault(
    prop =>
        IsPublic(prop) &&
        string.Equals(propertyName, prop.Name, StringComparison.OrdinalIgnoreCase));

However, we already have the property otherPropertyInfo above. The name comparison is broken, and GetRuntimeProperty only returns public methods so this whole thing is redundant and broken.

So we can just use the actual PropertyInfo we found before

var attributes = CustomAttributeExtensions.GetCustomAttributes(property, true);
var display = attributes.OfType<DisplayAttribute>().FirstOrDefault();
if (display != null)
{
return display.GetName();
}

return propertyName;
return OtherProperty;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old value passed to the parameter propertyName was OtherProperty. Now that that parameter is gone, we use OtherProperty directly

}

throw;
throw tie.InnerException;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know for sure that InnerException will always be non-null in all circumstances?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the try block:

try
{
    // 1-parameter form is ValidationResult Method(object value)
    // 2-parameter form is ValidationResult Method(object value, ValidationContext context),
    var methodParams = _isSingleArgumentMethod
        ? new object[] { convertedValue }
        : new[] { convertedValue, validationContext };

    var result = (ValidationResult)methodInfo.Invoke(null, methodParams);

    // We capture the message they provide us only in the event of failure,
    // otherwise we use the normal message supplied via the ctor
    _lastMessage = null;

    if (result != null)
    {
        _lastMessage = result.ErrorMessage;
    }

    return result;
}
catch (TargetInvocationException tie)
{
    throw tie.InnerException;
}

The only place where the TIE can be thrown is from reflection invoke, and there will always be a non-null inner exception in this case.

// A cast exception previously occurred if a non-{string|array} property was passed
// in so preserve this behavior if the value does not implement ICollection
length = ((Array)value).Length;
throw new InvalidCastException(SR.Format(SR.MaxLengthAttribute_InvalidValueType, value.GetType()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if it is an array? You're removing functionality here which was added to solve a previous issue.


In reply to: 121382258 [](ancestors = 121382258)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this was addressing https://github.com/dotnet/corefx/issues/18361


In reply to: 121792346 [](ancestors = 121792346,121382258)

@@ -212,12 +213,9 @@ internal bool TryGetPropertyStoreItem(string propertyName, out PropertyStoreItem

if (_propertyStoreItems == null)
{
lock (_syncRoot)
lock (_lock)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can tell, this is a buggy change. Why did you change this? It's now using a process-wide lock to protect instance state, and it removed the double-check that would ensure that multiple threads always see the same value even in the face of race conditions.

Copy link
Author

@hughbe hughbe Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. I wasn't sure about the double check. I reverted this change

@@ -236,7 +237,7 @@ public static void ValidateProperty(object value, ValidationContext validationCo
var err = GetValidationErrors(value, validationContext, attributes, false).FirstOrDefault();
if (err != null)
{
err.ThrowValidationException();
throw err.GetValidationException();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change these? If it's purely about getting to 100% code coverage by allowing the compiler to see that the subsequent brace is unreachable, that's not worth the change.

Copy link
Author

@hughbe hughbe Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was why, yes. I reverted this change

}

[Serializable]
public class SubException : ValidationException
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is a SubException type necessary for this test? Shouldn't a PNSE be produced as well when working with ValidationException directly?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValidationException is not [Serializable] so can't be serialized. The test is deserialization, so obviously if we can't serialize, then we can't test deserialization

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've deleted this test

// A cast exception previously occurred if a non-{string|array} property was passed
// in so preserve this behavior if the value does not implement ICollection
length = ((Array)value).Length;
throw new InvalidCastException(SR.Format(SR.LengthAttribute_InvalidValueType, value.GetType()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw new InvalidCastException(SR.Format(SR.LengthAttribute_InvalidValueType, value.GetType())); [](start = 20, length = 96)

As for MaxLengthAttribute: what if it is an array? You're removing functionality here which was added to solve a previous issue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that's what I first thought when I saw the code and why I fixed this. All Arrays are ICollections, so this code path will never be hit as they will take the precious if block. We have tests that cover this in the project.
What this is actually doing is simulating an InvalidCastException for comparability with before we added ICollection support.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense - thanks for clarifying.

@lajones
Copy link
Contributor

lajones commented Jun 13, 2017

@hughbe General comment - could we have a separate PR for each issue please? Would like to address the fix for #20932 separately from the addition of other tests.

@hughbe
Copy link
Author

hughbe commented Jun 13, 2017

Gladly! Will do so soon

@hughbe hughbe changed the title Add System.ComponentModel.Annotations tests and fix a netfx compat bug Add System.ComponentModel.Annotations tests Jun 16, 2017
@hughbe
Copy link
Author

hughbe commented Jun 16, 2017

I've extracted the netfx compat fix to #21031, this just adds some tests and cleans up some code

@hughbe
Copy link
Author

hughbe commented Jun 19, 2017

Test Portable Linux x64 Debug Build

@hughbe
Copy link
Author

hughbe commented Jun 19, 2017

I rebased this PR to fix merge conflicts

@@ -69,6 +69,26 @@ public static void Constructor(Type validatorType, string method)
Assert.Equal(method, attribute.Method);
}

[Fact]
public void FormatErrorMessage_NoError_ContainsName()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Just checking - did you mean this to say "NoError"? The test is about what happens to the error message? The test below checks that validation fails - so did you mean "validation passes" here? If so maybe add that assert or rename the tests.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not quite that validation passes. It's actually that no validation has ever been performed on that instance - i've updated the comments accordingly

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Now it all makes sense. Thanks for the updates.

@lajones
Copy link
Contributor

lajones commented Jun 20, 2017

@ajcvickers This looks goods to me. Do you want to review before @hughbe merges this in?

@hughbe
Copy link
Author

hughbe commented Jun 21, 2017

I don't actually have permission to merge it in as I'm not an MSFT employee so that's up to you guys :) thanks for reviewing

@lajones
Copy link
Contributor

lajones commented Jun 21, 2017

My bad. I meant "before we merge this in for @hughbe" 😄

@hughbe
Copy link
Author

hughbe commented Jun 27, 2017

Friendly ping :)

@lajones lajones merged commit 1a76f61 into dotnet:master Jun 27, 2017
@hughbe hughbe deleted the componentmodel-annotations-tests branch June 27, 2017 23:54
dotnet-maestro-bot pushed a commit to dotnet-maestro-bot/corefx that referenced this pull request Nov 12, 2018
* Moving the Utf8Formatter and Utf8Parser into S.P.Corelib

* Doing some minimal cleanup to lineup types and get the Utf8Parser/Utf8Formatter building

* Updating the Utf8 Float Parser to have different buffers for Single vs Double

* Fixing the Utf8Parser to track trailing zero digits and to properly mark the end of the buffer

* Fixing a couple of issues in Utf8Parser.Number

Signed-off-by: dotnet-bot <[email protected]>
tannergooding added a commit that referenced this pull request Nov 13, 2018
* Moving the Utf8Formatter and Utf8Parser into S.P.Corelib

* Doing some minimal cleanup to lineup types and get the Utf8Parser/Utf8Formatter building

* Updating the Utf8 Float Parser to have different buffers for Single vs Double

* Fixing the Utf8Parser to track trailing zero digits and to properly mark the end of the buffer

* Fixing a couple of issues in Utf8Parser.Number

Signed-off-by: dotnet-bot <[email protected]>
jlennox pushed a commit to jlennox/corefx that referenced this pull request Dec 16, 2018
* Moving the Utf8Formatter and Utf8Parser into S.P.Corelib

* Doing some minimal cleanup to lineup types and get the Utf8Parser/Utf8Formatter building

* Updating the Utf8 Float Parser to have different buffers for Single vs Double

* Fixing the Utf8Parser to track trailing zero digits and to properly mark the end of the buffer

* Fixing a couple of issues in Utf8Parser.Number

Signed-off-by: dotnet-bot <[email protected]>
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…otations-tests

Add System.ComponentModel.Annotations tests

Commit migrated from dotnet/corefx@1a76f61
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants