using System.Collections;
using System.Management.Automation;
using Microsoft.PowerShell;
using Xunit;
namespace PSTests.Parallel
{
public static class ProgressNodeTests
{
[Theory]
[InlineData(79)]
[InlineData(80)]
[InlineData(81)]
public static void ProgressBar_OriginalBugScenario_MustNotExceedMaxWidth(int maxWidth)
{
var rawUI = new TestProgressRawUI();
var record = new ProgressRecord(0, "My Status", "1/6 次の段階");
var node = new ProgressNode(1, record);
node.PercentComplete = 50;
node.SecondsRemaining = 120;
node.Style = ProgressNode.RenderStyle.Ansi;
var strCollection = new ArrayList();
node.Render(strCollection, 0, maxWidth, rawUI);
var output = strCollection[0] as string;
int actualWidth = ConsoleControl.LengthInBufferCells(output, 0, checkEscapeSequences: true);
Assert.True(
actualWidth <= maxWidth,
$"Issue #21293 (width {maxWidth}): Progress bar ({actualWidth} cells) exceeds maxWidth ({maxWidth}). Output: {output}");
}
[Theory]
[InlineData(79)]
[InlineData(80)]
[InlineData(81)]
public static void ProgressBar_WithDoubleWidthChars_MustNotExceedMaxWidth(int maxWidth)
{
var rawUI = new TestProgressRawUI();
var record = new ProgressRecord(0, "Activity", "日本語テキスト");
var node = new ProgressNode(1, record);
node.PercentComplete = 50;
node.Style = ProgressNode.RenderStyle.Ansi;
var strCollection = new ArrayList();
node.Render(strCollection, 0, maxWidth, rawUI);
var output = strCollection[0] as string;
int actualWidth = ConsoleControl.LengthInBufferCells(output, 0, checkEscapeSequences: true);
Assert.True(
actualWidth <= maxWidth,
$"Progress bar width {maxWidth}: ({actualWidth}) exceeds maxWidth ({maxWidth}). Output: {output}");
}
[Theory]
[InlineData(79)]
[InlineData(80)]
[InlineData(81)]
public static void ProgressBar_WithEmoji_MustNotExceedMaxWidth(int maxWidth)
{
var rawUI = new TestProgressRawUI();
var record = new ProgressRecord(0, "Upload", "📁ファイル転送中🔄処理中📊");
var node = new ProgressNode(1, record);
node.PercentComplete = 50;
node.Style = ProgressNode.RenderStyle.Ansi;
var strCollection = new ArrayList();
node.Render(strCollection, 0, maxWidth, rawUI);
var output = strCollection[0] as string;
int actualWidth = ConsoleControl.LengthInBufferCells(output, 0, checkEscapeSequences: true);
Assert.True(
actualWidth <= maxWidth,
$"Emoji progress bar width {maxWidth}: ({actualWidth} cells) exceeds maxWidth ({maxWidth}). Output: {output}");
}
[Theory]
[InlineData("あいうえお", 79)]
[InlineData("あいうえお", 80)]
[InlineData("あいうえお", 81)]
[InlineData("データ処理中", 79)]
[InlineData("データ処理中", 80)]
[InlineData("データ処理中", 81)]
[InlineData("ファイルをアップロード中", 79)]
[InlineData("ファイルをアップロード中", 80)]
[InlineData("ファイルをアップロード中", 81)]
public static void ProgressBar_VariousDoubleWidthLengths_MustNotExceedMaxWidth(string statusText, int maxWidth)
{
var rawUI = new TestProgressRawUI();
var record = new ProgressRecord(0, "Test", statusText);
var node = new ProgressNode(1, record);
node.PercentComplete = 50;
node.Style = ProgressNode.RenderStyle.Ansi;
var strCollection = new ArrayList();
node.Render(strCollection, 0, maxWidth, rawUI);
var output = strCollection[0] as string;
int actualWidth = ConsoleControl.LengthInBufferCells(output, 0, checkEscapeSequences: true);
Assert.True(
actualWidth <= maxWidth,
$"Status '{statusText}' width {maxWidth}: resulted in width {actualWidth} exceeding maxWidth {maxWidth}. Output: {output}");
}
[Theory]
[InlineData(79)]
[InlineData(80)]
[InlineData(81)]
public static void ProgressBar_MixedWidthText_MustNotExceedMaxWidth(int maxWidth)
{
var rawUI = new TestProgressRawUI();
var record = new ProgressRecord(0, "Process", "File_日本語_document.txt");
var node = new ProgressNode(1, record);
node.PercentComplete = 50;
node.Style = ProgressNode.RenderStyle.Ansi;
var strCollection = new ArrayList();
node.Render(strCollection, 0, maxWidth, rawUI);
var output = strCollection[0] as string;
int actualWidth = ConsoleControl.LengthInBufferCells(output, 0, checkEscapeSequences: true);
Assert.True(
actualWidth <= maxWidth,
$"Mixed-width text width {maxWidth}: resulted in width {actualWidth} exceeding maxWidth {maxWidth}. Output: {output}");
}
[Theory]
[InlineData(79)]
[InlineData(80)]
[InlineData(81)]
public static void ProgressBar_LongDoubleWidthString_MustNotExceedMaxWidth(int maxWidth)
{
var rawUI = new TestProgressRawUI();
var record = new ProgressRecord(0, "処理", "ファイルを処理中です今しばらくお待ちください");
var node = new ProgressNode(1, record);
node.PercentComplete = 50;
node.Style = ProgressNode.RenderStyle.Ansi;
var strCollection = new ArrayList();
node.Render(strCollection, 0, maxWidth, rawUI);
var output = strCollection[0] as string;
int actualWidth = ConsoleControl.LengthInBufferCells(output, 0, checkEscapeSequences: true);
Assert.True(
actualWidth <= maxWidth,
$"Long double-width text width {maxWidth}: resulted in width {actualWidth} exceeding maxWidth {maxWidth}. Output: {output}");
}
[Theory]
[InlineData(79)]
[InlineData(80)]
[InlineData(81)]
public static void ProgressBar_Truncation_MustRespectCharacterBoundaries(int maxWidth)
{
var rawUI = new TestProgressRawUI();
var record = new ProgressRecord(0, "Downloading", "ファイル処理中123456789012345678901234567890");
var node = new ProgressNode(1, record);
node.PercentComplete = 50;
node.Style = ProgressNode.RenderStyle.Ansi;
var strCollection = new ArrayList();
node.Render(strCollection, 0, maxWidth, rawUI);
var output = strCollection[0] as string;
int actualWidth = ConsoleControl.LengthInBufferCells(output, 0, checkEscapeSequences: true);
Assert.True(
actualWidth <= maxWidth,
$"Truncated progress bar width {maxWidth}: ({actualWidth} cells) exceeds maxWidth ({maxWidth}). Output: {output}");
}
[Theory]
[InlineData(79)]
[InlineData(80)]
[InlineData(81)]
public static void ProgressBar_LongStatusWithDoubleWidth_MustNotExceedMaxWidth(int maxWidth)
{
var rawUI = new TestProgressRawUI();
var record = new ProgressRecord(0, "Long Text Test", "🚀🎯⚡💾✅ 絵文字も含めた超長文テストケース with emojis and very long text to test the truncation feature properly");
var node = new ProgressNode(1, record);
node.PercentComplete = 100;
node.Style = ProgressNode.RenderStyle.Ansi;
var strCollection = new ArrayList();
node.Render(strCollection, 0, maxWidth, rawUI);
var output = strCollection[0] as string;
int actualWidth = ConsoleControl.LengthInBufferCells(output, 0, checkEscapeSequences: true);
Assert.True(
actualWidth <= maxWidth,
$"Progress bar with long truncated status width {maxWidth}: ({actualWidth} cells) exceeds maxWidth ({maxWidth}). Output: {output}");
int closingBracketCount = 0;
for (int i = output.Length - 1; i >= 0 && output[i] == ']'; i--)
{
closingBracketCount++;
}
Assert.True(
closingBracketCount <= 1,
$"Found {closingBracketCount} consecutive closing brackets at end (width {maxWidth}), expected 1. Output: {output}");
}
[Theory]
[InlineData(79)]
[InlineData(80)]
[InlineData(81)]
public static void ExtremeTest_ActivityWidthBug_SmallMaxWidth(int maxWidth)
{
var rawUI = new TestProgressRawUI();
string longActivity = "日本語日本語日本語日本語日本語";
var record = new ProgressRecord(0, longActivity, "St");
var node = new ProgressNode(1, record);
node.PercentComplete = 50;
node.SecondsRemaining = 30;
node.Style = ProgressNode.RenderStyle.Ansi;
var strCollection = new ArrayList();
node.Render(strCollection, 0, maxWidth, rawUI);
var output = strCollection[0] as string;
int actualWidth = ConsoleControl.LengthInBufferCells(output, 0, checkEscapeSequences: true);
Assert.True(
actualWidth <= maxWidth,
$"maxWidth {maxWidth} test: width {actualWidth} exceeds {maxWidth}. Output: [{output}]");
}
[Theory]
[InlineData(79)]
[InlineData(80)]
[InlineData(81)]
public static void ExtremeTest_SurrogatePairAtVTBoundary(int maxWidth)
{
var rawUI = new TestProgressRawUI();
var record = new ProgressRecord(0, "A", "X🚀Y🚀Z🚀");
var node = new ProgressNode(1, record);
node.PercentComplete = 33;
node.Style = ProgressNode.RenderStyle.Ansi;
var strCollection = new ArrayList();
node.Render(strCollection, 0, maxWidth, rawUI);
var output = strCollection[0] as string;
int actualWidth = ConsoleControl.LengthInBufferCells(output, 0, checkEscapeSequences: true);
Assert.True(
actualWidth <= maxWidth,
$"Surrogate pair test width {maxWidth}: width {actualWidth} exceeds {maxWidth}. Output: [{output}]");
}
}
}