diff --git a/src/Analyzer.Legacy/Analyzer.Legacy.csproj b/src/Analyzer.Legacy/Analyzer.Legacy.csproj index 5aa6262..d333184 100644 --- a/src/Analyzer.Legacy/Analyzer.Legacy.csproj +++ b/src/Analyzer.Legacy/Analyzer.Legacy.csproj @@ -54,7 +54,7 @@ - + diff --git a/src/Analyzer.Tests/EventSources/LogFieldAndPropertyDoNotExistEventSource.cs b/src/Analyzer.Tests/EventSources/LogFieldAndPropertyDoNotExistEventSource.cs new file mode 100644 index 0000000..5f7caad --- /dev/null +++ b/src/Analyzer.Tests/EventSources/LogFieldAndPropertyDoNotExistEventSource.cs @@ -0,0 +1,10 @@ +using System.Diagnostics.Tracing; + +namespace Thor.Analyzer.Tests.EventSources +{ + [EventSource(Name = "LogFieldAndPropertyDoesNotExist")] + public sealed class LogFieldAndPropertyDoNotExistEventSource + : EventSource + { + } +} \ No newline at end of file diff --git a/src/Analyzer.Tests/EventSources/LogFieldDoesNotExistEventSource.cs b/src/Analyzer.Tests/EventSources/LogFieldDoesNotExistEventSource.cs deleted file mode 100644 index bf65102..0000000 --- a/src/Analyzer.Tests/EventSources/LogFieldDoesNotExistEventSource.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Diagnostics.Tracing; - -namespace Thor.Analyzer.Tests.EventSources -{ - [EventSource(Name = "LogFieldDoesNotExist")] - public sealed class LogFieldDoesNotExistEventSource - : EventSource - { - } -} \ No newline at end of file diff --git a/src/Analyzer.Tests/EventSources/LogFieldDoesNotHaveValueEventSource.cs b/src/Analyzer.Tests/EventSources/LogFieldDoesNotHaveValueEventSource.cs deleted file mode 100644 index a7e243f..0000000 --- a/src/Analyzer.Tests/EventSources/LogFieldDoesNotHaveValueEventSource.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Diagnostics.Tracing; - -namespace Thor.Analyzer.Tests.EventSources -{ - [EventSource(Name = "LogFieldDoesNotHaveValue")] - public sealed class LogFieldDoesNotHaveValueEventSource - : EventSource - { - public static readonly LogFieldDoesNotHaveValueEventSource Log; - } -} \ No newline at end of file diff --git a/src/Analyzer.Tests/EventSources/LogFieldNullEventSource.cs b/src/Analyzer.Tests/EventSources/LogFieldNullEventSource.cs new file mode 100644 index 0000000..3b24784 --- /dev/null +++ b/src/Analyzer.Tests/EventSources/LogFieldNullEventSource.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.Tracing; + +namespace Thor.Analyzer.Tests.EventSources +{ + [EventSource(Name = "LogFieldNull")] + public sealed class LogFieldNullEventSource + : EventSource + { + public static readonly LogFieldNullEventSource Log; + } +} \ No newline at end of file diff --git a/src/Analyzer.Tests/EventSources/LogPropertyEventSource.cs b/src/Analyzer.Tests/EventSources/LogPropertyEventSource.cs new file mode 100644 index 0000000..2d66726 --- /dev/null +++ b/src/Analyzer.Tests/EventSources/LogPropertyEventSource.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.Tracing; + +namespace Thor.Analyzer.Tests.EventSources +{ + [EventSource(Name = "LogProperty")] + public sealed class LogPropertyEventSource + : EventSource + { + public static LogPropertyEventSource Log { get; } = new LogPropertyEventSource(); + } +} \ No newline at end of file diff --git a/src/Analyzer.Tests/EventSources/LogPropertyNotPublicEventSource.cs b/src/Analyzer.Tests/EventSources/LogPropertyNotPublicEventSource.cs new file mode 100644 index 0000000..f5dc1bc --- /dev/null +++ b/src/Analyzer.Tests/EventSources/LogPropertyNotPublicEventSource.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.Tracing; + +namespace Thor.Analyzer.Tests.EventSources +{ + [EventSource(Name = "LogPropertyNotPublic")] + public sealed class LogPropertyNotPublicEventSource + : EventSource + { + internal static LogPropertyNotPublicEventSource Log { get; } = + new LogPropertyNotPublicEventSource(); + } +} \ No newline at end of file diff --git a/src/Analyzer.Tests/EventSources/LogPropertyNotReadOnlyEventSource.cs b/src/Analyzer.Tests/EventSources/LogPropertyNotReadOnlyEventSource.cs new file mode 100644 index 0000000..edf47a6 --- /dev/null +++ b/src/Analyzer.Tests/EventSources/LogPropertyNotReadOnlyEventSource.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.Tracing; + +namespace Thor.Analyzer.Tests.EventSources +{ + [EventSource(Name = "LogPropertyNotReadOnly")] + public sealed class LogPropertyNotReadOnlyEventSource + : EventSource + { + public static LogPropertyNotReadOnlyEventSource Log { get; set; } = + new LogPropertyNotReadOnlyEventSource(); + } +} \ No newline at end of file diff --git a/src/Analyzer.Tests/EventSources/LogPropertyNotStaticEventSource.cs b/src/Analyzer.Tests/EventSources/LogPropertyNotStaticEventSource.cs new file mode 100644 index 0000000..7d2cc5e --- /dev/null +++ b/src/Analyzer.Tests/EventSources/LogPropertyNotStaticEventSource.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.Tracing; + +namespace Thor.Analyzer.Tests.EventSources +{ + [EventSource(Name = "LogPropertyNotStatic")] + public sealed class LogPropertyNotStaticEventSource + : EventSource + { + public LogPropertyNotStaticEventSource Log { get; } = + new LogPropertyNotStaticEventSource(); + } +} \ No newline at end of file diff --git a/src/Analyzer.Tests/EventSources/LogPropertyNullEventSource.cs b/src/Analyzer.Tests/EventSources/LogPropertyNullEventSource.cs new file mode 100644 index 0000000..0623efe --- /dev/null +++ b/src/Analyzer.Tests/EventSources/LogPropertyNullEventSource.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.Tracing; + +namespace Thor.Analyzer.Tests.EventSources +{ + [EventSource(Name = "LogPropertyNull")] + public sealed class LogPropertyNullEventSource + : EventSource + { + public static LogPropertyNullEventSource Log { get; } + } +} \ No newline at end of file diff --git a/src/Analyzer.Tests/Rules/MustHaveStaticLogFieldOrPropertyTests.cs b/src/Analyzer.Tests/Rules/MustHaveStaticLogFieldOrPropertyTests.cs new file mode 100644 index 0000000..1330569 --- /dev/null +++ b/src/Analyzer.Tests/Rules/MustHaveStaticLogFieldOrPropertyTests.cs @@ -0,0 +1,224 @@ +using Thor.Analyzer.Rules; +using Thor.Analyzer.Tests.EventSources; +using FluentAssertions; +using Moq; +using Xunit; + +namespace Thor.Analyzer.Tests.Rules +{ + public class MustHaveStaticLogFieldOrPropertyTests + : EventSourceRuleTestBase + { + protected override MustHaveStaticLogFieldOrProperty CreateRule(IRuleSet ruleSet) + { + return new MustHaveStaticLogFieldOrProperty(ruleSet); + } + + [Fact(DisplayName = "Apply: Should return an error if log field and property does not exist")] + public void Apply_NoLogFieldAndProperty() + { + // arrange + LogFieldAndPropertyDoNotExistEventSource eventSource = new LogFieldAndPropertyDoNotExistEventSource(); + SchemaReader reader = new SchemaReader(eventSource); + EventSourceSchema schema = reader.Read(); + IRuleSet ruleSet = new Mock().Object; + IEventSourceRule rule = CreateRule(ruleSet); + + // act + IResult result = rule.Apply(schema, eventSource); + + // assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + } + + [Fact(DisplayName = "Apply: Should return an error if log field is not public")] + public void Apply_LogFieldNotPublic() + { + // arrange + LogFieldNotPublicEventSource eventSource = new LogFieldNotPublicEventSource(); + SchemaReader reader = new SchemaReader(eventSource); + EventSourceSchema schema = reader.Read(); + IRuleSet ruleSet = new Mock().Object; + IEventSourceRule rule = CreateRule(ruleSet); + + // act + IResult result = rule.Apply(schema, eventSource); + + // assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + } + + [Fact(DisplayName = "Apply: Should return an error if log field is not readonly")] + public void Apply_LogFieldNotReadOnly() + { + // arrange + LogFieldNotReadOnlyEventSource eventSource = new LogFieldNotReadOnlyEventSource(); + SchemaReader reader = new SchemaReader(eventSource); + EventSourceSchema schema = reader.Read(); + IRuleSet ruleSet = new Mock().Object; + IEventSourceRule rule = CreateRule(ruleSet); + + // act + IResult result = rule.Apply(schema, eventSource); + + // assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + } + + /* + todo: figure out why this test leads to StackOverflowException + + [Fact(DisplayName = "Apply: Should return an error if log field is not static")] + public void Apply_LogFieldNotStatic() + { + // arrange + LogFieldNotStaticEventSource eventSource = new LogFieldNotStaticEventSource(); + SchemaReader reader = new SchemaReader(eventSource); + EventSourceSchema schema = reader.Read(); + IRuleSet ruleSet = new Mock().Object; + IEventSourceRule rule = CreateRule(ruleSet); + + // act + IResult result = rule.Apply(schema, eventSource); + + // assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + } + */ + + [Fact(DisplayName = "Apply: Should return an error if log field returns no value")] + public void Apply_LogFieldNull() + { + // arrange + LogFieldNullEventSource eventSource = new LogFieldNullEventSource(); + SchemaReader reader = new SchemaReader(eventSource); + EventSourceSchema schema = reader.Read(); + IRuleSet ruleSet = new Mock().Object; + IEventSourceRule rule = CreateRule(ruleSet); + + // act + IResult result = rule.Apply(schema, eventSource); + + // assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + } + + [Fact(DisplayName = "Apply: Should return a success if the log field was found as expected")] + public void Apply_LogFieldAsExpected() + { + // arrange + LogFieldEventSource eventSource = new LogFieldEventSource(); + SchemaReader reader = new SchemaReader(eventSource); + EventSourceSchema schema = reader.Read(); + IRuleSet ruleSet = new Mock().Object; + IEventSourceRule rule = CreateRule(ruleSet); + + // act + IResult result = rule.Apply(schema, eventSource); + + // assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + } + + [Fact(DisplayName = "Apply: Should return an error if log property is not public")] + public void Apply_LogPropertyNotPublic() + { + // arrange + LogPropertyNotPublicEventSource eventSource = new LogPropertyNotPublicEventSource(); + SchemaReader reader = new SchemaReader(eventSource); + EventSourceSchema schema = reader.Read(); + IRuleSet ruleSet = new Mock().Object; + IEventSourceRule rule = CreateRule(ruleSet); + + // act + IResult result = rule.Apply(schema, eventSource); + + // assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + } + + [Fact(DisplayName = "Apply: Should return an error if log property is not readonly")] + public void Apply_LogPropertyNotReadOnly() + { + // arrange + LogPropertyNotReadOnlyEventSource eventSource = new LogPropertyNotReadOnlyEventSource(); + SchemaReader reader = new SchemaReader(eventSource); + EventSourceSchema schema = reader.Read(); + IRuleSet ruleSet = new Mock().Object; + IEventSourceRule rule = CreateRule(ruleSet); + + // act + IResult result = rule.Apply(schema, eventSource); + + // assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + } + + /* + todo: figure out why this test leads to StackOverflowException + + [Fact(DisplayName = "Apply: Should return an error if log property is not static")] + public void Apply_LogPropertyNotStatic() + { + // arrange + LogPropertyNotStaticEventSource eventSource = new LogPropertyNotStaticEventSource(); + SchemaReader reader = new SchemaReader(eventSource); + EventSourceSchema schema = reader.Read(); + IRuleSet ruleSet = new Mock().Object; + IEventSourceRule rule = CreateRule(ruleSet); + + // act + IResult result = rule.Apply(schema, eventSource); + + // assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + } + */ + + [Fact(DisplayName = "Apply: Should return an error if log property returns no value")] + public void Apply_LogPropertyNull() + { + // arrange + LogPropertyNullEventSource eventSource = new LogPropertyNullEventSource(); + SchemaReader reader = new SchemaReader(eventSource); + EventSourceSchema schema = reader.Read(); + IRuleSet ruleSet = new Mock().Object; + IEventSourceRule rule = CreateRule(ruleSet); + + // act + IResult result = rule.Apply(schema, eventSource); + + // assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + } + + [Fact(DisplayName = "Apply: Should return a success if the log property was found as expected")] + public void Apply_LogPropertyAsExpected() + { + // arrange + LogPropertyEventSource eventSource = new LogPropertyEventSource(); + SchemaReader reader = new SchemaReader(eventSource); + EventSourceSchema schema = reader.Read(); + IRuleSet ruleSet = new Mock().Object; + IEventSourceRule rule = CreateRule(ruleSet); + + // act + IResult result = rule.Apply(schema, eventSource); + + // assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + } + } +} + \ No newline at end of file diff --git a/src/Analyzer.Tests/Rules/MustHaveStaticLogPropertyTests.cs b/src/Analyzer.Tests/Rules/MustHaveStaticLogPropertyTests.cs deleted file mode 100644 index 6f56cfd..0000000 --- a/src/Analyzer.Tests/Rules/MustHaveStaticLogPropertyTests.cs +++ /dev/null @@ -1,130 +0,0 @@ -using Thor.Analyzer.Rules; -using Thor.Analyzer.Tests.EventSources; -using FluentAssertions; -using Moq; -using Xunit; - -namespace Thor.Analyzer.Tests.Rules -{ - public class MustHaveStaticLogPropertyTests - : EventSourceRuleTestBase - { - protected override MustHaveStaticLogProperty CreateRule(IRuleSet ruleSet) - { - return new MustHaveStaticLogProperty(ruleSet); - } - - [Fact(DisplayName = "Apply: Should return an error if log field does not exist")] - public void Apply_NoLogField() - { - // arrange - LogFieldDoesNotExistEventSource eventSource = new LogFieldDoesNotExistEventSource(); - SchemaReader reader = new SchemaReader(eventSource); - EventSourceSchema schema = reader.Read(); - IRuleSet ruleSet = new Mock().Object; - IEventSourceRule rule = CreateRule(ruleSet); - - // act - IResult result = rule.Apply(schema, eventSource); - - // assert - result.Should().NotBeNull(); - result.Should().BeOfType(); - } - - [Fact(DisplayName = "Apply: Should return an error if log field is not public")] - public void Apply_LogFieldNotPublic() - { - // arrange - LogFieldNotPublicEventSource eventSource = new LogFieldNotPublicEventSource(); - SchemaReader reader = new SchemaReader(eventSource); - EventSourceSchema schema = reader.Read(); - IRuleSet ruleSet = new Mock().Object; - IEventSourceRule rule = CreateRule(ruleSet); - - // act - IResult result = rule.Apply(schema, eventSource); - - // assert - result.Should().NotBeNull(); - result.Should().BeOfType(); - } - - [Fact(DisplayName = "Apply: Should return an error if log field is not readonly")] - public void Apply_LogFieldNotReadOnly() - { - // arrange - LogFieldNotReadOnlyEventSource eventSource = new LogFieldNotReadOnlyEventSource(); - SchemaReader reader = new SchemaReader(eventSource); - EventSourceSchema schema = reader.Read(); - IRuleSet ruleSet = new Mock().Object; - IEventSourceRule rule = CreateRule(ruleSet); - - // act - IResult result = rule.Apply(schema, eventSource); - - // assert - result.Should().NotBeNull(); - result.Should().BeOfType(); - } - - /* - todo: figure out why this test leads to StackOverflowException - - [Fact(DisplayName = "Apply: Should return an error if log field is not static")] - public void Apply_LogFieldNotStatic() - { - // arrange - LogFieldNotStaticEventSource eventSource = new LogFieldNotStaticEventSource(); - SchemaReader reader = new SchemaReader(eventSource); - EventSourceSchema schema = reader.Read(); - IRuleSet ruleSet = new Mock().Object; - IEventSourceRule rule = CreateRule(ruleSet); - - // act - IResult result = rule.Apply(schema, eventSource); - - // assert - result.Should().NotBeNull(); - result.Should().BeOfType(); - } - */ - - [Fact(DisplayName = "Apply: Should return an error if log field returns no value")] - public void Apply_LogFieldNoValue() - { - // arrange - LogFieldDoesNotHaveValueEventSource eventSource = - new LogFieldDoesNotHaveValueEventSource(); - SchemaReader reader = new SchemaReader(eventSource); - EventSourceSchema schema = reader.Read(); - IRuleSet ruleSet = new Mock().Object; - IEventSourceRule rule = CreateRule(ruleSet); - - // act - IResult result = rule.Apply(schema, eventSource); - - // assert - result.Should().NotBeNull(); - result.Should().BeOfType(); - } - - [Fact(DisplayName = "Apply: Should return a success if a log field as defined was found")] - public void Apply_Success() - { - // arrange - LogFieldEventSource eventSource = new LogFieldEventSource(); - SchemaReader reader = new SchemaReader(eventSource); - EventSourceSchema schema = reader.Read(); - IRuleSet ruleSet = new Mock().Object; - IEventSourceRule rule = CreateRule(ruleSet); - - // act - IResult result = rule.Apply(schema, eventSource); - - // assert - result.Should().NotBeNull(); - result.Should().BeOfType(); - } - } -} \ No newline at end of file diff --git a/src/Analyzer/Rules/BestPracticeRuleSet.cs b/src/Analyzer/Rules/BestPracticeRuleSet.cs index 7b6841d..5095600 100644 --- a/src/Analyzer/Rules/BestPracticeRuleSet.cs +++ b/src/Analyzer/Rules/BestPracticeRuleSet.cs @@ -17,7 +17,7 @@ public BestPracticeRuleSet() { new MustBeSealed(this), new MustHaveSinglePrivateConstructor(this), - new MustHaveStaticLogProperty(this), + new MustHaveStaticLogFieldOrProperty(this), new MustHaveValidName(this) }; } diff --git a/src/Analyzer/Rules/MustHaveStaticLogProperty.cs b/src/Analyzer/Rules/MustHaveStaticLogFieldOrProperty.cs similarity index 50% rename from src/Analyzer/Rules/MustHaveStaticLogProperty.cs rename to src/Analyzer/Rules/MustHaveStaticLogFieldOrProperty.cs index a273612..4039665 100644 --- a/src/Analyzer/Rules/MustHaveStaticLogProperty.cs +++ b/src/Analyzer/Rules/MustHaveStaticLogFieldOrProperty.cs @@ -10,16 +10,16 @@ namespace Thor.Analyzer.Rules { /// - /// A rule which probes for missing Log properties. + /// A rule which probes for missing Log field or property. /// - public class MustHaveStaticLogProperty + public class MustHaveStaticLogFieldOrProperty : IEventSourceRule { /// - /// Initiates a new instance of the class. + /// Initiates a new instance of the class. /// /// A ruleset which is the parent of this rule. - public MustHaveStaticLogProperty(IRuleSet ruleSet) + public MustHaveStaticLogFieldOrProperty(IRuleSet ruleSet) { if (ruleSet == null) { @@ -45,16 +45,33 @@ public IResult Apply(EventSourceSchema schema, EventSource eventSource) } Type eventSourceType = eventSource.GetType(); - FieldInfo field = eventSourceType.GetField("Log"); + PropertyInfo property = eventSourceType.GetProperty("Log"); - if (field == null || !field.IsStatic || !field.IsInitOnly || - !field.FieldType.IsAssignableFrom(eventSourceType) || field.GetValue(null) == null) + if (property == null) { - return new Error(this, "Did not found a public readonly 'Log' field which is " + - "static and holds an instance of its own type."); + FieldInfo field = eventSourceType.GetField("Log"); + + if (field == null || !field.IsStatic || !field.IsInitOnly || + !field.FieldType.IsAssignableFrom(eventSourceType) || + field.GetValue(null) == null) + { + return CreateError(); + } + } + else if (!property.CanRead || property.CanWrite || !property.GetGetMethod().IsStatic || + !property.PropertyType.IsAssignableFrom(eventSourceType) || + property.GetValue(null) == null) + { + return CreateError(); } return new Success(this); } + + private Error CreateError() + { + return new Error(this, "Did not found a public readonly 'Log' field or property " + + "which is static and holds an instance of its own type."); + } } } \ No newline at end of file