Skip to content

Increase code coverage #24

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

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0ca4bd6
Add obselete property.
nilay-kapadia Feb 9, 2023
212107e
Add coverage for enum in Maps.
nilay-kapadia Feb 9, 2023
03d7629
Remove unused method.
nilay-kapadia Feb 9, 2023
8e2afb5
Add testModel for ObjectProperty coverage.
nilay-kapadia Feb 9, 2023
1e7cbbf
Further remove unused methods.
nilay-kapadia Feb 9, 2023
302cdce
Add coverage for EnumPropEntity sourcevalue.
nilay-kapadia Feb 9, 2023
cd0be3a
Add coverage for exceptions.
nilay-kapadia Feb 9, 2023
1e9190a
Dead code deletion.
nilay-kapadia Mar 2, 2023
4caa0a5
Ignore generated coverage file
jarz Mar 3, 2023
65edfd0
Test for explicit @id on enum
jarz Mar 3, 2023
7fe46fd
Remove dead code and update code coverage.
nilay-kapadia Mar 6, 2023
1fc77d2
Exclude code coverage.
nilay-kapadia Mar 6, 2023
62231d5
Fix line endings.
nilay-kapadia Mar 6, 2023
3e5eeca
line changes.
nilay-kapadia Mar 6, 2023
106b6d6
Merge branch 'main' into users/nilaykapadia/code-coverage
nilay-kapadia Mar 6, 2023
0f92506
Attempt to fix failing tests.
nilay-kapadia Mar 6, 2023
f782626
Fix directory separator to be cross-platform
jarz Mar 7, 2023
4ea0a23
Custom exceptions instead of generic Exceptions
jarz Mar 8, 2023
5664d6b
Merge branch 'users/nilaykapadia/code-coverage' of github.com:microso…
jarz Mar 8, 2023
ca791ae
Address comments.
nilay-kapadia Apr 3, 2023
f12aa82
Merge from main.
nilay-kapadia Apr 3, 2023
b3d66e7
merge from main
nilay-kapadia Apr 3, 2023
1ab533b
Update command tests.
nilay-kapadia Apr 4, 2023
d95453f
Address comments.
nilay-kapadia Apr 11, 2023
597f100
Update obsolete warning to nowarn.
nilay-kapadia Apr 12, 2023
be8ade3
minor fix.
nilay-kapadia Apr 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
with:
project-to-build: "src/Generator/Generator.csproj"
project-to-test: "test/Generator.Tests/Generator.Tests.csproj"
coverage-threshold: 89
coverage-threshold: 100

Pack:
uses: microsoft/digitalworkplace-workflows/.github/workflows/pack.yml@v3.1
Expand Down
12 changes: 5 additions & 7 deletions src/Generator/Base/ClassEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,17 @@

namespace Microsoft.DigitalWorkplace.DigitalTwins.Models.Generator;

using Microsoft.DigitalWorkplace.DigitalTwins.Models.Generator.Exceptions;

internal abstract class ClassEntity : Entity
{
internal string? Parent { get; set; }

internal IEnumerable<DTPropertyInfo> Properties { get; set; } = Enumerable.Empty<DTPropertyInfo>();

internal IEnumerable<DTFieldInfo> Fields { get; set; } = Enumerable.Empty<DTFieldInfo>();

internal List<Property> PropertyContent { get; set; } = new List<Property>();
internal List<Property> PropertyContent { get; } = new List<Property>();

internal IList<Property> NonRelationshipProperties => PropertyContent.Where(p => !(p is RelationshipProperty)).ToList();

internal List<Command> CommandContent { get; set; } = new List<Command>();
internal List<Command> CommandContent { get; } = new List<Command>();

internal ClassEntity(ModelGeneratorOptions options) : base(options)
{
Expand Down Expand Up @@ -85,7 +83,7 @@ protected Property CreateProperty(DTContentInfo content)
return new RelationshipProperty(relationship, Options);
}

throw new Exception($"Unsupported content type: {content.EntityKind}");
throw new UnsupportedContentTypeException(content.EntityKind.ToString());
}

protected override void WriteContent(StreamWriter streamWriter)
Expand Down
4 changes: 2 additions & 2 deletions src/Generator/Base/Command.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal class Command : Writable

private string EnclosingClass { get; init; }

public List<Entity> ProducedEntities { get; set; } = new List<Entity>();
public List<Entity> ProducedEntities { get; } = new List<Entity>();

internal Command(DTCommandInfo commandInfo, string enclosingClass, ModelGeneratorOptions options) : base(options)
{
Expand Down Expand Up @@ -116,7 +116,7 @@ private string GetDTCommandPayloadType(DTCommandInfo commandInfo, DTCommandPaylo
default:
if (!Types.TryGetNullable(commandPayloadInfo.Schema.EntityKind, out var type) || type is null)
{
throw new UnsupportedPrimativeTypeException(commandPayloadInfo.Schema.EntityKind, commandPayloadInfo.Name, enclosingClass);
throw new UnsupportedPrimitiveTypeException(commandPayloadInfo.Schema.EntityKind, commandPayloadInfo.Name, enclosingClass);
}

return type;
Expand Down
5 changes: 0 additions & 5 deletions src/Generator/Base/Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,6 @@ protected void WriteUsingAzure(StreamWriter streamWriter)
streamWriter.WriteLine($"{indent}using Azure;");
}

protected void WriteUsingReflection(StreamWriter streamWriter)
{
streamWriter.WriteLine($"{indent}using System.Reflection;");
}

protected void WriteUsingLinq(StreamWriter streamWriter)
{
streamWriter.WriteLine($"{indent}using System.Linq;");
Expand Down
1 change: 1 addition & 0 deletions src/Generator/Base/EnumEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ protected override void WriteSignature(StreamWriter streamWriter)
streamWriter.WriteLine($"{indent}public enum {Name}");
}

[ExcludeFromCodeCoverage]
protected override void WriteContent(StreamWriter streamWriter)
{
}
Expand Down
86 changes: 7 additions & 79 deletions src/Generator/Base/Property.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,14 @@ internal abstract class Property : Writable
{
private string name = string.Empty;

private string type = string.Empty;

private bool hasBodies => Getter?.Body != null && Setter?.Body != null;

private IDictionary<string, bool> needsConvertedMapping = new Dictionary<string, bool>
{
{ "int?", false },
{ "int", false },
{ "float", false },
{ "float?", false },
{ "string", false },
{ "bool", false },
{ "bool?", false }
};

private IList<string> interfaceTransformTypes = new List<string>
{
"IEnumerable",
"IDictionary",
"IList",
"ICollection"
};

internal string Type { get => type; set => HandleTypeSetter(value); }

internal string? NonInterfaceType { get; set; }
internal string Type { get; set; } = string.Empty;

internal string Name { get => name; set => name = CapitalizeFirstLetter(value); }

internal string JsonName { get; set; } = string.Empty;

internal string? DictionaryPatchType { get; set; }

internal bool NeedsConvertMethod { get; set; }

internal bool UseNonInterfaceType => NonInterfaceType != Type;

internal bool JsonIgnore { get; set; } = false;

internal bool Nullable { get; set; } = false;

internal bool Initialized { get; set; } = false;

internal bool Obsolete { get; set; } = false;
Expand All @@ -56,7 +23,7 @@ internal abstract class Property : Writable

internal PropertySetter Setter { get; set; }

internal List<Entity> ProducedEntities { get; set; } = new List<Entity>();
internal List<Entity> ProducedEntities { get; } = new List<Entity>();

internal Property(ModelGeneratorOptions options) : base(options)
{
Expand Down Expand Up @@ -85,30 +52,11 @@ internal virtual void WriteTo(StreamWriter streamWriter)
streamWriter.WriteLine($"{indent}{indent}{Helper.ObsoleteAttribute}");
}

var nullable = Nullable ? "?" : string.Empty;
streamWriter.Write($"{indent}{indent}public {Type}{nullable} {Name}");

// If bodies exist, then add newlines to format.
if (hasBodies)
{
streamWriter.WriteLine();
streamWriter.WriteLine($"{indent}{indent}{{");

streamWriter.Write($"{indent}{indent}{indent}");
Getter.WriteTo(streamWriter);

streamWriter.Write($"{indent}{indent}{indent}");
Setter.WriteTo(streamWriter);

streamWriter.Write($"{indent}{indent}}}");
}
else
{
streamWriter.Write(" { ");
Getter?.WriteTo(streamWriter);
Setter?.WriteTo(streamWriter);
streamWriter.Write("}");
}
streamWriter.Write($"{indent}{indent}public {Type} {Name}");
streamWriter.Write(" { ");
Getter?.WriteTo(streamWriter);
Setter?.WriteTo(streamWriter);
streamWriter.Write("}");

streamWriter.WriteLine(Initialized ? $" = new {Type}();" : string.Empty);
}
Expand All @@ -117,24 +65,4 @@ protected void WriteJsonPropertyAttribute(StreamWriter streamWriter, string prop
{
streamWriter.WriteLine($"{indent}{indent}[JsonPropertyName(\"{property}\")]");
}

private void HandleTypeSetter(string value)
{
type = value;
NonInterfaceType = interfaceTransformTypes.Any(t => type.StartsWith(t)) ? type.TrimStart('I') : type;
NeedsConvertMethod = !needsConvertedMapping.ContainsKey(type) ? true : needsConvertedMapping[type];
SetDictionaryPatchType();
}

private void SetDictionaryPatchType()
{
if (!type.StartsWith("IDictionary"))
{
return;
}

var end = type.Split(',')[1];
var valueType = end.TrimStart().TrimEnd('>');
DictionaryPatchType = $"{type}, {valueType}";
}
}
5 changes: 0 additions & 5 deletions src/Generator/Base/Writable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,4 @@ protected static string CapitalizeFirstLetter(string word)
{
return char.ToUpper(word[0]) + word.Substring(1);
}

protected static string LowercaseFirstLetter(string word)
{
return char.ToLower(word[0]) + word.Substring(1);
}
}
10 changes: 3 additions & 7 deletions src/Generator/Entity/ModelEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,14 @@ internal class ModelEntity : ClassEntity
internal ModelEntity(DTInterfaceInfo interfaceInfo, ModelGeneratorOptions options) : base(options)
{
ModelId = interfaceInfo.Id;
Properties = interfaceInfo.Contents.Values
.Where(c => c.Id.Labels.Contains(Name) && c.EntityKind == DTEntityKind.Property && c is DTPropertyInfo)
.Select(c => (DTPropertyInfo)c);
Name = GetClassName(interfaceInfo.Id);
Parent = interfaceInfo.Extends.Count() > 0 ? GetClassName(interfaceInfo.Extends.First().Id) : nameof(BasicDigitalTwin);
FileDirectory = ExtractDirectory(ModelId);
var properties = GetContentInfo<DTPropertyInfo>(interfaceInfo);
PropertyContent.AddRange(properties.Select(CreateProperty));
var relationships = GetContentInfo<DTRelationshipInfo>(interfaceInfo);
PropertyContent.AddRange(relationships.Select(CreateProperty));
var contents = interfaceInfo.Contents.Select(c => c.Value).Where(c => c.Id.Labels.Contains(Name) && c is not DTCommandInfo);
var commands = GetContentInfo<DTCommandInfo>(interfaceInfo);
CommandContent.AddRange(commands.Select((contentInfo) => new Command((DTCommandInfo)contentInfo, Name, Options)));

PropertyContent.AddRange(contents.Select(CreateProperty));
}

protected override void WriteConstructor(StreamWriter streamWriter)
Expand Down
1 change: 0 additions & 1 deletion src/Generator/Entity/ObjectEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ internal ObjectEntity(DTNamedEntityInfo entityInfo, DTObjectInfo objectInfo, str
Name = $"{Name}Data";
}

Fields = objectInfo.Fields;
AllowOverwrite = true;
PropertyContent.AddRange(objectInfo.Fields.Select(f => CreateProperty(f, f.Schema)));
}
Expand Down
4 changes: 0 additions & 4 deletions src/Generator/Entity/RelationshipCollectionEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,17 @@ internal class RelationshipCollectionEntity : ClassEntity
{
internal DTRelationshipInfo RelationshipInfo { get; set; }

private string Target { get; set; }

private string NamePrefix { get; set; }

internal RelationshipCollectionEntity(DTRelationshipInfo info, ModelGeneratorOptions options) : base(options)
{
RelationshipInfo = info;
Properties = info.Properties;
var enclosingClass = info.DefinedIn.Labels.Last();
NamePrefix = $"{enclosingClass}{CapitalizeFirstLetter(RelationshipInfo.Name)}";
Name = $"{NamePrefix}RelationshipCollection";
FileDirectory = Path.Combine("Relationship", ExtractDirectory(RelationshipInfo.DefinedIn), enclosingClass);
var targetType = RelationshipInfo.Target == null ? nameof(BasicDigitalTwin) : $"{RelationshipInfo.Target.Labels.Last()}";
Parent = $"RelationshipCollection<{NamePrefix}Relationship, {targetType}>";
Target = RelationshipInfo.Target == null ? "null" : $"typeof({RelationshipInfo.Target.Labels.Last()})";
}

protected override void WriteConstructor(StreamWriter streamWriter)
Expand Down
6 changes: 1 addition & 5 deletions src/Generator/Entity/RelationshipEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,18 @@ internal class RelationshipEntity : ClassEntity
{
internal DTRelationshipInfo RelationshipInfo { get; set; }

private string Target { get; set; }

private string SourceType { get; set; }

private string TargetType { get; set; }

internal RelationshipEntity(DTRelationshipInfo info, ModelGeneratorOptions options) : base(options)
{
RelationshipInfo = info;
Properties = info.Properties;
SourceType = info.DefinedIn.Labels.Last();
Name = $"{SourceType}{CapitalizeFirstLetter(RelationshipInfo.Name)}Relationship";
FileDirectory = Path.Combine("Relationship", ExtractDirectory(RelationshipInfo.DefinedIn), SourceType);
TargetType = RelationshipInfo.Target == null ? nameof(BasicDigitalTwin) : $"{RelationshipInfo.Target.Labels.Last()}";
TargetType = RelationshipInfo.Target == null ? nameof(BasicDigitalTwin) : RelationshipInfo.Target.Labels.Last();
Parent = $"Relationship<{TargetType}>";
Target = RelationshipInfo.Target == null ? "null" : $"typeof({RelationshipInfo.Target.Labels.Last()})";
PropertyContent.AddRange(info.Properties.Select(p => CreateProperty(p, p.Schema)));
}

Expand Down
25 changes: 25 additions & 0 deletions src/Generator/Exceptions/UnsupportedContentTypeException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.DigitalWorkplace.DigitalTwins.Models.Generator.Exceptions;

/// <summary>
/// The exception that is thrown when the Generator encounters an unsupported content type in a DTDL model.
/// </summary>
public class UnsupportedContentTypeException : Exception
{
/// <summary>
/// Gets the unsupported content type.
/// </summary>
[ExcludeFromCodeCoverage]
public string ContentType { get; init; }

/// <summary>
/// Initializes a new instance of the <see cref="UnsupportedContentTypeException"/> class.
/// </summary>
/// <param name="contentType">The unsupported content type.</param>
public UnsupportedContentTypeException(string contentType) : base($"Unsupported content type: {contentType}")
{
ContentType = contentType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,38 @@ namespace Microsoft.DigitalWorkplace.DigitalTwins.Models.Generator.Exceptions;
using Microsoft.Azure.DigitalTwins.Parser;

/// <summary>
/// The exception that is thrown when the Generator encounters an unsupported primative type in a DTDL model.
/// The exception that is thrown when the Generator encounters an unsupported primitive type in a DTDL model.
/// </summary>
public class UnsupportedPrimativeTypeException : Exception
public class UnsupportedPrimitiveTypeException : Exception
{
/// <summary>
/// Gets or inits the unsupported primative type.
/// Gets the unsupported primitive type.
/// </summary>
public DTEntityKind PrimativeType { get; init; }
[ExcludeFromCodeCoverage]
public DTEntityKind PrimitiveType { get; init; }

/// <summary>
/// gets or inits the name of the entity with the unsupported primative type.
/// Gets the name of the entity with the unsupported primitive type.
/// </summary>
[ExcludeFromCodeCoverage]
public string EntityName { get; init; }

/// <summary>
/// Gets or inits the class that encloses the unsupported primative type.
/// Gets the class that encloses the unsupported primitive type.
/// </summary>
[ExcludeFromCodeCoverage]
public string EnclosingClass { get; init; }

/// <summary>
/// Initializes a new instance of the <see cref="UnsupportedPrimativeTypeException"/> class.
/// Initializes a new instance of the <see cref="UnsupportedPrimitiveTypeException"/> class.
/// </summary>
/// <param name="primativeType">The unsupported primative type.</param>
/// <param name="entityName">The name of the entity with the unsupported primative type.</param>
/// <param name="enclosingClass">The class that encloses the unsupported primative type.</param>
public UnsupportedPrimativeTypeException(DTEntityKind primativeType, string entityName, string enclosingClass)
: base($"Unsupported primitive property type: {primativeType} for {entityName} in {enclosingClass}")
/// <param name="primitiveType">The unsupported primitive type.</param>
/// <param name="entityName">The name of the entity with the unsupported primitive type.</param>
/// <param name="enclosingClass">The class that encloses the unsupported primitive type.</param>
public UnsupportedPrimitiveTypeException(DTEntityKind primitiveType, string entityName, string enclosingClass)
: base($"Unsupported primitive property type: {primitiveType} for {entityName} in {enclosingClass}")
{
PrimativeType = primativeType;
PrimitiveType = primitiveType;
EntityName = entityName;
EnclosingClass = enclosingClass;
}
Expand Down
1 change: 1 addition & 0 deletions src/Generator/Generator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
<Using Include="System.Linq" />
<Using Include="System.Text" />
<Using Include="System.Text.RegularExpressions" />
<Using Include="System.Diagnostics.CodeAnalysis"/>
</ItemGroup>

<ItemGroup>
Expand Down
11 changes: 0 additions & 11 deletions src/Generator/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,4 @@ internal static bool IsObsolete(this DTNamedEntityInfo entity)
{
return !string.IsNullOrEmpty(entity.Comment) && entity.Comment.Equals(ObsoleteAttribute, StringComparison.OrdinalIgnoreCase);
}

internal static string Serialize(this AccessModifier modifier) => modifier switch
{
AccessModifier.Public => "public",
AccessModifier.Internal => "internal",
AccessModifier.Protected => "protected",
AccessModifier.Private => "private",
AccessModifier.ProtectedInternal => "protected internal",
AccessModifier.PrivateProtected => "private protected",
_ => throw new Exception($"Invalid value for AccessModifier: {modifier}"),
};
}
Loading