using System;
using Amphora;
using Glass.Text;
using Glass.Typesetting;
using Glass.UnitTests.Typesetting;
using UnitTestSharp;
namespace Glass.UnitTests.Text
{
public class FaceResolverTests : TestFixture
{
private const string Page = "family.png";
///
/// A family of four faces on one page, each a different width so a test can tell them apart.
///
private class Typeface
{
public readonly SpriteFontDefinition Regular = FacedMeasuringTests.Face(10, page: Page);
public readonly SpriteFontDefinition Bold = FacedMeasuringTests.Face(11, page: Page);
public readonly SpriteFontDefinition Italic = FacedMeasuringTests.Face(12, page: Page);
public readonly SpriteFontDefinition BoldItalic = FacedMeasuringTests.Face(13, page: Page);
public Typeface()
{
new FontFamily(Regular, Bold, Italic, BoldItalic);
}
}
private static DynamicArray Spans(params StyleSpan[] spans)
{
var returnMe = new DynamicArray(capacity: spans.Length);
foreach (StyleSpan span in spans)
{
returnMe.Add(span);
}
return returnMe;
}
private static StyleSpan Span(int start, int end, TextStyle style) => new StyleSpan(start, end, style);
public class ResolveTests : TestFixture
{
public void NoSpans_LeaveEverythingInTheBaseFont()
{
var typeface = new Typeface();
CheckTrue(FaceResolver.Resolve(typeface.Regular, null, reuse: null).IsUniform);
CheckTrue(FaceResolver.Resolve(typeface.Regular, Spans(), reuse: null).IsUniform);
}
public void SpansThatAskForNoFace_LeaveEverythingInTheBaseFont()
{
var typeface = new Typeface();
DynamicArray spans = Spans(Span(0, 3, new TextStyle { TintColor = Prism.Colors.Red }));
CheckTrue(FaceResolver.Resolve(typeface.Regular, spans, reuse: null).IsUniform);
}
public void BoldSpan_IsSetInTheBoldFace()
{
var typeface = new Typeface();
DynamicArray spans = Spans(Span(2, 4, new TextStyle { Bold = true }));
FaceRuns faces = FaceResolver.Resolve(typeface.Regular, spans, reuse: null);
CheckTrue(ReferenceEquals(typeface.Regular, faces.FontAt(1)));
CheckTrue(ReferenceEquals(typeface.Bold, faces.FontAt(2)));
CheckTrue(ReferenceEquals(typeface.Bold, faces.FontAt(3)));
CheckTrue(ReferenceEquals(typeface.Regular, faces.FontAt(4)));
}
public void ItalicSpan_IsSetInTheItalicFace()
{
var typeface = new Typeface();
DynamicArray spans = Spans(Span(0, 2, new TextStyle { Italic = true }));
FaceRuns faces = FaceResolver.Resolve(typeface.Regular, spans, reuse: null);
CheckTrue(ReferenceEquals(typeface.Italic, faces.FontAt(0)));
}
public void BoldItalicSpan_IsSetInTheBoldItalicFace()
{
var typeface = new Typeface();
DynamicArray spans = Spans(Span(0, 2, new TextStyle { Bold = true, Italic = true }));
FaceRuns faces = FaceResolver.Resolve(typeface.Regular, spans, reuse: null);
CheckTrue(ReferenceEquals(typeface.BoldItalic, faces.FontAt(1)));
}
public void BoldOffInARegularFont_ChangesNothing()
{
var typeface = new Typeface();
DynamicArray spans = Spans(Span(0, 2, new TextStyle { Bold = false, Italic = false }));
CheckTrue(FaceResolver.Resolve(typeface.Regular, spans, reuse: null).IsUniform);
}
public void ItalicOverABoldBaseFont_IsBoldItalic()
{
var typeface = new Typeface();
DynamicArray spans = Spans(Span(0, 2, new TextStyle { Italic = true }));
FaceRuns faces = FaceResolver.Resolve(typeface.Bold, spans, reuse: null);
CheckTrue(ReferenceEquals(typeface.BoldItalic, faces.FontAt(0)));
}
public void BoldOffOverABoldBaseFont_IsRegular()
{
var typeface = new Typeface();
DynamicArray spans = Spans(Span(1, 2, new TextStyle { Bold = false }));
FaceRuns faces = FaceResolver.Resolve(typeface.Bold, spans, reuse: null);
CheckTrue(ReferenceEquals(typeface.Bold, faces.FontAt(0)));
CheckTrue(ReferenceEquals(typeface.Regular, faces.FontAt(1)));
}
public void BoldOverABoldBaseFont_ChangesNothing()
{
var typeface = new Typeface();
DynamicArray spans = Spans(Span(0, 2, new TextStyle { Bold = true }));
CheckTrue(FaceResolver.Resolve(typeface.Bold, spans, reuse: null).IsUniform);
}
public void ExplicitFont_IsSetInIt()
{
var typeface = new Typeface();
SpriteFontDefinition other = FacedMeasuringTests.Face(20, page: Page);
DynamicArray spans = Spans(Span(1, 3, new TextStyle { Font = other }));
FaceRuns faces = FaceResolver.Resolve(typeface.Regular, spans, reuse: null);
CheckTrue(ReferenceEquals(other, faces.FontAt(2)));
}
public void BoldOverAnExplicitFont_IsItsFamilysBoldFace()
{
var typeface = new Typeface();
var other = new Typeface();
DynamicArray spans = Spans(Span(0, 2, new TextStyle { Font = other.Regular, Bold = true }));
FaceRuns faces = FaceResolver.Resolve(typeface.Regular, spans, reuse: null);
CheckTrue(ReferenceEquals(other.Bold, faces.FontAt(0)));
}
public void SeparateSpans_MakeSeparateRuns()
{
var typeface = new Typeface();
DynamicArray spans = Spans(
Span(0, 2, new TextStyle { Bold = true }), Span(3, 5, new TextStyle { Italic = true }));
FaceRuns faces = FaceResolver.Resolve(typeface.Regular, spans, reuse: null);
CheckTrue(ReferenceEquals(typeface.Bold, faces.FontAt(1)));
CheckTrue(ReferenceEquals(typeface.Regular, faces.FontAt(2)));
CheckTrue(ReferenceEquals(typeface.Italic, faces.FontAt(4)));
}
public void OverlappingSpans_LetTheFirstToHoldACharacterDecideItsFont()
{
var typeface = new Typeface();
DynamicArray spans = Spans(
Span(0, 4, new TextStyle { Bold = true }), Span(2, 6, new TextStyle { Italic = true }));
FaceRuns faces = FaceResolver.Resolve(typeface.Regular, spans, reuse: null);
CheckTrue(ReferenceEquals(typeface.Bold, faces.FontAt(3)));
CheckTrue(ReferenceEquals(typeface.Italic, faces.FontAt(4)));
CheckTrue(ReferenceEquals(typeface.Italic, faces.FontAt(5)));
CheckTrue(ReferenceEquals(typeface.Regular, faces.FontAt(6)));
}
public void ASpanOfNoFaceCoveringAnother_KeepsTheBaseFontThere()
{
var typeface = new Typeface();
DynamicArray spans = Spans(Span(0, 6, new TextStyle { TintColor = Prism.Colors.Red }),
Span(2, 4, new TextStyle { Bold = true }));
CheckTrue(FaceResolver.Resolve(typeface.Regular, spans, reuse: null).IsUniform);
}
public void ASpanInsideAnother_AddsNothing()
{
var typeface = new Typeface();
DynamicArray spans = Spans(
Span(0, 6, new TextStyle { Bold = true }), Span(2, 4, new TextStyle { Italic = true }));
FaceRuns faces = FaceResolver.Resolve(typeface.Regular, spans, reuse: null);
CheckTrue(ReferenceEquals(typeface.Bold, faces.FontAt(3)));
CheckEqual(2, faces.Count);
}
public void SpansThatMeetInTheSameFace_AreOneRun()
{
var typeface = new Typeface();
DynamicArray spans = Spans(
Span(0, 2, new TextStyle { Bold = true }), Span(2, 4, new TextStyle { Bold = true }));
CheckEqual(2, FaceResolver.Resolve(typeface.Regular, spans, reuse: null).Count);
}
public void BoldInAFontOfNoFamily_Throws()
{
SpriteFontDefinition lone = FacedMeasuringTests.Face(10, page: Page);
DynamicArray spans = Spans(Span(0, 2, new TextStyle { Bold = true }));
CheckThrow(typeof(InvalidOperationException));
FaceResolver.Resolve(lone, spans, reuse: null);
}
public void ItalicInAFamilyWithNoItalicFace_Throws()
{
SpriteFontDefinition regular = FacedMeasuringTests.Face(10, page: Page);
new FontFamily(regular, bold: FacedMeasuringTests.Face(11, page: Page));
DynamicArray spans = Spans(Span(0, 2, new TextStyle { Italic = true }));
CheckThrow(typeof(InvalidOperationException));
FaceResolver.Resolve(regular, spans, reuse: null);
}
public void AnExplicitFontOnAnotherPage_Throws()
{
var typeface = new Typeface();
SpriteFontDefinition elsewhere = FacedMeasuringTests.Face(10, page: "other.png");
DynamicArray spans = Spans(Span(0, 2, new TextStyle { Font = elsewhere }));
CheckThrow(typeof(InvalidOperationException));
FaceResolver.Resolve(typeface.Regular, spans, reuse: null);
}
public void NullBaseFont_Throws()
{
CheckThrow(typeof(ArgumentNullException));
FaceResolver.Resolve(null, Spans(), reuse: null);
}
public void Reuse_IsFilledInPlace()
{
var typeface = new Typeface();
var reuse = new FaceRuns(typeface.Regular);
reuse.Add(0, 9, typeface.Italic);
DynamicArray spans = Spans(Span(1, 2, new TextStyle { Bold = true }));
FaceRuns faces = FaceResolver.Resolve(typeface.Regular, spans, reuse);
CheckTrue(ReferenceEquals(reuse, faces));
CheckTrue(ReferenceEquals(typeface.Regular, faces.FontAt(0)));
CheckTrue(ReferenceEquals(typeface.Bold, faces.FontAt(1)));
CheckTrue(ReferenceEquals(typeface.Regular, faces.FontAt(5)));
}
public void ReuseBuiltOnAnotherFont_IsLeftAloneAndNewRunsMade()
{
var typeface = new Typeface();
var reuse = new FaceRuns(typeface.Bold);
FaceRuns faces = FaceResolver.Resolve(typeface.Regular, Spans(), reuse);
CheckFalse(ReferenceEquals(reuse, faces));
CheckTrue(ReferenceEquals(typeface.Regular, faces.BaseFont));
}
private static readonly Typeface AllocationTypeface = new Typeface();
private static readonly FaceRuns AllocationRuns = new FaceRuns(AllocationTypeface.Regular);
private static readonly DynamicArray AllocationSpans = Spans(
Span(0, 2, new TextStyle { Bold = true }), Span(3, 5, new TextStyle { Italic = true }));
[AllocationTest]
public void Resolving_AllocatesNothingOnceWarm()
{
FaceRuns faces = FaceResolver.Resolve(AllocationTypeface.Regular, AllocationSpans, AllocationRuns);
CheckNoAllocations();
CheckEqual(4, faces.Count);
}
}
}
}