Skip to content

Commit

Permalink
Unit tests for SourceCodeElements
Browse files Browse the repository at this point in the history
  • Loading branch information
tareqimbasher committed Jun 9, 2024
1 parent 8ca63c1 commit 219d6df
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 166 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Extensions.Logging;
using NetPad.Apps.Data.EntityFrameworkCore.DataConnections;
using NetPad.Apps.Data.EntityFrameworkCore.Scaffolding.Transforms;
using NetPad.CodeAnalysis;
using NetPad.Configuration;
using NetPad.Data;
using NetPad.DotNet;
Expand Down Expand Up @@ -239,10 +240,7 @@ private async Task<ScaffoldedSourceFile> ParseScaffoldedSourceFileAsync(FileInfo

var usings = nodes
.OfType<UsingDirectiveSyntax>()
.Select(u => string.Join(
' ',
u.NormalizeWhitespace().ChildNodes().Select(x => x.ToFullString()))
)
.Select(u => u.GetNamespaceString())
.ToArray();

var classDeclaration = nodes.OfType<ClassDeclarationSyntax>().Single();
Expand Down
17 changes: 17 additions & 0 deletions src/Core/NetPad.Runtime/CodeAnalysis/CodeAnalysisExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace NetPad.CodeAnalysis;

public static class CodeAnalysisExtensions
{
/// <summary>
/// Gets the namespace value specified by the using directive.
/// </summary>
public static string GetNamespaceString(this UsingDirectiveSyntax node)
{
return string.Join(' ',
node.ChildNodes()
.Select(x => x.NormalizeWhitespace().ToFullString()));
}
}
10 changes: 1 addition & 9 deletions src/Core/NetPad.Runtime/DotNet/Code.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
namespace NetPad.DotNet;

[method: JsonConstructor]
public class Code(Namespace? @namespace, string? value) : SourceCodeElement<string?>(value)
public record Code(Namespace? Namespace, string? Value) : SourceCodeElement<string?>(Value)
{
public Code(string? value) : this(null, value)
{
}

public Namespace? Namespace { get; } = @namespace;

public override bool ValueChanged()
{
return _valueChanged || (Namespace != null && Namespace.ValueChanged());
Expand All @@ -33,10 +31,4 @@ public override string ToCodeString()

return sb.ToString();
}

protected override IEnumerable<object?> GetEqualityComponents()
{
yield return base.GetEqualityComponents();
yield return Namespace;
}
}
50 changes: 15 additions & 35 deletions src/Core/NetPad.Runtime/DotNet/Namespace.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
namespace NetPad.DotNet;

public class Namespace : SourceCodeElement<string>
public record Namespace : SourceCodeElement<string>
{
public Namespace(string value) : base(value)
public Namespace(string value) : base(Normalize(value))
{
Validate(value);
}

public override string ToCodeString() => $"namespace {Value};";
Expand All @@ -14,54 +13,35 @@ public Namespace(string value) : base(value)
return new Namespace(value);
}

public override bool Equals(object? obj)
{
if (obj is string str)
return Value == str;

return base.Equals(obj);
}

public override int GetHashCode() => base.GetHashCode();

public static void Validate(string value)
public static string Normalize(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Cannot be null or whitespace", nameof(value));
}

if (value.StartsWith(' ') || value.EndsWith(' '))
{
value = value.Trim();
}

if (value.StartsWith("namespace"))
{
throw new ArgumentException("Cannot start with the keyword 'namespace'", nameof(value));
}

char firstChar = value.First();
char firstChar = value[0];

if (!char.IsLetter(firstChar) && firstChar != '_')
{
throw new ArgumentException("Must start with a letter or an underscore", nameof(value));
}

if (value.EndsWith(";"))
{
throw new ArgumentException("Cannot end with a semi-colon", nameof(value));
}

if (value.Contains(' '))
throw new ArgumentException("Cannot contain spaces", nameof(value));
}

public static Namespace Parse(string text)
{
if (string.IsNullOrWhiteSpace(text))
return new Namespace(text);

if (text.StartsWith(' ') || text.EndsWith(' '))
text = text.Trim();

if (text.StartsWith("namespace "))
text = text["namespace".Length..];

int ixInvalidChar = Array.FindIndex(text.ToCharArray(), c => !char.IsLetter(c) && c != '_');
if (ixInvalidChar >= 0)
text = text[..ixInvalidChar];

return text;
return value.ReplaceLineEndings(string.Empty);
}
}
35 changes: 12 additions & 23 deletions src/Core/NetPad.Runtime/DotNet/SourceCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,37 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NetPad.CodeAnalysis;

namespace NetPad.DotNet;

[method: JsonConstructor]
public class SourceCode(Code? code, IEnumerable<Using>? usings = null)
public class SourceCode
{
private readonly HashSet<Using> _usings = usings?.ToHashSet() ?? new HashSet<Using>();
private readonly HashSet<Using> _usings;
private bool _valueChanged;

public SourceCode() : this(null, Array.Empty<Using>())
{
}

public SourceCode(IEnumerable<string> usings) : this(null, usings)
{
}

public SourceCode(params string[] usings) : this(null, usings)
[JsonConstructor]
public SourceCode(Code? code, IEnumerable<Using>? usings = null)
{
Code = code ?? new Code(null);
_usings = usings?.ToHashSet() ?? [];
}

public SourceCode(IEnumerable<Using> usings) : this(null, usings)
{
}

public SourceCode(params Using[] usings) : this(null, usings)
public SourceCode(string? code, IEnumerable<string>? usings = null)
: this(code == null ? null : new Code(code), usings?.Select(u => new Using(u)))
{
}

public SourceCode(string? code, IEnumerable<string>? usings = null)
: this(
code == null ? null : new Code(code),
usings?.Select(u => new Using(u))
)
public SourceCode(IEnumerable<string> usings) : this(null, usings)
{
}

public IEnumerable<Using> Usings => _usings;
public Code Code { get; } = code ?? new Code(null, null);
public Code Code { get; }
public bool ValueChanged() => _valueChanged || Code.ValueChanged() || _usings.Any(u => u.ValueChanged());

public void AddUsing(string @using)
Expand Down Expand Up @@ -82,10 +74,7 @@ public static SourceCode Parse(string text)
.ToArray();

var usings = usingDirectives
.Select(u => string.Join(
' ',
u.NormalizeWhitespace().ChildNodes().Select(x => x.ToFullString()))
)
.Select(u => u.GetNamespaceString())
.ToArray();

var code = root
Expand Down
18 changes: 5 additions & 13 deletions src/Core/NetPad.Runtime/DotNet/SourceCodeElement.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
namespace NetPad.DotNet;

public abstract class SourceCodeElement : ValueObject
public abstract record SourceCodeElement<TValue>(TValue Value)
{
protected bool _valueChanged;

public TValue Value { get; private set; } = Value;

public virtual bool ValueChanged() => _valueChanged;

public abstract string ToCodeString();
}

public abstract class SourceCodeElement<TValue>(TValue value) : SourceCodeElement
{
public TValue Value { get; private set; } = value;

public void Update(TValue value)
public void Update(TValue newValue)
{
Value = value;
Value = newValue;
_valueChanged = true;
}

protected override IEnumerable<object?> GetEqualityComponents()
{
yield return Value;
}
}
47 changes: 15 additions & 32 deletions src/Core/NetPad.Runtime/DotNet/Using.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
namespace NetPad.DotNet;

public class Using : SourceCodeElement<string>
public record Using : SourceCodeElement<string>
{
public Using(string value) : base(value)
public Using(string value) : base(Normalize(value))
{
Validate(value);
}

public override string ToCodeString() => ToCodeString(false);
Expand All @@ -15,51 +14,35 @@ public Using(string value) : base(value)
return new Using(value);
}

public override bool Equals(object? obj)
{
if (obj is string str)
return Value == str;

return base.Equals(obj);
}

public override int GetHashCode() => base.GetHashCode();

public static void Validate(string value)
public static string Normalize(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Cannot be null or whitespace", nameof(value));
}

if (value.StartsWith(' ') || value.EndsWith(' '))
{
value = value.Trim();
}

if (value.StartsWith("using"))
{
throw new ArgumentException("Cannot start with the keyword 'using'", nameof(value));
}

char firstChar = value.First();
char firstChar = value[0];

if (!char.IsLetter(firstChar) && firstChar != '_')
{
throw new ArgumentException("Must start with a letter or an underscore", nameof(value));
}

if (value.EndsWith(";"))
{
throw new ArgumentException("Cannot end with a semi-colon", nameof(value));
}

public static Using Parse(string text)
{
if (string.IsNullOrWhiteSpace(text))
return new Using(text);

if (text.StartsWith(' ') || text.EndsWith(' '))
text = text.Trim();

if (text.StartsWith("using "))
text = text["using".Length..];

int ixInvalidChar = Array.FindIndex(text.ToCharArray(), c => !char.IsLetter(c) && c != '_');
if (ixInvalidChar >= 0)
text = text[..ixInvalidChar];
}

return text;
return value.ReplaceLineEndings(string.Empty);
}
}
50 changes: 0 additions & 50 deletions src/Core/NetPad.Runtime/ValueObject.cs

This file was deleted.

Loading

0 comments on commit 219d6df

Please sign in to comment.