TextFormatterTests.cs 137 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624
  1. using System.Text;
  2. using Xunit.Abstractions;
  3. using static Terminal.Gui.SpinnerStyle;
  4. // Alias Console to MockConsole so we don't accidentally use Console
  5. namespace Terminal.Gui.TextTests;
  6. public class TextFormatterTests
  7. {
  8. private readonly ITestOutputHelper _output;
  9. public TextFormatterTests (ITestOutputHelper output) { _output = output; }
  10. public static IEnumerable<object []> CMGlyphs =>
  11. new List<object []> { new object [] { $"{CM.Glyphs.LeftBracket} Say Hello 你 {CM.Glyphs.RightBracket}", 16, 15 } };
  12. public static IEnumerable<object []> FormatEnvironmentNewLine =>
  13. new List<object []>
  14. {
  15. new object []
  16. {
  17. $"Line1{Environment.NewLine}Line2{Environment.NewLine}Line3{Environment.NewLine}",
  18. 60,
  19. new [] { "Line1", "Line2", "Line3" }
  20. }
  21. };
  22. public static IEnumerable<object []> SplitEnvironmentNewLine =>
  23. new List<object []>
  24. {
  25. new object []
  26. {
  27. $"First Line 界{Environment.NewLine}Second Line 界{Environment.NewLine}Third Line 界",
  28. new [] { "First Line 界", "Second Line 界", "Third Line 界" }
  29. },
  30. new object []
  31. {
  32. $"First Line 界{Environment.NewLine}Second Line 界{Environment.NewLine}Third Line 界{Environment.NewLine}",
  33. new [] { "First Line 界", "Second Line 界", "Third Line 界", "" }
  34. }
  35. };
  36. [Fact]
  37. public void Basic_Usage_With_AutoSize_True ()
  38. {
  39. var testText = "test";
  40. var testBounds = new Rectangle (0, 0, 100, 1);
  41. var tf = new TextFormatter ();
  42. // Manually set AutoSize to true
  43. tf.AutoSize = true;
  44. tf.Text = testText;
  45. Size expectedSize = new (testText.Length, 1);
  46. Assert.Equal (testText, tf.Text);
  47. Assert.Equal (TextAlignment.Left, tf.Alignment);
  48. Assert.Equal (expectedSize, tf.Size);
  49. tf.Draw (testBounds, new Attribute (), new Attribute ());
  50. Assert.Equal (expectedSize, tf.Size);
  51. Assert.NotEmpty (tf.GetLines ());
  52. tf.Alignment = TextAlignment.Right;
  53. expectedSize = new (testText.Length, 1);
  54. Assert.Equal (testText, tf.Text);
  55. Assert.Equal (TextAlignment.Right, tf.Alignment);
  56. Assert.Equal (expectedSize, tf.Size);
  57. tf.Draw (testBounds, new Attribute (), new Attribute ());
  58. Assert.Equal (expectedSize, tf.Size);
  59. Assert.NotEmpty (tf.GetLines ());
  60. tf.Alignment = TextAlignment.Right;
  61. expectedSize = new (testText.Length * 2, 1);
  62. tf.Size = expectedSize;
  63. Assert.Equal (testText, tf.Text);
  64. Assert.Equal (TextAlignment.Right, tf.Alignment);
  65. Assert.Equal (expectedSize, tf.Size);
  66. tf.Draw (testBounds, new Attribute (), new Attribute ());
  67. Assert.Equal (expectedSize, tf.Size);
  68. Assert.NotEmpty (tf.GetLines ());
  69. tf.Alignment = TextAlignment.Centered;
  70. expectedSize = new (testText.Length * 2, 1);
  71. tf.Size = expectedSize;
  72. Assert.Equal (testText, tf.Text);
  73. Assert.Equal (TextAlignment.Centered, tf.Alignment);
  74. Assert.Equal (expectedSize, tf.Size);
  75. tf.Draw (testBounds, new Attribute (), new Attribute ());
  76. Assert.Equal (expectedSize, tf.Size);
  77. Assert.NotEmpty (tf.GetLines ());
  78. }
  79. [Theory]
  80. [InlineData (null)]
  81. [InlineData ("")]
  82. public void CalcRect_Invalid_Returns_Empty (string text)
  83. {
  84. Assert.Equal (Rectangle.Empty, TextFormatter.CalcRect (0, 0, text));
  85. Assert.Equal (new (new (1, 2), Size.Empty), TextFormatter.CalcRect (1, 2, text));
  86. Assert.Equal (new (new (-1, -2), Size.Empty), TextFormatter.CalcRect (-1, -2, text));
  87. }
  88. [Theory]
  89. [InlineData ("line1\nline2", 5, 2)]
  90. [InlineData ("\nline2", 5, 2)]
  91. [InlineData ("\n\n", 0, 3)]
  92. [InlineData ("\n\n\n", 0, 4)]
  93. [InlineData ("line1\nline2\nline3long!", 10, 3)]
  94. [InlineData ("line1\nline2\n\n", 5, 4)]
  95. [InlineData ("line1\r\nline2", 5, 2)]
  96. [InlineData (" ~  s  gui.cs   master ↑10\n", 31, 2)]
  97. [InlineData ("\n ~  s  gui.cs   master ↑10", 31, 2)]
  98. [InlineData (" ~  s  gui.cs   master\n↑10", 27, 2)]
  99. public void CalcRect_MultiLine_Returns_nHigh (string text, int expectedWidth, int expectedLines)
  100. {
  101. Assert.Equal (new (0, 0, expectedWidth, expectedLines), TextFormatter.CalcRect (0, 0, text));
  102. string [] lines = text.Split (text.Contains (Environment.NewLine) ? Environment.NewLine : "\n");
  103. int maxWidth = lines.Max (s => s.GetColumns ());
  104. var lineWider = 0;
  105. for (var i = 0; i < lines.Length; i++)
  106. {
  107. int w = lines [i].GetColumns ();
  108. if (w == maxWidth)
  109. {
  110. lineWider = i;
  111. }
  112. }
  113. Assert.Equal (new (0, 0, maxWidth, expectedLines), TextFormatter.CalcRect (0, 0, text));
  114. Assert.Equal (
  115. new (
  116. 0,
  117. 0,
  118. lines [lineWider].ToRuneList ().Sum (r => Math.Max (r.GetColumns (), 0)),
  119. expectedLines
  120. ),
  121. TextFormatter.CalcRect (0, 0, text)
  122. );
  123. }
  124. [Theory]
  125. [InlineData ("test")]
  126. [InlineData (" ~  s  gui.cs   master ↑10")]
  127. public void CalcRect_SingleLine_Returns_1High (string text)
  128. {
  129. Assert.Equal (new (0, 0, text.GetRuneCount (), 1), TextFormatter.CalcRect (0, 0, text));
  130. Assert.Equal (new (0, 0, text.GetColumns (), 1), TextFormatter.CalcRect (0, 0, text));
  131. }
  132. [Theory]
  133. [InlineData (14, 1, TextDirection.LeftRight_TopBottom)]
  134. [InlineData (1, 14, TextDirection.TopBottom_LeftRight)]
  135. public void CalcRect_With_Combining_Runes (int width, int height, TextDirection textDirection)
  136. {
  137. var text = "Les Mise\u0328\u0301rables";
  138. Assert.Equal (new (0, 0, width, height), TextFormatter.CalcRect (0, 0, text, textDirection));
  139. }
  140. [Theory]
  141. [InlineData ("test", TextDirection.LeftRight_TopBottom)]
  142. [InlineData (" ~  s  gui.cs   master ↑10", TextDirection.LeftRight_TopBottom)]
  143. [InlineData ("Say Hello view4 你", TextDirection.LeftRight_TopBottom)]
  144. [InlineData ("Say Hello view4 你", TextDirection.RightLeft_TopBottom)]
  145. [InlineData ("Say Hello view4 你", TextDirection.LeftRight_BottomTop)]
  146. [InlineData ("Say Hello view4 你", TextDirection.RightLeft_BottomTop)]
  147. public void CalcRect_Horizontal_Width_Correct (string text, TextDirection textDirection)
  148. {
  149. // The width is the number of columns in the text
  150. Assert.Equal (new Size (text.GetColumns (), 1), TextFormatter.CalcRect (0, 0, text, textDirection).Size);
  151. }
  152. [Theory]
  153. [InlineData ("test", TextDirection.TopBottom_LeftRight)]
  154. [InlineData (" ~  s  gui.cs   master ↑10", TextDirection.TopBottom_LeftRight)]
  155. [InlineData ("Say Hello view4 你", TextDirection.TopBottom_LeftRight)]
  156. [InlineData ("Say Hello view4 你", TextDirection.TopBottom_RightLeft)]
  157. [InlineData ("Say Hello view4 你", TextDirection.BottomTop_LeftRight)]
  158. [InlineData ("Say Hello view4 你", TextDirection.BottomTop_RightLeft)]
  159. public void CalcRect_Vertical_Height_Correct (string text, TextDirection textDirection)
  160. {
  161. // The height is based both the number of lines and the number of wide chars
  162. Assert.Equal (new Size (1 + text.GetColumns () - text.Length, text.Length), TextFormatter.CalcRect (0, 0, text, textDirection).Size);
  163. }
  164. [Theory]
  165. [InlineData ("")]
  166. [InlineData (null)]
  167. [InlineData ("test")]
  168. public void ClipAndJustify_Invalid_Returns_Original (string text)
  169. {
  170. string expected = string.IsNullOrEmpty (text) ? text : "";
  171. Assert.Equal (expected, TextFormatter.ClipAndJustify (text, 0, TextAlignment.Left));
  172. Assert.Equal (expected, TextFormatter.ClipAndJustify (text, 0, TextAlignment.Left));
  173. Assert.Throws<ArgumentOutOfRangeException> (
  174. () =>
  175. TextFormatter.ClipAndJustify (text, -1, TextAlignment.Left)
  176. );
  177. }
  178. [Theory]
  179. [InlineData ("test", "", 0)]
  180. [InlineData ("test", "te", 2)]
  181. [InlineData ("test", "test", int.MaxValue)]
  182. [InlineData ("A sentence has words.", "A sentence has words.", 22)] // should fit
  183. [InlineData ("A sentence has words.", "A sentence has words.", 21)] // should fit
  184. [InlineData ("A sentence has words.", "A sentence has words.", int.MaxValue)] // should fit
  185. [InlineData ("A sentence has words.", "A sentence has words", 20)] // Should not fit
  186. [InlineData ("A sentence has words.", "A sentence", 10)] // Should not fit
  187. [InlineData ("A\tsentence\thas\twords.", "A sentence has words.", int.MaxValue)]
  188. [InlineData ("A\tsentence\thas\twords.", "A sentence", 10)]
  189. [InlineData ("line1\nline2\nline3long!", "line1\nline2\nline3long!", int.MaxValue)]
  190. [InlineData ("line1\nline2\nline3long!", "line1\nline", 10)]
  191. [InlineData (" ~  s  gui.cs   master ↑10", " ~  s  ", 10)] // Unicode
  192. [InlineData ("Ð ÑÐ", "Ð ÑÐ", 5)] // should fit
  193. [InlineData ("Ð ÑÐ", "Ð ÑÐ", 4)] // should fit
  194. [InlineData ("Ð ÑÐ", "Ð Ñ", 3)] // Should not fit
  195. public void ClipAndJustify_Valid_Centered (string text, string justifiedText, int maxWidth)
  196. {
  197. var align = TextAlignment.Centered;
  198. var textDirection = TextDirection.LeftRight_TopBottom;
  199. var tabWidth = 1;
  200. Assert.Equal (
  201. justifiedText,
  202. TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth)
  203. );
  204. int expectedClippedWidth = Math.Min (justifiedText.GetRuneCount (), maxWidth);
  205. Assert.Equal (
  206. justifiedText,
  207. TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth)
  208. );
  209. Assert.True (justifiedText.GetRuneCount () <= maxWidth);
  210. Assert.True (justifiedText.GetColumns () <= maxWidth);
  211. Assert.Equal (expectedClippedWidth, justifiedText.GetRuneCount ());
  212. Assert.Equal (
  213. expectedClippedWidth,
  214. justifiedText.ToRuneList ().Sum (r => Math.Max (r.GetColumns (), 1))
  215. );
  216. Assert.True (expectedClippedWidth <= maxWidth);
  217. Assert.Equal (
  218. StringExtensions.ToString (justifiedText.ToRunes () [..expectedClippedWidth]),
  219. justifiedText
  220. );
  221. }
  222. [Theory]
  223. [InlineData ("test", "", 0)]
  224. [InlineData ("test", "te", 2)]
  225. [InlineData ("test", "test", int.MaxValue)] // This doesn't throw because it only create a word with length 1
  226. [InlineData ("A sentence has words.", "A sentence has words.", 22)] // should fit
  227. [InlineData ("A sentence has words.", "A sentence has words.", 21)] // should fit
  228. [InlineData (
  229. "A sentence has words.",
  230. "A sentence has words.",
  231. 500
  232. )] // should fit
  233. [InlineData ("A sentence has words.", "A sentence has words", 20)] // Should not fit
  234. [InlineData ("A sentence has words.", "A sentence", 10)] // Should not fit
  235. // Now throw System.OutOfMemoryException. See https://stackoverflow.com/questions/20672920/maxcapacity-of-stringbuilder
  236. //[InlineData ("A\tsentence\thas\twords.", "A sentence has words.", int.MaxValue)]
  237. [InlineData ("A\tsentence\thas\twords.", "A sentence", 10)]
  238. [InlineData (
  239. "line1\nline2\nline3long!",
  240. "line1\nline2\nline3long!",
  241. int.MaxValue
  242. )] // This doesn't throw because it only create a line with length 1
  243. [InlineData ("line1\nline2\nline3long!", "line1\nline", 10)]
  244. [InlineData (" ~  s  gui.cs   master ↑10", " ~  s  ", 10)] // Unicode
  245. [InlineData ("Ð ÑÐ", "Ð ÑÐ", 5)] // should fit
  246. [InlineData ("Ð ÑÐ", "Ð ÑÐ", 4)] // should fit
  247. [InlineData ("Ð ÑÐ", "Ð Ñ", 3)] // Should not fit
  248. public void ClipAndJustify_Valid_Justified (string text, string justifiedText, int maxWidth)
  249. {
  250. var align = TextAlignment.Justified;
  251. var textDirection = TextDirection.LeftRight_TopBottom;
  252. var tabWidth = 1;
  253. Assert.Equal (
  254. justifiedText,
  255. TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth)
  256. );
  257. int expectedClippedWidth = Math.Min (justifiedText.GetRuneCount (), maxWidth);
  258. Assert.Equal (
  259. justifiedText,
  260. TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth)
  261. );
  262. Assert.True (justifiedText.GetRuneCount () <= maxWidth);
  263. Assert.True (justifiedText.GetColumns () <= maxWidth);
  264. Assert.Equal (expectedClippedWidth, justifiedText.GetRuneCount ());
  265. Assert.Equal (
  266. expectedClippedWidth,
  267. justifiedText.ToRuneList ().Sum (r => Math.Max (r.GetColumns (), 1))
  268. );
  269. Assert.True (expectedClippedWidth <= maxWidth);
  270. Assert.Equal (
  271. StringExtensions.ToString (justifiedText.ToRunes () [..expectedClippedWidth]),
  272. justifiedText
  273. );
  274. // see Justify_ tests below
  275. }
  276. [Theory]
  277. [InlineData ("test", "", 0)]
  278. [InlineData ("test", "te", 2)]
  279. [InlineData ("test", "test", int.MaxValue)]
  280. [InlineData ("A sentence has words.", "A sentence has words.", 22)] // should fit
  281. [InlineData ("A sentence has words.", "A sentence has words.", 21)] // should fit
  282. [InlineData ("A sentence has words.", "A sentence has words.", int.MaxValue)] // should fit
  283. [InlineData ("A sentence has words.", "A sentence has words", 20)] // Should not fit
  284. [InlineData ("A sentence has words.", "A sentence", 10)] // Should not fit
  285. [InlineData ("A\tsentence\thas\twords.", "A sentence has words.", int.MaxValue)]
  286. [InlineData ("A\tsentence\thas\twords.", "A sentence", 10)]
  287. [InlineData ("line1\nline2\nline3long!", "line1\nline2\nline3long!", int.MaxValue)]
  288. [InlineData ("line1\nline2\nline3long!", "line1\nline", 10)]
  289. [InlineData (" ~  s  gui.cs   master ↑10", " ~  s  ", 10)] // Unicode
  290. [InlineData ("Ð ÑÐ", "Ð ÑÐ", 5)] // should fit
  291. [InlineData ("Ð ÑÐ", "Ð ÑÐ", 4)] // should fit
  292. [InlineData ("Ð ÑÐ", "Ð Ñ", 3)] // Should not fit
  293. public void ClipAndJustify_Valid_Left (string text, string justifiedText, int maxWidth)
  294. {
  295. var align = TextAlignment.Left;
  296. var textDirection = TextDirection.LeftRight_BottomTop;
  297. var tabWidth = 1;
  298. Assert.Equal (
  299. justifiedText,
  300. TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth)
  301. );
  302. int expectedClippedWidth = Math.Min (justifiedText.GetRuneCount (), maxWidth);
  303. Assert.Equal (
  304. justifiedText,
  305. TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth)
  306. );
  307. Assert.True (justifiedText.GetRuneCount () <= maxWidth);
  308. Assert.True (justifiedText.GetColumns () <= maxWidth);
  309. Assert.Equal (expectedClippedWidth, justifiedText.GetRuneCount ());
  310. Assert.Equal (
  311. expectedClippedWidth,
  312. justifiedText.ToRuneList ().Sum (r => Math.Max (r.GetColumns (), 1))
  313. );
  314. Assert.True (expectedClippedWidth <= maxWidth);
  315. Assert.Equal (
  316. StringExtensions.ToString (justifiedText.ToRunes () [..expectedClippedWidth]),
  317. justifiedText
  318. );
  319. }
  320. [Theory]
  321. [InlineData ("test", "", 0)]
  322. [InlineData ("test", "te", 2)]
  323. [InlineData ("test", "test", int.MaxValue)]
  324. [InlineData ("A sentence has words.", "A sentence has words.", 22)] // should fit
  325. [InlineData ("A sentence has words.", "A sentence has words.", 21)] // should fit
  326. [InlineData ("A sentence has words.", "A sentence has words.", int.MaxValue)] // should fit
  327. [InlineData ("A sentence has words.", "A sentence has words", 20)] // Should not fit
  328. [InlineData ("A sentence has words.", "A sentence", 10)] // Should not fit
  329. [InlineData ("A\tsentence\thas\twords.", "A sentence has words.", int.MaxValue)]
  330. [InlineData ("A\tsentence\thas\twords.", "A sentence", 10)]
  331. [InlineData ("line1\nline2\nline3long!", "line1\nline2\nline3long!", int.MaxValue)]
  332. [InlineData ("line1\nline2\nline3long!", "line1\nline", 10)]
  333. [InlineData (" ~  s  gui.cs   master ↑10", " ~  s  ", 10)] // Unicode
  334. [InlineData ("Ð ÑÐ", "Ð ÑÐ", 5)] // should fit
  335. [InlineData ("Ð ÑÐ", "Ð ÑÐ", 4)] // should fit
  336. [InlineData ("Ð ÑÐ", "Ð Ñ", 3)] // Should not fit
  337. public void ClipAndJustify_Valid_Right (string text, string justifiedText, int maxWidth)
  338. {
  339. var align = TextAlignment.Right;
  340. var textDirection = TextDirection.LeftRight_BottomTop;
  341. var tabWidth = 1;
  342. Assert.Equal (
  343. justifiedText,
  344. TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth)
  345. );
  346. int expectedClippedWidth = Math.Min (justifiedText.GetRuneCount (), maxWidth);
  347. Assert.Equal (
  348. justifiedText,
  349. TextFormatter.ClipAndJustify (text, maxWidth, align, textDirection, tabWidth)
  350. );
  351. Assert.True (justifiedText.GetRuneCount () <= maxWidth);
  352. Assert.True (justifiedText.GetColumns () <= maxWidth);
  353. Assert.Equal (expectedClippedWidth, justifiedText.GetRuneCount ());
  354. Assert.Equal (
  355. expectedClippedWidth,
  356. justifiedText.ToRuneList ().Sum (r => Math.Max (r.GetColumns (), 1))
  357. );
  358. Assert.True (expectedClippedWidth <= maxWidth);
  359. Assert.Equal (
  360. StringExtensions.ToString (justifiedText.ToRunes () [..expectedClippedWidth]),
  361. justifiedText
  362. );
  363. }
  364. [Theory]
  365. [InlineData (14, 1, TextDirection.LeftRight_TopBottom, "Les Misęrables")]
  366. [InlineData (1, 14, TextDirection.TopBottom_LeftRight, "L\ne\ns\n \nM\ni\ns\nę\nr\na\nb\nl\ne\ns")]
  367. [InlineData (
  368. 4,
  369. 4,
  370. TextDirection.TopBottom_LeftRight,
  371. @"
  372. LMre
  373. eias
  374. ssb
  375. ęl "
  376. )]
  377. public void Draw_With_Combining_Runes (int width, int height, TextDirection textDirection, string expected)
  378. {
  379. var driver = new FakeDriver ();
  380. driver.Init ();
  381. var text = "Les Mise\u0328\u0301rables";
  382. var tf = new TextFormatter ();
  383. tf.AutoSize = true;
  384. tf.Direction = textDirection;
  385. tf.Text = text;
  386. Assert.True (tf.WordWrap);
  387. if (textDirection == TextDirection.LeftRight_TopBottom)
  388. {
  389. Assert.Equal (new (width, height), tf.Size);
  390. }
  391. else
  392. {
  393. Assert.Equal (new (1, text.GetColumns ()), tf.Size);
  394. tf.Size = new (width, height);
  395. }
  396. tf.Draw (
  397. new (0, 0, width, height),
  398. new Attribute (ColorName.White, ColorName.Black),
  399. new Attribute (ColorName.Blue, ColorName.Black),
  400. default (Rectangle),
  401. driver
  402. );
  403. TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
  404. driver.End ();
  405. }
  406. [Fact]
  407. [SetupFakeDriver]
  408. public void FillRemaining_True_False ()
  409. {
  410. ((FakeDriver)Application.Driver).SetBufferSize (22, 5);
  411. Attribute [] attrs =
  412. {
  413. Attribute.Default, new Attribute (ColorName.Green, ColorName.BrightMagenta),
  414. new Attribute (ColorName.Blue, ColorName.Cyan)
  415. };
  416. var tf = new TextFormatter { Size = new (14, 3), Text = "Test\nTest long\nTest long long\n", MultiLine = true };
  417. tf.Draw (
  418. new (1, 1, 19, 3),
  419. attrs [1],
  420. attrs [2]);
  421. Assert.False (tf.FillRemaining);
  422. TestHelpers.AssertDriverContentsWithFrameAre (
  423. @"
  424. Test
  425. Test long
  426. Test long long",
  427. _output);
  428. TestHelpers.AssertDriverAttributesAre (
  429. @"
  430. 000000000000000000000
  431. 011110000000000000000
  432. 011111111100000000000
  433. 011111111111111000000
  434. 000000000000000000000",
  435. null,
  436. attrs);
  437. tf.FillRemaining = true;
  438. tf.Draw (
  439. new (1, 1, 19, 3),
  440. attrs [1],
  441. attrs [2]);
  442. TestHelpers.AssertDriverAttributesAre (
  443. @"
  444. 000000000000000000000
  445. 011111111111111111110
  446. 011111111111111111110
  447. 011111111111111111110
  448. 000000000000000000000",
  449. null,
  450. attrs);
  451. }
  452. [Theory]
  453. [InlineData ("_k Before", true, 0, (KeyCode)'K')] // lower case should return uppercase Hotkey
  454. [InlineData ("a_k Second", true, 1, (KeyCode)'K')]
  455. [InlineData ("Last _k", true, 5, (KeyCode)'K')]
  456. [InlineData ("After k_", false, -1, KeyCode.Null)]
  457. [InlineData ("Multiple _k and _R", true, 9, (KeyCode)'K')]
  458. [InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к')] // Lower case Cryllic K (к)
  459. [InlineData ("_k Before", true, 0, (KeyCode)'K', true)] // Turn on FirstUpperCase and verify same results
  460. [InlineData ("a_k Second", true, 1, (KeyCode)'K', true)]
  461. [InlineData ("Last _k", true, 5, (KeyCode)'K', true)]
  462. [InlineData ("After k_", false, -1, KeyCode.Null, true)]
  463. [InlineData ("Multiple _k and _r", true, 9, (KeyCode)'K', true)]
  464. [InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к', true)] // Cryllic K (К)
  465. public void FindHotKey_AlphaLowerCase_Succeeds (
  466. string text,
  467. bool expectedResult,
  468. int expectedHotPos,
  469. KeyCode expectedKey,
  470. bool supportFirstUpperCase = false
  471. )
  472. {
  473. var hotKeySpecifier = (Rune)'_';
  474. bool result = TextFormatter.FindHotKey (
  475. text,
  476. hotKeySpecifier,
  477. out int hotPos,
  478. out Key hotKey,
  479. supportFirstUpperCase
  480. );
  481. if (expectedResult)
  482. {
  483. Assert.True (result);
  484. }
  485. else
  486. {
  487. Assert.False (result);
  488. }
  489. Assert.Equal (expectedResult, result);
  490. Assert.Equal (expectedHotPos, hotPos);
  491. Assert.Equal (expectedKey, hotKey);
  492. }
  493. [Theory]
  494. [InlineData ("_K Before", true, 0, (KeyCode)'K')]
  495. [InlineData ("a_K Second", true, 1, (KeyCode)'K')]
  496. [InlineData ("Last _K", true, 5, (KeyCode)'K')]
  497. [InlineData ("After K_", false, -1, KeyCode.Null)]
  498. [InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K')]
  499. [InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К)
  500. [InlineData ("_K Before", true, 0, (KeyCode)'K', true)] // Turn on FirstUpperCase and verify same results
  501. [InlineData ("a_K Second", true, 1, (KeyCode)'K', true)]
  502. [InlineData ("Last _K", true, 5, (KeyCode)'K', true)]
  503. [InlineData ("After K_", false, -1, KeyCode.Null, true)]
  504. [InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K', true)]
  505. [InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К', true)] // Cryllic K (К)
  506. public void FindHotKey_AlphaUpperCase_Succeeds (
  507. string text,
  508. bool expectedResult,
  509. int expectedHotPos,
  510. KeyCode expectedKey,
  511. bool supportFirstUpperCase = false
  512. )
  513. {
  514. var hotKeySpecifier = (Rune)'_';
  515. bool result = TextFormatter.FindHotKey (
  516. text,
  517. hotKeySpecifier,
  518. out int hotPos,
  519. out Key hotKey,
  520. supportFirstUpperCase
  521. );
  522. if (expectedResult)
  523. {
  524. Assert.True (result);
  525. }
  526. else
  527. {
  528. Assert.False (result);
  529. }
  530. Assert.Equal (expectedResult, result);
  531. Assert.Equal (expectedHotPos, hotPos);
  532. Assert.Equal (expectedKey, hotKey);
  533. }
  534. [Theory]
  535. [InlineData (null)]
  536. [InlineData ("")]
  537. [InlineData ("no hotkey")]
  538. [InlineData ("No hotkey, Upper Case")]
  539. [InlineData ("Non-english: Сохранить")]
  540. public void FindHotKey_Invalid_ReturnsFalse (string text)
  541. {
  542. var hotKeySpecifier = (Rune)'_';
  543. var supportFirstUpperCase = false;
  544. var hotPos = 0;
  545. Key hotKey = KeyCode.Null;
  546. var result = false;
  547. result = TextFormatter.FindHotKey (
  548. text,
  549. hotKeySpecifier,
  550. out hotPos,
  551. out hotKey,
  552. supportFirstUpperCase
  553. );
  554. Assert.False (result);
  555. Assert.Equal (-1, hotPos);
  556. Assert.Equal (KeyCode.Null, hotKey);
  557. }
  558. [Theory]
  559. [InlineData ("\"k before")]
  560. [InlineData ("ak second")]
  561. [InlineData ("last k")]
  562. [InlineData ("multiple k and r")]
  563. [InlineData ("12345")]
  564. [InlineData ("`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?")] // punctuation
  565. [InlineData (" ~  s  gui.cs   master ↑10")] // ~IsLetterOrDigit + Unicode
  566. [InlineData ("non-english: кдать")] // Lower case Cryllic K (к)
  567. public void FindHotKey_Legacy_FirstUpperCase_NotFound_Returns_False (string text)
  568. {
  569. var supportFirstUpperCase = true;
  570. var hotKeySpecifier = (Rune)0;
  571. bool result = TextFormatter.FindHotKey (
  572. text,
  573. hotKeySpecifier,
  574. out int hotPos,
  575. out Key hotKey,
  576. supportFirstUpperCase
  577. );
  578. Assert.False (result);
  579. Assert.Equal (-1, hotPos);
  580. Assert.Equal (KeyCode.Null, hotKey);
  581. }
  582. [Theory]
  583. [InlineData ("K Before", true, 0, (KeyCode)'K')]
  584. [InlineData ("aK Second", true, 1, (KeyCode)'K')]
  585. [InlineData ("last K", true, 5, (KeyCode)'K')]
  586. [InlineData ("multiple K and R", true, 9, (KeyCode)'K')]
  587. [InlineData ("non-english: Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К)
  588. public void FindHotKey_Legacy_FirstUpperCase_Succeeds (
  589. string text,
  590. bool expectedResult,
  591. int expectedHotPos,
  592. KeyCode expectedKey
  593. )
  594. {
  595. var supportFirstUpperCase = true;
  596. var hotKeySpecifier = (Rune)0;
  597. bool result = TextFormatter.FindHotKey (
  598. text,
  599. hotKeySpecifier,
  600. out int hotPos,
  601. out Key hotKey,
  602. supportFirstUpperCase
  603. );
  604. if (expectedResult)
  605. {
  606. Assert.True (result);
  607. }
  608. else
  609. {
  610. Assert.False (result);
  611. }
  612. Assert.Equal (expectedResult, result);
  613. Assert.Equal (expectedHotPos, hotPos);
  614. Assert.Equal (expectedKey, hotKey);
  615. }
  616. [Theory]
  617. [InlineData ("_1 Before", true, 0, (KeyCode)'1')] // Digits
  618. [InlineData ("a_1 Second", true, 1, (KeyCode)'1')]
  619. [InlineData ("Last _1", true, 5, (KeyCode)'1')]
  620. [InlineData ("After 1_", false, -1, KeyCode.Null)]
  621. [InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1')]
  622. [InlineData ("_1 Before", true, 0, (KeyCode)'1', true)] // Turn on FirstUpperCase and verify same results
  623. [InlineData ("a_1 Second", true, 1, (KeyCode)'1', true)]
  624. [InlineData ("Last _1", true, 5, (KeyCode)'1', true)]
  625. [InlineData ("After 1_", false, -1, KeyCode.Null, true)]
  626. [InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1', true)]
  627. public void FindHotKey_Numeric_Succeeds (
  628. string text,
  629. bool expectedResult,
  630. int expectedHotPos,
  631. KeyCode expectedKey,
  632. bool supportFirstUpperCase = false
  633. )
  634. {
  635. var hotKeySpecifier = (Rune)'_';
  636. bool result = TextFormatter.FindHotKey (
  637. text,
  638. hotKeySpecifier,
  639. out int hotPos,
  640. out Key hotKey,
  641. supportFirstUpperCase
  642. );
  643. if (expectedResult)
  644. {
  645. Assert.True (result);
  646. }
  647. else
  648. {
  649. Assert.False (result);
  650. }
  651. Assert.Equal (expectedResult, result);
  652. Assert.Equal (expectedHotPos, hotPos);
  653. Assert.Equal (expectedKey, hotKey);
  654. }
  655. [Theory]
  656. [InlineData ("_\"k before", true, (KeyCode)'"')] // BUGBUG: Not sure why this fails. " is a normal char
  657. [InlineData ("\"_k before", true, KeyCode.K)]
  658. [InlineData ("_`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'`')]
  659. [InlineData ("`_~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'~')]
  660. [InlineData (
  661. "`~!@#$%^&*()-__=+[{]}\\|;:'\",<.>/?",
  662. true,
  663. (KeyCode)'='
  664. )] // BUGBUG: Not sure why this fails. Ignore the first and consider the second
  665. [InlineData ("_ ~  s  gui.cs   master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode
  666. [InlineData (" ~  s  gui.cs  _ master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode
  667. [InlineData ("non-english: _кдать", true, (KeyCode)'к')] // Lower case Cryllic K (к)
  668. public void FindHotKey_Symbols_Returns_Symbol (string text, bool found, KeyCode expected)
  669. {
  670. var hotKeySpecifier = (Rune)'_';
  671. bool result = TextFormatter.FindHotKey (text, hotKeySpecifier, out int _, out Key hotKey);
  672. Assert.Equal (found, result);
  673. Assert.Equal (expected, hotKey);
  674. }
  675. [Fact]
  676. public void Format_Dont_Throw_ArgumentException_With_WordWrap_As_False_And_Keep_End_Spaces_As_True ()
  677. {
  678. Exception exception = Record.Exception (
  679. () =>
  680. TextFormatter.Format (
  681. "Some text",
  682. 4,
  683. TextAlignment.Left,
  684. false,
  685. true
  686. )
  687. );
  688. Assert.Null (exception);
  689. }
  690. [Theory]
  691. [InlineData (
  692. "Hello world, how are you today? Pretty neat!",
  693. 44,
  694. 80,
  695. "Hello world, how are you today? Pretty neat!"
  696. )]
  697. public void Format_Justified_Always_Returns_Text_Width_Equal_To_Passed_Width_Horizontal (
  698. string text,
  699. int runeCount,
  700. int maxWidth,
  701. string justifiedText
  702. )
  703. {
  704. Assert.Equal (runeCount, text.GetRuneCount ());
  705. var fmtText = string.Empty;
  706. for (int i = text.GetRuneCount (); i < maxWidth; i++)
  707. {
  708. fmtText = TextFormatter.Format (text, i, TextAlignment.Justified, false, true) [0];
  709. Assert.Equal (i, fmtText.GetRuneCount ());
  710. char c = fmtText [^1];
  711. Assert.True (text.EndsWith (c));
  712. }
  713. Assert.Equal (justifiedText, fmtText);
  714. }
  715. [Theory]
  716. [InlineData (
  717. "Hello world, how are you today? Pretty neat!",
  718. 44,
  719. 80,
  720. "Hello world, how are you today? Pretty neat!"
  721. )]
  722. public void Format_Justified_Always_Returns_Text_Width_Equal_To_Passed_Width_Vertical (
  723. string text,
  724. int runeCount,
  725. int maxWidth,
  726. string justifiedText
  727. )
  728. {
  729. Assert.Equal (runeCount, text.GetRuneCount ());
  730. var fmtText = string.Empty;
  731. for (int i = text.GetRuneCount (); i < maxWidth; i++)
  732. {
  733. fmtText = TextFormatter.Format (
  734. text,
  735. i,
  736. TextAlignment.Justified,
  737. false,
  738. true,
  739. 0,
  740. TextDirection.TopBottom_LeftRight
  741. ) [0];
  742. Assert.Equal (i, fmtText.GetRuneCount ());
  743. char c = fmtText [^1];
  744. Assert.True (text.EndsWith (c));
  745. }
  746. Assert.Equal (justifiedText, fmtText);
  747. }
  748. [Theory]
  749. [InlineData ("Truncate", 3, "Tru")]
  750. [InlineData ("デモエムポンズ", 3, "デ")]
  751. public void Format_Truncate_Simple_And_Wide_Runes (string text, int width, string expected)
  752. {
  753. List<string> list = TextFormatter.Format (text, width, false, false);
  754. Assert.Equal (expected, list [^1]);
  755. }
  756. [Theory]
  757. [MemberData (nameof (FormatEnvironmentNewLine))]
  758. public void Format_With_PreserveTrailingSpaces_And_Without_PreserveTrailingSpaces (
  759. string text,
  760. int width,
  761. IEnumerable<string> expected
  762. )
  763. {
  764. var preserveTrailingSpaces = false;
  765. List<string> formated = TextFormatter.Format (text, width, false, true, preserveTrailingSpaces);
  766. Assert.Equal (expected, formated);
  767. preserveTrailingSpaces = true;
  768. formated = TextFormatter.Format (text, width, false, true, preserveTrailingSpaces);
  769. Assert.Equal (expected, formated);
  770. }
  771. [Theory]
  772. [InlineData (
  773. " A sentence has words. \n This is the second Line - 2. ",
  774. 4,
  775. -50,
  776. TextAlignment.Left,
  777. true,
  778. false,
  779. new [] { " A", "sent", "ence", "has", "word", "s. ", " Thi", "s is", "the", "seco", "nd", "Line", "- 2." },
  780. " Asentencehaswords. This isthesecondLine- 2."
  781. )]
  782. [InlineData (
  783. " A sentence has words. \n This is the second Line - 2. ",
  784. 4,
  785. -50,
  786. TextAlignment.Left,
  787. true,
  788. true,
  789. new []
  790. {
  791. " A ",
  792. "sent",
  793. "ence",
  794. " ",
  795. "has ",
  796. "word",
  797. "s. ",
  798. " ",
  799. "This",
  800. " is ",
  801. "the ",
  802. "seco",
  803. "nd ",
  804. "Line",
  805. " - ",
  806. "2. "
  807. },
  808. " A sentence has words. This is the second Line - 2. "
  809. )]
  810. public void Format_WordWrap_PreserveTrailingSpaces (
  811. string text,
  812. int maxWidth,
  813. int widthOffset,
  814. TextAlignment textAlignment,
  815. bool wrap,
  816. bool preserveTrailingSpaces,
  817. IEnumerable<string> resultLines,
  818. string expectedWrappedText
  819. )
  820. {
  821. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  822. List<string> list = TextFormatter.Format (text, maxWidth, textAlignment, wrap, preserveTrailingSpaces);
  823. Assert.Equal (list.Count, resultLines.Count ());
  824. Assert.Equal (resultLines, list);
  825. var wrappedText = string.Empty;
  826. foreach (string txt in list)
  827. {
  828. wrappedText += txt;
  829. }
  830. Assert.Equal (expectedWrappedText, wrappedText);
  831. }
  832. [Theory]
  833. [InlineData ("Hello World", 11)]
  834. [InlineData ("こんにちは世界", 14)]
  835. public void GetColumns_Simple_And_Wide_Runes (string text, int width) { Assert.Equal (width, text.GetColumns ()); }
  836. [Theory]
  837. [InlineData ("Hello World", 6, 6)]
  838. [InlineData ("こんにちは 世界", 6, 3)]
  839. [MemberData (nameof (CMGlyphs))]
  840. public void GetLengthThatFits_List_Simple_And_Wide_Runes (string text, int columns, int expectedLength)
  841. {
  842. List<Rune> runes = text.ToRuneList ();
  843. Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns));
  844. }
  845. [Theory]
  846. [InlineData ("test", 3, 3)]
  847. [InlineData ("test", 4, 4)]
  848. [InlineData ("test", 10, 4)]
  849. public void GetLengthThatFits_Runelist (string text, int columns, int expectedLength)
  850. {
  851. List<Rune> runes = text.ToRuneList ();
  852. Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns));
  853. }
  854. [Theory]
  855. [InlineData ("Hello World", 6, 6)]
  856. [InlineData ("こんにちは 世界", 6, 3)]
  857. public void GetLengthThatFits_Simple_And_Wide_Runes (string text, int columns, int expectedLength)
  858. {
  859. Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns));
  860. }
  861. [Theory]
  862. [InlineData ("test", 3, 3)]
  863. [InlineData ("test", 4, 4)]
  864. [InlineData ("test", 10, 4)]
  865. [InlineData ("test", 1, 1)]
  866. [InlineData ("test", 0, 0)]
  867. [InlineData ("test", -1, 0)]
  868. [InlineData (null, -1, 0)]
  869. [InlineData ("", -1, 0)]
  870. public void GetLengthThatFits_String (string text, int columns, int expectedLength)
  871. {
  872. Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns));
  873. }
  874. [Fact]
  875. public void GetLengthThatFits_With_Combining_Runes ()
  876. {
  877. var text = "Les Mise\u0328\u0301rables";
  878. Assert.Equal (16, TextFormatter.GetLengthThatFits (text, 14));
  879. }
  880. [Fact]
  881. public void GetMaxColsForWidth_With_Combining_Runes ()
  882. {
  883. List<string> text = new () { "Les Mis", "e\u0328\u0301", "rables" };
  884. Assert.Equal (1, TextFormatter.GetMaxColsForWidth (text, 1));
  885. }
  886. [Theory]
  887. [InlineData (new [] { "0123456789" }, 1)]
  888. [InlineData (new [] { "Hello World" }, 1)]
  889. [InlineData (new [] { "Hello", "World" }, 2)]
  890. [InlineData (new [] { "こんにちは", "世界" }, 4)]
  891. public void GetColumnsRequiredForVerticalText_List_GetsWidth (IEnumerable<string> text, int expectedWidth)
  892. {
  893. Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ()));
  894. }
  895. [Theory]
  896. [InlineData (new [] { "Hello World" }, 1, 0, 1, 1)]
  897. [InlineData (new [] { "Hello", "World" }, 2, 1, 1, 1)]
  898. [InlineData (new [] { "こんにちは", "世界" }, 4, 1, 1, 2)]
  899. public void GetColumnsRequiredForVerticalText_List_Simple_And_Wide_Runes (
  900. IEnumerable<string> text,
  901. int expectedWidth,
  902. int index,
  903. int length,
  904. int expectedIndexWidth
  905. )
  906. {
  907. Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ()));
  908. Assert.Equal (expectedIndexWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList (), index, length));
  909. }
  910. [Fact]
  911. public void GetColumnsRequiredForVerticalText_List_With_Combining_Runes ()
  912. {
  913. List<string> text = new () { "Les Mis", "e\u0328\u0301", "rables" };
  914. Assert.Equal (1, TextFormatter.GetColumnsRequiredForVerticalText (text, 1, 1));
  915. }
  916. [Fact]
  917. public void GetColumnsRequiredForVerticalText_With_Combining_Runes ()
  918. {
  919. var text = "Les Mise\u0328\u0301rables";
  920. Assert.Equal (1, TextFormatter.GetWidestLineLength (text, 1, 1));
  921. }
  922. [Fact]
  923. public void Internal_Tests ()
  924. {
  925. var tf = new TextFormatter ();
  926. Assert.Equal (KeyCode.Null, tf.HotKey);
  927. tf.HotKey = KeyCode.CtrlMask | KeyCode.Q;
  928. Assert.Equal (KeyCode.CtrlMask | KeyCode.Q, tf.HotKey);
  929. }
  930. [Theory]
  931. [InlineData ("")]
  932. [InlineData (null)]
  933. [InlineData ("test")]
  934. public void Justify_Invalid (string text)
  935. {
  936. Assert.Equal (text, TextFormatter.Justify (text, 0));
  937. Assert.Equal (text, TextFormatter.Justify (text, 0));
  938. Assert.Throws<ArgumentOutOfRangeException> (() => TextFormatter.Justify (text, -1));
  939. }
  940. [Theory]
  941. // Even # of spaces
  942. // 0123456789
  943. [InlineData ("012 456 89", "012 456 89", 10, 0, "+", true)]
  944. [InlineData ("012 456 89", "012++456+89", 11, 1)]
  945. [InlineData ("012 456 89", "012 456 89", 12, 2, "++", true)]
  946. [InlineData ("012 456 89", "012+++456++89", 13, 3)]
  947. [InlineData ("012 456 89", "012 456 89", 14, 4, "+++", true)]
  948. [InlineData ("012 456 89", "012++++456+++89", 15, 5)]
  949. [InlineData ("012 456 89", "012 456 89", 16, 6, "++++", true)]
  950. [InlineData ("012 456 89", "012 456 89", 30, 20, "+++++++++++", true)]
  951. [InlineData ("012 456 89", "012+++++++++++++456++++++++++++89", 33, 23)]
  952. // Odd # of spaces
  953. // 01234567890123
  954. [InlineData ("012 456 89 end", "012 456 89 end", 14, 0, "+", true)]
  955. [InlineData ("012 456 89 end", "012++456+89+end", 15, 1)]
  956. [InlineData ("012 456 89 end", "012++456++89+end", 16, 2)]
  957. [InlineData ("012 456 89 end", "012 456 89 end", 17, 3, "++", true)]
  958. [InlineData ("012 456 89 end", "012+++456++89++end", 18, 4)]
  959. [InlineData ("012 456 89 end", "012+++456+++89++end", 19, 5)]
  960. [InlineData ("012 456 89 end", "012 456 89 end", 20, 6, "+++", true)]
  961. [InlineData ("012 456 89 end", "012++++++++456++++++++89+++++++end", 34, 20)]
  962. [InlineData ("012 456 89 end", "012+++++++++456+++++++++89++++++++end", 37, 23)]
  963. // Unicode
  964. // Even # of chars
  965. // 0123456789
  966. [InlineData ("пÑРвРÑ", "пÑРвРÑ", 10, 0, "+", true)]
  967. [InlineData ("пÑРвРÑ", "пÑÐ++вÐ+Ñ", 11, 1)]
  968. [InlineData ("пÑРвРÑ", "пÑРвРÑ", 12, 2, "++", true)]
  969. [InlineData ("пÑРвРÑ", "пÑÐ+++вÐ++Ñ", 13, 3)]
  970. [InlineData ("пÑРвРÑ", "пÑРвРÑ", 14, 4, "+++", true)]
  971. [InlineData ("пÑРвРÑ", "пÑÐ++++вÐ+++Ñ", 15, 5)]
  972. [InlineData ("пÑРвРÑ", "пÑРвРÑ", 16, 6, "++++", true)]
  973. [InlineData ("пÑРвРÑ", "пÑРвРÑ", 30, 20, "+++++++++++", true)]
  974. [InlineData ("пÑРвРÑ", "пÑÐ+++++++++++++вÐ++++++++++++Ñ", 33, 23)]
  975. // Unicode
  976. // Odd # of chars
  977. // 0123456789
  978. [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 10, 0, "+", true)]
  979. [InlineData ("Ð ÑРвРÑ", "Ð++ÑÐ+вÐ+Ñ", 11, 1)]
  980. [InlineData ("Ð ÑРвРÑ", "Ð++ÑÐ++вÐ+Ñ", 12, 2)]
  981. [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 13, 3, "++", true)]
  982. [InlineData ("Ð ÑРвРÑ", "Ð+++ÑÐ++вÐ++Ñ", 14, 4)]
  983. [InlineData ("Ð ÑРвРÑ", "Ð+++ÑÐ+++вÐ++Ñ", 15, 5)]
  984. [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 16, 6, "+++", true)]
  985. [InlineData ("Ð ÑРвРÑ", "Ð++++++++ÑÐ++++++++вÐ+++++++Ñ", 30, 20)]
  986. [InlineData ("Ð ÑРвРÑ", "Ð+++++++++ÑÐ+++++++++вÐ++++++++Ñ", 33, 23)]
  987. public void Justify_Sentence (
  988. string text,
  989. string justifiedText,
  990. int forceToWidth,
  991. int widthOffset,
  992. string replaceWith = null,
  993. bool replace = false
  994. )
  995. {
  996. var fillChar = '+';
  997. Assert.Equal (forceToWidth, text.GetRuneCount () + widthOffset);
  998. if (replace)
  999. {
  1000. justifiedText = text.Replace (" ", replaceWith);
  1001. }
  1002. Assert.Equal (justifiedText, TextFormatter.Justify (text, forceToWidth, fillChar));
  1003. Assert.True (Math.Abs (forceToWidth - justifiedText.GetRuneCount ()) < text.Count (s => s == ' '));
  1004. Assert.True (Math.Abs (forceToWidth - justifiedText.GetColumns ()) < text.Count (s => s == ' '));
  1005. }
  1006. [Theory]
  1007. [InlineData ("word")] // Even # of chars
  1008. [InlineData ("word.")] // Odd # of chars
  1009. [InlineData ("пÑивеÑ")] // Unicode (even #)
  1010. [InlineData ("пÑивеÑ.")] // Unicode (odd # of chars)
  1011. public void Justify_SingleWord (string text)
  1012. {
  1013. string justifiedText = text;
  1014. var fillChar = '+';
  1015. int width = text.GetRuneCount ();
  1016. Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
  1017. width = text.GetRuneCount () + 1;
  1018. Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
  1019. width = text.GetRuneCount () + 2;
  1020. Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
  1021. width = text.GetRuneCount () + 10;
  1022. Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
  1023. width = text.GetRuneCount () + 11;
  1024. Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar));
  1025. }
  1026. [Theory]
  1027. [InlineData ("Single Line 界", 14)]
  1028. [InlineData ("First Line 界\nSecond Line 界\nThird Line 界\n", 14)]
  1029. public void MaxWidthLine_With_And_Without_Newlines (string text, int expected) { Assert.Equal (expected, TextFormatter.GetWidestLineLength (text)); }
  1030. [Theory]
  1031. [InlineData (
  1032. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1033. 0,
  1034. 0,
  1035. false,
  1036. new [] { "" }
  1037. )]
  1038. [InlineData (
  1039. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1040. 0,
  1041. 1,
  1042. false,
  1043. new [] { "" }
  1044. )]
  1045. [InlineData (
  1046. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1047. 1,
  1048. 0,
  1049. false,
  1050. new [] { "" }
  1051. )]
  1052. [InlineData (
  1053. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1054. 0,
  1055. 0,
  1056. true,
  1057. new [] { "" }
  1058. )]
  1059. [InlineData (
  1060. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1061. 0,
  1062. 1,
  1063. true,
  1064. new [] { "" }
  1065. )]
  1066. [InlineData (
  1067. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1068. 1,
  1069. 0,
  1070. true,
  1071. new [] { "" }
  1072. )]
  1073. [InlineData (
  1074. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1075. 6,
  1076. 5,
  1077. false,
  1078. new [] { "First " }
  1079. )]
  1080. [InlineData ("1\n2\n3\n4\n5\n6", 6, 5, false, new [] { "1 2 3 " })]
  1081. [InlineData (
  1082. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1083. 6,
  1084. 5,
  1085. true,
  1086. new [] { "First ", "Second", "Third ", "Forty ", "Fiftee" }
  1087. )]
  1088. [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, false, new [] { "第一" })]
  1089. [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, true, new [] { "第一", "第二", "第三", "四十", "第十" })]
  1090. public void MultiLine_WordWrap_False_Horizontal_Direction (
  1091. string text,
  1092. int maxWidth,
  1093. int maxHeight,
  1094. bool multiLine,
  1095. IEnumerable<string> resultLines
  1096. )
  1097. {
  1098. var tf = new TextFormatter
  1099. {
  1100. Text = text, Size = new (maxWidth, maxHeight), WordWrap = false, MultiLine = multiLine
  1101. };
  1102. Assert.False (tf.AutoSize);
  1103. Assert.False (tf.WordWrap);
  1104. Assert.True (tf.MultiLine == multiLine);
  1105. Assert.Equal (TextDirection.LeftRight_TopBottom, tf.Direction);
  1106. List<string> splitLines = tf.GetLines ();
  1107. Assert.Equal (splitLines.Count, resultLines.Count ());
  1108. Assert.Equal (splitLines, resultLines);
  1109. }
  1110. [Theory]
  1111. [InlineData (
  1112. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1113. 0,
  1114. 0,
  1115. false,
  1116. new [] { "" }
  1117. )]
  1118. [InlineData (
  1119. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1120. 0,
  1121. 1,
  1122. false,
  1123. new [] { "" }
  1124. )]
  1125. [InlineData (
  1126. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1127. 1,
  1128. 0,
  1129. false,
  1130. new [] { "" }
  1131. )]
  1132. [InlineData (
  1133. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1134. 0,
  1135. 0,
  1136. true,
  1137. new [] { "" }
  1138. )]
  1139. [InlineData (
  1140. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1141. 0,
  1142. 1,
  1143. true,
  1144. new [] { "" }
  1145. )]
  1146. [InlineData (
  1147. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1148. 1,
  1149. 0,
  1150. true,
  1151. new [] { "" }
  1152. )]
  1153. [InlineData (
  1154. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1155. 6,
  1156. 5,
  1157. false,
  1158. new [] { "First" }
  1159. )]
  1160. [InlineData ("1\n2\n3\n4\n5\n6", 6, 5, false, new [] { "1 2 3" })]
  1161. [InlineData (
  1162. "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line",
  1163. 6,
  1164. 5,
  1165. true,
  1166. new [] { "First", "Secon", "Third", "Forty", "Fifte", "Seven" }
  1167. )]
  1168. [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, false, new [] { "第一行 第" })]
  1169. [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, true, new [] { "第一行", "第二行" })]
  1170. public void MultiLine_WordWrap_False_Vertical_Direction (
  1171. string text,
  1172. int maxWidth,
  1173. int maxHeight,
  1174. bool multiLine,
  1175. IEnumerable<string> resultLines
  1176. )
  1177. {
  1178. var tf = new TextFormatter
  1179. {
  1180. Text = text,
  1181. Size = new (maxWidth, maxHeight),
  1182. WordWrap = false,
  1183. MultiLine = multiLine,
  1184. Direction = TextDirection.TopBottom_LeftRight
  1185. };
  1186. Assert.False (tf.AutoSize);
  1187. Assert.False (tf.WordWrap);
  1188. Assert.True (tf.MultiLine == multiLine);
  1189. Assert.Equal (TextDirection.TopBottom_LeftRight, tf.Direction);
  1190. List<string> splitLines = tf.GetLines ();
  1191. Assert.Equal (splitLines.Count, resultLines.Count ());
  1192. Assert.Equal (splitLines, resultLines);
  1193. }
  1194. [Fact]
  1195. public void NeedsFormat_Sets ()
  1196. {
  1197. var testText = "test";
  1198. var testBounds = new Rectangle (0, 0, 100, 1);
  1199. var tf = new TextFormatter ();
  1200. tf.Text = "test";
  1201. Assert.True (tf.NeedsFormat); // get_Lines causes a Format
  1202. Assert.NotEmpty (tf.GetLines ());
  1203. Assert.False (tf.NeedsFormat); // get_Lines causes a Format
  1204. Assert.Equal (testText, tf.Text);
  1205. tf.Draw (testBounds, new Attribute (), new Attribute ());
  1206. Assert.False (tf.NeedsFormat);
  1207. tf.Size = new (1, 1);
  1208. Assert.True (tf.NeedsFormat);
  1209. Assert.NotEmpty (tf.GetLines ());
  1210. Assert.False (tf.NeedsFormat); // get_Lines causes a Format
  1211. tf.Alignment = TextAlignment.Centered;
  1212. Assert.True (tf.NeedsFormat);
  1213. Assert.NotEmpty (tf.GetLines ());
  1214. Assert.False (tf.NeedsFormat); // get_Lines causes a Format
  1215. }
  1216. [Theory]
  1217. [InlineData ("", -1, TextAlignment.Left, false, 0)]
  1218. [InlineData (null, 0, TextAlignment.Left, false, 1)]
  1219. [InlineData (null, 0, TextAlignment.Left, true, 1)]
  1220. [InlineData ("", 0, TextAlignment.Left, false, 1)]
  1221. [InlineData ("", 0, TextAlignment.Left, true, 1)]
  1222. public void Reformat_Invalid (string text, int maxWidth, TextAlignment textAlignment, bool wrap, int linesCount)
  1223. {
  1224. if (maxWidth < 0)
  1225. {
  1226. Assert.Throws<ArgumentOutOfRangeException> (
  1227. () =>
  1228. TextFormatter.Format (text, maxWidth, textAlignment, wrap)
  1229. );
  1230. }
  1231. else
  1232. {
  1233. List<string> list = TextFormatter.Format (text, maxWidth, textAlignment, wrap);
  1234. Assert.NotEmpty (list);
  1235. Assert.True (list.Count == linesCount);
  1236. Assert.Equal (string.Empty, list [0]);
  1237. }
  1238. }
  1239. [Theory]
  1240. [InlineData ("A sentence has words.\nLine 2.", 0, -29, TextAlignment.Left, false, 1, true)]
  1241. [InlineData ("A sentence has words.\nLine 2.", 1, -28, TextAlignment.Left, false, 1, false)]
  1242. [InlineData ("A sentence has words.\nLine 2.", 5, -24, TextAlignment.Left, false, 1, false)]
  1243. [InlineData ("A sentence has words.\nLine 2.", 28, -1, TextAlignment.Left, false, 1, false)]
  1244. // no clip
  1245. [InlineData ("A sentence has words.\nLine 2.", 29, 0, TextAlignment.Left, false, 1, false)]
  1246. [InlineData ("A sentence has words.\nLine 2.", 30, 1, TextAlignment.Left, false, 1, false)]
  1247. [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, TextAlignment.Left, false, 1, true)]
  1248. [InlineData ("A sentence has words.\r\nLine 2.", 1, -29, TextAlignment.Left, false, 1, false)]
  1249. [InlineData ("A sentence has words.\r\nLine 2.", 5, -25, TextAlignment.Left, false, 1, false)]
  1250. [InlineData ("A sentence has words.\r\nLine 2.", 29, -1, TextAlignment.Left, false, 1, false, 1)]
  1251. [InlineData ("A sentence has words.\r\nLine 2.", 30, 0, TextAlignment.Left, false, 1, false)]
  1252. [InlineData ("A sentence has words.\r\nLine 2.", 31, 1, TextAlignment.Left, false, 1, false)]
  1253. public void Reformat_NoWordrap_NewLines_MultiLine_False (
  1254. string text,
  1255. int maxWidth,
  1256. int widthOffset,
  1257. TextAlignment textAlignment,
  1258. bool wrap,
  1259. int linesCount,
  1260. bool stringEmpty,
  1261. int clipWidthOffset = 0
  1262. )
  1263. {
  1264. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  1265. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth) + clipWidthOffset;
  1266. List<string> list = TextFormatter.Format (text, maxWidth, textAlignment, wrap);
  1267. Assert.NotEmpty (list);
  1268. Assert.True (list.Count == linesCount);
  1269. if (stringEmpty)
  1270. {
  1271. Assert.Equal (string.Empty, list [0]);
  1272. }
  1273. else
  1274. {
  1275. Assert.NotEqual (string.Empty, list [0]);
  1276. }
  1277. if (text.Contains ("\r\n") && maxWidth > 0)
  1278. {
  1279. Assert.Equal (
  1280. StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth])
  1281. .Replace ("\r\n", " "),
  1282. list [0]
  1283. );
  1284. }
  1285. else if (text.Contains ('\n') && maxWidth > 0)
  1286. {
  1287. Assert.Equal (
  1288. StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth])
  1289. .Replace ("\n", " "),
  1290. list [0]
  1291. );
  1292. }
  1293. else
  1294. {
  1295. Assert.Equal (StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), list [0]);
  1296. }
  1297. }
  1298. [Theory]
  1299. [InlineData ("A sentence has words.\nLine 2.", 0, -29, TextAlignment.Left, false, 1, true, new [] { "" })]
  1300. [InlineData (
  1301. "A sentence has words.\nLine 2.",
  1302. 1,
  1303. -28,
  1304. TextAlignment.Left,
  1305. false,
  1306. 2,
  1307. false,
  1308. new [] { "A", "L" }
  1309. )]
  1310. [InlineData (
  1311. "A sentence has words.\nLine 2.",
  1312. 5,
  1313. -24,
  1314. TextAlignment.Left,
  1315. false,
  1316. 2,
  1317. false,
  1318. new [] { "A sen", "Line " }
  1319. )]
  1320. [InlineData (
  1321. "A sentence has words.\nLine 2.",
  1322. 28,
  1323. -1,
  1324. TextAlignment.Left,
  1325. false,
  1326. 2,
  1327. false,
  1328. new [] { "A sentence has words.", "Line 2." }
  1329. )]
  1330. //// no clip
  1331. [InlineData (
  1332. "A sentence has words.\nLine 2.",
  1333. 29,
  1334. 0,
  1335. TextAlignment.Left,
  1336. false,
  1337. 2,
  1338. false,
  1339. new [] { "A sentence has words.", "Line 2." }
  1340. )]
  1341. [InlineData (
  1342. "A sentence has words.\nLine 2.",
  1343. 30,
  1344. 1,
  1345. TextAlignment.Left,
  1346. false,
  1347. 2,
  1348. false,
  1349. new [] { "A sentence has words.", "Line 2." }
  1350. )]
  1351. [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, TextAlignment.Left, false, 1, true, new [] { "" })]
  1352. [InlineData (
  1353. "A sentence has words.\r\nLine 2.",
  1354. 1,
  1355. -29,
  1356. TextAlignment.Left,
  1357. false,
  1358. 2,
  1359. false,
  1360. new [] { "A", "L" }
  1361. )]
  1362. [InlineData (
  1363. "A sentence has words.\r\nLine 2.",
  1364. 5,
  1365. -25,
  1366. TextAlignment.Left,
  1367. false,
  1368. 2,
  1369. false,
  1370. new [] { "A sen", "Line " }
  1371. )]
  1372. [InlineData (
  1373. "A sentence has words.\r\nLine 2.",
  1374. 29,
  1375. -1,
  1376. TextAlignment.Left,
  1377. false,
  1378. 2,
  1379. false,
  1380. new [] { "A sentence has words.", "Line 2." }
  1381. )]
  1382. [InlineData (
  1383. "A sentence has words.\r\nLine 2.",
  1384. 30,
  1385. 0,
  1386. TextAlignment.Left,
  1387. false,
  1388. 2,
  1389. false,
  1390. new [] { "A sentence has words.", "Line 2." }
  1391. )]
  1392. [InlineData (
  1393. "A sentence has words.\r\nLine 2.",
  1394. 31,
  1395. 1,
  1396. TextAlignment.Left,
  1397. false,
  1398. 2,
  1399. false,
  1400. new [] { "A sentence has words.", "Line 2." }
  1401. )]
  1402. public void Reformat_NoWordrap_NewLines_MultiLine_True (
  1403. string text,
  1404. int maxWidth,
  1405. int widthOffset,
  1406. TextAlignment textAlignment,
  1407. bool wrap,
  1408. int linesCount,
  1409. bool stringEmpty,
  1410. IEnumerable<string> resultLines
  1411. )
  1412. {
  1413. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  1414. List<string> list = TextFormatter.Format (
  1415. text,
  1416. maxWidth,
  1417. textAlignment,
  1418. wrap,
  1419. false,
  1420. 0,
  1421. TextDirection.LeftRight_TopBottom,
  1422. true
  1423. );
  1424. Assert.NotEmpty (list);
  1425. Assert.True (list.Count == linesCount);
  1426. if (stringEmpty)
  1427. {
  1428. Assert.Equal (string.Empty, list [0]);
  1429. }
  1430. else
  1431. {
  1432. Assert.NotEqual (string.Empty, list [0]);
  1433. }
  1434. Assert.Equal (list, resultLines);
  1435. }
  1436. [Theory]
  1437. [InlineData ("A sentence has words.\nLine 2.", 0, -29, TextAlignment.Left, false, 1, true, new [] { "" })]
  1438. [InlineData (
  1439. "A sentence has words.\nLine 2.",
  1440. 1,
  1441. -28,
  1442. TextAlignment.Left,
  1443. false,
  1444. 2,
  1445. false,
  1446. new [] { "A", "L" }
  1447. )]
  1448. [InlineData (
  1449. "A sentence has words.\nLine 2.",
  1450. 5,
  1451. -24,
  1452. TextAlignment.Left,
  1453. false,
  1454. 2,
  1455. false,
  1456. new [] { "A sen", "Line " }
  1457. )]
  1458. [InlineData (
  1459. "A sentence has words.\nLine 2.",
  1460. 28,
  1461. -1,
  1462. TextAlignment.Left,
  1463. false,
  1464. 2,
  1465. false,
  1466. new [] { "A sentence has words.", "Line 2." }
  1467. )]
  1468. //// no clip
  1469. [InlineData (
  1470. "A sentence has words.\nLine 2.",
  1471. 29,
  1472. 0,
  1473. TextAlignment.Left,
  1474. false,
  1475. 2,
  1476. false,
  1477. new [] { "A sentence has words.", "Line 2." }
  1478. )]
  1479. [InlineData (
  1480. "A sentence has words.\nLine 2.",
  1481. 30,
  1482. 1,
  1483. TextAlignment.Left,
  1484. false,
  1485. 2,
  1486. false,
  1487. new [] { "A sentence has words.", "Line 2." }
  1488. )]
  1489. [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, TextAlignment.Left, false, 1, true, new [] { "" })]
  1490. [InlineData (
  1491. "A sentence has words.\r\nLine 2.",
  1492. 1,
  1493. -29,
  1494. TextAlignment.Left,
  1495. false,
  1496. 2,
  1497. false,
  1498. new [] { "A", "L" }
  1499. )]
  1500. [InlineData (
  1501. "A sentence has words.\r\nLine 2.",
  1502. 5,
  1503. -25,
  1504. TextAlignment.Left,
  1505. false,
  1506. 2,
  1507. false,
  1508. new [] { "A sen", "Line " }
  1509. )]
  1510. [InlineData (
  1511. "A sentence has words.\r\nLine 2.",
  1512. 29,
  1513. -1,
  1514. TextAlignment.Left,
  1515. false,
  1516. 2,
  1517. false,
  1518. new [] { "A sentence has words.", "Line 2." }
  1519. )]
  1520. [InlineData (
  1521. "A sentence has words.\r\nLine 2.",
  1522. 30,
  1523. 0,
  1524. TextAlignment.Left,
  1525. false,
  1526. 2,
  1527. false,
  1528. new [] { "A sentence has words.", "Line 2." }
  1529. )]
  1530. [InlineData (
  1531. "A sentence has words.\r\nLine 2.",
  1532. 31,
  1533. 1,
  1534. TextAlignment.Left,
  1535. false,
  1536. 2,
  1537. false,
  1538. new [] { "A sentence has words.", "Line 2." }
  1539. )]
  1540. public void Reformat_NoWordrap_NewLines_MultiLine_True_Vertical (
  1541. string text,
  1542. int maxWidth,
  1543. int widthOffset,
  1544. TextAlignment textAlignment,
  1545. bool wrap,
  1546. int linesCount,
  1547. bool stringEmpty,
  1548. IEnumerable<string> resultLines
  1549. )
  1550. {
  1551. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  1552. List<string> list = TextFormatter.Format (
  1553. text,
  1554. maxWidth,
  1555. textAlignment,
  1556. wrap,
  1557. false,
  1558. 0,
  1559. TextDirection.TopBottom_LeftRight,
  1560. true
  1561. );
  1562. Assert.NotEmpty (list);
  1563. Assert.True (list.Count == linesCount);
  1564. if (stringEmpty)
  1565. {
  1566. Assert.Equal (string.Empty, list [0]);
  1567. }
  1568. else
  1569. {
  1570. Assert.NotEqual (string.Empty, list [0]);
  1571. }
  1572. Assert.Equal (list, resultLines);
  1573. }
  1574. [Theory]
  1575. [InlineData ("", 0, 0, TextAlignment.Left, false, 1, true)]
  1576. [InlineData ("", 1, 1, TextAlignment.Left, false, 1, true)]
  1577. [InlineData ("A sentence has words.", 0, -21, TextAlignment.Left, false, 1, true)]
  1578. [InlineData ("A sentence has words.", 1, -20, TextAlignment.Left, false, 1, false)]
  1579. [InlineData ("A sentence has words.", 5, -16, TextAlignment.Left, false, 1, false)]
  1580. [InlineData ("A sentence has words.", 20, -1, TextAlignment.Left, false, 1, false)]
  1581. // no clip
  1582. [InlineData ("A sentence has words.", 21, 0, TextAlignment.Left, false, 1, false)]
  1583. [InlineData ("A sentence has words.", 22, 1, TextAlignment.Left, false, 1, false)]
  1584. public void Reformat_NoWordrap_SingleLine (
  1585. string text,
  1586. int maxWidth,
  1587. int widthOffset,
  1588. TextAlignment textAlignment,
  1589. bool wrap,
  1590. int linesCount,
  1591. bool stringEmpty
  1592. )
  1593. {
  1594. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  1595. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  1596. List<string> list = TextFormatter.Format (text, maxWidth, textAlignment, wrap);
  1597. Assert.NotEmpty (list);
  1598. Assert.True (list.Count == linesCount);
  1599. if (stringEmpty)
  1600. {
  1601. Assert.Equal (string.Empty, list [0]);
  1602. }
  1603. else
  1604. {
  1605. Assert.NotEqual (string.Empty, list [0]);
  1606. }
  1607. Assert.Equal (StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), list [0]);
  1608. }
  1609. [Theory]
  1610. // Unicode
  1611. [InlineData (
  1612. "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464",
  1613. 8,
  1614. -1,
  1615. TextAlignment.Left,
  1616. true,
  1617. false,
  1618. new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" }
  1619. )]
  1620. // no clip
  1621. [InlineData (
  1622. "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464",
  1623. 9,
  1624. 0,
  1625. TextAlignment.Left,
  1626. true,
  1627. false,
  1628. new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" }
  1629. )]
  1630. [InlineData (
  1631. "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464",
  1632. 10,
  1633. 1,
  1634. TextAlignment.Left,
  1635. true,
  1636. false,
  1637. new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" }
  1638. )]
  1639. public void Reformat_Unicode_Wrap_Spaces_NewLines (
  1640. string text,
  1641. int maxWidth,
  1642. int widthOffset,
  1643. TextAlignment textAlignment,
  1644. bool wrap,
  1645. bool preserveTrailingSpaces,
  1646. IEnumerable<string> resultLines
  1647. )
  1648. {
  1649. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  1650. List<string> list = TextFormatter.Format (text, maxWidth, textAlignment, wrap, preserveTrailingSpaces);
  1651. Assert.Equal (list.Count, resultLines.Count ());
  1652. Assert.Equal (resultLines, list);
  1653. }
  1654. [Theory]
  1655. // Unicode
  1656. // Even # of chars
  1657. // 0123456789
  1658. [InlineData ("\u2660пÑРвРÑ", 10, -1, TextAlignment.Left, true, false, new [] { "\u2660пÑРвÐ", "Ñ" })]
  1659. // no clip
  1660. [InlineData ("\u2660пÑРвРÑ", 11, 0, TextAlignment.Left, true, false, new [] { "\u2660пÑРвРÑ" })]
  1661. [InlineData ("\u2660пÑРвРÑ", 12, 1, TextAlignment.Left, true, false, new [] { "\u2660пÑРвРÑ" })]
  1662. // Unicode
  1663. // Odd # of chars
  1664. // 0123456789
  1665. [InlineData ("\u2660 ÑРвРÑ", 9, -1, TextAlignment.Left, true, false, new [] { "\u2660 ÑРвÐ", "Ñ" })]
  1666. // no clip
  1667. [InlineData ("\u2660 ÑРвРÑ", 10, 0, TextAlignment.Left, true, false, new [] { "\u2660 ÑРвРÑ" })]
  1668. [InlineData ("\u2660 ÑРвРÑ", 11, 1, TextAlignment.Left, true, false, new [] { "\u2660 ÑРвРÑ" })]
  1669. public void Reformat_Unicode_Wrap_Spaces_No_NewLines (
  1670. string text,
  1671. int maxWidth,
  1672. int widthOffset,
  1673. TextAlignment textAlignment,
  1674. bool wrap,
  1675. bool preserveTrailingSpaces,
  1676. IEnumerable<string> resultLines
  1677. )
  1678. {
  1679. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  1680. List<string> list = TextFormatter.Format (text, maxWidth, textAlignment, wrap, preserveTrailingSpaces);
  1681. Assert.Equal (list.Count, resultLines.Count ());
  1682. Assert.Equal (resultLines, list);
  1683. }
  1684. [Theory]
  1685. // Even # of spaces
  1686. // 0123456789
  1687. [InlineData ("012 456 89", 0, -10, TextAlignment.Left, true, true, true, new [] { "" })]
  1688. [InlineData (
  1689. "012 456 89",
  1690. 1,
  1691. -9,
  1692. TextAlignment.Left,
  1693. true,
  1694. true,
  1695. false,
  1696. new [] { "0", "1", "2", " ", "4", "5", "6", " ", "8", "9" },
  1697. "01245689"
  1698. )]
  1699. [InlineData ("012 456 89", 5, -5, TextAlignment.Left, true, true, false, new [] { "012 ", "456 ", "89" })]
  1700. [InlineData ("012 456 89", 9, -1, TextAlignment.Left, true, true, false, new [] { "012 456 ", "89" })]
  1701. // no clip
  1702. [InlineData ("012 456 89", 10, 0, TextAlignment.Left, true, true, false, new [] { "012 456 89" })]
  1703. [InlineData ("012 456 89", 11, 1, TextAlignment.Left, true, true, false, new [] { "012 456 89" })]
  1704. // Odd # of spaces
  1705. // 01234567890123
  1706. [InlineData ("012 456 89 end", 13, -1, TextAlignment.Left, true, true, false, new [] { "012 456 89 ", "end" })]
  1707. // no clip
  1708. [InlineData ("012 456 89 end", 14, 0, TextAlignment.Left, true, true, false, new [] { "012 456 89 end" })]
  1709. [InlineData ("012 456 89 end", 15, 1, TextAlignment.Left, true, true, false, new [] { "012 456 89 end" })]
  1710. public void Reformat_Wrap_Spaces_No_NewLines (
  1711. string text,
  1712. int maxWidth,
  1713. int widthOffset,
  1714. TextAlignment textAlignment,
  1715. bool wrap,
  1716. bool preserveTrailingSpaces,
  1717. bool stringEmpty,
  1718. IEnumerable<string> resultLines,
  1719. string noSpaceText = ""
  1720. )
  1721. {
  1722. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  1723. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  1724. List<string> list = TextFormatter.Format (text, maxWidth, textAlignment, wrap, preserveTrailingSpaces);
  1725. Assert.NotEmpty (list);
  1726. Assert.True (list.Count == resultLines.Count ());
  1727. if (stringEmpty)
  1728. {
  1729. Assert.Equal (string.Empty, list [0]);
  1730. }
  1731. else
  1732. {
  1733. Assert.NotEqual (string.Empty, list [0]);
  1734. }
  1735. Assert.Equal (resultLines, list);
  1736. if (maxWidth > 0)
  1737. {
  1738. // remove whitespace chars
  1739. if (maxWidth < 5)
  1740. {
  1741. expectedClippedWidth = text.GetRuneCount () - text.Sum (r => r == ' ' ? 1 : 0);
  1742. }
  1743. else
  1744. {
  1745. expectedClippedWidth = Math.Min (
  1746. text.GetRuneCount (),
  1747. maxWidth - text.Sum (r => r == ' ' ? 1 : 0)
  1748. );
  1749. }
  1750. list = TextFormatter.Format (text, maxWidth, TextAlignment.Left, wrap);
  1751. if (maxWidth == 1)
  1752. {
  1753. Assert.Equal (expectedClippedWidth, list.Count);
  1754. Assert.Equal (noSpaceText, string.Concat (list.ToArray ()));
  1755. }
  1756. if (maxWidth > 1 && maxWidth < 10)
  1757. {
  1758. Assert.Equal (
  1759. StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]),
  1760. list [0]
  1761. );
  1762. }
  1763. }
  1764. }
  1765. [Theory]
  1766. [InlineData (null)]
  1767. [InlineData ("")]
  1768. [InlineData ("a")]
  1769. public void RemoveHotKeySpecifier_InValid_ReturnsOriginal (string text)
  1770. {
  1771. var hotKeySpecifier = (Rune)'_';
  1772. if (text == null)
  1773. {
  1774. Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, 0, hotKeySpecifier));
  1775. Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, -1, hotKeySpecifier));
  1776. Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, 100, hotKeySpecifier));
  1777. }
  1778. else
  1779. {
  1780. Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, 0, hotKeySpecifier));
  1781. Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, -1, hotKeySpecifier));
  1782. Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, 100, hotKeySpecifier));
  1783. }
  1784. }
  1785. [Theory]
  1786. [InlineData ("all lower case", 0)]
  1787. [InlineData ("K Before", 0)]
  1788. [InlineData ("aK Second", 1)]
  1789. [InlineData ("Last K", 5)]
  1790. [InlineData ("fter K", 7)]
  1791. [InlineData ("Multiple K and R", 9)]
  1792. [InlineData ("Non-english: Кдать", 13)]
  1793. public void RemoveHotKeySpecifier_Valid_Legacy_ReturnsOriginal (string text, int hotPos)
  1794. {
  1795. var hotKeySpecifier = (Rune)'_';
  1796. Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, hotPos, hotKeySpecifier));
  1797. }
  1798. [Theory]
  1799. [InlineData ("_K Before", 0, "K Before")]
  1800. [InlineData ("a_K Second", 1, "aK Second")]
  1801. [InlineData ("Last _K", 5, "Last K")]
  1802. [InlineData ("After K_", 7, "After K")]
  1803. [InlineData ("Multiple _K and _R", 9, "Multiple K and _R")]
  1804. [InlineData ("Non-english: _Кдать", 13, "Non-english: Кдать")]
  1805. public void RemoveHotKeySpecifier_Valid_ReturnsStripped (string text, int hotPos, string expectedText)
  1806. {
  1807. var hotKeySpecifier = (Rune)'_';
  1808. Assert.Equal (expectedText, TextFormatter.RemoveHotKeySpecifier (text, hotPos, hotKeySpecifier));
  1809. }
  1810. [Theory]
  1811. [InlineData ("test", 0, 't', "test")]
  1812. [InlineData ("test", 1, 'e', "test")]
  1813. [InlineData ("Ok", 0, 'O', "Ok")]
  1814. [InlineData ("[◦ Ok ◦]", 3, 'O', "[◦ Ok ◦]")]
  1815. [InlineData ("^k", 0, '^', "^k")]
  1816. public void ReplaceHotKeyWithTag (string text, int hotPos, uint tag, string expected)
  1817. {
  1818. var tf = new TextFormatter ();
  1819. List<Rune> runes = text.ToRuneList ();
  1820. Rune rune;
  1821. if (Rune.TryGetRuneAt (text, hotPos, out rune))
  1822. {
  1823. Assert.Equal (rune, (Rune)tag);
  1824. }
  1825. string result = TextFormatter.ReplaceHotKeyWithTag (text, hotPos);
  1826. Assert.Equal (result, expected);
  1827. Assert.Equal ((Rune)tag, result.ToRunes () [hotPos]);
  1828. Assert.Equal (text.GetRuneCount (), runes.Count);
  1829. Assert.Equal (text, StringExtensions.ToString (runes));
  1830. }
  1831. [Theory]
  1832. [MemberData (nameof (SplitEnvironmentNewLine))]
  1833. public void SplitNewLine_Ending__With_Or_Without_NewLine_Probably_CRLF (
  1834. string text,
  1835. IEnumerable<string> expected
  1836. )
  1837. {
  1838. List<string> splited = TextFormatter.SplitNewLine (text);
  1839. Assert.Equal (expected, splited);
  1840. }
  1841. [Theory]
  1842. [InlineData (
  1843. "First Line 界\nSecond Line 界\nThird Line 界\n",
  1844. new [] { "First Line 界", "Second Line 界", "Third Line 界", "" }
  1845. )]
  1846. public void SplitNewLine_Ending_With_NewLine_Only_LF (string text, IEnumerable<string> expected)
  1847. {
  1848. List<string> splited = TextFormatter.SplitNewLine (text);
  1849. Assert.Equal (expected, splited);
  1850. }
  1851. [Theory]
  1852. [InlineData (
  1853. "First Line 界\nSecond Line 界\nThird Line 界",
  1854. new [] { "First Line 界", "Second Line 界", "Third Line 界" }
  1855. )]
  1856. public void SplitNewLine_Ending_Without_NewLine_Only_LF (string text, IEnumerable<string> expected)
  1857. {
  1858. List<string> splited = TextFormatter.SplitNewLine (text);
  1859. Assert.Equal (expected, splited);
  1860. }
  1861. [Theory]
  1862. [InlineData ("New Test 你", 10, 10, 20320, 20320, 9, "你")]
  1863. [InlineData ("New Test \U0001d539", 10, 11, 120121, 55349, 9, "𝔹")]
  1864. public void String_Array_Is_Not_Always_Equal_ToRunes_Array (
  1865. string text,
  1866. int runesLength,
  1867. int stringLength,
  1868. int runeValue,
  1869. int stringValue,
  1870. int index,
  1871. string expected
  1872. )
  1873. {
  1874. Rune [] usToRunes = text.ToRunes ();
  1875. Assert.Equal (runesLength, usToRunes.Length);
  1876. Assert.Equal (stringLength, text.Length);
  1877. Assert.Equal (runeValue, usToRunes [index].Value);
  1878. Assert.Equal (stringValue, text [index]);
  1879. Assert.Equal (expected, usToRunes [index].ToString ());
  1880. if (char.IsHighSurrogate (text [index]))
  1881. {
  1882. // Rune array length isn't equal to string array
  1883. Assert.Equal (expected, new string (new [] { text [index], text [index + 1] }));
  1884. }
  1885. else
  1886. {
  1887. // Rune array length is equal to string array
  1888. Assert.Equal (expected, text [index].ToString ());
  1889. }
  1890. }
  1891. [Theory]
  1892. [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
  1893. [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
  1894. [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
  1895. [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
  1896. public void TabWith_PreserveTrailingSpaces_False (
  1897. int width,
  1898. int height,
  1899. TextDirection textDirection,
  1900. int tabWidth,
  1901. string expected
  1902. )
  1903. {
  1904. var driver = new FakeDriver ();
  1905. driver.Init ();
  1906. var text = "This is a \tTab";
  1907. var tf = new TextFormatter ();
  1908. tf.AutoSize = true;
  1909. tf.Direction = textDirection;
  1910. tf.TabWidth = tabWidth;
  1911. tf.Text = text;
  1912. Assert.True (tf.WordWrap);
  1913. Assert.False (tf.PreserveTrailingSpaces);
  1914. Assert.Equal (new (width, height), tf.Size);
  1915. tf.Draw (
  1916. new (0, 0, width, height),
  1917. new Attribute (ColorName.White, ColorName.Black),
  1918. new Attribute (ColorName.Blue, ColorName.Black),
  1919. default (Rectangle),
  1920. driver
  1921. );
  1922. TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
  1923. driver.End ();
  1924. }
  1925. [Theory]
  1926. [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
  1927. [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
  1928. [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
  1929. [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
  1930. public void TabWith_PreserveTrailingSpaces_True (
  1931. int width,
  1932. int height,
  1933. TextDirection textDirection,
  1934. int tabWidth,
  1935. string expected
  1936. )
  1937. {
  1938. var driver = new FakeDriver ();
  1939. driver.Init ();
  1940. var text = "This is a \tTab";
  1941. var tf = new TextFormatter ();
  1942. tf.AutoSize = true;
  1943. tf.Direction = textDirection;
  1944. tf.TabWidth = tabWidth;
  1945. tf.PreserveTrailingSpaces = true;
  1946. tf.Text = text;
  1947. Assert.True (tf.WordWrap);
  1948. Assert.Equal (new (width, height), tf.Size);
  1949. tf.Draw (
  1950. new (0, 0, width, height),
  1951. new Attribute (ColorName.White, ColorName.Black),
  1952. new Attribute (ColorName.Blue, ColorName.Black),
  1953. default (Rectangle),
  1954. driver
  1955. );
  1956. TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
  1957. driver.End ();
  1958. }
  1959. [Theory]
  1960. [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")]
  1961. [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")]
  1962. [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")]
  1963. [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")]
  1964. public void TabWith_WordWrap_True (
  1965. int width,
  1966. int height,
  1967. TextDirection textDirection,
  1968. int tabWidth,
  1969. string expected
  1970. )
  1971. {
  1972. var driver = new FakeDriver ();
  1973. driver.Init ();
  1974. var text = "This is a \tTab";
  1975. var tf = new TextFormatter ();
  1976. tf.AutoSize = true;
  1977. tf.Direction = textDirection;
  1978. tf.TabWidth = tabWidth;
  1979. tf.WordWrap = true;
  1980. tf.Text = text;
  1981. Assert.False (tf.PreserveTrailingSpaces);
  1982. Assert.Equal (new (width, height), tf.Size);
  1983. tf.Draw (
  1984. new (0, 0, width, height),
  1985. new Attribute (ColorName.White, ColorName.Black),
  1986. new Attribute (ColorName.Blue, ColorName.Black),
  1987. default (Rectangle),
  1988. driver
  1989. );
  1990. TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver);
  1991. driver.End ();
  1992. }
  1993. [Theory]
  1994. [InlineData ("123456789", 3, "123")]
  1995. [InlineData ("Hello World", 8, "Hello Wo")]
  1996. public void TestClipOrPad_LongWord (string text, int fillPad, string expectedText)
  1997. {
  1998. // word is long but we want it to fill # space only
  1999. Assert.Equal (expectedText, TextFormatter.ClipOrPad (text, fillPad));
  2000. }
  2001. [Theory]
  2002. [InlineData ("fff", 6, "fff ")]
  2003. [InlineData ("Hello World", 16, "Hello World ")]
  2004. public void TestClipOrPad_ShortWord (string text, int fillPad, string expectedText)
  2005. {
  2006. // word is short but we want it to fill # so it should be padded
  2007. Assert.Equal (expectedText, TextFormatter.ClipOrPad (text, fillPad));
  2008. }
  2009. [Theory]
  2010. [InlineData ("你你", TextDirection.LeftRight_TopBottom, 4, 1)]
  2011. [InlineData ("AB", TextDirection.LeftRight_TopBottom, 2, 1)]
  2012. [InlineData ("你你", TextDirection.TopBottom_LeftRight, 1, 4)] // BUGBUG: Vertical wide char is broken. This should be 2,2
  2013. [InlineData ("AB", TextDirection.TopBottom_LeftRight, 1, 2)]
  2014. public void AutoSize_True_TextDirection_Correct_Size (string text, TextDirection textDirection, int expectedWidth, int expectedHeight)
  2015. {
  2016. var tf = new TextFormatter { Direction = textDirection, Text = text };
  2017. Assert.False (tf.AutoSize);
  2018. // If autosize is false, no auto sizing!
  2019. Assert.Equal (Size.Empty, tf.Size);
  2020. tf.Size = new (1, 1); // This should have no impact (autosize overrides)
  2021. tf.AutoSize = true;
  2022. Assert.Equal (new Size (expectedWidth, expectedHeight), tf.Size);
  2023. }
  2024. [Theory]
  2025. [InlineData (TextAlignment.Left, false)]
  2026. [InlineData (TextAlignment.Centered, true)]
  2027. [InlineData (TextAlignment.Right, false)]
  2028. [InlineData (TextAlignment.Justified, true)]
  2029. public void TestSize_DirectionChange_AutoSize_True_Or_False_Horizontal (
  2030. TextAlignment textAlignment,
  2031. bool autoSize
  2032. )
  2033. {
  2034. var tf = new TextFormatter
  2035. {
  2036. Direction = TextDirection.LeftRight_TopBottom, Text = "你你", Alignment = textAlignment, AutoSize = autoSize
  2037. };
  2038. Assert.Equal (4, tf.Size.Width);
  2039. Assert.Equal (1, tf.Size.Height);
  2040. tf.Direction = TextDirection.TopBottom_LeftRight;
  2041. if (autoSize/* && textAlignment != TextAlignment.Justified*/)
  2042. {
  2043. Assert.Equal (2, tf.Size.Width);
  2044. Assert.Equal (2, tf.Size.Height);
  2045. }
  2046. else
  2047. {
  2048. Assert.Equal (4, tf.Size.Width);
  2049. Assert.Equal (1, tf.Size.Height);
  2050. }
  2051. }
  2052. [Theory]
  2053. [InlineData (VerticalTextAlignment.Top, false)]
  2054. [InlineData (VerticalTextAlignment.Middle, true)]
  2055. [InlineData (VerticalTextAlignment.Bottom, false)]
  2056. [InlineData (VerticalTextAlignment.Justified, true)]
  2057. public void TestSize_DirectionChange_AutoSize_True_Or_False_Vertical (
  2058. VerticalTextAlignment textAlignment,
  2059. bool autoSize
  2060. )
  2061. {
  2062. var tf = new TextFormatter
  2063. {
  2064. Direction = TextDirection.TopBottom_LeftRight,
  2065. Text = "你你",
  2066. VerticalAlignment = textAlignment,
  2067. AutoSize = autoSize
  2068. };
  2069. Assert.Equal (2, tf.Size.Width);
  2070. Assert.Equal (2, tf.Size.Height);
  2071. tf.Direction = TextDirection.LeftRight_TopBottom;
  2072. if (autoSize/* && textAlignment != VerticalTextAlignment.Justified*/)
  2073. {
  2074. Assert.Equal (4, tf.Size.Width);
  2075. Assert.Equal (1, tf.Size.Height);
  2076. }
  2077. else
  2078. {
  2079. Assert.Equal (2, tf.Size.Width);
  2080. Assert.Equal (2, tf.Size.Height);
  2081. }
  2082. }
  2083. [Theory]
  2084. [InlineData (TextDirection.LeftRight_TopBottom, false)]
  2085. [InlineData (TextDirection.LeftRight_TopBottom, true)]
  2086. [InlineData (TextDirection.TopBottom_LeftRight, false)]
  2087. [InlineData (TextDirection.TopBottom_LeftRight, true)]
  2088. public void TestSize_SizeChange_AutoSize_True_Or_False (TextDirection textDirection, bool autoSize)
  2089. {
  2090. var tf = new TextFormatter { Direction = textDirection, Text = "你你", AutoSize = autoSize };
  2091. if (textDirection == TextDirection.LeftRight_TopBottom)
  2092. {
  2093. Assert.Equal (4, tf.Size.Width);
  2094. Assert.Equal (1, tf.Size.Height);
  2095. }
  2096. else
  2097. {
  2098. Assert.Equal (2, tf.Size.Width);
  2099. Assert.Equal (2, tf.Size.Height);
  2100. }
  2101. tf.Size = new (1, 1);
  2102. if (autoSize)
  2103. {
  2104. if (textDirection == TextDirection.LeftRight_TopBottom)
  2105. {
  2106. Assert.Equal (4, tf.Size.Width);
  2107. Assert.Equal (1, tf.Size.Height);
  2108. }
  2109. else
  2110. {
  2111. Assert.Equal (2, tf.Size.Width);
  2112. Assert.Equal (2, tf.Size.Height);
  2113. }
  2114. }
  2115. else
  2116. {
  2117. Assert.Equal (1, tf.Size.Width);
  2118. Assert.Equal (1, tf.Size.Height);
  2119. }
  2120. }
  2121. [Theory]
  2122. [InlineData (TextAlignment.Left, false)]
  2123. [InlineData (TextAlignment.Centered, true)]
  2124. [InlineData (TextAlignment.Right, false)]
  2125. [InlineData (TextAlignment.Justified, true)]
  2126. public void TestSize_SizeChange_AutoSize_True_Or_False_Horizontal (TextAlignment textAlignment, bool autoSize)
  2127. {
  2128. var tf = new TextFormatter
  2129. {
  2130. Direction = TextDirection.LeftRight_TopBottom, Text = "你你", Alignment = textAlignment, AutoSize = autoSize
  2131. };
  2132. Assert.Equal (4, tf.Size.Width);
  2133. Assert.Equal (1, tf.Size.Height);
  2134. tf.Size = new (1, 1);
  2135. if (autoSize)
  2136. {
  2137. Assert.Equal (4, tf.Size.Width);
  2138. Assert.Equal (1, tf.Size.Height);
  2139. }
  2140. else
  2141. {
  2142. Assert.Equal (1, tf.Size.Width);
  2143. Assert.Equal (1, tf.Size.Height);
  2144. }
  2145. }
  2146. [Theory]
  2147. [InlineData (VerticalTextAlignment.Top, false)]
  2148. [InlineData (VerticalTextAlignment.Middle, true)]
  2149. [InlineData (VerticalTextAlignment.Bottom, false)]
  2150. [InlineData (VerticalTextAlignment.Justified, true)]
  2151. public void TestSize_SizeChange_AutoSize_True_Or_False_Vertical (
  2152. VerticalTextAlignment textAlignment,
  2153. bool autoSize
  2154. )
  2155. {
  2156. var tf = new TextFormatter
  2157. {
  2158. Direction = TextDirection.TopBottom_LeftRight,
  2159. Text = "你你",
  2160. VerticalAlignment = textAlignment,
  2161. AutoSize = autoSize
  2162. };
  2163. Assert.Equal (2, tf.Size.Width);
  2164. Assert.Equal (2, tf.Size.Height);
  2165. tf.Size = new (1, 1);
  2166. if (autoSize)
  2167. {
  2168. Assert.Equal (2, tf.Size.Width);
  2169. Assert.Equal (2, tf.Size.Height);
  2170. }
  2171. else
  2172. {
  2173. Assert.Equal (1, tf.Size.Width);
  2174. Assert.Equal (1, tf.Size.Height);
  2175. }
  2176. }
  2177. [Theory]
  2178. // BUGBUG: This is a bug in the current implementation, the expected size should be 0, 0 because autosize is false
  2179. [InlineData ("你", TextDirection.LeftRight_TopBottom, false, 2, 1)]
  2180. [InlineData ("你", TextDirection.LeftRight_TopBottom, true, 2, 1)]
  2181. // BUGBUG: This is a bug in the current implementation, the expected size should be 0, 0 because autosize is false
  2182. [InlineData ("你", TextDirection.TopBottom_LeftRight, false, 1, 2)]
  2183. [InlineData ("你", TextDirection.TopBottom_LeftRight, true, 1, 2)]
  2184. // BUGBUG: This is a bug in the current implementation, the expected size should be 0, 0 because autosize is false
  2185. [InlineData ("你你", TextDirection.LeftRight_TopBottom, false, 4, 1)]
  2186. [InlineData ("你你", TextDirection.LeftRight_TopBottom, true, 4, 1)]
  2187. // BUGBUG: This is a bug in the current implementation, the expected size should be 0, 0 because autosize is false
  2188. [InlineData ("你你", TextDirection.TopBottom_LeftRight, false, 1, 4)]
  2189. [InlineData ("你你", TextDirection.TopBottom_LeftRight, true, 1, 4)]
  2190. public void Text_Set_SizeIsCorrect (string text, TextDirection textDirection, bool autoSize, int expectedWidth, int expectedHeight)
  2191. {
  2192. var tf = new TextFormatter { Direction = textDirection, Text = text, AutoSize = autoSize };
  2193. Assert.Equal (new Size (expectedWidth, expectedHeight), tf.Size);
  2194. }
  2195. [Theory]
  2196. [InlineData (TextDirection.LeftRight_TopBottom, false)]
  2197. [InlineData (TextDirection.LeftRight_TopBottom, true)]
  2198. [InlineData (TextDirection.TopBottom_LeftRight, false)]
  2199. [InlineData (TextDirection.TopBottom_LeftRight, true)]
  2200. public void TestSize_TextChange (TextDirection textDirection, bool autoSize)
  2201. {
  2202. var tf = new TextFormatter { Direction = textDirection, Text = "你", AutoSize = autoSize };
  2203. Assert.Equal (new Size (2, 1), tf.Size);
  2204. tf.Text = "你你";
  2205. Assert.Equal (autoSize, tf.AutoSize);
  2206. if (autoSize)
  2207. {
  2208. if (textDirection == TextDirection.LeftRight_TopBottom)
  2209. {
  2210. Assert.Equal (4, tf.Size.Width);
  2211. Assert.Equal (1, tf.Size.Height);
  2212. }
  2213. else
  2214. {
  2215. Assert.Equal (2, tf.Size.Width);
  2216. Assert.Equal (2, tf.Size.Height);
  2217. }
  2218. }
  2219. else
  2220. {
  2221. Assert.Equal (2, tf.Size.Width);
  2222. Assert.Equal (1, tf.Size.Height);
  2223. }
  2224. }
  2225. [Fact]
  2226. public void WordWrap_BigWidth ()
  2227. {
  2228. List<string> wrappedLines;
  2229. var text = "Constantinople";
  2230. wrappedLines = TextFormatter.WordWrapText (text, 100);
  2231. Assert.True (wrappedLines.Count == 1);
  2232. Assert.Equal ("Constantinople", wrappedLines [0]);
  2233. }
  2234. [Fact]
  2235. public void WordWrap_Invalid ()
  2236. {
  2237. var text = string.Empty;
  2238. var width = 0;
  2239. Assert.Empty (TextFormatter.WordWrapText (null, width));
  2240. Assert.Empty (TextFormatter.WordWrapText (text, width));
  2241. Assert.Throws<ArgumentOutOfRangeException> (() => TextFormatter.WordWrapText (text, -1));
  2242. }
  2243. [Theory]
  2244. [InlineData ("A sentence has words.", 3, -18, new [] { "A", "sen", "ten", "ce", "has", "wor", "ds." })]
  2245. [InlineData (
  2246. "A sentence has words.",
  2247. 2,
  2248. -19,
  2249. new [] { "A", "se", "nt", "en", "ce", "ha", "s", "wo", "rd", "s." }
  2250. )]
  2251. [InlineData (
  2252. "A sentence has words.",
  2253. 1,
  2254. -20,
  2255. new [] { "A", "s", "e", "n", "t", "e", "n", "c", "e", "h", "a", "s", "w", "o", "r", "d", "s", "." }
  2256. )]
  2257. public void WordWrap_Narrow_Default (
  2258. string text,
  2259. int maxWidth,
  2260. int widthOffset,
  2261. IEnumerable<string> resultLines
  2262. )
  2263. {
  2264. // Calls WordWrapText (text, width) and thus preserveTrailingSpaces defaults to false
  2265. List<string> wrappedLines;
  2266. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  2267. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  2268. wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
  2269. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2270. Assert.True (
  2271. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
  2272. );
  2273. Assert.True (
  2274. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  2275. );
  2276. Assert.Equal (resultLines, wrappedLines);
  2277. }
  2278. [Theory]
  2279. [InlineData ("A sentence has words.", 21, 0, new [] { "A sentence has words." })]
  2280. [InlineData ("A sentence has words.", 20, -1, new [] { "A sentence has", "words." })]
  2281. [InlineData ("A sentence has words.", 15, -6, new [] { "A sentence has", "words." })]
  2282. [InlineData ("A sentence has words.", 14, -7, new [] { "A sentence has", "words." })]
  2283. [InlineData ("A sentence has words.", 13, -8, new [] { "A sentence", "has words." })]
  2284. // Unicode
  2285. [InlineData (
  2286. "A Unicode sentence (пÑивеÑ) has words.",
  2287. 42,
  2288. 0,
  2289. new [] { "A Unicode sentence (пÑивеÑ) has words." }
  2290. )]
  2291. [InlineData (
  2292. "A Unicode sentence (пÑивеÑ) has words.",
  2293. 41,
  2294. -1,
  2295. new [] { "A Unicode sentence (пÑивеÑ) has", "words." }
  2296. )]
  2297. [InlineData (
  2298. "A Unicode sentence (пÑивеÑ) has words.",
  2299. 36,
  2300. -6,
  2301. new [] { "A Unicode sentence (пÑивеÑ) has", "words." }
  2302. )]
  2303. [InlineData (
  2304. "A Unicode sentence (пÑивеÑ) has words.",
  2305. 35,
  2306. -7,
  2307. new [] { "A Unicode sentence (пÑивеÑ) has", "words." }
  2308. )]
  2309. [InlineData (
  2310. "A Unicode sentence (пÑивеÑ) has words.",
  2311. 34,
  2312. -8,
  2313. new [] { "A Unicode sentence (пÑивеÑ)", "has words." }
  2314. )]
  2315. [InlineData (
  2316. "A Unicode sentence (пÑивеÑ) has words.",
  2317. 25,
  2318. -17,
  2319. new [] { "A Unicode sentence", "(пÑивеÑ) has words." }
  2320. )]
  2321. public void WordWrap_NoNewLines_Default (
  2322. string text,
  2323. int maxWidth,
  2324. int widthOffset,
  2325. IEnumerable<string> resultLines
  2326. )
  2327. {
  2328. // Calls WordWrapText (text, width) and thus preserveTrailingSpaces defaults to false
  2329. List<string> wrappedLines;
  2330. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  2331. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  2332. wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
  2333. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2334. Assert.True (
  2335. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
  2336. );
  2337. Assert.True (
  2338. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  2339. );
  2340. Assert.Equal (resultLines, wrappedLines);
  2341. }
  2342. [Theory]
  2343. [InlineData ("これが最初の行です。 こんにちは世界。 これが2行目です。", 29, 0, new [] { "これが最初の行です。", "こんにちは世界。", "これが2行目です。" })]
  2344. public void WordWrap_PreserveTrailingSpaces_False_Unicode_Wide_Runes (
  2345. string text,
  2346. int maxWidth,
  2347. int widthOffset,
  2348. IEnumerable<string> resultLines
  2349. )
  2350. {
  2351. List<string> wrappedLines;
  2352. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  2353. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  2354. wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
  2355. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2356. Assert.True (
  2357. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
  2358. );
  2359. Assert.True (
  2360. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  2361. );
  2362. Assert.Equal (resultLines, wrappedLines);
  2363. }
  2364. [Theory]
  2365. [InlineData ("文に は言葉 があり ます。", 14, 0, new [] { "文に は言葉", "があり ます。" })]
  2366. [InlineData ("文に は言葉 があり ます。", 3, -11, new [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })]
  2367. [InlineData ("文に は言葉 があり ます。", 2, -12, new [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })]
  2368. [InlineData (
  2369. "文に は言葉 があり ます。",
  2370. 1,
  2371. -13,
  2372. new [] { " ", " ", " " }
  2373. )] // Just Spaces; should result in a single space for each line
  2374. public void WordWrap_PreserveTrailingSpaces_False_Wide_Runes (
  2375. string text,
  2376. int maxWidth,
  2377. int widthOffset,
  2378. IEnumerable<string> resultLines
  2379. )
  2380. {
  2381. List<string> wrappedLines;
  2382. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  2383. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  2384. wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
  2385. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2386. Assert.True (
  2387. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
  2388. );
  2389. Assert.True (
  2390. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  2391. );
  2392. Assert.Equal (resultLines, wrappedLines);
  2393. }
  2394. [Theory]
  2395. [InlineData (null, 1, new string [] { })] // null input
  2396. [InlineData ("", 1, new string [] { })] // Empty input
  2397. [InlineData ("1 34", 1, new [] { "1", "3", "4" })] // Single Spaces
  2398. [InlineData ("1", 1, new [] { "1" })] // Short input
  2399. [InlineData ("12", 1, new [] { "1", "2" })]
  2400. [InlineData ("123", 1, new [] { "1", "2", "3" })]
  2401. [InlineData ("123456", 1, new [] { "1", "2", "3", "4", "5", "6" })] // No spaces
  2402. [InlineData (" ", 1, new [] { " " })] // Just Spaces; should result in a single space
  2403. [InlineData (" ", 1, new [] { " " })]
  2404. [InlineData (" ", 1, new [] { " ", " " })]
  2405. [InlineData (" ", 1, new [] { " ", " " })]
  2406. [InlineData ("12 456", 1, new [] { "1", "2", "4", "5", "6" })] // Single Spaces
  2407. [InlineData (" 2 456", 1, new [] { " ", "2", "4", "5", "6" })] // Leading spaces should be preserved.
  2408. [InlineData (" 2 456 8", 1, new [] { " ", "2", "4", "5", "6", "8" })]
  2409. [InlineData (
  2410. "A sentence has words. ",
  2411. 1,
  2412. new [] { "A", "s", "e", "n", "t", "e", "n", "c", "e", "h", "a", "s", "w", "o", "r", "d", "s", "." }
  2413. )] // Complex example
  2414. [InlineData ("12 567", 1, new [] { "1", "2", " ", "5", "6", "7" })] // Double Spaces
  2415. [InlineData (" 3 567", 1, new [] { " ", "3", "5", "6", "7" })] // Double Leading spaces should be preserved.
  2416. [InlineData (" 3 678 1", 1, new [] { " ", "3", " ", "6", "7", "8", " ", "1" })]
  2417. [InlineData ("1 456", 1, new [] { "1", " ", "4", "5", "6" })]
  2418. [InlineData (
  2419. "A sentence has words. ",
  2420. 1,
  2421. new []
  2422. {
  2423. "A", " ", "s", "e", "n", "t", "e", "n", "c", "e", " ", "h", "a", "s", "w", "o", "r", "d", "s", ".", " "
  2424. }
  2425. )] // Double space Complex example
  2426. public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_1 (
  2427. string text,
  2428. int width,
  2429. IEnumerable<string> resultLines
  2430. )
  2431. {
  2432. List<string> wrappedLines = TextFormatter.WordWrapText (text, width);
  2433. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2434. Assert.Equal (resultLines, wrappedLines);
  2435. var breakLines = "";
  2436. foreach (string line in wrappedLines)
  2437. {
  2438. breakLines += $"{line}{Environment.NewLine}";
  2439. }
  2440. var expected = string.Empty;
  2441. foreach (string line in resultLines)
  2442. {
  2443. expected += $"{line}{Environment.NewLine}";
  2444. }
  2445. Assert.Equal (expected, breakLines);
  2446. }
  2447. [Theory]
  2448. [InlineData (null, 3, new string [] { })] // null input
  2449. [InlineData ("", 3, new string [] { })] // Empty input
  2450. [InlineData ("1", 3, new [] { "1" })] // Short input
  2451. [InlineData ("12", 3, new [] { "12" })]
  2452. [InlineData ("123", 3, new [] { "123" })]
  2453. [InlineData ("123456", 3, new [] { "123", "456" })] // No spaces
  2454. [InlineData ("1234567", 3, new [] { "123", "456", "7" })] // No spaces
  2455. [InlineData (" ", 3, new [] { " " })] // Just Spaces; should result in a single space
  2456. [InlineData (" ", 3, new [] { " " })]
  2457. [InlineData (" ", 3, new [] { " " })]
  2458. [InlineData (" ", 3, new [] { " " })]
  2459. [InlineData ("12 456", 3, new [] { "12", "456" })] // Single Spaces
  2460. [InlineData (" 2 456", 3, new [] { " 2", "456" })] // Leading spaces should be preserved.
  2461. [InlineData (" 2 456 8", 3, new [] { " 2", "456", "8" })]
  2462. [InlineData (
  2463. "A sentence has words. ",
  2464. 3,
  2465. new [] { "A", "sen", "ten", "ce", "has", "wor", "ds." }
  2466. )] // Complex example
  2467. [InlineData ("12 567", 3, new [] { "12 ", "567" })] // Double Spaces
  2468. [InlineData (" 3 567", 3, new [] { " 3", "567" })] // Double Leading spaces should be preserved.
  2469. [InlineData (" 3 678 1", 3, new [] { " 3", " 67", "8 ", "1" })]
  2470. [InlineData ("1 456", 3, new [] { "1 ", "456" })]
  2471. [InlineData (
  2472. "A sentence has words. ",
  2473. 3,
  2474. new [] { "A ", "sen", "ten", "ce ", " ", "has", "wor", "ds.", " " }
  2475. )] // Double space Complex example
  2476. public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_3 (
  2477. string text,
  2478. int width,
  2479. IEnumerable<string> resultLines
  2480. )
  2481. {
  2482. List<string> wrappedLines = TextFormatter.WordWrapText (text, width);
  2483. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2484. Assert.Equal (resultLines, wrappedLines);
  2485. var breakLines = "";
  2486. foreach (string line in wrappedLines)
  2487. {
  2488. breakLines += $"{line}{Environment.NewLine}";
  2489. }
  2490. var expected = string.Empty;
  2491. foreach (string line in resultLines)
  2492. {
  2493. expected += $"{line}{Environment.NewLine}";
  2494. }
  2495. Assert.Equal (expected, breakLines);
  2496. }
  2497. [Theory]
  2498. [InlineData (null, 50, new string [] { })] // null input
  2499. [InlineData ("", 50, new string [] { })] // Empty input
  2500. [InlineData ("1", 50, new [] { "1" })] // Short input
  2501. [InlineData ("12", 50, new [] { "12" })]
  2502. [InlineData ("123", 50, new [] { "123" })]
  2503. [InlineData ("123456", 50, new [] { "123456" })] // No spaces
  2504. [InlineData ("1234567", 50, new [] { "1234567" })] // No spaces
  2505. [InlineData (" ", 50, new [] { " " })] // Just Spaces; should result in a single space
  2506. [InlineData (" ", 50, new [] { " " })]
  2507. [InlineData (" ", 50, new [] { " " })]
  2508. [InlineData ("12 456", 50, new [] { "12 456" })] // Single Spaces
  2509. [InlineData (" 2 456", 50, new [] { " 2 456" })] // Leading spaces should be preserved.
  2510. [InlineData (" 2 456 8", 50, new [] { " 2 456 8" })]
  2511. [InlineData ("A sentence has words. ", 50, new [] { "A sentence has words. " })] // Complex example
  2512. [InlineData ("12 567", 50, new [] { "12 567" })] // Double Spaces
  2513. [InlineData (" 3 567", 50, new [] { " 3 567" })] // Double Leading spaces should be preserved.
  2514. [InlineData (" 3 678 1", 50, new [] { " 3 678 1" })]
  2515. [InlineData ("1 456", 50, new [] { "1 456" })]
  2516. [InlineData (
  2517. "A sentence has words. ",
  2518. 50,
  2519. new [] { "A sentence has words. " }
  2520. )] // Double space Complex example
  2521. public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_50 (
  2522. string text,
  2523. int width,
  2524. IEnumerable<string> resultLines
  2525. )
  2526. {
  2527. List<string> wrappedLines = TextFormatter.WordWrapText (text, width);
  2528. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2529. Assert.Equal (resultLines, wrappedLines);
  2530. var breakLines = "";
  2531. foreach (string line in wrappedLines)
  2532. {
  2533. breakLines += $"{line}{Environment.NewLine}";
  2534. }
  2535. var expected = string.Empty;
  2536. foreach (string line in resultLines)
  2537. {
  2538. expected += $"{line}{Environment.NewLine}";
  2539. }
  2540. Assert.Equal (expected, breakLines);
  2541. }
  2542. [Theory]
  2543. [InlineData ("A sentence has words.", 14, -7, new [] { "A sentence ", "has words." })]
  2544. [InlineData ("A sentence has words.", 8, -13, new [] { "A ", "sentence", " has ", "words." })]
  2545. [InlineData ("A sentence has words.", 6, -15, new [] { "A ", "senten", "ce ", "has ", "words." })]
  2546. [InlineData ("A sentence has words.", 3, -18, new [] { "A ", "sen", "ten", "ce ", "has", " ", "wor", "ds." })]
  2547. [InlineData (
  2548. "A sentence has words.",
  2549. 2,
  2550. -19,
  2551. new [] { "A ", "se", "nt", "en", "ce", " ", "ha", "s ", "wo", "rd", "s." }
  2552. )]
  2553. [InlineData (
  2554. "A sentence has words.",
  2555. 1,
  2556. -20,
  2557. new []
  2558. {
  2559. "A", " ", "s", "e", "n", "t", "e", "n", "c", "e", " ", "h", "a", "s", " ", "w", "o", "r", "d", "s", "."
  2560. }
  2561. )]
  2562. public void WordWrap_PreserveTrailingSpaces_True (
  2563. string text,
  2564. int maxWidth,
  2565. int widthOffset,
  2566. IEnumerable<string> resultLines
  2567. )
  2568. {
  2569. List<string> wrappedLines;
  2570. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  2571. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  2572. wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true);
  2573. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2574. Assert.True (
  2575. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
  2576. );
  2577. Assert.True (
  2578. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  2579. );
  2580. Assert.Equal (resultLines, wrappedLines);
  2581. }
  2582. [Theory]
  2583. [InlineData ("文に は言葉 があり ます。", 14, 0, new [] { "文に は言葉 ", "があり ます。" })]
  2584. [InlineData ("文に は言葉 があり ます。", 3, -11, new [] { "文", "に ", "は", "言", "葉 ", "が", "あ", "り ", "ま", "す", "。" })]
  2585. [InlineData (
  2586. "文に は言葉 があり ます。",
  2587. 2,
  2588. -12,
  2589. new [] { "文", "に", " ", "は", "言", "葉", " ", "が", "あ", "り", " ", "ま", "す", "。" }
  2590. )]
  2591. [InlineData ("文に は言葉 があり ます。", 1, -13, new string [] { })]
  2592. public void WordWrap_PreserveTrailingSpaces_True_Wide_Runes (
  2593. string text,
  2594. int maxWidth,
  2595. int widthOffset,
  2596. IEnumerable<string> resultLines
  2597. )
  2598. {
  2599. List<string> wrappedLines;
  2600. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  2601. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  2602. wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true);
  2603. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2604. Assert.True (
  2605. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
  2606. );
  2607. Assert.True (
  2608. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  2609. );
  2610. Assert.Equal (resultLines, wrappedLines);
  2611. }
  2612. [Theory]
  2613. [InlineData ("A sentence has words. ", 3, new [] { "A ", "sen", "ten", "ce ", "has", " ", "wor", "ds.", " " })]
  2614. [InlineData (
  2615. "A sentence has words. ",
  2616. 3,
  2617. new [] { "A ", " ", "sen", "ten", "ce ", " ", " ", " ", "has", " ", "wor", "ds.", " " }
  2618. )]
  2619. public void WordWrap_PreserveTrailingSpaces_True_With_Simple_Runes_Width_3 (
  2620. string text,
  2621. int width,
  2622. IEnumerable<string> resultLines
  2623. )
  2624. {
  2625. List<string> wrappedLines = TextFormatter.WordWrapText (text, width, true);
  2626. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2627. Assert.Equal (resultLines, wrappedLines);
  2628. var breakLines = "";
  2629. foreach (string line in wrappedLines)
  2630. {
  2631. breakLines += $"{line}{Environment.NewLine}";
  2632. }
  2633. var expected = string.Empty;
  2634. foreach (string line in resultLines)
  2635. {
  2636. expected += $"{line}{Environment.NewLine}";
  2637. }
  2638. Assert.Equal (expected, breakLines);
  2639. // Double space Complex example - this is how VS 2022 does it
  2640. //text = "A sentence has words. ";
  2641. //breakLines = "";
  2642. //wrappedLines = TextFormatter.WordWrapText (text, width, preserveTrailingSpaces: true);
  2643. //foreach (var line in wrappedLines) {
  2644. // breakLines += $"{line}{Environment.NewLine}";
  2645. //}
  2646. //expected = "A " + Environment.NewLine +
  2647. // " se" + Environment.NewLine +
  2648. // " nt" + Environment.NewLine +
  2649. // " en" + Environment.NewLine +
  2650. // " ce" + Environment.NewLine +
  2651. // " " + Environment.NewLine +
  2652. // " " + Environment.NewLine +
  2653. // " " + Environment.NewLine +
  2654. // " ha" + Environment.NewLine +
  2655. // " s " + Environment.NewLine +
  2656. // " wo" + Environment.NewLine +
  2657. // " rd" + Environment.NewLine +
  2658. // " s." + Environment.NewLine;
  2659. //Assert.Equal (expected, breakLines);
  2660. }
  2661. [Theory]
  2662. [InlineData ("A sentence\t\t\t has words.", 14, -10, new [] { "A sentence\t", "\t\t has ", "words." })]
  2663. [InlineData (
  2664. "A sentence\t\t\t has words.",
  2665. 8,
  2666. -16,
  2667. new [] { "A ", "sentence", "\t\t", "\t ", "has ", "words." }
  2668. )]
  2669. [InlineData (
  2670. "A sentence\t\t\t has words.",
  2671. 3,
  2672. -21,
  2673. new [] { "A ", "sen", "ten", "ce", "\t", "\t", "\t", " ", "has", " ", "wor", "ds." }
  2674. )]
  2675. [InlineData (
  2676. "A sentence\t\t\t has words.",
  2677. 2,
  2678. -22,
  2679. new [] { "A ", "se", "nt", "en", "ce", "\t", "\t", "\t", " ", "ha", "s ", "wo", "rd", "s." }
  2680. )]
  2681. [InlineData (
  2682. "A sentence\t\t\t has words.",
  2683. 1,
  2684. -23,
  2685. new []
  2686. {
  2687. "A",
  2688. " ",
  2689. "s",
  2690. "e",
  2691. "n",
  2692. "t",
  2693. "e",
  2694. "n",
  2695. "c",
  2696. "e",
  2697. "\t",
  2698. "\t",
  2699. "\t",
  2700. " ",
  2701. "h",
  2702. "a",
  2703. "s",
  2704. " ",
  2705. "w",
  2706. "o",
  2707. "r",
  2708. "d",
  2709. "s",
  2710. "."
  2711. }
  2712. )]
  2713. public void WordWrap_PreserveTrailingSpaces_True_With_Tab (
  2714. string text,
  2715. int maxWidth,
  2716. int widthOffset,
  2717. IEnumerable<string> resultLines,
  2718. int tabWidth = 4
  2719. )
  2720. {
  2721. List<string> wrappedLines;
  2722. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  2723. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  2724. wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true, tabWidth);
  2725. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2726. Assert.True (
  2727. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
  2728. );
  2729. Assert.True (
  2730. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  2731. );
  2732. Assert.Equal (resultLines, wrappedLines);
  2733. }
  2734. [Theory]
  2735. [InlineData ("Constantinople", 14, 0, new [] { "Constantinople" })]
  2736. [InlineData ("Constantinople", 12, -2, new [] { "Constantinop", "le" })]
  2737. [InlineData ("Constantinople", 9, -5, new [] { "Constanti", "nople" })]
  2738. [InlineData ("Constantinople", 7, -7, new [] { "Constan", "tinople" })]
  2739. [InlineData ("Constantinople", 5, -9, new [] { "Const", "antin", "ople" })]
  2740. [InlineData ("Constantinople", 4, -10, new [] { "Cons", "tant", "inop", "le" })]
  2741. [InlineData (
  2742. "Constantinople",
  2743. 1,
  2744. -13,
  2745. new [] { "C", "o", "n", "s", "t", "a", "n", "t", "i", "n", "o", "p", "l", "e" }
  2746. )]
  2747. public void WordWrap_SingleWordLine (
  2748. string text,
  2749. int maxWidth,
  2750. int widthOffset,
  2751. IEnumerable<string> resultLines
  2752. )
  2753. {
  2754. List<string> wrappedLines;
  2755. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  2756. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  2757. wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
  2758. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2759. Assert.True (
  2760. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
  2761. );
  2762. Assert.True (
  2763. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  2764. );
  2765. Assert.Equal (resultLines, wrappedLines);
  2766. }
  2767. [Theory]
  2768. [InlineData ("This\u00A0is\n\u00A0a\u00A0sentence.", 20, 0, new [] { "This\u00A0is\u00A0a\u00A0sentence." })]
  2769. [InlineData ("This\u00A0is\n\u00A0a\u00A0sentence.", 19, -1, new [] { "This\u00A0is\u00A0a\u00A0sentence." })]
  2770. [InlineData (
  2771. "\u00A0\u00A0\u00A0\u00A0\u00A0test\u00A0sentence.",
  2772. 19,
  2773. 0,
  2774. new [] { "\u00A0\u00A0\u00A0\u00A0\u00A0test\u00A0sentence." }
  2775. )]
  2776. public void WordWrap_Unicode_2LinesWithNonBreakingSpace (
  2777. string text,
  2778. int maxWidth,
  2779. int widthOffset,
  2780. IEnumerable<string> resultLines
  2781. )
  2782. {
  2783. List<string> wrappedLines;
  2784. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  2785. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  2786. wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
  2787. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2788. Assert.True (
  2789. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
  2790. );
  2791. Assert.True (
  2792. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  2793. );
  2794. Assert.Equal (resultLines, wrappedLines);
  2795. }
  2796. [Theory]
  2797. [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 19, 0, new [] { "This\u00A0is\u00A0a\u00A0sentence." })]
  2798. [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 18, -1, new [] { "This\u00A0is\u00A0a\u00A0sentence", "." })]
  2799. [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 17, -2, new [] { "This\u00A0is\u00A0a\u00A0sentenc", "e." })]
  2800. [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 14, -5, new [] { "This\u00A0is\u00A0a\u00A0sent", "ence." })]
  2801. [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 10, -9, new [] { "This\u00A0is\u00A0a\u00A0", "sentence." })]
  2802. [InlineData (
  2803. "This\u00A0is\u00A0a\u00A0sentence.",
  2804. 7,
  2805. -12,
  2806. new [] { "This\u00A0is", "\u00A0a\u00A0sent", "ence." }
  2807. )]
  2808. [InlineData (
  2809. "This\u00A0is\u00A0a\u00A0sentence.",
  2810. 5,
  2811. -14,
  2812. new [] { "This\u00A0", "is\u00A0a\u00A0", "sente", "nce." }
  2813. )]
  2814. [InlineData (
  2815. "This\u00A0is\u00A0a\u00A0sentence.",
  2816. 1,
  2817. -18,
  2818. new []
  2819. {
  2820. "T", "h", "i", "s", "\u00A0", "i", "s", "\u00A0", "a", "\u00A0", "s", "e", "n", "t", "e", "n", "c", "e", "."
  2821. }
  2822. )]
  2823. public void WordWrap_Unicode_LineWithNonBreakingSpace (
  2824. string text,
  2825. int maxWidth,
  2826. int widthOffset,
  2827. IEnumerable<string> resultLines
  2828. )
  2829. {
  2830. List<string> wrappedLines;
  2831. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  2832. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  2833. wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
  2834. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2835. Assert.True (
  2836. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
  2837. );
  2838. Assert.True (
  2839. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  2840. );
  2841. Assert.Equal (resultLines, wrappedLines);
  2842. }
  2843. [Theory]
  2844. [InlineData (
  2845. "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
  2846. 51,
  2847. 0,
  2848. new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" }
  2849. )]
  2850. [InlineData (
  2851. "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
  2852. 50,
  2853. -1,
  2854. new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" }
  2855. )]
  2856. [InlineData (
  2857. "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
  2858. 46,
  2859. -5,
  2860. new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮ", "ฯะัาำ" }
  2861. )]
  2862. [InlineData (
  2863. "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
  2864. 26,
  2865. -25,
  2866. new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" }
  2867. )]
  2868. [InlineData (
  2869. "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
  2870. 17,
  2871. -34,
  2872. new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑ", "ฒณดตถทธนบปผฝพฟภมย", "รฤลฦวศษสหฬอฮฯะัาำ" }
  2873. )]
  2874. [InlineData (
  2875. "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
  2876. 13,
  2877. -38,
  2878. new [] { "กขฃคฅฆงจฉชซฌญ", "ฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦว", "ศษสหฬอฮฯะัาำ" }
  2879. )]
  2880. [InlineData (
  2881. "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ",
  2882. 1,
  2883. -50,
  2884. new []
  2885. {
  2886. "ก",
  2887. "ข",
  2888. "ฃ",
  2889. "ค",
  2890. "ฅ",
  2891. "ฆ",
  2892. "ง",
  2893. "จ",
  2894. "ฉ",
  2895. "ช",
  2896. "ซ",
  2897. "ฌ",
  2898. "ญ",
  2899. "ฎ",
  2900. "ฏ",
  2901. "ฐ",
  2902. "ฑ",
  2903. "ฒ",
  2904. "ณ",
  2905. "ด",
  2906. "ต",
  2907. "ถ",
  2908. "ท",
  2909. "ธ",
  2910. "น",
  2911. "บ",
  2912. "ป",
  2913. "ผ",
  2914. "ฝ",
  2915. "พ",
  2916. "ฟ",
  2917. "ภ",
  2918. "ม",
  2919. "ย",
  2920. "ร",
  2921. "ฤ",
  2922. "ล",
  2923. "ฦ",
  2924. "ว",
  2925. "ศ",
  2926. "ษ",
  2927. "ส",
  2928. "ห",
  2929. "ฬ",
  2930. "อ",
  2931. "ฮ",
  2932. "ฯ",
  2933. "ะั",
  2934. "า",
  2935. "ำ"
  2936. }
  2937. )]
  2938. public void WordWrap_Unicode_SingleWordLine (
  2939. string text,
  2940. int maxWidth,
  2941. int widthOffset,
  2942. IEnumerable<string> resultLines
  2943. )
  2944. {
  2945. List<string> wrappedLines;
  2946. IEnumerable<Rune> zeroWidth = text.EnumerateRunes ().Where (r => r.GetColumns () == 0);
  2947. Assert.Single (zeroWidth);
  2948. Assert.Equal ('ั', zeroWidth.ElementAt (0).Value);
  2949. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  2950. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  2951. wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
  2952. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  2953. Assert.True (
  2954. expectedClippedWidth
  2955. >= (wrappedLines.Count > 0
  2956. ? wrappedLines.Max (
  2957. l => l.GetRuneCount ()
  2958. + zeroWidth.Count ()
  2959. - 1
  2960. + widthOffset
  2961. )
  2962. : 0)
  2963. );
  2964. Assert.True (
  2965. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  2966. );
  2967. Assert.Equal (resultLines, wrappedLines);
  2968. }
  2969. /// <summary>WordWrap strips CRLF</summary>
  2970. [Theory]
  2971. [InlineData (
  2972. "A sentence has words.\nA paragraph has lines.",
  2973. 44,
  2974. 0,
  2975. new [] { "A sentence has words.A paragraph has lines." }
  2976. )]
  2977. [InlineData (
  2978. "A sentence has words.\nA paragraph has lines.",
  2979. 43,
  2980. -1,
  2981. new [] { "A sentence has words.A paragraph has lines." }
  2982. )]
  2983. [InlineData (
  2984. "A sentence has words.\nA paragraph has lines.",
  2985. 38,
  2986. -6,
  2987. new [] { "A sentence has words.A paragraph has", "lines." }
  2988. )]
  2989. [InlineData (
  2990. "A sentence has words.\nA paragraph has lines.",
  2991. 34,
  2992. -10,
  2993. new [] { "A sentence has words.A paragraph", "has lines." }
  2994. )]
  2995. [InlineData (
  2996. "A sentence has words.\nA paragraph has lines.",
  2997. 27,
  2998. -17,
  2999. new [] { "A sentence has words.A", "paragraph has lines." }
  3000. )]
  3001. // Unicode
  3002. [InlineData (
  3003. "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
  3004. 69,
  3005. 0,
  3006. new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии." }
  3007. )]
  3008. [InlineData (
  3009. "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
  3010. 68,
  3011. -1,
  3012. new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии." }
  3013. )]
  3014. [InlineData (
  3015. "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
  3016. 63,
  3017. -6,
  3018. new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has", "Линии." }
  3019. )]
  3020. [InlineData (
  3021. "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
  3022. 59,
  3023. -10,
  3024. new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт", "has Линии." }
  3025. )]
  3026. [InlineData (
  3027. "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.",
  3028. 52,
  3029. -17,
  3030. new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode", "Пункт has Линии." }
  3031. )]
  3032. public void WordWrap_WithNewLines (string text, int maxWidth, int widthOffset, IEnumerable<string> resultLines)
  3033. {
  3034. List<string> wrappedLines;
  3035. Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset);
  3036. int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth);
  3037. wrappedLines = TextFormatter.WordWrapText (text, maxWidth);
  3038. Assert.Equal (wrappedLines.Count, resultLines.Count ());
  3039. Assert.True (
  3040. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0)
  3041. );
  3042. Assert.True (
  3043. expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
  3044. );
  3045. Assert.Equal (resultLines, wrappedLines);
  3046. }
  3047. [SetupFakeDriver]
  3048. [Theory]
  3049. [InlineData ("A", 0, false, "")]
  3050. [InlineData ("A", 1, false, "A")]
  3051. [InlineData ("A", 2, false, "A")]
  3052. [InlineData ("AB", 1, false, "A")]
  3053. [InlineData ("AB", 2, false, "AB")]
  3054. [InlineData ("ABC", 3, false, "ABC")]
  3055. [InlineData ("ABC", 4, false, "ABC")]
  3056. [InlineData ("ABC", 6, false, "ABC")]
  3057. [InlineData ("A", 0, true, "")]
  3058. [InlineData ("A", 1, true, "A")]
  3059. [InlineData ("A", 2, true, "A")]
  3060. [InlineData ("AB", 1, true, "A")]
  3061. [InlineData ("AB", 2, true, "AB")]
  3062. [InlineData ("ABC", 3, true, "ABC")]
  3063. [InlineData ("ABC", 4, true, "ABC")]
  3064. [InlineData ("ABC", 6, true, "ABC")]
  3065. public void Draw_Horizontal_Left (string text, int width, bool autoSize, string expectedText)
  3066. {
  3067. TextFormatter tf = new ()
  3068. {
  3069. Text = text,
  3070. Alignment = TextAlignment.Left,
  3071. AutoSize = autoSize,
  3072. };
  3073. if (!autoSize)
  3074. {
  3075. tf.Size = new Size (width, 1);
  3076. }
  3077. tf.Draw (new Rectangle (0, 0, width, 1), Attribute.Default, Attribute.Default);
  3078. TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
  3079. }
  3080. [SetupFakeDriver]
  3081. [Theory]
  3082. [InlineData ("A", 0, false, "")]
  3083. [InlineData ("A", 1, false, "A")]
  3084. [InlineData ("A", 2, false, " A")]
  3085. [InlineData ("AB", 1, false, "A")]
  3086. [InlineData ("AB", 2, false, "AB")]
  3087. [InlineData ("ABC", 3, false, "ABC")]
  3088. [InlineData ("ABC", 4, false, " ABC")]
  3089. [InlineData ("ABC", 6, false, " ABC")]
  3090. [InlineData ("A", 0, true, "")]
  3091. [InlineData ("A", 1, true, "A")]
  3092. [InlineData ("A", 2, true, " A")]
  3093. [InlineData ("AB", 1, true, "")] // BUGBUG: Should be "B". See https://github.com/gui-cs/Terminal.Gui/issues/3418#issuecomment-2067771418 for a partial fix
  3094. [InlineData ("AB", 2, true, "AB")]
  3095. [InlineData ("ABC", 3, true, "ABC")]
  3096. [InlineData ("ABC", 4, true, " ABC")]
  3097. [InlineData ("ABC", 6, true, " ABC")]
  3098. public void Draw_Horizontal_Right (string text, int width, bool autoSize, string expectedText)
  3099. {
  3100. TextFormatter tf = new ()
  3101. {
  3102. Text = text,
  3103. Alignment = TextAlignment.Right,
  3104. AutoSize = autoSize,
  3105. };
  3106. if (!autoSize)
  3107. {
  3108. tf.Size = new Size (width, 1);
  3109. }
  3110. tf.Draw (new Rectangle (Point.Empty, new (width, 1)), Attribute.Default, Attribute.Default);
  3111. TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
  3112. }
  3113. [SetupFakeDriver]
  3114. [Theory]
  3115. [InlineData ("A", 0, false, "")]
  3116. [InlineData ("A", 1, false, "A")]
  3117. [InlineData ("A", 2, false, "A")]
  3118. [InlineData ("A", 3, false, " A")]
  3119. [InlineData ("AB", 1, false, "A")]
  3120. [InlineData ("AB", 2, false, "AB")]
  3121. [InlineData ("ABC", 3, false, "ABC")]
  3122. [InlineData ("ABC", 4, false, "ABC")]
  3123. [InlineData ("ABC", 5, false, " ABC")]
  3124. [InlineData ("ABC", 6, false, " ABC")]
  3125. [InlineData ("ABC", 9, false, " ABC")]
  3126. [InlineData ("A", 0, true, "")]
  3127. [InlineData ("A", 1, true, "A")]
  3128. [InlineData ("A", 2, true, "A")]
  3129. [InlineData ("A", 3, true, " A")]
  3130. [InlineData ("AB", 1, true, "A")]
  3131. [InlineData ("AB", 2, true, "AB")]
  3132. [InlineData ("ABC", 3, true, "ABC")]
  3133. [InlineData ("ABC", 4, true, "ABC")]
  3134. [InlineData ("ABC", 5, true, " ABC")]
  3135. [InlineData ("ABC", 6, true, " ABC")]
  3136. [InlineData ("ABC", 9, true, " ABC")]
  3137. public void Draw_Horizontal_Centered (string text, int width, bool autoSize, string expectedText)
  3138. {
  3139. TextFormatter tf = new ()
  3140. {
  3141. Text = text,
  3142. Alignment = TextAlignment.Centered,
  3143. AutoSize = autoSize,
  3144. };
  3145. if (!autoSize)
  3146. {
  3147. tf.Size = new Size (width, 1);
  3148. }
  3149. tf.Draw (new Rectangle (0, 0, width, 1), Attribute.Default, Attribute.Default);
  3150. TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
  3151. }
  3152. [SetupFakeDriver]
  3153. [Theory]
  3154. [InlineData ("A", 0, false, "")]
  3155. [InlineData ("A", 1, false, "A")]
  3156. [InlineData ("A", 2, false, "A")]
  3157. [InlineData ("A B", 3, false, "A B")]
  3158. [InlineData ("A B", 1, false, "A")]
  3159. [InlineData ("A B", 2, false, "A")]
  3160. [InlineData ("A B", 3, false, "A B")]
  3161. [InlineData ("A B", 4, false, "A B")]
  3162. [InlineData ("A B", 5, false, "A B")]
  3163. [InlineData ("A B", 6, false, "A B")]
  3164. [InlineData ("A B", 10, false, "A B")]
  3165. [InlineData ("ABC ABC", 10, false, "ABC ABC")]
  3166. [InlineData ("A", 0, true, "")]
  3167. [InlineData ("A", 1, true, "A")]
  3168. [InlineData ("A", 2, true, "A")]
  3169. [InlineData ("A B", 3, true, "A B")]
  3170. [InlineData ("A B", 1, true, "A")]
  3171. [InlineData ("A B", 2, true, "A")]
  3172. [InlineData ("A B", 3, true, "A B")]
  3173. [InlineData ("A B", 4, true, "A B")]
  3174. [InlineData ("A B", 5, true, "A B")]
  3175. [InlineData ("A B", 6, true, "A B")]
  3176. [InlineData ("A B", 10, true, "A B")]
  3177. [InlineData ("ABC ABC", 10, true, "ABC ABC")]
  3178. public void Draw_Horizontal_Justified (string text, int width, bool autoSize, string expectedText)
  3179. {
  3180. TextFormatter tf = new ()
  3181. {
  3182. Text = text,
  3183. Alignment = TextAlignment.Justified,
  3184. AutoSize = autoSize,
  3185. };
  3186. if (!autoSize)
  3187. {
  3188. tf.Size = new Size (width, 1);
  3189. }
  3190. tf.Draw (new Rectangle (0, 0, width, 1), Attribute.Default, Attribute.Default);
  3191. TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
  3192. }
  3193. [SetupFakeDriver]
  3194. [Theory]
  3195. [InlineData ("A", 5, false, "A")]
  3196. [InlineData ("AB12", 5, false, @"
  3197. A
  3198. B
  3199. 1
  3200. 2")]
  3201. [InlineData ("AB\n12", 5, false, @"
  3202. A1
  3203. B2")]
  3204. [InlineData ("", 1, false, "")]
  3205. public void Draw_Vertical_TopBottom_LeftRight (string text, int height, bool autoSize, string expectedText)
  3206. {
  3207. TextFormatter tf = new ()
  3208. {
  3209. Text = text,
  3210. AutoSize = autoSize,
  3211. Direction = TextDirection.TopBottom_LeftRight,
  3212. };
  3213. if (!autoSize)
  3214. {
  3215. tf.Size = new Size (5, height);
  3216. }
  3217. tf.Draw (new Rectangle (0, 0, 5, height), Attribute.Default, Attribute.Default);
  3218. TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
  3219. }
  3220. [SetupFakeDriver]
  3221. [Theory]
  3222. [InlineData ("Hello World", 15, 1, "Hello World")]
  3223. [InlineData ("Well Done\nNice Work", 15, 2, @"
  3224. Well Done
  3225. Nice Work")]
  3226. [InlineData ("你好 世界", 15, 1, "你好 世界")]
  3227. [InlineData ("做 得好\n幹 得好", 15, 2, @"
  3228. 做 得好
  3229. 幹 得好")]
  3230. public void Justify_Horizontal (string text, int width, int height, string expectedText)
  3231. {
  3232. TextFormatter tf = new ()
  3233. {
  3234. Text = text,
  3235. Alignment = TextAlignment.Justified,
  3236. Size = new Size (width, height),
  3237. MultiLine = true
  3238. };
  3239. tf.Draw (new Rectangle (0, 0, width, height), Attribute.Default, Attribute.Default);
  3240. TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
  3241. }
  3242. [SetupFakeDriver]
  3243. [Theory]
  3244. [InlineData ("Hello World", 1, 15, "H\ne\nl\nl\no\n \n \n \n \n \nW\no\nr\nl\nd")]
  3245. [InlineData ("Well Done\nNice Work", 2, 15, @"
  3246. WN
  3247. ei
  3248. lc
  3249. le
  3250. DW
  3251. oo
  3252. nr
  3253. ek")]
  3254. [InlineData ("你好 世界", 2, 15, "你\n好\n \n \n \n \n \n \n \n \n \n \n \n世\n界")]
  3255. [InlineData ("做 得好\n幹 得好", 4, 15, @"
  3256. 做幹
  3257. 得得
  3258. 好好")]
  3259. public void Justify_Vertical (string text, int width, int height, string expectedText)
  3260. {
  3261. TextFormatter tf = new ()
  3262. {
  3263. Text = text,
  3264. Direction = TextDirection.TopBottom_LeftRight,
  3265. VerticalAlignment = VerticalTextAlignment.Justified,
  3266. Size = new Size (width, height),
  3267. MultiLine = true
  3268. };
  3269. tf.Draw (new Rectangle (0, 0, width, height), Attribute.Default, Attribute.Default);
  3270. TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output);
  3271. }
  3272. }