Skip to content

Commit

Permalink
Refactor tests, splitting out Consolonia.NUnit library and a ton of u…
Browse files Browse the repository at this point in the history
…nit tests (#143)

* rename/move files/projects
* update unit tests
* Split unit tests out from consolonia support package
* renamed  test support library to Consolonia.NUnit and turned on package
* renamed Consolonia.GalleryTests to Consolonia.Gallery.Tests
* Created Consolonia.Core.Tests library
* Added unit tests for
  * Pixels
  * PixelBackground
  * PixelForeground
  * SimpleSymbol
  * DrawingBoxSymbol
  * Glyph calculation
  * Colors
* Refactored Gallery.Tests
* Move type attribute up into gallery test because the way it was set up you couldn't double on a unit test and go to the unit test, and you could only have 1 unit test per class.
  • Loading branch information
tomlm authored Nov 10, 2024
1 parent 94d53c5 commit eb7c2da
Show file tree
Hide file tree
Showing 50 changed files with 1,289 additions and 219 deletions.
2 changes: 1 addition & 1 deletion src/Consolonia.Core/Assembly.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Consolonia.TestsCore")]
[assembly: InternalsVisibleTo("Consolonia.Core.Tests")]

[assembly: CLSCompliant(false)] //todo: should we make it compliant?
8 changes: 4 additions & 4 deletions src/Consolonia.Core/Drawing/DrawingContextImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public void DrawBitmap(IBitmapImpl source, double opacity, Rect sourceRect, Rect
Color background = GetBackgroundColorForQuadPixel(quadColors, quadPixel);

var imagePixel = new Pixel(
new PixelForeground(new SimpleSymbol(quadPixel), color: foreground),
new PixelForeground(new SimpleSymbol(quadPixel), foreground),
new PixelBackground(background));
CurrentClip.ExecuteWithClipping(new Point(px, py),
() =>
Expand Down Expand Up @@ -349,10 +349,10 @@ private void ApplyTextDecorationLineInternal(ref Point head, IPen pen, Line line
pixel =>
{
var newPixelForeground = new PixelForeground(pixel.Foreground.Symbol,
pixel.Foreground.Color,
pixel.Foreground.Weight,
pixel.Foreground.Style,
textDecoration,
pixel.Foreground.Color);
textDecoration);
return pixel.Blend(new Pixel(newPixelForeground, pixel.Background));
});
});
Expand Down Expand Up @@ -499,7 +499,7 @@ private void DrawPixelAndMoveHead(ref Point head, Line line, LineStyle? lineStyl
// Each glyph maps to a pixel as a starting point.
// Emoji's and Ligatures are complex strings, so they start at a point and then overlap following pixels
// the x and y are adjusted accodingly.
foreach (string glyph in text.GetGlyphs())
foreach (string glyph in text.GetGlyphs(_consoleWindow.Console.SupportsComplexEmoji))
{
Point characterPoint =
whereToDraw.Transform(Matrix.CreateTranslation(currentXPosition, currentYPosition));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,41 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace Consolonia.Core.Drawing.PixelBufferImplementation
{
/// <summary>
/// https://en.wikipedia.org/wiki/Box-drawing_character
/// </summary>
[DebuggerDisplay("DrawingBox {Text}")]
public struct DrawingBoxSymbol : ISymbol
public struct DrawingBoxSymbol : ISymbol, IEquatable<DrawingBoxSymbol>
{
// all 0bXXXX_0000 are special values
private const byte BoldSymbol = 0b0001_0000;
private const byte EmptySymbol = 0b0;
private readonly byte _upRightDownLeft;

public DrawingBoxSymbol(byte upRightDownLeft)
{
_upRightDownLeft = upRightDownLeft;
Text = GetBoxSymbol(_upRightDownLeft).ToString();
}

private byte _upRightDownLeft;

public string Text => GetBoxSymbol().ToString();
public string Text { get; private init; }

public ushort Width { get; } = 1;

/// <summary>
/// https://en.wikipedia.org/wiki/Code_page_437
/// </summary>
private char GetBoxSymbol()
private static char GetBoxSymbol(byte upRightDownLeft)
{
//DOS linedraw characters are not ordered in any programmatic manner, and calculating a particular character shape needs to use a look-up table. from https://en.wikipedia.org/wiki/Box-drawing_character

byte leftPart = (byte)(_upRightDownLeft & 0b1111_0000);
byte leftPart = (byte)(upRightDownLeft & 0b1111_0000);
bool hasLeftPart = leftPart > 0;

switch (_upRightDownLeft & 0b0000_1111)
switch (upRightDownLeft & 0b0000_1111)
{
case 0b0000_1000:
case 0b0000_0010:
Expand All @@ -56,7 +57,7 @@ private char GetBoxSymbol()

default:
{
return _upRightDownLeft switch
return upRightDownLeft switch
{
EmptySymbol => char.MinValue,
BoldSymbol => '█',
Expand Down Expand Up @@ -107,12 +108,13 @@ public ISymbol Blend(ref ISymbol symbolAbove)
{
if (symbolAbove.IsWhiteSpace()) return this;

if (symbolAbove is not DrawingBoxSymbol drawingBoxSymbol) return symbolAbove;
if (symbolAbove is not DrawingBoxSymbol drawingBoxSymbol)
return symbolAbove;

if (drawingBoxSymbol._upRightDownLeft == BoldSymbol || _upRightDownLeft == BoldSymbol)
_upRightDownLeft = BoldSymbol;
else
_upRightDownLeft |= drawingBoxSymbol._upRightDownLeft;
return this;
return new DrawingBoxSymbol(BoldSymbol);

return new DrawingBoxSymbol((byte)(_upRightDownLeft | drawingBoxSymbol._upRightDownLeft));
}

public static DrawingBoxSymbol UpRightDownLeftFromPattern(byte pattern, LineStyle lineStyle)
Expand All @@ -131,5 +133,30 @@ public static DrawingBoxSymbol UpRightDownLeftFromPattern(byte pattern, LineStyl
throw new ArgumentOutOfRangeException(nameof(lineStyle), lineStyle, null);
}
}

public bool Equals(DrawingBoxSymbol other)
{
return _upRightDownLeft == other._upRightDownLeft;
}

public override bool Equals([NotNullWhen(true)] object obj)
{
return obj is DrawingBoxSymbol other && Equals(other);
}

public override int GetHashCode()
{
return _upRightDownLeft.GetHashCode();
}

public static bool operator ==(DrawingBoxSymbol left, DrawingBoxSymbol right)
{
return left.Equals(right);
}

public static bool operator !=(DrawingBoxSymbol left, DrawingBoxSymbol right)
{
return !left.Equals(right);
}
}
}
89 changes: 70 additions & 19 deletions src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Media;

// ReSharper disable MemberCanBePrivate.Global
Expand All @@ -8,7 +9,7 @@
namespace Consolonia.Core.Drawing.PixelBufferImplementation
{
[DebuggerDisplay("'{Foreground.Symbol.Text}' [{Foreground.Color}, {Background.Color}]")]
public readonly struct Pixel
public readonly struct Pixel : IEquatable<Pixel>
{
public PixelForeground Foreground { get; }

Expand All @@ -18,52 +19,75 @@ public readonly struct Pixel

public Pixel(bool isCaret)
{
Foreground = new PixelForeground(new SimpleSymbol());
Background = new PixelBackground(PixelBackgroundMode.Transparent);
Foreground = new PixelForeground();
Background = new PixelBackground();
IsCaret = isCaret;
}

public Pixel(ISymbol symbol, Color foregroundColor, FontStyle style = FontStyle.Normal,
FontWeight weight = FontWeight.Normal, TextDecorationCollection textDecorations = null) : this(
new PixelForeground(symbol, weight, style, textDecorations, foregroundColor),
/// <summary>
/// Make a pixel foreground with transparent background
/// </summary>
/// <param name="symbol"></param>
/// <param name="foregroundColor"></param>
/// <param name="style"></param>
/// <param name="weight"></param>
/// <param name="textDecorations"></param>
public Pixel(ISymbol symbol,
Color foregroundColor,
FontStyle style = FontStyle.Normal,
FontWeight weight = FontWeight.Normal,
TextDecorationCollection textDecorations = null) : this(
new PixelForeground(symbol, foregroundColor, weight, style, textDecorations),
new PixelBackground(PixelBackgroundMode.Transparent))
{
}

public Pixel(Color backgroundColor) : this(
new PixelBackground(PixelBackgroundMode.Colored, backgroundColor))
{
}

public Pixel(PixelBackgroundMode mode) : this(new PixelBackground(mode))
{
}

public Pixel(PixelBackground background) : this(new PixelForeground(new SimpleSymbol()),
background)
/// <summary>
/// Make a pixel with only background color, but no foreground
/// </summary>
/// <param name="background"></param>
public Pixel(PixelBackground background) :
this(new PixelForeground(new SimpleSymbol(), Colors.Transparent),
background)
{
}

public Pixel(PixelForeground foreground, PixelBackground background, bool isCaret = false)
/// <summary>
/// Make a pixel with foreground and background
/// </summary>
/// <param name="foreground"></param>
/// <param name="background"></param>
/// <param name="isCaret"></param>
public Pixel(PixelForeground foreground,
PixelBackground background,
bool isCaret = false)
{
Foreground = foreground;
Background = background;
IsCaret = isCaret;
}

/// <summary>
/// Blend the pixelAbove with the this pixel.
/// </summary>
/// <param name="pixelAbove"></param>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public Pixel Blend(Pixel pixelAbove)
{
PixelForeground newForeground;
PixelBackground newBackground;

if (pixelAbove.IsCaret) return new Pixel(Foreground, Background, true);

switch (pixelAbove.Background.Mode)
{
case PixelBackgroundMode.Colored:
// merge pixelAbove into this pixel using alpha channel.
Color mergedColors = MergeColors(Background.Color, pixelAbove.Background.Color);
newForeground = pixelAbove.Foreground;
newBackground = new PixelBackground(mergedColors);
return new Pixel(newForeground, newBackground);
return new Pixel(newForeground, newBackground, pixelAbove.IsCaret);

case PixelBackgroundMode.Transparent:
// if the foreground is transparent, ignore pixelAbove foreground.
Expand Down Expand Up @@ -108,5 +132,32 @@ private static Color MergeColors(Color target, Color source)

return new Color(0xFF, red, green, blue);
}

public bool Equals(Pixel other)
{
return Foreground.Equals(other.Foreground) &&
Background.Equals(other.Background) &&
IsCaret.Equals(other.IsCaret);
}

public override bool Equals([NotNullWhen(true)] object obj)
{
return obj is Pixel other && Equals(other);
}

public override int GetHashCode()
{
return HashCode.Combine(Foreground, Background, IsCaret);
}

public static bool operator ==(Pixel left, Pixel right)
{
return left.Equals(right);
}

public static bool operator !=(Pixel left, Pixel right)
{
return !left.Equals(right);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Media;

namespace Consolonia.Core.Drawing.PixelBufferImplementation
{
[DebuggerDisplay("[{Color}, {Mode}]")]
public readonly struct PixelBackground
public readonly struct PixelBackground : IEquatable<PixelBackground>
{
public PixelBackground()
{
Mode = PixelBackgroundMode.Transparent;
Color = Colors.Transparent;
}

public PixelBackground(Color color)
{
Mode = color.A == 0 ? PixelBackgroundMode.Transparent : PixelBackgroundMode.Colored;
Expand All @@ -15,7 +22,7 @@ public PixelBackground(Color color)

public PixelBackground(PixelBackgroundMode mode, Color? color = null)
{
Color = color ?? Colors.Black;
Color = color ?? Colors.Transparent;
Mode = mode;
}

Expand Down Expand Up @@ -44,5 +51,30 @@ public PixelBackground Shade()

return new PixelBackground(newMode, newColor);
}

public bool Equals(PixelBackground other)
{
return Color.Equals(other.Color) && Mode == other.Mode;
}

public override bool Equals([NotNullWhen(true)] object obj)
{
return obj is PixelBackground other && Equals(other);
}

public override int GetHashCode()
{
return HashCode.Combine(Color, Mode);
}

public static bool operator ==(PixelBackground left, PixelBackground right)
{
return left.Equals(right);
}

public static bool operator !=(PixelBackground left, PixelBackground right)
{
return !left.Equals(right);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using Avalonia;

// ReSharper disable UnusedMember.Global
Expand Down Expand Up @@ -53,5 +54,10 @@ public bool Equals(PixelBufferCoordinate secondPoint)
{
return X == secondPoint.X && Y == secondPoint.Y;
}

public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
}
Loading

0 comments on commit eb7c2da

Please sign in to comment.