using System; using Amphora; using Prism; using Glass.Text; using UnitTestSharp; using static Glass.Text.Plurals; using static Glass.Text.Styles; namespace Glass.UnitTests.Text { public class PluralLineTests : TestFixture { public static string Render(in PluralLine line) { return line.ToString(); } public static string In(string tag, in PluralLine line) { char[] scratch = null; return line.ViewIn(NumberFormat.Invariant, PluralRules.For(tag), ref scratch).ToString(); } public static string BoxLine(int boxes) { return Render(Plural(Count(boxes), one: $"There is {Amount} box left.", other: $"There are {Amount} boxes left.")); } public class CtorTests : TestFixture { public void KeepsTheSelectorAndWhetherItIsOrdinal() { var line = new PluralLine(Count(3), true, $"x", default, default, default, default, default, default); CheckEqual(NumberKind.Count, line.Selector.Kind); CheckEqual(3L, line.Selector.Number); CheckTrue(line.IsOrdinal); } public void WithoutAnOtherVariant_Throws() { CheckThrow(typeof(ArgumentException)); new PluralLine(Count(3), false, default, default, default, default, default, default, default); } public void ASelectorWithNoPluralForm_Throws() { CheckThrow(typeof(ArgumentException)); new PluralLine(Scientific(3, 2), false, $"x", default, default, default, default, default, default); } public void AStringSelector_Throws() { CheckThrow(typeof(ArgumentException)); new PluralLine("three", false, $"x", default, default, default, default, default, default); } } public class VariantOfTests : TestFixture { public void ReturnsEachVariantGiven() { PluralLine line = Plural(Count(1), zero: $"z", one: $"o", two: $"t", few: $"f", many: $"m", other: $"x"); CheckEqual("z", line.VariantOf(PluralCategory.Zero).ToString()); CheckEqual("o", line.VariantOf(PluralCategory.One).ToString()); CheckEqual("t", line.VariantOf(PluralCategory.Two).ToString()); CheckEqual("f", line.VariantOf(PluralCategory.Few).ToString()); CheckEqual("m", line.VariantOf(PluralCategory.Many).ToString()); CheckEqual("x", line.VariantOf(PluralCategory.Other).ToString()); } public void AVariantNotGiven_IsNotBuilt() { PluralLine line = Plural(Count(1), other: $"x"); CheckFalse(line.VariantOf(PluralCategory.One).IsBuilt); CheckTrue(line.VariantOf(PluralCategory.Other).IsBuilt); } } public class NoneVariantTests : TestFixture { public void IsTheVariantGivenForExactlyZero() { PluralLine line = Plural(Count(1), none: $"nothing", other: $"x"); CheckEqual("nothing", line.NoneVariant().ToString()); } public void WhenNotGiven_IsNotBuilt() { CheckFalse(Plural(Count(1), other: $"x").NoneVariant().IsBuilt); } } public class CategoryInTests : TestFixture { public void CountsByCardinalRules() { CheckEqual(PluralCategory.One, Plural(Count(1), other: $"x").CategoryIn(PluralRules.English)); CheckEqual(PluralCategory.Other, Plural(Count(2), other: $"x").CategoryIn(PluralRules.English)); } public void AnOrdinalLine_UsesOrdinalRules() { CheckEqual(PluralCategory.Two, Ordinal(Plain(2), other: $"x").CategoryIn(PluralRules.English)); } public void TheLanguagesRulesDecide() { CheckEqual(PluralCategory.Few, Plural(Count(2), other: $"x").CategoryIn(PluralRules.For("ru"))); } public void NullRules_Throw() { PluralLine line = Plural(Count(1), other: $"x"); CheckThrow(typeof(ArgumentNullException)); line.CategoryIn(null); } } public class ViewInTests : TestFixture { public void English_PicksOneForOneAndOtherForTheRest() { CheckEqual("There is 1 box left.", BoxLine(1)); CheckEqual("There are 2 boxes left.", BoxLine(2)); CheckEqual("There are 0 boxes left.", BoxLine(0)); } public void TheAmount_IsWrittenInTheGivenFormat() { var line = Plural(Count(1234567), other: $"{Amount} boxes"); char[] scratch = null; CheckEqual("1.234.567 boxes", line.ViewIn(NumberFormat.FromCulture( new System.Globalization.CultureInfo("de-DE")), PluralRules.English, ref scratch).ToString()); } public void None_BeatsTheCategoryForExactlyZero() { var line = Plural(Count(0), none: $"No boxes left.", other: $"{Amount} boxes left."); CheckEqual("No boxes left.", Render(line)); } public void None_BeatsAFormThatCoversZero() { var line = Plural(Count(0), none: $"None", one: $"One", other: $"Many"); CheckEqual("None", In("fr", line)); } public void None_IsNotUsedForOtherNumbers() { var line = Plural(Count(1), none: $"None", one: $"One", other: $"Many"); CheckEqual("One", Render(line)); } public void None_IsUsedForAFixedNumberThatShowsAsZero() { var line = Plural(Fixed(0.04, 1), none: $"None", other: $"{Amount}"); CheckEqual("None", Render(line)); } public void None_IsNotUsedForAFixedNumberThatShowsAsNonZero() { var line = Plural(Fixed(0.06, 1), none: $"None", other: $"{Amount}"); CheckEqual("0.1", Render(line)); } public void AFormNotGiven_FallsBackToOther() { var line = Plural(Count(2), one: $"One", other: $"Other"); CheckEqual("Other", In("ru", line)); } [IgnoreTest] public string Boxes(int count) { return In("ru", Plural(Count(count), one: $"{Amount} korobka", few: $"{Amount} korobki", many: $"{Amount} korobok", other: $"{Amount} korobki")); } public void Russian_PicksFromItsFourForms() { CheckEqual("1 korobka", Boxes(1)); CheckEqual("2 korobki", Boxes(2)); CheckEqual("5 korobok", Boxes(5)); CheckEqual("21 korobka", Boxes(21)); CheckEqual("11 korobok", Boxes(11)); } public void AFixedSelector_ChoosesByItsDisplayedDecimals() { CheckEqual("1", Render(Plural(Fixed(1, 0), one: $"{Amount}", other: $"{Amount} more"))); CheckEqual("1.0 more", Render(Plural(Fixed(1, 1), one: $"{Amount}", other: $"{Amount} more"))); } public void ASelectorShownAsASymbol_IsOther() { var line = Plural(Fixed(double.NaN, 1), one: $"One", other: $"{Amount}"); CheckEqual("NaN", Render(line)); } public void OrdinalsFollowTheOrdinalRules() { CheckEqual("1st", OrdinalLine(1)); CheckEqual("2nd", OrdinalLine(2)); CheckEqual("3rd", OrdinalLine(3)); CheckEqual("4th", OrdinalLine(4)); CheckEqual("11th", OrdinalLine(11)); CheckEqual("21st", OrdinalLine(21)); CheckEqual("22nd", OrdinalLine(22)); CheckEqual("23rd", OrdinalLine(23)); CheckEqual("101st", OrdinalLine(101)); } [IgnoreTest] public string OrdinalLine(int place) { return Render(Ordinal(Plain(place), one: $"{Amount}st", two: $"{Amount}nd", few: $"{Amount}rd", other: $"{Amount}th")); } public void NullRules_Throw() { var line = Plural(Count(1), other: $"x"); char[] scratch = null; CheckThrow(typeof(ArgumentNullException)); line.ViewIn(NumberFormat.Invariant, null, ref scratch); } public void NullFormat_Throws() { var line = Plural(Count(1), other: $"x"); char[] scratch = null; CheckThrow(typeof(ArgumentNullException)); line.ViewIn(null, PluralRules.English, ref scratch); } public void TheScratchBuffer_IsReusedAcrossLines() { char[] scratch = null; var first = Plural(Count(1), other: $"a long first variant of the line"); var second = Plural(Count(1), other: $"short"); first.ViewIn(NumberFormat.Invariant, PluralRules.English, ref scratch); char[] grown = scratch; second.ViewIn(NumberFormat.Invariant, PluralRules.English, ref scratch); CheckTrue(ReferenceEquals(grown, scratch)); } } public class ViewInWithSpansTests : TestFixture { private char[] scratch; private readonly DynamicArray spans = new DynamicArray(); [IgnoreTest] public string Read(in PluralLine line) { return line.ViewIn(NumberFormat.Invariant, PluralRules.English, ref scratch, spans).ToString(); } [IgnoreTest] public int LengthOfRead(in PluralLine line) { StringView view = line.ViewIn(NumberFormat.Invariant, PluralRules.English, ref scratch, spans); return view.EndIndex - view.StartIndex; } [UnitTestSharp.AllocationTest] public void ReadingAPluralLine_AllocatesNothingOnceTheBuffersHaveGrown() { int boxes = 3; int length = LengthOfRead(Plural(Count(boxes), one: $"There is {Tint(Colors.Gold, Amount)} box.", other: $"There are {Tint(Colors.Gold, Amount)} boxes.")); CheckNoAllocations(); CheckEqual("There are 3 boxes.".Length, length); } public void AStyledAmount_HasASpanOverIt() { string text = Read(Plural(Count(12), other: $"Got {Tint(Colors.Gold, Amount)} boxes")); CheckEqual("Got 12 boxes", text); CheckEqual(1, spans.Count); CheckEqual(new StyleSpan(4, 6, new TextStyle { TintColor = Colors.Gold }), spans[0]); } public void TheSpansOfTheChosenVariant_ReplaceThoseOfAnEarlierRead() { Read(Plural(Count(12), other: $"{Tint(Colors.Gold, Amount)} {Tint(Colors.Red, Amount)}")); Read(Plural(Count(12), other: $"plain")); CheckEqual(0, spans.Count); } } public class ToStringTests : TestFixture { public void IsTheVariantEnglishChooses() { CheckEqual("There is 1 box left.", BoxLine(1)); } public void WritesCountsInTheInvariantFormat() { CheckEqual("1,000 boxes", Render(Plural(Count(1000), other: $"{Amount} boxes"))); } } } }