public static bool Run()
        {
            Console.WriteLine("Text Block Load Test");
            Console.WriteLine("--------------------");
            Console.WriteLine();

            string typefaceName = "Arial";
            float  Scale        = 1.5f;
            var    styleSmall   = new Style()
            {
                FontFamily = typefaceName, FontSize = 12 * Scale
            };
            var styleScript = new Style()
            {
                FontFamily = "Segoe Script", FontSize = 18 * Scale
            };
            var styleHeading = new Style()
            {
                FontFamily = typefaceName, FontSize = 24 * Scale, FontWeight = 700
            };
            var styleNormal = new Style()
            {
                FontFamily = typefaceName, FontSize = 18 * Scale, LineHeight = 1.0f
            };
            var styleBold = new Style()
            {
                FontFamily = typefaceName, FontSize = 18 * Scale, FontWeight = 700
            };
            var styleUnderline = new Style()
            {
                FontFamily = typefaceName, FontSize = 18 * Scale, Underline = UnderlineStyle.Gapped
            };
            var styleStrike = new Style()
            {
                FontFamily = typefaceName, FontSize = 18 * Scale, StrikeThrough = StrikeThroughStyle.Solid
            };
            var styleSubScript = new Style()
            {
                FontFamily = typefaceName, FontSize = 18 * Scale, FontVariant = FontVariant.SubScript
            };
            var styleSuperScript = new Style()
            {
                FontFamily = typefaceName, FontSize = 18 * Scale, FontVariant = FontVariant.SuperScript
            };
            var styleItalic = new Style()
            {
                FontFamily = typefaceName, FontItalic = true, FontSize = 18 * Scale
            };
            var styleBoldLarge = new Style()
            {
                FontFamily = typefaceName, FontSize = 28 * Scale, FontWeight = 700
            };
            var styleRed = new Style()
            {
                FontFamily = typefaceName, FontSize = 18 * Scale                         /*, TextColor = new SKColor(0xFFFF0000) */
            };
            var styleBlue = new Style()
            {
                FontFamily = typefaceName, FontSize = 18 * Scale                          /*, TextColor = new SKColor(0xFF0000FF) */
            };


            var tr = new TestResults();
            var tb = new TextBlock();

            for (int i = 0; i < 1000; i++)
            {
                tr.EnterTest();

                tb.Clear();
                tb.MaxWidth = 1000;
                tb.AddText("Welcome to RichTextKit!\n", styleHeading);
                tb.AddText("\nRichTextKit is a rich text layout, rendering and measurement library for SkiaSharp.\n\nIt supports normal, ", styleNormal);
                tb.AddText("bold", styleBold);
                tb.AddText(", ", styleNormal);
                tb.AddText("italic", styleItalic);
                tb.AddText(", ", styleNormal);
                tb.AddText("underline", styleUnderline);
                tb.AddText(" (including ", styleNormal);
                tb.AddText("gaps over descenders", styleUnderline);
                tb.AddText("), ", styleNormal);
                tb.AddText("strikethrough", styleStrike);
                tb.AddText(", superscript (E=mc", styleNormal);
                tb.AddText("2", styleSuperScript);
                tb.AddText("), subscript (H", styleNormal);
                tb.AddText("2", styleSubScript);
                tb.AddText("O), ", styleNormal);
                tb.AddText("colored ", styleRed);
                tb.AddText("text", styleBlue);
                tb.AddText(" and ", styleNormal);
                tb.AddText("mixed ", styleNormal);
                tb.AddText("sizes", styleSmall);
                tb.AddText(" and ", styleNormal);
                tb.AddText("fonts", styleScript);
                tb.AddText(".\n\n", styleNormal);
                tb.AddText("Font fallback means emojis work: 🌐 🍪 🍕 🚀 and ", styleNormal);
                tb.AddText("text shaping and bi-directional text support means complex scripts and languages like Arabic: مرحبا بالعالم, Japanese: ハローワールド, Chinese: 世界您好 and Hindi: हैलो वर्ल्ड are rendered correctly!\n\n", styleNormal);
                tb.AddText("RichTextKit also supports left/center/right text alignment, word wrapping, truncation with ellipsis place-holder, text measurement, hit testing, painting a selection range, caret position & shape helpers.", styleNormal);
                tb.Layout();
                tr.LeaveTest();
                tr.TestPassed(true);
            }

            tr.Dump();
            return(tr.AllPassed);
        }
        public static bool Run()
        {
            Console.WriteLine("Bidi Character Tests");
            Console.WriteLine("--------------------");
            Console.WriteLine();

            // Read the test file
            var location = System.IO.Path.GetDirectoryName(typeof(Program).Assembly.Location);
            var lines    = System.IO.File.ReadAllLines(System.IO.Path.Combine(location, "BidiCharacterTest.txt"));

            // Parse lines
            var tests = new List <Test>();

            for (int lineNumber = 1; lineNumber < lines.Length + 1; lineNumber++)
            {
                // Get the line, remove comments
                var line = lines[lineNumber - 1].Split('#')[0].Trim();

                // Ignore blank/comment only lines
                if (string.IsNullOrWhiteSpace(line))
                {
                    continue;
                }

                // Split into fields
                var fields = line.Split(';');

                var test = new Test();
                test.LineNumber = lineNumber;

                // Parse field 0 - code points
                test.CodePoints = fields[0].Split(' ').Select(x => x.Trim()).Where(x => !string.IsNullOrEmpty(x)).Select(x => Convert.ToInt32(x, 16)).ToArray();

                // Parse field 1 - paragraph level
                test.ParagraphLevel = sbyte.Parse(fields[1]);

                // Parse field 2 - resolved paragraph level
                test.ResolvedParagraphLevel = sbyte.Parse(fields[2]);

                // Parse field 3 - resolved levels
                test.ResolvedLevels = fields[3].Split(' ').Select(x => x.Trim()).Where(x => !string.IsNullOrEmpty(x)).Select(x => x == "x" ? (sbyte)-1 : Convert.ToSByte(x)).ToArray();

                // Parse field 4 - resolved levels
                test.ResolvedOrder = fields[4].Split(' ').Select(x => x.Trim()).Where(x => !string.IsNullOrEmpty(x)).Select(x => Convert.ToInt32(x)).ToArray();

                tests.Add(test);
            }

            Console.WriteLine($"Test data loaded: {tests.Count} test cases");

            var bidi     = new Bidi();
            var bidiData = new BidiData();

            // Run tests...
            var tr = new TestResults();

            for (int testNumber = 0; testNumber < tests.Count; testNumber++)
            {
                var t = tests[testNumber];

                // Arrange
                bidiData.Init(new Slice <int>(t.CodePoints), t.ParagraphLevel);

                // Act

                tr.EnterTest();
                for (int i = 0; i < 10; i++)
                {
                    bidi.Process(bidiData);
                }
                tr.LeaveTest();
                var resultLevels         = bidi.ResolvedLevels;
                int resultParagraphLevel = bidi.ResolvedParagraphEmbeddingLevel;

                // Assert
                bool passed = true;
                if (t.ResolvedParagraphLevel != resultParagraphLevel)
                {
                    passed = false;
                }
                for (int i = 0; i < t.ResolvedLevels.Length; i++)
                {
                    if (t.ResolvedLevels[i] == -1)
                    {
                        continue;
                    }

                    if (t.ResolvedLevels[i] != resultLevels[i])
                    {
                        passed = false;
                        break;
                    }
                }

                /*
                 * if (!passed)
                 * {
                 *  Console.WriteLine($"Failed line {t.LineNumber}");
                 *  Console.WriteLine();
                 *  Console.WriteLine($"             Code Points: {string.Join(" ", t.CodePoints.Select(x => x.ToString("X4")))}");
                 *  Console.WriteLine($"      Pair Bracket Types: {string.Join(" ", bidiData.PairedBracketTypes.Select(x => "   " + x.ToString()))}");
                 *  Console.WriteLine($"     Pair Bracket Values: {string.Join(" ", bidiData.PairedBracketValues.Select(x => x.ToString("X4")))}");
                 *  Console.WriteLine($"             Embed Level: {t.ParagraphLevel}");
                 *  Console.WriteLine($"    Expected Embed Level: {t.ResolvedParagraphLevel}");
                 *  Console.WriteLine($"      Actual Embed Level: {resultParagraphLevel}");
                 *  Console.WriteLine($"          Directionality: {string.Join(" ", bidiData.Types)}");
                 *  Console.WriteLine($"         Expected Levels: {string.Join(" ", t.ResolvedLevels)}");
                 *  Console.WriteLine($"           Actual Levels: {string.Join(" ", resultLevels)}");
                 *  Console.WriteLine();
                 *  return false;
                 * }
                 */

                // Record it
                tr.TestPassed(passed);
            }

            tr.Dump();

            return(tr.AllPassed);
        }
Exemple #3
0
        public static bool Run()
        {
            Console.WriteLine("Bidi Class Tests");
            Console.WriteLine("----------------");
            Console.WriteLine();

            // Read the test file
            var location = System.IO.Path.GetDirectoryName(typeof(Program).Assembly.Location);
            var lines    = System.IO.File.ReadAllLines(System.IO.Path.Combine(location, "BidiTest.txt"));

            var bidi = new Bidi();

            List <Test> tests = new List <Test>();

            // Process each line
            int[] levels = null;
            for (int lineNumber = 1; lineNumber < lines.Length + 1; lineNumber++)
            {
                // Get the line, remove comments
                var line = lines[lineNumber - 1].Split('#')[0].Trim();

                // Ignore blank/comment only lines
                if (string.IsNullOrWhiteSpace(line))
                {
                    continue;
                }

                // Directive?
                if (line.StartsWith("@"))
                {
                    if (line.StartsWith("@Levels:"))
                    {
                        levels = line.Substring(8).Trim().Split(' ').Where(x => x.Length > 0).Select(x =>
                        {
                            if (x == "x")
                            {
                                return(-1);
                            }
                            else
                            {
                                return(int.Parse(x));
                            }
                        }).ToArray();
                    }
                    continue;
                }

                // Split data line
                var parts = line.Split(';');
                System.Diagnostics.Debug.Assert(parts.Length == 2);

                // Get the directions
                var directions = parts[0].Split(' ').Select(x => DirectionalityFromName(x)).ToArray();

                // Get the bit set
                var bitset = Convert.ToInt32(parts[1].Trim(), 16);

                var pairTypes  = Enumerable.Repeat(PairedBracketType.n, directions.Length).ToArray();
                var pairValues = Enumerable.Repeat(0, directions.Length).ToArray();

                for (int bit = 1; bit < 8; bit <<= 1)
                {
                    if ((bitset & bit) == 0)
                    {
                        continue;
                    }

                    sbyte paragraphEmbeddingLevel;
                    switch (bit)
                    {
                    case 1:
                        paragraphEmbeddingLevel = 2;            // Auto
                        break;

                    case 2:
                        paragraphEmbeddingLevel = 0;            // LTR
                        break;

                    case 4:
                        paragraphEmbeddingLevel = 1;            // RTL
                        break;

                    default:
                        throw new NotImplementedException();
                    }


                    tests.Add(new Test()
                    {
                        Types = new Slice <Directionality>(directions),
                        ParagraphEmbeddingLevel = paragraphEmbeddingLevel,
                        ExpectedLevels          = levels,
                        LineNumber = lineNumber,
                    });
                }
            }

            Console.WriteLine($"Test data loaded: {tests.Count} test cases");

            var tr = new TestResults();

#if FOR_PROFILING
            for (int repeat = 0; repeat < 50; repeat++)
#endif
            for (int testNumber = 0; testNumber < tests.Count; testNumber++)
            {
                var t = tests[testNumber];

                // Run the algorithm...
                Slice <sbyte> resultLevels;
                tr.EnterTest();
                bidi.Process(t.Types, Slice <PairedBracketType> .Empty, Slice <int> .Empty, t.ParagraphEmbeddingLevel, false, null, null, null);
                tr.LeaveTest();
                resultLevels = bidi.ResolvedLevels;


                // Check the results match
                bool pass = true;
                if (resultLevels.Length == t.ExpectedLevels.Length)
                {
                    for (int i = 0; i < t.ExpectedLevels.Length; i++)
                    {
                        if (t.ExpectedLevels[i] == -1)
                        {
                            continue;
                        }

                        if (resultLevels[i] != t.ExpectedLevels[i])
                        {
                            pass = false;
                            break;
                        }
                    }
                }
                else
                {
                    pass = false;
                }

                tr.TestPassed(pass);

                if (pass)
                {
//                    Console.WriteLine($"Passed line {t.LineNumber} {t.ParagraphEmbeddingLevel}");
                }
                else
                {
                    Console.WriteLine($"Failed line {t.LineNumber}");
                    Console.WriteLine();
                    Console.WriteLine($"        Data: {string.Join(" ", t.Types)}");
                    Console.WriteLine($" Embed Level: {t.ParagraphEmbeddingLevel}");
                    Console.WriteLine($"    Expected: {string.Join(" ", t.ExpectedLevels)}");
                    Console.WriteLine($"      Actual: {string.Join(" ", resultLevels)}");
                    Console.WriteLine();
                    return(false);
                }
            }

            tr.Dump();

            return(tr.AllPassed);
        }
Exemple #4
0
        public static bool Run()
        {
            Console.WriteLine("Line Breaker Tests");
            Console.WriteLine("------------------");
            Console.WriteLine();

            // Read the test file
            var location = System.IO.Path.GetDirectoryName(typeof(LineBreakTest).Assembly.Location);
            var lines    = System.IO.File.ReadAllLines(System.IO.Path.Combine(location, "LineBreakTest.txt"));

            // Process each line
            var tests = new List <Test>();

            for (int lineNumber = 1; lineNumber < lines.Length + 1; lineNumber++)
            {
                // Ignore deliberately skipped test?
                if (_skipLines.Contains(lineNumber - 1))
                {
                    continue;
                }

                // Get the line, remove comments
                var line = lines[lineNumber - 1].Split('#')[0].Trim();

                // Ignore blank/comment only lines
                if (string.IsNullOrWhiteSpace(line))
                {
                    continue;
                }

                var codePoints  = new List <int>();
                var breakPoints = new List <int>();

                // Parse the test
                var p = 0;
                while (p < line.Length)
                {
                    // Ignore white space
                    if (char.IsWhiteSpace(line[p]))
                    {
                        p++;
                        continue;
                    }

                    if (line[p] == '×')
                    {
                        p++;
                        continue;
                    }

                    if (line[p] == '÷')
                    {
                        breakPoints.Add(codePoints.Count);
                        p++;
                        continue;
                    }

                    int codePointPos = p;
                    while (p < line.Length && IsHexDigit(line[p]))
                    {
                        p++;
                    }

                    var codePointStr = line.Substring(codePointPos, p - codePointPos);
                    var codePoint    = Convert.ToInt32(codePointStr, 16);
                    codePoints.Add(codePoint);
                }

                // Create test
                var test = new Test()
                {
                    LineNumber  = lineNumber,
                    CodePoints  = codePoints.ToArray(),
                    BreakPoints = breakPoints.ToArray(),
                };
                tests.Add(test);
            }

            var lineBreaker = new LineBreaker();
            var tr          = new TestResults();

            var foundBreaks = new List <int>();

            foundBreaks.Capacity = 100;

            for (int testNumber = 0; testNumber < tests.Count; testNumber++)
            {
                var t = tests[testNumber];

                foundBreaks.Clear();

                // Run the line breaker and build a list of break points
                tr.EnterTest();
                lineBreaker.Reset(new Slice <int>(t.CodePoints));
                while (lineBreaker.NextBreak(out var b))
                {
                    foundBreaks.Add(b.PositionWrap);
                }
                tr.LeaveTest();

                // Check the same
                bool pass = true;
                if (foundBreaks.Count != t.BreakPoints.Length)
                {
                    pass = false;
                }
                else
                {
                    for (int i = 0; i < foundBreaks.Count; i++)
                    {
                        if (foundBreaks[i] != t.BreakPoints[i])
                        {
                            pass = false;
                        }
                    }
                }

                if (!pass)
                {
                    Console.WriteLine($"Failed test on line {t.LineNumber}");
                    Console.WriteLine();
                    Console.WriteLine($"    Code Points: {string.Join(" ", t.CodePoints)}");
                    Console.WriteLine($"Expected Breaks: {string.Join(" ", t.BreakPoints)}");
                    Console.WriteLine($"  Actual Breaks: {string.Join(" ", foundBreaks)}");
                    Console.WriteLine($"     Char Props: {string.Join(" ", t.CodePoints.Select(x => UnicodeClasses.LineBreakClass(x)))}");
                    Console.WriteLine();
                    return(false);
                }

                // Record it
                tr.TestPassed(pass);
            }

            tr.Dump();

            return(tr.AllPassed);
        }