Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to check for system members? #198

Open
simonthum opened this issue Dec 11, 2022 · 3 comments
Open

How to check for system members? #198

simonthum opened this issue Dec 11, 2022 · 3 comments
Labels
kind/feature Categorizes issue or PR as related to a new feature.

Comments

@simonthum
Copy link
Contributor

I have difficulties checking for unwanted system references - I failed to jot them down in a statically typed way.

Consider:

IArchRule rule1 =
    Classes().Should().NotCallAny("System.DateTime::get_Now");

This works but is not exactly intuitive. I went on to try:

IArchRule rule2 =
    Classes().Should().NotCallAny(
    MethodMembers().That().AreDeclaredIn(
        Types(true).That().HaveFullName(typeof(DateTime).FullName)));

But there is no MethodMembers(true) that would allow to check for members of system types this way.

Is there another way I'm missing to achieve rule1 statically typed?

@x789
Copy link

x789 commented Feb 28, 2023

I would also like to implement a test that only certain types can access the system time.
However, the solution mentioned by @simonthum unfortunately does not work for me.

I tried unsuccessfully with
Classes().Should().NotCallAny(MethodMembers().That().AreDeclaredIn(typeof(System.DateTimeOffset)));

However, this is reasonable to me since _ = DateTimeOffset.Now is an access to a property.
However, I cannot replace MethodMembers with PropertyMembers because the result is not accepted by NotCallAny.

I wonder if there is a way to check for property access, or is this functionality not (yet) part of ArchUnitNET?

@x789
Copy link

x789 commented Mar 2, 2023

I was wrong -- Properties are a C# feature which are transformed into methods by the compiler. After specifying an empty parameter list and adding the assembly of DateTimeOffset to the architecture, it worked:

var rule = Types().That().ResideInAssembly(myAssembly).Should().NotCallAny("System.DateTimeOffset::get_Now()");

I didn't find a way to define the rule strongly typed either. For the meantime I'm using an extension method. Maybe this is an acceptable workaround for others.

// Extension method
public static IArchRule NotAccessGetter<TType>(this ObjectsShould<TypesShouldConjunction, IType> builder, object? property, [CallerArgumentExpression("property")] string typeAndPropertyName = null!)
{
  var names = typeAndPropertyName.Split('.');
  if (names[0] != typeof(TType).Name) throw new ArgumentException($"'{names[1]}' is not part of '{names[0]}'.");

  return builder.NotCallAny(MethodMembers().That().AreDeclaredIn(typeof(TType)).And().HaveName($"get_{names[1]}()"));
}
// Usage example
[Fact]
public void Test1()
{
  // Remember to add the assembly that contains DateTimeOffset to your `architecture`.
  var types = Types().That().ResideInAssembly(myAssembly);
  types.Should().NotAccessGetter<DateTimeOffset>(DateTimeOffset.Now).Check(architecture);
  types.Should().NotAccessGetter<DateTimeOffset>(DateTimeOffset.UtcNow).Check(architecture);
}

@simonthum
Copy link
Contributor Author

@x789 I like that creative solution, although having to actually call the Property may make it difficult to use in some contexts.

@alexanderlinne alexanderlinne added the kind/feature Categorizes issue or PR as related to a new feature. label Mar 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/feature Categorizes issue or PR as related to a new feature.
Projects
None yet
Development

No branches or pull requests

3 participants