// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; using FluentAssertions; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Tools.Test.Utilities; using Xunit; namespace Microsoft.DotNet.Tests { public class PrintableTableTests : TestBase { [Fact] public void GivenNoColumnsItPrintsNoLines() { var table = new PrintableTable(); var lines = new List(); table.PrintRows(new string[][] {}, l => lines.Add(l)); lines.Should().BeEmpty(); } [Fact] public void GivenAnEmptyRowsCollectionItPrintsColumnHeaders() { RunTest(new TestData() { Columns = new[] { "First Column", "2nd Column", "Another Column" }, Rows = new string[][] { }, ExpectedLines = new[] { "First Column 2nd Column Another Column", "------------------------------------------------" }, ExpectedTableWidth = 48 }); } [Fact] public void GivenASingleRowItPrintsCorrectly() { RunTest(new TestData() { Columns = new[] { "1st", "2nd", "3rd" }, Rows = new string[][] { new[] { "first", "second", "third" } }, ExpectedLines = new[] { "1st 2nd 3rd ", "----------------------------", "first second third" }, ExpectedTableWidth = 28 }); } [Fact] public void GivenMultipleRowsItPrintsCorrectly() { RunTest(new TestData() { Columns = new[] { "First", "Second", "Third", "Fourth", "Fifth" }, Rows = new string[][] { new[] { "1st", "2nd", "3rd", "4th", "5th" }, new [] { "a", "b", "c", "d", "e" }, new [] { "much longer string 1", "much longer string 2", "much longer string 3", "much longer string 4", "much longer string 5", } }, ExpectedLines = new[] { "First Second Third Fourth Fifth ", "----------------------------------------------------------------------------------------------------------------------------", "1st 2nd 3rd 4th 5th ", "a b c d e ", "much longer string 1 much longer string 2 much longer string 3 much longer string 4 much longer string 5" }, ExpectedTableWidth = 124 }); } [Fact] public void GivenARowWithEmptyStringsItPrintsCorrectly() { RunTest(new TestData() { Columns = new[] { "First", "Second", "Third", "Fourth", "Fifth" }, Rows = new string[][] { new[] { "1st", "2nd", "3rd", "4th", "5th" }, new [] { "", "", "", "", "" }, new [] { "much longer string 1", "much longer string 2", "much longer string 3", "much longer string 4", "much longer string 5", } }, ExpectedLines = new[] { "First Second Third Fourth Fifth ", "----------------------------------------------------------------------------------------------------------------------------", "1st 2nd 3rd 4th 5th ", " ", "much longer string 1 much longer string 2 much longer string 3 much longer string 4 much longer string 5" }, ExpectedTableWidth = 124 }); } [Fact] public void GivenColumnsWithMaximumWidthsItPrintsCorrectly() { RunTest(new TestData() { Columns = new[] { "First", "Second", "Third", }, ColumnWidths = new[] { 3, int.MaxValue, 4 }, Rows = new string[][] { new[] { "123", "1234567890", "1234" }, new [] { "1", "1", "1", }, new [] { "12345", "a much longer string", "1234567890" }, new [] { "123456", "hello world", "12345678" } }, ExpectedLines = new[] { "Fir Second Thir", "st d ", "---------------------------------------", "123 1234567890 1234", "1 1 1 ", "123 a much longer string 1234", "45 5678", " 90 ", "123 hello world 1234", "456 5678" }, ExpectedTableWidth = 39 }); } [Fact] public void GivenARowContainingUnicodeCharactersItPrintsCorrectly() { RunTest(new TestData() { Columns = new[] { "Poem" }, Rows = new string[][] { new [] { "\u3044\u308D\u306F\u306B\u307B\u3078\u3068\u3061\u308A\u306C\u308B\u3092" } }, ExpectedLines = new[] { "Poem ", "------------", "\u3044\u308D\u306F\u306B\u307B\u3078\u3068\u3061\u308A\u306C\u308B\u3092" }, ExpectedTableWidth = 12 }); } [Fact] public void GivenARowContainingUnicodeCharactersItWrapsCorrectly() { RunTest(new TestData() { Columns = new[] { "Poem" }, ColumnWidths = new [] { 5 }, Rows = new string[][] { new [] { "\u3044\u308D\u306F\u306B\u307B\u3078\u3068\u3061\u308A\u306C\u308B\u3092" } }, ExpectedLines = new[] { "Poem ", "-----", "\u3044\u308D\u306F\u306B\u307B", "\u3078\u3068\u3061\u308A\u306C", "\u308B\u3092 " }, ExpectedTableWidth = 5 }); } [Fact] public void GivenARowContainingUnicodeCombiningCharactersItPrintsCorrectly() { // The unicode string is "test" with "enclosing circle backslash" around each character // Given 0x20E0 is a combining character, the string should be four graphemes in length, // despite having eight codepoints. Thus there should be 10 spaces following the characters. RunTest(new TestData() { Columns = new[] { "Unicode String" }, Rows = new string[][] { new [] { "\u0074\u20E0\u0065\u20E0\u0073\u20E0\u0074\u20E0" } }, ExpectedLines = new[] { "Unicode String", "--------------", "\u0074\u20E0\u0065\u20E0\u0073\u20E0\u0074\u20E0 " }, ExpectedTableWidth = 14 }); } [Fact] public void GivenARowContainingUnicodeCombiningCharactersItWrapsCorrectly() { // See comment for GivenARowContainingUnicodeCombiningCharactersItPrintsCorrectly regarding string content // This should wrap after the second grapheme rather than the second code point (constituting the first grapheme) RunTest(new TestData() { Columns = new[] { "01" }, ColumnWidths = new[] { 2 }, Rows = new string[][] { new [] { "\u0074\u20E0\u0065\u20E0\u0073\u20E0\u0074\u20E0" } }, ExpectedLines = new[] { "01", "--", "\u0074\u20E0\u0065\u20E0", "\u0073\u20E0\u0074\u20E0" }, ExpectedTableWidth = 2 }); } [Fact] public void GivenAnEmptyColumnHeaderItPrintsTheColumnHeaderAsEmpty() { RunTest(new TestData() { Columns = new[] { "First", "", "Third", }, Rows = new string[][] { new[] { "1st", "2nd", "3rd" } }, ExpectedLines = new[] { "First Third", "-------------------------", "1st 2nd 3rd " }, ExpectedTableWidth = 25 }); } [Fact] public void GivenAllEmptyColumnHeadersItPrintsTheEntireHeaderAsEmpty() { RunTest(new TestData() { Columns = new[] { null, "", null, }, Rows = new string[][] { new[] { "1st", "2nd", "3rd" } }, ExpectedLines = new[] { " ", "---------------------", "1st 2nd 3rd" }, ExpectedTableWidth = 21 }); } [Fact] public void GivenZeroWidthColumnsItSkipsTheColumns() { RunTest(new TestData() { Columns = new[] { "", "First", null, "Second", "" }, Rows = new string[][] { new[] { "", "1st", null, "2nd", "" } }, ExpectedLines = new[] { "First Second", "-----------------", "1st 2nd " }, ExpectedTableWidth = 17 }); } public class TestData { public IEnumerable Columns { get; set; } public int[] ColumnWidths { get; set; } public IEnumerable Rows { get; set; } public IEnumerable ExpectedLines { get; set; } public int ExpectedTableWidth { get; set; } } private void RunTest(TestData data) { var table = new PrintableTable(); int index = 0; foreach (var column in data.Columns) { var i = index; table.AddColumn( column, r => r[i], data.ColumnWidths?[i] ?? int.MaxValue); ++index; } var lines = new List(); table.PrintRows(data.Rows, l => lines.Add(l)); lines.Should().Equal(data.ExpectedLines); table.CalculateWidth(data.Rows).Should().Be(data.ExpectedTableWidth); } } }